mirror of
https://github.com/nlohmann/json.git
synced 2026-10-06 06:30:31 +00:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6b4f4aade6 |
@@ -361,7 +361,7 @@ std::cout << j_string << " == " << serialized_string << std::endl;
|
|||||||
|
|
||||||
[`.dump()`](https://json.nlohmann.me/api/basic_json/dump/) returns the originally stored string value.
|
[`.dump()`](https://json.nlohmann.me/api/basic_json/dump/) returns the originally stored string value.
|
||||||
|
|
||||||
Note the library only supports UTF-8. When you store strings with different encodings in the library, calling [`dump()`](https://json.nlohmann.me/api/basic_json/dump/) may throw an exception unless `json::error_handler_t::replace`, `json::error_handler_t::ignore`, or `json::error_handler_t::keep` are used as error handlers.
|
Note the library only supports UTF-8. When you store strings with different encodings in the library, calling [`dump()`](https://json.nlohmann.me/api/basic_json/dump/) may throw an exception unless `json::error_handler_t::replace` or `json::error_handler_t::ignore` are used as error handlers.
|
||||||
|
|
||||||
#### To/from streams (e.g., files, string streams)
|
#### To/from streams (e.g., files, string streams)
|
||||||
|
|
||||||
@@ -1914,7 +1914,7 @@ The library supports **Unicode input** as follows:
|
|||||||
- [Unicode noncharacters](https://www.unicode.org/faq/private_use.html#nonchar1) will not be replaced by the library.
|
- [Unicode noncharacters](https://www.unicode.org/faq/private_use.html#nonchar1) will not be replaced by the library.
|
||||||
- Invalid surrogates (e.g., incomplete pairs such as `\uDEAD`) will yield parse errors.
|
- Invalid surrogates (e.g., incomplete pairs such as `\uDEAD`) will yield parse errors.
|
||||||
- The strings stored in the library are UTF-8 encoded. When using the default string type (`std::string`), note that its length/size functions return the number of stored bytes rather than the number of characters or glyphs.
|
- The strings stored in the library are UTF-8 encoded. When using the default string type (`std::string`), note that its length/size functions return the number of stored bytes rather than the number of characters or glyphs.
|
||||||
- When you store strings with different encodings in the library, calling [`dump()`](https://json.nlohmann.me/api/basic_json/dump/) may throw an exception unless `json::error_handler_t::replace`, `json::error_handler_t::ignore`, or `json::error_handler_t::keep` are used as error handlers.
|
- When you store strings with different encodings in the library, calling [`dump()`](https://json.nlohmann.me/api/basic_json/dump/) may throw an exception unless `json::error_handler_t::replace` or `json::error_handler_t::ignore` are used as error handlers.
|
||||||
- To store wide strings (e.g., `std::wstring`), you need to convert them to a UTF-8 encoded `std::string` before, see [an example](https://json.nlohmann.me/home/faq/#wide-string-handling).
|
- To store wide strings (e.g., `std::wstring`), you need to convert them to a UTF-8 encoded `std::string` before, see [an example](https://json.nlohmann.me/home/faq/#wide-string-handling).
|
||||||
|
|
||||||
### Comments in JSON
|
### Comments in JSON
|
||||||
|
|||||||
@@ -141,8 +141,14 @@ The library uses the following mapping from JSON values types to BJData types ac
|
|||||||
parsed back as a regular array,
|
parsed back as a regular array,
|
||||||
- every entry of `"_ArraySize_"` is a positive integer, and their product is representable as a `std::size_t`,
|
- every entry of `"_ArraySize_"` is a positive integer, and their product is representable as a `std::size_t`,
|
||||||
- `"_ArrayData_"` is an array holding exactly that many elements, and
|
- `"_ArrayData_"` is an array holding exactly that many elements, and
|
||||||
- every element of `"_ArrayData_"` is a number of the kind named by `"_ArrayType_"` (a floating-point number for
|
- every element of `"_ArrayData_"` is a number of the kind named by `"_ArrayType_"`: for the integer types, a
|
||||||
`single` and `double`, an integer otherwise).
|
value that fits the named width; for `double`, any value; for `single`, a value that survives narrowing to
|
||||||
|
`float` and back without change (for instance, `0.1` does not, since it is not exactly representable as
|
||||||
|
`float`).
|
||||||
|
|
||||||
|
An annotated object is always read back with its keys in the order shown above, `"_ArrayType_"`, `"_ArraySize_"`,
|
||||||
|
`"_ArrayData_"`, regardless of the order the ND-array's header stores them in on the wire. This matters for
|
||||||
|
`ordered_json`, whose comparison takes key order into account.
|
||||||
|
|
||||||
The current version of this library does not yet support automatic detection of and conversion from a nested JSON
|
The current version of this library does not yet support automatic detection of and conversion from a nested JSON
|
||||||
array input to a BJData ND-array.
|
array input to a BJData ND-array.
|
||||||
|
|||||||
@@ -64,7 +64,6 @@ serialization fails by default. The fourth argument of `dump` selects an
|
|||||||
- `strict` (default) — throw a [`type_error.316`](../home/exceptions.md#jsonexceptiontype_error316) exception.
|
- `strict` (default) — throw a [`type_error.316`](../home/exceptions.md#jsonexceptiontype_error316) exception.
|
||||||
- `replace` — replace invalid bytes with the Unicode replacement character U+FFFD (`�`).
|
- `replace` — replace invalid bytes with the Unicode replacement character U+FFFD (`�`).
|
||||||
- `ignore` — silently drop invalid bytes.
|
- `ignore` — silently drop invalid bytes.
|
||||||
- `keep` — copy invalid bytes to the output unchanged; the result is not valid UTF-8.
|
|
||||||
|
|
||||||
??? example "Example: serialize invalid UTF-8 with different error handlers"
|
??? example "Example: serialize invalid UTF-8 with different error handlers"
|
||||||
|
|
||||||
|
|||||||
@@ -771,7 +771,6 @@ as well for a string value or object key that is not valid UTF-8 if their `error
|
|||||||
- Pass an error handler as last parameter to the `dump()` function to avoid this exception:
|
- Pass an error handler as last parameter to the `dump()` function to avoid this exception:
|
||||||
- `json::error_handler_t::replace` will replace invalid bytes sequences with `U+FFFD`
|
- `json::error_handler_t::replace` will replace invalid bytes sequences with `U+FFFD`
|
||||||
- `json::error_handler_t::ignore` will silently ignore invalid byte sequences
|
- `json::error_handler_t::ignore` will silently ignore invalid byte sequences
|
||||||
- `json::error_handler_t::keep` will copy invalid byte sequences to the output unchanged
|
|
||||||
|
|
||||||
### json.exception.type_error.317
|
### json.exception.type_error.317
|
||||||
|
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ The library supports **Unicode input** as follows:
|
|||||||
- The library will not replace [Unicode noncharacters](http://www.unicode.org/faq/private_use.html#nonchar1).
|
- The library will not replace [Unicode noncharacters](http://www.unicode.org/faq/private_use.html#nonchar1).
|
||||||
- Invalid surrogates (e.g., incomplete pairs such as `\uDEAD`) will yield parse errors.
|
- Invalid surrogates (e.g., incomplete pairs such as `\uDEAD`) will yield parse errors.
|
||||||
- The strings stored in the library are UTF-8 encoded. When using the default string type (`std::string`), note that its length/size functions return the number of stored bytes rather than the number of characters or glyphs.
|
- The strings stored in the library are UTF-8 encoded. When using the default string type (`std::string`), note that its length/size functions return the number of stored bytes rather than the number of characters or glyphs.
|
||||||
- When you store strings with different encodings in the library, calling [`dump()`](../api/basic_json/dump.md) may throw an exception unless `json::error_handler_t::replace`, `json::error_handler_t::ignore`, or `json::error_handler_t::keep` are used as error handlers.
|
- When you store strings with different encodings in the library, calling [`dump()`](../api/basic_json/dump.md) may throw an exception unless `json::error_handler_t::replace` or `json::error_handler_t::ignore` are used as error handlers.
|
||||||
|
|
||||||
In most cases, the parser is right to complain, because the input is not UTF-8 encoded. This is especially true for Microsoft Windows, where Latin-1 or ISO 8859-1 is often the standard encoding.
|
In most cases, the parser is right to complain, because the input is not UTF-8 encoded. This is especially true for Microsoft Windows, where Latin-1 or ISO 8859-1 is often the standard encoding.
|
||||||
|
|
||||||
|
|||||||
@@ -2706,10 +2706,15 @@ class binary_reader
|
|||||||
is_ndarray can only return `true` when its initial value
|
is_ndarray can only return `true` when its initial value
|
||||||
is `false`
|
is `false`
|
||||||
@param[in] prefix type marker if already read, otherwise set to 0
|
@param[in] prefix type marker if already read, otherwise set to 0
|
||||||
|
@param[in] ndarray_dtype the element type marker of the enclosing bjdata ndarray if
|
||||||
|
already known (it precedes the dimension vector read here),
|
||||||
|
otherwise 0; used to emit the "_ArrayType_" annotation key
|
||||||
|
before "_ArraySize_" if a dimension vector turns out to
|
||||||
|
describe an ndarray
|
||||||
|
|
||||||
@return whether size determination completed
|
@return whether size determination completed
|
||||||
*/
|
*/
|
||||||
bool get_ubjson_size_value(std::size_t& result, bool& is_ndarray, char_int_type prefix = 0)
|
bool get_ubjson_size_value(std::size_t& result, bool& is_ndarray, char_int_type prefix = 0, char_int_type ndarray_dtype = 0)
|
||||||
{
|
{
|
||||||
if (prefix == 0)
|
if (prefix == 0)
|
||||||
{
|
{
|
||||||
@@ -2822,8 +2827,34 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(!sax->start_object(3)))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// the element type precedes the dimension vector (see get_ubjson_size_type)
|
||||||
|
// and is passed down as ndarray_dtype; emit it here so the annotation keys
|
||||||
|
// follow the documented _ArrayType_, _ArraySize_, _ArrayData_ order
|
||||||
|
if (ndarray_dtype != 0)
|
||||||
|
{
|
||||||
|
const char* type_name = bjd_type_name(ndarray_dtype);
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(type_name == nullptr))
|
||||||
|
{
|
||||||
|
auto last_token = get_token_string();
|
||||||
|
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
|
||||||
|
exception_message("invalid byte: 0x" + last_token, "type"), nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
string_t type_key = "_ArrayType_";
|
||||||
|
string_t type = type_name; // sax->string() takes a reference
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(!sax->key(type_key) || !sax->string(type)))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
string_t key = "_ArraySize_";
|
string_t key = "_ArraySize_";
|
||||||
if (JSON_HEDLEY_UNLIKELY(!sax->start_object(3) || !sax->key(key) || !sax->start_array(dim.size())))
|
if (JSON_HEDLEY_UNLIKELY(!sax->key(key) || !sax->start_array(dim.size())))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -2931,7 +2962,7 @@ class binary_reader
|
|||||||
exception_message(concat("expected '#' after type information; last byte: 0x", last_token), "size"), nullptr));
|
exception_message(concat("expected '#' after type information; last byte: 0x", last_token), "size"), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool is_error = get_ubjson_size_value(result.first, is_ndarray);
|
const bool is_error = get_ubjson_size_value(result.first, is_ndarray, 0, result.second);
|
||||||
// an ndarray was read here only if the flag flipped; when it was
|
// an ndarray was read here only if the flag flipped; when it was
|
||||||
// seeded true, get_ubjson_size_value() already rejected the nested
|
// seeded true, get_ubjson_size_value() already rejected the nested
|
||||||
// dimension vector
|
// dimension vector
|
||||||
@@ -3124,27 +3155,17 @@ class binary_reader
|
|||||||
if (input_format == input_format_t::bjdata && size_and_type.first != npos && (size_and_type.second & (1 << 8)) != 0)
|
if (input_format == input_format_t::bjdata && size_and_type.first != npos && (size_and_type.second & (1 << 8)) != 0)
|
||||||
{
|
{
|
||||||
size_and_type.second &= ~(static_cast<char_int_type>(1) << 8); // use bit 8 to indicate ndarray, here we remove the bit to restore the type marker
|
size_and_type.second &= ~(static_cast<char_int_type>(1) << 8); // use bit 8 to indicate ndarray, here we remove the bit to restore the type marker
|
||||||
const char* type_name = bjd_type_name(size_and_type.second);
|
|
||||||
string_t key = "_ArrayType_";
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(type_name == nullptr))
|
|
||||||
{
|
|
||||||
auto last_token = get_token_string();
|
|
||||||
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
|
|
||||||
exception_message("invalid byte: 0x" + last_token, "type"), nullptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
string_t type = type_name; // sax->string() takes a reference
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!sax->key(key) || !sax->string(type)))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// the "_ArrayType_" and "_ArraySize_" annotation keys were already emitted by
|
||||||
|
// get_ubjson_size_value() (the type marker is known before the dimension vector
|
||||||
|
// that determines size_and_type.first is read, so it is emitted first there to
|
||||||
|
// match the documented _ArrayType_, _ArraySize_, _ArrayData_ key order)
|
||||||
if (size_and_type.second == 'C' || size_and_type.second == 'B')
|
if (size_and_type.second == 'C' || size_and_type.second == 'B')
|
||||||
{
|
{
|
||||||
size_and_type.second = 'U';
|
size_and_type.second = 'U';
|
||||||
}
|
}
|
||||||
|
|
||||||
key = "_ArrayData_";
|
string_t key = "_ArrayData_";
|
||||||
if (JSON_HEDLEY_UNLIKELY(!sax->key(key) || !sax->start_array(size_and_type.first) ))
|
if (JSON_HEDLEY_UNLIKELY(!sax->key(key) || !sax->start_array(size_and_type.first) ))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -1817,16 +1817,31 @@ class binary_writer
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief validate (dry_run) or write one BJData ND-array element of dtype 'd' (single precision)
|
@brief validate (dry_run) or write one BJData ND-array element of dtype 'd' (single precision)
|
||||||
@return whether @a el's value fits a float without overflow; always true when @a dry_run is false
|
@return whether @a el's value survives narrowing to float and back without any change
|
||||||
|
(so the ND-array round-trips exactly), or is infinite or NaN; always true when
|
||||||
|
@a dry_run is false
|
||||||
*/
|
*/
|
||||||
bool write_bjdata_ndarray_float_element(const BasicJsonType& el, const bool dry_run)
|
bool write_bjdata_ndarray_float_element(const BasicJsonType& el, const bool dry_run)
|
||||||
{
|
{
|
||||||
const auto dval = el.template get<double>();
|
const auto dval = el.template get<double>();
|
||||||
if (dry_run)
|
if (dry_run)
|
||||||
{
|
{
|
||||||
return !std::isfinite(dval) ||
|
#ifdef __GNUC__
|
||||||
|
JSON_HEDLEY_DIAGNOSTIC_PUSH
|
||||||
|
JSON_HEDLEY_PRAGMA(GCC diagnostic ignored "-Wfloat-equal")
|
||||||
|
#endif
|
||||||
|
// a value that would be rounded (rather than exactly represented) by the
|
||||||
|
// narrowing to float is treated like an out-of-range integer element; this
|
||||||
|
// is the same criterion write_compact_float() uses for CBOR/MessagePack
|
||||||
|
const bool in_range = std::isnan(dval) ||
|
||||||
(dval >= static_cast<double>(std::numeric_limits<float>::lowest()) &&
|
(dval >= static_cast<double>(std::numeric_limits<float>::lowest()) &&
|
||||||
dval <= static_cast<double>((std::numeric_limits<float>::max)()));
|
dval <= static_cast<double>((std::numeric_limits<float>::max)()) &&
|
||||||
|
static_cast<double>(static_cast<float>(dval)) == dval) ||
|
||||||
|
std::isinf(dval);
|
||||||
|
#ifdef __GNUC__
|
||||||
|
JSON_HEDLEY_DIAGNOSTIC_POP
|
||||||
|
#endif
|
||||||
|
return in_range;
|
||||||
}
|
}
|
||||||
write_number(static_cast<float>(dval), true);
|
write_number(static_cast<float>(dval), true);
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -16423,10 +16423,15 @@ class binary_reader
|
|||||||
is_ndarray can only return `true` when its initial value
|
is_ndarray can only return `true` when its initial value
|
||||||
is `false`
|
is `false`
|
||||||
@param[in] prefix type marker if already read, otherwise set to 0
|
@param[in] prefix type marker if already read, otherwise set to 0
|
||||||
|
@param[in] ndarray_dtype the element type marker of the enclosing bjdata ndarray if
|
||||||
|
already known (it precedes the dimension vector read here),
|
||||||
|
otherwise 0; used to emit the "_ArrayType_" annotation key
|
||||||
|
before "_ArraySize_" if a dimension vector turns out to
|
||||||
|
describe an ndarray
|
||||||
|
|
||||||
@return whether size determination completed
|
@return whether size determination completed
|
||||||
*/
|
*/
|
||||||
bool get_ubjson_size_value(std::size_t& result, bool& is_ndarray, char_int_type prefix = 0)
|
bool get_ubjson_size_value(std::size_t& result, bool& is_ndarray, char_int_type prefix = 0, char_int_type ndarray_dtype = 0)
|
||||||
{
|
{
|
||||||
if (prefix == 0)
|
if (prefix == 0)
|
||||||
{
|
{
|
||||||
@@ -16539,8 +16544,34 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(!sax->start_object(3)))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// the element type precedes the dimension vector (see get_ubjson_size_type)
|
||||||
|
// and is passed down as ndarray_dtype; emit it here so the annotation keys
|
||||||
|
// follow the documented _ArrayType_, _ArraySize_, _ArrayData_ order
|
||||||
|
if (ndarray_dtype != 0)
|
||||||
|
{
|
||||||
|
const char* type_name = bjd_type_name(ndarray_dtype);
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(type_name == nullptr))
|
||||||
|
{
|
||||||
|
auto last_token = get_token_string();
|
||||||
|
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
|
||||||
|
exception_message("invalid byte: 0x" + last_token, "type"), nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
string_t type_key = "_ArrayType_";
|
||||||
|
string_t type = type_name; // sax->string() takes a reference
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(!sax->key(type_key) || !sax->string(type)))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
string_t key = "_ArraySize_";
|
string_t key = "_ArraySize_";
|
||||||
if (JSON_HEDLEY_UNLIKELY(!sax->start_object(3) || !sax->key(key) || !sax->start_array(dim.size())))
|
if (JSON_HEDLEY_UNLIKELY(!sax->key(key) || !sax->start_array(dim.size())))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -16648,7 +16679,7 @@ class binary_reader
|
|||||||
exception_message(concat("expected '#' after type information; last byte: 0x", last_token), "size"), nullptr));
|
exception_message(concat("expected '#' after type information; last byte: 0x", last_token), "size"), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool is_error = get_ubjson_size_value(result.first, is_ndarray);
|
const bool is_error = get_ubjson_size_value(result.first, is_ndarray, 0, result.second);
|
||||||
// an ndarray was read here only if the flag flipped; when it was
|
// an ndarray was read here only if the flag flipped; when it was
|
||||||
// seeded true, get_ubjson_size_value() already rejected the nested
|
// seeded true, get_ubjson_size_value() already rejected the nested
|
||||||
// dimension vector
|
// dimension vector
|
||||||
@@ -16841,27 +16872,17 @@ class binary_reader
|
|||||||
if (input_format == input_format_t::bjdata && size_and_type.first != npos && (size_and_type.second & (1 << 8)) != 0)
|
if (input_format == input_format_t::bjdata && size_and_type.first != npos && (size_and_type.second & (1 << 8)) != 0)
|
||||||
{
|
{
|
||||||
size_and_type.second &= ~(static_cast<char_int_type>(1) << 8); // use bit 8 to indicate ndarray, here we remove the bit to restore the type marker
|
size_and_type.second &= ~(static_cast<char_int_type>(1) << 8); // use bit 8 to indicate ndarray, here we remove the bit to restore the type marker
|
||||||
const char* type_name = bjd_type_name(size_and_type.second);
|
|
||||||
string_t key = "_ArrayType_";
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(type_name == nullptr))
|
|
||||||
{
|
|
||||||
auto last_token = get_token_string();
|
|
||||||
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
|
|
||||||
exception_message("invalid byte: 0x" + last_token, "type"), nullptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
string_t type = type_name; // sax->string() takes a reference
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!sax->key(key) || !sax->string(type)))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// the "_ArrayType_" and "_ArraySize_" annotation keys were already emitted by
|
||||||
|
// get_ubjson_size_value() (the type marker is known before the dimension vector
|
||||||
|
// that determines size_and_type.first is read, so it is emitted first there to
|
||||||
|
// match the documented _ArrayType_, _ArraySize_, _ArrayData_ key order)
|
||||||
if (size_and_type.second == 'C' || size_and_type.second == 'B')
|
if (size_and_type.second == 'C' || size_and_type.second == 'B')
|
||||||
{
|
{
|
||||||
size_and_type.second = 'U';
|
size_and_type.second = 'U';
|
||||||
}
|
}
|
||||||
|
|
||||||
key = "_ArrayData_";
|
string_t key = "_ArrayData_";
|
||||||
if (JSON_HEDLEY_UNLIKELY(!sax->key(key) || !sax->start_array(size_and_type.first) ))
|
if (JSON_HEDLEY_UNLIKELY(!sax->key(key) || !sax->start_array(size_and_type.first) ))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@@ -23277,16 +23298,31 @@ class binary_writer
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief validate (dry_run) or write one BJData ND-array element of dtype 'd' (single precision)
|
@brief validate (dry_run) or write one BJData ND-array element of dtype 'd' (single precision)
|
||||||
@return whether @a el's value fits a float without overflow; always true when @a dry_run is false
|
@return whether @a el's value survives narrowing to float and back without any change
|
||||||
|
(so the ND-array round-trips exactly), or is infinite or NaN; always true when
|
||||||
|
@a dry_run is false
|
||||||
*/
|
*/
|
||||||
bool write_bjdata_ndarray_float_element(const BasicJsonType& el, const bool dry_run)
|
bool write_bjdata_ndarray_float_element(const BasicJsonType& el, const bool dry_run)
|
||||||
{
|
{
|
||||||
const auto dval = el.template get<double>();
|
const auto dval = el.template get<double>();
|
||||||
if (dry_run)
|
if (dry_run)
|
||||||
{
|
{
|
||||||
return !std::isfinite(dval) ||
|
#ifdef __GNUC__
|
||||||
|
JSON_HEDLEY_DIAGNOSTIC_PUSH
|
||||||
|
JSON_HEDLEY_PRAGMA(GCC diagnostic ignored "-Wfloat-equal")
|
||||||
|
#endif
|
||||||
|
// a value that would be rounded (rather than exactly represented) by the
|
||||||
|
// narrowing to float is treated like an out-of-range integer element; this
|
||||||
|
// is the same criterion write_compact_float() uses for CBOR/MessagePack
|
||||||
|
const bool in_range = std::isnan(dval) ||
|
||||||
(dval >= static_cast<double>(std::numeric_limits<float>::lowest()) &&
|
(dval >= static_cast<double>(std::numeric_limits<float>::lowest()) &&
|
||||||
dval <= static_cast<double>((std::numeric_limits<float>::max)()));
|
dval <= static_cast<double>((std::numeric_limits<float>::max)()) &&
|
||||||
|
static_cast<double>(static_cast<float>(dval)) == dval) ||
|
||||||
|
std::isinf(dval);
|
||||||
|
#ifdef __GNUC__
|
||||||
|
JSON_HEDLEY_DIAGNOSTIC_POP
|
||||||
|
#endif
|
||||||
|
return in_range;
|
||||||
}
|
}
|
||||||
write_number(static_cast<float>(dval), true);
|
write_number(static_cast<float>(dval), true);
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
using nlohmann::json;
|
using nlohmann::json;
|
||||||
|
using ordered_json = nlohmann::ordered_json;
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <climits>
|
#include <climits>
|
||||||
@@ -2183,29 +2184,33 @@ TEST_CASE("BJData")
|
|||||||
|
|
||||||
SECTION("start_array() in ndarray _ArraySize_")
|
SECTION("start_array() in ndarray _ArraySize_")
|
||||||
{
|
{
|
||||||
|
// _ArrayType_ (2 events: key + string) is now emitted before
|
||||||
|
// _ArraySize_ (see GitHub issue #5661), which shifts the events
|
||||||
|
// below later by the same 2 events
|
||||||
std::vector<uint8_t> const v = {'[', '$', 'i', '#', '[', '$', 'i', '#', 'i', 2, 2, 1, 1, 2};
|
std::vector<uint8_t> const v = {'[', '$', 'i', '#', '[', '$', 'i', '#', 'i', 2, 2, 1, 1, 2};
|
||||||
SaxCountdown scp(2);
|
SaxCountdown scp(4);
|
||||||
CHECK_FALSE(json::sax_parse(v, &scp, json::input_format_t::bjdata));
|
CHECK_FALSE(json::sax_parse(v, &scp, json::input_format_t::bjdata));
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("number_integer() in ndarray _ArraySize_")
|
SECTION("number_integer() in ndarray _ArraySize_")
|
||||||
{
|
{
|
||||||
std::vector<uint8_t> const v = {'[', '$', 'U', '#', '[', '$', 'i', '#', 'i', 2, 2, 1, 1, 2};
|
std::vector<uint8_t> const v = {'[', '$', 'U', '#', '[', '$', 'i', '#', 'i', 2, 2, 1, 1, 2};
|
||||||
SaxCountdown scp(3);
|
SaxCountdown scp(5);
|
||||||
CHECK_FALSE(json::sax_parse(v, &scp, json::input_format_t::bjdata));
|
CHECK_FALSE(json::sax_parse(v, &scp, json::input_format_t::bjdata));
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("key() in ndarray _ArrayType_")
|
SECTION("key() in ndarray _ArrayType_")
|
||||||
{
|
{
|
||||||
|
// _ArrayType_ is emitted right after start_object(), before _ArraySize_
|
||||||
std::vector<uint8_t> const v = {'[', '$', 'U', '#', '[', '$', 'U', '#', 'i', 2, 2, 2, 1, 2, 3, 4};
|
std::vector<uint8_t> const v = {'[', '$', 'U', '#', '[', '$', 'U', '#', 'i', 2, 2, 2, 1, 2, 3, 4};
|
||||||
SaxCountdown scp(6);
|
SaxCountdown scp(1);
|
||||||
CHECK_FALSE(json::sax_parse(v, &scp, json::input_format_t::bjdata));
|
CHECK_FALSE(json::sax_parse(v, &scp, json::input_format_t::bjdata));
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("string() in ndarray _ArrayType_")
|
SECTION("string() in ndarray _ArrayType_")
|
||||||
{
|
{
|
||||||
std::vector<uint8_t> const v = {'[', '$', 'U', '#', '[', '$', 'U', '#', 'i', 2, 2, 2, 1, 2, 3, 4};
|
std::vector<uint8_t> const v = {'[', '$', 'U', '#', '[', '$', 'U', '#', 'i', 2, 2, 2, 1, 2, 3, 4};
|
||||||
SaxCountdown scp(7);
|
SaxCountdown scp(2);
|
||||||
CHECK_FALSE(json::sax_parse(v, &scp, json::input_format_t::bjdata));
|
CHECK_FALSE(json::sax_parse(v, &scp, json::input_format_t::bjdata));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2808,6 +2813,22 @@ TEST_CASE("BJData")
|
|||||||
CHECK(out_single.at(0) == '{');
|
CHECK(out_single.at(0) == '{');
|
||||||
CHECK(json::from_bjdata(out_single) == j_single);
|
CHECK(json::from_bjdata(out_single) == j_single);
|
||||||
|
|
||||||
|
// a double element that is finite and within the range of "single"
|
||||||
|
// but is not exactly representable as a float, so narrowing it would
|
||||||
|
// silently round it (0.1 is read back as 0.10000000149011612); this,
|
||||||
|
// like the overflow case above, falls back to a plain object (see
|
||||||
|
// GitHub issue #5661)
|
||||||
|
json const j_single_rounded = json({{"_ArrayType_", "single"}, {"_ArraySize_", {2, 1}}, {"_ArrayData_", {1.5, 0.1}}});
|
||||||
|
const auto out_single_rounded = json::to_bjdata(j_single_rounded);
|
||||||
|
CHECK(out_single_rounded.at(0) == '{');
|
||||||
|
CHECK(json::from_bjdata(out_single_rounded) == j_single_rounded);
|
||||||
|
|
||||||
|
// a double element that underflows to 0 when narrowed to "single"
|
||||||
|
json const j_single_underflow = json({{"_ArrayType_", "single"}, {"_ArraySize_", {2, 1}}, {"_ArrayData_", {1.5, 1e-300}}});
|
||||||
|
const auto out_single_underflow = json::to_bjdata(j_single_underflow);
|
||||||
|
CHECK(out_single_underflow.at(0) == '{');
|
||||||
|
CHECK(json::from_bjdata(out_single_underflow) == j_single_underflow);
|
||||||
|
|
||||||
// in-range boundary values still use the compact ndarray encoding
|
// in-range boundary values still use the compact ndarray encoding
|
||||||
json const j_uint8_ok = json({{"_ArrayType_", "uint8"}, {"_ArraySize_", {2, 1}}, {"_ArrayData_", {0, 255}}});
|
json const j_uint8_ok = json({{"_ArrayType_", "uint8"}, {"_ArraySize_", {2, 1}}, {"_ArrayData_", {0, 255}}});
|
||||||
CHECK(json::to_bjdata(j_uint8_ok) == std::vector<uint8_t>({'[', '$', 'U', '#', '[', 'i', 2, 'i', 1, ']', 0, 255}));
|
CHECK(json::to_bjdata(j_uint8_ok) == std::vector<uint8_t>({'[', '$', 'U', '#', '[', 'i', 2, 'i', 1, ']', 0, 255}));
|
||||||
@@ -2821,6 +2842,23 @@ TEST_CASE("BJData")
|
|||||||
CHECK(json::from_bjdata(out_single_ok) == json({{"_ArrayType_", "single"}, {"_ArraySize_", {2, 1}}, {"_ArrayData_", {1.5f, -1.5f}}}));
|
CHECK(json::from_bjdata(out_single_ok) == json({{"_ArrayType_", "single"}, {"_ArraySize_", {2, 1}}, {"_ArrayData_", {1.5f, -1.5f}}}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SECTION("ndarray annotation keys are read back in the documented order")
|
||||||
|
{
|
||||||
|
// from_bjdata() must emit the annotation object's keys in the order
|
||||||
|
// used throughout the documentation, _ArrayType_, _ArraySize_,
|
||||||
|
// _ArrayData_: the type marker precedes the dimension vector on the
|
||||||
|
// wire (see get_ubjson_size_type()), so it is known, and emitted,
|
||||||
|
// before _ArraySize_. For a plain json this key order is invisible
|
||||||
|
// (its comparison ignores it), but for an ordered_json it is not (see
|
||||||
|
// GitHub issue #5661).
|
||||||
|
const ordered_json o = ordered_json::parse(R"({"_ArrayType_":"uint8","_ArraySize_":[2,2],"_ArrayData_":[1,2,3,4]})");
|
||||||
|
const auto packed = ordered_json::to_bjdata(o);
|
||||||
|
CHECK(packed.at(0) == '[');
|
||||||
|
const ordered_json o_back = ordered_json::from_bjdata(packed);
|
||||||
|
CHECK(o_back == o);
|
||||||
|
CHECK(o_back.dump() == o.dump());
|
||||||
|
}
|
||||||
|
|
||||||
SECTION("ndarray that would not be read back as an annotated object stays as object")
|
SECTION("ndarray that would not be read back as an annotated object stays as object")
|
||||||
{
|
{
|
||||||
// the reader only restores an annotated object from an ND-array
|
// the reader only restores an annotated object from an ND-array
|
||||||
|
|||||||
@@ -808,15 +808,6 @@ TEST_CASE("regression tests 2")
|
|||||||
CHECK(j == k);
|
CHECK(j == k);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("issue #4552 - UTF-8 invalid characters are not always ignored when dumping with error_handler_t::ignore")
|
|
||||||
{
|
|
||||||
json node;
|
|
||||||
node["test"] = "test\334\005";
|
|
||||||
CHECK(node.dump(-1, ' ', false, json::error_handler_t::ignore) == "{\"test\":\"test\\u0005\"}");
|
|
||||||
CHECK(node.dump(-1, ' ', false, json::error_handler_t::keep) == "{\"test\":\"test\334\\u0005\"}");
|
|
||||||
CHECK(node.dump(-1, ' ', true, json::error_handler_t::keep) == "{\"test\":\"test\334\\u0005\"}");
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef JSON_HAS_CPP_17
|
#ifdef JSON_HAS_CPP_17
|
||||||
SECTION("issue #5066 - MSVC converts json to std::variant<json> via the conversion operator")
|
SECTION("issue #5066 - MSVC converts json to std::variant<json> via the conversion operator")
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -102,8 +102,6 @@ TEST_CASE("serialization")
|
|||||||
CHECK(j.dump(-1, ' ', false, json::error_handler_t::ignore) == "\"äü\"");
|
CHECK(j.dump(-1, ' ', false, json::error_handler_t::ignore) == "\"äü\"");
|
||||||
CHECK(j.dump(-1, ' ', false, json::error_handler_t::replace) == "\"ä\xEF\xBF\xBDü\"");
|
CHECK(j.dump(-1, ' ', false, json::error_handler_t::replace) == "\"ä\xEF\xBF\xBDü\"");
|
||||||
CHECK(j.dump(-1, ' ', true, json::error_handler_t::replace) == "\"\\u00e4\\ufffd\\u00fc\"");
|
CHECK(j.dump(-1, ' ', true, json::error_handler_t::replace) == "\"\\u00e4\\ufffd\\u00fc\"");
|
||||||
CHECK(j.dump(-1, ' ', false, json::error_handler_t::keep) == "\"ä\xA9ü\"");
|
|
||||||
CHECK(j.dump(-1, ' ', true, json::error_handler_t::keep) == "\"\\u00e4\xA9\\u00fc\"");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("invalid character (regression guard for shared UTF-8 decoder, see #5529)")
|
SECTION("invalid character (regression guard for shared UTF-8 decoder, see #5529)")
|
||||||
@@ -126,8 +124,6 @@ TEST_CASE("serialization")
|
|||||||
CHECK(j.dump(-1, ' ', false, json::error_handler_t::ignore) == "\"123\"");
|
CHECK(j.dump(-1, ' ', false, json::error_handler_t::ignore) == "\"123\"");
|
||||||
CHECK(j.dump(-1, ' ', false, json::error_handler_t::replace) == "\"123\xEF\xBF\xBD\"");
|
CHECK(j.dump(-1, ' ', false, json::error_handler_t::replace) == "\"123\xEF\xBF\xBD\"");
|
||||||
CHECK(j.dump(-1, ' ', true, json::error_handler_t::replace) == "\"123\\ufffd\"");
|
CHECK(j.dump(-1, ' ', true, json::error_handler_t::replace) == "\"123\\ufffd\"");
|
||||||
CHECK(j.dump(-1, ' ', false, json::error_handler_t::keep) == "\"123\xC2\"");
|
|
||||||
CHECK(j.dump(-1, ' ', true, json::error_handler_t::keep) == "\"123\xC2\"");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("unexpected character")
|
SECTION("unexpected character")
|
||||||
@@ -140,39 +136,6 @@ TEST_CASE("serialization")
|
|||||||
CHECK(j.dump(-1, ' ', false, json::error_handler_t::ignore) == "\"123456\"");
|
CHECK(j.dump(-1, ' ', false, json::error_handler_t::ignore) == "\"123456\"");
|
||||||
CHECK(j.dump(-1, ' ', false, json::error_handler_t::replace) == "\"123\xEF\xBF\xBD\x34\x35\x36\"");
|
CHECK(j.dump(-1, ' ', false, json::error_handler_t::replace) == "\"123\xEF\xBF\xBD\x34\x35\x36\"");
|
||||||
CHECK(j.dump(-1, ' ', true, json::error_handler_t::replace) == "\"123\\ufffd456\"");
|
CHECK(j.dump(-1, ' ', true, json::error_handler_t::replace) == "\"123\\ufffd456\"");
|
||||||
CHECK(j.dump(-1, ' ', false, json::error_handler_t::keep) == "\"123\xF1\xB0\x34\x35\x36\"");
|
|
||||||
CHECK(j.dump(-1, ' ', true, json::error_handler_t::keep) == "\"123\xF1\xB0\x34\x35\x36\"");
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("keep: valid characters are still escaped")
|
|
||||||
{
|
|
||||||
// an invalid byte followed by characters that must be escaped
|
|
||||||
const json j = "\xC2\"\\\n\xFF\x05";
|
|
||||||
CHECK(j.dump(-1, ' ', false, json::error_handler_t::keep) == "\"\xC2\\\"\\\\\\n\xFF\\u0005\"");
|
|
||||||
CHECK(j.dump(-1, ' ', true, json::error_handler_t::keep) == "\"\xC2\\\"\\\\\\n\xFF\\u0005\"");
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("keep: truncated multibyte sequences")
|
|
||||||
{
|
|
||||||
CHECK(json("\xF0\x9F\x98").dump(-1, ' ', false, json::error_handler_t::keep) == "\"\xF0\x9F\x98\"");
|
|
||||||
CHECK(json("\xF0\x9F\x98").dump(-1, ' ', true, json::error_handler_t::keep) == "\"\xF0\x9F\x98\"");
|
|
||||||
CHECK(json("\xF0\x9F\x98" "a").dump(-1, ' ', false, json::error_handler_t::keep) == "\"\xF0\x9F\x98" "a\"");
|
|
||||||
CHECK(json("\xF0\x9F\x98" "a").dump(-1, ' ', true, json::error_handler_t::keep) == "\"\xF0\x9F\x98" "a\"");
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("keep: long string with many invalid bytes")
|
|
||||||
{
|
|
||||||
// exceeds the internal string buffer several times
|
|
||||||
std::string input;
|
|
||||||
std::string expected = "\"";
|
|
||||||
for (int i = 0; i < 2000; ++i)
|
|
||||||
{
|
|
||||||
input += "\xFF\xE2\x82\n\xC3\xA4";
|
|
||||||
expected += "\xFF\xE2\x82\\n\xC3\xA4";
|
|
||||||
}
|
|
||||||
expected += "\"";
|
|
||||||
const json j = input;
|
|
||||||
CHECK(j.dump(-1, ' ', false, json::error_handler_t::keep) == expected);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("U+FFFD Substitution of Maximal Subparts")
|
SECTION("U+FFFD Substitution of Maximal Subparts")
|
||||||
|
|||||||
@@ -14,7 +14,6 @@
|
|||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
using nlohmann::json;
|
using nlohmann::json;
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@@ -76,11 +75,8 @@ void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3
|
|||||||
static std::string s_replaced2;
|
static std::string s_replaced2;
|
||||||
static std::string s_replaced_ascii;
|
static std::string s_replaced_ascii;
|
||||||
static std::string s_replaced2_ascii;
|
static std::string s_replaced2_ascii;
|
||||||
static std::string s_kept;
|
|
||||||
static std::string s_kept2;
|
|
||||||
static std::string s_kept_ascii;
|
|
||||||
|
|
||||||
// dumping with ignore/replace/keep must not throw in any case
|
// dumping with ignore/replace must not throw in any case
|
||||||
s_ignored = j.dump(-1, ' ', false, json::error_handler_t::ignore);
|
s_ignored = j.dump(-1, ' ', false, json::error_handler_t::ignore);
|
||||||
s_ignored2 = j2.dump(-1, ' ', false, json::error_handler_t::ignore);
|
s_ignored2 = j2.dump(-1, ' ', false, json::error_handler_t::ignore);
|
||||||
s_ignored_ascii = j.dump(-1, ' ', true, json::error_handler_t::ignore);
|
s_ignored_ascii = j.dump(-1, ' ', true, json::error_handler_t::ignore);
|
||||||
@@ -89,9 +85,6 @@ void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3
|
|||||||
s_replaced2 = j2.dump(-1, ' ', false, json::error_handler_t::replace);
|
s_replaced2 = j2.dump(-1, ' ', false, json::error_handler_t::replace);
|
||||||
s_replaced_ascii = j.dump(-1, ' ', true, json::error_handler_t::replace);
|
s_replaced_ascii = j.dump(-1, ' ', true, json::error_handler_t::replace);
|
||||||
s_replaced2_ascii = j2.dump(-1, ' ', true, json::error_handler_t::replace);
|
s_replaced2_ascii = j2.dump(-1, ' ', true, json::error_handler_t::replace);
|
||||||
s_kept = j.dump(-1, ' ', false, json::error_handler_t::keep);
|
|
||||||
s_kept2 = j2.dump(-1, ' ', false, json::error_handler_t::keep);
|
|
||||||
s_kept_ascii = j.dump(-1, ' ', true, json::error_handler_t::keep);
|
|
||||||
|
|
||||||
if (success_expected)
|
if (success_expected)
|
||||||
{
|
{
|
||||||
@@ -101,7 +94,6 @@ void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3
|
|||||||
// all dumps should agree on the string
|
// all dumps should agree on the string
|
||||||
CHECK(s_strict == s_ignored);
|
CHECK(s_strict == s_ignored);
|
||||||
CHECK(s_strict == s_replaced);
|
CHECK(s_strict == s_replaced);
|
||||||
CHECK(s_strict == s_kept);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -113,20 +105,6 @@ void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3
|
|||||||
|
|
||||||
// check that replace string contains a replacement character
|
// check that replace string contains a replacement character
|
||||||
CHECK(s_replaced.find("\xEF\xBF\xBD") != std::string::npos);
|
CHECK(s_replaced.find("\xEF\xBF\xBD") != std::string::npos);
|
||||||
|
|
||||||
// ignore drops the invalid bytes, keep copies them
|
|
||||||
CHECK(s_ignored != s_kept);
|
|
||||||
CHECK(s_ignored_ascii != s_kept_ascii);
|
|
||||||
|
|
||||||
// unless a byte needs escaping, keep copies the input unchanged
|
|
||||||
const bool needs_escaping = std::any_of(json_string.begin(), json_string.end(), [](char c)
|
|
||||||
{
|
|
||||||
return static_cast<unsigned char>(c) < 0x20 || c == '"' || c == '\\';
|
|
||||||
});
|
|
||||||
if (!needs_escaping)
|
|
||||||
{
|
|
||||||
CHECK(s_kept == "\"" + json_string + "\"");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// check that prefix and suffix are preserved
|
// check that prefix and suffix are preserved
|
||||||
@@ -138,8 +116,6 @@ void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3
|
|||||||
CHECK(s_replaced2.substr(s_replaced2.size() - 4, 3) == "xyz");
|
CHECK(s_replaced2.substr(s_replaced2.size() - 4, 3) == "xyz");
|
||||||
CHECK(s_replaced2_ascii.substr(1, 3) == "abc");
|
CHECK(s_replaced2_ascii.substr(1, 3) == "abc");
|
||||||
CHECK(s_replaced2_ascii.substr(s_replaced2_ascii.size() - 4, 3) == "xyz");
|
CHECK(s_replaced2_ascii.substr(s_replaced2_ascii.size() - 4, 3) == "xyz");
|
||||||
CHECK(s_kept2.substr(1, 3) == "abc");
|
|
||||||
CHECK(s_kept2.substr(s_kept2.size() - 4, 3) == "xyz");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void check_utf8string(bool success_expected, int byte1, int byte2, int byte3, int byte4);
|
void check_utf8string(bool success_expected, int byte1, int byte2, int byte3, int byte4);
|
||||||
|
|||||||
@@ -14,7 +14,6 @@
|
|||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
using nlohmann::json;
|
using nlohmann::json;
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@@ -76,11 +75,8 @@ void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3
|
|||||||
static std::string s_replaced2;
|
static std::string s_replaced2;
|
||||||
static std::string s_replaced_ascii;
|
static std::string s_replaced_ascii;
|
||||||
static std::string s_replaced2_ascii;
|
static std::string s_replaced2_ascii;
|
||||||
static std::string s_kept;
|
|
||||||
static std::string s_kept2;
|
|
||||||
static std::string s_kept_ascii;
|
|
||||||
|
|
||||||
// dumping with ignore/replace/keep must not throw in any case
|
// dumping with ignore/replace must not throw in any case
|
||||||
s_ignored = j.dump(-1, ' ', false, json::error_handler_t::ignore);
|
s_ignored = j.dump(-1, ' ', false, json::error_handler_t::ignore);
|
||||||
s_ignored2 = j2.dump(-1, ' ', false, json::error_handler_t::ignore);
|
s_ignored2 = j2.dump(-1, ' ', false, json::error_handler_t::ignore);
|
||||||
s_ignored_ascii = j.dump(-1, ' ', true, json::error_handler_t::ignore);
|
s_ignored_ascii = j.dump(-1, ' ', true, json::error_handler_t::ignore);
|
||||||
@@ -89,9 +85,6 @@ void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3
|
|||||||
s_replaced2 = j2.dump(-1, ' ', false, json::error_handler_t::replace);
|
s_replaced2 = j2.dump(-1, ' ', false, json::error_handler_t::replace);
|
||||||
s_replaced_ascii = j.dump(-1, ' ', true, json::error_handler_t::replace);
|
s_replaced_ascii = j.dump(-1, ' ', true, json::error_handler_t::replace);
|
||||||
s_replaced2_ascii = j2.dump(-1, ' ', true, json::error_handler_t::replace);
|
s_replaced2_ascii = j2.dump(-1, ' ', true, json::error_handler_t::replace);
|
||||||
s_kept = j.dump(-1, ' ', false, json::error_handler_t::keep);
|
|
||||||
s_kept2 = j2.dump(-1, ' ', false, json::error_handler_t::keep);
|
|
||||||
s_kept_ascii = j.dump(-1, ' ', true, json::error_handler_t::keep);
|
|
||||||
|
|
||||||
if (success_expected)
|
if (success_expected)
|
||||||
{
|
{
|
||||||
@@ -101,7 +94,6 @@ void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3
|
|||||||
// all dumps should agree on the string
|
// all dumps should agree on the string
|
||||||
CHECK(s_strict == s_ignored);
|
CHECK(s_strict == s_ignored);
|
||||||
CHECK(s_strict == s_replaced);
|
CHECK(s_strict == s_replaced);
|
||||||
CHECK(s_strict == s_kept);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -113,20 +105,6 @@ void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3
|
|||||||
|
|
||||||
// check that replace string contains a replacement character
|
// check that replace string contains a replacement character
|
||||||
CHECK(s_replaced.find("\xEF\xBF\xBD") != std::string::npos);
|
CHECK(s_replaced.find("\xEF\xBF\xBD") != std::string::npos);
|
||||||
|
|
||||||
// ignore drops the invalid bytes, keep copies them
|
|
||||||
CHECK(s_ignored != s_kept);
|
|
||||||
CHECK(s_ignored_ascii != s_kept_ascii);
|
|
||||||
|
|
||||||
// unless a byte needs escaping, keep copies the input unchanged
|
|
||||||
const bool needs_escaping = std::any_of(json_string.begin(), json_string.end(), [](char c)
|
|
||||||
{
|
|
||||||
return static_cast<unsigned char>(c) < 0x20 || c == '"' || c == '\\';
|
|
||||||
});
|
|
||||||
if (!needs_escaping)
|
|
||||||
{
|
|
||||||
CHECK(s_kept == "\"" + json_string + "\"");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// check that prefix and suffix are preserved
|
// check that prefix and suffix are preserved
|
||||||
@@ -138,8 +116,6 @@ void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3
|
|||||||
CHECK(s_replaced2.substr(s_replaced2.size() - 4, 3) == "xyz");
|
CHECK(s_replaced2.substr(s_replaced2.size() - 4, 3) == "xyz");
|
||||||
CHECK(s_replaced2_ascii.substr(1, 3) == "abc");
|
CHECK(s_replaced2_ascii.substr(1, 3) == "abc");
|
||||||
CHECK(s_replaced2_ascii.substr(s_replaced2_ascii.size() - 4, 3) == "xyz");
|
CHECK(s_replaced2_ascii.substr(s_replaced2_ascii.size() - 4, 3) == "xyz");
|
||||||
CHECK(s_kept2.substr(1, 3) == "abc");
|
|
||||||
CHECK(s_kept2.substr(s_kept2.size() - 4, 3) == "xyz");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void check_utf8string(bool success_expected, int byte1, int byte2, int byte3, int byte4);
|
void check_utf8string(bool success_expected, int byte1, int byte2, int byte3, int byte4);
|
||||||
|
|||||||
@@ -14,7 +14,6 @@
|
|||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
using nlohmann::json;
|
using nlohmann::json;
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@@ -76,11 +75,8 @@ void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3
|
|||||||
static std::string s_replaced2;
|
static std::string s_replaced2;
|
||||||
static std::string s_replaced_ascii;
|
static std::string s_replaced_ascii;
|
||||||
static std::string s_replaced2_ascii;
|
static std::string s_replaced2_ascii;
|
||||||
static std::string s_kept;
|
|
||||||
static std::string s_kept2;
|
|
||||||
static std::string s_kept_ascii;
|
|
||||||
|
|
||||||
// dumping with ignore/replace/keep must not throw in any case
|
// dumping with ignore/replace must not throw in any case
|
||||||
s_ignored = j.dump(-1, ' ', false, json::error_handler_t::ignore);
|
s_ignored = j.dump(-1, ' ', false, json::error_handler_t::ignore);
|
||||||
s_ignored2 = j2.dump(-1, ' ', false, json::error_handler_t::ignore);
|
s_ignored2 = j2.dump(-1, ' ', false, json::error_handler_t::ignore);
|
||||||
s_ignored_ascii = j.dump(-1, ' ', true, json::error_handler_t::ignore);
|
s_ignored_ascii = j.dump(-1, ' ', true, json::error_handler_t::ignore);
|
||||||
@@ -89,9 +85,6 @@ void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3
|
|||||||
s_replaced2 = j2.dump(-1, ' ', false, json::error_handler_t::replace);
|
s_replaced2 = j2.dump(-1, ' ', false, json::error_handler_t::replace);
|
||||||
s_replaced_ascii = j.dump(-1, ' ', true, json::error_handler_t::replace);
|
s_replaced_ascii = j.dump(-1, ' ', true, json::error_handler_t::replace);
|
||||||
s_replaced2_ascii = j2.dump(-1, ' ', true, json::error_handler_t::replace);
|
s_replaced2_ascii = j2.dump(-1, ' ', true, json::error_handler_t::replace);
|
||||||
s_kept = j.dump(-1, ' ', false, json::error_handler_t::keep);
|
|
||||||
s_kept2 = j2.dump(-1, ' ', false, json::error_handler_t::keep);
|
|
||||||
s_kept_ascii = j.dump(-1, ' ', true, json::error_handler_t::keep);
|
|
||||||
|
|
||||||
if (success_expected)
|
if (success_expected)
|
||||||
{
|
{
|
||||||
@@ -101,7 +94,6 @@ void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3
|
|||||||
// all dumps should agree on the string
|
// all dumps should agree on the string
|
||||||
CHECK(s_strict == s_ignored);
|
CHECK(s_strict == s_ignored);
|
||||||
CHECK(s_strict == s_replaced);
|
CHECK(s_strict == s_replaced);
|
||||||
CHECK(s_strict == s_kept);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -113,20 +105,6 @@ void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3
|
|||||||
|
|
||||||
// check that replace string contains a replacement character
|
// check that replace string contains a replacement character
|
||||||
CHECK(s_replaced.find("\xEF\xBF\xBD") != std::string::npos);
|
CHECK(s_replaced.find("\xEF\xBF\xBD") != std::string::npos);
|
||||||
|
|
||||||
// ignore drops the invalid bytes, keep copies them
|
|
||||||
CHECK(s_ignored != s_kept);
|
|
||||||
CHECK(s_ignored_ascii != s_kept_ascii);
|
|
||||||
|
|
||||||
// unless a byte needs escaping, keep copies the input unchanged
|
|
||||||
const bool needs_escaping = std::any_of(json_string.begin(), json_string.end(), [](char c)
|
|
||||||
{
|
|
||||||
return static_cast<unsigned char>(c) < 0x20 || c == '"' || c == '\\';
|
|
||||||
});
|
|
||||||
if (!needs_escaping)
|
|
||||||
{
|
|
||||||
CHECK(s_kept == "\"" + json_string + "\"");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// check that prefix and suffix are preserved
|
// check that prefix and suffix are preserved
|
||||||
@@ -138,8 +116,6 @@ void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3
|
|||||||
CHECK(s_replaced2.substr(s_replaced2.size() - 4, 3) == "xyz");
|
CHECK(s_replaced2.substr(s_replaced2.size() - 4, 3) == "xyz");
|
||||||
CHECK(s_replaced2_ascii.substr(1, 3) == "abc");
|
CHECK(s_replaced2_ascii.substr(1, 3) == "abc");
|
||||||
CHECK(s_replaced2_ascii.substr(s_replaced2_ascii.size() - 4, 3) == "xyz");
|
CHECK(s_replaced2_ascii.substr(s_replaced2_ascii.size() - 4, 3) == "xyz");
|
||||||
CHECK(s_kept2.substr(1, 3) == "abc");
|
|
||||||
CHECK(s_kept2.substr(s_kept2.size() - 4, 3) == "xyz");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void check_utf8string(bool success_expected, int byte1, int byte2, int byte3, int byte4);
|
void check_utf8string(bool success_expected, int byte1, int byte2, int byte3, int byte4);
|
||||||
|
|||||||
@@ -14,7 +14,6 @@
|
|||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
using nlohmann::json;
|
using nlohmann::json;
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@@ -76,11 +75,8 @@ void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3
|
|||||||
static std::string s_replaced2;
|
static std::string s_replaced2;
|
||||||
static std::string s_replaced_ascii;
|
static std::string s_replaced_ascii;
|
||||||
static std::string s_replaced2_ascii;
|
static std::string s_replaced2_ascii;
|
||||||
static std::string s_kept;
|
|
||||||
static std::string s_kept2;
|
|
||||||
static std::string s_kept_ascii;
|
|
||||||
|
|
||||||
// dumping with ignore/replace/keep must not throw in any case
|
// dumping with ignore/replace must not throw in any case
|
||||||
s_ignored = j.dump(-1, ' ', false, json::error_handler_t::ignore);
|
s_ignored = j.dump(-1, ' ', false, json::error_handler_t::ignore);
|
||||||
s_ignored2 = j2.dump(-1, ' ', false, json::error_handler_t::ignore);
|
s_ignored2 = j2.dump(-1, ' ', false, json::error_handler_t::ignore);
|
||||||
s_ignored_ascii = j.dump(-1, ' ', true, json::error_handler_t::ignore);
|
s_ignored_ascii = j.dump(-1, ' ', true, json::error_handler_t::ignore);
|
||||||
@@ -89,9 +85,6 @@ void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3
|
|||||||
s_replaced2 = j2.dump(-1, ' ', false, json::error_handler_t::replace);
|
s_replaced2 = j2.dump(-1, ' ', false, json::error_handler_t::replace);
|
||||||
s_replaced_ascii = j.dump(-1, ' ', true, json::error_handler_t::replace);
|
s_replaced_ascii = j.dump(-1, ' ', true, json::error_handler_t::replace);
|
||||||
s_replaced2_ascii = j2.dump(-1, ' ', true, json::error_handler_t::replace);
|
s_replaced2_ascii = j2.dump(-1, ' ', true, json::error_handler_t::replace);
|
||||||
s_kept = j.dump(-1, ' ', false, json::error_handler_t::keep);
|
|
||||||
s_kept2 = j2.dump(-1, ' ', false, json::error_handler_t::keep);
|
|
||||||
s_kept_ascii = j.dump(-1, ' ', true, json::error_handler_t::keep);
|
|
||||||
|
|
||||||
if (success_expected)
|
if (success_expected)
|
||||||
{
|
{
|
||||||
@@ -101,7 +94,6 @@ void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3
|
|||||||
// all dumps should agree on the string
|
// all dumps should agree on the string
|
||||||
CHECK(s_strict == s_ignored);
|
CHECK(s_strict == s_ignored);
|
||||||
CHECK(s_strict == s_replaced);
|
CHECK(s_strict == s_replaced);
|
||||||
CHECK(s_strict == s_kept);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -113,20 +105,6 @@ void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3
|
|||||||
|
|
||||||
// check that replace string contains a replacement character
|
// check that replace string contains a replacement character
|
||||||
CHECK(s_replaced.find("\xEF\xBF\xBD") != std::string::npos);
|
CHECK(s_replaced.find("\xEF\xBF\xBD") != std::string::npos);
|
||||||
|
|
||||||
// ignore drops the invalid bytes, keep copies them
|
|
||||||
CHECK(s_ignored != s_kept);
|
|
||||||
CHECK(s_ignored_ascii != s_kept_ascii);
|
|
||||||
|
|
||||||
// unless a byte needs escaping, keep copies the input unchanged
|
|
||||||
const bool needs_escaping = std::any_of(json_string.begin(), json_string.end(), [](char c)
|
|
||||||
{
|
|
||||||
return static_cast<unsigned char>(c) < 0x20 || c == '"' || c == '\\';
|
|
||||||
});
|
|
||||||
if (!needs_escaping)
|
|
||||||
{
|
|
||||||
CHECK(s_kept == "\"" + json_string + "\"");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// check that prefix and suffix are preserved
|
// check that prefix and suffix are preserved
|
||||||
@@ -138,8 +116,6 @@ void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3
|
|||||||
CHECK(s_replaced2.substr(s_replaced2.size() - 4, 3) == "xyz");
|
CHECK(s_replaced2.substr(s_replaced2.size() - 4, 3) == "xyz");
|
||||||
CHECK(s_replaced2_ascii.substr(1, 3) == "abc");
|
CHECK(s_replaced2_ascii.substr(1, 3) == "abc");
|
||||||
CHECK(s_replaced2_ascii.substr(s_replaced2_ascii.size() - 4, 3) == "xyz");
|
CHECK(s_replaced2_ascii.substr(s_replaced2_ascii.size() - 4, 3) == "xyz");
|
||||||
CHECK(s_kept2.substr(1, 3) == "abc");
|
|
||||||
CHECK(s_kept2.substr(s_kept2.size() - 4, 3) == "xyz");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void check_utf8string(bool success_expected, int byte1, int byte2, int byte3, int byte4);
|
void check_utf8string(bool success_expected, int byte1, int byte2, int byte3, int byte4);
|
||||||
|
|||||||
Reference in New Issue
Block a user