Compare commits

..
Author SHA1 Message Date
Niels Lohmann d3d39a9c10 Fix narrowing of the optimized-container count in BJData and UBJSON tests
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-26 06:52:50 +01:00
Niels Lohmann 24ff6eaa43 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>
2026-10-25 20:05:29 +01:00
Niels Lohmann 9d9bf927ff 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>
2026-10-25 20:05:29 +01:00
Niels Lohmann cc39e1f8da 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>
2026-10-25 20:05:28 +01:00
Niels Lohmann 5ac95db5a9 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>
2026-10-25 20:05:27 +01:00
Niels Lohmann d1d142633a 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>
2026-10-25 20:05:26 +01:00
Niels Lohmann 49e9eaac4f 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>
2026-10-25 20:05:25 +01:00
Niels Lohmann 4b35877ae5 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>
2026-10-25 20:05:24 +01:00
Niels Lohmann 34f9be2aa2 docs: drop discarded-value remarks
Remove the mention that a discarded value cannot be serialized to CBOR
(type_error.321) from the exception-safety and mapping sections. The
integer table and all added prose already follow the table-format and
Oxford-comma rules.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-25 20:05:23 +01:00
Niels Lohmann 8c4818d5f7 Address review of the CBOR changes
Cite RFC 8949 Section 5.3.1 for duplicate keys, describe the out_of_range.408 condition precisely, rewrap a long line, and update the stale get_string comment (per-chunk UTF-8 check). single_include regenerated.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-25 17:56:22 +01:00
Niels Lohmann df1bca6dae 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>
2026-10-25 17:46:08 +01:00
Niels Lohmann b22cad367c docs: correct and extend the CBOR documentation
Fix the negative-integer rows (they used MessagePack boundaries), the
number_integer range of the 0x1B row, and the half-precision entry in the
list of unused types; correct the NaN history (0xF9 since 3.8.0) and the
tagged-item wording (only the tag directly in front of a byte string is
stored, and only in the 0xD8..0xDB form). Document that to_cbor is not
RFC 8949 deterministic, that duplicate keys are accepted (last wins),
tags in front of keys, per-chunk UTF-8 validation, the exceptions
parse_error.113, out_of_range.406/408 of from_cbor, the pointer and
C array input traps, the partially written output on exceptions, and the
store/ignore results in the binary values example. Drop the claim that
the CBOR reader needs StringType::append(const StringType&) and
BinaryType::insert(pos, first, last).

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-25 17:46:05 +01:00
Niels Lohmann 95169a63fa Skip CBOR tags in front of map keys and validate indefinite text chunks
A tag in front of a text string map key was rejected with parse_error.113
even with cbor_tag_handler_t::ignore or store, although tags in front of
values are handled. The tags are now skipped iteratively before the key is
read; the handler error still rejects them.

The chunks of an indefinite-length text string were concatenated before
the single UTF-8 check, so a code point split over two chunks, or an
invalid chunk, was accepted. RFC 8949, Section 3.2.3 requires every chunk
to be valid on its own. With error_handler_t::strict, replace and ignore
each chunk is now checked; keep is unchanged. The chunks are appended with
append(ptr, len), so StringType needs nothing new.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-25 17:46:05 +01:00
61 changed files with 2510 additions and 2432 deletions

No files matched your search

