mirror of
https://github.com/nlohmann/json.git
synced 2026-11-01 13:10:38 +00:00
48fa92ffa96be1a47e2960dba1d0d916c7bd892d
87
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
0115bb47b3 |
Name the source and license of adapted code in the file comments (#5965)
The files with code adapted from fast_float, Zmij and simdjson carried the authors' SPDX copyright lines, but their file comments did not say what was taken or under which license: number_parse.hpp, to_chars.hpp, simd_scan.hpp and view/simd.hpp now do, as zmij.hpp and the yyjson-based view files already did. README and the license page now also name fast_float's eight-digit parsing and the lexer's use of simdjson's UTF-8 check. Comments only. Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
54f6b3ef06 |
json_view: source positions in materialize() with JSON_DIAGNOSTIC_POSITIONS (#5945)
* json_view: source positions in materialize() with JSON_DIAGNOSTIC_POSITIONS With JSON_DIAGNOSTIC_POSITIONS, materialize() now gives every value the start_pos()/end_pos() that basic_json::parse() would give it, so exceptions about materialized values name the same bytes (#5864). - json_sax_dom_parser takes the lexer type as a defaulted template parameter; materialize() passes position_source, which reports the positions it derives from the index. diagnostic_positions treats npos as "no position" and keeps ranges all-or-nothing. - Strings with escapes keep the offset of their opening quote plus one in `next` (only with the macro); materialize() scans from there to the closing quote. The end of an array or object is found by scanning from the end of its last element over whitespace, comments and a trailing comma. - In editable documents, values written by edits, arrays and objects whose elements changed, and containers whose last element has no position get none. Images keep the flag is_new, and load() checks that `next` of a string is within the text. Nothing changes on the parse path without the macro. The view's own exceptions still carry no byte range, so the tests that compare them with those of materialized values skip it under the macro, as they skip the path under JSON_DIAGNOSTICS. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Indent comment in unit-json_view_from_json.cpp Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Address review: clearer position code and docs - position_source gets clear(); set_scalar() returns early for new values, switches on node_kind, and uses no local copies; the one position_source per materialize() call is documented - no npos + 1 for containers without a position; a comment on why the end of a container is reported as its closing bracket - less duplication under #if in materialize() and the builder - diagnostic_positions sets npos explicitly - docs: positions of basic_json values after changes, positions in images depend on how the document was parsed, when next is set, and the cost of positions while parsing Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Share the comment scanners of the view builder with materialize() skip_line_comment() and find_block_comment_end() in scan.hpp are used by the builder's comment() and by position_source::container_end(), which had its own copy. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Address review: if/else for container positions, clearer next field - the opening bracket of a container sets positions with an if/else instead of a ternary, and the frame takes its start from positions - the closing bracket of a container is found in nested if/else; the parent's last_end is taken from positions, so it is npos whenever no range is set - architecture.md: the next field reads "or 0 if it was not; all other values: 0" instead of "else 0; otherwise 0" Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fix GCC unused-result and clang-tidy raw-string findings in unit-json_view_positions.cpp Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
a47170346a |
docs: new design for the documentation site (#5934)
* docs: new design for the documentation site Apply the brand to the Material for MkDocs site: Inter and JetBrains Mono served by the site (upright and real italic), brand colours, blue header band with the syntax pattern, wordmark in the header, new home page (hero, cards, animated feature tour), 404 page, polish for tabs, search, footer, focus rings, tables and code blocks, and theme features (code copy, previous/next footer, TOC follow, search suggestions). Draw the number railroad diagram, the parser callback events and the iterator illustrations as inline SVGs that follow the light and dark scheme. The generator scripts are in docs/mkdocs/scripts/figures; the feature slideshow (json-light.svg, json-dark.svg, replacing json.gif on the home page) is built by docs/mkdocs/scripts/slideshow. Decision nodes in five flowcharts become hexagons, which need far less room. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * docs: add figures, table captions and a macro chooser - Figures with numbered captions: SAX stepper, json_view layout and edit stepper, exception map, JSON pointer and patch walkers, QA pipeline, scaling and binary size charts, roadmap lanes, release timeline, integration decision tree and namespace builder - Table captions (nj-table-caption) on 9 pages - Compile times: modules and PCH charts, filesystem section, re-measured Figure 1, links between features/modules.md and compile_times.md - Interactive macro chooser for features/arbitrary_types.md - Link check: exclude URLs that answer the plugin with 400/401/419 or are dead (customers.md) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * docs: macro chooser generates adl_serializer for non-default-constructible types Signed-off-by: Niels Lohmann <mail@nlohmann.me> * docs: cross-link concept pages and API pages in See also sections Add See also links in both directions between the concept pages (features, home, integration) and the API pages, write API links in code font, give every entry a comment after a dash, and show the callback event figure on the parser callbacks page. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * docs: address review comments on the new design - Home: add a "Latest release" button - Sponsors: update the list and group it by tier - Features: list all feature pages on the index page - Releases: add highlights per release, link the 4.0 roadmap, add the first commit to the timeline - Exceptions: prefix all example messages with the exception name - API: no code font for format names in the See also links of the to_/from_ pages - Merge Patch: show the document before, the patch, and the result - Replace "--" with typographic dashes, mark external links with an icon, center Mermaid diagrams Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
5a2819c940 |
json_view: one SIMD macro set with the library (#5921)
The view's vector code follows JSON_NO_SIMD, like the lexer and the float writer, and skips the CPU check of SSSE3 when the code is compiled for SSSE3 anyway (__SSSE3__), like the lexer. JSON_VIEW_NO_SIMD and JSON_VIEW_USE_SSSE3 and their ABI tags _vns and _vssse3 are removed; they were never released. JSON_NO_SIMD keeps its tag _ns. Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
4cdfa6d0f4 |
json_view: no save() and load() on big-endian targets (#5920)
The image format is little-endian only. Instead of throwing type_error.320 at runtime, save_image(), load_image() and the members save() and load() do not exist on a big-endian target, so a call fails to compile, like set() on a read-only document. type_error.320 remains for saving a discarded document. Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
24078c862d |
docs: fix BON8 documentation gaps found in the format review (#5932)
* docs: fix BON8 documentation gaps found in the format review Verified each claim with small C++ probes before writing it. - index.md: BON8 serialization is also incomplete for invalid UTF-8. - bon8.md: document duplicate keys (last wins), the error ids for malformed input incl. 0xF5..0xF7 + 0x80..0xBF, exact string termination wording, wide integers and discarded values in the unsupported list, and title two untitled admonitions. - exceptions.md: to_bon8 in type_error.316, BON8 in parse_error.112 (with example) and .113, wide integers in out_of_range.407. - binary_values.md: bytes 0..39 take one byte in BON8. - from_bon8.md: note on strict UTF-8 and duplicate keys. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Address review of the BON8 documentation Restore the correct statement that marker bytes cannot begin a UTF-8 character (0x80..0xBF occur in valid UTF-8 as continuation bytes), and reword two sentences. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * docs: drop the discarded-value remark from the BON8 page Discarded values are not mentioned on the individual format pages. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * docs: do not list invalid UTF-8 strings as a BON8 serialization gap JSON text must be valid UTF-8, and to_bon8 rejects ill-formed strings by design (type_error.316), as documented on the BON8 page. Restore the original cell and realign the table. Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
60107eda3f |
Read BJData ND-arrays of float16, restrict ND-array char to 0..127, correct BJData docs (#5931)
* Read ND-arrays of half floats and enforce the char limit in ND-arrays BJData N-dimensional arrays with element type 'h' (float16) failed to read with a misleading parse_error.112, because the reader's dtype name table lacked 'h'. It now maps to "half", the JData name. The writer still does not pack "half"; such an object is written as a plain object and reads back equal. BJData limits 'C' (char) to 0..127 in Draft 2 and Draft 3, and the 1-D reader enforces this, but ND-arrays with _ArrayType_ "char" wrote values up to 255 as 'C' elements and read them back without a check. The writer now only packs values in 0..127 (others fall back to a plain object that round-trips), and the reader rejects 'C' elements above 127 with parse_error.113 like the 1-D reader. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * docs: correct and complete the BJData documentation Verify the BJData chapter against the library and the Draft 2/3/4 specifications and fix what did not match: - bjdata.md: correct the list of unused markers (N, not Z), the ND-array example (3x2, not 2x3), the conditions under which an object is packed as an ND-array (exactly three keys, row vectors, float-only elements for single/double, char 0..127, byte only in Draft 3), the binary table row and notes (which flag/draft combinations write [$B), the "$" type restriction (Draft 3, Z is excluded too), the H marker in the deserialization table, and the claim of a complete deserialization mapping. Document the supported drafts, that Draft 4 (SoA containers, extension type E) and column-major ND-arrays are rejected, ND-arrays of half floats, duplicate object keys, and link the Draft 3 specifications instead of master. - Add an API page for basic_json::bjdata_version_t with an example, and link it from the nav, the API index and to_bjdata. - Update binary_formats/index.md and binary_values.md, which still claimed that BJData has no binary type, and fix a wrong byte comment there. - Document parse_error.115 and the out_of_range.408 cases for from_bjdata, the precise other_error.502 condition for to_bjdata, and reword the note on truncated unsigned integers in exceptions.md (only ND-array elements were truncated before 3.13.0). Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Regenerate single_include and apply astyle Result of make amalgamate (including make pretty) for the preceding changes. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Address review of the BJData documentation The $ type restriction exists since Draft 2 (Draft 3 added B), $ requires a count, ND char above 127 is rejected on read, and the new API page is listed in alphabetical order in the nav. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * docs: format tables, use the Oxford comma Reformat the tables changed by this PR (BJData serialization and deserialization mappings, completeness and binary-value tables) so all rows are aligned, and add the Oxford comma to lists of three or more items in the added text. Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
dc768a7d5d |
Fix BSON binary subtype 0x02 and check the declared document size earlier (#5930)
* Write and read the inner length of BSON binary subtype 0x02 The BSON specification defines the payload of the "binary (old)" subtype 0x02 as an int32 followed by that many bytes. to_bson wrote the plain payload instead, so decoders such as pymongo and libbson-based readers rejected the bytes, and from_bson returned the inner length as part of the payload (documented as lenient behavior in gh-5333). The writer now emits the outer length (payload + 4), the subtype, the inner int32 holding the payload size, and the payload. The reader requires an outer length of at least 4 and an inner length equal to the outer length minus 4 (the cases the bson-corpus lists as decode errors), reports parse error 112 otherwise, and returns only the payload. Both directions change together so that the round trip keeps working. The single-reservation fast path for scalar entries declines subtype 0x02 because it has no room for the inner length. This is a deliberate exception to the opt-in rule for behavior changes: the previous output was invalid BSON. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Check the declared BSON document size before reading its content The BSON reader compared a document's declared size with the number of bytes read only when it reached the terminator. The declared size never bounded the reads, so a SAX handler saw the keys and values of a document whose size was too small before parse error 112 was reported. The container frame now stores the position where the document ends according to its declared size. open_bson_document rejects a size below 5 (size prefix plus terminator) and an embedded document that does not end before the terminator of its enclosing document; the element loop rejects an element whose type byte leaves no room for the name terminator and the document terminator. The terminator check stays and still catches a declared size that is too large. No input that was accepted before is rejected now; documents with a wrong size are reported earlier, with fewer SAX events, a different byte position and, in three cases, a different message. The error id (parse_error.112) is unchanged and strict=false still does not skip the check. The tests that asserted the old messages and positions are updated, with the outer sizes of two inputs corrected so that they still exercise the embedded document. New tests cover the SAX events, a size below 5, and an embedded document that fits exactly. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Document BSON subtype 0x02, document size checks, and lenient reads Describe the inner length of binary subtype 0x02 that to_bson now writes and from_bson checks and removes. Add the document size checks (parse_error.112, not affected by strict=false) to bson.md and from_bson.md, and extend the list of lenient reads with the UUID and MD5 payload lengths (not checked), duplicate keys (last value wins), embedded NUL bytes in strings, and the ignored trailing bytes with strict=false. State that the members of an object are written in iteration order. Correct the statement that element names are never validated on read: they are checked and sanitized like string values by the error handler. Add the BSON message to the examples of out_of_range.415 and type_error.321, the subtype limit of 255 to the binary values page, CBOR to the formats that use byte_container_with_subtype, and fix a typo in the incomplete mapping box. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Regenerate single_include and apply astyle Result of make amalgamate (including make pretty) for the preceding changes. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Address review of the BSON documentation Describe exactly when a too small document size is detected and note that subtype 2 data written by earlier versions is not readable. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * docs: use the Oxford comma, drop discarded-value remarks Add the serial comma in a list added by this PR and remove the BSON example of type_error.321 (with the type_error.317 remark) so the docs do not mention discarded values for BSON; restore the original singular heading of that section. No tables were changed by this PR. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fix -Weffc++ in the BSON recorder test and a clang-tidy use-auto hit Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fix clang-tidy readability-redundant-member-init in BSON test Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
0c3570d44a |
Validate the UBJSON/BJData container type marker for count 0; fix UBJSON docs (#5929)
* Validate the optimized-container type marker for count 0 in UBJSON and BJData
The '$' type marker of an optimized container was only checked when an
element was read, so containers with count 0 accepted any marker:
`[$x#i0`, `{$x#i0` and `[$E#i0` parsed as empty containers although a
non-empty container with the same marker is rejected and the UBJSON
specification requires a legal marker after '$'.
get_ubjson_size_type() now checks the marker right after '$' against the
allowed set (UBJSON: Z N T F i U I l L d D H C S [ {; BJData: B U i u I m
l M L h d D C, with the existing exclusion of T F Z N S H [ {) and reports
parse_error.112 "marker ... is not a permitted optimized array type".
Input that was accepted and is now rejected: an optimized container with
count 0 and an invalid '$' type marker. Valid input is unaffected. Two
existing tests that used an invalid marker incidentally ("[$#" and a
negative-count case) were adapted.
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
* Correct and complete the UBJSON documentation
Fix statements that disagree with the behavior (checked with probes):
- `Z` is null; the unused marker is `N`.
- `$` requires `#`; use_type with use_size=false throws other_error.502 only
for non-empty arrays, objects and binary values.
- NaN and infinity are written as IEEE 754 float64 and read back unchanged;
the UBJSON specification encodes infinity as null.
- `H` is documented (semantics, parse_error.115, out_of_range.406); the
"complete mapping" claim is qualified.
- use_type "same type" means same marker.
- Long Z/T/F arrays lose only the `$` annotation; the `#` count and the
element markers are kept.
- from_ubjson lists the 112, 113, 115, 406 and 408 causes that occur for
UBJSON, without the BJData-only ND-array wording.
- Duplicate object keys keep the last value; the `$` type marker is
validated even for count 0.
- Smaller fixes: float32 row, "map" -> "object", https link, exception 110
strict case, to_ubjson history of discarded values.
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
* Regenerate single_include and apply astyle
Result of make amalgamate (including make pretty) for the preceding changes.
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
* Address review of the UBJSON documentation
Clarify that empty containers carry no $ annotation of their own and note that a counted N object is not supported.
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
* docs: format tables, use the Oxford comma, drop discarded-value remarks
Reformat the two UBJSON mapping tables that this PR changed (uniform column
widths, padded cells), use the serial comma in lists of three or more in the
added text, and restore the original type_error.321 bullet in to_ubjson.md.
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
* Fix narrowing of the optimized-container count in BJData and UBJSON tests
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
---------
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
|
||
|
|
2bbe80c043 |
Document MessagePack ext types and fix related doc errors (#5928)
* Document MessagePack ext types and fix related doc errors Add an "Extension types (ext, fixext)" section to the MessagePack page: ext/fixext are read as binary, the signed ext type is exposed as an unsigned subtype 0..255 (-1 reads as 255), Timestamp is not interpreted, subtypes are always written as ext/fixext, and subtypes above 255 throw out_of_range.415. Add the 0xC1 row to the deserialization table, note that duplicate keys keep the last value, and correct the NaN/Infinity explanation, the "31 bytes / 15 elements" wording, the ill-formed UTF-8 sentence, and the generic 0xC1-key error message. Add fixext16 and the unsigned subtype mapping to binary_t.md and binary_values.md, reword "maximum of the MessagePack ext type" in to_msgpack.md and exceptions.md, and mark MessagePack deserialization as incomplete (non-string keys) in the binary formats overview. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Address review of the MessagePack documentation Name parse_error.113 for a 0xC1 map key, move the duplicate-key sentence out of the ext section, and keep the 0xC1 row in marker order. Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
c20b6eddf4 |
docs: correct JSON Pointer documentation and add tests (#5937)
Fix inaccurate or missing statements about unflatten (third round-trip failure, empty token, typo), value (type_error.306 before pointer lookup), contains (pointer overload), parent_pointer and operator<< (bad_alloc), out_of_range.410 example, template parameter requirements (resize(n)), and document the URI fragment form and non-unique member names of RFC 6901. Add tests for the unflatten and value() cases. Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
d6431a9104 |
Throw out_of_range.411 for JSON Patch add into a null parent (#5917) (#5918)
Claude-Session: https://claude.ai/code/session_01MKJf48vnGtTE25KscmjfaW Signed-off-by: Niels Lohmann <niels.lohmann@gmail.com> Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com> |
||
|
|
fe231dc692 |
docs: write std::chrono durations as unit/value objects; reword a title with escaped quotes (#5911)
Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
ffd4242dc2 |
Docs: fill documentation gaps found in user questions (#5907)
* Docs: fill documentation gaps found in user questions Add pages on class-hierarchy/smart-pointer/variant/chrono recipes, working without exceptions, and large, streamed and partial input. Extend the FAQ (23 entries, Known bugs first), parse error documentation (empty-input checklist, line/column, diagnostic positions), element access, conversions, creating values, ADL troubleshooting, memory, and build/distribution notes. Add 14 examples with generated output. Correct the statement that parse errors cannot be recovered from with -fno-exceptions. The topics come from recurring questions on Stack Overflow, Reddit, Hacker News, YouTube, and the project's GitHub discussions and issues. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * docs: declare range-for lists in variables to keep astyle quiet astyle reformats a braced init list placed directly in a range-for header; declare the list first so check_amalgamation passes. Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
cc917d7123 |
docs: cross-reference json_view and json_document for 3.13.0 (#5892)
* docs: cross-reference json_view and json_document for 3.13.0 Document the new zero-copy document/view API across the docs: add a "Documents and views" section to the basic_json API hub linking to all specializations, point parsing/conversion/modifying pages to the view-based alternatives, add document/view sections to feature pages, update the landing page to mention json_view.hpp/json_literals.hpp, and cross-link debugging/exceptions. Also document wide-string inputs (and ill-formed UTF-16/32 rejection), add parse/accept examples for wide strings, fix related doc gaps, and align features/parsing and FAQ with the added support. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * docs: fix article before std::istream in FAQ Signed-off-by: Niels Lohmann <mail@nlohmann.me> * docs: clarify document inputs, decoding, get<T>() and editing Address review comments: say which inputs are borrowed and that a wide string pointer is an extra input, which values are decoded during parsing, that get<T>() reads only built-in types directly, all forms of set() and that the document's insert() is array-only, and how to store wide strings. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * docs: integers are converted while parsing a document Also say "They accept every input" in basic_json_document::parse. Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
52b72b5684 |
Improve documentation: byte layouts, release pages, See also, dark-mode images (#5906)
- Make the iterator range illustrations legible in dark mode (prefers-color-scheme rules inside the SVGs). - Add a "Byte layout" section with a color-coded byte table to each binary format page (BJData, BON8, BSON, CBOR, MessagePack, UBJSON), and highlight the smallest size per file in the comparison table. - Add "See also" sections to the overview pages that lacked one. - Split the release notes into one page per release, grouped by minor version on the overview page, with an "API changes" section per release generated from the API pages' version histories (hooks/api_changes.py). - Fact-check the release notes and version histories against the release tags and correct wrong or misleading entries (e.g., json_serializer 2.1.0, merge_patch 3.1.0, items, count, operator[], to_bjdata, std::string_view key support). Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
6fa14d8f95 |
Write binary formats through reserved sink space and number runs (#5885)
Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
533cbfee51 |
Let generate_natvis.py cover custom ABI tag combinations (#5880)
Since #5877, the checked-in nlohmann_json.natvis only covers namespaces with up to two ABI tags. The generator gets options to produce a file for other configurations: - --abi-tags TAGS adds the namespaces of one configuration (repeatable) - --all generates every combination of ABI tags - --list-tags prints the tags with the macros that enable them - --output FILE writes somewhere else than the repository root `make natvis NATVIS_ARGS="..."` passes the options through. The default output is unchanged. The options are documented in the generator's README, the debugging page, and FILES.md, and checked in CI. Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
e9970066c5 |
Scan strings with SSE2, SSSE3, or NEON (#5853)
* Scan strings with SSE2, SSSE3, or NEON The string fast path found the end of a run of plain characters 8 bytes at a time (SWAR) and validated non-ASCII text one sequence at a time, calling validate_one_utf8() out of line for each. The vector code of json_view's scanner, in the new shared header detail/input/simd_scan.hpp, now does both 16 bytes at a time: SSE2 finds the end of a run on x86-64, and the UTF-8 check of Keiser and Lemire runs with NEON on AArch64 and with SSSE3 on x86-64. SSSE3 is not part of x86-64, so the check is compiled for it with a function attribute and used when the CPU has it (cached in a constant-initialized atomic), else the scalar check; code compiled with -mssse3 skips the CPU check. The first word of a run is still checked with SWAR (the first four with NEON, where a vector mask costs more), as most keys and short values end there, and the scalar helpers are always inlined. On anything else, the scalar code is unchanged. The new macro JSON_NO_SIMD selects the portable code everywhere, including the SSE2/NEON digit formatting of the floating-point writer. The lexer tests now also run with JSON_NO_SIMD and, on x86-64, with -mssse3, and a new test places every byte that ends a run at each offset across several 16-byte blocks, after ASCII and after CJK text. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Keep the string scan from slowing down short strings and dump() on Arm Three benchmarks were 7-9% slower than develop on Apple M1 (Apple Clang, -O3 -flto): Accept/jeopardy, Dump/citm_catalog, and Dump/jeopardy. - dump(): the serializer passes a string without its closing quote, so plain_run_end() ran out of input and called the out-of-line NEON loop for every string, with fewer than 16 bytes left. The loop is now only called for 16 bytes or more. - accept(): no string of jeopardy.json reaches the vector UTF-8 check, yet compiling it into the lexer's scan function made it 5% slower: the function runs once per token and now saved more registers and kept a stack slot. lexer_string_bulk_run() checks the first four words inline and continues out of line, and the serializer keeps string_bulk_run() inline, which is faster there. - The byte that ends a run is read from the scanned buffer instead of through get(), which Apple Clang merged with the other get() calls that load the read position again right after it was stored (3%). Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Define string_bulk_run_from() only where lexer_string_bulk_run() calls it With NEON and simdutf in C++17, lexer_string_bulk_run() takes the string_bulk_run() path, so the out-of-line helper was unused. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Do not dispatch the SSSE3 string scan with GCC's C++20 modules GCC does not keep the target("ssse3") attribute on functions it reads back from a module interface, so the module build (ci_module_cpp20, gcc:latest) rejected the intrinsics in vector_utf8_run() with "needs isa option -mssse3". With __cpp_modules, GCC now uses the portable UTF-8 check like other compilers without the target attribute; the SSE2 scan stays, and -mssse3 or more still compiles the SSSE3 check directly. Clang is not affected. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Include <cpuid.h> only once when json.hpp and json_view.hpp are both used The SSSE3 dispatch of the string scan and the one of json_view each include <cpuid.h>. Before GCC 11 the header has no include guard, so a translation unit that includes both headers fails with GCC 10: "redefinition of 'unsigned int __get_cpuid_max(unsigned int, unsigned int*)'". Guard both includes with NLOHMANN_JSON_CPUID_INCLUDED. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Add an ABI tag for JSON_NO_SIMD JSON_NO_SIMD selects between two definitions of the internal scanning and formatting functions. Like JSON_VIEW_NO_SIMD (_vns), it now appends _ns to the inline namespace, so translation units built with and without it can be linked into one program without ODR violations. Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
3f8677d028 |
Limit Natvis namespace variants to two ABI tags (#5877)
The generator wrote a namespace entry for every combination of ABI tags, so nlohmann_json.natvis doubled with each new tag. With the 12 tags on develop it had reached 57.9 MB, and one more tag would push it past GitHub's 100 MB file limit. It now covers the default namespace, every single tag, and every pair of tags (versioned and unversioned), which is 158 entries / 1 MB with 12 tags. Taken over from #5868. Refs #5863. Signed-off-by: Kartikey <kg704925@gmail.com> Signed-off-by: Niels Lohmann <mail@nlohmann.me> Co-authored-by: Kartikey <kg704925@gmail.com> |
||
|
|
bd5227c2ec |
Fix silent wrong results for custom number types (#5805)
* Fix silent wrong results for custom number types
Squashed onto develop
|
||
|
|
4971200b54 |
Add images of json_documents: save() and load() (#5632)
* Add images of json_documents: save() and load()
An image is a document stored so that loading it needs no
parsing: save() writes the node index, the text and the decoded
strings; the static load() reads an image written by save().
load() takes a pointer and size, a borrowed vector, or an owned
rvalue vector; the nodes are copied so they are aligned and can
be edited, while the text and decoded strings stay in the image.
image_check controls how much load() trusts the input: full
checks structure, bounds, strings and numbers, the parser's own
guarantees; bounds checks structure and bounds only; none skips
all checks, for images from a trusted source.
Layout is little-endian only ("NJVI" header, nodes, text, decoded
strings), following the idea of zero-copy formats such as
FlatBuffers and YaFF; the check follows FlatBuffers' Verifier.
New errors: parse_error.116 for a malformed image or a failed
check, type_error.320 for a discarded document or a big-endian
target.
A dedicated fuzzer and 6,000 seeded corruptions, checked under
ASan/UBSan, found and fixed two gaps: unchecked reserved header
fields, and unbounded null/boolean offsets that could make
dump() throw std::length_error.
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
* Fix MSVC errors in image test and add image_check doc anchor
Move the raw string literal out of the CHECK macro (MSVC preprocessor),
cast 64-bit header fields to std::size_t (C4244 on 32-bit), and give the
image_check section in load.md a real anchor for the mkdocs strict build.
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
* Check each distinct string and float range once in the full image check
The full check scanned the contents of every string node and every float token over its own range, so many nodes pointing to one large range made load() quadratic. The structural walk now only collects the ranges; after it, each kind is sorted and checked once per distinct range. Ranges of one kind that overlap without being identical are rejected, as save() never writes them (nodes that share a value share the whole range). The cost is linear in the size of the image plus sorting the ranges.
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
* Adapt the image tests to named documents and last-wins lookups
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
* Test that the targets of links never reach an image
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
* Test lookups in loaded images with duplicate and colliding keys
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
* Release the list of large objects once load() has built the indexes
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
* Test that invalid UTF-8 of a loaded image is not copied into an editable document
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
* Document that editable documents refuse invalid UTF-8 copied from a loaded image
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
* Expect first-wins lookups in the image tests
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
* Avoid useless casts of header fields in the image tests
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
* Fix clang-tidy 22 findings in unit-json_view_image.cpp and image.hpp
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
* Fix MSVC C4244 and C2017 in the image test
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
* Fix the indentation of a loop in the image test
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
* Make json_view.hpp pass include-what-you-use
ci_single_binaries runs IWYU with --error on every header. For json_view.hpp
it suggested adding <array> (std::array is used), the headers that json.hpp
already provides (abi_config, abi_macros, input_adapters, json_pointer,
cpp_future, string_concat, value_t, json_fwd), and <version> for
std::nullptr_t, and removing <cstddef>.
- include <array>
- keep <cstddef> (nullptr_t, size_t; IWYU attributes them to <version> and <cstring>)
- tell IWYU not to suggest the headers that json.hpp provides: the amalgamated
json_view.hpp only includes json.hpp, so including them here would duplicate
their definitions
- drop edit_storage.hpp, which edit.hpp includes
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
* Silence a clang-tidy C++17 finding in the image test
bugprone-suspicious-stringview-data-usage flags data() of the source view
under C++17, although the length is passed and checked by the REQUIRE
before it.
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
* Build the image link test without exceptions
load_result() exists only with exceptions; with JSON_NOEXCEPTION the test
loads the image with the check directly (a failed check aborts).
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
* Regenerate the amalgamated headers
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
* Follow the renames from the review of the view builder
The review of #5620 renamed document_data's tape/tape_size/tape_cap to
nodes/node_count/node_capacity and made the decode arena a
std::vector<char>; adapt the code of this change to it.
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
* Rename the emit lambda in the image writer for Qt compatibility
Qt defines emit as an empty macro, so a Qt header included before
json_view.hpp broke the lambda's declaration (see #5865). The slots
variables in the image tests are renamed as well.
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
* Address the review of json_document images
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
* Regenerate the amalgamated headers
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
---------
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
|
||
|
|
823eee3b59 |
Add editable json_documents: set, push_back, insert, and erase (#5630)
* Add editable json_documents: set, push_back, insert, and erase basic_json_document gets a second template parameter, Editable (false by default), plus the aliases json_editable_document, json_editable_view, ordered_json_editable_document and ordered_json_editable_view. Editable documents can change values and structure without rewriting the source text: set()/push_back() on values, keys, array indices and JSON pointers; insert() before an array element; erase() of an object key, array index or JSON pointer. New values and element sequences go into edit storage that the document owns and never moves, so views keep referring to their value across edits and a parsed node never moves. Read-only documents walk the plain node array and are unaffected. Strings are checked for UTF-8 on entry, so dump() of an editable document never throws type_error.316. Binary values cannot be stored (type_error.319). A seeded differential test applies random edits to an editable document and to the equivalent ordered_json and compares both after every step. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fix old clang and MSVC C4127 in editable json documents Value-initialize the const std::less in find_parent (clang 3.4/3.6 do not implement DR 253), and test the Editable template argument through a function to avoid MSVC C4127. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Copy nested values into a document without recursion Counting and copying the nodes of a view or a basic_json value into an editable document recursed once per nesting level, so that a deeply nested value overflowed the stack. The four functions now walk the value with an explicit stack, as materialize() does. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Do not leave a half-assigned container when set_moved() throws assign() rewrote the kind, length and extent of a slot before set_moved() registered its new element sequence. set_moved() can throw (std::bad_alloc from reserving the bookkeeping vectors) for a slot that is not moved yet, which left a container without the moved flag that showed its old children. The bookkeeping is now reserved first (reserve_moved()), so that nothing after the first write to the slot can throw. block_of() reserves before it marks anything for the same reason. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Check strings copied from another document for valid UTF-8 An editable document only holds valid UTF-8, but copy_scalar() copied the strings and keys of a view of another document unchecked. A document of a weaker check (or a borrowed text that changed after parsing) could therefore bring ill-formed UTF-8 into it. The copy is checked now, with the error that dump() reports for the string; copies within the same document stay unchecked. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Allow the edit arena to grow up to 4 GiB - 1 bytes append_text() doubled the capacity of the arena and rejected the result if it exceeded 4 GiB, so that an arena of more than 2 GiB could not grow even though the 32-bit offsets of nodes address 4 GiB - 1 bytes. The capacity is now clamped to that limit (text_capacity()), and an append is rejected only if the bytes themselves do not fit. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Set a null value below a pointer as basic_json does set(json_pointer, value) turned a null parent into an object for every last reference token, so "/a/0" and "/a/-" below {"a":null} made {"a":{"0":1}}, where basic_json's operator[](json_pointer) makes an array. A null parent now becomes an array for "-" and for digit tokens (filled with nulls up to the index) and an object otherwise. An invalid index is reported before the parent changes. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Replace a container by a scalar in constant time once it is linked Replacing an array or object that spans several nodes by a scalar looks up its parent from the root (find_parent), which is linear in the size of the document: setting every element of a 20,000 element array took more than half a second. The parent only has to switch to links once; afterward the extent of the replaced value no longer matters. Nodes that an entry of a moved sequence links to are now marked (node_flags::linked), and assign() skips the lookup for them. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Update the json_view banner and type_error.319 for editable documents The banner still described a read-only view, and the documentation of type_error.319 listed set and push_back only, although insert rejects binary values as well. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Assign the member that lookups find when set() meets duplicate keys An editable document reads the last member of a repeated key (as the read-only document does), but set(object, key, value) assigned the first one: a view taken from object[key] before the call did not show the new value. It now assigns the last member's value, keeps the key at the position of its first occurrence (where materialize() puts it), and drops the other members. The seeded differential test now also edits documents whose objects repeat keys and compares every lookup (operator[], at, find, value, contains, count, and JSON pointers) with the parsed basic_json value. A targeted test covers duplicates before and after edits that move the object, in large objects with an index, in moved arrays, and in values copied from other documents. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Assign the first member when set() meets duplicate keys again Lookups find the first member of a repeated key again, so set(object, key, value) assigns that member (keeping the key where it is) and drops the others, as documented before, and a view taken from object[key] shows the new value. This undoes the code and documentation changes of |
||
|
|
3ff37ead75 |
Scan json_view strings with SIMD and index large objects (#5628)
* 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> * Fix GCC module build and MSVC C4127 in the json_view SIMD code GCC ignores the target attribute in modules, so the SSSE3 dispatch is disabled for the module interface (the check stays portable, SSE2 is kept). Make the 8-vs-16 byte unrolling condition in scan_string_run a preprocessor/template split to avoid a constant condition (C4127). Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Remove an accidentally committed GCC module cache Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Bound the probe length of the large-object hash index The key hash is not seeded, so keys chosen to collide made building the table quadratic (20,000 colliding keys took 470 ms to parse). A key may now sit at most 64 slots from its home slot; if a key would sit further away, the table is dropped and the object is searched linearly. Lookups stop after the same distance. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Release the spare capacity of the hash index in shrink_to_fit shrink_to_fit() now trims the tables of large objects like the node array and the decoded strings, and the list of large objects is released as soon as the tables are built. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Use the portable string scan on non-x86 targets that define __SSE2__ WebAssembly with -msse2 defines __SSE2__, but has no <cpuid.h> or x86 intrinsics headers. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Document the hash index number in node::extra Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Keep the last of duplicate keys in the object hash index Lookups in objects with 128 members or more now return the last member of a repeated key, like the linear search of smaller objects and like materialize() and basic_json::parse(). build_object_index let the first occurrence win, so the same text gave different results depending on the size of the object. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Keep the first of duplicate keys in the object hash index again Lookups in objects with 128 members or more return the first member of a repeated key again, like the linear search of smaller objects. This undoes the code change of a69542046; its test now expects the first member from lookups (with and without a table) and the last value from materialize() and basic_json::parse(). Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fix clang-tidy 22 findings in the json_view headers Silence bugprone-casting-through-void for the SSE loads (a reinterpret_cast would trip -Wcast-align=strict), use auto for a cast initialiser, add parentheses to a mixed expression, and name bugprone-std-namespace-modification in the NOLINTs of the tuple_size/tuple_element specialisations. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fix clang-tidy 22 findings in unit-json_view.cpp and unit-json_view_builder.cpp Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Avoid GCC useless-cast and strict-overflow warnings in unit-json_view.cpp Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Index strings with size_t in the colliding-keys test (MSVC C4244 on Win32) 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> * Move the dump, comparison, and large-object tests to unit-json_view_dump.cpp The dump and comparison tests moved to their own file in the dump pull request; the hash index tests of this branch join them there. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Regenerate the amalgamated headers Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Follow the renames from the review of the view builder The review of #5620 renamed document_data's tape/tape_size/tape_cap to nodes/node_count/node_capacity and made the decode arena a std::vector<char>; adapt the code of this change to it. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Rename slots in the object index for Qt compatibility Qt defines slots as a macro, so a Qt header included before json_view.hpp broke the declarations of the local variable (see #5865). The test variable is renamed as well. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Address the review of the json_view object index and SIMD scan From gregmarr's review of #5628: - Docs and comments say that the hash index is one of the object's keys. - object_index.hpp: conditional_static_cast for the hash, "slot" for the table position, no flag for a degenerate table, and a static_assert that the smallest indexed object is larger than the probe window. - scan.hpp unrolls with NLOHMANN_VIEW_REPEAT_FIRST_8/SECOND_8. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Regenerate the amalgamated headers Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Encode JSON_VIEW_NO_SIMD and JSON_VIEW_USE_SSSE3 in the ABI namespace Both macros select between definitions of the same inline functions, so translation units that disagree on them violated the ODR. Like the other ABI-affecting macros, they now add a tag to the inline namespace (_vns and _vssse3), and the docs replace the "Define consistently" warning with the usual ABI compatibility note. From gregmarr's review of #5628. nlohmann_json.natvis covers twice as many tags now, so it lists four times as many namespaces (14.7 MB instead of 3.4 MB). Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Regenerate the amalgamated headers Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Do not include type_traits.hpp directly in the object index json.hpp, which object_index.hpp already includes, provides it; included directly, the json_view amalgamation (which only treats nlohmann/json.hpp as external) copied the json internals into json_view.hpp a second time, so the fuzzers, which include both single headers, failed with redefinitions (make_void, integer_sequence, priority_tag, ...). Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Compare keys with key_equals() in the object hash index As the linear lookups do since the second review round of #5622. 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> |
||
|
|
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> |
||
|
|
ccc005c012 |
Fix contradictions between the documentation, the README, and the library (#5825)
- Discarded values: the legacy macro only changes <= and >=; != is true by default - Out-of-range numbers are rejected while parsing, not stored as NaN - merge_diff round trip: point to the ordered_json caveat - ensure_ascii example shows the escaped output - out_of_range.405 is thrown for "remove"/"move" on the root, not "add" - CBOR stores binary subtypes as tags - Integer/float comparison uses exact values, not conversion - diff, flatten, and the binary writers are depth-bounded - FAQ: max_digits10 for serialization, trailing commas are supported - out_of_range.407 is still thrown by to_bon8 - FetchContent_MakeAvailable needs CMake 3.14 - json_fwd.hpp contents, operator< link, RFC 7396 for merge patch - README: dump() wording, merge_diff, MSYS2/build2, patch comment typos, operator[] link, embedded CMake example aligned with the docs 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> |
||
|
|
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> |
||
|
|
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> |
||
|
|
6197341a21 |
Clean up non-code files (#5801)
* Clean up README, contribution guide, and repository metadata - REUSE.toml: fix the Hedley path and SPDX id (CC0-1.0), mark the docset icons as the public-domain JSON logo - CITATION.cff: v3.12.0 was released on 2025-04-11 - FILES.md: list all workflows, describe the Meson option defaults - README: ctest -LE, table of contents, typos, stale Android/MinGW advice, moved links, merge duplicate Thanks entries, list the analysis tools used in CI - CONTRIBUTING: iterative parser, json_literals.hpp is generated, links - .gitignore: backups and release outputs; .gitattributes: mark generated files - labeler: label other build systems and .github documentation - MODULE.bazel: add the module version Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Clean up CI, CMake, Makefile, and Bazel files - CMakeLists.txt: avoid VERSION_GREATER_EQUAL, which CMake < 3.7 lacks - ci.cmake: test JSON_DisableTupleReferenceConversion in ci_cmake_flags, remove unused variables and unreachable per-compiler targets, look up Clang tools consistently, forward CMAKE_CXX_FLAGS to ci_module_cpp20, format json_literals.hpp and remove backups in ci_test_amalgamation - BUILD.bazel: add json_literals.hpp to the single-header target - workflows: format json_literals.hpp before copying it, drop the obsolete natvis --version plumbing, name natvis and macro_builder in failure messages, drop the duplicate amalgamation job, install Valgrind only where needed, republish docs on version bumps, fix stale names - Makefile: complete .PHONY and help, check-amalgamation always restores the checked-in files, natvis uses its own venv, macro_builder_check installs astyle, clean removes the fuzzer binaries - remove tools/amalgamate/config_json_view.json (json_view.hpp is not on develop yet) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fix inaccuracies in the documentation - version history: json_base_class_t (3.11.3), JSON_HAS_CPP_11 (3.10.0), JSON_HAS_RANGES exclusions, define-type macros, ABI tags - releases: 3.12.0 raised the minimum CMake version - from_*: the (ptr, len) overloads are deleted, not removed, in 4.0.0 - contains/count/find: document the deleted integral overloads - add JSON_HAS_RANGE_VIEW_CONVERSION and list the JSON_HAS_* macros in the macro overview - mention BON8 and error_handler where binary formats are listed - broken links, outdated URLs, warning count, Hunter v0.26.12 - copy_markdown_source hook: expand snippets in the Markdown copies Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Pass /EHsc to the Windows C++20 module build ci_module_cpp20 now forwards CMAKE_CXX_FLAGS to the module build. The Windows workflow sets CMAKE_CXX_FLAGS, which replaces CMake's MSVC defaults including /EHsc, so <chrono> failed with C4530 under /WX. Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- 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>
|
||
|
|
3c465beb61 |
Add tests for error_handler_t::keep in dump() (#4555)
Squashed onto develop from: - Add error_handler_t::keep to copy invalid UTF-8 bytes unchanged - Mention error_handler_t::keep in the README Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
5379e04ce4 |
Throw type_error.321 when serializing discarded values to binary formats (#5761)
* Throw type_error.321 when serializing discarded values to binary formats The CBOR, MessagePack, UBJSON, BJData, and BSON writers silently skipped the payload of a value_t::discarded value nested in an array or object, while still writing its slot in the element/member count (and, for BSON, its entry header), producing a binary document whose declared size does not match what was actually written. Throw type_error.321 instead, for a discarded value anywhere in the tree, including at the top level. Rewritten from the original PR against the current (non-recursive option aside) binary_writer.hpp, which has changed substantially since this was first proposed; the out_of_range.412 MessagePack size check and unrelated test reformatting from that PR are dropped as out of scope here. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Document and test type_error.321 for discarded binary values Add docs for the new exception (home/exceptions.md and the Exceptions sections of to_cbor/to_msgpack/to_ubjson/to_bjdata/to_bson) and test coverage for a discarded value nested in an array or object, nested deeper, and (for UBJSON/BJData) inside an optimized same-type array, for each of CBOR, MessagePack, UBJSON, BJData, and BSON. Adjust the three pre-existing "discarded" tests that asserted the old silent behavior (empty/short output) to expect type_error.321 instead. Co-authored-by: ameliabarnabyhub <312084480+ameliabarnabyhub@users.noreply.github.com> Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> Co-authored-by: ameliabarnabyhub <ameliabarnabyhub@users.noreply.github.com> Co-authored-by: ameliabarnabyhub <312084480+ameliabarnabyhub@users.noreply.github.com> |
||
|
|
8c1f60a45e |
Store maps with enum keys as objects (opt-in) (#5600)
* Store maps with enum keys as objects (opt-in) Maps with enum keys, such as std::map<E, T>, are stored as arrays of [key, value] pairs, because enums are not convertible to the string type of object keys - even if NLOHMANN_JSON_SERIALIZE_ENUM maps them to strings (#4378). The new JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS macro stores them as objects instead, converting each key with the enum's to_json. It applies to any map-like type with enum keys (std::map with any comparator, std::unordered_map, ...). A key that does not convert to a string throws type_error.302, and two keys converting to the same string throw the new type_error.318, rather than losing an entry. The macro changes the output of inline functions, so it is part of the ABI tag (_ekmo). Reading needs no macro: std::map and std::unordered_map with enum keys are now also read from objects, converting each key with the enum's from_json. That input was rejected before, and arrays of pairs are still read, so data written either way can be read. This supersedes #4531, which first proposed storing these maps as objects. Co-authored-by: Muhammad Amir bin Mohamad Ghazaly <amirghaz@umich.edu> Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Keep multimaps with enum keys as arrays of pairs With JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS, is_enum_keyed_map also matched std::multimap and std::unordered_multimap. Storing them as objects throws type_error.318 as soon as a key occurs twice, which is the normal case for a multimap, so such values could no longer be serialized at all once the macro was enabled, although they are stored losslessly as arrays of [key, value] pairs without it. Exclude maps with non-unique keys from is_enum_keyed_map. They are detected by insert(value_type) returning an iterator rather than a pair<iterator, bool>. Map-like types without such an insert() are still treated as before. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Move the default enum-keyed map tests out of unit-conversions.cpp The Windows clang 20.1.8 job (MinGW, Debug) failed to link test-conversions_cpp17 with "relocation truncated to fit: IMAGE_REL_AMD64_REL32 against .rdata": the object file of unit-conversions.cpp was already close to the limit, and the new "maps with enum keys" test case pushed it over. windows.yml asks to keep these objects small by splitting test files. Move the test case unchanged into unit-enum_keyed_maps_default.cpp, with the three enums it needs. It still honors a -D flag for JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS, as before. unit-conversions.cpp is back to its state on develop. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Build the enum-keyed map test object instead of parsing it ci_test_diagnostic_positions failed in unit-enum_keyed_maps_default.cpp: with JSON_DIAGNOSTIC_POSITIONS, a parsed value adds its byte range to the exception message ("(bytes 0-7) type must be array, but is object"), so the exact-message checks did not match. Build the object in memory, like unit-custom-array-type.cpp does. Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> Co-authored-by: Muhammad Amir bin Mohamad Ghazaly <amirghaz@umich.edu> |
||
|
|
6b4b825af2 |
Handle numbers that do not fit narrow number types in the binary readers (#5607)
* Handle numbers that do not fit narrow number types in the binary readers With custom number types narrower than the values in a binary document, for example basic_json<..., std::int32_t, std::uint32_t, float>, every binary reader (CBOR, MessagePack, UBJSON, BJData, BSON, BON8) passed the decoded number to the SAX interface with an implicit conversion: the integer 5000000000 silently became 705032704, and a finite double such as 1e300 became infinity. The lexer handles the same values in JSON text: an integer that fits neither integer type is stored as number_float_t, and a finite number that overflows number_float_t is rejected with out_of_range.406. Pass every number read from binary input through three helpers that apply the lexer's rules: - emit_signed(): number_integer_t, else number_unsigned_t for a non-negative value, else number_float_t - emit_unsigned(): number_unsigned_t, else number_float_t - emit_float(): out_of_range.406 if a finite value overflows number_float_t; infinity and NaN are passed on For consistency, a CBOR negative integer below the range of number_integer_t is now stored as number_float_t, like a too small integer in JSON text, instead of being rejected with parse_error.112. With the default number types, this is the only change in behavior. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fix MSVC and clang 3.5 in the narrow number type test MSVC types 3000000000 and 5000000000 as unsigned long, so json(-3000000000) triggered C4146 (unary minus on an unsigned type), which /WX turns into an error. Use LL literals, as elsewhere in the tests. clang 3.5 cannot convert the lambdas in the braced initializer of the format table to function pointers. Use named functions instead. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Check integer-to-float fallbacks for overflow in the binary readers emit_signed, emit_unsigned, and the CBOR negative integer fallback now pass their number_float_t fallback through emit_float, so a value that overflows number_float_t is rejected with out_of_range.406 like a floating-point value, instead of silently becoming infinity. This only matters for a number_float_t that cannot represent 2^64, such as a half-precision type. The CBOR value -1 - n is computed as long double so that emit_float sees a finite value. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Use the input_format member instead of passing the format to binary_reader helpers The helpers (get_number, get_to, get_string, get_binary, get_bytes, emit_signed, emit_unsigned, emit_float, unexpect_eof, exception_message) are members of binary_reader, which already stores the format it was constructed with, so the parameter was redundant. Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
73e9eae3c1 |
Add an error_handler parameter for UTF-8 to the binary readers and writers (#5746)
Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
f56b418c56 |
Follow each binary format's UTF-8 rule: strict writers (CBOR/UBJSON/BJData/BSON), lenient readers (#5741)
Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
78ddd95794 |
Document GCC < 11 incomplete-type error with optional members (#3669) (#5596)
* Document GCC < 11 incomplete-type error with optional members (#3669) With GCC 10 and older in C++11/14 mode, a free to_json() for a type holding an optional<Dummy> (Dummy constructible from json) fails with "invalid use of incomplete type detector<...to_json_function...>". ADL for Dummy finds the unrelated to_json and closes an instantiation cycle through optional's converting constructor. The same error reproduces without the library, so it can't be fixed here. Add a FAQ entry explaining the cause and the hidden-friend workaround, recommend hidden friends in the arbitrary types docs, and add a regression test that keeps the workaround compiling on GCC 7-10. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Use the #3669 fixture's optional member in to_json Issue3669Holder::d is never read, so clang's -Weverything -Werror build (ci_test_clang) fails with -Wunused-private-field. Reference the member in the hidden-friend to_json; this does not affect the instantiation cycle the fixture reproduces. Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
63c10a51fc |
Review and extend the documentation, and check it in CI (#5638)
* Review and extend the documentation, and check it in CI A review of all documentation pages found factual errors, dead links, missing cross-references, and gaps in examples. This fixes them and adds checks so the same problems are caught automatically. Fixes: - wrong signatures and version histories (operator!= C++20 member, binary() subtype type, get<PointerType>(), JSON_NO_THREAD_LOCAL, ...) - stale descriptions (number parsing since #5283, UBJSON table, SAX example that no longer compiled, tsl::ordered_map advice) - dead internal and external links; repology.org badges (the domain is suspended) replaced by badges that query the registries directly - deprecation notes link the migration guide; the guide itself fixed Additions: - "See also" sections, cross-references, 25 runnable examples, 12 Mermaid diagrams, new API pages for json_pointer::operator<=> and byte_container_with_subtype::operator==/!= - landing page, guides for untrusted input and performance - "unreleased" badge after versions newer than the latest release Checks: - strict documentation build (broken links/anchors fail it); CI and the publish workflow fetch the full history the build needs - weekly external link check, Mermaid syntax check in CI - check_structure.py: example titles, heading levels, alt texts, header links, docset index coverage; its unused-example check works again - all examples produce the same output on every platform Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Keep the customer links that could not be fixed A dead link on the customers page is still the evidence of where the use of the library was documented. Keep the original URLs of the entries without a working replacement (Marne, Cisco Webex Desk Camera, Philips Hue, CyberArk) and exclude exactly these URLs from the link check. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Correct the duplicate-key recipe's claim about SAX positions The SAX interface's key() receives no position either; only parse_error() does. Also note that the recipe does not report the path to the repeated key (see discussion #5085). Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Say the library is available as a single header and mention json_fwd.hpp Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Correct documentation errors found while hunting for bugs - patch/patch_inplace: list the JSON pointer errors parse_error.106-109 and out_of_range.402/404, and quote the actual parse_error.105 message. - unflatten: list parse_error.106/107/108 and out_of_range.404. - to_bson: list out_of_range.415 (binary subtype above 255) and note that 412 and 415 are new in 3.13.0. - to_string: state that string_t must be convertible to std::string, also in the StringType requirements table. - JSON Lines: a `while (input >> j)` loop also throws after the last value for concatenated JSON values; show a loop that works for both. - BON8: a string gets 0xFF only if nothing follows it in the message; a string at the end of an array or object is ended by 0xFE. - custom_string_type.hpp: add operator+=(char), which the "Always required" list asks for (json_pointer::to_string, flatten, unflatten, and diff did not compile), and an ADL int_to_string for diff and items. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Cache the release headers with functools.lru_cache Codacy (Pylint) flagged the mutable default argument that header() used as its cache. functools.lru_cache keeps the same memoization without it. The script's output is unchanged. 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> |
||
|
|
9e1a09eec0 |
Name the key type when rejecting non-string CBOR/MessagePack map keys (#5594)
* Name the key type when rejecting non-string CBOR/MessagePack map keys CBOR and MessagePack allow map keys of any type, but JSON object keys are always strings, so such maps are rejected. The error so far was the one for a malformed string (e.g. "expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0xC0" for a nil key), which does not tell the user what went wrong. Report the type of the key instead: syntax error while parsing MessagePack object key: only string keys are supported, but found nil; last byte: 0xC0 The exception id (parse_error.113) and type are unchanged. Malformed string keys and a missing key keep their previous messages. Document the restriction on the CBOR and MessagePack pages. Refs #2766, #3381 Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Point the MessagePack key note to the spec's profile section The note linked to "Serialization: type to format conversion", which says nothing about key types. Restricting map keys to strings is only mentioned in the "Profile" section (under "Future discussion") as an example of a JSON-compatible profile, so link there and describe it as such instead of as a permission. Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
f7972970a4 |
Throw instead of writing MessagePack lengths beyond UINT32_MAX (#5584)
* Throw instead of writing MessagePack lengths beyond UINT32_MAX MessagePack stores the length of a string, binary value, array, or object in at most 32 bits. For a larger value, to_msgpack wrote no length at all, so the output could not be read back. It now throws out_of_range.412, which BSON already uses for its 32-bit length fields. The check lives in one function, so each length is written by an if/else chain that ends in a plain else, without a condition that can never be false. It is tested with string and binary types that report a size beyond UINT32_MAX without allocating it, like the BSON tests do. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fix the CI failures of the MessagePack length check - mark to_msgpack_length's value as used when exceptions are disabled (-Wunused-parameter, misc-unused-parameters) - put "Exception safety" before "Exceptions" in to_msgpack.md, as the documentation style check requires - create the test's string value from its type: constructing it from a beyond_uint32_string_t considers the std::filesystem::path conversion, which libstdc++ 10 reports as ambiguous for a class derived from std::string (clang 13) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Skip the MessagePack string length test for clang with libstdc++ 10 C++17 builds consider the std::filesystem::path conversion for the string type, and with clang and libstdc++ 10 that conversion is ambiguous for a class derived from std::string. Creating the value from its type did not avoid it, since any basic_json with that string type instantiates the check. The binary and ext cases are still tested there. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Keep the MessagePack string test type and its alias in one block astyle indented the alias oddly when it had an #ifdef of its own after the binary alias; declare it right after the string type, in the same block. Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
d19f7f5dce |
Fix BSON conformance issue (#5185)
* 🐛 fix BSON conformance issue Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 🐛 fix BSON conformance issue Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 🐛 reject ill-formed UTF-8 in CBOR/MessagePack/BSON text strings at decode time (#5531) from_cbor()/from_msgpack()/from_bson() copied the raw bytes of a decoded text string into the resulting json value without any UTF-8 validation, even though RFC 8949 §3.1 (CBOR) and the MessagePack/BSON specifications all require text strings to be valid UTF-8. Malformed input only failed later, if the value was dump()'d, with a type_error.316 - so the allow_exceptions=false pattern used specifically to get a discarded sentinel instead of an exception did not discard this category of malformed input, unlike every other kind of malformed binary input this library rejects at decode time (see #5529). Fix this at the single choke point shared by BSON/CBOR/MessagePack/UBJSON string reads, binary_reader::get_string(): validate the bytes with the UTF-8 DFA right after they are read, and report failures the same way as every other binary_reader error (parse_error.113), so allow_exceptions and strict discarding behave consistently. get_binary()/binary blob reads are untouched and still accept arbitrary bytes, since only text strings are required to be UTF-8. There were two independent implementations of a UTF-8 validator: the lexer's streaming scanner, and the serializer's Hoehrmann DFA used by dump_escaped_impl(). Rather than write a third, the serializer's decode() function, its utf8d table and the UTF8_ACCEPT/UTF8_REJECT constants are extracted into detail/string_utils.hpp (a low-level header already included before both detail/input/ and detail/output/), alongside a new is_valid_utf8() helper built on the same decode() step. serializer.hpp's dump_escaped_impl() now calls the shared decode(), so there is exactly one UTF-8 validator in the codebase; dump()'s exact type_error.316 messages and byte-index reporting are unchanged (see the added regression-guard test in unit-serialization.cpp). Claude-Session: https://claude.ai/code/session_01N4RQ1Ahan5YAGbnAQGjZTY Signed-off-by: Niels Lohmann <mail@nlohmann.me> Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> * ⚡ validate only newly read bytes of binary-format strings get_string() validated the whole result after each call, but get_bytes() appends to it and CBOR indefinite-length strings collect all chunks in the same result, so every chunk re-validated everything read before it. An input of many small chunks took quadratic time (80000 one-byte chunks, 160 KB of input, took about 7 seconds). Only the newly read bytes are validated now, which also matches RFC 8949's requirement that every chunk is valid UTF-8 on its own. Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
ad2c14b985 |
Document response times, supported versions, access, secrets, and dependency policies (#5580)
Answer the OpenSSF Best Practices criteria that asked for policies the project follows but had not written down: - SECURITY.md: a first response within 14 days, publishing an advisory with credit once a fix is released, and that only the latest release receives security fixes. - Governance: who has access to the project's resources, how write or admin access is granted, and how CI secrets are stored and rotated. - Quality assurance: how dependencies of the build, test, and documentation tooling are pinned, scanned, and kept free of known vulnerabilities. Also update the assurance case, since comparison no longer recurses per nesting level (#5390), and point the best practices badge and links to bestpractices.dev under the program's current name. Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
3901b223e5 |
Complete the architecture documentation page (#5570)
* Complete the architecture documentation page Replace the placeholder bullets and TODOs with a description of the component pipeline (with a diagram), the source layout, the template parameters, the value storage (now in struct data), the input and output adapters, and the SAX interface. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Link sources and basic_json, document full input adapter interface Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Align the default column of the template parameter table Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> |
||
|
|
ed513715a8 |
Document that a NUL byte in the input is treated as end of input (#5534)
* docs: document that a NUL byte in the input is treated as end of input A NUL byte anywhere in the input - trailing, or embedded ahead of more otherwise well-formed JSON - is currently treated the same as genuine end of input, so parsing silently stops there instead of raising the parse_error.101 any other unexpected byte triggers. This mirrors the NUL-terminated-C-string convention already used when no explicit input length is given (json::parse(const char*) already stops at strlen()), just applied uniformly rather than only when a length is genuinely unavailable. This behavior predates this change and is not being altered here - changing it would be an observable, backwards-incompatible behavior change for any caller that (knowingly or not) depends on it, which is not something to do silently in a patch. Documenting the current, verified behavior as a new FAQ entry instead, so it's an intentional and discoverable part of the contract rather than a surprise. Fixes #5530. Signed-off-by: Niels Lohmann <niels.lohmann@gmail.com> Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N4RQ1Ahan5YAGbnAQGjZTY * Add JSON_STRICT_NUL_HANDLING opt-in macro for issue #5530 A NUL byte anywhere in the input is currently treated the same as real end of input, rather than raising parse_error.101 like any other unexpected byte (documented in the previous commit's FAQ entry). A full unconditional fix was tried in PR #5532 but rejected as too risky to ship by default: any caller could depend on the current behavior, even unknowingly (e.g. a zero-padded buffer). On PR #5534, gregmarr proposed a compile-time opt-in flag instead, and the maintainer agreed, wanting it available now and defaulting to the corrected behavior in 4.0.0. This mirrors the existing JSON_BRACE_INIT_COPY_SEMANTICS precedent as closely as sensible: - JSON_STRICT_NUL_HANDLING defaults to 0 (off); the three lexer sites that treat '\0' as EOF/comment-terminator are gated with `#if !JSON_STRICT_NUL_HANDLING` so the default-off behavior is byte-for-byte identical to today's. - input_adapters.hpp's `T (&array)[N]` overload additionally trims a single trailing '\0' from a `char` array (e.g. a string literal like `json::parse("123")`) when the macro is on, so that case keeps working; every other element type (unsigned char, std::uint8_t, ...) always keeps its full extent. This intentionally does *not* reuse the existing strlen()-based pointer overload via SFINAE-excluding `char` from the array overload, as originally sketched for this change: that approach is ambiguous against the newer generic container overload added since PR #5532, and even where it compiles, strlen()-scanning a `char` array that is not NUL-terminated within its bounds reads past the end of the array (confirmed with AddressSanitizer). Trimming only a single trailing byte, without scanning, avoids both problems. - Documented via docs/mkdocs/docs/api/macros/json_strict_nul_handling.md, linked from the macros index/nav/features page, the FAQ entry, and the parse/accept/operator>> reference pages. - Tested in unit-class_parser.cpp and unit-deserialization.cpp, default state unguarded and opt-in state guarded. Since the library itself #undefs the macro at the end of json.hpp (as JSON_BRACE_INIT_COPY_SEMANTICS already does), a plain `#if defined(JSON_STRICT_NUL_HANDLING)` guard after the include never actually triggers; the tests instead capture the command-line value into a test-local macro before including the header. A few pre-existing fixtures elsewhere (std::array<uint8_t, N> sized one larger than their literal, relying on value-initialization to silently add a trailing zero byte) needed the same one-byte adjustment to keep passing under the opt-in behavior. Unlike the precedent, this adds a proper `JSON_StrictNulHandling` CMake option (rather than a raw -DCMAKE_CXX_FLAGS injection) and wires its ci_test_strict_nul_handling target into the ci_cmake_options job matrix in .github/workflows/ubuntu.yml, so the opt-in build is actually exercised in CI -- closing the one gap in the precedent's own CI setup (ci_test_brace_init_copy_semantics is defined but never referenced by any workflow, so it has never actually run). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Clarify where JSON_STRICT_NUL_HANDLING does not reject NUL bytes Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <niels.lohmann@gmail.com> Signed-off-by: Niels Lohmann <mail@nlohmann.me> Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
0b20b7e622 |
Reject MessagePack/BSON binary subtypes that don't fit their wire format (#5469)
* Reject MessagePack/BSON binary subtypes that don't fit their wire format Both formats store byte_container_with_subtype's subtype (a uint64_t) in a single byte. The writers cast to std::int8_t/std::uint8_t without a range check, so subtypes above 255 were silently truncated modulo 256 instead of raising an error. Throw out_of_range.413 instead when the subtype exceeds the representable range of 0-255. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Move the new binary-subtype regression test out of unit-regression2.cpp unit-regression2.cpp is already at the edge of what the MinGW linker can relocate; adding this test's ~26 lines tips test-regression2_cpp20 (clang, Windows) over into "relocation truncated to fit: IMAGE_REL_AMD64_REL32 against `.rdata'" (see |