mirror of
https://github.com/nlohmann/json.git
synced 2026-11-04 03:39:22 +00:00
Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c530cd0e01 | ||
|
|
a80aed8b74 | ||
|
|
bae6216491 | ||
|
|
7503394a30 | ||
|
|
caabb97337 |
No files matched your search
@@ -344,8 +344,8 @@ class binary_reader
|
|||||||
nested document could declare a length that disagrees with where its
|
nested document could declare a length that disagrees with where its
|
||||||
terminator actually falls and quietly hand the bytes in between to the
|
terminator actually falls and quietly hand the bytes in between to the
|
||||||
enclosing document. A well-formed document is at least 5 bytes (the prefix
|
enclosing document. A well-formed document is at least 5 bytes (the prefix
|
||||||
plus the terminator); the equality also rejects those impossible sizes,
|
plus the terminator); open_bson_document() rejects a smaller size before
|
||||||
since at least 5 bytes are always consumed.
|
it is stored, so @a document_size is never negative here.
|
||||||
|
|
||||||
@param[in] document_start value of chars_read before the size prefix
|
@param[in] document_start value of chars_read before the size prefix
|
||||||
@param[in] document_size the declared document size
|
@param[in] document_size the declared document size
|
||||||
@@ -353,7 +353,7 @@ class binary_reader
|
|||||||
*/
|
*/
|
||||||
bool check_bson_document_size(const std::size_t document_start, const std::int32_t document_size)
|
bool check_bson_document_size(const std::size_t document_start, const std::int32_t document_size)
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(document_size < 0 || static_cast<std::size_t>(document_size) != chars_read - document_start))
|
if (JSON_HEDLEY_UNLIKELY(static_cast<std::size_t>(document_size) != chars_read - document_start))
|
||||||
{
|
{
|
||||||
return bson_document_size_error(document_size, chars_read - document_start);
|
return bson_document_size_error(document_size, chars_read - document_start);
|
||||||
}
|
}
|
||||||
@@ -3091,8 +3091,6 @@ class binary_reader
|
|||||||
if (size_and_type.first != npos)
|
if (size_and_type.first != npos)
|
||||||
{
|
{
|
||||||
if (size_and_type.second != 0)
|
if (size_and_type.second != 0)
|
||||||
{
|
|
||||||
if (size_and_type.second != 'N')
|
|
||||||
{
|
{
|
||||||
for (std::size_t i = 0; i < size_and_type.first; ++i)
|
for (std::size_t i = 0; i < size_and_type.first; ++i)
|
||||||
{
|
{
|
||||||
@@ -3103,7 +3101,6 @@ class binary_reader
|
|||||||
dim.push_back(dimlen);
|
dim.push_back(dimlen);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (std::size_t i = 0; i < size_and_type.first; ++i)
|
for (std::size_t i = 0; i < size_and_type.first; ++i)
|
||||||
@@ -3303,11 +3300,9 @@ class binary_reader
|
|||||||
// follow the documented _ArrayType_, _ArraySize_, _ArrayData_ order
|
// follow the documented _ArrayType_, _ArraySize_, _ArrayData_ order
|
||||||
if (ndarray_dtype != 0)
|
if (ndarray_dtype != 0)
|
||||||
{
|
{
|
||||||
|
// every optimized type marker is_valid_optimized_type() accepts for BJData has a name
|
||||||
const char* type_name = bjd_type_name(ndarray_dtype);
|
const char* type_name = bjd_type_name(ndarray_dtype);
|
||||||
if (JSON_HEDLEY_UNLIKELY(type_name == nullptr))
|
JSON_ASSERT(type_name != nullptr);
|
||||||
{
|
|
||||||
return unexpected_byte("invalid byte", "type");
|
|
||||||
}
|
|
||||||
|
|
||||||
string_t type_key = "_ArrayType_";
|
string_t type_key = "_ArrayType_";
|
||||||
string_t type = type_name; // sax->string() takes a reference
|
string_t type = type_name; // sax->string() takes a reference
|
||||||
@@ -3390,12 +3385,6 @@ 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 (input_format == input_format_t::bjdata
|
|
||||||
&& JSON_HEDLEY_UNLIKELY(is_bjd_excluded_optimized_type(result.second)))
|
|
||||||
{
|
|
||||||
return optimized_type_error();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof("type")))
|
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof("type")))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@@ -3423,7 +3412,7 @@ class binary_reader
|
|||||||
// an ndarray was read here only if the flag flipped; when it was
|
// an ndarray was read here only if the flag flipped; when it was
|
||||||
// seeded true, get_ubjson_size_value() already rejected the nested
|
// seeded true, get_ubjson_size_value() already rejected the nested
|
||||||
// dimension vector
|
// dimension vector
|
||||||
if (input_format == input_format_t::bjdata && is_ndarray && !inside_ndarray)
|
if (is_ndarray && !inside_ndarray)
|
||||||
{
|
{
|
||||||
result.second |= (1 << 8); // use bit 8 to indicate ndarray, all UBJSON and BJData markers should be ASCII letters
|
result.second |= (1 << 8); // use bit 8 to indicate ndarray, all UBJSON and BJData markers should be ASCII letters
|
||||||
}
|
}
|
||||||
@@ -3433,7 +3422,7 @@ class binary_reader
|
|||||||
if (current == '#')
|
if (current == '#')
|
||||||
{
|
{
|
||||||
const bool is_error = get_ubjson_size_value(result.first, is_ndarray);
|
const bool is_error = get_ubjson_size_value(result.first, is_ndarray);
|
||||||
if (input_format == input_format_t::bjdata && is_ndarray && !inside_ndarray)
|
if (is_ndarray && !inside_ndarray)
|
||||||
{
|
{
|
||||||
return last_byte_error(exception_id::unexpected_byte, "ndarray requires both type and size", "size");
|
return last_byte_error(exception_id::unexpected_byte, "ndarray requires both type and size", "size");
|
||||||
}
|
}
|
||||||
@@ -3607,7 +3596,6 @@ class binary_reader
|
|||||||
JSON_HEDLEY_NEVER_INLINE
|
JSON_HEDLEY_NEVER_INLINE
|
||||||
bool parse_ubjson_typed_run(std::size_t& remaining, const char_int_type type, std::true_type /*bulk*/)
|
bool parse_ubjson_typed_run(std::size_t& remaining, const char_int_type type, std::true_type /*bulk*/)
|
||||||
{
|
{
|
||||||
const bool bjdata = input_format == input_format_t::bjdata;
|
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case 'U':
|
case 'U':
|
||||||
@@ -3621,11 +3609,11 @@ class binary_reader
|
|||||||
case 'L':
|
case 'L':
|
||||||
return parse_ubjson_typed_run<std::int64_t>(remaining);
|
return parse_ubjson_typed_run<std::int64_t>(remaining);
|
||||||
case 'u':
|
case 'u':
|
||||||
return !bjdata || parse_ubjson_typed_run<std::uint16_t>(remaining);
|
return parse_ubjson_typed_run<std::uint16_t>(remaining);
|
||||||
case 'm':
|
case 'm':
|
||||||
return !bjdata || parse_ubjson_typed_run<std::uint32_t>(remaining);
|
return parse_ubjson_typed_run<std::uint32_t>(remaining);
|
||||||
case 'M':
|
case 'M':
|
||||||
return !bjdata || parse_ubjson_typed_run<std::uint64_t>(remaining);
|
return parse_ubjson_typed_run<std::uint64_t>(remaining);
|
||||||
case 'd':
|
case 'd':
|
||||||
return parse_ubjson_typed_run<float>(remaining);
|
return parse_ubjson_typed_run<float>(remaining);
|
||||||
case 'D':
|
case 'D':
|
||||||
@@ -3706,7 +3694,7 @@ class binary_reader
|
|||||||
// if bit-8 of size_and_type.second is set to 1, encode bjdata ndarray as an object in JData annotated array format (https://github.com/NeuroJSON/jdata):
|
// if bit-8 of size_and_type.second is set to 1, encode bjdata ndarray as an object in JData annotated array format (https://github.com/NeuroJSON/jdata):
|
||||||
// {"_ArrayType_" : "typeid", "_ArraySize_" : [n1, n2, ...], "_ArrayData_" : [v1, v2, ...]}
|
// {"_ArrayType_" : "typeid", "_ArraySize_" : [n1, n2, ...], "_ArrayData_" : [v1, v2, ...]}
|
||||||
|
|
||||||
if (input_format == input_format_t::bjdata && size_and_type.first != npos && (size_and_type.second & (1 << 8)) != 0)
|
if ((size_and_type.second & (1 << 8)) != 0)
|
||||||
{
|
{
|
||||||
size_and_type.second &= ~(static_cast<char_int_type>(1) << 8); // use bit 8 to indicate ndarray, here we remove the bit to restore the type marker
|
size_and_type.second &= ~(static_cast<char_int_type>(1) << 8); // use bit 8 to indicate ndarray, here we remove the bit to restore the type marker
|
||||||
|
|
||||||
@@ -3765,7 +3753,7 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If BJData type marker is 'B' decode as binary
|
// If BJData type marker is 'B' decode as binary
|
||||||
if (input_format == input_format_t::bjdata && size_and_type.first != npos && size_and_type.second == 'B')
|
if (size_and_type.first != npos && size_and_type.second == 'B')
|
||||||
{
|
{
|
||||||
binary_t result;
|
binary_t result;
|
||||||
return get_binary(size_and_type.first, result) && sax->binary(result);
|
return get_binary(size_and_type.first, result) && sax->binary(result);
|
||||||
@@ -3814,7 +3802,7 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
|
|
||||||
// do not accept ND-array size in objects in BJData
|
// do not accept ND-array size in objects in BJData
|
||||||
if (input_format == input_format_t::bjdata && size_and_type.first != npos && (size_and_type.second & (1 << 8)) != 0)
|
if ((size_and_type.second & (1 << 8)) != 0)
|
||||||
{
|
{
|
||||||
return last_byte_error(exception_id::unexpected_byte, "BJData object does not support ND-array size in optimized format", "object");
|
return last_byte_error(exception_id::unexpected_byte, "BJData object does not support ND-array size in optimized format", "object");
|
||||||
}
|
}
|
||||||
@@ -5095,8 +5083,9 @@ class binary_reader
|
|||||||
JSON_PRIVATE_UNLESS_TESTED:
|
JSON_PRIVATE_UNLESS_TESTED:
|
||||||
/*!
|
/*!
|
||||||
@brief check whether @a marker is a type marker that may follow '$'
|
@brief check whether @a marker is a type marker that may follow '$'
|
||||||
UBJSON: Z N T F i U I l L d D H C S [ {; BJData: the numeric markers and C
|
UBJSON: Z N T F i U I l L d D H C S [ {; BJData: the numeric markers and C.
|
||||||
(BJData's excluded markers are rejected separately).
|
The markers BJData excludes (F H N S T Z [ {) are the ones
|
||||||
|
binary_writer::is_bjdata_excluded_type_marker() lists; keep the two in sync.
|
||||||
*/
|
*/
|
||||||
bool is_valid_optimized_type(const char_int_type marker) const noexcept
|
bool is_valid_optimized_type(const char_int_type marker) const noexcept
|
||||||
{
|
{
|
||||||
@@ -5131,20 +5120,6 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief whether @a marker is excluded from BJData's optimized ND-array types
|
|
||||||
@return whether @a marker is one of 'F', 'H', 'N', 'S', 'T', 'Z', '[', '{'
|
|
||||||
|
|
||||||
Mirrors binary_writer's @ref binary_writer::is_bjdata_excluded_type_marker
|
|
||||||
"is_bjdata_excluded_type_marker()`, which encodes the same list the other
|
|
||||||
way; keep the two in sync.
|
|
||||||
*/
|
|
||||||
static constexpr bool is_bjd_excluded_optimized_type(const char_int_type marker) noexcept
|
|
||||||
{
|
|
||||||
return marker == '[' || marker == '{' || marker == 'S' || marker == 'H'
|
|
||||||
|| marker == 'T' || marker == 'F' || marker == 'N' || marker == 'Z';
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief look up the ND-array element type name for a BJData dtype marker
|
@brief look up the ND-array element type name for a BJData dtype marker
|
||||||
@return the type name ("uint8", "int8", ...), or nullptr if @a marker does
|
@return the type name ("uint8", "int8", ...), or nullptr if @a marker does
|
||||||
|
|||||||
@@ -3126,22 +3126,19 @@ class binary_writer
|
|||||||
// negative fixnum
|
// negative fixnum
|
||||||
write_number(static_cast<std::int8_t>(j.m_data.m_value.number_integer));
|
write_number(static_cast<std::int8_t>(j.m_data.m_value.number_integer));
|
||||||
}
|
}
|
||||||
else if (j.m_data.m_value.number_integer >= (std::numeric_limits<std::int8_t>::min)() &&
|
else if (j.m_data.m_value.number_integer >= (std::numeric_limits<std::int8_t>::min)())
|
||||||
j.m_data.m_value.number_integer <= (std::numeric_limits<std::int8_t>::max)())
|
|
||||||
{
|
{
|
||||||
// int 8
|
// int 8
|
||||||
oa.write_character(to_char_type(0xD0));
|
oa.write_character(to_char_type(0xD0));
|
||||||
write_number(static_cast<std::int8_t>(j.m_data.m_value.number_integer));
|
write_number(static_cast<std::int8_t>(j.m_data.m_value.number_integer));
|
||||||
}
|
}
|
||||||
else if (j.m_data.m_value.number_integer >= (std::numeric_limits<std::int16_t>::min)() &&
|
else if (j.m_data.m_value.number_integer >= (std::numeric_limits<std::int16_t>::min)())
|
||||||
j.m_data.m_value.number_integer <= (std::numeric_limits<std::int16_t>::max)())
|
|
||||||
{
|
{
|
||||||
// int 16
|
// int 16
|
||||||
oa.write_character(to_char_type(0xD1));
|
oa.write_character(to_char_type(0xD1));
|
||||||
write_number(static_cast<std::int16_t>(j.m_data.m_value.number_integer));
|
write_number(static_cast<std::int16_t>(j.m_data.m_value.number_integer));
|
||||||
}
|
}
|
||||||
else if (j.m_data.m_value.number_integer >= (std::numeric_limits<std::int32_t>::min)() &&
|
else if (j.m_data.m_value.number_integer >= (std::numeric_limits<std::int32_t>::min)())
|
||||||
j.m_data.m_value.number_integer <= (std::numeric_limits<std::int32_t>::max)())
|
|
||||||
{
|
{
|
||||||
// int 32
|
// int 32
|
||||||
oa.write_character(to_char_type(0xD2));
|
oa.write_character(to_char_type(0xD2));
|
||||||
@@ -4057,7 +4054,7 @@ class binary_writer
|
|||||||
Containers, strings, high-precision numbers, booleans and null cannot be
|
Containers, strings, high-precision numbers, booleans and null cannot be
|
||||||
declared as the single type of an optimized container in BJData; such a
|
declared as the single type of an optimized container in BJData; such a
|
||||||
container is written unoptimized. The reader rejects them with the same
|
container is written unoptimized. The reader rejects them with the same
|
||||||
list (binary_reader::is_bjd_excluded_optimized_type()).
|
list (binary_reader::is_valid_optimized_type()).
|
||||||
*/
|
*/
|
||||||
static constexpr bool is_bjdata_excluded_type_marker(const CharType marker) noexcept
|
static constexpr bool is_bjdata_excluded_type_marker(const CharType marker) noexcept
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -116,10 +116,6 @@ inline void copy_short(CharType* dst, const CharType* src, const std::size_t len
|
|||||||
/// the number of characters an empty vector is grown to by output_vector_sink
|
/// the number of characters an empty vector is grown to by output_vector_sink
|
||||||
constexpr std::size_t min_vector_sink_capacity = 64;
|
constexpr std::size_t min_vector_sink_capacity = 64;
|
||||||
|
|
||||||
/// the number of characters output_vector_sink makes writable ahead of its
|
|
||||||
/// position at a time: 64 KiB of the vector's capacity, not all of it
|
|
||||||
constexpr std::size_t vector_sink_window = 65536;
|
|
||||||
|
|
||||||
/// @brief non-virtual output sink writing into a std::vector
|
/// @brief non-virtual output sink writing into a std::vector
|
||||||
///
|
///
|
||||||
/// This sink is not part of the virtual output_adapter_protocol hierarchy: it is
|
/// This sink is not part of the virtual output_adapter_protocol hierarchy: it is
|
||||||
@@ -133,8 +129,7 @@ constexpr std::size_t vector_sink_window = 65536;
|
|||||||
/// encoding them: insert() is an out-of-line call into a routine that can insert
|
/// encoding them: insert() is an out-of-line call into a routine that can insert
|
||||||
/// anywhere, and resize() is one as well with libstdc++. The sink therefore keeps
|
/// anywhere, and resize() is one as well with libstdc++. The sink therefore keeps
|
||||||
/// its own write position: it grows the vector geometrically ahead of the
|
/// its own write position: it grows the vector geometrically ahead of the
|
||||||
/// position (making @ref vector_sink_window characters of it writable at a
|
/// position and copies each write to it, so a write is a capacity check and a
|
||||||
/// time) and copies each write to it, so a write is a capacity check and a
|
|
||||||
/// short copy. The destructor shrinks the vector to the bytes actually written,
|
/// short copy. The destructor shrinks the vector to the bytes actually written,
|
||||||
/// also when a write throws; shrinking a vector never allocates. The sink owns
|
/// also when a write throws; shrinking a vector never allocates. The sink owns
|
||||||
/// that trim, so it can be moved but not copied, and the vector must not be used
|
/// that trim, so it can be moved but not copied, and the vector must not be used
|
||||||
@@ -282,17 +277,9 @@ class output_vector_sink
|
|||||||
// reallocate for every few bytes
|
// reallocate for every few bytes
|
||||||
const std::size_t written = position();
|
const std::size_t written = position();
|
||||||
const std::size_t needed = written + length;
|
const std::size_t needed = written + length;
|
||||||
const std::size_t capacity = (std::max)(min_vector_sink_capacity, needed <= v->capacity()
|
v->resize((std::max)(min_vector_sink_capacity, needed <= v->capacity()
|
||||||
? v->capacity()
|
? v->capacity()
|
||||||
: (std::max)(needed, v->capacity() * 2));
|
: (std::max)(needed, v->capacity() * 2)));
|
||||||
v->reserve(capacity);
|
|
||||||
// resize() zeroes the characters it adds. Zeroing all of a doubled
|
|
||||||
// capacity, megabytes of a fresh allocation, at once faults in its
|
|
||||||
// pages and evicts them from the cache before the writer gets there,
|
|
||||||
// so its stores miss again; a window ahead of the position is still
|
|
||||||
// cached when they come (the binary writers on a long array of
|
|
||||||
// numbers: 4-26 % faster on x86, 1-2.5 % on Apple M1)
|
|
||||||
v->resize((std::min)(capacity, (std::max)(needed, written + vector_sink_window)));
|
|
||||||
cursor = v->data() + written;
|
cursor = v->data() + written;
|
||||||
limit = v->data() + v->size();
|
limit = v->data() + v->size();
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+23
-64
@@ -18359,8 +18359,8 @@ class binary_reader
|
|||||||
nested document could declare a length that disagrees with where its
|
nested document could declare a length that disagrees with where its
|
||||||
terminator actually falls and quietly hand the bytes in between to the
|
terminator actually falls and quietly hand the bytes in between to the
|
||||||
enclosing document. A well-formed document is at least 5 bytes (the prefix
|
enclosing document. A well-formed document is at least 5 bytes (the prefix
|
||||||
plus the terminator); the equality also rejects those impossible sizes,
|
plus the terminator); open_bson_document() rejects a smaller size before
|
||||||
since at least 5 bytes are always consumed.
|
it is stored, so @a document_size is never negative here.
|
||||||
|
|
||||||
@param[in] document_start value of chars_read before the size prefix
|
@param[in] document_start value of chars_read before the size prefix
|
||||||
@param[in] document_size the declared document size
|
@param[in] document_size the declared document size
|
||||||
@@ -18368,7 +18368,7 @@ class binary_reader
|
|||||||
*/
|
*/
|
||||||
bool check_bson_document_size(const std::size_t document_start, const std::int32_t document_size)
|
bool check_bson_document_size(const std::size_t document_start, const std::int32_t document_size)
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(document_size < 0 || static_cast<std::size_t>(document_size) != chars_read - document_start))
|
if (JSON_HEDLEY_UNLIKELY(static_cast<std::size_t>(document_size) != chars_read - document_start))
|
||||||
{
|
{
|
||||||
return bson_document_size_error(document_size, chars_read - document_start);
|
return bson_document_size_error(document_size, chars_read - document_start);
|
||||||
}
|
}
|
||||||
@@ -21106,8 +21106,6 @@ class binary_reader
|
|||||||
if (size_and_type.first != npos)
|
if (size_and_type.first != npos)
|
||||||
{
|
{
|
||||||
if (size_and_type.second != 0)
|
if (size_and_type.second != 0)
|
||||||
{
|
|
||||||
if (size_and_type.second != 'N')
|
|
||||||
{
|
{
|
||||||
for (std::size_t i = 0; i < size_and_type.first; ++i)
|
for (std::size_t i = 0; i < size_and_type.first; ++i)
|
||||||
{
|
{
|
||||||
@@ -21118,7 +21116,6 @@ class binary_reader
|
|||||||
dim.push_back(dimlen);
|
dim.push_back(dimlen);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (std::size_t i = 0; i < size_and_type.first; ++i)
|
for (std::size_t i = 0; i < size_and_type.first; ++i)
|
||||||
@@ -21318,11 +21315,9 @@ class binary_reader
|
|||||||
// follow the documented _ArrayType_, _ArraySize_, _ArrayData_ order
|
// follow the documented _ArrayType_, _ArraySize_, _ArrayData_ order
|
||||||
if (ndarray_dtype != 0)
|
if (ndarray_dtype != 0)
|
||||||
{
|
{
|
||||||
|
// every optimized type marker is_valid_optimized_type() accepts for BJData has a name
|
||||||
const char* type_name = bjd_type_name(ndarray_dtype);
|
const char* type_name = bjd_type_name(ndarray_dtype);
|
||||||
if (JSON_HEDLEY_UNLIKELY(type_name == nullptr))
|
JSON_ASSERT(type_name != nullptr);
|
||||||
{
|
|
||||||
return unexpected_byte("invalid byte", "type");
|
|
||||||
}
|
|
||||||
|
|
||||||
string_t type_key = "_ArrayType_";
|
string_t type_key = "_ArrayType_";
|
||||||
string_t type = type_name; // sax->string() takes a reference
|
string_t type = type_name; // sax->string() takes a reference
|
||||||
@@ -21405,12 +21400,6 @@ 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 (input_format == input_format_t::bjdata
|
|
||||||
&& JSON_HEDLEY_UNLIKELY(is_bjd_excluded_optimized_type(result.second)))
|
|
||||||
{
|
|
||||||
return optimized_type_error();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof("type")))
|
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof("type")))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@@ -21438,7 +21427,7 @@ class binary_reader
|
|||||||
// an ndarray was read here only if the flag flipped; when it was
|
// an ndarray was read here only if the flag flipped; when it was
|
||||||
// seeded true, get_ubjson_size_value() already rejected the nested
|
// seeded true, get_ubjson_size_value() already rejected the nested
|
||||||
// dimension vector
|
// dimension vector
|
||||||
if (input_format == input_format_t::bjdata && is_ndarray && !inside_ndarray)
|
if (is_ndarray && !inside_ndarray)
|
||||||
{
|
{
|
||||||
result.second |= (1 << 8); // use bit 8 to indicate ndarray, all UBJSON and BJData markers should be ASCII letters
|
result.second |= (1 << 8); // use bit 8 to indicate ndarray, all UBJSON and BJData markers should be ASCII letters
|
||||||
}
|
}
|
||||||
@@ -21448,7 +21437,7 @@ class binary_reader
|
|||||||
if (current == '#')
|
if (current == '#')
|
||||||
{
|
{
|
||||||
const bool is_error = get_ubjson_size_value(result.first, is_ndarray);
|
const bool is_error = get_ubjson_size_value(result.first, is_ndarray);
|
||||||
if (input_format == input_format_t::bjdata && is_ndarray && !inside_ndarray)
|
if (is_ndarray && !inside_ndarray)
|
||||||
{
|
{
|
||||||
return last_byte_error(exception_id::unexpected_byte, "ndarray requires both type and size", "size");
|
return last_byte_error(exception_id::unexpected_byte, "ndarray requires both type and size", "size");
|
||||||
}
|
}
|
||||||
@@ -21622,7 +21611,6 @@ class binary_reader
|
|||||||
JSON_HEDLEY_NEVER_INLINE
|
JSON_HEDLEY_NEVER_INLINE
|
||||||
bool parse_ubjson_typed_run(std::size_t& remaining, const char_int_type type, std::true_type /*bulk*/)
|
bool parse_ubjson_typed_run(std::size_t& remaining, const char_int_type type, std::true_type /*bulk*/)
|
||||||
{
|
{
|
||||||
const bool bjdata = input_format == input_format_t::bjdata;
|
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case 'U':
|
case 'U':
|
||||||
@@ -21636,11 +21624,11 @@ class binary_reader
|
|||||||
case 'L':
|
case 'L':
|
||||||
return parse_ubjson_typed_run<std::int64_t>(remaining);
|
return parse_ubjson_typed_run<std::int64_t>(remaining);
|
||||||
case 'u':
|
case 'u':
|
||||||
return !bjdata || parse_ubjson_typed_run<std::uint16_t>(remaining);
|
return parse_ubjson_typed_run<std::uint16_t>(remaining);
|
||||||
case 'm':
|
case 'm':
|
||||||
return !bjdata || parse_ubjson_typed_run<std::uint32_t>(remaining);
|
return parse_ubjson_typed_run<std::uint32_t>(remaining);
|
||||||
case 'M':
|
case 'M':
|
||||||
return !bjdata || parse_ubjson_typed_run<std::uint64_t>(remaining);
|
return parse_ubjson_typed_run<std::uint64_t>(remaining);
|
||||||
case 'd':
|
case 'd':
|
||||||
return parse_ubjson_typed_run<float>(remaining);
|
return parse_ubjson_typed_run<float>(remaining);
|
||||||
case 'D':
|
case 'D':
|
||||||
@@ -21721,7 +21709,7 @@ class binary_reader
|
|||||||
// if bit-8 of size_and_type.second is set to 1, encode bjdata ndarray as an object in JData annotated array format (https://github.com/NeuroJSON/jdata):
|
// if bit-8 of size_and_type.second is set to 1, encode bjdata ndarray as an object in JData annotated array format (https://github.com/NeuroJSON/jdata):
|
||||||
// {"_ArrayType_" : "typeid", "_ArraySize_" : [n1, n2, ...], "_ArrayData_" : [v1, v2, ...]}
|
// {"_ArrayType_" : "typeid", "_ArraySize_" : [n1, n2, ...], "_ArrayData_" : [v1, v2, ...]}
|
||||||
|
|
||||||
if (input_format == input_format_t::bjdata && size_and_type.first != npos && (size_and_type.second & (1 << 8)) != 0)
|
if ((size_and_type.second & (1 << 8)) != 0)
|
||||||
{
|
{
|
||||||
size_and_type.second &= ~(static_cast<char_int_type>(1) << 8); // use bit 8 to indicate ndarray, here we remove the bit to restore the type marker
|
size_and_type.second &= ~(static_cast<char_int_type>(1) << 8); // use bit 8 to indicate ndarray, here we remove the bit to restore the type marker
|
||||||
|
|
||||||
@@ -21780,7 +21768,7 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If BJData type marker is 'B' decode as binary
|
// If BJData type marker is 'B' decode as binary
|
||||||
if (input_format == input_format_t::bjdata && size_and_type.first != npos && size_and_type.second == 'B')
|
if (size_and_type.first != npos && size_and_type.second == 'B')
|
||||||
{
|
{
|
||||||
binary_t result;
|
binary_t result;
|
||||||
return get_binary(size_and_type.first, result) && sax->binary(result);
|
return get_binary(size_and_type.first, result) && sax->binary(result);
|
||||||
@@ -21829,7 +21817,7 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
|
|
||||||
// do not accept ND-array size in objects in BJData
|
// do not accept ND-array size in objects in BJData
|
||||||
if (input_format == input_format_t::bjdata && size_and_type.first != npos && (size_and_type.second & (1 << 8)) != 0)
|
if ((size_and_type.second & (1 << 8)) != 0)
|
||||||
{
|
{
|
||||||
return last_byte_error(exception_id::unexpected_byte, "BJData object does not support ND-array size in optimized format", "object");
|
return last_byte_error(exception_id::unexpected_byte, "BJData object does not support ND-array size in optimized format", "object");
|
||||||
}
|
}
|
||||||
@@ -23110,8 +23098,9 @@ class binary_reader
|
|||||||
JSON_PRIVATE_UNLESS_TESTED:
|
JSON_PRIVATE_UNLESS_TESTED:
|
||||||
/*!
|
/*!
|
||||||
@brief check whether @a marker is a type marker that may follow '$'
|
@brief check whether @a marker is a type marker that may follow '$'
|
||||||
UBJSON: Z N T F i U I l L d D H C S [ {; BJData: the numeric markers and C
|
UBJSON: Z N T F i U I l L d D H C S [ {; BJData: the numeric markers and C.
|
||||||
(BJData's excluded markers are rejected separately).
|
The markers BJData excludes (F H N S T Z [ {) are the ones
|
||||||
|
binary_writer::is_bjdata_excluded_type_marker() lists; keep the two in sync.
|
||||||
*/
|
*/
|
||||||
bool is_valid_optimized_type(const char_int_type marker) const noexcept
|
bool is_valid_optimized_type(const char_int_type marker) const noexcept
|
||||||
{
|
{
|
||||||
@@ -23146,20 +23135,6 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief whether @a marker is excluded from BJData's optimized ND-array types
|
|
||||||
@return whether @a marker is one of 'F', 'H', 'N', 'S', 'T', 'Z', '[', '{'
|
|
||||||
|
|
||||||
Mirrors binary_writer's @ref binary_writer::is_bjdata_excluded_type_marker
|
|
||||||
"is_bjdata_excluded_type_marker()`, which encodes the same list the other
|
|
||||||
way; keep the two in sync.
|
|
||||||
*/
|
|
||||||
static constexpr bool is_bjd_excluded_optimized_type(const char_int_type marker) noexcept
|
|
||||||
{
|
|
||||||
return marker == '[' || marker == '{' || marker == 'S' || marker == 'H'
|
|
||||||
|| marker == 'T' || marker == 'F' || marker == 'N' || marker == 'Z';
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief look up the ND-array element type name for a BJData dtype marker
|
@brief look up the ND-array element type name for a BJData dtype marker
|
||||||
@return the type name ("uint8", "int8", ...), or nullptr if @a marker does
|
@return the type name ("uint8", "int8", ...), or nullptr if @a marker does
|
||||||
@@ -27491,10 +27466,6 @@ inline void copy_short(CharType* dst, const CharType* src, const std::size_t len
|
|||||||
/// the number of characters an empty vector is grown to by output_vector_sink
|
/// the number of characters an empty vector is grown to by output_vector_sink
|
||||||
constexpr std::size_t min_vector_sink_capacity = 64;
|
constexpr std::size_t min_vector_sink_capacity = 64;
|
||||||
|
|
||||||
/// the number of characters output_vector_sink makes writable ahead of its
|
|
||||||
/// position at a time: 64 KiB of the vector's capacity, not all of it
|
|
||||||
constexpr std::size_t vector_sink_window = 65536;
|
|
||||||
|
|
||||||
/// @brief non-virtual output sink writing into a std::vector
|
/// @brief non-virtual output sink writing into a std::vector
|
||||||
///
|
///
|
||||||
/// This sink is not part of the virtual output_adapter_protocol hierarchy: it is
|
/// This sink is not part of the virtual output_adapter_protocol hierarchy: it is
|
||||||
@@ -27508,8 +27479,7 @@ constexpr std::size_t vector_sink_window = 65536;
|
|||||||
/// encoding them: insert() is an out-of-line call into a routine that can insert
|
/// encoding them: insert() is an out-of-line call into a routine that can insert
|
||||||
/// anywhere, and resize() is one as well with libstdc++. The sink therefore keeps
|
/// anywhere, and resize() is one as well with libstdc++. The sink therefore keeps
|
||||||
/// its own write position: it grows the vector geometrically ahead of the
|
/// its own write position: it grows the vector geometrically ahead of the
|
||||||
/// position (making @ref vector_sink_window characters of it writable at a
|
/// position and copies each write to it, so a write is a capacity check and a
|
||||||
/// time) and copies each write to it, so a write is a capacity check and a
|
|
||||||
/// short copy. The destructor shrinks the vector to the bytes actually written,
|
/// short copy. The destructor shrinks the vector to the bytes actually written,
|
||||||
/// also when a write throws; shrinking a vector never allocates. The sink owns
|
/// also when a write throws; shrinking a vector never allocates. The sink owns
|
||||||
/// that trim, so it can be moved but not copied, and the vector must not be used
|
/// that trim, so it can be moved but not copied, and the vector must not be used
|
||||||
@@ -27657,17 +27627,9 @@ class output_vector_sink
|
|||||||
// reallocate for every few bytes
|
// reallocate for every few bytes
|
||||||
const std::size_t written = position();
|
const std::size_t written = position();
|
||||||
const std::size_t needed = written + length;
|
const std::size_t needed = written + length;
|
||||||
const std::size_t capacity = (std::max)(min_vector_sink_capacity, needed <= v->capacity()
|
v->resize((std::max)(min_vector_sink_capacity, needed <= v->capacity()
|
||||||
? v->capacity()
|
? v->capacity()
|
||||||
: (std::max)(needed, v->capacity() * 2));
|
: (std::max)(needed, v->capacity() * 2)));
|
||||||
v->reserve(capacity);
|
|
||||||
// resize() zeroes the characters it adds. Zeroing all of a doubled
|
|
||||||
// capacity, megabytes of a fresh allocation, at once faults in its
|
|
||||||
// pages and evicts them from the cache before the writer gets there,
|
|
||||||
// so its stores miss again; a window ahead of the position is still
|
|
||||||
// cached when they come (the binary writers on a long array of
|
|
||||||
// numbers: 4-26 % faster on x86, 1-2.5 % on Apple M1)
|
|
||||||
v->resize((std::min)(capacity, (std::max)(needed, written + vector_sink_window)));
|
|
||||||
cursor = v->data() + written;
|
cursor = v->data() + written;
|
||||||
limit = v->data() + v->size();
|
limit = v->data() + v->size();
|
||||||
}
|
}
|
||||||
@@ -31123,22 +31085,19 @@ class binary_writer
|
|||||||
// negative fixnum
|
// negative fixnum
|
||||||
write_number(static_cast<std::int8_t>(j.m_data.m_value.number_integer));
|
write_number(static_cast<std::int8_t>(j.m_data.m_value.number_integer));
|
||||||
}
|
}
|
||||||
else if (j.m_data.m_value.number_integer >= (std::numeric_limits<std::int8_t>::min)() &&
|
else if (j.m_data.m_value.number_integer >= (std::numeric_limits<std::int8_t>::min)())
|
||||||
j.m_data.m_value.number_integer <= (std::numeric_limits<std::int8_t>::max)())
|
|
||||||
{
|
{
|
||||||
// int 8
|
// int 8
|
||||||
oa.write_character(to_char_type(0xD0));
|
oa.write_character(to_char_type(0xD0));
|
||||||
write_number(static_cast<std::int8_t>(j.m_data.m_value.number_integer));
|
write_number(static_cast<std::int8_t>(j.m_data.m_value.number_integer));
|
||||||
}
|
}
|
||||||
else if (j.m_data.m_value.number_integer >= (std::numeric_limits<std::int16_t>::min)() &&
|
else if (j.m_data.m_value.number_integer >= (std::numeric_limits<std::int16_t>::min)())
|
||||||
j.m_data.m_value.number_integer <= (std::numeric_limits<std::int16_t>::max)())
|
|
||||||
{
|
{
|
||||||
// int 16
|
// int 16
|
||||||
oa.write_character(to_char_type(0xD1));
|
oa.write_character(to_char_type(0xD1));
|
||||||
write_number(static_cast<std::int16_t>(j.m_data.m_value.number_integer));
|
write_number(static_cast<std::int16_t>(j.m_data.m_value.number_integer));
|
||||||
}
|
}
|
||||||
else if (j.m_data.m_value.number_integer >= (std::numeric_limits<std::int32_t>::min)() &&
|
else if (j.m_data.m_value.number_integer >= (std::numeric_limits<std::int32_t>::min)())
|
||||||
j.m_data.m_value.number_integer <= (std::numeric_limits<std::int32_t>::max)())
|
|
||||||
{
|
{
|
||||||
// int 32
|
// int 32
|
||||||
oa.write_character(to_char_type(0xD2));
|
oa.write_character(to_char_type(0xD2));
|
||||||
@@ -32054,7 +32013,7 @@ class binary_writer
|
|||||||
Containers, strings, high-precision numbers, booleans and null cannot be
|
Containers, strings, high-precision numbers, booleans and null cannot be
|
||||||
declared as the single type of an optimized container in BJData; such a
|
declared as the single type of an optimized container in BJData; such a
|
||||||
container is written unoptimized. The reader rejects them with the same
|
container is written unoptimized. The reader rejects them with the same
|
||||||
list (binary_reader::is_bjd_excluded_optimized_type()).
|
list (binary_reader::is_valid_optimized_type()).
|
||||||
*/
|
*/
|
||||||
static constexpr bool is_bjdata_excluded_type_marker(const CharType marker) noexcept
|
static constexpr bool is_bjdata_excluded_type_marker(const CharType marker) noexcept
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -50,14 +50,6 @@ std::vector<json> test_values()
|
|||||||
large_payloads.push_back(json::binary(std::vector<std::uint8_t>(static_cast<std::size_t>(100 * i), static_cast<std::uint8_t>(i))));
|
large_payloads.push_back(json::binary(std::vector<std::uint8_t>(static_cast<std::size_t>(100 * i), static_cast<std::uint8_t>(i))));
|
||||||
}
|
}
|
||||||
|
|
||||||
// several times vector_sink_window characters in every format: the vector
|
|
||||||
// sink makes the capacity it grows to writable a window at a time
|
|
||||||
json many_windows = json::array();
|
|
||||||
for (std::uint64_t i = 0; i < 30000; ++i)
|
|
||||||
{
|
|
||||||
many_windows.push_back((i * 0x9E3779B97F4A7C15u) >> 1u);
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
return
|
||||||
{
|
{
|
||||||
json(nullptr), json(true), json(false),
|
json(nullptr), json(true), json(false),
|
||||||
@@ -69,7 +61,7 @@ std::vector<json> test_values()
|
|||||||
json::array(), json::object(),
|
json::array(), json::object(),
|
||||||
json::array({1, 2, 3}), json({{"a", 1}, {"b", nullptr}}),
|
json::array({1, 2, 3}), json({{"a", 1}, {"b", nullptr}}),
|
||||||
json({{"nested", {{"deep", json::array({1, "two", 3.0, nullptr})}}}}),
|
json({{"nested", {{"deep", json::array({1, "two", 3.0, nullptr})}}}}),
|
||||||
big_array, big_object, large_payloads, many_windows,
|
big_array, big_object, large_payloads,
|
||||||
json::binary(std::vector<std::uint8_t>(5000, 0x42))
|
json::binary(std::vector<std::uint8_t>(5000, 0x42))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,23 +78,25 @@ TEST_CASE("BJData")
|
|||||||
{
|
{
|
||||||
SECTION("binary_reader BJData lookup tables")
|
SECTION("binary_reader BJData lookup tables")
|
||||||
{
|
{
|
||||||
// both lookups are static member functions
|
// bjd_type_name() is static; is_valid_optimized_type() needs a BJData reader
|
||||||
std::vector<std::uint8_t> const data;
|
std::vector<std::uint8_t> const data;
|
||||||
using reader_t = nlohmann::detail::binary_reader<json, decltype(nlohmann::detail::input_adapter(data))>;
|
using reader_t = nlohmann::detail::binary_reader<json, decltype(nlohmann::detail::input_adapter(data))>;
|
||||||
|
|
||||||
// the excluded optimized-type markers must match binary_writer's
|
// the markers BJData excludes as optimized types must match
|
||||||
// is_bjdata_excluded_type_marker(), which encodes the same 8 markers
|
// binary_writer's is_bjdata_excluded_type_marker(), which encodes the
|
||||||
|
// same 8 markers
|
||||||
|
reader_t reader(nlohmann::detail::input_adapter(data), json::input_format_t::bjdata);
|
||||||
for (const char marker :
|
for (const char marker :
|
||||||
{'[', '{', 'S', 'H', 'T', 'F', 'N', 'Z'
|
{'[', '{', 'S', 'H', 'T', 'F', 'N', 'Z', 'x'
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
CHECK(reader_t::is_bjd_excluded_optimized_type(static_cast<unsigned char>(marker)));
|
CHECK(!reader.is_valid_optimized_type(static_cast<unsigned char>(marker)));
|
||||||
}
|
}
|
||||||
for (const char marker :
|
for (const char marker :
|
||||||
{'U', 'i', 'u', 'I', 'm', 'l', 'M', 'L', 'd', 'D', 'C', 'B', 'x'
|
{'U', 'i', 'u', 'I', 'm', 'l', 'M', 'L', 'd', 'D', 'C', 'B', 'h'
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
CHECK(!reader_t::is_bjd_excluded_optimized_type(static_cast<unsigned char>(marker)));
|
CHECK(reader.is_valid_optimized_type(static_cast<unsigned char>(marker)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// every dtype marker must round-trip to its ND-array type name
|
// every dtype marker must round-trip to its ND-array type name
|
||||||
|
|||||||
Reference in new issue
Block a user