Exemple #1
0
        private CBORObject NextJSONValue(
            int firstChar,
            int[] nextChar,
            int depth)
        {
            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.FromRawUtf8(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 (this.endPos - this.index <= 2 ||
                    this.bytes[this.index] != (byte)0x72 ||
                    this.bytes[this.index + 1] != (byte)0x75 ||
                    this.bytes[this.index + 2] != (byte)0x65)
                {
                    this.RaiseError("Value can't be parsed.");
                }
                this.index += 3;
                nextChar[0] = this.SkipWhitespaceJSON();
                return(CBORObject.True);
            }

            case 'f': {
                // Parse false
                if (this.endPos - this.index <= 3 ||
                    this.bytes[this.index] != (byte)0x61 ||
                    this.bytes[this.index + 1] != (byte)0x6c ||
                    this.bytes[this.index + 2] != (byte)0x73 ||
                    this.bytes[this.index + 3] != (byte)0x65)
                {
                    this.RaiseError("Value can't be parsed.");
                }
                this.index += 4;
                nextChar[0] = this.SkipWhitespaceJSON();
                return(CBORObject.False);
            }

            case 'n': {
                // Parse null
                if (this.endPos - this.index <= 2 ||
                    this.bytes[this.index] != (byte)0x75 ||
                    this.bytes[this.index + 1] != (byte)0x6c ||
                    this.bytes[this.index + 2] != (byte)0x6c)
                {
                    this.RaiseError("Value can't be parsed.");
                }
                this.index += 3;
                nextChar[0] = this.SkipWhitespaceJSON();
                return(CBORObject.Null);
            }

            case '-': {
                // Parse a negative number
                return(this.NextJSONNegativeNumber(nextChar));
            }

            case '0':
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9': {
                // Parse a nonnegative number
                return(this.NextJSONNonnegativeNumber(c, nextChar));
            }

            default:
                this.RaiseError("Value can't be parsed.");
                break;
            }
            return(null);
        }
Exemple #2
0
        private CBORObject ParseJSONObject(int depth)
        {
            // Assumes that the last character read was '{'
            if (depth > 1000)
            {
                this.RaiseError("Too deeply nested");
            }
            int        c;
            CBORObject key = null;
            CBORObject obj;
            var        nextchar  = new int[1];
            var        seenComma = false;
            var        myHashMap = new SortedDictionary <CBORObject, CBORObject>();

            while (true)
            {
                c = this.SkipWhitespaceJSON();
                switch (c)
                {
                case -1:
                    this.RaiseError("A JSON object must end with '}'");
                    break;

                case '}':
                    if (seenComma)
                    {
                        // Situation like '{"0"=>1,}'
                        this.RaiseError("Trailing comma");
                        return(null);
                    }
                    return(CBORObject.FromRaw(myHashMap));

                default: {
                    // Read the next string
                    if (c < 0)
                    {
                        this.RaiseError("Unexpected end of data");
                        return(null);
                    }
                    if (c != '"')
                    {
                        this.RaiseError("Expected a string as a key");
                        return(null);
                    }
                    // Parse a string that represents the object's key
                    // The tokenizer already checked the string for invalid
                    // surrogate pairs, so just call the CBORObject
                    // constructor directly
                    obj = CBORObject.FromRawUtf8(this.NextJSONString());
                    key = obj;
                    break;
                }
                }
                if (this.SkipWhitespaceJSON() != ':')
                {
                    this.RaiseError("Expected a ':' after a key");
                }
                int oldCount = myHashMap.Count;
                // NOTE: Will overwrite existing value
                myHashMap[key] = this.NextJSONValue(
                    this.SkipWhitespaceJSON(),
                    nextchar,
                    depth);
                int newCount = myHashMap.Count;
                if (!this.options.AllowDuplicateKeys &&
                    oldCount == newCount)
                {
                    this.RaiseError("Duplicate key already exists");
                    return(null);
                }
                switch (nextchar[0])
                {
                case ',':
                    seenComma = true;
                    break;

                case '}':
                    return(CBORObject.FromRaw(myHashMap));

                default: this.RaiseError("Expected a ',' or '}'");
                    break;
                }
            }
        }
Exemple #3
0
        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);
        }
