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); }
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; } } }
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); }
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); }
public static CBORObject FromObject(ulong value) { return(CBORObject.FromObject(UInt64ToEInteger(value))); }
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."); } }
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"); }
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); }
/// <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); } }
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"); }
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); }