mirror of
https://github.com/nlohmann/json.git
synced 2026-10-23 14:28:54 +00:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d1235cecc9 |
No files matched your search
@@ -29,6 +29,13 @@ updates:
|
||||
- dependency-name: mermaid
|
||||
update-types: ["version-update:semver-major"]
|
||||
|
||||
- package-ecosystem: pip
|
||||
directory: /.github/requirements
|
||||
schedule:
|
||||
interval: daily
|
||||
cooldown:
|
||||
default-days: 7
|
||||
|
||||
- package-ecosystem: pip
|
||||
directory: /tools/astyle
|
||||
schedule:
|
||||
@@ -36,7 +43,7 @@ updates:
|
||||
cooldown:
|
||||
default-days: 7
|
||||
ignore:
|
||||
# astyle is deliberately pinned (see tools/astyle/requirements.txt): newer versions
|
||||
# astyle is deliberately pinned (see tools/astyle/requirements.in): newer versions
|
||||
# reformat the code, and the pinned version defines the formatting CI enforces
|
||||
- dependency-name: astyle
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
meson==1.12.1
|
||||
@@ -0,0 +1,10 @@
|
||||
#
|
||||
# This file is autogenerated by pip-compile with Python 3.14
|
||||
# by the following command:
|
||||
#
|
||||
# pip-compile --generate-hashes --no-emit-index-url --output-file=.github/requirements/meson.txt --strip-extras .github/requirements/meson.in
|
||||
#
|
||||
meson==1.12.1 \
|
||||
--hash=sha256:930bc7542cbd9f57009e182fd014eba48cf1a0180a7b9006d2d32d8f168d8b02 \
|
||||
--hash=sha256:ab0a6ca09f8ef70c564c8241fb5a23957886a0b53fb58412b5e07eaf07dba743
|
||||
# via -r .github/requirements/meson.in
|
||||
@@ -0,0 +1 @@
|
||||
semgrep==1.168.0
|
||||
File diff suppressed because it is too large.
Load diff
@@ -67,10 +67,10 @@ jobs:
|
||||
- name: Install astyle
|
||||
run: |
|
||||
python3 -mvenv venv
|
||||
venv/bin/pip3 install -r $MAIN_DIR/tools/astyle/requirements.txt
|
||||
venv/bin/pip3 install --require-hashes -r $MAIN_DIR/tools/astyle/requirements.txt
|
||||
|
||||
- name: Install generate_natvis dependencies
|
||||
run: pip3 install -r $NATVIS_TOOL_DIR/requirements.txt
|
||||
run: pip3 install --require-hashes -r $MAIN_DIR/tools/generate_natvis/requirements.txt
|
||||
|
||||
- name: Regenerate the tools/macro_builder tables in macro_scope.hpp
|
||||
run: |
|
||||
|
||||
@@ -53,7 +53,7 @@ jobs:
|
||||
# SEMGREP_APP_TOKEN is still passed through so registry auth works if a
|
||||
# token is ever added.
|
||||
- name: Install Semgrep
|
||||
run: python3 -m pip install --user semgrep==1.168.0
|
||||
run: python3 -m pip install --user --require-hashes -r .github/requirements/semgrep.txt
|
||||
|
||||
# `semgrep scan --sarif` always exits 0 even with findings; continue-on-error
|
||||
# is a safety net so the SARIF upload still runs if the scan itself errors.
|
||||
|
||||
@@ -42,7 +42,7 @@ jobs:
|
||||
- name: Check that Meson and pkg-config offer the CMake options
|
||||
run: make check_build_options
|
||||
- name: Install Meson
|
||||
run: pip install meson
|
||||
run: pip install --require-hashes -r .github/requirements/meson.txt
|
||||
- name: Install with Meson
|
||||
run: |
|
||||
meson setup build-meson --prefix=${{ github.workspace }}/install
|
||||
|
||||
@@ -341,14 +341,13 @@ def download(url, docs) -> str:
|
||||
raise ValueError(f'not an http(s) URL: {url}')
|
||||
req = urllib.request.Request(u.geturl(), headers={'User-Agent': USER_AGENT})
|
||||
# (the scheme is checked above)
|
||||
# nosemgrep: python.lang.security.audit.dynamic-urllib-use-detected.dynamic-urllib-use-detected
|
||||
with urllib.request.urlopen(req, timeout=20) as r: # nosec B310
|
||||
data = r.read()
|
||||
ctype = r.headers.get_content_type()
|
||||
path = urllib.parse.unquote(u.path).lstrip('/')
|
||||
ext = os.path.splitext(path)[1]
|
||||
if u.query or not ext or path.endswith('/'):
|
||||
digest = hashlib.sha256(url.encode()).hexdigest()[:12]
|
||||
digest = hashlib.sha1(url.encode(), usedforsecurity=False).hexdigest()[:12]
|
||||
path = os.path.join(os.path.dirname(path), digest + CONTENT_TYPE_EXT.get(ctype, ext or '.bin'))
|
||||
rel = os.path.normpath(os.path.join('assets', 'external', u.hostname, path))
|
||||
out = os.path.join(docs, rel)
|
||||
|
||||
+12
-11
@@ -1105,9 +1105,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/dompurify": {
|
||||
"version": "3.4.16",
|
||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.16.tgz",
|
||||
"integrity": "sha512-sqo+pNp3qRhCIpbgRi1y8Tgk27Bo2Ry7w0dC1NBeNTdZChWjz9Xb/KOoZbRP/R6pQZ80Qw8YhXw13hWWBbMRnQ==",
|
||||
"version": "3.4.17",
|
||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.17.tgz",
|
||||
"integrity": "sha512-B/pSlYVHQ58kqISD/yDjuJ1yfzI34m1gF4lAod2bwSRhiN4K5Cl3H8Af7hqB2PmTL911tdEudnnXn5FnhRoz0w==",
|
||||
"license": "(MPL-2.0 OR Apache-2.0)",
|
||||
"optionalDependencies": {
|
||||
"@types/trusted-types": "^2.0.7"
|
||||
@@ -1241,28 +1241,29 @@
|
||||
}
|
||||
},
|
||||
"node_modules/katex": {
|
||||
"version": "0.16.47",
|
||||
"resolved": "https://registry.npmjs.org/katex/-/katex-0.16.47.tgz",
|
||||
"integrity": "sha512-Eeo8Ys1doU1z+x8AZsPpQu+p/QcZBI5PeOo7QGQdy2x2m0MU/hYagBbGOmXwr5KVbEfVuWv9LpnQWeehogurjg==",
|
||||
"version": "0.18.11",
|
||||
"resolved": "https://registry.npmjs.org/katex/-/katex-0.18.11.tgz",
|
||||
"integrity": "sha512-yvyz/2VMKqoho/35Hat5w37wIEdWJ5ElVMidTAlWrxr5uW4aBGsEpveTmKh18/zw08v/7lGswCPon4OhqmqrTA==",
|
||||
"deprecated": "Accidentally published with breaking changes. Use 0.19.0 instead.",
|
||||
"funding": [
|
||||
"https://opencollective.com/katex",
|
||||
"https://github.com/sponsors/katex"
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"commander": "^8.3.0"
|
||||
"commander": "^15.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"katex": "cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/katex/node_modules/commander": {
|
||||
"version": "8.3.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
|
||||
"integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==",
|
||||
"version": "15.0.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-15.0.0.tgz",
|
||||
"integrity": "sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 12"
|
||||
"node": ">=22.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/khroma": {
|
||||
|
||||
@@ -6,5 +6,8 @@
|
||||
"dependencies": {
|
||||
"jsdom": "30.1.3",
|
||||
"mermaid": "11.17.2"
|
||||
},
|
||||
"overrides": {
|
||||
"katex": "0.18.11"
|
||||
}
|
||||
}
|
||||
@@ -69,10 +69,12 @@ void from_json(const BasicJsonType& j, std::optional<T>& opt)
|
||||
}
|
||||
#endif // JSON_HAS_CPP_17
|
||||
|
||||
// read a number value of any of the three number types into val;
|
||||
// returns false (leaving val untouched) if j is not a number
|
||||
template<typename BasicJsonType, typename ArithmeticType>
|
||||
bool get_number_value(const BasicJsonType& j, ArithmeticType& val)
|
||||
// overloads for basic_json template parameters
|
||||
template < typename BasicJsonType, typename ArithmeticType,
|
||||
enable_if_t < std::is_arithmetic<ArithmeticType>::value&&
|
||||
!std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,
|
||||
int > = 0 >
|
||||
void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)
|
||||
{
|
||||
switch (static_cast<value_t>(j))
|
||||
{
|
||||
@@ -83,17 +85,17 @@ bool get_number_value(const BasicJsonType& j, ArithmeticType& val)
|
||||
const auto* ptr = j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>();
|
||||
JSON_ASSERT(ptr != nullptr);
|
||||
val = static_cast<ArithmeticType>(*ptr);
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
case value_t::number_integer:
|
||||
{
|
||||
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
case value_t::number_float:
|
||||
{
|
||||
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
|
||||
case value_t::null:
|
||||
@@ -104,20 +106,7 @@ bool get_number_value(const BasicJsonType& j, ArithmeticType& val)
|
||||
case value_t::binary:
|
||||
case value_t::discarded:
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// overloads for basic_json template parameters
|
||||
template < typename BasicJsonType, typename ArithmeticType,
|
||||
enable_if_t < std::is_arithmetic<ArithmeticType>::value&&
|
||||
!std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,
|
||||
int > = 0 >
|
||||
void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!get_number_value(j, val)))
|
||||
{
|
||||
throw_type_must_be("number", j);
|
||||
throw_type_must_be("number", j);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -267,18 +256,18 @@ auto from_json_array_impl(const BasicJsonType& j, std::array<T, N>& arr,
|
||||
}
|
||||
}
|
||||
|
||||
// reserve() is called through this pair (used for arrays and objects alike)
|
||||
// so from_json_array_impl and the object from_json have a single body for both
|
||||
// containers that support reserve() and those that don't.
|
||||
template<typename ConstructibleContainerType>
|
||||
auto reserve_if_possible(ConstructibleContainerType& c, typename ConstructibleContainerType::size_type size, priority_tag<1> /*unused*/)
|
||||
-> decltype(c.reserve(size), void())
|
||||
// reserve() is called through this pair (modeled on from_json_object_reserve)
|
||||
// so from_json_array_impl below has a single body for both ConstructibleArrayType
|
||||
// that support reserve() and those that don't.
|
||||
template<typename ConstructibleArrayType>
|
||||
auto from_json_array_reserve(ConstructibleArrayType& arr, typename ConstructibleArrayType::size_type size, priority_tag<1> /*unused*/)
|
||||
-> decltype(arr.reserve(size), void())
|
||||
{
|
||||
c.reserve(size);
|
||||
arr.reserve(size);
|
||||
}
|
||||
|
||||
template<typename ConstructibleContainerType>
|
||||
void reserve_if_possible(ConstructibleContainerType& /*c*/, std::size_t /*size*/, priority_tag<0> /*unused*/)
|
||||
template<typename ConstructibleArrayType>
|
||||
void from_json_array_reserve(ConstructibleArrayType& /*arr*/, std::size_t /*size*/, priority_tag<0> /*unused*/)
|
||||
{}
|
||||
|
||||
// append through push_back() where available; containers without it (such as
|
||||
@@ -308,7 +297,7 @@ auto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, p
|
||||
{
|
||||
ConstructibleArrayType ret;
|
||||
const auto* inner_array = j.template get_ptr<const typename BasicJsonType::array_t*>();
|
||||
reserve_if_possible(ret, inner_array->size(), priority_tag<1> {});
|
||||
from_json_array_reserve(ret, inner_array->size(), priority_tag<1> {});
|
||||
for (const auto& elem : *inner_array)
|
||||
{
|
||||
// get<BasicJsonType>() returns *this, this won't call a from_json
|
||||
@@ -389,6 +378,17 @@ inline void from_json(const BasicJsonType& j, CompatibleArrayType& bin)
|
||||
}
|
||||
}
|
||||
|
||||
template<typename ConstructibleObjectType>
|
||||
auto from_json_object_reserve(ConstructibleObjectType& obj, typename ConstructibleObjectType::size_type size, priority_tag<1> /*unused*/)
|
||||
-> decltype(obj.reserve(size), void())
|
||||
{
|
||||
obj.reserve(size);
|
||||
}
|
||||
|
||||
template<typename ConstructibleObjectType>
|
||||
inline void from_json_object_reserve(ConstructibleObjectType& /*obj*/, std::size_t /*size*/, priority_tag<0> /*unused*/)
|
||||
{}
|
||||
|
||||
// the keys of the source are sorted for the usual object types, so hinting the
|
||||
// end makes every insertion constant-time (for containers that order their keys
|
||||
// the same way); containers without emplace_hint() (such as ordered_map) fall
|
||||
@@ -417,7 +417,7 @@ inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
|
||||
|
||||
ConstructibleObjectType ret;
|
||||
const auto* inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>();
|
||||
reserve_if_possible(ret, inner_object->size(), priority_tag<1> {});
|
||||
from_json_object_reserve(ret, inner_object->size(), priority_tag<1> {});
|
||||
for (const auto& p : *inner_object)
|
||||
{
|
||||
from_json_object_emplace(ret, p.first, p.second.template get<typename ConstructibleObjectType::mapped_type>(), priority_tag<1> {});
|
||||
@@ -437,10 +437,6 @@ template < typename BasicJsonType, typename ArithmeticType,
|
||||
int > = 0 >
|
||||
inline void from_json(const BasicJsonType& j, ArithmeticType& val)
|
||||
{
|
||||
// the number cases repeat get_number_value() on purpose: calling it here
|
||||
// changes the inlining of get<T>() in the std::array conversion, which then
|
||||
// runs 2.4x slower (GetArray benchmark, #5891); unlike get_arithmetic_value(),
|
||||
// a boolean is accepted
|
||||
switch (static_cast<value_t>(j))
|
||||
{
|
||||
case value_t::number_unsigned:
|
||||
|
||||
@@ -66,7 +66,6 @@ enum class exception_id : int
|
||||
invalid_string_or_size = 113, ///< a binary input contains an invalid string or size specification
|
||||
bson_unsupported_type = 114, ///< a BSON record type is not supported
|
||||
invalid_high_precision_number = 115, ///< a UBJSON/BJData high-precision number cannot be parsed
|
||||
invalid_document_image = 116, ///< json_document::load() rejects an image
|
||||
// invalid_iterator
|
||||
iterators_incompatible = 201, ///< the iterators of a range belong to different values
|
||||
iterator_from_other_value = 202, ///< an iterator does not belong to the value it is used with
|
||||
@@ -101,8 +100,6 @@ enum class exception_id : int
|
||||
invalid_utf8 = 316, ///< dump() finds a string that is not valid UTF-8
|
||||
type_not_serializable = 317, ///< a value cannot be serialized to the requested binary format
|
||||
enum_key_duplicate = 318, ///< an enum-keyed map has two keys that serialize to the same string
|
||||
document_binary_value = 319, ///< a binary value is stored in a json_document
|
||||
document_image_unsupported = 320, ///< a json_document image cannot be saved or loaded (discarded document, big-endian target)
|
||||
discarded_value_used = 321, ///< a discarded value is used
|
||||
// out_of_range
|
||||
array_index_out_of_range = 401, ///< an array index is out of range
|
||||
@@ -120,7 +117,6 @@ enum class exception_id : int
|
||||
patch_remove_parent_not_container = 413, ///< the parent of a JSON Patch 'remove' target is not a container
|
||||
patch_move_into_child = 414, ///< a JSON Patch 'move' moves a value into one of its children
|
||||
subtype_out_of_range = 415, ///< a binary subtype does not fit into one byte
|
||||
document_too_large = 416, ///< a json_document input, its edit storage, or an image reaches the 32-bit (4 GiB) limit
|
||||
// other_error
|
||||
internal_error = 500, ///< unreachable code was reached
|
||||
patch_test_failed = 501, ///< a JSON Patch 'test' operation failed
|
||||
@@ -150,13 +146,6 @@ class exception : public std::exception
|
||||
return concat("[json.exception.", ename, '.', std::to_string(id_), "] ");
|
||||
}
|
||||
|
||||
/// build the message "[json.exception.<ename>.<id>] <diagnostics><what_arg>"
|
||||
template<typename BasicJsonContext>
|
||||
static std::string what_impl(const std::string& ename, int id_, const std::string& what_arg, BasicJsonContext context)
|
||||
{
|
||||
return concat(name(ename, id_), diagnostics(context), what_arg);
|
||||
}
|
||||
|
||||
static std::string diagnostics(std::nullptr_t /*leaf_element*/)
|
||||
{
|
||||
return "";
|
||||
@@ -169,33 +158,48 @@ class exception : public std::exception
|
||||
std::vector<std::string> tokens;
|
||||
for (const auto* current = leaf_element; current != nullptr && current->m_parent != nullptr; current = current->m_parent)
|
||||
{
|
||||
const auto* parent = current->m_parent;
|
||||
if (parent->is_array())
|
||||
switch (current->m_parent->type())
|
||||
{
|
||||
for (std::size_t i = 0; i < parent->m_data.m_value.array->size(); ++i)
|
||||
case value_t::array:
|
||||
{
|
||||
if (&parent->m_data.m_value.array->operator[](i) == current)
|
||||
for (std::size_t i = 0; i < current->m_parent->m_data.m_value.array->size(); ++i)
|
||||
{
|
||||
tokens.emplace_back(std::to_string(i));
|
||||
break;
|
||||
if (¤t->m_parent->m_data.m_value.array->operator[](i) == current)
|
||||
{
|
||||
tokens.emplace_back(std::to_string(i));
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (parent->is_object())
|
||||
{
|
||||
for (const auto& element : *parent->m_data.m_value.object)
|
||||
|
||||
case value_t::object:
|
||||
{
|
||||
if (&element.second == current)
|
||||
for (const auto& element : *current->m_parent->m_data.m_value.object)
|
||||
{
|
||||
// data() is null-terminated, so a key containing
|
||||
// a null byte is cut short here rather than
|
||||
// truncating the whole message at what()
|
||||
tokens.emplace_back(element.first.data());
|
||||
break;
|
||||
if (&element.second == current)
|
||||
{
|
||||
// data() is null-terminated, so a key containing
|
||||
// a null byte is cut short here rather than
|
||||
// truncating the whole message at what()
|
||||
tokens.emplace_back(element.first.data());
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case value_t::null: // LCOV_EXCL_LINE
|
||||
case value_t::string: // LCOV_EXCL_LINE
|
||||
case value_t::boolean: // LCOV_EXCL_LINE
|
||||
case value_t::number_integer: // LCOV_EXCL_LINE
|
||||
case value_t::number_unsigned: // LCOV_EXCL_LINE
|
||||
case value_t::number_float: // LCOV_EXCL_LINE
|
||||
case value_t::binary: // LCOV_EXCL_LINE
|
||||
case value_t::discarded: // LCOV_EXCL_LINE
|
||||
default: // LCOV_EXCL_LINE
|
||||
break; // LCOV_EXCL_LINE
|
||||
}
|
||||
// all other parent types (primitives) contribute no token
|
||||
}
|
||||
|
||||
if (tokens.empty())
|
||||
@@ -311,7 +315,7 @@ class invalid_iterator : public exception
|
||||
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
|
||||
static invalid_iterator create(int id_, const std::string& what_arg, BasicJsonContext context)
|
||||
{
|
||||
const std::string w = exception::what_impl("invalid_iterator", id_, what_arg, context);
|
||||
const std::string w = concat(exception::name("invalid_iterator", id_), exception::diagnostics(context), what_arg);
|
||||
return {id_, w.c_str()};
|
||||
}
|
||||
|
||||
@@ -335,7 +339,7 @@ class type_error : public exception
|
||||
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
|
||||
static type_error create(int id_, const std::string& what_arg, BasicJsonContext context)
|
||||
{
|
||||
const std::string w = exception::what_impl("type_error", id_, what_arg, context);
|
||||
const std::string w = concat(exception::name("type_error", id_), exception::diagnostics(context), what_arg);
|
||||
return {id_, w.c_str()};
|
||||
}
|
||||
|
||||
@@ -358,7 +362,7 @@ class out_of_range : public exception
|
||||
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
|
||||
static out_of_range create(int id_, const std::string& what_arg, BasicJsonContext context)
|
||||
{
|
||||
const std::string w = exception::what_impl("out_of_range", id_, what_arg, context);
|
||||
const std::string w = concat(exception::name("out_of_range", id_), exception::diagnostics(context), what_arg);
|
||||
return {id_, w.c_str()};
|
||||
}
|
||||
|
||||
@@ -381,7 +385,7 @@ class other_error : public exception
|
||||
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
|
||||
static other_error create(int id_, const std::string& what_arg, BasicJsonContext context)
|
||||
{
|
||||
const std::string w = exception::what_impl("other_error", id_, what_arg, context);
|
||||
const std::string w = concat(exception::name("other_error", id_), exception::diagnostics(context), what_arg);
|
||||
return {id_, w.c_str()};
|
||||
}
|
||||
|
||||
|
||||
@@ -293,40 +293,6 @@ class binary_reader
|
||||
return is_object ? sax->end_object() : sax->end_array();
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief close the innermost open container and report whether that
|
||||
completed the value being read
|
||||
|
||||
The value begun by the driver is complete once its container is not
|
||||
inside another one.
|
||||
|
||||
@param[out] value_complete whether no container is left open
|
||||
@return whether the SAX parser accepted the end event
|
||||
*/
|
||||
bool leave_container_and_check(bool& value_complete)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!leave_container()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
value_complete = container_stack.empty();
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief read an object key and pass it to the SAX parser
|
||||
|
||||
@tparam GetKey member function that reads the key into a string
|
||||
@param[in,out] key string the key is read into; cleared first
|
||||
@return whether reading the key and the SAX event succeeded
|
||||
*/
|
||||
template<bool (binary_reader::*GetKey)(string_t&)>
|
||||
bool read_object_key(string_t& key)
|
||||
{
|
||||
key.clear();
|
||||
return (this->*GetKey)(key) && sax->key(key);
|
||||
}
|
||||
|
||||
//////////
|
||||
// BSON //
|
||||
//////////
|
||||
@@ -1098,34 +1064,6 @@ class binary_reader
|
||||
return last_byte_error(exception_id::invalid_string_or_size, concat("indefinite-length ", type_name, " is not allowed inside indefinite-length ", type_name, "; last byte: 0x", get_token_string()), context);
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief report an object key that is not a string
|
||||
|
||||
@param[in] found description of what was found instead of a string key
|
||||
@return false
|
||||
*/
|
||||
bool unsupported_key_type(const char* found) const
|
||||
{
|
||||
return last_byte_error(exception_id::invalid_string_or_size, "only string keys are supported, but found ", "object key", found);
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief read the length of a CBOR string or byte array and then its bytes
|
||||
|
||||
@tparam NumberType the type of the length field
|
||||
@tparam ContainerType the destination container (string_t or binary_t)
|
||||
@param[in] context further context information (for diagnostics)
|
||||
@param[out] result container the bytes are appended to
|
||||
|
||||
@return whether reading completed
|
||||
*/
|
||||
template<typename NumberType, typename ContainerType>
|
||||
bool get_cbor_sized_bytes(const char* context, ContainerType& result)
|
||||
{
|
||||
NumberType len{};
|
||||
return get_number(len) && get_bytes(len, context, result);
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief reads a definite-length CBOR string
|
||||
|
||||
@@ -1176,22 +1114,26 @@ class binary_reader
|
||||
|
||||
case 0x78: // UTF-8 string (one-byte uint8_t for n follows)
|
||||
{
|
||||
return get_cbor_sized_bytes<std::uint8_t>("string", result);
|
||||
std::uint8_t len{};
|
||||
return get_number(len) && get_string(len, result);
|
||||
}
|
||||
|
||||
case 0x79: // UTF-8 string (two-byte uint16_t for n follow)
|
||||
{
|
||||
return get_cbor_sized_bytes<std::uint16_t>("string", result);
|
||||
std::uint16_t len{};
|
||||
return get_number(len) && get_string(len, result);
|
||||
}
|
||||
|
||||
case 0x7A: // UTF-8 string (four-byte uint32_t for n follow)
|
||||
{
|
||||
return get_cbor_sized_bytes<std::uint32_t>("string", result);
|
||||
std::uint32_t len{};
|
||||
return get_number(len) && get_string(len, result);
|
||||
}
|
||||
|
||||
case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow)
|
||||
{
|
||||
return get_cbor_sized_bytes<std::uint64_t>("string", result);
|
||||
std::uint64_t len{};
|
||||
return get_number(len) && get_string(len, result);
|
||||
}
|
||||
|
||||
default:
|
||||
@@ -1326,7 +1268,7 @@ class binary_reader
|
||||
break;
|
||||
}
|
||||
|
||||
return unsupported_key_type(found);
|
||||
return last_byte_error(exception_id::invalid_string_or_size, "only string keys are supported, but found ", "object key", found);
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -1379,22 +1321,30 @@ class binary_reader
|
||||
|
||||
case 0x58: // Binary data (one-byte uint8_t for n follows)
|
||||
{
|
||||
return get_cbor_sized_bytes<std::uint8_t>("binary", result);
|
||||
std::uint8_t len{};
|
||||
return get_number(len) &&
|
||||
get_binary(len, result);
|
||||
}
|
||||
|
||||
case 0x59: // Binary data (two-byte uint16_t for n follow)
|
||||
{
|
||||
return get_cbor_sized_bytes<std::uint16_t>("binary", result);
|
||||
std::uint16_t len{};
|
||||
return get_number(len) &&
|
||||
get_binary(len, result);
|
||||
}
|
||||
|
||||
case 0x5A: // Binary data (four-byte uint32_t for n follow)
|
||||
{
|
||||
return get_cbor_sized_bytes<std::uint32_t>("binary", result);
|
||||
std::uint32_t len{};
|
||||
return get_number(len) &&
|
||||
get_binary(len, result);
|
||||
}
|
||||
|
||||
case 0x5B: // Binary data (eight-byte uint64_t for n follow)
|
||||
{
|
||||
return get_cbor_sized_bytes<std::uint64_t>("binary", result);
|
||||
std::uint64_t len{};
|
||||
return get_number(len) &&
|
||||
get_binary(len, result);
|
||||
}
|
||||
|
||||
default:
|
||||
@@ -1609,12 +1559,12 @@ class binary_reader
|
||||
|
||||
if (at_end)
|
||||
{
|
||||
bool value_complete = false;
|
||||
if (JSON_HEDLEY_UNLIKELY(!leave_container_and_check(value_complete)))
|
||||
if (JSON_HEDLEY_UNLIKELY(!leave_container()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (value_complete)
|
||||
// the value begun here is complete once its container is
|
||||
if (container_stack.empty())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -1623,7 +1573,8 @@ class binary_reader
|
||||
|
||||
if (top.is_object)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!read_object_key<&binary_reader::get_cbor_object_key>(key)))
|
||||
key.clear();
|
||||
if (JSON_HEDLEY_UNLIKELY(!get_cbor_object_key(key) || !sax->key(key)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -2094,17 +2045,20 @@ class binary_reader
|
||||
|
||||
case 0xD9: // str 8
|
||||
{
|
||||
return get_string_with_length<std::uint8_t>(result, context);
|
||||
std::uint8_t len{};
|
||||
return get_number(len) && get_string(len, result) && check_string_utf8(result, context);
|
||||
}
|
||||
|
||||
case 0xDA: // str 16
|
||||
{
|
||||
return get_string_with_length<std::uint16_t>(result, context);
|
||||
std::uint16_t len{};
|
||||
return get_number(len) && get_string(len, result) && check_string_utf8(result, context);
|
||||
}
|
||||
|
||||
case 0xDB: // str 32
|
||||
{
|
||||
return get_string_with_length<std::uint32_t>(result, context);
|
||||
std::uint32_t len{};
|
||||
return get_number(len) && get_string(len, result) && check_string_utf8(result, context);
|
||||
}
|
||||
|
||||
default:
|
||||
@@ -2201,7 +2155,7 @@ class binary_reader
|
||||
break;
|
||||
}
|
||||
|
||||
return unsupported_key_type(found);
|
||||
return last_byte_error(exception_id::invalid_string_or_size, "only string keys are supported, but found ", "object key", found);
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -2346,12 +2300,12 @@ class binary_reader
|
||||
|
||||
if (container_stack.back().remaining == 0)
|
||||
{
|
||||
bool value_complete = false;
|
||||
if (JSON_HEDLEY_UNLIKELY(!leave_container_and_check(value_complete)))
|
||||
if (JSON_HEDLEY_UNLIKELY(!leave_container()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (value_complete)
|
||||
// the value begun here is complete once its container is
|
||||
if (container_stack.empty())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -2364,7 +2318,8 @@ class binary_reader
|
||||
if (is_object)
|
||||
{
|
||||
get();
|
||||
if (JSON_HEDLEY_UNLIKELY(!read_object_key<&binary_reader::get_msgpack_object_key>(key)))
|
||||
key.clear();
|
||||
if (JSON_HEDLEY_UNLIKELY(!get_msgpack_object_key(key) || !sax->key(key)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -2548,7 +2503,8 @@ class binary_reader
|
||||
{
|
||||
case 'U':
|
||||
{
|
||||
return get_string_with_length<std::uint8_t>(result, context);
|
||||
std::uint8_t len{};
|
||||
return get_number(len) && get_string(len, result) && check_string_utf8(result, context);
|
||||
}
|
||||
|
||||
case 'i':
|
||||
@@ -2581,7 +2537,8 @@ class binary_reader
|
||||
{
|
||||
break;
|
||||
}
|
||||
return get_string_with_length<std::uint16_t>(result, context);
|
||||
std::uint16_t len{};
|
||||
return get_number(len) && get_string(len, result) && check_string_utf8(result, context);
|
||||
}
|
||||
|
||||
case 'm':
|
||||
@@ -2590,7 +2547,8 @@ class binary_reader
|
||||
{
|
||||
break;
|
||||
}
|
||||
return get_string_with_length<std::uint32_t>(result, context);
|
||||
std::uint32_t len{};
|
||||
return get_number(len) && get_string(len, result) && check_string_utf8(result, context);
|
||||
}
|
||||
|
||||
case 'M':
|
||||
@@ -2599,7 +2557,8 @@ class binary_reader
|
||||
{
|
||||
break;
|
||||
}
|
||||
return get_string_with_length<std::uint64_t>(result, context);
|
||||
std::uint64_t len{};
|
||||
return get_number(len) && get_string(len, result) && check_string_utf8(result, context);
|
||||
}
|
||||
|
||||
default:
|
||||
@@ -3415,12 +3374,12 @@ class binary_reader
|
||||
|
||||
if (at_end)
|
||||
{
|
||||
bool value_complete = false;
|
||||
if (JSON_HEDLEY_UNLIKELY(!leave_container_and_check(value_complete)))
|
||||
if (JSON_HEDLEY_UNLIKELY(!leave_container()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (value_complete)
|
||||
// the value begun here is complete once its container is
|
||||
if (container_stack.empty())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -3429,7 +3388,8 @@ class binary_reader
|
||||
|
||||
if (top.is_object)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!read_object_key<&binary_reader::get_bon8_key>(key)))
|
||||
key.clear();
|
||||
if (JSON_HEDLEY_UNLIKELY(!get_bon8_key(key) || !sax->key(key)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -4169,22 +4129,6 @@ class binary_reader
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief read a length of type @a NumberType, then a string of that many bytes
|
||||
|
||||
@tparam NumberType the type of the length field
|
||||
@param[out] result string the bytes are appended to
|
||||
@param[in] context further context information (for diagnostics)
|
||||
|
||||
@return whether reading and validating the string succeeded
|
||||
*/
|
||||
template<typename NumberType>
|
||||
bool get_string_with_length(string_t& result, const char* context)
|
||||
{
|
||||
NumberType len{};
|
||||
return get_number(len) && get_string(len, result) && check_string_utf8(result, context);
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief create a byte array by reading bytes from the input
|
||||
|
||||
@@ -4394,7 +4338,37 @@ class binary_reader
|
||||
const std::string& context) const
|
||||
{
|
||||
std::string error_msg = "syntax error while parsing ";
|
||||
error_msg += input_format_name(input_format);
|
||||
|
||||
switch (input_format)
|
||||
{
|
||||
case input_format_t::cbor:
|
||||
error_msg += "CBOR";
|
||||
break;
|
||||
|
||||
case input_format_t::msgpack:
|
||||
error_msg += "MessagePack";
|
||||
break;
|
||||
|
||||
case input_format_t::ubjson:
|
||||
error_msg += "UBJSON";
|
||||
break;
|
||||
|
||||
case input_format_t::bson:
|
||||
error_msg += "BSON";
|
||||
break;
|
||||
|
||||
case input_format_t::bjdata:
|
||||
error_msg += "BJData";
|
||||
break;
|
||||
|
||||
case input_format_t::bon8:
|
||||
error_msg += "BON8";
|
||||
break;
|
||||
|
||||
case input_format_t::json: // LCOV_EXCL_LINE
|
||||
default: // LCOV_EXCL_LINE
|
||||
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
return concat(error_msg, ' ', context, ": ", detail);
|
||||
}
|
||||
|
||||
@@ -67,36 +67,6 @@ namespace detail
|
||||
/// the supported input formats
|
||||
enum class input_format_t { json, cbor, msgpack, ubjson, bson, bjdata, bon8 };
|
||||
|
||||
/// @return the name of a binary input format, as used in exception messages
|
||||
inline const char* input_format_name(const input_format_t format) noexcept
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case input_format_t::cbor:
|
||||
return "CBOR";
|
||||
case input_format_t::msgpack:
|
||||
return "MessagePack";
|
||||
case input_format_t::ubjson:
|
||||
return "UBJSON";
|
||||
case input_format_t::bson:
|
||||
return "BSON";
|
||||
case input_format_t::bjdata:
|
||||
return "BJData";
|
||||
case input_format_t::bon8:
|
||||
return "BON8";
|
||||
case input_format_t::json: // LCOV_EXCL_LINE
|
||||
default: // LCOV_EXCL_LINE
|
||||
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
|
||||
return ""; // LCOV_EXCL_LINE
|
||||
}
|
||||
}
|
||||
|
||||
/// @return the message of the parse_error thrown or reported for an empty input
|
||||
inline const char* empty_input_message() noexcept
|
||||
{
|
||||
return "attempting to parse an empty input; check that your input string or stream contains the expected JSON";
|
||||
}
|
||||
|
||||
////////////////////
|
||||
// input adapters //
|
||||
////////////////////
|
||||
@@ -1298,7 +1268,7 @@ inline file_input_adapter input_adapter(std::FILE* file)
|
||||
{
|
||||
if (file == nullptr)
|
||||
{
|
||||
JSON_THROW(parse_error::create(exception_id::syntax_error, 0, empty_input_message(), nullptr));
|
||||
JSON_THROW(parse_error::create(exception_id::syntax_error, 0, "attempting to parse an empty input; check that your input string or stream contains the expected JSON", nullptr));
|
||||
}
|
||||
return file_input_adapter(file);
|
||||
}
|
||||
@@ -1307,7 +1277,7 @@ inline input_stream_adapter input_adapter(std::istream& stream)
|
||||
{
|
||||
if (stream.rdbuf() == nullptr)
|
||||
{
|
||||
JSON_THROW(parse_error::create(exception_id::syntax_error, 0, empty_input_message(), nullptr));
|
||||
JSON_THROW(parse_error::create(exception_id::syntax_error, 0, "attempting to parse an empty input; check that your input string or stream contains the expected JSON", nullptr));
|
||||
}
|
||||
return input_stream_adapter(stream);
|
||||
}
|
||||
@@ -1332,7 +1302,7 @@ contiguous_bytes_input_adapter input_adapter(CharT b)
|
||||
{
|
||||
if (b == nullptr)
|
||||
{
|
||||
JSON_THROW(parse_error::create(exception_id::syntax_error, 0, empty_input_message(), nullptr));
|
||||
JSON_THROW(parse_error::create(exception_id::syntax_error, 0, "attempting to parse an empty input; check that your input string or stream contains the expected JSON", nullptr));
|
||||
}
|
||||
auto length = std::strlen(reinterpret_cast<const char*>(b));
|
||||
const auto* ptr = reinterpret_cast<const char*>(b);
|
||||
|
||||
@@ -315,52 +315,6 @@ struct diagnostic_positions
|
||||
};
|
||||
#endif
|
||||
|
||||
/*!
|
||||
@brief error state shared by the DOM-building SAX parsers
|
||||
|
||||
Holds whether a parse error occurred and whether errors are thrown, and
|
||||
implements the parse_error() and is_errored() members of the SAX interface for
|
||||
json_sax_dom_parser and json_sax_dom_callback_parser.
|
||||
*/
|
||||
class json_sax_dom_error_state
|
||||
{
|
||||
public:
|
||||
explicit json_sax_dom_error_state(const bool allow_exceptions_) noexcept
|
||||
: allow_exceptions(allow_exceptions_)
|
||||
{}
|
||||
|
||||
// make class move-only
|
||||
json_sax_dom_error_state(const json_sax_dom_error_state&) = delete;
|
||||
json_sax_dom_error_state(json_sax_dom_error_state&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
|
||||
json_sax_dom_error_state& operator=(const json_sax_dom_error_state&) = delete;
|
||||
json_sax_dom_error_state& operator=(json_sax_dom_error_state&&) = delete; // the const member prevents assignment
|
||||
~json_sax_dom_error_state() = default;
|
||||
|
||||
template<class Exception>
|
||||
bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/,
|
||||
const Exception& ex)
|
||||
{
|
||||
errored = true;
|
||||
static_cast<void>(ex);
|
||||
if (allow_exceptions)
|
||||
{
|
||||
JSON_THROW(ex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr bool is_errored() const
|
||||
{
|
||||
return errored;
|
||||
}
|
||||
|
||||
private:
|
||||
/// whether a syntax error occurred
|
||||
bool errored = false;
|
||||
/// whether to throw exceptions in case of errors
|
||||
const bool allow_exceptions = true;
|
||||
};
|
||||
|
||||
/*!
|
||||
@brief SAX implementation to create a JSON value from SAX events
|
||||
|
||||
@@ -375,7 +329,7 @@ constructor contains the parsed value.
|
||||
@tparam BasicJsonType the JSON type
|
||||
*/
|
||||
template<typename BasicJsonType, typename InputAdapterType>
|
||||
class json_sax_dom_parser : public json_sax_dom_error_state
|
||||
class json_sax_dom_parser
|
||||
{
|
||||
public:
|
||||
using number_integer_t = typename BasicJsonType::number_integer_t;
|
||||
@@ -391,7 +345,7 @@ class json_sax_dom_parser : public json_sax_dom_error_state
|
||||
@param[in] allow_exceptions_ whether parse errors yield exceptions
|
||||
*/
|
||||
explicit json_sax_dom_parser(BasicJsonType& r, const bool allow_exceptions_ = true, lexer_t* lexer_ = nullptr)
|
||||
: json_sax_dom_error_state(allow_exceptions_), root(r), m_lexer_ref(lexer_)
|
||||
: root(r), allow_exceptions(allow_exceptions_), m_lexer_ref(lexer_)
|
||||
{}
|
||||
|
||||
// make class move-only
|
||||
@@ -520,6 +474,24 @@ class json_sax_dom_parser : public json_sax_dom_error_state
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class Exception>
|
||||
bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/,
|
||||
const Exception& ex)
|
||||
{
|
||||
errored = true;
|
||||
static_cast<void>(ex);
|
||||
if (allow_exceptions)
|
||||
{
|
||||
JSON_THROW(ex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr bool is_errored() const
|
||||
{
|
||||
return errored;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/*!
|
||||
@@ -573,12 +545,16 @@ class json_sax_dom_parser : public json_sax_dom_error_state
|
||||
std::vector<BasicJsonType*> ref_stack {};
|
||||
/// helper to hold the reference for the next object element
|
||||
BasicJsonType* object_element = nullptr;
|
||||
/// whether a syntax error occurred
|
||||
bool errored = false;
|
||||
/// whether to throw exceptions in case of errors
|
||||
const bool allow_exceptions = true;
|
||||
/// the lexer reference to obtain the current position
|
||||
lexer_t* m_lexer_ref = nullptr;
|
||||
};
|
||||
|
||||
template<typename BasicJsonType, typename InputAdapterType>
|
||||
class json_sax_dom_callback_parser : public json_sax_dom_error_state
|
||||
class json_sax_dom_callback_parser
|
||||
{
|
||||
public:
|
||||
using number_integer_t = typename BasicJsonType::number_integer_t;
|
||||
@@ -594,7 +570,7 @@ class json_sax_dom_callback_parser : public json_sax_dom_error_state
|
||||
parser_callback_t cb,
|
||||
const bool allow_exceptions_ = true,
|
||||
lexer_t* lexer_ = nullptr)
|
||||
: json_sax_dom_error_state(allow_exceptions_), root(r), callback(std::move(cb)), m_lexer_ref(lexer_)
|
||||
: root(r), callback(std::move(cb)), allow_exceptions(allow_exceptions_), m_lexer_ref(lexer_)
|
||||
{
|
||||
keep_stack.push_back(true);
|
||||
}
|
||||
@@ -861,6 +837,24 @@ class json_sax_dom_callback_parser : public json_sax_dom_error_state
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class Exception>
|
||||
bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/,
|
||||
const Exception& ex)
|
||||
{
|
||||
errored = true;
|
||||
static_cast<void>(ex);
|
||||
if (allow_exceptions)
|
||||
{
|
||||
JSON_THROW(ex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr bool is_errored() const
|
||||
{
|
||||
return errored;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// if there is a pending duplicate-key stash entry for this exact slot,
|
||||
@@ -1102,8 +1096,12 @@ class json_sax_dom_callback_parser : public json_sax_dom_error_state
|
||||
/// value is later rejected by the callback, instead of erasing the
|
||||
/// member entirely
|
||||
std::vector<std::pair<BasicJsonType*, BasicJsonType>> duplicate_key_stash {};
|
||||
/// whether a syntax error occurred
|
||||
bool errored = false;
|
||||
/// callback function
|
||||
const parser_callback_t callback = nullptr;
|
||||
/// whether to throw exceptions in case of errors
|
||||
const bool allow_exceptions = true;
|
||||
/// a discarded value for the callback
|
||||
BasicJsonType discarded = BasicJsonType::value_t::discarded;
|
||||
/// the lexer reference to obtain the current position
|
||||
|
||||
@@ -656,74 +656,194 @@ class lexer : public lexer_base<BasicJsonType>
|
||||
|
||||
// invalid control characters
|
||||
case 0x00:
|
||||
{
|
||||
error_message = "invalid string: control character U+0000 (NUL) must be escaped to \\u0000";
|
||||
return token_type::parse_error;
|
||||
}
|
||||
|
||||
case 0x01:
|
||||
{
|
||||
error_message = "invalid string: control character U+0001 (SOH) must be escaped to \\u0001";
|
||||
return token_type::parse_error;
|
||||
}
|
||||
|
||||
case 0x02:
|
||||
{
|
||||
error_message = "invalid string: control character U+0002 (STX) must be escaped to \\u0002";
|
||||
return token_type::parse_error;
|
||||
}
|
||||
|
||||
case 0x03:
|
||||
{
|
||||
error_message = "invalid string: control character U+0003 (ETX) must be escaped to \\u0003";
|
||||
return token_type::parse_error;
|
||||
}
|
||||
|
||||
case 0x04:
|
||||
{
|
||||
error_message = "invalid string: control character U+0004 (EOT) must be escaped to \\u0004";
|
||||
return token_type::parse_error;
|
||||
}
|
||||
|
||||
case 0x05:
|
||||
{
|
||||
error_message = "invalid string: control character U+0005 (ENQ) must be escaped to \\u0005";
|
||||
return token_type::parse_error;
|
||||
}
|
||||
|
||||
case 0x06:
|
||||
{
|
||||
error_message = "invalid string: control character U+0006 (ACK) must be escaped to \\u0006";
|
||||
return token_type::parse_error;
|
||||
}
|
||||
|
||||
case 0x07:
|
||||
{
|
||||
error_message = "invalid string: control character U+0007 (BEL) must be escaped to \\u0007";
|
||||
return token_type::parse_error;
|
||||
}
|
||||
|
||||
case 0x08:
|
||||
{
|
||||
error_message = "invalid string: control character U+0008 (BS) must be escaped to \\u0008 or \\b";
|
||||
return token_type::parse_error;
|
||||
}
|
||||
|
||||
case 0x09:
|
||||
{
|
||||
error_message = "invalid string: control character U+0009 (HT) must be escaped to \\u0009 or \\t";
|
||||
return token_type::parse_error;
|
||||
}
|
||||
|
||||
case 0x0A:
|
||||
{
|
||||
error_message = "invalid string: control character U+000A (LF) must be escaped to \\u000A or \\n";
|
||||
return token_type::parse_error;
|
||||
}
|
||||
|
||||
case 0x0B:
|
||||
{
|
||||
error_message = "invalid string: control character U+000B (VT) must be escaped to \\u000B";
|
||||
return token_type::parse_error;
|
||||
}
|
||||
|
||||
case 0x0C:
|
||||
{
|
||||
error_message = "invalid string: control character U+000C (FF) must be escaped to \\u000C or \\f";
|
||||
return token_type::parse_error;
|
||||
}
|
||||
|
||||
case 0x0D:
|
||||
{
|
||||
error_message = "invalid string: control character U+000D (CR) must be escaped to \\u000D or \\r";
|
||||
return token_type::parse_error;
|
||||
}
|
||||
|
||||
case 0x0E:
|
||||
{
|
||||
error_message = "invalid string: control character U+000E (SO) must be escaped to \\u000E";
|
||||
return token_type::parse_error;
|
||||
}
|
||||
|
||||
case 0x0F:
|
||||
{
|
||||
error_message = "invalid string: control character U+000F (SI) must be escaped to \\u000F";
|
||||
return token_type::parse_error;
|
||||
}
|
||||
|
||||
case 0x10:
|
||||
{
|
||||
error_message = "invalid string: control character U+0010 (DLE) must be escaped to \\u0010";
|
||||
return token_type::parse_error;
|
||||
}
|
||||
|
||||
case 0x11:
|
||||
{
|
||||
error_message = "invalid string: control character U+0011 (DC1) must be escaped to \\u0011";
|
||||
return token_type::parse_error;
|
||||
}
|
||||
|
||||
case 0x12:
|
||||
{
|
||||
error_message = "invalid string: control character U+0012 (DC2) must be escaped to \\u0012";
|
||||
return token_type::parse_error;
|
||||
}
|
||||
|
||||
case 0x13:
|
||||
{
|
||||
error_message = "invalid string: control character U+0013 (DC3) must be escaped to \\u0013";
|
||||
return token_type::parse_error;
|
||||
}
|
||||
|
||||
case 0x14:
|
||||
{
|
||||
error_message = "invalid string: control character U+0014 (DC4) must be escaped to \\u0014";
|
||||
return token_type::parse_error;
|
||||
}
|
||||
|
||||
case 0x15:
|
||||
{
|
||||
error_message = "invalid string: control character U+0015 (NAK) must be escaped to \\u0015";
|
||||
return token_type::parse_error;
|
||||
}
|
||||
|
||||
case 0x16:
|
||||
{
|
||||
error_message = "invalid string: control character U+0016 (SYN) must be escaped to \\u0016";
|
||||
return token_type::parse_error;
|
||||
}
|
||||
|
||||
case 0x17:
|
||||
{
|
||||
error_message = "invalid string: control character U+0017 (ETB) must be escaped to \\u0017";
|
||||
return token_type::parse_error;
|
||||
}
|
||||
|
||||
case 0x18:
|
||||
{
|
||||
error_message = "invalid string: control character U+0018 (CAN) must be escaped to \\u0018";
|
||||
return token_type::parse_error;
|
||||
}
|
||||
|
||||
case 0x19:
|
||||
{
|
||||
error_message = "invalid string: control character U+0019 (EM) must be escaped to \\u0019";
|
||||
return token_type::parse_error;
|
||||
}
|
||||
|
||||
case 0x1A:
|
||||
{
|
||||
error_message = "invalid string: control character U+001A (SUB) must be escaped to \\u001A";
|
||||
return token_type::parse_error;
|
||||
}
|
||||
|
||||
case 0x1B:
|
||||
{
|
||||
error_message = "invalid string: control character U+001B (ESC) must be escaped to \\u001B";
|
||||
return token_type::parse_error;
|
||||
}
|
||||
|
||||
case 0x1C:
|
||||
{
|
||||
error_message = "invalid string: control character U+001C (FS) must be escaped to \\u001C";
|
||||
return token_type::parse_error;
|
||||
}
|
||||
|
||||
case 0x1D:
|
||||
{
|
||||
error_message = "invalid string: control character U+001D (GS) must be escaped to \\u001D";
|
||||
return token_type::parse_error;
|
||||
}
|
||||
|
||||
case 0x1E:
|
||||
{
|
||||
error_message = "invalid string: control character U+001E (RS) must be escaped to \\u001E";
|
||||
return token_type::parse_error;
|
||||
}
|
||||
|
||||
case 0x1F:
|
||||
{
|
||||
static const char* const control_character_messages[32] = // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
{
|
||||
"invalid string: control character U+0000 (NUL) must be escaped to \\u0000",
|
||||
"invalid string: control character U+0001 (SOH) must be escaped to \\u0001",
|
||||
"invalid string: control character U+0002 (STX) must be escaped to \\u0002",
|
||||
"invalid string: control character U+0003 (ETX) must be escaped to \\u0003",
|
||||
"invalid string: control character U+0004 (EOT) must be escaped to \\u0004",
|
||||
"invalid string: control character U+0005 (ENQ) must be escaped to \\u0005",
|
||||
"invalid string: control character U+0006 (ACK) must be escaped to \\u0006",
|
||||
"invalid string: control character U+0007 (BEL) must be escaped to \\u0007",
|
||||
"invalid string: control character U+0008 (BS) must be escaped to \\u0008 or \\b",
|
||||
"invalid string: control character U+0009 (HT) must be escaped to \\u0009 or \\t",
|
||||
"invalid string: control character U+000A (LF) must be escaped to \\u000A or \\n",
|
||||
"invalid string: control character U+000B (VT) must be escaped to \\u000B",
|
||||
"invalid string: control character U+000C (FF) must be escaped to \\u000C or \\f",
|
||||
"invalid string: control character U+000D (CR) must be escaped to \\u000D or \\r",
|
||||
"invalid string: control character U+000E (SO) must be escaped to \\u000E",
|
||||
"invalid string: control character U+000F (SI) must be escaped to \\u000F",
|
||||
"invalid string: control character U+0010 (DLE) must be escaped to \\u0010",
|
||||
"invalid string: control character U+0011 (DC1) must be escaped to \\u0011",
|
||||
"invalid string: control character U+0012 (DC2) must be escaped to \\u0012",
|
||||
"invalid string: control character U+0013 (DC3) must be escaped to \\u0013",
|
||||
"invalid string: control character U+0014 (DC4) must be escaped to \\u0014",
|
||||
"invalid string: control character U+0015 (NAK) must be escaped to \\u0015",
|
||||
"invalid string: control character U+0016 (SYN) must be escaped to \\u0016",
|
||||
"invalid string: control character U+0017 (ETB) must be escaped to \\u0017",
|
||||
"invalid string: control character U+0018 (CAN) must be escaped to \\u0018",
|
||||
"invalid string: control character U+0019 (EM) must be escaped to \\u0019",
|
||||
"invalid string: control character U+001A (SUB) must be escaped to \\u001A",
|
||||
"invalid string: control character U+001B (ESC) must be escaped to \\u001B",
|
||||
"invalid string: control character U+001C (FS) must be escaped to \\u001C",
|
||||
"invalid string: control character U+001D (GS) must be escaped to \\u001D",
|
||||
"invalid string: control character U+001E (RS) must be escaped to \\u001E",
|
||||
"invalid string: control character U+001F (US) must be escaped to \\u001F"
|
||||
};
|
||||
error_message = control_character_messages[static_cast<std::size_t>(current)];
|
||||
error_message = "invalid string: control character U+001F (US) must be escaped to \\u001F";
|
||||
return token_type::parse_error;
|
||||
}
|
||||
|
||||
|
||||
@@ -165,7 +165,20 @@ class parser
|
||||
|
||||
if (result)
|
||||
{
|
||||
return finish_input(*sax, strict);
|
||||
if (strict)
|
||||
{
|
||||
// strict mode: next byte must be EOF
|
||||
if (get_token() != token_type::end_of_input)
|
||||
{
|
||||
return syntax_error(*sax, exception_message(token_type::end_of_input, "value"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// the caller keeps using the input: position it right after
|
||||
// the value by leaving the character that terminated it
|
||||
m_lexer.release_lookahead();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -189,39 +202,6 @@ class parser
|
||||
return m_lexer.get_string();
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief apply the end-of-input handling after a value was parsed
|
||||
|
||||
In strict mode, the next token must be the end of the input; otherwise
|
||||
parse_error.101 is reported through @a sax. In non-strict mode, the
|
||||
lookahead is released so the caller can keep reading the input right after
|
||||
the parsed value.
|
||||
|
||||
@param[in,out] sax the SAX parser to report an error to
|
||||
@param[in] strict whether to expect the last token to be EOF
|
||||
@return true, or what @a sax returned for the error
|
||||
*/
|
||||
template<typename SAX>
|
||||
bool finish_input(SAX& sax, const bool strict)
|
||||
{
|
||||
if (strict)
|
||||
{
|
||||
// strict mode: next byte must be EOF
|
||||
if (get_token() != token_type::end_of_input)
|
||||
{
|
||||
return syntax_error(sax, exception_message(token_type::end_of_input, "value"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// the caller keeps using the input: position it right after
|
||||
// the value by leaving the character that terminated it
|
||||
m_lexer.release_lookahead();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief run a DOM SAX parser to completion and position the lexer
|
||||
|
||||
@@ -241,7 +221,20 @@ class parser
|
||||
{
|
||||
sax_parse_internal(&sdp);
|
||||
|
||||
static_cast<void>(finish_input(sdp, strict));
|
||||
if (strict)
|
||||
{
|
||||
// in strict mode, input must be completely read
|
||||
if (get_token() != token_type::end_of_input)
|
||||
{
|
||||
syntax_error(sdp, exception_message(token_type::end_of_input, "value"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// the caller keeps using the input: position it right after
|
||||
// the value by leaving the character that terminated it
|
||||
m_lexer.release_lookahead();
|
||||
}
|
||||
|
||||
return !sdp.is_errored();
|
||||
}
|
||||
@@ -411,7 +404,7 @@ class parser
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(m_lexer.get_chars_read_total() == 1))
|
||||
{
|
||||
return syntax_error(*sax, empty_input_message());
|
||||
return syntax_error(*sax, "attempting to parse an empty input; check that your input string or stream contains the expected JSON");
|
||||
}
|
||||
|
||||
return syntax_error(*sax, exception_message(token_type::literal_or_value, "value"));
|
||||
|
||||
@@ -96,7 +96,7 @@ JSON_HEDLEY_ALWAYS_INLINE std::size_t find_string_special(const unsigned char* d
|
||||
// printable ASCII except '"' and '\\' - is emitted unchanged. Note this differs
|
||||
// from is_string_special() only in that 0x7F is also a stop (it is escaped as
|
||||
// \u007f under ensure_ascii).
|
||||
constexpr bool is_ascii_copyable(unsigned char c) noexcept
|
||||
inline bool is_ascii_copyable(unsigned char c) noexcept
|
||||
{
|
||||
return c >= 0x20u && c < 0x7Fu && c != '"' && c != '\\';
|
||||
}
|
||||
|
||||
@@ -108,18 +108,33 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
||||
{
|
||||
JSON_ASSERT(m_object != nullptr);
|
||||
|
||||
if (m_object->is_object())
|
||||
switch (m_object->m_data.m_type)
|
||||
{
|
||||
m_it.object_iterator = typename object_t::iterator();
|
||||
}
|
||||
else if (m_object->is_array())
|
||||
{
|
||||
m_it.array_iterator = typename array_t::iterator();
|
||||
}
|
||||
else
|
||||
{
|
||||
// primitive (is_primitive(), including discarded)
|
||||
m_it.primitive_iterator = primitive_iterator_t();
|
||||
case value_t::object:
|
||||
{
|
||||
m_it.object_iterator = typename object_t::iterator();
|
||||
break;
|
||||
}
|
||||
|
||||
case value_t::array:
|
||||
{
|
||||
m_it.array_iterator = typename array_t::iterator();
|
||||
break;
|
||||
}
|
||||
|
||||
case value_t::null:
|
||||
case value_t::string:
|
||||
case value_t::boolean:
|
||||
case value_t::number_integer:
|
||||
case value_t::number_unsigned:
|
||||
case value_t::number_float:
|
||||
case value_t::binary:
|
||||
case value_t::discarded:
|
||||
default:
|
||||
{
|
||||
m_it.primitive_iterator = primitive_iterator_t();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,28 +249,33 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
||||
{
|
||||
JSON_ASSERT(m_object != nullptr);
|
||||
|
||||
if (m_object->is_object())
|
||||
switch (m_object->m_data.m_type)
|
||||
{
|
||||
m_it.object_iterator = m_object->m_data.m_value.object->end();
|
||||
}
|
||||
else if (m_object->is_array())
|
||||
{
|
||||
m_it.array_iterator = m_object->m_data.m_value.array->end();
|
||||
}
|
||||
else
|
||||
{
|
||||
// primitive (is_primitive(), including discarded)
|
||||
m_it.primitive_iterator.set_end();
|
||||
}
|
||||
}
|
||||
case value_t::object:
|
||||
{
|
||||
m_it.object_iterator = m_object->m_data.m_value.object->end();
|
||||
break;
|
||||
}
|
||||
|
||||
/// @throw invalid_iterator.212 if @a other points to another container
|
||||
template<typename IterImpl>
|
||||
JSON_HEDLEY_ALWAYS_INLINE void check_same_container(const IterImpl& other) const
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))
|
||||
{
|
||||
throw_literal<invalid_iterator>(exception_id::iterators_compare_different_values, "cannot compare iterators of different containers", m_object);
|
||||
case value_t::array:
|
||||
{
|
||||
m_it.array_iterator = m_object->m_data.m_value.array->end();
|
||||
break;
|
||||
}
|
||||
|
||||
case value_t::null:
|
||||
case value_t::string:
|
||||
case value_t::boolean:
|
||||
case value_t::number_integer:
|
||||
case value_t::number_unsigned:
|
||||
case value_t::number_float:
|
||||
case value_t::binary:
|
||||
case value_t::discarded:
|
||||
default:
|
||||
{
|
||||
m_it.primitive_iterator.set_end();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -404,7 +424,10 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
||||
bool operator==(const IterImpl& other) const
|
||||
{
|
||||
// if objects are not the same, the comparison is undefined
|
||||
check_same_container(other);
|
||||
if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))
|
||||
{
|
||||
throw_literal<invalid_iterator>(exception_id::iterators_compare_different_values, "cannot compare iterators of different containers", m_object);
|
||||
}
|
||||
|
||||
// value-initialized forward iterators can be compared, and must compare equal to other value-initialized iterators of the same type #4493
|
||||
if (m_object == nullptr)
|
||||
@@ -443,7 +466,10 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
||||
bool operator<(const iter_impl& other) const
|
||||
{
|
||||
// if objects are not the same, the comparison is undefined
|
||||
check_same_container(other);
|
||||
if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))
|
||||
{
|
||||
throw_literal<invalid_iterator>(exception_id::iterators_compare_different_values, "cannot compare iterators of different containers", m_object);
|
||||
}
|
||||
|
||||
// value-initialized forward iterators can be compared, and must compare equal to other value-initialized iterators of the same type #4493
|
||||
if (m_object == nullptr)
|
||||
@@ -452,18 +478,25 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_object->is_object())
|
||||
switch (m_object->m_data.m_type)
|
||||
{
|
||||
throw_literal<invalid_iterator>(exception_id::iterator_order_on_object, "cannot compare order of object iterators", m_object);
|
||||
}
|
||||
case value_t::object:
|
||||
throw_literal<invalid_iterator>(exception_id::iterator_order_on_object, "cannot compare order of object iterators", m_object);
|
||||
|
||||
if (m_object->is_array())
|
||||
{
|
||||
return (m_it.array_iterator < other.m_it.array_iterator);
|
||||
}
|
||||
case value_t::array:
|
||||
return (m_it.array_iterator < other.m_it.array_iterator);
|
||||
|
||||
// primitive (is_primitive(), including discarded)
|
||||
return (m_it.primitive_iterator < other.m_it.primitive_iterator);
|
||||
case value_t::null:
|
||||
case value_t::string:
|
||||
case value_t::boolean:
|
||||
case value_t::number_integer:
|
||||
case value_t::number_unsigned:
|
||||
case value_t::number_float:
|
||||
case value_t::binary:
|
||||
case value_t::discarded:
|
||||
default:
|
||||
return (m_it.primitive_iterator < other.m_it.primitive_iterator);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -501,19 +534,30 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
||||
{
|
||||
JSON_ASSERT(m_object != nullptr);
|
||||
|
||||
if (m_object->is_object())
|
||||
switch (m_object->m_data.m_type)
|
||||
{
|
||||
throw_literal<invalid_iterator>(exception_id::iterator_arithmetic_on_object, "cannot use offsets with object iterators", m_object);
|
||||
}
|
||||
case value_t::object:
|
||||
throw_literal<invalid_iterator>(exception_id::iterator_arithmetic_on_object, "cannot use offsets with object iterators", m_object);
|
||||
|
||||
if (m_object->is_array())
|
||||
{
|
||||
std::advance(m_it.array_iterator, i);
|
||||
}
|
||||
else
|
||||
{
|
||||
// primitive (is_primitive(), including discarded)
|
||||
m_it.primitive_iterator += i;
|
||||
case value_t::array:
|
||||
{
|
||||
std::advance(m_it.array_iterator, i);
|
||||
break;
|
||||
}
|
||||
|
||||
case value_t::null:
|
||||
case value_t::string:
|
||||
case value_t::boolean:
|
||||
case value_t::number_integer:
|
||||
case value_t::number_unsigned:
|
||||
case value_t::number_float:
|
||||
case value_t::binary:
|
||||
case value_t::discarded:
|
||||
default:
|
||||
{
|
||||
m_it.primitive_iterator += i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
@@ -569,18 +613,25 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
|
||||
{
|
||||
JSON_ASSERT(m_object != nullptr);
|
||||
|
||||
if (m_object->is_object())
|
||||
switch (m_object->m_data.m_type)
|
||||
{
|
||||
throw_literal<invalid_iterator>(exception_id::iterator_arithmetic_on_object, "cannot use offsets with object iterators", m_object);
|
||||
}
|
||||
case value_t::object:
|
||||
throw_literal<invalid_iterator>(exception_id::iterator_arithmetic_on_object, "cannot use offsets with object iterators", m_object);
|
||||
|
||||
if (m_object->is_array())
|
||||
{
|
||||
return m_it.array_iterator - other.m_it.array_iterator;
|
||||
}
|
||||
case value_t::array:
|
||||
return m_it.array_iterator - other.m_it.array_iterator;
|
||||
|
||||
// primitive (is_primitive(), including discarded)
|
||||
return m_it.primitive_iterator - other.m_it.primitive_iterator;
|
||||
case value_t::null:
|
||||
case value_t::string:
|
||||
case value_t::boolean:
|
||||
case value_t::number_integer:
|
||||
case value_t::number_unsigned:
|
||||
case value_t::number_float:
|
||||
case value_t::binary:
|
||||
case value_t::discarded:
|
||||
default:
|
||||
return m_it.primitive_iterator - other.m_it.primitive_iterator;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
|
||||
@@ -109,25 +109,35 @@ template<typename IteratorType> class iteration_proxy_value
|
||||
{
|
||||
JSON_ASSERT(anchor.m_object != nullptr);
|
||||
|
||||
if (anchor.m_object->is_array())
|
||||
switch (anchor.m_object->type())
|
||||
{
|
||||
// use integer array index as key
|
||||
if (array_index != array_index_last)
|
||||
case value_t::array:
|
||||
{
|
||||
int_to_string( array_index_str, array_index );
|
||||
array_index_last = array_index;
|
||||
if (array_index != array_index_last)
|
||||
{
|
||||
int_to_string( array_index_str, array_index );
|
||||
array_index_last = array_index;
|
||||
}
|
||||
return array_index_str;
|
||||
}
|
||||
return array_index_str;
|
||||
}
|
||||
|
||||
if (anchor.m_object->is_object())
|
||||
{
|
||||
// use key from the object
|
||||
return anchor.key();
|
||||
}
|
||||
case value_t::object:
|
||||
return anchor.key();
|
||||
|
||||
// use an empty key for all primitive types
|
||||
return empty_str;
|
||||
// use an empty key for all primitive types
|
||||
case value_t::null:
|
||||
case value_t::string:
|
||||
case value_t::boolean:
|
||||
case value_t::number_integer:
|
||||
case value_t::number_unsigned:
|
||||
case value_t::number_float:
|
||||
case value_t::binary:
|
||||
case value_t::discarded:
|
||||
default:
|
||||
return empty_str;
|
||||
}
|
||||
}
|
||||
|
||||
/// return value of the iterator
|
||||
|
||||
@@ -38,102 +38,6 @@ NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
namespace detail
|
||||
{
|
||||
struct json_pointer_access;
|
||||
|
||||
/*!
|
||||
@brief result of parse_array_index()
|
||||
|
||||
json_pointer::array_index() maps each value to the corresponding
|
||||
parse_error/out_of_range exception; json_pointer::contains() and
|
||||
get_checked_or_null(), which must not throw for an out-of-range or
|
||||
unrepresentable index, and the json_view pointer resolution switch on it
|
||||
directly instead.
|
||||
*/
|
||||
enum class array_index_status
|
||||
{
|
||||
ok, ///< @a s is a valid, representable array index
|
||||
leading_zero, ///< @a s begins with '0' but has more than one character
|
||||
not_a_number, ///< @a s is neither empty nor "-" and does not begin with a digit
|
||||
unresolved, ///< @a s could not be converted to an integer
|
||||
exceeds_size_type ///< @a s converts to an integer that exceeds SizeType
|
||||
};
|
||||
|
||||
/*!
|
||||
@brief convert a JSON Pointer reference token into an array index (RFC 6901, Sect. 4)
|
||||
|
||||
@param[in] s reference token to be converted into an array index; its data()
|
||||
must be NUL-terminated
|
||||
@param[out] idx the integer representation of @a s if array_index_status::ok
|
||||
is returned; left unchanged otherwise
|
||||
|
||||
@return whether @a s is a valid array index, and if not, why
|
||||
|
||||
@note this function never throws; json_pointer::array_index(), the callers that
|
||||
must not throw, and the json_view pointer resolution build on it instead
|
||||
of each re-implementing the RFC 6901 digit rules and the range check
|
||||
@note force-inlined: as a free function template it is no longer inlined into
|
||||
the pointer resolution, which makes JsonPointerAt up to 17% slower (#5891)
|
||||
*/
|
||||
template<typename SizeType, typename StringType>
|
||||
JSON_HEDLEY_ALWAYS_INLINE array_index_status parse_array_index(const StringType& s, SizeType& idx) noexcept
|
||||
{
|
||||
// error condition (cf. RFC 6901, Sect. 4)
|
||||
if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && s[0] == '0'))
|
||||
{
|
||||
return array_index_status::leading_zero;
|
||||
}
|
||||
|
||||
// error condition (cf. RFC 6901, Sect. 4); this also covers single-
|
||||
// character tokens, so "/x" and "/xy" fail alike; "-" and the empty
|
||||
// token are left to the conversion below and are reported as unresolved
|
||||
if (JSON_HEDLEY_UNLIKELY(!s.empty() && !(s.size() == 1 && s[0] == '-') && !(s[0] >= '0' && s[0] <= '9')))
|
||||
{
|
||||
return array_index_status::not_a_number;
|
||||
}
|
||||
|
||||
// fast path: up to 18 decimal digits cannot overflow unsigned long long or
|
||||
// SizeType (9 digits for a 32-bit SizeType), so strtoull and errno are not needed
|
||||
constexpr std::size_t max_fast_digits = sizeof(SizeType) >= 8 ? 18 : 9;
|
||||
if (!s.empty() && s.size() <= max_fast_digits)
|
||||
{
|
||||
SizeType acc = 0;
|
||||
for (std::size_t i = 0; i < s.size(); ++i)
|
||||
{
|
||||
const char c = s[i];
|
||||
if (JSON_HEDLEY_UNLIKELY(c < '0' || c > '9'))
|
||||
{
|
||||
break; // not a plain number: let strtoull decide
|
||||
}
|
||||
acc = static_cast<SizeType>((acc * 10) + static_cast<SizeType>(c - '0'));
|
||||
if (JSON_HEDLEY_UNLIKELY(i == s.size() - 1))
|
||||
{
|
||||
idx = acc;
|
||||
return array_index_status::ok;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char* p = s.data();
|
||||
char* p_end = nullptr; // NOLINT(misc-const-correctness)
|
||||
errno = 0; // strtoull doesn't reset errno
|
||||
const unsigned long long res = std::strtoull(p, &p_end, 10); // NOLINT(runtime/int)
|
||||
if (p == p_end // invalid input or empty string
|
||||
|| errno == ERANGE // out of range
|
||||
|| JSON_HEDLEY_UNLIKELY(static_cast<std::size_t>(p_end - p) != s.size())) // incomplete read
|
||||
{
|
||||
return array_index_status::unresolved;
|
||||
}
|
||||
|
||||
// the index does not fit into SizeType; on 64-bit platforms this is
|
||||
// only SIZE_MAX itself (see #2203 and #5395)
|
||||
if (res >= static_cast<unsigned long long>((std::numeric_limits<SizeType>::max)())) // NOLINT(runtime/int)
|
||||
{
|
||||
return array_index_status::exceeds_size_type;
|
||||
}
|
||||
|
||||
idx = static_cast<SizeType>(res);
|
||||
return array_index_status::ok;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document
|
||||
@@ -398,28 +302,6 @@ class json_pointer
|
||||
JSON_THROW(detail::out_of_range::create(detail::exception_id::patch_on_root, "JSON pointer has no parent", nullptr));
|
||||
}
|
||||
|
||||
/// whether the '~' at @a pos of @a s is not followed by '0' or '1'
|
||||
JSON_HEDLEY_ALWAYS_INLINE static bool is_invalid_escape(const string_t& s, std::size_t pos)
|
||||
{
|
||||
return pos == s.size() - 1 || (s[pos + 1] != '0' && s[pos + 1] != '1');
|
||||
}
|
||||
|
||||
/// @throw parse_error.107 always
|
||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE static void throw_missing_slash(const string_t& pointer)
|
||||
{
|
||||
static_cast<void>(pointer); // unused when JSON_NOEXCEPTION is defined
|
||||
JSON_THROW(detail::parse_error::create(detail::exception_id::pointer_missing_slash, 1, detail::concat("JSON pointer must be empty or begin with '/' - was: '", pointer, "'"), nullptr));
|
||||
}
|
||||
|
||||
/// @throw parse_error.107 if a nonempty @a pointer does not begin with '/'
|
||||
JSON_HEDLEY_ALWAYS_INLINE static void check_begins_with_slash(const string_t& pointer)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(pointer[0] != '/'))
|
||||
{
|
||||
throw_missing_slash(pointer);
|
||||
}
|
||||
}
|
||||
|
||||
/// @throw out_of_range.404 always
|
||||
template<typename BasicJsonContext>
|
||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE static void throw_unresolved(const string_t& reference_token, BasicJsonContext context)
|
||||
@@ -429,7 +311,21 @@ class json_pointer
|
||||
JSON_THROW(detail::out_of_range::create(detail::exception_id::pointer_unresolved, detail::concat("unresolved reference token '", reference_token, "'"), context));
|
||||
}
|
||||
|
||||
using array_index_status = detail::array_index_status;
|
||||
/*!
|
||||
@brief result of @ref parse_array_index
|
||||
|
||||
@ref array_index maps each value to the corresponding parse_error/out_of_range
|
||||
exception; @ref contains and @ref get_checked_or_null, which must not throw for
|
||||
an out-of-range or unrepresentable index, switch on it directly instead.
|
||||
*/
|
||||
enum class array_index_status
|
||||
{
|
||||
ok, ///< @a s is a valid, representable array index
|
||||
leading_zero, ///< @a s begins with '0' but has more than one character
|
||||
not_a_number, ///< @a s is neither empty nor "-" and does not begin with a digit
|
||||
unresolved, ///< @a s could not be converted to an integer
|
||||
exceeds_size_type ///< @a s converts to an integer that exceeds size_type
|
||||
};
|
||||
|
||||
/// whether a reference token is "-" (a plain comparison with a string literal is not always inlined)
|
||||
static bool is_dash(const string_t& s) noexcept
|
||||
@@ -437,11 +333,78 @@ class json_pointer
|
||||
return s.size() == 1 && s[0] == '-';
|
||||
}
|
||||
|
||||
/// detail::parse_array_index() for the size_type of @a BasicJsonType
|
||||
/*!
|
||||
@param[in] s reference token to be converted into an array index
|
||||
@param[out] idx the integer representation of @a s if @ref array_index_status::ok
|
||||
is returned; left unchanged otherwise
|
||||
|
||||
@return whether @a s is a valid array index, and if not, why
|
||||
|
||||
@note this function never throws; @ref array_index and the callers that must not
|
||||
throw (@ref contains, @ref get_checked_or_null) build on it instead of each
|
||||
re-implementing the RFC 6901 digit rules and the @a size_type range check
|
||||
*/
|
||||
template<typename BasicJsonType>
|
||||
static array_index_status parse_array_index(const string_t& s, typename BasicJsonType::size_type& idx) noexcept
|
||||
{
|
||||
return detail::parse_array_index(s, idx);
|
||||
using size_type = typename BasicJsonType::size_type;
|
||||
|
||||
// error condition (cf. RFC 6901, Sect. 4)
|
||||
if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && s[0] == '0'))
|
||||
{
|
||||
return array_index_status::leading_zero;
|
||||
}
|
||||
|
||||
// error condition (cf. RFC 6901, Sect. 4); this also covers single-
|
||||
// character tokens, so "/x" and "/xy" fail alike; "-" and the empty
|
||||
// token are left to the conversion below and are reported as unresolved
|
||||
if (JSON_HEDLEY_UNLIKELY(!s.empty() && !is_dash(s) && !(s[0] >= '0' && s[0] <= '9')))
|
||||
{
|
||||
return array_index_status::not_a_number;
|
||||
}
|
||||
|
||||
// fast path: up to 18 decimal digits cannot overflow unsigned long long or
|
||||
// size_type (9 digits for a 32-bit size_type), so strtoull and errno are not needed
|
||||
constexpr std::size_t max_fast_digits = sizeof(size_type) >= 8 ? 18 : 9;
|
||||
if (!s.empty() && s.size() <= max_fast_digits)
|
||||
{
|
||||
size_type acc = 0;
|
||||
for (std::size_t i = 0; i < s.size(); ++i)
|
||||
{
|
||||
const char c = s[i];
|
||||
if (JSON_HEDLEY_UNLIKELY(c < '0' || c > '9'))
|
||||
{
|
||||
break; // not a plain number: let strtoull decide
|
||||
}
|
||||
acc = static_cast<size_type>((acc * 10) + static_cast<size_type>(c - '0'));
|
||||
if (JSON_HEDLEY_UNLIKELY(i == s.size() - 1))
|
||||
{
|
||||
idx = acc;
|
||||
return array_index_status::ok;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char* p = s.data();
|
||||
char* p_end = nullptr; // NOLINT(misc-const-correctness)
|
||||
errno = 0; // strtoull doesn't reset errno
|
||||
const unsigned long long res = std::strtoull(p, &p_end, 10); // NOLINT(runtime/int)
|
||||
if (p == p_end // invalid input or empty string
|
||||
|| errno == ERANGE // out of range
|
||||
|| JSON_HEDLEY_UNLIKELY(static_cast<std::size_t>(p_end - p) != s.size())) // incomplete read
|
||||
{
|
||||
return array_index_status::unresolved;
|
||||
}
|
||||
|
||||
// the index does not fit into size_type; on 64-bit platforms this is
|
||||
// only SIZE_MAX itself (see #2203 and #5395)
|
||||
if (res >= static_cast<unsigned long long>((std::numeric_limits<size_type>::max)())) // NOLINT(runtime/int)
|
||||
{
|
||||
return array_index_status::exceeds_size_type;
|
||||
}
|
||||
|
||||
idx = static_cast<size_type>(res);
|
||||
return array_index_status::ok;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -491,7 +454,10 @@ class json_pointer
|
||||
return reference_tokens.empty();
|
||||
}
|
||||
|
||||
check_begins_with_slash(reference_string);
|
||||
if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/'))
|
||||
{
|
||||
JSON_THROW(detail::parse_error::create(detail::exception_id::pointer_missing_slash, 1, detail::concat("JSON pointer must be empty or begin with '/' - was: '", reference_string, "'"), nullptr));
|
||||
}
|
||||
|
||||
// the whole string is validated, whether or not it matches
|
||||
const std::size_t size = reference_string.size();
|
||||
@@ -499,7 +465,7 @@ class json_pointer
|
||||
pos != string_t::npos;
|
||||
pos = reference_string.find_first_of('~', pos + 1))
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(is_invalid_escape(reference_string, pos)))
|
||||
if (JSON_HEDLEY_UNLIKELY(pos == size - 1 || (reference_string[pos + 1] != '0' && reference_string[pos + 1] != '1')))
|
||||
{
|
||||
JSON_THROW(detail::parse_error::create(detail::exception_id::pointer_invalid_escape, 0, "escape character '~' must be followed with '0' or '1'", nullptr));
|
||||
}
|
||||
@@ -777,7 +743,10 @@ class json_pointer
|
||||
}
|
||||
|
||||
const char* const data = key.data();
|
||||
check_begins_with_slash(key);
|
||||
if (JSON_HEDLEY_UNLIKELY(data[0] != '/'))
|
||||
{
|
||||
JSON_THROW(detail::parse_error::create(detail::exception_id::pointer_missing_slash, 1, detail::concat("JSON pointer must be empty or begin with '/' - was: '", key, "'"), nullptr));
|
||||
}
|
||||
|
||||
// The beginning of the key that it has in common with the previous
|
||||
// one, which was checked already, is skipped: the scan starts with
|
||||
@@ -810,7 +779,7 @@ class json_pointer
|
||||
else if (data[i] == '~')
|
||||
{
|
||||
// ~ must be followed by 0 or 1
|
||||
if (JSON_HEDLEY_UNLIKELY(is_invalid_escape(key, i)))
|
||||
if (JSON_HEDLEY_UNLIKELY(i + 1 == size || (data[i + 1] != '0' && data[i + 1] != '1')))
|
||||
{
|
||||
JSON_THROW(detail::parse_error::create(detail::exception_id::pointer_invalid_escape, 0, "escape character '~' must be followed with '0' or '1'", nullptr));
|
||||
}
|
||||
@@ -909,46 +878,58 @@ class json_pointer
|
||||
++end;
|
||||
}
|
||||
|
||||
if (ptr->is_object())
|
||||
switch (ptr->type())
|
||||
{
|
||||
token.clear();
|
||||
token.append(begin, static_cast<std::size_t>(end - begin));
|
||||
const auto it = ptr->m_data.m_value.object->find(token);
|
||||
if (JSON_HEDLEY_UNLIKELY(it == ptr->m_data.m_value.object->end()))
|
||||
case detail::value_t::object:
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
ptr = &it->second;
|
||||
}
|
||||
else if (ptr->is_array())
|
||||
{
|
||||
// an index of up to 18 digits without a leading zero fits into size_type
|
||||
// (9 digits for a 32-bit size_type); everything else is left to json_pointer
|
||||
constexpr std::size_t max_digits = sizeof(typename BasicJsonType::size_type) >= 8 ? 18 : 9;
|
||||
const auto digits = static_cast<std::size_t>(end - begin);
|
||||
if (JSON_HEDLEY_UNLIKELY(digits == 0 || digits > max_digits || (digits > 1 && *begin == '0')))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
typename BasicJsonType::size_type idx = 0;
|
||||
for (const char* c = begin; c != end; ++c)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(*c < '0' || *c > '9'))
|
||||
token.clear();
|
||||
token.append(begin, static_cast<std::size_t>(end - begin));
|
||||
const auto it = ptr->m_data.m_value.object->find(token);
|
||||
if (JSON_HEDLEY_UNLIKELY(it == ptr->m_data.m_value.object->end()))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
idx = static_cast<typename BasicJsonType::size_type>((idx * 10) + static_cast<typename BasicJsonType::size_type>(*c - '0'));
|
||||
ptr = &it->second;
|
||||
break;
|
||||
}
|
||||
if (JSON_HEDLEY_UNLIKELY(idx >= ptr->m_data.m_value.array->size()))
|
||||
|
||||
case detail::value_t::array:
|
||||
{
|
||||
return nullptr;
|
||||
// an index of up to 18 digits without a leading zero fits into size_type
|
||||
// (9 digits for a 32-bit size_type); everything else is left to json_pointer
|
||||
constexpr std::size_t max_digits = sizeof(typename BasicJsonType::size_type) >= 8 ? 18 : 9;
|
||||
const auto digits = static_cast<std::size_t>(end - begin);
|
||||
if (JSON_HEDLEY_UNLIKELY(digits == 0 || digits > max_digits || (digits > 1 && *begin == '0')))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
typename BasicJsonType::size_type idx = 0;
|
||||
for (const char* c = begin; c != end; ++c)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(*c < '0' || *c > '9'))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
idx = static_cast<typename BasicJsonType::size_type>((idx * 10) + static_cast<typename BasicJsonType::size_type>(*c - '0'));
|
||||
}
|
||||
if (JSON_HEDLEY_UNLIKELY(idx >= ptr->m_data.m_value.array->size()))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
ptr = &(*ptr->m_data.m_value.array)[idx];
|
||||
break;
|
||||
}
|
||||
ptr = &(*ptr->m_data.m_value.array)[idx];
|
||||
}
|
||||
else
|
||||
{
|
||||
// primitive value with a reference token left
|
||||
return nullptr;
|
||||
|
||||
case detail::value_t::null:
|
||||
case detail::value_t::string:
|
||||
case detail::value_t::boolean:
|
||||
case detail::value_t::number_integer:
|
||||
case detail::value_t::number_unsigned:
|
||||
case detail::value_t::number_float:
|
||||
case detail::value_t::binary:
|
||||
case detail::value_t::discarded:
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
pos = static_cast<std::size_t>(end - path.data());
|
||||
steps.emplace_back(pos, ptr);
|
||||
@@ -998,28 +979,40 @@ class json_pointer
|
||||
: detail::value_t::object;
|
||||
}
|
||||
|
||||
if (ptr->is_object())
|
||||
switch (ptr->type())
|
||||
{
|
||||
// use unchecked object access
|
||||
ptr = &object_member(*ptr, reference_token);
|
||||
}
|
||||
else if (ptr->is_array())
|
||||
{
|
||||
if (reference_token == "-")
|
||||
case detail::value_t::object:
|
||||
{
|
||||
// explicitly treat "-" as index beyond the end
|
||||
ptr = &ptr->operator[](ptr->m_data.m_value.array->size());
|
||||
// use unchecked object access
|
||||
ptr = &object_member(*ptr, reference_token);
|
||||
break;
|
||||
}
|
||||
else
|
||||
|
||||
case detail::value_t::array:
|
||||
{
|
||||
// convert array index to number; unchecked access
|
||||
ptr = &ptr->operator[](array_index<BasicJsonType>(reference_token));
|
||||
if (reference_token == "-")
|
||||
{
|
||||
// explicitly treat "-" as index beyond the end
|
||||
ptr = &ptr->operator[](ptr->m_data.m_value.array->size());
|
||||
}
|
||||
else
|
||||
{
|
||||
// convert array index to number; unchecked access
|
||||
ptr = &ptr->operator[](array_index<BasicJsonType>(reference_token));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// primitive value with a reference token left
|
||||
throw_unresolved(reference_token, ptr);
|
||||
|
||||
case detail::value_t::null:
|
||||
case detail::value_t::string:
|
||||
case detail::value_t::boolean:
|
||||
case detail::value_t::number_integer:
|
||||
case detail::value_t::number_unsigned:
|
||||
case detail::value_t::number_float:
|
||||
case detail::value_t::binary:
|
||||
case detail::value_t::discarded:
|
||||
default:
|
||||
throw_unresolved(reference_token, ptr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1037,39 +1030,51 @@ class json_pointer
|
||||
{
|
||||
for (const auto& reference_token : reference_tokens)
|
||||
{
|
||||
if (ptr->is_object())
|
||||
switch (ptr->type())
|
||||
{
|
||||
const auto it = ptr->m_data.m_value.object->find(reference_token);
|
||||
if (JSON_HEDLEY_UNLIKELY(it == ptr->m_data.m_value.object->end()))
|
||||
case detail::value_t::object:
|
||||
{
|
||||
// at() throws out_of_range.403 with the usual message
|
||||
ptr->at(reference_token);
|
||||
}
|
||||
ptr = &it->second;
|
||||
}
|
||||
else if (ptr->is_array())
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(is_dash(reference_token)))
|
||||
{
|
||||
// "-" always fails the range check
|
||||
JSON_THROW(detail::out_of_range::create(detail::exception_id::pointer_past_the_end_index, detail::concat(
|
||||
"array index '-' (", std::to_string(ptr->m_data.m_value.array->size()),
|
||||
") is out of range"), ptr));
|
||||
const auto it = ptr->m_data.m_value.object->find(reference_token);
|
||||
if (JSON_HEDLEY_UNLIKELY(it == ptr->m_data.m_value.object->end()))
|
||||
{
|
||||
// at() throws out_of_range.403 with the usual message
|
||||
ptr->at(reference_token);
|
||||
}
|
||||
ptr = &it->second;
|
||||
break;
|
||||
}
|
||||
|
||||
const auto idx = array_index<BasicJsonType>(reference_token);
|
||||
// Bounds check before access to avoid exception with JSON_NOEXCEPTION
|
||||
if (JSON_HEDLEY_UNLIKELY(idx >= ptr->m_data.m_value.array->size()))
|
||||
case detail::value_t::array:
|
||||
{
|
||||
JSON_THROW(detail::out_of_range::create(detail::exception_id::array_index_out_of_range, detail::concat(
|
||||
"array index ", std::to_string(idx), " is out of range"), ptr));
|
||||
if (JSON_HEDLEY_UNLIKELY(is_dash(reference_token)))
|
||||
{
|
||||
// "-" always fails the range check
|
||||
JSON_THROW(detail::out_of_range::create(detail::exception_id::pointer_past_the_end_index, detail::concat(
|
||||
"array index '-' (", std::to_string(ptr->m_data.m_value.array->size()),
|
||||
") is out of range"), ptr));
|
||||
}
|
||||
|
||||
const auto idx = array_index<BasicJsonType>(reference_token);
|
||||
// Bounds check before access to avoid exception with JSON_NOEXCEPTION
|
||||
if (JSON_HEDLEY_UNLIKELY(idx >= ptr->m_data.m_value.array->size()))
|
||||
{
|
||||
JSON_THROW(detail::out_of_range::create(detail::exception_id::array_index_out_of_range, detail::concat(
|
||||
"array index ", std::to_string(idx), " is out of range"), ptr));
|
||||
}
|
||||
ptr = &(*ptr->m_data.m_value.array)[idx];
|
||||
break;
|
||||
}
|
||||
ptr = &(*ptr->m_data.m_value.array)[idx];
|
||||
}
|
||||
else
|
||||
{
|
||||
// primitive value with a reference token left
|
||||
throw_unresolved(reference_token, ptr);
|
||||
|
||||
case detail::value_t::null:
|
||||
case detail::value_t::string:
|
||||
case detail::value_t::boolean:
|
||||
case detail::value_t::number_integer:
|
||||
case detail::value_t::number_unsigned:
|
||||
case detail::value_t::number_float:
|
||||
case detail::value_t::binary:
|
||||
case detail::value_t::discarded:
|
||||
default:
|
||||
throw_unresolved(reference_token, ptr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1098,32 +1103,44 @@ class json_pointer
|
||||
{
|
||||
for (const auto& reference_token : reference_tokens)
|
||||
{
|
||||
if (ptr->is_object())
|
||||
switch (ptr->type())
|
||||
{
|
||||
// use unchecked object access; the const operator[]
|
||||
// asserts that the key exists
|
||||
const auto it = ptr->m_data.m_value.object->find(reference_token);
|
||||
JSON_ASSERT(it != ptr->m_data.m_value.object->end());
|
||||
ptr = &it->second;
|
||||
}
|
||||
else if (ptr->is_array())
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(is_dash(reference_token)))
|
||||
case detail::value_t::object:
|
||||
{
|
||||
// "-" cannot be used for const access
|
||||
JSON_THROW(detail::out_of_range::create(detail::exception_id::pointer_past_the_end_index, detail::concat("array index '-' (", std::to_string(ptr->m_data.m_value.array->size()), ") is out of range"), ptr));
|
||||
// use unchecked object access; the const operator[]
|
||||
// asserts that the key exists
|
||||
const auto it = ptr->m_data.m_value.object->find(reference_token);
|
||||
JSON_ASSERT(it != ptr->m_data.m_value.object->end());
|
||||
ptr = &it->second;
|
||||
break;
|
||||
}
|
||||
|
||||
// use unchecked array access; the const operator[]
|
||||
// asserts that the index exists
|
||||
const auto idx = array_index<BasicJsonType>(reference_token);
|
||||
JSON_ASSERT(idx < ptr->m_data.m_value.array->size());
|
||||
ptr = &(*ptr->m_data.m_value.array)[idx];
|
||||
}
|
||||
else
|
||||
{
|
||||
// primitive value with a reference token left
|
||||
throw_unresolved(reference_token, ptr);
|
||||
case detail::value_t::array:
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(is_dash(reference_token)))
|
||||
{
|
||||
// "-" cannot be used for const access
|
||||
JSON_THROW(detail::out_of_range::create(detail::exception_id::pointer_past_the_end_index, detail::concat("array index '-' (", std::to_string(ptr->m_data.m_value.array->size()), ") is out of range"), ptr));
|
||||
}
|
||||
|
||||
// use unchecked array access; the const operator[]
|
||||
// asserts that the index exists
|
||||
const auto idx = array_index<BasicJsonType>(reference_token);
|
||||
JSON_ASSERT(idx < ptr->m_data.m_value.array->size());
|
||||
ptr = &(*ptr->m_data.m_value.array)[idx];
|
||||
break;
|
||||
}
|
||||
|
||||
case detail::value_t::null:
|
||||
case detail::value_t::string:
|
||||
case detail::value_t::boolean:
|
||||
case detail::value_t::number_integer:
|
||||
case detail::value_t::number_unsigned:
|
||||
case detail::value_t::number_float:
|
||||
case detail::value_t::binary:
|
||||
case detail::value_t::discarded:
|
||||
default:
|
||||
throw_unresolved(reference_token, ptr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1147,54 +1164,66 @@ class json_pointer
|
||||
{
|
||||
for (const auto& reference_token : reference_tokens)
|
||||
{
|
||||
if (ptr->is_object())
|
||||
switch (ptr->type())
|
||||
{
|
||||
const auto it = ptr->m_data.m_value.object->find(reference_token);
|
||||
if (JSON_HEDLEY_UNLIKELY(it == ptr->m_data.m_value.object->end()))
|
||||
case detail::value_t::object:
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
ptr = &it->second;
|
||||
}
|
||||
else if (ptr->is_array())
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(is_dash(reference_token)))
|
||||
{
|
||||
// "-" always fails the range check
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// a malformed index throws parse_error.106/109; an
|
||||
// index that is syntactically valid but cannot be
|
||||
// represented (out_of_range.404/410) is treated like an
|
||||
// out-of-range index below
|
||||
typename BasicJsonType::size_type idx{};
|
||||
switch (parse_array_index<BasicJsonType>(reference_token, idx))
|
||||
{
|
||||
// the branches differ in their messages, not after JSON_THROW's expansion
|
||||
// NOLINTNEXTLINE(bugprone-branch-clone)
|
||||
case array_index_status::leading_zero:
|
||||
JSON_THROW(detail::parse_error::create(detail::exception_id::pointer_index_leading_zero, 0, detail::concat("array index '", reference_token, "' must not begin with '0'"), nullptr));
|
||||
case array_index_status::not_a_number:
|
||||
JSON_THROW(detail::parse_error::create(detail::exception_id::pointer_index_not_a_number, 0, detail::concat("array index '", reference_token, "' is not a number"), nullptr));
|
||||
case array_index_status::unresolved:
|
||||
case array_index_status::exceeds_size_type:
|
||||
const auto it = ptr->m_data.m_value.object->find(reference_token);
|
||||
if (JSON_HEDLEY_UNLIKELY(it == ptr->m_data.m_value.object->end()))
|
||||
{
|
||||
return nullptr;
|
||||
case array_index_status::ok:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
ptr = &it->second;
|
||||
break;
|
||||
}
|
||||
|
||||
if (JSON_HEDLEY_UNLIKELY(idx >= ptr->m_data.m_value.array->size()))
|
||||
case detail::value_t::array:
|
||||
{
|
||||
return nullptr;
|
||||
if (JSON_HEDLEY_UNLIKELY(is_dash(reference_token)))
|
||||
{
|
||||
// "-" always fails the range check
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// a malformed index throws parse_error.106/109; an
|
||||
// index that is syntactically valid but cannot be
|
||||
// represented (out_of_range.404/410) is treated like an
|
||||
// out-of-range index below
|
||||
typename BasicJsonType::size_type idx{};
|
||||
switch (parse_array_index<BasicJsonType>(reference_token, idx))
|
||||
{
|
||||
// the branches differ in their messages, not after JSON_THROW's expansion
|
||||
// NOLINTNEXTLINE(bugprone-branch-clone)
|
||||
case array_index_status::leading_zero:
|
||||
JSON_THROW(detail::parse_error::create(detail::exception_id::pointer_index_leading_zero, 0, detail::concat("array index '", reference_token, "' must not begin with '0'"), nullptr));
|
||||
case array_index_status::not_a_number:
|
||||
JSON_THROW(detail::parse_error::create(detail::exception_id::pointer_index_not_a_number, 0, detail::concat("array index '", reference_token, "' is not a number"), nullptr));
|
||||
case array_index_status::unresolved:
|
||||
case array_index_status::exceeds_size_type:
|
||||
return nullptr;
|
||||
case array_index_status::ok:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (JSON_HEDLEY_UNLIKELY(idx >= ptr->m_data.m_value.array->size()))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
ptr = &(*ptr->m_data.m_value.array)[idx];
|
||||
break;
|
||||
}
|
||||
ptr = &(*ptr->m_data.m_value.array)[idx];
|
||||
}
|
||||
else
|
||||
{
|
||||
// primitive value with a reference token left
|
||||
return nullptr;
|
||||
|
||||
case detail::value_t::null:
|
||||
case detail::value_t::string:
|
||||
case detail::value_t::boolean:
|
||||
case detail::value_t::number_integer:
|
||||
case detail::value_t::number_unsigned:
|
||||
case detail::value_t::number_float:
|
||||
case detail::value_t::binary:
|
||||
case detail::value_t::discarded:
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1210,48 +1239,63 @@ class json_pointer
|
||||
{
|
||||
for (const auto& reference_token : reference_tokens)
|
||||
{
|
||||
if (ptr->is_object())
|
||||
switch (ptr->type())
|
||||
{
|
||||
const auto it = ptr->m_data.m_value.object->find(reference_token);
|
||||
if (it == ptr->m_data.m_value.object->end())
|
||||
case detail::value_t::object:
|
||||
{
|
||||
// we did not find the key in the object
|
||||
return false;
|
||||
const auto it = ptr->m_data.m_value.object->find(reference_token);
|
||||
if (it == ptr->m_data.m_value.object->end())
|
||||
{
|
||||
// we did not find the key in the object
|
||||
return false;
|
||||
}
|
||||
|
||||
ptr = &it->second;
|
||||
break;
|
||||
}
|
||||
|
||||
ptr = &it->second;
|
||||
}
|
||||
else if (ptr->is_array())
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(is_dash(reference_token)))
|
||||
case detail::value_t::array:
|
||||
{
|
||||
// "-" always fails the range check
|
||||
return false;
|
||||
if (JSON_HEDLEY_UNLIKELY(is_dash(reference_token)))
|
||||
{
|
||||
// "-" always fails the range check
|
||||
return false;
|
||||
}
|
||||
|
||||
// any parse failure (malformed index, or one that is syntactically
|
||||
// valid but not representable as size_type) means the reference
|
||||
// token cannot denote an existing array element -- contains() must
|
||||
// not throw (see #5395), so it is treated as "not found"
|
||||
typename BasicJsonType::size_type idx{};
|
||||
if (JSON_HEDLEY_UNLIKELY(parse_array_index<BasicJsonType>(reference_token, idx) != array_index_status::ok))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (idx >= ptr->m_data.m_value.array->size())
|
||||
{
|
||||
// index out of range
|
||||
return false;
|
||||
}
|
||||
|
||||
ptr = &(*ptr->m_data.m_value.array)[idx];
|
||||
break;
|
||||
}
|
||||
|
||||
// any parse failure (malformed index, or one that is syntactically
|
||||
// valid but not representable as size_type) means the reference
|
||||
// token cannot denote an existing array element -- contains() must
|
||||
// not throw (see #5395), so it is treated as "not found"
|
||||
typename BasicJsonType::size_type idx{};
|
||||
if (JSON_HEDLEY_UNLIKELY(parse_array_index<BasicJsonType>(reference_token, idx) != array_index_status::ok))
|
||||
case detail::value_t::null:
|
||||
case detail::value_t::string:
|
||||
case detail::value_t::boolean:
|
||||
case detail::value_t::number_integer:
|
||||
case detail::value_t::number_unsigned:
|
||||
case detail::value_t::number_float:
|
||||
case detail::value_t::binary:
|
||||
case detail::value_t::discarded:
|
||||
default:
|
||||
{
|
||||
// we do not expect primitive values if there is still a
|
||||
// reference token to process
|
||||
return false;
|
||||
}
|
||||
|
||||
if (idx >= ptr->m_data.m_value.array->size())
|
||||
{
|
||||
// index out of range
|
||||
return false;
|
||||
}
|
||||
|
||||
ptr = &(*ptr->m_data.m_value.array)[idx];
|
||||
}
|
||||
else
|
||||
{
|
||||
// we do not expect primitive values if there is still a
|
||||
// reference token to process
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1279,7 +1323,10 @@ class json_pointer
|
||||
}
|
||||
|
||||
// check if a nonempty reference string begins with slash
|
||||
check_begins_with_slash(reference_string);
|
||||
if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/'))
|
||||
{
|
||||
JSON_THROW(detail::parse_error::create(detail::exception_id::pointer_missing_slash, 1, detail::concat("JSON pointer must be empty or begin with '/' - was: '", reference_string, "'"), nullptr));
|
||||
}
|
||||
|
||||
// every slash starts a reference token
|
||||
const std::size_t size = reference_string.size();
|
||||
@@ -1320,7 +1367,9 @@ class json_pointer
|
||||
JSON_ASSERT(reference_token[pos] == '~');
|
||||
|
||||
// ~ must be followed by 0 or 1
|
||||
if (JSON_HEDLEY_UNLIKELY(is_invalid_escape(reference_token, pos)))
|
||||
if (JSON_HEDLEY_UNLIKELY(pos == reference_token.size() - 1 ||
|
||||
(reference_token[pos + 1] != '0' &&
|
||||
reference_token[pos + 1] != '1')))
|
||||
{
|
||||
JSON_THROW(detail::parse_error::create(detail::exception_id::pointer_invalid_escape, 0, "escape character '~' must be followed with '0' or '1'", nullptr));
|
||||
}
|
||||
@@ -1477,34 +1526,50 @@ class json_pointer
|
||||
auto hint = object.end();
|
||||
const auto enter = [&stack, &path, &object, &hint](const BasicJsonType & v)
|
||||
{
|
||||
if (v.is_array())
|
||||
switch (v.type())
|
||||
{
|
||||
if (v.m_data.m_value.array->empty())
|
||||
case detail::value_t::array:
|
||||
{
|
||||
// flatten empty array as null
|
||||
add_flattened(object, hint, path, nullptr, detail::priority_tag<2> {});
|
||||
if (v.m_data.m_value.array->empty())
|
||||
{
|
||||
// flatten empty array as null
|
||||
add_flattened(object, hint, path, nullptr, detail::priority_tag<2> {});
|
||||
}
|
||||
else
|
||||
{
|
||||
stack.emplace_back(&v, object_const_iterator(), path.size());
|
||||
}
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
||||
case detail::value_t::object:
|
||||
{
|
||||
stack.emplace_back(&v, object_const_iterator(), path.size());
|
||||
if (v.m_data.m_value.object->empty())
|
||||
{
|
||||
// flatten empty object as null
|
||||
add_flattened(object, hint, path, nullptr, detail::priority_tag<2> {});
|
||||
}
|
||||
else
|
||||
{
|
||||
stack.emplace_back(&v, v.m_data.m_value.object->begin(), path.size());
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (v.is_object())
|
||||
{
|
||||
if (v.m_data.m_value.object->empty())
|
||||
|
||||
case detail::value_t::null:
|
||||
case detail::value_t::string:
|
||||
case detail::value_t::boolean:
|
||||
case detail::value_t::number_integer:
|
||||
case detail::value_t::number_unsigned:
|
||||
case detail::value_t::number_float:
|
||||
case detail::value_t::binary:
|
||||
case detail::value_t::discarded:
|
||||
default:
|
||||
{
|
||||
// flatten empty object as null
|
||||
add_flattened(object, hint, path, nullptr, detail::priority_tag<2> {});
|
||||
// add a primitive value with its reference string
|
||||
add_flattened(object, hint, path, v, detail::priority_tag<2> {});
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
stack.emplace_back(&v, v.m_data.m_value.object->begin(), path.size());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// add a primitive value with its reference string
|
||||
add_flattened(object, hint, path, v, detail::priority_tag<2> {});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
|
||||
#include <nlohmann/detail/bit_ops.hpp>
|
||||
#include <nlohmann/detail/input/binary_reader.hpp>
|
||||
#include <nlohmann/detail/input/input_adapters.hpp>
|
||||
#include <nlohmann/detail/input/string_scan.hpp>
|
||||
#include <nlohmann/detail/macro_scope.hpp>
|
||||
#include <nlohmann/detail/output/error_handler.hpp>
|
||||
@@ -312,7 +311,9 @@ class binary_writer
|
||||
write_cbor_head(0x40, N);
|
||||
|
||||
// step 2: write each element
|
||||
write_bytes(j.m_data.m_value.binary->data(), N);
|
||||
oa.write_characters(
|
||||
reinterpret_cast<const CharType*>(j.m_data.m_value.binary->data()),
|
||||
N);
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -325,7 +326,7 @@ class binary_writer
|
||||
|
||||
case value_t::discarded:
|
||||
default:
|
||||
throw_on_discarded(j, input_format_name(input_format_t::cbor));
|
||||
throw_on_discarded(j, "CBOR");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -525,7 +526,9 @@ class binary_writer
|
||||
}
|
||||
|
||||
// step 2: write the byte string
|
||||
write_bytes(j.m_data.m_value.binary->data(), N);
|
||||
oa.write_characters(
|
||||
reinterpret_cast<const CharType*>(j.m_data.m_value.binary->data()),
|
||||
N);
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -539,7 +542,7 @@ class binary_writer
|
||||
|
||||
case value_t::discarded:
|
||||
default:
|
||||
throw_on_discarded(j, input_format_name(input_format_t::msgpack));
|
||||
throw_on_discarded(j, "MessagePack");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -623,7 +626,7 @@ class binary_writer
|
||||
case value_t::array:
|
||||
{
|
||||
bool prefix_required = true;
|
||||
const bool write_closer = write_ubjson_start_container<true>(j, use_count, use_type, add_prefix, use_bjdata, prefix_required);
|
||||
const bool write_closer = write_ubjson_start_array(j, use_count, use_type, add_prefix, use_bjdata, prefix_required);
|
||||
|
||||
{
|
||||
const auto& array = *j.m_data.m_value.array;
|
||||
@@ -673,7 +676,9 @@ class binary_writer
|
||||
|
||||
if (use_type)
|
||||
{
|
||||
write_bytes(j.m_data.m_value.binary->data(), j.m_data.m_value.binary->size());
|
||||
oa.write_characters(
|
||||
reinterpret_cast<const CharType*>(j.m_data.m_value.binary->data()),
|
||||
j.m_data.m_value.binary->size());
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -699,7 +704,7 @@ class binary_writer
|
||||
}
|
||||
|
||||
bool prefix_required = true;
|
||||
const bool write_closer = write_ubjson_start_container<false>(j, use_count, use_type, add_prefix, use_bjdata, prefix_required);
|
||||
const bool write_closer = write_ubjson_start_object(j, use_count, use_type, add_prefix, use_bjdata, prefix_required);
|
||||
|
||||
write_ubjson_members(j, use_count, use_type, prefix_required, use_bjdata, bjdata_version, depth);
|
||||
|
||||
@@ -713,7 +718,7 @@ class binary_writer
|
||||
|
||||
case value_t::discarded:
|
||||
default:
|
||||
throw_on_discarded(j, input_format_name(use_bjdata ? input_format_t::bjdata : input_format_t::ubjson));
|
||||
throw_on_discarded(j, use_bjdata ? "BJData" : "UBJSON");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -923,56 +928,53 @@ class binary_writer
|
||||
JSON_THROW(other_error::create(exception_id::size_marker_required, "use_type requires use_size = true", &j));
|
||||
}
|
||||
|
||||
/// @brief write @a size bytes starting at @a data to the output adapter
|
||||
JSON_HEDLEY_ALWAYS_INLINE
|
||||
void write_bytes(const void* data, const std::size_t size)
|
||||
{
|
||||
oa.write_characters(static_cast<const CharType*>(data), size);
|
||||
}
|
||||
|
||||
/// @brief write the MessagePack prefix of an array or object with @a N
|
||||
/// elements: the fix marker @a fix_marker | n, or @a marker_16 or
|
||||
/// @a marker_32 followed by the length
|
||||
JSON_HEDLEY_ALWAYS_INLINE
|
||||
void write_msgpack_container_prefix(const std::size_t N, const BasicJsonType& j,
|
||||
const std::uint8_t fix_marker, const std::uint8_t marker_16, const std::uint8_t marker_32)
|
||||
void write_msgpack_array_prefix(const std::size_t N, const BasicJsonType& j)
|
||||
{
|
||||
const auto n = to_msgpack_length(N, j);
|
||||
if (n <= 15)
|
||||
{
|
||||
// fixarray, fixmap
|
||||
oa.write_character(to_char_type(static_cast<std::uint8_t>(fix_marker | n)));
|
||||
// fixarray
|
||||
oa.write_character(to_char_type(static_cast<std::uint8_t>(0x90 | n)));
|
||||
}
|
||||
else if (n <= (std::numeric_limits<std::uint16_t>::max)())
|
||||
{
|
||||
// array 16, map 16
|
||||
oa.write_character(to_char_type(marker_16));
|
||||
// array 16
|
||||
oa.write_character(to_char_type(0xDC));
|
||||
write_number(static_cast<std::uint16_t>(n));
|
||||
}
|
||||
else
|
||||
{
|
||||
// array 32, map 32
|
||||
oa.write_character(to_char_type(marker_32));
|
||||
// array 32
|
||||
oa.write_character(to_char_type(0xDD));
|
||||
write_number(static_cast<std::uint32_t>(n));
|
||||
}
|
||||
}
|
||||
|
||||
JSON_HEDLEY_ALWAYS_INLINE
|
||||
void write_msgpack_array_prefix(const std::size_t N, const BasicJsonType& j)
|
||||
{
|
||||
write_msgpack_container_prefix(N, j, 0x90, 0xDC, 0xDD);
|
||||
}
|
||||
|
||||
JSON_HEDLEY_ALWAYS_INLINE
|
||||
void write_msgpack_object_prefix(const std::size_t N, const BasicJsonType& j)
|
||||
{
|
||||
write_msgpack_container_prefix(N, j, 0x80, 0xDE, 0xDF);
|
||||
const auto n = to_msgpack_length(N, j);
|
||||
if (n <= 15)
|
||||
{
|
||||
// fixmap
|
||||
oa.write_character(to_char_type(static_cast<std::uint8_t>(0x80 | (n & 0xF))));
|
||||
}
|
||||
else if (n <= (std::numeric_limits<std::uint16_t>::max)())
|
||||
{
|
||||
// map 16
|
||||
oa.write_character(to_char_type(0xDE));
|
||||
write_number(static_cast<std::uint16_t>(n));
|
||||
}
|
||||
else
|
||||
{
|
||||
// map 32
|
||||
oa.write_character(to_char_type(0xDF));
|
||||
write_number(static_cast<std::uint32_t>(n));
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief a CBOR, MessagePack, BON8, or (as the base of @ref ubjson_frame)
|
||||
/// UBJSON array or object whose elements @ref write_container_iterative,
|
||||
/// @ref write_bon8_iterative, or @ref write_ubjson_iterative is still
|
||||
/// writing
|
||||
/// @brief a CBOR, MessagePack, or BON8 array or object whose elements
|
||||
/// @ref write_cbor_iterative, @ref write_msgpack_iterative, or
|
||||
/// @ref write_bon8_iterative is still writing
|
||||
struct binary_container_frame
|
||||
{
|
||||
explicit binary_container_frame(const BasicJsonType* value_) noexcept
|
||||
@@ -1008,82 +1010,19 @@ class binary_writer
|
||||
typename BasicJsonType::array_t::const_iterator array_it{};
|
||||
};
|
||||
|
||||
/// @brief the CBOR specifics of @ref write_container_iterative
|
||||
struct cbor_walk
|
||||
{
|
||||
static void array_head(binary_writer& w, const BasicJsonType& j)
|
||||
{
|
||||
w.write_cbor_head(0x80, j.m_data.m_value.array->size());
|
||||
}
|
||||
|
||||
static void object_head(binary_writer& w, const BasicJsonType& j)
|
||||
{
|
||||
w.write_cbor_head(0xA0, j.m_data.m_value.object->size());
|
||||
}
|
||||
|
||||
static void scalar(binary_writer& w, const BasicJsonType& j)
|
||||
{
|
||||
w.write_cbor(j);
|
||||
}
|
||||
|
||||
static void string(binary_writer& w, const string_t& key, const BasicJsonType& context)
|
||||
{
|
||||
w.write_cbor_string(key, context);
|
||||
}
|
||||
|
||||
template<typename KeyType>
|
||||
static void key(binary_writer& w, const KeyType& k, const BasicJsonType& object)
|
||||
{
|
||||
w.write_cbor_key(k, object);
|
||||
}
|
||||
};
|
||||
|
||||
/// @brief the MessagePack specifics of @ref write_container_iterative
|
||||
struct msgpack_walk
|
||||
{
|
||||
static void array_head(binary_writer& w, const BasicJsonType& j)
|
||||
{
|
||||
w.write_msgpack_array_prefix(j.m_data.m_value.array->size(), j);
|
||||
}
|
||||
|
||||
static void object_head(binary_writer& w, const BasicJsonType& j)
|
||||
{
|
||||
w.write_msgpack_object_prefix(j.m_data.m_value.object->size(), j);
|
||||
}
|
||||
|
||||
static void scalar(binary_writer& w, const BasicJsonType& j)
|
||||
{
|
||||
w.write_msgpack(j);
|
||||
}
|
||||
|
||||
static void string(binary_writer& w, const string_t& key, const BasicJsonType& context)
|
||||
{
|
||||
w.write_msgpack_string(key, context);
|
||||
}
|
||||
|
||||
template<typename KeyType>
|
||||
static void key(binary_writer& w, const KeyType& k, const BasicJsonType& object)
|
||||
{
|
||||
w.write_msgpack_key(k, object);
|
||||
}
|
||||
};
|
||||
|
||||
/*!
|
||||
@brief write @a j with @a Walk::scalar, or write its header and push a
|
||||
frame for @ref write_container_iterative to continue with its
|
||||
elements
|
||||
@brief write @a j with @ref write_cbor, or write its header and push a
|
||||
frame for @ref write_cbor_iterative to continue with its elements
|
||||
|
||||
A scalar, and an empty array or object, are written out in full: there is
|
||||
nothing below them for @ref write_container_iterative to come back to, so
|
||||
nothing below them for @ref write_cbor_iterative to come back to, so
|
||||
nothing is pushed for them.
|
||||
*/
|
||||
template<typename Walk>
|
||||
JSON_HEDLEY_ALWAYS_INLINE
|
||||
void write_container_value_or_push(const BasicJsonType& j, std::vector<binary_container_frame>& stack)
|
||||
void write_cbor_value_or_push(const BasicJsonType& j, std::vector<binary_container_frame>& stack)
|
||||
{
|
||||
if (j.is_array())
|
||||
{
|
||||
Walk::array_head(*this, j);
|
||||
write_cbor_head(0x80, j.m_data.m_value.array->size());
|
||||
if (!j.m_data.m_value.array->empty())
|
||||
{
|
||||
stack.emplace_back(&j);
|
||||
@@ -1093,7 +1032,7 @@ class binary_writer
|
||||
|
||||
if (j.is_object())
|
||||
{
|
||||
Walk::object_head(*this, j);
|
||||
write_cbor_head(0xA0, j.m_data.m_value.object->size());
|
||||
if (!j.m_data.m_value.object->empty())
|
||||
{
|
||||
stack.emplace_back(&j);
|
||||
@@ -1101,26 +1040,22 @@ class binary_writer
|
||||
return;
|
||||
}
|
||||
|
||||
Walk::scalar(*this, j);
|
||||
write_cbor(j);
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief write out @a root and everything below it without the call stack
|
||||
|
||||
Emits the same bytes as the recursive writer, keeping the containers it has
|
||||
Emits the same bytes as @ref write_cbor, keeping the containers it has
|
||||
entered on an explicit stack instead of descending into them. Only reached
|
||||
for values nested deeper than @ref recursion_depth_limit, which is why it
|
||||
is not written for speed. Shared by CBOR and MessagePack, which differ only
|
||||
in the @a Walk policy (@ref cbor_walk, @ref msgpack_walk); BON8 has its own
|
||||
walker, as it tracks an open string run and closes a container with a
|
||||
marker.
|
||||
is not written for speed.
|
||||
*/
|
||||
template<typename Walk>
|
||||
void write_container_iterative(const BasicJsonType& root)
|
||||
void write_cbor_iterative(const BasicJsonType& root)
|
||||
{
|
||||
// only a container with elements is ever pushed; see write_container_value_or_push
|
||||
// only a container with elements is ever pushed; see write_cbor_value_or_push
|
||||
std::vector<binary_container_frame> stack;
|
||||
write_container_value_or_push<Walk>(root, stack);
|
||||
write_cbor_value_or_push(root, stack);
|
||||
|
||||
while (!stack.empty())
|
||||
{
|
||||
@@ -1138,7 +1073,7 @@ class binary_writer
|
||||
// read the child before pushing: entering it can move every frame
|
||||
const BasicJsonType* child = &(*current.array_it);
|
||||
++stack.back().array_it;
|
||||
write_container_value_or_push<Walk>(*child, stack);
|
||||
write_cbor_value_or_push(*child, stack);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1150,53 +1085,110 @@ class binary_writer
|
||||
}
|
||||
|
||||
// the key is written directly (not via a temporary basic_json),
|
||||
// with the object as diagnostics context, as in the recursive writer
|
||||
Walk::key(*this, current.object_it->first, *current.value);
|
||||
// with the object as diagnostics context, as in write_cbor
|
||||
write_cbor_key(current.object_it->first, *current.value);
|
||||
const BasicJsonType* child = &(current.object_it->second);
|
||||
++stack.back().object_it;
|
||||
write_container_value_or_push<Walk>(*child, stack);
|
||||
write_cbor_value_or_push(*child, stack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void write_cbor_iterative(const BasicJsonType& root)
|
||||
{
|
||||
write_container_iterative<cbor_walk>(root);
|
||||
}
|
||||
/*!
|
||||
@brief write @a j with @ref write_msgpack, or write its header and push a
|
||||
frame for @ref write_msgpack_iterative to continue with its elements
|
||||
|
||||
void write_msgpack_iterative(const BasicJsonType& root)
|
||||
@sa @ref write_cbor_value_or_push
|
||||
*/
|
||||
void write_msgpack_value_or_push(const BasicJsonType& j, std::vector<binary_container_frame>& stack)
|
||||
{
|
||||
write_container_iterative<msgpack_walk>(root);
|
||||
if (j.is_array())
|
||||
{
|
||||
write_msgpack_array_prefix(j.m_data.m_value.array->size(), j);
|
||||
if (!j.m_data.m_value.array->empty())
|
||||
{
|
||||
stack.emplace_back(&j);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (j.is_object())
|
||||
{
|
||||
write_msgpack_object_prefix(j.m_data.m_value.object->size(), j);
|
||||
if (!j.m_data.m_value.object->empty())
|
||||
{
|
||||
stack.emplace_back(&j);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
write_msgpack(j);
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief write the start of an array (@a IsArray) or object: its opening
|
||||
marker, the optimized type, and the count
|
||||
@brief write out @a root and everything below it without the call stack
|
||||
|
||||
@return true when a closing ']' or '}' still has to be written after the
|
||||
elements
|
||||
@sa @ref write_cbor_iterative
|
||||
*/
|
||||
template<bool IsArray>
|
||||
bool write_ubjson_start_container(const BasicJsonType& j, const bool use_count, const bool use_type,
|
||||
const bool add_prefix, const bool use_bjdata, bool& prefix_required)
|
||||
void write_msgpack_iterative(const BasicJsonType& root)
|
||||
{
|
||||
std::vector<binary_container_frame> stack;
|
||||
write_msgpack_value_or_push(root, stack);
|
||||
|
||||
while (!stack.empty())
|
||||
{
|
||||
const binary_container_frame current = stack.back();
|
||||
|
||||
if (current.value->is_array())
|
||||
{
|
||||
const auto& array = *current.value->m_data.m_value.array;
|
||||
if (current.array_it == array.cend())
|
||||
{
|
||||
stack.pop_back();
|
||||
continue;
|
||||
}
|
||||
|
||||
const BasicJsonType* child = &(*current.array_it);
|
||||
++stack.back().array_it;
|
||||
write_msgpack_value_or_push(*child, stack);
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto& object = *current.value->m_data.m_value.object;
|
||||
if (current.object_it == object.cend())
|
||||
{
|
||||
stack.pop_back();
|
||||
continue;
|
||||
}
|
||||
|
||||
// as in write_cbor_iterative, the key is written directly with
|
||||
// the object as diagnostics context
|
||||
write_msgpack_key(current.object_it->first, *current.value);
|
||||
const BasicJsonType* child = &(current.object_it->second);
|
||||
++stack.back().object_it;
|
||||
write_msgpack_value_or_push(*child, stack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @return true when a closing ']' still has to be written after the elements
|
||||
bool write_ubjson_start_array(const BasicJsonType& j, const bool use_count, const bool use_type,
|
||||
const bool add_prefix, const bool use_bjdata, bool& prefix_required)
|
||||
{
|
||||
prefix_required = true;
|
||||
if (add_prefix)
|
||||
{
|
||||
oa.write_character(to_char_type(IsArray ? '[' : '{'));
|
||||
oa.write_character(to_char_type('['));
|
||||
}
|
||||
|
||||
const std::size_t size = IsArray ? j.m_data.m_value.array->size() : j.m_data.m_value.object->size();
|
||||
|
||||
if (use_type && size != 0)
|
||||
if (use_type && !j.m_data.m_value.array->empty())
|
||||
{
|
||||
if (!use_count)
|
||||
{
|
||||
throw_size_marker_required(j);
|
||||
}
|
||||
const CharType first_prefix = ubjson_prefix(j.front(), use_bjdata);
|
||||
// the first element of an array has been compared with itself
|
||||
const bool same_prefix = std::all_of(IsArray ? j.begin() + 1 : j.begin(), j.end(),
|
||||
const bool same_prefix = std::all_of(j.begin() + 1, j.end(),
|
||||
[this, first_prefix, use_bjdata](const BasicJsonType & v)
|
||||
{
|
||||
return ubjson_prefix(v, use_bjdata) == first_prefix;
|
||||
@@ -1208,9 +1200,8 @@ class binary_writer
|
||||
// those, at one byte per element, so the result can be read back.
|
||||
// Objects are not affected: every element is preceded by its key.
|
||||
const bool valueless_type = (first_prefix == 'Z' || first_prefix == 'T' || first_prefix == 'F');
|
||||
const bool excessive_valueless = IsArray
|
||||
&& valueless_type
|
||||
&& size > detail::max_valueless_container_size;
|
||||
const bool excessive_valueless = valueless_type
|
||||
&& j.m_data.m_value.array->size() > detail::max_valueless_container_size;
|
||||
|
||||
if (same_prefix && !excessive_valueless
|
||||
&& !(use_bjdata && is_bjdata_excluded_type_marker(first_prefix)))
|
||||
@@ -1223,7 +1214,46 @@ class binary_writer
|
||||
|
||||
if (use_count)
|
||||
{
|
||||
write_ubjson_count(size, use_bjdata);
|
||||
write_ubjson_count(j.m_data.m_value.array->size(), use_bjdata);
|
||||
}
|
||||
|
||||
return !use_count;
|
||||
}
|
||||
|
||||
/// @return true when a closing '}' still has to be written after the elements
|
||||
bool write_ubjson_start_object(const BasicJsonType& j, const bool use_count, const bool use_type,
|
||||
const bool add_prefix, const bool use_bjdata, bool& prefix_required)
|
||||
{
|
||||
prefix_required = true;
|
||||
if (add_prefix)
|
||||
{
|
||||
oa.write_character(to_char_type('{'));
|
||||
}
|
||||
|
||||
if (use_type && !j.m_data.m_value.object->empty())
|
||||
{
|
||||
if (!use_count)
|
||||
{
|
||||
throw_size_marker_required(j);
|
||||
}
|
||||
const CharType first_prefix = ubjson_prefix(j.front(), use_bjdata);
|
||||
const bool same_prefix = std::all_of(j.begin(), j.end(),
|
||||
[this, first_prefix, use_bjdata](const BasicJsonType & v)
|
||||
{
|
||||
return ubjson_prefix(v, use_bjdata) == first_prefix;
|
||||
});
|
||||
|
||||
if (same_prefix && !(use_bjdata && is_bjdata_excluded_type_marker(first_prefix)))
|
||||
{
|
||||
prefix_required = false;
|
||||
oa.write_character(to_char_type('$'));
|
||||
oa.write_character(first_prefix);
|
||||
}
|
||||
}
|
||||
|
||||
if (use_count)
|
||||
{
|
||||
write_ubjson_count(j.m_data.m_value.object->size(), use_bjdata);
|
||||
}
|
||||
|
||||
return !use_count;
|
||||
@@ -1251,12 +1281,21 @@ class binary_writer
|
||||
|
||||
/// @brief an object or array @ref write_ubjson_iterative is still writing
|
||||
/// the elements of
|
||||
struct ubjson_frame : binary_container_frame
|
||||
struct ubjson_frame
|
||||
{
|
||||
ubjson_frame(const BasicJsonType* value_, const bool prefix_required_) noexcept
|
||||
: binary_container_frame(value_)
|
||||
: value(value_)
|
||||
, prefix_required(prefix_required_)
|
||||
{}
|
||||
{
|
||||
if (value->is_object())
|
||||
{
|
||||
object_it = value->m_data.m_value.object->cbegin();
|
||||
}
|
||||
else
|
||||
{
|
||||
array_it = value->m_data.m_value.array->cbegin();
|
||||
}
|
||||
}
|
||||
|
||||
// declared for GCC's -Weffc++, which asks for them in a class with
|
||||
// pointer members and a non-trivial destructor; the exception
|
||||
@@ -1268,9 +1307,13 @@ class binary_writer
|
||||
ubjson_frame& operator=(ubjson_frame&&) = default;
|
||||
~ubjson_frame() = default;
|
||||
|
||||
/// the array or object being written
|
||||
const BasicJsonType* value;
|
||||
/// whether value's elements each carry their own type marker; an
|
||||
/// optimized ($type) container writes it once for all of them instead
|
||||
bool prefix_required;
|
||||
typename BasicJsonType::object_t::const_iterator object_it{};
|
||||
typename BasicJsonType::array_t::const_iterator array_it{};
|
||||
};
|
||||
|
||||
/*!
|
||||
@@ -1281,7 +1324,7 @@ class binary_writer
|
||||
elements of an optimized container, and everything below the
|
||||
top level, never repeat it)
|
||||
|
||||
@sa @ref write_container_value_or_push
|
||||
@sa @ref write_cbor_value_or_push
|
||||
*/
|
||||
void write_ubjson_value_or_push(const BasicJsonType& j, const bool add_prefix, const bool use_count,
|
||||
const bool use_type, const bool use_bjdata, const bjdata_version_t bjdata_version,
|
||||
@@ -1304,11 +1347,11 @@ class binary_writer
|
||||
bool prefix_required = true;
|
||||
if (is_array)
|
||||
{
|
||||
write_ubjson_start_container<true>(j, use_count, use_type, add_prefix, use_bjdata, prefix_required);
|
||||
write_ubjson_start_array(j, use_count, use_type, add_prefix, use_bjdata, prefix_required);
|
||||
}
|
||||
else
|
||||
{
|
||||
write_ubjson_start_container<false>(j, use_count, use_type, add_prefix, use_bjdata, prefix_required);
|
||||
write_ubjson_start_object(j, use_count, use_type, add_prefix, use_bjdata, prefix_required);
|
||||
}
|
||||
|
||||
const bool empty = is_array ? j.m_data.m_value.array->empty() : j.m_data.m_value.object->empty();
|
||||
@@ -1318,7 +1361,7 @@ class binary_writer
|
||||
return;
|
||||
}
|
||||
|
||||
// write_ubjson_start_container returns !use_count, i.e. whether a
|
||||
// write_ubjson_start_array/_object return !use_count, i.e. whether a
|
||||
// closer still has to be written; use_count is constant for the whole
|
||||
// document, so that is recomputed here instead of being carried along
|
||||
if (!use_count)
|
||||
@@ -1507,15 +1550,14 @@ class binary_writer
|
||||
{
|
||||
oa.write_character(to_char_type(element_type));
|
||||
|
||||
if (JSON_HEDLEY_LIKELY(error_handler == error_handler_t::keep || error_handler == error_handler_t::strict))
|
||||
if (error_handler == error_handler_t::keep || error_handler == error_handler_t::strict || is_valid_utf8(name))
|
||||
{
|
||||
write_bytes(name.data(), name.size());
|
||||
oa.write_characters(reinterpret_cast<const CharType*>(name.data()), name.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
string_t storage;
|
||||
const string_t& written = sanitize_utf8_for_bson_write(name, storage);
|
||||
write_bytes(written.data(), written.size());
|
||||
const string_t sanitized = sanitize_utf8(name, error_handler);
|
||||
oa.write_characters(reinterpret_cast<const CharType*>(sanitized.data()), sanitized.size());
|
||||
}
|
||||
|
||||
// the terminating null byte is written explicitly rather than taken
|
||||
@@ -1591,11 +1633,16 @@ class binary_writer
|
||||
{
|
||||
write_bson_entry_header(name, 0x02);
|
||||
|
||||
string_t storage;
|
||||
const string_t& written = sanitize_utf8_for_bson_write(value, storage);
|
||||
const bool sanitize = error_handler != error_handler_t::keep
|
||||
&& error_handler != error_handler_t::strict
|
||||
&& !is_valid_utf8(value);
|
||||
const string_t sanitized = sanitize ? sanitize_utf8(value, error_handler) : string_t{};
|
||||
const string_t& written = sanitize ? sanitized : value;
|
||||
|
||||
write_number<std::int32_t>(to_bson_length(written.size() + 1ul), true);
|
||||
write_bytes(written.data(), written.size());
|
||||
oa.write_characters(
|
||||
reinterpret_cast<const CharType*>(written.data()),
|
||||
written.size());
|
||||
// the terminating null byte is written explicitly rather than taken
|
||||
// from the buffer, so that string_t::data() need not be null-terminated
|
||||
oa.write_character(to_char_type(0x00));
|
||||
@@ -1701,7 +1748,7 @@ class binary_writer
|
||||
|
||||
write_number(value.has_subtype() ? static_cast<std::uint8_t>(value.subtype()) : static_cast<std::uint8_t>(0x00));
|
||||
|
||||
write_bytes(value.data(), value.size());
|
||||
oa.write_characters(reinterpret_cast<const CharType*>(value.data()), value.size());
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -1731,14 +1778,14 @@ class binary_writer
|
||||
case value_t::number_integer:
|
||||
if (JSON_HEDLEY_UNLIKELY(!integer_fits<std::int64_t>(j.m_data.m_value.number_integer)))
|
||||
{
|
||||
throw_integer_out_of_range(j, input_format_name(input_format_t::bson), "int64");
|
||||
throw_integer_out_of_range(j, "BSON", "int64");
|
||||
}
|
||||
return calc_bson_integer_size(static_cast<std::int64_t>(j.m_data.m_value.number_integer));
|
||||
|
||||
case value_t::number_unsigned:
|
||||
if (JSON_HEDLEY_UNLIKELY(!integer_fits_max<std::uint64_t>(j.m_data.m_value.number_unsigned)))
|
||||
{
|
||||
throw_integer_out_of_range(j, input_format_name(input_format_t::bson), "uint64");
|
||||
throw_integer_out_of_range(j, "BSON", "uint64");
|
||||
}
|
||||
return calc_bson_unsigned_size(conditional_static_cast<std::uint64_t>(j.m_data.m_value.number_unsigned));
|
||||
|
||||
@@ -1749,7 +1796,7 @@ class binary_writer
|
||||
return 0ul;
|
||||
|
||||
case value_t::discarded:
|
||||
throw_on_discarded(j, input_format_name(input_format_t::bson));
|
||||
throw_on_discarded(j, "BSON");
|
||||
|
||||
// LCOV_EXCL_START
|
||||
case value_t::object:
|
||||
@@ -1913,7 +1960,7 @@ class binary_writer
|
||||
// the terminating null byte is written explicitly rather than
|
||||
// taken from the buffer, so that string_t::data() need not be
|
||||
// null-terminated
|
||||
write_bytes(value.data(), value.size());
|
||||
oa.write_characters(reinterpret_cast<const CharType*>(value.data()), value.size());
|
||||
oa.write_character(to_char_type(0x00));
|
||||
return true;
|
||||
}
|
||||
@@ -1930,7 +1977,7 @@ class binary_writer
|
||||
p += sizeof(std::int32_t);
|
||||
*p++ = value.has_subtype() ? to_char_type(static_cast<std::uint8_t>(value.subtype())) : to_char_type(0x00);
|
||||
oa.commit(static_cast<std::size_t>(p - out));
|
||||
write_bytes(value.data(), value.size());
|
||||
oa.write_characters(reinterpret_cast<const CharType*>(value.data()), value.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1978,7 +2025,7 @@ class binary_writer
|
||||
return write_bson_null(name);
|
||||
|
||||
case value_t::discarded:
|
||||
throw_on_discarded(j, input_format_name(input_format_t::bson));
|
||||
throw_on_discarded(j, "BSON");
|
||||
|
||||
// LCOV_EXCL_START
|
||||
case value_t::object:
|
||||
@@ -2759,7 +2806,7 @@ class binary_writer
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!integer_fits_max<std::uint64_t>(j.m_data.m_value.number_unsigned)))
|
||||
{
|
||||
throw_integer_out_of_range(j, input_format_name(input_format_t::cbor), "uint64");
|
||||
throw_integer_out_of_range(j, "CBOR", "uint64");
|
||||
}
|
||||
write_cbor_head(0x00, conditional_static_cast<std::uint64_t>(j.m_data.m_value.number_unsigned));
|
||||
return;
|
||||
@@ -2769,7 +2816,7 @@ class binary_writer
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!integer_fits_max<std::uint64_t>(j.m_data.m_value.number_integer)))
|
||||
{
|
||||
throw_integer_out_of_range(j, input_format_name(input_format_t::cbor), "[-2^64, 2^64-1]");
|
||||
throw_integer_out_of_range(j, "CBOR", "[-2^64, 2^64-1]");
|
||||
}
|
||||
// CBOR does not differentiate between positive signed
|
||||
// integers and unsigned integers
|
||||
@@ -2780,7 +2827,7 @@ class binary_writer
|
||||
// a negative integer n is encoded as -1 - n
|
||||
if (JSON_HEDLEY_UNLIKELY(!integer_fits_max<std::uint64_t>(-1 - j.m_data.m_value.number_integer)))
|
||||
{
|
||||
throw_integer_out_of_range(j, input_format_name(input_format_t::cbor), "[-2^64, 2^64-1]");
|
||||
throw_integer_out_of_range(j, "CBOR", "[-2^64, 2^64-1]");
|
||||
}
|
||||
write_cbor_head(0x20, static_cast<std::uint64_t>(-1 - j.m_data.m_value.number_integer));
|
||||
}
|
||||
@@ -2856,7 +2903,7 @@ class binary_writer
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!integer_fits_max<std::uint64_t>(j.m_data.m_value.number_unsigned)))
|
||||
{
|
||||
throw_integer_out_of_range(j, input_format_name(input_format_t::msgpack), "uint64");
|
||||
throw_integer_out_of_range(j, "MessagePack", "uint64");
|
||||
}
|
||||
write_msgpack_unsigned(static_cast<std::uint64_t>(j.m_data.m_value.number_unsigned));
|
||||
return;
|
||||
@@ -2866,7 +2913,7 @@ class binary_writer
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!integer_fits_max<std::uint64_t>(j.m_data.m_value.number_integer)))
|
||||
{
|
||||
throw_integer_out_of_range(j, input_format_name(input_format_t::msgpack), "[-2^63, 2^64-1]");
|
||||
throw_integer_out_of_range(j, "MessagePack", "[-2^63, 2^64-1]");
|
||||
}
|
||||
// MessagePack does not differentiate between positive
|
||||
// signed integers and unsigned integers.
|
||||
@@ -2876,7 +2923,7 @@ class binary_writer
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!integer_fits_min<std::int64_t>(j.m_data.m_value.number_integer)))
|
||||
{
|
||||
throw_integer_out_of_range(j, input_format_name(input_format_t::msgpack), "[-2^63, 2^64-1]");
|
||||
throw_integer_out_of_range(j, "MessagePack", "[-2^63, 2^64-1]");
|
||||
}
|
||||
if (j.m_data.m_value.number_integer >= -32)
|
||||
{
|
||||
@@ -3010,37 +3057,40 @@ class binary_writer
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief write an object key as a CBOR or MessagePack string (@a Walk)
|
||||
@brief write an object key as a CBOR text string
|
||||
|
||||
A key convertible to string_t is written directly. Other key types (only
|
||||
an explicit conversion, or only a to_json overload) go through a temporary
|
||||
basic_json, as in version 3.12.0; the temporary is then the diagnostics
|
||||
context for strict UTF-8 checks.
|
||||
*/
|
||||
template<typename Walk, typename Key = typename BasicJsonType::object_t::key_type,
|
||||
template<typename Key = typename BasicJsonType::object_t::key_type,
|
||||
enable_if_t<std::is_convertible<Key, string_t>::value, int> = 0>
|
||||
void write_object_key(const typename BasicJsonType::object_t::key_type& key, const BasicJsonType& context)
|
||||
{
|
||||
Walk::string(*this, key, context);
|
||||
}
|
||||
|
||||
template < typename Walk, typename Key = typename BasicJsonType::object_t::key_type,
|
||||
enable_if_t < !std::is_convertible<Key, string_t>::value, int > = 0 >
|
||||
void write_object_key(const typename BasicJsonType::object_t::key_type& key, const BasicJsonType& /*context*/)
|
||||
{
|
||||
Walk::scalar(*this, BasicJsonType(key));
|
||||
}
|
||||
|
||||
/// @brief write an object key as a CBOR text string
|
||||
void write_cbor_key(const typename BasicJsonType::object_t::key_type& key, const BasicJsonType& context)
|
||||
{
|
||||
write_object_key<cbor_walk>(key, context);
|
||||
write_cbor_string(key, context);
|
||||
}
|
||||
|
||||
/// @brief write an object key as a MessagePack str
|
||||
template < typename Key = typename BasicJsonType::object_t::key_type,
|
||||
enable_if_t < !std::is_convertible<Key, string_t>::value, int > = 0 >
|
||||
void write_cbor_key(const typename BasicJsonType::object_t::key_type& key, const BasicJsonType& /*context*/)
|
||||
{
|
||||
write_cbor(BasicJsonType(key));
|
||||
}
|
||||
|
||||
/// @brief write an object key as a MessagePack str, as in @ref write_cbor_key
|
||||
template<typename Key = typename BasicJsonType::object_t::key_type,
|
||||
enable_if_t<std::is_convertible<Key, string_t>::value, int> = 0>
|
||||
void write_msgpack_key(const typename BasicJsonType::object_t::key_type& key, const BasicJsonType& context)
|
||||
{
|
||||
write_object_key<msgpack_walk>(key, context);
|
||||
write_msgpack_string(key, context);
|
||||
}
|
||||
|
||||
template < typename Key = typename BasicJsonType::object_t::key_type,
|
||||
enable_if_t < !std::is_convertible<Key, string_t>::value, int > = 0 >
|
||||
void write_msgpack_key(const typename BasicJsonType::object_t::key_type& key, const BasicJsonType& /*context*/)
|
||||
{
|
||||
write_msgpack(BasicJsonType(key));
|
||||
}
|
||||
|
||||
/// the longest string that is written, with its head of one or two
|
||||
@@ -3115,7 +3165,9 @@ class binary_writer
|
||||
write_cbor_head(0x60, sanitized.size());
|
||||
|
||||
// step 2: write the string
|
||||
write_bytes(sanitized.data(), sanitized.size());
|
||||
oa.write_characters(
|
||||
reinterpret_cast<const CharType*>(sanitized.data()),
|
||||
sanitized.size());
|
||||
}
|
||||
|
||||
/////////////
|
||||
@@ -3196,7 +3248,9 @@ class binary_writer
|
||||
}
|
||||
|
||||
// step 2: write the string
|
||||
write_bytes(sanitized.data(), sanitized.size());
|
||||
oa.write_characters(
|
||||
reinterpret_cast<const CharType*>(sanitized.data()),
|
||||
sanitized.size());
|
||||
}
|
||||
|
||||
////////////
|
||||
@@ -3479,7 +3533,9 @@ class binary_writer
|
||||
oa.write_character(to_char_type('S'));
|
||||
}
|
||||
write_number_with_ubjson_prefix(sanitized.size(), true, use_bjdata);
|
||||
write_bytes(sanitized.data(), sanitized.size());
|
||||
oa.write_characters(
|
||||
reinterpret_cast<const CharType*>(sanitized.data()),
|
||||
sanitized.size());
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -4279,7 +4335,7 @@ class binary_writer
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!integer_fits_max<std::int64_t>(j.m_data.m_value.number_unsigned)))
|
||||
{
|
||||
throw_integer_out_of_range(j, input_format_name(input_format_t::bon8), "int64");
|
||||
throw_integer_out_of_range(j, "BON8", "int64");
|
||||
}
|
||||
write_bon8_integer(static_cast<std::int64_t>(j.m_data.m_value.number_unsigned));
|
||||
string_open = false;
|
||||
@@ -4290,7 +4346,7 @@ class binary_writer
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!integer_fits<std::int64_t>(j.m_data.m_value.number_integer)))
|
||||
{
|
||||
throw_integer_out_of_range(j, input_format_name(input_format_t::bon8), "int64");
|
||||
throw_integer_out_of_range(j, "BON8", "int64");
|
||||
}
|
||||
write_bon8_integer(static_cast<std::int64_t>(j.m_data.m_value.number_integer));
|
||||
string_open = false;
|
||||
@@ -4358,7 +4414,7 @@ class binary_writer
|
||||
|
||||
case value_t::discarded:
|
||||
default:
|
||||
throw_on_discarded(j, input_format_name(input_format_t::bon8));
|
||||
throw_on_discarded(j, "BON8");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4369,7 +4425,7 @@ class binary_writer
|
||||
A scalar, and an empty array or object, are written out in full and not
|
||||
pushed.
|
||||
|
||||
@sa @ref write_container_value_or_push
|
||||
@sa @ref write_cbor_value_or_push
|
||||
*/
|
||||
void write_bon8_value_or_push(const BasicJsonType& j, bool& string_open, std::vector<binary_container_frame>& stack)
|
||||
{
|
||||
@@ -4755,7 +4811,7 @@ class binary_writer
|
||||
}
|
||||
else
|
||||
{
|
||||
write_bytes(s.data(), s.size());
|
||||
oa.write_characters(reinterpret_cast<const CharType*>(s.data()), s.size());
|
||||
string_open = true;
|
||||
}
|
||||
}
|
||||
@@ -4846,31 +4902,6 @@ class binary_writer
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief the write-pass counterpart of @ref sanitize_utf8_for_write for BSON
|
||||
|
||||
The size pass (@ref calc_bson_entry_header_size, @ref
|
||||
calc_bson_string_size) has already validated @a s, and thrown for @ref
|
||||
error_handler_t::strict if it was ill-formed. Here, @ref
|
||||
error_handler_t::strict therefore behaves like @ref error_handler_t::keep,
|
||||
and @ref error_handler_t::replace / @ref error_handler_t::ignore produce
|
||||
exactly the bytes the size pass measured. Hence no context is needed.
|
||||
|
||||
@param[in] s the string (value or object key) to write
|
||||
@param[out] storage backing storage for a sanitized copy
|
||||
|
||||
@return a reference to @a s, or to @a storage once it holds a sanitized copy
|
||||
*/
|
||||
const string_t& sanitize_utf8_for_bson_write(const string_t& s, string_t& storage) const
|
||||
{
|
||||
if (error_handler == error_handler_t::keep || error_handler == error_handler_t::strict || is_valid_utf8(s))
|
||||
{
|
||||
return s; // NOLINT(bugprone-return-const-ref-from-parameter): callers pass lvalues that outlive the call
|
||||
}
|
||||
storage = sanitize_utf8(s, error_handler);
|
||||
return storage;
|
||||
}
|
||||
|
||||
/// deleted: anything but a string_t would bind a temporary that dies before the returned reference is used
|
||||
template < typename T, enable_if_t < !std::is_same<T, string_t>::value, int > = 0 >
|
||||
const string_t& sanitize_utf8_for_write(const T& /*s*/, const BasicJsonType& /*context*/, string_t& /*storage*/) const = delete; // NOLINT(hicpp-use-equals-delete,modernize-use-equals-delete): a private helper's guard, not part of the interface
|
||||
|
||||
@@ -966,28 +966,6 @@ class serializer
|
||||
}
|
||||
}
|
||||
|
||||
/// write out the bytes pending in @ref string_buffer and reset @a bytes
|
||||
JSON_HEDLEY_ALWAYS_INLINE void emit_pending(std::size_t& bytes)
|
||||
{
|
||||
if (bytes != 0)
|
||||
{
|
||||
put_buffer(string_buffer, bytes);
|
||||
bytes = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// write buffer and reset index if fewer than 13 bytes are left, as this
|
||||
/// is the maximal number of bytes to be written ("\uxxxx\uxxxx\0") for one
|
||||
/// code point
|
||||
JSON_HEDLEY_ALWAYS_INLINE void reserve_string_buffer(std::size_t& bytes)
|
||||
{
|
||||
if (string_buffer.size() - bytes < 13)
|
||||
{
|
||||
put_buffer(string_buffer, bytes);
|
||||
bytes = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief worker for @ref dump_escaped
|
||||
|
||||
@@ -1046,7 +1024,11 @@ class serializer
|
||||
{
|
||||
// emit any bytes still pending in string_buffer first to
|
||||
// preserve output order, then write the run directly
|
||||
emit_pending(bytes);
|
||||
if (bytes != 0)
|
||||
{
|
||||
put_buffer(string_buffer, bytes);
|
||||
bytes = 0;
|
||||
}
|
||||
put_run(s, i, run);
|
||||
bytes_after_last_accept = 0;
|
||||
undumped_chars = 0;
|
||||
@@ -1067,10 +1049,11 @@ class serializer
|
||||
{
|
||||
// emit any bytes still pending in string_buffer first to
|
||||
// preserve output order
|
||||
emit_pending(bytes);
|
||||
// deliberately not put_buffer(): a fixed 8-byte memcpy
|
||||
// has no asserts and no write-through branch, which
|
||||
// matters on the hot path
|
||||
if (bytes != 0)
|
||||
{
|
||||
put_buffer(string_buffer, bytes);
|
||||
bytes = 0;
|
||||
}
|
||||
const auto& escape = ascii_escapes()[data[i]];
|
||||
if (JSON_HEDLEY_UNLIKELY(write_buffer.size() - write_buffer_pos < escape.size()))
|
||||
{
|
||||
@@ -1145,7 +1128,14 @@ class serializer
|
||||
string_buffer[bytes++] = s[i];
|
||||
}
|
||||
|
||||
reserve_string_buffer(bytes);
|
||||
// write buffer and reset index; there must be 13 bytes
|
||||
// left, as this is the maximal number of bytes to be
|
||||
// written ("\uxxxx\uxxxx\0") for one code point
|
||||
if (string_buffer.size() - bytes < 13)
|
||||
{
|
||||
put_buffer(string_buffer, bytes);
|
||||
bytes = 0;
|
||||
}
|
||||
|
||||
// remember the byte position of this accept
|
||||
bytes_after_last_accept = bytes;
|
||||
@@ -1197,7 +1187,14 @@ class serializer
|
||||
string_buffer[bytes++] = '\xBD';
|
||||
}
|
||||
|
||||
reserve_string_buffer(bytes);
|
||||
// write buffer and reset index; there must be 13 bytes
|
||||
// left, as this is the maximal number of bytes to be
|
||||
// written ("\uxxxx\uxxxx\0") for one code point
|
||||
if (string_buffer.size() - bytes < 13)
|
||||
{
|
||||
put_buffer(string_buffer, bytes);
|
||||
bytes = 0;
|
||||
}
|
||||
|
||||
bytes_after_last_accept = bytes;
|
||||
}
|
||||
@@ -1230,7 +1227,14 @@ class serializer
|
||||
string_buffer[bytes++] = s[i];
|
||||
}
|
||||
|
||||
reserve_string_buffer(bytes);
|
||||
// write buffer and reset index; there must be 13 bytes
|
||||
// left, as this is the maximal number of bytes to be
|
||||
// written ("\uxxxx\uxxxx\0") for one code point
|
||||
if (string_buffer.size() - bytes < 13)
|
||||
{
|
||||
put_buffer(string_buffer, bytes);
|
||||
bytes = 0;
|
||||
}
|
||||
|
||||
bytes_after_last_accept = bytes;
|
||||
undumped_chars = 0;
|
||||
@@ -1596,17 +1600,30 @@ class serializer
|
||||
*/
|
||||
static const std::array<std::uint8_t, 256>& ascii_copyable() noexcept
|
||||
{
|
||||
// constant-initialized from the predicate: no runtime loop, no guard
|
||||
static constexpr std::array<std::uint8_t, 256> copyable = make_ascii_copyable(make_index_sequence<256> {});
|
||||
static const std::array<std::uint8_t, 256> copyable =
|
||||
{
|
||||
{
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x00
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x10
|
||||
1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x20
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x30
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x40
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 0x50
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 0x70
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x80
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x90
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xA0
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xB0
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xC0
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xD0
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xE0
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 0xF0
|
||||
}
|
||||
};
|
||||
return copyable;
|
||||
}
|
||||
|
||||
template<std::size_t... Is>
|
||||
static constexpr std::array<std::uint8_t, 256> make_ascii_copyable(index_sequence<Is...> /*unused*/) noexcept
|
||||
{
|
||||
return {{ static_cast<std::uint8_t>(is_ascii_copyable(static_cast<unsigned char>(Is)) ? 1 : 0)... }};
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief the JSON form of each character below 0x60
|
||||
|
||||
@@ -1735,6 +1752,7 @@ class serializer
|
||||
*/
|
||||
JSON_HEDLEY_NEVER_INLINE std::size_t write_u_escapes(const unsigned char* data, std::size_t n)
|
||||
{
|
||||
constexpr const char* nibble_to_hex = "0123456789abcdef";
|
||||
std::size_t i = 0;
|
||||
while (i < n && data[i] >= 0x80)
|
||||
{
|
||||
@@ -1771,21 +1789,26 @@ class serializer
|
||||
char* d = write_buffer.data() + write_buffer_pos;
|
||||
if (codepoint > 0xFFFFu)
|
||||
{
|
||||
d = put_u_escape(d, static_cast<std::uint16_t>(0xD7C0u + (codepoint >> 10u)));
|
||||
d = put_u_escape(d, static_cast<std::uint16_t>(0xD7C0u + (codepoint >> 10u)), nibble_to_hex);
|
||||
codepoint = 0xDC00u + (codepoint & 0x3FFu);
|
||||
}
|
||||
d = put_u_escape(d, static_cast<std::uint16_t>(codepoint));
|
||||
d = put_u_escape(d, static_cast<std::uint16_t>(codepoint), nibble_to_hex);
|
||||
write_buffer_pos = static_cast<std::size_t>(d - write_buffer.data());
|
||||
i += length;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
static char* put_u_escape(char* d, const std::uint16_t codeunit) noexcept
|
||||
static char* put_u_escape(char* d, const std::uint16_t codeunit, const char* nibble_to_hex) noexcept
|
||||
{
|
||||
const auto unit = static_cast<std::uint32_t>(codeunit);
|
||||
d[0] = '\\';
|
||||
d[1] = 'u';
|
||||
return hex4_lower(codeunit, d + 2);
|
||||
d[2] = nibble_to_hex[(unit >> 12u) & 0x0Fu];
|
||||
d[3] = nibble_to_hex[(unit >> 8u) & 0x0Fu];
|
||||
d[4] = nibble_to_hex[(unit >> 4u) & 0x0Fu];
|
||||
d[5] = nibble_to_hex[unit & 0x0Fu];
|
||||
return d + 6;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -1794,7 +1817,7 @@ class serializer
|
||||
* Branch-free replacement for `snprintf(buf, 7, "\\u%04x", codeunit)` in the
|
||||
* string escaping hot path. It writes exactly six characters ('\\', 'u' and
|
||||
* four hex digits) at position @a pos of @a string_buffer via a nibble
|
||||
* lookup table (@ref hex4_lower), avoiding the format-string parsing and locale machinery of
|
||||
* lookup table, avoiding the format-string parsing and locale machinery of
|
||||
* `snprintf`. Advances @a pos by the number of bytes written (6).
|
||||
*
|
||||
* @param[in] pos position in @a string_buffer to write at; there must
|
||||
@@ -1804,9 +1827,13 @@ class serializer
|
||||
void write_u_escape(std::size_t& pos, std::uint16_t codeunit) noexcept
|
||||
{
|
||||
JSON_ASSERT(string_buffer.size() - pos >= 6);
|
||||
constexpr const char* nibble_to_hex = "0123456789abcdef";
|
||||
string_buffer[pos + 0] = '\\';
|
||||
string_buffer[pos + 1] = 'u';
|
||||
hex4_lower(codeunit, &string_buffer[pos + 2]);
|
||||
string_buffer[pos + 2] = nibble_to_hex[(codeunit >> 12u) & 0x0Fu];
|
||||
string_buffer[pos + 3] = nibble_to_hex[(codeunit >> 8u) & 0x0Fu];
|
||||
string_buffer[pos + 4] = nibble_to_hex[(codeunit >> 4u) & 0x0Fu];
|
||||
string_buffer[pos + 5] = nibble_to_hex[codeunit & 0x0Fu];
|
||||
pos += 6;
|
||||
}
|
||||
|
||||
|
||||
@@ -48,24 +48,6 @@ inline std::string hex_byte(const std::uint8_t byte)
|
||||
return result;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief write a UTF-16 code unit as four lowercase hexadecimal digits
|
||||
|
||||
The lowercase counterpart of hex_byte(), used for the `\\uXXXX` escapes of the
|
||||
serializers, whose output is lowercase. Writes exactly four characters to
|
||||
@a out and returns the pointer past them.
|
||||
*/
|
||||
JSON_HEDLEY_ALWAYS_INLINE char* hex4_lower(const std::uint16_t codeunit, char* out) noexcept
|
||||
{
|
||||
constexpr const char* nibble_to_hex = "0123456789abcdef";
|
||||
const unsigned int unit = codeunit;
|
||||
out[0] = nibble_to_hex[(unit >> 12u) & 0x0Fu];
|
||||
out[1] = nibble_to_hex[(unit >> 8u) & 0x0Fu];
|
||||
out[2] = nibble_to_hex[(unit >> 4u) & 0x0Fu];
|
||||
out[3] = nibble_to_hex[unit & 0x0Fu];
|
||||
return out + 4;
|
||||
}
|
||||
|
||||
///////////////////
|
||||
// UTF-8 encoding //
|
||||
///////////////////
|
||||
@@ -121,66 +103,6 @@ void encode_utf8(std::uint32_t cp, const Out& out)
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief the shape of a well-formed UTF-8 sequence, by its lead byte
|
||||
|
||||
@a length is 1 for ASCII, 2 to 4 for a valid lead byte, and 0 for a byte that
|
||||
cannot start a well-formed sequence (a continuation byte, 0xC0, 0xC1, or 0xF5
|
||||
to 0xFF). For a multibyte sequence, the second byte must lie in
|
||||
[@a second_lo, @a second_hi], and every later byte in [0x80, 0xBF]
|
||||
(RFC 3629, section 4; Unicode Table 3-7). The narrower second-byte ranges
|
||||
exclude overlong forms (0xE0, 0xF0), surrogates (0xED), and code points
|
||||
beyond U+10FFFF (0xF4).
|
||||
*/
|
||||
struct utf8_sequence_shape
|
||||
{
|
||||
std::uint8_t length;
|
||||
std::uint8_t second_lo;
|
||||
std::uint8_t second_hi;
|
||||
};
|
||||
|
||||
/// @return the shape of a UTF-8 sequence starting with @a lead, see utf8_sequence_shape
|
||||
inline utf8_sequence_shape utf8_shape(const std::uint8_t lead) noexcept
|
||||
{
|
||||
if (lead < 0x80u)
|
||||
{
|
||||
return {1, 0, 0};
|
||||
}
|
||||
if (lead < 0xC2u)
|
||||
{
|
||||
return {0, 0, 0};
|
||||
}
|
||||
if (lead <= 0xDFu)
|
||||
{
|
||||
return {2, 0x80u, 0xBFu};
|
||||
}
|
||||
if (lead == 0xE0u)
|
||||
{
|
||||
return {3, 0xA0u, 0xBFu};
|
||||
}
|
||||
if (lead == 0xEDu)
|
||||
{
|
||||
return {3, 0x80u, 0x9Fu};
|
||||
}
|
||||
if (lead <= 0xEFu)
|
||||
{
|
||||
return {3, 0x80u, 0xBFu};
|
||||
}
|
||||
if (lead == 0xF0u)
|
||||
{
|
||||
return {4, 0x90u, 0xBFu};
|
||||
}
|
||||
if (lead <= 0xF3u)
|
||||
{
|
||||
return {4, 0x80u, 0xBFu};
|
||||
}
|
||||
if (lead == 0xF4u)
|
||||
{
|
||||
return {4, 0x80u, 0x8Fu};
|
||||
}
|
||||
return {0, 0, 0};
|
||||
}
|
||||
|
||||
///////////////////
|
||||
// UTF-8 decoding //
|
||||
///////////////////
|
||||
@@ -196,7 +118,7 @@ This is a single-byte step of a "shift-based" UTF-8 decoder originally
|
||||
written by Björn Hoehrmann. See
|
||||
http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.
|
||||
|
||||
The library checks UTF-8 well-formedness (RFC 3629, section 4) in five
|
||||
The library checks UTF-8 well-formedness (RFC 3629, section 4) in three
|
||||
places, which differ in speed, diagnostics, and how they read the input:
|
||||
|
||||
- decode() below: the serializer, to escape and, in strict mode, reject
|
||||
@@ -210,15 +132,10 @@ places, which differ in speed, diagnostics, and how they read the input:
|
||||
bulk string scan, the bulk path of the BON8 reader, and the BON8 writer.
|
||||
They must accept exactly what the lexer's switch accepts.
|
||||
- the byte path of binary_reader::get_bon8_string(): BON8 input without bulk
|
||||
access, and the bytes the bulk path leaves to it. It keeps its own ranges:
|
||||
taking them from utf8_shape() made BON8 decoding 2-3% slower (#5891).
|
||||
- check_utf8() in detail/view/edit.hpp: strings stored in an editable
|
||||
json_document, with the index of the offending byte. It also uses
|
||||
utf8_shape().
|
||||
access, and the bytes the bulk path leaves to it.
|
||||
|
||||
All five must accept the same set of sequences, which the test "UTF-8
|
||||
validators accept the same sequences" in unit-class_lexer.cpp checks
|
||||
exhaustively against decode() and utf8_shape().
|
||||
All four must accept the same set of sequences, so a change to one needs a
|
||||
matching change to the others.
|
||||
|
||||
@param[in,out] state the current decoder state
|
||||
@param[in,out] codep codepoint (valid only if resulting state is UTF8_ACCEPT)
|
||||
|
||||
@@ -177,35 +177,6 @@ inline bool owns_memory(const value_t t) noexcept
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief whether a value of the given type is neither an object nor an array
|
||||
|
||||
Unlike basic_json::is_primitive(), this includes value_t::discarded: it names
|
||||
the types for which iterators, JSON Pointer resolution, and the serializer take
|
||||
the "single value" path. Every enumerator is listed so that -Wswitch-enum flags
|
||||
this function when a new type is added.
|
||||
*/
|
||||
inline bool is_primitive(const value_t t) noexcept
|
||||
{
|
||||
switch (t)
|
||||
{
|
||||
case value_t::object:
|
||||
case value_t::array:
|
||||
return false;
|
||||
|
||||
case value_t::null:
|
||||
case value_t::string:
|
||||
case value_t::boolean:
|
||||
case value_t::number_integer:
|
||||
case value_t::number_unsigned:
|
||||
case value_t::number_float:
|
||||
case value_t::binary:
|
||||
case value_t::discarded:
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
@brief compare an integer with a floating point number without precision loss
|
||||
|
||||
@@ -51,6 +51,11 @@ struct first_true<false, Conditions...> : std::integral_constant < int, 1 + firs
|
||||
inline void check_utf8(const char* s, std::size_t n)
|
||||
{
|
||||
const auto* const p = reinterpret_cast<const unsigned char*>(s); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||
const auto hex = [](unsigned char c)
|
||||
{
|
||||
constexpr const char* digits = "0123456789ABCDEF";
|
||||
return std::string{digits[c >> 4u], digits[c & 0xFu]};
|
||||
};
|
||||
for (std::size_t i = 0; i < n;)
|
||||
{
|
||||
const unsigned char c = p[i];
|
||||
@@ -59,22 +64,39 @@ inline void check_utf8(const char* s, std::size_t n)
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
const utf8_sequence_shape shape = utf8_shape(c);
|
||||
const std::size_t len = shape.length;
|
||||
if (len == 0)
|
||||
std::size_t len = 0;
|
||||
unsigned char lo = 0x80;
|
||||
unsigned char hi = 0xBF;
|
||||
if (c >= 0xC2 && c <= 0xDF)
|
||||
{
|
||||
throw_type_error(exception_id::invalid_utf8, concat("invalid UTF-8 byte at index ", std::to_string(i), ": 0x", hex_byte(c)));
|
||||
len = 2;
|
||||
}
|
||||
else if (c >= 0xE0 && c <= 0xEF)
|
||||
{
|
||||
len = 3;
|
||||
lo = c == 0xE0 ? 0xA0 : 0x80;
|
||||
hi = c == 0xED ? 0x9F : 0xBF;
|
||||
}
|
||||
else if (c >= 0xF0 && c <= 0xF4)
|
||||
{
|
||||
len = 4;
|
||||
lo = c == 0xF0 ? 0x90 : 0x80;
|
||||
hi = c == 0xF4 ? 0x8F : 0xBF;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw_type_error(316, concat("invalid UTF-8 byte at index ", std::to_string(i), ": 0x", hex(c)));
|
||||
}
|
||||
for (std::size_t k = 1; k < len; ++k)
|
||||
{
|
||||
if (i + k == n)
|
||||
{
|
||||
throw_type_error(exception_id::invalid_utf8, concat("incomplete UTF-8 string; last byte: 0x", hex_byte(p[n - 1])));
|
||||
throw_type_error(316, concat("incomplete UTF-8 string; last byte: 0x", hex(p[n - 1])));
|
||||
}
|
||||
const unsigned char b = p[i + k];
|
||||
if (b < (k == 1 ? shape.second_lo : 0x80) || b > (k == 1 ? shape.second_hi : 0xBF))
|
||||
if (b < (k == 1 ? lo : 0x80) || b > (k == 1 ? hi : 0xBF))
|
||||
{
|
||||
throw_type_error(exception_id::invalid_utf8, concat("invalid UTF-8 byte at index ", std::to_string(i + k), ": 0x", hex_byte(b)));
|
||||
throw_type_error(316, concat("invalid UTF-8 byte at index ", std::to_string(i + k), ": 0x", hex(b)));
|
||||
}
|
||||
}
|
||||
i += len;
|
||||
@@ -122,7 +144,7 @@ class editor
|
||||
node* const o = own(object);
|
||||
if (o->kind != static_cast<std::uint8_t>(value_t::object) && o->kind != static_cast<std::uint8_t>(value_t::null))
|
||||
{
|
||||
throw_type_error(exception_id::subscript_wrong_type, "cannot use operator[] with a string argument with ", object.type_name());
|
||||
throw_type_error(305, "cannot use operator[] with a string argument with ", object.type_name());
|
||||
}
|
||||
check_utf8(key.data(), key.size());
|
||||
const encoded e = encode(std::forward<V>(value));
|
||||
@@ -173,7 +195,7 @@ class editor
|
||||
node* const a = own(array);
|
||||
if (a->kind != static_cast<std::uint8_t>(value_t::array))
|
||||
{
|
||||
throw_type_error(exception_id::subscript_wrong_type, "cannot use operator[] with a numeric argument with ", array.type_name());
|
||||
throw_type_error(305, "cannot use operator[] with a numeric argument with ", array.type_name());
|
||||
}
|
||||
check_index(idx, a->len);
|
||||
const encoded e = encode(std::forward<V>(value));
|
||||
@@ -190,7 +212,7 @@ class editor
|
||||
node* const a = own(array);
|
||||
if (a->kind != static_cast<std::uint8_t>(value_t::array) && a->kind != static_cast<std::uint8_t>(value_t::null))
|
||||
{
|
||||
throw_type_error(exception_id::push_back_wrong_type, "cannot use push_back() with ", array.type_name());
|
||||
throw_type_error(308, "cannot use push_back() with ", array.type_name());
|
||||
}
|
||||
const encoded e = encode(std::forward<V>(value));
|
||||
if (a->kind == static_cast<std::uint8_t>(value_t::null))
|
||||
@@ -214,7 +236,7 @@ class editor
|
||||
node* const a = own(array);
|
||||
if (a->kind != static_cast<std::uint8_t>(value_t::array))
|
||||
{
|
||||
throw_type_error(exception_id::insert_wrong_type, "cannot use insert() with ", array.type_name());
|
||||
throw_type_error(309, "cannot use insert() with ", array.type_name());
|
||||
}
|
||||
check_index(idx, a->len + 1);
|
||||
const encoded e = encode(std::forward<V>(value));
|
||||
@@ -234,7 +256,7 @@ class editor
|
||||
node* const o = own(object);
|
||||
if (o->kind != static_cast<std::uint8_t>(value_t::object))
|
||||
{
|
||||
throw_type_error(exception_id::erase_wrong_type, "cannot use erase() with ", object.type_name());
|
||||
throw_type_error(307, "cannot use erase() with ", object.type_name());
|
||||
}
|
||||
for (const node* k = nav::first(m_doc, o), *end = nav::end(m_doc, o); k != end; k = document_data::after(k + 1))
|
||||
{
|
||||
@@ -252,7 +274,7 @@ class editor
|
||||
node* const a = own(array);
|
||||
if (a->kind != static_cast<std::uint8_t>(value_t::array))
|
||||
{
|
||||
throw_type_error(exception_id::erase_wrong_type, "cannot use erase() with ", array.type_name());
|
||||
throw_type_error(307, "cannot use erase() with ", array.type_name());
|
||||
}
|
||||
check_index(idx, a->len);
|
||||
node* const h = block_of(m_doc, a, 0);
|
||||
@@ -275,7 +297,7 @@ class editor
|
||||
{
|
||||
if (NLOHMANN_VIEW_UNLIKELY(v.m_doc != &m_doc || v.m_node == nullptr))
|
||||
{
|
||||
throw_invalid_iterator(exception_id::iterator_from_other_value, "view does not belong to this document");
|
||||
throw_invalid_iterator(202, "view does not belong to this document");
|
||||
}
|
||||
edit_state_of(m_doc);
|
||||
return const_cast<node*>(v.m_node); // NOLINT(cppcoreguidelines-pro-type-const-cast): the nodes belong to this document
|
||||
@@ -285,7 +307,7 @@ class editor
|
||||
{
|
||||
if (idx >= limit)
|
||||
{
|
||||
throw_array_index_out_of_range(idx);
|
||||
throw_out_of_range(401, concat("array index ", std::to_string(idx), " is out of range"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -426,7 +448,7 @@ class editor
|
||||
{
|
||||
if (NLOHMANN_VIEW_UNLIKELY(v.m_node == nullptr))
|
||||
{
|
||||
throw_type_error(exception_id::type_mismatch, "type must be a value, but is ", "discarded");
|
||||
throw_type_error(302, "type must be a value, but is ", "discarded");
|
||||
}
|
||||
encoded r;
|
||||
if (!is_container(*v.m_node))
|
||||
@@ -576,7 +598,7 @@ class editor
|
||||
{
|
||||
if (NLOHMANN_VIEW_UNLIKELY(len >= 0xFFFFFFFFu))
|
||||
{
|
||||
throw_out_of_range(exception_id::document_too_large, "strings of 4 GiB or more are not supported by json_document"); // LCOV_EXCL_LINE
|
||||
throw_out_of_range(416, "strings of 4 GiB or more are not supported by json_document"); // LCOV_EXCL_LINE
|
||||
}
|
||||
node n = plain_node(value_t::string);
|
||||
n.flags = static_cast<std::uint8_t>(n.flags | node_flags::edited);
|
||||
@@ -651,12 +673,12 @@ class editor
|
||||
return string_node(s.data(), s.size());
|
||||
}
|
||||
case value_t::binary:
|
||||
throw_type_error(exception_id::document_binary_value, "cannot store a binary value in a json_document", "");
|
||||
throw_type_error(319, "cannot store a binary value in a json_document", "");
|
||||
case value_t::discarded:
|
||||
case value_t::object:
|
||||
case value_t::array:
|
||||
default:
|
||||
throw_type_error(exception_id::type_mismatch, "type must be a value, but is ", "discarded");
|
||||
throw_type_error(302, "type must be a value, but is ", "discarded");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ inline std::size_t text_capacity(std::size_t cap, std::size_t used, std::size_t
|
||||
constexpr std::size_t limit = 0xFFFFFFFFu;
|
||||
if (NLOHMANN_VIEW_UNLIKELY(used > limit || n > limit - used))
|
||||
{
|
||||
throw_out_of_range(exception_id::document_too_large, "edits of 4 GiB or more are not supported by json_document");
|
||||
throw_out_of_range(416, "edits of 4 GiB or more are not supported by json_document");
|
||||
}
|
||||
const std::size_t needed = used + n;
|
||||
const std::size_t wanted = needed + (std::min)(limit - needed, std::size_t{256});
|
||||
@@ -135,7 +135,7 @@ inline void reserve_moved(document_data& d)
|
||||
document_data::edit_state& e = edit_state_of(d);
|
||||
if (e.moved.size() >= 0xFFFFFFFFu)
|
||||
{
|
||||
throw_out_of_range(exception_id::document_too_large, "more than 4294967295 edited arrays and objects are not supported by json_document"); // LCOV_EXCL_LINE
|
||||
throw_out_of_range(416, "more than 4294967295 edited arrays and objects are not supported by json_document"); // LCOV_EXCL_LINE
|
||||
}
|
||||
if (e.moved.size() == e.moved.capacity() || e.moved_cap.size() == e.moved_cap.capacity())
|
||||
{
|
||||
|
||||
@@ -25,52 +25,32 @@ namespace view
|
||||
// Exceptions are thrown out of line, so that the accessors that may throw stay
|
||||
// small enough to be inlined.
|
||||
|
||||
[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_type_error(exception_id id, const char* prefix, const char* type)
|
||||
[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_type_error(int id, const char* prefix, const char* type)
|
||||
{
|
||||
NLOHMANN_VIEW_THROW(type_error::create(id, concat(prefix, type), nullptr));
|
||||
}
|
||||
|
||||
[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_type_error(exception_id id, const std::string& msg)
|
||||
[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_type_error(int id, const std::string& msg)
|
||||
{
|
||||
NLOHMANN_VIEW_THROW(type_error::create(id, msg, nullptr));
|
||||
}
|
||||
|
||||
[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_out_of_range(exception_id id, const std::string& msg)
|
||||
[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_out_of_range(int id, const std::string& msg)
|
||||
{
|
||||
NLOHMANN_VIEW_THROW(out_of_range::create(id, msg, nullptr));
|
||||
}
|
||||
|
||||
[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_invalid_iterator(exception_id id, const char* msg)
|
||||
[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_invalid_iterator(int id, const char* msg)
|
||||
{
|
||||
NLOHMANN_VIEW_THROW(invalid_iterator::create(id, msg, nullptr));
|
||||
}
|
||||
|
||||
/// a parse error without a position (as those of json_pointer)
|
||||
[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_parse_error(exception_id id, const std::string& msg)
|
||||
[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_parse_error(int id, const std::string& msg)
|
||||
{
|
||||
NLOHMANN_VIEW_THROW(parse_error::create(id, 0, msg, nullptr));
|
||||
}
|
||||
|
||||
/// out_of_range.401: the array index @a idx is not below the size
|
||||
template<typename Index>
|
||||
[[noreturn]] NLOHMANN_VIEW_NOINLINE void throw_array_index_out_of_range(const Index idx)
|
||||
{
|
||||
throw_out_of_range(exception_id::array_index_out_of_range, concat("array index ", std::to_string(idx), " is out of range"));
|
||||
}
|
||||
|
||||
/// out_of_range.402: the JSON Pointer token '-' of an array of @a size elements
|
||||
[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_array_index_past_end(std::size_t size)
|
||||
{
|
||||
throw_out_of_range(exception_id::pointer_past_the_end_index, concat("array index '-' (", std::to_string(size), ") is out of range"));
|
||||
}
|
||||
|
||||
/// out_of_range.403: the object has no member @a key
|
||||
template<typename Key>
|
||||
[[noreturn]] NLOHMANN_VIEW_NOINLINE void throw_key_not_found(const Key& key)
|
||||
{
|
||||
throw_out_of_range(exception_id::key_not_found, concat("key '", key, "' not found"));
|
||||
}
|
||||
|
||||
/// SAX handler of the error replay: accepts every event without storing it,
|
||||
/// and throws the exception the parser reports
|
||||
template<typename BasicJsonType>
|
||||
@@ -169,7 +149,7 @@ template<typename BasicJsonType>
|
||||
if (f.code == error_code::input_too_large)
|
||||
{
|
||||
// (the limit is detail::view::max_input_size(): 4 GiB minus 16 bytes)
|
||||
NLOHMANN_VIEW_THROW(out_of_range::create(exception_id::document_too_large, "input of 4294967280 bytes or more is not supported by json_document", nullptr));
|
||||
NLOHMANN_VIEW_THROW(out_of_range::create(416, "input of 4294967280 bytes or more is not supported by json_document", nullptr));
|
||||
}
|
||||
throwing_sax<BasicJsonType> sax;
|
||||
const bool accepted = BasicJsonType::sax_parse(src, src + size, &sax, input_format_t::json, true, ignore_comments, ignore_trailing_commas);
|
||||
@@ -189,7 +169,7 @@ template<typename BasicJsonType>
|
||||
}
|
||||
}
|
||||
pos.chars_read_current_line = off + 1 - line_start;
|
||||
NLOHMANN_VIEW_THROW(parse_error::create(exception_id::syntax_error, pos, "syntax error while parsing value", nullptr));
|
||||
NLOHMANN_VIEW_THROW(parse_error::create(101, pos, "syntax error while parsing value", nullptr));
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
|
||||
@@ -186,7 +186,7 @@ inline void compact_nodes(const document_data& d, std::size_t arena_size, std::v
|
||||
inline std::vector<std::uint8_t> save_image(const document_data& d)
|
||||
{
|
||||
#if !NLOHMANN_VIEW_LITTLE_ENDIAN
|
||||
throw_type_error(exception_id::document_image_unsupported, "json_document images need a little-endian target"); // LCOV_EXCL_LINE
|
||||
throw_type_error(320, "json_document images need a little-endian target"); // LCOV_EXCL_LINE
|
||||
#endif
|
||||
const std::size_t arena_size = d.arena_size;
|
||||
std::vector<node> compacted;
|
||||
@@ -203,7 +203,7 @@ inline std::vector<std::uint8_t> save_image(const document_data& d)
|
||||
if (NLOHMANN_VIEW_UNLIKELY(text_size >= image_limit || total_arena >= image_limit || count >= image_limit))
|
||||
{
|
||||
// LCOV_EXCL_START (4 GiB)
|
||||
throw_out_of_range(exception_id::document_too_large, "images of 4 GiB or more are not supported by json_document");
|
||||
throw_out_of_range(416, "images of 4 GiB or more are not supported by json_document");
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
image_header h{};
|
||||
@@ -660,7 +660,7 @@ inline bool check_image(const node* nodes, std::size_t count, const unsigned cha
|
||||
|
||||
[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_invalid_image(const char* what)
|
||||
{
|
||||
throw_parse_error(exception_id::invalid_document_image, concat("invalid json_document image: ", what));
|
||||
throw_parse_error(116, concat("invalid json_document image: ", what));
|
||||
}
|
||||
|
||||
/// Read an image into d. The text and the decoded strings stay in the image;
|
||||
@@ -668,7 +668,7 @@ inline bool check_image(const node* nodes, std::size_t count, const unsigned cha
|
||||
inline void load_image(document_data& d, const std::uint8_t* image, std::size_t size, image_check check)
|
||||
{
|
||||
#if !NLOHMANN_VIEW_LITTLE_ENDIAN
|
||||
throw_type_error(exception_id::document_image_unsupported, "json_document images need a little-endian target"); // LCOV_EXCL_LINE
|
||||
throw_type_error(320, "json_document images need a little-endian target"); // LCOV_EXCL_LINE
|
||||
#endif
|
||||
if (image == nullptr || size < sizeof(image_header))
|
||||
{
|
||||
|
||||
@@ -110,7 +110,7 @@ class view_iterator
|
||||
{
|
||||
if (NLOHMANN_VIEW_UNLIKELY(m_value_offset == 0))
|
||||
{
|
||||
throw_invalid_iterator(exception_id::iterator_key_not_object, "cannot use key() for non-object iterators");
|
||||
throw_invalid_iterator(207, "cannot use key() for non-object iterators");
|
||||
}
|
||||
return string_view_t(m_doc->str(*m_pos), m_pos->len);
|
||||
}
|
||||
|
||||
@@ -32,15 +32,51 @@ enum class pointer_mode
|
||||
contains, ///< as basic_json::contains(): no exceptions at all
|
||||
};
|
||||
|
||||
/// the outcome of reading an array index: detail::parse_array_index(), shared with json_pointer
|
||||
using index_status = detail::array_index_status;
|
||||
/// the outcome of reading an array index from a reference token
|
||||
enum class index_status
|
||||
{
|
||||
ok,
|
||||
leading_zero, ///< parse_error.106
|
||||
not_number, ///< parse_error.109
|
||||
unresolved, ///< out_of_range.404
|
||||
too_large, ///< out_of_range.410
|
||||
};
|
||||
|
||||
/// reads an array index like json_pointer::array_index (RFC 6901, Sect. 4),
|
||||
/// but reports errors instead of throwing them
|
||||
template<typename StringType>
|
||||
index_status array_index(const StringType& s, std::size_t& idx) noexcept
|
||||
{
|
||||
return detail::parse_array_index(s, idx);
|
||||
// as basic_json: the empty token and "-" (past the last element) are
|
||||
// unresolved rather than not a number
|
||||
if (s.empty() || (s.size() == 1 && s[0] == '-'))
|
||||
{
|
||||
return index_status::unresolved;
|
||||
}
|
||||
if (s[0] < '0' || s[0] > '9')
|
||||
{
|
||||
return index_status::not_number;
|
||||
}
|
||||
if (s.size() > 1 && s[0] == '0')
|
||||
{
|
||||
return index_status::leading_zero;
|
||||
}
|
||||
std::uint64_t v = 0;
|
||||
for (const auto c : s)
|
||||
{
|
||||
const auto d = static_cast<unsigned>(static_cast<unsigned char>(c)) - '0';
|
||||
if (d > 9 || v > ((std::numeric_limits<std::uint64_t>::max)() - d) / 10)
|
||||
{
|
||||
return index_status::unresolved; // not a number, or beyond unsigned long long
|
||||
}
|
||||
v = (v * 10) + d;
|
||||
}
|
||||
if (v >= (std::numeric_limits<std::size_t>::max)()) // (std::size_t converts to std::uint64_t implicitly)
|
||||
{
|
||||
return index_status::too_large;
|
||||
}
|
||||
idx = static_cast<std::size_t>(v);
|
||||
return index_status::ok;
|
||||
}
|
||||
|
||||
/// throws the exception json_pointer::array_index throws for this status
|
||||
@@ -50,15 +86,15 @@ template<typename StringType>
|
||||
switch (status)
|
||||
{
|
||||
case index_status::leading_zero:
|
||||
throw_parse_error(exception_id::pointer_index_leading_zero, concat("array index '", s, "' must not begin with '0'"));
|
||||
case index_status::not_a_number:
|
||||
throw_parse_error(exception_id::pointer_index_not_a_number, concat("array index '", s, "' is not a number"));
|
||||
case index_status::exceeds_size_type:
|
||||
throw_out_of_range(exception_id::value_out_of_range, concat("array index ", s, " exceeds size_type")); // LCOV_EXCL_LINE
|
||||
throw_parse_error(106, concat("array index '", s, "' must not begin with '0'"));
|
||||
case index_status::not_number:
|
||||
throw_parse_error(109, concat("array index '", s, "' is not a number"));
|
||||
case index_status::too_large:
|
||||
throw_out_of_range(410, concat("array index ", s, " exceeds size_type")); // LCOV_EXCL_LINE
|
||||
case index_status::unresolved:
|
||||
case index_status::ok:
|
||||
default:
|
||||
throw_out_of_range(exception_id::pointer_unresolved, concat("unresolved reference token '", s, "'"));
|
||||
throw_out_of_range(404, concat("unresolved reference token '", s, "'"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,7 +119,7 @@ View resolve_pointer(View cur, const Tokens& tokens, pointer_mode mode)
|
||||
{
|
||||
if (mode == pointer_mode::checked)
|
||||
{
|
||||
throw_key_not_found(token);
|
||||
throw_out_of_range(403, concat("key '", token, "' not found"));
|
||||
}
|
||||
return View();
|
||||
}
|
||||
@@ -95,7 +131,7 @@ View resolve_pointer(View cur, const Tokens& tokens, pointer_mode mode)
|
||||
{
|
||||
if (throwing)
|
||||
{
|
||||
throw_array_index_past_end(cur.size());
|
||||
throw_out_of_range(402, concat("array index '-' (", std::to_string(cur.size()), ") is out of range"));
|
||||
}
|
||||
return View();
|
||||
}
|
||||
@@ -103,7 +139,7 @@ View resolve_pointer(View cur, const Tokens& tokens, pointer_mode mode)
|
||||
const index_status status = array_index(token, idx);
|
||||
if (status != index_status::ok)
|
||||
{
|
||||
const bool parse_error = status == index_status::leading_zero || status == index_status::not_a_number;
|
||||
const bool parse_error = status == index_status::leading_zero || status == index_status::not_number;
|
||||
if (throwing || (mode == pointer_mode::value && parse_error))
|
||||
{
|
||||
throw_array_index_error(status, token);
|
||||
@@ -114,7 +150,7 @@ View resolve_pointer(View cur, const Tokens& tokens, pointer_mode mode)
|
||||
{
|
||||
if (mode == pointer_mode::checked)
|
||||
{
|
||||
throw_array_index_out_of_range(idx);
|
||||
throw_out_of_range(401, concat("array index ", std::to_string(idx), " is out of range"));
|
||||
}
|
||||
return View();
|
||||
}
|
||||
@@ -124,7 +160,7 @@ View resolve_pointer(View cur, const Tokens& tokens, pointer_mode mode)
|
||||
{
|
||||
if (throwing)
|
||||
{
|
||||
throw_out_of_range(exception_id::pointer_unresolved, concat("unresolved reference token '", token, "'"));
|
||||
throw_out_of_range(404, concat("unresolved reference token '", token, "'"));
|
||||
}
|
||||
return View();
|
||||
}
|
||||
|
||||
@@ -890,17 +890,21 @@ class view_serializer
|
||||
m_out.put("null");
|
||||
return;
|
||||
}
|
||||
write_float(x, std::integral_constant<bool, ::nlohmann::detail::has_native_float_format<number_float_t>::value>());
|
||||
static constexpr bool is_ieee_single_or_double
|
||||
= std::numeric_limits<number_float_t>::is_iec559
|
||||
&& ((std::numeric_limits<number_float_t>::digits == 24 && std::numeric_limits<number_float_t>::max_exponent == 128)
|
||||
|| (std::numeric_limits<number_float_t>::digits == 53 && std::numeric_limits<number_float_t>::max_exponent == 1024));
|
||||
write_float(x, std::integral_constant<bool, is_ieee_single_or_double>());
|
||||
}
|
||||
|
||||
void write_float(number_float_t x, std::true_type /*has_native_float_format*/)
|
||||
void write_float(number_float_t x, std::true_type /*is_ieee_single_or_double*/)
|
||||
{
|
||||
std::array<char, 64> buf{};
|
||||
const char* const end = ::nlohmann::detail::to_chars(buf.data(), buf.data() + buf.size(), x);
|
||||
m_out.put(buf.data(), static_cast<std::size_t>(end - buf.data()));
|
||||
}
|
||||
|
||||
void write_float(number_float_t x, std::false_type /*has_native_float_format*/)
|
||||
void write_float(number_float_t x, std::false_type /*is_ieee_single_or_double*/)
|
||||
{
|
||||
// other types (e.g. long double) are rare: the library writes them
|
||||
const string_t s = BasicJsonType(x).dump();
|
||||
@@ -1027,8 +1031,8 @@ class view_serializer
|
||||
|
||||
void write_u_escape(std::uint32_t u)
|
||||
{
|
||||
std::array<char, 6> e = {{'\\', 'u', 0, 0, 0, 0}};
|
||||
hex4_lower(static_cast<std::uint16_t>(u), &e[2]);
|
||||
static constexpr const char* hex = "0123456789abcdef";
|
||||
const std::array<char, 6> e = {{'\\', 'u', hex[(u >> 12u) & 0xFu], hex[(u >> 8u) & 0xFu], hex[(u >> 4u) & 0xFu], hex[u & 0xFu]}};
|
||||
m_out.put(e.data(), e.size());
|
||||
}
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ NLOHMANN_VIEW_ALWAYS_INLINE T arithmetic_value(const document_data& d, const nod
|
||||
case value_t::binary:
|
||||
case value_t::discarded:
|
||||
default:
|
||||
throw_type_error(exception_id::type_mismatch, "type must be number, but is ", value_type_name(static_cast<value_t>(n.kind)));
|
||||
throw_type_error(302, "type must be number, but is ", value_type_name(static_cast<value_t>(n.kind)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ std::vector<U, A> vector_value(const View& v)
|
||||
{
|
||||
if (NLOHMANN_VIEW_UNLIKELY(!v.is_array()))
|
||||
{
|
||||
throw_type_error(exception_id::type_mismatch, "type must be array, but is ", v.type_name());
|
||||
throw_type_error(302, "type must be array, but is ", v.type_name());
|
||||
}
|
||||
std::vector<U, A> r;
|
||||
r.reserve(v.size());
|
||||
@@ -88,7 +88,7 @@ Map map_value(const View& v)
|
||||
{
|
||||
if (NLOHMANN_VIEW_UNLIKELY(!v.is_object()))
|
||||
{
|
||||
throw_type_error(exception_id::type_mismatch, "type must be object, but is ", v.type_name());
|
||||
throw_type_error(302, "type must be object, but is ", v.type_name());
|
||||
}
|
||||
Map r;
|
||||
for (auto it = v.begin(); it != v.end(); ++it)
|
||||
|
||||
+353
-302
@@ -374,9 +374,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
result["name"] = "JSON for Modern C++";
|
||||
result["url"] = "https://github.com/nlohmann/json";
|
||||
result["version"]["string"] =
|
||||
detail::concat<string_t>(detail::to_string<string_t>(static_cast<std::size_t>(NLOHMANN_JSON_VERSION_MAJOR)), '.',
|
||||
detail::to_string<string_t>(static_cast<std::size_t>(NLOHMANN_JSON_VERSION_MINOR)), '.',
|
||||
detail::to_string<string_t>(static_cast<std::size_t>(NLOHMANN_JSON_VERSION_PATCH)));
|
||||
detail::concat(std::to_string(NLOHMANN_JSON_VERSION_MAJOR), '.',
|
||||
std::to_string(NLOHMANN_JSON_VERSION_MINOR), '.',
|
||||
std::to_string(NLOHMANN_JSON_VERSION_PATCH));
|
||||
result["version"]["major"] = NLOHMANN_JSON_VERSION_MAJOR;
|
||||
result["version"]["minor"] = NLOHMANN_JSON_VERSION_MINOR;
|
||||
result["version"]["patch"] = NLOHMANN_JSON_VERSION_PATCH;
|
||||
@@ -398,10 +398,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
#elif defined(__clang__)
|
||||
result["compiler"] = {{"family", "clang"}, {"version", __clang_version__}};
|
||||
#elif defined(__GNUC__) || defined(__GNUG__)
|
||||
result["compiler"] = {{"family", "gcc"}, {"version", detail::concat<string_t>(
|
||||
detail::to_string<string_t>(static_cast<std::size_t>(__GNUC__)), '.',
|
||||
detail::to_string<string_t>(static_cast<std::size_t>(__GNUC_MINOR__)), '.',
|
||||
detail::to_string<string_t>(static_cast<std::size_t>(__GNUC_PATCHLEVEL__)))
|
||||
result["compiler"] = {{"family", "gcc"}, {"version", detail::concat(
|
||||
std::to_string(__GNUC__), '.',
|
||||
std::to_string(__GNUC_MINOR__), '.',
|
||||
std::to_string(__GNUC_PATCHLEVEL__))
|
||||
}
|
||||
};
|
||||
#elif defined(__HP_aCC)
|
||||
@@ -419,9 +419,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
#endif
|
||||
|
||||
#if defined(_MSVC_LANG)
|
||||
result["compiler"]["c++"] = detail::to_string<string_t>(static_cast<std::size_t>(_MSVC_LANG));
|
||||
result["compiler"]["c++"] = std::to_string(_MSVC_LANG);
|
||||
#elif defined(__cplusplus)
|
||||
result["compiler"]["c++"] = detail::to_string<string_t>(static_cast<std::size_t>(__cplusplus));
|
||||
result["compiler"]["c++"] = std::to_string(__cplusplus);
|
||||
#else
|
||||
result["compiler"]["c++"] = "unknown";
|
||||
#endif
|
||||
@@ -732,15 +732,23 @@ private:
|
||||
// true if v is not an array/object, or is an already-empty one
|
||||
static bool has_no_children(const basic_json& v) noexcept
|
||||
{
|
||||
if (detail::is_primitive(v.m_data.m_type))
|
||||
switch (v.m_data.m_type)
|
||||
{
|
||||
return true;
|
||||
case value_t::array:
|
||||
return v.m_data.m_value.array->empty();
|
||||
case value_t::object:
|
||||
return v.m_data.m_value.object->empty();
|
||||
case value_t::null:
|
||||
case value_t::string:
|
||||
case value_t::boolean:
|
||||
case value_t::number_integer:
|
||||
case value_t::number_unsigned:
|
||||
case value_t::number_float:
|
||||
case value_t::binary:
|
||||
case value_t::discarded:
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
if (v.m_data.m_type == value_t::array)
|
||||
{
|
||||
return v.m_data.m_value.array->empty();
|
||||
}
|
||||
return v.m_data.m_value.object->empty();
|
||||
}
|
||||
|
||||
// true if no child of the non-empty array/object v has children of
|
||||
@@ -845,17 +853,28 @@ private:
|
||||
}
|
||||
|
||||
public:
|
||||
template<typename T>
|
||||
static void destroy_pointer(T*& ptr) noexcept
|
||||
void destroy_string() noexcept
|
||||
{
|
||||
if (ptr == nullptr)
|
||||
if (string == nullptr)
|
||||
{
|
||||
// not initialized (e.g., due to exception in the ctor)
|
||||
return;
|
||||
}
|
||||
AllocatorType<T> alloc;
|
||||
std::allocator_traits<decltype(alloc)>::destroy(alloc, ptr);
|
||||
std::allocator_traits<decltype(alloc)>::deallocate(alloc, ptr, 1);
|
||||
AllocatorType<string_t> alloc;
|
||||
std::allocator_traits<decltype(alloc)>::destroy(alloc, string);
|
||||
std::allocator_traits<decltype(alloc)>::deallocate(alloc, string, 1);
|
||||
}
|
||||
|
||||
void destroy_binary() noexcept
|
||||
{
|
||||
if (binary == nullptr)
|
||||
{
|
||||
// not initialized (e.g., due to exception in the ctor)
|
||||
return;
|
||||
}
|
||||
AllocatorType<binary_t> alloc;
|
||||
std::allocator_traits<decltype(alloc)>::destroy(alloc, binary);
|
||||
std::allocator_traits<decltype(alloc)>::deallocate(alloc, binary, 1);
|
||||
}
|
||||
|
||||
// t must be value_t::array or value_t::object
|
||||
@@ -1021,11 +1040,11 @@ public:
|
||||
switch (t)
|
||||
{
|
||||
case value_t::string:
|
||||
destroy_pointer(string);
|
||||
destroy_string();
|
||||
break;
|
||||
|
||||
case value_t::binary:
|
||||
destroy_pointer(binary);
|
||||
destroy_binary();
|
||||
break;
|
||||
|
||||
case value_t::object:
|
||||
@@ -1087,24 +1106,36 @@ public:
|
||||
void set_parents()
|
||||
{
|
||||
#if JSON_DIAGNOSTICS
|
||||
if (detail::is_primitive(m_data.m_type))
|
||||
switch (m_data.m_type)
|
||||
{
|
||||
return;
|
||||
}
|
||||
case value_t::array:
|
||||
{
|
||||
for (auto& element : *m_data.m_value.array)
|
||||
{
|
||||
element.m_parent = this;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_data.m_type == value_t::array)
|
||||
{
|
||||
for (auto& element : *m_data.m_value.array)
|
||||
case value_t::object:
|
||||
{
|
||||
element.m_parent = this;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto& element : *m_data.m_value.object)
|
||||
{
|
||||
element.second.m_parent = this;
|
||||
for (auto& element : *m_data.m_value.object)
|
||||
{
|
||||
element.second.m_parent = this;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case value_t::null:
|
||||
case value_t::string:
|
||||
case value_t::boolean:
|
||||
case value_t::number_integer:
|
||||
case value_t::number_unsigned:
|
||||
case value_t::number_float:
|
||||
case value_t::binary:
|
||||
case value_t::discarded:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -2552,7 +2583,8 @@ public:
|
||||
case value_t::string:
|
||||
case value_t::binary:
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!primitive_range_is_complete(first, last)))
|
||||
if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin()
|
||||
|| !last.m_it.primitive_iterator.is_end()))
|
||||
{
|
||||
detail::throw_literal<invalid_iterator>(detail::exception_id::iterator_range_out_of_range, "iterators out of range", first.m_object);
|
||||
}
|
||||
@@ -3026,129 +3058,6 @@ public:
|
||||
JSON_THROW(std::length_error(detail::concat("array index ", std::to_string(idx), " exceeds size_type")));
|
||||
}
|
||||
|
||||
/// @throw type_error.309 always, as insert() is not defined for the type of @a j
|
||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
||||
static void throw_insert_wrong_type(const basic_json& j)
|
||||
{
|
||||
detail::throw_cannot_use_with(detail::exception_id::insert_wrong_type, "insert()", j);
|
||||
}
|
||||
|
||||
/// @throw invalid_iterator.202 always, as an iterator belongs to another value than @a obj
|
||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
||||
static void throw_iterator_from_other_value(const basic_json* obj)
|
||||
{
|
||||
detail::throw_literal<invalid_iterator>(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", obj);
|
||||
}
|
||||
|
||||
/// @throw type_error.310 always, as swap() with the given operand is not defined for the type of @a j
|
||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
||||
static void throw_swap_wrong_type(const char* operation, const basic_json& j)
|
||||
{
|
||||
detail::throw_cannot_use_with(detail::exception_id::swap_wrong_type, operation, j);
|
||||
}
|
||||
|
||||
/// exchange the stored @a Stored value (if the value has that type) with @a other
|
||||
template<typename Stored, typename Other>
|
||||
void swap_impl(Other& other, const char* operation)
|
||||
{
|
||||
Stored* const stored = get_impl_ptr(static_cast<Stored*>(nullptr));
|
||||
if (JSON_HEDLEY_UNLIKELY(stored == nullptr))
|
||||
{
|
||||
throw_swap_wrong_type(operation, *this);
|
||||
}
|
||||
|
||||
using std::swap;
|
||||
swap(*stored, other);
|
||||
}
|
||||
|
||||
/// whether [first, last) spans the whole of a primitive value
|
||||
template<typename IteratorType>
|
||||
JSON_HEDLEY_ALWAYS_INLINE
|
||||
static bool primitive_range_is_complete(const IteratorType& first, const IteratorType& last) noexcept
|
||||
{
|
||||
return first.m_it.primitive_iterator.is_begin() && last.m_it.primitive_iterator.is_end();
|
||||
}
|
||||
|
||||
/// destroy the current (primitive) value and become null
|
||||
void reset_to_null_impl()
|
||||
{
|
||||
m_data.m_value.destroy(m_data.m_type);
|
||||
m_data.m_value = {};
|
||||
m_data.m_type = value_t::null;
|
||||
assert_invariant();
|
||||
}
|
||||
|
||||
/// append @a val (moved or copied) to the array; a null value becomes an array
|
||||
template<typename ValueType>
|
||||
JSON_HEDLEY_ALWAYS_INLINE
|
||||
void push_back_array_impl(ValueType&& val)
|
||||
{
|
||||
// push_back only works for null objects or arrays
|
||||
if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))
|
||||
{
|
||||
detail::throw_cannot_use_with(detail::exception_id::push_back_wrong_type, "push_back()", *this);
|
||||
}
|
||||
|
||||
// transform a null object into an array
|
||||
if (is_null())
|
||||
{
|
||||
convert_null_to<array_t>();
|
||||
}
|
||||
|
||||
// add the element to the array
|
||||
const auto old_capacity = container_capacity();
|
||||
m_data.m_value.array->push_back(std::forward<ValueType>(val));
|
||||
set_parent_after_insert(m_data.m_value.array->back(), old_capacity);
|
||||
}
|
||||
|
||||
/// parse the input behind an already created adapter
|
||||
template<typename InputAdapterType>
|
||||
static basic_json parse_impl(InputAdapterType&& adapter,
|
||||
parser_callback_t cb,
|
||||
const bool allow_exceptions,
|
||||
const bool ignore_comments,
|
||||
const bool ignore_trailing_commas)
|
||||
{
|
||||
basic_json result;
|
||||
auto p = parser(std::forward<InputAdapterType>(adapter), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas);
|
||||
p.parse(true, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// check the input behind an already created adapter
|
||||
template<typename InputAdapterType>
|
||||
static bool accept_impl(InputAdapterType&& adapter,
|
||||
const bool ignore_comments,
|
||||
const bool ignore_trailing_commas)
|
||||
{
|
||||
return parser(std::forward<InputAdapterType>(adapter), nullptr, false, ignore_comments, ignore_trailing_commas, true).accept(true);
|
||||
}
|
||||
|
||||
/// generate SAX events from the input behind an already created adapter
|
||||
template<typename InputAdapterType, typename SAX>
|
||||
static bool sax_parse_impl(InputAdapterType ia, SAX* sax,
|
||||
const input_format_t format,
|
||||
const bool strict,
|
||||
const bool ignore_comments,
|
||||
const bool ignore_trailing_commas,
|
||||
const cbor_tag_handler_t tag_handler)
|
||||
{
|
||||
return format == input_format_t::json
|
||||
? parser(std::move(ia), nullptr, true, ignore_comments, ignore_trailing_commas).sax_parse(sax, strict)
|
||||
: detail::binary_reader<basic_json, InputAdapterType, SAX>(std::move(ia), format).sax_parse(sax, strict, tag_handler);
|
||||
}
|
||||
|
||||
/// serialize @a j into a new vector; @a write receives the vector-backed writer
|
||||
template<typename WriteFunction>
|
||||
static std::vector<std::uint8_t> to_binary_vector(const basic_json& j, const error_handler_t error_handler, WriteFunction write)
|
||||
{
|
||||
std::vector<std::uint8_t> result;
|
||||
result.reserve(detail::binary_initial_capacity(j));
|
||||
auto writer = vector_writer(result, error_handler);
|
||||
write(writer);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// @throw out_of_range.401 always, as @a idx is out of range of @a j
|
||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
||||
static void throw_array_index_out_of_range(size_type idx, const basic_json& j)
|
||||
@@ -4131,7 +4040,7 @@ public:
|
||||
// make sure the iterator fits the current value
|
||||
if (JSON_HEDLEY_UNLIKELY(this != pos.m_object))
|
||||
{
|
||||
throw_iterator_from_other_value(this);
|
||||
detail::throw_literal<invalid_iterator>(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this);
|
||||
}
|
||||
|
||||
IteratorType result = end();
|
||||
@@ -4150,7 +4059,10 @@ public:
|
||||
detail::throw_literal<invalid_iterator>(detail::exception_id::iterator_out_of_range, "iterator out of range", this);
|
||||
}
|
||||
|
||||
reset_to_null_impl();
|
||||
m_data.m_value.destroy(m_data.m_type);
|
||||
m_data.m_value = {};
|
||||
m_data.m_type = value_t::null;
|
||||
assert_invariant();
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -4200,12 +4112,16 @@ public:
|
||||
case value_t::string:
|
||||
case value_t::binary:
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!primitive_range_is_complete(first, last)))
|
||||
if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin()
|
||||
|| !last.m_it.primitive_iterator.is_end()))
|
||||
{
|
||||
detail::throw_literal<invalid_iterator>(detail::exception_id::iterator_range_out_of_range, "iterators out of range", this);
|
||||
}
|
||||
|
||||
reset_to_null_impl();
|
||||
m_data.m_value.destroy(m_data.m_type);
|
||||
m_data.m_value = {};
|
||||
m_data.m_type = value_t::null;
|
||||
assert_invariant();
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -4686,20 +4602,34 @@ public:
|
||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||
size_type max_size() const noexcept
|
||||
{
|
||||
if (detail::is_primitive(m_data.m_type))
|
||||
switch (m_data.m_type)
|
||||
{
|
||||
// all other types have max_size() == size()
|
||||
return size();
|
||||
}
|
||||
case value_t::array:
|
||||
{
|
||||
// delegate call to array_t::max_size()
|
||||
return m_data.m_value.array->max_size();
|
||||
}
|
||||
|
||||
if (m_data.m_type == value_t::array)
|
||||
{
|
||||
// delegate call to array_t::max_size()
|
||||
return m_data.m_value.array->max_size();
|
||||
}
|
||||
case value_t::object:
|
||||
{
|
||||
// delegate call to object_t::max_size()
|
||||
return m_data.m_value.object->max_size();
|
||||
}
|
||||
|
||||
// delegate call to object_t::max_size()
|
||||
return m_data.m_value.object->max_size();
|
||||
case value_t::null:
|
||||
case value_t::string:
|
||||
case value_t::boolean:
|
||||
case value_t::number_integer:
|
||||
case value_t::number_unsigned:
|
||||
case value_t::number_float:
|
||||
case value_t::binary:
|
||||
case value_t::discarded:
|
||||
default:
|
||||
{
|
||||
// all other types have max_size() == size()
|
||||
return size();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @}
|
||||
@@ -4777,7 +4707,22 @@ public:
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/push_back/
|
||||
void push_back(basic_json&& val)
|
||||
{
|
||||
push_back_array_impl(std::move(val));
|
||||
// push_back only works for null objects or arrays
|
||||
if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))
|
||||
{
|
||||
detail::throw_cannot_use_with(detail::exception_id::push_back_wrong_type, "push_back()", *this);
|
||||
}
|
||||
|
||||
// transform a null object into an array
|
||||
if (is_null())
|
||||
{
|
||||
convert_null_to<array_t>();
|
||||
}
|
||||
|
||||
// add the element to the array (move semantics)
|
||||
const auto old_capacity = container_capacity();
|
||||
m_data.m_value.array->push_back(std::move(val));
|
||||
set_parent_after_insert(m_data.m_value.array->back(), old_capacity);
|
||||
// if val is moved from, basic_json move constructor marks it null, so we do not call the destructor
|
||||
}
|
||||
|
||||
@@ -4793,7 +4738,22 @@ public:
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/push_back/
|
||||
void push_back(const basic_json& val)
|
||||
{
|
||||
push_back_array_impl(val);
|
||||
// push_back only works for null objects or arrays
|
||||
if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))
|
||||
{
|
||||
detail::throw_cannot_use_with(detail::exception_id::push_back_wrong_type, "push_back()", *this);
|
||||
}
|
||||
|
||||
// transform a null object into an array
|
||||
if (is_null())
|
||||
{
|
||||
convert_null_to<array_t>();
|
||||
}
|
||||
|
||||
// add the element to the array
|
||||
const auto old_capacity = container_capacity();
|
||||
m_data.m_value.array->push_back(val);
|
||||
set_parent_after_insert(m_data.m_value.array->back(), old_capacity);
|
||||
}
|
||||
|
||||
/// @brief add an object to an array
|
||||
@@ -4939,13 +4899,13 @@ public:
|
||||
// insert only works for arrays
|
||||
if (JSON_HEDLEY_UNLIKELY(!is_array()))
|
||||
{
|
||||
throw_insert_wrong_type(*this);
|
||||
detail::throw_cannot_use_with(detail::exception_id::insert_wrong_type, "insert()", *this);
|
||||
}
|
||||
|
||||
// check if iterator pos fits to this JSON value
|
||||
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
|
||||
{
|
||||
throw_iterator_from_other_value(this);
|
||||
detail::throw_literal<invalid_iterator>(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this);
|
||||
}
|
||||
|
||||
// insert to array and return iterator
|
||||
@@ -4959,13 +4919,13 @@ public:
|
||||
// insert only works for arrays
|
||||
if (JSON_HEDLEY_UNLIKELY(!is_array()))
|
||||
{
|
||||
throw_insert_wrong_type(*this);
|
||||
detail::throw_cannot_use_with(detail::exception_id::insert_wrong_type, "insert()", *this);
|
||||
}
|
||||
|
||||
// check if iterator pos fits to this JSON value
|
||||
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
|
||||
{
|
||||
throw_iterator_from_other_value(this);
|
||||
detail::throw_literal<invalid_iterator>(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this);
|
||||
}
|
||||
|
||||
// moving into a local first keeps this safe even if val aliases
|
||||
@@ -4981,13 +4941,13 @@ public:
|
||||
// insert only works for arrays
|
||||
if (JSON_HEDLEY_UNLIKELY(!is_array()))
|
||||
{
|
||||
throw_insert_wrong_type(*this);
|
||||
detail::throw_cannot_use_with(detail::exception_id::insert_wrong_type, "insert()", *this);
|
||||
}
|
||||
|
||||
// check if iterator pos fits to this JSON value
|
||||
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
|
||||
{
|
||||
throw_iterator_from_other_value(this);
|
||||
detail::throw_literal<invalid_iterator>(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this);
|
||||
}
|
||||
|
||||
// insert to array and return iterator
|
||||
@@ -5001,13 +4961,13 @@ public:
|
||||
// insert only works for arrays
|
||||
if (JSON_HEDLEY_UNLIKELY(!is_array()))
|
||||
{
|
||||
throw_insert_wrong_type(*this);
|
||||
detail::throw_cannot_use_with(detail::exception_id::insert_wrong_type, "insert()", *this);
|
||||
}
|
||||
|
||||
// check if iterator pos fits to this JSON value
|
||||
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
|
||||
{
|
||||
throw_iterator_from_other_value(this);
|
||||
detail::throw_literal<invalid_iterator>(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this);
|
||||
}
|
||||
|
||||
// check if range iterators belong to the same JSON object
|
||||
@@ -5038,13 +4998,13 @@ public:
|
||||
// insert only works for arrays
|
||||
if (JSON_HEDLEY_UNLIKELY(!is_array()))
|
||||
{
|
||||
throw_insert_wrong_type(*this);
|
||||
detail::throw_cannot_use_with(detail::exception_id::insert_wrong_type, "insert()", *this);
|
||||
}
|
||||
|
||||
// check if iterator pos fits to this JSON value
|
||||
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
|
||||
{
|
||||
throw_iterator_from_other_value(this);
|
||||
detail::throw_literal<invalid_iterator>(detail::exception_id::iterator_from_other_value, "iterator does not fit current value", this);
|
||||
}
|
||||
|
||||
// copy the values first: ilist may refer to elements of this array
|
||||
@@ -5066,7 +5026,7 @@ public:
|
||||
// insert only works for objects
|
||||
if (JSON_HEDLEY_UNLIKELY(!is_object()))
|
||||
{
|
||||
throw_insert_wrong_type(*this);
|
||||
detail::throw_cannot_use_with(detail::exception_id::insert_wrong_type, "insert()", *this);
|
||||
}
|
||||
|
||||
// check if range iterators belong to the same JSON object
|
||||
@@ -5308,37 +5268,82 @@ public:
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/swap/
|
||||
void swap(array_t& other) // NOLINT(bugprone-exception-escape,cppcoreguidelines-noexcept-swap,performance-noexcept-swap)
|
||||
{
|
||||
swap_impl<array_t>(other, "swap(array_t&)");
|
||||
set_parents();
|
||||
// swap only works for arrays
|
||||
if (JSON_HEDLEY_LIKELY(is_array()))
|
||||
{
|
||||
using std::swap;
|
||||
swap(*(m_data.m_value.array), other);
|
||||
set_parents();
|
||||
}
|
||||
else
|
||||
{
|
||||
detail::throw_cannot_use_with(detail::exception_id::swap_wrong_type, "swap(array_t&)", *this);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief exchanges the values
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/swap/
|
||||
void swap(object_t& other) // NOLINT(bugprone-exception-escape,cppcoreguidelines-noexcept-swap,performance-noexcept-swap)
|
||||
{
|
||||
swap_impl<object_t>(other, "swap(object_t&)");
|
||||
set_parents();
|
||||
// swap only works for objects
|
||||
if (JSON_HEDLEY_LIKELY(is_object()))
|
||||
{
|
||||
using std::swap;
|
||||
swap(*(m_data.m_value.object), other);
|
||||
set_parents();
|
||||
}
|
||||
else
|
||||
{
|
||||
detail::throw_cannot_use_with(detail::exception_id::swap_wrong_type, "swap(object_t&)", *this);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief exchanges the values
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/swap/
|
||||
void swap(string_t& other) // NOLINT(bugprone-exception-escape,cppcoreguidelines-noexcept-swap,performance-noexcept-swap)
|
||||
{
|
||||
swap_impl<string_t>(other, "swap(string_t&)");
|
||||
// swap only works for strings
|
||||
if (JSON_HEDLEY_LIKELY(is_string()))
|
||||
{
|
||||
using std::swap;
|
||||
swap(*(m_data.m_value.string), other);
|
||||
}
|
||||
else
|
||||
{
|
||||
detail::throw_cannot_use_with(detail::exception_id::swap_wrong_type, "swap(string_t&)", *this);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief exchanges the values
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/swap/
|
||||
void swap(binary_t& other) // NOLINT(bugprone-exception-escape,cppcoreguidelines-noexcept-swap,performance-noexcept-swap)
|
||||
{
|
||||
swap_impl<binary_t>(other, "swap(binary_t&)");
|
||||
// swap only works for binary values
|
||||
if (JSON_HEDLEY_LIKELY(is_binary()))
|
||||
{
|
||||
using std::swap;
|
||||
swap(*(m_data.m_value.binary), other);
|
||||
}
|
||||
else
|
||||
{
|
||||
detail::throw_cannot_use_with(detail::exception_id::swap_wrong_type, "swap(binary_t&)", *this);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief exchanges the values
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/swap/
|
||||
void swap(typename binary_t::container_type& other) // NOLINT(bugprone-exception-escape)
|
||||
{
|
||||
swap_impl<binary_t>(other, "swap(binary_t::container_type&)");
|
||||
// swap only works for binary values
|
||||
if (JSON_HEDLEY_LIKELY(is_binary()))
|
||||
{
|
||||
using std::swap;
|
||||
swap(*(m_data.m_value.binary), other);
|
||||
}
|
||||
else
|
||||
{
|
||||
detail::throw_cannot_use_with(detail::exception_id::swap_wrong_type, "swap(binary_t::container_type&)", *this);
|
||||
}
|
||||
}
|
||||
|
||||
/// @}
|
||||
@@ -6019,7 +6024,10 @@ public:
|
||||
const bool ignore_comments = false,
|
||||
const bool ignore_trailing_commas = false)
|
||||
{
|
||||
return parse_impl(detail::input_adapter(std::forward<InputType>(i)), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas);
|
||||
basic_json result;
|
||||
auto p = parser(detail::input_adapter(std::forward<InputType>(i)), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas);
|
||||
p.parse(true, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// @brief deserialize from a pair of character iterators (or an iterator+sentinel pair, C++20 ranges support)
|
||||
@@ -6034,7 +6042,10 @@ public:
|
||||
const bool ignore_comments = false,
|
||||
const bool ignore_trailing_commas = false)
|
||||
{
|
||||
return parse_impl(detail::input_adapter(std::move(first), std::move(last)), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas);
|
||||
basic_json result;
|
||||
auto p = parser(detail::input_adapter(std::move(first), std::move(last)), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas);
|
||||
p.parse(true, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||
@@ -6048,7 +6059,10 @@ public:
|
||||
= delete;
|
||||
#else
|
||||
{
|
||||
return parse_impl(i.get(), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas);
|
||||
basic_json result;
|
||||
auto p = parser(i.get(), std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas);
|
||||
p.parse(true, result);
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -6060,7 +6074,7 @@ public:
|
||||
const bool ignore_comments = false,
|
||||
const bool ignore_trailing_commas = false)
|
||||
{
|
||||
return accept_impl(detail::input_adapter(std::forward<InputType>(i)), ignore_comments, ignore_trailing_commas);
|
||||
return parser(detail::input_adapter(std::forward<InputType>(i)), nullptr, false, ignore_comments, ignore_trailing_commas, true).accept(true);
|
||||
}
|
||||
|
||||
/// @brief check if the input is valid JSON (iterator pair, or iterator+sentinel pair for C++20 ranges support)
|
||||
@@ -6072,7 +6086,7 @@ public:
|
||||
const bool ignore_comments = false,
|
||||
const bool ignore_trailing_commas = false)
|
||||
{
|
||||
return accept_impl(detail::input_adapter(std::move(first), std::move(last)), ignore_comments, ignore_trailing_commas);
|
||||
return parser(detail::input_adapter(std::move(first), std::move(last)), nullptr, false, ignore_comments, ignore_trailing_commas, true).accept(true);
|
||||
}
|
||||
|
||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||
@@ -6084,7 +6098,7 @@ public:
|
||||
= delete;
|
||||
#else
|
||||
{
|
||||
return accept_impl(i.get(), ignore_comments, ignore_trailing_commas);
|
||||
return parser(i.get(), nullptr, false, ignore_comments, ignore_trailing_commas, true).accept(true);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -6099,7 +6113,10 @@ public:
|
||||
const bool ignore_trailing_commas = false,
|
||||
const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
|
||||
{
|
||||
return sax_parse_impl(detail::input_adapter(std::forward<InputType>(i)), sax, format, strict, ignore_comments, ignore_trailing_commas, tag_handler);
|
||||
auto ia = detail::input_adapter(std::forward<InputType>(i));
|
||||
return format == input_format_t::json
|
||||
? parser(std::move(ia), nullptr, true, ignore_comments, ignore_trailing_commas).sax_parse(sax, strict)
|
||||
: detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(sax, strict, tag_handler);
|
||||
}
|
||||
|
||||
/// @brief generate SAX events (iterator pair, or iterator+sentinel pair for C++20 ranges support)
|
||||
@@ -6114,7 +6131,10 @@ public:
|
||||
const bool ignore_trailing_commas = false,
|
||||
const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
|
||||
{
|
||||
return sax_parse_impl(detail::input_adapter(std::move(first), std::move(last)), sax, format, strict, ignore_comments, ignore_trailing_commas, tag_handler);
|
||||
auto ia = detail::input_adapter(std::move(first), std::move(last));
|
||||
return format == input_format_t::json
|
||||
? parser(std::move(ia), nullptr, true, ignore_comments, ignore_trailing_commas).sax_parse(sax, strict)
|
||||
: detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(sax, strict, tag_handler);
|
||||
}
|
||||
|
||||
/// @brief generate SAX events
|
||||
@@ -6148,7 +6168,12 @@ public:
|
||||
= delete;
|
||||
#else
|
||||
{
|
||||
return sax_parse_impl(i.get(), sax, format, strict, ignore_comments, ignore_trailing_commas, tag_handler);
|
||||
auto ia = i.get();
|
||||
return format == input_format_t::json
|
||||
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
|
||||
? parser(std::move(ia), nullptr, true, ignore_comments, ignore_trailing_commas).sax_parse(sax, strict)
|
||||
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
|
||||
: detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(sax, strict, tag_handler);
|
||||
}
|
||||
#endif
|
||||
#if defined(__clang__)
|
||||
@@ -6296,10 +6321,10 @@ public:
|
||||
static std::vector<std::uint8_t> to_cbor(const basic_json& j,
|
||||
const error_handler_t error_handler = detail::binary_writer_default_error_handler())
|
||||
{
|
||||
return to_binary_vector(j, error_handler, [&](vector_binary_writer<std::uint8_t>& w)
|
||||
{
|
||||
w.write_cbor(j);
|
||||
});
|
||||
std::vector<std::uint8_t> result;
|
||||
result.reserve(detail::binary_initial_capacity(j));
|
||||
vector_writer(result, error_handler).write_cbor(j);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// @brief create a CBOR serialization of a given JSON value
|
||||
@@ -6327,10 +6352,10 @@ public:
|
||||
static std::vector<std::uint8_t> to_msgpack(const basic_json& j,
|
||||
const error_handler_t error_handler = error_handler_t::keep)
|
||||
{
|
||||
return to_binary_vector(j, error_handler, [&](vector_binary_writer<std::uint8_t>& w)
|
||||
{
|
||||
w.write_msgpack(j);
|
||||
});
|
||||
std::vector<std::uint8_t> result;
|
||||
result.reserve(detail::binary_initial_capacity(j));
|
||||
vector_writer(result, error_handler).write_msgpack(j);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// @brief create a MessagePack serialization of a given JSON value
|
||||
@@ -6360,10 +6385,10 @@ public:
|
||||
const bool use_type = false,
|
||||
const error_handler_t error_handler = detail::binary_writer_default_error_handler())
|
||||
{
|
||||
return to_binary_vector(j, error_handler, [&](vector_binary_writer<std::uint8_t>& w)
|
||||
{
|
||||
w.write_ubjson(j, use_size, use_type);
|
||||
});
|
||||
std::vector<std::uint8_t> result;
|
||||
result.reserve(detail::binary_initial_capacity(j));
|
||||
vector_writer(result, error_handler).write_ubjson(j, use_size, use_type);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// @brief create a UBJSON serialization of a given JSON value
|
||||
@@ -6396,10 +6421,10 @@ public:
|
||||
const bjdata_version_t version = bjdata_version_t::draft2,
|
||||
const error_handler_t error_handler = detail::binary_writer_default_error_handler())
|
||||
{
|
||||
return to_binary_vector(j, error_handler, [&](vector_binary_writer<std::uint8_t>& w)
|
||||
{
|
||||
w.write_ubjson(j, use_size, use_type, true, true, version);
|
||||
});
|
||||
std::vector<std::uint8_t> result;
|
||||
result.reserve(detail::binary_initial_capacity(j));
|
||||
vector_writer(result, error_handler).write_ubjson(j, use_size, use_type, true, true, version);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// @brief create a BJData serialization of a given JSON value
|
||||
@@ -6431,10 +6456,10 @@ public:
|
||||
static std::vector<std::uint8_t> to_bson(const basic_json& j,
|
||||
const error_handler_t error_handler = detail::binary_writer_default_error_handler())
|
||||
{
|
||||
return to_binary_vector(j, error_handler, [&](vector_binary_writer<std::uint8_t>& w)
|
||||
{
|
||||
w.write_bson(j);
|
||||
});
|
||||
std::vector<std::uint8_t> result;
|
||||
result.reserve(detail::binary_initial_capacity(j));
|
||||
vector_writer(result, error_handler).write_bson(j);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// @brief create a BSON serialization of a given JSON value
|
||||
@@ -6461,10 +6486,10 @@ public:
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/to_bon8/
|
||||
static std::vector<std::uint8_t> to_bon8(const basic_json& j)
|
||||
{
|
||||
return to_binary_vector(j, detail::binary_writer_default_error_handler(), [&](vector_binary_writer<std::uint8_t>& w)
|
||||
{
|
||||
w.write_bon8(j);
|
||||
});
|
||||
std::vector<std::uint8_t> result;
|
||||
result.reserve(detail::binary_initial_capacity(j));
|
||||
vector_writer(result).write_bon8(j);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// @brief create a BON8 serialization of a given JSON value
|
||||
@@ -7800,68 +7825,82 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
if (detail::is_primitive(source.type()))
|
||||
switch (source.type())
|
||||
{
|
||||
// both primitive types: replace value
|
||||
diff_replace(result, path, target);
|
||||
return;
|
||||
}
|
||||
|
||||
if (source.is_array())
|
||||
{
|
||||
const std::size_t path_length = path.size();
|
||||
|
||||
// first pass: traverse common elements
|
||||
std::size_t i = 0;
|
||||
while (i < source.size() && i < target.size())
|
||||
case value_t::array:
|
||||
{
|
||||
// recursive call to compare array values at index i
|
||||
if (!(source[i] == target[i]))
|
||||
const std::size_t path_length = path.size();
|
||||
|
||||
// first pass: traverse common elements
|
||||
std::size_t i = 0;
|
||||
while (i < source.size() && i < target.size())
|
||||
{
|
||||
diff_append_index(path, i);
|
||||
diff_unequal(result, source[i], target[i], path, depth + 1);
|
||||
path.resize(path_length);
|
||||
// recursive call to compare array values at index i
|
||||
if (!(source[i] == target[i]))
|
||||
{
|
||||
diff_append_index(path, i);
|
||||
diff_unequal(result, source[i], target[i], path, depth + 1);
|
||||
path.resize(path_length);
|
||||
}
|
||||
++i;
|
||||
}
|
||||
++i;
|
||||
|
||||
// We now reached the end of at least one array
|
||||
// in a second pass, traverse the remaining elements
|
||||
diff_array_tails(result, source, target, path, i);
|
||||
break;
|
||||
}
|
||||
|
||||
// We now reached the end of at least one array
|
||||
// in a second pass, traverse the remaining elements
|
||||
diff_array_tails(result, source, target, path, i);
|
||||
}
|
||||
else
|
||||
{
|
||||
diff_target_values target_values;
|
||||
std::vector<const_iterator> added;
|
||||
if (diff_object_keys(result, source, target, path, target_values, added))
|
||||
case value_t::object:
|
||||
{
|
||||
// fast path: target_values holds, for every member of
|
||||
// source in order, its value in target -- so no further
|
||||
// lookup is needed. Deleted keys are interleaved here
|
||||
// too, in source's original order, to match the
|
||||
// historical (pre-reordering-aware) output order.
|
||||
const std::size_t path_length = path.size();
|
||||
std::size_t i = 0;
|
||||
for (auto it = source.cbegin(); it != source.cend(); ++it, ++i)
|
||||
diff_target_values target_values;
|
||||
std::vector<const_iterator> added;
|
||||
if (diff_object_keys(result, source, target, path, target_values, added))
|
||||
{
|
||||
if (target_values[i] != nullptr)
|
||||
// fast path: target_values holds, for every member of
|
||||
// source in order, its value in target -- so no further
|
||||
// lookup is needed. Deleted keys are interleaved here
|
||||
// too, in source's original order, to match the
|
||||
// historical (pre-reordering-aware) output order.
|
||||
const std::size_t path_length = path.size();
|
||||
std::size_t i = 0;
|
||||
for (auto it = source.cbegin(); it != source.cend(); ++it, ++i)
|
||||
{
|
||||
if (!(it.value() == *target_values[i]))
|
||||
if (target_values[i] != nullptr)
|
||||
{
|
||||
diff_append_key(path, it.key());
|
||||
diff_unequal(result, it.value(), *target_values[i], path, depth + 1);
|
||||
path.resize(path_length);
|
||||
if (!(it.value() == *target_values[i]))
|
||||
{
|
||||
diff_append_key(path, it.key());
|
||||
diff_unequal(result, it.value(), *target_values[i], path, depth + 1);
|
||||
path.resize(path_length);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// found a key that is not in target -> remove it
|
||||
diff_remove(result, diff_key_path(path, it.key()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// found a key that is not in target -> remove it
|
||||
diff_remove(result, diff_key_path(path, it.key()));
|
||||
}
|
||||
}
|
||||
|
||||
// append the "add" operations for brand-new keys
|
||||
diff_added(result, added, path);
|
||||
// append the "add" operations for brand-new keys
|
||||
diff_added(result, added, path);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case value_t::null:
|
||||
case value_t::string:
|
||||
case value_t::boolean:
|
||||
case value_t::number_integer:
|
||||
case value_t::number_unsigned:
|
||||
case value_t::number_float:
|
||||
case value_t::binary:
|
||||
case value_t::discarded:
|
||||
default:
|
||||
{
|
||||
// both primitive types: replace value
|
||||
diff_replace(result, path, target);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7907,33 +7946,45 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
if (detail::is_primitive(s.type()))
|
||||
switch (s.type())
|
||||
{
|
||||
// both primitive types: replace value
|
||||
diff_replace(result, current_path, t);
|
||||
return;
|
||||
}
|
||||
|
||||
if (s.is_array())
|
||||
{
|
||||
stack.emplace_back(&s, &t, current_path.size());
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<const basic_json*> target_values;
|
||||
std::vector<const_iterator> added;
|
||||
if (diff_object_keys(result, s, t, current_path, target_values, added))
|
||||
case value_t::array:
|
||||
{
|
||||
// fast path: the frame walks source in lockstep with
|
||||
// common_keys, as diff_recursively does, and appends
|
||||
// added_ops once all members are done
|
||||
stack.emplace_back(&s, &t, current_path.size());
|
||||
stack.back().member = s.cbegin();
|
||||
stack.back().target_values = std::move(target_values);
|
||||
stack.back().added = std::move(added);
|
||||
return;
|
||||
}
|
||||
|
||||
case value_t::object:
|
||||
{
|
||||
std::vector<const basic_json*> target_values;
|
||||
std::vector<const_iterator> added;
|
||||
if (diff_object_keys(result, s, t, current_path, target_values, added))
|
||||
{
|
||||
// fast path: the frame walks source in lockstep with
|
||||
// common_keys, as diff_recursively does, and appends
|
||||
// added_ops once all members are done
|
||||
stack.emplace_back(&s, &t, current_path.size());
|
||||
stack.back().member = s.cbegin();
|
||||
stack.back().target_values = std::move(target_values);
|
||||
stack.back().added = std::move(added);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
case value_t::null:
|
||||
case value_t::string:
|
||||
case value_t::boolean:
|
||||
case value_t::number_integer:
|
||||
case value_t::number_unsigned:
|
||||
case value_t::number_float:
|
||||
case value_t::binary:
|
||||
case value_t::discarded:
|
||||
default:
|
||||
{
|
||||
// both primitive types: replace value
|
||||
diff_replace(result, current_path, t);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -267,7 +267,7 @@ class basic_json_view
|
||||
{
|
||||
return {};
|
||||
}
|
||||
detail::view::throw_type_error(detail::exception_id::subscript_wrong_type, "cannot use operator[] with a string argument with ", type_name());
|
||||
detail::view::throw_type_error(305, "cannot use operator[] with a string argument with ", type_name());
|
||||
}
|
||||
return lookup(key);
|
||||
}
|
||||
@@ -293,7 +293,7 @@ class basic_json_view
|
||||
{
|
||||
return {};
|
||||
}
|
||||
detail::view::throw_type_error(detail::exception_id::subscript_wrong_type, "cannot use operator[] with a numeric argument with ", type_name());
|
||||
detail::view::throw_type_error(305, "cannot use operator[] with a numeric argument with ", type_name());
|
||||
}
|
||||
return idx < m_node->len ? basic_json_view(m_doc, navigation::value(detail::view::element_at<Editable>(*m_doc, m_node, idx))) : basic_json_view();
|
||||
}
|
||||
@@ -326,12 +326,12 @@ class basic_json_view
|
||||
{
|
||||
if (NLOHMANN_VIEW_UNLIKELY(!is_object()))
|
||||
{
|
||||
detail::view::throw_type_error(detail::exception_id::at_wrong_type, "cannot use at() with ", type_name());
|
||||
detail::view::throw_type_error(304, "cannot use at() with ", type_name());
|
||||
}
|
||||
const basic_json_view r = lookup(key);
|
||||
if (NLOHMANN_VIEW_UNLIKELY(r.is_discarded()))
|
||||
{
|
||||
detail::view::throw_key_not_found(std::string(key.data(), key.size()));
|
||||
detail::view::throw_out_of_range(403, detail::concat("key '", std::string(key.data(), key.size()), "' not found"));
|
||||
}
|
||||
return r;
|
||||
}
|
||||
@@ -352,11 +352,11 @@ class basic_json_view
|
||||
{
|
||||
if (NLOHMANN_VIEW_UNLIKELY(!is_array()))
|
||||
{
|
||||
detail::view::throw_type_error(detail::exception_id::at_wrong_type, "cannot use at() with ", type_name());
|
||||
detail::view::throw_type_error(304, "cannot use at() with ", type_name());
|
||||
}
|
||||
if (NLOHMANN_VIEW_UNLIKELY(idx >= m_node->len))
|
||||
{
|
||||
detail::view::throw_array_index_out_of_range(idx);
|
||||
detail::view::throw_out_of_range(401, detail::concat("array index ", std::to_string(idx), " is out of range"));
|
||||
}
|
||||
return basic_json_view(m_doc, navigation::value(detail::view::element_at<Editable>(*m_doc, m_node, idx)));
|
||||
}
|
||||
@@ -384,7 +384,7 @@ class basic_json_view
|
||||
{
|
||||
if (NLOHMANN_VIEW_UNLIKELY(!is_object()))
|
||||
{
|
||||
detail::view::throw_type_error(detail::exception_id::value_wrong_type, "cannot use value() with ", type_name());
|
||||
detail::view::throw_type_error(306, "cannot use value() with ", type_name());
|
||||
}
|
||||
const basic_json_view r = lookup(key);
|
||||
return r.is_discarded() ? default_value : r.template get<T>();
|
||||
@@ -403,7 +403,7 @@ class basic_json_view
|
||||
{
|
||||
if (NLOHMANN_VIEW_UNLIKELY(!is_structured()))
|
||||
{
|
||||
detail::view::throw_type_error(detail::exception_id::value_wrong_type, "cannot use value() with ", type_name());
|
||||
detail::view::throw_type_error(306, "cannot use value() with ", type_name());
|
||||
}
|
||||
const basic_json_view r = detail::view::resolve_pointer(*this, detail::json_pointer_access::reference_tokens(ptr), detail::view::pointer_mode::value);
|
||||
return r.is_discarded() ? default_value : r.template get<T>();
|
||||
@@ -421,7 +421,7 @@ class basic_json_view
|
||||
const iterator it = begin();
|
||||
if (NLOHMANN_VIEW_UNLIKELY(it == end()))
|
||||
{
|
||||
detail::view::throw_invalid_iterator(detail::exception_id::iterator_value_unavailable, "cannot get value");
|
||||
detail::view::throw_invalid_iterator(214, "cannot get value");
|
||||
}
|
||||
return *it;
|
||||
}
|
||||
@@ -573,7 +573,7 @@ class basic_json_view
|
||||
{
|
||||
if (NLOHMANN_VIEW_UNLIKELY(!is_string()))
|
||||
{
|
||||
detail::view::throw_type_error(detail::exception_id::type_mismatch, "type must be string, but is ", type_name());
|
||||
detail::view::throw_type_error(302, "type must be string, but is ", type_name());
|
||||
}
|
||||
return {m_doc->str(*m_node), m_node->len};
|
||||
}
|
||||
@@ -585,7 +585,7 @@ class basic_json_view
|
||||
{
|
||||
if (NLOHMANN_VIEW_UNLIKELY(!is_number()))
|
||||
{
|
||||
detail::view::throw_type_error(detail::exception_id::type_mismatch, "type must be number, but is ", type_name());
|
||||
detail::view::throw_type_error(302, "type must be number, but is ", type_name());
|
||||
}
|
||||
return {m_doc->str(*m_node), detail::view::number_length(*m_node)};
|
||||
}
|
||||
@@ -795,7 +795,7 @@ class basic_json_view
|
||||
{
|
||||
if (NLOHMANN_VIEW_UNLIKELY(!is_boolean()))
|
||||
{
|
||||
detail::view::throw_type_error(detail::exception_id::type_mismatch, "type must be boolean, but is ", type_name());
|
||||
detail::view::throw_type_error(302, "type must be boolean, but is ", type_name());
|
||||
}
|
||||
return (m_node->flags & detail::view::node_flags::is_true) != 0;
|
||||
}
|
||||
@@ -805,7 +805,7 @@ class basic_json_view
|
||||
{
|
||||
if (NLOHMANN_VIEW_UNLIKELY(m_node == nullptr))
|
||||
{
|
||||
detail::view::throw_type_error(detail::exception_id::type_mismatch, "type must be number, but is ", type_name());
|
||||
detail::view::throw_type_error(302, "type must be number, but is ", type_name());
|
||||
}
|
||||
return detail::view::arithmetic_value<T, BasicJsonType>(*m_doc, *m_node);
|
||||
}
|
||||
@@ -814,7 +814,7 @@ class basic_json_view
|
||||
{
|
||||
if (NLOHMANN_VIEW_UNLIKELY(!is_null()))
|
||||
{
|
||||
detail::view::throw_type_error(detail::exception_id::type_mismatch, "type must be null, but is ", type_name());
|
||||
detail::view::throw_type_error(302, "type must be null, but is ", type_name());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
@@ -1123,7 +1123,7 @@ class basic_json_document
|
||||
{
|
||||
if (NLOHMANN_VIEW_UNLIKELY(!m_data || m_data->discarded))
|
||||
{
|
||||
detail::view::throw_type_error(detail::exception_id::document_image_unsupported, "cannot save a discarded json_document");
|
||||
detail::view::throw_type_error(320, "cannot save a discarded json_document");
|
||||
}
|
||||
return detail::view::save_image(*m_data);
|
||||
}
|
||||
@@ -1224,7 +1224,7 @@ class basic_json_document
|
||||
const std::size_t idx = pointer_index(token);
|
||||
if (idx >= 0xFFFFFFFFu)
|
||||
{
|
||||
detail::view::throw_array_index_out_of_range(idx);
|
||||
detail::view::throw_out_of_range(401, detail::concat("array index ", std::to_string(idx), " is out of range"));
|
||||
}
|
||||
for (std::size_t i = 0; i < idx; ++i)
|
||||
{
|
||||
@@ -1280,7 +1280,7 @@ class basic_json_document
|
||||
{
|
||||
if (ptr.empty())
|
||||
{
|
||||
detail::view::throw_out_of_range(detail::exception_id::patch_on_root, "JSON pointer has no parent");
|
||||
detail::view::throw_out_of_range(405, "JSON pointer has no parent");
|
||||
}
|
||||
const view_type parent = root().at(ptr.parent_pointer());
|
||||
const auto& token = ptr.back();
|
||||
@@ -1300,7 +1300,7 @@ class basic_json_document
|
||||
static_assert(Editable, "only an editable document can be edited: use basic_json_document<BasicJsonType, true> (json_editable_document)");
|
||||
if (NLOHMANN_VIEW_UNLIKELY(!m_data || m_data->discarded))
|
||||
{
|
||||
detail::view::throw_invalid_iterator(detail::exception_id::iterator_from_other_value, "view does not belong to this document");
|
||||
detail::view::throw_invalid_iterator(202, "view does not belong to this document");
|
||||
}
|
||||
return detail::view::editor<BasicJsonType, view_type>(*m_data);
|
||||
}
|
||||
@@ -1317,7 +1317,7 @@ class basic_json_document
|
||||
{
|
||||
if (idx < 0)
|
||||
{
|
||||
detail::view::throw_array_index_out_of_range(idx);
|
||||
detail::view::throw_out_of_range(401, detail::concat("array index ", std::to_string(idx), " is out of range"));
|
||||
}
|
||||
return static_cast<std::size_t>(idx);
|
||||
}
|
||||
|
||||
@@ -125,19 +125,6 @@ private:
|
||||
Container::pop_back();
|
||||
}
|
||||
|
||||
/// @brief remove the entry with the given key; returns the number of removed entries
|
||||
template<class K>
|
||||
size_type erase_impl(const K& key)
|
||||
{
|
||||
const auto it = find_impl(*this, key);
|
||||
if (it != this->end())
|
||||
{
|
||||
erase_at(it);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public:
|
||||
template<class V, detail::enable_if_t<
|
||||
detail::is_constructible<T, V>::value, int> = 0>
|
||||
@@ -227,14 +214,26 @@ public:
|
||||
|
||||
size_type erase(const key_type& key)
|
||||
{
|
||||
return erase_impl(key);
|
||||
const auto it = find_impl(*this, key);
|
||||
if (it != this->end())
|
||||
{
|
||||
erase_at(it);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<class KeyType, detail::enable_if_t<
|
||||
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
|
||||
size_type erase(KeyType && key) // NOLINT(cppcoreguidelines-missing-std-forward)
|
||||
{
|
||||
return erase_impl(key);
|
||||
const auto it = find_impl(*this, key);
|
||||
if (it != this->end())
|
||||
{
|
||||
erase_at(it);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
iterator erase(iterator pos)
|
||||
|
||||
Generated
+1604
-1428
File diff suppressed because it is too large.
Load diff
Generated
+136
-94
@@ -2674,52 +2674,32 @@ namespace view
|
||||
// Exceptions are thrown out of line, so that the accessors that may throw stay
|
||||
// small enough to be inlined.
|
||||
|
||||
[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_type_error(exception_id id, const char* prefix, const char* type)
|
||||
[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_type_error(int id, const char* prefix, const char* type)
|
||||
{
|
||||
NLOHMANN_VIEW_THROW(type_error::create(id, concat(prefix, type), nullptr));
|
||||
}
|
||||
|
||||
[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_type_error(exception_id id, const std::string& msg)
|
||||
[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_type_error(int id, const std::string& msg)
|
||||
{
|
||||
NLOHMANN_VIEW_THROW(type_error::create(id, msg, nullptr));
|
||||
}
|
||||
|
||||
[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_out_of_range(exception_id id, const std::string& msg)
|
||||
[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_out_of_range(int id, const std::string& msg)
|
||||
{
|
||||
NLOHMANN_VIEW_THROW(out_of_range::create(id, msg, nullptr));
|
||||
}
|
||||
|
||||
[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_invalid_iterator(exception_id id, const char* msg)
|
||||
[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_invalid_iterator(int id, const char* msg)
|
||||
{
|
||||
NLOHMANN_VIEW_THROW(invalid_iterator::create(id, msg, nullptr));
|
||||
}
|
||||
|
||||
/// a parse error without a position (as those of json_pointer)
|
||||
[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_parse_error(exception_id id, const std::string& msg)
|
||||
[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_parse_error(int id, const std::string& msg)
|
||||
{
|
||||
NLOHMANN_VIEW_THROW(parse_error::create(id, 0, msg, nullptr));
|
||||
}
|
||||
|
||||
/// out_of_range.401: the array index @a idx is not below the size
|
||||
template<typename Index>
|
||||
[[noreturn]] NLOHMANN_VIEW_NOINLINE void throw_array_index_out_of_range(const Index idx)
|
||||
{
|
||||
throw_out_of_range(exception_id::array_index_out_of_range, concat("array index ", std::to_string(idx), " is out of range"));
|
||||
}
|
||||
|
||||
/// out_of_range.402: the JSON Pointer token '-' of an array of @a size elements
|
||||
[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_array_index_past_end(std::size_t size)
|
||||
{
|
||||
throw_out_of_range(exception_id::pointer_past_the_end_index, concat("array index '-' (", std::to_string(size), ") is out of range"));
|
||||
}
|
||||
|
||||
/// out_of_range.403: the object has no member @a key
|
||||
template<typename Key>
|
||||
[[noreturn]] NLOHMANN_VIEW_NOINLINE void throw_key_not_found(const Key& key)
|
||||
{
|
||||
throw_out_of_range(exception_id::key_not_found, concat("key '", key, "' not found"));
|
||||
}
|
||||
|
||||
/// SAX handler of the error replay: accepts every event without storing it,
|
||||
/// and throws the exception the parser reports
|
||||
template<typename BasicJsonType>
|
||||
@@ -2818,7 +2798,7 @@ template<typename BasicJsonType>
|
||||
if (f.code == error_code::input_too_large)
|
||||
{
|
||||
// (the limit is detail::view::max_input_size(): 4 GiB minus 16 bytes)
|
||||
NLOHMANN_VIEW_THROW(out_of_range::create(exception_id::document_too_large, "input of 4294967280 bytes or more is not supported by json_document", nullptr));
|
||||
NLOHMANN_VIEW_THROW(out_of_range::create(416, "input of 4294967280 bytes or more is not supported by json_document", nullptr));
|
||||
}
|
||||
throwing_sax<BasicJsonType> sax;
|
||||
const bool accepted = BasicJsonType::sax_parse(src, src + size, &sax, input_format_t::json, true, ignore_comments, ignore_trailing_commas);
|
||||
@@ -2838,7 +2818,7 @@ template<typename BasicJsonType>
|
||||
}
|
||||
}
|
||||
pos.chars_read_current_line = off + 1 - line_start;
|
||||
NLOHMANN_VIEW_THROW(parse_error::create(exception_id::syntax_error, pos, "syntax error while parsing value", nullptr));
|
||||
NLOHMANN_VIEW_THROW(parse_error::create(101, pos, "syntax error while parsing value", nullptr));
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
@@ -2913,7 +2893,7 @@ inline std::size_t text_capacity(std::size_t cap, std::size_t used, std::size_t
|
||||
constexpr std::size_t limit = 0xFFFFFFFFu;
|
||||
if (NLOHMANN_VIEW_UNLIKELY(used > limit || n > limit - used))
|
||||
{
|
||||
throw_out_of_range(exception_id::document_too_large, "edits of 4 GiB or more are not supported by json_document");
|
||||
throw_out_of_range(416, "edits of 4 GiB or more are not supported by json_document");
|
||||
}
|
||||
const std::size_t needed = used + n;
|
||||
const std::size_t wanted = needed + (std::min)(limit - needed, std::size_t{256});
|
||||
@@ -2964,7 +2944,7 @@ inline void reserve_moved(document_data& d)
|
||||
document_data::edit_state& e = edit_state_of(d);
|
||||
if (e.moved.size() >= 0xFFFFFFFFu)
|
||||
{
|
||||
throw_out_of_range(exception_id::document_too_large, "more than 4294967295 edited arrays and objects are not supported by json_document"); // LCOV_EXCL_LINE
|
||||
throw_out_of_range(416, "more than 4294967295 edited arrays and objects are not supported by json_document"); // LCOV_EXCL_LINE
|
||||
}
|
||||
if (e.moved.size() == e.moved.capacity() || e.moved_cap.size() == e.moved_cap.capacity())
|
||||
{
|
||||
@@ -3496,6 +3476,11 @@ struct first_true<false, Conditions...> : std::integral_constant < int, 1 + firs
|
||||
inline void check_utf8(const char* s, std::size_t n)
|
||||
{
|
||||
const auto* const p = reinterpret_cast<const unsigned char*>(s); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||
const auto hex = [](unsigned char c)
|
||||
{
|
||||
constexpr const char* digits = "0123456789ABCDEF";
|
||||
return std::string{digits[c >> 4u], digits[c & 0xFu]};
|
||||
};
|
||||
for (std::size_t i = 0; i < n;)
|
||||
{
|
||||
const unsigned char c = p[i];
|
||||
@@ -3504,22 +3489,39 @@ inline void check_utf8(const char* s, std::size_t n)
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
const utf8_sequence_shape shape = utf8_shape(c);
|
||||
const std::size_t len = shape.length;
|
||||
if (len == 0)
|
||||
std::size_t len = 0;
|
||||
unsigned char lo = 0x80;
|
||||
unsigned char hi = 0xBF;
|
||||
if (c >= 0xC2 && c <= 0xDF)
|
||||
{
|
||||
throw_type_error(exception_id::invalid_utf8, concat("invalid UTF-8 byte at index ", std::to_string(i), ": 0x", hex_byte(c)));
|
||||
len = 2;
|
||||
}
|
||||
else if (c >= 0xE0 && c <= 0xEF)
|
||||
{
|
||||
len = 3;
|
||||
lo = c == 0xE0 ? 0xA0 : 0x80;
|
||||
hi = c == 0xED ? 0x9F : 0xBF;
|
||||
}
|
||||
else if (c >= 0xF0 && c <= 0xF4)
|
||||
{
|
||||
len = 4;
|
||||
lo = c == 0xF0 ? 0x90 : 0x80;
|
||||
hi = c == 0xF4 ? 0x8F : 0xBF;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw_type_error(316, concat("invalid UTF-8 byte at index ", std::to_string(i), ": 0x", hex(c)));
|
||||
}
|
||||
for (std::size_t k = 1; k < len; ++k)
|
||||
{
|
||||
if (i + k == n)
|
||||
{
|
||||
throw_type_error(exception_id::invalid_utf8, concat("incomplete UTF-8 string; last byte: 0x", hex_byte(p[n - 1])));
|
||||
throw_type_error(316, concat("incomplete UTF-8 string; last byte: 0x", hex(p[n - 1])));
|
||||
}
|
||||
const unsigned char b = p[i + k];
|
||||
if (b < (k == 1 ? shape.second_lo : 0x80) || b > (k == 1 ? shape.second_hi : 0xBF))
|
||||
if (b < (k == 1 ? lo : 0x80) || b > (k == 1 ? hi : 0xBF))
|
||||
{
|
||||
throw_type_error(exception_id::invalid_utf8, concat("invalid UTF-8 byte at index ", std::to_string(i + k), ": 0x", hex_byte(b)));
|
||||
throw_type_error(316, concat("invalid UTF-8 byte at index ", std::to_string(i + k), ": 0x", hex(b)));
|
||||
}
|
||||
}
|
||||
i += len;
|
||||
@@ -3567,7 +3569,7 @@ class editor
|
||||
node* const o = own(object);
|
||||
if (o->kind != static_cast<std::uint8_t>(value_t::object) && o->kind != static_cast<std::uint8_t>(value_t::null))
|
||||
{
|
||||
throw_type_error(exception_id::subscript_wrong_type, "cannot use operator[] with a string argument with ", object.type_name());
|
||||
throw_type_error(305, "cannot use operator[] with a string argument with ", object.type_name());
|
||||
}
|
||||
check_utf8(key.data(), key.size());
|
||||
const encoded e = encode(std::forward<V>(value));
|
||||
@@ -3618,7 +3620,7 @@ class editor
|
||||
node* const a = own(array);
|
||||
if (a->kind != static_cast<std::uint8_t>(value_t::array))
|
||||
{
|
||||
throw_type_error(exception_id::subscript_wrong_type, "cannot use operator[] with a numeric argument with ", array.type_name());
|
||||
throw_type_error(305, "cannot use operator[] with a numeric argument with ", array.type_name());
|
||||
}
|
||||
check_index(idx, a->len);
|
||||
const encoded e = encode(std::forward<V>(value));
|
||||
@@ -3635,7 +3637,7 @@ class editor
|
||||
node* const a = own(array);
|
||||
if (a->kind != static_cast<std::uint8_t>(value_t::array) && a->kind != static_cast<std::uint8_t>(value_t::null))
|
||||
{
|
||||
throw_type_error(exception_id::push_back_wrong_type, "cannot use push_back() with ", array.type_name());
|
||||
throw_type_error(308, "cannot use push_back() with ", array.type_name());
|
||||
}
|
||||
const encoded e = encode(std::forward<V>(value));
|
||||
if (a->kind == static_cast<std::uint8_t>(value_t::null))
|
||||
@@ -3659,7 +3661,7 @@ class editor
|
||||
node* const a = own(array);
|
||||
if (a->kind != static_cast<std::uint8_t>(value_t::array))
|
||||
{
|
||||
throw_type_error(exception_id::insert_wrong_type, "cannot use insert() with ", array.type_name());
|
||||
throw_type_error(309, "cannot use insert() with ", array.type_name());
|
||||
}
|
||||
check_index(idx, a->len + 1);
|
||||
const encoded e = encode(std::forward<V>(value));
|
||||
@@ -3679,7 +3681,7 @@ class editor
|
||||
node* const o = own(object);
|
||||
if (o->kind != static_cast<std::uint8_t>(value_t::object))
|
||||
{
|
||||
throw_type_error(exception_id::erase_wrong_type, "cannot use erase() with ", object.type_name());
|
||||
throw_type_error(307, "cannot use erase() with ", object.type_name());
|
||||
}
|
||||
for (const node* k = nav::first(m_doc, o), *end = nav::end(m_doc, o); k != end; k = document_data::after(k + 1))
|
||||
{
|
||||
@@ -3697,7 +3699,7 @@ class editor
|
||||
node* const a = own(array);
|
||||
if (a->kind != static_cast<std::uint8_t>(value_t::array))
|
||||
{
|
||||
throw_type_error(exception_id::erase_wrong_type, "cannot use erase() with ", array.type_name());
|
||||
throw_type_error(307, "cannot use erase() with ", array.type_name());
|
||||
}
|
||||
check_index(idx, a->len);
|
||||
node* const h = block_of(m_doc, a, 0);
|
||||
@@ -3720,7 +3722,7 @@ class editor
|
||||
{
|
||||
if (NLOHMANN_VIEW_UNLIKELY(v.m_doc != &m_doc || v.m_node == nullptr))
|
||||
{
|
||||
throw_invalid_iterator(exception_id::iterator_from_other_value, "view does not belong to this document");
|
||||
throw_invalid_iterator(202, "view does not belong to this document");
|
||||
}
|
||||
edit_state_of(m_doc);
|
||||
return const_cast<node*>(v.m_node); // NOLINT(cppcoreguidelines-pro-type-const-cast): the nodes belong to this document
|
||||
@@ -3730,7 +3732,7 @@ class editor
|
||||
{
|
||||
if (idx >= limit)
|
||||
{
|
||||
throw_array_index_out_of_range(idx);
|
||||
throw_out_of_range(401, concat("array index ", std::to_string(idx), " is out of range"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3871,7 +3873,7 @@ class editor
|
||||
{
|
||||
if (NLOHMANN_VIEW_UNLIKELY(v.m_node == nullptr))
|
||||
{
|
||||
throw_type_error(exception_id::type_mismatch, "type must be a value, but is ", "discarded");
|
||||
throw_type_error(302, "type must be a value, but is ", "discarded");
|
||||
}
|
||||
encoded r;
|
||||
if (!is_container(*v.m_node))
|
||||
@@ -4021,7 +4023,7 @@ class editor
|
||||
{
|
||||
if (NLOHMANN_VIEW_UNLIKELY(len >= 0xFFFFFFFFu))
|
||||
{
|
||||
throw_out_of_range(exception_id::document_too_large, "strings of 4 GiB or more are not supported by json_document"); // LCOV_EXCL_LINE
|
||||
throw_out_of_range(416, "strings of 4 GiB or more are not supported by json_document"); // LCOV_EXCL_LINE
|
||||
}
|
||||
node n = plain_node(value_t::string);
|
||||
n.flags = static_cast<std::uint8_t>(n.flags | node_flags::edited);
|
||||
@@ -4096,12 +4098,12 @@ class editor
|
||||
return string_node(s.data(), s.size());
|
||||
}
|
||||
case value_t::binary:
|
||||
throw_type_error(exception_id::document_binary_value, "cannot store a binary value in a json_document", "");
|
||||
throw_type_error(319, "cannot store a binary value in a json_document", "");
|
||||
case value_t::discarded:
|
||||
case value_t::object:
|
||||
case value_t::array:
|
||||
default:
|
||||
throw_type_error(exception_id::type_mismatch, "type must be a value, but is ", "discarded");
|
||||
throw_type_error(302, "type must be a value, but is ", "discarded");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4729,7 +4731,7 @@ inline void compact_nodes(const document_data& d, std::size_t arena_size, std::v
|
||||
inline std::vector<std::uint8_t> save_image(const document_data& d)
|
||||
{
|
||||
#if !NLOHMANN_VIEW_LITTLE_ENDIAN
|
||||
throw_type_error(exception_id::document_image_unsupported, "json_document images need a little-endian target"); // LCOV_EXCL_LINE
|
||||
throw_type_error(320, "json_document images need a little-endian target"); // LCOV_EXCL_LINE
|
||||
#endif
|
||||
const std::size_t arena_size = d.arena_size;
|
||||
std::vector<node> compacted;
|
||||
@@ -4746,7 +4748,7 @@ inline std::vector<std::uint8_t> save_image(const document_data& d)
|
||||
if (NLOHMANN_VIEW_UNLIKELY(text_size >= image_limit || total_arena >= image_limit || count >= image_limit))
|
||||
{
|
||||
// LCOV_EXCL_START (4 GiB)
|
||||
throw_out_of_range(exception_id::document_too_large, "images of 4 GiB or more are not supported by json_document");
|
||||
throw_out_of_range(416, "images of 4 GiB or more are not supported by json_document");
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
image_header h{};
|
||||
@@ -5203,7 +5205,7 @@ inline bool check_image(const node* nodes, std::size_t count, const unsigned cha
|
||||
|
||||
[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_invalid_image(const char* what)
|
||||
{
|
||||
throw_parse_error(exception_id::invalid_document_image, concat("invalid json_document image: ", what));
|
||||
throw_parse_error(116, concat("invalid json_document image: ", what));
|
||||
}
|
||||
|
||||
/// Read an image into d. The text and the decoded strings stay in the image;
|
||||
@@ -5211,7 +5213,7 @@ inline bool check_image(const node* nodes, std::size_t count, const unsigned cha
|
||||
inline void load_image(document_data& d, const std::uint8_t* image, std::size_t size, image_check check)
|
||||
{
|
||||
#if !NLOHMANN_VIEW_LITTLE_ENDIAN
|
||||
throw_type_error(exception_id::document_image_unsupported, "json_document images need a little-endian target"); // LCOV_EXCL_LINE
|
||||
throw_type_error(320, "json_document images need a little-endian target"); // LCOV_EXCL_LINE
|
||||
#endif
|
||||
if (image == nullptr || size < sizeof(image_header))
|
||||
{
|
||||
@@ -5565,7 +5567,7 @@ class view_iterator
|
||||
{
|
||||
if (NLOHMANN_VIEW_UNLIKELY(m_value_offset == 0))
|
||||
{
|
||||
throw_invalid_iterator(exception_id::iterator_key_not_object, "cannot use key() for non-object iterators");
|
||||
throw_invalid_iterator(207, "cannot use key() for non-object iterators");
|
||||
}
|
||||
return string_view_t(m_doc->str(*m_pos), m_pos->len);
|
||||
}
|
||||
@@ -5917,15 +5919,51 @@ enum class pointer_mode
|
||||
contains, ///< as basic_json::contains(): no exceptions at all
|
||||
};
|
||||
|
||||
/// the outcome of reading an array index: detail::parse_array_index(), shared with json_pointer
|
||||
using index_status = detail::array_index_status;
|
||||
/// the outcome of reading an array index from a reference token
|
||||
enum class index_status
|
||||
{
|
||||
ok,
|
||||
leading_zero, ///< parse_error.106
|
||||
not_number, ///< parse_error.109
|
||||
unresolved, ///< out_of_range.404
|
||||
too_large, ///< out_of_range.410
|
||||
};
|
||||
|
||||
/// reads an array index like json_pointer::array_index (RFC 6901, Sect. 4),
|
||||
/// but reports errors instead of throwing them
|
||||
template<typename StringType>
|
||||
index_status array_index(const StringType& s, std::size_t& idx) noexcept
|
||||
{
|
||||
return detail::parse_array_index(s, idx);
|
||||
// as basic_json: the empty token and "-" (past the last element) are
|
||||
// unresolved rather than not a number
|
||||
if (s.empty() || (s.size() == 1 && s[0] == '-'))
|
||||
{
|
||||
return index_status::unresolved;
|
||||
}
|
||||
if (s[0] < '0' || s[0] > '9')
|
||||
{
|
||||
return index_status::not_number;
|
||||
}
|
||||
if (s.size() > 1 && s[0] == '0')
|
||||
{
|
||||
return index_status::leading_zero;
|
||||
}
|
||||
std::uint64_t v = 0;
|
||||
for (const auto c : s)
|
||||
{
|
||||
const auto d = static_cast<unsigned>(static_cast<unsigned char>(c)) - '0';
|
||||
if (d > 9 || v > ((std::numeric_limits<std::uint64_t>::max)() - d) / 10)
|
||||
{
|
||||
return index_status::unresolved; // not a number, or beyond unsigned long long
|
||||
}
|
||||
v = (v * 10) + d;
|
||||
}
|
||||
if (v >= (std::numeric_limits<std::size_t>::max)()) // (std::size_t converts to std::uint64_t implicitly)
|
||||
{
|
||||
return index_status::too_large;
|
||||
}
|
||||
idx = static_cast<std::size_t>(v);
|
||||
return index_status::ok;
|
||||
}
|
||||
|
||||
/// throws the exception json_pointer::array_index throws for this status
|
||||
@@ -5935,15 +5973,15 @@ template<typename StringType>
|
||||
switch (status)
|
||||
{
|
||||
case index_status::leading_zero:
|
||||
throw_parse_error(exception_id::pointer_index_leading_zero, concat("array index '", s, "' must not begin with '0'"));
|
||||
case index_status::not_a_number:
|
||||
throw_parse_error(exception_id::pointer_index_not_a_number, concat("array index '", s, "' is not a number"));
|
||||
case index_status::exceeds_size_type:
|
||||
throw_out_of_range(exception_id::value_out_of_range, concat("array index ", s, " exceeds size_type")); // LCOV_EXCL_LINE
|
||||
throw_parse_error(106, concat("array index '", s, "' must not begin with '0'"));
|
||||
case index_status::not_number:
|
||||
throw_parse_error(109, concat("array index '", s, "' is not a number"));
|
||||
case index_status::too_large:
|
||||
throw_out_of_range(410, concat("array index ", s, " exceeds size_type")); // LCOV_EXCL_LINE
|
||||
case index_status::unresolved:
|
||||
case index_status::ok:
|
||||
default:
|
||||
throw_out_of_range(exception_id::pointer_unresolved, concat("unresolved reference token '", s, "'"));
|
||||
throw_out_of_range(404, concat("unresolved reference token '", s, "'"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5968,7 +6006,7 @@ View resolve_pointer(View cur, const Tokens& tokens, pointer_mode mode)
|
||||
{
|
||||
if (mode == pointer_mode::checked)
|
||||
{
|
||||
throw_key_not_found(token);
|
||||
throw_out_of_range(403, concat("key '", token, "' not found"));
|
||||
}
|
||||
return View();
|
||||
}
|
||||
@@ -5980,7 +6018,7 @@ View resolve_pointer(View cur, const Tokens& tokens, pointer_mode mode)
|
||||
{
|
||||
if (throwing)
|
||||
{
|
||||
throw_array_index_past_end(cur.size());
|
||||
throw_out_of_range(402, concat("array index '-' (", std::to_string(cur.size()), ") is out of range"));
|
||||
}
|
||||
return View();
|
||||
}
|
||||
@@ -5988,7 +6026,7 @@ View resolve_pointer(View cur, const Tokens& tokens, pointer_mode mode)
|
||||
const index_status status = array_index(token, idx);
|
||||
if (status != index_status::ok)
|
||||
{
|
||||
const bool parse_error = status == index_status::leading_zero || status == index_status::not_a_number;
|
||||
const bool parse_error = status == index_status::leading_zero || status == index_status::not_number;
|
||||
if (throwing || (mode == pointer_mode::value && parse_error))
|
||||
{
|
||||
throw_array_index_error(status, token);
|
||||
@@ -5999,7 +6037,7 @@ View resolve_pointer(View cur, const Tokens& tokens, pointer_mode mode)
|
||||
{
|
||||
if (mode == pointer_mode::checked)
|
||||
{
|
||||
throw_array_index_out_of_range(idx);
|
||||
throw_out_of_range(401, concat("array index ", std::to_string(idx), " is out of range"));
|
||||
}
|
||||
return View();
|
||||
}
|
||||
@@ -6009,7 +6047,7 @@ View resolve_pointer(View cur, const Tokens& tokens, pointer_mode mode)
|
||||
{
|
||||
if (throwing)
|
||||
{
|
||||
throw_out_of_range(exception_id::pointer_unresolved, concat("unresolved reference token '", token, "'"));
|
||||
throw_out_of_range(404, concat("unresolved reference token '", token, "'"));
|
||||
}
|
||||
return View();
|
||||
}
|
||||
@@ -6918,17 +6956,21 @@ class view_serializer
|
||||
m_out.put("null");
|
||||
return;
|
||||
}
|
||||
write_float(x, std::integral_constant<bool, ::nlohmann::detail::has_native_float_format<number_float_t>::value>());
|
||||
static constexpr bool is_ieee_single_or_double
|
||||
= std::numeric_limits<number_float_t>::is_iec559
|
||||
&& ((std::numeric_limits<number_float_t>::digits == 24 && std::numeric_limits<number_float_t>::max_exponent == 128)
|
||||
|| (std::numeric_limits<number_float_t>::digits == 53 && std::numeric_limits<number_float_t>::max_exponent == 1024));
|
||||
write_float(x, std::integral_constant<bool, is_ieee_single_or_double>());
|
||||
}
|
||||
|
||||
void write_float(number_float_t x, std::true_type /*has_native_float_format*/)
|
||||
void write_float(number_float_t x, std::true_type /*is_ieee_single_or_double*/)
|
||||
{
|
||||
std::array<char, 64> buf{};
|
||||
const char* const end = ::nlohmann::detail::to_chars(buf.data(), buf.data() + buf.size(), x);
|
||||
m_out.put(buf.data(), static_cast<std::size_t>(end - buf.data()));
|
||||
}
|
||||
|
||||
void write_float(number_float_t x, std::false_type /*has_native_float_format*/)
|
||||
void write_float(number_float_t x, std::false_type /*is_ieee_single_or_double*/)
|
||||
{
|
||||
// other types (e.g. long double) are rare: the library writes them
|
||||
const string_t s = BasicJsonType(x).dump();
|
||||
@@ -7055,8 +7097,8 @@ class view_serializer
|
||||
|
||||
void write_u_escape(std::uint32_t u)
|
||||
{
|
||||
std::array<char, 6> e = {{'\\', 'u', 0, 0, 0, 0}};
|
||||
hex4_lower(static_cast<std::uint16_t>(u), &e[2]);
|
||||
static constexpr const char* hex = "0123456789abcdef";
|
||||
const std::array<char, 6> e = {{'\\', 'u', hex[(u >> 12u) & 0xFu], hex[(u >> 8u) & 0xFu], hex[(u >> 4u) & 0xFu], hex[u & 0xFu]}};
|
||||
m_out.put(e.data(), e.size());
|
||||
}
|
||||
|
||||
@@ -7253,7 +7295,7 @@ NLOHMANN_VIEW_ALWAYS_INLINE T arithmetic_value(const document_data& d, const nod
|
||||
case value_t::binary:
|
||||
case value_t::discarded:
|
||||
default:
|
||||
throw_type_error(exception_id::type_mismatch, "type must be number, but is ", value_type_name(static_cast<value_t>(n.kind)));
|
||||
throw_type_error(302, "type must be number, but is ", value_type_name(static_cast<value_t>(n.kind)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7263,7 +7305,7 @@ std::vector<U, A> vector_value(const View& v)
|
||||
{
|
||||
if (NLOHMANN_VIEW_UNLIKELY(!v.is_array()))
|
||||
{
|
||||
throw_type_error(exception_id::type_mismatch, "type must be array, but is ", v.type_name());
|
||||
throw_type_error(302, "type must be array, but is ", v.type_name());
|
||||
}
|
||||
std::vector<U, A> r;
|
||||
r.reserve(v.size());
|
||||
@@ -7281,7 +7323,7 @@ Map map_value(const View& v)
|
||||
{
|
||||
if (NLOHMANN_VIEW_UNLIKELY(!v.is_object()))
|
||||
{
|
||||
throw_type_error(exception_id::type_mismatch, "type must be object, but is ", v.type_name());
|
||||
throw_type_error(302, "type must be object, but is ", v.type_name());
|
||||
}
|
||||
Map r;
|
||||
for (auto it = v.begin(); it != v.end(); ++it)
|
||||
@@ -7520,7 +7562,7 @@ class basic_json_view
|
||||
{
|
||||
return {};
|
||||
}
|
||||
detail::view::throw_type_error(detail::exception_id::subscript_wrong_type, "cannot use operator[] with a string argument with ", type_name());
|
||||
detail::view::throw_type_error(305, "cannot use operator[] with a string argument with ", type_name());
|
||||
}
|
||||
return lookup(key);
|
||||
}
|
||||
@@ -7546,7 +7588,7 @@ class basic_json_view
|
||||
{
|
||||
return {};
|
||||
}
|
||||
detail::view::throw_type_error(detail::exception_id::subscript_wrong_type, "cannot use operator[] with a numeric argument with ", type_name());
|
||||
detail::view::throw_type_error(305, "cannot use operator[] with a numeric argument with ", type_name());
|
||||
}
|
||||
return idx < m_node->len ? basic_json_view(m_doc, navigation::value(detail::view::element_at<Editable>(*m_doc, m_node, idx))) : basic_json_view();
|
||||
}
|
||||
@@ -7579,12 +7621,12 @@ class basic_json_view
|
||||
{
|
||||
if (NLOHMANN_VIEW_UNLIKELY(!is_object()))
|
||||
{
|
||||
detail::view::throw_type_error(detail::exception_id::at_wrong_type, "cannot use at() with ", type_name());
|
||||
detail::view::throw_type_error(304, "cannot use at() with ", type_name());
|
||||
}
|
||||
const basic_json_view r = lookup(key);
|
||||
if (NLOHMANN_VIEW_UNLIKELY(r.is_discarded()))
|
||||
{
|
||||
detail::view::throw_key_not_found(std::string(key.data(), key.size()));
|
||||
detail::view::throw_out_of_range(403, detail::concat("key '", std::string(key.data(), key.size()), "' not found"));
|
||||
}
|
||||
return r;
|
||||
}
|
||||
@@ -7605,11 +7647,11 @@ class basic_json_view
|
||||
{
|
||||
if (NLOHMANN_VIEW_UNLIKELY(!is_array()))
|
||||
{
|
||||
detail::view::throw_type_error(detail::exception_id::at_wrong_type, "cannot use at() with ", type_name());
|
||||
detail::view::throw_type_error(304, "cannot use at() with ", type_name());
|
||||
}
|
||||
if (NLOHMANN_VIEW_UNLIKELY(idx >= m_node->len))
|
||||
{
|
||||
detail::view::throw_array_index_out_of_range(idx);
|
||||
detail::view::throw_out_of_range(401, detail::concat("array index ", std::to_string(idx), " is out of range"));
|
||||
}
|
||||
return basic_json_view(m_doc, navigation::value(detail::view::element_at<Editable>(*m_doc, m_node, idx)));
|
||||
}
|
||||
@@ -7637,7 +7679,7 @@ class basic_json_view
|
||||
{
|
||||
if (NLOHMANN_VIEW_UNLIKELY(!is_object()))
|
||||
{
|
||||
detail::view::throw_type_error(detail::exception_id::value_wrong_type, "cannot use value() with ", type_name());
|
||||
detail::view::throw_type_error(306, "cannot use value() with ", type_name());
|
||||
}
|
||||
const basic_json_view r = lookup(key);
|
||||
return r.is_discarded() ? default_value : r.template get<T>();
|
||||
@@ -7656,7 +7698,7 @@ class basic_json_view
|
||||
{
|
||||
if (NLOHMANN_VIEW_UNLIKELY(!is_structured()))
|
||||
{
|
||||
detail::view::throw_type_error(detail::exception_id::value_wrong_type, "cannot use value() with ", type_name());
|
||||
detail::view::throw_type_error(306, "cannot use value() with ", type_name());
|
||||
}
|
||||
const basic_json_view r = detail::view::resolve_pointer(*this, detail::json_pointer_access::reference_tokens(ptr), detail::view::pointer_mode::value);
|
||||
return r.is_discarded() ? default_value : r.template get<T>();
|
||||
@@ -7674,7 +7716,7 @@ class basic_json_view
|
||||
const iterator it = begin();
|
||||
if (NLOHMANN_VIEW_UNLIKELY(it == end()))
|
||||
{
|
||||
detail::view::throw_invalid_iterator(detail::exception_id::iterator_value_unavailable, "cannot get value");
|
||||
detail::view::throw_invalid_iterator(214, "cannot get value");
|
||||
}
|
||||
return *it;
|
||||
}
|
||||
@@ -7826,7 +7868,7 @@ class basic_json_view
|
||||
{
|
||||
if (NLOHMANN_VIEW_UNLIKELY(!is_string()))
|
||||
{
|
||||
detail::view::throw_type_error(detail::exception_id::type_mismatch, "type must be string, but is ", type_name());
|
||||
detail::view::throw_type_error(302, "type must be string, but is ", type_name());
|
||||
}
|
||||
return {m_doc->str(*m_node), m_node->len};
|
||||
}
|
||||
@@ -7838,7 +7880,7 @@ class basic_json_view
|
||||
{
|
||||
if (NLOHMANN_VIEW_UNLIKELY(!is_number()))
|
||||
{
|
||||
detail::view::throw_type_error(detail::exception_id::type_mismatch, "type must be number, but is ", type_name());
|
||||
detail::view::throw_type_error(302, "type must be number, but is ", type_name());
|
||||
}
|
||||
return {m_doc->str(*m_node), detail::view::number_length(*m_node)};
|
||||
}
|
||||
@@ -8048,7 +8090,7 @@ class basic_json_view
|
||||
{
|
||||
if (NLOHMANN_VIEW_UNLIKELY(!is_boolean()))
|
||||
{
|
||||
detail::view::throw_type_error(detail::exception_id::type_mismatch, "type must be boolean, but is ", type_name());
|
||||
detail::view::throw_type_error(302, "type must be boolean, but is ", type_name());
|
||||
}
|
||||
return (m_node->flags & detail::view::node_flags::is_true) != 0;
|
||||
}
|
||||
@@ -8058,7 +8100,7 @@ class basic_json_view
|
||||
{
|
||||
if (NLOHMANN_VIEW_UNLIKELY(m_node == nullptr))
|
||||
{
|
||||
detail::view::throw_type_error(detail::exception_id::type_mismatch, "type must be number, but is ", type_name());
|
||||
detail::view::throw_type_error(302, "type must be number, but is ", type_name());
|
||||
}
|
||||
return detail::view::arithmetic_value<T, BasicJsonType>(*m_doc, *m_node);
|
||||
}
|
||||
@@ -8067,7 +8109,7 @@ class basic_json_view
|
||||
{
|
||||
if (NLOHMANN_VIEW_UNLIKELY(!is_null()))
|
||||
{
|
||||
detail::view::throw_type_error(detail::exception_id::type_mismatch, "type must be null, but is ", type_name());
|
||||
detail::view::throw_type_error(302, "type must be null, but is ", type_name());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
@@ -8376,7 +8418,7 @@ class basic_json_document
|
||||
{
|
||||
if (NLOHMANN_VIEW_UNLIKELY(!m_data || m_data->discarded))
|
||||
{
|
||||
detail::view::throw_type_error(detail::exception_id::document_image_unsupported, "cannot save a discarded json_document");
|
||||
detail::view::throw_type_error(320, "cannot save a discarded json_document");
|
||||
}
|
||||
return detail::view::save_image(*m_data);
|
||||
}
|
||||
@@ -8477,7 +8519,7 @@ class basic_json_document
|
||||
const std::size_t idx = pointer_index(token);
|
||||
if (idx >= 0xFFFFFFFFu)
|
||||
{
|
||||
detail::view::throw_array_index_out_of_range(idx);
|
||||
detail::view::throw_out_of_range(401, detail::concat("array index ", std::to_string(idx), " is out of range"));
|
||||
}
|
||||
for (std::size_t i = 0; i < idx; ++i)
|
||||
{
|
||||
@@ -8533,7 +8575,7 @@ class basic_json_document
|
||||
{
|
||||
if (ptr.empty())
|
||||
{
|
||||
detail::view::throw_out_of_range(detail::exception_id::patch_on_root, "JSON pointer has no parent");
|
||||
detail::view::throw_out_of_range(405, "JSON pointer has no parent");
|
||||
}
|
||||
const view_type parent = root().at(ptr.parent_pointer());
|
||||
const auto& token = ptr.back();
|
||||
@@ -8553,7 +8595,7 @@ class basic_json_document
|
||||
static_assert(Editable, "only an editable document can be edited: use basic_json_document<BasicJsonType, true> (json_editable_document)");
|
||||
if (NLOHMANN_VIEW_UNLIKELY(!m_data || m_data->discarded))
|
||||
{
|
||||
detail::view::throw_invalid_iterator(detail::exception_id::iterator_from_other_value, "view does not belong to this document");
|
||||
detail::view::throw_invalid_iterator(202, "view does not belong to this document");
|
||||
}
|
||||
return detail::view::editor<BasicJsonType, view_type>(*m_data);
|
||||
}
|
||||
@@ -8570,7 +8612,7 @@ class basic_json_document
|
||||
{
|
||||
if (idx < 0)
|
||||
{
|
||||
detail::view::throw_array_index_out_of_range(idx);
|
||||
detail::view::throw_out_of_range(401, detail::concat("array index ", std::to_string(idx), " is out of range"));
|
||||
}
|
||||
return static_cast<std::size_t>(idx);
|
||||
}
|
||||
|
||||
@@ -2042,118 +2042,3 @@ TEST_CASE("string scanning kernels")
|
||||
CHECK(nlohmann::detail::read_eight_bytes(bytes.data() + 8) == 0xFF0F0E0D0C0B0A09u);
|
||||
CHECK(nlohmann::detail::read_eight_bytes(reinterpret_cast<const char*>(bytes.data()) + 3) == 0x0B0A090807060504u);
|
||||
}
|
||||
|
||||
TEST_CASE("find_ascii_copyable_run agrees with is_ascii_copyable for every byte")
|
||||
{
|
||||
// the SWAR mask in find_ascii_copyable_run() encodes the predicate a
|
||||
// second time; check every byte value at every position of a 24-byte buffer
|
||||
// (three words) and for several lengths, with the byte at the end or inside
|
||||
for (unsigned int value = 0; value < 256; ++value)
|
||||
{
|
||||
for (std::size_t position = 0; position < 24; ++position)
|
||||
{
|
||||
std::array<unsigned char, 24> buffer{};
|
||||
buffer.fill(static_cast<unsigned char>('a'));
|
||||
buffer[position] = static_cast<unsigned char>(value);
|
||||
const bool copyable = nlohmann::detail::is_ascii_copyable(static_cast<unsigned char>(value));
|
||||
for (const std::size_t n :
|
||||
{
|
||||
position, position + 1, std::size_t{24}
|
||||
})
|
||||
{
|
||||
if (n > 24)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const std::size_t expected = (copyable || n <= position) ? n : position;
|
||||
CAPTURE(value)
|
||||
CAPTURE(position)
|
||||
CAPTURE(n)
|
||||
CHECK(nlohmann::detail::find_ascii_copyable_run(buffer.data(), n) == expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("UTF-8 validators accept the same sequences")
|
||||
{
|
||||
// The library checks UTF-8 in several places: detail::decode (serializer),
|
||||
// the per-lead-byte switch of the lexer (json::accept), validate_one_utf8
|
||||
// (bulk string scan), utf8_shape, and the byte path of the BON8 reader.
|
||||
// They must accept exactly the same sequences (RFC 3629, section 4).
|
||||
using nlohmann::json;
|
||||
|
||||
// bytes for the third and fourth position: the first two pairs are checked
|
||||
// against all validators, the others only against the cheap ones
|
||||
const std::array<std::array<unsigned char, 2>, 6> tails =
|
||||
{
|
||||
{
|
||||
{{0x80, 0x80}}, {{0xBF, 0xBF}}, {{0x7F, 0x80}}, {{0xC0, 0x80}}, {{0x80, 0x7F}}, {{0x80, 0xC0}}
|
||||
}
|
||||
};
|
||||
|
||||
for (unsigned lead = 0x80; lead <= 0xFF; ++lead)
|
||||
{
|
||||
const auto shape = nlohmann::detail::utf8_shape(static_cast<std::uint8_t>(lead));
|
||||
for (unsigned second = 0; second <= 0xFF; ++second)
|
||||
{
|
||||
for (std::size_t t = 0; t < tails.size(); ++t)
|
||||
{
|
||||
const std::array<unsigned char, 4> bytes = {{static_cast<unsigned char>(lead), static_cast<unsigned char>(second), tails[t][0], tails[t][1]}};
|
||||
|
||||
// expectation from the shape: the sequence is bytes[0..length)
|
||||
const std::size_t length = shape.length;
|
||||
bool expected = length >= 2 && second >= shape.second_lo && second <= shape.second_hi;
|
||||
for (std::size_t i = 2; expected && i < length; ++i)
|
||||
{
|
||||
expected = bytes[i] >= 0x80 && bytes[i] <= 0xBF;
|
||||
}
|
||||
|
||||
// the DFA of the serializer
|
||||
bool dfa = length >= 2;
|
||||
if (dfa)
|
||||
{
|
||||
std::uint8_t state = nlohmann::detail::UTF8_ACCEPT;
|
||||
std::uint32_t codep = 0;
|
||||
for (std::size_t i = 0; i < length; ++i)
|
||||
{
|
||||
nlohmann::detail::decode(state, codep, bytes[i]);
|
||||
}
|
||||
dfa = state == nlohmann::detail::UTF8_ACCEPT;
|
||||
}
|
||||
|
||||
// the bulk path: exactly the sequence, and one byte short
|
||||
const std::size_t bulk = nlohmann::detail::validate_one_utf8(bytes.data(), length);
|
||||
const std::size_t bulk_short = length > 0 ? nlohmann::detail::validate_one_utf8(bytes.data(), length - 1) : 0;
|
||||
|
||||
CAPTURE(lead)
|
||||
CAPTURE(second)
|
||||
CAPTURE(t)
|
||||
CHECK((dfa == expected && bulk == (expected ? length : 0) && bulk_short == 0));
|
||||
|
||||
if (t < 2 && length >= 2)
|
||||
{
|
||||
// the lexer's byte path
|
||||
const std::string sequence(reinterpret_cast<const char*>(bytes.data()), length); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||
CHECK(json::accept("\"" + sequence + "\"") == expected);
|
||||
|
||||
// the byte path of the BON8 reader (a string ends at 0xFF)
|
||||
if (lead >= 0xC2 && lead <= 0xF7)
|
||||
{
|
||||
std::vector<std::uint8_t> packed(bytes.begin(), bytes.begin() + static_cast<std::ptrdiff_t>(length));
|
||||
packed.push_back(0xFF);
|
||||
const json j = json::from_bon8(packed, true, false);
|
||||
if (expected)
|
||||
{
|
||||
CHECK(j == json(sequence));
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECK_FALSE((j.is_string() && j.get<std::string>() == sequence));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
# astyle is deliberately pinned: newer versions reformat the code, and this version
|
||||
# defines the formatting that `make amalgamate` produces and check_amalgamation.yml enforces.
|
||||
# Dependabot is told to ignore it in .github/dependabot.yml.
|
||||
astyle==3.4.13
|
||||
@@ -1,4 +1,62 @@
|
||||
# astyle is deliberately pinned: newer versions reformat the code, and this version
|
||||
# defines the formatting that `make amalgamate` produces and check_amalgamation.yml enforces.
|
||||
# Dependabot is told to ignore it in .github/dependabot.yml.
|
||||
astyle==3.4.13
|
||||
#
|
||||
# This file is autogenerated by pip-compile with Python 3.14
|
||||
# by the following command:
|
||||
#
|
||||
# pip-compile --generate-hashes --no-emit-index-url --output-file=tools/astyle/requirements.txt --strip-extras tools/astyle/requirements.in
|
||||
#
|
||||
astyle==3.4.13 \
|
||||
--hash=sha256:0456c5bb1bed18b30f7d9e9c7d6499684a62cc808949173d52bf6def655f48b9 \
|
||||
--hash=sha256:061b320ea1d265ac01babdd82b056c7b55913c726c770cff26191af78884c5ab \
|
||||
--hash=sha256:0ad050135504994bee50233db59bedf95f1343977118e27f495eaf2f401ecfe5 \
|
||||
--hash=sha256:162cb29abc5ddd5d03ab48b15fd8603baf04c1ba5913e1ef475e09818744216b \
|
||||
--hash=sha256:181d7b6da8eef2cc4dc757dc3381f0d1f2932d6ceb2f6ce5bea432de698e9a8f \
|
||||
--hash=sha256:191abde742ff5ae788a489722a669f7bc0b18cefc1020b617bc0e84361b71a58 \
|
||||
--hash=sha256:1bf59aa3006546d795443d1ff69682f989fe95e4c71850c67f8251eab183fee3 \
|
||||
--hash=sha256:31c86eb8aae517b9cf1debd325ea5f6ac653d52787d5c5c9d56197afc9bc97e3 \
|
||||
--hash=sha256:35b979a62d1499b2daf241517ddb0aee3ab4b82dc82aa3f8604a2db1e9cc4e66 \
|
||||
--hash=sha256:36a9f14f88c6bc3c4240f977a3f3fd3d0eac84373bc10004258712e6b3cb08f2 \
|
||||
--hash=sha256:46ad33bf795e68f4b18bf947cc7276e26579ab6df89574617c7797b0ed2cf473 \
|
||||
--hash=sha256:4803a16330b67b98c994b87b984af00d7dcaff0d4af69d17ab0fd705141725fe \
|
||||
--hash=sha256:481b6e195dfa56380bb19cd134dc698cd7f5732d7d5cd781c13b5c15112b8fc2 \
|
||||
--hash=sha256:48f0218622e807360d50d9d419e527d8e4c0f7f49c51c6fac3d4c6f343a0573e \
|
||||
--hash=sha256:4a3179783910ea386125d88a16e86cc6cfee41c513d0d8e692bdabd17577ce93 \
|
||||
--hash=sha256:508d5debe7874a5dd1b491bbae0c48ebc94aa3736545718208bb33c648962e62 \
|
||||
--hash=sha256:55b4335f20b717f6ec57a237d29246e3c7196bd014e509f2aa376aca36ea9967 \
|
||||
--hash=sha256:56714bb2e2a0fe3911474b66e563aac508d97a9f48d5e55c8e182b1675db0e05 \
|
||||
--hash=sha256:599ab574cd53b7105877d5e1b6ab0c933b771e4b2343b0bc54aa06af64653449 \
|
||||
--hash=sha256:5b183422a1edbce971758b2b4a2b180127d9e8c46119183d410c77ec1d42a516 \
|
||||
--hash=sha256:5c6de6933f7818b4380b309704c45da9330d906bbe479bb76023261e63545900 \
|
||||
--hash=sha256:5f7efd14d9aaee69a4a33e60722a8a09d0b7cb834aed698ce0793cecec4e7452 \
|
||||
--hash=sha256:66ecd8a2a778bc78bdb2df4f5f371f100c6f180ae859e7d414f374eb2636b531 \
|
||||
--hash=sha256:6fa1c231c82a0ea6ccfa8e19276dec4753d9e084229d606f9522260fe37b955f \
|
||||
--hash=sha256:76d53ee9e07f8ad32050363c8402923095020856c44015968b4dd9ea5b7a18a1 \
|
||||
--hash=sha256:81f0173912da9cf687cf2ed1361026fd957fcfd5b9673803c50265db63427d7f \
|
||||
--hash=sha256:81f4345adbbb463a2d5ce314a1ca689a0aa05a6240c86fc0225e92474e3f44b4 \
|
||||
--hash=sha256:840e1d0436a1a3793ebe14c95fefe797f77bd78d6194a7c0bcdde705cb583403 \
|
||||
--hash=sha256:8d6a69f9b316b3941cd43f16eb109f738bb171963408b5f135521ef70ad364f1 \
|
||||
--hash=sha256:8e2879d770e3fbf53ff62728f4d62a6edcacd1ed3d62b36c012706d778048340 \
|
||||
--hash=sha256:900e442ed002e5e05c31628fac817db62f6d28e1cc2426040c69a10d39f92651 \
|
||||
--hash=sha256:920c8516f420da68765ced84692e550d0f34932663219c48db9b0c79544002d3 \
|
||||
--hash=sha256:9417573994a9d264f873cd6122f9e0ea7044d628d9a8b9e84dbbc85b0e7c2880 \
|
||||
--hash=sha256:95eaf11aadd770bccd833c8ffe222ea6055fe5c2c4b3974cb43f3366913b62a4 \
|
||||
--hash=sha256:9b7471f4a5f174604cdfdf4f9091ba319edb52471e07672ea71745157d121fe7 \
|
||||
--hash=sha256:9e016b099f809c8d4894fa6d36ef925973f52ebc50136306b1b4b9ce65094063 \
|
||||
--hash=sha256:9e04dbd51f564316a4879448258d9b9e68ade9b062351ea6fc7356d0f42471a0 \
|
||||
--hash=sha256:a002a3a1ac187ba1b441f678aca16b4ecea47a1f3d2462c3ec9c59e71e6083b7 \
|
||||
--hash=sha256:a177039c38f6f6a43f46bcd3571756be385c0c47462e6a06eb5950397d0bdcbe \
|
||||
--hash=sha256:a8963c261b980a0c12fc1523f86eab3edc67940c114ea544b21d2b44e9196d23 \
|
||||
--hash=sha256:a97f8ef2a054ec067e3af65bc5b145c51b6c2a5f2efe997a72a9d958c3ab6d1a \
|
||||
--hash=sha256:aa8ba72c1349b7e45d1ce01d69daecfac680713624d27d83ebf2a9d1b4984f65 \
|
||||
--hash=sha256:abba627fb930a5398414ba398169f52e3865855501b5ee5430bb53d0974074c6 \
|
||||
--hash=sha256:ac8db98b392b609f36cd7d6e7f18a247376723b4397578a6f450a445a9dacb3d \
|
||||
--hash=sha256:c9d4b1a0a7fbc35b96273695143c791df9c1a4dcab0815ae6d9db8483314430f \
|
||||
--hash=sha256:cf247a3bca29f43398af4e20c7c922b7f5130a22f306f068ad038b94e72854d6 \
|
||||
--hash=sha256:d3da892b9ec2932a2cb49941aae79447f6baafe5023ee31680c9cabcc471b451 \
|
||||
--hash=sha256:db8c4555b66067aba253c2f32dc4ff6ee92882dd3e95a00ebb3bbf145efe067d \
|
||||
--hash=sha256:dde61d5acb6008ebefcecc37625b7f2776df2f368f92d47714e5f50276347c9f \
|
||||
--hash=sha256:dede5398c41b3a00ad0a9f77c57c05444640f8b28500b9bb4624ab6f2d0a90b7 \
|
||||
--hash=sha256:e3cc45784dbac7ade37967e24cbd6b5b89517f3df8fbed276dffffdcf289f04d \
|
||||
--hash=sha256:eb4d38f4a6be78cd28ce44397a1244c08af15c1a00293c40634c2104b44c943c \
|
||||
--hash=sha256:f9e6cd9ee60a8c42eab85cb57e0c82b1c947261b71f5091bbb9f61e864c64e07 \
|
||||
--hash=sha256:fb1ddc816b483c83de15db98847a5d2dd3ba9df9f4a937ad47aabadeeaeed58c
|
||||
# via -r tools/astyle/requirements.in
|
||||
@@ -139,12 +139,9 @@ if __name__ == '__main__':
|
||||
|
||||
namespaces += [f'{namespaces[0]}::{ns}' for ns in inline_namespaces]
|
||||
|
||||
# The template renders a local XML file, not a web response, and autoescape is enabled.
|
||||
# nosemgrep: python.flask.security.xss.audit.direct-use-of-jinja2.direct-use-of-jinja2
|
||||
env = jinja2.Environment(loader=jinja2.FileSystemLoader(searchpath=sys.path[0]), autoescape=True, trim_blocks=True,
|
||||
lstrip_blocks=True, keep_trailing_newline=True)
|
||||
template = env.get_template('nlohmann_json.natvis.j2')
|
||||
# nosemgrep: python.flask.security.xss.audit.direct-use-of-jinja2.direct-use-of-jinja2
|
||||
natvis = template.render(namespaces=namespaces)
|
||||
|
||||
output = args.output or os.path.join(args.repo_root, 'nlohmann_json.natvis')
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
jinja2==3.1.6
|
||||
@@ -1 +1,163 @@
|
||||
jinja2==3.1.6
|
||||
#
|
||||
# This file is autogenerated by pip-compile with Python 3.14
|
||||
# by the following command:
|
||||
#
|
||||
# pip-compile --generate-hashes --no-emit-index-url --output-file=tools/generate_natvis/requirements.txt --strip-extras tools/generate_natvis/requirements.in
|
||||
#
|
||||
jinja2==3.1.6 \
|
||||
--hash=sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d \
|
||||
--hash=sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67
|
||||
# via -r tools/generate_natvis/requirements.in
|
||||
markupsafe==3.0.4 \
|
||||
--hash=sha256:007e1ffd9bf65bb6ee96df7b258fc632a4868dd5566037986c64781f35a36e98 \
|
||||
--hash=sha256:02fa4acbc6a3fc5c693c34d4dd8c1130b7fe99cc915181b0ddd6f72aeb296002 \
|
||||
--hash=sha256:03470d1a8268e692ecf79ecd565593e59d44219377a7ead61f1f1b94c1f7ff6b \
|
||||
--hash=sha256:04e7902ba80ee4bac1d50a549606527a1dcf0476cd81403db41099d3b60ec653 \
|
||||
--hash=sha256:051417f74bcaaefa316276e0ff723f541616ca51043d070da00249d9bddd3e3c \
|
||||
--hash=sha256:05295589e619b9bed252a86b532b8e27350abc372d18ba89b59375325e91ec1e \
|
||||
--hash=sha256:06de8ef6331f6e822c28d577dc8bf43fe398800477c49498f38fc38b67ff33fc \
|
||||
--hash=sha256:0764a13d34cae40db7bbf3a09b7e9b491bf4603e20b263a7a9d6b8e324975d0a \
|
||||
--hash=sha256:077293e425f28ec737dbcad442a71752e28f8ae27cde3d68acd1fb212091cd92 \
|
||||
--hash=sha256:0930db9bdc62d22944e10b066448bb65dc9abe9112880c7cab8da54db4284d5f \
|
||||
--hash=sha256:0cee7cb0f9a1b6892ea482237d9403b3d1b4603aee057d0ff01f0fac2d019a97 \
|
||||
--hash=sha256:0d9c47709875fdb321452056622e930c52afbc07a7d780762fbb8b4d91ce6fa4 \
|
||||
--hash=sha256:11935df9bf455ed0c04eb87bcd720f02b1fe5e02128a9430f23aed6f93336fc7 \
|
||||
--hash=sha256:12a606a492de952afcb43b59a14aaaaad120e708d3663dd0fdf2d738d427a691 \
|
||||
--hash=sha256:14bd2d845d62ab678eaf81da89d7b621b51756c72346745c1a594c09d49207a2 \
|
||||
--hash=sha256:15ba9e28640feef770374b116a6f019c21f52404aeabe516aa7f800587b98cfc \
|
||||
--hash=sha256:18a801868a884f216e784d7d14db2a4077143ce7610440aee2ce8f734e7cfcde \
|
||||
--hash=sha256:1c0df495a977d10460a94941799c72d5b5ab03d3858d949b55b5a66c8f371c99 \
|
||||
--hash=sha256:1caa2fa5a6184fb233153b35f654e6687bd555476f6170f29d8ee9be1a8b0af9 \
|
||||
--hash=sha256:1e1451fab512d1bcc3dc26988ec1edb0b82c2db909132872cd9356070a6b63df \
|
||||
--hash=sha256:1f1f9477e174582b0a1b583d60b66e1f2cf5d3fe12cee985e4aedf44766600e5 \
|
||||
--hash=sha256:2628d3a8cb648ecebb3c5d6b0a1052d400e4d8b7ac0fb786be8d285b50040d17 \
|
||||
--hash=sha256:26e9867520db70d37f7fb421a7f0d8adb40171011fb84ce869afa1a83370dfa8 \
|
||||
--hash=sha256:2a6ef68ae94aed8721934072b27a3b654ea2100b97e4ab864cf1489c90926fbc \
|
||||
--hash=sha256:2b2b1e18af909b448bb3cf9e3433366f7a8726271fc214e8b10e0f62a78c724b \
|
||||
--hash=sha256:2cb3dd71fc6be918ad4264346a8ed69485f9b7ed7bf35495d8e22807cd6b8bea \
|
||||
--hash=sha256:2d1b7d9308288661f56672b1b157d75fc536714d3638487bbea17b6318a78248 \
|
||||
--hash=sha256:2dad610540cb2e6272855c178f08ae9a1c7ac258a7fb71660553a5f104b42741 \
|
||||
--hash=sha256:2e5a7cd7fdd14fcb1ae5d7d8bf23d24fbd1daefd1fbca2580132e1ea75f098b5 \
|
||||
--hash=sha256:2e9ad7dd851bf45fab9f75cbff4cb493fee9979e8d8c7c9c3ee119022518edd6 \
|
||||
--hash=sha256:340cbb1957ba99929cbf19a75626d36ba1ae21d1730b287d1cf7f824a20c4fc7 \
|
||||
--hash=sha256:34bdde374c5932765d7dc685c4a1d191a3207852d67e8e0a9eb6ea85156181f1 \
|
||||
--hash=sha256:353bd63081912ab8cfa6a0c7d185934cdf8426f04c618bba6bc4b394f2069b67 \
|
||||
--hash=sha256:387d8cd30e69b3f0a72877b9ae717033396404e19095b17fe89753a981fda44f \
|
||||
--hash=sha256:3882fb412298575bae3b9c46868251f15cc69307359f87bb1b382e53d6e5a2c9 \
|
||||
--hash=sha256:38fc55594dab834470b6733dead2ee9e3f657fb0608c769dcafa0ba5ab52f45c \
|
||||
--hash=sha256:396ec4e65cc889f69786b3b89478b471cee5a3bcf468b9d9bb03e1a30fb291fc \
|
||||
--hash=sha256:39dbacefc411633db5b4378b066a9aca70a3d7e2922c9e578d825f844026eeba \
|
||||
--hash=sha256:3a93d9616ddecfb393727a0041a562cf0b15a244e20f2bd25efc7949be4c4f17 \
|
||||
--hash=sha256:3d23795802fc8bd72534836d64489bbf0f67c088959091bdb22e10735a5107bf \
|
||||
--hash=sha256:434139499bb20b502ed3baa1f169e618f924a97e7a777fea1a49446d80106cf6 \
|
||||
--hash=sha256:436e3ffc6310d3c41878c601db29098102fe5d8a467c49da4a4125254e0980f2 \
|
||||
--hash=sha256:489505b03f692c3f376394e49194fa7a7f9e8558d6e293a7056a0032b0c38163 \
|
||||
--hash=sha256:4a540e2d3192792fc84eced57bef37851ccb2b41f73291bb17408eea77bcd278 \
|
||||
--hash=sha256:4a7cdc2a420ca01058182da4253329764d4bfa055564d1eced90e6ba1e8b1d3d \
|
||||
--hash=sha256:4bced6e2a6dba6a28f7dd3c6ce14df1b2dd495923f16ea484cad03decd463b2b \
|
||||
--hash=sha256:4cf3468d5ec187ffffcaca8e61929a37448f215dafc1386a12c750a72fe53634 \
|
||||
--hash=sha256:4e2c4809c14559aa7ef426f27fb35afbb38104c349a903bf8f3600456764bb38 \
|
||||
--hash=sha256:4ed644d75aa94a2baf7ec3a96eaa160ea58c742eb9d27c6506053c5c40fc84ed \
|
||||
--hash=sha256:4f6e0852a0283b1b1fd776eeb7b766a5f440b3e2bd31ab51af3b400585f3965c \
|
||||
--hash=sha256:5066b244f576f91afc8ee3ba029a89f99d39c79b1853fe9d39bea9f0afbec148 \
|
||||
--hash=sha256:5086f9975abb1ab531ee6afca1761e4b59a19b446f3f6522ed776963228cfe5a \
|
||||
--hash=sha256:50b5bedc9ed8a94fc8857a42ef4f84a81ea88f8d4f05dc8705fb23ee6d8dcca7 \
|
||||
--hash=sha256:52704c5d36eb6dda8866493decd61111fff86244c9b1ad225ca01b9e91e5970f \
|
||||
--hash=sha256:55ffd6ce583d97dc71dc92e930324c8c0d25aea7e3ade6ae54ef77cedb096811 \
|
||||
--hash=sha256:569d65055d367e3dcdf30c3f41119467b73d9ee9faf332bdf40402644f5ac08e \
|
||||
--hash=sha256:57f9947a7e57a081c1e3e0a2dd0d2dcf290a4531450e6f611e30084c222a7295 \
|
||||
--hash=sha256:5989cb26b2e1efc6a42216a9f6b5ee495ce5ace2e5b352a9af489976b32d1ee2 \
|
||||
--hash=sha256:5c22873ad1f0532ba40fa1727f3c0fc1bbbaab6d373d4cbe3f0dc74b2e2521c7 \
|
||||
--hash=sha256:5e8b3d0b18fd623afa12ecb2ce8d8becef69f9b5440c6330c7972200e0bb84b0 \
|
||||
--hash=sha256:61631e08084be9e21a8967ec3139c7616ed7c5e9368e05c86d1b39562c8a57b6 \
|
||||
--hash=sha256:64511c54db4e4987aef4c41923235927428729e8174c5dba488429be70a998ed \
|
||||
--hash=sha256:6669c1bf34080161ce49c589cc512ef24d4c704ac9d2b2d3667f519c60418378 \
|
||||
--hash=sha256:672d207103e6b16ca098611b0f9efad6bc00afd47c03d6ef62186495ca677dc0 \
|
||||
--hash=sha256:6768d67d1bce64270e0fdc2e69309d68b9b18ae56ddf6c711d168e9d051c2cac \
|
||||
--hash=sha256:6a45c3d514f2436064db00d7fc8778d888f0236ebfed649b53d13a59e69ad51b \
|
||||
--hash=sha256:6bd9e1788e15bfcf6a9082de42e30387e7b85d211ab21e57a939bb8cfaaf8d96 \
|
||||
--hash=sha256:6d2a9efe686f9de00d0d1ea32a4a5a86d558a2277501bd78d964214eab625e59 \
|
||||
--hash=sha256:6da83a088f8ef93b2d483a8232a4dbf4d69d3d8496b568a03c56becac43e1808 \
|
||||
--hash=sha256:7018d4af1cd272e847aa5917983ab5e83e4f6579f9dbfecd4a79c0ca80b144c2 \
|
||||
--hash=sha256:71f88e749ea29f67f21f3b36433c1dc54c7729ed2a6d9e2da2e0d9e0d7b224eb \
|
||||
--hash=sha256:737c9c3981998eba27f11786f84fddcbabc74068b72a4a1f454ea02094b57b65 \
|
||||
--hash=sha256:73e77980c7207854f00fc4e71fb1626868d5740ab4012623d55c7a99ad122a72 \
|
||||
--hash=sha256:799c39bdf5e2f1292fedd3009f7b3c9e760f10b2420cb9638d56920840ff6db8 \
|
||||
--hash=sha256:7a83aa6e4805df46fed18e989d3d16f86ef60cb50bbc8d9ce3a6be89165fbf6e \
|
||||
--hash=sha256:7d3391b2188d18737cb2fa147028b1096236eaa7e156446c650a489fa2cadc91 \
|
||||
--hash=sha256:7e1636da3d8dfc220b6dd10264db5f2b165e4888c4518594898fbe381049af8a \
|
||||
--hash=sha256:805c8b84534fa10891890f0e4be39f3a99e94615d93e8836bf9fa1fdca2feeb2 \
|
||||
--hash=sha256:811d02d5122171c1941357efd8f9bf4ffe907b7f0a1a4e729a880e4be3f46e3e \
|
||||
--hash=sha256:8138eb83940ec7299024d92d4dee45f601b9e6c5ffde9d25f4e35e326203c707 \
|
||||
--hash=sha256:83b3944fea42a8400edf92fd1770fb8d0d4f7de651353bd2d8525a92dba69a21 \
|
||||
--hash=sha256:849dd2bb0e5e4ab2b71c7191726a4a8d5aa8a610daa584728cbee0b710ddc4ef \
|
||||
--hash=sha256:8698d70a8081ee8c090dbb394768b5789a1da8b131b5499f89d071dd3cfaf6be \
|
||||
--hash=sha256:8781a792a070cf2bd1b86d3aa943894115faaba6e88122a7bf32d62072742453 \
|
||||
--hash=sha256:88d59b473bfb03259722600839af9bbd7fa13a2eb514beefeedb95997882f69a \
|
||||
--hash=sha256:8909c2f1c6dd65e054ac4b573a91c8384d1492281e55d82d159d653f7a13adf6 \
|
||||
--hash=sha256:8965520ac587c94a4ac48b729be3d8b8de00af39699b17585dfb599babe77977 \
|
||||
--hash=sha256:8b5d563170ff8ba3181caa967c99a3c804d1dedb702c7cb93a6a7c32247da978 \
|
||||
--hash=sha256:8e124f974786f831d6043728e38296969d3579db8896fe004682f5758e613581 \
|
||||
--hash=sha256:8f0fac8b13d14bb06c68195f849371924ae53dd7b1c00fed24650f704383b692 \
|
||||
--hash=sha256:9240187afb63d2f9ddc3e032c670356fe941f6e20662ea168a5dc3f1f317e1b3 \
|
||||
--hash=sha256:925f929d6b59a8b3f8b8c6ac363cd0af7eecc81efb3071770b3c6717c450a369 \
|
||||
--hash=sha256:9348cbb300d224fe3b89793262cb093504d4ae927004468463f745188a193e4a \
|
||||
--hash=sha256:9388003072b95f2f1e3fd908604194d653ba21330d811961a78b7da1a77e9e36 \
|
||||
--hash=sha256:9438a2648b2195980cb2dd8e53ed7b8df91319e2d0b70ae61a9e1d1bc8d3bec9 \
|
||||
--hash=sha256:94e4c421742086aeee4c32a506eec8859d7634aad943f7e6aacf70f813478768 \
|
||||
--hash=sha256:94f5407f7bc64fa6463906b896f9904beeeb7dd8dc116ee8e9056c8714ff9916 \
|
||||
--hash=sha256:971a3bbb75d97ae4e2e8f7d4834236f86f85f0c85e04ab2e191db1123b04f80b \
|
||||
--hash=sha256:9e227f3dbe6bde7491cf0a9965d00b88c6b1a4a95d11480ddf88bb96d397c19f \
|
||||
--hash=sha256:9e25feb9e330b63edb0278a0acdf85e50d0cb0fbf49c3084abbe4e24ae195346 \
|
||||
--hash=sha256:9f098115c247e11d138ab83a28fa0323c77015007ea2df73ba5fd714dfefd67c \
|
||||
--hash=sha256:a18f38cafc329bac5e3c2b96c765b4c96d3d103421ed22ab7988c1e3fce27464 \
|
||||
--hash=sha256:a4bbd2d87dd233b9fc5812160c3d0ffbe42edc22a26ce0469f58479ede633fe9 \
|
||||
--hash=sha256:a5fcffb37e602b0b3c1638a97746b9b96125caa9bcf6fa41d337a9261de231ee \
|
||||
--hash=sha256:a8e9f292fcda89b324f2f5c91d13f1424a153e40fc2756f38ee23b15835ff300 \
|
||||
--hash=sha256:a9f54054101545a9a9cccefddf54316aa6e4491611fcbef9e91b3b6bebec04f6 \
|
||||
--hash=sha256:aa2c838cc024642cc04c6854232f32b43e5e22833dd11119c1766c7873b8370d \
|
||||
--hash=sha256:ac0c7c9f1609b0c4c114feb1d7a3409564c7fb77e360bed9e97e5d25dfeaf868 \
|
||||
--hash=sha256:add96447a86d205ab616665d53b2950ee81083757f56e6ea833c8b2917646b46 \
|
||||
--hash=sha256:ae9dcb8fbe244cb82f8a6458b455b927a03685e383d9bacf1ea5ce180b96dc97 \
|
||||
--hash=sha256:b4a635a0487774f841cb1fb62e907e7195cc95bc761e053184b8acc3ceb20733 \
|
||||
--hash=sha256:b4d12837e0203bbace818ff4a7461afdcd78bcd782351cea148139180d7bcffe \
|
||||
--hash=sha256:b61687d0828e72bf5cda24a2690188f37170bd31c9359ac97e4e66569f120a16 \
|
||||
--hash=sha256:b807e598953730f82e4eae3bd30f6a122cf6b31c398c6b504c0e04c13c170429 \
|
||||
--hash=sha256:b8cd1f918b26fd7b1832ece557cc18f2d8747309ff8b3f0ef9d4250c5ad67a39 \
|
||||
--hash=sha256:b91cc9d336957239ff200f30097e6fea2dc6d6fb3c81e853eaa09eac904fd894 \
|
||||
--hash=sha256:bd3ce56ae2cbae3ba82b683bc425cd7e48d2ed8b10f3e818186b6f5646d9271c \
|
||||
--hash=sha256:be6cb0c799abb0e2ba3e618e6d28ddddf7e485f6c2ce938dfa237daf3905072c \
|
||||
--hash=sha256:befb4158af32106b9a93db8d6d1d1cbbd418c0d5aca0cabb7b1780abf0c89169 \
|
||||
--hash=sha256:bf053da3c97a4bc5ecfbb218cdd2983febd91c617be8367d139882aa11e490aa \
|
||||
--hash=sha256:c02e8f18bdedba082cef725942ac823b9b60656db07f7e265cb31618dfd00d77 \
|
||||
--hash=sha256:c1bc67752d5f21013cfe430df4062441714eab79f65a6a05e01505957e9c35fe \
|
||||
--hash=sha256:c61750fadcd119d0825bcb7d7d675dd264dcc89cc05292aab5be68ebdbb374ad \
|
||||
--hash=sha256:c90d5b3d4e944e065a301d741b3c1d784f6bd1f503aa68b4967e32b2ba313d85 \
|
||||
--hash=sha256:c9a7f43c0b202b334cc9184af09bb8f21d3a209e038efaf106936fb69e6b026e \
|
||||
--hash=sha256:cb96e6e088d6cf71c1ea977510948320234824cf226e32f6f6e044f7a9c82b34 \
|
||||
--hash=sha256:cf63c214fe879a65e69a386f915e36104fc84254ab141240f8854602d8e0be2a \
|
||||
--hash=sha256:d1aca03ede943eb80ab3d63bb082c84b7aab85ea83bd0fd0c200260945fb49d9 \
|
||||
--hash=sha256:d2e56fd3b00222722abfb3f5f0759ddbae4b90811b5ad4343c64030ad1bde70c \
|
||||
--hash=sha256:d5f93ebbeb8032d47e349328ec8662d973d9b05a70b3c35df1f91fe419b84749 \
|
||||
--hash=sha256:d882a373d8093c2941e01291b7ced96e9cbe4781da9a7751ca7e6c70385e5214 \
|
||||
--hash=sha256:d920abdfa61279ba1a2ef9484aab07bf03331f8c08a10120fa332353d06e6932 \
|
||||
--hash=sha256:da2af0d7aebfc2074080d72efa6ab8317c62481ef1f896f65d9999c1c01f4494 \
|
||||
--hash=sha256:dd8ea6ebee7aedbf7c749fa80521d9ccf1ba473e0d1e14805caafbaad281c889 \
|
||||
--hash=sha256:de8b364c423ef0a4bad9069657d617f9a5d2b2062457a89b1fa16ee199c399c1 \
|
||||
--hash=sha256:df1ae86ff54725a01fa1a0510b914ca53a161b7050be74f6204e24aded5971d0 \
|
||||
--hash=sha256:dff05cb7016dff1e9fd68f4122c127b65dfc59de5306cfb7ad92f956f230bee2 \
|
||||
--hash=sha256:e1a622f13970d81f95d0c72f9dc090dce9085fccfa4c9f2174377ee32bd15786 \
|
||||
--hash=sha256:e49fb0d1ce92cfa0cb198cc5b1b11cdf9d0638658e2a2db2687e39db7c87fc78 \
|
||||
--hash=sha256:e5c802729725bd07e2bc3ab7b76dc7e0bbfc53129d8f1eb1c002c24cf774717e \
|
||||
--hash=sha256:e841068dc0be4cb6dfb5c890eb88cbdcff2f4a332393c7ec94e8e618bd32c1a8 \
|
||||
--hash=sha256:e916035e3e9930cbdfdd10abf48861340221857f45509565898e012263f7b289 \
|
||||
--hash=sha256:eba154571c16e032112afac0dc2dfe9e63c2ceb7aedd07bb7eecf2ce26d4dd4c \
|
||||
--hash=sha256:f03460ff076f70ab595bb45a0205ccea1971443575b6920c52e755dec2b3fbfe \
|
||||
--hash=sha256:f0ec3b750b59375eab5b0fb2b9254810c00a3375be6d789899f1055a1d556237 \
|
||||
--hash=sha256:f291bcf42ae98eb5107edb162c3c998b4a89648fd8e99ed4cbd12705292788cd \
|
||||
--hash=sha256:f61efe1d2fe0de16158a5fe1d1cf3c14bdb6aecd54d8938fd26512c525c1f624 \
|
||||
--hash=sha256:f68edfc67aabac33708941f26f22a7b8e9f81429bc0cf249fcf7d66b23af8d19 \
|
||||
--hash=sha256:fa95848c929b6a75f6848d3c9793e59db365ee436776e57db835cdbfa79ba977 \
|
||||
--hash=sha256:fd9f8797427910198f95bced71ddfed61130d7e349213bfb8466c9c99e2c46a8 \
|
||||
--hash=sha256:fdb4ca07ab75ffadab4a8b135ad59cdbb3156b99310f3d565370da74a15d6bd3
|
||||
# via jinja2
|
||||
Reference in new issue
Block a user