Exemple #4
0
        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;

            c = this.index < this.endPos ? ((int)this.bytes[this.index++]) &
                0xff : -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.bytes[this.index++]) &
                       0xff : -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.bytes[this.index++]) & 0xff : -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);
            #if DEBUG
                        if ((
                                (EDecimal)obj.ToObject(
                                    typeof(EDecimal))).CompareToValue(EDecimal.FromInt32(cval)) !=
                            0)
                        {
                            this.RaiseError(String.Empty + obj);
                        }
            #endif
                        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);
          #if DEBUG
                    if ((
                            (EDecimal)obj.ToObject(
                                typeof(EDecimal))).CompareToValue(EDecimal.FromInt32(cval)) !=
                        0)
                    {
                        this.RaiseError(String.Empty + obj);
                    }
          #endif
                    needObj = false;
                }
            }
            if (needObj)
            {
                while (c == '-' || c == '+' || c == '.' || (c >= '0' && c <= '9') ||
                       c == 'e' || c == 'E')
                {
                    c = this.index < this.endPos ? ((int)this.bytes[this.index++]) &
                        0xff : -1;
                }
                // 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");
                }
                int numberEndIndex = c < 0 ? this.endPos : this.index - 1;
                obj = CBORDataUtilities.ParseJSONNumber(
                    this.bytes,
                    numberStartIndex,
                    numberEndIndex - numberStartIndex,
                    this.options);
        #if DEBUG
                if (this.options.NumberConversion == JSONOptions.ConversionMode.Full &&
                    (
                        (EDecimal)obj.ToObject(
                            typeof(EDecimal))).CompareToValue(EDecimal.FromString(this.bytes,
                                                                                  numberStartIndex,
                                                                                  numberEndIndex - numberStartIndex)) != 0)
                {
                    this.RaiseError(String.Empty + obj);
                }
        #endif
                if (obj == null)
                {
                    string errstr = String.Empty;
                    // errstr = (str.Length <= 100) ? str : (str.Substring(0,
                    // 100) + "...");
                    this.RaiseError("JSON number can't be parsed. " + errstr);
                }
            }
            if (c == -1 || (c != 0x20 && c != 0x0a && c != 0x0d && c != 0x09))
            {
                nextChar[0] = c;
            }
            else
            {
                nextChar[0] = this.SkipWhitespaceJSON();
            }
            return(obj);
        }
Exemple #5
0
 public static CBORObject FromObject(ulong value)
 {
     return(CBORObject.FromObject(UInt64ToEInteger(value)));
 }
