Compare commits
9
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7ff56d7c91 | ||
|
|
4b09029f4e | ||
|
|
6e8e7d01df | ||
|
|
077c65872b | ||
|
|
2638f276d0 | ||
|
|
66d4f3602b | ||
|
|
612e9ade2c | ||
|
|
b3ab23e11c | ||
|
|
31538a91ed |
No files matched your search
@@ -0,0 +1,48 @@
|
|||||||
|
# <small>nlohmann::basic_json::</small>bjdata_version_t
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
enum class bjdata_version_t
|
||||||
|
{
|
||||||
|
draft2,
|
||||||
|
draft3
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
This enumeration is used in [`to_bjdata`](to_bjdata.md) to choose the version of the BJData specification to write:
|
||||||
|
|
||||||
|
draft2
|
||||||
|
: BJData Draft 2 (the default). Binary values are written as arrays of `U` integers, and ND-arrays with
|
||||||
|
`"_ArrayType_": "byte"` are written as regular objects, since the `B` marker is not part of Draft 2.
|
||||||
|
|
||||||
|
draft3
|
||||||
|
: BJData Draft 3. Binary values are written as `B` elements: as an optimized binary array (`[$B#`) if both `use_size`
|
||||||
|
and `use_type` are set, and as an array of `B`-prefixed elements otherwise. ND-arrays with
|
||||||
|
`"_ArrayType_": "byte"` are written as packed arrays (`[$B#[...]`).
|
||||||
|
|
||||||
|
Only the writer is affected by the version. [`from_bjdata`](from_bjdata.md) reads the markers of both drafts.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
??? example
|
||||||
|
|
||||||
|
The example below shows how the different values of the `bjdata_version_t` influence the serialization of a binary
|
||||||
|
value with [`to_bjdata`](to_bjdata.md).
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
--8<-- "examples/bjdata_version_t.cpp"
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
```json
|
||||||
|
--8<-- "examples/bjdata_version_t.output"
|
||||||
|
```
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
- [to_bjdata](to_bjdata.md) serializes a JSON value to BJData
|
||||||
|
- [BJData](../../features/binary_formats/bjdata.md) - the article on the BJData format
|
||||||
|
|
||||||
|
## Version history
|
||||||
|
|
||||||
|
- Added in version 3.12.0.
|
||||||
@@ -82,8 +82,11 @@ 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 a parse error occurs
|
- Throws [parse_error.112](../../home/exceptions.md#jsonexceptionparse_error112) if a parse error occurs
|
||||||
- Throws [parse_error.113](../../home/exceptions.md#jsonexceptionparse_error113) if a string could not be parsed
|
- 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`
|
successfully, 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 number
|
||||||
- Throws [out_of_range.408](../../home/exceptions.md#jsonexceptionout_of_range408) if the size of an optimized container
|
- 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`
|
cannot be represented by `std::size_t` or exceeds the container's `max_size()`, or if the product of the dimensions of
|
||||||
|
an n-dimensional array overflows
|
||||||
|
|
||||||
## Complexity
|
## Complexity
|
||||||
|
|
||||||
|
|||||||
@@ -74,6 +74,11 @@ 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 a parse error occurs, for instance
|
- Throws [parse_error.112](../../home/exceptions.md#jsonexceptionparse_error112) if a parse error occurs, for instance
|
||||||
an invalid byte, a string that is not valid UTF-8, or an object key that is not a string
|
an invalid byte, a string that is not valid UTF-8, or an object key that is not a string
|
||||||
|
|
||||||
|
The UTF-8 validity of strings is always checked, independent of
|
||||||
|
[`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md), because the end of a BON8 string is determined from
|
||||||
|
its encoding. Duplicate object keys are accepted; the last value wins (see
|
||||||
|
[BON8](../../features/binary_formats/bon8.md)).
|
||||||
|
|
||||||
## Complexity
|
## Complexity
|
||||||
|
|
||||||
Linear in the size of the input.
|
Linear in the size of the input.
|
||||||
|
|||||||
@@ -121,6 +121,7 @@ The class satisfies the following concept requirements:
|
|||||||
- [**json_pointer**](../json_pointer/index.md) - JSON Pointer implementation
|
- [**json_pointer**](../json_pointer/index.md) - JSON Pointer implementation
|
||||||
- [**json_serializer**](json_serializer.md) - type of the serializer to for conversions from/to JSON
|
- [**json_serializer**](json_serializer.md) - type of the serializer to for conversions from/to JSON
|
||||||
- [**error_handler_t**](error_handler_t.md) - type to choose behavior on decoding errors
|
- [**error_handler_t**](error_handler_t.md) - type to choose behavior on decoding errors
|
||||||
|
- [**bjdata_version_t**](bjdata_version_t.md) - type to choose the BJData version to write
|
||||||
- [**cbor_tag_handler_t**](cbor_tag_handler_t.md) - type to choose how to handle CBOR tags
|
- [**cbor_tag_handler_t**](cbor_tag_handler_t.md) - type to choose how to handle CBOR tags
|
||||||
- **initializer_list_t** - type for initializer lists of `basic_json` values
|
- **initializer_list_t** - type for initializer lists of `basic_json` values
|
||||||
- [**input_format_t**](input_format_t.md) - type to choose the format to parse
|
- [**input_format_t**](input_format_t.md) - type to choose the format to parse
|
||||||
|
|||||||
@@ -43,7 +43,8 @@ The exact mapping and its limitations are described on a [dedicated page](../../
|
|||||||
`#!cpp false` by default.
|
`#!cpp false` by default.
|
||||||
|
|
||||||
`version` (in)
|
`version` (in)
|
||||||
: which version of BJData to use (see note on "Binary values" on [BJData](../../features/binary_formats/bjdata.md));
|
: which version of BJData to use, see [`bjdata_version_t`](bjdata_version_t.md) and the note on "Binary values" on
|
||||||
|
[BJData](../../features/binary_formats/bjdata.md);
|
||||||
optional, `#!cpp bjdata_version_t::draft2` by default.
|
optional, `#!cpp bjdata_version_t::draft2` by default.
|
||||||
|
|
||||||
`error_handler` (in)
|
`error_handler` (in)
|
||||||
@@ -64,7 +65,9 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
|
|||||||
## Exceptions
|
## Exceptions
|
||||||
|
|
||||||
- Throws [`other_error.502`](../../home/exceptions.md#jsonexceptionother_error502) if `use_type` is true and `use_size`
|
- Throws [`other_error.502`](../../home/exceptions.md#jsonexceptionother_error502) if `use_type` is true and `use_size`
|
||||||
is false, and `j` contains a non-empty array, object, or binary value.
|
is false, and `j` contains a non-empty array or object, or a non-empty binary value (with
|
||||||
|
`version = bjdata_version_t::draft3`, any binary value, including an empty one).
|
||||||
|
Scalars, empty arrays, and empty objects do not throw.
|
||||||
- Throws [type_error.316](../../home/exceptions.md#jsonexceptiontype_error316) if a string or object key in `j` is
|
- 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` (the default only if
|
not valid UTF-8 and `error_handler` is `strict` (the default only if
|
||||||
[`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled)
|
[`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled)
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
#include <iomanip>
|
||||||
|
#include <iostream>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
void print_hex(const char* label, const std::vector<std::uint8_t>& v)
|
||||||
|
{
|
||||||
|
std::cout << label << ':';
|
||||||
|
for (auto b : v)
|
||||||
|
{
|
||||||
|
std::cout << ' ' << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(b);
|
||||||
|
}
|
||||||
|
std::cout << std::dec << std::setfill(' ') << '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
// a binary value
|
||||||
|
json j = json::binary({0xCA, 0xFE, 0xBA, 0xBE});
|
||||||
|
|
||||||
|
// Draft 2 (the default) writes an array of integers
|
||||||
|
auto v2 = json::to_bjdata(j, true, true, json::bjdata_version_t::draft2);
|
||||||
|
print_hex("draft2", v2);
|
||||||
|
|
||||||
|
// Draft 3 writes an optimized binary array
|
||||||
|
auto v3 = json::to_bjdata(j, true, true, json::bjdata_version_t::draft3);
|
||||||
|
print_hex("draft3", v3);
|
||||||
|
|
||||||
|
// reading yields an array for Draft 2 and a binary value for Draft 3
|
||||||
|
std::cout << json::from_bjdata(v2) << '\n';
|
||||||
|
std::cout << json::from_bjdata(v3) << '\n';
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
draft2: 5b 24 55 23 69 04 ca fe ba be
|
||||||
|
draft3: 5b 24 42 23 69 04 ca fe ba be
|
||||||
|
[202,254,186,190]
|
||||||
|
{"bytes":[202,254,186,190],"subtype":null}
|
||||||
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
The [BJData format](https://neurojson.org) was derived from and improved upon
|
The [BJData format](https://neurojson.org) was derived from and improved upon
|
||||||
[Universal Binary JSON(UBJSON)](https://ubjson.org) specification (Draft 12). Specifically, it introduces an optimized
|
[Universal Binary JSON(UBJSON)](https://ubjson.org) specification (Draft 12). Specifically, it introduces an optimized
|
||||||
array container for efficient storage of N-dimensional packed arrays (**ND-arrays**); it also adds 5 new type markers -
|
array container for efficient storage of N-dimensional packed arrays (**ND-arrays**); it also adds four new type markers -
|
||||||
`[u] - uint16`, `[m] - uint32`, `[M] - uint64`, `[h] - float16` and `[B] - byte` - to unambiguously map common binary
|
`[u] - uint16`, `[m] - uint32`, `[M] - uint64`, and `[h] - float16` - to unambiguously map common binary numeric types
|
||||||
numeric types; furthermore, it uses little-endian (LE) to store all numerics instead of big-endian (BE) as in UBJSON to
|
(Draft 3 adds `[B] - byte`); furthermore, it uses little-endian (LE) to store all numerics instead of big-endian (BE) as in UBJSON to
|
||||||
avoid unnecessary conversions on commonly available platforms.
|
avoid unnecessary conversions on commonly available platforms.
|
||||||
|
|
||||||
Compared to other binary JSON-like formats such as MessagePack and CBOR, both BJData and UBJSON demonstrate a rare
|
Compared to other binary JSON-like formats such as MessagePack and CBOR, both BJData and UBJSON demonstrate a rare
|
||||||
@@ -15,11 +15,30 @@ simple processing.
|
|||||||
|
|
||||||
!!! abstract "References"
|
!!! abstract "References"
|
||||||
|
|
||||||
- [BJData Specification](https://neurojson.org/bjdata/draft2)
|
- [BJData Specification (Draft 3)](https://neurojson.org/bjdata/draft3)
|
||||||
|
- [BJData Specification (Draft 2)](https://neurojson.org/bjdata/draft2)
|
||||||
|
|
||||||
|
!!! info "Supported drafts"
|
||||||
|
|
||||||
|
The library supports BJData Draft 2 (the default) and Draft 3, except for column-major N-dimensional arrays (see
|
||||||
|
below). The draft used for writing is selected with the
|
||||||
|
[`version` parameter](../../api/basic_json/bjdata_version_t.md) of
|
||||||
|
[`to_bjdata`](../../api/basic_json/to_bjdata.md); [`from_bjdata`](../../api/basic_json/from_bjdata.md) has no such
|
||||||
|
parameter and reads the markers of both drafts.
|
||||||
|
|
||||||
|
!!! warning "Draft 4 and column-major ND-arrays are not supported"
|
||||||
|
|
||||||
|
BJData Draft 4 (April 2026) adds structure-of-arrays (SoA) containers (`[$` or `{$` followed by a schema object)
|
||||||
|
and the extension type `E`. `from_bjdata` does not implement them and throws
|
||||||
|
[`parse_error.112`](../../home/exceptions.md#jsonexceptionparse_error112) for such input; `to_bjdata` never writes
|
||||||
|
them.
|
||||||
|
|
||||||
|
Likewise, column-major ND-arrays of Draft 3 (the `#` marker followed by an array that wraps the dimension vector)
|
||||||
|
are rejected with [`parse_error.113`](../../home/exceptions.md#jsonexceptionparse_error113).
|
||||||
|
|
||||||
## Byte layout
|
## Byte layout
|
||||||
|
|
||||||
BJData follows the structure of UBJSON: a one-byte, human-readable ASCII *type marker* followed by the payload of that type, with strings prefixed by a length that is itself an integer value. In contrast to UBJSON, numbers are stored in little-endian byte order, and additional markers cover unsigned 16/32/64-bit integers, half-precision floats, and bytes. Containers can be written unoptimized (opened with `[` or `{`, closed with `]` or `}`) or optimized with a declared element type (`$`) and/or count (`#`). BJData extends the optimized form: the count can be a list of dimensions, which stores an N-dimensional packed array as a flat sequence of values without any per-element markers.
|
BJData follows the structure of UBJSON: a one-byte, human-readable ASCII *type marker* followed by the payload of that type, with strings prefixed by a length that is itself an integer value. In contrast to UBJSON, numbers are stored in little-endian byte order, and additional markers cover unsigned 16/32/64-bit integers, half-precision floats, and bytes. Containers can be written unoptimized (opened with `[` or `{`, closed with `]` or `}`) or optimized with a count (`#`), optionally preceded by a declared element type (`$`). BJData extends the optimized form: the count can be a list of dimensions, which stores an N-dimensional packed array as a flat sequence of values without any per-element markers.
|
||||||
|
|
||||||
Consider the following JSON value (shown formatted for readability):
|
Consider the following JSON value (shown formatted for readability):
|
||||||
|
|
||||||
@@ -55,7 +74,7 @@ Consider the following JSON value (shown formatted for readability):
|
|||||||
The library uses the following mapping from JSON values types to BJData types according to the BJData specification:
|
The library uses the following mapping from JSON values types to BJData types according to the BJData specification:
|
||||||
|
|
||||||
| JSON value type | value/range | BJData type | marker |
|
| JSON value type | value/range | BJData type | marker |
|
||||||
|-----------------|-------------------------------------------|----------------|--------|
|
|-----------------|-------------------------------------------|-------------|-------------------------------------------------------------------------|
|
||||||
| null | `null` | null | `Z` |
|
| null | `null` | null | `Z` |
|
||||||
| boolean | `true` | true | `T` |
|
| boolean | `true` | true | `T` |
|
||||||
| boolean | `false` | false | `F` |
|
| boolean | `false` | false | `F` |
|
||||||
@@ -82,7 +101,7 @@ The library uses the following mapping from JSON values types to BJData types ac
|
|||||||
| string | *with shortest length indicator* | string | `S` |
|
| string | *with shortest length indicator* | string | `S` |
|
||||||
| array | *see notes on optimized format/ND-array* | array | `[` |
|
| array | *see notes on optimized format/ND-array* | array | `[` |
|
||||||
| object | *see notes on optimized format* | map | `{` |
|
| object | *see notes on optimized format* | map | `{` |
|
||||||
| binary | *see notes on binary values* | array | `[$B` |
|
| binary | *see notes on binary values* | array | `[$B` (Draft 3 with `use_size` and `use_type`), `[U`, or `[B` otherwise |
|
||||||
|
|
||||||
!!! success "Complete mapping"
|
!!! success "Complete mapping"
|
||||||
|
|
||||||
@@ -107,10 +126,16 @@ The library uses the following mapping from JSON values types to BJData types ac
|
|||||||
|
|
||||||
!!! info "Unused BJData markers"
|
!!! info "Unused BJData markers"
|
||||||
|
|
||||||
The following markers are not used in the conversion:
|
The following markers are not written:
|
||||||
|
|
||||||
- `Z`: no-op values are not created.
|
- `N`: no-op values are not created (they are skipped when reading).
|
||||||
- `C`: single-byte strings are serialized with `S` markers.
|
- `C`: single-character strings are serialized with `S` markers (`C` is read, and written for the elements of
|
||||||
|
ND-arrays with `"_ArrayType_": "char"`).
|
||||||
|
- `h`: floating-point numbers are written as `D`, not as float16 (`h` is read).
|
||||||
|
- `B`: only written in Draft 3 mode.
|
||||||
|
- `d` is only written for `single` ND-arrays or if `number_float_t` is `float`; `H` is only written for integers
|
||||||
|
wider than 64 bits (custom `number_integer_t`/`number_unsigned_t`). An unsigned integer above 9223372036854775807
|
||||||
|
is written as `M`.
|
||||||
|
|
||||||
!!! info "NaN/infinity handling"
|
!!! info "NaN/infinity handling"
|
||||||
|
|
||||||
@@ -142,7 +167,7 @@ The library uses the following mapping from JSON values types to BJData types ac
|
|||||||
BJData extends UBJSON's optimized array **size** marker to support ND-arrays of uniform numerical data types
|
BJData extends UBJSON's optimized array **size** marker to support ND-arrays of uniform numerical data types
|
||||||
(referred to as *packed arrays*). For example, the 2-D `uint8` integer array `[[1,2],[3,4],[5,6]]`, stored as nested
|
(referred to as *packed arrays*). For example, the 2-D `uint8` integer array `[[1,2],[3,4],[5,6]]`, stored as nested
|
||||||
optimized array in UBJSON `[ [$U#i2 1 2 [$U#i2 3 4 [$U#i2 5 6 ]`, can be further compressed in BJData to
|
optimized array in UBJSON `[ [$U#i2 1 2 [$U#i2 3 4 [$U#i2 5 6 ]`, can be further compressed in BJData to
|
||||||
`[$U#[$i#i2 2 3 1 2 3 4 5 6` or `[$U#[i2 i3] 1 2 3 4 5 6`.
|
`[$U#[$i#i2 3 2 1 2 3 4 5 6` or `[$U#[i3 i2] 1 2 3 4 5 6`.
|
||||||
|
|
||||||
To maintain type and size information, ND-arrays are converted to JSON objects following the **annotated array
|
To maintain type and size information, ND-arrays are converted to JSON objects following the **annotated array
|
||||||
format** (defined in the [JData specification (Draft 3)][JDataAAFmt]), when parsed using
|
format** (defined in the [JData specification (Draft 3)][JDataAAFmt]), when parsed using
|
||||||
@@ -152,11 +177,14 @@ The library uses the following mapping from JSON values types to BJData types ac
|
|||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"_ArrayType_": "uint8",
|
"_ArrayType_": "uint8",
|
||||||
"_ArraySize_": [2,3],
|
"_ArraySize_": [3,2],
|
||||||
"_ArrayData_": [1,2,3,4,5,6]
|
"_ArrayData_": [1,2,3,4,5,6]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
ND-arrays with the element type `h` (float16) are returned with `"_ArrayType_": "half"`. `to_bjdata` does not pack
|
||||||
|
`"half"` objects; they are written as regular objects, which read back as an equal value.
|
||||||
|
|
||||||
Likewise, when a JSON object in the above form is serialized using
|
Likewise, when a JSON object in the above form is serialized using
|
||||||
[`to_bjdata`](../../api/basic_json/to_bjdata.md), it is automatically converted into a compact BJData ND-array.
|
[`to_bjdata`](../../api/basic_json/to_bjdata.md), it is automatically converted into a compact BJData ND-array.
|
||||||
|
|
||||||
@@ -167,34 +195,40 @@ The library uses the following mapping from JSON values types to BJData types ac
|
|||||||
object; otherwise it is serialized as a regular JSON object, so the annotation is never lost in a round trip. This requires
|
object; otherwise it is serialized as a regular JSON object, so the annotation is never lost in a round trip. This requires
|
||||||
all of the following:
|
all of the following:
|
||||||
|
|
||||||
|
- the object has exactly the three keys `"_ArrayType_"`, `"_ArraySize_"`, and `"_ArrayData_"` (any other key, e.g.,
|
||||||
|
`"_ArrayOrder_"`, makes it a regular object),
|
||||||
- `"_ArrayType_"` is one of `uint8`, `int8`, `uint16`, `int16`, `uint32`, `int32`, `uint64`, `int64`, `single`,
|
- `"_ArrayType_"` is one of `uint8`, `int8`, `uint16`, `int16`, `uint32`, `int32`, `uint64`, `int64`, `single`,
|
||||||
`double`, `char`, or `byte`,
|
`double`, `char`, or (only with `version = bjdata_version_t::draft3`, since the `B` marker is not part of Draft 2)
|
||||||
|
`byte`; the names are case-sensitive,
|
||||||
- `"_ArraySize_"` is an array, since the dimensions are written as the ND-array header's length,
|
- `"_ArraySize_"` is an array, since the dimensions are written as the ND-array header's length,
|
||||||
- `"_ArraySize_"` has at least two entries and is not a 1×N row vector (first entry 1), since other shapes are
|
- `"_ArraySize_"` has at least two entries and is not a two-entry row vector (`[1,N]`), since other shapes are
|
||||||
parsed back as a regular array,
|
parsed back as a regular array (`[1,2,2]` is packed, `[1,4]` is not),
|
||||||
- every entry of `"_ArraySize_"` is a positive integer, and their product is representable as a `std::size_t`,
|
- every entry of `"_ArraySize_"` is a positive integer, and their product is representable as a `std::size_t`,
|
||||||
- `"_ArrayData_"` is an array holding exactly that many elements, and
|
- `"_ArrayData_"` is an array holding exactly that many elements, and
|
||||||
- every element of `"_ArrayData_"` is a number of the kind named by `"_ArrayType_"`: for the integer types, a
|
- every element of `"_ArrayData_"` is a number of the kind named by `"_ArrayType_"`: for the integer types, an
|
||||||
value that fits the named width; for `double`, any value; for `single`, a value that survives narrowing to
|
integer (a floating-point number such as `1.0` does not qualify) that fits the named width (for `char`, 0..127);
|
||||||
`float` and back without change (for instance, `0.1` does not, since it is not exactly representable as
|
for `double`, a floating-point number (an integer such as `1` does not qualify); for `single`, a floating-point
|
||||||
`float`).
|
number that survives narrowing to `float` and back without change (for instance, `0.1` does not, since it is not
|
||||||
|
exactly representable as `float`).
|
||||||
|
|
||||||
An annotated object is always read back with its keys in the order shown above, `"_ArrayType_"`, `"_ArraySize_"`,
|
With `ordered_json`, an annotated object read from BJData has its keys in the order `"_ArrayType_"`,
|
||||||
`"_ArrayData_"`, regardless of the order the ND-array's header stores them in on the wire. This matters for
|
`"_ArraySize_"`, `"_ArrayData_"`; with `json`, they are sorted like those of any other object.
|
||||||
`ordered_json`, whose comparison takes key order into account.
|
|
||||||
|
|
||||||
The current version of this library does not yet support automatic detection of and conversion from a nested JSON
|
The current version of this library does not yet support automatic detection of and conversion from a nested JSON
|
||||||
array input to a BJData ND-array.
|
array input to a BJData ND-array.
|
||||||
|
|
||||||
[JDataAAFmt]: https://github.com/NeuroJSON/jdata/blob/master/JData_specification.md#annotated-storage-of-n-d-arrays
|
[JDataAAFmt]: https://github.com/NeuroJSON/jdata/blob/Draft-3/JData_specification.md#annotated-storage-of-n-d-arrays
|
||||||
|
|
||||||
!!! info "Restrictions in optimized data types for arrays and objects"
|
!!! info "Restrictions in optimized data types for arrays and objects"
|
||||||
|
|
||||||
Due to diminished space saving, hampered readability, and increased security risks, in BJData, the allowed data
|
Due to diminished space saving, hampered readability, and increased security risks, in BJData, the
|
||||||
types following the `$` marker in an optimized array and object container are restricted to
|
allowed data types following the `$` marker in an optimized array and object container are restricted to
|
||||||
**non-zero-fixed-length** data types. Therefore, the valid optimized type markers can only be one of
|
**non-zero-fixed-length** data types. Therefore, the valid optimized type markers can only be one of
|
||||||
`UiuImlMLhdDCB`. This also means other variable (`[{SH`) or zero-length types (`TFN`) can not be used in an
|
`UiuImlMLhdDC`, plus `B` since Draft 3 (the reader accepts `B` in both drafts). This also means other variable (`[{SH`) or zero-length types (`TFZN`) can not be used in an
|
||||||
optimized array or object in BJData.
|
optimized array or object in BJData. The library enforces this restriction for both drafts. A `C` (char) element above 127 is rejected on read with
|
||||||
|
[`parse_error.113`](../../home/exceptions.md#jsonexceptionparse_error113), in 1-D and in N-dimensional arrays. With `use_type`, the
|
||||||
|
elements must have the same marker (`[1,200,3]` is not optimized, since it mixes `i` and `U`); booleans, null,
|
||||||
|
strings, arrays, and objects are never optimized with `$`.
|
||||||
|
|
||||||
!!! info "Binary values"
|
!!! info "Binary values"
|
||||||
|
|
||||||
@@ -202,14 +236,16 @@ The library uses the following mapping from JSON values types to BJData types ac
|
|||||||
in optimized arrays to designate binary data. This means that, unlike UBJSON, binary data can be both serialized and
|
in optimized arrays to designate binary data. This means that, unlike UBJSON, binary data can be both serialized and
|
||||||
deserialized.
|
deserialized.
|
||||||
|
|
||||||
To preserve compatibility with BJData Draft 2, the Draft 3 optimized binary array must be explicitly enabled using
|
To preserve compatibility with BJData Draft 2, the Draft 3 optimized binary array must be explicitly enabled: pass
|
||||||
the `version` parameter of [`to_bjdata`](../../api/basic_json/to_bjdata.md).
|
`version = bjdata_version_t::draft3` together with `use_size = true` and `use_type = true` to
|
||||||
|
[`to_bjdata`](../../api/basic_json/to_bjdata.md). With Draft 3 and only `use_size` (or neither), a binary value is
|
||||||
|
written as an array of `B` elements.
|
||||||
|
|
||||||
In Draft2 mode (default), if the JSON data contains the binary type, the value stored as a list of integers, as
|
In Draft 2 mode (default), if the JSON data contains the binary type, the value is stored as a list of integers, as
|
||||||
suggested by the BJData documentation. In particular, this means that the serialization and the deserialization of
|
suggested by the BJData documentation. In particular, this means that the serialization and the deserialization of
|
||||||
JSON containing binary values into BJData and back will result in a different JSON object.
|
JSON containing binary values into BJData and back will result in a different JSON object.
|
||||||
|
|
||||||
[BJDataBinArr]: https://github.com/NeuroJSON/bjdata/blob/master/Binary_JData_Specification.md#optimized-binary-array
|
[BJDataBinArr]: https://github.com/NeuroJSON/bjdata/blob/Draft-3/Binary_JData_Specification.md#optimized-binary-array
|
||||||
|
|
||||||
??? example "Example: serialize JSON values to BJData, with and without size/type optimization"
|
??? example "Example: serialize JSON values to BJData, with and without size/type optimization"
|
||||||
|
|
||||||
@@ -228,7 +264,7 @@ The library uses the following mapping from JSON values types to BJData types ac
|
|||||||
The library maps BJData types to JSON value types as follows:
|
The library maps BJData types to JSON value types as follows:
|
||||||
|
|
||||||
| BJData type | JSON value type | marker |
|
| BJData type | JSON value type | marker |
|
||||||
|-------------|------------------------------------------|----------|
|
|-----------------------|--------------------------------------------------|----------|
|
||||||
| no-op | *no value, next value is read* | `N` |
|
| no-op | *no value, next value is read* | `N` |
|
||||||
| null | `null` | `Z` |
|
| null | `null` | `Z` |
|
||||||
| false | `false` | `F` |
|
| false | `false` | `F` |
|
||||||
@@ -236,6 +272,7 @@ The library maps BJData types to JSON value types as follows:
|
|||||||
| float16 | number_float | `h` |
|
| float16 | number_float | `h` |
|
||||||
| float32 | number_float | `d` |
|
| float32 | number_float | `d` |
|
||||||
| float64 | number_float | `D` |
|
| float64 | number_float | `D` |
|
||||||
|
| high-precision number | number_integer, number_unsigned, or number_float | `H` |
|
||||||
| uint8 | number_unsigned | `U` |
|
| uint8 | number_unsigned | `U` |
|
||||||
| int8 | number_integer | `i` |
|
| int8 | number_integer | `i` |
|
||||||
| uint16 | number_unsigned | `u` |
|
| uint16 | number_unsigned | `u` |
|
||||||
@@ -254,7 +291,18 @@ The library maps BJData types to JSON value types as follows:
|
|||||||
|
|
||||||
!!! success "Complete mapping"
|
!!! success "Complete mapping"
|
||||||
|
|
||||||
The mapping is **complete** in the sense that any BJData value can be converted to a JSON value.
|
The mapping is **complete** for the BJData types of Draft 2 and Draft 3 with one exception: column-major
|
||||||
|
N-dimensional arrays are rejected with [`parse_error.113`](../../home/exceptions.md#jsonexceptionparse_error113).
|
||||||
|
The Draft 4 constructs (structure-of-arrays containers and the extension type `E`) are not supported either.
|
||||||
|
|
||||||
|
The digits of an `H` value are parsed like a JSON number: integers that fit 64 bits become `number_integer` or
|
||||||
|
`number_unsigned`, anything else a `number_float`; invalid text throws
|
||||||
|
[`parse_error.115`](../../home/exceptions.md#jsonexceptionparse_error115).
|
||||||
|
|
||||||
|
!!! info "Duplicate object keys"
|
||||||
|
|
||||||
|
If an object contains the same key more than once, `from_bjdata` accepts it and keeps the last value; with
|
||||||
|
`ordered_json`, the key keeps the position of its first occurrence.
|
||||||
|
|
||||||
!!! warning "Ill-formed UTF-8 in string values and object keys"
|
!!! warning "Ill-formed UTF-8 in string values and object keys"
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# BON8
|
# BON8
|
||||||
|
|
||||||
BON8 (Binary Object Notation 8) is a compact binary serialization format for JSON values. It uses the byte values that
|
BON8 (Binary Object Notation 8) is a compact binary serialization format for JSON values. It uses byte values that
|
||||||
cannot begin a UTF-8 character as type markers, so strings are stored as plain UTF-8 without a length prefix: a string
|
cannot begin a UTF-8 character as type markers, so strings are stored as plain UTF-8 without a length prefix: a string
|
||||||
ends at the first byte that cannot continue it. Integers from -10 to 39, `true`, `false`, `null`, and the floating-point
|
ends at the first byte that cannot continue it. Integers from -10 to 39, `true`, `false`, `null`, and the floating-point
|
||||||
values -1.0, 0.0, and 1.0 take a single byte, and arrays and objects with up to four elements need no terminator.
|
values -1.0, 0.0, and 1.0 take a single byte, and arrays and objects with up to four elements need no terminator.
|
||||||
@@ -12,7 +12,7 @@ values -1.0, 0.0, and 1.0 take a single byte, and arrays and objects with up to
|
|||||||
|
|
||||||
## Byte layout
|
## Byte layout
|
||||||
|
|
||||||
BON8 exploits the fact that some byte values can never start a UTF-8 character. These bytes serve as *type markers*, so strings can be stored as plain UTF-8 without a length prefix: a string ends at the first byte that cannot continue it (often the marker of the next value). Small integers, `true`, `false`, `null`, and a few common floats are a single byte. Arrays and objects are introduced by a marker; those with up to four elements encode their size in the marker and need no terminator, while larger ones are closed by the end marker `0xFE`.
|
BON8 exploits the fact that some byte values can never start a UTF-8 character. These bytes serve as *type markers*, so strings can be stored as plain UTF-8 without a length prefix: a string ends at the first byte that cannot continue it: a marker byte, or a lead byte 0xC2..0xF7 followed by a byte outside 0x80..0xBF. The ending byte is often the marker of the next value. Small integers, `true`, `false`, `null`, and a few common floats are a single byte. Arrays and objects are introduced by a marker; those with up to four elements encode their size in the marker and need no terminator, while larger ones are closed by the end marker `0xFE`.
|
||||||
|
|
||||||
Consider the following JSON value (shown formatted for readability):
|
Consider the following JSON value (shown formatted for readability):
|
||||||
|
|
||||||
@@ -33,7 +33,7 @@ Consider the following JSON value (shown formatted for readability):
|
|||||||
<div class="byte-layout" markdown="0"><table>
|
<div class="byte-layout" markdown="0"><table>
|
||||||
<thead><tr><th>JSON</th><th>Bytes</th><th>Meaning</th></tr></thead><tbody>
|
<thead><tr><th>JSON</th><th>Bytes</th><th>Meaning</th></tr></thead><tbody>
|
||||||
<tr><td><code>{</code></td><td><span class="bl bl-t">8A</span></td><td>object with 4 members</td></tr>
|
<tr><td><code>{</code></td><td><span class="bl bl-t">8A</span></td><td>object with 4 members</td></tr>
|
||||||
<tr><td><code>"id": 300,</code></td><td><span class="bl bl-d">69 64</span> <span class="bl bl-t">C4</span> <span class="bl bl-d">04</span></td><td>key: UTF-8 bytes; the string ends at the next byte, which cannot continue a UTF-8 character; value: 2-byte positive integer (lead byte, then a byte below 0x80)</td></tr>
|
<tr><td><code>"id": 300,</code></td><td><span class="bl bl-d">69 64</span> <span class="bl bl-t">C4</span> <span class="bl bl-d">04</span></td><td>key: UTF-8 bytes; the string ends at 0xC4, because the byte after it (0x04) is not a UTF-8 continuation byte, so 0xC4 starts an integer; value: 2-byte positive integer (lead byte, then a byte below 0x80)</td></tr>
|
||||||
<tr><td><code>"name": "json",</code></td><td><span class="bl bl-d">6E 61 6D 65</span> <span class="bl bl-l">FF</span> <span class="bl bl-d">6A 73 6F 6E</span> <span class="bl bl-l">FF</span></td><td>key: UTF-8 bytes; 0xFF terminates the string as another string follows; value: UTF-8 bytes; terminated as a string key follows</td></tr>
|
<tr><td><code>"name": "json",</code></td><td><span class="bl bl-d">6E 61 6D 65</span> <span class="bl bl-l">FF</span> <span class="bl bl-d">6A 73 6F 6E</span> <span class="bl bl-l">FF</span></td><td>key: UTF-8 bytes; 0xFF terminates the string as another string follows; value: UTF-8 bytes; terminated as a string key follows</td></tr>
|
||||||
<tr><td><code>"pi": 3.14,</code></td><td><span class="bl bl-d">70 69</span> <span class="bl bl-t">8F</span> <span class="bl bl-d">40 09 1E B8 51 EB 85 1F</span></td><td>key: UTF-8 bytes; value: binary64 (IEEE 754, big-endian); its marker also ends <code>"pi"</code></td></tr>
|
<tr><td><code>"pi": 3.14,</code></td><td><span class="bl bl-d">70 69</span> <span class="bl bl-t">8F</span> <span class="bl bl-d">40 09 1E B8 51 EB 85 1F</span></td><td>key: UTF-8 bytes; value: binary64 (IEEE 754, big-endian); its marker also ends <code>"pi"</code></td></tr>
|
||||||
<tr><td><code>"tags": [</code></td><td><span class="bl bl-d">74 61 67 73</span> <span class="bl bl-t">82</span></td><td>key: UTF-8 bytes; value: array with 2 elements; its marker also ends <code>"tags"</code></td></tr>
|
<tr><td><code>"tags": [</code></td><td><span class="bl bl-d">74 61 67 73</span> <span class="bl bl-t">82</span></td><td>key: UTF-8 bytes; value: array with 2 elements; its marker also ends <code>"tags"</code></td></tr>
|
||||||
@@ -100,7 +100,8 @@ serialized as 0x81 0x65 0xFF, but `[1,2,3,4,"e"]` as 0x85 0x91 0x92 0x93 0x94 0x
|
|||||||
|
|
||||||
The following values can **not** be converted to a BON8 value:
|
The following values can **not** be converted to a BON8 value:
|
||||||
|
|
||||||
- unsigned integers above 9223372036854775807, because BON8 has no unsigned 64-bit integer type
|
- integers outside the int64 range (unsigned integers above 9223372036854775807 and, for number types wider than
|
||||||
|
64 bits, any larger or smaller integer), because BON8 integers are limited to int64
|
||||||
([out_of_range.407](../../home/exceptions.md#jsonexceptionout_of_range407))
|
([out_of_range.407](../../home/exceptions.md#jsonexceptionout_of_range407))
|
||||||
- strings that are not valid UTF-8, because the end of a string is determined from its encoding
|
- strings that are not valid UTF-8, because the end of a string is determined from its encoding
|
||||||
([type_error.316](../../home/exceptions.md#jsonexceptiontype_error316))
|
([type_error.316](../../home/exceptions.md#jsonexceptiontype_error316))
|
||||||
@@ -168,7 +169,13 @@ The library maps BON8 types to JSON value types as follows:
|
|||||||
|
|
||||||
Non-negative integers are read as number_unsigned, negative integers as number_integer.
|
Non-negative integers are read as number_unsigned, negative integers as number_integer.
|
||||||
|
|
||||||
!!! info
|
A byte 0xF5..0xF7 followed by 0x80..0xBF is neither a string nor an integer and is rejected with
|
||||||
|
[parse_error.112](../../home/exceptions.md#jsonexceptionparse_error112) (invalid UTF-8 byte). The same error is
|
||||||
|
reported for any other invalid byte (such as a stray 0xFE), for ill-formed UTF-8, and for an object key that is not a
|
||||||
|
string. Input that ends prematurely, trailing data after the value, and a missing final 0xFF after a string at the end
|
||||||
|
of the message are reported as [parse_error.110](../../home/exceptions.md#jsonexceptionparse_error110).
|
||||||
|
|
||||||
|
!!! info "Non-canonical input"
|
||||||
|
|
||||||
Values that do not use the canonical representation, such as integers with a longer encoding than necessary,
|
Values that do not use the canonical representation, such as integers with a longer encoding than necessary,
|
||||||
arrays and objects with up to four elements that are terminated by 0xFE, unsorted object keys, or a 0xFF after a
|
arrays and objects with up to four elements that are terminated by 0xFE, unsorted object keys, or a 0xFF after a
|
||||||
@@ -176,7 +183,10 @@ Non-negative integers are read as number_unsigned, negative integers as number_i
|
|||||||
|
|
||||||
Strings must be valid UTF-8, and a string at the very end of a message must be terminated by 0xFF.
|
Strings must be valid UTF-8, and a string at the very end of a message must be terminated by 0xFF.
|
||||||
|
|
||||||
!!! info
|
Duplicate object keys are accepted; the last value wins (`ordered_json` keeps the position of the first occurrence).
|
||||||
|
The specification does not say how to treat them; the HikoGUI reference decoder keeps the first value.
|
||||||
|
|
||||||
|
!!! info "Round trips"
|
||||||
|
|
||||||
Any BON8 output created by `to_bon8` can be successfully parsed by `from_bon8`.
|
Any BON8 output created by `to_bon8` can be successfully parsed by `from_bon8`.
|
||||||
|
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ to efficiently encode JSON values to byte vectors and to decode such vectors.
|
|||||||
### Completeness
|
### Completeness
|
||||||
|
|
||||||
| Format | Serialization | Deserialization |
|
| Format | Serialization | Deserialization |
|
||||||
|-------------|-----------------------------------------------|----------------------------------------------|
|
|-------------|-----------------------------------------------|-----------------------------------------------------------------------|
|
||||||
| BJData | complete | complete |
|
| BJData | complete | incomplete: no column-major ND-arrays, no Draft 4 SoA/extension types |
|
||||||
| BON8 | incomplete: no unsigned integers above int64 | complete |
|
| BON8 | incomplete: no unsigned integers above int64 | complete |
|
||||||
| BSON | incomplete: top-level value must be an object | incomplete, but all JSON types are supported |
|
| 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 |
|
| CBOR | complete | incomplete, but all JSON types are supported |
|
||||||
@@ -28,8 +28,8 @@ to efficiently encode JSON values to byte vectors and to decode such vectors.
|
|||||||
### Binary values
|
### Binary values
|
||||||
|
|
||||||
| Format | Binary values | Binary subtypes |
|
| Format | Binary values | Binary subtypes |
|
||||||
|-------------|---------------|-----------------|
|
|-------------|--------------------------|-----------------|
|
||||||
| BJData | not supported | not supported |
|
| BJData | supported (Draft 3 only) | not supported |
|
||||||
| BON8 | not supported | not supported |
|
| BON8 | not supported | not supported |
|
||||||
| BSON | supported | supported |
|
| BSON | supported | supported |
|
||||||
| CBOR | supported | supported |
|
| CBOR | supported | supported |
|
||||||
|
|||||||
@@ -132,8 +132,11 @@ is an integer or `null`.
|
|||||||
|
|
||||||
### BJData
|
### BJData
|
||||||
|
|
||||||
[BJData](binary_formats/bjdata.md) neither supports binary values nor subtypes and proposes to serialize binary values
|
[BJData](binary_formats/bjdata.md) Draft 3 supports binary values as optimized arrays of `B` (byte) elements, but no
|
||||||
as an array of uint8 values. The library implements this translation.
|
subtypes. With `version = bjdata_version_t::draft3` and both `use_size` and `use_type` set, `to_bjdata` writes a binary
|
||||||
|
value as `[$B#...`, and `from_bjdata` reads it back as a binary value (without subtype). In all other cases, including
|
||||||
|
the default Draft 2 mode, the library follows the Draft 2 proposal and writes an array of integers (`U` in Draft 2, `B`
|
||||||
|
in Draft 3), which is read back as an array.
|
||||||
|
|
||||||
??? example "Example: serialize a binary value to BJData"
|
??? example "Example: serialize a binary value to BJData"
|
||||||
|
|
||||||
@@ -171,7 +174,7 @@ as an array of uint8 values. The library implements this translation.
|
|||||||
|
|
||||||
```c
|
```c
|
||||||
0x7B // '{'
|
0x7B // '{'
|
||||||
0x23 0x69 0x01 // '#' 'i' type of the array elements: unsigned integers
|
0x23 0x69 0x01 // '#' 'i' 1 number of object members
|
||||||
0x69 0x06 // i 6 (length of the key)
|
0x69 0x06 // i 6 (length of the key)
|
||||||
0x62 0x69 0x6E 0x61 0x72 0x79 // "binary"
|
0x62 0x69 0x6E 0x61 0x72 0x79 // "binary"
|
||||||
0x5B // '[' array
|
0x5B // '[' array
|
||||||
@@ -180,7 +183,7 @@ as an array of uint8 values. The library implements this translation.
|
|||||||
0xCA 0xFE 0xBA 0xBE // content
|
0xCA 0xFE 0xBA 0xBE // content
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that subtype (42) is **not** serialized and that BJData has **no binary type**, and deserializing `v` would
|
Note that subtype (42) is **not** serialized and that, in Draft 2 mode, BJData has **no binary type**, and deserializing `v` would
|
||||||
yield the following value:
|
yield the following value:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
@@ -213,7 +216,7 @@ array of integers.
|
|||||||
0x87 // object with 1 member
|
0x87 // object with 1 member
|
||||||
0x62 0x69 0x6E 0x61 0x72 0x79 // "binary"
|
0x62 0x69 0x6E 0x61 0x72 0x79 // "binary"
|
||||||
0x84 // array with 4 elements
|
0x84 // array with 4 elements
|
||||||
0xC3 0x22 0xC3 0x56 0xC3 0x12 0xC3 0x16 // content (each byte as a 2-byte integer)
|
0xC3 0x22 0xC3 0x56 0xC3 0x12 0xC3 0x16 // content (each byte, here all above 39, as a 2-byte integer)
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that the subtype is lost, and deserializing `v` would yield the following value:
|
Note that the subtype is lost, and deserializing `v` would yield the following value:
|
||||||
|
|||||||
@@ -346,7 +346,7 @@ been read, or (with `strict = true`) further bytes follow the value.
|
|||||||
|
|
||||||
### json.exception.parse_error.112
|
### json.exception.parse_error.112
|
||||||
|
|
||||||
An unexpected byte was read in a [binary format](../features/binary_formats/index.md) or length information is invalid ([BSON](../features/binary_formats/bson.md)).
|
An unexpected byte was read in a [binary format](../features/binary_formats/index.md) or length information is invalid ([BSON](../features/binary_formats/bson.md)). In [BON8](../features/binary_formats/bon8.md), this exception is also thrown for a key that is not a string and for ill-formed UTF-8.
|
||||||
|
|
||||||
!!! failure "Example messages"
|
!!! failure "Example messages"
|
||||||
|
|
||||||
@@ -371,15 +371,21 @@ An unexpected byte was read in a [binary format](../features/binary_formats/inde
|
|||||||
```
|
```
|
||||||
[json.exception.parse_error.112] parse error at byte 5: syntax error while parsing BSON document: document size 6 does not match the number of bytes read (5)
|
[json.exception.parse_error.112] parse error at byte 5: syntax error while parsing BSON document: document size 6 does not match the number of bytes read (5)
|
||||||
```
|
```
|
||||||
|
```
|
||||||
|
[json.exception.parse_error.112] parse error at byte 2: syntax error while parsing BON8 string: invalid UTF-8 byte: 0x80
|
||||||
|
```
|
||||||
|
|
||||||
### json.exception.parse_error.113
|
### json.exception.parse_error.113
|
||||||
|
|
||||||
A string could not be read from a [binary format](../features/binary_formats/index.md): either a value that is not a
|
A string could not be read from a [binary format](../features/binary_formats/index.md): either a value that is not a
|
||||||
string was read where one was required (for instance as a map key), the string's length specification is invalid, or
|
string was read where one was required (for instance as a map key), the string's length specification is invalid, or
|
||||||
the string's bytes are not valid UTF-8 and the `error_handler` parameter of the corresponding `from_*` function is
|
the string's bytes are not valid UTF-8 and the `error_handler` parameter of the corresponding `from_*` function is
|
||||||
set to `strict`. By default (`error_handler_t::keep`), the bytes of a string are not checked for valid UTF-8 on read;
|
set to `strict`. By default (`error_handler_t::keep`), the bytes of a string are not checked for valid UTF-8 on read.
|
||||||
see the ill-formed UTF-8 notes on the individual [binary format](../features/binary_formats/index.md) pages for how
|
This applies to the `from_*` functions that have an `error_handler` parameter; `from_bon8` does not have one.
|
||||||
|
See the ill-formed UTF-8 notes on the individual [binary format](../features/binary_formats/index.md) pages for how
|
||||||
such a string is handled depending on `error_handler`.
|
such a string is handled depending on `error_handler`.
|
||||||
|
[`from_bon8()`](../api/basic_json/from_bon8.md) never throws this exception: BON8 non-string keys and ill-formed UTF-8
|
||||||
|
are reported as [parse_error.112](#jsonexceptionparse_error112).
|
||||||
|
|
||||||
CBOR and MessagePack allow map keys of any type, but JSON object keys are always strings. Maps with keys of any other
|
CBOR and MessagePack allow map keys of any type, but JSON object keys are always strings. Maps with keys of any other
|
||||||
type (for instance integers or `null`) are therefore not supported; see the notes on
|
type (for instance integers or `null`) are therefore not supported; see the notes on
|
||||||
@@ -418,13 +424,16 @@ The parsing of the corresponding BSON record type is not implemented (yet).
|
|||||||
|
|
||||||
### json.exception.parse_error.115
|
### json.exception.parse_error.115
|
||||||
|
|
||||||
A UBJSON high-precision number could not be parsed.
|
A UBJSON or BJData high-precision number could not be parsed.
|
||||||
|
|
||||||
!!! failure "Example message"
|
!!! failure "Example messages"
|
||||||
|
|
||||||
```
|
```
|
||||||
[json.exception.parse_error.115] parse error at byte 5: syntax error while parsing UBJSON high-precision number: invalid number text: 1A
|
[json.exception.parse_error.115] parse error at byte 5: syntax error while parsing UBJSON high-precision number: invalid number text: 1A
|
||||||
```
|
```
|
||||||
|
```
|
||||||
|
[json.exception.parse_error.115] parse error at byte 5: syntax error while parsing BJData high-precision number: invalid number text: 1A
|
||||||
|
```
|
||||||
|
|
||||||
### json.exception.parse_error.116
|
### json.exception.parse_error.116
|
||||||
|
|
||||||
@@ -812,6 +821,9 @@ The binary writers [`to_cbor()`](../api/basic_json/to_cbor.md), [`to_ubjson()`](
|
|||||||
as well for a string value or object key that is not valid UTF-8 if their `error_handler` is `strict` (the default if
|
as well for a string value or object key that is not valid UTF-8 if their `error_handler` is `strict` (the default if
|
||||||
[`JSON_STRICT_BINARY_UTF8`](../api/macros/json_strict_binary_utf8.md) is enabled). So does
|
[`JSON_STRICT_BINARY_UTF8`](../api/macros/json_strict_binary_utf8.md) is enabled). So does
|
||||||
[`to_msgpack()`](../api/basic_json/to_msgpack.md) if `error_handler_t::strict` is passed.
|
[`to_msgpack()`](../api/basic_json/to_msgpack.md) if `error_handler_t::strict` is passed.
|
||||||
|
[`to_bon8()`](../api/basic_json/to_bon8.md) always throws this exception for such a string or key, regardless of
|
||||||
|
[`JSON_STRICT_BINARY_UTF8`](../api/macros/json_strict_binary_utf8.md), because the end of a BON8 string is determined
|
||||||
|
from its UTF-8 encoding.
|
||||||
|
|
||||||
!!! failure "Example message"
|
!!! failure "Example message"
|
||||||
|
|
||||||
@@ -1006,7 +1018,8 @@ double-precision number when `number_float_t` is `#!cpp float`.
|
|||||||
An integer number cannot be represented by the binary format it is serialized to:
|
An integer number cannot be represented by the binary format it is serialized to:
|
||||||
|
|
||||||
- [BON8](../features/binary_formats/bon8.md) only stores integers that fit into int64, so
|
- [BON8](../features/binary_formats/bon8.md) only stores integers that fit into int64, so
|
||||||
[`to_bon8`](../api/basic_json/to_bon8.md) throws for unsigned integers greater than 9223372036854775807.
|
[`to_bon8`](../api/basic_json/to_bon8.md) throws for unsigned integers greater than 9223372036854775807 and, with a
|
||||||
|
number type wider than 64 bits, for any integer outside the int64 range.
|
||||||
- [CBOR](../features/binary_formats/cbor.md), [MessagePack](../features/binary_formats/messagepack.md), and
|
- [CBOR](../features/binary_formats/cbor.md), [MessagePack](../features/binary_formats/messagepack.md), and
|
||||||
[BSON](../features/binary_formats/bson.md) store integers in at most 64 bits. With the default number types, every
|
[BSON](../features/binary_formats/bson.md) store integers in at most 64 bits. With the default number types, every
|
||||||
integer fits, but a [`number_integer_t`](../api/basic_json/number_integer_t.md) or
|
integer fits, but a [`number_integer_t`](../api/basic_json/number_integer_t.md) or
|
||||||
@@ -1025,7 +1038,7 @@ exception, because they serialize integers beyond 64 bits as high-precision numb
|
|||||||
- Since version 3.12.0, integer numbers beyond int64 are serialized as uint64 BSON numbers.
|
- Since version 3.12.0, integer numbers beyond int64 are serialized as uint64 BSON numbers.
|
||||||
|
|
||||||
Before version 3.13.0, CBOR, MessagePack, and BSON silently truncated integers wider than 64 bits, and BJData
|
Before version 3.13.0, CBOR, MessagePack, and BSON silently truncated integers wider than 64 bits, and BJData
|
||||||
truncated unsigned integers wider than 64 bits.
|
truncated unsigned integers wider than 64 bits that were elements of ND-arrays with `"_ArrayType_": "uint64"`.
|
||||||
|
|
||||||
!!! failure "Example messages"
|
!!! failure "Example messages"
|
||||||
|
|
||||||
@@ -1233,8 +1246,9 @@ A JSON Patch operation 'test' failed. The unsuccessful operation is also printed
|
|||||||
### json.exception.other_error.502
|
### 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
|
[`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` for a value that contains a non-empty array, object, or binary value. UBJSON
|
`use_type = true` but `use_size = false`, and the value contains a non-empty array or object (or, with BJData Draft 3,
|
||||||
requires a size marker (`#`) after a type marker (`$`); scalars and empty containers do not throw.
|
any binary value). UBJSON requires a size marker (`#`) after a type marker (`$`); scalars and empty arrays or objects
|
||||||
|
do not throw.
|
||||||
|
|
||||||
!!! failure "Example message"
|
!!! failure "Example message"
|
||||||
|
|
||||||
|
|||||||
@@ -173,6 +173,7 @@ nav:
|
|||||||
- 'begin': api/basic_json/begin.md
|
- 'begin': api/basic_json/begin.md
|
||||||
- 'binary': api/basic_json/binary.md
|
- 'binary': api/basic_json/binary.md
|
||||||
- 'binary_t': api/basic_json/binary_t.md
|
- 'binary_t': api/basic_json/binary_t.md
|
||||||
|
- 'bjdata_version_t': api/basic_json/bjdata_version_t.md
|
||||||
- 'boolean_t': api/basic_json/boolean_t.md
|
- 'boolean_t': api/basic_json/boolean_t.md
|
||||||
- 'cbegin': api/basic_json/cbegin.md
|
- 'cbegin': api/basic_json/cbegin.md
|
||||||
- 'cbor_tag_handler_t': api/basic_json/cbor_tag_handler_t.md
|
- 'cbor_tag_handler_t': api/basic_json/cbor_tag_handler_t.md
|
||||||
|
|||||||
@@ -3277,6 +3277,8 @@ class binary_reader
|
|||||||
// get_ubjson_size_value() (the type marker is known before the dimension vector
|
// get_ubjson_size_value() (the type marker is known before the dimension vector
|
||||||
// that determines size_and_type.first is read, so it is emitted first there to
|
// that determines size_and_type.first is read, so it is emitted first there to
|
||||||
// match the documented _ArrayType_, _ArraySize_, _ArrayData_ key order)
|
// match the documented _ArrayType_, _ArraySize_, _ArrayData_ key order)
|
||||||
|
// 'C' elements are read as unsigned bytes, but BJData limits a char to 0..127
|
||||||
|
const bool nd_is_char = size_and_type.second == 'C';
|
||||||
if (size_and_type.second == 'C' || size_and_type.second == 'B')
|
if (size_and_type.second == 'C' || size_and_type.second == 'B')
|
||||||
{
|
{
|
||||||
size_and_type.second = 'U';
|
size_and_type.second = 'U';
|
||||||
@@ -3290,6 +3292,24 @@ class binary_reader
|
|||||||
|
|
||||||
for (std::size_t i = 0; i < size_and_type.first; ++i)
|
for (std::size_t i = 0; i < size_and_type.first; ++i)
|
||||||
{
|
{
|
||||||
|
if (nd_is_char)
|
||||||
|
{
|
||||||
|
get();
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof("char")))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(current > 127))
|
||||||
|
{
|
||||||
|
return last_byte_error(exception_id::invalid_string_or_size, "byte after 'C' must be in range 0x00..0x7F", "char", "");
|
||||||
|
}
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(!sax->number_unsigned(static_cast<number_unsigned_t>(current))))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second)))
|
if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second)))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@@ -4655,6 +4675,8 @@ class binary_reader
|
|||||||
return "uint8";
|
return "uint8";
|
||||||
case 'd':
|
case 'd':
|
||||||
return "single";
|
return "single";
|
||||||
|
case 'h':
|
||||||
|
return "half";
|
||||||
case 'i':
|
case 'i':
|
||||||
return "int8";
|
return "int8";
|
||||||
case 'l':
|
case 'l':
|
||||||
|
|||||||
@@ -3848,7 +3848,7 @@ class binary_writer
|
|||||||
A C++11 `constexpr` function cannot contain a `switch`, so this is a plain
|
A C++11 `constexpr` function cannot contain a `switch`, so this is a plain
|
||||||
comparison chain instead; it is only reached once per ND-array candidate
|
comparison chain instead; it is only reached once per ND-array candidate
|
||||||
object. Keep in sync with binary_reader's `bjd_type_name()`, which maps
|
object. Keep in sync with binary_reader's `bjd_type_name()`, which maps
|
||||||
the other way.
|
the other way (it also reads "half", which is never written).
|
||||||
*/
|
*/
|
||||||
static CharType bjdata_ndarray_type_marker(const string_t& name)
|
static CharType bjdata_ndarray_type_marker(const string_t& name)
|
||||||
{
|
{
|
||||||
@@ -4056,10 +4056,13 @@ class binary_writer
|
|||||||
switch (dtype)
|
switch (dtype)
|
||||||
{
|
{
|
||||||
case 'U':
|
case 'U':
|
||||||
case 'C':
|
|
||||||
case 'B':
|
case 'B':
|
||||||
ok = write_bjdata_ndarray_element<std::uint8_t>(el, dry_run);
|
ok = write_bjdata_ndarray_element<std::uint8_t>(el, dry_run);
|
||||||
break;
|
break;
|
||||||
|
case 'C':
|
||||||
|
// BJData limits a char to 0..127 (the intersection of the int8 and uint8 ranges)
|
||||||
|
ok = bjdata_ndarray_value_in_range<std::int8_t>(el) && bjdata_ndarray_value_in_range<std::uint8_t>(el);
|
||||||
|
break;
|
||||||
case 'i':
|
case 'i':
|
||||||
ok = write_bjdata_ndarray_element<std::int8_t>(el, dry_run);
|
ok = write_bjdata_ndarray_element<std::int8_t>(el, dry_run);
|
||||||
break;
|
break;
|
||||||
|
|||||||
Generated
+27
-2
@@ -20744,6 +20744,8 @@ class binary_reader
|
|||||||
// get_ubjson_size_value() (the type marker is known before the dimension vector
|
// get_ubjson_size_value() (the type marker is known before the dimension vector
|
||||||
// that determines size_and_type.first is read, so it is emitted first there to
|
// that determines size_and_type.first is read, so it is emitted first there to
|
||||||
// match the documented _ArrayType_, _ArraySize_, _ArrayData_ key order)
|
// match the documented _ArrayType_, _ArraySize_, _ArrayData_ key order)
|
||||||
|
// 'C' elements are read as unsigned bytes, but BJData limits a char to 0..127
|
||||||
|
const bool nd_is_char = size_and_type.second == 'C';
|
||||||
if (size_and_type.second == 'C' || size_and_type.second == 'B')
|
if (size_and_type.second == 'C' || size_and_type.second == 'B')
|
||||||
{
|
{
|
||||||
size_and_type.second = 'U';
|
size_and_type.second = 'U';
|
||||||
@@ -20757,6 +20759,24 @@ class binary_reader
|
|||||||
|
|
||||||
for (std::size_t i = 0; i < size_and_type.first; ++i)
|
for (std::size_t i = 0; i < size_and_type.first; ++i)
|
||||||
{
|
{
|
||||||
|
if (nd_is_char)
|
||||||
|
{
|
||||||
|
get();
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof("char")))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(current > 127))
|
||||||
|
{
|
||||||
|
return last_byte_error(exception_id::invalid_string_or_size, "byte after 'C' must be in range 0x00..0x7F", "char", "");
|
||||||
|
}
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(!sax->number_unsigned(static_cast<number_unsigned_t>(current))))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second)))
|
if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second)))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@@ -22122,6 +22142,8 @@ class binary_reader
|
|||||||
return "uint8";
|
return "uint8";
|
||||||
case 'd':
|
case 'd':
|
||||||
return "single";
|
return "single";
|
||||||
|
case 'h':
|
||||||
|
return "half";
|
||||||
case 'i':
|
case 'i':
|
||||||
return "int8";
|
return "int8";
|
||||||
case 'l':
|
case 'l':
|
||||||
@@ -30276,7 +30298,7 @@ class binary_writer
|
|||||||
A C++11 `constexpr` function cannot contain a `switch`, so this is a plain
|
A C++11 `constexpr` function cannot contain a `switch`, so this is a plain
|
||||||
comparison chain instead; it is only reached once per ND-array candidate
|
comparison chain instead; it is only reached once per ND-array candidate
|
||||||
object. Keep in sync with binary_reader's `bjd_type_name()`, which maps
|
object. Keep in sync with binary_reader's `bjd_type_name()`, which maps
|
||||||
the other way.
|
the other way (it also reads "half", which is never written).
|
||||||
*/
|
*/
|
||||||
static CharType bjdata_ndarray_type_marker(const string_t& name)
|
static CharType bjdata_ndarray_type_marker(const string_t& name)
|
||||||
{
|
{
|
||||||
@@ -30484,10 +30506,13 @@ class binary_writer
|
|||||||
switch (dtype)
|
switch (dtype)
|
||||||
{
|
{
|
||||||
case 'U':
|
case 'U':
|
||||||
case 'C':
|
|
||||||
case 'B':
|
case 'B':
|
||||||
ok = write_bjdata_ndarray_element<std::uint8_t>(el, dry_run);
|
ok = write_bjdata_ndarray_element<std::uint8_t>(el, dry_run);
|
||||||
break;
|
break;
|
||||||
|
case 'C':
|
||||||
|
// BJData limits a char to 0..127 (the intersection of the int8 and uint8 ranges)
|
||||||
|
ok = bjdata_ndarray_value_in_range<std::int8_t>(el) && bjdata_ndarray_value_in_range<std::uint8_t>(el);
|
||||||
|
break;
|
||||||
case 'i':
|
case 'i':
|
||||||
ok = write_bjdata_ndarray_element<std::int8_t>(el, dry_run);
|
ok = write_bjdata_ndarray_element<std::int8_t>(el, dry_run);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -2916,6 +2916,35 @@ TEST_CASE("BJData")
|
|||||||
CHECK(json::from_bjdata(out_single_ok) == json({{"_ArrayType_", "single"}, {"_ArraySize_", {2, 1}}, {"_ArrayData_", {1.5f, -1.5f}}}));
|
CHECK(json::from_bjdata(out_single_ok) == json({{"_ArrayType_", "single"}, {"_ArraySize_", {2, 1}}, {"_ArrayData_", {1.5f, -1.5f}}}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SECTION("ndarray with _ArrayType_ \"char\" is limited to 0..127")
|
||||||
|
{
|
||||||
|
// both BJData drafts limit a char to 0..127; larger values must not
|
||||||
|
// be written as 'C' elements (the reader rejects them), so such an
|
||||||
|
// object stays a plain object that round-trips
|
||||||
|
for (const auto version :
|
||||||
|
{
|
||||||
|
json::bjdata_version_t::draft2, json::bjdata_version_t::draft3
|
||||||
|
})
|
||||||
|
{
|
||||||
|
json const j_ok = json({{"_ArrayType_", "char"}, {"_ArraySize_", {2, 2}}, {"_ArrayData_", {0, 97, 98, 127}}});
|
||||||
|
const auto out_ok = json::to_bjdata(j_ok, true, true, version);
|
||||||
|
CHECK(out_ok == std::vector<uint8_t>({'[', '$', 'C', '#', '[', '$', 'i', '#', 'i', 2, 2, 2, 0, 97, 98, 127}));
|
||||||
|
CHECK(json::from_bjdata(out_ok) == j_ok);
|
||||||
|
|
||||||
|
for (const int bad :
|
||||||
|
{
|
||||||
|
128, 200, 255, 256, -1
|
||||||
|
})
|
||||||
|
{
|
||||||
|
CAPTURE(bad)
|
||||||
|
json const j_bad = json({{"_ArrayType_", "char"}, {"_ArraySize_", {2, 2}}, {"_ArrayData_", {97, 98, 99, bad}}});
|
||||||
|
const auto out_bad = json::to_bjdata(j_bad, true, true, version);
|
||||||
|
CHECK(out_bad.at(0) == '{');
|
||||||
|
CHECK(json::from_bjdata(out_bad) == j_bad);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SECTION("ndarray annotation keys are read back in the documented order")
|
SECTION("ndarray annotation keys are read back in the documented order")
|
||||||
{
|
{
|
||||||
// from_bjdata() must emit the annotation object's keys in the order
|
// from_bjdata() must emit the annotation object's keys in the order
|
||||||
@@ -3324,6 +3353,44 @@ TEST_CASE("BJData")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SECTION("ndarray of half-precision floats can be read")
|
||||||
|
{
|
||||||
|
// the 'h' element type is read as _ArrayType_ "half"
|
||||||
|
json _;
|
||||||
|
const std::vector<uint8_t> half = {'[', '$', 'h', '#', '[', '$', 'U', '#', 'U', 2, 2, 2, 0x00, 0x3C, 0x00, 0x40, 0x00, 0x42, 0x00, 0x44};
|
||||||
|
const json j_half = json::parse(R"({"_ArrayType_":"half","_ArraySize_":[2,2],"_ArrayData_":[1.0,2.0,3.0,4.0]})");
|
||||||
|
CHECK(json::from_bjdata(half) == j_half);
|
||||||
|
|
||||||
|
// the writer does not pack "half": it writes a plain object that reads back equal
|
||||||
|
CHECK(json::from_bjdata(json::to_bjdata(j_half)) == j_half);
|
||||||
|
CHECK(json::from_bjdata(json::to_bjdata(j_half, true, true, json::bjdata_version_t::draft3)) == j_half);
|
||||||
|
|
||||||
|
// truncated half data
|
||||||
|
CHECK_THROWS_AS(_ = json::from_bjdata(std::vector<uint8_t>(half.begin(), half.end() - 1)), json::parse_error&);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("ndarray of char rejects values above 127")
|
||||||
|
{
|
||||||
|
json _;
|
||||||
|
const std::vector<uint8_t> ok = {'[', '$', 'C', '#', '[', '$', 'U', '#', 'U', 2, 2, 2, 'a', 'b', 'c', 0x7F};
|
||||||
|
CHECK(json::from_bjdata(ok) == json::parse(R"({"_ArrayType_":"char","_ArraySize_":[2,2],"_ArrayData_":[97,98,99,127]})"));
|
||||||
|
|
||||||
|
std::vector<uint8_t> bad = ok;
|
||||||
|
bad.back() = 0xC8;
|
||||||
|
CHECK_THROWS_AS(_ = json::from_bjdata(bad), json::parse_error&);
|
||||||
|
CHECK(json::from_bjdata(bad, true, false).is_discarded());
|
||||||
|
|
||||||
|
// the same restriction holds for 1-D arrays
|
||||||
|
CHECK_THROWS_AS(_ = json::from_bjdata(std::vector<uint8_t>({'[', '$', 'C', '#', 'i', 1, 0xC8})), json::parse_error&);
|
||||||
|
|
||||||
|
// truncated input
|
||||||
|
CHECK_THROWS_AS(_ = json::from_bjdata(std::vector<uint8_t>(ok.begin(), ok.end() - 1)), json::parse_error&);
|
||||||
|
|
||||||
|
// 'B' (byte) elements may use the full range
|
||||||
|
const std::vector<uint8_t> byte_nd = {'[', '$', 'B', '#', '[', '$', 'U', '#', 'U', 2, 2, 2, 1, 2, 3, 0xC8};
|
||||||
|
CHECK(json::from_bjdata(byte_nd) == json::parse(R"({"_ArrayType_":"byte","_ArraySize_":[2,2],"_ArrayData_":[1,2,3,200]})"));
|
||||||
|
}
|
||||||
|
|
||||||
SECTION("do not accept NTFZ markers in ndarray optimized type (with count)")
|
SECTION("do not accept NTFZ markers in ndarray optimized type (with count)")
|
||||||
{
|
{
|
||||||
json _;
|
json _;
|
||||||
|
|||||||
Reference in new issue
Block a user