Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6b6cc84eb6 | ||
|
|
34ca0efc03 |
No files matched your search
@@ -15,7 +15,9 @@ the patch. This function applies a JSON patch in place and returns void.
|
|||||||
|
|
||||||
## Exception safety
|
## Exception safety
|
||||||
|
|
||||||
No guarantees, value may be corrupted by an unsuccessful patch operation.
|
No guarantees: if an operation fails, the operations before it stay applied, and a failing `move` operation may already
|
||||||
|
have removed the value at its "from" location. Use [`patch`](patch.md) if the value must be unchanged after a failed
|
||||||
|
patch.
|
||||||
|
|
||||||
## Exceptions
|
## Exceptions
|
||||||
|
|
||||||
@@ -62,8 +64,19 @@ the JSON value, the complexity can usually be neglected.
|
|||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
Unlike [`patch`](patch.md), `patch_inplace` applies the operation "in place" and no copy of the JSON value is created.
|
Unlike [`patch`](patch.md), `patch_inplace` applies the operation "in place" and no copy of the JSON value is created.
|
||||||
That makes it faster for large documents by avoiding the copy. However, the JSON value might be corrupted if the
|
That makes it faster for large documents by avoiding the copy.
|
||||||
function throws an exception.
|
|
||||||
|
!!! warning "Not atomic"
|
||||||
|
|
||||||
|
`patch_inplace` applies the operations one after the other and does not undo them when one fails. After an
|
||||||
|
exception, the value holds the result of the operations before the failing one, and a failing `move` operation
|
||||||
|
(a "remove" followed by an "add") may already have removed the value at its "from" location; see the second example
|
||||||
|
below. [RFC 6902](https://tools.ietf.org/html/rfc6902#section-5) requires that a patch with a failing operation is
|
||||||
|
not deemed successful, but leaves open what happens to the document.
|
||||||
|
|
||||||
|
If the value must stay unchanged when the patch fails, use [`patch`](patch.md), which applies the patch to a copy:
|
||||||
|
`#!cpp j = j.patch(json_patch);`. Validating the patch beforehand is not enough, as "test" operations and missing
|
||||||
|
paths are only detected while the patch is applied.
|
||||||
|
|
||||||
Duplicate member names inside an operation object are resolved by the parser before the patch is applied (the last one
|
Duplicate member names inside an operation object are resolved by the parser before the patch is applied (the last one
|
||||||
is kept, see [`object_t`](object_t.md#behavior)), so `#!json {"op": "add", "op": "remove", "path": "/a"}` is a
|
is kept, see [`object_t`](object_t.md#behavior)), so `#!json {"op": "add", "op": "remove", "path": "/a"}` is a
|
||||||
|
|||||||
@@ -0,0 +1,127 @@
|
|||||||
|
# Reading and writing files
|
||||||
|
|
||||||
|
The library has no functions that take a file name. It reads from and writes to streams, so files are handled with
|
||||||
|
`#!cpp std::ifstream` and `#!cpp std::ofstream` (or a `#!cpp FILE*` when reading). This page collects the recipes for
|
||||||
|
the common cases and the mistakes that show up again and again.
|
||||||
|
|
||||||
|
## Reading a JSON file
|
||||||
|
|
||||||
|
Pass the stream to [`parse`](../api/basic_json/parse.md) directly, and check that the file was opened:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <fstream>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
json read_json_file(const std::string& path)
|
||||||
|
{
|
||||||
|
std::ifstream f(path);
|
||||||
|
if (!f.is_open())
|
||||||
|
{
|
||||||
|
throw std::runtime_error("cannot open " + path);
|
||||||
|
}
|
||||||
|
return json::parse(f);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Check that the file is open.** A stream that failed to open looks like an empty input, so `parse` reports
|
||||||
|
[`parse_error.101`](../home/exceptions.md#jsonexceptionparse_error101) ("unexpected end of input") at line 1,
|
||||||
|
column 1 instead of saying that the file is missing. A relative path is resolved against the working directory of the
|
||||||
|
running program, which is often not the directory of the source file (see the
|
||||||
|
[FAQ](../home/faq.md#parsing-a-file-fails-with-unexpected-end-of-input)).
|
||||||
|
- **Don't read the file into a string first.** Parsing from a `#!cpp std::ifstream` is only about 10 to 20 % slower
|
||||||
|
than reading the whole file into a string with one `read()` and parsing that, and it needs no memory for the copy (see
|
||||||
|
[input sources and performance](parsing/index.md#input-sources-and-performance)). The common way to read a file with
|
||||||
|
`#!cpp std::istreambuf_iterator<char>` is so slow that it takes about twice as long as parsing from the stream. Read
|
||||||
|
the file into a string only if you need the text itself, for example for an error message.
|
||||||
|
- `parse` requires the file to hold exactly one JSON value. Files with one value per line are read with
|
||||||
|
[JSON Lines](parsing/json_lines.md); files too large for memory are covered in
|
||||||
|
[Large, streamed, and partial input](parsing/large_and_partial_input.md).
|
||||||
|
|
||||||
|
## Writing a JSON file
|
||||||
|
|
||||||
|
[`dump`](../api/basic_json/dump.md) the value into a string first, then write the string:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
void write_json_file(const std::string& path, const json& j)
|
||||||
|
{
|
||||||
|
const std::string text = j.dump(4) + '\n'; // may throw: nothing is written yet
|
||||||
|
|
||||||
|
std::ofstream f(path, std::ios::binary);
|
||||||
|
if (!f.is_open())
|
||||||
|
{
|
||||||
|
throw std::runtime_error("cannot open " + path);
|
||||||
|
}
|
||||||
|
f << text;
|
||||||
|
f.close();
|
||||||
|
if (f.fail())
|
||||||
|
{
|
||||||
|
throw std::runtime_error("cannot write " + path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Serialize before opening the file.** Opening a `#!cpp std::ofstream` truncates the file. `dump` and
|
||||||
|
[`operator<<`](../api/operator_ltlt.md) throw [`type_error.316`](../home/exceptions.md#jsonexceptiontype_error316)
|
||||||
|
when a string is not valid UTF-8 (see [handling invalid UTF-8](serialization.md#handling-invalid-utf-8)), and
|
||||||
|
`#!cpp f << std::setw(4) << j` may already have written part of the value when it throws. Serializing into a string
|
||||||
|
first leaves the old file untouched in that case.
|
||||||
|
- **Check the stream after closing it.** A full disk or a missing permission sets the stream's error state; it does not
|
||||||
|
throw unless you enabled exceptions on the stream with `exceptions()`.
|
||||||
|
- `std::ios::binary` writes the text as it is. Without it, Windows writes `\r\n` for every newline of a pretty-printed
|
||||||
|
value.
|
||||||
|
|
||||||
|
## Replacing a file without corrupting it
|
||||||
|
|
||||||
|
The library never writes a file itself, so this section is not about the library. It is general C++ advice for a
|
||||||
|
problem users of the library keep running into.
|
||||||
|
|
||||||
|
A program that crashes or is killed while it writes leaves a truncated file behind. To make sure readers see either the
|
||||||
|
old or the new contents, write to a temporary file in the same directory and rename it over the original
|
||||||
|
(C++17, `#!cpp <filesystem>`):
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
void replace_json_file(const std::filesystem::path& path, const json& j)
|
||||||
|
{
|
||||||
|
std::filesystem::path tmp = path;
|
||||||
|
tmp += ".tmp"; // use a unique name if several processes may write the file
|
||||||
|
|
||||||
|
write_json_file(tmp.string(), j);
|
||||||
|
std::filesystem::rename(tmp, path); // replaces path
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The temporary file must be in the same directory (more precisely, on the same file system) as the target: a rename
|
||||||
|
across file systems does not fall back to a copy, it fails and throws `#!cpp std::filesystem::filesystem_error`. On
|
||||||
|
POSIX systems, the rename atomically replaces the file. Standard C++ has no way to flush a file to the disk (`fsync`),
|
||||||
|
so this protects against a crashed program, but not necessarily against a power loss.
|
||||||
|
|
||||||
|
## Binary formats
|
||||||
|
|
||||||
|
The [binary formats](binary_formats/index.md) work the same way, with the streams opened in binary mode:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// read
|
||||||
|
std::ifstream in("data.cbor", std::ios::binary);
|
||||||
|
json j = json::from_cbor(in);
|
||||||
|
|
||||||
|
// write
|
||||||
|
const std::vector<std::uint8_t> bytes = json::to_cbor(j);
|
||||||
|
std::ofstream out("data.cbor", std::ios::binary);
|
||||||
|
out.write(reinterpret_cast<const char*>(bytes.data()), static_cast<std::streamsize>(bytes.size()));
|
||||||
|
```
|
||||||
|
|
||||||
|
Without `std::ios::binary`, Windows turns the bytes `0x0D 0x0A` into `0x0A` and may stop at a byte `0x1A` while reading,
|
||||||
|
and turns `0x0A` into `0x0D 0x0A` while writing, which corrupts the data.
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
- [Parsing](parsing/index.md) - the inputs `parse` accepts
|
||||||
|
- [Serialization](serialization.md) - the options of `dump`
|
||||||
|
- [`parse`](../api/basic_json/parse.md), [`dump`](../api/basic_json/dump.md)
|
||||||
|
- [`operator>>`](../api/operator_gtgt.md), [`operator<<`](../api/operator_ltlt.md)
|
||||||
@@ -46,6 +46,8 @@ C++ types, and finally serialize it again.
|
|||||||
[BJData](binary_formats/bjdata.md), [BON8](binary_formats/bon8.md), [BSON](binary_formats/bson.md),
|
[BJData](binary_formats/bjdata.md), [BON8](binary_formats/bon8.md), [BSON](binary_formats/bson.md),
|
||||||
[CBOR](binary_formats/cbor.md), [MessagePack](binary_formats/messagepack.md), or [UBJSON](binary_formats/ubjson.md).
|
[CBOR](binary_formats/cbor.md), [MessagePack](binary_formats/messagepack.md), or [UBJSON](binary_formats/ubjson.md).
|
||||||
- [Binary values](binary_values.md) — store and exchange raw byte sequences.
|
- [Binary values](binary_values.md) — store and exchange raw byte sequences.
|
||||||
|
- [Reading and writing files](files.md) — read and write files safely, including replacing a file without
|
||||||
|
corrupting it.
|
||||||
|
|
||||||
## How values are stored and configured
|
## How values are stored and configured
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,8 @@ other three continue on its result.
|
|||||||
|
|
||||||
[`patch`](../api/basic_json/patch.md) leaves the original value unchanged and returns the patched result as a copy.
|
[`patch`](../api/basic_json/patch.md) leaves the original value unchanged and returns the patched result as a copy.
|
||||||
If the document is large and the original value is no longer needed,
|
If the document is large and the original value is no longer needed,
|
||||||
[`patch_inplace`](../api/basic_json/patch_inplace.md) applies the same operations in place instead.
|
[`patch_inplace`](../api/basic_json/patch_inplace.md) applies the same operations in place instead. Unlike
|
||||||
|
`patch`, it is not atomic: if an operation fails, the operations before it stay applied.
|
||||||
|
|
||||||
!!! note "Duplicate member names"
|
!!! note "Duplicate member names"
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ std::ifstream f("example.json");
|
|||||||
json data = json::parse(f);
|
json data = json::parse(f);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
See [Reading and writing files](../files.md) for checking that the file was opened and for writing files safely.
|
||||||
|
|
||||||
The input must be encoded in UTF-8; other encodings are not supported. The exception is wide-character input, which
|
The input must be encoded in UTF-8; other encodings are not supported. The exception is wide-character input, which
|
||||||
is read as UTF-16 or UTF-32 — depending on the character type — and converted to UTF-8; see the
|
is read as UTF-16 or UTF-32 — depending on the character type — and converted to UTF-8; see the
|
||||||
[`parse`](../../api/basic_json/parse.md) page for an example. A single input may contain only one JSON value.
|
[`parse`](../../api/basic_json/parse.md) page for an example. A single input may contain only one JSON value.
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ To write a value directly to a stream (for example, a file or `#!cpp std::cout`)
|
|||||||
std::cout << j << std::endl;
|
std::cout << j << std::endl;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
To write a file without leaving a truncated file behind when serialization fails, see
|
||||||
|
[Reading and writing files](files.md#writing-a-json-file).
|
||||||
|
|
||||||
!!! note "String, not raw value"
|
!!! note "String, not raw value"
|
||||||
|
|
||||||
`dump` always returns a **JSON text**. Serializing a JSON string therefore includes the surrounding quotes and
|
`dump` always returns a **JSON text**. Serializing a JSON string therefore includes the surrounding quotes and
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ in the source files that need it, instantiate `basic_json` once, and consider pr
|
|||||||
The parser did not see any character: the file was most likely not opened. A relative path is resolved against the
|
The parser did not see any character: the file was most likely not opened. A relative path is resolved against the
|
||||||
working directory of the running program, which is often the build directory of your IDE, not the source directory.
|
working directory of the running program, which is often the build directory of your IDE, not the source directory.
|
||||||
Check `is_open()` and try an absolute path. Other causes are an empty file and a stream that was already read. See
|
Check `is_open()` and try an absolute path. Other causes are an empty file and a stream that was already read. See
|
||||||
[`parse_error.101`](exceptions.md#jsonexceptionparse_error101).
|
[`parse_error.101`](exceptions.md#jsonexceptionparse_error101) and [Reading and writing files](../features/files.md).
|
||||||
|
|
||||||
### Input that arrives in chunks
|
### Input that arrives in chunks
|
||||||
|
|
||||||
|
|||||||
@@ -130,6 +130,7 @@ nav:
|
|||||||
- features/element_access/unchecked_access.md
|
- features/element_access/unchecked_access.md
|
||||||
- features/element_access/checked_access.md
|
- features/element_access/checked_access.md
|
||||||
- features/element_access/default_value.md
|
- features/element_access/default_value.md
|
||||||
|
- features/files.md
|
||||||
- features/iterators.md
|
- features/iterators.md
|
||||||
- features/merge_patch.md
|
- features/merge_patch.md
|
||||||
- features/json_patch.md
|
- features/json_patch.md
|
||||||
|
|||||||
@@ -344,8 +344,8 @@ class binary_reader
|
|||||||
nested document could declare a length that disagrees with where its
|
nested document could declare a length that disagrees with where its
|
||||||
terminator actually falls and quietly hand the bytes in between to the
|
terminator actually falls and quietly hand the bytes in between to the
|
||||||
enclosing document. A well-formed document is at least 5 bytes (the prefix
|
enclosing document. A well-formed document is at least 5 bytes (the prefix
|
||||||
plus the terminator); open_bson_document() rejects a smaller size before
|
plus the terminator); the equality also rejects those impossible sizes,
|
||||||
it is stored, so @a document_size is never negative here.
|
since at least 5 bytes are always consumed.
|
||||||
|
|
||||||
@param[in] document_start value of chars_read before the size prefix
|
@param[in] document_start value of chars_read before the size prefix
|
||||||
@param[in] document_size the declared document size
|
@param[in] document_size the declared document size
|
||||||
@@ -353,7 +353,7 @@ class binary_reader
|
|||||||
*/
|
*/
|
||||||
bool check_bson_document_size(const std::size_t document_start, const std::int32_t document_size)
|
bool check_bson_document_size(const std::size_t document_start, const std::int32_t document_size)
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(static_cast<std::size_t>(document_size) != chars_read - document_start))
|
if (JSON_HEDLEY_UNLIKELY(document_size < 0 || static_cast<std::size_t>(document_size) != chars_read - document_start))
|
||||||
{
|
{
|
||||||
return bson_document_size_error(document_size, chars_read - document_start);
|
return bson_document_size_error(document_size, chars_read - document_start);
|
||||||
}
|
}
|
||||||
@@ -3091,6 +3091,8 @@ class binary_reader
|
|||||||
if (size_and_type.first != npos)
|
if (size_and_type.first != npos)
|
||||||
{
|
{
|
||||||
if (size_and_type.second != 0)
|
if (size_and_type.second != 0)
|
||||||
|
{
|
||||||
|
if (size_and_type.second != 'N')
|
||||||
{
|
{
|
||||||
for (std::size_t i = 0; i < size_and_type.first; ++i)
|
for (std::size_t i = 0; i < size_and_type.first; ++i)
|
||||||
{
|
{
|
||||||
@@ -3101,6 +3103,7 @@ class binary_reader
|
|||||||
dim.push_back(dimlen);
|
dim.push_back(dimlen);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (std::size_t i = 0; i < size_and_type.first; ++i)
|
for (std::size_t i = 0; i < size_and_type.first; ++i)
|
||||||
@@ -3300,9 +3303,11 @@ class binary_reader
|
|||||||
// follow the documented _ArrayType_, _ArraySize_, _ArrayData_ order
|
// follow the documented _ArrayType_, _ArraySize_, _ArrayData_ order
|
||||||
if (ndarray_dtype != 0)
|
if (ndarray_dtype != 0)
|
||||||
{
|
{
|
||||||
// every optimized type marker is_valid_optimized_type() accepts for BJData has a name
|
|
||||||
const char* type_name = bjd_type_name(ndarray_dtype);
|
const char* type_name = bjd_type_name(ndarray_dtype);
|
||||||
JSON_ASSERT(type_name != nullptr);
|
if (JSON_HEDLEY_UNLIKELY(type_name == nullptr))
|
||||||
|
{
|
||||||
|
return unexpected_byte("invalid byte", "type");
|
||||||
|
}
|
||||||
|
|
||||||
string_t type_key = "_ArrayType_";
|
string_t type_key = "_ArrayType_";
|
||||||
string_t type = type_name; // sax->string() takes a reference
|
string_t type = type_name; // sax->string() takes a reference
|
||||||
@@ -3385,6 +3390,12 @@ class binary_reader
|
|||||||
if (current == '$')
|
if (current == '$')
|
||||||
{
|
{
|
||||||
result.second = get(); // must not ignore 'N', because 'N' maybe the type
|
result.second = get(); // must not ignore 'N', because 'N' maybe the type
|
||||||
|
if (input_format == input_format_t::bjdata
|
||||||
|
&& JSON_HEDLEY_UNLIKELY(is_bjd_excluded_optimized_type(result.second)))
|
||||||
|
{
|
||||||
|
return optimized_type_error();
|
||||||
|
}
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof("type")))
|
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof("type")))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@@ -3412,7 +3423,7 @@ class binary_reader
|
|||||||
// an ndarray was read here only if the flag flipped; when it was
|
// an ndarray was read here only if the flag flipped; when it was
|
||||||
// seeded true, get_ubjson_size_value() already rejected the nested
|
// seeded true, get_ubjson_size_value() already rejected the nested
|
||||||
// dimension vector
|
// dimension vector
|
||||||
if (is_ndarray && !inside_ndarray)
|
if (input_format == input_format_t::bjdata && is_ndarray && !inside_ndarray)
|
||||||
{
|
{
|
||||||
result.second |= (1 << 8); // use bit 8 to indicate ndarray, all UBJSON and BJData markers should be ASCII letters
|
result.second |= (1 << 8); // use bit 8 to indicate ndarray, all UBJSON and BJData markers should be ASCII letters
|
||||||
}
|
}
|
||||||
@@ -3422,7 +3433,7 @@ class binary_reader
|
|||||||
if (current == '#')
|
if (current == '#')
|
||||||
{
|
{
|
||||||
const bool is_error = get_ubjson_size_value(result.first, is_ndarray);
|
const bool is_error = get_ubjson_size_value(result.first, is_ndarray);
|
||||||
if (is_ndarray && !inside_ndarray)
|
if (input_format == input_format_t::bjdata && is_ndarray && !inside_ndarray)
|
||||||
{
|
{
|
||||||
return last_byte_error(exception_id::unexpected_byte, "ndarray requires both type and size", "size");
|
return last_byte_error(exception_id::unexpected_byte, "ndarray requires both type and size", "size");
|
||||||
}
|
}
|
||||||
@@ -3596,6 +3607,7 @@ class binary_reader
|
|||||||
JSON_HEDLEY_NEVER_INLINE
|
JSON_HEDLEY_NEVER_INLINE
|
||||||
bool parse_ubjson_typed_run(std::size_t& remaining, const char_int_type type, std::true_type /*bulk*/)
|
bool parse_ubjson_typed_run(std::size_t& remaining, const char_int_type type, std::true_type /*bulk*/)
|
||||||
{
|
{
|
||||||
|
const bool bjdata = input_format == input_format_t::bjdata;
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case 'U':
|
case 'U':
|
||||||
@@ -3609,11 +3621,11 @@ class binary_reader
|
|||||||
case 'L':
|
case 'L':
|
||||||
return parse_ubjson_typed_run<std::int64_t>(remaining);
|
return parse_ubjson_typed_run<std::int64_t>(remaining);
|
||||||
case 'u':
|
case 'u':
|
||||||
return parse_ubjson_typed_run<std::uint16_t>(remaining);
|
return !bjdata || parse_ubjson_typed_run<std::uint16_t>(remaining);
|
||||||
case 'm':
|
case 'm':
|
||||||
return parse_ubjson_typed_run<std::uint32_t>(remaining);
|
return !bjdata || parse_ubjson_typed_run<std::uint32_t>(remaining);
|
||||||
case 'M':
|
case 'M':
|
||||||
return parse_ubjson_typed_run<std::uint64_t>(remaining);
|
return !bjdata || parse_ubjson_typed_run<std::uint64_t>(remaining);
|
||||||
case 'd':
|
case 'd':
|
||||||
return parse_ubjson_typed_run<float>(remaining);
|
return parse_ubjson_typed_run<float>(remaining);
|
||||||
case 'D':
|
case 'D':
|
||||||
@@ -3694,7 +3706,7 @@ class binary_reader
|
|||||||
// if bit-8 of size_and_type.second is set to 1, encode bjdata ndarray as an object in JData annotated array format (https://github.com/NeuroJSON/jdata):
|
// if bit-8 of size_and_type.second is set to 1, encode bjdata ndarray as an object in JData annotated array format (https://github.com/NeuroJSON/jdata):
|
||||||
// {"_ArrayType_" : "typeid", "_ArraySize_" : [n1, n2, ...], "_ArrayData_" : [v1, v2, ...]}
|
// {"_ArrayType_" : "typeid", "_ArraySize_" : [n1, n2, ...], "_ArrayData_" : [v1, v2, ...]}
|
||||||
|
|
||||||
if ((size_and_type.second & (1 << 8)) != 0)
|
if (input_format == input_format_t::bjdata && size_and_type.first != npos && (size_and_type.second & (1 << 8)) != 0)
|
||||||
{
|
{
|
||||||
size_and_type.second &= ~(static_cast<char_int_type>(1) << 8); // use bit 8 to indicate ndarray, here we remove the bit to restore the type marker
|
size_and_type.second &= ~(static_cast<char_int_type>(1) << 8); // use bit 8 to indicate ndarray, here we remove the bit to restore the type marker
|
||||||
|
|
||||||
@@ -3753,7 +3765,7 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If BJData type marker is 'B' decode as binary
|
// If BJData type marker is 'B' decode as binary
|
||||||
if (size_and_type.first != npos && size_and_type.second == 'B')
|
if (input_format == input_format_t::bjdata && size_and_type.first != npos && size_and_type.second == 'B')
|
||||||
{
|
{
|
||||||
binary_t result;
|
binary_t result;
|
||||||
return get_binary(size_and_type.first, result) && sax->binary(result);
|
return get_binary(size_and_type.first, result) && sax->binary(result);
|
||||||
@@ -3802,7 +3814,7 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
|
|
||||||
// do not accept ND-array size in objects in BJData
|
// do not accept ND-array size in objects in BJData
|
||||||
if ((size_and_type.second & (1 << 8)) != 0)
|
if (input_format == input_format_t::bjdata && size_and_type.first != npos && (size_and_type.second & (1 << 8)) != 0)
|
||||||
{
|
{
|
||||||
return last_byte_error(exception_id::unexpected_byte, "BJData object does not support ND-array size in optimized format", "object");
|
return last_byte_error(exception_id::unexpected_byte, "BJData object does not support ND-array size in optimized format", "object");
|
||||||
}
|
}
|
||||||
@@ -5083,9 +5095,8 @@ class binary_reader
|
|||||||
JSON_PRIVATE_UNLESS_TESTED:
|
JSON_PRIVATE_UNLESS_TESTED:
|
||||||
/*!
|
/*!
|
||||||
@brief check whether @a marker is a type marker that may follow '$'
|
@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.
|
UBJSON: Z N T F i U I l L d D H C S [ {; BJData: the numeric markers and C
|
||||||
The markers BJData excludes (F H N S T Z [ {) are the ones
|
(BJData's excluded markers are rejected separately).
|
||||||
binary_writer::is_bjdata_excluded_type_marker() lists; keep the two in sync.
|
|
||||||
*/
|
*/
|
||||||
bool is_valid_optimized_type(const char_int_type marker) const noexcept
|
bool is_valid_optimized_type(const char_int_type marker) const noexcept
|
||||||
{
|
{
|
||||||
@@ -5120,6 +5131,20 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@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', '[', '{'
|
||||||
|
|
||||||
|
Mirrors binary_writer's @ref binary_writer::is_bjdata_excluded_type_marker
|
||||||
|
"is_bjdata_excluded_type_marker()`, which encodes the same list the other
|
||||||
|
way; keep the two in sync.
|
||||||
|
*/
|
||||||
|
static constexpr bool is_bjd_excluded_optimized_type(const char_int_type marker) noexcept
|
||||||
|
{
|
||||||
|
return marker == '[' || marker == '{' || marker == 'S' || marker == 'H'
|
||||||
|
|| marker == 'T' || marker == 'F' || marker == 'N' || marker == 'Z';
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief look up the ND-array element type name for a BJData dtype marker
|
@brief look up the ND-array element type name for a BJData dtype marker
|
||||||
@return the type name ("uint8", "int8", ...), or nullptr if @a marker does
|
@return the type name ("uint8", "int8", ...), or nullptr if @a marker does
|
||||||
|
|||||||
@@ -3126,19 +3126,22 @@ class binary_writer
|
|||||||
// negative fixnum
|
// negative fixnum
|
||||||
write_number(static_cast<std::int8_t>(j.m_data.m_value.number_integer));
|
write_number(static_cast<std::int8_t>(j.m_data.m_value.number_integer));
|
||||||
}
|
}
|
||||||
else if (j.m_data.m_value.number_integer >= (std::numeric_limits<std::int8_t>::min)())
|
else if (j.m_data.m_value.number_integer >= (std::numeric_limits<std::int8_t>::min)() &&
|
||||||
|
j.m_data.m_value.number_integer <= (std::numeric_limits<std::int8_t>::max)())
|
||||||
{
|
{
|
||||||
// int 8
|
// int 8
|
||||||
oa.write_character(to_char_type(0xD0));
|
oa.write_character(to_char_type(0xD0));
|
||||||
write_number(static_cast<std::int8_t>(j.m_data.m_value.number_integer));
|
write_number(static_cast<std::int8_t>(j.m_data.m_value.number_integer));
|
||||||
}
|
}
|
||||||
else if (j.m_data.m_value.number_integer >= (std::numeric_limits<std::int16_t>::min)())
|
else if (j.m_data.m_value.number_integer >= (std::numeric_limits<std::int16_t>::min)() &&
|
||||||
|
j.m_data.m_value.number_integer <= (std::numeric_limits<std::int16_t>::max)())
|
||||||
{
|
{
|
||||||
// int 16
|
// int 16
|
||||||
oa.write_character(to_char_type(0xD1));
|
oa.write_character(to_char_type(0xD1));
|
||||||
write_number(static_cast<std::int16_t>(j.m_data.m_value.number_integer));
|
write_number(static_cast<std::int16_t>(j.m_data.m_value.number_integer));
|
||||||
}
|
}
|
||||||
else if (j.m_data.m_value.number_integer >= (std::numeric_limits<std::int32_t>::min)())
|
else if (j.m_data.m_value.number_integer >= (std::numeric_limits<std::int32_t>::min)() &&
|
||||||
|
j.m_data.m_value.number_integer <= (std::numeric_limits<std::int32_t>::max)())
|
||||||
{
|
{
|
||||||
// int 32
|
// int 32
|
||||||
oa.write_character(to_char_type(0xD2));
|
oa.write_character(to_char_type(0xD2));
|
||||||
@@ -4054,7 +4057,7 @@ class binary_writer
|
|||||||
Containers, strings, high-precision numbers, booleans and null cannot be
|
Containers, strings, high-precision numbers, booleans and null cannot be
|
||||||
declared as the single type of an optimized container in BJData; such a
|
declared as the single type of an optimized container in BJData; such a
|
||||||
container is written unoptimized. The reader rejects them with the same
|
container is written unoptimized. The reader rejects them with the same
|
||||||
list (binary_reader::is_valid_optimized_type()).
|
list (binary_reader::is_bjd_excluded_optimized_type()).
|
||||||
*/
|
*/
|
||||||
static constexpr bool is_bjdata_excluded_type_marker(const CharType marker) noexcept
|
static constexpr bool is_bjdata_excluded_type_marker(const CharType marker) noexcept
|
||||||
{
|
{
|
||||||
|
|||||||
Generated
+48
-20
@@ -18359,8 +18359,8 @@ class binary_reader
|
|||||||
nested document could declare a length that disagrees with where its
|
nested document could declare a length that disagrees with where its
|
||||||
terminator actually falls and quietly hand the bytes in between to the
|
terminator actually falls and quietly hand the bytes in between to the
|
||||||
enclosing document. A well-formed document is at least 5 bytes (the prefix
|
enclosing document. A well-formed document is at least 5 bytes (the prefix
|
||||||
plus the terminator); open_bson_document() rejects a smaller size before
|
plus the terminator); the equality also rejects those impossible sizes,
|
||||||
it is stored, so @a document_size is never negative here.
|
since at least 5 bytes are always consumed.
|
||||||
|
|
||||||
@param[in] document_start value of chars_read before the size prefix
|
@param[in] document_start value of chars_read before the size prefix
|
||||||
@param[in] document_size the declared document size
|
@param[in] document_size the declared document size
|
||||||
@@ -18368,7 +18368,7 @@ class binary_reader
|
|||||||
*/
|
*/
|
||||||
bool check_bson_document_size(const std::size_t document_start, const std::int32_t document_size)
|
bool check_bson_document_size(const std::size_t document_start, const std::int32_t document_size)
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(static_cast<std::size_t>(document_size) != chars_read - document_start))
|
if (JSON_HEDLEY_UNLIKELY(document_size < 0 || static_cast<std::size_t>(document_size) != chars_read - document_start))
|
||||||
{
|
{
|
||||||
return bson_document_size_error(document_size, chars_read - document_start);
|
return bson_document_size_error(document_size, chars_read - document_start);
|
||||||
}
|
}
|
||||||
@@ -21106,6 +21106,8 @@ class binary_reader
|
|||||||
if (size_and_type.first != npos)
|
if (size_and_type.first != npos)
|
||||||
{
|
{
|
||||||
if (size_and_type.second != 0)
|
if (size_and_type.second != 0)
|
||||||
|
{
|
||||||
|
if (size_and_type.second != 'N')
|
||||||
{
|
{
|
||||||
for (std::size_t i = 0; i < size_and_type.first; ++i)
|
for (std::size_t i = 0; i < size_and_type.first; ++i)
|
||||||
{
|
{
|
||||||
@@ -21116,6 +21118,7 @@ class binary_reader
|
|||||||
dim.push_back(dimlen);
|
dim.push_back(dimlen);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (std::size_t i = 0; i < size_and_type.first; ++i)
|
for (std::size_t i = 0; i < size_and_type.first; ++i)
|
||||||
@@ -21315,9 +21318,11 @@ class binary_reader
|
|||||||
// follow the documented _ArrayType_, _ArraySize_, _ArrayData_ order
|
// follow the documented _ArrayType_, _ArraySize_, _ArrayData_ order
|
||||||
if (ndarray_dtype != 0)
|
if (ndarray_dtype != 0)
|
||||||
{
|
{
|
||||||
// every optimized type marker is_valid_optimized_type() accepts for BJData has a name
|
|
||||||
const char* type_name = bjd_type_name(ndarray_dtype);
|
const char* type_name = bjd_type_name(ndarray_dtype);
|
||||||
JSON_ASSERT(type_name != nullptr);
|
if (JSON_HEDLEY_UNLIKELY(type_name == nullptr))
|
||||||
|
{
|
||||||
|
return unexpected_byte("invalid byte", "type");
|
||||||
|
}
|
||||||
|
|
||||||
string_t type_key = "_ArrayType_";
|
string_t type_key = "_ArrayType_";
|
||||||
string_t type = type_name; // sax->string() takes a reference
|
string_t type = type_name; // sax->string() takes a reference
|
||||||
@@ -21400,6 +21405,12 @@ class binary_reader
|
|||||||
if (current == '$')
|
if (current == '$')
|
||||||
{
|
{
|
||||||
result.second = get(); // must not ignore 'N', because 'N' maybe the type
|
result.second = get(); // must not ignore 'N', because 'N' maybe the type
|
||||||
|
if (input_format == input_format_t::bjdata
|
||||||
|
&& JSON_HEDLEY_UNLIKELY(is_bjd_excluded_optimized_type(result.second)))
|
||||||
|
{
|
||||||
|
return optimized_type_error();
|
||||||
|
}
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof("type")))
|
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof("type")))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@@ -21427,7 +21438,7 @@ class binary_reader
|
|||||||
// an ndarray was read here only if the flag flipped; when it was
|
// an ndarray was read here only if the flag flipped; when it was
|
||||||
// seeded true, get_ubjson_size_value() already rejected the nested
|
// seeded true, get_ubjson_size_value() already rejected the nested
|
||||||
// dimension vector
|
// dimension vector
|
||||||
if (is_ndarray && !inside_ndarray)
|
if (input_format == input_format_t::bjdata && is_ndarray && !inside_ndarray)
|
||||||
{
|
{
|
||||||
result.second |= (1 << 8); // use bit 8 to indicate ndarray, all UBJSON and BJData markers should be ASCII letters
|
result.second |= (1 << 8); // use bit 8 to indicate ndarray, all UBJSON and BJData markers should be ASCII letters
|
||||||
}
|
}
|
||||||
@@ -21437,7 +21448,7 @@ class binary_reader
|
|||||||
if (current == '#')
|
if (current == '#')
|
||||||
{
|
{
|
||||||
const bool is_error = get_ubjson_size_value(result.first, is_ndarray);
|
const bool is_error = get_ubjson_size_value(result.first, is_ndarray);
|
||||||
if (is_ndarray && !inside_ndarray)
|
if (input_format == input_format_t::bjdata && is_ndarray && !inside_ndarray)
|
||||||
{
|
{
|
||||||
return last_byte_error(exception_id::unexpected_byte, "ndarray requires both type and size", "size");
|
return last_byte_error(exception_id::unexpected_byte, "ndarray requires both type and size", "size");
|
||||||
}
|
}
|
||||||
@@ -21611,6 +21622,7 @@ class binary_reader
|
|||||||
JSON_HEDLEY_NEVER_INLINE
|
JSON_HEDLEY_NEVER_INLINE
|
||||||
bool parse_ubjson_typed_run(std::size_t& remaining, const char_int_type type, std::true_type /*bulk*/)
|
bool parse_ubjson_typed_run(std::size_t& remaining, const char_int_type type, std::true_type /*bulk*/)
|
||||||
{
|
{
|
||||||
|
const bool bjdata = input_format == input_format_t::bjdata;
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case 'U':
|
case 'U':
|
||||||
@@ -21624,11 +21636,11 @@ class binary_reader
|
|||||||
case 'L':
|
case 'L':
|
||||||
return parse_ubjson_typed_run<std::int64_t>(remaining);
|
return parse_ubjson_typed_run<std::int64_t>(remaining);
|
||||||
case 'u':
|
case 'u':
|
||||||
return parse_ubjson_typed_run<std::uint16_t>(remaining);
|
return !bjdata || parse_ubjson_typed_run<std::uint16_t>(remaining);
|
||||||
case 'm':
|
case 'm':
|
||||||
return parse_ubjson_typed_run<std::uint32_t>(remaining);
|
return !bjdata || parse_ubjson_typed_run<std::uint32_t>(remaining);
|
||||||
case 'M':
|
case 'M':
|
||||||
return parse_ubjson_typed_run<std::uint64_t>(remaining);
|
return !bjdata || parse_ubjson_typed_run<std::uint64_t>(remaining);
|
||||||
case 'd':
|
case 'd':
|
||||||
return parse_ubjson_typed_run<float>(remaining);
|
return parse_ubjson_typed_run<float>(remaining);
|
||||||
case 'D':
|
case 'D':
|
||||||
@@ -21709,7 +21721,7 @@ class binary_reader
|
|||||||
// if bit-8 of size_and_type.second is set to 1, encode bjdata ndarray as an object in JData annotated array format (https://github.com/NeuroJSON/jdata):
|
// if bit-8 of size_and_type.second is set to 1, encode bjdata ndarray as an object in JData annotated array format (https://github.com/NeuroJSON/jdata):
|
||||||
// {"_ArrayType_" : "typeid", "_ArraySize_" : [n1, n2, ...], "_ArrayData_" : [v1, v2, ...]}
|
// {"_ArrayType_" : "typeid", "_ArraySize_" : [n1, n2, ...], "_ArrayData_" : [v1, v2, ...]}
|
||||||
|
|
||||||
if ((size_and_type.second & (1 << 8)) != 0)
|
if (input_format == input_format_t::bjdata && size_and_type.first != npos && (size_and_type.second & (1 << 8)) != 0)
|
||||||
{
|
{
|
||||||
size_and_type.second &= ~(static_cast<char_int_type>(1) << 8); // use bit 8 to indicate ndarray, here we remove the bit to restore the type marker
|
size_and_type.second &= ~(static_cast<char_int_type>(1) << 8); // use bit 8 to indicate ndarray, here we remove the bit to restore the type marker
|
||||||
|
|
||||||
@@ -21768,7 +21780,7 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If BJData type marker is 'B' decode as binary
|
// If BJData type marker is 'B' decode as binary
|
||||||
if (size_and_type.first != npos && size_and_type.second == 'B')
|
if (input_format == input_format_t::bjdata && size_and_type.first != npos && size_and_type.second == 'B')
|
||||||
{
|
{
|
||||||
binary_t result;
|
binary_t result;
|
||||||
return get_binary(size_and_type.first, result) && sax->binary(result);
|
return get_binary(size_and_type.first, result) && sax->binary(result);
|
||||||
@@ -21817,7 +21829,7 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
|
|
||||||
// do not accept ND-array size in objects in BJData
|
// do not accept ND-array size in objects in BJData
|
||||||
if ((size_and_type.second & (1 << 8)) != 0)
|
if (input_format == input_format_t::bjdata && size_and_type.first != npos && (size_and_type.second & (1 << 8)) != 0)
|
||||||
{
|
{
|
||||||
return last_byte_error(exception_id::unexpected_byte, "BJData object does not support ND-array size in optimized format", "object");
|
return last_byte_error(exception_id::unexpected_byte, "BJData object does not support ND-array size in optimized format", "object");
|
||||||
}
|
}
|
||||||
@@ -23098,9 +23110,8 @@ class binary_reader
|
|||||||
JSON_PRIVATE_UNLESS_TESTED:
|
JSON_PRIVATE_UNLESS_TESTED:
|
||||||
/*!
|
/*!
|
||||||
@brief check whether @a marker is a type marker that may follow '$'
|
@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.
|
UBJSON: Z N T F i U I l L d D H C S [ {; BJData: the numeric markers and C
|
||||||
The markers BJData excludes (F H N S T Z [ {) are the ones
|
(BJData's excluded markers are rejected separately).
|
||||||
binary_writer::is_bjdata_excluded_type_marker() lists; keep the two in sync.
|
|
||||||
*/
|
*/
|
||||||
bool is_valid_optimized_type(const char_int_type marker) const noexcept
|
bool is_valid_optimized_type(const char_int_type marker) const noexcept
|
||||||
{
|
{
|
||||||
@@ -23135,6 +23146,20 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@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', '[', '{'
|
||||||
|
|
||||||
|
Mirrors binary_writer's @ref binary_writer::is_bjdata_excluded_type_marker
|
||||||
|
"is_bjdata_excluded_type_marker()`, which encodes the same list the other
|
||||||
|
way; keep the two in sync.
|
||||||
|
*/
|
||||||
|
static constexpr bool is_bjd_excluded_optimized_type(const char_int_type marker) noexcept
|
||||||
|
{
|
||||||
|
return marker == '[' || marker == '{' || marker == 'S' || marker == 'H'
|
||||||
|
|| marker == 'T' || marker == 'F' || marker == 'N' || marker == 'Z';
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief look up the ND-array element type name for a BJData dtype marker
|
@brief look up the ND-array element type name for a BJData dtype marker
|
||||||
@return the type name ("uint8", "int8", ...), or nullptr if @a marker does
|
@return the type name ("uint8", "int8", ...), or nullptr if @a marker does
|
||||||
@@ -31085,19 +31110,22 @@ class binary_writer
|
|||||||
// negative fixnum
|
// negative fixnum
|
||||||
write_number(static_cast<std::int8_t>(j.m_data.m_value.number_integer));
|
write_number(static_cast<std::int8_t>(j.m_data.m_value.number_integer));
|
||||||
}
|
}
|
||||||
else if (j.m_data.m_value.number_integer >= (std::numeric_limits<std::int8_t>::min)())
|
else if (j.m_data.m_value.number_integer >= (std::numeric_limits<std::int8_t>::min)() &&
|
||||||
|
j.m_data.m_value.number_integer <= (std::numeric_limits<std::int8_t>::max)())
|
||||||
{
|
{
|
||||||
// int 8
|
// int 8
|
||||||
oa.write_character(to_char_type(0xD0));
|
oa.write_character(to_char_type(0xD0));
|
||||||
write_number(static_cast<std::int8_t>(j.m_data.m_value.number_integer));
|
write_number(static_cast<std::int8_t>(j.m_data.m_value.number_integer));
|
||||||
}
|
}
|
||||||
else if (j.m_data.m_value.number_integer >= (std::numeric_limits<std::int16_t>::min)())
|
else if (j.m_data.m_value.number_integer >= (std::numeric_limits<std::int16_t>::min)() &&
|
||||||
|
j.m_data.m_value.number_integer <= (std::numeric_limits<std::int16_t>::max)())
|
||||||
{
|
{
|
||||||
// int 16
|
// int 16
|
||||||
oa.write_character(to_char_type(0xD1));
|
oa.write_character(to_char_type(0xD1));
|
||||||
write_number(static_cast<std::int16_t>(j.m_data.m_value.number_integer));
|
write_number(static_cast<std::int16_t>(j.m_data.m_value.number_integer));
|
||||||
}
|
}
|
||||||
else if (j.m_data.m_value.number_integer >= (std::numeric_limits<std::int32_t>::min)())
|
else if (j.m_data.m_value.number_integer >= (std::numeric_limits<std::int32_t>::min)() &&
|
||||||
|
j.m_data.m_value.number_integer <= (std::numeric_limits<std::int32_t>::max)())
|
||||||
{
|
{
|
||||||
// int 32
|
// int 32
|
||||||
oa.write_character(to_char_type(0xD2));
|
oa.write_character(to_char_type(0xD2));
|
||||||
@@ -32013,7 +32041,7 @@ class binary_writer
|
|||||||
Containers, strings, high-precision numbers, booleans and null cannot be
|
Containers, strings, high-precision numbers, booleans and null cannot be
|
||||||
declared as the single type of an optimized container in BJData; such a
|
declared as the single type of an optimized container in BJData; such a
|
||||||
container is written unoptimized. The reader rejects them with the same
|
container is written unoptimized. The reader rejects them with the same
|
||||||
list (binary_reader::is_valid_optimized_type()).
|
list (binary_reader::is_bjd_excluded_optimized_type()).
|
||||||
*/
|
*/
|
||||||
static constexpr bool is_bjdata_excluded_type_marker(const CharType marker) noexcept
|
static constexpr bool is_bjdata_excluded_type_marker(const CharType marker) noexcept
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -78,25 +78,23 @@ TEST_CASE("BJData")
|
|||||||
{
|
{
|
||||||
SECTION("binary_reader BJData lookup tables")
|
SECTION("binary_reader BJData lookup tables")
|
||||||
{
|
{
|
||||||
// bjd_type_name() is static; is_valid_optimized_type() needs a BJData reader
|
// both lookups are static member functions
|
||||||
std::vector<std::uint8_t> const data;
|
std::vector<std::uint8_t> const data;
|
||||||
using reader_t = nlohmann::detail::binary_reader<json, decltype(nlohmann::detail::input_adapter(data))>;
|
using reader_t = nlohmann::detail::binary_reader<json, decltype(nlohmann::detail::input_adapter(data))>;
|
||||||
|
|
||||||
// the markers BJData excludes as optimized types must match
|
// the excluded optimized-type markers must match binary_writer's
|
||||||
// binary_writer's is_bjdata_excluded_type_marker(), which encodes the
|
// is_bjdata_excluded_type_marker(), which encodes the same 8 markers
|
||||||
// same 8 markers
|
|
||||||
reader_t reader(nlohmann::detail::input_adapter(data), json::input_format_t::bjdata);
|
|
||||||
for (const char marker :
|
for (const char marker :
|
||||||
{'[', '{', 'S', 'H', 'T', 'F', 'N', 'Z', 'x'
|
{'[', '{', 'S', 'H', 'T', 'F', 'N', 'Z'
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
CHECK(!reader.is_valid_optimized_type(static_cast<unsigned char>(marker)));
|
CHECK(reader_t::is_bjd_excluded_optimized_type(static_cast<unsigned char>(marker)));
|
||||||
}
|
}
|
||||||
for (const char marker :
|
for (const char marker :
|
||||||
{'U', 'i', 'u', 'I', 'm', 'l', 'M', 'L', 'd', 'D', 'C', 'B', 'h'
|
{'U', 'i', 'u', 'I', 'm', 'l', 'M', 'L', 'd', 'D', 'C', 'B', 'x'
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
CHECK(reader.is_valid_optimized_type(static_cast<unsigned char>(marker)));
|
CHECK(!reader_t::is_bjd_excluded_optimized_type(static_cast<unsigned char>(marker)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// every dtype marker must round-trip to its ND-array type name
|
// every dtype marker must round-trip to its ND-array type name
|
||||||
|
|||||||
Reference in new issue
Block a user