Exemple #6
0
        internal static CBORObject ParseJSONNumber(
            byte[] chars,
            int offset,
            int count,
            JSONOptions options,
            int[] endOfNumber)
        {
            if (chars == null || chars.Length == 0 || count <= 0)
            {
                return(null);
            }
            if (offset < 0 || offset > chars.Length)
            {
                return(null);
            }
            if (count > chars.Length || chars.Length - offset < count)
            {
                return(null);
            }
            JSONOptions opt = options ?? CBORDataUtilities.DefaultOptions;
            bool        preserveNegativeZero = options.PreserveNegativeZero;

            JSONOptions.ConversionMode kind = options.NumberConversion;
            int endPos        = offset + count;
            int initialOffset = offset;
            var negative      = false;

            if (chars[initialOffset] == '-')
            {
                ++offset;
                negative = true;
            }
            int numOffset              = offset;
            var haveDecimalPoint       = false;
            var haveDigits             = false;
            var haveDigitsAfterDecimal = false;
            var haveExponent           = false;
            int i = offset;
            var decimalPointPos = -1;
            // Check syntax
            int k = i;

            if (endPos - 1 > k && chars[k] == '0' && chars[k + 1] >= '0' &&
                chars[k + 1] <= '9')
            {
                if (endOfNumber != null)
                {
                    endOfNumber[0] = k + 2;
                }
                return(null);
            }
            for (; k < endPos; ++k)
            {
                byte c = chars[k];
                if (c >= '0' && c <= '9')
                {
                    haveDigits              = true;
                    haveDigitsAfterDecimal |= haveDecimalPoint;
                }
                else if (c == '.')
                {
                    if (!haveDigits || haveDecimalPoint)
                    {
                        // no digits before the decimal point,
                        // or decimal point already seen
                        if (endOfNumber != null)
                        {
                            endOfNumber[0] = k;
                        }
                        return(null);
                    }
                    haveDecimalPoint = true;
                    decimalPointPos  = k;
                }
                else if (c == 'E' || c == 'e')
                {
                    ++k;
                    haveExponent = true;
                    break;
                }
                else
                {
                    if (endOfNumber != null)
                    {
                        endOfNumber[0] = k;
                        // Check if character can validly appear after a JSON number
                        if (c != ',' && c != ']' && c != '}' &&
                            c != 0x20 && c != 0x0a && c != 0x0d && c != 0x09)
                        {
                            return(null);
                        }
                        else
                        {
                            endPos = k;
                            break;
                        }
                    }
                    return(null);
                }
            }
            if (!haveDigits || (haveDecimalPoint && !haveDigitsAfterDecimal))
            {
                if (endOfNumber != null)
                {
                    endOfNumber[0] = k;
                }
                return(null);
            }
            var exponentPos = -1;
            var negativeExp = false;

            if (haveExponent)
            {
                haveDigits = false;
                if (k == endPos)
                {
                    if (endOfNumber != null)
                    {
                        endOfNumber[0] = k;
                    }
                    return(null);
                }
                byte c = chars[k];
                if (c == '+')
                {
                    ++k;
                }
                else if (c == '-')
                {
                    negativeExp = true;
                    ++k;
                }
                for (; k < endPos; ++k)
                {
                    c = chars[k];
                    if (c >= '0' && c <= '9')
                    {
                        if (exponentPos < 0 && c != '0')
                        {
                            exponentPos = k;
                        }
                        haveDigits = true;
                    }
                    else if (endOfNumber != null)
                    {
                        endOfNumber[0] = k;
                        // Check if character can validly appear after a JSON number
                        if (c != ',' && c != ']' && c != '}' &&
                            c != 0x20 && c != 0x0a && c != 0x0d && c != 0x09)
                        {
                            return(null);
                        }
                        else
                        {
                            endPos = k;
                            break;
                        }
                    }
                    else
                    {
                        return(null);
                    }
                }
                if (!haveDigits)
                {
                    if (endOfNumber != null)
                    {
                        endOfNumber[0] = k;
                    }
                    return(null);
                }
            }
            if (endOfNumber != null)
            {
                endOfNumber[0] = endPos;
            }
            if (exponentPos >= 0 && endPos - exponentPos > 20)
            {
                // Exponent too high for precision to overcome (which
                // has a length no bigger than Int32.MaxValue, which is 10 digits
                // long)
                if (negativeExp)
                {
                    // underflow
                    if (kind == JSONOptions.ConversionMode.Double ||
                        kind == JSONOptions.ConversionMode.IntOrFloat)
                    {
                        if (!negative)
                        {
                            return(CBORObject.FromFloatingPointBits(0, 2));
                        }
                        else
                        {
                            return(CBORObject.FromFloatingPointBits(0x8000, 2));
                        }
                    }
                    else if (kind ==
                             JSONOptions.ConversionMode.IntOrFloatFromDouble)
                    {
                        return(CBORObject.FromObject(0));
                    }
                }
                else
                {
                    // overflow
                    if (kind == JSONOptions.ConversionMode.Double ||
                        kind == JSONOptions.ConversionMode.IntOrFloatFromDouble ||
                        kind == JSONOptions.ConversionMode.IntOrFloat)
                    {
                        return(CBORObject.FromFloatingPointBits(
                                   negative ? DoubleNegInfinity : DoublePosInfinity,
                                   8));
                    }
                    else if (kind == JSONOptions.ConversionMode.Decimal128)
                    {
                        return(CBORObject.FromObject(negative ?
                                                     EDecimal.NegativeInfinity : EDecimal.PositiveInfinity));
                    }
                }
            }
            if (!haveExponent && !haveDecimalPoint &&
                (endPos - numOffset) <= 16)
            {
                // Very common case of all-digit JSON number strings
                // less than 2^53 (with or without number sign)
                long v  = 0L;
                int  vi = numOffset;
                for (; vi < endPos; ++vi)
                {
                    v = (v * 10) + (int)(chars[vi] - '0');
                }
                if ((v != 0 || !negative) && v < (1L << 53) - 1)
                {
                    if (negative)
                    {
                        v = -v;
                    }
                    if (kind == JSONOptions.ConversionMode.Double)
                    {
                        return(CBORObject.FromFloatingPointBits(EFloat.FromInt64(v).ToDoubleBits(), 8));
                    }
                    else if (kind == JSONOptions.ConversionMode.Decimal128)
                    {
                        return(CBORObject.FromObject(EDecimal.FromInt64(v)));
                    }
                    else
                    {
                        return(CBORObject.FromObject(v));
                    }
                }
            }
            if (kind == JSONOptions.ConversionMode.Full)
            {
                if (!haveDecimalPoint && !haveExponent)
                {
                    EInteger ei = EInteger.FromSubstring(chars, initialOffset, endPos);
                    if (preserveNegativeZero && ei.IsZero && negative)
                    {
                        // TODO: In next major version, change to EDecimal.NegativeZero
                        return(CBORObject.FromFloatingPointBits(0x8000, 2));
                    }
                    return(CBORObject.FromObject(ei));
                }
                if (!haveExponent && haveDecimalPoint)
                {
                    // No more than 18 digits plus one decimal point (which
                    // should fit a long)
                    long lv         = 0L;
                    int  expo       = -(endPos - (decimalPointPos + 1));
                    int  vi         = numOffset;
                    var  digitCount = 0;
                    for (; vi < decimalPointPos; ++vi)
                    {
                        if (digitCount < 0 || digitCount >= 18)
                        {
                            digitCount = -1;
                            break;
                        }
                        else if (digitCount > 0 || chars[vi] != '0')
                        {
                            ++digitCount;
                        }
                        lv = checked ((lv * 10) + (int)(chars[vi] - '0'));
                    }
                    for (vi = decimalPointPos + 1; vi < endPos; ++vi)
                    {
                        if (digitCount < 0 || digitCount >= 18)
                        {
                            digitCount = -1;
                            break;
                        }
                        else if (digitCount > 0 || chars[vi] != '0')
                        {
                            ++digitCount;
                        }
                        lv = checked ((lv * 10) + (int)(chars[vi] - '0'));
                    }
                    if (negative)
                    {
                        lv = -lv;
                    }
                    if (digitCount >= 0 && (!negative || lv != 0))
                    {
                        if (expo == 0)
                        {
                            return(CBORObject.FromObject(lv));
                        }
                        else
                        {
                            CBORObject cbor = CBORObject.FromArrayBackedObject(
                                new CBORObject[] {
                                CBORObject.FromObject(expo),
                                CBORObject.FromObject(lv),
                            });
                            return(cbor.WithTag(4));
                        }
                    }
                }
                // DebugUtility.Log("convfull " + chars.Substring(initialOffset, endPos -
                // initialOffset));
                EDecimal ed = EDecimal.FromString(
                    chars,
                    initialOffset,
                    endPos - initialOffset);
                if (ed.IsZero && negative)
                {
                    if (ed.Exponent.IsZero)
                    {
                        // TODO: In next major version, use EDecimal
                        // for preserveNegativeZero
                        return(preserveNegativeZero ?
                               CBORObject.FromFloatingPointBits(0x8000, 2) :
                               CBORObject.FromObject(0));
                    }
                    else if (!preserveNegativeZero)
                    {
                        return(CBORObject.FromObject(ed.Negate()));
                    }
                    else
                    {
                        return(CBORObject.FromObject(ed));
                    }
                }
                else
                {
                    return(ed.Exponent.IsZero ? CBORObject.FromObject(ed.Mantissa) :
                           CBORObject.FromObject(ed));
                }
            }
            else if (kind == JSONOptions.ConversionMode.Double)
            {
                EFloat ef = EFloat.FromString(
                    chars,
                    initialOffset,
                    endPos - initialOffset,
                    EContext.Binary64);
                long lb = ef.ToDoubleBits();
                if (!preserveNegativeZero && (lb == 1L << 63 || lb == 0L))
                {
                    lb = 0L;
                }
                return(CBORObject.FromFloatingPointBits(lb, 8));
            }
            else if (kind == JSONOptions.ConversionMode.Decimal128)
            {
                EDecimal ed = EDecimal.FromString(
                    chars,
                    initialOffset,
                    endPos - initialOffset,
                    EContext.Decimal128);
                if (!preserveNegativeZero && ed.IsNegative && ed.IsZero)
                {
                    ed = ed.Negate();
                }
                return(CBORObject.FromObject(ed));
            }
            else if (kind == JSONOptions.ConversionMode.IntOrFloatFromDouble)
            {
                EFloat ef = EFloat.FromString(
                    chars,
                    initialOffset,
                    endPos - initialOffset,
                    EContext.Binary64);
                long lb = ef.ToDoubleBits();
                return((!CBORUtilities.IsBeyondSafeRange(lb) &&
                        CBORUtilities.IsIntegerValue(lb)) ?
                       CBORObject.FromObject(CBORUtilities.GetIntegerValue(lb)) :
                       CBORObject.FromFloatingPointBits(lb, 8));
            }
            else if (kind == JSONOptions.ConversionMode.IntOrFloat)
            {
                EContext ctx = EContext.Binary64.WithBlankFlags();
                EFloat   ef  = EFloat.FromString(
                    chars,
                    initialOffset,
                    endPos - initialOffset,
                    ctx);
                long lb = ef.ToDoubleBits();
                if ((ctx.Flags & EContext.FlagInexact) != 0)
                {
                    // Inexact conversion to double, meaning that the string doesn't
                    // represent an integer in [-(2^53)+1, 2^53), which is representable
                    // exactly as double, so treat as ConversionMode.Double
                    if (!preserveNegativeZero && (lb == 1L << 63 || lb == 0L))
                    {
                        lb = 0L;
                    }
                    return(CBORObject.FromFloatingPointBits(lb, 8));
                }
                else
                {
                    // Exact conversion; treat as ConversionMode.IntToFloatFromDouble
                    return((!CBORUtilities.IsBeyondSafeRange(lb) &&
                            CBORUtilities.IsIntegerValue(lb)) ?
                           CBORObject.FromObject(CBORUtilities.GetIntegerValue(lb)) :
                           CBORObject.FromFloatingPointBits(lb, 8));
                }
            }
            else
            {
                throw new ArgumentException("Unsupported conversion kind.");
            }
        }
