mirror of
https://github.com/nlohmann/json.git
synced 2026-10-06 14:40:32 +00:00
f39b5b1baee56d0eceb22c9eacb2ea749085d257
18
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
9cb9f7fbad |
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>
|
||
|
|
a0b98325cf |
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> |
||
|
|
6ee5e89803 |
Scan json_view strings with SIMD and index large objects
Speed up json_view's parser with SIMD scanning and a hash table for large objects. Long runs of string bytes are scanned 16 bytes at a time with NEON (AArch64, GCC and Clang) and SSE2 (x86-64), both baseline instruction sets. Keys keep 16 table checks before the vector loop, because their lengths repeat from record to record; string values get 8, because their lengths vary more. Non-ASCII text is validated 16 bytes at a time with simdjson's "lookup4" check (Keiser and Lemire, 2021), with NEON on AArch64 and, on x86-64, with SSSE3. SSSE3 is not part of baseline x86-64, so the check is compiled for SSSE3 with a function attribute and used only where CPUID reports it, which all x86-64 CPUs since about 2011 do; the answer is cached in a statically initialized atomic, so there is no guard of a local static and no global constructor. The same input is accepted either way. JSON_VIEW_NO_SIMD selects the portable code. On x86-64, string runs are now checked vector-first: one SSE2 compare from the first byte finds the end of most keys and short values, instead of a branch per byte for the first 8-16 bytes. AArch64 keeps the byte-wise steps, where a NEON mask costs more and the branches predict well. Entering an object or array no longer stalls: open() stores the parent's frame field by field instead of building it on the stack and reading it back with wider loads, which waited for the narrower stores to retire. Objects with 128 members or more get an open-addressing hash table built when the object closes, so operator[], at(), find(), contains(), count(), value(), and JSON pointers take constant time on average in such objects; of duplicate keys, the first is kept, as for the linear search. The idea comes from Boost.JSON. simdjson is credited in simd.hpp's SPDX block, the README, and license.md. Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
d26cbef976 |
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> |
||
|
|
0adb7783a4 |
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> |
||
|
|
4303ba674d |
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> |
||
|
|
17842694f6 |
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> |
||
|
|
d8d47be4a5 |
Fix CI on develop after merging the ready-to-merge PRs (#5754)
* Keep the serializer conversion for objects whose keys cannot be converted #5591 added a test converting nlohmann::json into a basic_json whose string type cannot be constructed from std::string. That instantiates convert_iteratively(), whose members.emplace_back(next.key(), ...) needs exactly that key conversion, and broke the build of unit-alt-string. Dispatch on the key's constructibility and leave such conversions to the serializers, as the levels above the nesting bound already do (#3425). Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fix the remaining CI failures on develop - unit-wstring: with a 16-bit wchar_t (Windows), a lone surrogate is reported as the ill-formed byte 0xFF since #5704; the std::wstring expectations still had the previous <U+0000>. - ci_single_binaries: json_literals.hpp (#5610) and json.hpp include each other on purpose, and IWYU, not following the cycle, asks to replace json.hpp with json_fwd.hpp. Report its findings without failing the build, as already done for json.hpp. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fix the library warnings and noexcept specifications from the merged PRs - binary_reader: rename the error_handler constructor parameter, which shadowed the member (-Wshadow, -Wshadow-field-in-constructor; #5746) - basic_json(copy_construct_tag, ...): declare it noexcept when copying the base class is (GCC 16 -Wnoexcept; #5690) - the scalar-on-left legacy comparison operators: noexcept only when converting the scalar is, like their member counterparts (#5682, #5751) - compare_leaves: use std::is_eq/is_lt/is_gt instead of comparing a std::partial_ordering with 0 (-Wzero-as-null-pointer-constant; #5686) - serializer: silence MSVC C4127 for the EnsureAscii template parameter (#5741, #5746) - clang-tidy: return the sanitized reference in binary_writer, take the key of ordered_map::find_impl by const reference (#5727), and mark the switches over parse_array_index (#5728) - ordered_map: keep <memory> for std::allocator (IWYU) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Split unit-conversions.cpp so MinGW can link it clang 18 with the MinGW linker failed to link test-conversions_cpp17 ("relocation truncated to fit: IMAGE_REL_AMD64_REL32"). As windows.yml recommends, keep the objects small by splitting the test file. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fix the tests added by the merged PRs for all CI configurations - discard the results of dump() and from_*() in CHECK_THROWS with utils::ignore_return_value (GCC -Werror=unused-result) - give unit-bson's huge_string_t a default constructor (MSVC C2512, GCC 5, clang 3.5) - unit-disabled_exceptions: use the literals namespace when the global UDLs are off (ci_test_noglobaludls; #5700) - unit-binary_utf8_strict: expect the JSON pointer prefix with JSON_DIAGNOSTICS (#5741) - skip the tests that rely on exceptions under JSON_NOEXCEPTION (#5678, #5732) - clang-tidy and clang -Werror: static test data, CAPTURE(...);, const-correctness, use-after-move alias, unused conversion operator, a missing <iterator> include Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Title the macro examples and add JSON_STRICT_BINARY_UTF8 to the docset The documentation style check requires "Example: ..." titles on pages with several examples (#5741, #5591) and a docset entry for every macro page. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Regenerate BUILD.bazel and nlohmann_json.natvis Signed-off-by: Niels Lohmann <mail@nlohmann.me> #5746 added detail/output/error_handler.hpp and #5741 the json_abi_sbu8 ABI tag. * Install libidn11 for the CMake 3.5.0 binary in ci_cmake_flags Signed-off-by: Niels Lohmann <mail@nlohmann.me> #5733 moved ci_cmake_options from ubuntu:focal to ubuntu:24.04, which no longer ships libidn.so.11; the CMake 3.5.0 release binary links against it, so every ci_cmake_flags run has failed since. Install focal's libidn11 package for that matrix entry only. * Suppress Infer's false STACK_VARIABLE_ADDRESS_ESCAPE in get_impl get_impl() returns its local by value. A test added by the merged PRs instantiates it with a type Infer misreads, so ci_infer reported the 2021 code for the first time. Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
38a2db260c |
Remove dead metaprogramming and duplicated code in traits and pointers (#5728)
* Remove unused is_sax and is_detected_convertible detail::is_sax had no user: the parser and the binary reader only use is_sax_static_asserts, so is_sax was a second, unchecked copy of the SAX event list. is_sax_static_asserts asserted boolean(bool) twice in a row, and detail::is_detected_convertible was never used anywhere. Remove all three and include <cstddef> for size_t instead of <cstdint>. Only names in nlohmann::detail are removed; behavior, public API and ABI are unchanged. The diagnostics for an incomplete SAX handler are the same, apart from the duplicated boolean() message. Part of #5708 Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Replace meta/logic.hpp with a disjunction trait meta/logic.hpp added a second set of type-level boolean helpers (cxpr_and, cxpr_or, cxpr_not, ...) next to the existing conjunction and negation in type_traits.hpp. It was used only by one static_assert in from_json_tuple_impl, two of its templates were never used, and it was the only header without the license banner and relied on transitive includes for <type_traits>. Add the missing disjunction next to conjunction and negation, use the three in the static_assert, and delete logic.hpp together with its BUILD.bazel entry. same_sign now uses disjunction as well, which resolves the 2022 TODO waiting for such a trait. The static_assert accepts and rejects the same types as before. Only names in nlohmann::detail change; behavior, public API and ABI are unchanged. Part of #5708 Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Remove unused would_call_std_* from NLOHMANN_CAN_CALL_STD_FUNC_IMPL Besides detail::result_of_begin/end, which is_range and iterator_t use, the macro defined a namespace detail2 with a tag type, a catch-all overload and would_call_std_begin/end, plus would_call_std_begin/end structs directly in namespace nlohmann. Nothing has used them since they were added in #3020. Reduce the macro to its detail part. Without the trailing struct the ';' after the two invocations would be an empty declaration that -Wextra-semi flags, so drop it. macro_scope.hpp included meta/detected.hpp only for this macro; all users of detected.hpp include it (or type_traits.hpp) themselves, so remove the include. Behavior and ABI are unchanged. The undocumented, untested and unused names nlohmann::would_call_std_begin, nlohmann::would_call_std_end and namespace nlohmann::detail2 are no longer declared. Part of #5708 Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Simplify is_ordered_map to reuse has_capacity is_ordered_map re-detected capacity() with a C++03 sizeof/vararg trick right after has_capacity did the same detection through is_detected. For ordered_map, the old trick took the address of std::vector::capacity, which [namespace.std]/6 makes unspecified. Reuse has_capacity instead, which removes the unspecified-behavior pointer-to-std-member and two NOLINT suppressions. Part of #5708 Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Remove duplicate const overload of json_pointer::get_checked The const and non-const get_checked() overloads had byte-identical 50-line bodies, differing only in the signature. The remaining template deduces a const-qualified BasicJsonType for const callers, so at(), the out_of_range::create() calls and the bounds check all still work. Part of #5708 Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fix tautological clause in iter_impl's iterator category assertion The static_assert meant to check the LegacyBidirectionalIterator named requirement had a first clause comparing std::bidirectional_iterator_tag to itself, which is always true and checks nothing; only array_t::iterator was actually being checked, despite the message claiming object iterators were checked too. Drop the tautological clause, reword the message to describe what is actually checked, and note that object_t may use a forward-only iterator as long as reverse iteration and operator-- are unused. The check is intentionally not extended to object_t::iterator, since that would reject object types with forward-only iterators that compile and work correctly today. Part of #5708 Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fix misplaced and stale comments in JSON_HAS_RANGES and conversions The JSON_HAS_RANGES feature-detection block had its libc++ comment sitting above the clang+libstdc++ branch it does not describe, leaving the libc++ branch uncommented and the clang+libstdc++ branch without its own rationale. Move each comment to sit under its own branch, and give the clang+libstdc++ branch (added in issue 5161) its own one-line reason referencing that issue instead of reusing the libc++ branch's comment. Also fix a duplicated-word typo ("in large in large cpp files") in from_json.hpp, drop two unanswered 2017 design questions left as comments in type_traits.hpp and from_json.hpp that no longer reflect open questions, and correct NLOHMANN_JSON_SERIALIZE_ENUM_STRICT's @since tag from 3.12.0 to 3.13.0, the release it was actually introduced in. Part of #5708 Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Support any-rank C arrays in from_json, not just rank 1-4 from_json() for C arrays had four hand-unrolled overloads (rank 1-4, added incrementally in #4262), each with its own nested loops. to_json() already handles any rank recursively, so a rank-5+ C array could be serialized but not read back with get_to()/get<>(). Replace the four overloads with one from_json() SFINAE-constrained on get<remove_all_extents<T>::type>() existing, forwarding to a pair of mutually recursive from_json_c_array_element() helpers: one assigns a non-array element via get<T>(), the other loops over a array element and recurses one dimension at a time. Each dimension still goes through at(), so type_error.304/out_of_range.401 stay unchanged; ranks 1-4 keep their existing behavior and semantics. Adds rank-5 round-trip and mismatched-shape tests to unit-conversions.cpp. Public API: additive only (rank 5+ C arrays become readable). Signed-off-by: Niels Lohmann <mail@nlohmann.me> #5708 item 1 * Move templated_json_throw into nlohmann::detail templated_json_throw() was defined in macro_scope.hpp, which is included outside NLOHMANN_JSON_NAMESPACE_BEGIN, so the helper leaked into the global namespace as ::templated_json_throw with no ABI tag. Unqualified lookup in NLOHMANN_JSON_SERIALIZE_ENUM_STRICT could then bind to a same-named function declared in the user's own namespace instead, which fails to compile with Clang ("does not name a template"). Move the helper next to the exception classes in exceptions.hpp, inside nlohmann::detail, and call it qualified as ::nlohmann::detail::templated_json_throw<...>(...) from both macro expansion sites. Rewrite the doc comment to give the real reason for the helper (JSON_THROW may expand to code that discards its argument, e.g. when exceptions are disabled) and fix the "supress" typo. templated_json_throw was never released (added by #5151 after v3.12.0), so it can be moved freely. Adds a regression test that expands NLOHMANN_JSON_SERIALIZE_ENUM_STRICT inside a namespace declaring its own templated_json_throw. Public API: no change (::templated_json_throw was an unreleased, unintentional global-namespace leak with no callers relying on its location). Overlaps #5698, which rewrites the same two macro call lines; the overlapping hunks are small and should be trivial to reconcile on rebase. Signed-off-by: Niels Lohmann <mail@nlohmann.me> #5708 item 2 * Factor the repeated JSON_HAS_RANGES/MinGW guard into one macro The std::ranges view conversion (excluded on MinGW because of its incomplete C++20 ranges support, #4916) was gated by the same #if JSON_HAS_RANGES && !defined(__MINGW32__) condition at seven independent sites in to_json.hpp and type_traits.hpp, with the MinGW rationale duplicated in two of them and missing from the rest. Since the sites come in matching pairs (one enables is_compatible_range_view and a view-based overload, the other adds the exclusion to the plain-array-type overload), a drift between any pair would produce an ambiguous or missing overload on exactly one platform. Add JSON_HAS_RANGE_VIEW_CONVERSION next to JSON_HAS_RANGES in macro_scope.hpp, combining both conditions with the #4916 reasoning in one place, #undef it in macro_unscope.hpp, and use it at all seven sites. This does not fold the MinGW check into JSON_HAS_RANGES itself: JSON_HAS_RANGES is user-overridable and also gates the enable_borrowed_range specialization in iteration_proxy.hpp, which is not excluded on MinGW. No behavior or public API change: JSON_HAS_RANGE_VIEW_CONVERSION expands to exactly the condition that was previously written out at each site. Overlaps #5585, #5600 and #3575, which touch the same to_json.hpp and type_traits.hpp lines; the change here is a mechanical search-and-replace of the guard condition and should rebase cleanly. Signed-off-by: Niels Lohmann <mail@nlohmann.me> #5708 item 11 * De-duplicate from_json.hpp's map and array-fallback bodies Several from_json() overload pairs in from_json.hpp were copies of each other, so a fix has to be applied twice (as #5681 already does): - from_json(..., std::map&) and from_json(..., std::unordered_map&) for non-string keys had identical 16-line bodies: array check, m.clear(), pair check loop, m.emplace(...). Route both through a new from_json_pair_array_to_map(j, m) helper. - The from_json_array_impl priority_tag<1> and priority_tag<0> fallbacks ran the same std::transform/std::inserter loop, differing only in ret.reserve(j.size()). Merge them into one body and, modeled on the existing from_json_object_reserve, add a from_json_array_reserve pair so the reserve() call is only made for ConstructibleArrayType that support it. Error ids (type_error.302), messages, diagnostic paths ((at(0)/at(1)) and behavior for types with/without reserve() are unchanged; only the duplication is removed. Public API: no change. Overlaps #5681, which changes the "&j" to "&p" line in both map bodies; the shared helper here should make that a one-line change instead of two on rebase. Signed-off-by: Niels Lohmann <mail@nlohmann.me> #5708 item 5 * Unify json_pointer's three array-index parsers array_index(), contains() and get_checked_or_null() each re-implemented the RFC 6901 array-index rules and the size_type range check: array_index() does the canonical parse and throws; contains() (which must not throw, #5395) re-validates every digit by hand and runs its own strtoull/ERANGE check before calling array_index() anyway, parsing every array token twice; get_checked_or_null() wraps array_index() in JSON_TRY/ JSON_INTERNAL_CATCH (detail::out_of_range&) to turn an unrepresentable index into "not found". Add a single private, noexcept parse_array_index(s, idx) returning an array_index_status (ok / leading_zero / not_a_number / unresolved / exceeds_size_type). array_index() becomes a thin wrapper mapping each status to the existing parse_error.106/109 or out_of_range.404/410; contains() and get_checked_or_null() switch on the status directly. This removes contains()'s digit-validation loop and its second strtoull call, and get_checked_or_null()'s JSON_TRY/JSON_INTERNAL_CATCH. Bugfix as a consequence: get_checked_or_null()'s JSON_TRY/ JSON_INTERNAL_CATCH was dead code under JSON_NOEXCEPTION (JSON_TRY expands to "if(true)" and the catch to "if(false)", so JSON_THROW's std::abort() ran unconditionally), meaning value() and contains() would abort instead of returning the default/false for an out-of-range-sized or oversized array index when exceptions are disabled (#5672). Switching on parse_array_index()'s return value instead of relying on an actual throw/catch fixes this: get_checked_or_null() now returns nullptr for array_index_status::unresolved/exceeds_size_type in every build configuration, and still calls JSON_THROW (aborting under JSON_NOEXCEPTION, as before) only for a malformed index (leading_zero/not_a_number), matching its documented @throw list. All existing error ids, messages and diagnostic paths are unchanged; a few reference tokens that used to fail contains()'s manual per-character validation (e.g. "1a") now fail via array_index_status::unresolved instead, with no observable difference since contains() only returns bool. Adds regression tests to unit-element_access2.cpp's "access on array type" section covering value() with an index that exceeds size_type and one with a trailing non-digit, both of which must yield the default value rather than abort/throw. Public API: no change. Overlaps #5700, #5614 and #5692, which touch the contains() and get_checked_or_null() array hunks; this change replaces those hunks with calls into the new shared parser, so a rebase will need to re-apply their token-handling changes (e.g. the empty-token case) on top of the switch statements here. Signed-off-by: Niels Lohmann <mail@nlohmann.me> #5708 item 4 * Regenerate single_include after merging develop The merge commit kept develop's single_include/nlohmann/json.hpp because make amalgamate saw it as up to date. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Address review: switch in array_index, drop redundant inline - json_pointer::array_index() dispatches on array_index_status with a switch, matching the other parse_array_index() caller - drop `inline` from the function templates this PR adds or moves in from_json.hpp - reword a comment that described the change rather than the code Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
6fc0d501f3 |
Move the user-defined string literals to <nlohmann/json_literals.hpp> and add JSON_NO_AUTOMATIC_UDLS (#5610)
* Add JSON_NO_UDLS to leave out the user-defined string literals The bodies of operator""_json and operator""_json_pointer call the parser, so every translation unit including the library instantiates it, even if it never parses anything. Defining JSON_NO_UDLS leaves the literals out entirely, which saves 15-35% compile time for such translation units (#5294). Nothing changes if the macro is not defined. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Mention JSON_NO_UDLS in the list of exported module symbols Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Move the user-defined string literals to <nlohmann/json_literals.hpp> Following the review in #5294, the literals now live in their own header instead of being removed entirely: <nlohmann/json.hpp> includes it at the end unless JSON_NO_AUTOMATIC_UDLS (renamed from JSON_NO_UDLS) is defined, so a project can opt out globally and include the header only where the literals are used. The header only uses public and standard macros, because the library's internal macros are undefined at the end of json.hpp and the amalgamation inlines macro_scope.hpp only once. For the same reason, the library no longer defines and undefines JSON_USE_GLOBAL_UDLS, so a user's definition is still visible to the header. The single-header copy is identical to the multi-header one, as it only includes <nlohmann/json.hpp>. The module always exports the literals. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fix CI: include cycle, GCC 4.8 literal operator spacing, and global UDLs off in the JSON_NO_AUTOMATIC_UDLS test - Suppress clang-tidy misc-header-include-cycle on the intentional mutual include of json.hpp and json_literals.hpp. - Use operator"" _json with a space for GCC 4.8 in the test's detection aliases, as the header does. - Only test the global literal operators when JSON_USE_GLOBAL_UDLS is on. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Declare the literal operators through a local macro The GCC 4.8 spacing condition was repeated for both operator definitions and the global using-declarations. NLOHMANN_JSON_LITERAL_OPERATOR(suffix) now selects operator""##suffix or operator"" suffix in one place and is undefined at the end of json_literals.hpp. Suggested by gregmarr in review. Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
d268eaa693 |
Convert floats with Eisel-Lemire when std::from_chars is unavailable (#5617)
* Convert floats with Eisel-Lemire when std::from_chars is unavailable Float tokens that Clinger's fast path cannot convert (e.g. the 17-digit coordinates of canada.json) went to strtod unless std::from_chars was available. It is not used in C++11/14, and not with libc++, which does not define __cpp_lib_to_chars. The Eisel-Lemire algorithm (after fast_float's compute_float) now converts them with integer arithmetic, correctly rounded for any token with at most 19 significant digits. Longer tokens are truncated; the result is used if w and w + 1 round alike, else strtod decides as before. Overflow still yields infinity (out_of_range.406). The table of powers of five (fast_float's) lives in pow5_table.hpp; a unit test recomputes every entry with big-integer arithmetic. Further tests: known values generated with Python (whose float() is correctly rounded), 200,000 round trips through to_chars, and the 128-bit multiplication and leading-zero count against big-integer references (both with and without a 128-bit type). Checked against strtod on 6.5 million tokens, among them 60,000 exact halfway cases: no difference. json::parse on canada.json: -8.6% (C++11), -7.6% (C++17, Apple clang); other files unchanged. Compile time of a TU including json.hpp: +0.7%. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fix CI: unused parse result and find() == npos in the Eisel-Lemire tests GCC (-Werror=unused-result) rejected CHECK_THROWS_WITH_AS(json::parse(...)) because parse() is [[nodiscard]]; assign the result to a dummy json as the other tests do. clang-tidy flagged longer.find('.') == npos with abseil-string-find-str-contains; store the position in a variable first. Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
6c8ea0a6d1 |
Remove Bazel alwayslink=True (#5376)
This should have no effect for header only libraries as mentioned. It was previously removed in |
||
|
|
7c90ec2323 |
Hash deeply nested values without recursing per nesting level (#5546)
* Hash deeply nested values without recursing per nesting level std::hash<basic_json> hashed an array or object by hashing each element, which called detail::hash again once per nesting level. A value nested deeply enough - 50,000 levels of objects on an 8 MiB stack - exhausted the call stack and terminated the process. parse() accepts such values without complaint, since the parser is iterative, and a parsed value is hashed wherever it is used as a key in an unordered container. Bound the descent the same way dump() does: detail::hash takes the nesting level, and once hash_depth_limit() (128) levels have been entered, hash_iteratively() hashes what is left on an explicit stack. It combines the seeds in exactly the same order, so hash values are unchanged. A value nested less deeply than the bound is hashed by the same code as before, without allocating, and is as fast as before. Tests check that every depth up to twice the bound hashes exactly like the recursive definition of the hash, and that values nested 100,000 levels deep hash without crashing. Fixes #5545 for std::hash. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Declare hash_frame's constructor noexcept GCC's -Wnoexcept (an error in CI) flags the emplace_back() into the hash stack under C++26: the constructor cannot throw, since cbegin() is noexcept, but it did not say so. dump_frame's constructor is noexcept for the same reason. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Share one recursion depth limit, and copy the hash frame out of the stack dump() and hash() each defined their own limit on how many nesting levels they recurse into, and the operations still to come would have added more, free to diverge over time. They now all use detail::recursion_depth_limit(), in a header of its own; serializer::dump_depth_limit() and hash_depth_limit() are gone. hash_iteratively() now copies the frame it works on out of the stack and changes the frame only through stack.back(), so nothing can refer into the stack after entering an element has grown it. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Parenthesize multiplications in the hash test for clang-tidy Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
305ca7dadd |
Add missing headers to BUILD.bazel and check it in CI (#5554)
* Add missing headers to BUILD.bazel and make its generator reproduce it The "json" cc_library did not list three headers that the library includes: - detail/meta/logic.hpp (added in #5016, included by from_json.hpp) - detail/input/number_parse.hpp (added in #5283, included by lexer.hpp) - detail/input/string_scan.hpp (added in #5283, included by lexer.hpp and serializer.hpp) Bazel's sandbox only exposes declared headers, so any target depending on @nlohmann_json//:json and including <nlohmann/json.hpp> failed with "'nlohmann/detail/meta/logic.hpp' file not found". The file could not simply be regenerated, because the generator behind "make BUILD.bazel" was stale: it wrote only the "json" cc_library and dropped the load() statements, the license block, and the "singleheader-json" target that were added by hand in #4584. The generator now emits the complete file, so its output differs from the previous BUILD.bazel only by the three headers. It also resolves the glob against the project root instead of the working directory and sorts the list explicitly. "make BUILD.bazel" is now phony: in a fresh checkout, BUILD.bazel is not older than the headers, so make considered it up to date, and a removed header would never trigger a rebuild. "make check-amalgamation" also checks that BUILD.bazel is up to date. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Check in CI that BUILD.bazel is up to date The "Check amalgamation" workflow now also regenerates BUILD.bazel, so a pull request that adds, renames, or removes a header without updating the Bazel header list fails, and the attached amalgamation.patch contains the fix. The failure comment and the contribution guidelines mention the new check, and the comment now links to the existing "Amalgamate the source code" section instead of the "Files to change" anchor that was removed in #4560. Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
26cfec34be | Clean up and document project files (#4560) | ||
|
|
0cb1241d5e |
Improve Bazel support: Switch to Bzlmod (#4584)
* Improve Bazel support: Switch to Bzlmod Signed-off-by: Vertexwahn <julian.amann@tum.de> * Update documentation Signed-off-by: Vertexwahn <julian.amann@tum.de> * Fix spelling Signed-off-by: Vertexwahn <julian.amann@tum.de> * Fix snippet filename error Signed-off-by: Vertexwahn <julian.amann@tum.de> --------- Signed-off-by: Vertexwahn <julian.amann@tum.de> |
||
|
|
e509007df2 | Remove alwayslink=True Bazel flag (#4396) | ||
|
|
2d1f9b6a7d | Add Bazel build support (#3709) |