mirror of
https://github.com/nlohmann/json.git
synced 2026-10-25 15:28:54 +00:00
ea3dca202b2e94455a0c17bbccbf270711af4ba7
9
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
bcc19dca3f |
json_view: view_mode enum instead of bool Editable, node_kind for node::kind
basic_json_document and basic_json_view take a view_mode (read_only, the default, or editable) instead of a bool: basic_json_document<json, true> did not say what true means. The shortcut aliases are unchanged. The kind of a node is a node_kind (the value_t values plus link, 10 as before), which removes the static_cast<std::uint8_t>(value_t::...) of every comparison. The node and image layouts are unchanged. Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
fe5e401870 |
Consolidate duplicated detail helpers and bodies (#5891) (#5901)
* Add shared detail helpers for the #5891 cleanup Add detail::is_primitive(value_t), detail::hex4_lower(), detail::utf8_shape(), detail::input_format_name(), detail::empty_input_message(), the exception ids 116, 319, 320 and 416 to detail::exception_id, and make detail::is_ascii_copyable() constexpr. Later commits switch the duplicated code over to them. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 1.4: BSON writers share one write-pass UTF-8 sanitizer (#5891) write_bson_entry_header and write_bson_string re-derived the error-handler policy differently. Both now use sanitize_utf8_for_bson_write, which treats strict like keep (the size pass already validated) and sanitizes replace/ignore into the bytes the size pass measured. No context plumbing is needed. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.1.1: one MessagePack container prefix helper (#5891) Array and object prefixes differed only in the fix marker and the 16/32-bit markers; both now delegate to write_msgpack_container_prefix. Output is byte-identical. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.1.2: one iterative walker for CBOR and MessagePack (#5891) write_cbor_iterative and write_msgpack_iterative (and their *_value_or_push helpers) were identical but for the head, scalar, and key writers. They are now one template over a small policy struct, so everything inlines. BON8 keeps its own walker (open string run, closing marker), noted in a comment. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.1.3, 2.1.8: one UBJSON container start helper, shared frame base (#5891) write_ubjson_start_array/_object are now write_ubjson_start_container<IsArray>, keeping the array-only begin()+1 scan and excessive_valueless rule. ubjson_frame derives from binary_container_frame instead of duplicating it. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.1.4: write_bytes helper for the byte-string write sites (#5891) The 13 oa.write_characters(reinterpret_cast<const CharType*>(x.data()), n) sites now call write_bytes(const void*, size_t); copy_characters sites and the buffer-based writes are untouched. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.1.5: shared object-key writer for CBOR and MessagePack (#5891) write_cbor_key and write_msgpack_key were the same SFINAE pair apart from the string/scalar writers; both now delegate to write_object_key<Walk>. UBJSON key and to_format_length stay as they are. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.3.10: writer-side format names from input_format_name (#5891) The CBOR/MessagePack/UBJSON/BSON/BJData/BON8 literals in throw_on_discarded and throw_integer_out_of_range calls now come from detail::input_format_name; the texts are identical, and UBJSON vs BJData still follows use_bjdata. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * serializer: constexpr ascii table, hex4_lower, dedupe helpers (#5891) 1.1: build ascii_copyable() at compile time from is_ascii_copyable and test find_ascii_copyable_run against it for every byte. 1.2/2.5.4: use detail::hex4_lower in both \uXXXX writers. 2.1.6: factor the repeated flush/emit blocks in dump_escaped_impl into always-inline helpers. 2.1.7: comment why the escape copy does not use put_buffer. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 1.6: use string_t-aware to_string/concat in meta() (#5891) Replaces the four std::to_string sites; rendered text unchanged. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 1.7: use detail::is_primitive in json.hpp switches (#5891) has_no_children, set_parents, max_size and the two diff walkers. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.2.1: factor insert()/iterator throw helpers (#5891) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.2.2: share swap() overloads via swap_impl (#5891) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.2.4: factor primitive reset and range check in erase()/range ctor (#5891) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.2.6: share push_back array bodies via push_back_array_impl (#5891) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.2.7: route parse/accept/sax_parse through *_impl helpers (#5891) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.2.8: share the vector-returning to_* bodies via to_binary_vector (#5891) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.2.10: merge destroy_string/destroy_binary into destroy_pointer (#5891) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.4.1/1.7: exceptions: share what_impl, flatten diagnostics switch (#5891) The four create() bodies build their message through one protected static helper; the diagnostics parent walk handles array/object and ignores all other types. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.4.2/2.4.3/1.7: json_pointer: share slash and escape checks, drop primitive case lists (#5891) The leading-slash and invalid '~' escape checks are shared helpers; the reference-token walks test object and array first and treat everything else as primitive. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 1.7/2.4.9: iter_impl, iteration_proxy: drop primitive case lists, share compare preamble (#5891) Object and array stay the first branches; everything else is the primitive case. operator== and operator< share the different-container check. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.4.5/2.4.6: from_json: share get_number_value and reserve_if_possible (#5891) The three number cases of get_arithmetic_value and the arithmetic from_json share one helper (boolean stays accepted only by the latter); the array and object reserve overload pairs become one. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.4.8: ordered_map: share erase_impl between the key erase overloads (#5891) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 1.7: json_pointer: flatten walk drops the primitive case list (#5891) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 1.2/1.3/1.5/2.5.1/2.5.2/2.5.5: share helpers in the view code (#5891) - detail::exception_id at every numeric id; throw_* helpers take it - hex_byte, hex4_lower, utf8_shape replace local hex/table code - throw_array_index_out_of_range/_past_end, throw_key_not_found - has_native_float_format replaces the hand-written IEEE check No change of messages, ids or output. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 1.5: use utf8_shape in get_bon8_string, add exhaustive UTF-8 validator test (#5891) get_bon8_string takes the valid range of the second byte from utf8_shape() (0xF5..0xF7 stay errors, unget behavior unchanged). validate_one_utf8 keeps its if-chain (hot bulk path); the new test checks decode, validate_one_utf8, utf8_shape, json::accept and the BON8 reader against each other for every lead and second byte. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.3.1: get_cbor_sized_bytes for the CBOR chunk getters (#5891) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.3.2: get_string_with_length for the plain-order string sites (#5891) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.3.3, 2.3.4, 2.3.10: unsupported_key_type, empty_input_message, input_format_name in the readers (#5891) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.3.7, 2.3.9: read_object_key and leave_container_and_check for CBOR/MessagePack/BON8 (#5891) UBJSON keys (different getter signature) and its container tail stay as is. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.3.5: json_sax_dom_error_state base for the DOM SAX parsers (#5891) Holds errored, allow_exceptions, parse_error() and is_errored(). The scalar handlers stay in each class (parse hot path). Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.3.6: table of control-character messages in the lexer (#5891) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.3.8: finish_input helper for the strict-EOF block in the parser (#5891) sax_parse keeps its if (result) guard and return value. Also adds the clang-tidy NOLINT for the 2.3.6 message table. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.5.7: share parse_array_index between json_pointer and json_view (#5891) Move json_pointer::parse_array_index() and its status enum to namespace detail (json_pointer keeps a forwarding member), and let the json_view pointer resolution use it instead of its own copy of the RFC 6901 index grammar. Both accepted the same tokens with the same outcome. Also update the comment on detail::decode(), which listed four of the five UTF-8 validators and now points to the test that keeps them in sync. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Amalgamate (#5891) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.4.5: keep the inlined number cases in from_json(ArithmeticType&) (#5891) Calling get_number_value() from from_json(ArithmeticType&) made GetArray<std::array<int, 1000>> 2.4x slower (it changes the inlining of the std::array conversion), so that overload keeps its own switch, with a comment. get_arithmetic_value() still uses the shared helper. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.5.7: force-inline detail::parse_array_index (#5891) Moving parse_array_index out of json_pointer stopped clang from inlining it into the pointer resolution (JsonPointerAt/citm_catalog +17%, JsonPointerAt/twitter +8%); JSON_HEDLEY_ALWAYS_INLINE restores the baseline. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 1.5: keep get_bon8_string's own UTF-8 ranges (#5891) Taking the second-byte range from utf8_shape() made BON8 decoding 2-3% slower (FromBinaryShape/scalars, FromBinaryBuffer, alignment-pinned builds), so the byte path keeps its hand-written ranges. The exhaustive validator test still checks that it accepts the same sequences as the other validators. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Amalgamate (#5891) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fix CI failures: constexpr, NRVO, cppcheck, IWYU (#5891) - json_sax_dom_error_state::is_errored() cannot be constexpr: the class is not a literal type (GCC 4.8, GCC -Wpedantic, Clang 3.5). - to_binary_vector: destroy the vector writer before returning, as the output_vector_sink sets the size of the vector in its destructor. Without NRVO (MSVC Debug in C++14 mode), the vector was moved out before it was resized. - parser::sax_parse: return false instead of result after the early exit (cppcheck identicalConditionAfterEarlyExit). - json.hpp: export detail/exceptions.hpp for IWYU, so json_view.hpp can name exception_id without including a detail header. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Address review: throw helpers take input_format_t, simpler type checks - throw_on_discarded() and throw_integer_out_of_range() take an input_format_t and call input_format_name() themselves - has_no_children(), set_parents() and max_size() check array, then object, instead of calling is_primitive() first - parse_impl() chains like accept_impl(); sax_parse_impl() uses if/else - std::array for the control character messages; negation<> in write_object_key(); clarify the is_primitive() comment Signed-off-by: Niels Lohmann <mail@nlohmann.me> * json_view: use exception_id in the value() overloads Signed-off-by: Niels Lohmann <mail@nlohmann.me> * json_sax: assert the invariant of place_container for the static analyzer Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
e9c4fe3cdf |
json_view: indented and ensure_ascii dumps through the fast writer (#5889)
* Write the indented output of json_view's dump() with the fast writer
The compact writer of dump() (cursor in a register, fixed-size copies of
tokens) served dump(-1) only; every dump(indent), and everything with
ensure_ascii that was indented, went through a generic walk that calls put()
for each token and put_repeated() (a memset call) for each line break. The
fast writer now writes indented output, too, with or without ensure_ascii, and
the generic walk is gone. A line break is a copy of a 64-byte ruler (a line
break and 63 indentation characters) when the line is at most 64 bytes, and a
store plus memset beyond that. The depth is counted only in the indented
instantiations (counting it in the compact one cost an editable document 5 %
with Clang). The indented walks are out of line (dump_pretty()): inlined
into dump() next to the compact ones, they made compact dumps of integer
arrays 7 % slower.
A decoded string (one with escape sequences) of a damaged image loaded with
image_check::bounds can contain invalid UTF-8. As before, the compact dump
writes such bytes unchanged, and the indented dump throws type_error.316; its
runs are found by the lexer's validating scan (a scalar validation of every
sequence made dump(2) of decoded CJK text twice as slow as before).
dump(2) instructions per call, macOS arm64 (before -> after):
twitter citm canada jeopardy(4k)
json 3.75M -> 1.85M 8.28M -> 3.93M 67.1M -> 56.0M 9.56M -> 4.81M
editable 3.82M -> 1.84M 8.48M -> 4.02M 68.1M -> 56.2M 9.66M -> 4.75M
Linux instructions (GCC 13 / Clang 18), dump(2) and dump(2, ' ', true):
twitter -25 % / -30 % -11 % / -11 %
citm -29 % / -28 % -24 % / -24 %
jeopardy(4k) -25 % / -32 % -20 % / -24 %
CJK text with \u escapes -6 % / -11 % -2 % / -2 %
The compact dump is unchanged (within +0.4 % on Linux, within noise on the Mac).
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
* Scan the strings of json_view's dump() with ensure_ascii 16 bytes at a time
With ensure_ascii, a string of the source that has a byte >= 0x7F was written
by a generic path: put() per piece and a validate-and-decode step for every
character beyond ASCII. It now goes through a writer with the cursor in a
local variable. The part of the string that has nothing to escape is found
16 bytes at a time (SSE2 or NEON; the existing 8-byte scan without vector
support) and copied; strings that are ASCII throughout are still decided by
the 8-byte check of dump() with ensure_ascii (a 16-byte scan for them was
2-5 % slower for short strings). The two-byte sequences (lead C2..DF) and
the three-byte sequences whose continuation bytes may have any value (lead
E1..EF except ED) are decoded with a few compares; E0, ED, four-byte
sequences, and damaged input go through validate_one_utf8() as before, so
damaged images still throw type_error.316.
dump(-1, ' ', true) instructions per call, macOS arm64 (develop -> now);
the compact dump without ensure_ascii and dumps of ASCII text are unchanged:
twitter 5.42M -> 3.57M citm 3.28M -> 3.24M jeopardy(4k) 5.78M -> 5.65M
CJK text, raw 66.5M -> 35.3M escaped \u 32.7M -> 17.0M
Latin text with accents 11.5M -> 9.4M ASCII text 4.40M -> 4.40M
Linux instructions (GCC 13 / Clang 18), dump(-1, ' ', true):
twitter -9 % / -33 % CJK text -14 % / -47 % Latin text -8 % / -18 %
citm -0.2 % / -0.4 % ASCII text -0.1 % / -0.3 %
The compact dump without ensure_ascii is unchanged (canada 0.0 %).
The unit test covers every character of the Basic Multilingual Plane, and a
character that needs an escape at each position of strings of 0..40 bytes.
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
* Find the end of the plain run of a decoded string 16 bytes at a time with SSE2
The strings of a document that had escape sequences are written in runs
without a quote, backslash, or control character; the compact dump found the
end of a run 8 bytes at a time with a SWAR test. With SSE2 (x86-64) it now
compares 16 bytes per step. NEON keeps the SWAR code: there the mask of a
step costs more than it saves on the short runs that are typical here.
Instructions of dump(), Linux (GCC 13 / Clang 18):
twitter.json 1.99M -> 1.88M (-5.2 %) / 1.97M -> 1.92M (-3.0 %)
CJK text with \u escapes 5.98M -> 4.52M (-24.5 %) / 5.30M -> 4.25M (-19.9 %)
ASCII text with a few escapes 4.28M -> 4.09M (-4.4 %) / 4.24M -> 4.11M (-3.2 %)
jeopardy (4,000 questions) 5.08M -> 4.78M (-5.8 %) / 4.97M -> 4.80M (-3.4 %)
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
* Rename depth_counter's open() and close() to enter() and leave()
Flawfinder took them for the file functions open() and close().
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
* Fix the clang-tidy findings in json_view's dump and its tests
- the SSE2 loop of the string scan is within NOLINTBEGIN/END
(portability-simd-intrinsics), as in to_chars.hpp
- depth_counter<false>'s leave() and get() keep the interface of
depth_counter<true> instead of being static
- write_ascii() compares the room left as a size_t variable
- the test's texts are a std::array, and a JSON fragment with escapes
is a raw string literal
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
* Move dump_ascii() and dump_pretty() next to dump()
Address a review comment: the two dispatchers that dump() calls now follow
it directly.
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
---------
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
|
||
|
|
3e031b06a5 |
Use standard library helpers where available, with C++11 fallbacks (#5876)
* Use standard library helpers where available, with C++11 fallbacks Add detail helpers that call the standard library function when the library provides it (checked with the __cpp_lib_* feature-test macros, so no new ABI-tag macros) and a C++11 implementation otherwise: - detail::clamp (std::clamp): by value, so that static constexpr members need no out-of-line definition before C++17; used in json_view's dump() buffer sizing, where step() can now be a static constexpr member. - detail::bit_cast (std::bit_cast): replaces dtoa_impl::reinterpret_bits and the memcpy bit copies in the number parser, to_chars, the json_view serializer, and binary_writer::to_char_type. - detail::byteswap (std::byteswap): one implementation instead of the binary writer's own byte_swap() overloads and the bswap chain in to_chars' store_msb_first(). - count_leading_zeros/count_trailing_zeros: two 32-bit _BitScanReverse/ _BitScanForward calls for MSVC x86/ARM32 instead of the loop, and std::countl_zero/countr_zero for compilers without builtins. - conjunction, disjunction, negation, bool_constant, and uncvref_t map to their std counterparts in C++17/C++20. Builtins and intrinsics stay ahead of the <bit> functions, as MSVC's std::countl_zero checks for LZCNT at run time unless AVX2 is enabled. The generated code is unchanged with Clang (C++11/20/23, arm64 and x86-64) and GCC 16 for parsing, dumping, and the binary formats. The binary reader keeps its own byte_swap() and the writer its memcpy calls: the smaller code from detail::byteswap/std::bit_cast there made GCC 16 inline get_number()/write_number() differently, which slowed reading by 10-18 % and writing by about 1 %. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Name the target byte order once (JSON_LITTLE_ENDIAN_TARGET, JSON_BIG_ENDIAN_TARGET) to_chars.hpp and bit_ops.hpp repeated the long __BYTE_ORDER__/_MSC_VER conditions; macro_scope.hpp now defines them once (undefined again in macro_unscope.hpp). Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Explain why the serializer growth bound is not a clamp Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
7c26e99ac5 |
Copy ASCII strings in the view's dump with ensure_ascii (#5879)
With ensure_ascii, the view's dump wrote every string through the escaping path, which decodes UTF-8 byte by byte, even strings of the source that are plain ASCII. A string of the source without escape sequences has no quote, backslash, or control character, so ensure_ascii leaves it unchanged exactly when it has no byte >= 0x7F. The dump now checks that eight bytes at a time (one add and one or per word) and copies such strings like the dump without ensure_ascii. The compact walk (dump_compact) now also serves ensure_ascii, through a separate out-of-line instantiation, so the walk without ensure_ascii keeps its speed. Parsing is unchanged. A flag set by the parser would let the dump skip the check and was about twice as fast for compact output, but it costs every parse of string-heavy input 1-2% with GCC. Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
4155e2938f |
Write json_view's dump() without a call or conversion per token (#5633)
* Write json_view's dump() without a call or conversion per token The default dump() (no indentation, no ensure_ascii) gets its own writer that makes the same walk and produces the same output: - the write position stays in a local variable instead of a member, so the compiler keeps it in a register across stores through aliasing char pointers; - strings and number tokens are copied with fixed-size 32-byte moves wherever enough source bytes remain, instead of one memcpy call per token; - the innermost open container lives in local variables; a stack that starts as a local array of 32 entries holds the rest; - unedited documents are walked through the node array in order, and integer tokens are read from the source directly. On top of that, float tokens of at most 15 significant digits are written straight from their digits via zmij::to_shortest() and write_shortest(), without converting to a double and back: such decimals are farther apart than a double's rounding interval, so the token's digits are the double's shortest digits. Tokens of 16+ digits, or edited values, still go through decimal_to_float(). The view's own NEON write_decimal() is removed in favor of the shared writer, and the dump output now grows in 64 KiB steps instead of being resized to its estimate at once. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fix MSVC C4127 and Infer finding in the view serializer Test the template options through enabled() so that conditions combined with them are not constant, and mark the nav::value() null dereference as a false positive like materialize.hpp does. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fix out-of-bounds read when dumping a float token of an unchecked image A float node of an image loaded with image_check::bounds can hold a token whose bytes are not digits, so its value need not have the digit count recorded in the node. write_double_at() passed that count to write_short_decimal(), which indexes its table of powers of ten by it: an assertion failure in debug builds, an out-of-bounds read in release builds. Use the counted overload only if the value has exactly that many digits, otherwise count them. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Tidy the view serializer: doxygen block placement, enabled() for a constant Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Use a named document in the dump test: root() of a temporary does not compile Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Test write_short_decimal and powers_of_ten_16 against to_chars Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fix clang-tidy 22 findings in unit-to_chars.cpp Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Write doubles from their bits only where double is IEEE 754 binary64 Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Split the value and pointer edit tests off unit-json_view_edit.cpp The MinGW linker of the Windows clang jobs cannot link object files with more than 32767 sections ("relocation truncated to fit: IMAGE_REL_AMD64_REL32 against `.rdata'"). unit-json_view_edit.cpp has 35409 sections since the fast dump instantiates more code for editable documents, so its test cases "views and values", "deeply nested values", "pointers below a null value", and "strings of other documents are checked" move into unit-json_view_edit_values.cpp. The helper exception_of_call() that both files use moves into json_view_test_helpers.hpp. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Move the dump tests of this branch to unit-json_view_dump.cpp The dump and comparison tests moved to their own file in the dump pull request; the tests of the fast dump() join them there. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Regenerate the amalgamated headers Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Address review: one powers-of-ten table, literal sizes from the type - dtoa_impl::powers_of_ten() (10^0 to 10^19, one entry per line) replaces powers_of_ten_16() and the table of int_pow10() - put_literal() takes the length of a string literal from its type - small_stack holds the heap vector by reference - the growth step of the output buffer is written in hex Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Explain why the growth of the dump's stack is static Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Regenerate the amalgamated headers Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
4971200b54 |
Add images of json_documents: save() and load() (#5632)
* Add images of json_documents: save() and load()
An image is a document stored so that loading it needs no
parsing: save() writes the node index, the text and the decoded
strings; the static load() reads an image written by save().
load() takes a pointer and size, a borrowed vector, or an owned
rvalue vector; the nodes are copied so they are aligned and can
be edited, while the text and decoded strings stay in the image.
image_check controls how much load() trusts the input: full
checks structure, bounds, strings and numbers, the parser's own
guarantees; bounds checks structure and bounds only; none skips
all checks, for images from a trusted source.
Layout is little-endian only ("NJVI" header, nodes, text, decoded
strings), following the idea of zero-copy formats such as
FlatBuffers and YaFF; the check follows FlatBuffers' Verifier.
New errors: parse_error.116 for a malformed image or a failed
check, type_error.320 for a discarded document or a big-endian
target.
A dedicated fuzzer and 6,000 seeded corruptions, checked under
ASan/UBSan, found and fixed two gaps: unchecked reserved header
fields, and unbounded null/boolean offsets that could make
dump() throw std::length_error.
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
* Fix MSVC errors in image test and add image_check doc anchor
Move the raw string literal out of the CHECK macro (MSVC preprocessor),
cast 64-bit header fields to std::size_t (C4244 on 32-bit), and give the
image_check section in load.md a real anchor for the mkdocs strict build.
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
* Check each distinct string and float range once in the full image check
The full check scanned the contents of every string node and every float token over its own range, so many nodes pointing to one large range made load() quadratic. The structural walk now only collects the ranges; after it, each kind is sorted and checked once per distinct range. Ranges of one kind that overlap without being identical are rejected, as save() never writes them (nodes that share a value share the whole range). The cost is linear in the size of the image plus sorting the ranges.
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
* Adapt the image tests to named documents and last-wins lookups
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
* Test that the targets of links never reach an image
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
* Test lookups in loaded images with duplicate and colliding keys
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
* Release the list of large objects once load() has built the indexes
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
* Test that invalid UTF-8 of a loaded image is not copied into an editable document
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
* Document that editable documents refuse invalid UTF-8 copied from a loaded image
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
* Expect first-wins lookups in the image tests
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
* Avoid useless casts of header fields in the image tests
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
* Fix clang-tidy 22 findings in unit-json_view_image.cpp and image.hpp
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
* Fix MSVC C4244 and C2017 in the image test
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
* Fix the indentation of a loop in the image test
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
* Make json_view.hpp pass include-what-you-use
ci_single_binaries runs IWYU with --error on every header. For json_view.hpp
it suggested adding <array> (std::array is used), the headers that json.hpp
already provides (abi_config, abi_macros, input_adapters, json_pointer,
cpp_future, string_concat, value_t, json_fwd), and <version> for
std::nullptr_t, and removing <cstddef>.
- include <array>
- keep <cstddef> (nullptr_t, size_t; IWYU attributes them to <version> and <cstring>)
- tell IWYU not to suggest the headers that json.hpp provides: the amalgamated
json_view.hpp only includes json.hpp, so including them here would duplicate
their definitions
- drop edit_storage.hpp, which edit.hpp includes
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
* Silence a clang-tidy C++17 finding in the image test
bugprone-suspicious-stringview-data-usage flags data() of the source view
under C++17, although the length is passed and checked by the REQUIRE
before it.
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
* Build the image link test without exceptions
load_result() exists only with exceptions; with JSON_NOEXCEPTION the test
loads the image with the check directly (a failed check aborts).
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
* Regenerate the amalgamated headers
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
* Follow the renames from the review of the view builder
The review of #5620 renamed document_data's tape/tape_size/tape_cap to
nodes/node_count/node_capacity and made the decode arena a
std::vector<char>; adapt the code of this change to it.
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
* Rename the emit lambda in the image writer for Qt compatibility
Qt defines emit as an empty macro, so a Qt header included before
json_view.hpp broke the lambda's declaration (see #5865). The slots
variables in the image tests are renamed as well.
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
* Address the review of json_document images
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
* Regenerate the amalgamated headers
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
---------
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
|
||
|
|
823eee3b59 |
Add editable json_documents: set, push_back, insert, and erase (#5630)
* Add editable json_documents: set, push_back, insert, and erase basic_json_document gets a second template parameter, Editable (false by default), plus the aliases json_editable_document, json_editable_view, ordered_json_editable_document and ordered_json_editable_view. Editable documents can change values and structure without rewriting the source text: set()/push_back() on values, keys, array indices and JSON pointers; insert() before an array element; erase() of an object key, array index or JSON pointer. New values and element sequences go into edit storage that the document owns and never moves, so views keep referring to their value across edits and a parsed node never moves. Read-only documents walk the plain node array and are unaffected. Strings are checked for UTF-8 on entry, so dump() of an editable document never throws type_error.316. Binary values cannot be stored (type_error.319). A seeded differential test applies random edits to an editable document and to the equivalent ordered_json and compares both after every step. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fix old clang and MSVC C4127 in editable json documents Value-initialize the const std::less in find_parent (clang 3.4/3.6 do not implement DR 253), and test the Editable template argument through a function to avoid MSVC C4127. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Copy nested values into a document without recursion Counting and copying the nodes of a view or a basic_json value into an editable document recursed once per nesting level, so that a deeply nested value overflowed the stack. The four functions now walk the value with an explicit stack, as materialize() does. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Do not leave a half-assigned container when set_moved() throws assign() rewrote the kind, length and extent of a slot before set_moved() registered its new element sequence. set_moved() can throw (std::bad_alloc from reserving the bookkeeping vectors) for a slot that is not moved yet, which left a container without the moved flag that showed its old children. The bookkeeping is now reserved first (reserve_moved()), so that nothing after the first write to the slot can throw. block_of() reserves before it marks anything for the same reason. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Check strings copied from another document for valid UTF-8 An editable document only holds valid UTF-8, but copy_scalar() copied the strings and keys of a view of another document unchecked. A document of a weaker check (or a borrowed text that changed after parsing) could therefore bring ill-formed UTF-8 into it. The copy is checked now, with the error that dump() reports for the string; copies within the same document stay unchecked. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Allow the edit arena to grow up to 4 GiB - 1 bytes append_text() doubled the capacity of the arena and rejected the result if it exceeded 4 GiB, so that an arena of more than 2 GiB could not grow even though the 32-bit offsets of nodes address 4 GiB - 1 bytes. The capacity is now clamped to that limit (text_capacity()), and an append is rejected only if the bytes themselves do not fit. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Set a null value below a pointer as basic_json does set(json_pointer, value) turned a null parent into an object for every last reference token, so "/a/0" and "/a/-" below {"a":null} made {"a":{"0":1}}, where basic_json's operator[](json_pointer) makes an array. A null parent now becomes an array for "-" and for digit tokens (filled with nulls up to the index) and an object otherwise. An invalid index is reported before the parent changes. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Replace a container by a scalar in constant time once it is linked Replacing an array or object that spans several nodes by a scalar looks up its parent from the root (find_parent), which is linear in the size of the document: setting every element of a 20,000 element array took more than half a second. The parent only has to switch to links once; afterward the extent of the replaced value no longer matters. Nodes that an entry of a moved sequence links to are now marked (node_flags::linked), and assign() skips the lookup for them. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Update the json_view banner and type_error.319 for editable documents The banner still described a read-only view, and the documentation of type_error.319 listed set and push_back only, although insert rejects binary values as well. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Assign the member that lookups find when set() meets duplicate keys An editable document reads the last member of a repeated key (as the read-only document does), but set(object, key, value) assigned the first one: a view taken from object[key] before the call did not show the new value. It now assigns the last member's value, keeps the key at the position of its first occurrence (where materialize() puts it), and drops the other members. The seeded differential test now also edits documents whose objects repeat keys and compares every lookup (operator[], at, find, value, contains, count, and JSON pointers) with the parsed basic_json value. A targeted test covers duplicates before and after edits that move the object, in large objects with an index, in moved arrays, and in values copied from other documents. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Assign the first member when set() meets duplicate keys again Lookups find the first member of a repeated key again, so set(object, key, value) assigns that member (keeping the key where it is) and drops the others, as documented before, and a view taken from object[key] shows the new value. This undoes the code and documentation changes of |
||
|
|
4c3a3eb5b5 |
Add dump() and comparisons to json_view (#5624)
* Add dump() and comparisons to json_view Add basic_json_view::dump() and the comparison operators, and read floats from the parser's digit layout instead of rescanning the token. dump(indent, indent_char, ensure_ascii, number_format) writes a value the way ordered_json::parse(text).dump() writes it for the same arguments: members in document order, all of them should a key occur more than once; strings escaped by the same rules, using the library's scanning kernels; floats written with the library's to_chars conversion, so the output equals basic_json's byte for byte; integers copied from the source, where they are already canonical, except -0, which parse() reads as 0. There is no error_handler argument, because the view only holds valid UTF-8. number_format::source copies numbers exactly as they appear in the source (e.g. "1.50", "1E2", "-0"), which basic_json cannot provide. operator<< takes the indentation from the stream width, as for basic_json. The writer walks iteratively, so nesting depth is limited by memory only. operator== and operator!= compare two views, or a view and a basic_json value in either order, by the rules basic_json's operator== uses: numbers compare by value across their types, objects compare by their members with duplicate keys resolved as parse() resolves them, member order matters only where the object type keeps one, and discarded views compare as discarded basic_json values do, including under JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON. Nothing is materialized except single scalars. While parsing, the view now records where the integer digits, the fraction digits, and the exponent of a float token are, so floats and doubles with at most 19 digits are read from that layout with the library's decimal_to_float() instead of rescanning the token. Both round correctly, so the values are those of parse(). get<double>(), materialize(), dump(), and the comparisons all use it. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Avoid raw string with escapes inside CHECK macro (MSVC C2017) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Size the dump buffer of a small value by its own extent The source extent of a value was read from the next node, falling back to the rest of the document when that node held a decoded string. dump() of a small value could thus allocate a buffer as large as the document. Skip a few such nodes, cap the fallback estimate, and shrink a buffer that is much larger than its output. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Hold the documents of the dump and comparison tests in names root() of a temporary document is deleted: its views would dangle. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Do not shrink the output of dump() finish() copied the whole output when the buffer was more than twice as large as the result (citm dump +11%). The tighter source_extent() estimate already keeps the buffer of a small value small. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Document and test first-wins lookups next to dump() and == dump() writes every member and == resolves duplicate keys as parse() does, whereas lookups find the first member of a duplicate key. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fix the clang-tidy 22 findings in json_view and its tests Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Split the dump and comparison tests off unit-json_view.cpp The MinGW linker of the Windows clang jobs cannot link object files with more than 32767 sections ("relocation truncated to fit: IMAGE_REL_AMD64_REL32 against `.rdata'"). unit-json_view.cpp reaches that limit as the stack grows, so its "json_view dump" and "json_view comparison" test cases move into unit-json_view_dump.cpp. The test generator and has_duplicate_keys() that both files use move into json_view_test_helpers.hpp. The new file mentions JSON_HAS_CPP_17, so it is built for C++17 like the file it was split from. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Make json_view.hpp pass include-what-you-use ci_single_binaries runs IWYU with --error on every header. For json_view.hpp it suggested adding <array> (std::array is used), the headers that json.hpp already provides (abi_config, abi_macros, input_adapters, json_pointer, cpp_future, string_concat, value_t, json_fwd), and <version> for std::nullptr_t, and removing <cstddef>. - include <array> - keep <cstddef> (nullptr_t, size_t; IWYU attributes them to <version> and <cstring>) - tell IWYU not to suggest the headers that json.hpp provides: the amalgamated json_view.hpp only includes json.hpp, so including them here would duplicate their definitions Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Regenerate the amalgamated headers Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Follow the renames from the review of the view builder The review of #5620 renamed document_data's tape/tape_size/tape_cap to nodes/node_count/node_capacity and made the decode arena a std::vector<char>; adapt the code of this change to it. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Keep the stream includes of json_view.hpp for IWYU json_view.hpp uses std::istream and std::ostream itself, but IWYU sees them through the exported json.hpp and asked to remove both includes, which fails ci_single_binaries (single_json_view). Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Address the review of json_view dump() and comparisons From gregmarr's review of #5624: - The output buffer takes string literals without a length. - write_float() names the IEEE single/double condition once, as the library's serializer does. - equal() checks the types first and compares with !=; the reference to the top frame is noted as dangling after pop_back(). - dump.md: one paragraph per case in the notes. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Regenerate the amalgamated headers Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Exclude all json_view test files from Infer's reports The same std::unique_ptr false positive (USE_AFTER_DELETE) also shows for two json_documents parsed in one lambda in unit-json_view_dump.cpp (ci_infer). It affects any test that holds several documents, so the exclusion now covers tests/src/unit-json_view*.cpp; ASan and Valgrind cover these tests. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Allow the string literal parameter of the view serializer's put() for clang-tidy The overload takes the literal by array reference so that its length is a template parameter, like scan_literal() and the C array overloads of to_json() and from_json(). Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Bind the serializer's frame only after the check for its end Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Regenerate the amalgamated headers Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> |