Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1c588321c5 | ||
|
|
cff8548496 |
No files matched your search
@@ -465,15 +465,17 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
|
|
||||||
const std::size_t element_type_parse_position = chars_read;
|
const std::size_t element_type_parse_position = chars_read;
|
||||||
|
// an array's elements are named "0", "1", ... in the wire format,
|
||||||
|
// and those names are not passed on: they are skipped unless
|
||||||
|
// error_handler_t::strict must reject ill-formed UTF-8 in them
|
||||||
|
const bool is_object = container_stack.back().is_object;
|
||||||
key.clear();
|
key.clear();
|
||||||
if (JSON_HEDLEY_UNLIKELY(!get_bson_cstr(key)))
|
if (JSON_HEDLEY_UNLIKELY(!get_bson_cstr(key, is_object || error_handler == error_handler_t::strict)))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// an array's elements are named "0", "1", ... in the wire format,
|
if (is_object && !sax->key(key))
|
||||||
// and those names are not passed on
|
|
||||||
if (container_stack.back().is_object && !sax->key(key))
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -489,14 +491,18 @@ class binary_reader
|
|||||||
@brief Parses a C-style string from the BSON input.
|
@brief Parses a C-style string from the BSON input.
|
||||||
@param[in,out] result A reference to the string variable where the read
|
@param[in,out] result A reference to the string variable where the read
|
||||||
string is to be stored.
|
string is to be stored.
|
||||||
|
@param[in] store whether the characters are needed; if not (the names of
|
||||||
|
an array's elements), they are skipped and @a result may
|
||||||
|
be left unchanged, and they are not checked against
|
||||||
|
@ref error_handler
|
||||||
@return `true` if the \\x00-byte indicating the end of the string was
|
@return `true` if the \\x00-byte indicating the end of the string was
|
||||||
encountered before the EOF; false` indicates an unexpected EOF.
|
encountered before the EOF; false` indicates an unexpected EOF.
|
||||||
*/
|
*/
|
||||||
bool get_bson_cstr(string_t& result)
|
bool get_bson_cstr(string_t& result, const bool store = true)
|
||||||
{
|
{
|
||||||
if (get_bson_cstr_bulk(result, std::integral_constant<bool, bulk_scan> {}))
|
if (get_bson_cstr_bulk(result, store, std::integral_constant<bool, bulk_scan> {}))
|
||||||
{
|
{
|
||||||
return check_string_utf8(result, "key");
|
return !store || check_string_utf8(result, "key");
|
||||||
}
|
}
|
||||||
|
|
||||||
auto out = std::back_inserter(result);
|
auto out = std::back_inserter(result);
|
||||||
@@ -509,7 +515,7 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
if (current == 0x00)
|
if (current == 0x00)
|
||||||
{
|
{
|
||||||
return check_string_utf8(result, "key");
|
return !store || check_string_utf8(result, "key");
|
||||||
}
|
}
|
||||||
*out++ = static_cast<typename string_t::value_type>(current);
|
*out++ = static_cast<typename string_t::value_type>(current);
|
||||||
}
|
}
|
||||||
@@ -519,10 +525,11 @@ class binary_reader
|
|||||||
@brief read a C-style string from contiguous input in one step
|
@brief read a C-style string from contiguous input in one step
|
||||||
|
|
||||||
@param[in,out] result the string to append to
|
@param[in,out] result the string to append to
|
||||||
|
@param[in] store whether to append the characters to @a result
|
||||||
@return whether the string was read; if the input has no \\x00-byte, nothing
|
@return whether the string was read; if the input has no \\x00-byte, nothing
|
||||||
is read, and @ref get_bson_cstr reports the end of the input
|
is read, and @ref get_bson_cstr reports the end of the input
|
||||||
*/
|
*/
|
||||||
bool get_bson_cstr_bulk(string_t& result, std::true_type /*bulk*/)
|
bool get_bson_cstr_bulk(string_t& result, const bool store, std::true_type /*bulk*/)
|
||||||
{
|
{
|
||||||
const std::size_t remaining = ia.bulk_remaining();
|
const std::size_t remaining = ia.bulk_remaining();
|
||||||
if (remaining == 0)
|
if (remaining == 0)
|
||||||
@@ -530,18 +537,15 @@ class binary_reader
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const auto* const data = reinterpret_cast<const unsigned char*>(ia.bulk_data());
|
const auto* const data = reinterpret_cast<const unsigned char*>(ia.bulk_data());
|
||||||
// a plain loop rather than std::memchr: most keys are short (array
|
const std::size_t length = find_zero_byte(data, remaining);
|
||||||
// indices are keys, too), and the call would cost more than it saves
|
|
||||||
std::size_t length = 0;
|
|
||||||
while (length < remaining && data[length] != 0x00)
|
|
||||||
{
|
|
||||||
++length;
|
|
||||||
}
|
|
||||||
if (length == remaining)
|
if (length == remaining)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
result.append(reinterpret_cast<const typename string_t::value_type*>(data), length);
|
if (store)
|
||||||
|
{
|
||||||
|
result.append(reinterpret_cast<const typename string_t::value_type*>(data), length);
|
||||||
|
}
|
||||||
// consume the string and its \x00-byte, as the byte-wise path does
|
// consume the string and its \x00-byte, as the byte-wise path does
|
||||||
ia.bulk_skip(length + 1);
|
ia.bulk_skip(length + 1);
|
||||||
chars_read += length + 1;
|
chars_read += length + 1;
|
||||||
@@ -550,7 +554,7 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// input that is not contiguous: C-style strings are read byte by byte
|
/// input that is not contiguous: C-style strings are read byte by byte
|
||||||
bool get_bson_cstr_bulk(string_t& /*result*/, std::false_type /*bulk*/) const noexcept
|
bool get_bson_cstr_bulk(string_t& /*result*/, const bool /*store*/, std::false_type /*bulk*/) const noexcept
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -268,10 +268,10 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
public:
|
public:
|
||||||
using token_type = typename lexer_base<BasicJsonType>::token_type;
|
using token_type = typename lexer_base<BasicJsonType>::token_type;
|
||||||
|
|
||||||
explicit lexer(InputAdapterType&& adapter, bool ignore_comments_ = false, bool discard_values_ = false) noexcept
|
explicit lexer(InputAdapterType&& adapter, bool ignore_comments_ = false, bool discard_number_values_ = false) noexcept
|
||||||
: ia(std::move(adapter))
|
: ia(std::move(adapter))
|
||||||
, ignore_comments(ignore_comments_)
|
, ignore_comments(ignore_comments_)
|
||||||
, discard_values(discard_values_)
|
, discard_number_values(discard_number_values_)
|
||||||
{
|
{
|
||||||
// the token string of an error before the first token is everything
|
// the token string of an error before the first token is everything
|
||||||
// read so far
|
// read so far
|
||||||
@@ -388,8 +388,6 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
checks if it is inside the range. If a violation was detected, set up an
|
checks if it is inside the range. If a violation was detected, set up an
|
||||||
error message and return false. Otherwise, return true.
|
error message and return false. Otherwise, return true.
|
||||||
|
|
||||||
@param[in] keep whether the bytes are added to token_buffer (false for
|
|
||||||
accept(), see scan())
|
|
||||||
@param[in] ranges list of integers; interpreted as list of pairs of
|
@param[in] ranges list of integers; interpreted as list of pairs of
|
||||||
inclusive lower and upper bound, respectively
|
inclusive lower and upper bound, respectively
|
||||||
|
|
||||||
@@ -398,18 +396,17 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
|
|
||||||
@return true if and only if no range violation was detected
|
@return true if and only if no range violation was detected
|
||||||
*/
|
*/
|
||||||
template<bool Keep>
|
bool next_byte_in_range(std::initializer_list<char_int_type> ranges)
|
||||||
bool next_byte_in_range(std::integral_constant<bool, Keep> keep, std::initializer_list<char_int_type> ranges)
|
|
||||||
{
|
{
|
||||||
JSON_ASSERT(ranges.size() == 2 || ranges.size() == 4 || ranges.size() == 6);
|
JSON_ASSERT(ranges.size() == 2 || ranges.size() == 4 || ranges.size() == 6);
|
||||||
add_string_char(keep, current);
|
add(current);
|
||||||
|
|
||||||
for (auto range = ranges.begin(); range != ranges.end(); ++range)
|
for (auto range = ranges.begin(); range != ranges.end(); ++range)
|
||||||
{
|
{
|
||||||
get();
|
get();
|
||||||
if (JSON_HEDLEY_LIKELY(*range <= current && current <= *(++range))) // NOLINT(bugprone-inc-dec-in-conditions)
|
if (JSON_HEDLEY_LIKELY(*range <= current && current <= *(++range))) // NOLINT(bugprone-inc-dec-in-conditions)
|
||||||
{
|
{
|
||||||
add_string_char(keep, current);
|
add(current);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -422,16 +419,14 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// contiguous input: bulk-append the run of ordinary characters and complete
|
/// contiguous input: bulk-append the run of ordinary characters and complete
|
||||||
/// well-formed UTF-8 sequences starting at the current read position (only
|
/// well-formed UTF-8 sequences starting at the current read position, then
|
||||||
/// skip it if @a keep is false), then
|
|
||||||
/// read the first byte that needs individual handling (the closing quote, an
|
/// read the first byte that needs individual handling (the closing quote, an
|
||||||
/// escape, a control character, or an ill-formed UTF-8 byte) like get().
|
/// escape, a control character, or an ill-formed UTF-8 byte) like get().
|
||||||
/// That byte is taken from the buffer that was just scanned. Through get(),
|
/// That byte is taken from the buffer that was just scanned. Through get(),
|
||||||
/// Apple Clang merged this read with the other get() calls of the scan
|
/// Apple Clang merged this read with the other get() calls of the scan
|
||||||
/// function, which load the read position again right after it was stored
|
/// function, which load the read position again right after it was stored
|
||||||
/// (3% of json::accept() on jeopardy.json on Apple M1).
|
/// (3% of json::accept() on jeopardy.json on Apple M1).
|
||||||
template<bool Keep>
|
char_int_type get_after_string_run(std::true_type /*bulk*/)
|
||||||
char_int_type get_after_string_run(std::true_type /*bulk*/, std::integral_constant<bool, Keep> keep)
|
|
||||||
{
|
{
|
||||||
// a pending unget must be consumed through the normal path first
|
// a pending unget must be consumed through the normal path first
|
||||||
if (next_unget)
|
if (next_unget)
|
||||||
@@ -449,7 +444,7 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
const std::size_t pos = lexer_string_bulk_run(data, remaining);
|
const std::size_t pos = lexer_string_bulk_run(data, remaining);
|
||||||
if (pos != 0)
|
if (pos != 0)
|
||||||
{
|
{
|
||||||
append_string_run(keep, data, pos);
|
token_buffer.append(reinterpret_cast<const typename string_t::value_type*>(data), pos);
|
||||||
bulk_consume(reinterpret_cast<const char*>(data), pos);
|
bulk_consume(reinterpret_cast<const char*>(data), pos);
|
||||||
}
|
}
|
||||||
if (pos == remaining)
|
if (pos == remaining)
|
||||||
@@ -493,8 +488,7 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// streaming input: no bulk fast path
|
/// streaming input: no bulk fast path
|
||||||
template<bool Keep>
|
char_int_type get_after_string_run(std::false_type /*bulk*/)
|
||||||
char_int_type get_after_string_run(std::false_type /*bulk*/, std::integral_constant<bool, Keep> /*keep*/)
|
|
||||||
{
|
{
|
||||||
return get();
|
return get();
|
||||||
}
|
}
|
||||||
@@ -508,18 +502,13 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
may contain \0 bytes), and token_buffer.size() is the number of bytes in the
|
may contain \0 bytes), and token_buffer.size() is the number of bytes in the
|
||||||
string.
|
string.
|
||||||
|
|
||||||
@param[in] keep whether the string is copied into token_buffer; if false
|
|
||||||
(accept(), see scan()), it is only checked, with the same
|
|
||||||
errors, and token_buffer stays empty
|
|
||||||
|
|
||||||
@return token_type::value_string if string could be successfully scanned,
|
@return token_type::value_string if string could be successfully scanned,
|
||||||
token_type::parse_error otherwise
|
token_type::parse_error otherwise
|
||||||
|
|
||||||
@note In case of errors, variable error_message contains a textual
|
@note In case of errors, variable error_message contains a textual
|
||||||
description.
|
description.
|
||||||
*/
|
*/
|
||||||
template<bool Keep>
|
token_type scan_string()
|
||||||
token_type scan_string(std::integral_constant<bool, Keep> keep)
|
|
||||||
{
|
{
|
||||||
// reset token_buffer (ignore opening quote)
|
// reset token_buffer (ignore opening quote)
|
||||||
reset();
|
reset();
|
||||||
@@ -531,7 +520,7 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
{
|
{
|
||||||
// bulk-consume ordinary characters from contiguous input, then
|
// bulk-consume ordinary characters from contiguous input, then
|
||||||
// get the next character, the one that ends the run
|
// get the next character, the one that ends the run
|
||||||
switch (get_after_string_run(std::integral_constant<bool, bulk_scan>(), keep))
|
switch (get_after_string_run(std::integral_constant<bool, bulk_scan>()))
|
||||||
{
|
{
|
||||||
// end of file while parsing the string
|
// end of file while parsing the string
|
||||||
case char_traits<char_type>::eof():
|
case char_traits<char_type>::eof():
|
||||||
@@ -553,35 +542,35 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
{
|
{
|
||||||
// quotation mark
|
// quotation mark
|
||||||
case '\"':
|
case '\"':
|
||||||
add_string_char(keep, '\"');
|
add('\"');
|
||||||
break;
|
break;
|
||||||
// reverse solidus
|
// reverse solidus
|
||||||
case '\\':
|
case '\\':
|
||||||
add_string_char(keep, '\\');
|
add('\\');
|
||||||
break;
|
break;
|
||||||
// solidus
|
// solidus
|
||||||
case '/':
|
case '/':
|
||||||
add_string_char(keep, '/');
|
add('/');
|
||||||
break;
|
break;
|
||||||
// backspace
|
// backspace
|
||||||
case 'b':
|
case 'b':
|
||||||
add_string_char(keep, '\b');
|
add('\b');
|
||||||
break;
|
break;
|
||||||
// form feed
|
// form feed
|
||||||
case 'f':
|
case 'f':
|
||||||
add_string_char(keep, '\f');
|
add('\f');
|
||||||
break;
|
break;
|
||||||
// line feed
|
// line feed
|
||||||
case 'n':
|
case 'n':
|
||||||
add_string_char(keep, '\n');
|
add('\n');
|
||||||
break;
|
break;
|
||||||
// carriage return
|
// carriage return
|
||||||
case 'r':
|
case 'r':
|
||||||
add_string_char(keep, '\r');
|
add('\r');
|
||||||
break;
|
break;
|
||||||
// tab
|
// tab
|
||||||
case 't':
|
case 't':
|
||||||
add_string_char(keep, '\t');
|
add('\t');
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// unicode escapes
|
// unicode escapes
|
||||||
@@ -649,7 +638,10 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
JSON_ASSERT(0x00 <= codepoint && codepoint <= 0x10FFFF);
|
JSON_ASSERT(0x00 <= codepoint && codepoint <= 0x10FFFF);
|
||||||
|
|
||||||
// translate codepoint into bytes
|
// translate codepoint into bytes
|
||||||
add_codepoint(keep, codepoint);
|
encode_utf8(static_cast<std::uint32_t>(codepoint), [this](std::uint32_t byte)
|
||||||
|
{
|
||||||
|
add(static_cast<char_int_type>(byte));
|
||||||
|
});
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -832,7 +824,7 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
case 0x7E:
|
case 0x7E:
|
||||||
case 0x7F:
|
case 0x7F:
|
||||||
{
|
{
|
||||||
add_string_char(keep, current);
|
add(current);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -868,7 +860,7 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
case 0xDE:
|
case 0xDE:
|
||||||
case 0xDF:
|
case 0xDF:
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!next_byte_in_range(keep, {0x80, 0xBF})))
|
if (JSON_HEDLEY_UNLIKELY(!next_byte_in_range({0x80, 0xBF})))
|
||||||
{
|
{
|
||||||
return token_type::parse_error;
|
return token_type::parse_error;
|
||||||
}
|
}
|
||||||
@@ -878,7 +870,7 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
// U+0800..U+0FFF: bytes E0 A0..BF 80..BF
|
// U+0800..U+0FFF: bytes E0 A0..BF 80..BF
|
||||||
case 0xE0:
|
case 0xE0:
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range(keep, {0xA0, 0xBF, 0x80, 0xBF}))))
|
if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0xA0, 0xBF, 0x80, 0xBF}))))
|
||||||
{
|
{
|
||||||
return token_type::parse_error;
|
return token_type::parse_error;
|
||||||
}
|
}
|
||||||
@@ -902,7 +894,7 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
case 0xEE:
|
case 0xEE:
|
||||||
case 0xEF:
|
case 0xEF:
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range(keep, {0x80, 0xBF, 0x80, 0xBF}))))
|
if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF}))))
|
||||||
{
|
{
|
||||||
return token_type::parse_error;
|
return token_type::parse_error;
|
||||||
}
|
}
|
||||||
@@ -912,7 +904,7 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
// U+D000..U+D7FF: bytes ED 80..9F 80..BF
|
// U+D000..U+D7FF: bytes ED 80..9F 80..BF
|
||||||
case 0xED:
|
case 0xED:
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range(keep, {0x80, 0x9F, 0x80, 0xBF}))))
|
if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0x9F, 0x80, 0xBF}))))
|
||||||
{
|
{
|
||||||
return token_type::parse_error;
|
return token_type::parse_error;
|
||||||
}
|
}
|
||||||
@@ -922,7 +914,7 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
// U+10000..U+3FFFF F0 90..BF 80..BF 80..BF
|
// U+10000..U+3FFFF F0 90..BF 80..BF 80..BF
|
||||||
case 0xF0:
|
case 0xF0:
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range(keep, {0x90, 0xBF, 0x80, 0xBF, 0x80, 0xBF}))))
|
if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x90, 0xBF, 0x80, 0xBF, 0x80, 0xBF}))))
|
||||||
{
|
{
|
||||||
return token_type::parse_error;
|
return token_type::parse_error;
|
||||||
}
|
}
|
||||||
@@ -934,7 +926,7 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
case 0xF2:
|
case 0xF2:
|
||||||
case 0xF3:
|
case 0xF3:
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range(keep, {0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF}))))
|
if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF}))))
|
||||||
{
|
{
|
||||||
return token_type::parse_error;
|
return token_type::parse_error;
|
||||||
}
|
}
|
||||||
@@ -944,7 +936,7 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
// U+100000..U+10FFFF F4 80..8F 80..BF 80..BF
|
// U+100000..U+10FFFF F4 80..8F 80..BF 80..BF
|
||||||
case 0xF4:
|
case 0xF4:
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range(keep, {0x80, 0x8F, 0x80, 0xBF, 0x80, 0xBF}))))
|
if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0x8F, 0x80, 0xBF, 0x80, 0xBF}))))
|
||||||
{
|
{
|
||||||
return token_type::parse_error;
|
return token_type::parse_error;
|
||||||
}
|
}
|
||||||
@@ -1435,7 +1427,7 @@ scan_number_done:
|
|||||||
token_type convert_number(token_type number_type, std::size_t mantissa_end)
|
token_type convert_number(token_type number_type, std::size_t mantissa_end)
|
||||||
{
|
{
|
||||||
// accept() only needs to know whether the input is valid, so it sets
|
// accept() only needs to know whether the input is valid, so it sets
|
||||||
// discard_values (see json.hpp), and an integer token whose
|
// discard_number_values (see json.hpp), and an integer token whose
|
||||||
// digit count shows that it fits is reported without calling
|
// digit count shows that it fits is reported without calling
|
||||||
// convert_integer(). A number with up to 18 digits always fits into
|
// convert_integer(). A number with up to 18 digits always fits into
|
||||||
// both std::uint64_t and std::int64_t (18 nines is about 1e18, below
|
// both std::uint64_t and std::int64_t (18 nines is about 1e18, below
|
||||||
@@ -1450,7 +1442,7 @@ scan_number_done:
|
|||||||
// json_sax_acceptor, whose number callbacks discard their argument and
|
// json_sax_acceptor, whose number callbacks discard their argument and
|
||||||
// return true, and the parser rejects neither integers nor finite
|
// return true, and the parser rejects neither integers nor finite
|
||||||
// floats. value_unsigned/value_integer are left unset here, so a caller
|
// floats. value_unsigned/value_integer are left unset here, so a caller
|
||||||
// that reads the converted value must not set discard_values.
|
// that reads the converted value must not set discard_number_values.
|
||||||
//
|
//
|
||||||
// On contiguous input, scan_number_bulk_contiguous() converts integer
|
// On contiguous input, scan_number_bulk_contiguous() converts integer
|
||||||
// tokens itself and does not pass them to this function, unless
|
// tokens itself and does not pass them to this function, unless
|
||||||
@@ -1458,7 +1450,7 @@ scan_number_done:
|
|||||||
// reached for input without bulk access (e.g. streams), with
|
// reached for input without bulk access (e.g. streams), with
|
||||||
// JSON_DIAGNOSTIC_POSITIONS, or when scan_number_bulk_contiguous()
|
// JSON_DIAGNOSTIC_POSITIONS, or when scan_number_bulk_contiguous()
|
||||||
// falls back to scan_number().
|
// falls back to scan_number().
|
||||||
if (discard_values)
|
if (discard_number_values)
|
||||||
{
|
{
|
||||||
constexpr std::size_t safe_digit_count = 18;
|
constexpr std::size_t safe_digit_count = 18;
|
||||||
if (number_type == token_type::value_unsigned && token_buffer.size() <= safe_digit_count)
|
if (number_type == token_type::value_unsigned && token_buffer.size() <= safe_digit_count)
|
||||||
@@ -1709,7 +1701,7 @@ scan_number_done:
|
|||||||
// w < 10^19, so a value below 10^max_exponent10 cannot overflow, and a
|
// w < 10^19, so a value below 10^max_exponent10 cannot overflow, and a
|
||||||
// value that underflows is a finite number as well. Only a token that
|
// value that underflows is a finite number as well. Only a token that
|
||||||
// may overflow has to be converted to find out.
|
// may overflow has to be converted to find out.
|
||||||
if (JSON_HEDLEY_UNLIKELY(discard_values && s.exponent + 19 <= std::numeric_limits<number_float_t>::max_exponent10))
|
if (JSON_HEDLEY_UNLIKELY(discard_number_values && s.exponent + 19 <= std::numeric_limits<number_float_t>::max_exponent10))
|
||||||
{
|
{
|
||||||
value_float = 0;
|
value_float = 0;
|
||||||
return true;
|
return true;
|
||||||
@@ -2041,34 +2033,6 @@ scan_number_done:
|
|||||||
token_buffer.push_back(static_cast<typename string_t::value_type>(c));
|
token_buffer.push_back(static_cast<typename string_t::value_type>(c));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// add a character of a string token; accept() checks the string but does
|
|
||||||
/// not keep its value (@a keep is false)
|
|
||||||
void add_string_char(std::true_type /*keep*/, char_int_type c)
|
|
||||||
{
|
|
||||||
token_buffer.push_back(static_cast<typename string_t::value_type>(c));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void add_string_char(std::false_type /*keep*/, char_int_type /*c*/) noexcept {}
|
|
||||||
|
|
||||||
/// add the UTF-8 bytes of a code point from a \\u escape to a string token
|
|
||||||
void add_codepoint(std::true_type /*keep*/, int codepoint)
|
|
||||||
{
|
|
||||||
encode_utf8(static_cast<std::uint32_t>(codepoint), [this](std::uint32_t byte)
|
|
||||||
{
|
|
||||||
add(static_cast<char_int_type>(byte));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
static void add_codepoint(std::false_type /*keep*/, int /*codepoint*/) noexcept {}
|
|
||||||
|
|
||||||
/// append a run of ordinary string characters (see get_after_string_run())
|
|
||||||
void append_string_run(std::true_type /*keep*/, const unsigned char* data, std::size_t n)
|
|
||||||
{
|
|
||||||
token_buffer.append(reinterpret_cast<const typename string_t::value_type*>(data), n);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void append_string_run(std::false_type /*keep*/, const unsigned char* /*data*/, std::size_t /*n*/) noexcept {}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/////////////////////
|
/////////////////////
|
||||||
// value getters
|
// value getters
|
||||||
@@ -2463,11 +2427,7 @@ scan_number_done:
|
|||||||
|
|
||||||
// string
|
// string
|
||||||
case '\"':
|
case '\"':
|
||||||
// accept() (discard_values) checks strings without copying them
|
return scan_string();
|
||||||
// into token_buffer. The choice is made once per string: a
|
|
||||||
// check inside the string loop made number-heavy documents up
|
|
||||||
// to 3 % slower (mesh.json, Apple M1)
|
|
||||||
return discard_values ? scan_string(std::false_type {}) : scan_string(std::true_type {});
|
|
||||||
|
|
||||||
// number
|
// number
|
||||||
case '-':
|
case '-':
|
||||||
@@ -2552,13 +2512,11 @@ scan_number_done:
|
|||||||
std::size_t decimal_point_position = std::string::npos;
|
std::size_t decimal_point_position = std::string::npos;
|
||||||
|
|
||||||
/// whether the caller only needs the token types and never looks at the
|
/// whether the caller only needs the token types and never looks at the
|
||||||
/// values of number and string tokens; set only by accept(), which parses
|
/// converted numeric values; set only by accept(), which parses through
|
||||||
/// through json_sax_acceptor. When set, convert_number() skips converting
|
/// json_sax_acceptor. When set, convert_number() skips converting integer
|
||||||
/// integer tokens whose digit count guarantees that they fit into 64 bits
|
/// tokens whose digit count guarantees that they fit into 64 bits (see
|
||||||
/// (see there), floats are converted only when they may overflow (see
|
/// there)
|
||||||
/// convert_accumulated_float()), and strings are checked without being
|
const bool discard_number_values = false;
|
||||||
/// copied into token_buffer (see scan())
|
|
||||||
const bool discard_values = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|||||||
@@ -89,9 +89,9 @@ class parser
|
|||||||
const bool allow_exceptions_ = true,
|
const bool allow_exceptions_ = true,
|
||||||
const bool ignore_comments = false,
|
const bool ignore_comments = false,
|
||||||
const bool ignore_trailing_commas_ = false,
|
const bool ignore_trailing_commas_ = false,
|
||||||
const bool discard_values_ = false)
|
const bool discard_number_values_ = false)
|
||||||
: callback(std::move(cb))
|
: callback(std::move(cb))
|
||||||
, m_lexer(std::move(adapter), ignore_comments, discard_values_)
|
, m_lexer(std::move(adapter), ignore_comments, discard_number_values_)
|
||||||
, allow_exceptions(allow_exceptions_)
|
, allow_exceptions(allow_exceptions_)
|
||||||
, ignore_trailing_commas(ignore_trailing_commas_)
|
, ignore_trailing_commas(ignore_trailing_commas_)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -90,6 +90,34 @@ JSON_HEDLEY_ALWAYS_INLINE std::size_t find_string_special(const unsigned char* d
|
|||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// return the index of the first 0x00 byte in [data, data+n), or n if there is
|
||||||
|
// none; scans 8 bytes at a time (BSON's C-style strings)
|
||||||
|
JSON_HEDLEY_ALWAYS_INLINE std::size_t find_zero_byte(const unsigned char* data, std::size_t n) noexcept
|
||||||
|
{
|
||||||
|
constexpr std::uint64_t ones = 0x0101010101010101ull;
|
||||||
|
constexpr std::uint64_t high = 0x8080808080808080ull;
|
||||||
|
std::size_t i = 0;
|
||||||
|
for (; i + 8 <= n; i += 8)
|
||||||
|
{
|
||||||
|
const std::uint64_t v = read_eight_bytes(data + i);
|
||||||
|
const std::uint64_t zero = (v - ones) & ~v & high;
|
||||||
|
if (zero != 0)
|
||||||
|
{
|
||||||
|
// the lowest flagged byte is the first zero byte (see
|
||||||
|
// find_string_special())
|
||||||
|
return i + (static_cast<std::size_t>(count_trailing_zeros(zero)) / 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (; i < n; ++i)
|
||||||
|
{
|
||||||
|
if (data[i] == 0x00)
|
||||||
|
{
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
// classify a byte as one the serializer must NOT copy verbatim when
|
// classify a byte as one the serializer must NOT copy verbatim when
|
||||||
// ensure_ascii is requested: the closing quote, an escape, a control character
|
// ensure_ascii is requested: the closing quote, an escape, a control character
|
||||||
// (< 0x20), DEL (0x7F), or any non-ASCII byte (>= 0x80). Everything else -
|
// (< 0x20), DEL (0x7F), or any non-ASCII byte (>= 0x80). Everything else -
|
||||||
|
|||||||
@@ -184,11 +184,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
const bool allow_exceptions = true,
|
const bool allow_exceptions = true,
|
||||||
const bool ignore_comments = false,
|
const bool ignore_comments = false,
|
||||||
const bool ignore_trailing_commas = false,
|
const bool ignore_trailing_commas = false,
|
||||||
const bool discard_values = false
|
const bool discard_number_values = false
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
return ::nlohmann::detail::parser<basic_json, InputAdapterType>(std::move(adapter),
|
return ::nlohmann::detail::parser<basic_json, InputAdapterType>(std::move(adapter),
|
||||||
std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas, discard_values);
|
std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas, discard_number_values);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
Generated
+95
-105
@@ -12458,6 +12458,34 @@ JSON_HEDLEY_ALWAYS_INLINE std::size_t find_string_special(const unsigned char* d
|
|||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// return the index of the first 0x00 byte in [data, data+n), or n if there is
|
||||||
|
// none; scans 8 bytes at a time (BSON's C-style strings)
|
||||||
|
JSON_HEDLEY_ALWAYS_INLINE std::size_t find_zero_byte(const unsigned char* data, std::size_t n) noexcept
|
||||||
|
{
|
||||||
|
constexpr std::uint64_t ones = 0x0101010101010101ull;
|
||||||
|
constexpr std::uint64_t high = 0x8080808080808080ull;
|
||||||
|
std::size_t i = 0;
|
||||||
|
for (; i + 8 <= n; i += 8)
|
||||||
|
{
|
||||||
|
const std::uint64_t v = read_eight_bytes(data + i);
|
||||||
|
const std::uint64_t zero = (v - ones) & ~v & high;
|
||||||
|
if (zero != 0)
|
||||||
|
{
|
||||||
|
// the lowest flagged byte is the first zero byte (see
|
||||||
|
// find_string_special())
|
||||||
|
return i + (static_cast<std::size_t>(count_trailing_zeros(zero)) / 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (; i < n; ++i)
|
||||||
|
{
|
||||||
|
if (data[i] == 0x00)
|
||||||
|
{
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
// classify a byte as one the serializer must NOT copy verbatim when
|
// classify a byte as one the serializer must NOT copy verbatim when
|
||||||
// ensure_ascii is requested: the closing quote, an escape, a control character
|
// ensure_ascii is requested: the closing quote, an escape, a control character
|
||||||
// (< 0x20), DEL (0x7F), or any non-ASCII byte (>= 0x80). Everything else -
|
// (< 0x20), DEL (0x7F), or any non-ASCII byte (>= 0x80). Everything else -
|
||||||
@@ -13124,10 +13152,10 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
public:
|
public:
|
||||||
using token_type = typename lexer_base<BasicJsonType>::token_type;
|
using token_type = typename lexer_base<BasicJsonType>::token_type;
|
||||||
|
|
||||||
explicit lexer(InputAdapterType&& adapter, bool ignore_comments_ = false, bool discard_values_ = false) noexcept
|
explicit lexer(InputAdapterType&& adapter, bool ignore_comments_ = false, bool discard_number_values_ = false) noexcept
|
||||||
: ia(std::move(adapter))
|
: ia(std::move(adapter))
|
||||||
, ignore_comments(ignore_comments_)
|
, ignore_comments(ignore_comments_)
|
||||||
, discard_values(discard_values_)
|
, discard_number_values(discard_number_values_)
|
||||||
{
|
{
|
||||||
// the token string of an error before the first token is everything
|
// the token string of an error before the first token is everything
|
||||||
// read so far
|
// read so far
|
||||||
@@ -13244,8 +13272,6 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
checks if it is inside the range. If a violation was detected, set up an
|
checks if it is inside the range. If a violation was detected, set up an
|
||||||
error message and return false. Otherwise, return true.
|
error message and return false. Otherwise, return true.
|
||||||
|
|
||||||
@param[in] keep whether the bytes are added to token_buffer (false for
|
|
||||||
accept(), see scan())
|
|
||||||
@param[in] ranges list of integers; interpreted as list of pairs of
|
@param[in] ranges list of integers; interpreted as list of pairs of
|
||||||
inclusive lower and upper bound, respectively
|
inclusive lower and upper bound, respectively
|
||||||
|
|
||||||
@@ -13254,18 +13280,17 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
|
|
||||||
@return true if and only if no range violation was detected
|
@return true if and only if no range violation was detected
|
||||||
*/
|
*/
|
||||||
template<bool Keep>
|
bool next_byte_in_range(std::initializer_list<char_int_type> ranges)
|
||||||
bool next_byte_in_range(std::integral_constant<bool, Keep> keep, std::initializer_list<char_int_type> ranges)
|
|
||||||
{
|
{
|
||||||
JSON_ASSERT(ranges.size() == 2 || ranges.size() == 4 || ranges.size() == 6);
|
JSON_ASSERT(ranges.size() == 2 || ranges.size() == 4 || ranges.size() == 6);
|
||||||
add_string_char(keep, current);
|
add(current);
|
||||||
|
|
||||||
for (auto range = ranges.begin(); range != ranges.end(); ++range)
|
for (auto range = ranges.begin(); range != ranges.end(); ++range)
|
||||||
{
|
{
|
||||||
get();
|
get();
|
||||||
if (JSON_HEDLEY_LIKELY(*range <= current && current <= *(++range))) // NOLINT(bugprone-inc-dec-in-conditions)
|
if (JSON_HEDLEY_LIKELY(*range <= current && current <= *(++range))) // NOLINT(bugprone-inc-dec-in-conditions)
|
||||||
{
|
{
|
||||||
add_string_char(keep, current);
|
add(current);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -13278,16 +13303,14 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// contiguous input: bulk-append the run of ordinary characters and complete
|
/// contiguous input: bulk-append the run of ordinary characters and complete
|
||||||
/// well-formed UTF-8 sequences starting at the current read position (only
|
/// well-formed UTF-8 sequences starting at the current read position, then
|
||||||
/// skip it if @a keep is false), then
|
|
||||||
/// read the first byte that needs individual handling (the closing quote, an
|
/// read the first byte that needs individual handling (the closing quote, an
|
||||||
/// escape, a control character, or an ill-formed UTF-8 byte) like get().
|
/// escape, a control character, or an ill-formed UTF-8 byte) like get().
|
||||||
/// That byte is taken from the buffer that was just scanned. Through get(),
|
/// That byte is taken from the buffer that was just scanned. Through get(),
|
||||||
/// Apple Clang merged this read with the other get() calls of the scan
|
/// Apple Clang merged this read with the other get() calls of the scan
|
||||||
/// function, which load the read position again right after it was stored
|
/// function, which load the read position again right after it was stored
|
||||||
/// (3% of json::accept() on jeopardy.json on Apple M1).
|
/// (3% of json::accept() on jeopardy.json on Apple M1).
|
||||||
template<bool Keep>
|
char_int_type get_after_string_run(std::true_type /*bulk*/)
|
||||||
char_int_type get_after_string_run(std::true_type /*bulk*/, std::integral_constant<bool, Keep> keep)
|
|
||||||
{
|
{
|
||||||
// a pending unget must be consumed through the normal path first
|
// a pending unget must be consumed through the normal path first
|
||||||
if (next_unget)
|
if (next_unget)
|
||||||
@@ -13305,7 +13328,7 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
const std::size_t pos = lexer_string_bulk_run(data, remaining);
|
const std::size_t pos = lexer_string_bulk_run(data, remaining);
|
||||||
if (pos != 0)
|
if (pos != 0)
|
||||||
{
|
{
|
||||||
append_string_run(keep, data, pos);
|
token_buffer.append(reinterpret_cast<const typename string_t::value_type*>(data), pos);
|
||||||
bulk_consume(reinterpret_cast<const char*>(data), pos);
|
bulk_consume(reinterpret_cast<const char*>(data), pos);
|
||||||
}
|
}
|
||||||
if (pos == remaining)
|
if (pos == remaining)
|
||||||
@@ -13349,8 +13372,7 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// streaming input: no bulk fast path
|
/// streaming input: no bulk fast path
|
||||||
template<bool Keep>
|
char_int_type get_after_string_run(std::false_type /*bulk*/)
|
||||||
char_int_type get_after_string_run(std::false_type /*bulk*/, std::integral_constant<bool, Keep> /*keep*/)
|
|
||||||
{
|
{
|
||||||
return get();
|
return get();
|
||||||
}
|
}
|
||||||
@@ -13364,18 +13386,13 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
may contain \0 bytes), and token_buffer.size() is the number of bytes in the
|
may contain \0 bytes), and token_buffer.size() is the number of bytes in the
|
||||||
string.
|
string.
|
||||||
|
|
||||||
@param[in] keep whether the string is copied into token_buffer; if false
|
|
||||||
(accept(), see scan()), it is only checked, with the same
|
|
||||||
errors, and token_buffer stays empty
|
|
||||||
|
|
||||||
@return token_type::value_string if string could be successfully scanned,
|
@return token_type::value_string if string could be successfully scanned,
|
||||||
token_type::parse_error otherwise
|
token_type::parse_error otherwise
|
||||||
|
|
||||||
@note In case of errors, variable error_message contains a textual
|
@note In case of errors, variable error_message contains a textual
|
||||||
description.
|
description.
|
||||||
*/
|
*/
|
||||||
template<bool Keep>
|
token_type scan_string()
|
||||||
token_type scan_string(std::integral_constant<bool, Keep> keep)
|
|
||||||
{
|
{
|
||||||
// reset token_buffer (ignore opening quote)
|
// reset token_buffer (ignore opening quote)
|
||||||
reset();
|
reset();
|
||||||
@@ -13387,7 +13404,7 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
{
|
{
|
||||||
// bulk-consume ordinary characters from contiguous input, then
|
// bulk-consume ordinary characters from contiguous input, then
|
||||||
// get the next character, the one that ends the run
|
// get the next character, the one that ends the run
|
||||||
switch (get_after_string_run(std::integral_constant<bool, bulk_scan>(), keep))
|
switch (get_after_string_run(std::integral_constant<bool, bulk_scan>()))
|
||||||
{
|
{
|
||||||
// end of file while parsing the string
|
// end of file while parsing the string
|
||||||
case char_traits<char_type>::eof():
|
case char_traits<char_type>::eof():
|
||||||
@@ -13409,35 +13426,35 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
{
|
{
|
||||||
// quotation mark
|
// quotation mark
|
||||||
case '\"':
|
case '\"':
|
||||||
add_string_char(keep, '\"');
|
add('\"');
|
||||||
break;
|
break;
|
||||||
// reverse solidus
|
// reverse solidus
|
||||||
case '\\':
|
case '\\':
|
||||||
add_string_char(keep, '\\');
|
add('\\');
|
||||||
break;
|
break;
|
||||||
// solidus
|
// solidus
|
||||||
case '/':
|
case '/':
|
||||||
add_string_char(keep, '/');
|
add('/');
|
||||||
break;
|
break;
|
||||||
// backspace
|
// backspace
|
||||||
case 'b':
|
case 'b':
|
||||||
add_string_char(keep, '\b');
|
add('\b');
|
||||||
break;
|
break;
|
||||||
// form feed
|
// form feed
|
||||||
case 'f':
|
case 'f':
|
||||||
add_string_char(keep, '\f');
|
add('\f');
|
||||||
break;
|
break;
|
||||||
// line feed
|
// line feed
|
||||||
case 'n':
|
case 'n':
|
||||||
add_string_char(keep, '\n');
|
add('\n');
|
||||||
break;
|
break;
|
||||||
// carriage return
|
// carriage return
|
||||||
case 'r':
|
case 'r':
|
||||||
add_string_char(keep, '\r');
|
add('\r');
|
||||||
break;
|
break;
|
||||||
// tab
|
// tab
|
||||||
case 't':
|
case 't':
|
||||||
add_string_char(keep, '\t');
|
add('\t');
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// unicode escapes
|
// unicode escapes
|
||||||
@@ -13505,7 +13522,10 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
JSON_ASSERT(0x00 <= codepoint && codepoint <= 0x10FFFF);
|
JSON_ASSERT(0x00 <= codepoint && codepoint <= 0x10FFFF);
|
||||||
|
|
||||||
// translate codepoint into bytes
|
// translate codepoint into bytes
|
||||||
add_codepoint(keep, codepoint);
|
encode_utf8(static_cast<std::uint32_t>(codepoint), [this](std::uint32_t byte)
|
||||||
|
{
|
||||||
|
add(static_cast<char_int_type>(byte));
|
||||||
|
});
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -13688,7 +13708,7 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
case 0x7E:
|
case 0x7E:
|
||||||
case 0x7F:
|
case 0x7F:
|
||||||
{
|
{
|
||||||
add_string_char(keep, current);
|
add(current);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -13724,7 +13744,7 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
case 0xDE:
|
case 0xDE:
|
||||||
case 0xDF:
|
case 0xDF:
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!next_byte_in_range(keep, {0x80, 0xBF})))
|
if (JSON_HEDLEY_UNLIKELY(!next_byte_in_range({0x80, 0xBF})))
|
||||||
{
|
{
|
||||||
return token_type::parse_error;
|
return token_type::parse_error;
|
||||||
}
|
}
|
||||||
@@ -13734,7 +13754,7 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
// U+0800..U+0FFF: bytes E0 A0..BF 80..BF
|
// U+0800..U+0FFF: bytes E0 A0..BF 80..BF
|
||||||
case 0xE0:
|
case 0xE0:
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range(keep, {0xA0, 0xBF, 0x80, 0xBF}))))
|
if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0xA0, 0xBF, 0x80, 0xBF}))))
|
||||||
{
|
{
|
||||||
return token_type::parse_error;
|
return token_type::parse_error;
|
||||||
}
|
}
|
||||||
@@ -13758,7 +13778,7 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
case 0xEE:
|
case 0xEE:
|
||||||
case 0xEF:
|
case 0xEF:
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range(keep, {0x80, 0xBF, 0x80, 0xBF}))))
|
if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF}))))
|
||||||
{
|
{
|
||||||
return token_type::parse_error;
|
return token_type::parse_error;
|
||||||
}
|
}
|
||||||
@@ -13768,7 +13788,7 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
// U+D000..U+D7FF: bytes ED 80..9F 80..BF
|
// U+D000..U+D7FF: bytes ED 80..9F 80..BF
|
||||||
case 0xED:
|
case 0xED:
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range(keep, {0x80, 0x9F, 0x80, 0xBF}))))
|
if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0x9F, 0x80, 0xBF}))))
|
||||||
{
|
{
|
||||||
return token_type::parse_error;
|
return token_type::parse_error;
|
||||||
}
|
}
|
||||||
@@ -13778,7 +13798,7 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
// U+10000..U+3FFFF F0 90..BF 80..BF 80..BF
|
// U+10000..U+3FFFF F0 90..BF 80..BF 80..BF
|
||||||
case 0xF0:
|
case 0xF0:
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range(keep, {0x90, 0xBF, 0x80, 0xBF, 0x80, 0xBF}))))
|
if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x90, 0xBF, 0x80, 0xBF, 0x80, 0xBF}))))
|
||||||
{
|
{
|
||||||
return token_type::parse_error;
|
return token_type::parse_error;
|
||||||
}
|
}
|
||||||
@@ -13790,7 +13810,7 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
case 0xF2:
|
case 0xF2:
|
||||||
case 0xF3:
|
case 0xF3:
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range(keep, {0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF}))))
|
if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF}))))
|
||||||
{
|
{
|
||||||
return token_type::parse_error;
|
return token_type::parse_error;
|
||||||
}
|
}
|
||||||
@@ -13800,7 +13820,7 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
// U+100000..U+10FFFF F4 80..8F 80..BF 80..BF
|
// U+100000..U+10FFFF F4 80..8F 80..BF 80..BF
|
||||||
case 0xF4:
|
case 0xF4:
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range(keep, {0x80, 0x8F, 0x80, 0xBF, 0x80, 0xBF}))))
|
if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0x8F, 0x80, 0xBF, 0x80, 0xBF}))))
|
||||||
{
|
{
|
||||||
return token_type::parse_error;
|
return token_type::parse_error;
|
||||||
}
|
}
|
||||||
@@ -14291,7 +14311,7 @@ scan_number_done:
|
|||||||
token_type convert_number(token_type number_type, std::size_t mantissa_end)
|
token_type convert_number(token_type number_type, std::size_t mantissa_end)
|
||||||
{
|
{
|
||||||
// accept() only needs to know whether the input is valid, so it sets
|
// accept() only needs to know whether the input is valid, so it sets
|
||||||
// discard_values (see json.hpp), and an integer token whose
|
// discard_number_values (see json.hpp), and an integer token whose
|
||||||
// digit count shows that it fits is reported without calling
|
// digit count shows that it fits is reported without calling
|
||||||
// convert_integer(). A number with up to 18 digits always fits into
|
// convert_integer(). A number with up to 18 digits always fits into
|
||||||
// both std::uint64_t and std::int64_t (18 nines is about 1e18, below
|
// both std::uint64_t and std::int64_t (18 nines is about 1e18, below
|
||||||
@@ -14306,7 +14326,7 @@ scan_number_done:
|
|||||||
// json_sax_acceptor, whose number callbacks discard their argument and
|
// json_sax_acceptor, whose number callbacks discard their argument and
|
||||||
// return true, and the parser rejects neither integers nor finite
|
// return true, and the parser rejects neither integers nor finite
|
||||||
// floats. value_unsigned/value_integer are left unset here, so a caller
|
// floats. value_unsigned/value_integer are left unset here, so a caller
|
||||||
// that reads the converted value must not set discard_values.
|
// that reads the converted value must not set discard_number_values.
|
||||||
//
|
//
|
||||||
// On contiguous input, scan_number_bulk_contiguous() converts integer
|
// On contiguous input, scan_number_bulk_contiguous() converts integer
|
||||||
// tokens itself and does not pass them to this function, unless
|
// tokens itself and does not pass them to this function, unless
|
||||||
@@ -14314,7 +14334,7 @@ scan_number_done:
|
|||||||
// reached for input without bulk access (e.g. streams), with
|
// reached for input without bulk access (e.g. streams), with
|
||||||
// JSON_DIAGNOSTIC_POSITIONS, or when scan_number_bulk_contiguous()
|
// JSON_DIAGNOSTIC_POSITIONS, or when scan_number_bulk_contiguous()
|
||||||
// falls back to scan_number().
|
// falls back to scan_number().
|
||||||
if (discard_values)
|
if (discard_number_values)
|
||||||
{
|
{
|
||||||
constexpr std::size_t safe_digit_count = 18;
|
constexpr std::size_t safe_digit_count = 18;
|
||||||
if (number_type == token_type::value_unsigned && token_buffer.size() <= safe_digit_count)
|
if (number_type == token_type::value_unsigned && token_buffer.size() <= safe_digit_count)
|
||||||
@@ -14565,7 +14585,7 @@ scan_number_done:
|
|||||||
// w < 10^19, so a value below 10^max_exponent10 cannot overflow, and a
|
// w < 10^19, so a value below 10^max_exponent10 cannot overflow, and a
|
||||||
// value that underflows is a finite number as well. Only a token that
|
// value that underflows is a finite number as well. Only a token that
|
||||||
// may overflow has to be converted to find out.
|
// may overflow has to be converted to find out.
|
||||||
if (JSON_HEDLEY_UNLIKELY(discard_values && s.exponent + 19 <= std::numeric_limits<number_float_t>::max_exponent10))
|
if (JSON_HEDLEY_UNLIKELY(discard_number_values && s.exponent + 19 <= std::numeric_limits<number_float_t>::max_exponent10))
|
||||||
{
|
{
|
||||||
value_float = 0;
|
value_float = 0;
|
||||||
return true;
|
return true;
|
||||||
@@ -14897,34 +14917,6 @@ scan_number_done:
|
|||||||
token_buffer.push_back(static_cast<typename string_t::value_type>(c));
|
token_buffer.push_back(static_cast<typename string_t::value_type>(c));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// add a character of a string token; accept() checks the string but does
|
|
||||||
/// not keep its value (@a keep is false)
|
|
||||||
void add_string_char(std::true_type /*keep*/, char_int_type c)
|
|
||||||
{
|
|
||||||
token_buffer.push_back(static_cast<typename string_t::value_type>(c));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void add_string_char(std::false_type /*keep*/, char_int_type /*c*/) noexcept {}
|
|
||||||
|
|
||||||
/// add the UTF-8 bytes of a code point from a \\u escape to a string token
|
|
||||||
void add_codepoint(std::true_type /*keep*/, int codepoint)
|
|
||||||
{
|
|
||||||
encode_utf8(static_cast<std::uint32_t>(codepoint), [this](std::uint32_t byte)
|
|
||||||
{
|
|
||||||
add(static_cast<char_int_type>(byte));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
static void add_codepoint(std::false_type /*keep*/, int /*codepoint*/) noexcept {}
|
|
||||||
|
|
||||||
/// append a run of ordinary string characters (see get_after_string_run())
|
|
||||||
void append_string_run(std::true_type /*keep*/, const unsigned char* data, std::size_t n)
|
|
||||||
{
|
|
||||||
token_buffer.append(reinterpret_cast<const typename string_t::value_type*>(data), n);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void append_string_run(std::false_type /*keep*/, const unsigned char* /*data*/, std::size_t /*n*/) noexcept {}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/////////////////////
|
/////////////////////
|
||||||
// value getters
|
// value getters
|
||||||
@@ -15319,11 +15311,7 @@ scan_number_done:
|
|||||||
|
|
||||||
// string
|
// string
|
||||||
case '\"':
|
case '\"':
|
||||||
// accept() (discard_values) checks strings without copying them
|
return scan_string();
|
||||||
// into token_buffer. The choice is made once per string: a
|
|
||||||
// check inside the string loop made number-heavy documents up
|
|
||||||
// to 3 % slower (mesh.json, Apple M1)
|
|
||||||
return discard_values ? scan_string(std::false_type {}) : scan_string(std::true_type {});
|
|
||||||
|
|
||||||
// number
|
// number
|
||||||
case '-':
|
case '-':
|
||||||
@@ -15408,13 +15396,11 @@ scan_number_done:
|
|||||||
std::size_t decimal_point_position = std::string::npos;
|
std::size_t decimal_point_position = std::string::npos;
|
||||||
|
|
||||||
/// whether the caller only needs the token types and never looks at the
|
/// whether the caller only needs the token types and never looks at the
|
||||||
/// values of number and string tokens; set only by accept(), which parses
|
/// converted numeric values; set only by accept(), which parses through
|
||||||
/// through json_sax_acceptor. When set, convert_number() skips converting
|
/// json_sax_acceptor. When set, convert_number() skips converting integer
|
||||||
/// integer tokens whose digit count guarantees that they fit into 64 bits
|
/// tokens whose digit count guarantees that they fit into 64 bits (see
|
||||||
/// (see there), floats are converted only when they may overflow (see
|
/// there)
|
||||||
/// convert_accumulated_float()), and strings are checked without being
|
const bool discard_number_values = false;
|
||||||
/// copied into token_buffer (see scan())
|
|
||||||
const bool discard_values = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
@@ -18283,15 +18269,17 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
|
|
||||||
const std::size_t element_type_parse_position = chars_read;
|
const std::size_t element_type_parse_position = chars_read;
|
||||||
|
// an array's elements are named "0", "1", ... in the wire format,
|
||||||
|
// and those names are not passed on: they are skipped unless
|
||||||
|
// error_handler_t::strict must reject ill-formed UTF-8 in them
|
||||||
|
const bool is_object = container_stack.back().is_object;
|
||||||
key.clear();
|
key.clear();
|
||||||
if (JSON_HEDLEY_UNLIKELY(!get_bson_cstr(key)))
|
if (JSON_HEDLEY_UNLIKELY(!get_bson_cstr(key, is_object || error_handler == error_handler_t::strict)))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// an array's elements are named "0", "1", ... in the wire format,
|
if (is_object && !sax->key(key))
|
||||||
// and those names are not passed on
|
|
||||||
if (container_stack.back().is_object && !sax->key(key))
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -18307,14 +18295,18 @@ class binary_reader
|
|||||||
@brief Parses a C-style string from the BSON input.
|
@brief Parses a C-style string from the BSON input.
|
||||||
@param[in,out] result A reference to the string variable where the read
|
@param[in,out] result A reference to the string variable where the read
|
||||||
string is to be stored.
|
string is to be stored.
|
||||||
|
@param[in] store whether the characters are needed; if not (the names of
|
||||||
|
an array's elements), they are skipped and @a result may
|
||||||
|
be left unchanged, and they are not checked against
|
||||||
|
@ref error_handler
|
||||||
@return `true` if the \\x00-byte indicating the end of the string was
|
@return `true` if the \\x00-byte indicating the end of the string was
|
||||||
encountered before the EOF; false` indicates an unexpected EOF.
|
encountered before the EOF; false` indicates an unexpected EOF.
|
||||||
*/
|
*/
|
||||||
bool get_bson_cstr(string_t& result)
|
bool get_bson_cstr(string_t& result, const bool store = true)
|
||||||
{
|
{
|
||||||
if (get_bson_cstr_bulk(result, std::integral_constant<bool, bulk_scan> {}))
|
if (get_bson_cstr_bulk(result, store, std::integral_constant<bool, bulk_scan> {}))
|
||||||
{
|
{
|
||||||
return check_string_utf8(result, "key");
|
return !store || check_string_utf8(result, "key");
|
||||||
}
|
}
|
||||||
|
|
||||||
auto out = std::back_inserter(result);
|
auto out = std::back_inserter(result);
|
||||||
@@ -18327,7 +18319,7 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
if (current == 0x00)
|
if (current == 0x00)
|
||||||
{
|
{
|
||||||
return check_string_utf8(result, "key");
|
return !store || check_string_utf8(result, "key");
|
||||||
}
|
}
|
||||||
*out++ = static_cast<typename string_t::value_type>(current);
|
*out++ = static_cast<typename string_t::value_type>(current);
|
||||||
}
|
}
|
||||||
@@ -18337,10 +18329,11 @@ class binary_reader
|
|||||||
@brief read a C-style string from contiguous input in one step
|
@brief read a C-style string from contiguous input in one step
|
||||||
|
|
||||||
@param[in,out] result the string to append to
|
@param[in,out] result the string to append to
|
||||||
|
@param[in] store whether to append the characters to @a result
|
||||||
@return whether the string was read; if the input has no \\x00-byte, nothing
|
@return whether the string was read; if the input has no \\x00-byte, nothing
|
||||||
is read, and @ref get_bson_cstr reports the end of the input
|
is read, and @ref get_bson_cstr reports the end of the input
|
||||||
*/
|
*/
|
||||||
bool get_bson_cstr_bulk(string_t& result, std::true_type /*bulk*/)
|
bool get_bson_cstr_bulk(string_t& result, const bool store, std::true_type /*bulk*/)
|
||||||
{
|
{
|
||||||
const std::size_t remaining = ia.bulk_remaining();
|
const std::size_t remaining = ia.bulk_remaining();
|
||||||
if (remaining == 0)
|
if (remaining == 0)
|
||||||
@@ -18348,18 +18341,15 @@ class binary_reader
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const auto* const data = reinterpret_cast<const unsigned char*>(ia.bulk_data());
|
const auto* const data = reinterpret_cast<const unsigned char*>(ia.bulk_data());
|
||||||
// a plain loop rather than std::memchr: most keys are short (array
|
const std::size_t length = find_zero_byte(data, remaining);
|
||||||
// indices are keys, too), and the call would cost more than it saves
|
|
||||||
std::size_t length = 0;
|
|
||||||
while (length < remaining && data[length] != 0x00)
|
|
||||||
{
|
|
||||||
++length;
|
|
||||||
}
|
|
||||||
if (length == remaining)
|
if (length == remaining)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
result.append(reinterpret_cast<const typename string_t::value_type*>(data), length);
|
if (store)
|
||||||
|
{
|
||||||
|
result.append(reinterpret_cast<const typename string_t::value_type*>(data), length);
|
||||||
|
}
|
||||||
// consume the string and its \x00-byte, as the byte-wise path does
|
// consume the string and its \x00-byte, as the byte-wise path does
|
||||||
ia.bulk_skip(length + 1);
|
ia.bulk_skip(length + 1);
|
||||||
chars_read += length + 1;
|
chars_read += length + 1;
|
||||||
@@ -18368,7 +18358,7 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// input that is not contiguous: C-style strings are read byte by byte
|
/// input that is not contiguous: C-style strings are read byte by byte
|
||||||
bool get_bson_cstr_bulk(string_t& /*result*/, std::false_type /*bulk*/) const noexcept
|
bool get_bson_cstr_bulk(string_t& /*result*/, const bool /*store*/, std::false_type /*bulk*/) const noexcept
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -22623,9 +22613,9 @@ class parser
|
|||||||
const bool allow_exceptions_ = true,
|
const bool allow_exceptions_ = true,
|
||||||
const bool ignore_comments = false,
|
const bool ignore_comments = false,
|
||||||
const bool ignore_trailing_commas_ = false,
|
const bool ignore_trailing_commas_ = false,
|
||||||
const bool discard_values_ = false)
|
const bool discard_number_values_ = false)
|
||||||
: callback(std::move(cb))
|
: callback(std::move(cb))
|
||||||
, m_lexer(std::move(adapter), ignore_comments, discard_values_)
|
, m_lexer(std::move(adapter), ignore_comments, discard_number_values_)
|
||||||
, allow_exceptions(allow_exceptions_)
|
, allow_exceptions(allow_exceptions_)
|
||||||
, ignore_trailing_commas(ignore_trailing_commas_)
|
, ignore_trailing_commas(ignore_trailing_commas_)
|
||||||
{
|
{
|
||||||
@@ -37022,11 +37012,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
const bool allow_exceptions = true,
|
const bool allow_exceptions = true,
|
||||||
const bool ignore_comments = false,
|
const bool ignore_comments = false,
|
||||||
const bool ignore_trailing_commas = false,
|
const bool ignore_trailing_commas = false,
|
||||||
const bool discard_values = false
|
const bool discard_number_values = false
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
return ::nlohmann::detail::parser<basic_json, InputAdapterType>(std::move(adapter),
|
return ::nlohmann::detail::parser<basic_json, InputAdapterType>(std::move(adapter),
|
||||||
std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas, discard_values);
|
std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas, discard_number_values);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
+35
-2
@@ -1164,7 +1164,13 @@ TEST_CASE("BSON keys from contiguous and stream input")
|
|||||||
// contiguous input reads a key up to its \x00-byte in one step, a stream
|
// contiguous input reads a key up to its \x00-byte in one step, a stream
|
||||||
// reads it byte by byte; both must give the same value or error for the
|
// reads it byte by byte; both must give the same value or error for the
|
||||||
// complete document and for every truncation of it
|
// complete document and for every truncation of it
|
||||||
const json j = {{"", true}, {"k", {1, 2, 3}}, {std::string(40, 'x'), {{"nested key", "value"}}}};
|
json j = {{"", true}, {"k", {1, 2, 3}}, {std::string(40, 'x'), {{"nested key", "value"}}}};
|
||||||
|
// keys and array element names ("0" to "11") that end at every position of
|
||||||
|
// an 8-byte word, which contiguous input scans at a time
|
||||||
|
for (std::size_t length = 1; length <= 17; ++length)
|
||||||
|
{
|
||||||
|
j[std::string(length, static_cast<char>('a' + length))] = json::array({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, {11}});
|
||||||
|
}
|
||||||
const std::vector<std::uint8_t> bson = json::to_bson(j);
|
const std::vector<std::uint8_t> bson = json::to_bson(j);
|
||||||
CHECK(json::from_bson(bson) == j);
|
CHECK(json::from_bson(bson) == j);
|
||||||
|
|
||||||
@@ -1184,7 +1190,7 @@ TEST_CASE("BSON keys from contiguous and stream input")
|
|||||||
}
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
std::istringstream stream(std::string(input.begin(), input.end()));
|
std::istringstream stream(std::string(reinterpret_cast<const char*>(input.data()), input.size()));
|
||||||
from_stream = json::from_bson(stream).dump();
|
from_stream = json::from_bson(stream).dump();
|
||||||
}
|
}
|
||||||
catch (const json::parse_error& e)
|
catch (const json::parse_error& e)
|
||||||
@@ -1194,6 +1200,33 @@ TEST_CASE("BSON keys from contiguous and stream input")
|
|||||||
CHECK(from_vector == from_stream);
|
CHECK(from_vector == from_stream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("BSON array element names")
|
||||||
|
{
|
||||||
|
// an array's element names are not part of the value, so they are not
|
||||||
|
// passed on; error_handler_t::strict still rejects ill-formed UTF-8 in them
|
||||||
|
const std::vector<std::uint8_t> bson =
|
||||||
|
{
|
||||||
|
0x1D, 0x00, 0x00, 0x00, // size (little endian)
|
||||||
|
0x04, 'a', 0x00, // entry: array "a"
|
||||||
|
0x15, 0x00, 0x00, 0x00, // size (little endian)
|
||||||
|
0x08, 0xFF, 0x00, 0x01, // entry: boolean named "\xFF", true
|
||||||
|
0x08, '1', '2', '3', '4', '5', '6', '7', '8', '9', 0x00, 0x00, // entry: boolean named "123456789", false
|
||||||
|
0x00, // end of the array
|
||||||
|
0x00 // end of the document
|
||||||
|
};
|
||||||
|
const json expected = {{"a", {true, false}}};
|
||||||
|
|
||||||
|
CHECK(json::from_bson(bson) == expected);
|
||||||
|
CHECK(json::from_bson(bson, true, true, json::error_handler_t::replace) == expected);
|
||||||
|
CHECK(json::from_bson(bson, true, true, json::error_handler_t::ignore) == expected);
|
||||||
|
|
||||||
|
json _;
|
||||||
|
CHECK_THROWS_WITH_AS(_ = json::from_bson(bson, true, true, json::error_handler_t::strict), "[json.exception.parse_error.113] parse error at byte 14: syntax error while parsing BSON key: invalid string: ill-formed UTF-8 byte", json::parse_error&);
|
||||||
|
|
||||||
|
std::istringstream stream(std::string(reinterpret_cast<const char*>(bson.data()), bson.size()));
|
||||||
|
CHECK(json::from_bson(stream) == expected);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
TEST_CASE("Negative size of binary value")
|
TEST_CASE("Negative size of binary value")
|
||||||
|
|||||||
@@ -2982,50 +2982,6 @@ TEST_CASE("accept() agrees with parse() on floats around the overflow limit")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("accept() agrees with parse() on strings, which it checks without copying them")
|
|
||||||
{
|
|
||||||
// accept() scans strings and object keys with a separate instantiation of
|
|
||||||
// the string scanner that does not copy them into the token buffer; it
|
|
||||||
// must accept and reject exactly what parse() does
|
|
||||||
const std::vector<std::string> pieces =
|
|
||||||
{
|
|
||||||
"", "abc", "\\\"\\\\\\/\\b\\f\\n\\r\\t", "\\u0041\\u00e4\\u20AC", "\\ud83d\\ude00", "\\uD83D\\uDE00",
|
|
||||||
"\\ud800", "\\udc00", "\\ud800\\u0041", "\\ud800x", "\\udbff\\udfff", "\\u12", "\\u12g4", "\\x", "\\",
|
|
||||||
"\xc3\xa4", "\xe2\x82\xac", "\xf0\x9f\x98\x80", "\xf4\x8f\xbf\xbf", "\xc0\xaf", "\xed\xa0\x80", "\xf4\x90\x80\x80",
|
|
||||||
"\xff", "\xe2\x82", "\xc3", "\x01", "\x1f", "\x7f", "\n", std::string(1, '\0')
|
|
||||||
};
|
|
||||||
|
|
||||||
for (std::size_t run = 0; run <= 70; ++run)
|
|
||||||
{
|
|
||||||
for (const std::string& piece : pieces)
|
|
||||||
{
|
|
||||||
// a run of ordinary characters puts the piece at every offset of the bulk scanners
|
|
||||||
const std::string str = std::string(run, 'a') + piece;
|
|
||||||
for (const std::string& text :
|
|
||||||
{
|
|
||||||
"\"" + str + "\"", "[\"" + str + "\"]", "{\"" + str + "\":1}", "{\"k\":\"" + str + "\",\"" + str + "\":[\"" + str + "\"]}",
|
|
||||||
"[\"" + str
|
|
||||||
})
|
|
||||||
{
|
|
||||||
CAPTURE(text)
|
|
||||||
for (int options = 0; options < 4; ++options)
|
|
||||||
{
|
|
||||||
const bool ignore_comments = (options & 1) != 0;
|
|
||||||
const bool ignore_trailing_commas = (options & 2) != 0;
|
|
||||||
const bool parses = !json::parse(text, nullptr, false, ignore_comments, ignore_trailing_commas).is_discarded();
|
|
||||||
CHECK(json::accept(text, ignore_comments, ignore_trailing_commas) == parses);
|
|
||||||
|
|
||||||
std::istringstream stream(text);
|
|
||||||
CHECK(json::accept(stream, ignore_comments, ignore_trailing_commas) == parses);
|
|
||||||
|
|
||||||
const std::list<char> chars(text.begin(), text.end());
|
|
||||||
CHECK(json::accept(chars.begin(), chars.end(), ignore_comments, ignore_trailing_commas) == parses);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
// a DOM parser that reports parse errors without throwing, as in the sax_no_exception example
|
// a DOM parser that reports parse errors without throwing, as in the sax_no_exception example
|
||||||
|
|||||||
Reference in new issue
Block a user