Skip to content

Commit

Permalink
Fix more bugs, including a bug in ParseJSONNumbers involving zeros wi…
Browse files Browse the repository at this point in the history
…th high exponents
  • Loading branch information
peteroupc committed May 31, 2021
1 parent fb2618b commit bf38a11
Show file tree
Hide file tree
Showing 15 changed files with 519 additions and 264 deletions.
22 changes: 20 additions & 2 deletions CBOR/PeterO/Cbor/CBORDataUtilitiesByteArrayString.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ internal static CBORObject ParseJSONNumber(
var haveDecimalPoint = false;
var haveDigits = false;
var haveDigitsAfterDecimal = false;
var haveNonzeroDigits = false;
var haveExponent = false;
int i = offset;
var decimalPointPos = -1;
Expand All @@ -60,9 +61,13 @@ internal static CBORObject ParseJSONNumber(
}
for (; k < endPos; ++k) {
byte c = chars[k];
if (c >= '0' && c <= '9') {
if (c == '0') {
haveDigits = true;
haveDigitsAfterDecimal |= haveDecimalPoint;
} else if (c >= '1' && c <= '9') {
haveDigits = true;
haveDigitsAfterDecimal |= haveDecimalPoint;
haveNonzeroDigits = true;
} else if (c == '.') {
if (!haveDigits || haveDecimalPoint) {
// no digits before the decimal point,
Expand Down Expand Up @@ -151,7 +156,20 @@ internal static CBORObject ParseJSONNumber(
// Exponent too high for precision to overcome (which
// has a length no bigger than Int32.MaxValue, which is 10 digits
// long)
if (negativeExp) {
if (!haveNonzeroDigits) {
// zero
if (kind == JSONOptions.ConversionMode.Double) {
if (!negative) {
return CBORObject.FromFloatingPointBits(0, 2);
} else {
return CBORObject.FromFloatingPointBits(0x8000, 2);
}
} else if (kind ==
JSONOptions.ConversionMode.IntOrFloatFromDouble ||
kind == JSONOptions.ConversionMode.IntOrFloat) {
return CBORObject.FromObject(0);
}
} else if (negativeExp) {
// underflow
if (kind == JSONOptions.ConversionMode.Double ||
kind == JSONOptions.ConversionMode.IntOrFloat) {
Expand Down
22 changes: 20 additions & 2 deletions CBOR/PeterO/Cbor/CBORDataUtilitiesCharArrayString.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ internal static CBORObject ParseJSONNumber(
var haveDecimalPoint = false;
var haveDigits = false;
var haveDigitsAfterDecimal = false;
var haveNonzeroDigits = false;
var haveExponent = false;
int i = offset;
var decimalPointPos = -1;
Expand All @@ -60,9 +61,13 @@ internal static CBORObject ParseJSONNumber(
}
for (; k < endPos; ++k) {
char c = chars[k];
if (c >= '0' && c <= '9') {
if (c == '0') {
haveDigits = true;
haveDigitsAfterDecimal |= haveDecimalPoint;
} else if (c >= '1' && c <= '9') {
haveDigits = true;
haveDigitsAfterDecimal |= haveDecimalPoint;
haveNonzeroDigits = true;
} else if (c == '.') {
if (!haveDigits || haveDecimalPoint) {
// no digits before the decimal point,
Expand Down Expand Up @@ -151,7 +156,20 @@ internal static CBORObject ParseJSONNumber(
// Exponent too high for precision to overcome (which
// has a length no bigger than Int32.MaxValue, which is 10 digits
// long)
if (negativeExp) {
if (!haveNonzeroDigits) {
// zero
if (kind == JSONOptions.ConversionMode.Double) {
if (!negative) {
return CBORObject.FromFloatingPointBits(0, 2);
} else {
return CBORObject.FromFloatingPointBits(0x8000, 2);
}
} else if (kind ==
JSONOptions.ConversionMode.IntOrFloatFromDouble ||
kind == JSONOptions.ConversionMode.IntOrFloat) {
return CBORObject.FromObject(0);
}
} else if (negativeExp) {
// underflow
if (kind == JSONOptions.ConversionMode.Double ||
kind == JSONOptions.ConversionMode.IntOrFloat) {
Expand Down
22 changes: 20 additions & 2 deletions CBOR/PeterO/Cbor/CBORDataUtilitiesTextString.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ internal static CBORObject ParseJSONNumber(
var haveDecimalPoint = false;
var haveDigits = false;
var haveDigitsAfterDecimal = false;
var haveNonzeroDigits = false;
var haveExponent = false;
int i = offset;
var decimalPointPos = -1;
Expand All @@ -60,9 +61,13 @@ internal static CBORObject ParseJSONNumber(
}
for (; k < endPos; ++k) {
char c = chars[k];
if (c >= '0' && c <= '9') {
if (c == '0') {
haveDigits = true;
haveDigitsAfterDecimal |= haveDecimalPoint;
} else if (c >= '1' && c <= '9') {
haveDigits = true;
haveDigitsAfterDecimal |= haveDecimalPoint;
haveNonzeroDigits = true;
} else if (c == '.') {
if (!haveDigits || haveDecimalPoint) {
// no digits before the decimal point,
Expand Down Expand Up @@ -151,7 +156,20 @@ internal static CBORObject ParseJSONNumber(
// Exponent too high for precision to overcome (which
// has a length no bigger than Int32.MaxValue, which is 10 digits
// long)
if (negativeExp) {
if (!haveNonzeroDigits) {
// zero
if (kind == JSONOptions.ConversionMode.Double) {
if (!negative) {
return CBORObject.FromFloatingPointBits(0, 2);
} else {
return CBORObject.FromFloatingPointBits(0x8000, 2);
}
} else if (kind ==
JSONOptions.ConversionMode.IntOrFloatFromDouble ||
kind == JSONOptions.ConversionMode.IntOrFloat) {
return CBORObject.FromObject(0);
}
} else if (negativeExp) {
// underflow
if (kind == JSONOptions.ConversionMode.Double ||
kind == JSONOptions.ConversionMode.IntOrFloat) {
Expand Down
63 changes: 47 additions & 16 deletions CBOR/PeterO/Cbor/CBORObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5573,10 +5573,18 @@ public byte[] EncodeToBytes(CBOREncodeOptions options) {
}
}

/// <summary>Not documented yet.</summary>
/// <summary>Not documented yet.</summary>
/// <param name='pointer'>Not documented yet.</param>
/// <returns>The return value is not documented yet.</returns>
/// <summary>Gets the CBOR object referred to by a JSON Pointer
/// according to RFC6901. For more information, see the overload taking
/// a default value parameter.</summary>
/// <param name='pointer'>A JSON pointer according to RFC 6901.</param>
/// <returns>An object within this CBOR object. Returns this object if
/// pointer is the empty string (even if this object has a CBOR type
/// other than array or map).</returns>
/// <exception cref='PeterO.Cbor.CBORException'>Thrown if the pointer
/// is null, or if the pointer is invalid, or if there is no object at
/// the given pointer, or the special key "-" appears in the pointer,
/// or if the pointer is non-empty and this object has a CBOR type
/// other than array or map.</exception>
public CBORObject AtJSONPointer(string pointer) {
CBORObject ret = this.AtJSONPointer(pointer, null);
if (ret == null) {
Expand All @@ -5585,11 +5593,33 @@ public CBORObject AtJSONPointer(string pointer) {
return ret;
}

/// <summary>Not documented yet.</summary>
/// <summary>Not documented yet.</summary>
/// <returns>The return value is not documented yet.</returns>
/// <param name='pointer'>Not documented yet.</param>
/// <param name='defaultValue'>Not documented yet.</param>
/// <summary>Gets the CBOR object referred to by a JSON Pointer
/// according to RFC6901, or a default value if the operation fails.
/// The syntax for a JSON Pointer is:
/// <pre>'/' KEY '/' KEY [...]</pre> where KEY represents a key into
/// the JSON object or its sub-objects in the hierarchy. For example,
/// <pre>/foo/2/bar</pre> means the same as
/// <pre>obj['foo'][2]['bar']</pre> in JavaScript. If "~" and/or "/"
/// occurs in a key, it must be escaped with "~0" or "~1",
/// respectively, in a JSON pointer. JSON pointers also support the
/// special key "-" (as in "/foo/-") to indicate the end of an array,
/// but this method treats this key as an error since it refers to a
/// nonexistent item. Indices to arrays (such as 2 in the example) must
/// contain only basic digits 0 to 9 and no leading zeros. (Note that
/// RFC 6901 was published before JSON was extended to support
/// top-level values other than arrays and key-value
/// dictionaries.).</summary>
/// <param name='pointer'>A JSON pointer according to RFC 6901.</param>
/// <param name='defaultValue'>The parameter <paramref
/// name='defaultValue'/> is a Cbor.CBORObject object.</param>
/// <returns>An object within the specified JSON object. Returns this
/// object if pointer is the empty string (even if this object has a
/// CBOR type other than array or map). Returns <paramref
/// name='defaultValue'/> if the pointer is null, or if the pointer is
/// invalid, or if there is no object at the given pointer, or the
/// special key "-" appears in the pointer, or if the pointer is
/// non-empty and this object has a CBOR type other than array or
/// map.</returns>
public CBORObject AtJSONPointer(string pointer, CBORObject defaultValue) {
return JSONPointer.GetObject(this, pointer, null);
}
Expand All @@ -5601,25 +5631,26 @@ public CBORObject AtJSONPointer(string pointer, CBORObject defaultValue) {
/// <param name='patch'>A JSON patch in the form of a CBOR object; it
/// has the form summarized in the remarks.</param>
/// <returns>The result of the patch operation.</returns>
/// <exception cref='CBORException'>The parameter "patch" is null or
/// the patch operation failed.</exception>
/// <exception cref='PeterO.Cbor.CBORException'>The parameter <paramref
/// name='patch'/> is null or the patch operation failed.</exception>
/// <remarks><b>Remarks:</b> A JSON patch is an array with one or more
/// maps. Each map has the following keys:
/// <list>
/// <item>"op" - Required. This key's value is the patch operation and
/// must be "add", "remove", "move", "copy", "test", or "replace", in
/// lower case and no other case combination.</item>
/// basic lower case letters and no other case combination.</item>
/// <item>"value" - Required if the operation is "add", "replace", or
/// "test" and specifies the item to add (insert), or that will replace
/// the existing item, or to check an existing item for equality,
/// respectively. (For "test", the operation fails if the existing item
/// doesn't match the specified value.)</item>
/// <item>"path" - Required for all operations. A JSON Pointer (RFC
/// 6901) specifying the target path in the CBOR object for the
/// operation.</item>
/// 6901) specifying the destination path in the CBOR object for the
/// operation. For more information, see RFC 6901 or the documentation
/// for AtJSONPointer(pointer, defaultValue).</item>
/// <item>"from" - Required if the operation is "move" or "copy". A
/// JSON Pointer (RFC 6901) specifying the target path in the CBOR
/// object where the source value is located.</item></list></remarks>
/// JSON Pointer (RFC 6901) specifying the path in the CBOR object
/// where the source value is located.</item></list></remarks>
public CBORObject ApplyJSONPatch(CBORObject patch) {
return JSONPatch.Patch(this, patch);
}
Expand Down
56 changes: 19 additions & 37 deletions CBOR/PeterO/Cbor/JSONPointer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -132,33 +132,6 @@ public static JSONPointer FromPointer(CBORObject obj, string pointer) {
}
}

/// <summary>Gets the CBOR object referred to by a JSON Pointer
/// according to RFC6901. The syntax for pointers is:
/// <pre>'/' KEY '/' KEY [...]</pre> where KEY represents a key into
/// the JSON object or its sub-objects in the hierarchy. For example,
/// <pre>/foo/2/bar</pre> means the same as
/// <pre>obj['foo'][2]['bar']</pre> in JavaScript. If "~" and/or "/"
/// occurs in a key, it must be escaped with "~0" or "~1",
/// respectively, in a JSON pointer. JSON pointers also support the
/// special key "-" (as in "/foo/-") to indicate the end of an array,
/// but this method treats this key as an error since it refers to a
/// nonexistent item. Indices to arrays (such as 2 in the example) must
/// contain only basic digits 0 to 9 and no leading zeros.</summary>
/// <returns>An object within the specified JSON object. Returns this
/// object if pointer is the empty string (even if this object has a
/// CBOR type other than array or map). Returns <paramref
/// name='defaultValue'/> if the pointer is null, or if the pointer is
/// invalid, or if there is no object at the given pointer, or the
/// special key "-" appears in the pointer, or if the pointer is
/// non-empty and this object has a CBOR type other than array or map.
/// (Note that RFC 6901 was published before JSON was extended to
/// support top-level values other than arrays and key-value
/// dictionaries.)</returns>
/// <exception cref='ArgumentNullException'>The parameter <paramref
/// name='pointer'/> or <paramref name='obj'/> is null.</exception>
/// <param name='obj'>Not documented yet.</param>
/// <param name='pointer'>Not documented yet.</param>
/// <param name='defaultValue'>Not documented yet.</param>
public static CBORObject GetObject(
CBORObject obj,
string pointer,
Expand All @@ -176,8 +149,12 @@ public static CBORObject GetObject(
if (obj.Type != CBORType.Array && obj.Type != CBORType.Map) {
return defaultValue;
}
CBORObject cobj = JSONPointer.FromPointer(obj, pointer).GetValue();
return cobj == null ? defaultValue : cobj;
try {
CBORObject cobj = JSONPointer.FromPointer(obj, pointer).GetValue();
return cobj == null ? defaultValue : cobj;
} catch (CBORException) {
return defaultValue;
}
}

private static int ReadPositiveInteger(
Expand Down Expand Up @@ -243,6 +220,10 @@ private JSONPointer(CBORObject jsonobj, string refValue) {
}

public bool Exists() {
if (this.refValue.Length == 0) {
// Root always exists
return true;
}
if (this.jsonobj.Type == CBORType.Array) {
if (this.refValue.Equals("-", StringComparison.Ordinal)) {
return false;
Expand Down Expand Up @@ -287,6 +268,10 @@ public CBORObject GetParent() {
}

public CBORObject GetValue() {
if (this.refValue.Length == 0) {
// Root always exists
return this.jsonobj;
}
CBORObject tmpcbor = null;
if (this.jsonobj.Type == CBORType.Array) {
int index = this.GetIndex();
Expand All @@ -300,7 +285,7 @@ public CBORObject GetValue() {
// DebugUtility.Log("jsonobj=" + this.jsonobj + " refValue=[" + this.refValue
// + "]");
tmpcbor = this.jsonobj;
return tmpcbor[this.refValue];
return tmpcbor.GetOrDefault(this.refValue, null);
} else {
return (this.refValue.Length == 0) ? this.jsonobj : null;
}
Expand Down Expand Up @@ -354,8 +339,7 @@ public static IDictionary<string, CBORObject> GetPointersWithKeyAndRemove(
/// <pre>{ "/0" => "value1", // "/0" points to
/// {"key":"value1","foo":"foovalue"} "/1" => "value2" // "/1" points
/// to {"key":"value2","bar":"barvalue"} }</pre> and the JSON object
/// will remain unchanged. @param root object to search @param
/// keyToFind the key to search for. @return a map:
/// will remain unchanged.
/// <list>
/// <item>The keys in the map are JSON Pointers to the objects within
/// <i>root</i> that contained a key named
Expand All @@ -366,11 +350,9 @@ public static IDictionary<string, CBORObject> GetPointersWithKeyAndRemove(
/// named
/// <i>keyToFind</i>.</item></list> The JSON Pointers are relative to
/// the root object.</summary>
/// <param name='root'>The parameter <paramref name='root'/> is not
/// documented yet.</param>
/// <param name='keyToFind'>The parameter <paramref name='keyToFind'/>
/// is not documented yet.</param>
/// <returns>An IDictionary(string, CBORObject) object.</returns>
/// <param name='root'>Object to search.</param>
/// <param name='keyToFind'>The key to search for.</param>
/// <returns>A map:.</returns>
/// <exception cref='ArgumentNullException'>The parameter <paramref
/// name='root'/> is null.</exception>
public static IDictionary<string, CBORObject> GetPointersWithKey(
Expand Down
Loading

0 comments on commit bf38a11

Please sign in to comment.