Exemple #7
0
        public CBORObject ReadForFirstByte(int firstbyte)
        {
            if (this.depth > 500)
            {
                throw new CBORException("Too deeply nested");
            }
            if (firstbyte < 0)
            {
                throw new CBORException("Premature end of data");
            }
            if (firstbyte == 0xff)
            {
                throw new CBORException("Unexpected break code encountered");
            }
            int        type       = (firstbyte >> 5) & 0x07;
            int        additional = firstbyte & 0x1f;
            long       uadditional;
            CBORObject fixedObject;

            if (this.options.Ctap2Canonical)
            {
                if (additional >= 0x1c)
                {
                    // NOTE: Includes stop byte and indefinite length data items
                    throw new CBORException("Invalid canonical CBOR encountered");
                }
                // Check if this represents a fixed object (NOTE: All fixed objects
                // comply with CTAP2 canonical CBOR).
                fixedObject = CBORObject.GetFixedObject(firstbyte);
                if (fixedObject != null)
                {
                    return(fixedObject);
                }
                if (type == 6)
                {
                    throw new CBORException("Tags not allowed in canonical CBOR");
                }
                uadditional = ReadDataLength(
                    this.stream,
                    firstbyte,
                    type,
                    type == 7);
                if (type == 0)
                {
                    return((uadditional >> 63) != 0 ?
                           CBORObject.FromObject(ToUnsignedEInteger(uadditional)) :
                           CBORObject.FromObject(uadditional));
                }
                else if (type == 1)
                {
                    return((uadditional >> 63) != 0 ? CBORObject.FromObject(
                               ToUnsignedEInteger(uadditional).Add(1).Negate()) :
                           CBORObject.FromObject((-uadditional) - 1L));
                }
                else if (type == 7)
                {
                    if (additional < 24)
                    {
                        return(CBORObject.FromSimpleValue(additional));
                    }
                    else if (additional == 24 && uadditional < 32)
                    {
                        throw new CBORException("Invalid simple value encoding");
                    }
                    else if (additional == 24)
                    {
                        return(CBORObject.FromSimpleValue((int)uadditional));
                    }
                    else if (additional == 25)
                    {
                        return(CBORObject.FromFloatingPointBits(uadditional, 2));
                    }
                    else if (additional == 26)
                    {
                        return(CBORObject.FromFloatingPointBits(uadditional, 4));
                    }
                    else if (additional == 27)
                    {
                        return(CBORObject.FromFloatingPointBits(uadditional, 8));
                    }
                }
                else if (type >= 2 && type <= 5)
                {
                    return(this.ReadStringArrayMap(type, uadditional));
                }
                throw new CBORException("Unexpected data encountered");
            }
            int expectedLength = CBORObject.GetExpectedLength(firstbyte);

            // Data checks
            if (expectedLength == -1)
            {
                // if the head byte is invalid
                throw new CBORException("Unexpected data encountered");
            }
            // Check if this represents a fixed object
            fixedObject = CBORObject.GetFixedObject(firstbyte);
            if (fixedObject != null)
            {
                return(fixedObject);
            }
            // Read fixed-length data
            byte[] data = null;
            if (expectedLength != 0)
            {
                data = new byte[expectedLength];
                // include the first byte because GetFixedLengthObject
                // will assume it exists for some head bytes
                data[0] = unchecked ((byte)firstbyte);
                if (expectedLength > 1 &&
                    this.stream.Read(data, 1, expectedLength - 1) != expectedLength
                    - 1)
                {
                    throw new CBORException("Premature end of data");
                }
                CBORObject cbor = CBORObject.GetFixedLengthObject(firstbyte, data);
                if (this.stringRefs != null && (type == 2 || type == 3))
                {
                    this.stringRefs.AddStringIfNeeded(cbor, expectedLength - 1);
                }
                return(cbor);
            }
            if (additional == 31)
            {
                // Indefinite-length for major types 2 to 5 (other major
                // types were already handled in the call to
                // GetFixedLengthObject).
                switch (type)
                {
                case 2: {
                    // Streaming byte string
                    using (var ms = new MemoryStream()) {
                        // Requires same type as this one
                        while (true)
                        {
                            int nextByte = this.stream.ReadByte();
                            if (nextByte == 0xff)
                            {
                                // break if the "break" code was read
                                break;
                            }
                            long len = ReadDataLength(this.stream, nextByte, 2);
                            if ((len >> 63) != 0 || len > Int32.MaxValue)
                            {
                                throw new CBORException("Length" + ToUnsignedEInteger(len)
                                                        +
                                                        " is bigger than supported ");
                            }
                            if (nextByte != 0x40)
                            {
                                // NOTE: 0x40 means the empty byte string
                                ReadByteData(this.stream, len, ms);
                            }
                        }
                        if (ms.Position > Int32.MaxValue)
                        {
                            throw new
                                  CBORException("Length of bytes to be streamed is bigger" +
                                                "\u0020than supported ");
                        }
                        data = ms.ToArray();
                        return(CBORObject.FromRaw(data));
                    }
                }

                case 3: {
                    // Streaming text string
                    var builder = new StringBuilder();
                    while (true)
                    {
                        int nextByte = this.stream.ReadByte();
                        if (nextByte == 0xff)
                        {
                            // break if the "break" code was read
                            break;
                        }
                        long len = ReadDataLength(this.stream, nextByte, 3);
                        if ((len >> 63) != 0 || len > Int32.MaxValue)
                        {
                            throw new CBORException("Length" + ToUnsignedEInteger(len) +
                                                    " is bigger than supported");
                        }
                        if (nextByte != 0x60)
                        {
                            // NOTE: 0x60 means the empty string
                            if (PropertyMap.ExceedsKnownLength(this.stream, len))
                            {
                                throw new CBORException("Premature end of data");
                            }
                            switch (
                                DataUtilities.ReadUtf8(
                                    this.stream,
                                    (int)len,
                                    builder,
                                    false))
                            {
                            case -1:
                                throw new CBORException("Invalid UTF-8");

                            case -2:
                                throw new CBORException("Premature end of data");
                            }
                        }
                    }
                    return(CBORObject.FromRaw(builder.ToString()));
                }

                case 4: {
                    CBORObject cbor    = CBORObject.NewArray();
                    var        vtindex = 0;
                    // Indefinite-length array
                    while (true)
                    {
                        int headByte = this.stream.ReadByte();
                        if (headByte < 0)
                        {
                            throw new CBORException("Premature end of data");
                        }
                        if (headByte == 0xff)
                        {
                            // Break code was read
                            break;
                        }
                        ++this.depth;
                        CBORObject o = this.ReadForFirstByte(
                            headByte);
                        --this.depth;
                        cbor.Add(o);
                        ++vtindex;
                    }
                    return(cbor);
                }

                case 5: {
                    CBORObject cbor = CBORObject.NewMap();
                    // Indefinite-length map
                    while (true)
                    {
                        int headByte = this.stream.ReadByte();
                        if (headByte < 0)
                        {
                            throw new CBORException("Premature end of data");
                        }
                        if (headByte == 0xff)
                        {
                            // Break code was read
                            break;
                        }
                        ++this.depth;
                        CBORObject key   = this.ReadForFirstByte(headByte);
                        CBORObject value = this.ReadInternal();
                        --this.depth;
                        int oldCount = cbor.Count;
                        cbor[key] = value;
                        int newCount = cbor.Count;
                        if (!this.options.AllowDuplicateKeys && oldCount == newCount)
                        {
                            throw new CBORException("Duplicate key already exists");
                        }
                    }
                    return(cbor);
                }

                default: throw new CBORException("Unexpected data encountered");
                }
            }
            EInteger bigintAdditional = EInteger.Zero;

            uadditional = ReadDataLength(this.stream, firstbyte, type);
            // The following doesn't check for major types 0 and 1,
            // since all of them are fixed-length types and are
            // handled in the call to GetFixedLengthObject.
            if (type >= 2 && type <= 5)
            {
                return(this.ReadStringArrayMap(type, uadditional));
            }
            if (type == 6) // Tagged item
            {
                var haveFirstByte = false;
                var newFirstByte  = -1;
                if (this.options.ResolveReferences && (uadditional >> 32) == 0)
                {
                    // NOTE: HandleItemTag treats only certain tags up to 256 specially
                    this.HandleItemTag(uadditional);
                }
                ++this.depth;
                CBORObject o = haveFirstByte ? this.ReadForFirstByte(
                    newFirstByte) : this.ReadInternal();
                --this.depth;
                if ((uadditional >> 63) != 0)
                {
                    return(CBORObject.FromObjectAndTag(o,
                                                       ToUnsignedEInteger(uadditional)));
                }
                if (uadditional < 65536)
                {
                    if (this.options.ResolveReferences)
                    {
                        int uaddl = uadditional >= 257 ? 257 : (uadditional < 0 ? 0 :
                                                                (int)uadditional);
                        switch (uaddl)
                        {
                        case 256:
                            // string tag
                            this.stringRefs.Pop();
                            break;

                        case 25:
                            // stringref tag
                            if (o.IsTagged || o.Type != CBORType.Integer)
                            {
                                throw new CBORException("stringref must be an unsigned" +
                                                        "\u0020integer");
                            }
                            return(this.stringRefs.GetString(o.AsEIntegerValue()));
                        }
                    }
                    return(CBORObject.FromObjectAndTag(
                               o,
                               (int)uadditional));
                }
                return(CBORObject.FromObjectAndTag(
                           o,
                           (EInteger)uadditional));
            }
            throw new CBORException("Unexpected data encountered");
        }
