Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9c349b1d7c | ||
|
|
367963f6bb | ||
|
|
4fe670b5f4 |
No files matched your search
@@ -13,15 +13,9 @@ function never throws an exception for invalid input, and the returned `#!cpp bo
|
|||||||
is returned.
|
is returned.
|
||||||
|
|
||||||
The result is the same as that of `BasicJsonType::accept()` by design; the difference is how the check is made. This
|
The result is the same as that of `BasicJsonType::accept()` by design; the difference is how the check is made. This
|
||||||
function runs the document's builder, which checks the text as [`parse()`](parse.md) does, while
|
function runs the document's index builder (and discards the index), while `BasicJsonType::accept()` runs the SAX
|
||||||
`BasicJsonType::accept()` runs the SAX parser with a handler that ignores every event. Use this one if you go on to
|
parser with a handler that ignores every event. Use this one if you go on to parse the same input with
|
||||||
parse the same input with [`parse()`](parse.md) anyway, so that both use the same code; either is fine for a plain
|
[`parse()`](parse.md) anyway, so that both use the same code; either is fine for a plain yes/no answer.
|
||||||
yes/no answer.
|
|
||||||
|
|
||||||
When both `ignore_comments` and `ignore_trailing_commas` are `#!cpp false` (the default), the builder writes no index.
|
|
||||||
With either option set, it builds the index as [`parse()`](parse.md) does and discards it: the result is the same, only
|
|
||||||
the time differs. (Neither option needs the index; the variant that skips it exists only for the default options to
|
|
||||||
keep the code size down.)
|
|
||||||
|
|
||||||
## Template parameters
|
## Template parameters
|
||||||
|
|
||||||
|
|||||||
@@ -1977,6 +1977,23 @@ class serializer
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 17 to 20 digits: the first one to four, then twice eight
|
||||||
|
if (magnitude_fits_64_bits(abs_value, std::integral_constant<bool, magnitude_has_at_most_64_bits>()))
|
||||||
|
{
|
||||||
|
const auto value = static_cast<std::uint64_t>(abs_value);
|
||||||
|
const std::uint64_t top = value / 10000000000000000u; // 1 to 1844
|
||||||
|
const std::uint64_t low16 = value - (top * 10000000000000000u);
|
||||||
|
const std::uint64_t middle = low16 / 100000000u;
|
||||||
|
const std::uint64_t first = dtoa_impl::eight_digit_bytes(top);
|
||||||
|
const auto leading = static_cast<unsigned>(count_leading_zeros(first) / 8); // (first is not 0)
|
||||||
|
char* const p = buffer_ptr + n_chars;
|
||||||
|
dtoa_impl::store_msb_first(p, (first << (8 * leading)) + 0x3030303030303030u);
|
||||||
|
dtoa_impl::store_msb_first(p + 8 - leading, dtoa_impl::eight_digit_bytes(middle) + 0x3030303030303030u);
|
||||||
|
dtoa_impl::store_msb_first(p + 16 - leading, dtoa_impl::eight_digit_bytes(low16 - (middle * 100000000u)) + 0x3030303030303030u);
|
||||||
|
write_buffer_pos += n_chars + 24 - leading;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
n_chars += count_digits(abs_value);
|
n_chars += count_digits(abs_value);
|
||||||
|
|
||||||
// spare 1 byte for '\0'
|
// spare 1 byte for '\0'
|
||||||
@@ -2122,6 +2139,23 @@ class serializer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// whether every number_magnitude_t fits 64 bits
|
||||||
|
static constexpr bool magnitude_has_at_most_64_bits = std::numeric_limits<number_magnitude_t>::is_specialized
|
||||||
|
&& std::numeric_limits<number_magnitude_t>::digits <= 64;
|
||||||
|
|
||||||
|
/// whether @a x fits 64 bits: always if number_magnitude_t has at most
|
||||||
|
/// 64 bits (selected by type, as the comparison would be reported as
|
||||||
|
/// always true), else compared
|
||||||
|
static bool magnitude_fits_64_bits(const number_magnitude_t& /*unused*/, std::true_type /*unused*/) noexcept
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool magnitude_fits_64_bits(const number_magnitude_t& x, std::false_type /*unused*/) noexcept
|
||||||
|
{
|
||||||
|
return x <= 18446744073709551615u;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Helper function for dump_integer
|
* Helper function for dump_integer
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -69,8 +69,7 @@ struct parse_failure
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// FloatType: the number_float_t of the document, whose overflow parse() rejects
|
/// FloatType: the number_float_t of the document, whose overflow parse() rejects
|
||||||
/// Store: false for accept(): the grammar, number, string and UTF-8 checks run as for parse(), but no node is written
|
template<typename FloatType, bool Comments, bool TrailingCommas, bool NulIsEnd, bool Sentinel>
|
||||||
template<typename FloatType, bool Comments, bool TrailingCommas, bool NulIsEnd, bool Sentinel, bool Store>
|
|
||||||
class builder
|
class builder
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -246,87 +245,11 @@ class builder
|
|||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// the string from p, the first backslash, control character, ill-formed UTF-8 byte, or the end, checked and not
|
|
||||||
/// decoded (accept() stores nothing, so the arena is not written): the checks, errors and error positions of
|
|
||||||
/// slow_string(), for everything up to the closing quote
|
|
||||||
NLOHMANN_VIEW_NOINLINE decoded check_string(const unsigned char* p)
|
|
||||||
{
|
|
||||||
const unsigned char* const end = m_end;
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
if (p == end)
|
|
||||||
{
|
|
||||||
return failed(error_code::string_missing_quote, p);
|
|
||||||
}
|
|
||||||
const unsigned char c = *p;
|
|
||||||
if (c == '"')
|
|
||||||
{
|
|
||||||
++p;
|
|
||||||
return decoded{p, 0, 0};
|
|
||||||
}
|
|
||||||
if (c != '\\')
|
|
||||||
{
|
|
||||||
return failed(c < 0x20 ? error_code::string_control_character : error_code::string_utf8, p);
|
|
||||||
}
|
|
||||||
++p;
|
|
||||||
if (p == end)
|
|
||||||
{
|
|
||||||
return failed(error_code::string_missing_quote, p);
|
|
||||||
}
|
|
||||||
const unsigned char d = *p++;
|
|
||||||
if (d == 'u')
|
|
||||||
{
|
|
||||||
const int cp = hex4(p); // (moves p past the four digits)
|
|
||||||
if (NLOHMANN_VIEW_UNLIKELY(cp < 0))
|
|
||||||
{
|
|
||||||
return failed(error_code::string_unicode_hex, p);
|
|
||||||
}
|
|
||||||
if (NLOHMANN_VIEW_UNLIKELY((cp & 0xF800) == 0xD800)) // a surrogate
|
|
||||||
{
|
|
||||||
if (cp >= 0xDC00)
|
|
||||||
{
|
|
||||||
return failed(error_code::string_surrogate_low, p);
|
|
||||||
}
|
|
||||||
if (end - p < 2 || p[0] != '\\' || p[1] != 'u')
|
|
||||||
{
|
|
||||||
return failed(error_code::string_surrogate_high, p);
|
|
||||||
}
|
|
||||||
p += 2;
|
|
||||||
const int lo = hex4(p); // (moves p past the four digits)
|
|
||||||
if (lo < 0)
|
|
||||||
{
|
|
||||||
return failed(error_code::string_unicode_hex, p);
|
|
||||||
}
|
|
||||||
if (lo < 0xDC00 || lo > 0xDFFF)
|
|
||||||
{
|
|
||||||
return failed(error_code::string_surrogate_high, p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (!(d == '"' || d == '\\' || d == '/' || d == 'b' || d == 'f' || d == 'n' || d == 'r' || d == 't'))
|
|
||||||
{
|
|
||||||
--p;
|
|
||||||
return failed(error_code::string_escape, p);
|
|
||||||
}
|
|
||||||
if (p != end && *p == '\\')
|
|
||||||
{
|
|
||||||
continue; // (escapes in a row: no run of text between them)
|
|
||||||
}
|
|
||||||
p = scan_string_run(p, end); // the text up to the next backslash, quote, control character, or ill-formed UTF-8
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// s points to the start of the string, p to a backslash, a control
|
/// s points to the start of the string, p to a backslash, a control
|
||||||
/// character, an ill-formed UTF-8 byte, or the end: decode the string into
|
/// character, an ill-formed UTF-8 byte, or the end: decode the string into
|
||||||
/// the arena
|
/// the arena
|
||||||
NLOHMANN_VIEW_NOINLINE decoded slow_string(const unsigned char* s, const unsigned char* p)
|
NLOHMANN_VIEW_NOINLINE decoded slow_string(const unsigned char* s, const unsigned char* p)
|
||||||
{
|
{
|
||||||
if (!enabled(Store))
|
|
||||||
{
|
|
||||||
// (accept(): the string is checked, not decoded)
|
|
||||||
static_cast<void>(s);
|
|
||||||
return check_string(p);
|
|
||||||
}
|
|
||||||
// single-character escapes; 0: invalid (and 'u', handled separately)
|
// single-character escapes; 0: invalid (and 'u', handled separately)
|
||||||
static const std::array<char, 128> simple_escape =
|
static const std::array<char, 128> simple_escape =
|
||||||
{
|
{
|
||||||
@@ -420,6 +343,21 @@ class builder
|
|||||||
}
|
}
|
||||||
arena_ensure(w, w_end, 4);
|
arena_ensure(w, w_end, 4);
|
||||||
w = put_utf8(w, cp);
|
w = put_utf8(w, cp);
|
||||||
|
// A chain of unicode escapes ("\u3042\u3044\u3046", as Python's json.dumps writes non-ASCII text) is
|
||||||
|
// decoded here, without going round the loop above for each of them. Anything else (a surrogate, a
|
||||||
|
// bad digit, fewer than six bytes) leaves p at the backslash: the loop above handles it, and reports
|
||||||
|
// the same error at the same position as it would without this loop.
|
||||||
|
while (end - p >= 6 && p[0] == '\\' && p[1] == 'u')
|
||||||
|
{
|
||||||
|
const int next = hex_codepoint(p + 2);
|
||||||
|
if (next < 0 || (next & 0xF800) == 0xD800)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
p += 6;
|
||||||
|
arena_ensure(w, w_end, 4);
|
||||||
|
w = put_utf8(w, next);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (NLOHMANN_VIEW_LIKELY(d < 128 && simple_escape[d] != 0))
|
else if (NLOHMANN_VIEW_LIKELY(d < 128 && simple_escape[d] != 0))
|
||||||
{
|
{
|
||||||
@@ -686,14 +624,11 @@ run_found:
|
|||||||
std::size_t max_depth = 0;
|
std::size_t max_depth = 0;
|
||||||
|
|
||||||
NLOHMANN_VIEW_ALWAYS_INLINE bool run()
|
NLOHMANN_VIEW_ALWAYS_INLINE bool run()
|
||||||
{
|
|
||||||
if (enabled(Store))
|
|
||||||
{
|
{
|
||||||
cold.m_doc.reserve(estimate_nodes(reinterpret_cast<const char*>(b), static_cast<std::size_t>(e - b)));
|
cold.m_doc.reserve(estimate_nodes(reinterpret_cast<const char*>(b), static_cast<std::size_t>(e - b)));
|
||||||
base = cold.m_doc.nodes;
|
base = cold.m_doc.nodes;
|
||||||
out = base;
|
out = base;
|
||||||
cap = base + cold.m_doc.node_capacity;
|
cap = base + cold.m_doc.node_capacity;
|
||||||
}
|
|
||||||
|
|
||||||
if (e - p >= 3 && p[0] == 0xEF && p[1] == 0xBB && p[2] == 0xBF)
|
if (e - p >= 3 && p[0] == 0xEF && p[1] == 0xBB && p[2] == 0xBF)
|
||||||
{
|
{
|
||||||
@@ -876,12 +811,9 @@ close_object:
|
|||||||
// a large object gets a hash index for its keys (objects only, so that closing
|
// a large object gets a hash index for its keys (objects only, so that closing
|
||||||
// an array pays nothing for this)
|
// an array pays nothing for this)
|
||||||
if (NLOHMANN_VIEW_UNLIKELY(cur_count >= document_data::index_min_members))
|
if (NLOHMANN_VIEW_UNLIKELY(cur_count >= document_data::index_min_members))
|
||||||
{
|
|
||||||
if (enabled(Store))
|
|
||||||
{
|
{
|
||||||
cold.note_large_object(cur_idx);
|
cold.note_large_object(cur_idx);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
close_container:
|
close_container:
|
||||||
close();
|
close();
|
||||||
@@ -1040,12 +972,6 @@ indent_done:
|
|||||||
/// an integer's value); two stores on little-endian targets
|
/// an integer's value); two stores on little-endian targets
|
||||||
NLOHMANN_VIEW_ALWAYS_INLINE node* push_node(node_kind k, std::uint8_t flags, std::uint16_t extra, std::size_t off, std::uint64_t second)
|
NLOHMANN_VIEW_ALWAYS_INLINE node* push_node(node_kind k, std::uint8_t flags, std::uint16_t extra, std::size_t off, std::uint64_t second)
|
||||||
{
|
{
|
||||||
if (!enabled(Store))
|
|
||||||
{
|
|
||||||
// accept(): nothing is stored, so what computes the arguments is not run either. base and out stay
|
|
||||||
// null: their difference, which open() takes, is 0.
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
if (NLOHMANN_VIEW_UNLIKELY(out == cap))
|
if (NLOHMANN_VIEW_UNLIKELY(out == cap))
|
||||||
{
|
{
|
||||||
const auto n = static_cast<std::size_t>(out - base);
|
const auto n = static_cast<std::size_t>(out - base);
|
||||||
@@ -1103,13 +1029,10 @@ indent_done:
|
|||||||
}
|
}
|
||||||
|
|
||||||
NLOHMANN_VIEW_ALWAYS_INLINE void close()
|
NLOHMANN_VIEW_ALWAYS_INLINE void close()
|
||||||
{
|
|
||||||
if (enabled(Store))
|
|
||||||
{
|
{
|
||||||
node& n = base[cur_idx];
|
node& n = base[cur_idx];
|
||||||
n.len = cur_count;
|
n.len = cur_count;
|
||||||
n.next = static_cast<std::uint32_t>(out - base) - cur_idx;
|
n.next = static_cast<std::uint32_t>(out - base) - cur_idx;
|
||||||
}
|
|
||||||
if (--depth != 0)
|
if (--depth != 0)
|
||||||
{
|
{
|
||||||
if (NLOHMANN_VIEW_LIKELY(depth <= 64))
|
if (NLOHMANN_VIEW_LIKELY(depth <= 64))
|
||||||
@@ -1284,44 +1207,34 @@ indent_done:
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// run the builder with compile-time options
|
/// run the builder with compile-time options
|
||||||
template<typename FloatType, bool Comments, bool TrailingCommas, bool NulIsEnd, bool Store>
|
template<typename FloatType, bool Comments, bool TrailingCommas, bool NulIsEnd>
|
||||||
inline bool build_with(document_data& d, const char* src, std::size_t size, bool sentinel, parse_failure& failure)
|
inline bool build_with(document_data& d, const char* src, std::size_t size, bool sentinel, parse_failure& failure)
|
||||||
{
|
{
|
||||||
if (sentinel)
|
if (sentinel)
|
||||||
{
|
{
|
||||||
builder<FloatType, Comments, TrailingCommas, NulIsEnd, true, Store> bld(d, src, size);
|
builder<FloatType, Comments, TrailingCommas, NulIsEnd, true> bld(d, src, size);
|
||||||
const bool ok = bld.run();
|
const bool ok = bld.run();
|
||||||
failure = bld.failure();
|
failure = bld.failure();
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
builder<FloatType, Comments, TrailingCommas, NulIsEnd, false, Store> bld(d, src, size);
|
builder<FloatType, Comments, TrailingCommas, NulIsEnd, false> bld(d, src, size);
|
||||||
const bool ok = bld.run();
|
const bool ok = bld.run();
|
||||||
failure = bld.failure();
|
failure = bld.failure();
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// sentinel: src[size] is readable and 0 (e.g. std::string); FloatType: the
|
/// sentinel: src[size] is readable and 0 (e.g. std::string); FloatType: the
|
||||||
/// number_float_t of the document; validate_only: accept(), only the result is
|
/// number_float_t of the document
|
||||||
/// used
|
|
||||||
template<typename FloatType, bool NulIsEnd>
|
template<typename FloatType, bool NulIsEnd>
|
||||||
inline bool build(document_data& d, const char* src, std::size_t size, bool comments, bool trailing_commas, bool sentinel, bool validate_only,
|
inline bool build(document_data& d, const char* src, std::size_t size, bool comments, bool trailing_commas, bool sentinel, parse_failure& failure)
|
||||||
parse_failure& failure)
|
|
||||||
{
|
{
|
||||||
if (validate_only && !comments && !trailing_commas)
|
|
||||||
{
|
|
||||||
// accept() of plain JSON: nothing is stored. The other option sets
|
|
||||||
// could skip the stores just as well; they keep the builder that
|
|
||||||
// stores only so that there are two more instantiations of the parse
|
|
||||||
// loop, not eight.
|
|
||||||
return build_with<FloatType, false, false, NulIsEnd, false>(d, src, size, sentinel, failure);
|
|
||||||
}
|
|
||||||
if (comments)
|
if (comments)
|
||||||
{
|
{
|
||||||
return trailing_commas ? build_with<FloatType, true, true, NulIsEnd, true>(d, src, size, sentinel, failure)
|
return trailing_commas ? build_with<FloatType, true, true, NulIsEnd>(d, src, size, sentinel, failure)
|
||||||
: build_with<FloatType, true, false, NulIsEnd, true>(d, src, size, sentinel, failure);
|
: build_with<FloatType, true, false, NulIsEnd>(d, src, size, sentinel, failure);
|
||||||
}
|
}
|
||||||
return trailing_commas ? build_with<FloatType, false, true, NulIsEnd, true>(d, src, size, sentinel, failure)
|
return trailing_commas ? build_with<FloatType, false, true, NulIsEnd>(d, src, size, sentinel, failure)
|
||||||
: build_with<FloatType, false, false, NulIsEnd, true>(d, src, size, sentinel, failure);
|
: build_with<FloatType, false, false, NulIsEnd>(d, src, size, sentinel, failure);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace view
|
} // namespace view
|
||||||
|
|||||||
@@ -1060,7 +1060,7 @@ class basic_json_document
|
|||||||
const bool ignore_trailing_commas = false)
|
const bool ignore_trailing_commas = false)
|
||||||
{
|
{
|
||||||
basic_json_document d;
|
basic_json_document d;
|
||||||
d.read_range(first, last, error_mode(allow_exceptions), ignore_comments, ignore_trailing_commas);
|
d.read_range(first, last, allow_exceptions, ignore_comments, ignore_trailing_commas);
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1075,7 +1075,7 @@ class basic_json_document
|
|||||||
basic_json_document d;
|
basic_json_document d;
|
||||||
std::string buf;
|
std::string buf;
|
||||||
append_input(buf, std::forward<InputType>(input));
|
append_input(buf, std::forward<InputType>(input));
|
||||||
d.build_owned(std::move(buf), error_mode(allow_exceptions), ignore_comments, ignore_trailing_commas);
|
d.build_owned(std::move(buf), allow_exceptions, ignore_comments, ignore_trailing_commas);
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1088,8 +1088,7 @@ class basic_json_document
|
|||||||
static bool accept(InputType&& input, const bool ignore_comments = false, const bool ignore_trailing_commas = false)
|
static bool accept(InputType&& input, const bool ignore_comments = false, const bool ignore_trailing_commas = false)
|
||||||
{
|
{
|
||||||
basic_json_document d;
|
basic_json_document d;
|
||||||
d.read_kind(std::forward<InputType>(input), on_error::validate, ignore_comments, ignore_trailing_commas,
|
d.read(std::forward<InputType>(input), false, ignore_comments, ignore_trailing_commas);
|
||||||
std::integral_constant<detail::view::input_kind, detail::view::classify_input<InputType>::value> {});
|
|
||||||
return !d.is_discarded();
|
return !d.is_discarded();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1105,7 +1104,7 @@ class basic_json_document
|
|||||||
const bool ignore_comments = false,
|
const bool ignore_comments = false,
|
||||||
const bool ignore_trailing_commas = false)
|
const bool ignore_trailing_commas = false)
|
||||||
{
|
{
|
||||||
read_kind(std::forward<InputType>(input), error_mode(allow_exceptions), ignore_comments, ignore_trailing_commas,
|
read_kind(std::forward<InputType>(input), allow_exceptions, ignore_comments, ignore_trailing_commas,
|
||||||
std::integral_constant<detail::view::input_kind, detail::view::classify_input<InputType>::value> {});
|
std::integral_constant<detail::view::input_kind, detail::view::classify_input<InputType>::value> {});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1464,22 +1463,6 @@ class basic_json_document
|
|||||||
return idx;
|
return idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// what a failed parse does
|
|
||||||
enum class on_error : std::uint8_t
|
|
||||||
{
|
|
||||||
discard, ///< the document is discarded
|
|
||||||
raise, ///< parse_error is thrown
|
|
||||||
validate ///< accept(): the document is discarded, and no node is stored while parsing
|
|
||||||
};
|
|
||||||
|
|
||||||
static on_error error_mode(bool allow_exceptions) noexcept
|
|
||||||
{
|
|
||||||
return allow_exceptions ? on_error::raise : on_error::discard;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// NulIsEnd of the builder: a null byte ends the input unless strict_nul_handling
|
|
||||||
static constexpr bool nul_is_end = !detail::abi_config::strict_nul_handling;
|
|
||||||
|
|
||||||
/// create the storage (sized for the input) on first use
|
/// create the storage (sized for the input) on first use
|
||||||
void ensure_data(const char* src, std::size_t size)
|
void ensure_data(const char* src, std::size_t size)
|
||||||
{
|
{
|
||||||
@@ -1491,7 +1474,7 @@ class basic_json_document
|
|||||||
|
|
||||||
/// parse a buffer the document takes ownership of (it replaces the
|
/// parse a buffer the document takes ownership of (it replaces the
|
||||||
/// previous owned copy)
|
/// previous owned copy)
|
||||||
void build_owned(std::string&& buf, on_error allow_exceptions, bool comments, bool trailing_commas)
|
void build_owned(std::string&& buf, bool allow_exceptions, bool comments, bool trailing_commas)
|
||||||
{
|
{
|
||||||
ensure_data(buf.data(), buf.size());
|
ensure_data(buf.data(), buf.size());
|
||||||
m_data->owned = std::move(buf);
|
m_data->owned = std::move(buf);
|
||||||
@@ -1501,7 +1484,7 @@ class basic_json_document
|
|||||||
/// parse a copy of the input; the copy is appended to the cleared owned
|
/// parse a copy of the input; the copy is appended to the cleared owned
|
||||||
/// buffer, so that its capacity is reused
|
/// buffer, so that its capacity is reused
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void build_copy(T&& input, on_error allow_exceptions, bool comments, bool trailing_commas)
|
void build_copy(T&& input, bool allow_exceptions, bool comments, bool trailing_commas)
|
||||||
{
|
{
|
||||||
if (!m_data)
|
if (!m_data)
|
||||||
{
|
{
|
||||||
@@ -1522,7 +1505,7 @@ class basic_json_document
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// sentinel: src[size] is readable and 0 (std::string, C strings)
|
/// sentinel: src[size] is readable and 0 (std::string, C strings)
|
||||||
void build(const char* src, std::size_t size, on_error allow_exceptions, bool comments, bool trailing_commas, bool owned, bool sentinel)
|
void build(const char* src, std::size_t size, bool allow_exceptions, bool comments, bool trailing_commas, bool owned, bool sentinel)
|
||||||
{
|
{
|
||||||
ensure_data(src, size);
|
ensure_data(src, size);
|
||||||
document_data& d = *m_data;
|
document_data& d = *m_data;
|
||||||
@@ -1549,8 +1532,7 @@ class basic_json_document
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ok = detail::view::build<typename BasicJsonType::number_float_t, nul_is_end>(d, src, size, comments, trailing_commas, sentinel,
|
ok = detail::view::build < typename BasicJsonType::number_float_t, !detail::abi_config::strict_nul_handling > (d, src, size, comments, trailing_commas, sentinel, failure);
|
||||||
allow_exceptions == on_error::validate, failure);
|
|
||||||
}
|
}
|
||||||
if (NLOHMANN_VIEW_LIKELY(ok))
|
if (NLOHMANN_VIEW_LIKELY(ok))
|
||||||
{
|
{
|
||||||
@@ -1563,7 +1545,7 @@ class basic_json_document
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
std::vector<std::uint32_t>().swap(d.large_objects);
|
std::vector<std::uint32_t>().swap(d.large_objects);
|
||||||
if (allow_exceptions == on_error::raise)
|
if (allow_exceptions)
|
||||||
{
|
{
|
||||||
detail::view::throw_parse_failure<BasicJsonType>(failure, src, size, comments, trailing_commas);
|
detail::view::throw_parse_failure<BasicJsonType>(failure, src, size, comments, trailing_commas);
|
||||||
}
|
}
|
||||||
@@ -1571,13 +1553,13 @@ class basic_json_document
|
|||||||
|
|
||||||
// --- input dispatch (see detail::view::input_kind) ---
|
// --- input dispatch (see detail::view::input_kind) ---
|
||||||
|
|
||||||
void read_kind(std::string&& s, on_error ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::move_string> /*unused*/)
|
void read_kind(std::string&& s, bool ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::move_string> /*unused*/)
|
||||||
{
|
{
|
||||||
build_owned(std::move(s), ae, c, tc);
|
build_owned(std::move(s), ae, c, tc);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename CharT>
|
template<typename CharT>
|
||||||
void read_kind(CharT* s, on_error ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::c_string> /*unused*/)
|
void read_kind(CharT* s, bool ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::c_string> /*unused*/)
|
||||||
{
|
{
|
||||||
if (s == nullptr)
|
if (s == nullptr)
|
||||||
{
|
{
|
||||||
@@ -1591,7 +1573,7 @@ class basic_json_document
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename Array>
|
template<typename Array>
|
||||||
void read_kind(Array& a, on_error ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::char_array> /*unused*/)
|
void read_kind(Array& a, bool ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::char_array> /*unused*/)
|
||||||
{
|
{
|
||||||
using CharT = typename std::remove_cv<typename std::remove_extent<Array>::type>::type;
|
using CharT = typename std::remove_cv<typename std::remove_extent<Array>::type>::type;
|
||||||
const std::size_t n = std::extent<Array>::value;
|
const std::size_t n = std::extent<Array>::value;
|
||||||
@@ -1602,7 +1584,7 @@ class basic_json_document
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void read_kind(const T& s, on_error ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::borrow_range> /*unused*/)
|
void read_kind(const T& s, bool ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::borrow_range> /*unused*/)
|
||||||
{
|
{
|
||||||
// std::basic_string guarantees data()[size()] == 0: use it as sentinel
|
// std::basic_string guarantees data()[size()] == 0: use it as sentinel
|
||||||
const auto size = static_cast<std::size_t>(s.size());
|
const auto size = static_cast<std::size_t>(s.size());
|
||||||
@@ -1611,13 +1593,13 @@ class basic_json_document
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void read_kind(const T& s, on_error ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::copy_range> /*unused*/)
|
void read_kind(const T& s, bool ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::copy_range> /*unused*/)
|
||||||
{
|
{
|
||||||
build_copy(s, ae, c, tc);
|
build_copy(s, ae, c, tc);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename CharT>
|
template<typename CharT>
|
||||||
void read_kind(CharT* s, on_error ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::wide_c_string> /*unused*/)
|
void read_kind(CharT* s, bool ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::wide_c_string> /*unused*/)
|
||||||
{
|
{
|
||||||
if (s == nullptr)
|
if (s == nullptr)
|
||||||
{
|
{
|
||||||
@@ -1629,13 +1611,13 @@ class basic_json_document
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void read_kind(T&& input, on_error ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::adapter> /*unused*/)
|
void read_kind(T&& input, bool ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::adapter> /*unused*/)
|
||||||
{
|
{
|
||||||
build_copy(std::forward<T>(input), ae, c, tc);
|
build_copy(std::forward<T>(input), ae, c, tc);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename IteratorType>
|
template<typename IteratorType>
|
||||||
void read_range(IteratorType first, IteratorType last, on_error ae, bool c, bool tc)
|
void read_range(IteratorType first, IteratorType last, bool ae, bool c, bool tc)
|
||||||
{
|
{
|
||||||
using value_type = typename std::remove_cv<typename std::iterator_traits<IteratorType>::value_type>::type;
|
using value_type = typename std::remove_cv<typename std::iterator_traits<IteratorType>::value_type>::type;
|
||||||
// borrow the range where the library's input adapter scans it as one
|
// borrow the range where the library's input adapter scans it as one
|
||||||
@@ -1646,14 +1628,14 @@ class basic_json_document
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename IteratorType>
|
template<typename IteratorType>
|
||||||
void read_range_impl(IteratorType first, IteratorType last, on_error ae, bool c, bool tc, std::true_type /*contiguous bytes*/)
|
void read_range_impl(IteratorType first, IteratorType last, bool ae, bool c, bool tc, std::true_type /*contiguous bytes*/)
|
||||||
{
|
{
|
||||||
const auto size = static_cast<std::size_t>(std::distance(first, last));
|
const auto size = static_cast<std::size_t>(std::distance(first, last));
|
||||||
build(size == 0 ? "" : reinterpret_cast<const char*>(&*first), size, ae, c, tc, false, size == 0); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
|
build(size == 0 ? "" : reinterpret_cast<const char*>(&*first), size, ae, c, tc, false, size == 0); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename IteratorType>
|
template<typename IteratorType>
|
||||||
void read_range_impl(IteratorType first, IteratorType last, on_error ae, bool c, bool tc, std::false_type /*other*/)
|
void read_range_impl(IteratorType first, IteratorType last, bool ae, bool c, bool tc, std::false_type /*other*/)
|
||||||
{
|
{
|
||||||
build_copy(detail::view::make_adapter_input(detail::input_adapter(first, last)), ae, c, tc);
|
build_copy(detail::view::make_adapter_input(detail::input_adapter(first, last)), ae, c, tc);
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+34
@@ -36224,6 +36224,23 @@ class serializer
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 17 to 20 digits: the first one to four, then twice eight
|
||||||
|
if (magnitude_fits_64_bits(abs_value, std::integral_constant<bool, magnitude_has_at_most_64_bits>()))
|
||||||
|
{
|
||||||
|
const auto value = static_cast<std::uint64_t>(abs_value);
|
||||||
|
const std::uint64_t top = value / 10000000000000000u; // 1 to 1844
|
||||||
|
const std::uint64_t low16 = value - (top * 10000000000000000u);
|
||||||
|
const std::uint64_t middle = low16 / 100000000u;
|
||||||
|
const std::uint64_t first = dtoa_impl::eight_digit_bytes(top);
|
||||||
|
const auto leading = static_cast<unsigned>(count_leading_zeros(first) / 8); // (first is not 0)
|
||||||
|
char* const p = buffer_ptr + n_chars;
|
||||||
|
dtoa_impl::store_msb_first(p, (first << (8 * leading)) + 0x3030303030303030u);
|
||||||
|
dtoa_impl::store_msb_first(p + 8 - leading, dtoa_impl::eight_digit_bytes(middle) + 0x3030303030303030u);
|
||||||
|
dtoa_impl::store_msb_first(p + 16 - leading, dtoa_impl::eight_digit_bytes(low16 - (middle * 100000000u)) + 0x3030303030303030u);
|
||||||
|
write_buffer_pos += n_chars + 24 - leading;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
n_chars += count_digits(abs_value);
|
n_chars += count_digits(abs_value);
|
||||||
|
|
||||||
// spare 1 byte for '\0'
|
// spare 1 byte for '\0'
|
||||||
@@ -36369,6 +36386,23 @@ class serializer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// whether every number_magnitude_t fits 64 bits
|
||||||
|
static constexpr bool magnitude_has_at_most_64_bits = std::numeric_limits<number_magnitude_t>::is_specialized
|
||||||
|
&& std::numeric_limits<number_magnitude_t>::digits <= 64;
|
||||||
|
|
||||||
|
/// whether @a x fits 64 bits: always if number_magnitude_t has at most
|
||||||
|
/// 64 bits (selected by type, as the comparison would be reported as
|
||||||
|
/// always true), else compared
|
||||||
|
static bool magnitude_fits_64_bits(const number_magnitude_t& /*unused*/, std::true_type /*unused*/) noexcept
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool magnitude_fits_64_bits(const number_magnitude_t& x, std::false_type /*unused*/) noexcept
|
||||||
|
{
|
||||||
|
return x <= 18446744073709551615u;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Helper function for dump_integer
|
* Helper function for dump_integer
|
||||||
*
|
*
|
||||||
|
|||||||
Generated
+44
-149
@@ -1311,8 +1311,7 @@ struct parse_failure
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// FloatType: the number_float_t of the document, whose overflow parse() rejects
|
/// FloatType: the number_float_t of the document, whose overflow parse() rejects
|
||||||
/// Store: false for accept(): the grammar, number, string and UTF-8 checks run as for parse(), but no node is written
|
template<typename FloatType, bool Comments, bool TrailingCommas, bool NulIsEnd, bool Sentinel>
|
||||||
template<typename FloatType, bool Comments, bool TrailingCommas, bool NulIsEnd, bool Sentinel, bool Store>
|
|
||||||
class builder
|
class builder
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -1488,87 +1487,11 @@ class builder
|
|||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// the string from p, the first backslash, control character, ill-formed UTF-8 byte, or the end, checked and not
|
|
||||||
/// decoded (accept() stores nothing, so the arena is not written): the checks, errors and error positions of
|
|
||||||
/// slow_string(), for everything up to the closing quote
|
|
||||||
NLOHMANN_VIEW_NOINLINE decoded check_string(const unsigned char* p)
|
|
||||||
{
|
|
||||||
const unsigned char* const end = m_end;
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
if (p == end)
|
|
||||||
{
|
|
||||||
return failed(error_code::string_missing_quote, p);
|
|
||||||
}
|
|
||||||
const unsigned char c = *p;
|
|
||||||
if (c == '"')
|
|
||||||
{
|
|
||||||
++p;
|
|
||||||
return decoded{p, 0, 0};
|
|
||||||
}
|
|
||||||
if (c != '\\')
|
|
||||||
{
|
|
||||||
return failed(c < 0x20 ? error_code::string_control_character : error_code::string_utf8, p);
|
|
||||||
}
|
|
||||||
++p;
|
|
||||||
if (p == end)
|
|
||||||
{
|
|
||||||
return failed(error_code::string_missing_quote, p);
|
|
||||||
}
|
|
||||||
const unsigned char d = *p++;
|
|
||||||
if (d == 'u')
|
|
||||||
{
|
|
||||||
const int cp = hex4(p); // (moves p past the four digits)
|
|
||||||
if (NLOHMANN_VIEW_UNLIKELY(cp < 0))
|
|
||||||
{
|
|
||||||
return failed(error_code::string_unicode_hex, p);
|
|
||||||
}
|
|
||||||
if (NLOHMANN_VIEW_UNLIKELY((cp & 0xF800) == 0xD800)) // a surrogate
|
|
||||||
{
|
|
||||||
if (cp >= 0xDC00)
|
|
||||||
{
|
|
||||||
return failed(error_code::string_surrogate_low, p);
|
|
||||||
}
|
|
||||||
if (end - p < 2 || p[0] != '\\' || p[1] != 'u')
|
|
||||||
{
|
|
||||||
return failed(error_code::string_surrogate_high, p);
|
|
||||||
}
|
|
||||||
p += 2;
|
|
||||||
const int lo = hex4(p); // (moves p past the four digits)
|
|
||||||
if (lo < 0)
|
|
||||||
{
|
|
||||||
return failed(error_code::string_unicode_hex, p);
|
|
||||||
}
|
|
||||||
if (lo < 0xDC00 || lo > 0xDFFF)
|
|
||||||
{
|
|
||||||
return failed(error_code::string_surrogate_high, p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (!(d == '"' || d == '\\' || d == '/' || d == 'b' || d == 'f' || d == 'n' || d == 'r' || d == 't'))
|
|
||||||
{
|
|
||||||
--p;
|
|
||||||
return failed(error_code::string_escape, p);
|
|
||||||
}
|
|
||||||
if (p != end && *p == '\\')
|
|
||||||
{
|
|
||||||
continue; // (escapes in a row: no run of text between them)
|
|
||||||
}
|
|
||||||
p = scan_string_run(p, end); // the text up to the next backslash, quote, control character, or ill-formed UTF-8
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// s points to the start of the string, p to a backslash, a control
|
/// s points to the start of the string, p to a backslash, a control
|
||||||
/// character, an ill-formed UTF-8 byte, or the end: decode the string into
|
/// character, an ill-formed UTF-8 byte, or the end: decode the string into
|
||||||
/// the arena
|
/// the arena
|
||||||
NLOHMANN_VIEW_NOINLINE decoded slow_string(const unsigned char* s, const unsigned char* p)
|
NLOHMANN_VIEW_NOINLINE decoded slow_string(const unsigned char* s, const unsigned char* p)
|
||||||
{
|
{
|
||||||
if (!enabled(Store))
|
|
||||||
{
|
|
||||||
// (accept(): the string is checked, not decoded)
|
|
||||||
static_cast<void>(s);
|
|
||||||
return check_string(p);
|
|
||||||
}
|
|
||||||
// single-character escapes; 0: invalid (and 'u', handled separately)
|
// single-character escapes; 0: invalid (and 'u', handled separately)
|
||||||
static const std::array<char, 128> simple_escape =
|
static const std::array<char, 128> simple_escape =
|
||||||
{
|
{
|
||||||
@@ -1662,6 +1585,21 @@ class builder
|
|||||||
}
|
}
|
||||||
arena_ensure(w, w_end, 4);
|
arena_ensure(w, w_end, 4);
|
||||||
w = put_utf8(w, cp);
|
w = put_utf8(w, cp);
|
||||||
|
// A chain of unicode escapes ("\u3042\u3044\u3046", as Python's json.dumps writes non-ASCII text) is
|
||||||
|
// decoded here, without going round the loop above for each of them. Anything else (a surrogate, a
|
||||||
|
// bad digit, fewer than six bytes) leaves p at the backslash: the loop above handles it, and reports
|
||||||
|
// the same error at the same position as it would without this loop.
|
||||||
|
while (end - p >= 6 && p[0] == '\\' && p[1] == 'u')
|
||||||
|
{
|
||||||
|
const int next = hex_codepoint(p + 2);
|
||||||
|
if (next < 0 || (next & 0xF800) == 0xD800)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
p += 6;
|
||||||
|
arena_ensure(w, w_end, 4);
|
||||||
|
w = put_utf8(w, next);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (NLOHMANN_VIEW_LIKELY(d < 128 && simple_escape[d] != 0))
|
else if (NLOHMANN_VIEW_LIKELY(d < 128 && simple_escape[d] != 0))
|
||||||
{
|
{
|
||||||
@@ -1928,14 +1866,11 @@ run_found:
|
|||||||
std::size_t max_depth = 0;
|
std::size_t max_depth = 0;
|
||||||
|
|
||||||
NLOHMANN_VIEW_ALWAYS_INLINE bool run()
|
NLOHMANN_VIEW_ALWAYS_INLINE bool run()
|
||||||
{
|
|
||||||
if (enabled(Store))
|
|
||||||
{
|
{
|
||||||
cold.m_doc.reserve(estimate_nodes(reinterpret_cast<const char*>(b), static_cast<std::size_t>(e - b)));
|
cold.m_doc.reserve(estimate_nodes(reinterpret_cast<const char*>(b), static_cast<std::size_t>(e - b)));
|
||||||
base = cold.m_doc.nodes;
|
base = cold.m_doc.nodes;
|
||||||
out = base;
|
out = base;
|
||||||
cap = base + cold.m_doc.node_capacity;
|
cap = base + cold.m_doc.node_capacity;
|
||||||
}
|
|
||||||
|
|
||||||
if (e - p >= 3 && p[0] == 0xEF && p[1] == 0xBB && p[2] == 0xBF)
|
if (e - p >= 3 && p[0] == 0xEF && p[1] == 0xBB && p[2] == 0xBF)
|
||||||
{
|
{
|
||||||
@@ -2118,12 +2053,9 @@ close_object:
|
|||||||
// a large object gets a hash index for its keys (objects only, so that closing
|
// a large object gets a hash index for its keys (objects only, so that closing
|
||||||
// an array pays nothing for this)
|
// an array pays nothing for this)
|
||||||
if (NLOHMANN_VIEW_UNLIKELY(cur_count >= document_data::index_min_members))
|
if (NLOHMANN_VIEW_UNLIKELY(cur_count >= document_data::index_min_members))
|
||||||
{
|
|
||||||
if (enabled(Store))
|
|
||||||
{
|
{
|
||||||
cold.note_large_object(cur_idx);
|
cold.note_large_object(cur_idx);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
close_container:
|
close_container:
|
||||||
close();
|
close();
|
||||||
@@ -2282,12 +2214,6 @@ indent_done:
|
|||||||
/// an integer's value); two stores on little-endian targets
|
/// an integer's value); two stores on little-endian targets
|
||||||
NLOHMANN_VIEW_ALWAYS_INLINE node* push_node(node_kind k, std::uint8_t flags, std::uint16_t extra, std::size_t off, std::uint64_t second)
|
NLOHMANN_VIEW_ALWAYS_INLINE node* push_node(node_kind k, std::uint8_t flags, std::uint16_t extra, std::size_t off, std::uint64_t second)
|
||||||
{
|
{
|
||||||
if (!enabled(Store))
|
|
||||||
{
|
|
||||||
// accept(): nothing is stored, so what computes the arguments is not run either. base and out stay
|
|
||||||
// null: their difference, which open() takes, is 0.
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
if (NLOHMANN_VIEW_UNLIKELY(out == cap))
|
if (NLOHMANN_VIEW_UNLIKELY(out == cap))
|
||||||
{
|
{
|
||||||
const auto n = static_cast<std::size_t>(out - base);
|
const auto n = static_cast<std::size_t>(out - base);
|
||||||
@@ -2345,13 +2271,10 @@ indent_done:
|
|||||||
}
|
}
|
||||||
|
|
||||||
NLOHMANN_VIEW_ALWAYS_INLINE void close()
|
NLOHMANN_VIEW_ALWAYS_INLINE void close()
|
||||||
{
|
|
||||||
if (enabled(Store))
|
|
||||||
{
|
{
|
||||||
node& n = base[cur_idx];
|
node& n = base[cur_idx];
|
||||||
n.len = cur_count;
|
n.len = cur_count;
|
||||||
n.next = static_cast<std::uint32_t>(out - base) - cur_idx;
|
n.next = static_cast<std::uint32_t>(out - base) - cur_idx;
|
||||||
}
|
|
||||||
if (--depth != 0)
|
if (--depth != 0)
|
||||||
{
|
{
|
||||||
if (NLOHMANN_VIEW_LIKELY(depth <= 64))
|
if (NLOHMANN_VIEW_LIKELY(depth <= 64))
|
||||||
@@ -2526,44 +2449,34 @@ indent_done:
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// run the builder with compile-time options
|
/// run the builder with compile-time options
|
||||||
template<typename FloatType, bool Comments, bool TrailingCommas, bool NulIsEnd, bool Store>
|
template<typename FloatType, bool Comments, bool TrailingCommas, bool NulIsEnd>
|
||||||
inline bool build_with(document_data& d, const char* src, std::size_t size, bool sentinel, parse_failure& failure)
|
inline bool build_with(document_data& d, const char* src, std::size_t size, bool sentinel, parse_failure& failure)
|
||||||
{
|
{
|
||||||
if (sentinel)
|
if (sentinel)
|
||||||
{
|
{
|
||||||
builder<FloatType, Comments, TrailingCommas, NulIsEnd, true, Store> bld(d, src, size);
|
builder<FloatType, Comments, TrailingCommas, NulIsEnd, true> bld(d, src, size);
|
||||||
const bool ok = bld.run();
|
const bool ok = bld.run();
|
||||||
failure = bld.failure();
|
failure = bld.failure();
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
builder<FloatType, Comments, TrailingCommas, NulIsEnd, false, Store> bld(d, src, size);
|
builder<FloatType, Comments, TrailingCommas, NulIsEnd, false> bld(d, src, size);
|
||||||
const bool ok = bld.run();
|
const bool ok = bld.run();
|
||||||
failure = bld.failure();
|
failure = bld.failure();
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// sentinel: src[size] is readable and 0 (e.g. std::string); FloatType: the
|
/// sentinel: src[size] is readable and 0 (e.g. std::string); FloatType: the
|
||||||
/// number_float_t of the document; validate_only: accept(), only the result is
|
/// number_float_t of the document
|
||||||
/// used
|
|
||||||
template<typename FloatType, bool NulIsEnd>
|
template<typename FloatType, bool NulIsEnd>
|
||||||
inline bool build(document_data& d, const char* src, std::size_t size, bool comments, bool trailing_commas, bool sentinel, bool validate_only,
|
inline bool build(document_data& d, const char* src, std::size_t size, bool comments, bool trailing_commas, bool sentinel, parse_failure& failure)
|
||||||
parse_failure& failure)
|
|
||||||
{
|
{
|
||||||
if (validate_only && !comments && !trailing_commas)
|
|
||||||
{
|
|
||||||
// accept() of plain JSON: nothing is stored. The other option sets
|
|
||||||
// could skip the stores just as well; they keep the builder that
|
|
||||||
// stores only so that there are two more instantiations of the parse
|
|
||||||
// loop, not eight.
|
|
||||||
return build_with<FloatType, false, false, NulIsEnd, false>(d, src, size, sentinel, failure);
|
|
||||||
}
|
|
||||||
if (comments)
|
if (comments)
|
||||||
{
|
{
|
||||||
return trailing_commas ? build_with<FloatType, true, true, NulIsEnd, true>(d, src, size, sentinel, failure)
|
return trailing_commas ? build_with<FloatType, true, true, NulIsEnd>(d, src, size, sentinel, failure)
|
||||||
: build_with<FloatType, true, false, NulIsEnd, true>(d, src, size, sentinel, failure);
|
: build_with<FloatType, true, false, NulIsEnd>(d, src, size, sentinel, failure);
|
||||||
}
|
}
|
||||||
return trailing_commas ? build_with<FloatType, false, true, NulIsEnd, true>(d, src, size, sentinel, failure)
|
return trailing_commas ? build_with<FloatType, false, true, NulIsEnd>(d, src, size, sentinel, failure)
|
||||||
: build_with<FloatType, false, false, NulIsEnd, true>(d, src, size, sentinel, failure);
|
: build_with<FloatType, false, false, NulIsEnd>(d, src, size, sentinel, failure);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace view
|
} // namespace view
|
||||||
@@ -10203,7 +10116,7 @@ class basic_json_document
|
|||||||
const bool ignore_trailing_commas = false)
|
const bool ignore_trailing_commas = false)
|
||||||
{
|
{
|
||||||
basic_json_document d;
|
basic_json_document d;
|
||||||
d.read_range(first, last, error_mode(allow_exceptions), ignore_comments, ignore_trailing_commas);
|
d.read_range(first, last, allow_exceptions, ignore_comments, ignore_trailing_commas);
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -10218,7 +10131,7 @@ class basic_json_document
|
|||||||
basic_json_document d;
|
basic_json_document d;
|
||||||
std::string buf;
|
std::string buf;
|
||||||
append_input(buf, std::forward<InputType>(input));
|
append_input(buf, std::forward<InputType>(input));
|
||||||
d.build_owned(std::move(buf), error_mode(allow_exceptions), ignore_comments, ignore_trailing_commas);
|
d.build_owned(std::move(buf), allow_exceptions, ignore_comments, ignore_trailing_commas);
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -10231,8 +10144,7 @@ class basic_json_document
|
|||||||
static bool accept(InputType&& input, const bool ignore_comments = false, const bool ignore_trailing_commas = false)
|
static bool accept(InputType&& input, const bool ignore_comments = false, const bool ignore_trailing_commas = false)
|
||||||
{
|
{
|
||||||
basic_json_document d;
|
basic_json_document d;
|
||||||
d.read_kind(std::forward<InputType>(input), on_error::validate, ignore_comments, ignore_trailing_commas,
|
d.read(std::forward<InputType>(input), false, ignore_comments, ignore_trailing_commas);
|
||||||
std::integral_constant<detail::view::input_kind, detail::view::classify_input<InputType>::value> {});
|
|
||||||
return !d.is_discarded();
|
return !d.is_discarded();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -10248,7 +10160,7 @@ class basic_json_document
|
|||||||
const bool ignore_comments = false,
|
const bool ignore_comments = false,
|
||||||
const bool ignore_trailing_commas = false)
|
const bool ignore_trailing_commas = false)
|
||||||
{
|
{
|
||||||
read_kind(std::forward<InputType>(input), error_mode(allow_exceptions), ignore_comments, ignore_trailing_commas,
|
read_kind(std::forward<InputType>(input), allow_exceptions, ignore_comments, ignore_trailing_commas,
|
||||||
std::integral_constant<detail::view::input_kind, detail::view::classify_input<InputType>::value> {});
|
std::integral_constant<detail::view::input_kind, detail::view::classify_input<InputType>::value> {});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -10607,22 +10519,6 @@ class basic_json_document
|
|||||||
return idx;
|
return idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// what a failed parse does
|
|
||||||
enum class on_error : std::uint8_t
|
|
||||||
{
|
|
||||||
discard, ///< the document is discarded
|
|
||||||
raise, ///< parse_error is thrown
|
|
||||||
validate ///< accept(): the document is discarded, and no node is stored while parsing
|
|
||||||
};
|
|
||||||
|
|
||||||
static on_error error_mode(bool allow_exceptions) noexcept
|
|
||||||
{
|
|
||||||
return allow_exceptions ? on_error::raise : on_error::discard;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// NulIsEnd of the builder: a null byte ends the input unless strict_nul_handling
|
|
||||||
static constexpr bool nul_is_end = !detail::abi_config::strict_nul_handling;
|
|
||||||
|
|
||||||
/// create the storage (sized for the input) on first use
|
/// create the storage (sized for the input) on first use
|
||||||
void ensure_data(const char* src, std::size_t size)
|
void ensure_data(const char* src, std::size_t size)
|
||||||
{
|
{
|
||||||
@@ -10634,7 +10530,7 @@ class basic_json_document
|
|||||||
|
|
||||||
/// parse a buffer the document takes ownership of (it replaces the
|
/// parse a buffer the document takes ownership of (it replaces the
|
||||||
/// previous owned copy)
|
/// previous owned copy)
|
||||||
void build_owned(std::string&& buf, on_error allow_exceptions, bool comments, bool trailing_commas)
|
void build_owned(std::string&& buf, bool allow_exceptions, bool comments, bool trailing_commas)
|
||||||
{
|
{
|
||||||
ensure_data(buf.data(), buf.size());
|
ensure_data(buf.data(), buf.size());
|
||||||
m_data->owned = std::move(buf);
|
m_data->owned = std::move(buf);
|
||||||
@@ -10644,7 +10540,7 @@ class basic_json_document
|
|||||||
/// parse a copy of the input; the copy is appended to the cleared owned
|
/// parse a copy of the input; the copy is appended to the cleared owned
|
||||||
/// buffer, so that its capacity is reused
|
/// buffer, so that its capacity is reused
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void build_copy(T&& input, on_error allow_exceptions, bool comments, bool trailing_commas)
|
void build_copy(T&& input, bool allow_exceptions, bool comments, bool trailing_commas)
|
||||||
{
|
{
|
||||||
if (!m_data)
|
if (!m_data)
|
||||||
{
|
{
|
||||||
@@ -10665,7 +10561,7 @@ class basic_json_document
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// sentinel: src[size] is readable and 0 (std::string, C strings)
|
/// sentinel: src[size] is readable and 0 (std::string, C strings)
|
||||||
void build(const char* src, std::size_t size, on_error allow_exceptions, bool comments, bool trailing_commas, bool owned, bool sentinel)
|
void build(const char* src, std::size_t size, bool allow_exceptions, bool comments, bool trailing_commas, bool owned, bool sentinel)
|
||||||
{
|
{
|
||||||
ensure_data(src, size);
|
ensure_data(src, size);
|
||||||
document_data& d = *m_data;
|
document_data& d = *m_data;
|
||||||
@@ -10692,8 +10588,7 @@ class basic_json_document
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ok = detail::view::build<typename BasicJsonType::number_float_t, nul_is_end>(d, src, size, comments, trailing_commas, sentinel,
|
ok = detail::view::build < typename BasicJsonType::number_float_t, !detail::abi_config::strict_nul_handling > (d, src, size, comments, trailing_commas, sentinel, failure);
|
||||||
allow_exceptions == on_error::validate, failure);
|
|
||||||
}
|
}
|
||||||
if (NLOHMANN_VIEW_LIKELY(ok))
|
if (NLOHMANN_VIEW_LIKELY(ok))
|
||||||
{
|
{
|
||||||
@@ -10706,7 +10601,7 @@ class basic_json_document
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
std::vector<std::uint32_t>().swap(d.large_objects);
|
std::vector<std::uint32_t>().swap(d.large_objects);
|
||||||
if (allow_exceptions == on_error::raise)
|
if (allow_exceptions)
|
||||||
{
|
{
|
||||||
detail::view::throw_parse_failure<BasicJsonType>(failure, src, size, comments, trailing_commas);
|
detail::view::throw_parse_failure<BasicJsonType>(failure, src, size, comments, trailing_commas);
|
||||||
}
|
}
|
||||||
@@ -10714,13 +10609,13 @@ class basic_json_document
|
|||||||
|
|
||||||
// --- input dispatch (see detail::view::input_kind) ---
|
// --- input dispatch (see detail::view::input_kind) ---
|
||||||
|
|
||||||
void read_kind(std::string&& s, on_error ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::move_string> /*unused*/)
|
void read_kind(std::string&& s, bool ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::move_string> /*unused*/)
|
||||||
{
|
{
|
||||||
build_owned(std::move(s), ae, c, tc);
|
build_owned(std::move(s), ae, c, tc);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename CharT>
|
template<typename CharT>
|
||||||
void read_kind(CharT* s, on_error ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::c_string> /*unused*/)
|
void read_kind(CharT* s, bool ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::c_string> /*unused*/)
|
||||||
{
|
{
|
||||||
if (s == nullptr)
|
if (s == nullptr)
|
||||||
{
|
{
|
||||||
@@ -10734,7 +10629,7 @@ class basic_json_document
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename Array>
|
template<typename Array>
|
||||||
void read_kind(Array& a, on_error ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::char_array> /*unused*/)
|
void read_kind(Array& a, bool ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::char_array> /*unused*/)
|
||||||
{
|
{
|
||||||
using CharT = typename std::remove_cv<typename std::remove_extent<Array>::type>::type;
|
using CharT = typename std::remove_cv<typename std::remove_extent<Array>::type>::type;
|
||||||
const std::size_t n = std::extent<Array>::value;
|
const std::size_t n = std::extent<Array>::value;
|
||||||
@@ -10745,7 +10640,7 @@ class basic_json_document
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void read_kind(const T& s, on_error ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::borrow_range> /*unused*/)
|
void read_kind(const T& s, bool ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::borrow_range> /*unused*/)
|
||||||
{
|
{
|
||||||
// std::basic_string guarantees data()[size()] == 0: use it as sentinel
|
// std::basic_string guarantees data()[size()] == 0: use it as sentinel
|
||||||
const auto size = static_cast<std::size_t>(s.size());
|
const auto size = static_cast<std::size_t>(s.size());
|
||||||
@@ -10754,13 +10649,13 @@ class basic_json_document
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void read_kind(const T& s, on_error ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::copy_range> /*unused*/)
|
void read_kind(const T& s, bool ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::copy_range> /*unused*/)
|
||||||
{
|
{
|
||||||
build_copy(s, ae, c, tc);
|
build_copy(s, ae, c, tc);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename CharT>
|
template<typename CharT>
|
||||||
void read_kind(CharT* s, on_error ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::wide_c_string> /*unused*/)
|
void read_kind(CharT* s, bool ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::wide_c_string> /*unused*/)
|
||||||
{
|
{
|
||||||
if (s == nullptr)
|
if (s == nullptr)
|
||||||
{
|
{
|
||||||
@@ -10772,13 +10667,13 @@ class basic_json_document
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void read_kind(T&& input, on_error ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::adapter> /*unused*/)
|
void read_kind(T&& input, bool ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::adapter> /*unused*/)
|
||||||
{
|
{
|
||||||
build_copy(std::forward<T>(input), ae, c, tc);
|
build_copy(std::forward<T>(input), ae, c, tc);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename IteratorType>
|
template<typename IteratorType>
|
||||||
void read_range(IteratorType first, IteratorType last, on_error ae, bool c, bool tc)
|
void read_range(IteratorType first, IteratorType last, bool ae, bool c, bool tc)
|
||||||
{
|
{
|
||||||
using value_type = typename std::remove_cv<typename std::iterator_traits<IteratorType>::value_type>::type;
|
using value_type = typename std::remove_cv<typename std::iterator_traits<IteratorType>::value_type>::type;
|
||||||
// borrow the range where the library's input adapter scans it as one
|
// borrow the range where the library's input adapter scans it as one
|
||||||
@@ -10789,14 +10684,14 @@ class basic_json_document
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename IteratorType>
|
template<typename IteratorType>
|
||||||
void read_range_impl(IteratorType first, IteratorType last, on_error ae, bool c, bool tc, std::true_type /*contiguous bytes*/)
|
void read_range_impl(IteratorType first, IteratorType last, bool ae, bool c, bool tc, std::true_type /*contiguous bytes*/)
|
||||||
{
|
{
|
||||||
const auto size = static_cast<std::size_t>(std::distance(first, last));
|
const auto size = static_cast<std::size_t>(std::distance(first, last));
|
||||||
build(size == 0 ? "" : reinterpret_cast<const char*>(&*first), size, ae, c, tc, false, size == 0); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
|
build(size == 0 ? "" : reinterpret_cast<const char*>(&*first), size, ae, c, tc, false, size == 0); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename IteratorType>
|
template<typename IteratorType>
|
||||||
void read_range_impl(IteratorType first, IteratorType last, on_error ae, bool c, bool tc, std::false_type /*other*/)
|
void read_range_impl(IteratorType first, IteratorType last, bool ae, bool c, bool tc, std::false_type /*other*/)
|
||||||
{
|
{
|
||||||
build_copy(detail::view::make_adapter_input(detail::input_adapter(first, last)), ae, c, tc);
|
build_copy(detail::view::make_adapter_input(detail::input_adapter(first, last)), ae, c, tc);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,8 +59,7 @@ built build(const std::string& text, bool comments, bool trailing_commas, bool s
|
|||||||
r.copy.assign(text.begin(), text.end());
|
r.copy.assign(text.begin(), text.end());
|
||||||
src = r.copy.data();
|
src = r.copy.data();
|
||||||
}
|
}
|
||||||
constexpr bool nul_is_end = !nlohmann::detail::abi_config::strict_nul_handling;
|
r.ok = nlohmann::detail::view::build < FloatType, !nlohmann::detail::abi_config::strict_nul_handling > (*r.data, src, text.size(), comments, trailing_commas, sentinel, r.failure);
|
||||||
r.ok = nlohmann::detail::view::build<FloatType, nul_is_end>(*r.data, src, text.size(), comments, trailing_commas, sentinel, false, r.failure);
|
|
||||||
r.data->src = src;
|
r.data->src = src;
|
||||||
r.data->base[0] = src;
|
r.data->base[0] = src;
|
||||||
r.data->base[1] = r.data->arena.data();
|
r.data->base[1] = r.data->arena.data();
|
||||||
@@ -173,22 +172,6 @@ void check_string(const std::string& content)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// accept() stores no nodes: it must accept and reject what json::accept does, for all options and for inputs with
|
|
||||||
// and without a terminating NUL
|
|
||||||
void check_accept(const std::string& text)
|
|
||||||
{
|
|
||||||
CAPTURE(text)
|
|
||||||
const std::vector<char> bytes(text.begin(), text.end());
|
|
||||||
for (int options = 0; options < 4; ++options)
|
|
||||||
{
|
|
||||||
const bool comments = (options & 1) != 0;
|
|
||||||
const bool trailing_commas = (options & 2) != 0;
|
|
||||||
const bool accepted = json::accept(text, comments, trailing_commas);
|
|
||||||
CHECK(nlohmann::json_document::accept(text, comments, trailing_commas) == accepted);
|
|
||||||
CHECK(nlohmann::json_document::accept(bytes, comments, trailing_commas) == accepted);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// a small deterministic generator of documents
|
// a small deterministic generator of documents
|
||||||
struct generator
|
struct generator
|
||||||
{
|
{
|
||||||
@@ -440,133 +423,6 @@ TEST_CASE("json_view builder")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("accept() without an index")
|
|
||||||
{
|
|
||||||
for (const char* text :
|
|
||||||
{
|
|
||||||
"", " ", "\n", "null", "nul", "true", "[", "]", "[]", "{}", "[1,2,3]", "[1,2,3", "[1,2,3,]", "{\"a\":1,}", "{\"a\"}", "{\"a\":}",
|
|
||||||
"[1] x", "[1][2]", "\xEF\xBB\xBF[1]", "[\"a\\", "[\"\\u12\"]", "[\"\\ud800\"]", "[\"\x01\"]", "[\"\xff\"]", "[01]", "[1.]",
|
|
||||||
"[.5]", "[-]", "[1e]", "// c\n[1]", "/* c */ {\"a\" /* x */ : 1}", "/* c", "[1] // c", "1e400", "-1e400", "123456789012345678901234567890",
|
|
||||||
"{\"a\":1,\"a\":2}", "[[[]]]", "\"\\u00e4\\n\\ud83d\\ude00\""
|
|
||||||
})
|
|
||||||
{
|
|
||||||
check_accept(text);
|
|
||||||
}
|
|
||||||
// a null byte inside the text, and after it
|
|
||||||
check_accept(std::string("[1,\0]", 5));
|
|
||||||
check_accept(std::string("[1]\0", 4));
|
|
||||||
check_accept(std::string("\0", 1));
|
|
||||||
|
|
||||||
// generated documents and damaged copies, as for the builder above
|
|
||||||
generator g;
|
|
||||||
for (int i = 0; i < 2000; ++i)
|
|
||||||
{
|
|
||||||
std::string text;
|
|
||||||
g.value(text, 0);
|
|
||||||
check_accept(text);
|
|
||||||
std::string damaged = text;
|
|
||||||
const auto at = static_cast<std::size_t>(g.r(static_cast<int>(damaged.size())));
|
|
||||||
static const char replacements[] = {'x', '"', '\\', ',', ':', ']', '}', '[', '{', '1', '-', '.', 'e', '\0', '\n', '/'}; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
|
||||||
damaged[at] = replacements[g.r(16)];
|
|
||||||
check_accept(damaged);
|
|
||||||
check_accept(text.substr(0, at));
|
|
||||||
}
|
|
||||||
|
|
||||||
// nesting beyond the 64 levels that are kept on the builder's own stack, closed and not closed
|
|
||||||
for (const std::size_t depth :
|
|
||||||
{
|
|
||||||
63, 64, 65, 66, 200, 1000
|
|
||||||
})
|
|
||||||
{
|
|
||||||
check_accept(std::string(depth, '[') + std::string(depth, ']'));
|
|
||||||
check_accept(std::string(depth, '[') + std::string(depth - 1, ']'));
|
|
||||||
std::string objects;
|
|
||||||
for (std::size_t i = 0; i < depth; ++i)
|
|
||||||
{
|
|
||||||
objects += "{\"k\":";
|
|
||||||
}
|
|
||||||
objects += "1" + std::string(depth, '}');
|
|
||||||
check_accept(objects);
|
|
||||||
check_accept(objects.substr(0, objects.size() - 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
// objects with enough members to get a hash index when they are indexed (and duplicate keys)
|
|
||||||
std::string large = "{";
|
|
||||||
for (int i = 0; i < 300; ++i)
|
|
||||||
{
|
|
||||||
large += (i == 0 ? "\"k" : ",\"k") + std::to_string(i % 200) + "\":" + std::to_string(i);
|
|
||||||
}
|
|
||||||
check_accept(large + "}");
|
|
||||||
check_accept(large);
|
|
||||||
check_accept("[" + large + "},{" + large.substr(1) + "}]");
|
|
||||||
|
|
||||||
// the overflow check uses the floating-point type of the document
|
|
||||||
using float_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t, float>;
|
|
||||||
for (const char* text :
|
|
||||||
{
|
|
||||||
"1e39", "-1e39", "3.4028235e38", "3.4028236e38", "[1.5e38, 3.5e38]", "{\"a\": 1e-50, \"b\": 1e39}", "340282356779733661637539395458142568448"
|
|
||||||
})
|
|
||||||
{
|
|
||||||
CAPTURE(text)
|
|
||||||
CHECK(nlohmann::basic_json_document<float_json>::accept(text) == float_json::accept(text));
|
|
||||||
}
|
|
||||||
|
|
||||||
// strings are checked and not decoded: runs of text of every length from 0 to 40 between valid and invalid
|
|
||||||
// escapes, UTF-8 sequences and control characters, in values and in keys, cut near the end of the text
|
|
||||||
const std::array<const char*, 9> valid = {{"\\n", "\\\"", "\\\\", "\\/", "\\t", "\\u00e4", "\\u20ac", "\\ud83d\\ude00", "\\u0000"}};
|
|
||||||
const std::array<const char*, 12> invalid = {{"\\x", "\\ud800", "\\udc00", "\\u12G4", "\\u12", "\\ud83dx", "\\ud83d\\u0041", "\\ud83d\\ud83d", "\\", "\x01", "\x1f", "\x7f"}};
|
|
||||||
const std::array<const char*, 6> utf8 = {{"\xc3\xa4", "\xe2\x82\xac", "\xf0\x9f\x98\x80", "\xc3", "\xff", "\x80"}};
|
|
||||||
std::mt19937 rng(2029); // NOLINT(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed): reproducible
|
|
||||||
for (std::size_t run = 0; run <= 40; ++run)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < 30; ++i)
|
|
||||||
{
|
|
||||||
std::string content = std::string(rng() % 20, 'a');
|
|
||||||
const auto pieces = 1 + (rng() % 6);
|
|
||||||
for (unsigned k = 0; k < pieces; ++k)
|
|
||||||
{
|
|
||||||
content += valid[rng() % valid.size()];
|
|
||||||
content += std::string(k == 0 ? run : rng() % 41, static_cast<char>('b' + (rng() % 20)));
|
|
||||||
switch (rng() % 8)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
content += utf8[rng() % 3];
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
content += utf8[rng() % utf8.size()];
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
content += invalid[rng() % invalid.size()];
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (const std::string& text :
|
|
||||||
{
|
|
||||||
"[\"" + content + "\"]", "{\"" + content + "\":1}", "[\"" + content, "[\"" + content.substr(0, content.size() - (rng() % 4)) + "\"]"
|
|
||||||
})
|
|
||||||
{
|
|
||||||
check_accept(text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("escapes in a row")
|
|
||||||
{
|
|
||||||
for (const char* content :
|
|
||||||
{
|
|
||||||
"\\n", "\\n\\t\\\\", "\\u00e9\\u00e9\\u4e2d", "\\u00e9\\u00e", "\\u00e9\\", "\\n\\q", "\\n\\ud83d\\ude00\\n",
|
|
||||||
"\\ud83d\\ude00\\ud83d", "\\n\\n\\n abc \\n\\n", "\\n\x01", "\\n\xC3\x28", "\\n\\"
|
|
||||||
})
|
|
||||||
{
|
|
||||||
check_accept(std::string("[\"") + content + "\"]");
|
|
||||||
check_accept(std::string("[\"") + content);
|
|
||||||
check_accept(std::string("{\"") + content + "\":1}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("test files")
|
SECTION("test files")
|
||||||
{
|
{
|
||||||
for (const char* name :
|
for (const char* name :
|
||||||
@@ -719,6 +575,59 @@ TEST_CASE("json_view builder: escapes between runs of text")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("json_view builder: chains of unicode escapes")
|
||||||
|
{
|
||||||
|
// Consecutive \uXXXX escapes are decoded in a loop of their own. Chains of
|
||||||
|
// every length from 1 to 40 of escapes that give one, two, and three
|
||||||
|
// bytes of UTF-8, in upper and lower case, followed by what ends a chain:
|
||||||
|
// the end of the string, text, another escape, UTF-8, and everything that
|
||||||
|
// is not a valid escape (a bad digit at every position of the last
|
||||||
|
// escape, a lone surrogate, a surrogate pair, a cut in every place).
|
||||||
|
const std::array<const char*, 8> chain = {{"\\u0041", "\\u00e4", "\\u00E4", "\\u20ac", "\\u3042", "\\uFFFF", "\\u0000", "\\u07ff"}};
|
||||||
|
const std::array<const char*, 12> ends = {{"", "abc", "\\n", "\\u00e9", "\\ud83d\\ude00", "\\ud83d", "\\ude00", "\\ud83d\\u0041", "\\u12G4", "\\u12", "\xc3\xa4", "\\"}};
|
||||||
|
std::mt19937 rng(2027); // NOLINT(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed): reproducible
|
||||||
|
for (std::size_t length = 1; length <= 40; ++length)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 20; ++i)
|
||||||
|
{
|
||||||
|
std::string content = std::string(rng() % 20, 'a');
|
||||||
|
for (std::size_t k = 0; k < length; ++k)
|
||||||
|
{
|
||||||
|
content += chain[rng() % chain.size()];
|
||||||
|
}
|
||||||
|
const std::string whole = content;
|
||||||
|
for (const char* end : ends)
|
||||||
|
{
|
||||||
|
check_string(whole + end);
|
||||||
|
check_string(whole + end + std::string(rng() % 19, 'z'));
|
||||||
|
}
|
||||||
|
// a bad digit at every position of every escape near the end of the chain
|
||||||
|
for (std::size_t back = 1; back <= 6 && back <= length; ++back)
|
||||||
|
{
|
||||||
|
for (std::size_t digit = 2; digit < 6; ++digit)
|
||||||
|
{
|
||||||
|
std::string broken = whole;
|
||||||
|
broken[broken.size() - (6 * back) + digit] = "gG\"\x01 z"[rng() % 6];
|
||||||
|
check_string(broken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// the text cut in every place of the last three escapes
|
||||||
|
for (std::size_t cut = 0; cut <= 18 && cut <= whole.size(); ++cut)
|
||||||
|
{
|
||||||
|
check_string(whole.substr(0, whole.size() - cut));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// a chain longer than the first growth of the arena, in a key and in a value
|
||||||
|
std::string long_chain;
|
||||||
|
for (int i = 0; i < 3000; ++i)
|
||||||
|
{
|
||||||
|
long_chain += chain[static_cast<std::size_t>(i) % chain.size()];
|
||||||
|
}
|
||||||
|
check_string(long_chain);
|
||||||
|
check_string(long_chain + "\\ud83d\\ude00" + long_chain);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("json_view node integer bits")
|
TEST_CASE("json_view node integer bits")
|
||||||
{
|
{
|
||||||
using nlohmann::detail::view::integer_bits;
|
using nlohmann::detail::view::integer_bits;
|
||||||
|
|||||||
@@ -443,6 +443,30 @@ TEST_CASE("json_view from_json")
|
|||||||
CHECK(d.root().materialize().get<point>().x == 3);
|
CHECK(d.root().materialize().get<point>().x == 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SECTION("escaped keys: members are found by their decoded names")
|
||||||
|
{
|
||||||
|
// keys are decoded before they are compared, as by parse(); a lookup
|
||||||
|
// that compares the raw bytes of the source would miss these members
|
||||||
|
const json_document d = json_document::parse(R"([{"\u0078": 1, "y": 2}, {"V\u0061lue": 3, "Te\u0078t": "t"}, {"\u0062": "escaped"}, {"x": 1, "\u0078": 3, "y": 2}])");
|
||||||
|
const json j = d.root().materialize();
|
||||||
|
CHECK(d.root()[0].get<point>().x == 1);
|
||||||
|
CHECK(d.root()[0].get<point>().y == 2);
|
||||||
|
CHECK((d.root()[0].get<point>() == j[0].get<point>()));
|
||||||
|
CHECK(d.root()[1].get<with_names>().value == 3);
|
||||||
|
CHECK(d.root()[1].get<with_names>().text == "t");
|
||||||
|
CHECK(d.root()[2].get<with_defaults>().b == "escaped");
|
||||||
|
CHECK(d.root()[2].get<with_defaults>().a == 1);
|
||||||
|
// a duplicate key written once plain and once escaped: still the first member
|
||||||
|
CHECK(d.root()[3].get<point>().x == 1);
|
||||||
|
CHECK(j[3].get<point>().x == 3);
|
||||||
|
#if !defined(JSON_NOEXCEPTION) && !JSON_DIAGNOSTICS && !JSON_DIAGNOSTIC_POSITIONS
|
||||||
|
// an escaped backslash is part of the decoded key: "x\\" is not "x"
|
||||||
|
const json_document e = json_document::parse(R"({"x\\": 1, "y": 2})");
|
||||||
|
CHECK(exception_of_call([&] { static_cast<void>(e.root().get<point>()); }) == exception_of_call([&] { static_cast<void>(e.root().materialize().get<point>()); }));
|
||||||
|
CHECK(exception_of_call([&] { static_cast<void>(e.root().get<point>()); }) == "[json.exception.out_of_range.403] key 'x' not found");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
SECTION("editable documents and ordered_json")
|
SECTION("editable documents and ordered_json")
|
||||||
{
|
{
|
||||||
json_editable_document e = json_editable_document::parse(R"({"x": 1, "y": 2})");
|
json_editable_document e = json_editable_document::parse(R"({"x": 1, "y": 2})");
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ using nlohmann::json;
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
|
#include <limits>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -248,6 +249,51 @@ TEST_CASE_TEMPLATE("serialization for extreme integer values", T, int32_t, uint3
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("integers of every digit count are written like std::to_string")
|
||||||
|
{
|
||||||
|
// dump_integer() writes up to 16 digits in two groups of eight and 17 to
|
||||||
|
// 20 digits as one to four digits and two groups of eight
|
||||||
|
std::vector<std::uint64_t> values;
|
||||||
|
std::uint64_t power = 1;
|
||||||
|
for (int digits = 1; digits <= 20; ++digits)
|
||||||
|
{
|
||||||
|
values.push_back(power); // 1 followed by zeros
|
||||||
|
values.push_back(power + 1);
|
||||||
|
values.push_back((power * 2) - 1);
|
||||||
|
if (digits < 20)
|
||||||
|
{
|
||||||
|
values.push_back((power * 10) - 1); // all 9s
|
||||||
|
values.push_back((power * 10) - power); // 9 followed by zeros
|
||||||
|
power *= 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
values.push_back((std::numeric_limits<std::uint64_t>::max)());
|
||||||
|
values.push_back(static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()));
|
||||||
|
values.push_back(static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()) + 1);
|
||||||
|
std::uint64_t state = 0x9E3779B97F4A7C15u;
|
||||||
|
for (int i = 0; i < 20000; ++i)
|
||||||
|
{
|
||||||
|
state = (state * 6364136223846793005u) + 1442695040888963407u;
|
||||||
|
values.push_back(state >> (state % 61)); // every bit length
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto v : values)
|
||||||
|
{
|
||||||
|
CAPTURE(v)
|
||||||
|
CHECK(json(v).dump() == std::to_string(v));
|
||||||
|
if (v <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))
|
||||||
|
{
|
||||||
|
const auto s = static_cast<std::int64_t>(v);
|
||||||
|
CHECK(json(s).dump() == std::to_string(s));
|
||||||
|
CHECK(json(-s).dump() == std::to_string(-s));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CHECK(json((std::numeric_limits<std::int64_t>::min)()).dump() == "-9223372036854775808");
|
||||||
|
|
||||||
|
// in a container, so the digits are followed by other output
|
||||||
|
CHECK(json::array({std::uint64_t{18446744073709551615u}, std::int64_t{-1234567890123456789}, 1}).dump() == "[18446744073709551615,-1234567890123456789,1]");
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("dump with binary values")
|
TEST_CASE("dump with binary values")
|
||||||
{
|
{
|
||||||
auto binary = json::binary({1, 2, 3, 4});
|
auto binary = json::binary({1, 2, 3, 4});
|
||||||
|
|||||||
Reference in new issue
Block a user