mirror of
https://github.com/nlohmann/json.git
synced 2026-10-21 11:29:53 +00:00
4c3a3eb5b581ca153f03be2931331d4de8d22c4d
1073
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
43e97b629b |
Add element access, iteration, values, and JSON pointers to json_view (#5622)
* Add element access, iteration, values, and JSON pointers to json_view Give basic_json_view the read-only access functions of basic_json: operator[] and at() with keys and indices, front()/back(), find(), contains(), count(), begin()/end() and cbegin()/cend(), items() with structured bindings from C++17 on, and type_name(). Exceptions have the ids and messages of the const functions of basic_json. Where basic_json has undefined behavior the view answers safely: operator[] with a missing key or an out-of-range index returns a discarded view, and front()/back() of an empty container throw invalid_iterator.214. Objects are iterated in document order, and all members are visited; duplicate-key lookups find the first member (as yyjson and simdjson do), while parse(), materialize(), and the map conversions keep the last value, as parse() does. Keys of up to 16 bytes are compared with two overlapping loads. Add value conversions: get<T>()/get_to() for arithmetic types, bool, nullptr_t, strings (std::basic_string copied, string_view_t without a copy), BasicJsonType, views, std::vector, and maps with string keys; get_string() for the string without a copy; number_token() for the number exactly as written in the source; value() with keys and JSON pointers; and operator[]/at()/ contains() with JSON pointers. Everything else, including types with from_json(), goes through materialize() of that subtree. get<T>() of arithmetic types is inlined down to the conversion, so reading an integer needs no call. detail::json_pointer_access exposes a pointer's reference tokens to code outside basic_json. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Make view lookups last-wins and chained access safe * Lookups (operator[], at, find, contains, count, value, JSON pointers) return the last member of a duplicate key, as materialize() and parse() keep it. * operator[] on a discarded view returns a discarded view instead of throwing, so v["a"]["b"] is safe for a missing "a". * operator[] and at() take any integer type (not only int and size_t), fixing ambiguous calls with unsigned, long, std::int64_t, ... * Fix the operator[] documentation, which claimed a discarded view for a type mismatch where type_error.305 is thrown. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Replace the removed operator bool of basic_json_view in the header and its tests A view is tested with is_discarded(); the lookups in at(), value() and contains(json_pointer) and the unit tests no longer rely on the explicit conversion to bool. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Use named documents in the json_view tests that called root() on a temporary root() of an rvalue document is deleted, because the views would dangle. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Test discarded views with is_discarded() in the operator[] docs and examples The explicit conversion to bool of basic_json_view is gone. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Return the first member of duplicate keys from lookups again Looking up the last member of a duplicate key cannot stop at a match, so every lookup scanned the whole object (1.6 to 3.4 times slower for small objects). operator[](key), at, find, value, contains, count, and JSON pointer resolution return the first member again, as yyjson and simdjson do; materialize() and get<map>() keep the last value, as parse(). The documentation says so in the feature page and on each lookup page, and explains how to get the value parse() would give. The integer index templates and the discarded chaining of operator[] stay. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Do not force-inline the view's functions on MSVC __forceinline makes MSVC report C4714 for function templates it cannot inline (get<std::string>), an error under /WX. The forced inlining is only a performance hint, so use plain inline there. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Make json_view.hpp pass include-what-you-use Mark the includes that are needed by design (json.hpp, the macro unscope, <tuple> for the tuple_size specialization), take size_t from <cstring>, and drop the forward declaration of basic_json_document that the friend declaration makes redundant. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Avoid GCC useless-cast warnings in the integer index tests 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> * Fix the clang-tidy 22 findings in json_view and its tests The same changes as on the dump branch, where they were first made, so that this branch passes clang-tidy on its own. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Regenerate the amalgamated headers Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Report one-character non-numeric array indices like basic_json in the view Develop's #5800 made basic_json report a JSON pointer array index such as "/a" as parse_error.109 instead of out_of_range.404, as longer ones were; the view's array_index() follows, with "-" and the empty token still unresolved. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Address the review of json_view element access From gregmarr's review of #5622: - count() counts every member with the key, so count(key) > 1 detects duplicate keys; it was contains() as 0/1 before. - get_to() passes types that get<T>() does not convert directly to from_json with the existing value, as basic_json::get_to() does, so that a from_json which sets only the fields present keeps the others. - array_index() checks the empty token and "-" first; the results are unchanged. - Docs: no "if an exception is thrown" where a view never changes anything; the borrowed source must also stay unmodified; items() on a temporary view is safe; back()/front() notes shortened; the basic_json see-also entries say how the view's results can differ. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Regenerate the amalgamated headers Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Count matching keys as unsigned in the json_view access test The conditional added 1 or 0 as int to a std::size_t, which GCC rejects with -Werror=sign-conversion (ci_test_standards_gcc). Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Address the second review round of json_view element access From gregmarr's review of #5622: - begin()/end(): "a null value or a discarded view iterates as an empty range", singular like the primitive case before it. - The class page's list of function groups has no trailing commas. - document_data::key_equals() compares a key node with a string, for the key comparisons that were spelled out in each lookup. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Regenerate the amalgamated headers Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Write the negated enable_if conditions of json_view with negation<> With a ! or && in the condition, astyle spaces the whole template header (template < ... >); detail::negation and a named trait keep it compact, as the library writes such conditions elsewhere. From gregmarr's review of #5630. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Regenerate the amalgamated headers Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Keep detail::negation out of json_view.hpp's enable_ifs include-what-you-use asks json_view.hpp to include detail/meta/type_traits.hpp for it, which the single header must not do (the amalgamation would duplicate json's internals). The two conditions of value() go through a trait in value.hpp, like the others. 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> |
||
|
|
122d718330 |
Add json_document and json_view: node index, parser, and document (#5620)
* Add json_document and json_view: node index, parser, and document Add json_document and json_view, a read-only, zero-copy index of a JSON text, as the first public slice of the zero-copy view (#5295). A parse produces a flat array of 16-byte nodes in document order, one per value and one per object key. Strings stay in the source text; escaped strings are decoded into an arena. Integers are converted while their digits are in the cache; floats keep only their digit layout and are converted on read. Containers store the size of their subtree, so a reader can step over one in constant time. A document makes a handful of allocations, however many values it has. The parser accepts exactly what json::parse accepts, with every combination of ignore_comments and ignore_trailing_commas, with and without a trailing NUL, and under JSON_STRICT_NUL_HANDLING. It is portable C++11 and does not depend on byte order. basic_json_document adds parse, parse_copy, accept, read (reuses a document's memory), root, is_discarded, source, owns_source, node_count, memory_usage, and shrink_to_fit. basic_json_view adds type, the is_* queries, operator bool, size, empty, materialize, and source_offset. A parse error throws the same exception basic_json::parse would throw for the same input, message and position included. detail::abi_config keeps JSON_STRICT_NUL_HANDLING and JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON readable after json.hpp undefines them, in the ABI namespace so they always match the basic_json in use. A NUL byte that ends a // comment is the end of the input, as in parse() since #5696. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fix document_data for old Clang (3.4-3.6) Drop the empty braced NSDMIs of the std::string members: old Clang rejects the defaulted constructor when it is used by a member initializer before the end of the class definition. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Exclude libstdc++'s string_view comparison from the sign-change check basic_string_view::_S_compare stores the difference of two lengths in a signed difference_type on purpose, which -fsanitize=integer reports for every comparison with a shorter view (test-json_view_cpp17). Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Reject integer lengths in json_document::parse parse(ptr, len) compiled: len converted to allow_exceptions, and ptr was read as a C string, past the end of a buffer without a terminating NUL. Delete the overloads of parse, parse_copy, accept, and read that take an integer other than bool where the flags are expected. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Copy const rvalue strings and document the accepted inputs json_document::parse(std::move(const_string)) failed to compile with "no matching read_kind": only a non-const rvalue std::string can be moved from. Treat a const rvalue as a copied byte container. parse() does not accept everything BasicJsonType::parse() does: a FILE* and pointers to or arrays of wide characters are rejected at compile time. List the supported inputs instead. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Remove the conversion to bool from json_view explicit operator bool meant "refers to a value", which silently differs from what a basic_json converts to. Use !v.is_discarded() instead. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Delete json_document::root() on temporary documents auto v = json_document::parse(text).root() compiled and left the view dangling. Delete the overload for rvalue documents, take a named document in the tests, and document the lifetime rule. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Store node integers by halves on big-endian targets The non-little-endian path of the node writer wrote the integer's native word over len and next, so len got the high half there. Compose and split the value explicitly (len is the low half, next the high half); the little-endian path stays a plain memcpy. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Check the node array size for overflow reserve(n) and the growth of the index computed n * sizeof(node) without a check, which wraps around on 32-bit targets for inputs of about 1 GiB and allocates a too small array. Throw std::bad_alloc for a count beyond the address space and clamp the growth step to it. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * State the exact input size limit of json_document The check rejects inputs of 0xFFFFFFF0 bytes or more, but the exception message and the documentation said 4 GiB. Name the limit once (max_input_size), and state 4 GiB minus 16 bytes in the message and the documentation. Test the limit with a container that only claims the size. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Remove the unused legacy_discarded_value_comparison from abi_config Nothing reads it: the view does not compare values. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Align the banner comment of json_view.hpp Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fuzz json_document with exact-size buffers and parse options The fuzzer only parsed a std::string with default options. Also parse an exact-size byte vector, which has no NUL after its last byte and takes the bounds-checked path, and derive ignore_comments and ignore_trailing_commas from the first input byte, comparing against json::parse and json::accept with the same options. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Give document_data a user-provided constructor Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fix clang-tidy findings in unit-json_view_builder.cpp Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Annotate intentional patterns in unit-json_view.cpp for clang-tidy Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Suppress Flawfinder's read() finding on the deleted overload Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Check the steps of the deep-nesting test separately Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Make max_input_size a function (GCC -Wunused-const-variable) 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 - export json.hpp, keep macro_unscope.hpp, and drop the unused forward declaration of the document class Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Read the node array base after emit() in the view builder's open() emit() moves the node array when it grows, and the subtraction read base in the same expression, so the order was unspecified. MSVC Release builds without forced inlining read the old base; the container index then pointed outside the array and close() wrote out of bounds. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Silence a clang-tidy use-after-move finding in the json_view test const_text is const, so std::move does not move from it; the second call is part of the const rvalue test. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Regenerate the amalgamated headers Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Address the review of the view builder and document Changes promised in the replies to the review of #5620: - read() reuses the owned copy: a copied input (an rvalue byte container, a stream, FILE*, wide characters, an iterator range) is appended to the cleared owned buffer instead of a fresh std::string being move-assigned over it. A moved-in std::string still replaces it. - The error replay runs the library parser with a SAX handler that stores nothing and throws the reported exception, instead of BasicJsonType::parse(), which built a DOM up to the error. - Pointers to and arrays of wide characters, and FILE*, take the input adapter path (converted to UTF-8 into an owned buffer) instead of failing a static_assert. Wide pointers are NUL-terminated. - Streams are read in blocks through rdbuf() into the buffer, instead of character by character through the input adapter; eofbit is set at the end, as by the library's stream adapter. - tape/tape_size/tape_cap are now nodes/node_count/node_capacity. - The builder's members get descriptive, m_-prefixed names. - The shallow frame stack is a std::array (in a wrapper with a user-provided constructor: -Weffc++ wants the member initialized, and value-initializing the 64 frames costs up to 30 % on small documents). - The value dispatch macro has no NEXT parameter; the switch is left with break, and the macro is undefined after its last use. - literal() and digits_exceed() deduce the length from the string literal; each literal has one helper; the two `if (!is_float)` blocks are merged. - arena_ensure() sits right before the writes; the decode arena is a std::vector<char>. - The builder records the deepest nesting, and materialize() sizes its stack from it once (growing it only if a subtree is deeper). - Comment fixes (hex4, slow_string, the NUL rule, put_utf8, 0 - m, the arena resize, grow()) and formatting (macro backslashes, digit cases, one power of ten per line, value_t assertion next to is_container()). - build_with's template parameters follow the builder's order. Greg's idea of keeping the source offset in `off` and the arena offset in `next` for escaped strings was measured and not adopted: string access got 7-18 % slower with a select, and 3.5-18 % slower with a branch-free field pick (Apple M-series, clang -O3), parsing was unchanged. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Apply the review's wording fixes to the json_view documentation - Copying is disabled because of its cost, not because of two documents borrowing the same buffer; the unique_ptr follows from move-only. - Inputs: streams, FILE*, and wide characters (strings, arrays, pointers) are all supported and owned; point to parse_copy() for owned copies. - The error replay only runs for failing input, builds no values, and is skipped with allow_exceptions = false. - read() writes copied inputs into the previous owned copy. - Views can dangle like std::string_view, and why there is no shared/weak pointer design. - The 4 GiB limit (16-byte nodes, 16-byte scanner steps), streams being read to the end first, which macros apply, why materialize() has no source positions, and wording nits (accept() vs basic_json::accept(), is_binary, is_discarded, see-also lists, size table, "back into"). Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Say that json_view converts integers while parsing Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Address the review: null inline nodes, trait spelling, and max() in json_view - inline_nodes is null when the document has no inline nodes, instead of pointing past the end of the allocation; shrink_to_fit() no longer passes it to memcpy() for an empty node array - is_byte_char and is_wide_char use conjunction/disjunction, which astyle leaves unspaced - append_stream() uses (std::max) for the count read Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Include materialize.hpp in the view builder test The test calls detail::view::materialize(), which only the single header pulled in; the multiple-header build (JSON_TEST_USING_MULTIPLE_HEADERS) failed with "'materialize' is not a member of detail::view". Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Rename emit() in the view builder for Qt compatibility Qt defines emit as an empty macro, so a Qt header included before json_view.hpp broke the declaration of the builder's emit(), see issue #5865. It is now called push_node(). Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Do not copy an empty run into an unallocated arena memcpy() must not be passed a null pointer even for zero bytes, which arena_run() did for an empty run while the arena was still empty (ci_test_clang_sanitizer: "null pointer passed as argument 1" in builder.hpp). Only the slow path can see such a run. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Exclude the view builder test from Infer's reports Infer's std::unique_ptr model confuses the values that the test's build() helper returns by value and reports USE_AFTER_DELETE for one of them after the destructor of an unrelated temporary (ci_infer, 12 reports, all in tests/src/unit-json_view_builder.cpp). AddressSanitizer is clean on the same tests. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Skip the deleted-overload checks of json_view on MSVC 2015 MSVC 2015 does not treat selecting a deleted function in decltype as a substitution failure, so is_detected reports the deleted parse/parse_copy/accept/read(ptr, len) overloads as callable and the static_asserts in "integer arguments do not compile" fail (AppVeyor VS2015 jobs). Calling them still fails to compile there. Guard the negative checks as unit-delete_deprecated_functions.cpp does. Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
4da932f518 |
Make parsing from std::istream and FILE* fast (#5828)
* Make parsing from std::istream and FILE* fast
Squashed onto develop
|
||
|
|
cc8334113c |
Do not use Qt's keyword macros as identifiers (#5866)
* Do not use Qt's keyword macros as identifiers The prefix tree used by unflatten() (#5836) had a member named slots, which Qt defines as an empty macro, so including a Qt header before this library failed to compile. Rename it to table and add a test that includes the library with Qt's keyword macros (slots, signals, emit, foreach, forever) defined. Fixes #5865 Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Keep the Qt keyword macros in the test from failing -Wunused-macros Clang (-Werror) rejected the macros that the test defines but the library must not use, and clang-tidy asked for parentheses around the arguments of the Q_FOREACH imitation, where the variable is a declaration. Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
cd6b428ef0 |
Fix CI failures on develop from the API performance stack (#5860)
Fixes every CI failure develop has had since the API performance stack (#5831-#5842) was merged: compiler warnings and errors (Clang, GCC 4.9/11, MSVC 2015/2017/2022), clang-tidy, cpplint, Cppcheck, IWYU, the examples check, the no-exceptions, diagnostic-positions, implicit-conversions and strict-NUL test configurations, and the MinGW link (now with LLD). Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
5683f5e024 |
Define less_general() only without three-way comparison (#5859)
less_general() (#5840) calls operator<(value_t, value_t), which exists in C++20 only for GCC. Explicit instantiation of basic_json compiles every member, so clang-cl and MSVC failed on unit-explicit_instantiation and unit-regression2 in C++20 mode. Its only caller, operator<, is defined only without three-way comparison, as it was before #5840. Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
4da73dc5ff |
Fix compiler warnings on develop from the API performance stack (#5858)
These PRs were merged while their CI runs were cancelled, so the warnings went unnoticed. All of them fail the -Werror builds: - json_pointer.hpp (#5836): size_t += int under GCC -Wsign-conversion - binary_reader.hpp (#5839): char_int_type passed as char under GCC -Wconversion - json.hpp (#5838): @throw without a description under clang -Wdocumentation - string_escape.hpp (#5836): escape_into() was inserted between escape() and its documentation, so the @return was attached to a void function - unit-diagnostics.cpp (#5832): clang -Wnrvo in a lambda without an explicit return type - unit-to_json-rvalue.cpp (#5837): GCC -Wnoexcept (C++20) and clang -Wunneeded-member-function on the test type Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
b41228c227 |
Fix AppVeyor failures from the Zmij double writer (#5843)
MSVC 2015 warns C4554 on the shifts with casts in the fractional part of zmij::to_shortest despite the parentheses; with /WX every target fails to build. Name the shift amounts as constants instead. The shortest-digits test built the candidates with one digit fewer via a stream with std::setprecision(digits.size() - 2); the standard library of MSVC 2017 ignores a precision of 0, so for two-digit values the "shorter" candidates were the value itself. Truncate the digits instead: the value lies within half a unit of the last digit, so the truncated digits and their successor are the closest candidates. Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
dcb96e82fb |
Escape characters below 0x60 from a table straight into the write buffer (#5822)
* Escape characters below 0x60 from a table straight into the write buffer When the bulk scanner in dump_escaped_impl() stops at a control character, a quote or a backslash, that byte used to go through the UTF-8 decoder and a switch that wrote the escape byte by byte into string_buffer, which was then copied into write_buffer at the next run. A byte below 0x60 at a character boundary is a complete code point, so it now skips the decoder: its escape sequence comes from a 96-entry table of 8-byte rows (sequence padded with zeros, length in the last byte), and all 8 bytes are copied straight into write_buffer with one room check. The check sits inside the existing character-boundary block, so it runs once per scanner stop and not once per byte; testing every byte cost about 6% on twitter.json with ensure_ascii. The switch in the decoder's accept branch lost its cases for characters below 0x60, which can no longer reach it. Output is byte-identical. The string-buffer boundary tests now use \u-escaped non-ASCII characters, since simple escapes no longer reach string_buffer, and a new test case checks every ASCII character at every position against a reference escaper. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Avoid raw string literals with backslashes in CHECK macros MSVC's traditional preprocessor mis-lexes raw string literals inside macro arguments and reports illegal escape sequences (C2017, C3850). Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Sweep invalid UTF-8 at every position through dump() Compare dump() of strings with ill-formed UTF-8 (stray continuation bytes, invalid lead bytes, overlong encodings, surrogates, code points above U+10FFFF, and sequences cut short by ASCII, a quote, a backslash, a lead byte, or the end of the string) with an independent reference serializer, for every error handler and with ensure_ascii on and off. The pattern is put at every position of short strings to cross the strides of the bulk scanners, and of longer ones to cross the end of the 512-byte string buffer. Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
91b9ee2602 |
Update cppcheck to 1.5.2 (Cppcheck 2.20) and fix its new findings (#5829)
Cppcheck 2.20 rejects invocations of unknown function-like macros in #if conditions instead of evaluating them as 0. default_defines.hpp expands the Hedley macros to the compiler's feature-test operators (__has_attribute etc.), so define them as 0 in a generated include file. It also reports a false bufferAccessOutOfBounds in the float overload of float_from_bits, sizing the parameter as the double overload's; use the size of the source instead (equal by the static_assert). Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
ce28c722bc |
Allow the same type for number_integer_t and number_unsigned_t (#5815)
* Allow the same type for number_integer_t and number_unsigned_t basic_json did not compile with NumberUnsignedType == NumberIntegerType because overloads on both types collided. Such a basic_json now stores every integer as number_integer: the colliding overloads take a placeholder type, unsigned C++ integers are converted to number_integer_t, and value_t::number_unsigned maps to number_integer. Also fixes two problems this configuration would hit: the unsigned integer parser accepted values that wrap around to negative numbers, and dump() overflowed when computing the absolute value of the minimum. A repeated NumberFloatType gets a static_assert. Refs #2573 Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Use json::with_integers_t in the test and the docs Signed-off-by: Niels Lohmann <mail@nlohmann.me> * ✅ skip patch test on GCC 5 to avoid internal compiler error Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fix CI: name the unsigned overload type through template parameters basic_json used number_unsigned_overload_t<basic_json> while the class was still incomplete. MSVC rejected it as a recursive alias declaration and GCC 4.9, 5 and 7 crashed with an internal compiler error. basic_json now uses number_unsigned_overload_type<NumberIntegerType, NumberUnsignedType>, which also makes the GCC 5 workaround in the patch test unnecessary. Infer reported a null dereference for the number_unsigned case in from_json, which cannot happen for a basic_json whose number_unsigned_t is number_integer_t; assert it. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Address review: conjunction/negation trait, astyle-friendly alias, docs table order Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
6261d1e432 |
Construct the JSON Patch exceptions out of line (#5842)
patch_inplace() built its error messages inline. In particular get_value() concatenated the context-sensitive "operation 'x'" string on every call, even when nothing was wrong. The patch exceptions now come from NEVER_INLINE helpers: the index and key errors reuse basic_json's existing throw_array_index_out_of_range/throw_key_not_found, and the seven patch-specific messages get one helper each in detail/exceptions.hpp. Ids and messages are unchanged, and unit-throw_messages pins them (with and without JSON_DIAGNOSTICS). citm_catalog patch: -6.6 % (Apple clang), -9.5 % (GCC 16). The other remaining throw sites (parser::syntax_error, binary_writer, serializer, input_adapters, enum_key_duplicate, format_error) were prototyped and measured: no gain beyond noise, so they stay as they are. Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
ed86019a21 |
Move binary reader error construction out of line (#5839)
Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
4f11512641 |
Speed up container destruction, iterator access and diff() (#5835)
* Speed up container destruction, iterator access and diff() Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Destroy the first levels of a value by recursion The pointer-reversing walk in destroy_container() avoids the call stack but moves a value around for every container it visits. Recurse for the first recursion_depth_limit() levels instead - no allocation, no thread-local, a bounded frame - and hand anything deeper to the walk. Destroy twitter 0.49 -> 0.36 ms, citm 0.80 -> 0.74 ms (3.12.0: 0.47 / 1.00). Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
6a27b9acb2 |
Construct remaining hot-path exceptions out of line so core functions inline (#5838)
* Construct remaining hot-path exceptions out of line so core functions inline Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Move throw_key_not_found() before at_impl()'s comment block Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
91869983a0 |
Make the value lifecycle inline-friendly (#5840)
* Make the value lifecycle inline-friendly Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Compare standard containers without a thread-local depth count operator== on objects and arrays recursed through the containers' own operator== and counted the depth in a thread-local variable. Where reading a thread-local costs a call (macOS), that added about 10% to comparing documents with many small containers. For the standard containers, walk objects and arrays directly, compare scalars and strings inline, and pass the depth as an argument, falling back to compare_iteratively beyond the depth limit. Other container types keep the previous path. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Address review: owns_memory() helper, leaf compare naming, astyle - Add detail::owns_memory(value_t), a switch listing every enumerator so -Wswitch-enum flags it when a type is added; destroy() and the copy constructor use it instead of hand-written comparisons. Clang emits the same bitmask test as before. - Drop the parentheses around the scalar operands in JSON_IMPLEMENT_SCALAR_CASES; they are only needed for the container operands (#1530), which the comment now says. - Build equal_bounded_containers from detail::conjunction so astyle no longer pads the template brackets. - Rename equal_scalar() to equal_leaf() and document why it also compares strings, unlike JSON_IMPLEMENT_SCALAR_FAST. Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
4530d9681c |
Fix quadratic parent updates for ordered_json with JSON_DIAGNOSTICS (#5832)
* Fix quadratic parent updates for ordered_json with JSON_DIAGNOSTICS Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Only trust containers known to keep their elements in place Address review: decide whether an insertion may have moved the other members by naming the containers that are known not to, instead of the ones that are known to. - detail::has_stable_elements: std::map, std::multimap, std::unordered_map and std::unordered_multimap; object_capacity() reports 0 for them. - detail::is_nlohmann_ordered_map: only the real nlohmann::ordered_map (which only appends) reports its capacity(). - Any other object type reports unknown_size(), so it gets a full set_parents() on every insertion. Before, an object type without capacity() was treated as never moving, and one with capacity() (a sorted flat map, say) was trusted to move only on reallocation. - set_parents_after_object_erase() re-parents unless the object type is stable; the unused set_parent() is removed. - Comments say "the object type" instead of "ordered_json". - New test with an object type that moves its members without changing its capacity; it fails without this change. Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
ca7740fef2 |
Move elements when converting rvalue containers to JSON (#5837)
* Move elements when converting rvalue containers to JSON Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Simplify the rvalue to_json traits Use negation<disjunction<...>> instead of a conjunction of negations and keep the template argument lists compact (astyle only keeps the spaced '< ... >' form when it is written that way). Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
6ba53e63dd |
Speed up array-to-container from_json (#5833)
* Speed up array-to-container from_json Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Hint the end when converting an object to a standard container from_json for map-like containers called emplace() without a hint, which searches the tree for every key. The keys of the source object are sorted, so inserting with the end as the hint is constant time, as it was in 3.12.0 via std::inserter. get<std::map<...>>() of a 25,000 entry object: 4.2 ms -> 3.25 ms (3.12.0: 3.7 ms). Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fall back to emplace() in object from_json for containers without emplace_hint() ordered_map has no hinted insert, so the insert(end(), ...) call broke from_json into types holding an ordered_map (regression test #5122). Use emplace_hint() where available and plain emplace() otherwise. Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
0a4b3a84ee |
Speed up JSON pointer parsing, formatting, comparison and lookup (#5836)
* Speed up JSON pointer parsing, formatting, comparison and lookup Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Restore unflatten() speed after the linear-time rewrite The prefix tree introduced for linear unflatten() kept a std::map per prefix, copied each token into it and looked every token up a second time while creating the values. Replace the per-prefix maps by one open-addressing table keyed by (parent, token) that refers to the parsed tokens, skip the lookups for the prefix shared with the previous key (flattened objects are mostly sorted), and derive the prefix numbers of the second pass from parent links instead of lookups. Time and memory stay linear in the pointer depth, and the result is still independent of the iteration order of the flattened object. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Put [[noreturn]] before __attribute__ on the JSON pointer throw helpers Clang 18 on Linux rejects a C++11 attribute list after a GNU __attribute__ ("an attribute list cannot appear here"). Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
0c4e7c103c |
Make flatten() linear for ordered_json (#5834)
* Make flatten() linear for ordered_json Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Use priority_tag instead of a C-style variadic overload in add_flattened clang-tidy flags the ellipsis fallback (cert-dcl50-cpp, modernize-avoid-variadic-functions). Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
a6e9785ab6 |
Look up the key only once in the callback parser's key() (#5831)
Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
91a7a94cc6 |
Write doubles with the shortest digits (Żmij), digits in registers (#5634)
* Write doubles with the shortest digits (Żmij), digits in registers Write doubles with the conversion of Zmij by Victor Zverovich (MIT), ported to C++11 (detail/conversions/zmij.hpp). It finds the shortest decimal that reads back as the same double, and the closest one if there are several. Grisu2, used until now, is fast but not always shortest: it sometimes writes a 17th digit where 16 suffice, or a last digit that is not the closest. The layout is unchanged (1.5, 100.0, 1e+100, -0.0); float keeps Grisu2. Digits are converted eight at a time with the BCD conversion of Xiang JunBo, as in Zmij, and written with one byte swap per eight digits and fixed-size moves instead of per-digit loops. Leading and trailing zeros are counted from those bytes. to_chars() uses a local buffer when the caller's is shorter than the 41 bytes this may write. The powers of ten come from the number-parsing table, adjusted where it holds values rounded up, and extended with Zmij's compressed tables beyond 10^308. write_shortest() converts its 16 digits in one vector register (SSE2 on x86-64, NEON on 64-bit Arm, both baseline) and inserts the decimal point inside the register, avoiding a store-forwarding stall that cost about 25% of the time to write a double. dump() writes floats and integers straight into the serializer's write buffer instead of copying them from a member buffer, and small integers eight digits at a time. read_eight_bytes() and parse_eight_digits() are marked always-inline, which GCC had been calling out of line in the number-parsing loops. Of one million random doubles, about 0.14% are now written with different digits, always to a value that still reads back as the same double. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fix CI findings in the Zmij writer and its tests - bit_ops.hpp: include macro_scope.hpp (JSON_HEDLEY_ALWAYS_INLINE); fixes IWYU - to_chars.hpp: C4100 for the unused parameter in release builds, clang-tidy sign comparison, cpplint runtime/int, GCC -Wstrict-overflow (unsigned abs) - unit-to_chars.cpp: parse with the library instead of strtod (MinGW's strtod rounds some 16/17 digit inputs wrongly); no floating-point std::to_chars with icpc Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Select the Zmij conversion by a trait, not by the double overload The non-template overloads for double asserted binary64 doubles wherever json.hpp was included, so the library no longer compiled where double is not IEEE 754 binary64 (AVR, -fshort-double). A trait now picks Zmij for any binary64 type, including a long double of that format (MSVC, Apple Arm), and Grisu2 for the others. Remove the unused write_short_decimal, powers_of_ten_16, zmij::decimal, zmij::to_decimal, and shortest_digits(double). Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Credit Zmij's authors in to_chars.hpp, the README, and the license page Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Silence MSVC C4127 for a platform-constant check in the to_chars test 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> |
||
|
|
5b4c8f9b00 |
Speed up the lexer: own float parser, string scan, and \u table (#5738)
* Speed up the lexer: own float parser, string scan, and \u table Give the library its own correctly rounded float converter for binary32 and binary64 (IEEE 754), and speed up the lexer's string and escape scanning. The converter splits a number token into sign, significand, and decimal exponent, then tries Clinger's fast path, then a templated Eisel-Lemire step, and falls back to an exact big-integer digit comparison for tokens with more than 19 significant digits whose two candidate values round differently. This replaces std::from_chars and strtod/strtof for both formats, so parsed values no longer depend on the C/C++ library or the current locale. The strtold fallback kept for other long double formats (x87, binary128) now also copies a multi-byte decimal point correctly, fixing #5660. eisel_lemire() and decimal_to_float() are always inlined so callers keep the whole conversion in their hot loop. The string-scanning kernels in string_scan.hpp find a stop byte with the trailing-zero count of the SWAR mask instead of a byte loop, and scalar_string_bulk_run() validates a run of multi-byte UTF-8 sequences one after another instead of re-searching after each one. get_codepoint() decodes a contiguous \uXXXX escape with one table lookup per byte instead of four range-checked get() calls; the streaming path and all error positions are unchanged. Adds 508 generated hard float-parsing cases with expected binary32 and binary64 bits, and kernel-comparison tests for the string scans and the escape table against byte-by-byte references. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fix CI warnings in own float parser - pow5_table.hpp: pow5_128_largest_power was unused in this branch's own code (GCC -Werror=unused-const-variable); tie it to the table size with a static_assert instead of removing it, since a later branch in the stack (json-view/23-zmij) uses it. - number_parse.hpp: rename the local variable `copy` to `buffer` to satisfy cpplint's build/include_what_you_use check. - unit-class_lexer.cpp: extend the NOLINT list on the seeded mt19937 with bugprone-random-generator-seed, and parenthesize `8 * sizeof(Bits) - 1` for clang-tidy. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Load string scan words with memcpy and use MSVC bit-scan intrinsics Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Reword float conversion notes for the string type and number_float_t docs Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fix clang-tidy 22 findings in unit-class_lexer.cpp Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Mark the unsigned long of the MSVC bit scans for cpplint Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Regenerate the amalgamated headers Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Drop the position updates in the bulk \u path, which #5809 made unused Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Address review: std::any_of in has_nonzero_digit, comment punctuation Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Use data() instead of c_str() in the multi-byte decimal point path Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
e839bbd1c0 |
Export client-facing headers from json.hpp for IWYU / include-cleaner, and check clients in CI (#5813)
* Mark json_fwd.hpp as exported by json.hpp for IWYU / include-cleaner json_fwd.hpp is the only header declaring the nlohmann::json alias, so include-what-you-use and clang-tidy's misc-include-cleaner attributed the name to it and told users of <nlohmann/json.hpp> to include json_fwd.hpp as well (or instead, which breaks compilation). Fixes #5812 Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Export the remaining client-facing headers from json.hpp and check clients with IWYU Besides json_fwd.hpp, include analyzers also told clients of <nlohmann/json.hpp> to include detail/json_ref.hpp (brace initialization), json_literals.hpp (_json, _json_pointer), adl_serializer.hpp, detail/macro_scope.hpp (NLOHMANN_DEFINE_*) and detail/abi_macros.hpp. Mark those includes as exported, too. ci_single_binaries only checked each header on its own, so it could not notice this. It now also runs IWYU and clang-tidy's misc-include-cleaner on small client programs in tests/iwyu_consumer, which fail on develop. With the exports, json_literals.hpp no longer gets the json_fwd.hpp suggestion that kept it out of the IWYU gate, so it is gated again. Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
aa9620fb15 |
Skip the indentation after a newline 8 bytes at a time (#5810)
Pretty-printed JSON indents every line with a run of spaces, which skip_whitespace() read one character at a time. For contiguous input (bulk_scan) the lexer now skips the spaces after a newline 8 bytes at a time directly in the input buffer, including the leading spaces of the last, partial word. This bypasses get(), which is safe because bulk_scan implies the lazy position from the previous commit: nothing is counted per character. Compared to lazy positions alone, pretty-printed input (twitter, citm_catalog with indent 2 and 4) parses up to 13% faster and accept() gets up to 30% faster (GCC 13 and clang 18 on x86-64, Apple clang 21 on arm64). Minified input is unchanged within noise. Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
f23830b65e |
Derive the lexer position lazily for seekable input (#5809)
* Derive the lexer position lazily for seekable input The lexer counted the read position on every character: two increments and a newline check per get(), all on members kept in memory. For seekable input (pointers, strings, other random-access byte ranges) the position can be derived instead: chars_read_total is the adapter's read count (including reads at EOF) minus a pending unget, and line and column are recomputed from the consumed input only when get_position() is called, i.e. on parse errors. JSON_DIAGNOSTIC_POSITIONS uses the new O(1) get_chars_read_total(). Stream, file, wide-string and user-defined adapters keep the eager counters. Parsing pretty-printed input gets 8-12% faster and accept() 12-27% (canada, citm_catalog, twitter; clang 21 arm64 and GCC 13 x86-64). Error messages, byte offsets, SAX positions and diagnostic positions are unchanged (differential comparison against develop on 548k inputs). Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Move the std::vector<std::byte> binary type tests out of unit-regression3 The #4804/#4209 tests instantiated a second basic_json specialization (with_binary_t<std::vector<std::byte>>), including its parser and CBOR reader. With clang 18, the C++20 object of unit-regression3.cpp had 32656 COFF sections on develop; the lazy position helpers pushed it to 32975, past what the MinGW linker of the Windows clang jobs handles ("relocation truncated to fit: IMAGE_REL_AMD64_REL32 against .rdata"). unit-custom-binary-type.cpp already instantiates the same type, so the tests move there; unit-regression3.cpp drops to 27616 sections. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Report the parse errors of the position test via SAX The test caught json::parse_error, so it aborted in the ci_test_noexceptions build, where JSON_NOEXCEPTION turns the throw into std::abort(). sax_parse() hands the error position and the exception object to parse_error() without throwing, so the test now records them there and runs in every configuration. Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
40a62947d3 |
support JSON Merge Patch (RFC 7396) diff creation (#4965)
* support JSON Merge Patch (RFC 7396) diff creation The JSON merge patch document format describes the set of modifications to a resource's content, that more closely mimics the syntax of the resource being modified. However, and in contrast to JSON Patch (RFC 6902), a JSON Merge Patch cannot express certain modifications, e.g., changing an array element at a specific index, or setting a specific object value to null. The null value in a JSON Merge Patch is used to remove the key from the object. The diff algorithm is not part of the RFC 7396, but it was tested against all examples provided, plus additional cases on how null values are handled. Signed-off-by: Luís Murta <luis@murta.dev> * improve algorithm efficiency Signed-off-by: Luís Murta <luis@murta.dev> * fix inconsistent nulls and empty arrays Also add CHECKs for the patch, documentation and run `make amalgamate`. Signed-off-by: Luís Murta <luis@murta.dev> * documentation cleanups Signed-off-by: Luís Murta <luis@murta.dev> * add an iterative fallback to merge_diff Bound the recursion depth like diff() and merge_patch(), and test values nested past the bound. Signed-off-by: Luís Murta <luis@murta.dev> * address the remaining review comments Signed-off-by: Luís Murta <luis@murta.dev> * move multi-line raw strings out of CHECK macros GCC 4.8/5, ICPC and NVHPC mis-tokenize multi-line raw string literals inside macro arguments, breaking the build or the parsed JSON. Signed-off-by: Luís Murta <luis@murta.dev> --------- Signed-off-by: Luís Murta <luis@murta.dev> |
||
|
|
e8b747e4bc |
Re-amalgamate single_include after #5795 and #5806 (#5808)
#5795 and #5806 changed binary_writer.hpp but were merged without
updated amalgamated files, so the check_amalgamation run on develop
(
|
||
|
|
74a03dec84 |
Throw type_error.321 for discarded values in to_bon8 (#5802)
write_bon8_value silently skipped discarded values, but the array/object count marker still counted them, so [1, discarded] produced 82 91: a marker announcing two elements followed by one. Throw type_error.321 like the other binary writers do, at any nesting level. Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
2cca04ae6f |
Report one-character non-numeric array indices like longer ones (#5800)
* Report one-character non-numeric array indices like longer ones
A JSON pointer reference token that is not a number but has only one
character (e.g. "/a/x") was reported as out_of_range.404 ("unresolved
reference token"), because the "is not a number" check only ran for
tokens longer than one character; "/a/xy" got parse_error.109. Both now
throw parse_error.109. "-" and the empty token are still reported as
out_of_range.404. As a consequence, value(json_pointer, default) on an
array now throws for "/x" as it already did for "/xy".
Also document why ordered_map::erase's destroy/placement-new loop on
pair<const Key, T> is kept despite [basic.life]/8 before C++20.
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
* Document the one-character array index change
Add 3.13.0 version-history entries to at, operator[], value, patch,
patch_inplace, and unflatten, and describe in exceptions.md which array
indices throw parse_error.109 and which out_of_range.404.
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
---------
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
|
||
|
|
1649d9eda4 |
Use and extend the detail helpers to remove duplicated code (#5783)
* Use and extend the detail helpers to remove duplicated code Library: - binary_reader: format bytes with hex_byte() instead of snprintf - add throw_type_must_be() for the 20 copies of type_error.302 - binary_reader: add last_byte_error()/unexpected_byte() for the 32 "parse error at the last read byte" sites (replaces bon8_error) - json_sax: add check_container_size() for out_of_range.408 and diagnostic_positions::set_container_start/_end() - json_pointer: add throw_no_parent() (405) and throw_unresolved() (404) Tests: - unit-class_parser uses the shared utils::SaxCountdown - move SaxEventLogger (and its ExitAfter* variants) from unit-class_parser and unit-deserialization into the new tests/src/test_sax.hpp Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Factor out more repeated error paths and test boilerplate Library: - add throw_cannot_use_with() for the 34 copies of type_error.304-312 "cannot use X with Y" - iter_impl: add throw_cannot_get_value() (invalid_iterator.214) - parser: add syntax_error() for the 12 parse_error.101 sites - ordered_map: share the four at() bodies via at_impl() - json_sax_dom_callback_parser: add pop_container() for end_object() and end_array() - binary_reader: build the two UBJSON/BJData length-type messages with concat() and last_byte_error() Tests: - move same_value(), the NDEBUG guard, and step 0 (parse without exceptions) of the seven fuzzer drivers into tests/src/fuzzer_common.hpp Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Name the exception ids and address review comments Add detail::exception_id, a scoped enum with one named enumerator per documented exception id, and use it for every id in the library. The create() functions get an overload for it; the int overloads stay for user code. Following the review of #5783: add binary_reader::invalid_byte() and length_type_error(), basic_json::throw_subscript_wrong_type(), move the fuzzer includes and the using-declaration into fuzzer_common.hpp, and rename test_sax.hpp to sax_event_loggers.hpp. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Silence -Wweak-vtables for the SAX event loggers The loggers moved from anonymous namespaces in the test files into sax_event_loggers.hpp, so clang now warns that their vtables are emitted in every translation unit. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Silence MSVC 2015 C4100 in parser::syntax_error for static SAX::parse_error Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Inline invalid_byte() into its call sites The wrapper only fixed the message string of unexpected_byte(), which is the same kind of per-argument helper that was declined for throw_type_must_be(). Call unexpected_byte("invalid byte", ...) directly. Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
84ee12a67c |
Make UTF8_ACCEPT/UTF8_REJECT inline variables to avoid TU-local exposure in modules (#5797)
Signed-off-by: bvandijk <bas.van.dijk@cern.ch> Co-authored-by: bvandijk <bas.van.dijk@cern.ch> |
||
|
|
2913e96433 |
Unflatten in time and memory linear in the pointer depth (#5793)
* Unflatten in time and memory linear in the pointer depth #5443 made unflatten() decide between arrays and objects independently of the iteration order by collecting the pointer prefixes that have a reference token 0 below them in a std::set<std::vector<string_t>>. Every such prefix was stored as a copy of all its reference tokens, and get_and_create() compared whole prefix vectors at every step, so unflattening a pointer of depth d took time and memory quadratic in d: a 10,000-level array pointer took 18 s and 1.3 GB, a 100,000-level one did not finish. The prefixes are now numbered nodes of a tree, so each is stored once and get_and_create() follows the tree token by token. The result is unchanged, including its independence of the iteration order. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Initialize prefix_tree members to satisfy -Weffc++ Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Move prefix_tree setup and child insertion into member functions The constructor now creates the root node, add_child() inserts a reference token below a prefix and returns the child's number, and find_child() looks one up for get_and_create(). Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
44e8597701 |
Flatten deeply nested values without recursing per nesting level (#5792)
* Flatten deeply nested values without recursing per nesting level json_pointer::flatten() called itself once per nesting level, so flatten() on a value nested deeply enough exhausted the call stack. #5547 and #5548 fixed merge_patch() and diff() from #5393, but flatten() was left out. flatten() now walks the value with an explicit stack and keeps the path in one buffer that grows and shrinks with it. It has a single code path and no depth limit: the old version built a new path string per child, so the iterative one is no slower on shallow values and much faster on deep ones. The output, including the order of an ordered_json result, is unchanged. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Construct flatten frames in place Give the frame a constructor so both call sites can use emplace_back, as suggested in the review; index starts at 0 for every frame. Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
374dfe4f0f |
Keep converted object keys alive while writing UBJSON and BJData (#5791)
* Keep converted object keys alive while writing UBJSON and BJData Since #5746, write_ubjson and write_ubjson_iterative pass each object key to sanitize_utf8_for_write and keep the returned reference. When object_t::key_type is not string_t but converts to it, the argument is a temporary that is destroyed at the end of the statement, and the function returns a reference to it in every case but a sanitized copy, so the key bytes are read from a dead object (AddressSanitizer: stack-use-after-scope). Default json and ordered_json are unaffected. Bind the key to a named object_key_string_t first: a reference when key_type is string_t, so no copy is added there, and a converted copy otherwise. A deleted overload of sanitize_utf8_for_write for anything other than string_t turns a recurrence into a compile error. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Suppress -Wunused-member-function for the converting_key test type converting_key::data() is only called when JSON_DIAGNOSTICS is enabled. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fix clang-tidy findings in the UBJSON/BJData converted-key fix Suppress hicpp/modernize-use-equals-delete on the deleted sanitize_utf8_for_write overload: it guards a private helper and must stay private. Replace the C-style array in the new test with std::array. Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
69a0c1b82c |
Avoid allocating temporary basic_json for cbor and msgpack object keys (#5328)
* avoid allocating temporary basic_json for CBOR and MessagePack object keys Signed-off-by: alexprabhat99 <alexpbara@gmail.com> * add size() to the custom object key test type UBJSON and BJData access object keys through size() and c_str() directly, so the key type now provides both and the comment says why. Signed-off-by: alexprabhat99 <alexpbara@gmail.com> * address review: drop key size()/c_str(), test keys below the depth limit Nothing in the library calls size() or c_str() on an object key, so the test key type only keeps data(), which JSON_DIAGNOSTICS needs. The CBOR and MessagePack custom key tests now also nest objects deeper than detail::recursion_depth_limit(), so keys written by write_cbor_iterative and write_msgpack_iterative are covered as well. Signed-off-by: alexprabhat99 <alexpbara@gmail.com> --------- Signed-off-by: alexprabhat99 <alexpbara@gmail.com> |
||
|
|
a269794db7 |
Use MSVC intrinsics for full multiplication (#5782)
* Use MSVC intrinsics for full multiplication Signed-off-by: Suyog Verma <suyogverma0057@gmail.com> * Fix formatting in unit-class_lexer Signed-off-by: Suyog Verma <suyogverma0057@gmail.com> * Address review feedback Signed-off-by: Suyog Verma <suyogverma0057@gmail.com> --------- Signed-off-by: Suyog Verma <suyogverma0057@gmail.com> |
||
|
|
a5f5d3059b |
Make std::hash<basic_json> consistent with operator== for numbers (#5772)
* Make std::hash<basic_json> consistent with operator== for numbers operator== converts between number_integer, number_unsigned, and number_float before comparing, so json(0), json(0U), and json(0.0) all compare equal. hash() folded the specific value_t into the result for each of the three numeric cases, giving each a distinct hash and breaking the standard Hash requirement that a == b implies hash(a) == hash(b). A std::unordered_set could therefore hold all three as separate elements even though they compare equal. hash() now treats all three numeric variants the same way: it converts the value to number_float_t and combines it with a single shared type tag, so any two numbers operator== considers equal hash identically regardless of which internal type actually holds them. Updated the accompanying test to check this consistency directly (including via an actual unordered_set) instead of asserting that 0, 0U, and 0.0 hash differently, since that assumption was the bug. Also corrected the function's own doc comment and the std::hash API docs, which described the old behavior as intended. Fixes #5400 Signed-off-by: Afonso Januário <afonso-januario@hotmail.com> Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Remove now-unused number_integer_t/number_unsigned_t typedefs in hash() Merging the three numeric branches into one that only reads number_float_t left these two aliases unused, which several CI configurations treat as a build error under -Wunused-local-typedefs. Signed-off-by: Afonso Januário <afonso-januario@hotmail.com> Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Mark the unordered_set in the hash regression test const clang-tidy's misc-const-correctness check flagged it: the set is never mutated after construction, only read via size(). Signed-off-by: Afonso Januário <afonso-januario@hotmail.com> Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Normalize -0.0 in number hashes and test range ends operator== compares numbers exactly since #5459, so equal numbers share one value and convert to the same number_float_t. Update the comment accordingly, map -0.0 to 0.0 before hashing (std::hash need not do that), and test -0.0 and the ends of the integer ranges. Show hash(0.0) in the docs example and note the change in the version history. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Clarify hash documentation after review - Say "may hash differently" for null, false, and numbers, since a collision across types is possible. - Name the storage types (signed integer, unsigned integer, floating-point number) instead of example literals. - Explain that the hash survives converting an integer to number_float_t but not the lossy conversion back, and that unequal numbers may share a hash. - State that the example hash values are illustrative only and vary by platform, compiler, compiler version, and library version. Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Afonso Januário <afonso-januario@hotmail.com> Signed-off-by: Niels Lohmann <mail@nlohmann.me> Co-authored-by: Afonso Januário <afonso-januario@hotmail.com> |
||
|
|
43fe8928e1 |
Stop binary writers overflowing the stack on deep values (#5781)
* Stop binary writers overflowing the stack on deep values
to_cbor, to_msgpack, and to_ubjson recurse once per nesting level.
The parser is iterative, so a value the library accepts can crash on
the way back out.
Keep the existing recursive path for the first 128 levels and finish
anything deeper on a heap stack. Output is unchanged. BSON is left
alone because its extra size walk is a separate change.
Rebased onto the value-type output sink. The heap frames now initialize
every member, which is what -Weffc++ was rejecting.
See #5392.
Signed-off-by: ayush-singh-0601 <singhayush062006@gmail.com>
(cherry picked from commit
|
||
|
|
069ace74af |
Round-trip BJData ND-array annotations exactly (single precision, key order) (#5707)
Squashed onto develop from: - Round-trip BJData ND-array annotations exactly (single precision, key order) Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
367336c83d |
Fix CI on develop after #5585 (#5779)
* Fix CI on develop after #5585 - test-diagnostics-optimized: -O3 makes GCC's -Winline and -Wsuggest-attribute=pure/const warnings fire with the ci_test_gcc flag set; turn them off for this test. - test-diagnostics-optimized: suppress Clang's -Wexit-time-destructors for the static table in to_json. - Infer: raise pulse-max-disjuncts from 20 to 40. With the default, Pulse loses the stored type in basic_json::replace_value() and reports false null dereferences of get_ptr() results in unit-pointer_access.cpp. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Ignore Infer's false USE_AFTER_DELETE in ordered_map::erase Infer's std::string model keeps the buffer of a moved-from string, so the destroy-and-reconstruct loop in erase(first, last) looks like it destroys a buffer twice. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Mark throw_on_discarded()'s parameters as used without exceptions With JSON_NOEXCEPTION, JSON_THROW expands to std::abort(), so Clang's -Wunused-parameter breaks test-disabled_exceptions (since #5761). Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Skip the span_input_adapter sax_parse checks with deleted deprecated functions The #5676 regression test (#5740) calls the deprecated sax_parse(span_input_adapter&&, ...), which JSON_DELETE_DEPRECATED_FUNCTIONS deletes, so ci_test_delete_deprecated_functions failed to build. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fall back to the first entry in test-diagnostics-optimized's to_json clang-tidy (clang-analyzer-security.ArrayBound) flagged it->second for a value not in the table. Use the same fallback as NLOHMANN_JSON_SERIALIZE_ENUM; the test still fails with -Werror=array-bounds on the headers from before #5585. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fix clang-tidy findings in tests from #5762 and #5774 - unit-regression2.cpp (#5762): const/auto for the destroy() test values; NOLINT the intended copy in check_destroy_edge_case(). - unit-serialization.cpp (#5774): build the expected strings with += instead of chained operator+ (performance-inefficient-string-concatenation). Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
30e542e52a |
Declare the namespace-scope constants in to_chars.hpp inline (#5776)
MSVC warns with C5260 that kAlpha and kGamma have internal linkage when the header is used as a header unit or through a module. JSON_INLINE_VARIABLE makes them inline variables from C++17 on. Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
24dd7c63f0 |
Re-amalgamate after #5740 (#5777)
#5740 changed include/nlohmann/json.hpp without regenerating single_include/nlohmann/json.hpp. Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
1c754cfe31 |
Copy a pair-shaped array value under JSON_BRACE_INIT_COPY_SEMANTICS (#5701)
With JSON_BRACE_INIT_COPY_SEMANTICS enabled, single-element brace
initialization from a JSON value decided whether to copy the value or
build an object by inspecting the value's runtime shape: a two-element
array whose first element is a string, such as ["key", 42], was turned
into an object instead of being copied. This made the behavior depend
on the element's content, and it did not distinguish an existing value
of this shape from a nested braced pair written in the source, such as
the inner {"key", "value"} of {{"key", "value"}}.
json_ref now records whether it was constructed from a braced list
(true only for the std::initializer_list<json_ref> constructor used
for nested braced lists) or from a value. The initializer-list
constructor uses this to copy or move a single non-braced-list element
before deciding whether the list describes an object, so a JSON value
is always copied regardless of its shape, while a braced pair written
in the source still creates an object.
Fixes #5662.
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
|
||
|
|
ff6f3d7d4a |
Reject nested indefinite-length CBOR string chunks (#5766)
* fix(cbor): reject nested indefinite string chunks Signed-off-by: Joseph.Demarest <joseph@demarest.dev> Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Address review comments on nested indefinite-length CBOR strings Rename is_chunk to inside_indefinite, update the stale test section names, and use lowercase comments like the surrounding code. Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Joseph.Demarest <joseph@demarest.dev> Signed-off-by: Niels Lohmann <mail@nlohmann.me> Co-authored-by: Joseph.Demarest <joseph@demarest.dev> |
||
|
|
21a69230bd |
Create a value before giving it its type (#5585)
* Create a value before giving it its type Squashed onto develop from: - Create a value before giving it its type - Skip the failed-allocation test when exceptions are disabled - Keep the created pointer rather than an uninitialized json_value - Skip the vector<bool> failed-allocation check for VS 2015 with iterator debugging - Test the remaining to_json overloads with a failing allocation - Skip the to_json allocation-failure section on VS 2015 Debug - Fix false GCC -Warray-bounds error with JSON_DIAGNOSTICS at -O3 (#5744) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Store the new value with a helper in all to_json constructors Every external_constructor<>::construct now creates the new value first and hands it to basic_json::replace_value(), which destroys the old value, stores the new one before setting its type (as elsewhere in this PR), sets the parents, and checks the invariant. The std::vector<bool> and std::valarray overloads use array_t's range constructor, which the other array overloads already rely on. Range views keep their loop, as begin() and end() of a view may have different types. Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
162e13b86f |
Add with_*_t alias templates to create basic_json types with changed template parameters (#5758)
* Add helper types to make it easier to create a basic_json type with modified template parameters Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Rename with_changed_*_t aliases to with_*_t and merge integer/unsigned aliases Per review discussion on #3898 between gregmarr and nlohmann: - rename with_changed_X_t to with_X_t for brevity - replace the separate with_changed_integer_t/with_changed_unsigned_t aliases with a single with_integers_t<NumberIntegerType2, NumberUnsignedType2> - add @sa doc comment links for the upcoming documentation page Co-authored-by: Raphael Grimm <1005058+barcode@users.noreply.github.com> Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Add documentation for the with_*_t member alias templates Add docs/mkdocs/docs/api/basic_json/with_t.md documenting with_object_t, with_array_t, with_string_t, with_boolean_t, with_integers_t, with_float_t, with_allocator_t, with_json_serializer_t, with_binary_t and with_base_class_t, with an accompanying example, and link the page from the basic_json member types list and the mkdocs navigation. Co-authored-by: Raphael Grimm <1005058+barcode@users.noreply.github.com> Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Add tests for the with_*_t member alias templates Check with std::is_same that each with_*_t alias produces the expected basic_json type, and that with_string_t keeps nlohmann::ordered_map as the object type when used on ordered_json. Co-authored-by: Raphael Grimm <1005058+barcode@users.noreply.github.com> Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fix with_t nav entry and document chaining of the with_*_t aliases Indent the with_t entry in mkdocs.yml so it is listed under basic_json, explain that the aliases can be chained and work on ordered_json, and test both, including json::with_object_t<ordered_map> == ordered_json. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Add docset entry for basic_json::with_t Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> Co-authored-by: barcode <barcode@example.com> Co-authored-by: Raphael Grimm <1005058+barcode@users.noreply.github.com> |
||
|
|
69874e4544 |
Fix NUL bytes in UBJSON/BJData high-precision numbers (#5760)
* Fix NUL-terminated UBJSON and BJData high-precision payloads Signed-off-by: fhgffy <102001626+fhgffy@users.noreply.github.com> * Use English comments for the high-precision NUL fix Signed-off-by: fhgffy <102001626+fhgffy@users.noreply.github.com> * Track NUL bytes while reading high-precision payloads Signed-off-by: fhgffy <102001626+fhgffy@users.noreply.github.com> * Drop dates from code comments. Signed-off-by: fhgffy <102001626+fhgffy@users.noreply.github.com> * Report a NUL in a high-precision number where it is read Return the parse error from the read loop so the byte offset points at the NUL, and drop the separate check after lexing. Signed-off-by: fhgffy <102001626+fhgffy@users.noreply.github.com> * Use fixed text for the high-precision NUL error 2026-10-06: Use the known zero byte directly instead of formatting and concatenating it. Preserve the SAX token, error message, and byte offset. Signed-off-by: fhgffy <102001626+fhgffy@users.noreply.github.com> --------- Signed-off-by: fhgffy <102001626+fhgffy@users.noreply.github.com> |
||
|
|
9b179cee1e |
Handle all value_t enumerators in has_no_children() (#5770)
2026-10-07: add the scalar enum labels already handled by the default branch so -Werror=switch-enum builds succeed. Keep the existing behavior and synchronize the generated single header. Signed-off-by: fhgffy <102001626+fhgffy@users.noreply.github.com> |