Exemple #8
0
        private CBORObject ReadStringArrayMap(int type, long uadditional)
        {
            bool canonical = this.options.Ctap2Canonical;

            if (type == 2 || type == 3) // Byte string or text string
            {
                if ((uadditional >> 31) != 0)
                {
                    throw new CBORException("Length of " +
                                            ToUnsignedEInteger(uadditional).ToString() + " is bigger" +
                                            "\u0020than supported");
                }
                int hint = (uadditional > Int32.MaxValue ||
                            (uadditional >> 63) != 0) ? Int32.MaxValue : (int)uadditional;
                byte[] data = ReadByteData(this.stream, uadditional, null);
                if (type == 3)
                {
                    if (!CBORUtilities.CheckUtf8(data))
                    {
                        throw new CBORException("Invalid UTF-8");
                    }
                    return(this.ObjectFromUtf8Array(data, hint));
                }
                else
                {
                    return(this.ObjectFromByteArray(data, hint));
                }
            }
            if (type == 4) // Array
            {
                if (this.options.Ctap2Canonical && this.depth >= 4)
                {
                    throw new CBORException("Depth too high in canonical CBOR");
                }
                CBORObject cbor = CBORObject.NewArray();
                if ((uadditional >> 31) != 0)
                {
                    throw new CBORException("Length of " +
                                            ToUnsignedEInteger(uadditional).ToString() + " is bigger than" +
                                            "\u0020supported");
                }
                if (PropertyMap.ExceedsKnownLength(this.stream, uadditional))
                {
                    throw new CBORException("Remaining data too small for array" +
                                            "\u0020length");
                }
                ++this.depth;
                for (long i = 0; i < uadditional; ++i)
                {
                    cbor.Add(
                        this.ReadInternal());
                }
                --this.depth;
                return(cbor);
            }
            if (type == 5) // Map, type 5
            {
                if (this.options.Ctap2Canonical && this.depth >= 4)
                {
                    throw new CBORException("Depth too high in canonical CBOR");
                }
                CBORObject cbor = CBORObject.NewMap();
                if ((uadditional >> 31) != 0)
                {
                    throw new CBORException("Length of " +
                                            ToUnsignedEInteger(uadditional).ToString() + " is bigger than" +
                                            "\u0020supported");
                }
                if (PropertyMap.ExceedsKnownLength(this.stream, uadditional))
                {
                    throw new CBORException("Remaining data too small for map" +
                                            "\u0020length");
                }
                CBORObject             lastKey  = null;
                IComparer <CBORObject> comparer = CBORCanonical.Comparer;
                for (long i = 0; i < uadditional; ++i)
                {
                    ++this.depth;
                    CBORObject key   = this.ReadInternal();
                    CBORObject value = this.ReadInternal();
                    --this.depth;
                    if (this.options.Ctap2Canonical && lastKey != null)
                    {
                        int cmp = comparer.Compare(lastKey, key);
                        if (cmp > 0)
                        {
                            throw new CBORException("Map key not in canonical order");
                        }
                        else if (cmp == 0)
                        {
                            throw new CBORException("Duplicate map key");
                        }
                    }
                    if (!this.options.AllowDuplicateKeys)
                    {
                        if (cbor.ContainsKey(key))
                        {
                            throw new CBORException("Duplicate key already exists");
                        }
                    }
                    lastKey   = key;
                    cbor[key] = value;
                }
                return(cbor);
            }
            return(null);
        }
