This commit is contained in:
nlohmann
2026-09-25 18:19:13 +00:00
parent 10e005271b
commit ac7a671836
283 changed files with 865 additions and 527 deletions
File diff suppressed because one or more lines are too long
+1
View File
@@ -17,6 +17,7 @@ Some aspects of the library can be configured by defining preprocessor macros **
## Parsing
- [**JSON_PRECISE_STREAM_POSITION**](https://json.nlohmann.me/api/macros/json_precise_stream_position/index.md) - opt in to leaving an input stream positioned right after a parsed number
- [**JSON_STRICT_NUL_HANDLING**](https://json.nlohmann.me/api/macros/json_strict_nul_handling/index.md) - opt in to rejecting a NUL byte in the input instead of treating it as end of input
## Language support
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+10 -9
View File
@@ -7,16 +7,16 @@
When defined, the library does not use `#!cpp thread_local` storage. This is relevant for the few environments whose
toolchain does not support it.
The copy constructor copies the first levels of a value by copying the containers, which copy their elements, and
completes whatever is nested deeper than that without the call stack, so that copying a value cannot exhaust the stack
however deeply it is nested. It counts the levels it has descended into in a `#!cpp thread_local` variable, as a counter
shared between threads would be raced.
Copying a value and comparing two values both descend into the first levels by letting the containers copy or compare
themselves, and finish whatever is nested deeper than that without the call stack, so that neither can exhaust the stack
however deeply the values are nested. Each counts the levels it has descended into in a `#!cpp thread_local` variable, as
a counter shared between threads would be raced.
Without that counter, no descent can be bounded safely, so objects and arrays are copied without the call stack right
away. Copying keeps working exactly as it does otherwise - the same values come out, and deeply nested values are copied
just as safely - but copying is slower, because the containers no longer copy themselves. Copying the benchmark
documents takes 9% (`canada.json`) to 34% (`twitter.json`) longer; values built mostly from objects are affected the
most.
Without those counters, no descent can be bounded safely, so objects and arrays are copied and compared without the call
stack right away. Both keep working exactly as they do otherwise - the same values come out, the same comparisons hold,
and deeply nested values are handled just as safely - but both are slower, because the containers no longer copy or
compare themselves. Copying the benchmark documents takes 9% (`canada.json`) to 34% (`twitter.json`) longer, and
comparing two equal ones 10% (`citm_catalog.json`) to 90% (`canada.json`) longer.
## Default definition
@@ -28,6 +28,7 @@ By default, `#!cpp JSON_NO_THREAD_LOCAL` is not defined.
The library defines it by itself for Clang targeting MinGW, which does not survive the `#!cpp thread_local` storage:
copying a value segfaults there, with both old and current Clang versions, while GCC targeting MinGW is unaffected.
Copying and comparing fall back to working without the call stack there, as they do whenever the macro is defined.
## Examples
File diff suppressed because one or more lines are too long
+3 -3
View File
@@ -6,9 +6,9 @@
When defined, the library does not use `thread_local` storage. This is relevant for the few environments whose toolchain does not support it.
The copy constructor copies the first levels of a value by copying the containers, which copy their elements, and completes whatever is nested deeper than that without the call stack, so that copying a value cannot exhaust the stack however deeply it is nested. It counts the levels it has descended into in a `thread_local` variable, as a counter shared between threads would be raced.
Copying a value and comparing two values both descend into the first levels by letting the containers copy or compare themselves, and finish whatever is nested deeper than that without the call stack, so that neither can exhaust the stack however deeply the values are nested. Each counts the levels it has descended into in a `thread_local` variable, as a counter shared between threads would be raced.
Without that counter, no descent can be bounded safely, so objects and arrays are copied without the call stack right away. Copying keeps working exactly as it does otherwise - the same values come out, and deeply nested values are copied just as safely - but copying is slower, because the containers no longer copy themselves. Copying the benchmark documents takes 9% (`canada.json`) to 34% (`twitter.json`) longer; values built mostly from objects are affected the most.
Without those counters, no descent can be bounded safely, so objects and arrays are copied and compared without the call stack right away. Both keep working exactly as they do otherwise - the same values come out, the same comparisons hold, and deeply nested values are handled just as safely - but both are slower, because the containers no longer copy or compare themselves. Copying the benchmark documents takes 9% (`canada.json`) to 34% (`twitter.json`) longer, and comparing two equal ones 10% (`citm_catalog.json`) to 90% (`canada.json`) longer.
## Default definition
@@ -18,7 +18,7 @@ By default, `JSON_NO_THREAD_LOCAL` is not defined.
#undef JSON_NO_THREAD_LOCAL
```
The library defines it by itself for Clang targeting MinGW, which does not survive the `thread_local` storage: copying a value segfaults there, with both old and current Clang versions, while GCC targeting MinGW is unaffected.
The library defines it by itself for Clang targeting MinGW, which does not survive the `thread_local` storage: copying a value segfaults there, with both old and current Clang versions, while GCC targeting MinGW is unaffected. Copying and comparing fall back to working without the call stack there, as they do whenever the macro is defined.
## Examples
File diff suppressed because one or more lines are too long
+131
View File
@@ -0,0 +1,131 @@
# JSON_PRECISE_STREAM_POSITION
```cpp
#define JSON_PRECISE_STREAM_POSITION /* value */
```
When defined to `1`, [`operator>>`](../operator_gtgt.md) and [`sax_parse`](../basic_json/sax_parse.md) with
`strict = false` leave a `#!cpp std::istream` positioned right after the parsed value for every value type. By default,
the character that terminates a number is consumed as well.
The macro only affects reading from a `#!cpp std::istream` when the rest of the stream is not required to be consumed.
[`parse`](../basic_json/parse.md), [`accept`](../basic_json/accept.md), and all other inputs (strings, iterators,
containers, `#!cpp FILE*`) are never affected.
## Default definition
The default value is `0` (disabled — existing behavior is preserved).
```cpp
#define JSON_PRECISE_STREAM_POSITION 0
```
## Notes
!!! note "Background"
A number is the only JSON value whose end can be detected solely by reading the character that follows it. By
default, that character is consumed and not put back, so the stream is left one byte too far after a number, and
only after a number:
```cpp
std::istringstream input("1true");
json j;
input >> j; // j == 1, but the stream now starts at "rue"
```
With this macro, the character is only looked at and left in the stream, so the stream starts at `true`. This
does not require the stream buffer to support putting a character back.
This was not changed unconditionally, because code can depend on the consumed character, even unknowingly (see
[#5340](https://github.com/nlohmann/json/issues/5340)). Both of the following work by default only because the
character after each number is swallowed, and behave differently with this macro:
```cpp
std::istringstream input("1,2,3");
json j1, j2, j3;
input >> j1 >> j2 >> j3; // default: 1, 2, 3
// with the macro: throws parse_error.101 at the ','
```
```cpp
std::istringstream input("42\nfoo");
json j;
std::string line;
input >> j;
std::getline(input, line); // default: "foo"
// with the macro: "" (like after reading an int with >>)
```
In both cases, the behavior with the macro is what you already get today when the value is not a number: `"a","b"`
fails at the `,`, and `std::getline` after `{}` returns an empty string. This macro offers an opt-in path to
the consistent behavior ahead of version 4.0.0, where it is planned to become the default.
!!! warning "Opt-in only"
This macro must be defined **before** including `<nlohmann/json.hpp>`. Defining it after the include has no
effect.
!!! note "ABI compatibility"
The value of this macro is encoded in the [namespace](../../features/namespace.md) (tag `_psp`), resulting in
distinct symbol names. Translation units compiled with and without it can therefore be linked into the same program
without One Definition Rule (ODR) violations, but they cannot exchange instances of library types.
!!! tip "Workaround without the macro"
Separate the values in the stream with whitespace. The character consumed after a number is then the separator,
and whitespace before the next value is skipped anyway.
## Examples
??? example "Default behavior (macro not defined)"
Without the macro, the character after a number is consumed:
```cpp
#include <iostream>
#include <sstream>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
int main()
{
std::istringstream input("1true");
json j1, j2;
input >> j1; // j1 == 1
input >> j2; // throws parse_error.101: the stream now starts at "rue"
}
```
??? example "Opt-in precise stream position (macro defined to 1)"
With the macro, the stream is positioned right after the number:
```cpp
#define JSON_PRECISE_STREAM_POSITION 1
#include <iostream>
#include <sstream>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
int main()
{
std::istringstream input("1true");
json j1, j2;
input >> j1; // j1 == 1
input >> j2; // j2 == true
}
```
## See also
- [**operator>>**](../operator_gtgt.md) - deserialize from stream
- [**sax_parse**](../basic_json/sax_parse.md) - generate SAX events
## Version history
- Added in version 3.13.0.
- Planned to become the default (with the macro removed) in version 4.0.0.
File diff suppressed because one or more lines are too long
@@ -0,0 +1,116 @@
# JSON_PRECISE_STREAM_POSITION
```
#define JSON_PRECISE_STREAM_POSITION /* value */
```
When defined to `1`, [`operator>>`](https://json.nlohmann.me/api/operator_gtgt/index.md) and [`sax_parse`](https://json.nlohmann.me/api/basic_json/sax_parse/index.md) with `strict = false` leave a `std::istream` positioned right after the parsed value for every value type. By default, the character that terminates a number is consumed as well.
The macro only affects reading from a `std::istream` when the rest of the stream is not required to be consumed. [`parse`](https://json.nlohmann.me/api/basic_json/parse/index.md), [`accept`](https://json.nlohmann.me/api/basic_json/accept/index.md), and all other inputs (strings, iterators, containers, `FILE*`) are never affected.
## Default definition
The default value is `0` (disabled — existing behavior is preserved).
```
#define JSON_PRECISE_STREAM_POSITION 0
```
## Notes
Background
A number is the only JSON value whose end can be detected solely by reading the character that follows it. By default, that character is consumed and not put back, so the stream is left one byte too far after a number, and only after a number:
```
std::istringstream input("1true");
json j;
input >> j; // j == 1, but the stream now starts at "rue"
```
With this macro, the character is only looked at and left in the stream, so the stream starts at `true`. This does not require the stream buffer to support putting a character back.
This was not changed unconditionally, because code can depend on the consumed character, even unknowingly (see [#5340](https://github.com/nlohmann/json/issues/5340)). Both of the following work by default only because the character after each number is swallowed, and behave differently with this macro:
```
std::istringstream input("1,2,3");
json j1, j2, j3;
input >> j1 >> j2 >> j3; // default: 1, 2, 3
// with the macro: throws parse_error.101 at the ','
```
```
std::istringstream input("42\nfoo");
json j;
std::string line;
input >> j;
std::getline(input, line); // default: "foo"
// with the macro: "" (like after reading an int with >>)
```
In both cases, the behavior with the macro is what you already get today when the value is not a number: `"a","b"` fails at the `,`, and `std::getline` after `{}` returns an empty string. This macro offers an opt-in path to the consistent behavior ahead of version 4.0.0, where it is planned to become the default.
Opt-in only
This macro must be defined **before** including `<nlohmann/json.hpp>`. Defining it after the include has no effect.
ABI compatibility
The value of this macro is encoded in the [namespace](https://json.nlohmann.me/features/namespace/index.md) (tag `_psp`), resulting in distinct symbol names. Translation units compiled with and without it can therefore be linked into the same program without One Definition Rule (ODR) violations, but they cannot exchange instances of library types.
Workaround without the macro
Separate the values in the stream with whitespace. The character consumed after a number is then the separator, and whitespace before the next value is skipped anyway.
## Examples
Default behavior (macro not defined)
Without the macro, the character after a number is consumed:
```
#include <iostream>
#include <sstream>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
int main()
{
std::istringstream input("1true");
json j1, j2;
input >> j1; // j1 == 1
input >> j2; // throws parse_error.101: the stream now starts at "rue"
}
```
Opt-in precise stream position (macro defined to 1)
With the macro, the stream is positioned right after the number:
```
#define JSON_PRECISE_STREAM_POSITION 1
#include <iostream>
#include <sstream>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
int main()
{
std::istringstream input("1true");
json j1, j2;
input >> j1; // j1 == 1
input >> j2; // j2 == true
}
```
## See also
- [**operator>>**](https://json.nlohmann.me/api/operator_gtgt/index.md) - deserialize from stream
- [**sax_parse**](https://json.nlohmann.me/api/basic_json/sax_parse/index.md) - generate SAX events
## Version history
- Added in version 3.13.0.
- Planned to become the default (with the macro removed) in version 4.0.0.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long