mirror of
https://github.com/nlohmann/json.git
synced 2026-09-07 08:47:57 +00:00
An element of type 'Z' (null), 'T' (true) or 'F' (false) is encoded by its type marker alone, so an optimized UBJSON array of one of those has no payload: reading an element consumes no input at all. Its declared count is therefore the only thing that decides how much is allocated, and nothing bounded it. "[$Z#l" and a four-byte count is nine bytes of input describing two billion values; #2793 reports 35 GB and 150 seconds from ten bytes, and OSS-Fuzz has an out-of-memory and a timeout report for the same shape. Every other type costs at least one byte per element, so the end of the input bounds it. 'N' (no-op) is already skipped rather than stored. Objects are not affected either: each element is preceded by its key, which costs bytes. And BJData already refuses these markers as an optimized type, so this is a plain UBJSON matter. Reject a count above 1,048,576 elements for those three types with out_of_range.408, the code this reader already uses for a declared size it will not honour. The check runs before the SAX start event, so no container is opened and then abandoned. Rejecting on the read side alone would break the guarantee that anything to_ubjson() writes can be read back, and would trip the round-trip assertion in fuzzer-parse_ubjson.cpp. So the writer falls back to the unoptimized encoding, one byte per element, for arrays of these types above the same limit. Its decision depends only on the array's size, which is identical for a value and for anything parsed back from it, so the round trip is stable. No existing test changes: the largest such count in the test suite is 65,793. The excessive-size test that already used this shape still passes, now rejected a little earlier than by the max_size() check it used to reach. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
6.2 KiB
6.2 KiB
UBJSON
Universal Binary JSON (UBJSON) is a binary form directly imitating JSON, but requiring fewer bytes of data. It aims to achieve the generality of JSON, combined with being much easier to process than JSON.
!!! abstract "References"
- [UBJSON Website](http://ubjson.org)
Serialization
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 | 2147483649..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 | { |
!!! success "Complete mapping"
The mapping is **complete** in the sense that any JSON value type can be converted to a UBJSON value.
Any UBJSON output created by `to_ubjson` can be successfully parsed by `from_ubjson`.
!!! warning "Size constraints"
The following values can **not** be converted to a UBJSON value:
- strings with more than 9223372036854775807 bytes (theoretical)
!!! info "Unused UBJSON markers"
The following markers are not used in the conversion:
- `Z`: no-op values are not created.
- `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()` function which serializes NaN or Infinity to `null`.
!!! 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`.
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.
An array whose type marker is `Z` (null), `T` (true) or `F` (false) stores no payload at all, because the marker
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, and
`to_ubjson` writes longer arrays of these types without the annotation, so any value it produces can be read back.
!!! info "Binary values"
If the JSON data contains the binary type, the value stored is a list of integers, as suggested by the UBJSON
documentation. In particular, this means that serialization and the deserialization of a JSON containing binary
values into UBJSON and back will result in a different JSON object.
??? example
```cpp
--8<-- "examples/to_ubjson.cpp"
```
Output:
```c
--8<-- "examples/to_ubjson.output"
```
Deserialization
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) | { |
!!! success "Complete mapping"
The mapping is **complete** in the sense that any UBJSON value can be converted to a JSON value.
??? example
```cpp
--8<-- "examples/from_ubjson.cpp"
```
Output:
```json
--8<-- "examples/from_ubjson.output"
```