Exemple #9
0
        /// <summary>Converts a date/time in the form of a year, month, day,
        /// hour, minute, second, fractional seconds, and time offset to a CBOR
        /// object.</summary>
        /// <param name='bigYear'>The parameter <paramref name='bigYear'/> is a
        /// Numbers.EInteger object.</param>
        /// <param name='lesserFields'>An array that will store the fields
        /// (other than the year) of the date and time. See the
        /// TryGetDateTimeFields method for information on the "lesserFields"
        /// parameter.</param>
        /// <returns>A CBOR object encoding the given date fields according to
        /// the conversion type used to create this date converter.</returns>
        /// <exception cref='ArgumentNullException'>The parameter <paramref
        /// name='bigYear'/> or <paramref name='lesserFields'/> is
        /// null.</exception>
        /// <exception cref='PeterO.Cbor2.CBORException'>An error occurred in
        /// conversion.</exception>
        public CBORObject DateTimeFieldsToCBORObject(EInteger bigYear, int[]
                                                     lesserFields)
        {
            // TODO: In next minor version, add overload that takes int rather than
            // EInteger
            if (bigYear == null)
            {
                throw new ArgumentNullException(nameof(bigYear));
            }
            if (lesserFields == null)
            {
                throw new ArgumentNullException(nameof(lesserFields));
            }
            // TODO: Make into CBORException in next major version
            if (lesserFields.Length < 7)
            {
                throw new ArgumentException("\"lesserFields\" + \"'s length\" (" +
                                            lesserFields.Length + ") is not greater or equal to 7");
            }
            try {
                CBORUtilities.CheckYearAndLesserFields(bigYear, lesserFields);
                switch (this.convType)
                {
                case ConversionType.TaggedString: {
                    string str = CBORUtilities.ToAtomDateTimeString(bigYear,
                                                                    lesserFields);
                    return(CBORObject.FromObjectAndTag(str, 0));
                }

                case ConversionType.TaggedNumber:
                case ConversionType.UntaggedNumber:
                    try {
                        var    status = new int[1];
                        EFloat ef     = CBORUtilities.DateTimeToIntegerOrDouble(
                            bigYear,
                            lesserFields,
                            status);
                        if (status[0] == 0)
                        {
                            return(this.convType == ConversionType.TaggedNumber ?
                                   CBORObject.FromObjectAndTag(ef.ToEInteger(), 1) :
                                   CBORObject.FromObject(ef.ToEInteger()));
                        }
                        else if (status[0] == 1)
                        {
                            return(this.convType == ConversionType.TaggedNumber ?
                                   CBORObject.FromFloatingPointBits(ef.ToDoubleBits(),
                                                                    8).WithTag(1) : CBORObject.FromFloatingPointBits(ef.ToDoubleBits(), 8));
                        }
                        else
                        {
                            throw new CBORException("Too big or small to fit an integer" +
                                                    "\u0020or" +
                                                    "\u0020floating-point number");
                        }
                    } catch (ArgumentException ex) {
                        throw new CBORException(ex.Message, ex);
                    }

                default:
                    throw new CBORException("Internal error");
                }
            } catch (ArgumentException ex) {
                throw new CBORException(ex.Message, ex);
            }
        }
