private CBORObject NextJSONNonnegativeNumber(int c, int[] nextChar) { // Assumes the last character read was a digit CBORObject obj = null; int cval = c - '0'; int cstart = c; int startIndex = this.index - 1; var needObj = true; int numberStartIndex = this.index - 1; // DebugUtility.Log("js=" + (jstring)); c = this.index < this.endPos ? ((int)this.jstring[this.index++]) & 0xffff : -1; if (!(c == '-' || c == '+' || c == '.' || (c >= '0' && c <= '9') || c == 'e' || c == 'E')) { // Optimize for common case where JSON number // is a single digit without sign or exponent obj = CBORDataUtilities.ParseSmallNumber(cval, this.options); needObj = false; } else if (c >= '0' && c <= '9') { int csecond = c; if (cstart == '0') { // Leading zero followed by any digit is not allowed this.RaiseError("JSON number can't be parsed."); } cval = (cval * 10) + (int)(c - '0'); c = this.index < this.endPos ? ((int)this.jstring[this.index++]) : -1; if (c >= '0' && c <= '9') { var digits = 2; while (digits < 9 && (c >= '0' && c <= '9')) { cval = (cval * 10) + (int)(c - '0'); c = this.index < this.endPos ? ((int)this.jstring[this.index++]) : -1; ++digits; } if (!(c == 'e' || c == 'E' || c == '.' || (c >= '0' && c <= '9'))) { // All-digit number that's short enough obj = CBORDataUtilities.ParseSmallNumber(cval, this.options); needObj = false; } } else if (!(c == '-' || c == '+' || c == '.' || c == 'e' || c == 'E')) { // Optimize for common case where JSON number // is two digits without sign, decimal point, or exponent obj = CBORDataUtilities.ParseSmallNumber(cval, this.options); needObj = false; } } if (needObj) { // NOTE: Differs from CBORJson2, notably because the whole // rest of the string is checked whether the beginning of the rest // is a JSON number var endIndex = new int[1]; endIndex[0] = numberStartIndex; obj = CBORDataUtilities.ParseJSONNumber( this.jstring, numberStartIndex, this.endPos - numberStartIndex, this.options, endIndex); int numberEndIndex = endIndex[0]; this.index = numberEndIndex >= this.endPos ? this.endPos : (numberEndIndex + 1); if (obj == null) { int strlen = numberEndIndex - numberStartIndex; string errstr = this.jstring.Substring(numberStartIndex, Math.Min(100, strlen)); if (strlen > 100) { errstr += "..."; } this.RaiseError("JSON number can't be parsed. " + errstr); } #if DEBUG if (numberEndIndex < numberStartIndex) { throw new ArgumentException("numberEndIndex (" + numberEndIndex + ") is not greater or equal to " + numberStartIndex); } #endif c = numberEndIndex >= this.endPos ? -1 : this.jstring[numberEndIndex]; // check if character can validly appear after a JSON number if (c != ',' && c != ']' && c != '}' && c != -1 && c != 0x20 && c != 0x0a && c != 0x0d && c != 0x09) { this.RaiseError("Invalid character after JSON number"); } // DebugUtility.Log("endIndex="+endIndex[0]+", "+ // this.jstring.Substring(endIndex[0], // Math.Min(20, this.endPos-endIndex[0]))); } if (c == -1 || (c != 0x20 && c != 0x0a && c != 0x0d && c != 0x09)) { nextChar[0] = c; } else { nextChar[0] = this.SkipWhitespaceJSON(); } return(obj); }
private CBORObject NextJSONValue( int firstChar, int[] nextChar, int depth) { string str; int c = firstChar; CBORObject obj = null; if (c < 0) { this.RaiseError("Unexpected end of data"); } switch (c) { case '"': { // Parse a string // The tokenizer already checked the string for invalid // surrogate pairs, so just call the CBORObject // constructor directly obj = CBORObject.FromRaw(this.NextJSONString()); nextChar[0] = this.SkipWhitespaceJSON(); return(obj); } case '{': { // Parse an object obj = this.ParseJSONObject(depth + 1); nextChar[0] = this.SkipWhitespaceJSON(); return(obj); } case '[': { // Parse an array obj = this.ParseJSONArray(depth + 1); nextChar[0] = this.SkipWhitespaceJSON(); return(obj); } case 't': { // Parse true if ((c = this.ReadChar()) != 'r' || (c = this.ReadChar()) != 'u' || (c = this.ReadChar()) != 'e') { this.RaiseError("Value can't be parsed."); } c = this.ReadChar(); if (c == 0x20 || c == 0x0a || c == 0x0d || c == 0x09) { nextChar[0] = this.SkipWhitespaceJSON(); } else if (this.jsonSequenceMode && depth == 0) { nextChar[0] = c; this.RaiseError("JSON whitespace expected after top-level " + "number in JSON sequence"); } else { nextChar[0] = c; } return(CBORObject.True); } case 'f': { // Parse false if ((c = this.ReadChar()) != 'a' || (c = this.ReadChar()) != 'l' || (c = this.ReadChar()) != 's' || (c = this.ReadChar()) != 'e') { this.RaiseError("Value can't be parsed."); } c = this.ReadChar(); if (c == 0x20 || c == 0x0a || c == 0x0d || c == 0x09) { nextChar[0] = this.SkipWhitespaceJSON(); } else if (this.jsonSequenceMode && depth == 0) { nextChar[0] = c; this.RaiseError("JSON whitespace expected after top-level " + "number in JSON sequence"); } else { nextChar[0] = c; } return(CBORObject.False); } case 'n': { // Parse null if ((c = this.ReadChar()) != 'u' || (c = this.ReadChar()) != 'l' || (c = this.ReadChar()) != 'l') { this.RaiseError("Value can't be parsed."); } c = this.ReadChar(); if (c == 0x20 || c == 0x0a || c == 0x0d || c == 0x09) { nextChar[0] = this.SkipWhitespaceJSON(); } else if (this.jsonSequenceMode && depth == 0) { nextChar[0] = c; this.RaiseError("JSON whitespace expected after top-level " + "number in JSON sequence"); } else { nextChar[0] = c; } return(CBORObject.Null); } case '-': { // Parse a negative number return(this.NextJSONNegativeNumber(nextChar, depth)); } case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { // Parse a nonnegative number int cval = c - '0'; int cstart = c; var needObj = true; c = this.ReadChar(); if (!(c == '-' || c == '+' || c == '.' || (c >= '0' && c <= '9') || c == 'e' || c == 'E')) { // Optimize for common case where JSON number // is a single digit without sign or exponent obj = CBORDataUtilities.ParseSmallNumber(cval, this.options); needObj = false; } else if (c >= '0' && c <= '9') { int csecond = c; if (cstart == '0') { // Leading zero followed by any digit is not allowed this.RaiseError("JSON number can't be parsed."); } cval = (cval * 10) + (int)(c - '0'); c = this.ReadChar(); if (c >= '0' && c <= '9') { var digits = 2; var ctmp = new int[10]; ctmp[0] = cstart; ctmp[1] = csecond; while (digits < 9 && (c >= '0' && c <= '9')) { cval = (cval * 10) + (int)(c - '0'); ctmp[digits++] = c; c = this.ReadChar(); } if (c == 'e' || c == 'E' || c == '.' || (c >= '0' && c <= '9')) { // Not an all-digit number, or too long this.sb = this.sb ?? new StringBuilder(); this.sb.Remove(0, this.sb.Length); for (var vi = 0; vi < digits; ++vi) { this.sb.Append((char)ctmp[vi]); } } else { obj = CBORDataUtilities.ParseSmallNumber(cval, this.options); needObj = false; } } else if (!(c == '-' || c == '+' || c == '.' || c == 'e' || c == 'E')) { // Optimize for common case where JSON number // is two digits without sign, decimal point, or exponent obj = CBORDataUtilities.ParseSmallNumber(cval, this.options); needObj = false; } else { this.sb = this.sb ?? new StringBuilder(); this.sb.Remove(0, this.sb.Length); this.sb.Append((char)cstart); this.sb.Append((char)csecond); } } else { this.sb = this.sb ?? new StringBuilder(); this.sb.Remove(0, this.sb.Length); this.sb.Append((char)cstart); } if (needObj) { var charbuf = new char[32]; var charbufptr = 0; while ( c == '-' || c == '+' || c == '.' || (c >= '0' && c <= '9') || c == 'e' || c == 'E') { charbuf[charbufptr++] = (char)c; if (charbufptr >= 32) { this.sb.Append(charbuf, 0, 32); charbufptr = 0; } c = this.ReadChar(); } if (charbufptr > 0) { this.sb.Append(charbuf, 0, charbufptr); } // check if character can validly appear after a JSON number if (c != ',' && c != ']' && c != '}' && c != -1 && c != 0x20 && c != 0x0a && c != 0x0d && c != 0x09) { this.RaiseError("Invalid character after JSON number"); } str = this.sb.ToString(); obj = CBORDataUtilities.ParseJSONNumber(str, this.options); if (obj == null) { string errstr = (str.Length <= 100) ? str : (str.Substring(0, 100) + "..."); this.RaiseError("JSON number can't be parsed. " + errstr); } } if (c == 0x20 || c == 0x0a || c == 0x0d || c == 0x09) { nextChar[0] = this.SkipWhitespaceJSON(); } else if (this.jsonSequenceMode && depth == 0) { nextChar[0] = c; this.RaiseError("JSON whitespace expected after top-level " + "number in JSON sequence"); } else { nextChar[0] = c; } return(obj); } default: this.RaiseError("Value can't be parsed."); break; } return(null); }