support UBJSON-derived Binary JData (BJData) format
This commit is contained in:
parent
1a90c9463a
commit
3c820dd215
@ -95,12 +95,13 @@ class binary_reader
|
|||||||
@return whether parsing was successful
|
@return whether parsing was successful
|
||||||
*/
|
*/
|
||||||
JSON_HEDLEY_NON_NULL(3)
|
JSON_HEDLEY_NON_NULL(3)
|
||||||
bool sax_parse(const input_format_t format,
|
bool sax_parse(const input_format_t format_,
|
||||||
json_sax_t* sax_,
|
json_sax_t* sax_,
|
||||||
const bool strict = true,
|
const bool strict = true,
|
||||||
const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
|
const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
|
||||||
{
|
{
|
||||||
sax = sax_;
|
sax = sax_;
|
||||||
|
format = format_;
|
||||||
bool result = false;
|
bool result = false;
|
||||||
|
|
||||||
switch (format)
|
switch (format)
|
||||||
@ -118,6 +119,7 @@ class binary_reader
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case input_format_t::ubjson:
|
case input_format_t::ubjson:
|
||||||
|
case input_format_t::bjdata:
|
||||||
result = parse_ubjson_internal();
|
result = parse_ubjson_internal();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -129,7 +131,7 @@ class binary_reader
|
|||||||
// strict mode: next byte must be EOF
|
// strict mode: next byte must be EOF
|
||||||
if (result && strict)
|
if (result && strict)
|
||||||
{
|
{
|
||||||
if (format == input_format_t::ubjson)
|
if (format == input_format_t::ubjson || format == input_format_t::bjdata)
|
||||||
{
|
{
|
||||||
get_ignore_noop();
|
get_ignore_noop();
|
||||||
}
|
}
|
||||||
@ -1844,7 +1846,7 @@ class binary_reader
|
|||||||
get(); // TODO(niels): may we ignore N here?
|
get(); // TODO(niels): may we ignore N here?
|
||||||
}
|
}
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "value")))
|
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "value")))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1854,52 +1856,141 @@ class binary_reader
|
|||||||
case 'U':
|
case 'U':
|
||||||
{
|
{
|
||||||
std::uint8_t len{};
|
std::uint8_t len{};
|
||||||
return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result);
|
return get_number(format, len) && get_string(format, len, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'i':
|
case 'i':
|
||||||
{
|
{
|
||||||
std::int8_t len{};
|
std::int8_t len{};
|
||||||
return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result);
|
return get_number(format, len) && get_string(format, len, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'I':
|
case 'I':
|
||||||
{
|
{
|
||||||
std::int16_t len{};
|
std::int16_t len{};
|
||||||
return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result);
|
return get_number(format, len) && get_string(format, len, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'l':
|
case 'l':
|
||||||
{
|
{
|
||||||
std::int32_t len{};
|
std::int32_t len{};
|
||||||
return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result);
|
return get_number(format, len) && get_string(format, len, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'L':
|
case 'L':
|
||||||
{
|
{
|
||||||
std::int64_t len{};
|
std::int64_t len{};
|
||||||
return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result);
|
return get_number(format, len) && get_string(format, len, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
if (format == input_format_t::bjdata)
|
||||||
|
{
|
||||||
|
switch (current)
|
||||||
|
{
|
||||||
|
case 'u':
|
||||||
|
{
|
||||||
|
uint16_t len;
|
||||||
|
return get_number(format, len) and get_string(format, len, result);
|
||||||
|
}
|
||||||
|
case 'm':
|
||||||
|
{
|
||||||
|
uint32_t len;
|
||||||
|
return get_number(format, len) and get_string(format, len, result);
|
||||||
|
}
|
||||||
|
case 'M':
|
||||||
|
{
|
||||||
|
uint64_t len;
|
||||||
|
return get_number(format, len) and get_string(format, len, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
auto last_token = get_token_string();
|
auto last_token = get_token_string();
|
||||||
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
|
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
|
||||||
exception_message(input_format_t::ubjson, concat("expected length type specification (U, i, I, l, L); last byte: 0x", last_token), "string"), nullptr));
|
exception_message(format, concat("expected length type specification (U, i, I, l, L); last byte: 0x", last_token), "string"), nullptr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@param[out] dim an integer vector storing the ND array dimensions
|
||||||
|
@return whether array creation completed
|
||||||
|
*/
|
||||||
|
bool get_ubjson_ndarray_size(std::vector<size_t>& dim)
|
||||||
|
{
|
||||||
|
std::pair<std::size_t, char_int_type> size_and_type;
|
||||||
|
size_t dimlen;
|
||||||
|
|
||||||
|
bool is_optimized = get_ubjson_size_type(size_and_type);
|
||||||
|
|
||||||
|
if (size_and_type.first != string_t::npos)
|
||||||
|
{
|
||||||
|
if (size_and_type.second != 0)
|
||||||
|
{
|
||||||
|
if (size_and_type.second != 'N')
|
||||||
|
{
|
||||||
|
for (std::size_t i = 0; i < size_and_type.first; ++i)
|
||||||
|
{
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(not get_ubjson_size_value(dimlen, size_and_type.second)))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dim.push_back(dimlen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (std::size_t i = 0; i < size_and_type.first; ++i)
|
||||||
|
{
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(not get_ubjson_size_value(dimlen)))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dim.push_back(dimlen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
while (current != ']')
|
||||||
|
{
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(not get_ubjson_size_value(dimlen)))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dim.push_back(dimlen);
|
||||||
|
}
|
||||||
|
get_ignore_noop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@param[out] result determined size
|
@param[out] result determined size
|
||||||
@return whether size determination completed
|
@return whether size determination completed
|
||||||
*/
|
*/
|
||||||
bool get_ubjson_size_value(std::size_t& result)
|
bool get_ubjson_size_value(std::size_t& result, int prefix = 0)
|
||||||
{
|
{
|
||||||
switch (get_ignore_noop())
|
if (prefix == 0)
|
||||||
|
{
|
||||||
|
prefix = get_ignore_noop();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (prefix)
|
||||||
{
|
{
|
||||||
case 'U':
|
case 'U':
|
||||||
{
|
{
|
||||||
std::uint8_t number{};
|
std::uint8_t number{};
|
||||||
if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number)))
|
if (JSON_HEDLEY_UNLIKELY(!get_number(format, number)))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1910,7 +2001,7 @@ class binary_reader
|
|||||||
case 'i':
|
case 'i':
|
||||||
{
|
{
|
||||||
std::int8_t number{};
|
std::int8_t number{};
|
||||||
if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number)))
|
if (JSON_HEDLEY_UNLIKELY(!get_number(format, number)))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1921,7 +2012,7 @@ class binary_reader
|
|||||||
case 'I':
|
case 'I':
|
||||||
{
|
{
|
||||||
std::int16_t number{};
|
std::int16_t number{};
|
||||||
if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number)))
|
if (JSON_HEDLEY_UNLIKELY(!get_number(format, number)))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1932,7 +2023,7 @@ class binary_reader
|
|||||||
case 'l':
|
case 'l':
|
||||||
{
|
{
|
||||||
std::int32_t number{};
|
std::int32_t number{};
|
||||||
if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number)))
|
if (JSON_HEDLEY_UNLIKELY(!get_number(format, number)))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1943,7 +2034,7 @@ class binary_reader
|
|||||||
case 'L':
|
case 'L':
|
||||||
{
|
{
|
||||||
std::int64_t number{};
|
std::int64_t number{};
|
||||||
if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number)))
|
if (JSON_HEDLEY_UNLIKELY(!get_number(format, number)))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1953,9 +2044,62 @@ class binary_reader
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
|
if (format == input_format_t::bjdata)
|
||||||
|
{
|
||||||
|
switch (prefix)
|
||||||
|
{
|
||||||
|
case 'u':
|
||||||
|
{
|
||||||
|
uint16_t number;
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(not get_number(format, number)))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
result = static_cast<std::size_t>(number);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 'm':
|
||||||
|
{
|
||||||
|
uint32_t number;
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(not get_number(format, number)))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
result = static_cast<std::size_t>(number);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 'M':
|
||||||
|
{
|
||||||
|
uint64_t number;
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(not get_number(format, number)))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
result = static_cast<std::size_t>(number);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case '[':
|
||||||
|
{
|
||||||
|
std::vector<size_t> dim;
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(not get_ubjson_ndarray_size(dim)))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = 1;
|
||||||
|
for (std::size_t i = 0; i < dim.size(); ++i)
|
||||||
|
{
|
||||||
|
result *= dim.at(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
auto last_token = get_token_string();
|
auto last_token = get_token_string();
|
||||||
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
|
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
|
||||||
exception_message(input_format_t::ubjson, concat("expected length type specification (U, i, I, l, L) after '#'; last byte: 0x", last_token), "size"), nullptr));
|
exception_message(format, concat("expected length type specification (U, i, I, l, L) after '#'; last byte: 0x", last_token), "size"), nullptr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1980,7 +2124,7 @@ class binary_reader
|
|||||||
if (current == '$')
|
if (current == '$')
|
||||||
{
|
{
|
||||||
result.second = get(); // must not ignore 'N', because 'N' maybe the type
|
result.second = get(); // must not ignore 'N', because 'N' maybe the type
|
||||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "type")))
|
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "type")))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1988,13 +2132,13 @@ class binary_reader
|
|||||||
get_ignore_noop();
|
get_ignore_noop();
|
||||||
if (JSON_HEDLEY_UNLIKELY(current != '#'))
|
if (JSON_HEDLEY_UNLIKELY(current != '#'))
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "value")))
|
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "value")))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
auto last_token = get_token_string();
|
auto last_token = get_token_string();
|
||||||
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
|
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
|
||||||
exception_message(input_format_t::ubjson, concat("expected '#' after type information; last byte: 0x", last_token), "size"), nullptr));
|
exception_message(format, concat("expected '#' after type information; last byte: 0x", last_token), "size"), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
return get_ubjson_size_value(result.first);
|
return get_ubjson_size_value(result.first);
|
||||||
@ -2017,7 +2161,7 @@ class binary_reader
|
|||||||
switch (prefix)
|
switch (prefix)
|
||||||
{
|
{
|
||||||
case std::char_traits<char_type>::eof(): // EOF
|
case std::char_traits<char_type>::eof(): // EOF
|
||||||
return unexpect_eof(input_format_t::ubjson, "value");
|
return unexpect_eof(format, "value");
|
||||||
|
|
||||||
case 'T': // true
|
case 'T': // true
|
||||||
return sax->boolean(true);
|
return sax->boolean(true);
|
||||||
@ -2030,43 +2174,43 @@ class binary_reader
|
|||||||
case 'U':
|
case 'U':
|
||||||
{
|
{
|
||||||
std::uint8_t number{};
|
std::uint8_t number{};
|
||||||
return get_number(input_format_t::ubjson, number) && sax->number_unsigned(number);
|
return get_number(format, number) && sax->number_unsigned(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'i':
|
case 'i':
|
||||||
{
|
{
|
||||||
std::int8_t number{};
|
std::int8_t number{};
|
||||||
return get_number(input_format_t::ubjson, number) && sax->number_integer(number);
|
return get_number(format, number) && sax->number_integer(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'I':
|
case 'I':
|
||||||
{
|
{
|
||||||
std::int16_t number{};
|
std::int16_t number{};
|
||||||
return get_number(input_format_t::ubjson, number) && sax->number_integer(number);
|
return get_number(format, number) && sax->number_integer(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'l':
|
case 'l':
|
||||||
{
|
{
|
||||||
std::int32_t number{};
|
std::int32_t number{};
|
||||||
return get_number(input_format_t::ubjson, number) && sax->number_integer(number);
|
return get_number(format, number) && sax->number_integer(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'L':
|
case 'L':
|
||||||
{
|
{
|
||||||
std::int64_t number{};
|
std::int64_t number{};
|
||||||
return get_number(input_format_t::ubjson, number) && sax->number_integer(number);
|
return get_number(format, number) && sax->number_integer(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'd':
|
case 'd':
|
||||||
{
|
{
|
||||||
float number{};
|
float number{};
|
||||||
return get_number(input_format_t::ubjson, number) && sax->number_float(static_cast<number_float_t>(number), "");
|
return get_number(format, number) && sax->number_float(static_cast<number_float_t>(number), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'D':
|
case 'D':
|
||||||
{
|
{
|
||||||
double number{};
|
double number{};
|
||||||
return get_number(input_format_t::ubjson, number) && sax->number_float(static_cast<number_float_t>(number), "");
|
return get_number(format, number) && sax->number_float(static_cast<number_float_t>(number), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'H':
|
case 'H':
|
||||||
@ -2077,7 +2221,7 @@ class binary_reader
|
|||||||
case 'C': // char
|
case 'C': // char
|
||||||
{
|
{
|
||||||
get();
|
get();
|
||||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "char")))
|
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "char")))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -2085,7 +2229,7 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
auto last_token = get_token_string();
|
auto last_token = get_token_string();
|
||||||
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
|
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
|
||||||
exception_message(input_format_t::ubjson, concat("byte after 'C' must be in range 0x00..0x7F; last byte: 0x", last_token), "char"), nullptr));
|
exception_message(format, concat("byte after 'C' must be in range 0x00..0x7F; last byte: 0x", last_token), "char"), nullptr));
|
||||||
}
|
}
|
||||||
string_t s(1, static_cast<typename string_t::value_type>(current));
|
string_t s(1, static_cast<typename string_t::value_type>(current));
|
||||||
return sax->string(s);
|
return sax->string(s);
|
||||||
@ -2105,9 +2249,74 @@ class binary_reader
|
|||||||
|
|
||||||
default: // anything else
|
default: // anything else
|
||||||
{
|
{
|
||||||
|
if (format == input_format_t::bjdata)
|
||||||
|
{
|
||||||
|
switch (prefix)
|
||||||
|
{
|
||||||
|
case 'u':
|
||||||
|
{
|
||||||
|
uint16_t number;
|
||||||
|
return get_number(format, number) and sax->number_integer(number);
|
||||||
|
}
|
||||||
|
case 'm':
|
||||||
|
{
|
||||||
|
uint32_t number;
|
||||||
|
return get_number(format, number) and sax->number_integer(number);
|
||||||
|
}
|
||||||
|
case 'M':
|
||||||
|
{
|
||||||
|
uint64_t number;
|
||||||
|
return get_number(format, number) and sax->number_integer(number);
|
||||||
|
}
|
||||||
|
case 'h':
|
||||||
|
{
|
||||||
|
const int byte2 = get();
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "half")))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const int byte1 = get();
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "half")))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// code from RFC 7049, Appendix D, Figure 3:
|
||||||
|
// As half-precision floating-point numbers were only added
|
||||||
|
// to IEEE 754 in 2008, today's programming platforms often
|
||||||
|
// still only have limited support for them. It is very
|
||||||
|
// easy to include at least decoding support for them even
|
||||||
|
// without such support. An example of a small decoder for
|
||||||
|
// half-precision floating-point numbers in the C language
|
||||||
|
// is shown in Fig. 3.
|
||||||
|
const int half = (byte1 << 8) + byte2;
|
||||||
|
const double val = [&half]
|
||||||
|
{
|
||||||
|
const int exp = (half >> 10) & 0x1F;
|
||||||
|
const int mant = half & 0x3FF;
|
||||||
|
JSON_ASSERT(0 <= exp and exp <= 32);
|
||||||
|
JSON_ASSERT(0 <= mant and mant <= 1024);
|
||||||
|
switch (exp)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
return std::ldexp(mant, -24);
|
||||||
|
case 31:
|
||||||
|
return (mant == 0)
|
||||||
|
? std::numeric_limits<double>::infinity()
|
||||||
|
: std::numeric_limits<double>::quiet_NaN();
|
||||||
|
default:
|
||||||
|
return std::ldexp(mant + 1024, exp - 25);
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
return sax->number_float((half & 0x8000) != 0
|
||||||
|
? static_cast<number_float_t>(-val)
|
||||||
|
: static_cast<number_float_t>(val), "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
auto last_token = get_token_string();
|
auto last_token = get_token_string();
|
||||||
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
|
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
|
||||||
exception_message(input_format_t::ubjson, concat("invalid byte: 0x", last_token), "value"), nullptr));
|
exception_message(format, concat("invalid byte: 0x", last_token), "value"), nullptr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2267,7 +2476,7 @@ class binary_reader
|
|||||||
for (std::size_t i = 0; i < size; ++i)
|
for (std::size_t i = 0; i < size; ++i)
|
||||||
{
|
{
|
||||||
get();
|
get();
|
||||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "number")))
|
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "number")))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -2286,7 +2495,7 @@ class binary_reader
|
|||||||
if (JSON_HEDLEY_UNLIKELY(result_remainder != token_type::end_of_input))
|
if (JSON_HEDLEY_UNLIKELY(result_remainder != token_type::end_of_input))
|
||||||
{
|
{
|
||||||
return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read,
|
return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read,
|
||||||
exception_message(input_format_t::ubjson, concat("invalid number text: ", number_lexer.get_token_string()), "high-precision number"), nullptr));
|
exception_message(format, concat("invalid number text: ", number_lexer.get_token_string()), "high-precision number"), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (result_number)
|
switch (result_number)
|
||||||
@ -2313,7 +2522,7 @@ class binary_reader
|
|||||||
case token_type::literal_or_value:
|
case token_type::literal_or_value:
|
||||||
default:
|
default:
|
||||||
return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read,
|
return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read,
|
||||||
exception_message(input_format_t::ubjson, concat("invalid number text: ", number_lexer.get_token_string()), "high-precision number"), nullptr));
|
exception_message(format, concat("invalid number text: ", number_lexer.get_token_string()), "high-precision number"), nullptr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2377,7 +2586,8 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
|
|
||||||
// reverse byte order prior to conversion if necessary
|
// reverse byte order prior to conversion if necessary
|
||||||
if (is_little_endian != InputIsLittleEndian)
|
if ((is_little_endian != InputIsLittleEndian and format != input_format_t::bjdata) or
|
||||||
|
(is_little_endian == InputIsLittleEndian and format == input_format_t::bjdata))
|
||||||
{
|
{
|
||||||
vec[sizeof(NumberType) - i - 1] = static_cast<std::uint8_t>(current);
|
vec[sizeof(NumberType) - i - 1] = static_cast<std::uint8_t>(current);
|
||||||
}
|
}
|
||||||
@ -2514,6 +2724,10 @@ class binary_reader
|
|||||||
error_msg += "BSON";
|
error_msg += "BSON";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case input_format_t::bjdata:
|
||||||
|
error_msg += "BJData";
|
||||||
|
break;
|
||||||
|
|
||||||
case input_format_t::json: // LCOV_EXCL_LINE
|
case input_format_t::json: // LCOV_EXCL_LINE
|
||||||
default: // LCOV_EXCL_LINE
|
default: // LCOV_EXCL_LINE
|
||||||
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
|
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
|
||||||
@ -2535,6 +2749,9 @@ class binary_reader
|
|||||||
/// whether we can assume little endianness
|
/// whether we can assume little endianness
|
||||||
const bool is_little_endian = little_endianness();
|
const bool is_little_endian = little_endianness();
|
||||||
|
|
||||||
|
/// sax parser format
|
||||||
|
input_format_t format;
|
||||||
|
|
||||||
/// the SAX parser
|
/// the SAX parser
|
||||||
json_sax_t* sax = nullptr;
|
json_sax_t* sax = nullptr;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -23,7 +23,7 @@ namespace nlohmann
|
|||||||
namespace detail
|
namespace detail
|
||||||
{
|
{
|
||||||
/// the supported input formats
|
/// the supported input formats
|
||||||
enum class input_format_t { json, cbor, msgpack, ubjson, bson };
|
enum class input_format_t { json, cbor, msgpack, ubjson, bson, bjdata };
|
||||||
|
|
||||||
////////////////////
|
////////////////////
|
||||||
// input adapters //
|
// input adapters //
|
||||||
|
|||||||
@ -38,9 +38,10 @@ class binary_writer
|
|||||||
|
|
||||||
@param[in] adapter output adapter to write to
|
@param[in] adapter output adapter to write to
|
||||||
*/
|
*/
|
||||||
explicit binary_writer(output_adapter_t<CharType> adapter) : oa(std::move(adapter))
|
explicit binary_writer(output_adapter_t<CharType> adapter, const bool is_bjdata_ = false) : oa(std::move(adapter))
|
||||||
{
|
{
|
||||||
JSON_ASSERT(oa);
|
JSON_ASSERT(oa);
|
||||||
|
is_bjdata = is_bjdata_;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -1334,6 +1335,14 @@ class binary_writer
|
|||||||
}
|
}
|
||||||
write_number(static_cast<std::int16_t>(n));
|
write_number(static_cast<std::int16_t>(n));
|
||||||
}
|
}
|
||||||
|
else if (is_bjdata and n <= static_cast<uint64_t>((std::numeric_limits<uint16_t>::max)()))
|
||||||
|
{
|
||||||
|
if (add_prefix)
|
||||||
|
{
|
||||||
|
oa->write_character(to_char_type('u')); // uint16 - bjdata only
|
||||||
|
}
|
||||||
|
write_number(static_cast<std::uint16_t>(n));
|
||||||
|
}
|
||||||
else if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))
|
else if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))
|
||||||
{
|
{
|
||||||
if (add_prefix)
|
if (add_prefix)
|
||||||
@ -1342,6 +1351,14 @@ class binary_writer
|
|||||||
}
|
}
|
||||||
write_number(static_cast<std::int32_t>(n));
|
write_number(static_cast<std::int32_t>(n));
|
||||||
}
|
}
|
||||||
|
else if (is_bjdata and n <= static_cast<uint64_t>((std::numeric_limits<uint32_t>::max)()))
|
||||||
|
{
|
||||||
|
if (add_prefix)
|
||||||
|
{
|
||||||
|
oa->write_character(to_char_type('m')); // uint32 - bjdata only
|
||||||
|
}
|
||||||
|
write_number(static_cast<std::uint32_t>(n));
|
||||||
|
}
|
||||||
else if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))
|
else if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))
|
||||||
{
|
{
|
||||||
if (add_prefix)
|
if (add_prefix)
|
||||||
@ -1350,6 +1367,14 @@ class binary_writer
|
|||||||
}
|
}
|
||||||
write_number(static_cast<std::int64_t>(n));
|
write_number(static_cast<std::int64_t>(n));
|
||||||
}
|
}
|
||||||
|
else if (is_bjdata and n <= static_cast<uint64_t>((std::numeric_limits<uint64_t>::max)()))
|
||||||
|
{
|
||||||
|
if (add_prefix)
|
||||||
|
{
|
||||||
|
oa->write_character(to_char_type('M')); // uint64 - bjdata only
|
||||||
|
}
|
||||||
|
write_number(static_cast<std::uint64_t>(n));
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (add_prefix)
|
if (add_prefix)
|
||||||
@ -1397,6 +1422,14 @@ class binary_writer
|
|||||||
}
|
}
|
||||||
write_number(static_cast<std::int16_t>(n));
|
write_number(static_cast<std::int16_t>(n));
|
||||||
}
|
}
|
||||||
|
else if (is_bjdata and (static_cast<std::int64_t>((std::numeric_limits<std::uint16_t>::min)()) <= n && n <= static_cast<std::int64_t>((std::numeric_limits<std::uint16_t>::max)())))
|
||||||
|
{
|
||||||
|
if (add_prefix)
|
||||||
|
{
|
||||||
|
oa->write_character(to_char_type('u')); // uint16 - bjdata only
|
||||||
|
}
|
||||||
|
write_number(static_cast<uint16_t>(n));
|
||||||
|
}
|
||||||
else if ((std::numeric_limits<std::int32_t>::min)() <= n && n <= (std::numeric_limits<std::int32_t>::max)())
|
else if ((std::numeric_limits<std::int32_t>::min)() <= n && n <= (std::numeric_limits<std::int32_t>::max)())
|
||||||
{
|
{
|
||||||
if (add_prefix)
|
if (add_prefix)
|
||||||
@ -1405,6 +1438,14 @@ class binary_writer
|
|||||||
}
|
}
|
||||||
write_number(static_cast<std::int32_t>(n));
|
write_number(static_cast<std::int32_t>(n));
|
||||||
}
|
}
|
||||||
|
else if (is_bjdata and (static_cast<std::int64_t>((std::numeric_limits<std::uint32_t>::min)()) <= n && n <= static_cast<std::int64_t>((std::numeric_limits<std::uint32_t>::max)())))
|
||||||
|
{
|
||||||
|
if (add_prefix)
|
||||||
|
{
|
||||||
|
oa->write_character(to_char_type('m')); // uint32 - bjdata only
|
||||||
|
}
|
||||||
|
write_number(static_cast<uint32_t>(n));
|
||||||
|
}
|
||||||
else if ((std::numeric_limits<std::int64_t>::min)() <= n && n <= (std::numeric_limits<std::int64_t>::max)())
|
else if ((std::numeric_limits<std::int64_t>::min)() <= n && n <= (std::numeric_limits<std::int64_t>::max)())
|
||||||
{
|
{
|
||||||
if (add_prefix)
|
if (add_prefix)
|
||||||
@ -1413,6 +1454,14 @@ class binary_writer
|
|||||||
}
|
}
|
||||||
write_number(static_cast<std::int64_t>(n));
|
write_number(static_cast<std::int64_t>(n));
|
||||||
}
|
}
|
||||||
|
else if (is_bjdata and (static_cast<std::uint64_t>((std::numeric_limits<std::uint64_t>::min)()) <= n && n <= static_cast<std::uint64_t>((std::numeric_limits<std::uint64_t>::max)())))
|
||||||
|
{
|
||||||
|
if (add_prefix)
|
||||||
|
{
|
||||||
|
oa->write_character(to_char_type('M')); // uint64 - bjdata only
|
||||||
|
}
|
||||||
|
write_number(static_cast<std::uint64_t>(n));
|
||||||
|
}
|
||||||
// LCOV_EXCL_START
|
// LCOV_EXCL_START
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -1458,14 +1507,26 @@ class binary_writer
|
|||||||
{
|
{
|
||||||
return 'I';
|
return 'I';
|
||||||
}
|
}
|
||||||
|
if (is_bjdata and ((std::numeric_limits<std::uint16_t>::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits<std::uint16_t>::max)()))
|
||||||
|
{
|
||||||
|
return 'u';
|
||||||
|
}
|
||||||
if ((std::numeric_limits<std::int32_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int32_t>::max)())
|
if ((std::numeric_limits<std::int32_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int32_t>::max)())
|
||||||
{
|
{
|
||||||
return 'l';
|
return 'l';
|
||||||
}
|
}
|
||||||
|
if (is_bjdata and ((std::numeric_limits<std::uint32_t>::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits<std::uint32_t>::max)()))
|
||||||
|
{
|
||||||
|
return 'm';
|
||||||
|
}
|
||||||
if ((std::numeric_limits<std::int64_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int64_t>::max)())
|
if ((std::numeric_limits<std::int64_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int64_t>::max)())
|
||||||
{
|
{
|
||||||
return 'L';
|
return 'L';
|
||||||
}
|
}
|
||||||
|
if (is_bjdata and ((std::numeric_limits<std::uint64_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::uint64_t>::max)()))
|
||||||
|
{
|
||||||
|
return 'M';
|
||||||
|
}
|
||||||
// anything else is treated as high-precision number
|
// anything else is treated as high-precision number
|
||||||
return 'H'; // LCOV_EXCL_LINE
|
return 'H'; // LCOV_EXCL_LINE
|
||||||
}
|
}
|
||||||
@ -1484,14 +1545,26 @@ class binary_writer
|
|||||||
{
|
{
|
||||||
return 'I';
|
return 'I';
|
||||||
}
|
}
|
||||||
|
if (is_bjdata and j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::uint16_t>::max)()))
|
||||||
|
{
|
||||||
|
return 'u';
|
||||||
|
}
|
||||||
if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))
|
if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))
|
||||||
{
|
{
|
||||||
return 'l';
|
return 'l';
|
||||||
}
|
}
|
||||||
|
if (is_bjdata and j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::uint32_t>::max)()))
|
||||||
|
{
|
||||||
|
return 'm';
|
||||||
|
}
|
||||||
if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))
|
if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))
|
||||||
{
|
{
|
||||||
return 'L';
|
return 'L';
|
||||||
}
|
}
|
||||||
|
if (is_bjdata and j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::uint64_t>::max)()))
|
||||||
|
{
|
||||||
|
return 'M';
|
||||||
|
}
|
||||||
// anything else is treated as high-precision number
|
// anything else is treated as high-precision number
|
||||||
return 'H'; // LCOV_EXCL_LINE
|
return 'H'; // LCOV_EXCL_LINE
|
||||||
}
|
}
|
||||||
@ -1539,6 +1612,7 @@ class binary_writer
|
|||||||
@note This function needs to respect the system's endianness, because bytes
|
@note This function needs to respect the system's endianness, because bytes
|
||||||
in CBOR, MessagePack, and UBJSON are stored in network order (big
|
in CBOR, MessagePack, and UBJSON are stored in network order (big
|
||||||
endian) and therefore need reordering on little endian systems.
|
endian) and therefore need reordering on little endian systems.
|
||||||
|
BJData is similar to UBJSON but uses little endian by default.
|
||||||
*/
|
*/
|
||||||
template<typename NumberType, bool OutputIsLittleEndian = false>
|
template<typename NumberType, bool OutputIsLittleEndian = false>
|
||||||
void write_number(const NumberType n)
|
void write_number(const NumberType n)
|
||||||
@ -1548,7 +1622,7 @@ class binary_writer
|
|||||||
std::memcpy(vec.data(), &n, sizeof(NumberType));
|
std::memcpy(vec.data(), &n, sizeof(NumberType));
|
||||||
|
|
||||||
// step 2: write array to output (with possible reordering)
|
// step 2: write array to output (with possible reordering)
|
||||||
if (is_little_endian != OutputIsLittleEndian)
|
if ((!is_bjdata and (is_little_endian != OutputIsLittleEndian)) or (is_bjdata and !is_little_endian))
|
||||||
{
|
{
|
||||||
// reverse byte order prior to conversion if necessary
|
// reverse byte order prior to conversion if necessary
|
||||||
std::reverse(vec.begin(), vec.end());
|
std::reverse(vec.begin(), vec.end());
|
||||||
@ -1629,6 +1703,9 @@ class binary_writer
|
|||||||
/// whether we can assume little endianness
|
/// whether we can assume little endianness
|
||||||
const bool is_little_endian = little_endianness();
|
const bool is_little_endian = little_endianness();
|
||||||
|
|
||||||
|
/// whether to write in bjdata format
|
||||||
|
bool is_bjdata = false;
|
||||||
|
|
||||||
/// the output
|
/// the output
|
||||||
output_adapter_t<CharType> oa = nullptr;
|
output_adapter_t<CharType> oa = nullptr;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -3965,6 +3965,27 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
binary_writer<char>(o).write_ubjson(j, use_size, use_type);
|
binary_writer<char>(o).write_ubjson(j, use_size, use_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::vector<std::uint8_t> to_bjdata(const basic_json& j,
|
||||||
|
const bool use_size = false,
|
||||||
|
const bool use_type = false)
|
||||||
|
{
|
||||||
|
std::vector<std::uint8_t> result;
|
||||||
|
to_bjdata(j, result, use_size, use_type);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void to_bjdata(const basic_json& j, detail::output_adapter<std::uint8_t> o,
|
||||||
|
const bool use_size = false, const bool use_type = false)
|
||||||
|
{
|
||||||
|
binary_writer<std::uint8_t>(o, true).write_ubjson(j, use_size, use_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void to_bjdata(const basic_json& j, detail::output_adapter<char> o,
|
||||||
|
const bool use_size = false, const bool use_type = false)
|
||||||
|
{
|
||||||
|
binary_writer<char>(o, true).write_ubjson(j, use_size, use_type);
|
||||||
|
}
|
||||||
|
|
||||||
/// @brief create a BSON serialization of a given JSON value
|
/// @brief create a BSON serialization of a given JSON value
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/to_bson/
|
/// @sa https://json.nlohmann.me/api/basic_json/to_bson/
|
||||||
static std::vector<std::uint8_t> to_bson(const basic_json& j)
|
static std::vector<std::uint8_t> to_bson(const basic_json& j)
|
||||||
@ -4155,6 +4176,62 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
return res ? result : basic_json(value_t::discarded);
|
return res ? result : basic_json(value_t::discarded);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief create a JSON value from an input in UBJSON format
|
||||||
|
/// @sa https://json.nlohmann.me/api/basic_json/from_ubjson/
|
||||||
|
template<typename InputType>
|
||||||
|
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||||
|
static basic_json from_bjdata(InputType&& i,
|
||||||
|
const bool strict = true,
|
||||||
|
const bool allow_exceptions = true)
|
||||||
|
{
|
||||||
|
basic_json result;
|
||||||
|
detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
|
||||||
|
auto ia = detail::input_adapter(std::forward<InputType>(i));
|
||||||
|
const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::bjdata, &sdp, strict);
|
||||||
|
return res ? result : basic_json(value_t::discarded);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief create a JSON value from an input in BJData format
|
||||||
|
/// @sa https://github.com/NeuroJSON/bjdata/blob/master/Binary_JData_Specification.md
|
||||||
|
template<typename IteratorType>
|
||||||
|
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||||
|
static basic_json from_bjdata(IteratorType first, IteratorType last,
|
||||||
|
const bool strict = true,
|
||||||
|
const bool allow_exceptions = true)
|
||||||
|
{
|
||||||
|
basic_json result;
|
||||||
|
detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
|
||||||
|
auto ia = detail::input_adapter(std::move(first), std::move(last));
|
||||||
|
const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::bjdata, &sdp, strict);
|
||||||
|
return res ? result : basic_json(value_t::discarded);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||||
|
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bjdata(ptr, ptr + len))
|
||||||
|
static basic_json from_bjdata(const T* ptr, std::size_t len,
|
||||||
|
const bool strict = true,
|
||||||
|
const bool allow_exceptions = true)
|
||||||
|
{
|
||||||
|
return from_bjdata(ptr, ptr + len, strict, allow_exceptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||||
|
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bjdata(ptr, ptr + len))
|
||||||
|
static basic_json from_bjdata(detail::span_input_adapter&& i,
|
||||||
|
const bool strict = true,
|
||||||
|
const bool allow_exceptions = true)
|
||||||
|
{
|
||||||
|
basic_json result;
|
||||||
|
detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
|
||||||
|
auto ia = i.get();
|
||||||
|
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
|
||||||
|
const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::bjdata, &sdp, strict);
|
||||||
|
return res ? result : basic_json(value_t::discarded);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// @brief create a JSON value from an input in BSON format
|
/// @brief create a JSON value from an input in BSON format
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/from_bson/
|
/// @sa https://json.nlohmann.me/api/basic_json/from_bson/
|
||||||
template<typename InputType>
|
template<typename InputType>
|
||||||
|
|||||||
@ -5433,7 +5433,7 @@ namespace nlohmann
|
|||||||
namespace detail
|
namespace detail
|
||||||
{
|
{
|
||||||
/// the supported input formats
|
/// the supported input formats
|
||||||
enum class input_format_t { json, cbor, msgpack, ubjson, bson };
|
enum class input_format_t { json, cbor, msgpack, ubjson, bson, bjdata };
|
||||||
|
|
||||||
////////////////////
|
////////////////////
|
||||||
// input adapters //
|
// input adapters //
|
||||||
@ -8471,12 +8471,13 @@ class binary_reader
|
|||||||
@return whether parsing was successful
|
@return whether parsing was successful
|
||||||
*/
|
*/
|
||||||
JSON_HEDLEY_NON_NULL(3)
|
JSON_HEDLEY_NON_NULL(3)
|
||||||
bool sax_parse(const input_format_t format,
|
bool sax_parse(const input_format_t format_,
|
||||||
json_sax_t* sax_,
|
json_sax_t* sax_,
|
||||||
const bool strict = true,
|
const bool strict = true,
|
||||||
const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
|
const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
|
||||||
{
|
{
|
||||||
sax = sax_;
|
sax = sax_;
|
||||||
|
format = format_;
|
||||||
bool result = false;
|
bool result = false;
|
||||||
|
|
||||||
switch (format)
|
switch (format)
|
||||||
@ -8494,6 +8495,7 @@ class binary_reader
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case input_format_t::ubjson:
|
case input_format_t::ubjson:
|
||||||
|
case input_format_t::bjdata:
|
||||||
result = parse_ubjson_internal();
|
result = parse_ubjson_internal();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -8505,7 +8507,7 @@ class binary_reader
|
|||||||
// strict mode: next byte must be EOF
|
// strict mode: next byte must be EOF
|
||||||
if (result && strict)
|
if (result && strict)
|
||||||
{
|
{
|
||||||
if (format == input_format_t::ubjson)
|
if (format == input_format_t::ubjson || format == input_format_t::bjdata)
|
||||||
{
|
{
|
||||||
get_ignore_noop();
|
get_ignore_noop();
|
||||||
}
|
}
|
||||||
@ -10220,7 +10222,7 @@ class binary_reader
|
|||||||
get(); // TODO(niels): may we ignore N here?
|
get(); // TODO(niels): may we ignore N here?
|
||||||
}
|
}
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "value")))
|
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "value")))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -10230,52 +10232,141 @@ class binary_reader
|
|||||||
case 'U':
|
case 'U':
|
||||||
{
|
{
|
||||||
std::uint8_t len{};
|
std::uint8_t len{};
|
||||||
return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result);
|
return get_number(format, len) && get_string(format, len, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'i':
|
case 'i':
|
||||||
{
|
{
|
||||||
std::int8_t len{};
|
std::int8_t len{};
|
||||||
return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result);
|
return get_number(format, len) && get_string(format, len, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'I':
|
case 'I':
|
||||||
{
|
{
|
||||||
std::int16_t len{};
|
std::int16_t len{};
|
||||||
return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result);
|
return get_number(format, len) && get_string(format, len, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'l':
|
case 'l':
|
||||||
{
|
{
|
||||||
std::int32_t len{};
|
std::int32_t len{};
|
||||||
return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result);
|
return get_number(format, len) && get_string(format, len, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'L':
|
case 'L':
|
||||||
{
|
{
|
||||||
std::int64_t len{};
|
std::int64_t len{};
|
||||||
return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result);
|
return get_number(format, len) && get_string(format, len, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
if (format == input_format_t::bjdata)
|
||||||
|
{
|
||||||
|
switch (current)
|
||||||
|
{
|
||||||
|
case 'u':
|
||||||
|
{
|
||||||
|
uint16_t len;
|
||||||
|
return get_number(format, len) and get_string(format, len, result);
|
||||||
|
}
|
||||||
|
case 'm':
|
||||||
|
{
|
||||||
|
uint32_t len;
|
||||||
|
return get_number(format, len) and get_string(format, len, result);
|
||||||
|
}
|
||||||
|
case 'M':
|
||||||
|
{
|
||||||
|
uint64_t len;
|
||||||
|
return get_number(format, len) and get_string(format, len, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
auto last_token = get_token_string();
|
auto last_token = get_token_string();
|
||||||
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
|
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
|
||||||
exception_message(input_format_t::ubjson, concat("expected length type specification (U, i, I, l, L); last byte: 0x", last_token), "string"), nullptr));
|
exception_message(format, concat("expected length type specification (U, i, I, l, L); last byte: 0x", last_token), "string"), nullptr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@param[out] dim an integer vector storing the ND array dimensions
|
||||||
|
@return whether array creation completed
|
||||||
|
*/
|
||||||
|
bool get_ubjson_ndarray_size(std::vector<size_t>& dim)
|
||||||
|
{
|
||||||
|
std::pair<std::size_t, char_int_type> size_and_type;
|
||||||
|
size_t dimlen;
|
||||||
|
|
||||||
|
bool is_optimized = get_ubjson_size_type(size_and_type);
|
||||||
|
|
||||||
|
if (size_and_type.first != string_t::npos)
|
||||||
|
{
|
||||||
|
if (size_and_type.second != 0)
|
||||||
|
{
|
||||||
|
if (size_and_type.second != 'N')
|
||||||
|
{
|
||||||
|
for (std::size_t i = 0; i < size_and_type.first; ++i)
|
||||||
|
{
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(not get_ubjson_size_value(dimlen, size_and_type.second)))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dim.push_back(dimlen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (std::size_t i = 0; i < size_and_type.first; ++i)
|
||||||
|
{
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(not get_ubjson_size_value(dimlen)))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dim.push_back(dimlen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
while (current != ']')
|
||||||
|
{
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(not get_ubjson_size_value(dimlen)))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dim.push_back(dimlen);
|
||||||
|
}
|
||||||
|
get_ignore_noop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@param[out] result determined size
|
@param[out] result determined size
|
||||||
@return whether size determination completed
|
@return whether size determination completed
|
||||||
*/
|
*/
|
||||||
bool get_ubjson_size_value(std::size_t& result)
|
bool get_ubjson_size_value(std::size_t& result, int prefix = 0)
|
||||||
{
|
{
|
||||||
switch (get_ignore_noop())
|
if (prefix == 0)
|
||||||
|
{
|
||||||
|
prefix = get_ignore_noop();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (prefix)
|
||||||
{
|
{
|
||||||
case 'U':
|
case 'U':
|
||||||
{
|
{
|
||||||
std::uint8_t number{};
|
std::uint8_t number{};
|
||||||
if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number)))
|
if (JSON_HEDLEY_UNLIKELY(!get_number(format, number)))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -10286,7 +10377,7 @@ class binary_reader
|
|||||||
case 'i':
|
case 'i':
|
||||||
{
|
{
|
||||||
std::int8_t number{};
|
std::int8_t number{};
|
||||||
if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number)))
|
if (JSON_HEDLEY_UNLIKELY(!get_number(format, number)))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -10297,7 +10388,7 @@ class binary_reader
|
|||||||
case 'I':
|
case 'I':
|
||||||
{
|
{
|
||||||
std::int16_t number{};
|
std::int16_t number{};
|
||||||
if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number)))
|
if (JSON_HEDLEY_UNLIKELY(!get_number(format, number)))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -10308,7 +10399,7 @@ class binary_reader
|
|||||||
case 'l':
|
case 'l':
|
||||||
{
|
{
|
||||||
std::int32_t number{};
|
std::int32_t number{};
|
||||||
if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number)))
|
if (JSON_HEDLEY_UNLIKELY(!get_number(format, number)))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -10319,7 +10410,7 @@ class binary_reader
|
|||||||
case 'L':
|
case 'L':
|
||||||
{
|
{
|
||||||
std::int64_t number{};
|
std::int64_t number{};
|
||||||
if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number)))
|
if (JSON_HEDLEY_UNLIKELY(!get_number(format, number)))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -10329,9 +10420,62 @@ class binary_reader
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
|
if (format == input_format_t::bjdata)
|
||||||
|
{
|
||||||
|
switch (prefix)
|
||||||
|
{
|
||||||
|
case 'u':
|
||||||
|
{
|
||||||
|
uint16_t number;
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(not get_number(format, number)))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
result = static_cast<std::size_t>(number);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 'm':
|
||||||
|
{
|
||||||
|
uint32_t number;
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(not get_number(format, number)))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
result = static_cast<std::size_t>(number);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 'M':
|
||||||
|
{
|
||||||
|
uint64_t number;
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(not get_number(format, number)))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
result = static_cast<std::size_t>(number);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case '[':
|
||||||
|
{
|
||||||
|
std::vector<size_t> dim;
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(not get_ubjson_ndarray_size(dim)))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = 1;
|
||||||
|
for (std::size_t i = 0; i < dim.size(); ++i)
|
||||||
|
{
|
||||||
|
result *= dim.at(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
auto last_token = get_token_string();
|
auto last_token = get_token_string();
|
||||||
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
|
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
|
||||||
exception_message(input_format_t::ubjson, concat("expected length type specification (U, i, I, l, L) after '#'; last byte: 0x", last_token), "size"), nullptr));
|
exception_message(format, concat("expected length type specification (U, i, I, l, L) after '#'; last byte: 0x", last_token), "size"), nullptr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -10356,7 +10500,7 @@ class binary_reader
|
|||||||
if (current == '$')
|
if (current == '$')
|
||||||
{
|
{
|
||||||
result.second = get(); // must not ignore 'N', because 'N' maybe the type
|
result.second = get(); // must not ignore 'N', because 'N' maybe the type
|
||||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "type")))
|
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "type")))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -10364,13 +10508,13 @@ class binary_reader
|
|||||||
get_ignore_noop();
|
get_ignore_noop();
|
||||||
if (JSON_HEDLEY_UNLIKELY(current != '#'))
|
if (JSON_HEDLEY_UNLIKELY(current != '#'))
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "value")))
|
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "value")))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
auto last_token = get_token_string();
|
auto last_token = get_token_string();
|
||||||
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
|
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
|
||||||
exception_message(input_format_t::ubjson, concat("expected '#' after type information; last byte: 0x", last_token), "size"), nullptr));
|
exception_message(format, concat("expected '#' after type information; last byte: 0x", last_token), "size"), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
return get_ubjson_size_value(result.first);
|
return get_ubjson_size_value(result.first);
|
||||||
@ -10393,7 +10537,7 @@ class binary_reader
|
|||||||
switch (prefix)
|
switch (prefix)
|
||||||
{
|
{
|
||||||
case std::char_traits<char_type>::eof(): // EOF
|
case std::char_traits<char_type>::eof(): // EOF
|
||||||
return unexpect_eof(input_format_t::ubjson, "value");
|
return unexpect_eof(format, "value");
|
||||||
|
|
||||||
case 'T': // true
|
case 'T': // true
|
||||||
return sax->boolean(true);
|
return sax->boolean(true);
|
||||||
@ -10406,43 +10550,43 @@ class binary_reader
|
|||||||
case 'U':
|
case 'U':
|
||||||
{
|
{
|
||||||
std::uint8_t number{};
|
std::uint8_t number{};
|
||||||
return get_number(input_format_t::ubjson, number) && sax->number_unsigned(number);
|
return get_number(format, number) && sax->number_unsigned(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'i':
|
case 'i':
|
||||||
{
|
{
|
||||||
std::int8_t number{};
|
std::int8_t number{};
|
||||||
return get_number(input_format_t::ubjson, number) && sax->number_integer(number);
|
return get_number(format, number) && sax->number_integer(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'I':
|
case 'I':
|
||||||
{
|
{
|
||||||
std::int16_t number{};
|
std::int16_t number{};
|
||||||
return get_number(input_format_t::ubjson, number) && sax->number_integer(number);
|
return get_number(format, number) && sax->number_integer(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'l':
|
case 'l':
|
||||||
{
|
{
|
||||||
std::int32_t number{};
|
std::int32_t number{};
|
||||||
return get_number(input_format_t::ubjson, number) && sax->number_integer(number);
|
return get_number(format, number) && sax->number_integer(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'L':
|
case 'L':
|
||||||
{
|
{
|
||||||
std::int64_t number{};
|
std::int64_t number{};
|
||||||
return get_number(input_format_t::ubjson, number) && sax->number_integer(number);
|
return get_number(format, number) && sax->number_integer(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'd':
|
case 'd':
|
||||||
{
|
{
|
||||||
float number{};
|
float number{};
|
||||||
return get_number(input_format_t::ubjson, number) && sax->number_float(static_cast<number_float_t>(number), "");
|
return get_number(format, number) && sax->number_float(static_cast<number_float_t>(number), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'D':
|
case 'D':
|
||||||
{
|
{
|
||||||
double number{};
|
double number{};
|
||||||
return get_number(input_format_t::ubjson, number) && sax->number_float(static_cast<number_float_t>(number), "");
|
return get_number(format, number) && sax->number_float(static_cast<number_float_t>(number), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'H':
|
case 'H':
|
||||||
@ -10453,7 +10597,7 @@ class binary_reader
|
|||||||
case 'C': // char
|
case 'C': // char
|
||||||
{
|
{
|
||||||
get();
|
get();
|
||||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "char")))
|
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "char")))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -10461,7 +10605,7 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
auto last_token = get_token_string();
|
auto last_token = get_token_string();
|
||||||
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
|
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
|
||||||
exception_message(input_format_t::ubjson, concat("byte after 'C' must be in range 0x00..0x7F; last byte: 0x", last_token), "char"), nullptr));
|
exception_message(format, concat("byte after 'C' must be in range 0x00..0x7F; last byte: 0x", last_token), "char"), nullptr));
|
||||||
}
|
}
|
||||||
string_t s(1, static_cast<typename string_t::value_type>(current));
|
string_t s(1, static_cast<typename string_t::value_type>(current));
|
||||||
return sax->string(s);
|
return sax->string(s);
|
||||||
@ -10481,9 +10625,74 @@ class binary_reader
|
|||||||
|
|
||||||
default: // anything else
|
default: // anything else
|
||||||
{
|
{
|
||||||
|
if (format == input_format_t::bjdata)
|
||||||
|
{
|
||||||
|
switch (prefix)
|
||||||
|
{
|
||||||
|
case 'u':
|
||||||
|
{
|
||||||
|
uint16_t number;
|
||||||
|
return get_number(format, number) and sax->number_integer(number);
|
||||||
|
}
|
||||||
|
case 'm':
|
||||||
|
{
|
||||||
|
uint32_t number;
|
||||||
|
return get_number(format, number) and sax->number_integer(number);
|
||||||
|
}
|
||||||
|
case 'M':
|
||||||
|
{
|
||||||
|
uint64_t number;
|
||||||
|
return get_number(format, number) and sax->number_integer(number);
|
||||||
|
}
|
||||||
|
case 'h':
|
||||||
|
{
|
||||||
|
const int byte2 = get();
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "half")))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const int byte1 = get();
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "half")))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// code from RFC 7049, Appendix D, Figure 3:
|
||||||
|
// As half-precision floating-point numbers were only added
|
||||||
|
// to IEEE 754 in 2008, today's programming platforms often
|
||||||
|
// still only have limited support for them. It is very
|
||||||
|
// easy to include at least decoding support for them even
|
||||||
|
// without such support. An example of a small decoder for
|
||||||
|
// half-precision floating-point numbers in the C language
|
||||||
|
// is shown in Fig. 3.
|
||||||
|
const int half = (byte1 << 8) + byte2;
|
||||||
|
const double val = [&half]
|
||||||
|
{
|
||||||
|
const int exp = (half >> 10) & 0x1F;
|
||||||
|
const int mant = half & 0x3FF;
|
||||||
|
JSON_ASSERT(0 <= exp and exp <= 32);
|
||||||
|
JSON_ASSERT(0 <= mant and mant <= 1024);
|
||||||
|
switch (exp)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
return std::ldexp(mant, -24);
|
||||||
|
case 31:
|
||||||
|
return (mant == 0)
|
||||||
|
? std::numeric_limits<double>::infinity()
|
||||||
|
: std::numeric_limits<double>::quiet_NaN();
|
||||||
|
default:
|
||||||
|
return std::ldexp(mant + 1024, exp - 25);
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
return sax->number_float((half & 0x8000) != 0
|
||||||
|
? static_cast<number_float_t>(-val)
|
||||||
|
: static_cast<number_float_t>(val), "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
auto last_token = get_token_string();
|
auto last_token = get_token_string();
|
||||||
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
|
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
|
||||||
exception_message(input_format_t::ubjson, concat("invalid byte: 0x", last_token), "value"), nullptr));
|
exception_message(format, concat("invalid byte: 0x", last_token), "value"), nullptr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -10643,7 +10852,7 @@ class binary_reader
|
|||||||
for (std::size_t i = 0; i < size; ++i)
|
for (std::size_t i = 0; i < size; ++i)
|
||||||
{
|
{
|
||||||
get();
|
get();
|
||||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "number")))
|
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "number")))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -10662,7 +10871,7 @@ class binary_reader
|
|||||||
if (JSON_HEDLEY_UNLIKELY(result_remainder != token_type::end_of_input))
|
if (JSON_HEDLEY_UNLIKELY(result_remainder != token_type::end_of_input))
|
||||||
{
|
{
|
||||||
return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read,
|
return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read,
|
||||||
exception_message(input_format_t::ubjson, concat("invalid number text: ", number_lexer.get_token_string()), "high-precision number"), nullptr));
|
exception_message(format, concat("invalid number text: ", number_lexer.get_token_string()), "high-precision number"), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (result_number)
|
switch (result_number)
|
||||||
@ -10689,7 +10898,7 @@ class binary_reader
|
|||||||
case token_type::literal_or_value:
|
case token_type::literal_or_value:
|
||||||
default:
|
default:
|
||||||
return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read,
|
return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read,
|
||||||
exception_message(input_format_t::ubjson, concat("invalid number text: ", number_lexer.get_token_string()), "high-precision number"), nullptr));
|
exception_message(format, concat("invalid number text: ", number_lexer.get_token_string()), "high-precision number"), nullptr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -10753,7 +10962,8 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
|
|
||||||
// reverse byte order prior to conversion if necessary
|
// reverse byte order prior to conversion if necessary
|
||||||
if (is_little_endian != InputIsLittleEndian)
|
if ((is_little_endian != InputIsLittleEndian and format != input_format_t::bjdata) or
|
||||||
|
(is_little_endian == InputIsLittleEndian and format == input_format_t::bjdata))
|
||||||
{
|
{
|
||||||
vec[sizeof(NumberType) - i - 1] = static_cast<std::uint8_t>(current);
|
vec[sizeof(NumberType) - i - 1] = static_cast<std::uint8_t>(current);
|
||||||
}
|
}
|
||||||
@ -10890,6 +11100,10 @@ class binary_reader
|
|||||||
error_msg += "BSON";
|
error_msg += "BSON";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case input_format_t::bjdata:
|
||||||
|
error_msg += "BJData";
|
||||||
|
break;
|
||||||
|
|
||||||
case input_format_t::json: // LCOV_EXCL_LINE
|
case input_format_t::json: // LCOV_EXCL_LINE
|
||||||
default: // LCOV_EXCL_LINE
|
default: // LCOV_EXCL_LINE
|
||||||
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
|
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
|
||||||
@ -10911,6 +11125,9 @@ class binary_reader
|
|||||||
/// whether we can assume little endianness
|
/// whether we can assume little endianness
|
||||||
const bool is_little_endian = little_endianness();
|
const bool is_little_endian = little_endianness();
|
||||||
|
|
||||||
|
/// sax parser format
|
||||||
|
input_format_t format;
|
||||||
|
|
||||||
/// the SAX parser
|
/// the SAX parser
|
||||||
json_sax_t* sax = nullptr;
|
json_sax_t* sax = nullptr;
|
||||||
};
|
};
|
||||||
@ -13606,9 +13823,10 @@ class binary_writer
|
|||||||
|
|
||||||
@param[in] adapter output adapter to write to
|
@param[in] adapter output adapter to write to
|
||||||
*/
|
*/
|
||||||
explicit binary_writer(output_adapter_t<CharType> adapter) : oa(std::move(adapter))
|
explicit binary_writer(output_adapter_t<CharType> adapter, const bool is_bjdata_ = false) : oa(std::move(adapter))
|
||||||
{
|
{
|
||||||
JSON_ASSERT(oa);
|
JSON_ASSERT(oa);
|
||||||
|
is_bjdata = is_bjdata_;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -14902,6 +15120,14 @@ class binary_writer
|
|||||||
}
|
}
|
||||||
write_number(static_cast<std::int16_t>(n));
|
write_number(static_cast<std::int16_t>(n));
|
||||||
}
|
}
|
||||||
|
else if (is_bjdata and n <= static_cast<uint64_t>((std::numeric_limits<uint16_t>::max)()))
|
||||||
|
{
|
||||||
|
if (add_prefix)
|
||||||
|
{
|
||||||
|
oa->write_character(to_char_type('u')); // uint16 - bjdata only
|
||||||
|
}
|
||||||
|
write_number(static_cast<std::uint16_t>(n));
|
||||||
|
}
|
||||||
else if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))
|
else if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))
|
||||||
{
|
{
|
||||||
if (add_prefix)
|
if (add_prefix)
|
||||||
@ -14910,6 +15136,14 @@ class binary_writer
|
|||||||
}
|
}
|
||||||
write_number(static_cast<std::int32_t>(n));
|
write_number(static_cast<std::int32_t>(n));
|
||||||
}
|
}
|
||||||
|
else if (is_bjdata and n <= static_cast<uint64_t>((std::numeric_limits<uint32_t>::max)()))
|
||||||
|
{
|
||||||
|
if (add_prefix)
|
||||||
|
{
|
||||||
|
oa->write_character(to_char_type('m')); // uint32 - bjdata only
|
||||||
|
}
|
||||||
|
write_number(static_cast<std::uint32_t>(n));
|
||||||
|
}
|
||||||
else if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))
|
else if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))
|
||||||
{
|
{
|
||||||
if (add_prefix)
|
if (add_prefix)
|
||||||
@ -14918,6 +15152,14 @@ class binary_writer
|
|||||||
}
|
}
|
||||||
write_number(static_cast<std::int64_t>(n));
|
write_number(static_cast<std::int64_t>(n));
|
||||||
}
|
}
|
||||||
|
else if (is_bjdata and n <= static_cast<uint64_t>((std::numeric_limits<uint64_t>::max)()))
|
||||||
|
{
|
||||||
|
if (add_prefix)
|
||||||
|
{
|
||||||
|
oa->write_character(to_char_type('M')); // uint64 - bjdata only
|
||||||
|
}
|
||||||
|
write_number(static_cast<std::uint64_t>(n));
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (add_prefix)
|
if (add_prefix)
|
||||||
@ -14965,6 +15207,14 @@ class binary_writer
|
|||||||
}
|
}
|
||||||
write_number(static_cast<std::int16_t>(n));
|
write_number(static_cast<std::int16_t>(n));
|
||||||
}
|
}
|
||||||
|
else if (is_bjdata and (static_cast<std::int64_t>((std::numeric_limits<std::uint16_t>::min)()) <= n && n <= static_cast<std::int64_t>((std::numeric_limits<std::uint16_t>::max)())))
|
||||||
|
{
|
||||||
|
if (add_prefix)
|
||||||
|
{
|
||||||
|
oa->write_character(to_char_type('u')); // uint16 - bjdata only
|
||||||
|
}
|
||||||
|
write_number(static_cast<uint16_t>(n));
|
||||||
|
}
|
||||||
else if ((std::numeric_limits<std::int32_t>::min)() <= n && n <= (std::numeric_limits<std::int32_t>::max)())
|
else if ((std::numeric_limits<std::int32_t>::min)() <= n && n <= (std::numeric_limits<std::int32_t>::max)())
|
||||||
{
|
{
|
||||||
if (add_prefix)
|
if (add_prefix)
|
||||||
@ -14973,6 +15223,14 @@ class binary_writer
|
|||||||
}
|
}
|
||||||
write_number(static_cast<std::int32_t>(n));
|
write_number(static_cast<std::int32_t>(n));
|
||||||
}
|
}
|
||||||
|
else if (is_bjdata and (static_cast<std::int64_t>((std::numeric_limits<std::uint32_t>::min)()) <= n && n <= static_cast<std::int64_t>((std::numeric_limits<std::uint32_t>::max)())))
|
||||||
|
{
|
||||||
|
if (add_prefix)
|
||||||
|
{
|
||||||
|
oa->write_character(to_char_type('m')); // uint32 - bjdata only
|
||||||
|
}
|
||||||
|
write_number(static_cast<uint32_t>(n));
|
||||||
|
}
|
||||||
else if ((std::numeric_limits<std::int64_t>::min)() <= n && n <= (std::numeric_limits<std::int64_t>::max)())
|
else if ((std::numeric_limits<std::int64_t>::min)() <= n && n <= (std::numeric_limits<std::int64_t>::max)())
|
||||||
{
|
{
|
||||||
if (add_prefix)
|
if (add_prefix)
|
||||||
@ -14981,6 +15239,14 @@ class binary_writer
|
|||||||
}
|
}
|
||||||
write_number(static_cast<std::int64_t>(n));
|
write_number(static_cast<std::int64_t>(n));
|
||||||
}
|
}
|
||||||
|
else if (is_bjdata and (static_cast<std::uint64_t>((std::numeric_limits<std::uint64_t>::min)()) <= n && n <= static_cast<std::uint64_t>((std::numeric_limits<std::uint64_t>::max)())))
|
||||||
|
{
|
||||||
|
if (add_prefix)
|
||||||
|
{
|
||||||
|
oa->write_character(to_char_type('M')); // uint64 - bjdata only
|
||||||
|
}
|
||||||
|
write_number(static_cast<std::uint64_t>(n));
|
||||||
|
}
|
||||||
// LCOV_EXCL_START
|
// LCOV_EXCL_START
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -15026,14 +15292,26 @@ class binary_writer
|
|||||||
{
|
{
|
||||||
return 'I';
|
return 'I';
|
||||||
}
|
}
|
||||||
|
if (is_bjdata and ((std::numeric_limits<std::uint16_t>::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits<std::uint16_t>::max)()))
|
||||||
|
{
|
||||||
|
return 'u';
|
||||||
|
}
|
||||||
if ((std::numeric_limits<std::int32_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int32_t>::max)())
|
if ((std::numeric_limits<std::int32_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int32_t>::max)())
|
||||||
{
|
{
|
||||||
return 'l';
|
return 'l';
|
||||||
}
|
}
|
||||||
|
if (is_bjdata and ((std::numeric_limits<std::uint32_t>::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits<std::uint32_t>::max)()))
|
||||||
|
{
|
||||||
|
return 'm';
|
||||||
|
}
|
||||||
if ((std::numeric_limits<std::int64_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int64_t>::max)())
|
if ((std::numeric_limits<std::int64_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int64_t>::max)())
|
||||||
{
|
{
|
||||||
return 'L';
|
return 'L';
|
||||||
}
|
}
|
||||||
|
if (is_bjdata and ((std::numeric_limits<std::uint64_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::uint64_t>::max)()))
|
||||||
|
{
|
||||||
|
return 'M';
|
||||||
|
}
|
||||||
// anything else is treated as high-precision number
|
// anything else is treated as high-precision number
|
||||||
return 'H'; // LCOV_EXCL_LINE
|
return 'H'; // LCOV_EXCL_LINE
|
||||||
}
|
}
|
||||||
@ -15052,14 +15330,26 @@ class binary_writer
|
|||||||
{
|
{
|
||||||
return 'I';
|
return 'I';
|
||||||
}
|
}
|
||||||
|
if (is_bjdata and j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::uint16_t>::max)()))
|
||||||
|
{
|
||||||
|
return 'u';
|
||||||
|
}
|
||||||
if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))
|
if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))
|
||||||
{
|
{
|
||||||
return 'l';
|
return 'l';
|
||||||
}
|
}
|
||||||
|
if (is_bjdata and j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::uint32_t>::max)()))
|
||||||
|
{
|
||||||
|
return 'm';
|
||||||
|
}
|
||||||
if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))
|
if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))
|
||||||
{
|
{
|
||||||
return 'L';
|
return 'L';
|
||||||
}
|
}
|
||||||
|
if (is_bjdata and j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::uint64_t>::max)()))
|
||||||
|
{
|
||||||
|
return 'M';
|
||||||
|
}
|
||||||
// anything else is treated as high-precision number
|
// anything else is treated as high-precision number
|
||||||
return 'H'; // LCOV_EXCL_LINE
|
return 'H'; // LCOV_EXCL_LINE
|
||||||
}
|
}
|
||||||
@ -15107,6 +15397,7 @@ class binary_writer
|
|||||||
@note This function needs to respect the system's endianness, because bytes
|
@note This function needs to respect the system's endianness, because bytes
|
||||||
in CBOR, MessagePack, and UBJSON are stored in network order (big
|
in CBOR, MessagePack, and UBJSON are stored in network order (big
|
||||||
endian) and therefore need reordering on little endian systems.
|
endian) and therefore need reordering on little endian systems.
|
||||||
|
BJData is similar to UBJSON but uses little endian by default.
|
||||||
*/
|
*/
|
||||||
template<typename NumberType, bool OutputIsLittleEndian = false>
|
template<typename NumberType, bool OutputIsLittleEndian = false>
|
||||||
void write_number(const NumberType n)
|
void write_number(const NumberType n)
|
||||||
@ -15116,7 +15407,7 @@ class binary_writer
|
|||||||
std::memcpy(vec.data(), &n, sizeof(NumberType));
|
std::memcpy(vec.data(), &n, sizeof(NumberType));
|
||||||
|
|
||||||
// step 2: write array to output (with possible reordering)
|
// step 2: write array to output (with possible reordering)
|
||||||
if (is_little_endian != OutputIsLittleEndian)
|
if ((!is_bjdata and (is_little_endian != OutputIsLittleEndian)) or (is_bjdata and !is_little_endian))
|
||||||
{
|
{
|
||||||
// reverse byte order prior to conversion if necessary
|
// reverse byte order prior to conversion if necessary
|
||||||
std::reverse(vec.begin(), vec.end());
|
std::reverse(vec.begin(), vec.end());
|
||||||
@ -15197,6 +15488,9 @@ class binary_writer
|
|||||||
/// whether we can assume little endianness
|
/// whether we can assume little endianness
|
||||||
const bool is_little_endian = little_endianness();
|
const bool is_little_endian = little_endianness();
|
||||||
|
|
||||||
|
/// whether to write in bjdata format
|
||||||
|
bool is_bjdata = false;
|
||||||
|
|
||||||
/// the output
|
/// the output
|
||||||
output_adapter_t<CharType> oa = nullptr;
|
output_adapter_t<CharType> oa = nullptr;
|
||||||
};
|
};
|
||||||
@ -21413,6 +21707,27 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
binary_writer<char>(o).write_ubjson(j, use_size, use_type);
|
binary_writer<char>(o).write_ubjson(j, use_size, use_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::vector<std::uint8_t> to_bjdata(const basic_json& j,
|
||||||
|
const bool use_size = false,
|
||||||
|
const bool use_type = false)
|
||||||
|
{
|
||||||
|
std::vector<std::uint8_t> result;
|
||||||
|
to_bjdata(j, result, use_size, use_type);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void to_bjdata(const basic_json& j, detail::output_adapter<std::uint8_t> o,
|
||||||
|
const bool use_size = false, const bool use_type = false)
|
||||||
|
{
|
||||||
|
binary_writer<std::uint8_t>(o, true).write_ubjson(j, use_size, use_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void to_bjdata(const basic_json& j, detail::output_adapter<char> o,
|
||||||
|
const bool use_size = false, const bool use_type = false)
|
||||||
|
{
|
||||||
|
binary_writer<char>(o, true).write_ubjson(j, use_size, use_type);
|
||||||
|
}
|
||||||
|
|
||||||
/// @brief create a BSON serialization of a given JSON value
|
/// @brief create a BSON serialization of a given JSON value
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/to_bson/
|
/// @sa https://json.nlohmann.me/api/basic_json/to_bson/
|
||||||
static std::vector<std::uint8_t> to_bson(const basic_json& j)
|
static std::vector<std::uint8_t> to_bson(const basic_json& j)
|
||||||
@ -21603,6 +21918,62 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
return res ? result : basic_json(value_t::discarded);
|
return res ? result : basic_json(value_t::discarded);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief create a JSON value from an input in UBJSON format
|
||||||
|
/// @sa https://json.nlohmann.me/api/basic_json/from_ubjson/
|
||||||
|
template<typename InputType>
|
||||||
|
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||||
|
static basic_json from_bjdata(InputType&& i,
|
||||||
|
const bool strict = true,
|
||||||
|
const bool allow_exceptions = true)
|
||||||
|
{
|
||||||
|
basic_json result;
|
||||||
|
detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
|
||||||
|
auto ia = detail::input_adapter(std::forward<InputType>(i));
|
||||||
|
const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::bjdata, &sdp, strict);
|
||||||
|
return res ? result : basic_json(value_t::discarded);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief create a JSON value from an input in BJData format
|
||||||
|
/// @sa https://github.com/NeuroJSON/bjdata/blob/master/Binary_JData_Specification.md
|
||||||
|
template<typename IteratorType>
|
||||||
|
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||||
|
static basic_json from_bjdata(IteratorType first, IteratorType last,
|
||||||
|
const bool strict = true,
|
||||||
|
const bool allow_exceptions = true)
|
||||||
|
{
|
||||||
|
basic_json result;
|
||||||
|
detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
|
||||||
|
auto ia = detail::input_adapter(std::move(first), std::move(last));
|
||||||
|
const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::bjdata, &sdp, strict);
|
||||||
|
return res ? result : basic_json(value_t::discarded);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||||
|
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bjdata(ptr, ptr + len))
|
||||||
|
static basic_json from_bjdata(const T* ptr, std::size_t len,
|
||||||
|
const bool strict = true,
|
||||||
|
const bool allow_exceptions = true)
|
||||||
|
{
|
||||||
|
return from_bjdata(ptr, ptr + len, strict, allow_exceptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||||
|
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bjdata(ptr, ptr + len))
|
||||||
|
static basic_json from_bjdata(detail::span_input_adapter&& i,
|
||||||
|
const bool strict = true,
|
||||||
|
const bool allow_exceptions = true)
|
||||||
|
{
|
||||||
|
basic_json result;
|
||||||
|
detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
|
||||||
|
auto ia = i.get();
|
||||||
|
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
|
||||||
|
const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::bjdata, &sdp, strict);
|
||||||
|
return res ? result : basic_json(value_t::discarded);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// @brief create a JSON value from an input in BSON format
|
/// @brief create a JSON value from an input in BSON format
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/from_bson/
|
/// @sa https://json.nlohmann.me/api/basic_json/from_bson/
|
||||||
template<typename InputType>
|
template<typename InputType>
|
||||||
|
|||||||
84
test/src/fuzzer-parse_bjdata.cpp
Normal file
84
test/src/fuzzer-parse_bjdata.cpp
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
__ _____ _____ _____
|
||||||
|
__| | __| | | | JSON for Modern C++ (fuzz test support)
|
||||||
|
| | |__ | | | | | | version 3.10.5
|
||||||
|
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||||
|
|
||||||
|
This file implements a parser test suitable for fuzz testing. Given a byte
|
||||||
|
array data, it performs the following steps:
|
||||||
|
|
||||||
|
- j1 = from_bjdata(data)
|
||||||
|
- vec = to_bjdata(j1)
|
||||||
|
- j2 = from_bjdata(vec)
|
||||||
|
- assert(j1 == j2)
|
||||||
|
- vec2 = to_bjdata(j1, use_size = true, use_type = false)
|
||||||
|
- j3 = from_bjdata(vec2)
|
||||||
|
- assert(j1 == j3)
|
||||||
|
- vec3 = to_bjdata(j1, use_size = true, use_type = true)
|
||||||
|
- j4 = from_bjdata(vec3)
|
||||||
|
- assert(j1 == j4)
|
||||||
|
|
||||||
|
The provided function `LLVMFuzzerTestOneInput` can be used in different fuzzer
|
||||||
|
drivers.
|
||||||
|
|
||||||
|
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
// see http://llvm.org/docs/LibFuzzer.html
|
||||||
|
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// step 1: parse input
|
||||||
|
std::vector<uint8_t> vec1(data, data + size);
|
||||||
|
json j1 = json::from_bjdata(vec1);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// step 2.1: round trip without adding size annotations to container types
|
||||||
|
std::vector<uint8_t> vec2 = json::to_bjdata(j1, false, false);
|
||||||
|
|
||||||
|
// step 2.2: round trip with adding size annotations but without adding type annonations to container types
|
||||||
|
std::vector<uint8_t> vec3 = json::to_bjdata(j1, true, false);
|
||||||
|
|
||||||
|
// step 2.3: round trip with adding size as well as type annotations to container types
|
||||||
|
std::vector<uint8_t> vec4 = json::to_bjdata(j1, true, true);
|
||||||
|
|
||||||
|
// parse serialization
|
||||||
|
json j2 = json::from_bjdata(vec2);
|
||||||
|
json j3 = json::from_bjdata(vec3);
|
||||||
|
json j4 = json::from_bjdata(vec4);
|
||||||
|
|
||||||
|
// serializations must match
|
||||||
|
assert(json::to_bjdata(j2, false, false) == vec2);
|
||||||
|
assert(json::to_bjdata(j3, true, false) == vec3);
|
||||||
|
assert(json::to_bjdata(j4, true, true) == vec4);
|
||||||
|
}
|
||||||
|
catch (const json::parse_error&)
|
||||||
|
{
|
||||||
|
// parsing a BJData serialization must not fail
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const json::parse_error&)
|
||||||
|
{
|
||||||
|
// parse errors are ok, because input may be random bytes
|
||||||
|
}
|
||||||
|
catch (const json::type_error&)
|
||||||
|
{
|
||||||
|
// type errors can occur during parsing, too
|
||||||
|
}
|
||||||
|
catch (const json::out_of_range&)
|
||||||
|
{
|
||||||
|
// out of range errors may happen if provided sizes are excessive
|
||||||
|
}
|
||||||
|
|
||||||
|
// return 0 - non-zero return values are reserved for future use
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
2981
test/src/unit-bjdata.cpp
Normal file
2981
test/src/unit-bjdata.cpp
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user