Exemple #10
0
 private string TryGetDateTimeFieldsInternal(
     CBORObject obj,
     EInteger[] year,
     int[] lesserFields)
 {
     if (obj == null)
     {
         return("Object is null");
     }
     if (year == null)
     {
         throw new ArgumentNullException(nameof(year));
     }
     EInteger[] outYear = year;
     if (outYear.Length < 1)
     {
         throw new ArgumentException("\"year\" + \"'s length\" (" +
                                     outYear.Length + ") is not greater or equal to 1");
     }
     if (lesserFields == null)
     {
         throw new ArgumentNullException(nameof(lesserFields));
     }
     if (lesserFields.Length < 7)
     {
         throw new ArgumentException("\"lesserFields\" + \"'s length\" (" +
                                     lesserFields.Length + ") is not greater or equal to 7");
     }
     if (this.convType == ConversionType.UntaggedNumber)
     {
         if (obj.IsTagged)
         {
             return("May not be tagged");
         }
         CBORObject untagobj = obj;
         if (!untagobj.IsNumber)
         {
             return("Not a finite number");
         }
         CBORNumber num = untagobj.AsNumber();
         if (!num.IsFinite())
         {
             return("Not a finite number");
         }
         if (num.CompareTo(Int64.MinValue) < 0 ||
             num.CompareTo(Int64.MaxValue) > 0)
         {
             return("Too big or small to fit a DateTime");
         }
         if (num.CanFitInInt64())
         {
             CBORUtilities.BreakDownSecondsSinceEpoch(
                 num.ToInt64Checked(),
                 outYear,
                 lesserFields);
         }
         else
         {
             EDecimal dec;
             dec = (EDecimal)untagobj.ToObject(typeof(EDecimal));
             CBORUtilities.BreakDownSecondsSinceEpoch(
                 dec,
                 outYear,
                 lesserFields);
         }
         return(null); // no error
     }
     if (obj.HasMostOuterTag(0))
     {
         string str = obj.AsString();
         try {
             CBORUtilities.ParseAtomDateTimeString(str, outYear, lesserFields);
             return(null); // no error
         } catch (OverflowException ex) {
             return(ex.Message);
         } catch (InvalidOperationException ex) {
             return(ex.Message);
         } catch (ArgumentException ex) {
             return(ex.Message);
         }
     }
     else if (obj.HasMostOuterTag(1))
     {
         CBORObject untagobj = obj.UntagOne();
         if (!untagobj.IsNumber)
         {
             return("Not a finite number");
         }
         CBORNumber num = untagobj.AsNumber();
         if (!num.IsFinite())
         {
             return("Not a finite number");
         }
         if (num.CanFitInInt64())
         {
             CBORUtilities.BreakDownSecondsSinceEpoch(
                 num.ToInt64Checked(),
                 outYear,
                 lesserFields);
         }
         else
         {
             EDecimal dec;
             dec = (EDecimal)untagobj.ToObject(typeof(EDecimal));
             CBORUtilities.BreakDownSecondsSinceEpoch(
                 dec,
                 outYear,
                 lesserFields);
         }
         return(null); // No error
     }
     return("Not tag 0 or 1");
 }
Exemple #11
0
        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         = CBORDataUtilitiesTextString.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);
        }