private void continueNumber(CharArr arr) throws IOException { if (arr != out) arr.write(out); if ((nstate & HAS_EXPONENT) != 0) { readExpDigits(arr, Integer.MAX_VALUE); return; } if (nstate != 0) { readFrac(arr, Integer.MAX_VALUE); return; } for (; ; ) { int ch = getChar(); if (ch >= '0' && ch <= '9') { arr.write(ch); } else if (ch == '.') { arr.write(ch); readFrac(arr, Integer.MAX_VALUE); return; } else if (ch == 'e' || ch == 'E') { arr.write(ch); readExp(arr, Integer.MAX_VALUE); return; } else { if (ch != -1) start--; return; } } }
public static void unicodeEscape(int ch, CharArr out) { out.write('\\'); out.write('u'); out.write(HEX_CHARS[ch >>> 12]); out.write(HEX_CHARS[(ch >>> 8) & 0xf]); out.write(HEX_CHARS[(ch >>> 4) & 0xf]); out.write(HEX_CHARS[ch & 0xf]); }
// read digits right of decimal point private int readFrac(CharArr arr, int lim) throws IOException { nstate = HAS_FRACTION; // deliberate set instead of '|' while (--lim >= 0) { int ch = getChar(); if (ch >= '0' && ch <= '9') { arr.write(ch); } else if (ch == 'e' || ch == 'E') { arr.write(ch); return readExp(arr, lim); } else { if (ch != -1) start--; // back up return NUMBER; } } return BIGNUMBER; }
private CharArr readStringChars() throws IOException { char c = 0; int i; for (i = start; i < end; i++) { c = buf[i]; if (c == '"') { tmp.set(buf, start, i); // directly use input buffer start = i + 1; // advance past last '"' return tmp; } else if (c == '\\') { break; } } out.reset(); readStringChars2(out, i); return out; }
// call after 'e' or 'E' has been seen to read the rest of the exponent private int readExp(CharArr arr, int lim) throws IOException { nstate |= HAS_EXPONENT; int ch = getChar(); lim--; if (ch == '+' || ch == '-') { arr.write(ch); ch = getChar(); lim--; } // make sure at least one digit is read. if (ch < '0' || ch > '9') { throw err("missing exponent number"); } arr.write(ch); return readExpDigits(arr, lim); }
/** Reads a JSON numeric value into the output. */ public void getNumberChars(CharArr output) throws IOException { int ev = 0; if (valstate == 0) ev = nextEvent(); if (valstate == LONG || valstate == NUMBER) output.write(this.out); else if (valstate == BIGNUMBER) { continueNumber(output); } else { throw err("Unexpected " + ev); } valstate = 0; }
// continuation of readExpStart private int readExpDigits(CharArr arr, int lim) throws IOException { while (--lim >= 0) { int ch = getChar(); if (ch >= '0' && ch <= '9') { arr.write(ch); } else { if (ch != -1) start--; // back up return NUMBER; } } return BIGNUMBER; }
// middle is the pointer to the middle of a buffer to start scanning for a non-string // character ('"' or "/"). start<=middle<end // this should be faster for strings with fewer escapes, but probably slower for many escapes. private void readStringChars2(CharArr arr, int middle) throws IOException { for (; ; ) { if (middle >= end) { arr.write(buf, start, middle - start); start = middle; getMore(); middle = start; } int ch = buf[middle++]; if (ch == '"') { int len = middle - start - 1; if (len > 0) arr.write(buf, start, len); start = middle; return; } else if (ch == '\\') { int len = middle - start - 1; if (len > 0) arr.write(buf, start, len); start = middle; arr.write(readEscapedChar()); middle = start; } } }
public static void writeString(CharSequence val, int start, int end, CharArr out) { out.write('"'); writeStringPart(val, start, end, out); out.write('"'); }
// return the next event when parser is in a neutral state (no // map separators or array element separators to read private int next(int ch) throws IOException { for (; ; ) { switch (ch) { case ' ': case '\t': break; case '\r': case '\n': break; // try and keep track of linecounts? case '"': valstate = STRING; return STRING; case '{': push(); state = DID_OBJSTART; return OBJECT_START; case '[': push(); state = DID_ARRSTART; return ARRAY_START; case '0': out.reset(); // special case '0'? If next char isn't '.' val=0 ch = getChar(); if (ch == '.') { start--; ch = '0'; readNumber('0', false); return valstate; } else if (ch > '9' || ch < '0') { out.unsafeWrite('0'); if (ch != -1) start--; lval = 0; valstate = LONG; return LONG; } else { throw err("Leading zeros not allowed"); } case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': out.reset(); lval = readNumber(ch, false); return valstate; case '-': out.reset(); out.unsafeWrite('-'); ch = getChar(); if (ch < '0' || ch > '9') throw err("expected digit after '-'"); lval = readNumber(ch, true); return valstate; case 't': valstate = BOOLEAN; // TODO: test performance of this non-branching inline version. // if ((('r'-getChar())|('u'-getChar())|('e'-getChar())) != 0) err(""); expect(JSONUtil.TRUE_CHARS); bool = true; return BOOLEAN; case 'f': valstate = BOOLEAN; expect(JSONUtil.FALSE_CHARS); bool = false; return BOOLEAN; case 'n': valstate = NULL; expect(JSONUtil.NULL_CHARS); return NULL; case -1: if (getLevel() > 0) throw err("Premature EOF"); return EOF; default: throw err(null); } ch = getChar(); } }
public static void writeBoolean(boolean val, CharArr out) { out.write(val ? TRUE_CHARS : FALSE_CHARS); }
public static String toJSON(Object o) { CharArr out = new CharArr(); new JSONWriter(out).write(o); return out.toString(); }
public static void writeNumber(int number, CharArr out) { out.write(Integer.toString(number)); }
/** * @param o The object to convert to JSON * @param indentSize The number of space characters to use as an indent (default 2). 0=newlines * but no spaces, -1=no indent at all. * @return */ public static String toJSON(Object o, int indentSize) { CharArr out = new CharArr(); new JSONWriter(out, indentSize).write(o); return out.toString(); }
public static void writeStringPart(char[] val, int start, int end, CharArr out) { for (int i = start; i < end; i++) { char ch = val[i]; switch (ch) { case '"': case '\\': out.write('\\'); out.write(ch); break; case '\r': out.write('\\'); out.write('r'); break; case '\n': out.write('\\'); out.write('n'); break; case '\t': out.write('\\'); out.write('t'); break; case '\b': out.write('\\'); out.write('b'); break; case '\f': out.write('\\'); out.write('f'); break; // case '/': default: if (ch <= 0x1F) { unicodeEscape(ch, out); } else { // These characters are valid JSON, but not valid JavaScript if (ch == '\u2028' || ch == '\u2029') { unicodeEscape(ch, out); } else { out.write(ch); } } } } }
/** * Returns the long read... only significant if valstate==LONG after this call. firstChar should * be the first numeric digit read. */ private long readNumber(int firstChar, boolean isNeg) throws IOException { out.unsafeWrite(firstChar); // unsafe OK since we know output is big enough // We build up the number in the negative plane since it's larger (by one) than // the positive plane. long v = '0' - firstChar; // can't overflow a long in 18 decimal digits (i.e. 17 additional after the first). // we also need 22 additional to handle double so we'll handle in 2 separate loops. int i; for (i = 0; i < 17; i++) { int ch = getChar(); // TODO: is this switch faster as an if-then-else? switch (ch) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': v = v * 10 - (ch - '0'); out.unsafeWrite(ch); continue; case '.': out.unsafeWrite('.'); valstate = readFrac(out, 22 - i); return 0; case 'e': case 'E': out.unsafeWrite(ch); nstate = 0; valstate = readExp(out, 22 - i); return 0; default: // return the number, relying on nextEvent() to return an error // for invalid chars following the number. if (ch != -1) --start; // push back last char if not EOF valstate = LONG; return isNeg ? v : -v; } } // after this, we could overflow a long and need to do extra checking boolean overflow = false; long maxval = isNeg ? Long.MIN_VALUE : -Long.MAX_VALUE; for (; i < 22; i++) { int ch = getChar(); switch (ch) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (v < (0x8000000000000000L / 10)) overflow = true; // can't multiply by 10 w/o overflowing v *= 10; int digit = ch - '0'; if (v < maxval + digit) overflow = true; // can't add digit w/o overflowing v -= digit; out.unsafeWrite(ch); continue; case '.': out.unsafeWrite('.'); valstate = readFrac(out, 22 - i); return 0; case 'e': case 'E': out.unsafeWrite(ch); nstate = 0; valstate = readExp(out, 22 - i); return 0; default: // return the number, relying on nextEvent() to return an error // for invalid chars following the number. if (ch != -1) --start; // push back last char if not EOF valstate = overflow ? BIGNUMBER : LONG; return isNeg ? v : -v; } } nstate = 0; valstate = BIGNUMBER; return 0; }
public static void writeNull(CharArr out) { out.write(NULL_CHARS); }
public static void writeString(CharArr val, CharArr out) { writeString(val.getArray(), val.getStart(), val.getEnd(), out); }
public static void writeNumber(double number, CharArr out) { out.write(Double.toString(number)); }
public static void writeNumber(float number, CharArr out) { out.write(Float.toString(number)); }
public static void writeNumber(long number, CharArr out) { out.write(Long.toString(number)); }