@@ -113,15 +113,15 @@ basic_json(basic_json&& other) noexcept;
```mermaid
flowchart TD
A(["initializer_list init"]) --> B{{"empty, or every element is a 2-element<br/>array whose first element is a string?"}}
B -->|"yes"| C{{"type_deduction"}}
B -->|"no"| D{{"type_deduction"}}
A(["initializer_list init"]) --> B{"empty, or every element is a 2-element<br/>array whose first element is a string?"}
B -->|"yes"| C{"type_deduction"}
B -->|"no"| D{"type_deduction"}
C -->|"true"| OBJ["create object"]
C -->|"false"| E{{"manual_type"}}
C -->|"false"| E{"manual_type"}
E -->|"object"| OBJ
E -->|"array"| ARR["create array"]
D -->|"true"| ARR
D -->|"false"| F{{"manual_type"}}
D -->|"false"| F{"manual_type"}
F -->|"array"| ARR
F -->|"object"| ERR["throw type_error.301"]
```
+1 -3
View File
@@ -7,9 +7,7 @@ const_iterator begin() const noexcept;
Returns an iterator to the first element.
<div class="nj-fig-wrap" markdown>
--8<-- "images/range-begin-end.svg"
</div>
![Illustration from cppreference.com](../../images/range-begin-end.svg)
## Return value
+3 -2
View File
@@ -90,8 +90,9 @@ type `#!cpp binary_t*` must be dereferenced.
- MessagePack
- If a subtype is given and the binary array contains exactly 1, 2, 4, 8, or 16 elements, the fixext family (fixext1,
fixext2, fixext4, fixext8) is used. For other sizes, the ext family (ext8, ext16, ext32) is used. The subtype is
then added as a signed 8-bit integer.
fixext2, fixext4, fixext8, fixext16) is used. For other sizes, the ext family (ext8, ext16, ext32) is used. The
subtype is then written as the signed 8-bit ext type: subtypes 128 to 255 map to the negative (reserved) ext
types, and on reading the ext type is exposed as an unsigned subtype from 0 to 255.
- If no subtype is given, the bin family (bin8, bin16, bin32) is used.
- BSON
+1 -3
View File
@@ -6,9 +6,7 @@ const_iterator cbegin() const noexcept;
Returns an iterator to the first element.
<div class="nj-fig-wrap" markdown>
--8<-- "images/range-begin-end.svg"
</div>
![Illustration from cppreference.com](../../images/range-begin-end.svg)
## Return value
@@ -15,11 +15,13 @@ error
: report a parse error in case of a tag (the `from_cbor` overloads throw a `parse_error` exception by default)
ignore
: ignore tags
: ignore tags (also in front of a text string map key)
store
: store tagged byte strings (for bytes 0xd8..0xdb) as binary values with the tag as subtype; other tagged values are
read as if the tag were ignored. If several tags precede a byte string, only the innermost one is stored.
read as if the tag were ignored. If several tags precede a byte string, only the tag directly in front of it is
stored, and only when it has the form 0xd8..0xdb (a tag 0..23 written as 0xc0..0xd7 is skipped). Tags in front of a
text string map key are skipped.
## Examples
+1 -3
View File
@@ -6,9 +6,7 @@ const_iterator cend() const noexcept;
Returns an iterator to one past the last element.
<div class="nj-fig-wrap" markdown>
--8<-- "images/range-begin-end.svg"
</div>
![Illustration from cppreference.com](../../images/range-begin-end.svg)
## Return value
+1 -3
View File
@@ -6,9 +6,7 @@ const_reverse_iterator crbegin() const noexcept;
Returns an iterator to the reverse-beginning; that is, the last element.
<div class="nj-fig-wrap" markdown>
--8<-- "images/range-rbegin-rend.svg"
</div>
![Illustration from cppreference.com](../../images/range-rbegin-rend.svg)
## Return value
+1 -3
View File
@@ -7,9 +7,7 @@ const_reverse_iterator crend() const noexcept;
Returns an iterator to the reverse-end; that is, one before the first element. This element acts as a placeholder,
attempting to access it results in undefined behavior.
<div class="nj-fig-wrap" markdown>
--8<-- "images/range-rbegin-rend.svg"
</div>
![Illustration from cppreference.com](../../images/range-rbegin-rend.svg)
## Return value
+1 -3
View File
@@ -7,9 +7,7 @@ const_iterator end() const noexcept;
Returns an iterator to one past the last element.
<div class="nj-fig-wrap" markdown>
--8<-- "images/range-begin-end.svg"
</div>
![Illustration from cppreference.com](../../images/range-begin-end.svg)
## Return value
+12 -4
View File
@@ -32,8 +32,10 @@ The exact mapping and its limitations are described on a [dedicated page](../../
- an `std::istream` object
- a `FILE` pointer
- a C-style array of characters
- a pointer to a null-terminated string of single byte characters
- a C-style array of characters (its terminating NUL is read as trailing data: `parse_error.110` unless `strict` is
`false`)
- a pointer to a null-terminated string of single byte characters (read up to the first 0x00 byte, which is a valid
CBOR byte)
- a container `obj` for which `begin(obj)` and `end(obj)` produce a valid pair of iterators
(as found via ADL or member functions, with semantics compatible to `std::begin` and `std::end`)
@@ -89,8 +91,14 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
- Throws [parse_error.112](../../home/exceptions.md#jsonexceptionparse_error112) if unsupported features from CBOR were
used in the given input or if the input is not valid CBOR
- Throws [parse_error.113](../../home/exceptions.md#jsonexceptionparse_error113) if a map key is not a string (keys of
other types are not supported, as JSON object keys are always strings), or if a string value or object key is not
valid UTF-8 and `error_handler` is `strict`
other types are not supported, as JSON object keys are always strings), if the head of a string or byte string is
malformed (including a chunk of the wrong type or an indefinite-length string nested inside an indefinite-length
string), or if a string value or object key (or one chunk of an indefinite-length string) is not valid UTF-8 and
`error_handler` is `strict`
- Throws [out_of_range.408](../../home/exceptions.md#jsonexceptionout_of_range408) if an array or map announces
a length that `std::size_t` cannot hold, or that equals `std::size_t(-1)` (for example `9B FF FF FF FF FF FF FF FF`)
- Throws [out_of_range.406](../../home/exceptions.md#jsonexceptionout_of_range406) if a floating-point number does not
fit into `number_float_t`
## Complexity
+11 -4
View File
@@ -79,11 +79,18 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
- Throws [parse_error.110](../../home/exceptions.md#jsonexceptionparse_error110) if the given input ends prematurely or
the end of the file was not reached when `strict` was set to true
- Throws [parse_error.112](../../home/exceptions.md#jsonexceptionparse_error112) if a parse error occurs
- Throws [parse_error.112](../../home/exceptions.md#jsonexceptionparse_error112) if an invalid type marker is read
(including a type marker after `$` of an optimized container, even if its count is 0), or if `$` is not followed by `#`
- Throws [parse_error.113](../../home/exceptions.md#jsonexceptionparse_error113) if a string could not be parsed
successfully, or if a string value or object key is not valid UTF-8 and `error_handler` is `strict`
- Throws [out_of_range.408](../../home/exceptions.md#jsonexceptionout_of_range408) if the size of an optimized container
or n-dimensional array cannot be represented by `std::size_t`
successfully (negative or non-integer length, `C` followed by a byte above 0x7F), if the count of an optimized
container is negative, or if a string value or object key is not valid UTF-8 and `error_handler` is `strict`
- Throws [parse_error.115](../../home/exceptions.md#jsonexceptionparse_error115) if the text of a high-precision number
(`H`) is not a valid JSON number
- Throws [out_of_range.406](../../home/exceptions.md#jsonexceptionout_of_range406) if a high-precision number, or a
float64 when `number_float_t` is `float`, does not fit into `number_float_t`
- Throws [out_of_range.408](../../home/exceptions.md#jsonexceptionout_of_range408) if the count of an optimized
container exceeds the capacity of the container (or of `std::size_t`), or if an optimized array of `Z`, `T`, or `F` has
more than 1,048,576 elements
## Complexity
@@ -22,9 +22,7 @@ The parser callback distinguishes the following events:
## Examples
<div class="nj-fig-wrap" markdown>
--8<-- "images/callback_events.svg"
</div>
![Example when certain parse events are triggered](../../images/callback_events.png)
??? example
@@ -23,9 +23,7 @@ table describes the values of the parameters `depth`, `event`, and `parsed`.
| `parse_event_t::array_end` | the parser read `]` and finished processing a JSON array | depth of the parent of the JSON array | the parsed JSON array |
| `parse_event_t::value` | the parser finished reading a JSON value | depth of the value | the parsed JSON value |
<div class="nj-fig-wrap" markdown>
--8<-- "images/callback_events.svg"
</div>
![Example when certain parse events are triggered](../../images/callback_events.png)
Discarding a value (i.e., returning `#!cpp false`) has different effects depending on the context in which function was
called:
+1 -3
View File
@@ -7,9 +7,7 @@ const_reverse_iterator rbegin() const noexcept;
Returns an iterator to the reverse-beginning; that is, the last element.
<div class="nj-fig-wrap" markdown>
--8<-- "images/range-rbegin-rend.svg"
</div>
![Illustration from cppreference.com](../../images/range-rbegin-rend.svg)
## Return value
+1 -3
View File
@@ -8,9 +8,7 @@ const_reverse_iterator rend() const noexcept;
Returns an iterator to the reverse-end; that is, one before the first element. This element acts as a placeholder,
attempting to access it results in undefined behavior.
<div class="nj-fig-wrap" markdown>
--8<-- "images/range-rbegin-rend.svg"
</div>
![Illustration from cppreference.com](../../images/range-rbegin-rend.svg)
## Return value
+2 -1
View File
@@ -42,7 +42,8 @@ The exact mapping and its limitations are described on a [dedicated page](../../
## Exception safety
Strong guarantee: if an exception is thrown, there are no changes in the JSON value.
Strong guarantee: if an exception is thrown, there are no changes in the JSON value. Bytes that were already written to
the output adapter are not taken back.
## Exceptions
@@ -53,7 +53,7 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
value, array, or object exceeds 4294967295, the maximum MessagePack can store; example:
`"MessagePack length 4294967296 exceeds maximum of 4294967295"`
- Throws [`out_of_range.415`](../../home/exceptions.md#jsonexceptionout_of_range415) if the subtype of a binary value
exceeds 255, the maximum of the MessagePack ext type; example:
exceeds 255, the largest value the library can store in the one-byte MessagePack ext type; example:
`"subtype 70000 is too large for the MessagePack ext type (max 255)"`
- Throws [type_error.316](../../home/exceptions.md#jsonexceptiontype_error316) if a string or object key in `j` is
not valid UTF-8 and `error_handler` is `strict`
-739
View File
@@ -1,739 +0,0 @@
/* nlohmann/json brand for Material for MkDocs (values from brand/kit/tokens.css) */
@font-face {
font-family: "Inter";
src: url("fonts/Inter.woff2") format("woff2");
font-weight: 100 900;
font-display: swap;
}
@font-face {
font-family: "Inter";
src: url("fonts/Inter-Italic.woff2") format("woff2");
font-style: italic;
font-weight: 100 900;
font-display: swap;
}
@font-face {
font-family: "JetBrains Mono";
src: url("fonts/JetBrainsMono.woff2") format("woff2");
font-weight: 100 800;
font-display: swap;
}
@font-face {
font-family: "JetBrains Mono";
src: url("fonts/JetBrainsMono-Italic.woff2") format("woff2");
font-style: italic;
font-weight: 400;
font-display: swap;
}
:root {
--md-text-font: "Inter";
--md-code-font: "JetBrains Mono";
}
/* light */
[data-md-color-scheme="default"] {
--nj-blue: #1747a6;
--nj-blue-deep: #10357d;
--nj-blue-soft: #eaf1fd;
--nj-band: #1747a6;
--nj-red: #d93a3f;
--nj-red-soft: #fdeced;
--nj-string: #b4322a;
--nj-comment: #646d7a;
--md-primary-fg-color: var(--nj-band);
--md-primary-fg-color--light: #1d55c4;
--md-primary-fg-color--dark: #10357d;
--md-accent-fg-color: var(--nj-blue-deep);
--md-accent-fg-color--transparent: rgba(23, 71, 166, .1);
--md-typeset-a-color: var(--nj-blue);
--md-default-fg-color: #0f1419;
--md-default-fg-color--light: #4a515c;
--md-default-fg-color--lighter: #8a919c;
--md-default-fg-color--lightest: #e3e6eb;
--md-typeset-color: #0f1419;
--md-code-bg-color: #f5f7fa;
--md-code-fg-color: #0f1419;
}
/* dark */
[data-md-color-scheme="slate"] {
--nj-blue: #7aa7f5;
--nj-blue-deep: #a9c6f8;
--nj-blue-soft: #16233b;
--nj-band: #173e8c;
--nj-red: #f06a6e;
--nj-red-soft: #3a1c1e;
--nj-string: #f08a7e;
--nj-comment: #8b929e;
--md-hue: 220;
--md-primary-fg-color: var(--nj-band);
--md-primary-fg-color--light: #163f93;
--md-primary-fg-color--dark: #0c2556;
--md-accent-fg-color: var(--nj-blue-deep);
--md-accent-fg-color--transparent: rgba(122, 167, 245, .12);
--md-typeset-a-color: var(--nj-blue);
--md-default-bg-color: #0f1218;
--md-default-fg-color: #f2f4f7;
--md-default-fg-color--light: #b3b9c4;
--md-default-fg-color--lighter: #7b828e;
--md-default-fg-color--lightest: #272c36;
--md-typeset-color: #f2f4f7;
--md-code-bg-color: #171b23;
--md-code-fg-color: #f2f4f7;
--md-footer-bg-color: #0b0e13;
--md-footer-bg-color--dark: #08090d;
}
/* syntax colours (all at least 4.5:1 on the code panel in both schemes) */
[data-md-color-scheme="default"] {
--nj-hl-keyword: #1747a6;
--nj-hl-type: #0d6e72;
--nj-hl-function: #6b3fb8;
--nj-hl-string: #23793a;
--nj-hl-escape: #0f5d2a;
--nj-hl-key: #10357d;
--nj-hl-number: #b04a07;
--nj-hl-preproc: #a3247a;
}
[data-md-color-scheme="slate"] {
--nj-hl-keyword: #7aa7f5;
--nj-hl-type: #4fd1c5;
--nj-hl-function: #c4a3ff;
--nj-hl-string: #7ed491;
--nj-hl-escape: #b2ecc0;
--nj-hl-key: #a9c6f8;
--nj-hl-number: #f5a65b;
--nj-hl-preproc: #f48fc8;
}
.md-typeset .highlight :is(.k, .kd, .kn, .kr, .kp, .kv, .ow) { color: var(--nj-hl-keyword); font-weight: 600; }
.md-typeset .highlight :is(.kt, .nc, .nn, .na) { color: var(--nj-hl-type); }
.md-typeset .highlight :is(.nf, .fm, .nb, .bp) { color: var(--nj-hl-function); }
.md-typeset .highlight :is(.s, .s1, .s2, .sb, .sc, .sd, .sh, .sx, .sr, .ss, .sa, .dl, .cpf) { color: var(--nj-hl-string); }
.md-typeset .highlight :is(.se, .si) { color: var(--nj-hl-escape); font-weight: 600; }
.md-typeset .highlight .nt { color: var(--nj-hl-key); }
.md-typeset .highlight :is(.m, .mi, .mf, .mh, .mo, .mb, .il, .kc) { color: var(--nj-hl-number); }
.md-typeset .highlight .kc { font-weight: 500; }
.md-typeset .highlight .cp { color: var(--nj-hl-preproc); }
.md-typeset .highlight :is(.c, .c1, .cm, .ch, .cs) { color: var(--nj-comment); font-style: italic; }
.md-typeset .highlight :is(.o, .p) { color: var(--md-default-fg-color--light); }
.md-typeset .highlight :is(.n, .nx, .nv, .no, .ni, .nl, .py) { color: var(--md-code-fg-color); }
.md-typeset .highlight .err { color: inherit; background: none; }
/* typography: Inter with cv11/ss03, bold headlines with negative tracking */
body {
font-feature-settings: "cv11", "ss03";
}
.md-typeset {
font-size: .85rem; /* 17 px body */
line-height: 1.65;
}
.md-typeset h1 {
color: var(--md-typeset-color);
font-weight: 800;
letter-spacing: -.035em;
line-height: 1.1;
}
.md-typeset h2 {
font-weight: 800;
letter-spacing: -.025em;
line-height: 1.15;
}
.md-typeset h3 {
font-weight: 700;
letter-spacing: -.015em;
}
.md-typeset table:not([class]) {
font-variant-numeric: tabular-nums;
}
/* header and tabs: one blue band with the faint syntax pattern; wordmark instead of the site name.
A veil over the tile keeps the glyphs from running through the navigation text; the hero shows the pattern at full strength. */
.md-header, .md-tabs {
--nj-veil: linear-gradient(color-mix(in srgb, var(--md-primary-fg-color) 62%, transparent), color-mix(in srgb, var(--md-primary-fg-color) 62%, transparent));
background-color: var(--md-primary-fg-color);
background-image: var(--nj-veil), url("pattern-on-blue.svg");
background-size: auto, 160px 160px;
background-position: 0 0, 0 0;
}
.md-tabs {
background-position: 0 0, 0 -48px; /* continue the header's tile */
}
.md-header__button.md-logo img {
width: auto;
height: 1.5rem;
}
.md-header__topic:first-child .md-ellipsis { /* the wordmark replaces the site name at every width */
display: none;
}
@media screen and (max-width: 76.234375em) { /* Material hides the header logo here: show the wordmark on phones, too */
.md-header__button.md-logo {
display: block;
margin: 0 0 0 .2rem;
padding: .4rem .2rem;
}
.md-header__button[for="__drawer"] {
order: -1;
}
}
.md-nav__button.md-logo img {
width: auto;
height: 1.4rem;
}
/* shapes: 6 px corners on blocks, 4 px on inline code */
.md-typeset pre > code, .md-typeset .highlight, .md-typeset .highlighttable,
.md-typeset .admonition, .md-typeset details, .md-typeset .tabbed-set,
.md-typeset .grid.cards > ul > li, .md-typeset .md-button {
border-radius: 6px;
}
.md-typeset code {
border-radius: 4px;
}
/* inline code: the font alone marks it, no grey box; linked code keeps the link colour */
.md-typeset :not(pre) > code {
padding: 0;
background-color: transparent;
box-shadow: none;
color: inherit;
font-size: .88em;
}
.md-typeset .highlight pre > code, .md-typeset .highlighttable {
border: 1px solid var(--md-default-fg-color--lightest);
}
/* callouts: 3 px left border; note/info/tip in blue on blue-soft, danger/bug in red on red-soft */
.md-typeset .admonition, .md-typeset details {
border-width: 0 0 0 3px;
box-shadow: none;
font-size: .8rem;
}
.md-typeset :is(.admonition, details):is(.note, .info, .tip, .abstract, .example, .question) {
border-color: var(--nj-blue);
background-color: var(--nj-blue-soft);
}
.md-typeset :is(.note, .info, .tip, .abstract, .example, .question) > :is(.admonition-title, summary) {
background-color: transparent;
}
.md-typeset :is(.note, .info, .tip, .abstract, .example, .question) > :is(.admonition-title, summary)::before {
background-color: var(--nj-blue);
}
.md-typeset :is(.admonition, details):is(.danger, .bug, .failure) {
border-color: var(--nj-red);
background-color: var(--nj-red-soft);
}
.md-typeset :is(.danger, .bug, .failure) > :is(.admonition-title, summary) {
background-color: transparent;
}
.md-typeset :is(.danger, .bug, .failure) > :is(.admonition-title, summary)::before {
background-color: var(--nj-red);
}
/* buttons */
.md-typeset .md-button--primary {
background-color: var(--nj-blue);
border-color: var(--nj-blue);
}
/* home page hero (overrides/home.html): blue band with the syntax pattern, headline, lead, buttons, code panel */
.nj-hero {
--nj-hero-to: #123a8a;
color: #fff;
background-color: var(--md-primary-fg-color);
background-image: url("pattern-on-blue.svg"), linear-gradient(160deg, var(--md-primary-fg-color), var(--nj-hero-to));
background-size: 160px 160px, auto;
background-position: 0 -96px, 0 0; /* continue the tile of header and tabs */
}
[data-md-color-scheme="slate"] .nj-hero {
--nj-hero-to: #0c2556;
}
.nj-hero__inner {
display: grid;
grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
gap: 2.4rem;
align-items: center;
padding: 3rem .8rem 3.6rem;
}
.nj-hero__kicker {
display: inline-block;
margin: 0;
padding: .15rem .55rem;
border: 1px solid rgba(255, 255, 255, .35);
border-radius: 4px;
color: #bcd3fb;
font-family: var(--md-code-font-family);
font-size: .65rem; /* 13 px */
font-weight: 500;
letter-spacing: .02em;
}
.nj-hero__title {
margin: 0 0 .8rem;
color: #fff;
font-size: clamp(1.7rem, 4.2vw, 3.2rem); /* 34-64 px */
font-weight: 800;
line-height: 1.02;
letter-spacing: -.035em;
}
.nj-hero__title span {
color: #bcd3fb;
}
.nj-hero__lead {
max-width: 22em;
margin: 0 0 2rem;
color: rgba(255, 255, 255, .9);
font-size: clamp(1.1rem, 2.2vw, 1.6rem); /* 22-32 px */
font-style: italic;
font-weight: 500;
line-height: 1.35;
letter-spacing: -.015em;
}
.nj-hero__actions {
display: flex;
flex-wrap: wrap;
gap: .6rem;
margin: 0;
}
.nj-hero .md-button.nj-hero__button {
margin: 0;
padding: .55em 1.3em;
border: 1px solid rgba(255, 255, 255, .6);
border-radius: 6px;
color: #fff;
font-size: .75rem;
font-weight: 600;
}
.nj-hero .md-button--primary.nj-hero__button {
border-color: #fff;
background-color: #fff;
color: var(--md-primary-fg-color);
}
.nj-hero .md-button.nj-hero__button:is(:hover, :focus) {
border-color: #fff;
background-color: rgba(255, 255, 255, .12);
color: #fff;
}
.nj-hero .md-button--primary.nj-hero__button:is(:hover, :focus) {
background-color: #eaf1fd;
color: var(--md-primary-fg-color);
}
.nj-hero__code {
border-radius: 6px;
background-color: var(--md-code-bg-color);
box-shadow: 0 12px 32px rgba(5, 18, 48, .35);
}
.nj-hero__code pre {
margin: 0;
padding: 1.1rem 1.3rem;
overflow-x: auto;
color: var(--md-code-fg-color);
font-family: var(--md-code-font-family);
font-size: .7rem; /* 14 px */
line-height: 1.7;
}
.nj-hero__code .nj-k { color: var(--nj-hl-type); }
.nj-hero__code .nj-s { color: var(--nj-hl-string); }
.nj-hero__code .nj-n { color: var(--nj-hl-number); }
.nj-hero__code .nj-c { color: var(--nj-comment); font-style: italic; }
@media screen and (max-width: 59.984375em) {
.nj-hero__inner {
grid-template-columns: minmax(0, 1fr);
gap: 1.8rem;
padding: 2rem .8rem 2.4rem;
}
}
@media screen and (max-width: 29.984375em) {
.nj-hero__code pre {
font-size: .6rem;
}
}
/* the hero carries the headline: keep the page's h1 for the title and screen readers only */
body:has(.nj-hero) .md-content__inner > h1 {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
clip: rect(0 0 0 0);
white-space: nowrap;
}
/* cards: 1 px line border, 6 px corners, blue icon on soft blue, blue border on hover */
.md-typeset .grid.cards > ul > li {
padding: 1rem 1.1rem;
border: 1px solid var(--md-default-fg-color--lightest);
background-color: var(--md-default-bg-color);
transition: border-color 150ms, box-shadow 150ms, transform 150ms;
}
.md-typeset .grid.cards > ul > li:is(:hover, :focus-within) {
border-color: var(--nj-blue);
box-shadow: 0 6px 18px rgba(23, 71, 166, .12);
transform: translateY(-1px);
}
.md-typeset .grid.cards > ul > li > hr {
display: none;
}
.md-typeset .grid.cards .twemoji.lg {
box-sizing: content-box;
margin-right: .5rem;
padding: .3rem;
border-radius: 6px;
background-color: var(--nj-blue-soft);
color: var(--nj-blue);
}
.md-typeset .grid.cards > ul > li > p:first-child strong {
font-size: .9rem;
font-weight: 700;
letter-spacing: -.01em;
}
.md-typeset .grid.cards > ul > li > p:last-child a {
font-weight: 600;
}
@media (prefers-reduced-motion: reduce) {
.md-typeset .grid.cards > ul > li { transition: none; transform: none !important; }
}
/* home page: no table of contents, so keep the text at reading width and the cards in three columns */
body:has(.nj-hero) .md-content {
max-width: 58rem;
margin: 0 auto;
}
@media screen and (min-width: 60em) {
body:has(.nj-hero) .md-typeset .grid.cards {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
}
/* lists: Material's indent, bullets as small blue squares, numbers in the secondary text colour */
.md-typeset ul:not(.task-list, .md-annotation-list) > li {
position: relative;
list-style: none;
}
.md-typeset ul:not(.task-list, .md-annotation-list) > li::before {
content: "";
position: absolute;
top: .65em; /* middle of the first line at line-height 1.65 */
left: -.95em;
width: .36em;
height: .36em;
background-color: var(--nj-blue);
}
.md-typeset ol > li::marker {
color: var(--md-default-fg-color--light);
font-variant-numeric: tabular-nums;
}
.md-typeset .grid.cards > ul > li::before {
display: none;
}
/* wider page: Material's grid stops at 61rem (1220 px) */
.md-grid {
max-width: 72rem; /* 1440 px */
}
/* tables: full width, 1 px line border with 6 px corners, header row on the panel colour, 15 px text */
.md-typeset__table {
display: block; /* Material: inline-block, so tables only grow to their content */
}
.md-typeset table:not([class]) {
display: table;
width: 100%;
border: 1px solid var(--md-default-fg-color--lightest);
border-radius: 6px;
font-size: .75rem;
line-height: 1.55;
}
.md-typeset table:not([class]) th {
padding: .65em 1em;
border-bottom: 1px solid var(--md-default-fg-color--lightest);
background-color: var(--md-code-bg-color);
color: var(--md-typeset-color);
font-weight: 600;
vertical-align: bottom;
}
.md-typeset table:not([class]) th:first-child { border-top-left-radius: 5px; }
.md-typeset table:not([class]) th:last-child { border-top-right-radius: 5px; }
.md-typeset table:not([class]) td {
padding: .65em 1em;
border-top: 1px solid var(--md-default-fg-color--lightest);
}
.md-typeset table:not([class]) tbody tr:first-child td {
border-top: 0;
}
.md-typeset table:not([class]) tbody tr:hover {
background-color: var(--nj-blue-soft);
box-shadow: none;
}
.md-typeset table:not([class]) td code {
white-space: nowrap;
}
/* numbers and header code stay on one line; a table wider than the screen scrolls sideways instead */
.md-typeset table:not([class]) :is(td, th):is([align="right"], [style*="right"]),
.md-typeset table:not([class]) th code {
white-space: nowrap;
}
@media screen and (max-width: 44.984375em) {
.md-typeset table:not([class]) {
font-size: .7rem;
}
.md-typeset table:not([class]) :is(th, td) {
padding: .55em .75em;
}
}
/* more room before headings (Material: h2 1.6em, h3 1.6em, h4-h6 1-1.25em) and at the end of each page (1.2rem) */
.md-typeset h2 { margin-top: 2.4em; }
.md-typeset h3 { margin-top: 2.2em; }
.md-typeset h2 + h3 { margin-top: .8em; }
.md-typeset :is(h4, h5, h6) { margin-top: 1.8em; }
.md-content__inner {
margin-bottom: 4.8rem; /* 96 px */
}
/* Mermaid diagrams: white boxes with a blue outline, grey lines, Inter; shapes and spacing in js/mermaid-brand.js */
:root > * {
--md-mermaid-font-family: var(--md-text-font-family), sans-serif;
--md-mermaid-edge-color: var(--md-default-fg-color--lighter);
--md-mermaid-node-bg-color: var(--md-default-bg-color);
--md-mermaid-node-fg-color: var(--nj-blue);
--md-mermaid-label-bg-color: var(--md-default-bg-color);
--md-mermaid-label-fg-color: var(--md-default-fg-color);
--md-mermaid-sequence-actor-bg-color: var(--nj-blue-soft);
--md-mermaid-sequence-actor-line-color: var(--md-default-fg-color--lighter);
--md-mermaid-sequence-note-bg-color: var(--md-code-bg-color);
--md-mermaid-sequence-note-border-color: var(--md-default-fg-color--lightest);
--md-mermaid-sequence-number-fg-color: #fff;
}
/* a diagram wider than the column scrolls sideways (js/mermaid-brand.js stops shrinking it at 80 %) */
.md-typeset .mermaid {
overflow-x: auto;
}
/* ---- polish pass ---- */
/* header: a search field that reads as a field on the band, tabs with an active marker, readable GitHub facts */
.md-header .md-search__form {
border: 1px solid rgba(255, 255, 255, .22);
border-radius: 6px;
background-color: rgba(255, 255, 255, .12);
transition: background-color 150ms, border-color 150ms;
}
.md-header .md-search__form:hover {
border-color: rgba(255, 255, 255, .4);
background-color: rgba(255, 255, 255, .2);
}
.md-search__output {
border-radius: 0 0 6px 6px;
box-shadow: 0 16px 40px rgba(5, 18, 48, .28);
}
.md-search-result__link:focus-visible, .md-search-result__more summary:focus-visible {
outline: 2px solid var(--nj-blue);
outline-offset: -2px;
}
.md-tabs__link {
font-size: .72rem;
font-weight: 500;
opacity: .85;
}
.md-tabs__item--active .md-tabs__link, .md-tabs__link:hover {
opacity: 1;
}
.md-tabs__item--active {
box-shadow: inset 0 -2px 0 #fff;
}
.md-source__facts {
font-size: .62rem;
}
/* footer: deep brand blue instead of neutral black (the dark scheme keeps its own near-black) */
[data-md-color-scheme="default"] {
--md-footer-bg-color: #0d2a66;
--md-footer-bg-color--dark: #0a2152;
}
.md-footer-meta .md-copyright, .md-footer-meta .md-copyright a {
color: rgba(255, 255, 255, .72);
}
.md-footer__link {
border-radius: 6px;
transition: background-color 150ms;
}
.md-footer__link:is(:hover, :focus-visible) {
background-color: rgba(255, 255, 255, .08);
}
/* page title: tighter to the first paragraph; page actions (view/edit source) stay in the background */
.md-typeset h1 {
margin-bottom: 1rem;
}
.md-content__button {
opacity: .55;
transition: opacity 150ms;
}
.md-content__button:is(:hover, :focus-visible) {
opacity: 1;
}
body:has(.nj-hero) .md-content__button {
display: none;
}
/* sidebars: bold marker for the current page, rail and marker for the table of contents */
.md-nav__link--active {
font-weight: 600;
}
.md-nav--secondary > .md-nav__list {
margin-left: -.1rem;
border-left: 1px solid color-mix(in srgb, var(--md-default-fg-color) 16%, transparent);
}
.md-nav--secondary .md-nav__link {
margin-left: -1px;
padding-left: .6rem;
border-left: 2px solid transparent;
}
.md-nav--secondary .md-nav__link--active {
border-left-color: var(--nj-blue);
color: var(--nj-blue);
}
.md-nav--secondary .md-nav__list .md-nav__list {
padding-left: .6rem;
}
.md-nav--secondary .md-nav__title {
font-size: .64rem;
letter-spacing: .04em;
text-transform: uppercase;
}
/* keyboard focus: one visible ring in the brand blue */
.md-typeset a:focus-visible, .md-typeset .md-button:focus-visible, .md-typeset summary:focus-visible,
.md-nav__link:focus-visible, .md-tabs__link:focus-visible, .md-top:focus-visible {
outline: 2px solid var(--nj-blue);
outline-offset: 2px;
border-radius: 3px;
}
.md-header :is(a, button, label):focus-visible, .nj-hero a:focus-visible {
outline: 2px solid #fff;
outline-offset: 2px;
}
/* inline code must not stretch the line box (JetBrains Mono has tall metrics), abbreviations stay quiet */
.md-typeset :not(pre) > code {
line-height: 1;
}
.md-typeset abbr {
border-bottom-color: var(--md-default-fg-color--lighter);
}
.md-typeset :is(h1, h2, h3, h4, a, .grid.cards > ul > li > p:first-child) abbr {
border-bottom-color: transparent;
}
/* number railroad diagram: inline SVG (docs/images/json_syntax_number.svg), follows the colour scheme */
.md-typeset .railroad-wrap, .md-typeset .nj-fig-wrap {
overflow-x: auto;
margin: 1.5rem 0;
}
.md-typeset svg.railroad {
display: block;
width: 100%;
min-width: 36rem;
max-width: 52rem;
margin-inline: auto;
color: var(--md-default-fg-color);
font-family: var(--md-text-font-family), sans-serif;
}
.railroad .rr-t { fill: color-mix(in srgb, var(--md-typeset-a-color) 10%, transparent); stroke: var(--md-typeset-a-color); stroke-width: 2; }
.railroad .rr-track, .railroad .rr-n { stroke: color-mix(in srgb, currentColor 60%, var(--md-default-bg-color)); stroke-opacity: 1; } /* opaque, so overlapping segments do not darken */
.railroad .rr-n { fill: color-mix(in srgb, currentColor 6%, transparent); stroke-width: 2; }
.railroad .rr-bg { fill: var(--md-default-bg-color); }
.railroad .rr-arrow { fill: currentColor; fill-opacity: .6; }
.railroad text { fill: currentColor; text-anchor: middle; }
.railroad .rr-c { font-family: var(--md-code-font-family), monospace; font-size: 16px; font-weight: 500; }
.railroad .rr-b { font-size: 14.5px; font-weight: 500; }
.railroad .rr-l { font-size: 14px; text-anchor: start; fill-opacity: .6; }
.railroad .rr-name { font-size: 17px; font-weight: 700; fill-opacity: 1; }
/* figures drawn as inline SVG (docs/images/callback_events.svg, range-*.svg): they follow the colour scheme */
.md-typeset svg.nj-fig {
display: block;
width: 100%;
height: auto;
margin-inline: auto;
color: var(--md-default-fg-color);
font-family: var(--md-text-font-family), sans-serif;
}
.md-typeset svg.nj-fig--range { min-width: 30rem; max-width: 40rem; }
.md-typeset svg.nj-fig--cb { max-width: 30rem; }
.nj-fig { --fg-line: color-mix(in srgb, currentColor 55%, var(--md-default-bg-color)); }
.nj-fig .fg-cell { fill: color-mix(in srgb, var(--md-typeset-a-color) 14%, var(--md-default-bg-color)); stroke: var(--fg-line); stroke-width: 1.5; }
.nj-fig .fg-cell--end { fill: none; stroke-dasharray: 4 3; }
.nj-fig .fg-pill { fill: color-mix(in srgb, var(--md-typeset-a-color) 10%, var(--md-default-bg-color)); stroke: var(--md-typeset-a-color); stroke-width: 1.5; }
.nj-fig .fg-pilltext { fill: var(--md-typeset-a-color); font-family: var(--md-code-font-family), monospace; font-size: 14px; font-weight: 500; text-anchor: middle; }
.nj-fig .fg-arrow { stroke: var(--fg-line); stroke-width: 1.5; fill: none; }
.nj-fig .fg-arrow--dash { stroke-dasharray: 3 3; }
.nj-fig .fg-head { fill: var(--fg-line); }
.nj-fig .fg-note { fill: currentColor; fill-opacity: .7; font-size: 13.5px; text-anchor: middle; }
.nj-fig .fg-note--r { text-anchor: end; }
.nj-fig .fg-note--l { text-anchor: start; }
.nj-fig .fg-h { fill: currentColor; fill-opacity: .7; font-size: 15px; font-weight: 600; }
.nj-fig .fg-code { fill: currentColor; font-family: var(--md-code-font-family), monospace; font-size: 16px; white-space: pre; }
.nj-fig .fg-leg { font-size: 15px; }
.nj-fig { --cb-os: #4eb05a; --cb-oe: #1a6b34; --cb-k: #e5534b; --cb-as: #f0c000; --cb-ae: #b07d00; --cb-v: #1f6fd1; }
[data-md-color-scheme="slate"] .nj-fig { --cb-os: #7ee787; --cb-oe: #2ea043; --cb-k: #ff7b72; --cb-as: #f2cc60; --cb-ae: #c69026; --cb-v: #58a6ff; }
.nj-fig .cb-os { fill: var(--cb-os); }
.nj-fig .cb-oe { fill: var(--cb-oe); }
.nj-fig .cb-k { fill: var(--cb-k); }
.nj-fig .cb-as { fill: var(--cb-as); }
.nj-fig .cb-ae { fill: var(--cb-ae); }
.nj-fig .cb-v { fill: var(--cb-v); }
.md-typeset img[src*="json-light"], .md-typeset img[src*="json-dark"] {
border-radius: 8px;
}
.md-typeset img {
max-width: 100%;
height: auto;
}
/* home page: content uses the same column as every other page */
body:has(.nj-hero) .md-content {
max-width: none;
margin: 0;
}
.md-typeset .grid.cards {
width: auto;
}
/* 404 page (overrides/404.html) */
.nj-404 {
max-width: 30rem;
padding: 2rem 0 3rem;
}
.nj-404__code {
margin: 0 0 .4rem;
color: var(--nj-blue);
font-family: var(--md-code-font-family);
font-size: .8rem;
font-weight: 500;
}
.md-typeset .nj-404 h1 {
margin-bottom: .8rem;
}
.nj-404 .md-button {
margin: .4rem .4rem 0 0;
}
/* touch targets and phones */
@media screen and (max-width: 59.984375em) {
.md-nav__link {
padding-block: .2rem;
}
}
@media screen and (max-width: 29.984375em) {
.md-typeset h1 {
font-size: 1.7em;
}
.md-typeset .highlight pre > code {
font-size: .68rem;
}
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
-1
View File
@@ -1 +0,0 @@
<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><g font-family='Menlo,monospace' font-size='26' font-weight='600' fill='#ffffff' fill-opacity='0.10'><text x='14' y='34'>{</text><text x='92' y='22'>]</text><text x='136' y='60'>:</text><text x='52' y='82'>[</text><text x='118' y='112'>,</text><text x='20' y='140'>}</text><text x='78' y='150'>:</text><text x='140' y='158'>{</text></g></svg>

Before

Width:  |  Height:  |  Size: 408 B

+5 -5
View File
@@ -124,20 +124,20 @@ For all the macros, the first parameter is the name of the class/struct. The `DE
```mermaid
flowchart TD
A["choosing a NLOHMANN_DEFINE_* macro"] --> B{{"adding fields to a base class?"}}
A["choosing a NLOHMANN_DEFINE_* macro"] --> B{"adding fields to a base class?"}
B -->|"yes"| C["...DERIVED_TYPE..."]
B -->|"no"| D["...TYPE..."]
C --> E{{"need access to private members?"}}
C --> E{"need access to private members?"}
D --> E
E -->|"yes"| F["...INTRUSIVE... (used inside the class)"]
E -->|"no"| G["...NON_INTRUSIVE... (used in the namespace)"]
F --> H{{"only serializing, never parsing back?"}}
F --> H{"only serializing, never parsing back?"}
G --> H
H -->|"yes"| I["...ONLY_SERIALIZE"]
H -->|"no"| J{{"allow missing keys when parsing?"}}
H -->|"no"| J{"allow missing keys when parsing?"}
J -->|"yes"| K["...WITH_DEFAULT"]
J -->|"no"| L["plain (missing keys throw)"]
I --> M{{"need custom JSON key names?"}}
I --> M{"need custom JSON key names?"}
K --> M
L --> M
M -->|"yes"| N["...WITH_NAMES"]
@@ -11,7 +11,7 @@ extremely small code sizes, fairly small message size, and extensibility without
## Byte layout
CBOR encodes every data item as a *head* followed by optional content. The first byte of the head holds the *major type* in its upper three bits (unsigned integer, negative integer, byte string, text string, array, map, tag, or simple/float) and an *additional information* value in its lower five bits. Additional information below 24 is the argument itself (for example, a small integer or a short length); the values 24 to 27 announce that the argument follows in 1, 2, 4, or 8 big-endian bytes. Strings, arrays, and maps are prefixed with their length (or count of elements/pairs), and content follows directly, so a reader always knows how many items to expect without a terminator.
CBOR encodes every data item as a *head* followed by optional content. The first byte of the head holds the *major type* in its upper three bits (unsigned integer, negative integer, byte string, text string, array, map, tag, or simple/float) and an *additional information* value in its lower five bits. Additional information below 24 is the argument itself (for example, a small integer or a short length); the values 24 to 27 announce that the argument follows in 1, 2, 4, or 8 big-endian bytes. Strings, arrays, and maps are prefixed with their length (or count of elements/pairs), and content follows directly. A reader of definite-length items knows how many items to expect without a terminator; indefinite-length items (additional information 31) are instead closed by the "break" byte 0xFF, which `from_cbor` reads but `to_cbor` never writes.
Consider the following JSON value (shown formatted for readability):
@@ -52,16 +52,16 @@ The library uses the following mapping from JSON values types to CBOR types acco
| null | `null` | Null | 0xF6 |
| boolean | `true` | True | 0xF5 |
| boolean | `false` | False | 0xF4 |
| number_integer | -9223372036854775808..-2147483649 | Negative integer (8 bytes follow) | 0x3B |
| number_integer | -2147483648..-32769 | Negative integer (4 bytes follow) | 0x3A |
| number_integer | -32768..-129 | Negative integer (2 bytes follow) | 0x39 |
| number_integer | -128..-25 | Negative integer (1 byte follow) | 0x38 |
| number_integer | -9223372036854775808..-4294967297 | Negative integer (8 bytes follow) | 0x3B |
| number_integer | -4294967296..-65537 | Negative integer (4 bytes follow) | 0x3A |
| number_integer | -65536..-257 | Negative integer (2 bytes follow) | 0x39 |
| number_integer | -256..-25 | Negative integer (1 byte follow) | 0x38 |
| number_integer | -24..-1 | Negative integer | 0x20..0x37 |
| number_integer | 0..23 | Integer | 0x00..0x17 |
| number_integer | 24..255 | Unsigned integer (1 byte follow) | 0x18 |
| number_integer | 256..65535 | Unsigned integer (2 bytes follow) | 0x19 |
| number_integer | 65536..4294967295 | Unsigned integer (4 bytes follow) | 0x1A |
| number_integer | 4294967296..18446744073709551615 | Unsigned integer (8 bytes follow) | 0x1B |
| number_integer | 4294967296..9223372036854775807 | Unsigned integer (8 bytes follow) | 0x1B |
| number_unsigned | 0..23 | Integer | 0x00..0x17 |
| number_unsigned | 24..255 | Unsigned integer (1 byte follow) | 0x18 |
| number_unsigned | 256..65535 | Unsigned integer (2 bytes follow) | 0x19 |
@@ -95,7 +95,24 @@ see "binary" cells in the table above.
!!! success "Complete mapping"
The mapping is **complete** in the sense that any JSON value type can be converted to a CBOR value.
The mapping is **complete** in the sense that any JSON value type can be converted to a CBOR value, with one
exception: an integer outside the range of CBOR integers (only possible with a number type wider than 64 bits)
throws [`out_of_range.407`](../../home/exceptions.md#jsonexceptionout_of_range407).
!!! info "Not a deterministic encoding"
`to_cbor` does not produce the deterministic encoding of
[RFC 8949, Section 4.2.1](https://www.rfc-editor.org/rfc/rfc8949.html#section-4.2.1), and not always the preferred
serialization of Section 4.1:
- floating-point numbers are written as single precision (0xFA) when that is exact and as double precision (0xFB)
otherwise; half precision (0xF9) is used only for `NaN` and `Infinity`/`-Infinity` (`1.0` is `fa 3f 80 00 00`);
- a binary subtype 0..23 is written as `d8 xx` instead of the one-byte tag 0xC0..0xD7 (`d8 01 41 01`);
- object members are written in the order of `object_t` (for the default `std::map`: sorted by the key string, not
by the encoded key bytes).
Integers and lengths use the shortest form, and indefinite-length items are never written. Decoders must accept all
of this; the RFC does not require the preferred serialization.
!!! info "NaN/infinity handling"
@@ -105,9 +122,8 @@ see "binary" cells in the table above.
!!! note
Prior to version 3.13.0, NaN and Infinity were instead serialized as a CBOR double-precision float (type 0xFB,
9 bytes total), because the check used to select a smaller encoding compared magnitudes with NaN, which is
always `false` and caused the intended half-precision path to be skipped.
Prior to version 3.8.0, NaN and Infinity were instead serialized as a CBOR double-precision float (type 0xFB,
9 bytes total).
!!! info "Unused CBOR types"
@@ -124,7 +140,6 @@ see "binary" cells in the table above.
- expected conversions (0xD5..0xD7)
- simple values (0xE0..0xF3, 0xF8)
- undefined (0xF7)
- half-precision floats (0xF9)
- break (0xFF)
!!! info "Tagged items"
@@ -226,10 +241,20 @@ string of the same major type, as required by [RFC 8949, Section 3.2.3](https://
([RFC 8392](https://www.rfc-editor.org/rfc/rfc8392.html)), cannot be read with this library and need a
general-purpose CBOR library instead.
A tag in front of a text string key is handled like a tag in front of a value: it is skipped with
`cbor_tag_handler_t::ignore` or `cbor_tag_handler_t::store` and rejected with the default `error`. A tag does not
turn a key of another type into a text string.
A map with duplicate keys is well-formed, but invalid under
[RFC 8949, Section 5.3.1](https://www.rfc-editor.org/rfc/rfc8949.html#section-5.3.1). `from_cbor` accepts it and the
last value wins; the [SAX interface](../parsing/sax_interface.md) reports every occurrence.
!!! warning "Ill-formed UTF-8 in text strings"
[RFC 8949, Section 3.1](https://www.rfc-editor.org/rfc/rfc8949.html#section-3.1) requires CBOR text strings (major
type 3) to be valid UTF-8, but leaves it up to the decoder whether to enforce this, so checking is opt-in: with the
type 3) to be valid UTF-8 (a string with ill-formed UTF-8 is "well-formed but invalid"), and
[Section 5.3.1](https://www.rfc-editor.org/rfc/rfc8949.html#section-5.3.1) leaves it up to the decoder whether to
verify this, so checking is opt-in: with the
[`error_handler`](../../api/basic_json/from_cbor.md) parameter left at `keep` (the default), `from_cbor()` accepts a
text string (object keys included) whose bytes are not valid UTF-8 and hands them back unchanged. Passing
`error_handler_t::strict` makes `from_cbor()` check and throw
@@ -242,9 +267,14 @@ string of the same major type, as required by [RFC 8949, Section 3.2.3](https://
[`JSON_STRICT_BINARY_UTF8`](../../api/macros/json_strict_binary_utf8.md) is enabled), it throws the same exception
instead. Byte strings (major type 2) are unaffected, since they are not required to hold text.
The chunks of an indefinite-length text string are checked one by one, as required by
[RFC 8949, Section 3.2.3](https://www.rfc-editor.org/rfc/rfc8949.html#section-3.2.3): a code point cannot be spread
over two chunks. With `error_handler_t::strict` such a string throws `parse_error.113`; `replace` and `ignore`
sanitize each chunk on its own.
!!! warning "Tagged items"
Tagged items (0xC0..0xDB) will throw a parse error by default. They can be ignored by passing `cbor_tag_handler_t::ignore` to function `from_cbor`, in which case the tag is skipped and the enclosed data item is parsed on its own. Passing `cbor_tag_handler_t::store` to function `from_cbor` stores tagged byte strings (for bytes 0xd8..0xdb) as binary values with the tag as subtype; other tagged values are read as if the tag were ignored. If several tags precede a byte string, only the innermost one is stored. Note that no tag is ever interpreted: for instance, a text string tagged with tag 0 (date/time) stays a string.
Tagged items (0xC0..0xDB) will throw a parse error by default. They can be ignored by passing `cbor_tag_handler_t::ignore` to function `from_cbor`, in which case the tag is skipped and the enclosed data item is parsed on its own. Passing `cbor_tag_handler_t::store` to function `from_cbor` stores tagged byte strings (for bytes 0xd8..0xdb) as binary values with the tag as subtype; other tagged values are read as if the tag were ignored. If several tags precede a byte string, only the tag directly in front of it is stored, and only when it has the form 0xD8..0xDB (a tag 0..23 written as 0xC0..0xD7 is skipped); for example, `D8 05 C1 41 AA` yields subtype `null`, whereas `C1 D8 05 41 AA` yields 5. Tags in front of a text string map key are skipped as well. Note that no tag is ever interpreted: for instance, a text string tagged with tag 0 (date/time) stays a string.
??? example "Example: deserialize a JSON value from CBOR"
@@ -22,7 +22,7 @@ to efficiently encode JSON values to byte vectors and to decode such vectors.
| BON8 | incomplete: no unsigned integers above int64 | complete |
| BSON | incomplete: top-level value must be an object | incomplete, but all JSON types are supported |
| CBOR | complete | incomplete, but all JSON types are supported |
| MessagePack | complete | complete |
| MessagePack | complete | incomplete, but all JSON types are supported |
| UBJSON | complete | complete |
### Binary values
@@ -11,7 +11,7 @@ extra byte in addition to the strings themselves.
## Byte layout
MessagePack encodes each value as a one-byte *format marker*, optionally followed by a length or the payload. The marker range is partitioned to keep common values small: small non-negative integers (0 to 127) and small negative integers (-32 to -1) are the marker byte itself; short strings, arrays, and maps (up to 31 or 15 elements) hide their length in the marker's lower bits. Larger values use dedicated markers that announce the width (8, 16, 32, or 64 bits) of the length or number that follows, in big-endian byte order. Like CBOR, containers are length-prefixed, so no terminator is needed.
MessagePack encodes each value as a one-byte *format marker*, optionally followed by a length or the payload. The marker range is partitioned to keep common values small: small non-negative integers (0 to 127) and small negative integers (-32 to -1) are the marker byte itself; short strings, arrays, and maps (up to 31 bytes for strings and up to 15 elements for arrays and maps) hide their length in the marker's lower bits. Larger values use dedicated markers that announce the width (8, 16, 32, or 64 bits) of the length or number that follows, in big-endian byte order. Like CBOR, containers are length-prefixed, so no terminator is needed.
Consider the following JSON value (shown formatted for readability):
@@ -109,8 +109,9 @@ specification:
!!! note
Prior to version 3.13.0, NaN and Infinity were instead serialized as a MessagePack float 64 (type 0xCB, 9 bytes
total), because the check used to select the smaller float 32 encoding compared magnitudes with NaN, which is
always `false` and caused the float 32 path to be skipped.
total), because the check used to select the smaller float 32 encoding failed for NaN (all comparisons are `false`)
and for Infinity and -Infinity (outside the float range checked against `std::numeric_limits<float>::lowest()` and
`max()`), so the float 32 path was skipped.
??? example "Example: serialize a JSON value to MessagePack"
@@ -135,6 +136,7 @@ The library maps MessagePack types to JSON value types as follows:
| fixarray | array | 0x90..0x9F |
| fixstr | string | 0xA0..0xBF |
| nil | `null` | 0xC0 |
| (never used) | – | 0xC1 |
| false | `false` | 0xC2 |
| true | `true` | 0xC3 |
| float 32 | number_float | 0xCA |
@@ -167,6 +169,30 @@ The library maps MessagePack types to JSON value types as follows:
| fixext 16 | binary | 0xD8 |
| negative fixint | number_integer | 0xE0-0xFF |
The byte 0xC1 is reserved as "never used" by the specification. As a value it is rejected with
[`parse_error.112`](../../home/exceptions.md#jsonexceptionparse_error112) (`invalid byte: 0xC1`); as a map key it is
rejected with [`parse_error.113`](../../home/exceptions.md#jsonexceptionparse_error113) and the generic "expected length
specification" message, because the key is read as a string.
Duplicate keys in a map are accepted; the last value wins.
### Extension types (ext, fixext)
MessagePack ext and fixext values are read as binary values, and the ext type byte is stored as the binary subtype.
Although the specification defines the type as a signed 8-bit integer (0 to 127 for applications, -128 to -1 reserved,
with -1 being the Timestamp extension), the library reads it as an unsigned byte: a negative ext type `n` becomes
subtype `256+n`, so ext type -1 is read as subtype 255 and -128 as subtype 128. The Timestamp extension is not
interpreted; it is returned as binary with subtype 255. Use
[`has_subtype()`](../../api/byte_container_with_subtype/has_subtype.md) to tell a `bin` value (no subtype) from an ext
value (subtype, even if it is 0).
When serializing, a binary value with a subtype is always written as fixext (1, 2, 4, 8, or 16 bytes) or ext, even
for subtype 0 (`d6 00 01 02 03 04`); a binary value without a subtype is written as bin. Subtypes 128 to 255 are
written as the reserved negative ext types (255 as -1), so `json::binary({1,2,3,4}, 255)` becomes
`d6 ff 01 02 03 04`, which other MessagePack libraries decode as a 32-bit timestamp. A subtype above 255 throws
[`out_of_range.415`](../../home/exceptions.md#jsonexceptionout_of_range415). The payload length is limited to
4294967295 bytes like all other lengths.
!!! info
Any MessagePack output created by `to_msgpack` can be successfully parsed by `from_msgpack`.
@@ -177,7 +203,8 @@ The library maps MessagePack types to JSON value types as follows:
JSON-compatible [profile](https://github.com/msgpack/msgpack/blob/master/spec.md#profile) sketched in the
MessagePack specification, this library restricts map keys to `str` values. Maps with keys of any other type are
rejected with a [`parse_error.113`](../../home/exceptions.md#jsonexceptionparse_error113) exception (or, with
`allow_exceptions` set to `false`, a discarded value) naming the type of the key that was found, for instance:
`allow_exceptions` set to `false`, a discarded value). For most key types the message names the type that was
found, for instance:
```
[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing MessagePack object key: only string keys are supported, but found nil; last byte: 0xC0
@@ -189,7 +216,7 @@ The library maps MessagePack types to JSON value types as follows:
!!! warning "Ill-formed UTF-8 in string values"
The MessagePack specification explicitly allows a `str` value (`fixstr`, `str 8`, `str 16`, `str 32`) to contain
a byte sequence that is not valid UTF-8, and expects a deserializer to hand the original bytes back unchanged.
a byte sequence that is not valid UTF-8, and recommends that a deserializer provide a way to obtain the original bytes.
This library follows that by default: with its
[`error_handler`](../../api/basic_json/from_msgpack.md) parameter left at `keep` (the default),
`from_msgpack()` reads `str` bytes (object keys included) as-is, without validating them, so such a value
@@ -5,11 +5,11 @@ achieve the generality of JSON, combined with being much easier to process than
!!! abstract "References"
- [UBJSON Website](http://ubjson.org)
- [UBJSON Website](https://ubjson.org)
## Byte layout
UBJSON is built from just a one-byte, human-readable ASCII *type marker* (for example, `i` for int8, `S` for string, `[` for array) followed by the payload of that type. Strings and the like are prefixed with their length, which is itself a UBJSON integer value including its marker. Numbers are stored in big-endian byte order. Containers can be written in two ways: as an *unoptimized* container that is opened with `[` or `{` and closed with `]` or `}`, or as an *optimized* container that declares an element type (`$`) and/or a count (`#`) after the opening marker, which saves per-element markers and (for a count) the closing marker.
UBJSON is built from just a one-byte, human-readable ASCII *type marker* (for example, `i` for int8, `S` for string, `[` for array) followed by the payload of that type. Strings and the like are prefixed with their length, which is itself a UBJSON integer value including its marker. Numbers are stored in big-endian byte order. Containers can be written in two ways: as an *unoptimized* container that is opened with `[` or `{` and closed with `]` or `}`, or as an *optimized* container that declares a count (`#`) after the opening marker, optionally preceded by an element type (`$`); an element type is only allowed together with a count. An optimized container saves the closing marker and, with a type, the per-element markers.
Consider the following JSON value (shown formatted for readability):
@@ -44,29 +44,29 @@ Consider the following JSON value (shown formatted for readability):
The library uses the following mapping from JSON values types to UBJSON types according to the UBJSON specification:
| JSON value type | value/range | UBJSON type | marker |
|-----------------|-------------------------------------------|----------------|--------|
| null | `null` | null | `Z` |
| boolean | `true` | true | `T` |
| boolean | `false` | false | `F` |
| number_integer | -9223372036854775808..-2147483649 | int64 | `L` |
| number_integer | -2147483648..-32769 | int32 | `l` |
| number_integer | -32768..-129 | int16 | `I` |
| number_integer | -128..127 | int8 | `i` |
| number_integer | 128..255 | uint8 | `U` |
| number_integer | 256..32767 | int16 | `I` |
| number_integer | 32768..2147483647 | int32 | `l` |
| number_integer | 2147483648..9223372036854775807 | int64 | `L` |
| number_unsigned | 0..127 | int8 | `i` |
| number_unsigned | 128..255 | uint8 | `U` |
| number_unsigned | 256..32767 | int16 | `I` |
| number_unsigned | 32768..2147483647 | int32 | `l` |
| number_unsigned | 2147483648..9223372036854775807 | int64 | `L` |
| number_unsigned | 9223372036854775808..18446744073709551615 | high-precision | `H` |
| number_float | *any value* | float64 | `D` |
| string | *with shortest length indicator* | string | `S` |
| array | *see notes on optimized format* | array | `[` |
| object | *see notes on optimized format* | map | `{` |
| JSON value type | value/range | UBJSON type | marker |
|-----------------|-------------------------------------------|--------------------------------------------------|-----------|
| null | `null` | null | `Z` |
| boolean | `true` | true | `T` |
| boolean | `false` | false | `F` |
| number_integer | -9223372036854775808..-2147483649 | int64 | `L` |
| number_integer | -2147483648..-32769 | int32 | `l` |
| number_integer | -32768..-129 | int16 | `I` |
| number_integer | -128..127 | int8 | `i` |
| number_integer | 128..255 | uint8 | `U` |
| number_integer | 256..32767 | int16 | `I` |
| number_integer | 32768..2147483647 | int32 | `l` |
| number_integer | 2147483648..9223372036854775807 | int64 | `L` |
| number_unsigned | 0..127 | int8 | `i` |
| number_unsigned | 128..255 | uint8 | `U` |
| number_unsigned | 256..32767 | int16 | `I` |
| number_unsigned | 32768..2147483647 | int32 | `l` |
| number_unsigned | 2147483648..9223372036854775807 | int64 | `L` |
| number_unsigned | 9223372036854775808..18446744073709551615 | high-precision | `H` |
| number_float | *any value* | float64 (float32 if `number_float_t` is `float`) | `D` (`d`) |
| string | *with shortest length indicator* | string | `S` |
| array | *see notes on optimized format* | array | `[` |
| object | *see notes on optimized format* | object | `{` |
!!! success "Complete mapping"
@@ -93,20 +93,27 @@ The library uses the following mapping from JSON values types to UBJSON types ac
The following markers are not used in the conversion:
- `Z`: no-op values are not created.
- `N`: no-op values are never written (they are skipped when reading).
- `C`: single-byte strings are serialized with `S` markers.
!!! info "NaN/infinity handling"
If NaN or Infinity are stored inside a JSON number, they are serialized properly. This behavior differs from the
[`dump()`](../../api/basic_json/dump.md) function which serializes NaN or Infinity to `null`.
NaN and infinity are written as IEEE 754 float64 values (`D`) and read back unchanged. This differs from
[`dump()`](../../api/basic_json/dump.md), which serializes them to `null`, and from the UBJSON specification, which
encodes infinity as `null` (it does not mention NaN); other UBJSON readers may therefore not accept these values.
!!! info "Optimized formats"
The optimized formats for containers are supported: Parameter `use_size` adds size information to the beginning of a
container and removes the closing marker. Parameter `use_type` further checks whether all elements of a container
have the same type and adds the type marker to the beginning of the container. The `use_type` parameter must only be
used together with `use_size = true`.
used together with `use_size = true`: combining `use_type` with `use_size = false` throws
[`other_error.502`](../../home/exceptions.md#jsonexceptionother_error502) if the value contains a non-empty array,
object, or binary value.
Two elements have the same type if they are written with the same type marker. For example, `[1, 2]` is written with
the type `i`, but `[1, 200]` (`i`, `U`), `[true, false]` (`T`, `F`), and `[1, 2.5]` (`i`, `D`) are not; an empty
container is never given a `$` annotation of its own (an array of empty arrays is still typed `[`).
Note that `use_size = true` alone may result in larger representations - the benefit of this parameter is that the
receiving side is immediately informed on the number of elements of the container.
@@ -115,8 +122,9 @@ The library uses the following mapping from JSON values types to UBJSON types ac
already is the value. Its declared count is therefore the only thing that decides how much memory the receiving side
allocates, and a handful of bytes can describe billions of elements. `from_ubjson` rejects such an array with
[`out_of_range.408`](../../home/exceptions.md#jsonexceptionout_of_range408) when the count exceeds 1,048,576
(`1 << 20`), and `to_ubjson` writes longer arrays of these types without the annotation, so any value it produces
can be read back.
(`1 << 20`), and `to_ubjson` writes longer arrays of these types in the counted but untyped form: the `$` type marker is
dropped, the `#` count is kept, and every element is written with its own marker (`[#l 1048577 Z Z ...`), so any value
it produces can be read back. Objects and untyped counted arrays are not subject to this limit.
!!! info "Binary values"
@@ -140,27 +148,54 @@ The library uses the following mapping from JSON values types to UBJSON types ac
The library maps UBJSON types to JSON value types as follows:
| UBJSON type | JSON value type | marker |
|-------------|-----------------------------------------|--------|
| no-op | *no value, next value is read* | `N` |
| null | `null` | `Z` |
| false | `false` | `F` |
| true | `true` | `T` |
| float32 | number_float | `d` |
| float64 | number_float | `D` |
| uint8 | number_unsigned | `U` |
| int8 | number_integer | `i` |
| int16 | number_integer | `I` |
| int32 | number_integer | `l` |
| int64 | number_integer | `L` |
| string | string | `S` |
| char | string | `C` |
| array | array (optimized values are supported) | `[` |
| object | object (optimized values are supported) | `{` |
| UBJSON type | JSON value type | marker |
|----------------|--------------------------------------------------|--------|
| no-op | *no value, next value is read* | `N` |
| null | `null` | `Z` |
| false | `false` | `F` |
| true | `true` | `T` |
| float32 | number_float | `d` |
| float64 | number_float | `D` |
| uint8 | number_unsigned | `U` |
| int8 | number_integer | `i` |
| int16 | number_integer | `I` |
| int32 | number_integer | `l` |
| int64 | number_integer | `L` |
| string | string | `S` |
| char | string | `C` |
| high-precision | number_unsigned, number_integer, or number_float | `H` |
| array | array (optimized values are supported) | `[` |
| object | object (optimized values are supported) | `{` |
!!! info "High-precision numbers"
The payload of `H` is a length-prefixed string that is parsed as a JSON number (surrounding whitespace is ignored).
Integer text in the range of `number_unsigned_t` (non-negative) or `number_integer_t` (negative) is stored as such;
any other valid number text (larger or smaller integers, fractions, and exponents such as `1e2`) is stored as
`number_float`, so precision may be lost. Text that is not a valid JSON number raises
[`parse_error.115`](../../home/exceptions.md#jsonexceptionparse_error115), a number outside the range of
`number_float_t` (`1e999`) raises [`out_of_range.406`](../../home/exceptions.md#jsonexceptionout_of_range406).
!!! success "Complete mapping"
The mapping is **complete** in the sense that any UBJSON value can be converted to a JSON value.
Every well-formed UBJSON value can be converted to a JSON value, with these exceptions: a high-precision number
outside the range of `number_float_t` ([`out_of_range.406`](../../home/exceptions.md#jsonexceptionout_of_range406)),
an optimized array of `Z`, `T`, or `F` with more than 1,048,576 elements, and a count that exceeds the capacity of the
container ([`out_of_range.408`](../../home/exceptions.md#jsonexceptionout_of_range408)). High-precision numbers that
do not fit an integer type are stored as floating-point numbers and may lose precision. Ill-formed input is rejected
with a parse error.
!!! info "Duplicate object keys"
If an object contains the same key more than once, the last value is kept (the UBJSON specification does not
address duplicate keys).
!!! info "Type marker of optimized containers"
The type marker after `$` must be one of `Z`, `N`, `T`, `F`, `i`, `U`, `I`, `l`, `L`, `d`, `D`, `H`, `C`, `S`, `[`, or
`{`. It is checked even if the count is 0; any other marker raises
[`parse_error.112`](../../home/exceptions.md#jsonexceptionparse_error112). A counted object typed `N` (`{$N#i1 ...`)
is not supported.
!!! warning "Ill-formed UTF-8 in string values and object keys"
+14 -2
View File
@@ -301,6 +301,8 @@ representation using the length of the byte array.
[`json::from_cbor`](../api/basic_json/from_cbor.md) (see
[`cbor_tag_handler_t`](../api/basic_json/cbor_tag_handler_t.md)).
With `ignore`, the subtype is dropped (`"subtype": null`); with `store`, the tag is kept (`"subtype": 42`):
```json
{
"binary": {
@@ -310,11 +312,21 @@ representation using the length of the byte array.
}
```
```json
{
"binary": {
"bytes": [202, 254, 186, 190],
"subtype": 42
}
}
```
### MessagePack
[MessagePack](binary_formats/messagepack.md) supports binary values and subtypes. If a subtype is given, the ext family
is used. The library will choose the smallest representation among fixext1, fixext2, fixext4, fixext8, ext8, ext16, and
ext32. The subtype is then added as a signed 8-bit integer.
is used. The library will choose the smallest representation among fixext1, fixext2, fixext4, fixext8, fixext16, ext8,
ext16, and ext32. The subtype is then written as the signed 8-bit ext type: subtypes 128 to 255 map to the negative
(reserved) ext types, and on reading the ext type is exposed as an unsigned subtype from 0 to 255.
If no subtype is given, the bin family (bin8, bin16, bin32) is used.
@@ -14,11 +14,11 @@ the value, or `end()` if it is not found).
```mermaid
flowchart TD
A["accessing a value"] --> B{{"must it exist?"}}
A["accessing a value"] --> B{"must it exist?"}
B -->|"yes, missing is an error"| C["at() -- throws"]
B -->|"yes, but checking is my job"| D["operator[] -- unchecked"]
B -->|"no, a fallback is fine"| E["value() -- default value"]
A --> F{{"just testing first?"}}
A --> F{"just testing first?"}
F -->|"yes"| G["contains() / find()"]
```
+2 -6
View File
@@ -9,9 +9,7 @@ As for other containers, [`begin()`](../api/basic_json/begin.md) returns an iter
placeholder and cannot be dereferenced. In case of null values, empty arrays, or empty objects, `begin()` will return
`end()`.
<div class="nj-fig-wrap" markdown>
--8<-- "images/range-begin-end.svg"
</div>
![Illustration from cppreference.com](../images/range-begin-end.svg)
### Iteration order for objects
@@ -114,9 +112,7 @@ for (auto& [key, val] : j_object.items())
[`rbegin()`](../api/basic_json/rbegin.md) and [`rend()`](../api/basic_json/rend.md) return iterators in the reverse
sequence.
<div class="nj-fig-wrap" markdown>
--8<-- "images/range-rbegin-rend.svg"
</div>
![Illustration from cppreference.com](../images/range-rbegin-rend.svg)
??? example "Example: reverse iteration with `rbegin()` and `rend()`"
@@ -27,9 +27,7 @@ JSON defines the syntax of numbers as follows:
The following railroad diagram from [json.org](https://json.org) visualizes the number syntax:
<div class="railroad-wrap" markdown>
--8<-- "images/json_syntax_number.svg"
</div>
![Syntax for JSON numbers](../../images/json_syntax_number.png)
### Number interoperability
@@ -71,9 +69,9 @@ otherwise, it uses unsigned integer storage.
```mermaid
flowchart TD
A["number literal"] --> B{{"has a fraction (.) or exponent (e/E)?"}}
A["number literal"] --> B{"has a fraction (.) or exponent (e/E)?"}
B -->|"yes"| F["number_float_t"]
B -->|"no"| C{{"has a leading minus sign?"}}
B -->|"no"| C{"has a leading minus sign?"}
C -->|"yes"| D["try number_integer_t"]
C -->|"no"| E["try number_unsigned_t"]
D -->|"overflow"| F
@@ -368,8 +368,7 @@ using array_t = ArrayType<basic_json, AllocatorType<basic_json>>;
`#!cpp std::from_chars` is not available or declines the token, and `std::strtold` reads up to the null character. A type whose `data()`
is not null-terminated does not fail to compile; with such a `NumberFloatType` it can silently misparse
floating-point numbers.
- `append(const char*, size_type)`, used by [`dump`](../../api/basic_json/dump.md), and `append(const StringType&)`,
used by the CBOR reader for indefinite-length strings. The library's internal string concatenation additionally has
- `append(const char*, size_type)`, used by [`dump`](../../api/basic_json/dump.md) and by the binary readers. The library's internal string concatenation additionally has
to append a `#!cpp char` and a `#!cpp const char*`; for each it selects between `append(arg)`, `#!cpp operator+=`,
`append(first, last)`, and `append(data, size)`.
- The comparison operator `==` against another `StringType`, and `<` for use as a key of the chosen
@@ -678,8 +677,7 @@ using binary_t = nlohmann::byte_container_with_subtype<BinaryType>;
[`StringType`](#stringtype), whose non-`#!cpp const` `data()` is C++17 and later only.
- Default-constructible, copy-constructible, and move-constructible.
- Member functions `size()`, `empty()`, `data()`, `resize()`, `operator[]`, `back()`, `begin()`, `end()`, `cbegin()`,
and `cend()` with random-access iterators, and `insert(pos, first, last)`, which the CBOR reader uses to join the
chunks of an indefinite-length byte string. `push_back()` is **not** required.
and `cend()` with random-access iterators. `push_back()` is **not** required.
- Comparison operators: `==` is used by
[`byte_container_with_subtype`](../../api/byte_container_with_subtype/index.md), the relational operators by
[`basic_json`'s comparison operators](../../api/basic_json/operator_le.md).
+6 -5
View File
@@ -330,7 +330,7 @@ where they cannot be resolved.
### json.exception.parse_error.110
When parsing a [binary format](../features/binary_formats/index.md), the byte vector ends before the complete value has
been read.
been read, or (with `strict = true`) further bytes follow the value.
!!! failure "Example messages"
@@ -1045,8 +1045,8 @@ or the encoded length for [CBOR](../features/binary_formats/cbor.md).
The exception is also thrown for a [UBJSON](../features/binary_formats/ubjson.md) array of a type that is encoded by its
marker alone (`Z`, `T` or `F`) whose declared count exceeds 1,048,576 (`1 << 20`). Such an array has no payload, so its
count alone decides how much memory is allocated, and a handful of bytes would otherwise describe billions of values.
[`to_ubjson`](../api/basic_json/to_ubjson.md) writes longer arrays of these types without the size and type annotation,
so any value it produces can still be read back.
[`to_ubjson`](../api/basic_json/to_ubjson.md) writes longer arrays of these types without the `$` type annotation (the `#`
count and one marker per element are kept), so any value it produces can still be read back.
!!! failure "Example messages"
@@ -1153,7 +1153,7 @@ A JSON Patch `move` operation's `"from"` location is a proper prefix of its `"pa
### json.exception.out_of_range.415
MessagePack's ext type and BSON's binary subtype are each stored in a single byte. This exception is thrown when serializing a
[`byte_container_with_subtype`](../api/byte_container_with_subtype/index.md) whose subtype exceeds 255.
[`byte_container_with_subtype`](../api/byte_container_with_subtype/index.md) whose subtype exceeds 255, the largest value the library can store in that byte.
!!! failure "Example message"
@@ -1229,7 +1229,8 @@ A JSON Patch operation 'test' failed. The unsuccessful operation is also printed
### json.exception.other_error.502
[`to_ubjson`](../api/basic_json/to_ubjson.md) and [`to_bjdata`](../api/basic_json/to_bjdata.md) were called with
`use_type = true` but `use_size = false`. UBJSON requires a size marker (`#`) after a type marker (`$`).
`use_type = true` but `use_size = false` for a value that contains a non-empty array, object, or binary value. UBJSON
requires a size marker (`#`) after a type marker (`$`); scalars and empty containers do not throw.
!!! failure "Example message"
Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

@@ -1,39 +0,0 @@
<svg class="nj-fig nj-fig--cb" viewBox="0 0 480 262" role="img" aria-labelledby="fig-cb-title" xmlns="http://www.w3.org/2000/svg">
<title id="fig-cb-title">Example of the parse events a callback sees for the J&#8203;SON text {&quot;name&quot;: &quot;Berlin&quot;, &quot;location&quot;: [52.519444, 13.406667]}: object_start after the opening brace, key after each key, array_start after the opening bracket, value after each value, array_end after the closing bracket, and object_end after the closing brace. Every array and object also produces a value event when it is complete.</title>
<text class="fg-h" x="16" y="28">J&#8203;SON to parse</text>
<text class="fg-h" x="320" y="28">callback events</text>
<text class="fg-code" x="16" y="74" xml:space="preserve">{</text>
<circle class="cb-os" cx="35.6" cy="69" r="5.5"/>
<text class="fg-code" x="35.2" y="102" xml:space="preserve">&quot;name&quot;</text>
<circle class="cb-k" cx="102.8" cy="97" r="5.5"/>
<text class="fg-code" x="112.8" y="102" xml:space="preserve">: &quot;Berlin&quot;</text>
<circle class="cb-v" cx="218.8" cy="97" r="5.5"/>
<text class="fg-code" x="228.8" y="102" xml:space="preserve">,</text>
<text class="fg-code" x="35.2" y="130" xml:space="preserve">&quot;location&quot;</text>
<circle class="cb-k" cx="141.2" cy="125" r="5.5"/>
<text class="fg-code" x="151.2" y="130" xml:space="preserve">: [</text>
<circle class="cb-as" cx="190" cy="125" r="5.5"/>
<text class="fg-code" x="54.4" y="158" xml:space="preserve">52.519444</text>
<circle class="cb-v" cx="150.8" cy="153" r="5.5"/>
<text class="fg-code" x="160.8" y="158" xml:space="preserve">,</text>
<text class="fg-code" x="54.4" y="186" xml:space="preserve">13.406667</text>
<circle class="cb-v" cx="150.8" cy="181" r="5.5"/>
<text class="fg-code" x="35.2" y="214" xml:space="preserve">]</text>
<circle class="cb-ae" cx="54.8" cy="209" r="5.5"/>
<circle class="cb-v" cx="74.8" cy="209" r="5.5"/>
<text class="fg-code" x="16" y="242" xml:space="preserve">}</text>
<circle class="cb-oe" cx="35.6" cy="237" r="5.5"/>
<circle class="cb-v" cx="55.6" cy="237" r="5.5"/>
<circle class="cb-os" cx="327" cy="69" r="6"/>
<text class="fg-code fg-leg" x="344" y="74">object_start</text>
<circle class="cb-k" cx="327" cy="99" r="6"/>
<text class="fg-code fg-leg" x="344" y="104">key</text>
<circle class="cb-oe" cx="327" cy="129" r="6"/>
<text class="fg-code fg-leg" x="344" y="134">object_end</text>
<circle class="cb-as" cx="327" cy="159" r="6"/>
<text class="fg-code fg-leg" x="344" y="164">array_start</text>
<circle class="cb-ae" cx="327" cy="189" r="6"/>
<text class="fg-code fg-leg" x="344" y="194">array_end</text>
<circle class="cb-v" cx="327" cy="219" r="6"/>
<text class="fg-code fg-leg" x="344" y="224">value</text>
</svg>

Before

Width:  |  Height:  |  Size: 2.7 KiB

-1
View File
@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1200 675" width="1200" height="675" role="img"><title>JSON for Modern C++: feature slideshow (animated, 20 slides, 15 s each)</title><style>.bg{fill:#fff} .tx{fill:#0f1419} .tx2{fill:#3d444f} .tx3{fill:#8a919c} .com{fill:#646d7a}.bl{fill:#1747a6} .blk{fill:#1747a6} .kw{fill:#1747a6} .ty{fill:#0d6e72} .fn{fill:#6b3fb8} .str{fill:#23793a} .num{fill:#b04a07} .pp{fill:#a3247a}.panel{fill:#f5f7fa;stroke:#e3e6eb;stroke-width:1} .rule{stroke:#e3e6eb;stroke-width:1}.wm-a{fill:#0f1419} .wm-s{fill:#1747a6}.on-a,.on-tx{fill:#fff} .red{fill:#d93a3f} .on-panel{fill:#fff;fill-opacity:.08;stroke:#fff;stroke-opacity:.16} .on-s{fill:#bcd3fb} .w70{fill:#fff;fill-opacity:.7} .on-tx2{fill:#dbe6fb} .chip{fill:#fff;fill-opacity:.14}.pat{fill:#fff;fill-opacity:.09}.h1{stop-color:#1d55c4} .h2{stop-color:#123a8a}.seg{fill:#e3e6eb} .seg-on{fill:#1747a6}.seg.on-band{fill:#fff;fill-opacity:.25} .seg-on.on-band{fill:#fff;fill-opacity:1}.bg{fill:#0f1218} .tx,.wm-a{fill:#f2f4f7} .tx2{fill:#c3c8d1} .tx3{fill:#7b828e} .com{fill:#8b929e}.bl,.blk,.wm-s,.seg-on,.kw{fill:#7aa7f5} .ty{fill:#4fd1c5} .fn{fill:#c4a3ff} .str{fill:#7ed491} .num{fill:#f5a65b} .pp{fill:#f48fc8}.panel{fill:#171b23;stroke:#272c36} .rule{stroke:#272c36} .seg{fill:#272c36}.red{fill:#c8383d} .h1{stop-color:#163f93} .h2{stop-color:#0c2556}.s{opacity:0;animation:show 300s linear infinite backwards}.fill{transform-box:fill-box;transform-origin:left;animation:fill 300s linear infinite backwards;animation-delay:inherit}.s0{animation-name:show0} .sl{animation-name:showl}@keyframes show{0%{opacity:0;animation-timing-function:cubic-bezier(.2,.6,.2,1)} 0.2000%{opacity:1} 5.2000%{opacity:1} 5.2100%{opacity:0} 100%{opacity:0}}@keyframes show0{0%{opacity:1} 5.2000%{opacity:1} 5.2100%{opacity:0} 100%{opacity:0}}@keyframes showl{0%{opacity:0;animation-timing-function:cubic-bezier(.2,.6,.2,1)} 0.2000%{opacity:1} 5.0000%{opacity:1} 5.2000%{opacity:0} 100%{opacity:0}}@keyframes fill{0%{transform:scaleX(0)} 5.0000%{transform:scaleX(1)} 100%{transform:scaleX(1)}}@media (prefers-reduced-motion:reduce){.s,.fill{animation:none} .s:first-of-type{opacity:1}}</style><defs><path id="g0" d="M233 0 46 730H148L269 238Q281 191 289 148Q298 105 302 81Q306 105 315 148Q324 191 335 239L452 730H554L366 0H233Z"/><path id="g1" d="M97 0V730H522V641H195V424H487V337H195V89H522V0H97Z"/><path id="g2" d="M89 0V730H315Q382 730 432 703Q483 677 510 629Q538 582 538 518Q538 445 501 391Q463 338 401 316L548 0H439L300 305H187V0H89ZM187 393H315Q370 393 403 427Q436 461 436 517Q436 574 403 608Q370 642 315 642H187V393Z"/><path id="g3" d="M304 -10Q233 -10 179 15Q126 41 97 88Q68 136 68 202H167Q167 144 204 111Q242 78 304 78Q364 78 399 112Q435 146 435 201Q435 243 412 274Q390 306 347 317L239 346Q168 366 126 419Q85 471 85 543Q85 602 112 646Q138 691 186 715Q234 740 297 740Q361 740 409 715Q457 691 485 647Q512 603 512 545H414Q414 593 381 623Q348 653 297 653Q245 653 214 623Q182 593 182 545Q182 507 203 480Q224 453 262 442L373 412Q448 392 490 336Q532 279 532 201Q532 137 504 89Q476 42 424 16Q373 -10 304 -10Z"/><path id="g4" d="M101 0V90H250V640H101V730H499V640H350V90H499V0H101Z"/><path id="g5" d="M300 -10Q200 -10 142 48Q84 106 84 212V518Q84 625 142 682Q200 740 300 740Q400 740 458 682Q516 625 516 519V212Q516 106 458 48Q400 -10 300 -10ZM300 78Q357 78 387 110Q417 142 417 202V528Q417 588 387 620Q357 652 300 652Q244 652 213 620Q183 588 183 528V202Q183 142 213 110Q244 78 300 78Z"/><path id="g6" d="M85 0V730H214L432 114Q429 141 427 179Q424 217 422 259Q421 301 421 337V730H515V0H386L170 616Q172 591 174 553Q176 516 178 474Q179 433 179 396V0H85Z"/><path id="g8" d="M290 -10Q187 -10 127 46Q67 103 67 200H166Q166 143 200 110Q233 78 291 78Q348 78 382 110Q415 143 415 200V251Q415 308 382 341Q349 375 291 375H209V458L385 641H89V730H496V640L291 428L293 462Q395 462 455 405Q515 349 515 251V200Q515 103 454 46Q394 -10 290 -10Z"/><path id="g9" d="M300 -10Q262 -10 238 13Q215 36 215 72Q215 111 238 134Q262 158 300 158Q338 158 362 134Q385 111 385 72Q385 36 362 13Q338 -10 300 -10Z"/><path id="ga" d="M88 0V90H284V641L88 495V607L254 730H383V90H543V0H88Z"/><path id="gb" d="M300 -10Q232 -10 182 17Q132 43 104 92Q77 141 77 206V524Q77 590 104 638Q132 687 182 713Q232 740 300 740Q369 740 419 713Q468 687 496 638Q523 590 523 524V206Q523 141 496 92Q468 43 418 17Q368 -10 300 -10ZM300 75Q358 75 393 111Q428 148 428 206V524Q428 582 393 619Q358 655 300 655Q242 655 207 619Q172 582 172 524V206Q172 148 207 111Q242 75 300 75ZM300 309Q272 309 255 324Q238 339 238 368Q238 396 255 411Q272 425 300 425Q328 425 345 411Q362 396 362 368Q362 339 345 324Q328 309 300 309Z"/><path id="gc" d="M180 688V0H134V1056H180V816H168Q204 944 310 1008Q417 1072 544 1072Q659 1072 746 1024Q834 975 884 889Q934 803 934 688V0H888V688Q888 840 792 933Q696 1026 544 1026Q440 1026 358 982Q275 939 228 862Q180 786 180 688Z"/><path id="gd" d="M180 1490V0H134V1490H180Z"/><path id="ge" d="M530 -16Q394 -16 289 54Q184 125 125 248Q66 371 6Line truncated

Before

Width:  |  Height:  |  Size: 395 KiB

-1
View File
@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1200 675" width="1200" height="675" role="img"><title>JSON for Modern C++: feature slideshow (animated, 20 slides, 15 s each)</title><style>.bg{fill:#fff} .tx{fill:#0f1419} .tx2{fill:#3d444f} .tx3{fill:#8a919c} .com{fill:#646d7a}.bl{fill:#1747a6} .blk{fill:#1747a6} .kw{fill:#1747a6} .ty{fill:#0d6e72} .fn{fill:#6b3fb8} .str{fill:#23793a} .num{fill:#b04a07} .pp{fill:#a3247a}.panel{fill:#f5f7fa;stroke:#e3e6eb;stroke-width:1} .rule{stroke:#e3e6eb;stroke-width:1}.wm-a{fill:#0f1419} .wm-s{fill:#1747a6}.on-a,.on-tx{fill:#fff} .red{fill:#d93a3f} .on-panel{fill:#fff;fill-opacity:.08;stroke:#fff;stroke-opacity:.16} .on-s{fill:#bcd3fb} .w70{fill:#fff;fill-opacity:.7} .on-tx2{fill:#dbe6fb} .chip{fill:#fff;fill-opacity:.14}.pat{fill:#fff;fill-opacity:.09}.h1{stop-color:#1d55c4} .h2{stop-color:#123a8a}.seg{fill:#e3e6eb} .seg-on{fill:#1747a6}.seg.on-band{fill:#fff;fill-opacity:.25} .seg-on.on-band{fill:#fff;fill-opacity:1}.s{opacity:0;animation:show 300s linear infinite backwards}.fill{transform-box:fill-box;transform-origin:left;animation:fill 300s linear infinite backwards;animation-delay:inherit}.s0{animation-name:show0} .sl{animation-name:showl}@keyframes show{0%{opacity:0;animation-timing-function:cubic-bezier(.2,.6,.2,1)} 0.2000%{opacity:1} 5.2000%{opacity:1} 5.2100%{opacity:0} 100%{opacity:0}}@keyframes show0{0%{opacity:1} 5.2000%{opacity:1} 5.2100%{opacity:0} 100%{opacity:0}}@keyframes showl{0%{opacity:0;animation-timing-function:cubic-bezier(.2,.6,.2,1)} 0.2000%{opacity:1} 5.0000%{opacity:1} 5.2000%{opacity:0} 100%{opacity:0}}@keyframes fill{0%{transform:scaleX(0)} 5.0000%{transform:scaleX(1)} 100%{transform:scaleX(1)}}@media (prefers-reduced-motion:reduce){.s,.fill{animation:none} .s:first-of-type{opacity:1}}</style><defs><path id="g0" d="M233 0 46 730H148L269 238Q281 191 289 148Q298 105 302 81Q306 105 315 148Q324 191 335 239L452 730H554L366 0H233Z"/><path id="g1" d="M97 0V730H522V641H195V424H487V337H195V89H522V0H97Z"/><path id="g2" d="M89 0V730H315Q382 730 432 703Q483 677 510 629Q538 582 538 518Q538 445 501 391Q463 338 401 316L548 0H439L300 305H187V0H89ZM187 393H315Q370 393 403 427Q436 461 436 517Q436 574 403 608Q370 642 315 642H187V393Z"/><path id="g3" d="M304 -10Q233 -10 179 15Q126 41 97 88Q68 136 68 202H167Q167 144 204 111Q242 78 304 78Q364 78 399 112Q435 146 435 201Q435 243 412 274Q390 306 347 317L239 346Q168 366 126 419Q85 471 85 543Q85 602 112 646Q138 691 186 715Q234 740 297 740Q361 740 409 715Q457 691 485 647Q512 603 512 545H414Q414 593 381 623Q348 653 297 653Q245 653 214 623Q182 593 182 545Q182 507 203 480Q224 453 262 442L373 412Q448 392 490 336Q532 279 532 201Q532 137 504 89Q476 42 424 16Q373 -10 304 -10Z"/><path id="g4" d="M101 0V90H250V640H101V730H499V640H350V90H499V0H101Z"/><path id="g5" d="M300 -10Q200 -10 142 48Q84 106 84 212V518Q84 625 142 682Q200 740 300 740Q400 740 458 682Q516 625 516 519V212Q516 106 458 48Q400 -10 300 -10ZM300 78Q357 78 387 110Q417 142 417 202V528Q417 588 387 620Q357 652 300 652Q244 652 213 620Q183 588 183 528V202Q183 142 213 110Q244 78 300 78Z"/><path id="g6" d="M85 0V730H214L432 114Q429 141 427 179Q424 217 422 259Q421 301 421 337V730H515V0H386L170 616Q172 591 174 553Q176 516 178 474Q179 433 179 396V0H85Z"/><path id="g8" d="M290 -10Q187 -10 127 46Q67 103 67 200H166Q166 143 200 110Q233 78 291 78Q348 78 382 110Q415 143 415 200V251Q415 308 382 341Q349 375 291 375H209V458L385 641H89V730H496V640L291 428L293 462Q395 462 455 405Q515 349 515 251V200Q515 103 454 46Q394 -10 290 -10Z"/><path id="g9" d="M300 -10Q262 -10 238 13Q215 36 215 72Q215 111 238 134Q262 158 300 158Q338 158 362 134Q385 111 385 72Q385 36 362 13Q338 -10 300 -10Z"/><path id="ga" d="M88 0V90H284V641L88 495V607L254 730H383V90H543V0H88Z"/><path id="gb" d="M300 -10Q232 -10 182 17Q132 43 104 92Q77 141 77 206V524Q77 590 104 638Q132 687 182 713Q232 740 300 740Q369 740 419 713Q468 687 496 638Q523 590 523 524V206Q523 141 496 92Q468 43 418 17Q368 -10 300 -10ZM300 75Q358 75 393 111Q428 148 428 206V524Q428 582 393 619Q358 655 300 655Q242 655 207 619Q172 582 172 524V206Q172 148 207 111Q242 75 300 75ZM300 309Q272 309 255 324Q238 339 238 368Q238 396 255 411Q272 425 300 425Q328 425 345 411Q362 396 362 368Q362 339 345 324Q328 309 300 309Z"/><path id="gc" d="M180 688V0H134V1056H180V816H168Q204 944 310 1008Q417 1072 544 1072Q659 1072 746 1024Q834 975 884 889Q934 803 934 688V0H888V688Q888 840 792 933Q696 1026 544 1026Q440 1026 358 982Q275 939 228 862Q180 786 180 688Z"/><path id="gd" d="M180 1490V0H134V1490H180Z"/><path id="ge" d="M530 -16Q394 -16 289 54Q184 125 125 248Q66 371 66 528Q66 686 125 808Q184 931 289 1002Q394 1072 530 1072Q666 1072 770 1002Q874 931 933 808Q992 686 992 528Q992 371 933 248Q874 125 770 54Q666 -16 530 -16ZM530 30Q652 30 746 94Q840 157 893 270Q946 382 946 528Q946 674 893 786Q840 899 746 962Q652 1026 530 1026Q408 1026 314 962Q219 899 166 786Q112 674 112 528Q112 382 166 270Q219 157 314 94Q408 30 530 30Z"/><path id="gf" d="MLine truncated

Before

Width:  |  Height:  |  Size: 394 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

@@ -1,66 +0,0 @@
<svg class="railroad" viewBox="0 0 860 214" role="img" aria-labelledby="rr-title" xmlns="http://www.w3.org/2000/svg">
<title id="rr-title">Railroad diagram of the J&#8203;SON number syntax: optional minus, then 0 or a digit from 1 to 9 followed by digits, then an optional fraction (a dot and digits) and an optional exponent (e or E, an optional sign, and digits).</title>
<g class="rr-track" fill="none" stroke="currentColor" stroke-opacity=".6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M8 44 L850 44"/>
<path d="M24 44 L31 44 Q38 44 38 51 L38 82 Q38 92 48 92 L59 92"/>
<path d="M85 92 L96 92 Q106 92 106 82 L106 51 Q106 44 113 44 L120 44"/>
<path d="M120 44 L127 44 Q134 44 134 51 L134 82 Q134 92 144 92 L160 92"/>
<path d="M242 92 L370 92 Q380 92 380 82 L380 52 Q380 44 388 44 L396 44"/>
<path d="M242 92 L250 92 Q258 92 258 100 L258 126 Q258 136 268 136 L278 136"/>
<path d="M332 136 L340 136 Q348 136 348 128 L348 92"/>
<path d="M340 136 L340 160 Q340 170 330 170 L280 170 Q270 170 270 160 L270 136"/>
<path d="M396 44 L403 44 Q410 44 410 51 L410 84.5 Q410 92 417.5 92 L425 92"/>
<path d="M451 92 L462 92"/>
<path d="M524 92 L524 116 Q524 126 514 126 L466 126 Q456 126 456 116 L456 92"/>
<path d="M516 92 L523 92 Q530 92 530 85 L530 52 Q530 44 538 44 L546 44"/>
<path d="M546 44 L553 44 Q560 44 560 51 L560 83.5 Q560 92 568.5 92 L577 92"/>
<path d="M546 44 L553 44 Q560 44 560 51 L560 127.5 Q560 136 568.5 136 L577 136"/>
<path d="M603 92 L728 92"/>
<path d="M603 136 L611.5 136 Q620 136 620 127.5 L620 92"/>
<path d="M640 92 L647 92 Q654 92 654 99 L654 128 Q654 136 662 136 L670 136"/>
<path d="M640 92 L647 92 Q654 92 654 99 L654 172 Q654 180 662 180 L670 180"/>
<path d="M696 136 L704 136 Q712 136 712 128 L712 92"/>
<path d="M696 180 L704 180 Q712 180 712 172 L712 92"/>
<path d="M728 92 L746 92"/>
<path d="M808 92 L808 116 Q808 126 798 126 L748 126 Q738 126 738 116 L738 92"/>
<path d="M800 92 L808 92 Q816 92 816 84 L816 52 Q816 44 824 44 L832 44"/>
</g>
<polygon class="rr-arrow" points="2,38 12,44 2,50"/>
<polygon class="rr-arrow" points="846,38 858,44 846,50"/>
<circle class="rr-bg" cx="72" cy="92" r="13"/>
<circle class="rr-t" cx="72" cy="92" r="13"/>
<circle class="rr-bg" cx="257" cy="44" r="13"/>
<circle class="rr-t" cx="257" cy="44" r="13"/>
<rect class="rr-bg" x="160" y="78" width="82" height="28" rx="6"/>
<rect class="rr-n" x="160" y="78" width="82" height="28" rx="6"/>
<rect class="rr-bg" x="278" y="122" width="54" height="28" rx="6"/>
<rect class="rr-n" x="278" y="122" width="54" height="28" rx="6"/>
<circle class="rr-bg" cx="438" cy="92" r="13"/>
<circle class="rr-t" cx="438" cy="92" r="13"/>
<rect class="rr-bg" x="462" y="78" width="54" height="28" rx="6"/>
<rect class="rr-n" x="462" y="78" width="54" height="28" rx="6"/>
<circle class="rr-bg" cx="590" cy="92" r="13"/>
<circle class="rr-t" cx="590" cy="92" r="13"/>
<circle class="rr-bg" cx="590" cy="136" r="13"/>
<circle class="rr-t" cx="590" cy="136" r="13"/>
<circle class="rr-bg" cx="683" cy="136" r="13"/>
<circle class="rr-t" cx="683" cy="136" r="13"/>
<circle class="rr-bg" cx="683" cy="180" r="13"/>
<circle class="rr-t" cx="683" cy="180" r="13"/>
<rect class="rr-bg" x="746" y="78" width="54" height="28" rx="6"/>
<rect class="rr-n" x="746" y="78" width="54" height="28" rx="6"/>
<text class="rr-c" x="72" y="97">&#8722;</text>
<text class="rr-c" x="257" y="49">0</text>
<text class="rr-b" x="201" y="97">digit 1&#8211;9</text>
<text class="rr-b" x="305" y="141">digit</text>
<text class="rr-c" x="438" y="97">.</text>
<text class="rr-b" x="489" y="97">digit</text>
<text class="rr-c" x="590" y="97">e</text>
<text class="rr-c" x="590" y="141">E</text>
<text class="rr-c" x="683" y="141">&#8722;</text>
<text class="rr-c" x="683" y="185">+</text>
<text class="rr-b" x="773" y="97">digit</text>
<text class="rr-l rr-name" x="24" y="32">number</text>
<text class="rr-l" x="420" y="32">fraction</text>
<text class="rr-l" x="580" y="32">exponent</text>
</svg>

Before

Width:  |  Height:  |  Size: 3.9 KiB

+446 -27
View File
@@ -1,28 +1,447 @@
<svg class="nj-fig nj-fig--range" viewBox="0 0 600 150" role="img" aria-labelledby="fig-be-title" xmlns="http://www.w3.org/2000/svg">
<title id="fig-be-title">A row of fifteen elements followed by a dashed past-the-last position. begin() points to the first element and end() points to the past-the-last position.</title>
<defs><marker id="fig-be-arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto"><path class="fg-head" d="M0 1 L10 5 L0 9 z"/></marker></defs>
<rect class="fg-cell" x="30" y="62" width="30" height="30" rx="5"/>
<rect class="fg-cell" x="64" y="62" width="30" height="30" rx="5"/>
<rect class="fg-cell" x="98" y="62" width="30" height="30" rx="5"/>
<rect class="fg-cell" x="132" y="62" width="30" height="30" rx="5"/>
<rect class="fg-cell" x="166" y="62" width="30" height="30" rx="5"/>
<rect class="fg-cell" x="200" y="62" width="30" height="30" rx="5"/>
<rect class="fg-cell" x="234" y="62" width="30" height="30" rx="5"/>
<rect class="fg-cell" x="268" y="62" width="30" height="30" rx="5"/>
<rect class="fg-cell" x="302" y="62" width="30" height="30" rx="5"/>
<rect class="fg-cell" x="336" y="62" width="30" height="30" rx="5"/>
<rect class="fg-cell" x="370" y="62" width="30" height="30" rx="5"/>
<rect class="fg-cell" x="404" y="62" width="30" height="30" rx="5"/>
<rect class="fg-cell" x="438" y="62" width="30" height="30" rx="5"/>
<rect class="fg-cell" x="472" y="62" width="30" height="30" rx="5"/>
<rect class="fg-cell" x="506" y="62" width="30" height="30" rx="5"/>
<rect class="fg-cell fg-cell--end" x="540" y="62" width="30" height="30" rx="5"/>
<rect class="fg-pill" x="15" y="8" width="60" height="24" rx="6"/>
<text class="fg-pilltext" x="45" y="24">begin</text>
<line class="fg-arrow" x1="45" y1="32" x2="45" y2="60" marker-end="url(#fig-be-arrow)"/>
<rect class="fg-pill" x="531" y="8" width="48" height="24" rx="6"/>
<text class="fg-pilltext" x="555" y="24">end</text>
<line class="fg-arrow" x1="555" y1="32" x2="555" y2="60" marker-end="url(#fig-be-arrow)"/>
<text class="fg-note fg-note--r" x="537" y="132">Past-the-last element</text>
<line class="fg-arrow" x1="555" y1="124" x2="555" y2="96" marker-end="url(#fig-be-arrow)"/>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="545.89282"
height="156.9409"
id="svg2"
version="1.1"
inkscape:version="0.48.4 r9939"
sodipodi:docname="range-begin-end.svg">
<style type="text/css">
@media (prefers-color-scheme: dark) {
[style*="stroke:#000000"] { stroke: #e6e6e6 !important; }
[style*="fill:#000000"] { fill: #e6e6e6 !important; }
[style*="fill:#ffffff"] { fill: #1e1e1e !important; }
[style*="fill:#0000ff"] { fill: #7aa2ff !important; }
marker path { fill: #e6e6e6 !important; stroke: #e6e6e6 !important; }
stop[style*="#c1b1b1"] { stop-color: #5a5a5a !important; }
stop[style*="#b6b6b6"] { stop-color: #4a4a4a !important; }
stop[style*="#e3e3e3"] { stop-color: #2a2a2a !important; }
}
</style>
<defs
id="defs4">
<marker
inkscape:stockid="Arrow1Mend"
orient="auto"
refY="0"
refX="0"
id="Arrow1Mend"
style="overflow:visible">
<path
id="path6525"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
transform="matrix(-0.4,0,0,-0.4,-4,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow1Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow1Lend"
style="overflow:visible">
<path
id="path6519"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
transform="matrix(-0.8,0,0,-0.8,-10,0)"
inkscape:connector-curvature="0" />
</marker>
<linearGradient
id="linearGradient5488">
<stop
style="stop-color:#c1b1b1;stop-opacity:1;"
offset="0"
id="stop5490" />
<stop
style="stop-color:#e3e3e3;stop-opacity:0;"
offset="1"
id="stop5492" />
</linearGradient>
<linearGradient
id="linearGradient4040">
<stop
style="stop-color:#b6b6b6;stop-opacity:1;"
offset="0"
id="stop4042" />
<stop
style="stop-color:#e3e3e3;stop-opacity:0;"
offset="1"
id="stop4044" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5488"
id="linearGradient6284"
gradientUnits="userSpaceOnUse"
x1="81"
y1="1001.3622"
x2="81"
y2="971.36218" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5488-2"
id="linearGradient6284-3"
gradientUnits="userSpaceOnUse"
x1="81"
y1="1001.3622"
x2="81"
y2="971.36218" />
<linearGradient
id="linearGradient5488-2">
<stop
style="stop-color:#c1b1b1;stop-opacity:1;"
offset="0"
id="stop5490-5" />
<stop
style="stop-color:#e3e3e3;stop-opacity:0;"
offset="1"
id="stop5492-8" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5488-0"
id="linearGradient6284-5"
gradientUnits="userSpaceOnUse"
x1="81"
y1="1001.3622"
x2="81"
y2="971.36218" />
<linearGradient
id="linearGradient5488-0">
<stop
style="stop-color:#c1b1b1;stop-opacity:1;"
offset="0"
id="stop5490-1" />
<stop
style="stop-color:#e3e3e3;stop-opacity:0;"
offset="1"
id="stop5492-3" />
</linearGradient>
<marker
inkscape:stockid="Arrow1Mend"
orient="auto"
refY="0"
refX="0"
id="Arrow1Mend-8"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path6525-3"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
</marker>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5488"
id="linearGradient9026"
gradientUnits="userSpaceOnUse"
x1="81"
y1="1001.3622"
x2="81"
y2="971.36218" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.4"
inkscape:cx="332.80082"
inkscape:cy="165.04542"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1680"
inkscape:window-height="993"
inkscape:window-x="0"
inkscape:window-y="34"
inkscape:window-maximized="1"
inkscape:snap-bbox="true"
inkscape:object-nodes="true"
inkscape:snap-smooth-nodes="false"
inkscape:bbox-nodes="false"
inkscape:snap-bbox-edge-midpoints="false"
inkscape:object-paths="true"
fit-margin-top="10"
fit-margin-right="10"
fit-margin-bottom="10"
fit-margin-left="10" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-26.107143,-893.4807)">
<g
id="g7184">
<path
inkscape:tile-y0="219"
inkscape:tile-x0="51"
inkscape:tile-h="30.00002"
inkscape:tile-w="29.999997"
inkscape:tile-cy="234.00001"
inkscape:tile-cx="65.999998"
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0"
id="path3006"
d="m 80.999997,1001.3622 -29.999997,0 0,-30.00002 29.999997,0 z"
style="fill:url(#linearGradient9026);fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
<use
height="300"
width="600"
style="fill:none"
id="use4010"
transform="translate(29.999997,0)"
xlink:href="#path3006"
inkscape:tiled-clone-of="#path3006"
y="0"
x="0" />
<use
id="use4012"
transform="translate(59.999994,0)"
xlink:href="#path3006"
inkscape:tiled-clone-of="#path3006"
y="0"
x="0"
width="600"
height="300" />
<use
id="use4014"
transform="translate(89.999991,0)"
xlink:href="#path3006"
inkscape:tiled-clone-of="#path3006"
y="0"
x="0"
width="600"
height="300" />
<use
id="use4016"
transform="translate(119.99999,0)"
xlink:href="#path3006"
inkscape:tiled-clone-of="#path3006"
y="0"
x="0"
width="600"
height="300" />
<use
id="use4018"
transform="translate(149.99998,0)"
xlink:href="#path3006"
inkscape:tiled-clone-of="#path3006"
y="0"
x="0"
width="600"
height="300" />
<use
id="use4020"
transform="translate(179.99998,0)"
xlink:href="#path3006"
inkscape:tiled-clone-of="#path3006"
y="0"
x="0"
width="600"
height="300" />
<use
id="use4022"
transform="translate(209.99998,0)"
xlink:href="#path3006"
inkscape:tiled-clone-of="#path3006"
y="0"
x="0"
width="600"
height="300" />
<use
id="use4024"
transform="translate(239.99998,0)"
xlink:href="#path3006"
inkscape:tiled-clone-of="#path3006"
y="0"
x="0"
width="600"
height="300" />
<use
id="use4026"
transform="translate(269.99997,0)"
xlink:href="#path3006"
inkscape:tiled-clone-of="#path3006"
y="0"
x="0"
width="600"
height="300" />
<use
id="use4028"
transform="translate(299.99997,0)"
xlink:href="#path3006"
inkscape:tiled-clone-of="#path3006"
y="0"
x="0"
width="600"
height="300" />
<use
id="use4030"
transform="translate(329.99997,0)"
xlink:href="#path3006"
inkscape:tiled-clone-of="#path3006"
y="0"
x="0"
width="600"
height="300" />
<use
id="use4032"
transform="translate(359.99996,0)"
xlink:href="#path3006"
inkscape:tiled-clone-of="#path3006"
y="0"
x="0"
width="600"
height="300" />
<use
id="use4034"
transform="translate(389.99996,0)"
xlink:href="#path3006"
inkscape:tiled-clone-of="#path3006"
y="0"
x="0"
width="600"
height="300" />
<use
id="use4036"
transform="translate(419.99996,0)"
xlink:href="#path3006"
inkscape:tiled-clone-of="#path3006"
y="0"
x="0"
width="600"
height="300" />
<use
id="use4038"
transform="translate(449.99995,0)"
xlink:href="#path3006"
inkscape:tiled-clone-of="#path3006"
y="0"
x="0"
width="600"
height="300" />
<path
inkscape:tile-y0="219"
inkscape:tile-x0="51"
inkscape:tile-h="30.00002"
inkscape:tile-w="29.999997"
inkscape:tile-cy="234.00001"
inkscape:tile-cx="65.999998"
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0"
id="path3006-3"
d="m 560.99995,1001.3622 -30,0 0,-30.00002 30,0 z"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:2, 2;stroke-dashoffset:0;marker-start:none" />
</g>
<text
xml:space="preserve"
style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:55.00000119%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;font-family:Droid Sans;-inkscape-font-specification:Droid Sans"
x="392.94931"
y="1040.2404"
id="text6504"
sodipodi:linespacing="55.000001%"><tspan
sodipodi:role="line"
id="tspan6506"
x="392.94931"
y="1040.2404"
rotate="0 0 0 0 0 0 0 0 0.50000191 0 0 0 0 0 0 0 0 0 0 0 0 0"
dy="0"
dx="0"
style="font-size:12.80000019px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:DejaVu Sans;-inkscape-font-specification:DejaVu Sans">Past-the-last element</tspan></text>
<path
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow1Mend)"
d="m 531.84531,279.29187 c 14.75083,-11.1311 12.86656,-14.42379 15.15229,-29.29442"
id="path6510"
inkscape:connector-curvature="0"
transform="translate(0,752.36218)"
sodipodi:nodetypes="cc" />
<g
id="g7203"
transform="translate(-40,-18.482143)">
<path
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 128.39286,947.36218 -51.785717,0 0,-21.51786 51.785717,0 z"
id="use6288-9"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc"
inkscape:tile-x0="51"
inkscape:tile-y0="219" />
<text
transform="translate(0,752.36218)"
sodipodi:linespacing="55.000001%"
id="text7176"
y="189.10715"
x="80.535713"
style="font-size:15px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:55.00000119%;letter-spacing:0px;word-spacing:0px;fill:#0000ff;fill-opacity:1;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVu Sans"
xml:space="preserve"><tspan
y="189.10715"
x="80.535713"
id="tspan7178"
sodipodi:role="line">begin</tspan></text>
</g>
<g
id="g7238"
transform="translate(20.035895,11.306019)">
<path
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 538.39286,914.19254 -37.50001,0 0,-21.51786 37.50001,0 z"
id="use6288-9-7"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc"
inkscape:tile-x0="51"
inkscape:tile-y0="219" />
<text
sodipodi:linespacing="55.000001%"
id="text7176-3"
y="908.29968"
x="504.82141"
style="font-size:15px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:55.00000119%;letter-spacing:0px;word-spacing:0px;fill:#0000ff;fill-opacity:1;stroke:none;font-family:DejaVu Sans;-inkscape-font-specification:DejaVu Sans"
xml:space="preserve"><tspan
y="908.29968"
x="504.82141"
id="tspan7178-7"
sodipodi:role="line">end</tspan></text>
</g>
<path
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-mid:none;marker-end:url(#Arrow1Mend)"
d="M 60.16588,176.51786 C 64.809921,191.17027 67.41717,201.02245 68.196339,219"
id="path7243"
inkscape:connector-curvature="0"
transform="translate(0,752.36218)"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-mid:none;marker-end:url(#Arrow1Mend)"
d="m 540.40821,925.49856 c 2.63474,15.08647 5.72454,31.23577 6.06617,45.86362"
id="path7243-8"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 14 KiB

File diff suppressed because it is too large. Load diff

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 41 KiB

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-78 -1741 14091 2499" width="495" height="88" role="img" aria-label="nlohmann/json"><title>nlohmann/json</title><g transform="scale(1 -1)"><g fill="#ffffff"><path transform="translate(0.0 0)" d="M279.2 697.3V0H167.3V1104.2H276.1L276.4 850.4H255.4Q299 990.6 399.3 1055.2Q499.6 1119.9 626.3 1119.9Q739.7 1119.9 826.2 1072.4Q912.8 1024.9 962.1 935.2Q1011.4 845.5 1011.4 717.1V0H899.6V709.9Q899.6 851.3 818 933.9Q736.5 1016.6 601.8 1016.6Q510.1 1016.6 437 977.7Q363.8 938.9 321.5 867.2Q279.2 795.6 279.2 697.3Z"/><path transform="translate(1138.0 0)" d="M279.2 1490V0H167.3V1490H279.2Z"/><path transform="translate(1544.1 0)" d="M590.7 -23.1Q448.9 -23.1 339.6 50.3Q230.3 123.7 168.3 252.7Q106.3 381.6 106.3 548Q106.3 715.8 168.3 844.8Q230.3 973.8 339.6 1047.1Q448.9 1120.3 590.7 1120.3Q732.6 1120.3 841.7 1047.1Q950.9 973.8 1012.9 844.6Q1074.9 715.4 1074.9 548Q1074.9 381.6 1013.1 252.7Q951.3 123.7 842.1 50.3Q733 -23.1 590.7 -23.1ZM590.7 79.6Q705.6 79.6 788.9 141.9Q872.2 204.2 917.1 310.4Q962 416.7 962 548Q962 679.6 916.9 786Q871.8 892.4 788.5 955Q705.2 1017.6 590.7 1017.6Q476.7 1017.6 393.5 954.9Q310.3 892.2 265 786.1Q219.6 679.9 219.6 548Q219.6 416.6 264.8 310.4Q309.9 204.2 393.1 141.9Q476.2 79.6 590.7 79.6Z"/><path transform="translate(2684.1 0)" d="M279.2 697.3V0H167.3V1490H279.2V850.4H255.4Q299 990.6 399.3 1055.2Q499.6 1119.9 626.3 1119.9Q739.7 1119.9 826.4 1072.4Q913.2 1024.9 962.5 935.2Q1011.8 845.5 1011.8 717.1V0H899.6V709.9Q899.6 851.3 818 933.9Q736.5 1016.6 601.8 1016.6Q510.1 1016.6 437 977.7Q363.8 938.9 321.5 867.2Q279.2 795.6 279.2 697.3Z"/><path transform="translate(3822.2 0)" d="M167.3 0V1104.2H276.8L277.2 853.7H261.4Q285.1 945.7 337 1004.8Q388.9 1064 457.8 1093.1Q526.7 1122.2 599.7 1122.2Q722.9 1122.2 805.7 1046.7Q888.4 971.2 910.3 844.9H888.1Q907.9 930.7 959.4 992.8Q1011 1054.9 1085.3 1088.6Q1159.6 1122.2 1248.1 1122.2Q1349.1 1122.2 1427 1078Q1505 1033.7 1549.5 948.2Q1594 862.7 1594 738.2V0H1482.1V736.3Q1482.1 878 1408.3 948Q1334.4 1018.1 1221.8 1018.1Q1139.1 1018.1 1074.7 982.6Q1010.3 947 973.4 881.6Q936.6 816.3 936.6 726.7V0H824.7V745.3Q824.7 873 754.3 945.6Q683.9 1018.1 570.7 1018.1Q491.1 1018.1 424.8 981.1Q358.4 944 318.8 875.8Q279.2 807.7 279.2 713.4V0H167.3Z"/><path transform="translate(5542.2 0)" d="M560.3 -22.7Q425.4 -22.7 323.1 49.7Q220.8 122 163.6 251Q106.3 380.1 106.3 549.8Q106.3 718.7 164 847.4Q221.7 976 324.4 1047.9Q427.1 1119.9 561.9 1119.9Q657.4 1119.9 727.7 1085.2Q798.1 1050.6 845.2 994.3Q892.3 938.1 916.4 874.3H924.9V1104.2H1036.2V0H925.3V225.1H916.4Q891.1 161.2 843.7 104.4Q796.3 47.5 725.9 12.4Q655.4 -22.7 560.3 -22.7ZM574.9 79.9Q685.3 79.9 764.2 140.4Q843.1 200.8 885.1 307.1Q927.2 413.4 927.2 550.6Q927.2 687.8 885.1 793Q843.1 898.2 764.2 957.8Q685.3 1017.4 574.9 1017.4Q464.6 1017.4 384.9 957.2Q305.3 897.1 262.4 791.7Q219.6 686.2 219.6 550.6Q219.6 415 262.4 308.6Q305.3 202.3 384.9 141.1Q464.6 79.9 574.9 79.9Z"/><path transform="translate(6705.2 0)" d="M279.2 697.3V0H167.3V1104.2H276.1L276.4 850.4H255.4Q299 990.6 399.3 1055.2Q499.6 1119.9 626.3 1119.9Q739.7 1119.9 826.2 1072.4Q912.8 1024.9 962.1 935.2Q1011.4 845.5 1011.4 717.1V0H899.6V709.9Q899.6 851.3 818 933.9Q736.5 1016.6 601.8 1016.6Q510.1 1016.6 437 977.7Q363.8 938.9 321.5 867.2Q279.2 795.6 279.2 697.3Z"/><path transform="translate(7843.3 0)" d="M279.2 697.3V0H167.3V1104.2H276.1L276.4 850.4H255.4Q299 990.6 399.3 1055.2Q499.6 1119.9 626.3 1119.9Q739.7 1119.9 826.2 1072.4Q912.8 1024.9 962.1 935.2Q1011.4 845.5 1011.4 717.1V0H899.6V709.9Q899.6 851.3 818 933.9Q736.5 1016.6 601.8 1016.6Q510.1 1016.6 437 977.7Q363.8 938.9 321.5 867.2Q279.2 795.6 279.2 697.3Z"/><path transform="translate(9729.7 0)" d="M111.2 1104.2H459.2V-51.7Q459.4 -183.9 406.6 -264.4Q353.7 -344.9 255.5 -381.4Q157.2 -418 21.4 -418H-63.7V-152.7H-14.8Q55.3 -152.7 83.3 -125Q111.2 -97.4 111.2 -39.2V1104.2ZM285.1 1228.6Q208.7 1228.6 155.6 1278Q102.6 1327.5 102.6 1398.2Q102.6 1469.9 155.6 1518.8Q208.7 1567.8 285 1567.8Q361.5 1567.8 414.7 1518.9Q467.8 1470.1 467.8 1398.1Q467.8 1327.4 414.7 1278Q361.5 1228.6 285.1 1228.6Z"/><path transform="translate(10258.7 0)" d="M580.7 -22.1Q436.8 -22.1 326.1 19.4Q215.5 60.9 146.9 138.4Q78.2 215.9 61 323.7L388.3 363.3Q406.3 294.2 455.2 257.5Q504.1 220.8 589.7 220.8Q662.2 220.8 703.6 246.4Q745 271.9 745 313.9Q745 351.2 711.8 374.4Q678.7 397.6 610.3 410.8L416 447.9Q252.5 479.2 170.3 559.5Q88.1 639.9 88.1 767.7Q88.1 878 147.7 956.7Q207.4 1035.5 316.7 1077.7Q426.1 1119.9 575.6 1119.9Q717.4 1119.9 822.1 1080.8Q926.7 1041.7 989.4 969.8Q1052.1 897.9 1067.5 797.8L757.5 759.8Q745.3 815.8 699.4 848.2Q653.5 880.6 583.2 880.6Q518.8 880.6 476.8 855.3Q434.8 829.9 434.8 787.7Q434.8 753.9 462.5 729Q490.3 704.1 557.8 691L771.4 651.3Q937.2 620.2 1017.9 546.8Q1098.7 473.4 1098.7 352.5Q1098.7 239.1 1032.3 154.9Q965.9 70.8 849.2 24.3Q732.5 -22.1 580.7 -22.1Z"/><path transform="translate(11376.8 0)" d="M628.4 -21.6Q455.7 -21.6 329.7 49.8Q203.6 121.3 135.6 249.6Q67.7 378 67.7 548.9Q67.7 720.4 135.6 848.9Q203.6 977.3 329.7Line truncated

Before

Width:  |  Height:  |  Size: 5.8 KiB

+66 -8
View File
@@ -1,11 +1,63 @@
---
template: home.html
hide:
- toc
---
# JSON for Modern C++
![JSON for Modern C++](images/json.gif)
JSON for Modern C++ is a header-only C++11 library that turns JSON into a first-class C++ data type, using the operator
magic of modern C++ so that creating, reading, and modifying JSON values feels as natural as it does in languages like
Python. The whole library is available as a single header, `json.hpp`, with no dependencies, no subproject, and no
complex build system to set up; a companion header, `json_fwd.hpp`, provides forward declarations to keep compile times
down, `<nlohmann/json_literals.hpp>` holds the user-defined string literals on their own, and
`<nlohmann/json_view.hpp>` adds zero-copy views and editable documents. See
[header-only integration](integration/index.md) for details. It is heavily unit-tested with 100% code
coverage, checked with Valgrind and the Clang Sanitizers for memory leaks, and continuously fuzz-tested by Google
OSS-Fuzz.
## Quick start
Add the single header to your project and use the library like this:
```cpp
#include <iostream>
#include <nlohmann/json.hpp>
using nlohmann::json;
int main()
{
// parse a JSON string
json j = json::parse(R"({"happy": true, "pi": 3.141})");
// access and modify values
j["name"] = "Niels";
j["list"] = {1, 0, 2};
// read a value into a C++ variable and use it
double pi = j["pi"].get<double>();
std::cout << "2 * pi = " << 2 * pi << '\n';
// serialize with an indent of 4 spaces
std::cout << j.dump(4) << '\n';
}
```
Get the library by copying the single header [`json.hpp`](https://github.com/nlohmann/json/releases) from the
releases page into a directory `nlohmann` on your include path, or by installing it with a package manager:
```sh
brew install nlohmann-json # Homebrew
vcpkg install nlohmann-json # vcpkg
```
```cmake
find_package(nlohmann_json 3.12.0 REQUIRED)
target_link_libraries(myproject PRIVATE nlohmann_json::nlohmann_json)
```
See [Integration](integration/index.md) for CMake in detail, all supported package managers (Conan, Meson, Bazel,
Conda, and more), and pkg-config.
## Explore the documentation
<div class="grid cards" markdown>
- :octicons-rocket-24:{ .lg .middle } __Features__
@@ -60,5 +112,11 @@ hide:
</div>
![JSON for Modern C++](images/json-light.svg#only-light)
![JSON for Modern C++](images/json-dark.svg#only-dark)
!!! info "Unreleased changes"
This documentation is built from the `develop` branch and may describe changes that are not part of a release
yet. Their version numbers are followed by an <span class="unreleased-version">unreleased</span> badge; see
[Releases](home/releases/index.md) for what shipped in each version.
The library is licensed under the [MIT License](home/license.md). The source code, issue tracker, and discussions
are on [GitHub](https://github.com/nlohmann/json).
+4 -4
View File
@@ -5,13 +5,13 @@ pick one:
```mermaid
flowchart TD
A[Add the library to a C++ project] --> B{{Already using CMake?}}
B -- no --> C{{Using pkg-config or plain Makefiles?}}
A[Add the library to a C++ project] --> B{Already using CMake?}
B -- no --> C{Using pkg-config or plain Makefiles?}
C -- yes --> D[pkg-config]
C -- no --> E[Copy the single header]
B -- yes --> F{{Library installed system-wide?}}
B -- yes --> F{Library installed system-wide?}
F -- yes --> G["find_package()"]
F -- no --> H{{Use a package manager?}}
F -- no --> H{Use a package manager?}
H -- yes --> I[Package manager]
H -- no --> J["add_subdirectory() or FetchContent"]
```
-100
View File
@@ -1,100 +0,0 @@
/*
* Brand look for Mermaid diagrams.
*
* Material for MkDocs loads Mermaid on demand and calls mermaid.initialize() with its own theme CSS. This script
* catches Mermaid when it arrives on window.mermaid and wraps two calls:
* - initialize(): adds the brand's font, spacing and shape rules below to Material's options;
* - render(): marks hexagons ({{...}} in a flowchart) with the class "nj-decision", since in the SVG a hexagon and
* a parallelogram are both a <polygon> that CSS cannot tell apart; and keeps a wide diagram at 80 % of its size
* or more, so its text stays readable and the diagram scrolls sideways instead (see .mermaid in brand.css).
* The colours come from the --md-mermaid-* variables in brand.css, so the diagrams follow the light/dark toggle.
*/
(function () {
"use strict";
var css = [
// text: Inter everywhere, labels on boxes semibold
".label, .nodeLabel, .nodeLabel p, .edgeLabel, .edgeLabel p, .cluster span, text { font-family: var(--md-mermaid-font-family) !important; }",
".nodeLabel, .nodeLabel p { font-weight: 600; }",
".edgeLabel, .edgeLabel p { color: var(--md-default-fg-color--light) !important; font-size: 13px; font-weight: 500; }",
// boxes: 1.5 px outline, 6 px corners; "(...)" nodes stay rounder than "[...]" nodes
".node rect, .node path, .node polygon, .node circle, .node ellipse { stroke-width: 1.5px !important; }",
".node rect:not([rx]) { rx: 6px; ry: 6px; }",
".node rect[rx] { rx: 14px; ry: 14px; }",
// decisions: solid blue hexagons with white text
".node.nj-decision polygon { fill: var(--nj-band) !important; stroke: var(--nj-band) !important; stroke-linejoin: round; }",
".node.nj-decision .nodeLabel, .node.nj-decision .nodeLabel p { color: #fff !important; }",
// lines: 1.5 px, solid unless written as -.-
".edgePath .path, .flowchart-link { stroke-width: 1.5px !important; }",
"marker path { stroke: none !important; }",
// class diagrams; a node with its own fill ("style X fill:#CCCCFF" on the exception pages) is the highlighted
// one and gets soft blue, which also works in dark mode
"g.classGroup rect, .classGroup line, g.node.default > g > path { stroke-width: 1.5px !important; }",
".node path[style*='fill'], .node rect[style*='fill'] { fill: var(--nj-blue-soft) !important; }",
".relation { stroke-width: 1.5px !important; }",
// sequence diagrams
".actor { stroke-width: 1.5px !important; rx: 6px; ry: 6px; }",
".actor-line { stroke-width: 1px; }",
".messageLine0, .messageLine1 { stroke-width: 1.5px !important; }",
".note { rx: 6px; ry: 6px; }"
].join("\n");
var options = {
theme: "base",
fontFamily: "Inter, sans-serif",
themeVariables: { fontFamily: "Inter, sans-serif", fontSize: "15px" },
flowchart: { curve: "basis", padding: 12, nodeSpacing: 36, rankSpacing: 48, wrappingWidth: 240 }
};
var minScale = 0.8;
function postProcess(svg) {
// shrink to fit the column, but not below minScale
svg = svg.replace(/^(<svg[^>]*style="[^"]*)max-width:\s*([\d.]+)px;/, function (all, head, width) {
return head + "max-width: " + width + "px; min-width: " + Math.round(width * minScale) + "px;";
});
// add class "nj-decision" to every flowchart node drawn as a hexagon (a polygon with six corners)
return svg.replace(/<g class="node ([^"]*)"([^>]*)><polygon points="([^"]*)"/g, function (all, cls, rest, points) {
var corners = points.trim().split(/\s+/).length;
return corners === 6 ? '<g class="node nj-decision ' + cls + '"' + rest + '><polygon points="' + points + '"' : all;
});
}
function brand(mermaid) {
if (!mermaid || typeof mermaid.initialize !== "function" || mermaid.njBrand) {
return mermaid;
}
var initialize = mermaid.initialize.bind(mermaid);
var render = mermaid.render.bind(mermaid);
mermaid.initialize = function (config) {
config = Object.assign({}, config || {});
config.theme = options.theme;
config.fontFamily = options.fontFamily;
config.themeVariables = Object.assign({}, config.themeVariables, options.themeVariables);
config.flowchart = Object.assign({}, config.flowchart, options.flowchart);
config.themeCSS = (config.themeCSS || "") + "\n" + css;
return initialize(config);
};
mermaid.render = function () {
return render.apply(null, arguments).then(function (result) {
result.svg = postProcess(result.svg);
return result;
});
};
mermaid.njBrand = true;
return mermaid;
}
// Mermaid ends with globalThis["mermaid"] = ..., which runs the setter below
var current = brand(window.mermaid);
Object.defineProperty(window, "mermaid", {
configurable: true,
get: function () { return current; },
set: function (value) { current = brand(value); }
});
})();
+7 -17
View File
@@ -1,6 +1,5 @@
# Project information
site_name: JSON for Modern C++
site_description: JSON for Modern C++ is a header-only C++11 library that makes JSON a first-class data type.
site_author: Niels Lohmann
site_url: https://json.nlohmann.me/
@@ -15,38 +14,33 @@ copyright: Copyright &copy; 2013-2026 Niels Lohmann
# Configuration
theme:
name: material
custom_dir: overrides
language: en
palette:
- media: '(prefers-color-scheme: light)'
scheme: default
primary: custom
accent: custom
primary: indigo
accent: indigo
toggle:
icon: material/brightness-7
name: Switch to dark mode
- media: '(prefers-color-scheme: dark)'
scheme: slate
primary: custom
accent: custom
primary: indigo
accent: indigo
toggle:
icon: material/brightness-4
name: Switch to light mode
icon:
repo: fontawesome/brands/github
font: false
logo: images/wordmark-header.svg
font:
text: Roboto
code: JetBrains Mono
features:
- navigation.instant
- navigation.tracking
- navigation.tabs
- navigation.indexes
- navigation.top
- navigation.footer
- toc.follow
- search.suggest
- search.highlight
- content.code.copy
- content.tabs.link
- content.action.edit
- content.action.view
@@ -625,7 +619,3 @@ plugins:
extra_css:
- css/custom.css
- css/brand.css
extra_javascript:
- js/mermaid-brand.js
-15
View File
@@ -1,15 +0,0 @@
{#- 404 page: say what happened and offer the three useful ways out -#}
{% extends "main.html" %}
{% block content %}
<div class="nj-404">
<p class="nj-404__code">// error 404</p>
<h1>This page does not exist</h1>
<p>The page may have moved or been renamed. Try the search in the header, or start from one of these pages.</p>
<p>
<a href="{{ '' | url }}" class="md-button md-button--primary">Home</a>
<a href="{{ 'features/' | url }}" class="md-button">Features</a>
<a href="{{ 'api/basic_json/' | url }}" class="md-button">API documentation</a>
</p>
</div>
{% endblock %}
-33
View File
@@ -1,33 +0,0 @@
{#- Home page: the brand's blue hero band below the tabs (selected with "template: home.html" in index.md) -#}
{% extends "main.html" %}
{% block tabs %}
{{ super() }}
<section class="nj-hero">
<div class="nj-hero__inner md-grid">
<div class="nj-hero__text">
<h1 class="nj-hero__title">JSON for <span>Modern C++</span></h1>
<p class="nj-hero__lead">What if JSON was part of modern C++?</p>
<p class="nj-hero__actions">
<a href="{{ 'integration/' | url }}" class="md-button md-button--primary nj-hero__button">Get started</a>
<a href="{{ 'api/basic_json/' | url }}" class="md-button nj-hero__button">API documentation</a>
</p>
</div>
<div class="nj-hero__code" aria-hidden="true">
<pre><code><span class="nj-k">#include</span> <span class="nj-s">&lt;iostream&gt;</span>
<span class="nj-k">#include</span> <span class="nj-s">&lt;nlohmann/json.hpp&gt;</span>
<span class="nj-k">using</span> json = nlohmann::json;
<span class="nj-k">int</span> main()
{
json j = json::parse(<span class="nj-s">R"({"happy": true})"</span>);
j[<span class="nj-s">"pi"</span>] = <span class="nj-n">3.141</span>;
j[<span class="nj-s">"list"</span>] = {<span class="nj-n">1</span>, <span class="nj-n">0</span>, <span class="nj-n">2</span>};
std::cout &lt;&lt; j.dump(<span class="nj-n">4</span>) &lt;&lt; <span class="nj-s">"\n"</span>;
}</code></pre>
</div>
</div>
</section>
{% endblock %}
@@ -1,103 +0,0 @@
"""Draws the parser-callback and iterator illustrations as an inline SVG into docs/images/.
The SVG is not an image: the pages include its text with `--8<-- "images/callback_events.svg, range-begin-end.svg and range-rbegin-rend.svg"`, so it picks up the colours and fonts of the
page (see the `railroad` and `nj-fig` rules in docs/css/brand.css). Run `python3 scripts/figures/iterator_callback_figures.py` from docs/mkdocs after
changing the drawing, and commit the generated file. In SVG text write the word JSON as `J&#8203;SON`, otherwise the
abbreviation extension wraps it in an <abbr> and the text disappears.
"""
import html
from pathlib import Path
OUT = Path(__file__).resolve().parents[2] / "docs" / "images"
CW = 9.6 # JetBrains Mono advance at 16px
def write(name, W, H, cls, tid, title, body, defs=""):
s = ['<svg class="nj-fig %s" viewBox="0 0 %d %d" role="img" aria-labelledby="%s" xmlns="http://www.w3.org/2000/svg">' % (cls, W, H, tid),
'<title id="%s">%s</title>' % (tid, html.escape(title).replace('J&amp;#8203;SON', 'J&#8203;SON'))]
if defs: s.append(defs)
s += body
s.append('</svg>')
open(OUT / name, "w").write("\n".join(s) + "\n")
# ---------- callback events ----------
def callback():
body, y0, lh = [], 74, 28
body.append('<text class="fg-h" x="16" y="28">J&#8203;SON to parse</text>')
body.append('<text class="fg-h" x="320" y="28">callback events</text>')
lines = [
(0, [("{", None), (None, "os")]),
(2, [('"name"', None), (None, "k"), (': "Berlin"', None), (None, "v"), (",", None)]),
(2, [('"location"', None), (None, "k"), (": [", None), (None, "as")]),
(4, [("52.519444", None), (None, "v"), (",", None)]),
(4, [("13.406667", None), (None, "v")]),
(2, [("]", None), (None, "ae"), (None, "v")]),
(0, [("}", None), (None, "oe"), (None, "v")]),
]
for i, (indent, parts) in enumerate(lines):
y = y0 + i * lh
x = 16 + indent * CW
for text, ev in parts:
if text is not None:
body.append('<text class="fg-code" x="%g" y="%d" xml:space="preserve">%s</text>' % (x, y, html.escape(text)))
x += CW * len(text)
else:
body.append('<circle class="cb-%s" cx="%g" cy="%d" r="5.5"/>' % (ev, x + 10, y - 5))
x += 20 if text is None else 0
legend = [("os", "object_start"), ("k", "key"), ("oe", "object_end"), ("as", "array_start"), ("ae", "array_end"), ("v", "value")]
for i, (ev, label) in enumerate(legend):
y = 74 + i * 30
body.append('<circle class="cb-%s" cx="327" cy="%d" r="6"/>' % (ev, y - 5))
body.append('<text class="fg-code fg-leg" x="344" y="%d">%s</text>' % (y, label))
write("callback_events.svg", 480, 262, "nj-fig--cb", "fig-cb-title",
"Example of the parse events a callback sees for the J&#8203;SON text {\"name\": \"Berlin\", \"location\": [52.519444, 13.406667]}: object_start after the opening brace, key after each key, array_start after the opening bracket, value after each value, array_end after the closing bracket, and object_end after the closing brace. Every array and object also produces a value event when it is complete.", body)
# ---------- ranges ----------
BW, GAP, X0 = 30, 4, 30
def cell_x(i): return X0 + i * (BW + GAP)
def cell(i, y, kind):
c = "fg-cell" + (" fg-cell--end" if kind == "end" else "")
return '<rect class="%s" x="%d" y="%d" width="%d" height="30" rx="5"/>' % (c, cell_x(i), y, BW)
def pill(cx, y, label, W, label_w):
x = max(4, min(W - 4 - label_w, cx - label_w / 2))
return ['<rect class="fg-pill" x="%g" y="%d" width="%d" height="24" rx="6"/>' % (x, y, label_w),
'<text class="fg-pilltext" x="%g" y="%d">%s</text>' % (x + label_w / 2, y + 16.5, label)]
def arrow(x, ya, yb, mid, dashed=False):
return '<line class="fg-arrow%s" x1="%g" y1="%g" x2="%g" y2="%g" marker-end="url(#%s)"/>' % (" fg-arrow--dash" if dashed else "", x, ya, x, yb, mid)
def ranges1():
W, H, mid = 600, 150, "fig-be-arrow"
defs = '<defs><marker id="%s" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto"><path class="fg-head" d="M0 1 L10 5 L0 9 z"/></marker></defs>' % mid
body = []
for i in range(15): body.append(cell(i, 62, "elem"))
body.append(cell(15, 62, "end"))
body += pill(cell_x(0) + BW / 2, 8, "begin", W, 60); body.append(arrow(cell_x(0) + BW / 2, 32, 60, mid))
body += pill(cell_x(15) + BW / 2, 8, "end", W, 48); body.append(arrow(cell_x(15) + BW / 2, 32, 60, mid))
body.append('<text class="fg-note fg-note--r" x="%d" y="132">Past-the-last element</text>' % (cell_x(15) + BW / 2 - 18))
body.append(arrow(cell_x(15) + BW / 2, 124, 96, mid))
write("range-begin-end.svg", W, H, "nj-fig--range", "fig-be-title",
"A row of fifteen elements followed by a dashed past-the-last position. begin() points to the first element and end() points to the past-the-last position.", body, defs)
def ranges2():
W, H, mid = 600, 236, "fig-rbe-arrow"
defs = '<defs><marker id="%s" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto"><path class="fg-head" d="M0 1 L10 5 L0 9 z"/></marker></defs>' % mid
body = []
ytop, ybot = 62, 144
body.append(cell(0, ytop, "end"))
for i in range(1, 16): body.append(cell(i, ytop, "elem"))
for i in range(15): body.append(cell(i, ybot, "elem"))
body.append(cell(15, ybot, "end"))
body += pill(cell_x(0) + BW / 2, 8, "rend", W, 52); body.append(arrow(cell_x(0) + BW / 2, 32, 60, mid))
body += pill(cell_x(15) + BW / 2, 8, "rbegin", W, 68); body.append(arrow(cell_x(15) + BW / 2, 32, 60, mid))
body.append('<text class="fg-note fg-note--l" x="%d" y="24">Reverse past-the-last element</text>' % (cell_x(0) + BW / 2 + 36))
for col in (0, 15):
x = cell_x(col) + BW / 2
body.append('<line class="fg-arrow fg-arrow--dash" x1="%g" y1="94" x2="%g" y2="%d"/>' % (x, x, ybot - 2))
body.append('<text class="fg-note" x="300" y="123">A reverse iterator stores an iterator to the next element</text>')
body.append('<text class="fg-note" x="300" y="141" dy="0" style="display:none"></text>')
body += pill(cell_x(0) + BW / 2, 204, "begin", W, 60); body.append(arrow(cell_x(0) + BW / 2, 202, 178, mid))
body += pill(cell_x(15) + BW / 2, 204, "end", W, 48); body.append(arrow(cell_x(15) + BW / 2, 202, 178, mid))
body.append('<text class="fg-note fg-note--r" x="%d" y="221">Past-the-last element</text>' % (cell_x(15) + BW / 2 - 40))
write("range-rbegin-rend.svg", W, H, "nj-fig--range", "fig-rbe-title",
"Two rows of sixteen cells. The upper row shows the reversed sequence: rend() points to a dashed position before the first element and rbegin() to the last element. The lower row shows the forward sequence: begin() points to the first element and end() to a dashed position after the last element. A reverse iterator stores an iterator to the next element, so rend() corresponds to begin() and rbegin() to end().", body, defs)
callback(); ranges1(); ranges2()
@@ -1,76 +0,0 @@
"""Draws the JSON number railroad diagram as an inline SVG into docs/images/.
The SVG is not an image: the pages include its text with `--8<-- "images/json_syntax_number.svg"`, so it picks up the colours and fonts of the
page (see the `railroad` and `nj-fig` rules in docs/css/brand.css). Run `python3 scripts/figures/railroad_number.py` from docs/mkdocs after
changing the drawing, and commit the generated file. In SVG text write the word JSON as `J&#8203;SON`, otherwise the
abbreviation extension wraps it in an <abbr> and the text disappears.
"""
import math
from pathlib import Path
OUT = Path(__file__).resolve().parents[2] / "docs" / "images"
Y0, Y1, Y2, Y3 = 44, 92, 136, 180
R = 10
paths, shapes, texts = [], [], []
def rounded(pts, r=R):
d = "M%g %g" % pts[0]
for i in range(1, len(pts) - 1):
(x0, y0), (x1, y1), (x2, y2) = pts[i-1], pts[i], pts[i+1]
l1 = math.hypot(x1-x0, y1-y0); l2 = math.hypot(x2-x1, y2-y1)
rr = min(r, l1/2, l2/2)
ax, ay = x1 - (x1-x0)/l1*rr, y1 - (y1-y0)/l1*rr
bx, by = x1 + (x2-x1)/l2*rr, y1 + (y2-y1)/l2*rr
d += " L%g %g Q%g %g %g %g" % (ax, ay, x1, y1, bx, by)
d += " L%g %g" % pts[-1]
return d
def path(*pts): paths.append(rounded(list(pts)))
def circle(cx, cy, ch):
shapes.append('<circle class="rr-bg" cx="%g" cy="%g" r="13"/>' % (cx, cy))
shapes.append('<circle class="rr-t" cx="%g" cy="%g" r="13"/>' % (cx, cy))
texts.append('<text class="rr-c" x="%g" y="%g">%s</text>' % (cx, cy+5, ch))
def box(x0, x1, cy, label):
shapes.append('<rect class="rr-bg" x="%g" y="%g" width="%g" height="28" rx="6"/>' % (x0, cy-14, x1-x0))
shapes.append('<rect class="rr-n" x="%g" y="%g" width="%g" height="28" rx="6"/>' % (x0, cy-14, x1-x0))
texts.append('<text class="rr-b" x="%g" y="%g">%s</text>' % ((x0+x1)/2, cy+5, label))
def loop(xr, xl, cy): # return path below a box row
path((xr, cy), (xr, cy+34), (xl, cy+34), (xl, cy))
# start / main
path((8, Y0), (850, Y0))
# A: optional minus
path((24, Y0), (38, Y0), (38, Y1), (59, Y1)); circle(72, Y1, "&#8722;"); path((85, Y1), (106, Y1), (106, Y0), (120, Y0))
# B: 0 | 1-9 digit*
circle(257, Y0, "0")
path((120, Y0), (134, Y0), (134, Y1), (160, Y1)); box(160, 242, Y1, "digit 1&#8211;9")
path((242, Y1), (380, Y1), (380, Y0), (396, Y0))
path((242, Y1), (258, Y1), (258, Y2), (278, Y2)); box(278, 332, Y2, "digit")
path((332, Y2), (348, Y2), (348, Y1)); loop(340, 270, Y2)
# C: fraction
path((396, Y0), (410, Y0), (410, Y1), (425, Y1)); circle(438, Y1, ".")
path((451, Y1), (462, Y1)); box(462, 516, Y1, "digit"); loop(524, 456, Y1)
path((516, Y1), (530, Y1), (530, Y0), (546, Y0))
# D: exponent
path((546, Y0), (560, Y0), (560, Y1), (577, Y1)); circle(590, Y1, "e")
path((546, Y0), (560, Y0), (560, Y2), (577, Y2)); circle(590, Y2, "E")
path((603, Y1), (728, Y1)); path((603, Y2), (620, Y2), (620, Y1))
path((640, Y1), (654, Y1), (654, Y2), (670, Y2)); circle(683, Y2, "&#8722;")
path((640, Y1), (654, Y1), (654, Y3), (670, Y3)); circle(683, Y3, "+")
path((696, Y2), (712, Y2), (712, Y1)); path((696, Y3), (712, Y3), (712, Y1))
path((728, Y1), (746, Y1)); box(746, 800, Y1, "digit"); loop(808, 738, Y1)
path((800, Y1), (816, Y1), (816, Y0), (832, Y0))
svg = ['<svg class="railroad" viewBox="0 0 860 214" role="img" aria-labelledby="rr-title" xmlns="http://www.w3.org/2000/svg">',
'<title id="rr-title">Railroad diagram of the J&#8203;SON number syntax: optional minus, then 0 or a digit from 1 to 9 followed by digits, then an optional fraction (a dot and digits) and an optional exponent (e or E, an optional sign, and digits).</title>',
'<g class="rr-track" fill="none" stroke="currentColor" stroke-opacity=".6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">']
for d in paths: svg.append('<path d="%s"/>' % d)
svg.append('</g>')
svg.append('<polygon class="rr-arrow" points="2,%d 12,%d 2,%d"/>' % (Y0-6, Y0, Y0+6))
svg.append('<polygon class="rr-arrow" points="846,%d 858,%d 846,%d"/>' % (Y0-6, Y0, Y0+6))
svg += shapes + texts
svg.append('<text class="rr-l rr-name" x="24" y="%d">number</text>' % (Y0-12))
svg.append('<text class="rr-l" x="420" y="%d">fraction</text>' % (Y0-12))
svg.append('<text class="rr-l" x="580" y="%d">exponent</text>' % (Y0-12))
svg.append('</svg>')
open(OUT / "json_syntax_number.svg", "w").write("\n".join(svg) + "\n")
-1
View File
@@ -1 +0,0 @@
fonts/
-73
View File
@@ -1,73 +0,0 @@
# Rebuild the animated feature slideshow of the home page (docs/images/json-light.svg and json-dark.svg) when
# slides.md, build.py or the fonts change. Needs uv (build.py declares its dependencies inline, PEP 723), curl,
# unzip and shasum. Run it from docs/mkdocs/scripts/slideshow.
#
# make download missing fonts and build both SVGs
# make fonts download only the fonts
# make SECONDS=12 change the time per slide
# make clean remove the generated SVGs
# make distclean also remove the downloaded fonts
SECONDS ?= 15
IMAGES := ../../docs/images
LIGHT := $(IMAGES)/json-light.svg
DARK := $(IMAGES)/json-dark.svg
RUN := uv run --script build.py
FONTS := fonts/Inter.ttf fonts/JetBrainsMono.ttf
DEPS := build.py slides.md $(FONTS)
# Pinned sources; the checksums make a changed upstream file fail instead of silently changing the output.
# Inter: Google Fonts repository at a fixed commit (variable font with opsz and wght axes).
INTER_URL := https://raw.githubusercontent.com/google/fonts/0b58fb370093f9a9f4ff785d94405710b79de67c/ofl/inter/Inter%5Bopsz%2Cwght%5D.ttf
# JetBrains Mono: release 2.304 (the variable font in fonts/variable/ of the zip).
JBM_URL := https://github.com/JetBrains/JetBrainsMono/releases/download/v2.304/JetBrainsMono-2.304.zip
JBM_ZIP := fonts/JetBrainsMono-2.304.zip
SHA256_Inter.ttf := 29160a80ff49ddcab2c97711247e08b1fab27a484a329ce8b813d820dc559031
SHA256_JetBrainsMono.ttf := 662a196d58f1183bf2d77428b6d5283fe3f45161ab021bea4036bc98e5cac016
SHA256_JetBrainsMono-2.304.zip := 6f6376c6ed2960ea8a963cd7387ec9d76e3f629125bc33d1fdcd7eb7012f7bbf
# Check $@.tmp against its pinned checksum, then move it into place.
define verify
echo '$(SHA256_$(notdir $@)) $@.tmp' | shasum -a 256 -c --quiet - || { rm -f $@.tmp; exit 1; }
mv $@.tmp $@
endef
.PHONY: all fonts clean distclean
.INTERMEDIATE: $(JBM_ZIP)
all: $(LIGHT) $(DARK)
fonts: $(FONTS)
# fixed colours for the docs: an <img> cannot see Material's theme toggle, so the home page includes both
# and shows the one that matches
$(LIGHT): $(DEPS)
$(RUN) $@.tmp $(SECONDS) light
mv $@.tmp $@
$(DARK): $(DEPS)
$(RUN) $@.tmp $(SECONDS) dark
mv $@.tmp $@
fonts/Inter.ttf:
mkdir -p fonts
curl -fsSL -m 300 -o $@.tmp '$(INTER_URL)'
$(verify)
$(JBM_ZIP):
mkdir -p fonts
curl -fsSL -m 300 -o $@.tmp '$(JBM_URL)'
$(verify)
fonts/JetBrainsMono.ttf: | $(JBM_ZIP)
unzip -p $(JBM_ZIP) 'fonts/variable/JetBrainsMono[[]wght].ttf' > $@.tmp
$(verify)
clean:
rm -f $(LIGHT) $(DARK) $(IMAGES)/*.svg.tmp
distclean: clean
rm -f $(FONTS) fonts/*.tmp $(JBM_ZIP)
-27
View File
@@ -1,27 +0,0 @@
# Feature slideshow of the home page
`make` builds `docs/images/json-light.svg` and `json-dark.svg`, the animated feature tour on the home page. They replace
`json.gif` there. All text is outlined, so the SVGs need no fonts. Material's theme toggle cannot be seen from an
`<img>`, so the home page includes both files (`#only-light`, `#only-dark`) and Material shows the one that matches.
The slides are in [`slides.md`](slides.md): one `# Title` per slide (`{badge: TEXT}` at the end adds a red chip), `- `
bullets, and one fenced code block. `build.py` adds the title and closing slides around them, so the slide count follows
the file. Every snippet should compile against the current `develop`. Use a non-breaking space to keep words together
("16 bytes").
```bash
make # download missing fonts, build both SVGs
make SECONDS=12
make clean # remove the SVGs; make distclean also removes the fonts
```
Run it from this directory. It needs [uv](https://docs.astral.sh/uv/), plus curl, unzip and shasum for the fonts:
`build.py` declares its dependencies inline (PEP 723, pinned, so the output stays byte-identical) and `uv run --script`
provides them. `fonts/` is git-ignored: Inter (variable, Google Fonts repository) and JetBrains Mono 2.304 (variable)
are downloaded from pinned URLs and checked against their SHA-256; a changed upstream file stops the build.
`./build.py OUT.svg [seconds] [auto|light|dark]` works directly too. A mistake in `slides.md` stops the build with
`slides.md:LINE` and leaves the old SVGs in place.
Layout: title 48 px, bullets 24 px, code 19 px; the build warns when a slide's code would drop below 18 px. Syntax colours
are the `code-*` tokens of the brand (also used by `css/brand.css`). A CSS animation dissolves each slide over the
previous one and fills the progress bar; with reduced motion only the first slide shows.
-461
View File
@@ -1,461 +0,0 @@
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.9"
# dependencies = ["fonttools==4.67.0", "uharfbuzz==0.57.0"]
# ///
"""Animated SVG slideshow prototype for nlohmann/json, all text outlined (harfbuzz shaping, glyphs as <path> + <use>).
Usage: uv run --script build.py OUT.svg [seconds-per-slide] [auto|light|dark]
auto (the default) follows prefers-color-scheme. light and dark fix the colours, for pages with their own theme
toggle (MkDocs Material), which an <img> cannot see.
The pinned versions keep the output byte-identical; a different harfbuzz may shape slightly differently.
"""
import re, sys, os
import uharfbuzz as hb
from fontTools.pens.svgPathPen import SVGPathPen
HERE = os.path.dirname(os.path.abspath(__file__))
FONTS = os.path.join(HERE, "fonts")
W, H = 1200, 675
TOTAL = 0 # number of slides, set once slides.md is read (for the "02 / 20" labels and the progress segments)
# ---------------------------------------------------------------- glyphs
class Face:
def __init__(self, file, key, feats=None):
blob = hb.Blob.from_file_path(os.path.join(FONTS, file))
self.face = hb.Face(blob)
self.upem = self.face.upem
self.key = key
self.feats = feats or {}
def font(self, var):
f = hb.Font(self.face)
f.set_variations(var)
return f
INTER = Face("Inter.ttf", "i", {"cv11": True, "ss03": True, "kern": True})
MONO = Face("JetBrainsMono.ttf", "m", {"calt": False, "liga": False})
GLYPHS = {} # (face, var, gid) -> id
DEFS = []
def gid_ref(face, font, var, gid):
k = (face.key, tuple(sorted(var.items())), gid)
if k not in GLYPHS:
pen = SVGPathPen(None, ntos=lambda v: str(round(v)))
font.draw_glyph_with_pen(gid, pen)
d = pen.getCommands()
gidname = f"g{len(GLYPHS):x}"
GLYPHS[k] = gidname if d else None
if d:
DEFS.append(f'<path id="{gidname}" d="{d}"/>')
return GLYPHS[k]
def shape(face, text, wght, size, track=0.0):
if not text:
return [], 0, face.upem
var = {"wght": wght}
if face is INTER:
var["opsz"] = max(14, min(32, size))
font = face.font(var)
buf = hb.Buffer()
buf.add_str(text)
buf.guess_segment_properties()
hb.shape(font, buf, face.feats)
out, x = [], 0
tr = track * face.upem
for info, pos in zip(buf.glyph_infos, buf.glyph_positions):
out.append((gid_ref(face, font, var, info.codepoint), x + pos.x_offset, pos.y_offset))
x += pos.x_advance + tr
return out, x - tr, face.upem
def width(text, face=INTER, wght=400, size=16, track=0.0):
_, w, upem = shape(face, text, wght, size, track)
return w * size / upem
def run(text, x, y, size, wght=400, cls="tx", face=INTER, track=0.0):
"""One line of text, baseline at y. Returns (svg, width_px)."""
glyphs, w, upem = shape(face, text, wght, size, track)
s = size / upem
uses = "".join(f'<use href="#{g}" x="{gx}"' + (f' y="{-gy}"' if gy else "") + "/>"
for g, gx, gy in glyphs if g)
return (f'<g class="{cls}" transform="translate({x:g} {y:g}) scale({s:.6g} {-s:.6g})">{uses}</g>', w * s)
def runs(parts, x, y, size, face=INTER, track=0.0):
"""Several (text, wght, cls) pieces on one line."""
out = []
for text, wght, cls in parts:
svg, w = run(text, x, y, size, wght, cls, face, track)
out.append(svg)
x += w + track * size
return "".join(out), x
def wrap(text, maxw, size, wght=400):
lines = _wrap(text, maxw, size, wght)
# no one-word widow: pull the last word of the previous line down if it still fits
if len(lines) > 1 and " " not in lines[-1] and " " in lines[-2]:
head, last = lines[-2].rsplit(" ", 1)
if width(last + " " + lines[-1], size=size, wght=wght) <= maxw:
lines[-2:] = [head, last + " " + lines[-1]]
return lines
def _wrap(text, maxw, size, wght=400):
lines, cur = [], ""
for word in text.split(" "):
t = (cur + " " + word).strip()
if cur and width(t, size=size, wght=wght) > maxw:
lines.append(cur)
cur = word
else:
cur = t
return lines + [cur]
# ---------------------------------------------------------------- pieces
def wordmark(x, y, size, on="page"):
"""The wordmark with hand kerning around the slash (see the logo in the brand kit). Below 32 px it switches to the
small optical master (nlohmann 250 / slash 450), because Thin falls apart at footer size."""
a, sl = ("wm-a", "wm-s") if on == "page" else ("on-a", "on-s")
small = size < 32
w = (250, 450, 800) if small else (100, 400, 800)
k = (0.004, 0.024) if small else (-0.006, 0.012)
out = []
for i, (t, wg, c) in enumerate([("nlohmann", w[0], a), ("/", w[1], sl), ("json", w[2], a)]):
svg, wd = run(t, x, y, size, wg, c, track=-0.02)
out.append(svg)
x += wd - 0.02 * size + (k[i] * size if i < 2 else 0)
return "".join(out), x
KW = {"using", "auto", "return", "const", "struct", "for", "override"}
TYPES = {"int", "void", "bool", "double", "char", "float"}
CONSTANTS = {"true", "false", "nullptr"}
TOKEN = re.compile(r'(//.*$)|(R"\(.*?\)"|R"\(.*$|"(?:[^"\\]|\\.)*"|\'(?:[^\'\\]|\\.)\')|(<[\w/.]+>(?=\s*$))|(#\w+)'
r'|(\b\d+(?:\.\d+)?\b)|(\b[A-Za-z_]\w*\b)|(\s+)|(.)')
def code_block(lines, x, y, size, lh):
"""Lines of C++; a raw string literal may span lines."""
out, in_raw = [], False
for line in lines:
if line:
svg, in_raw = code_line(line, x, y, size, in_raw)
out.append(svg)
y += lh
else:
y += lh * BLANK
return "".join(out)
BLANK = 0.55 # an empty code line is a paragraph break, not a full line
def code_height(lines, size, lh):
n_full = sum(1 for l in lines if l)
n_blank = len(lines) - n_full
return lh * (n_full - 1) + lh * BLANK * n_blank + size
def code_line(line, x, y, size, in_raw=False):
if line.startswith("$ "):
return runs([("$ ", 400, "tx3"), (line[2:], 400, "tx")], x, y, size, MONO)[0], False
out, cx = [], x
if in_raw:
end = line.find(')"')
head = line if end < 0 else line[:end + 2]
svg, w = run(head, cx, y, size, 400, "str", MONO)
out.append(svg)
if end < 0:
return "".join(out), True
cx += w
line = line[end + 2:]
in_raw = False
for m in TOKEN.finditer(line):
com, st, inc, pp, num, ident, ws, other = m.groups()
t = m.group(0)
if st and st.startswith('R"(') and not st.endswith(')"'):
in_raw = True
face, wght, cls = MONO, 400, "tx"
if com:
cls = "com" # upright: JetBrains Mono Italic's cursive f reads as a florin at README size
elif st or inc:
cls = "str"
elif pp:
cls = "pp"
elif ident in KW:
wght, cls = 600, "kw"
elif ident in TYPES:
cls = "ty"
elif num or ident in CONSTANTS:
cls = "num"
elif ident and line[m.end():m.end() + 1] == "(":
cls = "fn" # a call or definition: name directly followed by "("
svg, w = run(t, cx, y, size, wght, cls, face)
if not ws:
out.append(svg)
cx += w
return "".join(out), in_raw
SEG, SEG_GAP = 20, 5
def progress(idx, n_proto, y):
"""TOTAL segments at the bottom right; earlier slides full, current one fills over the slide's time."""
seg, gap = SEG, SEG_GAP
x0 = W - 64 - TOTAL * seg - (TOTAL - 1) * gap
out = []
for k in range(TOTAL):
x = x0 + k * (seg + gap)
out.append(f'<rect class="seg" x="{x}" y="{y}" width="{seg}" height="3" rx="1.5"/>')
if k < idx:
out.append(f'<rect class="seg-on" x="{x}" y="{y}" width="{seg}" height="3" rx="1.5"/>')
elif k == idx:
out.append(f'<rect class="seg-on fill" x="{x}" y="{y}" width="{seg}" height="3" rx="1.5"/>')
return "".join(out)
def pattern_def():
tile = [("{", 14, 34), ("]", 92, 22), (":", 136, 60), ("[", 52, 82), (",", 118, 112),
("}", 20, 140), (":", 78, 150), ("{", 140, 158)]
glyphs = "".join(run(c, x, y, 26, 600, "pat", MONO)[0] for c, x, y in tile)
return f'<pattern id="pat" width="160" height="160" patternUnits="userSpaceOnUse">{glyphs}</pattern>'
# ---------------------------------------------------------------- slides
CODE_SIZES = []
SLOGAN = "What if JSON was part of modern C++?"
def band_bg():
"""Hero gradient plus the syntax pattern, faded out towards the text side (left)."""
return ('<rect width="1200" height="675" fill="url(#hero)"/>'
'<rect width="1200" height="675" fill="url(#pat)" mask="url(#patfade)"/>')
def footer_band(idx, n, left=None):
out = []
if left:
out.append(run(left, 64, FOOT_Y, 20, 600, "on-tx")[0])
out.append(progress(idx, n, FOOT_Y - 7.5).replace('class="seg"', 'class="seg on-band"').replace('class="seg-on', 'class="seg-on on-band'))
return "".join(out)
FOOT_Y = 640 # one footer baseline on every slide: wordmark or URL left, progress right
def slide_title(idx, n):
out = [band_bg()]
x = 96
label = "VERSION 3.13.0"
lw = width(label, MONO, 500, 15, 0.04)
out.append(f'<rect class="chip" x="{x}" y="186" width="{lw + 28:.1f}" height="34" rx="4"/>')
out.append(run(label, x + 14, 208.5, 15, 500, "on-tx", MONO, 0.04)[0])
out.append(wordmark(x - 6, 336, 112, on="band")[0])
out.append(runs([("// ", 400, "w70"), (SLOGAN, 400, "on-s")], x, 404, 26, MONO)[0])
out.append(footer_band(idx, n, "github.com/nlohmann/json"))
return "".join(out)
def slide_feature(idx, n, title, bullets, code, badge=None):
out = ['<rect class="bg" width="1200" height="675"/>']
x, top, bottom = 64, 136, 596
# title row: 48/800, -3 %
svg, tw = run(title, x - 2, 98, 48, 800, "tx", track=-0.03)
out.append(svg)
if badge:
cx = x + tw + 16
cw = width(badge, MONO, 600, 13, 0.06) + 18
out.append(f'<rect class="red" x="{cx:.1f}" y="68.5" width="{cw:.1f}" height="26" rx="4"/>')
out.append(run(badge, cx + 9, 86.2, 13, 600, "on-tx", MONO, 0.06)[0])
# code panel: fixed left edge, fixed width, fixed top on every slide; only its height follows the code
pad, lf = 28, 1.45
px, pw = 488, 1136 - 488
em = max(width(l, MONO, 400, 1) for l in code)
cs = 19.0
while cs > 14 and (em * cs > pw - 2 * pad or code_height(code, cs, lf * cs) > bottom - top - 2 * pad):
cs -= 0.5
CODE_SIZES.append((title, cs))
if cs < 18:
print(f" warning: {title!r} code at {cs} px ({em * 19:.0f} px wide at 19 px)", file=sys.stderr)
lh = lf * cs
ph = max(2 * pad + code_height(code, cs, lh), 240)
out.append(f'<rect class="panel" x="{px}" y="{top}" width="{pw}" height="{ph:.0f}" rx="6"/>')
first = top + pad + cs * 0.78
out.append(code_block(code, px + pad, first, cs, lh))
# bullets: 24/400 on 34, first baseline level with the first code baseline
colw = px - x - 40
by = first
for b in bullets:
out.append(f'<rect class="blk" x="{x}" y="{by - 10.6:.1f}" width="8" height="8" rx="1.5"/>')
for line in wrap(b, colw - 24, 24):
out.append(run(line, x + 24, by, 24, 400, "tx2")[0])
by += 34
by += 16
# footer: small-master wordmark left, progress right, no rule, no numeral
out.append(wordmark(64, FOOT_Y, 22)[0])
out.append(progress(idx, n, FOOT_Y - 7.5))
return "".join(out)
def slide_closing(idx, n):
out = [band_bg()]
x = 96
out.append(wordmark(x - 4, 146, 80, on="band")[0])
out.append(runs([("// ", 400, "w70"), (SLOGAN, 400, "on-s")], x, 200, 22, MONO)[0])
info = [
("{", None),
(' "copyright": ', "(c) 2013-2026 Niels Lohmann"),
(' "license": ', "MIT"),
(' "docs": ', "json.nlohmann.me"),
(' "github": ', "github.com/nlohmann/json"),
(' "sponsor": ', "github.com/sponsors/nlohmann"),
(' "mail": ', "mail@nlohmann.me"),
("}", None),
]
cs, lh, pad = 21, 33, 28
py = 244
pw = max(width(k + '"' + (v or "") + '",', MONO, 400, cs) for k, v in info) + 2 * pad
out.append(f'<rect class="on-panel" x="{x}" y="{py}" width="{pw:.0f}" height="{2 * pad + lh * (len(info) - 1) + cs:.0f}" rx="6"/>')
y = py + pad + cs * 0.8
for k, (key, val) in enumerate(info):
if val is None:
out.append(run(key, x + pad, y, cs, 400, "on-tx", MONO)[0])
else:
comma = "," if k < len(info) - 2 else ""
out.append(runs([(key, 400, "on-tx"), (f'"{val}"', 400, "on-s"), (comma, 400, "on-tx")], x + pad, y, cs, MONO)[0])
y += lh
out.append(footer_band(idx, n))
return "".join(out)
def load_slides(path):
"""Feature slides from Markdown: '# Title {badge: TEXT}', '- bullet' lines, one fenced code block."""
text = re.sub(r"<!--.*?-->", "", open(path).read(), flags=re.S)
slides, cur, in_code = [], None, False
for no, line in enumerate(text.split("\n"), 1):
if in_code:
if line.startswith("```"):
in_code = False
else:
cur["code"].append(line)
elif line.startswith("```"):
in_code = True
elif line.startswith("# "):
title, badge = line[2:].strip(), None
m = re.search(r"\s*\{badge:\s*(.*?)\}$", title)
if m:
title, badge = title[:m.start()], m.group(1)
cur = {"title": title, "bullets": [], "code": [], "badge": badge}
slides.append(cur)
elif line.startswith("- ") and cur:
cur["bullets"].append(line[2:].strip())
elif line[:1] in " \t" and line.strip() and cur and cur["bullets"]:
cur["bullets"][-1] += " " + line.strip()
elif line.strip():
raise SystemExit(f"{path}:{no}: unexpected line: {line!r}")
for sl in slides:
while sl["code"] and not sl["code"][-1].strip():
sl["code"].pop()
if not sl["code"]:
raise SystemExit(f"{path}: slide {sl['title']!r} has no code block")
return slides
FEATURES = load_slides(os.path.join(HERE, "slides.md"))
def _feature(f):
return lambda i, n: slide_feature(i, n, f["title"], f["bullets"], f["code"], f["badge"])
SLIDES = [slide_title] + [_feature(f) for f in FEATURES] + [slide_closing]
TOTAL = len(SLIDES)
# ---------------------------------------------------------------- assemble
def build(sec):
n = len(SLIDES)
T = n * sec
fade = 0.6
a, b, c = fade / T * 100, sec / T * 100, (sec + fade) / T * 100
def cls(i):
return "s s0" if i == 0 else "s sl" if i == n - 1 else "s"
bodies = [f'<g class="{cls(i)}" style="animation-delay:{i * sec:g}s">{fn(i, n)}</g>' for i, fn in enumerate(SLIDES)]
css = f"""
.bg{{fill:#fff}} .tx{{fill:#0f1419}} .tx2{{fill:#3d444f}} .tx3{{fill:#8a919c}} .com{{fill:#646d7a}}
.bl{{fill:#1747a6}} .blk{{fill:#1747a6}} .kw{{fill:#1747a6}} .ty{{fill:#0d6e72}} .fn{{fill:#6b3fb8}} .str{{fill:#23793a}} .num{{fill:#b04a07}} .pp{{fill:#a3247a}}
.panel{{fill:#f5f7fa;stroke:#e3e6eb;stroke-width:1}} .rule{{stroke:#e3e6eb;stroke-width:1}}
.wm-a{{fill:#0f1419}} .wm-s{{fill:#1747a6}}
.on-a,.on-tx{{fill:#fff}} .red{{fill:#d93a3f}} .on-panel{{fill:#fff;fill-opacity:.08;stroke:#fff;stroke-opacity:.16}} .on-s{{fill:#bcd3fb}} .w70{{fill:#fff;fill-opacity:.7}} .on-tx2{{fill:#dbe6fb}} .chip{{fill:#fff;fill-opacity:.14}}
.pat{{fill:#fff;fill-opacity:.09}}
.h1{{stop-color:#1d55c4}} .h2{{stop-color:#123a8a}}
.seg{{fill:#e3e6eb}} .seg-on{{fill:#1747a6}}
.seg.on-band{{fill:#fff;fill-opacity:.25}} .seg-on.on-band{{fill:#fff;fill-opacity:1}}
@media (prefers-color-scheme:dark){{
.bg{{fill:#0f1218}} .tx,.wm-a{{fill:#f2f4f7}} .tx2{{fill:#c3c8d1}} .tx3{{fill:#7b828e}} .com{{fill:#8b929e}}
.bl,.blk,.wm-s,.seg-on,.kw{{fill:#7aa7f5}} .ty{{fill:#4fd1c5}} .fn{{fill:#c4a3ff}} .str{{fill:#7ed491}} .num{{fill:#f5a65b}} .pp{{fill:#f48fc8}}
.panel{{fill:#171b23;stroke:#272c36}} .rule{{stroke:#272c36}} .seg{{fill:#272c36}}
.red{{fill:#c8383d}} .h1{{stop-color:#163f93}} .h2{{stop-color:#0c2556}}
}}
.s{{opacity:0;animation:show {T:g}s linear infinite backwards}}
.fill{{transform-box:fill-box;transform-origin:left;animation:fill {T:g}s linear infinite backwards;animation-delay:inherit}}
.s0{{animation-name:show0}} .sl{{animation-name:showl}}
@keyframes show{{0%{{opacity:0;animation-timing-function:cubic-bezier(.2,.6,.2,1)}} {a:.4f}%{{opacity:1}} {c:.4f}%{{opacity:1}} {c + .01:.4f}%{{opacity:0}} 100%{{opacity:0}}}}
@keyframes show0{{0%{{opacity:1}} {c:.4f}%{{opacity:1}} {c + .01:.4f}%{{opacity:0}} 100%{{opacity:0}}}}
@keyframes showl{{0%{{opacity:0;animation-timing-function:cubic-bezier(.2,.6,.2,1)}} {a:.4f}%{{opacity:1}} {b:.4f}%{{opacity:1}} {c:.4f}%{{opacity:0}} 100%{{opacity:0}}}}
@keyframes fill{{0%{{transform:scaleX(0)}} {b:.4f}%{{transform:scaleX(1)}} 100%{{transform:scaleX(1)}}}}
@media (prefers-reduced-motion:reduce){{.s,.fill{{animation:none}} .s:first-of-type{{opacity:1}}}}
"""
css = re.sub(r"\n\s*", "", css)
pat = pattern_def()
return (f'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 {W} {H}" width="{W}" height="{H}" role="img">'
f'<title>JSON for Modern C++: feature slideshow (animated, {n} slides, {sec:g} s each)</title>'
f'<style>{css}</style>'
f'<defs>{"".join(DEFS)}{pat}'
f'<linearGradient id="hero" gradientTransform="rotate(70 .5 .5)"><stop class="h1" offset="0"/><stop class="h2" offset="1"/></linearGradient>'
f'<linearGradient id="fadeg" x1="0" x2="1" y1="0" y2="0"><stop offset=".25" stop-color="#fff" stop-opacity="0"/><stop offset=".85" stop-color="#fff" stop-opacity="1"/></linearGradient>'
f'<mask id="patfade" maskUnits="userSpaceOnUse" x="0" y="0" width="{W}" height="{H}"><rect width="{W}" height="{H}" fill="url(#fadeg)"/></mask></defs>'
f'<rect class="bg" width="{W}" height="{H}"/>'
f'{"".join(bodies)}</svg>')
def fix_scheme(svg, scheme):
"""Drop the dark-mode block (light) or apply it unconditionally (dark)."""
if scheme == "auto":
return svg
start = svg.index("@media (prefers-color-scheme:dark){")
i, depth = svg.index("{", start), 0
for j in range(i, len(svg)):
depth += {"{": 1, "}": -1}.get(svg[j], 0)
if depth == 0:
break
inner = svg[i + 1:j] if scheme == "dark" else ""
return svg[:start] + inner + svg[j + 1:]
if __name__ == "__main__":
out = sys.argv[1]
sec = float(sys.argv[2]) if len(sys.argv) > 2 else 15
scheme = sys.argv[3] if len(sys.argv) > 3 else "auto"
if scheme not in ("auto", "light", "dark"):
sys.exit(f"unknown colour scheme {scheme!r}: use auto, light or dark")
svg = fix_scheme(build(sec), scheme)
# defs are appended while slides render, so build the slides first (done inside build via f-string order)
open(out, "w").write(svg)
print(out, len(svg.encode()) // 1024, "KB,", len(GLYPHS), "glyphs; code px:", " ".join(f"{c:g}" for _, c in CODE_SIZES))
-412
View File
@@ -1,412 +0,0 @@
<!--
Feature slides for the animated slideshow. build.py adds the title slide before them and the closing slide after them.
# Title one slide per heading; `{badge: TEXT}` at the end adds a red chip
- bullet bullets; a long bullet may continue on indented lines
```cpp ... ``` the code shown on the right (a line starting with `$ ` is a shell command)
-->
# Trivial integration
- single header file
- no build system needed
- vanilla C++11
- works with GCC 4.8+, Clang 3.4+ and MSVC 2015+
```cpp
#include <nlohmann/json.hpp>
// for convenience
using json = nlohmann::json;
int main()
{
// a JSON pretty-printer in 3 lines
json j;
std::cin >> j;
std::cout << std::setw(4) << j << std::endl;
}
$ g++ pretty.cpp -std=c++11 -o pretty
```
# First-class datatype
- write JSON like any other C++ literal
- initializer lists become arrays and objects
```cpp
json j = {
{"pi", 3.141},
{"happy", true},
{"name", "Niels"},
{"nothing", nullptr},
{"answer", {
{"everything", 42}
}},
{"list", {1, 0, 2}},
{"object", {
{"currency", "USD"},
{"value", 42.99}
}}
};
```
# Array construction
- arrays work the way you expect
- adding to a null value makes it an array
```cpp
// create null value
json j;
// add via member function
j.push_back("Hello");
j.emplace_back("World");
// add via += operator
j += false;
// add via index
j[2] = 42.3;
// gaps are filled with null
j[4] = "World";
```
# Object construction
- objects work the way you expect
- adding to a null value makes it an object
```cpp
// create null value
json j;
// add via member function
j.emplace("key2", "value");
j.push_back({"key", "value"});
// add via += operator
j += {"another key", false};
// add via key
j["pi"] = 3.1415926;
j["answer"]["everything"] = 42;
```
# Container operations
- use JSON like an STL container
- satisfies the ReversibleContainer requirements
```cpp
// container empty?
j.empty();
// current number of elements
j.size();
// maximal number of elements
j.max_size();
// swap elements
j.swap(j2);
std::swap(j, j2);
// clear a container
j.clear();
```
# STL containers
- switch between STL and JSON containers
- conversion works in both directions
```cpp
// some STL containers
std::vector<int> v;
std::unordered_map<std::string, bool> m;
// automatic transformation
json j_array(v);
json j_object(m);
// works both ways
auto l = j_array.get<std::list<int>>();
auto u = j_object
.get<std::map<std::string, bool>>();
```
# Type conversion
- convert your own types to and from JSON
- one macro writes to_json and from_json
- or write both functions for full control
```cpp
// some user-defined type
struct person {
std::string name;
int age;
};
// defines to_json and from_json
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(person,
name, age)
// conversion in action
person p {"Ned Flanders", 60};
json j = p;
// {"age": 60, "name": "Ned Flanders"}
auto p2 = j.get<person>();
```
# Type deduction
- types are deduced automatically
- member functions tell you the type
```cpp
// type deduction
json j1 = nullptr;
json j2 = false;
json j3 = 12.23;
json j4 = {1, 2, 3};
json j5 = {{"key", "value"}};
json j6 = "Hello";
// quick checks
j1.is_null();
j2.is_boolean();
j3.is_number();
j4.is_array();
j5.is_object();
j6.is_string();
```
# Serialization
- to a string or a stream
- pretty-printing included
```cpp
// default serialization
auto s1 = j.dump();
// pretty-printed
int indent = 4;
std::string s2 = j.dump(indent);
// to stream
std::cout << j << std::endl;
// pretty-printed
std::cout << std::setw(indent) << j << std::endl;
```
# Deserialization
- parse strings, streams and files
- the _json literal puts plain JSON in your code
- comments and trailing commas on request
```cpp
// from string
json j1 = json::parse("[1,null,false]");
// from file
std::ifstream f("example.json");
json j2 = json::parse(f);
// from string literal
json j3 = "[3.14159]"_json;
// from raw string literal
json j4 = R"(
{"one": 1, "two": 2, "three": [1, 2, 3]}
)"_json;
```
# SAX parsing
- the parser can emit SAX events
- process input without building a JSON value
```cpp
// define a SAX event processor
struct sax_processor : json::json_sax_t
{
bool boolean(bool val) override
{
return true;
}
// implement other functions...
};
// create object
sax_processor my_processor;
// use it to process input
json::sax_parse(input, &my_processor);
```
# Zero-copy views {badge: NEW IN 3.13}
- parse without building a tree: 16 bytes per value
- strings and numbers stay in the source text
- materialize a json value only where you need one
```cpp
#include <nlohmann/json_view.hpp>
// parse into a flat index, not a tree
std::ifstream f("big.json");
auto doc = nlohmann::json_document::parse(f);
// read-only views with familiar access
auto view = doc.root();
double pi = view["pi"].get<double>();
for (auto v : view["list"])
std::cout << v.get<int>() << '\n';
// build a json value only where needed
json part = view["list"].materialize();
```
# JSON Pointer
- address any element with a JSON Pointer
- flatten a value to pointer/value pairs, and back
```cpp
json j = {"good", "bad", "ugly"};
// JSON pointer to the 3rd list element
auto ptr = json::json_pointer("/2");
// use JSON pointer to access element
j[ptr] = {{"it", "cattivo"}};
// user-defined string literal
j["/2/en"_json_pointer] = "ugly";
// ["good","bad",{"en":"ugly","it":"cattivo"}]
json flat = j.flatten();
// {"/0":"good","/1":"bad","/2/en":"ugly",
// "/2/it":"cattivo"}
json j2 = flat.unflatten(); // j == j2
```
# JSON Patches
- use JSON Patch to describe edit operations
- create diffs from two JSON values
- or use a JSON Merge Patch that looks like the document
```cpp
json source = R"({"F": "Paris", "D": "Berlin"})"_json;
// a JSON Patch (RFC 6902)
json p1 = R"([
{"op": "add", "path": "/GB", "value": "London"}
])"_json;
json target = source.patch(p1);
// create a diff from two JSON values
json p2 = json::diff(target, source);
// [{"op": "remove", "path": "/GB"}]
// a JSON Merge Patch (RFC 7396)
target.merge_patch(R"({"F": null})"_json);
// {"D": "Berlin", "GB": "London"}
```
# Binary formats
- compact binary representations
- BJData, BON8, BSON, CBOR, MessagePack and UBJSON
```cpp
// a JSON value
json j = R"({"compact": true, "schema": 0})"_json;
// serialize to CBOR
std::vector<std::uint8_t> v = json::to_cbor(j);
// 0xA2, 0x67, 0x63, 0x6F, 0x6D, 0x70, 0x61,
// 0x63, 0x74, 0xF5, 0x66, 0x73, 0x63, 0x68,
// 0x65, 0x6D, 0x61, 0x00
// roundtrip
json j_from_cbor = json::from_cbor(v);
// the same for the other formats
auto b = json::to_bon8(j);
auto m = json::to_msgpack(j);
```
# Algorithms
- the standard algorithms just work
- sort, find, count, merge and more
```cpp
// sort JSON containers
std::sort(j.begin(), j.end());
// find element
auto pos = std::find(j.begin(), j.end(),
json("needle"));
// count all numbers
auto n = std::count_if(j.begin(), j.end(),
[](const json& v) { return v.is_number(); });
// merge two sorted containers
std::merge(j1.begin(), j1.end(),
j2.begin(), j2.end(),
std::back_inserter(j3));
```
# Iterators
- iterators, const iterators, and reverse iterators
- random access for arrays
- items() gives you keys and values
```cpp
json j = {1, 2, 3, 4, 5};
// range for
for (auto& el : j)
{
// print all values
std::cerr << el << '\n';
}
// classical iterators
for (auto it = j.begin(); it != j.end(); ++it)
{
// double all values
*it = it->get<int>() * 2;
}
```
# Structured bindings
- since C++17, unpack keys and values with structured bindings
```cpp
// JSON object
auto j = R"({"foo": 23, "bar": 42})"_json;
// structured bindings
for (auto& [key, val] : j.items())
{
std::cerr << key << ':' << val << '\n';
}
```
+121 -11
View File
@@ -1218,6 +1218,10 @@ class binary_reader
// level as required by RFC 8949, Section 3.2.3
bool indefinite = false;
// the chunk currently being validated; only used for the chunks of an
// indefinite-length string when the UTF-8 check can do something
string_t chunk;
while (true)
{
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof("string")))
@@ -1237,26 +1241,82 @@ class binary_reader
}
// a break marker closes the indefinite-length string; outside
// of one it falls through to the error below
// of one it falls through to the error below. Every chunk has
// been checked on its own, so there is nothing left to check.
if (indefinite && current == 0xFF)
{
return check_string_utf8(result, context);
}
if (JSON_HEDLEY_UNLIKELY(!get_cbor_string_chunk(result, indefinite)))
{
return false;
return true;
}
if (!indefinite)
{
return check_string_utf8(result, context);
return get_cbor_string_chunk(result, false) && check_string_utf8(result, context);
}
if (error_handler == error_handler_t::keep)
{
// nothing to check: read the chunk directly into the result
if (JSON_HEDLEY_UNLIKELY(!get_cbor_string_chunk(result, true)))
{
return false;
}
}
else
{
// RFC 8949, Section 3.2.3: each chunk is a text string of its
// own and must be valid UTF-8 by itself, i.e., a code point
// cannot be spread over two chunks
chunk.clear();
if (JSON_HEDLEY_UNLIKELY(!get_cbor_string_chunk(chunk, true) || !check_string_utf8(chunk, context)))
{
return false;
}
result.append(chunk.data(), chunk.size());
}
get();
}
}
/*!
@brief skips the tags in front of a CBOR object key
A tag may wrap any data item, so it may also wrap a key. Like in front of a
value, the tags are skipped for the handlers ignore and store; with the
handler error they are left to @ref get_cbor_object_key, which rejects the
key. The tag loop is iterative, so the number of tags is not limited by the
recursion depth.
@param[in] tag_handler how CBOR tags should be treated
@return whether the tags could be skipped (false on EOF in a tag argument)
@pre @a current is the first byte of the key
*/
bool skip_cbor_key_tags(const cbor_tag_handler_t tag_handler)
{
if (tag_handler == cbor_tag_handler_t::error)
{
return true;
}
// 0xC0..0xDB: tag with the tag number in the head (0xC0..0xD7) or in
// the 1, 2, 4, or 8 bytes that follow (0xD8..0xDB)
while (current >= 0xC0 && current <= 0xDB)
{
if (current >= 0xD8)
{
std::uint64_t tag_to_ignore{};
if (JSON_HEDLEY_UNLIKELY(!get_cbor_argument(tag_to_ignore)))
{
return false;
}
}
get();
}
return true;
}
/*!
@brief reads a CBOR object key
@@ -1623,6 +1683,10 @@ class binary_reader
if (top.is_object)
{
if (JSON_HEDLEY_UNLIKELY(!skip_cbor_key_tags(tag_handler)))
{
return false;
}
if (JSON_HEDLEY_UNLIKELY(!read_object_key<&binary_reader::get_cbor_object_key>(key)))
{
return false;
@@ -2936,6 +3000,14 @@ class binary_reader
return false;
}
// validate the type marker up front: otherwise it would only be
// checked when an element is read, and a container with count 0
// would accept any marker
if (JSON_HEDLEY_UNLIKELY(!is_valid_optimized_type(result.second)))
{
return optimized_type_error();
}
get_ignore_noop();
if (JSON_HEDLEY_UNLIKELY(current != '#'))
{
@@ -4130,9 +4202,9 @@ class binary_reader
// sequence), UBJSON, BJData, or BSON requires a decoder to reject
// ill-formed UTF-8. Checking (and, with @ref error_handler_t::strict,
// rejecting, or with `replace`/`ignore`, sanitizing) is opt-in via
// @ref error_handler, applied once the whole string (all chunks of
// an indefinite-length CBOR string included) has been assembled, by
// @ref check_string_utf8 at the call site.
// @ref error_handler, applied by @ref check_string_utf8 at the call
// site to the whole string or, for an indefinite-length CBOR string,
// to each chunk on its own (RFC 8949, Section 3.2.3).
return get_bytes(len, "string", result);
}
@@ -4432,6 +4504,44 @@ class binary_reader
std::size_t bon8_pushback_size = 0;
JSON_PRIVATE_UNLESS_TESTED:
/*!
@brief check whether @a marker is a type marker that may follow '$'
UBJSON: Z N T F i U I l L d D H C S [ {; BJData: the numeric markers and C
(BJData's excluded markers are rejected separately).
*/
bool is_valid_optimized_type(const char_int_type marker) const noexcept
{
switch (marker)
{
case 'i':
case 'U':
case 'I':
case 'l':
case 'L':
case 'd':
case 'D':
case 'C':
return true;
case 'Z':
case 'N':
case 'T':
case 'F':
case 'H':
case 'S':
case '[':
case '{':
return input_format != input_format_t::bjdata;
case 'B':
case 'u':
case 'm':
case 'M':
case 'h':
return input_format == input_format_t::bjdata;
default:
return false;
}
}
/*!
@brief whether @a marker is excluded from BJData's optimized ND-array types
@return whether @a marker is one of 'F', 'H', 'N', 'S', 'T', 'Z', '[', '{'
+121 -11
View File
@@ -18685,6 +18685,10 @@ class binary_reader
// level as required by RFC 8949, Section 3.2.3
bool indefinite = false;
// the chunk currently being validated; only used for the chunks of an
// indefinite-length string when the UTF-8 check can do something
string_t chunk;
while (true)
{
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof("string")))
@@ -18704,26 +18708,82 @@ class binary_reader
}
// a break marker closes the indefinite-length string; outside
// of one it falls through to the error below
// of one it falls through to the error below. Every chunk has
// been checked on its own, so there is nothing left to check.
if (indefinite && current == 0xFF)
{
return check_string_utf8(result, context);
}
if (JSON_HEDLEY_UNLIKELY(!get_cbor_string_chunk(result, indefinite)))
{
return false;
return true;
}
if (!indefinite)
{
return check_string_utf8(result, context);
return get_cbor_string_chunk(result, false) && check_string_utf8(result, context);
}
if (error_handler == error_handler_t::keep)
{
// nothing to check: read the chunk directly into the result
if (JSON_HEDLEY_UNLIKELY(!get_cbor_string_chunk(result, true)))
{
return false;
}
}
else
{
// RFC 8949, Section 3.2.3: each chunk is a text string of its
// own and must be valid UTF-8 by itself, i.e., a code point
// cannot be spread over two chunks
chunk.clear();
if (JSON_HEDLEY_UNLIKELY(!get_cbor_string_chunk(chunk, true) || !check_string_utf8(chunk, context)))
{
return false;
}
result.append(chunk.data(), chunk.size());
}
get();
}
}
/*!
@brief skips the tags in front of a CBOR object key
A tag may wrap any data item, so it may also wrap a key. Like in front of a
value, the tags are skipped for the handlers ignore and store; with the
handler error they are left to @ref get_cbor_object_key, which rejects the
key. The tag loop is iterative, so the number of tags is not limited by the
recursion depth.
@param[in] tag_handler how CBOR tags should be treated
@return whether the tags could be skipped (false on EOF in a tag argument)
@pre @a current is the first byte of the key
*/
bool skip_cbor_key_tags(const cbor_tag_handler_t tag_handler)
{
if (tag_handler == cbor_tag_handler_t::error)
{
return true;
}
// 0xC0..0xDB: tag with the tag number in the head (0xC0..0xD7) or in
// the 1, 2, 4, or 8 bytes that follow (0xD8..0xDB)
while (current >= 0xC0 && current <= 0xDB)
{
if (current >= 0xD8)
{
std::uint64_t tag_to_ignore{};
if (JSON_HEDLEY_UNLIKELY(!get_cbor_argument(tag_to_ignore)))
{
return false;
}
}
get();
}
return true;
}
/*!
@brief reads a CBOR object key
@@ -19090,6 +19150,10 @@ class binary_reader
if (top.is_object)
{
if (JSON_HEDLEY_UNLIKELY(!skip_cbor_key_tags(tag_handler)))
{
return false;
}
if (JSON_HEDLEY_UNLIKELY(!read_object_key<&binary_reader::get_cbor_object_key>(key)))
{
return false;
@@ -20403,6 +20467,14 @@ class binary_reader
return false;
}
// validate the type marker up front: otherwise it would only be
// checked when an element is read, and a container with count 0
// would accept any marker
if (JSON_HEDLEY_UNLIKELY(!is_valid_optimized_type(result.second)))
{
return optimized_type_error();
}
get_ignore_noop();
if (JSON_HEDLEY_UNLIKELY(current != '#'))
{
@@ -21597,9 +21669,9 @@ class binary_reader
// sequence), UBJSON, BJData, or BSON requires a decoder to reject
// ill-formed UTF-8. Checking (and, with @ref error_handler_t::strict,
// rejecting, or with `replace`/`ignore`, sanitizing) is opt-in via
// @ref error_handler, applied once the whole string (all chunks of
// an indefinite-length CBOR string included) has been assembled, by
// @ref check_string_utf8 at the call site.
// @ref error_handler, applied by @ref check_string_utf8 at the call
// site to the whole string or, for an indefinite-length CBOR string,
// to each chunk on its own (RFC 8949, Section 3.2.3).
return get_bytes(len, "string", result);
}
@@ -21899,6 +21971,44 @@ class binary_reader
std::size_t bon8_pushback_size = 0;
JSON_PRIVATE_UNLESS_TESTED:
/*!
@brief check whether @a marker is a type marker that may follow '$'
UBJSON: Z N T F i U I l L d D H C S [ {; BJData: the numeric markers and C
(BJData's excluded markers are rejected separately).
*/
bool is_valid_optimized_type(const char_int_type marker) const noexcept
{
switch (marker)
{
case 'i':
case 'U':
case 'I':
case 'l':
case 'L':
case 'd':
case 'D':
case 'C':
return true;
case 'Z':
case 'N':
case 'T':
case 'F':
case 'H':
case 'S':
case '[':
case '{':
return input_format != input_format_t::bjdata;
case 'B':
case 'u':
case 'm':
case 'M':
case 'h':
return input_format == input_format_t::bjdata;
default:
return false;
}
}
/*!
@brief whether @a marker is excluded from BJData's optimized ND-array types
@return whether @a marker is one of 'F', 'H', 'N', 'S', 'T', 'Z', '[', '{'
+59 -2
View File
@@ -3134,7 +3134,7 @@ TEST_CASE("BJData")
std::vector<uint8_t> const v1 = {'[', '#', 'i', 0xF1};
std::vector<uint8_t> const v2 = {'[', '$', 'I', '#', 'i', 0xF2};
std::vector<uint8_t> const v3 = {'[', '$', 'I', '#', '[', 'i', 0xF4, 'i', 0x02, ']'};
std::vector<uint8_t> const v4 = {'[', '$', 0xF6, '#', 'i', 0xF7};
std::vector<uint8_t> const v4 = {'[', '$', 'l', '#', 'i', 0xF7};
std::vector<uint8_t> const v5 = {'[', '$', 'I', '#', '[', 'i', 0xF5, 'i', 0xF1, ']'};
std::vector<uint8_t> const v6 = {'[', '#', '[', 'i', 0xF3, 'i', 0x02, ']'};
@@ -3455,7 +3455,7 @@ TEST_CASE("BJData")
CHECK(json::from_bjdata(v0, true, false).is_discarded());
std::vector<uint8_t> const vi = {'[', '$', '#'};
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vi), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing BJData value: unexpected end of input", json::parse_error&);
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vi), "[json.exception.parse_error.112] parse error at byte 3: syntax error while parsing BJData type: marker 0x23 is not a permitted optimized array type", json::parse_error&);
CHECK(json::from_bjdata(vi, true, false).is_discarded());
std::vector<uint8_t> const vU = {'[', '$', 'U'};
@@ -4622,3 +4622,60 @@ TEST_CASE("BJData large strings and binaries (chunked reader)")
}
}
}
TEST_CASE("BJData optimized container type marker is validated for count 0")
{
// the '$' type marker must be a legal marker even if no element follows
SECTION("invalid marker")
{
// 'Z', 'T', 'F', 'N', 'S', 'H', '[' and '{' are excluded in BJData
for (const char marker :
{'x', 'E', 'Z', 'T', 'F', 'N', 'S', 'H', '[', '{'
})
{
for (const char open :
{'[', '{'
})
{
for (const int count :
{
0, 1
})
{
CAPTURE(marker)
CAPTURE(open)
CAPTURE(count)
const std::vector<std::uint8_t> v = {static_cast<std::uint8_t>(open), '$', static_cast<std::uint8_t>(marker), '#', 'i', static_cast<std::uint8_t>(count)};
json _;
CHECK_THROWS_AS(_ = json::from_bjdata(v), json::parse_error&);
CHECK(json::from_bjdata(v, true, false).is_discarded());
}
}
}
}
SECTION("error message")
{
const std::vector<std::uint8_t> v = {'[', '$', 'x', '#', 'i', 0};
json _;
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(v), "[json.exception.parse_error.112] parse error at byte 3: syntax error while parsing BJData type: marker 0x78 is not a permitted optimized array type", json::parse_error&);
}
SECTION("valid marker")
{
for (const char marker :
{'i', 'U', 'I', 'u', 'l', 'm', 'L', 'M', 'h', 'd', 'D', 'C'
})
{
CAPTURE(marker)
const std::vector<std::uint8_t> va = {'[', '$', static_cast<std::uint8_t>(marker), '#', 'i', 0};
const std::vector<std::uint8_t> vo = {'{', '$', static_cast<std::uint8_t>(marker), '#', 'i', 0};
CHECK(json::from_bjdata(va) == json::array());
CHECK(json::from_bjdata(vo) == json::object());
}
// an optimized array of bytes is read as a binary value
const std::vector<std::uint8_t> vb = {'[', '$', 'B', '#', 'i', 0};
CHECK(json::from_bjdata(vb) == json::binary({}));
}
}
+148 -3
View File
@@ -1807,6 +1807,93 @@ TEST_CASE("CBOR")
CHECK(json::from_cbor(std::vector<uint8_t>({0xa1, 0xff, 0x01}), true, false).is_discarded());
}
SECTION("tag in front of a key")
{
const auto ignore = json::cbor_tag_handler_t::ignore;
const auto store = json::cbor_tag_handler_t::store;
const json expected = {{"a", 10}};
// the tags are skipped in front of a text string key, like in
// front of a value
const std::vector<std::vector<std::uint8_t>> tagged =
{
{0xA1, 0xC0, 0x61, 0x61, 0x0A}, // tag in the head
{0xA1, 0xD8, 0x05, 0x61, 0x61, 0x0A}, // 1-byte tag number
{0xA1, 0xD9, 0x01, 0x2C, 0x61, 0x61, 0x0A}, // 2-byte tag number
{0xA1, 0xDA, 0x00, 0x01, 0x00, 0x00, 0x61, 0x61, 0x0A}, // 4-byte tag number
{0xA1, 0xDB, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x61, 0x61, 0x0A}, // 8-byte tag number
{0xA1, 0xC0, 0xD8, 0x05, 0xC1, 0x61, 0x61, 0x0A}, // chained tags
{0xBF, 0xC0, 0x61, 0x61, 0x0A, 0xFF}, // indefinite-length map
{0xBF, 0x61, 0x62, 0x01, 0xC0, 0x61, 0x61, 0x0A, 0xFF}, // second key tagged
{0xA1, 0xC0, 0x7F, 0x61, 0x61, 0xFF, 0x0A}, // indefinite-length text key
{0xA1, 0xC0, 0x61, 0x61, 0xC0, 0x0A}, // tagged key and value
};
for (const auto& input : tagged)
{
CAPTURE(input)
json const j_ignore = json::from_cbor(input, true, true, ignore);
json const j_store = json::from_cbor(input, true, true, store);
CHECK(j_ignore.at("a") == 10);
CHECK(j_store.at("a") == 10);
CHECK(j_ignore == j_store);
}
CHECK(json::from_cbor(tagged[0], true, true, ignore) == expected);
CHECK(json::from_cbor(tagged[1], true, true, store) == expected);
// nested maps
CHECK(json::from_cbor(std::vector<std::uint8_t>({0xA1, 0xC0, 0x61, 0x61, 0xA1, 0xD8, 0x05, 0x61, 0x62, 0x01}), true, true, ignore) == json{{"a", {{"b", 1}}}});
// the tag handler error still rejects every tag
for (const auto& input : tagged)
{
CAPTURE(input)
json _;
CHECK_THROWS_AS(_ = json::from_cbor(input), json::parse_error&);
}
{
json _;
CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector<std::uint8_t>({0xA1, 0xD8, 0x05, 0x61, 0x61, 0x0A})), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing CBOR object key: only string keys are supported, but found a tag; last byte: 0xD8", json::parse_error&);
}
// a tag does not turn a key of another type into a text string
{
json _;
CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector<std::uint8_t>({0xA1, 0xC0, 0x01, 0x01}), true, true, ignore), "[json.exception.parse_error.113] parse error at byte 3: syntax error while parsing CBOR object key: only string keys are supported, but found an unsigned integer; last byte: 0x01", json::parse_error&);
CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector<std::uint8_t>({0xA1, 0xD8, 0x05, 0x41, 0x61, 0x01}), true, true, store), "[json.exception.parse_error.113] parse error at byte 4: syntax error while parsing CBOR object key: only string keys are supported, but found a byte string; last byte: 0x41", json::parse_error&);
CHECK(json::from_cbor(std::vector<std::uint8_t>({0xA1, 0xC0, 0x01, 0x01}), true, false, ignore).is_discarded());
}
// end of input after the tag, or inside the tag number
{
json _;
CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector<std::uint8_t>({0xA1, 0xC0}), true, true, ignore), "[json.exception.parse_error.110] parse error at byte 3: syntax error while parsing CBOR string: unexpected end of input", json::parse_error&);
CHECK_THROWS_AS(_ = json::from_cbor(std::vector<std::uint8_t>({0xA1, 0xD8}), true, true, ignore), json::parse_error&);
CHECK_THROWS_AS(_ = json::from_cbor(std::vector<std::uint8_t>({0xA1, 0xDB, 0x00, 0x00}), true, true, store), json::parse_error&);
CHECK_THROWS_AS(_ = json::from_cbor(std::vector<std::uint8_t>({0xBF, 0xC0}), true, true, store), json::parse_error&);
}
// SAX: the key event is reported without the tag (start_object,
// key, number_unsigned, end_object are four events)
{
const std::vector<std::uint8_t> input = {0xA1, 0xC0, 0x61, 0x61, 0x0A};
SaxCountdown four(4);
CHECK(json::sax_parse(input, &four, json::input_format_t::cbor, true, false, false, ignore));
SaxCountdown three(3);
CHECK(!json::sax_parse(input, &three, json::input_format_t::cbor, true, false, false, ignore));
SaxCountdown error_handler(4);
CHECK(!json::sax_parse(input, &error_handler, json::input_format_t::cbor));
}
// a long chain of tags needs no recursion
{
std::vector<std::uint8_t> input = {0xA1};
input.insert(input.end(), 100000, 0xC0);
input.insert(input.end(), {0x61, 0x61, 0x0A});
CHECK(json::from_cbor(input, true, true, ignore) == expected);
}
}
SECTION("non-string key (see #2766 and #3381)")
{
// only text strings map to JSON object keys; any other key is
@@ -1915,9 +2002,10 @@ TEST_CASE("CBOR")
{
json _;
// the chunks are concatenated as is, without checking that each
// chunk is valid UTF-8 on its own (RFC 8949, Section 3.2.3), so
// a code point split across two chunks yields a valid string
// with the default error handler keep, the chunks are concatenated
// as is, without checking that each chunk is valid UTF-8 on its
// own (RFC 8949, Section 3.2.3), so a code point split across two
// chunks yields a valid string
CHECK_NOTHROW(_ = json::from_cbor(std::vector<uint8_t>({0x7f, 0x61, 0xc3, 0x61, 0xa9, 0xff})));
CHECK(_ == "\xc3\xa9");
CHECK(_.dump() == "\"\xc3\xa9\"");
@@ -1937,6 +2025,63 @@ TEST_CASE("CBOR")
CHECK(json::from_cbor(std::vector<uint8_t>({0x7f, 0x62, 0xc3, 0xa9, 0x62, 0xc3, 0xb6, 0xff})) == "\xc3\xa9\xc3\xb6");
}
SECTION("ill-formed UTF-8 in a chunk of an indefinite-length string (RFC 8949, Section 3.2.3)")
{
const auto strict = json::error_handler_t::strict;
const auto replace = json::error_handler_t::replace;
const auto ignore = json::error_handler_t::ignore;
const auto keep = json::error_handler_t::keep;
const auto tags = json::cbor_tag_handler_t::error;
// a code point split over two chunks
const std::vector<std::uint8_t> split = {0x7f, 0x61, 0xc3, 0x61, 0xa9, 0xff};
// a 4-byte code point split over two chunks
const std::vector<std::uint8_t> split_emoji = {0x7f, 0x62, 0xf0, 0x9f, 0x62, 0x98, 0x80, 0xff};
// the same in an object key
const std::vector<std::uint8_t> split_key = {0xa1, 0x7f, 0x61, 0xc3, 0x61, 0xa9, 0xff, 0x01};
// a valid chunk followed by one with a truncated code point
const std::vector<std::uint8_t> truncated = {0x7f, 0x62, 0x41, 0xc3, 0x61, 0xa9, 0xff};
// strict: the string is invalid
for (const auto& input :
{
split, split_emoji, split_key, truncated
})
{
CAPTURE(input)
json _;
CHECK_THROWS_AS(_ = json::from_cbor(input, true, true, tags, strict), json::parse_error&);
CHECK(json::from_cbor(input, true, false, tags, strict).is_discarded());
}
{
json _;
CHECK_THROWS_WITH_AS(_ = json::from_cbor(split, true, true, tags, strict), "[json.exception.parse_error.113] parse error at byte 3: syntax error while parsing CBOR string: invalid string: ill-formed UTF-8 byte", json::parse_error&);
CHECK_THROWS_WITH_AS(_ = json::from_cbor(split_key, true, true, tags, strict), "[json.exception.parse_error.113] parse error at byte 4: syntax error while parsing CBOR key: invalid string: ill-formed UTF-8 byte", json::parse_error&);
}
// replace and ignore: every chunk is sanitized on its own
CHECK(json::from_cbor(split, true, true, tags, replace) == "\xEF\xBF\xBD\xEF\xBF\xBD");
CHECK(json::from_cbor(split, true, true, tags, ignore) == "");
CHECK(json::from_cbor(truncated, true, true, tags, replace) == "A\xEF\xBF\xBD\xEF\xBF\xBD");
CHECK(json::from_cbor(truncated, true, true, tags, ignore) == "A");
CHECK(json::from_cbor(split_key, true, true, tags, replace) == json{{"\xEF\xBF\xBD\xEF\xBF\xBD", 1}});
// keep: unchanged, the chunks are concatenated as is
CHECK(json::from_cbor(split, true, true, tags, keep) == "\xc3\xa9");
CHECK(json::from_cbor(split_emoji, true, true, tags, keep) == "\xf0\x9f\x98\x80");
// conforming input is unaffected by the error handler
const std::vector<std::uint8_t> valid = {0x7f, 0x62, 0xc3, 0xa9, 0x64, 0xf0, 0x9f, 0x98, 0x80, 0x60, 0xff};
for (const auto handler :
{
strict, replace, ignore, keep
})
{
CHECK(json::from_cbor(valid, true, true, tags, handler) == "\xc3\xa9\xf0\x9f\x98\x80");
CHECK(json::from_cbor(std::vector<std::uint8_t>({0x7f, 0xff}), true, true, tags, handler) == "");
}
}
SECTION("many chunks in indefinite-length string")
{
constexpr std::size_t chunks = 100000;
+53 -1
View File
@@ -2023,7 +2023,7 @@ TEST_CASE("UBJSON")
CHECK(json::from_ubjson(v0, true, false).is_discarded());
std::vector<uint8_t> const vi = {'[', '$', '#'};
CHECK_THROWS_WITH_AS(_ = json::from_ubjson(vi), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing UBJSON value: unexpected end of input", json::parse_error&);
CHECK_THROWS_WITH_AS(_ = json::from_ubjson(vi), "[json.exception.parse_error.112] parse error at byte 3: syntax error while parsing UBJSON type: marker 0x23 is not a permitted optimized array type", json::parse_error&);
CHECK(json::from_ubjson(vi, true, false).is_discarded());
std::vector<uint8_t> const vT = {'[', '$', 'T'};
@@ -3348,3 +3348,55 @@ TEST_CASE("UBJSON large strings (chunked reader)")
}
}
}
TEST_CASE("UBJSON optimized container type marker is validated for count 0")
{
// the '$' type marker must be a legal marker even if no element follows
SECTION("invalid marker")
{
for (const char marker :
{'x', 'E', 'B', 'u', 'm', 'M', 'h'
})
{
for (const char open :
{'[', '{'
})
{
for (const int count :
{
0, 1
})
{
CAPTURE(marker)
CAPTURE(open)
CAPTURE(count)
const std::vector<std::uint8_t> v = {static_cast<std::uint8_t>(open), '$', static_cast<std::uint8_t>(marker), '#', 'i', static_cast<std::uint8_t>(count)};
json _;
CHECK_THROWS_AS(_ = json::from_ubjson(v), json::parse_error&);
CHECK(json::from_ubjson(v, true, false).is_discarded());
}
}
}
}
SECTION("error message")
{
const std::vector<std::uint8_t> v = {'[', '$', 'x', '#', 'i', 0};
json _;
CHECK_THROWS_WITH_AS(_ = json::from_ubjson(v), "[json.exception.parse_error.112] parse error at byte 3: syntax error while parsing UBJSON type: marker 0x78 is not a permitted optimized array type", json::parse_error&);
}
SECTION("valid marker")
{
for (const char marker :
{'Z', 'N', 'T', 'F', 'i', 'U', 'I', 'l', 'L', 'd', 'D', 'H', 'C', 'S', '[', '{'
})
{
CAPTURE(marker)
const std::vector<std::uint8_t> va = {'[', '$', static_cast<std::uint8_t>(marker), '#', 'i', 0};
const std::vector<std::uint8_t> vo = {'{', '$', static_cast<std::uint8_t>(marker), '#', 'i', 0};
CHECK(json::from_ubjson(va) == json::array());
CHECK(json::from_ubjson(vo) == json::object());
}
}
}