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
|
||||
*/
|
||||
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_,
|
||||
const bool strict = true,
|
||||
const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
|
||||
{
|
||||
sax = sax_;
|
||||
format = format_;
|
||||
bool result = false;
|
||||
|
||||
switch (format)
|
||||
@ -118,6 +119,7 @@ class binary_reader
|
||||
break;
|
||||
|
||||
case input_format_t::ubjson:
|
||||
case input_format_t::bjdata:
|
||||
result = parse_ubjson_internal();
|
||||
break;
|
||||
|
||||
@ -129,7 +131,7 @@ class binary_reader
|
||||
// strict mode: next byte must be EOF
|
||||
if (result && strict)
|
||||
{
|
||||
if (format == input_format_t::ubjson)
|
||||
if (format == input_format_t::ubjson || format == input_format_t::bjdata)
|
||||
{
|
||||
get_ignore_noop();
|
||||
}
|
||||
@ -1844,7 +1846,7 @@ class binary_reader
|
||||
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;
|
||||
}
|
||||
@ -1854,52 +1856,141 @@ class binary_reader
|
||||
case 'U':
|
||||
{
|
||||
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':
|
||||
{
|
||||
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':
|
||||
{
|
||||
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':
|
||||
{
|
||||
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':
|
||||
{
|
||||
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:
|
||||
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();
|
||||
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
|
||||
@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':
|
||||
{
|
||||
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;
|
||||
}
|
||||
@ -1910,7 +2001,7 @@ class binary_reader
|
||||
case 'i':
|
||||
{
|
||||
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;
|
||||
}
|
||||
@ -1921,7 +2012,7 @@ class binary_reader
|
||||
case 'I':
|
||||
{
|
||||
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;
|
||||
}
|
||||
@ -1932,7 +2023,7 @@ class binary_reader
|
||||
case 'l':
|
||||
{
|
||||
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;
|
||||
}
|
||||
@ -1943,7 +2034,7 @@ class binary_reader
|
||||
case 'L':
|
||||
{
|
||||
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;
|
||||
}
|
||||
@ -1953,9 +2044,62 @@ class binary_reader
|
||||
|
||||
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();
|
||||
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 == '$')
|
||||
{
|
||||
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;
|
||||
}
|
||||
@ -1988,13 +2132,13 @@ class binary_reader
|
||||
get_ignore_noop();
|
||||
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;
|
||||
}
|
||||
auto last_token = get_token_string();
|
||||
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);
|
||||
@ -2017,7 +2161,7 @@ class binary_reader
|
||||
switch (prefix)
|
||||
{
|
||||
case std::char_traits<char_type>::eof(): // EOF
|
||||
return unexpect_eof(input_format_t::ubjson, "value");
|
||||
return unexpect_eof(format, "value");
|
||||
|
||||
case 'T': // true
|
||||
return sax->boolean(true);
|
||||
@ -2030,43 +2174,43 @@ class binary_reader
|
||||
case 'U':
|
||||
{
|
||||
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':
|
||||
{
|
||||
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':
|
||||
{
|
||||
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':
|
||||
{
|
||||
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':
|
||||
{
|
||||
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':
|
||||
{
|
||||
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':
|
||||
{
|
||||
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':
|
||||
@ -2077,7 +2221,7 @@ class binary_reader
|
||||
case 'C': // char
|
||||
{
|
||||
get();
|
||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "char")))
|
||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "char")))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -2085,7 +2229,7 @@ class binary_reader
|
||||
{
|
||||
auto last_token = get_token_string();
|
||||
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));
|
||||
return sax->string(s);
|
||||
@ -2105,9 +2249,74 @@ class binary_reader
|
||||
|
||||
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();
|
||||
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)
|
||||
{
|
||||
get();
|
||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "number")))
|
||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "number")))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -2286,7 +2495,7 @@ class binary_reader
|
||||
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,
|
||||
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)
|
||||
@ -2313,7 +2522,7 @@ class binary_reader
|
||||
case token_type::literal_or_value:
|
||||
default:
|
||||
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
|
||||
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);
|
||||
}
|
||||
@ -2514,6 +2724,10 @@ class binary_reader
|
||||
error_msg += "BSON";
|
||||
break;
|
||||
|
||||
case input_format_t::bjdata:
|
||||
error_msg += "BJData";
|
||||
break;
|
||||
|
||||
case input_format_t::json: // LCOV_EXCL_LINE
|
||||
default: // 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
|
||||
const bool is_little_endian = little_endianness();
|
||||
|
||||
/// sax parser format
|
||||
input_format_t format;
|
||||
|
||||
/// the SAX parser
|
||||
json_sax_t* sax = nullptr;
|
||||
};
|
||||
|
||||
@ -23,7 +23,7 @@ namespace nlohmann
|
||||
namespace detail
|
||||
{
|
||||
/// 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 //
|
||||
|
||||
@ -38,9 +38,10 @@ class binary_writer
|
||||
|
||||
@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);
|
||||
is_bjdata = is_bjdata_;
|
||||
}
|
||||
|
||||
/*!
|
||||
@ -1334,6 +1335,14 @@ class binary_writer
|
||||
}
|
||||
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)()))
|
||||
{
|
||||
if (add_prefix)
|
||||
@ -1342,6 +1351,14 @@ class binary_writer
|
||||
}
|
||||
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)()))
|
||||
{
|
||||
if (add_prefix)
|
||||
@ -1350,6 +1367,14 @@ class binary_writer
|
||||
}
|
||||
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
|
||||
{
|
||||
if (add_prefix)
|
||||
@ -1397,6 +1422,14 @@ class binary_writer
|
||||
}
|
||||
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)())
|
||||
{
|
||||
if (add_prefix)
|
||||
@ -1405,6 +1438,14 @@ class binary_writer
|
||||
}
|
||||
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)())
|
||||
{
|
||||
if (add_prefix)
|
||||
@ -1413,6 +1454,14 @@ class binary_writer
|
||||
}
|
||||
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
|
||||
else
|
||||
{
|
||||
@ -1458,14 +1507,26 @@ class binary_writer
|
||||
{
|
||||
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)())
|
||||
{
|
||||
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)())
|
||||
{
|
||||
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
|
||||
return 'H'; // LCOV_EXCL_LINE
|
||||
}
|
||||
@ -1484,14 +1545,26 @@ class binary_writer
|
||||
{
|
||||
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)()))
|
||||
{
|
||||
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)()))
|
||||
{
|
||||
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
|
||||
return 'H'; // LCOV_EXCL_LINE
|
||||
}
|
||||
@ -1539,6 +1612,7 @@ class binary_writer
|
||||
@note This function needs to respect the system's endianness, because bytes
|
||||
in CBOR, MessagePack, and UBJSON are stored in network order (big
|
||||
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>
|
||||
void write_number(const NumberType n)
|
||||
@ -1548,7 +1622,7 @@ class binary_writer
|
||||
std::memcpy(vec.data(), &n, sizeof(NumberType));
|
||||
|
||||
// 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
|
||||
std::reverse(vec.begin(), vec.end());
|
||||
@ -1629,6 +1703,9 @@ class binary_writer
|
||||
/// whether we can assume little endianness
|
||||
const bool is_little_endian = little_endianness();
|
||||
|
||||
/// whether to write in bjdata format
|
||||
bool is_bjdata = false;
|
||||
|
||||
/// the output
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/to_bson/
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
/// @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
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/from_bson/
|
||||
template<typename InputType>
|
||||
|
||||
@ -5433,7 +5433,7 @@ namespace nlohmann
|
||||
namespace detail
|
||||
{
|
||||
/// 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 //
|
||||
@ -8471,12 +8471,13 @@ class binary_reader
|
||||
@return whether parsing was successful
|
||||
*/
|
||||
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_,
|
||||
const bool strict = true,
|
||||
const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
|
||||
{
|
||||
sax = sax_;
|
||||
format = format_;
|
||||
bool result = false;
|
||||
|
||||
switch (format)
|
||||
@ -8494,6 +8495,7 @@ class binary_reader
|
||||
break;
|
||||
|
||||
case input_format_t::ubjson:
|
||||
case input_format_t::bjdata:
|
||||
result = parse_ubjson_internal();
|
||||
break;
|
||||
|
||||
@ -8505,7 +8507,7 @@ class binary_reader
|
||||
// strict mode: next byte must be EOF
|
||||
if (result && strict)
|
||||
{
|
||||
if (format == input_format_t::ubjson)
|
||||
if (format == input_format_t::ubjson || format == input_format_t::bjdata)
|
||||
{
|
||||
get_ignore_noop();
|
||||
}
|
||||
@ -10220,7 +10222,7 @@ class binary_reader
|
||||
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;
|
||||
}
|
||||
@ -10230,52 +10232,141 @@ class binary_reader
|
||||
case 'U':
|
||||
{
|
||||
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':
|
||||
{
|
||||
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':
|
||||
{
|
||||
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':
|
||||
{
|
||||
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':
|
||||
{
|
||||
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:
|
||||
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();
|
||||
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
|
||||
@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':
|
||||
{
|
||||
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;
|
||||
}
|
||||
@ -10286,7 +10377,7 @@ class binary_reader
|
||||
case 'i':
|
||||
{
|
||||
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;
|
||||
}
|
||||
@ -10297,7 +10388,7 @@ class binary_reader
|
||||
case 'I':
|
||||
{
|
||||
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;
|
||||
}
|
||||
@ -10308,7 +10399,7 @@ class binary_reader
|
||||
case 'l':
|
||||
{
|
||||
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;
|
||||
}
|
||||
@ -10319,7 +10410,7 @@ class binary_reader
|
||||
case 'L':
|
||||
{
|
||||
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;
|
||||
}
|
||||
@ -10329,9 +10420,62 @@ class binary_reader
|
||||
|
||||
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();
|
||||
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 == '$')
|
||||
{
|
||||
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;
|
||||
}
|
||||
@ -10364,13 +10508,13 @@ class binary_reader
|
||||
get_ignore_noop();
|
||||
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;
|
||||
}
|
||||
auto last_token = get_token_string();
|
||||
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);
|
||||
@ -10393,7 +10537,7 @@ class binary_reader
|
||||
switch (prefix)
|
||||
{
|
||||
case std::char_traits<char_type>::eof(): // EOF
|
||||
return unexpect_eof(input_format_t::ubjson, "value");
|
||||
return unexpect_eof(format, "value");
|
||||
|
||||
case 'T': // true
|
||||
return sax->boolean(true);
|
||||
@ -10406,43 +10550,43 @@ class binary_reader
|
||||
case 'U':
|
||||
{
|
||||
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':
|
||||
{
|
||||
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':
|
||||
{
|
||||
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':
|
||||
{
|
||||
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':
|
||||
{
|
||||
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':
|
||||
{
|
||||
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':
|
||||
{
|
||||
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':
|
||||
@ -10453,7 +10597,7 @@ class binary_reader
|
||||
case 'C': // char
|
||||
{
|
||||
get();
|
||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "char")))
|
||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "char")))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -10461,7 +10605,7 @@ class binary_reader
|
||||
{
|
||||
auto last_token = get_token_string();
|
||||
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));
|
||||
return sax->string(s);
|
||||
@ -10481,9 +10625,74 @@ class binary_reader
|
||||
|
||||
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();
|
||||
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)
|
||||
{
|
||||
get();
|
||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "number")))
|
||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "number")))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -10662,7 +10871,7 @@ class binary_reader
|
||||
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,
|
||||
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)
|
||||
@ -10689,7 +10898,7 @@ class binary_reader
|
||||
case token_type::literal_or_value:
|
||||
default:
|
||||
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
|
||||
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);
|
||||
}
|
||||
@ -10890,6 +11100,10 @@ class binary_reader
|
||||
error_msg += "BSON";
|
||||
break;
|
||||
|
||||
case input_format_t::bjdata:
|
||||
error_msg += "BJData";
|
||||
break;
|
||||
|
||||
case input_format_t::json: // LCOV_EXCL_LINE
|
||||
default: // 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
|
||||
const bool is_little_endian = little_endianness();
|
||||
|
||||
/// sax parser format
|
||||
input_format_t format;
|
||||
|
||||
/// the SAX parser
|
||||
json_sax_t* sax = nullptr;
|
||||
};
|
||||
@ -13606,9 +13823,10 @@ class binary_writer
|
||||
|
||||
@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);
|
||||
is_bjdata = is_bjdata_;
|
||||
}
|
||||
|
||||
/*!
|
||||
@ -14902,6 +15120,14 @@ class binary_writer
|
||||
}
|
||||
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)()))
|
||||
{
|
||||
if (add_prefix)
|
||||
@ -14910,6 +15136,14 @@ class binary_writer
|
||||
}
|
||||
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)()))
|
||||
{
|
||||
if (add_prefix)
|
||||
@ -14918,6 +15152,14 @@ class binary_writer
|
||||
}
|
||||
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
|
||||
{
|
||||
if (add_prefix)
|
||||
@ -14965,6 +15207,14 @@ class binary_writer
|
||||
}
|
||||
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)())
|
||||
{
|
||||
if (add_prefix)
|
||||
@ -14973,6 +15223,14 @@ class binary_writer
|
||||
}
|
||||
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)())
|
||||
{
|
||||
if (add_prefix)
|
||||
@ -14981,6 +15239,14 @@ class binary_writer
|
||||
}
|
||||
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
|
||||
else
|
||||
{
|
||||
@ -15026,14 +15292,26 @@ class binary_writer
|
||||
{
|
||||
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)())
|
||||
{
|
||||
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)())
|
||||
{
|
||||
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
|
||||
return 'H'; // LCOV_EXCL_LINE
|
||||
}
|
||||
@ -15052,14 +15330,26 @@ class binary_writer
|
||||
{
|
||||
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)()))
|
||||
{
|
||||
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)()))
|
||||
{
|
||||
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
|
||||
return 'H'; // LCOV_EXCL_LINE
|
||||
}
|
||||
@ -15107,6 +15397,7 @@ class binary_writer
|
||||
@note This function needs to respect the system's endianness, because bytes
|
||||
in CBOR, MessagePack, and UBJSON are stored in network order (big
|
||||
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>
|
||||
void write_number(const NumberType n)
|
||||
@ -15116,7 +15407,7 @@ class binary_writer
|
||||
std::memcpy(vec.data(), &n, sizeof(NumberType));
|
||||
|
||||
// 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
|
||||
std::reverse(vec.begin(), vec.end());
|
||||
@ -15197,6 +15488,9 @@ class binary_writer
|
||||
/// whether we can assume little endianness
|
||||
const bool is_little_endian = little_endianness();
|
||||
|
||||
/// whether to write in bjdata format
|
||||
bool is_bjdata = false;
|
||||
|
||||
/// the output
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/to_bson/
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
/// @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
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/from_bson/
|
||||
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