mirror of
https://github.com/nlohmann/json.git
synced 2026-09-30 19:50:34 +00:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1f0c3be6f3 | ||
|
|
c3b51addbf |
@@ -81,6 +81,7 @@ cc_library(
|
|||||||
"include/nlohmann/detail/view/pointer.hpp",
|
"include/nlohmann/detail/view/pointer.hpp",
|
||||||
"include/nlohmann/detail/view/scan.hpp",
|
"include/nlohmann/detail/view/scan.hpp",
|
||||||
"include/nlohmann/detail/view/serializer.hpp",
|
"include/nlohmann/detail/view/serializer.hpp",
|
||||||
|
"include/nlohmann/detail/view/simd.hpp",
|
||||||
"include/nlohmann/detail/view/string_ref.hpp",
|
"include/nlohmann/detail/view/string_ref.hpp",
|
||||||
"include/nlohmann/detail/view/value.hpp",
|
"include/nlohmann/detail/view/value.hpp",
|
||||||
"include/nlohmann/json.hpp",
|
"include/nlohmann/json.hpp",
|
||||||
|
|||||||
@@ -1405,6 +1405,7 @@ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR I
|
|||||||
- The class contains parts of [Google Abseil](https://github.com/abseil/abseil-cpp) which is licensed under the [Apache 2.0 License](https://opensource.org/licenses/Apache-2.0).
|
- The class contains parts of [Google Abseil](https://github.com/abseil/abseil-cpp) which is licensed under the [Apache 2.0 License](https://opensource.org/licenses/Apache-2.0).
|
||||||
- The class contains an adapted version of the Eisel-Lemire algorithm, its table of powers of five, and its digit comparison for long numbers from [fast_float](https://github.com/fastfloat/fast_float) by Daniel Lemire and contributors, which is available under the [MIT License](https://opensource.org/licenses/MIT) (used here), the Apache 2.0 License, and the Boost Software License. Copyright © 2021 The fast_float authors
|
- The class contains an adapted version of the Eisel-Lemire algorithm, its table of powers of five, and its digit comparison for long numbers from [fast_float](https://github.com/fastfloat/fast_float) by Daniel Lemire and contributors, which is available under the [MIT License](https://opensource.org/licenses/MIT) (used here), the Apache 2.0 License, and the Boost Software License. Copyright © 2021 The fast_float authors
|
||||||
- The view's parser (`<nlohmann/json_view.hpp>`) contains techniques and code adapted from [yyjson](https://github.com/ibireme/yyjson) by YaoYuan, which is licensed under the [MIT License](https://opensource.org/licenses/MIT) (see above): table-driven decoding of `\u` escapes and fixed-offset unrolled checks.
|
- The view's parser (`<nlohmann/json_view.hpp>`) contains techniques and code adapted from [yyjson](https://github.com/ibireme/yyjson) by YaoYuan, which is licensed under the [MIT License](https://opensource.org/licenses/MIT) (see above): table-driven decoding of `\u` escapes and fixed-offset unrolled checks.
|
||||||
|
- The view's parser (`<nlohmann/json_view.hpp>`) validates non-ASCII strings with the vector UTF-8 check of [simdjson](https://github.com/simdjson/simdjson) by Daniel Lemire, Geoff Langdale, John Keiser, and contributors (its "lookup4" algorithm and tables, after J. Keiser and D. Lemire, "Validating UTF-8 In Less Than One Instruction Per Byte", 2021), which is available under the [MIT License](https://opensource.org/licenses/MIT) (used here) and the Apache 2.0 License. Copyright © 2018-2025 The simdjson authors
|
||||||
|
|
||||||
<img align="right" src="https://git.fsfe.org/reuse/reuse-ci/raw/branch/master/reuse-horizontal.png" alt="REUSE Software">
|
<img align="right" src="https://git.fsfe.org/reuse/reuse-ci/raw/branch/master/reuse-horizontal.png" alt="REUSE Software">
|
||||||
|
|
||||||
|
|||||||
@@ -307,3 +307,5 @@ INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_JSON_SERIALIZE_ENUM'
|
|||||||
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_JSON_VERSION_MAJOR', 'Macro', 'api/macros/nlohmann_json_version_major/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_JSON_VERSION_MAJOR', 'Macro', 'api/macros/nlohmann_json_version_major/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_JSON_VERSION_MINOR', 'Macro', 'api/macros/nlohmann_json_version_major/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_JSON_VERSION_MINOR', 'Macro', 'api/macros/nlohmann_json_version_major/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_JSON_VERSION_PATCH', 'Macro', 'api/macros/nlohmann_json_version_major/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_JSON_VERSION_PATCH', 'Macro', 'api/macros/nlohmann_json_version_major/index.html');
|
||||||
|
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_VIEW_NO_SIMD', 'Macro', 'api/macros/json_view_no_simd/index.html');
|
||||||
|
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_VIEW_USE_SSSE3', 'Macro', 'api/macros/json_view_use_ssse3/index.html');
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ header. See also the [macro overview page](../../features/macros.md).
|
|||||||
- [**JSON_SKIP_UNSUPPORTED_COMPILER_CHECK**](json_skip_unsupported_compiler_check.md) - do not warn about unsupported compilers
|
- [**JSON_SKIP_UNSUPPORTED_COMPILER_CHECK**](json_skip_unsupported_compiler_check.md) - do not warn about unsupported compilers
|
||||||
- [**JSON_USE_GLOBAL_UDLS**](json_use_global_udls.md) - place user-defined string literals (UDLs) into the global namespace
|
- [**JSON_USE_GLOBAL_UDLS**](json_use_global_udls.md) - place user-defined string literals (UDLs) into the global namespace
|
||||||
- [**JSON_USE_SIMDUTF**](json_use_simdutf.md) - use the simdutf library to accelerate UTF-8 validation
|
- [**JSON_USE_SIMDUTF**](json_use_simdutf.md) - use the simdutf library to accelerate UTF-8 validation
|
||||||
|
- [**JSON_VIEW_NO_SIMD**](json_view_no_simd.md) - use only portable code in the parser of `json_view.hpp`
|
||||||
|
- [**JSON_VIEW_USE_SSSE3**](json_view_use_ssse3.md) - validate non-ASCII strings with SSSE3 in the parser of `json_view.hpp`
|
||||||
|
|
||||||
## Library version
|
## Library version
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
# JSON_VIEW_NO_SIMD
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#define JSON_VIEW_NO_SIMD
|
||||||
|
```
|
||||||
|
|
||||||
|
When defined, the parser of [`basic_json_document`](../basic_json_document/index.md) (`<nlohmann/json_view.hpp>`)
|
||||||
|
uses only portable C++ to scan strings. By default, it scans long runs of string bytes 16 at a time with NEON on
|
||||||
|
AArch64 (with GCC and Clang) and SSE2 on x86-64, which are part of the baseline instruction sets of these
|
||||||
|
architectures, and validates non-ASCII text with NEON (or SSSE3, see
|
||||||
|
[`JSON_VIEW_USE_SSSE3`](json_view_use_ssse3.md)).
|
||||||
|
|
||||||
|
The same input is accepted or rejected either way, with the same values, and errors are reported the same way; only
|
||||||
|
the speed differs. The macro exists for platforms whose compilers lack the intrinsics headers, and to test the portable
|
||||||
|
code.
|
||||||
|
|
||||||
|
!!! warning "Define consistently"
|
||||||
|
|
||||||
|
The macro selects between two definitions of the same inline functions. It must therefore be defined identically for
|
||||||
|
**every** translation unit that includes `<nlohmann/json_view.hpp>`; prefer a compile definition on the target.
|
||||||
|
|
||||||
|
## Default definition
|
||||||
|
|
||||||
|
By default, `#!cpp JSON_VIEW_NO_SIMD` is not defined, and the vector code is used where available.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#undef JSON_VIEW_NO_SIMD
|
||||||
|
```
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
??? example
|
||||||
|
|
||||||
|
The code below uses the portable string scanning of the view.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#define JSON_VIEW_NO_SIMD
|
||||||
|
#include <nlohmann/json_view.hpp>
|
||||||
|
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
- [JSON_VIEW_USE_SSSE3](json_view_use_ssse3.md) - validate non-ASCII strings with SSSE3 on x86-64
|
||||||
|
- [json_view](../../features/json_view.md) - the zero-copy view
|
||||||
|
|
||||||
|
## Version history
|
||||||
|
|
||||||
|
- Added in version 3.13.0.
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
# JSON_VIEW_USE_SSSE3
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#define JSON_VIEW_USE_SSSE3
|
||||||
|
```
|
||||||
|
|
||||||
|
When defined on x86-64, the parser of [`basic_json_document`](../basic_json_document/index.md)
|
||||||
|
(`<nlohmann/json_view.hpp>`) validates non-ASCII text in strings with SSSE3, 16 bytes at a time, using the "lookup4"
|
||||||
|
algorithm of [simdjson](https://github.com/simdjson/simdjson). Without it, non-ASCII text is validated one UTF-8
|
||||||
|
sequence at a time on x86-64; on AArch64, the vector check uses NEON and is always on.
|
||||||
|
|
||||||
|
SSSE3 is not part of the x86-64 baseline, so the code must be compiled for it: define the macro only together with a
|
||||||
|
compiler option that enables SSSE3 (e.g. `-mssse3`, or `-march=` with a CPU that has it), and only for programs that
|
||||||
|
run on such CPUs. The same input is accepted or rejected either way; only the speed of non-ASCII text differs.
|
||||||
|
|
||||||
|
!!! warning "Define consistently"
|
||||||
|
|
||||||
|
The macro selects between two definitions of the same inline functions. It must therefore be defined identically,
|
||||||
|
with the same compiler options, for **every** translation unit that includes `<nlohmann/json_view.hpp>`; mixing
|
||||||
|
translation units that define it with ones that do not is an ODR violation. Prefer a compile definition on the
|
||||||
|
target.
|
||||||
|
|
||||||
|
## Default definition
|
||||||
|
|
||||||
|
By default, `#!cpp JSON_VIEW_USE_SSSE3` is not defined.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#undef JSON_VIEW_USE_SSSE3
|
||||||
|
```
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
??? example
|
||||||
|
|
||||||
|
With CMake, for a program that only runs on CPUs with SSSE3:
|
||||||
|
|
||||||
|
```cmake
|
||||||
|
target_compile_definitions(your_target PRIVATE JSON_VIEW_USE_SSSE3)
|
||||||
|
target_compile_options(your_target PRIVATE -mssse3)
|
||||||
|
```
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
- [JSON_VIEW_NO_SIMD](json_view_no_simd.md) - use only portable code in the view's parser
|
||||||
|
- [JSON_USE_SIMDUTF](json_use_simdutf.md) - validate UTF-8 with simdutf in `basic_json`'s parser
|
||||||
|
|
||||||
|
## Version history
|
||||||
|
|
||||||
|
- Added in version 3.13.0.
|
||||||
@@ -23,3 +23,5 @@ The class contains a copy of [Hedley](https://nemequ.github.io/hedley/) from Eva
|
|||||||
The class contains an adapted version of the Eisel-Lemire algorithm, its table of powers of five, and its digit comparison for long numbers from [fast_float](https://github.com/fastfloat/fast_float) by Daniel Lemire and contributors, which is available under the [MIT License](https://opensource.org/licenses/MIT) (used here), the Apache 2.0 License, and the Boost Software License. Copyright © 2021 The fast_float authors
|
The class contains an adapted version of the Eisel-Lemire algorithm, its table of powers of five, and its digit comparison for long numbers from [fast_float](https://github.com/fastfloat/fast_float) by Daniel Lemire and contributors, which is available under the [MIT License](https://opensource.org/licenses/MIT) (used here), the Apache 2.0 License, and the Boost Software License. Copyright © 2021 The fast_float authors
|
||||||
|
|
||||||
The view's parser (`<nlohmann/json_view.hpp>`) contains techniques and code adapted from [yyjson](https://github.com/ibireme/yyjson) by YaoYuan, which is licensed under the [MIT License](https://opensource.org/licenses/MIT) (see above): table-driven decoding of `\u` escapes and fixed-offset unrolled checks.
|
The view's parser (`<nlohmann/json_view.hpp>`) contains techniques and code adapted from [yyjson](https://github.com/ibireme/yyjson) by YaoYuan, which is licensed under the [MIT License](https://opensource.org/licenses/MIT) (see above): table-driven decoding of `\u` escapes and fixed-offset unrolled checks.
|
||||||
|
|
||||||
|
The view's parser (`<nlohmann/json_view.hpp>`) validates non-ASCII strings with the vector UTF-8 check of [simdjson](https://github.com/simdjson/simdjson) by Daniel Lemire, Geoff Langdale, John Keiser, and contributors (its "lookup4" algorithm and tables, after J. Keiser and D. Lemire, "Validating UTF-8 In Less Than One Instruction Per Byte", 2021), which is available under the [MIT License](https://opensource.org/licenses/MIT) (used here) and the Apache 2.0 License. Copyright © 2018-2025 The simdjson authors
|
||||||
|
|||||||
@@ -369,6 +369,8 @@ nav:
|
|||||||
- 'JSON_USE_IMPLICIT_CONVERSIONS': api/macros/json_use_implicit_conversions.md
|
- 'JSON_USE_IMPLICIT_CONVERSIONS': api/macros/json_use_implicit_conversions.md
|
||||||
- 'JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON': api/macros/json_use_legacy_discarded_value_comparison.md
|
- 'JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON': api/macros/json_use_legacy_discarded_value_comparison.md
|
||||||
- 'JSON_USE_SIMDUTF': api/macros/json_use_simdutf.md
|
- 'JSON_USE_SIMDUTF': api/macros/json_use_simdutf.md
|
||||||
|
- 'JSON_VIEW_NO_SIMD': api/macros/json_view_no_simd.md
|
||||||
|
- 'JSON_VIEW_USE_SSSE3': api/macros/json_view_use_ssse3.md
|
||||||
- 'NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE, NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT, NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE, NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE, NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT, NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE': api/macros/nlohmann_define_derived_type.md
|
- 'NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE, NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT, NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE, NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE, NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT, NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE': api/macros/nlohmann_define_derived_type.md
|
||||||
- 'NLOHMANN_DEFINE_TYPE_INTRUSIVE, NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT, NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE': api/macros/nlohmann_define_type_intrusive.md
|
- 'NLOHMANN_DEFINE_TYPE_INTRUSIVE, NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT, NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE': api/macros/nlohmann_define_type_intrusive.md
|
||||||
- 'NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE, NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT, NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE': api/macros/nlohmann_define_type_non_intrusive.md
|
- 'NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE, NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT, NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE': api/macros/nlohmann_define_type_non_intrusive.md
|
||||||
|
|||||||
@@ -503,7 +503,7 @@ class builder
|
|||||||
switch (cur()) \
|
switch (cur()) \
|
||||||
{ \
|
{ \
|
||||||
case '"': \
|
case '"': \
|
||||||
if (NLOHMANN_VIEW_UNLIKELY(!string())) { return false; } \
|
if (NLOHMANN_VIEW_UNLIKELY(!string<true>())) { return false; } \
|
||||||
goto NEXT; \
|
goto NEXT; \
|
||||||
case '{': \
|
case '{': \
|
||||||
open(value_t::object); \
|
open(value_t::object); \
|
||||||
@@ -587,7 +587,7 @@ obj_key:
|
|||||||
{
|
{
|
||||||
return fail(error_code::expected_key);
|
return fail(error_code::expected_key);
|
||||||
}
|
}
|
||||||
if (NLOHMANN_VIEW_UNLIKELY(!string()))
|
if (NLOHMANN_VIEW_UNLIKELY(!string<false>()))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -690,7 +690,7 @@ root_done:
|
|||||||
switch (cur())
|
switch (cur())
|
||||||
{
|
{
|
||||||
case '"':
|
case '"':
|
||||||
return string();
|
return string<true>();
|
||||||
case 't':
|
case 't':
|
||||||
return literal("true", 4, value_t::boolean, node_flags::is_true);
|
return literal("true", 4, value_t::boolean, node_flags::is_true);
|
||||||
case 'f':
|
case 'f':
|
||||||
@@ -977,12 +977,13 @@ indent_done:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// a string (value or key) at p
|
/// a string at p: a value (Value) or a key
|
||||||
|
template<bool Value>
|
||||||
NLOHMANN_VIEW_ALWAYS_INLINE bool string()
|
NLOHMANN_VIEW_ALWAYS_INLINE bool string()
|
||||||
{
|
{
|
||||||
++p; // opening quote
|
++p; // opening quote
|
||||||
const unsigned char* const s = p;
|
const unsigned char* const s = p;
|
||||||
p = scan_string_run(p, e);
|
p = scan_string_run<Value>(p, e);
|
||||||
if (NLOHMANN_VIEW_LIKELY(p != e && *p == '"'))
|
if (NLOHMANN_VIEW_LIKELY(p != e && *p == '"'))
|
||||||
{
|
{
|
||||||
emit(value_t::string, 0, 0, static_cast<std::size_t>(s - b), static_cast<std::uint64_t>(p - s));
|
emit(value_t::string, 0, 0, static_cast<std::size_t>(s - b), static_cast<std::uint64_t>(p - s));
|
||||||
|
|||||||
@@ -19,3 +19,8 @@
|
|||||||
#undef NLOHMANN_VIEW_THROW
|
#undef NLOHMANN_VIEW_THROW
|
||||||
#undef NLOHMANN_VIEW_LITTLE_ENDIAN
|
#undef NLOHMANN_VIEW_LITTLE_ENDIAN
|
||||||
#undef NLOHMANN_VIEW_REPEAT16
|
#undef NLOHMANN_VIEW_REPEAT16
|
||||||
|
#undef NLOHMANN_VIEW_NEON
|
||||||
|
#undef NLOHMANN_VIEW_SSE2
|
||||||
|
#undef NLOHMANN_VIEW_SSSE3
|
||||||
|
#undef NLOHMANN_VIEW_VECTOR
|
||||||
|
#undef NLOHMANN_VIEW_VECTOR_UTF8
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
#include <nlohmann/detail/view/macro_scope.hpp>
|
#include <nlohmann/detail/view/macro_scope.hpp>
|
||||||
|
#include <nlohmann/detail/view/simd.hpp>
|
||||||
|
|
||||||
// Scanning primitives of the view's parser. The unrolled checks at fixed
|
// Scanning primitives of the view's parser. The unrolled checks at fixed
|
||||||
// offsets follow yyjson (https://github.com/ibireme/yyjson, MIT license): the
|
// offsets follow yyjson (https://github.com/ibireme/yyjson, MIT license): the
|
||||||
@@ -62,9 +63,12 @@ NLOHMANN_VIEW_ALWAYS_INLINE std::uint16_t load16(const unsigned char* p) noexcep
|
|||||||
|
|
||||||
/// Advance over plain string bytes and well-formed UTF-8. Stops at a quote,
|
/// Advance over plain string bytes and well-formed UTF-8. Stops at a quote,
|
||||||
/// a backslash, a control character, ill-formed UTF-8, or the end. The first
|
/// a backslash, a control character, ill-formed UTF-8, or the end. The first
|
||||||
/// 16 bytes are checked one by one, so that the position advances by
|
/// bytes are checked one by one, so that the position advances by constants
|
||||||
/// constants in predicted branches (most strings are short); longer runs
|
/// in predicted branches: 16 for keys, whose lengths repeat from record to
|
||||||
/// continue eight bytes at a time.
|
/// record, and 8 for string values (Value) where a vector loop follows, as
|
||||||
|
/// their lengths vary more. Longer runs continue 16 bytes at a time with NEON
|
||||||
|
/// or SSE2, else eight bytes at a time.
|
||||||
|
template<bool Value = false>
|
||||||
NLOHMANN_VIEW_ALWAYS_INLINE const unsigned char* scan_string_run(const unsigned char* p, const unsigned char* e) noexcept
|
NLOHMANN_VIEW_ALWAYS_INLINE const unsigned char* scan_string_run(const unsigned char* p, const unsigned char* e) noexcept
|
||||||
{
|
{
|
||||||
const std::uint8_t* plain = string_plain();
|
const std::uint8_t* plain = string_plain();
|
||||||
@@ -73,9 +77,23 @@ NLOHMANN_VIEW_ALWAYS_INLINE const unsigned char* scan_string_run(const unsigned
|
|||||||
if (e - p >= 16)
|
if (e - p >= 16)
|
||||||
{
|
{
|
||||||
#define NLOHMANN_VIEW_STEP(i) if (NLOHMANN_VIEW_LIKELY(plain[p[i]] != 0)) {} else { p += (i); goto stop; }
|
#define NLOHMANN_VIEW_STEP(i) if (NLOHMANN_VIEW_LIKELY(plain[p[i]] != 0)) {} else { p += (i); goto stop; }
|
||||||
NLOHMANN_VIEW_REPEAT16(NLOHMANN_VIEW_STEP)
|
NLOHMANN_VIEW_STEP(0) NLOHMANN_VIEW_STEP(1) NLOHMANN_VIEW_STEP(2) NLOHMANN_VIEW_STEP(3)
|
||||||
|
NLOHMANN_VIEW_STEP(4) NLOHMANN_VIEW_STEP(5) NLOHMANN_VIEW_STEP(6) NLOHMANN_VIEW_STEP(7)
|
||||||
|
if (!Value || !NLOHMANN_VIEW_VECTOR)
|
||||||
|
{
|
||||||
|
NLOHMANN_VIEW_STEP(8) NLOHMANN_VIEW_STEP(9) NLOHMANN_VIEW_STEP(10) NLOHMANN_VIEW_STEP(11)
|
||||||
|
NLOHMANN_VIEW_STEP(12) NLOHMANN_VIEW_STEP(13) NLOHMANN_VIEW_STEP(14) NLOHMANN_VIEW_STEP(15)
|
||||||
|
p += 8;
|
||||||
|
}
|
||||||
#undef NLOHMANN_VIEW_STEP
|
#undef NLOHMANN_VIEW_STEP
|
||||||
p += 16;
|
p += 8;
|
||||||
|
#if NLOHMANN_VIEW_VECTOR
|
||||||
|
p = vector_plain_run(p, e);
|
||||||
|
if (p != e && plain[*p] == 0)
|
||||||
|
{
|
||||||
|
goto stop;
|
||||||
|
}
|
||||||
|
#else
|
||||||
while (e - p >= 8)
|
while (e - p >= 8)
|
||||||
{
|
{
|
||||||
const std::uint64_t special = swar_string_special(read_eight_bytes(p));
|
const std::uint64_t special = swar_string_special(read_eight_bytes(p));
|
||||||
@@ -86,6 +104,7 @@ NLOHMANN_VIEW_ALWAYS_INLINE const unsigned char* scan_string_run(const unsigned
|
|||||||
}
|
}
|
||||||
p += 8;
|
p += 8;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
while (p != e && plain[*p] != 0)
|
while (p != e && plain[*p] != 0)
|
||||||
@@ -101,6 +120,10 @@ stop:
|
|||||||
{
|
{
|
||||||
return p; // quote, backslash, or control character
|
return p; // quote, backslash, or control character
|
||||||
}
|
}
|
||||||
|
#if NLOHMANN_VIEW_VECTOR_UTF8
|
||||||
|
// non-ASCII: the vector check, out of line
|
||||||
|
return scan_string_vector(p, e, plain);
|
||||||
|
#else
|
||||||
// non-ASCII: a run of well-formed sequences (the library's check, so
|
// non-ASCII: a run of well-formed sequences (the library's check, so
|
||||||
// that exactly what json::parse accepts is accepted)
|
// that exactly what json::parse accepts is accepted)
|
||||||
do
|
do
|
||||||
@@ -113,6 +136,7 @@ stop:
|
|||||||
p += n;
|
p += n;
|
||||||
}
|
}
|
||||||
while (p != e && *p >= 0x80);
|
while (p != e && *p >= 0x80);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,281 @@
|
|||||||
|
// __ _____ _____ _____
|
||||||
|
// __| | __| | | | JSON for Modern C++
|
||||||
|
// | | |__ | | | | | | version 3.12.0
|
||||||
|
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||||
|
//
|
||||||
|
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
||||||
|
// SPDX-FileCopyrightText: 2018-2025 The simdjson authors <https://github.com/simdjson/simdjson>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <array> // array
|
||||||
|
#include <cstddef> // size_t
|
||||||
|
#include <cstdint> // uint8_t, uint64_t
|
||||||
|
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
#include <nlohmann/detail/view/macro_scope.hpp>
|
||||||
|
|
||||||
|
// Vector code for long runs of string bytes. NEON (AArch64) and SSE2 (x86-64)
|
||||||
|
// belong to the baseline instruction sets and are used by default. The vector
|
||||||
|
// UTF-8 check needs NEON, or SSSE3 if JSON_VIEW_USE_SSSE3 is defined: SSSE3 is
|
||||||
|
// not part of x86-64, so it must not depend on the flags of a translation unit
|
||||||
|
// (two translation units with different flags would have different
|
||||||
|
// definitions of the same inline functions). JSON_VIEW_NO_SIMD selects the
|
||||||
|
// portable code.
|
||||||
|
#if !defined(JSON_VIEW_NO_SIMD) && defined(__aarch64__) && (defined(__GNUC__) || defined(__clang__)) && NLOHMANN_VIEW_LITTLE_ENDIAN
|
||||||
|
#include <arm_neon.h>
|
||||||
|
#define NLOHMANN_VIEW_NEON 1
|
||||||
|
#else
|
||||||
|
#define NLOHMANN_VIEW_NEON 0
|
||||||
|
#endif
|
||||||
|
#if !defined(JSON_VIEW_NO_SIMD) && !NLOHMANN_VIEW_NEON && (defined(__SSE2__) || defined(_M_X64) || (defined(_M_IX86_FP) && _M_IX86_FP >= 2))
|
||||||
|
#include <emmintrin.h>
|
||||||
|
#define NLOHMANN_VIEW_SSE2 1
|
||||||
|
#else
|
||||||
|
#define NLOHMANN_VIEW_SSE2 0
|
||||||
|
#endif
|
||||||
|
#if NLOHMANN_VIEW_SSE2 && defined(JSON_VIEW_USE_SSSE3)
|
||||||
|
#include <tmmintrin.h>
|
||||||
|
#define NLOHMANN_VIEW_SSSE3 1 // NOLINT(cppcoreguidelines-macro-to-enum,modernize-macro-to-enum)
|
||||||
|
#else
|
||||||
|
#define NLOHMANN_VIEW_SSSE3 0 // NOLINT(cppcoreguidelines-macro-to-enum,modernize-macro-to-enum)
|
||||||
|
#endif
|
||||||
|
#define NLOHMANN_VIEW_VECTOR (NLOHMANN_VIEW_NEON || NLOHMANN_VIEW_SSE2)
|
||||||
|
#define NLOHMANN_VIEW_VECTOR_UTF8 (NLOHMANN_VIEW_NEON || NLOHMANN_VIEW_SSSE3)
|
||||||
|
|
||||||
|
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
namespace view
|
||||||
|
{
|
||||||
|
|
||||||
|
#if NLOHMANN_VIEW_VECTOR
|
||||||
|
/*!
|
||||||
|
@brief the first byte of a string run that is a quote, a backslash, a control
|
||||||
|
character, or not ASCII, 16 bytes per step
|
||||||
|
|
||||||
|
Stops at such a byte, or where fewer than 16 bytes are left (the caller tells
|
||||||
|
the two apart). A signed compare with 0x20 finds control characters and
|
||||||
|
non-ASCII bytes at once.
|
||||||
|
*/
|
||||||
|
NLOHMANN_VIEW_ALWAYS_INLINE const unsigned char* vector_plain_run(const unsigned char* p, const unsigned char* e) noexcept
|
||||||
|
{
|
||||||
|
while (e - p >= 16)
|
||||||
|
{
|
||||||
|
#if NLOHMANN_VIEW_NEON
|
||||||
|
const uint8x16_t in = vld1q_u8(p);
|
||||||
|
const uint8x16_t special = vorrq_u8(vorrq_u8(vceqq_u8(in, vdupq_n_u8('"')), vceqq_u8(in, vdupq_n_u8('\\'))),
|
||||||
|
vcltq_s8(vreinterpretq_s8_u8(in), vdupq_n_s8(0x20)));
|
||||||
|
// one nibble per byte (the usual NEON replacement of x86's movemask, see
|
||||||
|
// D. Kutenin, "Porting x86 vector bitmask optimizations to Arm NEON", 2022)
|
||||||
|
const std::uint64_t bits = vget_lane_u64(vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(special), 4)), 0);
|
||||||
|
if (bits != 0)
|
||||||
|
{
|
||||||
|
return p + (count_trailing_zeros(bits) >> 2u);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
const __m128i in = _mm_loadu_si128(static_cast<const __m128i*>(static_cast<const void*>(p)));
|
||||||
|
const __m128i special = _mm_or_si128(_mm_or_si128(_mm_cmpeq_epi8(in, _mm_set1_epi8('"')), _mm_cmpeq_epi8(in, _mm_set1_epi8('\\'))),
|
||||||
|
_mm_cmplt_epi8(in, _mm_set1_epi8(0x20)));
|
||||||
|
const auto bits = static_cast<std::uint64_t>(static_cast<unsigned>(_mm_movemask_epi8(special)));
|
||||||
|
if (bits != 0)
|
||||||
|
{
|
||||||
|
return p + count_trailing_zeros(bits);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
p += 16;
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if NLOHMANN_VIEW_VECTOR_UTF8
|
||||||
|
/// Tables of the UTF-8 check of J. Keiser and D. Lemire, "Validating UTF-8 In
|
||||||
|
/// Less Than One Instruction Per Byte" (2021), as in simdjson ("lookup4"): each
|
||||||
|
/// maps a nibble (high and low nibble of the previous byte, high nibble of the
|
||||||
|
/// current byte) to the errors it allows; a byte pair is ill-formed if all
|
||||||
|
/// three have an error bit in common.
|
||||||
|
template<typename Dummy = void>
|
||||||
|
struct utf8_lookup4
|
||||||
|
{
|
||||||
|
static constexpr std::uint8_t too_short = 1u << 0u, too_long = 1u << 1u, overlong_3 = 1u << 2u, too_large = 1u << 3u;
|
||||||
|
static constexpr std::uint8_t surrogate = 1u << 4u, overlong_2 = 1u << 5u, too_large_1000 = 1u << 6u, overlong_4 = 1u << 6u;
|
||||||
|
static constexpr std::uint8_t two_conts = 1u << 7u, carry = too_short | too_long | two_conts;
|
||||||
|
static const std::array<std::uint8_t, 16> byte_1_high;
|
||||||
|
static const std::array<std::uint8_t, 16> byte_1_low;
|
||||||
|
static const std::array<std::uint8_t, 16> byte_2_high;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename Dummy>
|
||||||
|
const std::array<std::uint8_t, 16> utf8_lookup4<Dummy>::byte_1_high =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
too_long, too_long, too_long, too_long, too_long, too_long, too_long, too_long,
|
||||||
|
two_conts, two_conts, two_conts, two_conts,
|
||||||
|
too_short | overlong_2, too_short, too_short | overlong_3 | surrogate, too_short | too_large | too_large_1000 | overlong_4
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename Dummy>
|
||||||
|
const std::array<std::uint8_t, 16> utf8_lookup4<Dummy>::byte_1_low =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
carry | overlong_3 | overlong_2 | overlong_4, carry | overlong_2, carry, carry,
|
||||||
|
carry | too_large, carry | too_large | too_large_1000, carry | too_large | too_large_1000, carry | too_large | too_large_1000,
|
||||||
|
carry | too_large | too_large_1000, carry | too_large | too_large_1000, carry | too_large | too_large_1000, carry | too_large | too_large_1000,
|
||||||
|
carry | too_large | too_large_1000, carry | too_large | too_large_1000 | surrogate, carry | too_large | too_large_1000, carry | too_large | too_large_1000
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename Dummy>
|
||||||
|
const std::array<std::uint8_t, 16> utf8_lookup4<Dummy>::byte_2_high =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
too_short, too_short, too_short, too_short, too_short, too_short, too_short, too_short,
|
||||||
|
static_cast<std::uint8_t>(too_long | overlong_2 | two_conts | overlong_3 | too_large_1000 | overlong_4),
|
||||||
|
static_cast<std::uint8_t>(too_long | overlong_2 | two_conts | overlong_3 | too_large),
|
||||||
|
static_cast<std::uint8_t>(too_long | overlong_2 | two_conts | surrogate | too_large),
|
||||||
|
static_cast<std::uint8_t>(too_long | overlong_2 | two_conts | surrogate | too_large),
|
||||||
|
too_short, too_short, too_short, too_short
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// the end of scan_string_vector from block, where the vector loop stopped
|
||||||
|
/// (ill-formed UTF-8, or fewer than 16 bytes left): one byte or sequence at a
|
||||||
|
/// time, from the start of a sequence that crosses into the block
|
||||||
|
inline const unsigned char* scan_string_finish(const unsigned char* p, const unsigned char* block, const unsigned char* e, const std::uint8_t* plain) noexcept
|
||||||
|
{
|
||||||
|
for (int i = 1; i <= 3 && block - i >= p; ++i)
|
||||||
|
{
|
||||||
|
const unsigned char c = block[-i];
|
||||||
|
if (c < 0x80)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (c >= 0xC0)
|
||||||
|
{
|
||||||
|
const int len = 2 + static_cast<int>(c >= 0xE0) + static_cast<int>(c >= 0xF0);
|
||||||
|
if (len > i)
|
||||||
|
{
|
||||||
|
block -= i;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (p = block; p != e;)
|
||||||
|
{
|
||||||
|
if (*p < 0x80)
|
||||||
|
{
|
||||||
|
if (plain[*p] == 0)
|
||||||
|
{
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
++p;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const std::size_t n = validate_one_utf8(p, static_cast<std::size_t>(e - p));
|
||||||
|
if (n == 0)
|
||||||
|
{
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
p += n;
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief the rest of a string from p (a character boundary), 16 bytes per step
|
||||||
|
|
||||||
|
The first quote, backslash, or control character is found with vector
|
||||||
|
compares, and the UTF-8 check covers the bytes up to it. Returns where the
|
||||||
|
string scan stops, like scan_string_run: before ill-formed UTF-8 and for the
|
||||||
|
last bytes of the input, the bytes are checked one sequence at a time. Out of
|
||||||
|
line, so that no constants of the check occupy registers in the parse loop.
|
||||||
|
*/
|
||||||
|
NLOHMANN_VIEW_NOINLINE inline const unsigned char* scan_string_vector(const unsigned char* p, const unsigned char* e, const std::uint8_t* plain) noexcept
|
||||||
|
{
|
||||||
|
using lookup = utf8_lookup4<>;
|
||||||
|
const unsigned char* block = p;
|
||||||
|
#if NLOHMANN_VIEW_NEON
|
||||||
|
const uint8x16_t t1h = vld1q_u8(lookup::byte_1_high.data());
|
||||||
|
const uint8x16_t t1l = vld1q_u8(lookup::byte_1_low.data());
|
||||||
|
const uint8x16_t t2h = vld1q_u8(lookup::byte_2_high.data());
|
||||||
|
uint8x16_t prev = vdupq_n_u8(0);
|
||||||
|
while (e - block >= 16)
|
||||||
|
{
|
||||||
|
const uint8x16_t in = vld1q_u8(block);
|
||||||
|
const uint8x16_t special = vorrq_u8(vorrq_u8(vceqq_u8(in, vdupq_n_u8('"')), vceqq_u8(in, vdupq_n_u8('\\'))), vcltq_u8(in, vdupq_n_u8(0x20)));
|
||||||
|
const uint8x16_t prev1 = vextq_u8(prev, in, 15);
|
||||||
|
const uint8x16_t sc = vandq_u8(vandq_u8(vqtbl1q_u8(t1h, vshrq_n_u8(prev1, 4)), vqtbl1q_u8(t1l, vandq_u8(prev1, vdupq_n_u8(0x0F)))), vqtbl1q_u8(t2h, vshrq_n_u8(in, 4)));
|
||||||
|
const uint8x16_t must23 = vorrq_u8(vqsubq_u8(vextq_u8(prev, in, 14), vdupq_n_u8(0xE0 - 0x80)), vqsubq_u8(vextq_u8(prev, in, 13), vdupq_n_u8(0xF0 - 0x80)));
|
||||||
|
const uint8x16_t err = veorq_u8(vandq_u8(must23, vdupq_n_u8(0x80)), sc);
|
||||||
|
const std::uint64_t special_bits = vget_lane_u64(vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(special), 4)), 0);
|
||||||
|
const std::uint64_t err_bits = vget_lane_u64(vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(vtstq_u8(err, err)), 4)), 0);
|
||||||
|
if (special_bits != 0)
|
||||||
|
{
|
||||||
|
// errors up to the special byte count (an incomplete sequence
|
||||||
|
// before a quote shows at the quote); the bytes after it do not
|
||||||
|
const unsigned k = static_cast<unsigned>(count_trailing_zeros(special_bits)) >> 2u;
|
||||||
|
const std::uint64_t upto = k == 15 ? ~std::uint64_t{0} :
|
||||||
|
(std::uint64_t{1} << (4u * (k + 1u))) - 1u;
|
||||||
|
if ((err_bits & upto) == 0)
|
||||||
|
{
|
||||||
|
return block + k;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (err_bits != 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
prev = in;
|
||||||
|
block += 16;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// the same with SSSE3 (pshufb for the table lookups; nibbles from 16-bit
|
||||||
|
// shifts, as there are no byte shifts)
|
||||||
|
const __m128i t1h = _mm_loadu_si128(static_cast<const __m128i*>(static_cast<const void*>(lookup::byte_1_high.data())));
|
||||||
|
const __m128i t1l = _mm_loadu_si128(static_cast<const __m128i*>(static_cast<const void*>(lookup::byte_1_low.data())));
|
||||||
|
const __m128i t2h = _mm_loadu_si128(static_cast<const __m128i*>(static_cast<const void*>(lookup::byte_2_high.data())));
|
||||||
|
const __m128i nibble = _mm_set1_epi8(0x0F);
|
||||||
|
const __m128i zero = _mm_setzero_si128();
|
||||||
|
__m128i prev = zero;
|
||||||
|
while (e - block >= 16)
|
||||||
|
{
|
||||||
|
const __m128i in = _mm_loadu_si128(static_cast<const __m128i*>(static_cast<const void*>(block)));
|
||||||
|
const __m128i special = _mm_or_si128(_mm_or_si128(_mm_cmpeq_epi8(in, _mm_set1_epi8('"')), _mm_cmpeq_epi8(in, _mm_set1_epi8('\\'))),
|
||||||
|
_mm_cmpeq_epi8(_mm_subs_epu8(in, _mm_set1_epi8(0x1F)), zero)); // in < 0x20
|
||||||
|
const __m128i prev1 = _mm_alignr_epi8(in, prev, 15);
|
||||||
|
const __m128i sc = _mm_and_si128(_mm_and_si128(_mm_shuffle_epi8(t1h, _mm_and_si128(_mm_srli_epi16(prev1, 4), nibble)),
|
||||||
|
_mm_shuffle_epi8(t1l, _mm_and_si128(prev1, nibble))),
|
||||||
|
_mm_shuffle_epi8(t2h, _mm_and_si128(_mm_srli_epi16(in, 4), nibble)));
|
||||||
|
const __m128i must23 = _mm_or_si128(_mm_subs_epu8(_mm_alignr_epi8(in, prev, 14), _mm_set1_epi8(0xE0 - 0x80)),
|
||||||
|
_mm_subs_epu8(_mm_alignr_epi8(in, prev, 13), _mm_set1_epi8(0xF0 - 0x80)));
|
||||||
|
const __m128i err = _mm_xor_si128(_mm_and_si128(must23, _mm_set1_epi8(static_cast<char>(-128))), sc);
|
||||||
|
const auto special_bits = static_cast<unsigned>(_mm_movemask_epi8(special));
|
||||||
|
const auto err_bits = ~static_cast<unsigned>(_mm_movemask_epi8(_mm_cmpeq_epi8(err, zero))) & 0xFFFFu;
|
||||||
|
if (special_bits != 0)
|
||||||
|
{
|
||||||
|
const unsigned k = static_cast<unsigned>(count_trailing_zeros(static_cast<std::uint64_t>(special_bits)));
|
||||||
|
if ((err_bits & ((2u << k) - 1u)) == 0)
|
||||||
|
{
|
||||||
|
return block + k;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (err_bits != 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
prev = in;
|
||||||
|
block += 16;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return scan_string_finish(p, block, e, plain);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace view
|
||||||
|
} // namespace detail
|
||||||
|
NLOHMANN_JSON_NAMESPACE_END
|
||||||
@@ -395,6 +395,290 @@ NLOHMANN_JSON_NAMESPACE_END
|
|||||||
// #include <nlohmann/json.hpp>
|
// #include <nlohmann/json.hpp>
|
||||||
// #include <nlohmann/detail/view/macro_scope.hpp>
|
// #include <nlohmann/detail/view/macro_scope.hpp>
|
||||||
|
|
||||||
|
// #include <nlohmann/detail/view/simd.hpp>
|
||||||
|
// __ _____ _____ _____
|
||||||
|
// __| | __| | | | JSON for Modern C++
|
||||||
|
// | | |__ | | | | | | version 3.12.0
|
||||||
|
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||||
|
//
|
||||||
|
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
||||||
|
// SPDX-FileCopyrightText: 2018-2025 The simdjson authors <https://github.com/simdjson/simdjson>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include <array> // array
|
||||||
|
#include <cstddef> // size_t
|
||||||
|
#include <cstdint> // uint8_t, uint64_t
|
||||||
|
|
||||||
|
// #include <nlohmann/json.hpp>
|
||||||
|
// #include <nlohmann/detail/view/macro_scope.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
// Vector code for long runs of string bytes. NEON (AArch64) and SSE2 (x86-64)
|
||||||
|
// belong to the baseline instruction sets and are used by default. The vector
|
||||||
|
// UTF-8 check needs NEON, or SSSE3 if JSON_VIEW_USE_SSSE3 is defined: SSSE3 is
|
||||||
|
// not part of x86-64, so it must not depend on the flags of a translation unit
|
||||||
|
// (two translation units with different flags would have different
|
||||||
|
// definitions of the same inline functions). JSON_VIEW_NO_SIMD selects the
|
||||||
|
// portable code.
|
||||||
|
#if !defined(JSON_VIEW_NO_SIMD) && defined(__aarch64__) && (defined(__GNUC__) || defined(__clang__)) && NLOHMANN_VIEW_LITTLE_ENDIAN
|
||||||
|
#include <arm_neon.h>
|
||||||
|
#define NLOHMANN_VIEW_NEON 1
|
||||||
|
#else
|
||||||
|
#define NLOHMANN_VIEW_NEON 0
|
||||||
|
#endif
|
||||||
|
#if !defined(JSON_VIEW_NO_SIMD) && !NLOHMANN_VIEW_NEON && (defined(__SSE2__) || defined(_M_X64) || (defined(_M_IX86_FP) && _M_IX86_FP >= 2))
|
||||||
|
#include <emmintrin.h>
|
||||||
|
#define NLOHMANN_VIEW_SSE2 1
|
||||||
|
#else
|
||||||
|
#define NLOHMANN_VIEW_SSE2 0
|
||||||
|
#endif
|
||||||
|
#if NLOHMANN_VIEW_SSE2 && defined(JSON_VIEW_USE_SSSE3)
|
||||||
|
#include <tmmintrin.h>
|
||||||
|
#define NLOHMANN_VIEW_SSSE3 1 // NOLINT(cppcoreguidelines-macro-to-enum,modernize-macro-to-enum)
|
||||||
|
#else
|
||||||
|
#define NLOHMANN_VIEW_SSSE3 0 // NOLINT(cppcoreguidelines-macro-to-enum,modernize-macro-to-enum)
|
||||||
|
#endif
|
||||||
|
#define NLOHMANN_VIEW_VECTOR (NLOHMANN_VIEW_NEON || NLOHMANN_VIEW_SSE2)
|
||||||
|
#define NLOHMANN_VIEW_VECTOR_UTF8 (NLOHMANN_VIEW_NEON || NLOHMANN_VIEW_SSSE3)
|
||||||
|
|
||||||
|
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
namespace view
|
||||||
|
{
|
||||||
|
|
||||||
|
#if NLOHMANN_VIEW_VECTOR
|
||||||
|
/*!
|
||||||
|
@brief the first byte of a string run that is a quote, a backslash, a control
|
||||||
|
character, or not ASCII, 16 bytes per step
|
||||||
|
|
||||||
|
Stops at such a byte, or where fewer than 16 bytes are left (the caller tells
|
||||||
|
the two apart). A signed compare with 0x20 finds control characters and
|
||||||
|
non-ASCII bytes at once.
|
||||||
|
*/
|
||||||
|
NLOHMANN_VIEW_ALWAYS_INLINE const unsigned char* vector_plain_run(const unsigned char* p, const unsigned char* e) noexcept
|
||||||
|
{
|
||||||
|
while (e - p >= 16)
|
||||||
|
{
|
||||||
|
#if NLOHMANN_VIEW_NEON
|
||||||
|
const uint8x16_t in = vld1q_u8(p);
|
||||||
|
const uint8x16_t special = vorrq_u8(vorrq_u8(vceqq_u8(in, vdupq_n_u8('"')), vceqq_u8(in, vdupq_n_u8('\\'))),
|
||||||
|
vcltq_s8(vreinterpretq_s8_u8(in), vdupq_n_s8(0x20)));
|
||||||
|
// one nibble per byte (the usual NEON replacement of x86's movemask, see
|
||||||
|
// D. Kutenin, "Porting x86 vector bitmask optimizations to Arm NEON", 2022)
|
||||||
|
const std::uint64_t bits = vget_lane_u64(vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(special), 4)), 0);
|
||||||
|
if (bits != 0)
|
||||||
|
{
|
||||||
|
return p + (count_trailing_zeros(bits) >> 2u);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
const __m128i in = _mm_loadu_si128(static_cast<const __m128i*>(static_cast<const void*>(p)));
|
||||||
|
const __m128i special = _mm_or_si128(_mm_or_si128(_mm_cmpeq_epi8(in, _mm_set1_epi8('"')), _mm_cmpeq_epi8(in, _mm_set1_epi8('\\'))),
|
||||||
|
_mm_cmplt_epi8(in, _mm_set1_epi8(0x20)));
|
||||||
|
const auto bits = static_cast<std::uint64_t>(static_cast<unsigned>(_mm_movemask_epi8(special)));
|
||||||
|
if (bits != 0)
|
||||||
|
{
|
||||||
|
return p + count_trailing_zeros(bits);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
p += 16;
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if NLOHMANN_VIEW_VECTOR_UTF8
|
||||||
|
/// Tables of the UTF-8 check of J. Keiser and D. Lemire, "Validating UTF-8 In
|
||||||
|
/// Less Than One Instruction Per Byte" (2021), as in simdjson ("lookup4"): each
|
||||||
|
/// maps a nibble (high and low nibble of the previous byte, high nibble of the
|
||||||
|
/// current byte) to the errors it allows; a byte pair is ill-formed if all
|
||||||
|
/// three have an error bit in common.
|
||||||
|
template<typename Dummy = void>
|
||||||
|
struct utf8_lookup4
|
||||||
|
{
|
||||||
|
static constexpr std::uint8_t too_short = 1u << 0u, too_long = 1u << 1u, overlong_3 = 1u << 2u, too_large = 1u << 3u;
|
||||||
|
static constexpr std::uint8_t surrogate = 1u << 4u, overlong_2 = 1u << 5u, too_large_1000 = 1u << 6u, overlong_4 = 1u << 6u;
|
||||||
|
static constexpr std::uint8_t two_conts = 1u << 7u, carry = too_short | too_long | two_conts;
|
||||||
|
static const std::array<std::uint8_t, 16> byte_1_high;
|
||||||
|
static const std::array<std::uint8_t, 16> byte_1_low;
|
||||||
|
static const std::array<std::uint8_t, 16> byte_2_high;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename Dummy>
|
||||||
|
const std::array<std::uint8_t, 16> utf8_lookup4<Dummy>::byte_1_high =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
too_long, too_long, too_long, too_long, too_long, too_long, too_long, too_long,
|
||||||
|
two_conts, two_conts, two_conts, two_conts,
|
||||||
|
too_short | overlong_2, too_short, too_short | overlong_3 | surrogate, too_short | too_large | too_large_1000 | overlong_4
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename Dummy>
|
||||||
|
const std::array<std::uint8_t, 16> utf8_lookup4<Dummy>::byte_1_low =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
carry | overlong_3 | overlong_2 | overlong_4, carry | overlong_2, carry, carry,
|
||||||
|
carry | too_large, carry | too_large | too_large_1000, carry | too_large | too_large_1000, carry | too_large | too_large_1000,
|
||||||
|
carry | too_large | too_large_1000, carry | too_large | too_large_1000, carry | too_large | too_large_1000, carry | too_large | too_large_1000,
|
||||||
|
carry | too_large | too_large_1000, carry | too_large | too_large_1000 | surrogate, carry | too_large | too_large_1000, carry | too_large | too_large_1000
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename Dummy>
|
||||||
|
const std::array<std::uint8_t, 16> utf8_lookup4<Dummy>::byte_2_high =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
too_short, too_short, too_short, too_short, too_short, too_short, too_short, too_short,
|
||||||
|
static_cast<std::uint8_t>(too_long | overlong_2 | two_conts | overlong_3 | too_large_1000 | overlong_4),
|
||||||
|
static_cast<std::uint8_t>(too_long | overlong_2 | two_conts | overlong_3 | too_large),
|
||||||
|
static_cast<std::uint8_t>(too_long | overlong_2 | two_conts | surrogate | too_large),
|
||||||
|
static_cast<std::uint8_t>(too_long | overlong_2 | two_conts | surrogate | too_large),
|
||||||
|
too_short, too_short, too_short, too_short
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// the end of scan_string_vector from block, where the vector loop stopped
|
||||||
|
/// (ill-formed UTF-8, or fewer than 16 bytes left): one byte or sequence at a
|
||||||
|
/// time, from the start of a sequence that crosses into the block
|
||||||
|
inline const unsigned char* scan_string_finish(const unsigned char* p, const unsigned char* block, const unsigned char* e, const std::uint8_t* plain) noexcept
|
||||||
|
{
|
||||||
|
for (int i = 1; i <= 3 && block - i >= p; ++i)
|
||||||
|
{
|
||||||
|
const unsigned char c = block[-i];
|
||||||
|
if (c < 0x80)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (c >= 0xC0)
|
||||||
|
{
|
||||||
|
const int len = 2 + static_cast<int>(c >= 0xE0) + static_cast<int>(c >= 0xF0);
|
||||||
|
if (len > i)
|
||||||
|
{
|
||||||
|
block -= i;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (p = block; p != e;)
|
||||||
|
{
|
||||||
|
if (*p < 0x80)
|
||||||
|
{
|
||||||
|
if (plain[*p] == 0)
|
||||||
|
{
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
++p;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const std::size_t n = validate_one_utf8(p, static_cast<std::size_t>(e - p));
|
||||||
|
if (n == 0)
|
||||||
|
{
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
p += n;
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief the rest of a string from p (a character boundary), 16 bytes per step
|
||||||
|
|
||||||
|
The first quote, backslash, or control character is found with vector
|
||||||
|
compares, and the UTF-8 check covers the bytes up to it. Returns where the
|
||||||
|
string scan stops, like scan_string_run: before ill-formed UTF-8 and for the
|
||||||
|
last bytes of the input, the bytes are checked one sequence at a time. Out of
|
||||||
|
line, so that no constants of the check occupy registers in the parse loop.
|
||||||
|
*/
|
||||||
|
NLOHMANN_VIEW_NOINLINE inline const unsigned char* scan_string_vector(const unsigned char* p, const unsigned char* e, const std::uint8_t* plain) noexcept
|
||||||
|
{
|
||||||
|
using lookup = utf8_lookup4<>;
|
||||||
|
const unsigned char* block = p;
|
||||||
|
#if NLOHMANN_VIEW_NEON
|
||||||
|
const uint8x16_t t1h = vld1q_u8(lookup::byte_1_high.data());
|
||||||
|
const uint8x16_t t1l = vld1q_u8(lookup::byte_1_low.data());
|
||||||
|
const uint8x16_t t2h = vld1q_u8(lookup::byte_2_high.data());
|
||||||
|
uint8x16_t prev = vdupq_n_u8(0);
|
||||||
|
while (e - block >= 16)
|
||||||
|
{
|
||||||
|
const uint8x16_t in = vld1q_u8(block);
|
||||||
|
const uint8x16_t special = vorrq_u8(vorrq_u8(vceqq_u8(in, vdupq_n_u8('"')), vceqq_u8(in, vdupq_n_u8('\\'))), vcltq_u8(in, vdupq_n_u8(0x20)));
|
||||||
|
const uint8x16_t prev1 = vextq_u8(prev, in, 15);
|
||||||
|
const uint8x16_t sc = vandq_u8(vandq_u8(vqtbl1q_u8(t1h, vshrq_n_u8(prev1, 4)), vqtbl1q_u8(t1l, vandq_u8(prev1, vdupq_n_u8(0x0F)))), vqtbl1q_u8(t2h, vshrq_n_u8(in, 4)));
|
||||||
|
const uint8x16_t must23 = vorrq_u8(vqsubq_u8(vextq_u8(prev, in, 14), vdupq_n_u8(0xE0 - 0x80)), vqsubq_u8(vextq_u8(prev, in, 13), vdupq_n_u8(0xF0 - 0x80)));
|
||||||
|
const uint8x16_t err = veorq_u8(vandq_u8(must23, vdupq_n_u8(0x80)), sc);
|
||||||
|
const std::uint64_t special_bits = vget_lane_u64(vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(special), 4)), 0);
|
||||||
|
const std::uint64_t err_bits = vget_lane_u64(vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(vtstq_u8(err, err)), 4)), 0);
|
||||||
|
if (special_bits != 0)
|
||||||
|
{
|
||||||
|
// errors up to the special byte count (an incomplete sequence
|
||||||
|
// before a quote shows at the quote); the bytes after it do not
|
||||||
|
const unsigned k = static_cast<unsigned>(count_trailing_zeros(special_bits)) >> 2u;
|
||||||
|
const std::uint64_t upto = k == 15 ? ~std::uint64_t{0} :
|
||||||
|
(std::uint64_t{1} << (4u * (k + 1u))) - 1u;
|
||||||
|
if ((err_bits & upto) == 0)
|
||||||
|
{
|
||||||
|
return block + k;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (err_bits != 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
prev = in;
|
||||||
|
block += 16;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// the same with SSSE3 (pshufb for the table lookups; nibbles from 16-bit
|
||||||
|
// shifts, as there are no byte shifts)
|
||||||
|
const __m128i t1h = _mm_loadu_si128(static_cast<const __m128i*>(static_cast<const void*>(lookup::byte_1_high.data())));
|
||||||
|
const __m128i t1l = _mm_loadu_si128(static_cast<const __m128i*>(static_cast<const void*>(lookup::byte_1_low.data())));
|
||||||
|
const __m128i t2h = _mm_loadu_si128(static_cast<const __m128i*>(static_cast<const void*>(lookup::byte_2_high.data())));
|
||||||
|
const __m128i nibble = _mm_set1_epi8(0x0F);
|
||||||
|
const __m128i zero = _mm_setzero_si128();
|
||||||
|
__m128i prev = zero;
|
||||||
|
while (e - block >= 16)
|
||||||
|
{
|
||||||
|
const __m128i in = _mm_loadu_si128(static_cast<const __m128i*>(static_cast<const void*>(block)));
|
||||||
|
const __m128i special = _mm_or_si128(_mm_or_si128(_mm_cmpeq_epi8(in, _mm_set1_epi8('"')), _mm_cmpeq_epi8(in, _mm_set1_epi8('\\'))),
|
||||||
|
_mm_cmpeq_epi8(_mm_subs_epu8(in, _mm_set1_epi8(0x1F)), zero)); // in < 0x20
|
||||||
|
const __m128i prev1 = _mm_alignr_epi8(in, prev, 15);
|
||||||
|
const __m128i sc = _mm_and_si128(_mm_and_si128(_mm_shuffle_epi8(t1h, _mm_and_si128(_mm_srli_epi16(prev1, 4), nibble)),
|
||||||
|
_mm_shuffle_epi8(t1l, _mm_and_si128(prev1, nibble))),
|
||||||
|
_mm_shuffle_epi8(t2h, _mm_and_si128(_mm_srli_epi16(in, 4), nibble)));
|
||||||
|
const __m128i must23 = _mm_or_si128(_mm_subs_epu8(_mm_alignr_epi8(in, prev, 14), _mm_set1_epi8(0xE0 - 0x80)),
|
||||||
|
_mm_subs_epu8(_mm_alignr_epi8(in, prev, 13), _mm_set1_epi8(0xF0 - 0x80)));
|
||||||
|
const __m128i err = _mm_xor_si128(_mm_and_si128(must23, _mm_set1_epi8(static_cast<char>(-128))), sc);
|
||||||
|
const auto special_bits = static_cast<unsigned>(_mm_movemask_epi8(special));
|
||||||
|
const auto err_bits = ~static_cast<unsigned>(_mm_movemask_epi8(_mm_cmpeq_epi8(err, zero))) & 0xFFFFu;
|
||||||
|
if (special_bits != 0)
|
||||||
|
{
|
||||||
|
const unsigned k = static_cast<unsigned>(count_trailing_zeros(static_cast<std::uint64_t>(special_bits)));
|
||||||
|
if ((err_bits & ((2u << k) - 1u)) == 0)
|
||||||
|
{
|
||||||
|
return block + k;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (err_bits != 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
prev = in;
|
||||||
|
block += 16;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return scan_string_finish(p, block, e, plain);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace view
|
||||||
|
} // namespace detail
|
||||||
|
NLOHMANN_JSON_NAMESPACE_END
|
||||||
|
|
||||||
|
|
||||||
// Scanning primitives of the view's parser. The unrolled checks at fixed
|
// Scanning primitives of the view's parser. The unrolled checks at fixed
|
||||||
// offsets follow yyjson (https://github.com/ibireme/yyjson, MIT license): the
|
// offsets follow yyjson (https://github.com/ibireme/yyjson, MIT license): the
|
||||||
@@ -441,9 +725,12 @@ NLOHMANN_VIEW_ALWAYS_INLINE std::uint16_t load16(const unsigned char* p) noexcep
|
|||||||
|
|
||||||
/// Advance over plain string bytes and well-formed UTF-8. Stops at a quote,
|
/// Advance over plain string bytes and well-formed UTF-8. Stops at a quote,
|
||||||
/// a backslash, a control character, ill-formed UTF-8, or the end. The first
|
/// a backslash, a control character, ill-formed UTF-8, or the end. The first
|
||||||
/// 16 bytes are checked one by one, so that the position advances by
|
/// bytes are checked one by one, so that the position advances by constants
|
||||||
/// constants in predicted branches (most strings are short); longer runs
|
/// in predicted branches: 16 for keys, whose lengths repeat from record to
|
||||||
/// continue eight bytes at a time.
|
/// record, and 8 for string values (Value) where a vector loop follows, as
|
||||||
|
/// their lengths vary more. Longer runs continue 16 bytes at a time with NEON
|
||||||
|
/// or SSE2, else eight bytes at a time.
|
||||||
|
template<bool Value = false>
|
||||||
NLOHMANN_VIEW_ALWAYS_INLINE const unsigned char* scan_string_run(const unsigned char* p, const unsigned char* e) noexcept
|
NLOHMANN_VIEW_ALWAYS_INLINE const unsigned char* scan_string_run(const unsigned char* p, const unsigned char* e) noexcept
|
||||||
{
|
{
|
||||||
const std::uint8_t* plain = string_plain();
|
const std::uint8_t* plain = string_plain();
|
||||||
@@ -452,9 +739,23 @@ NLOHMANN_VIEW_ALWAYS_INLINE const unsigned char* scan_string_run(const unsigned
|
|||||||
if (e - p >= 16)
|
if (e - p >= 16)
|
||||||
{
|
{
|
||||||
#define NLOHMANN_VIEW_STEP(i) if (NLOHMANN_VIEW_LIKELY(plain[p[i]] != 0)) {} else { p += (i); goto stop; }
|
#define NLOHMANN_VIEW_STEP(i) if (NLOHMANN_VIEW_LIKELY(plain[p[i]] != 0)) {} else { p += (i); goto stop; }
|
||||||
NLOHMANN_VIEW_REPEAT16(NLOHMANN_VIEW_STEP)
|
NLOHMANN_VIEW_STEP(0) NLOHMANN_VIEW_STEP(1) NLOHMANN_VIEW_STEP(2) NLOHMANN_VIEW_STEP(3)
|
||||||
|
NLOHMANN_VIEW_STEP(4) NLOHMANN_VIEW_STEP(5) NLOHMANN_VIEW_STEP(6) NLOHMANN_VIEW_STEP(7)
|
||||||
|
if (!Value || !NLOHMANN_VIEW_VECTOR)
|
||||||
|
{
|
||||||
|
NLOHMANN_VIEW_STEP(8) NLOHMANN_VIEW_STEP(9) NLOHMANN_VIEW_STEP(10) NLOHMANN_VIEW_STEP(11)
|
||||||
|
NLOHMANN_VIEW_STEP(12) NLOHMANN_VIEW_STEP(13) NLOHMANN_VIEW_STEP(14) NLOHMANN_VIEW_STEP(15)
|
||||||
|
p += 8;
|
||||||
|
}
|
||||||
#undef NLOHMANN_VIEW_STEP
|
#undef NLOHMANN_VIEW_STEP
|
||||||
p += 16;
|
p += 8;
|
||||||
|
#if NLOHMANN_VIEW_VECTOR
|
||||||
|
p = vector_plain_run(p, e);
|
||||||
|
if (p != e && plain[*p] == 0)
|
||||||
|
{
|
||||||
|
goto stop;
|
||||||
|
}
|
||||||
|
#else
|
||||||
while (e - p >= 8)
|
while (e - p >= 8)
|
||||||
{
|
{
|
||||||
const std::uint64_t special = swar_string_special(read_eight_bytes(p));
|
const std::uint64_t special = swar_string_special(read_eight_bytes(p));
|
||||||
@@ -465,6 +766,7 @@ NLOHMANN_VIEW_ALWAYS_INLINE const unsigned char* scan_string_run(const unsigned
|
|||||||
}
|
}
|
||||||
p += 8;
|
p += 8;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
while (p != e && plain[*p] != 0)
|
while (p != e && plain[*p] != 0)
|
||||||
@@ -480,6 +782,10 @@ stop:
|
|||||||
{
|
{
|
||||||
return p; // quote, backslash, or control character
|
return p; // quote, backslash, or control character
|
||||||
}
|
}
|
||||||
|
#if NLOHMANN_VIEW_VECTOR_UTF8
|
||||||
|
// non-ASCII: the vector check, out of line
|
||||||
|
return scan_string_vector(p, e, plain);
|
||||||
|
#else
|
||||||
// non-ASCII: a run of well-formed sequences (the library's check, so
|
// non-ASCII: a run of well-formed sequences (the library's check, so
|
||||||
// that exactly what json::parse accepts is accepted)
|
// that exactly what json::parse accepts is accepted)
|
||||||
do
|
do
|
||||||
@@ -492,6 +798,7 @@ stop:
|
|||||||
p += n;
|
p += n;
|
||||||
}
|
}
|
||||||
while (p != e && *p >= 0x80);
|
while (p != e && *p >= 0x80);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1047,7 +1354,7 @@ class builder
|
|||||||
switch (cur()) \
|
switch (cur()) \
|
||||||
{ \
|
{ \
|
||||||
case '"': \
|
case '"': \
|
||||||
if (NLOHMANN_VIEW_UNLIKELY(!string())) { return false; } \
|
if (NLOHMANN_VIEW_UNLIKELY(!string<true>())) { return false; } \
|
||||||
goto NEXT; \
|
goto NEXT; \
|
||||||
case '{': \
|
case '{': \
|
||||||
open(value_t::object); \
|
open(value_t::object); \
|
||||||
@@ -1131,7 +1438,7 @@ obj_key:
|
|||||||
{
|
{
|
||||||
return fail(error_code::expected_key);
|
return fail(error_code::expected_key);
|
||||||
}
|
}
|
||||||
if (NLOHMANN_VIEW_UNLIKELY(!string()))
|
if (NLOHMANN_VIEW_UNLIKELY(!string<false>()))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1234,7 +1541,7 @@ root_done:
|
|||||||
switch (cur())
|
switch (cur())
|
||||||
{
|
{
|
||||||
case '"':
|
case '"':
|
||||||
return string();
|
return string<true>();
|
||||||
case 't':
|
case 't':
|
||||||
return literal("true", 4, value_t::boolean, node_flags::is_true);
|
return literal("true", 4, value_t::boolean, node_flags::is_true);
|
||||||
case 'f':
|
case 'f':
|
||||||
@@ -1521,12 +1828,13 @@ indent_done:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// a string (value or key) at p
|
/// a string at p: a value (Value) or a key
|
||||||
|
template<bool Value>
|
||||||
NLOHMANN_VIEW_ALWAYS_INLINE bool string()
|
NLOHMANN_VIEW_ALWAYS_INLINE bool string()
|
||||||
{
|
{
|
||||||
++p; // opening quote
|
++p; // opening quote
|
||||||
const unsigned char* const s = p;
|
const unsigned char* const s = p;
|
||||||
p = scan_string_run(p, e);
|
p = scan_string_run<Value>(p, e);
|
||||||
if (NLOHMANN_VIEW_LIKELY(p != e && *p == '"'))
|
if (NLOHMANN_VIEW_LIKELY(p != e && *p == '"'))
|
||||||
{
|
{
|
||||||
emit(value_t::string, 0, 0, static_cast<std::size_t>(s - b), static_cast<std::uint64_t>(p - s));
|
emit(value_t::string, 0, 0, static_cast<std::size_t>(s - b), static_cast<std::uint64_t>(p - s));
|
||||||
@@ -4751,6 +5059,11 @@ class tuple_element<N, ::nlohmann::detail::view::view_item<View>> // NOLINT(cert
|
|||||||
#undef NLOHMANN_VIEW_THROW
|
#undef NLOHMANN_VIEW_THROW
|
||||||
#undef NLOHMANN_VIEW_LITTLE_ENDIAN
|
#undef NLOHMANN_VIEW_LITTLE_ENDIAN
|
||||||
#undef NLOHMANN_VIEW_REPEAT16
|
#undef NLOHMANN_VIEW_REPEAT16
|
||||||
|
#undef NLOHMANN_VIEW_NEON
|
||||||
|
#undef NLOHMANN_VIEW_SSE2
|
||||||
|
#undef NLOHMANN_VIEW_SSSE3
|
||||||
|
#undef NLOHMANN_VIEW_VECTOR
|
||||||
|
#undef NLOHMANN_VIEW_VECTOR_UTF8
|
||||||
|
|
||||||
|
|
||||||
#endif // INCLUDE_NLOHMANN_JSON_VIEW_HPP_
|
#endif // INCLUDE_NLOHMANN_JSON_VIEW_HPP_
|
||||||
|
|||||||
@@ -324,6 +324,26 @@ json_test_add_test_for(src/unit-diagnostic-positions.cpp
|
|||||||
MAIN test_main CXX_STANDARDS ${test_cxx_standards} ${test_force}
|
MAIN test_main CXX_STANDARDS ${test_cxx_standards} ${test_force}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# the json_view parser again with the portable string scanning instead of
|
||||||
|
# NEON/SSE2, and on x86-64 with the SSSE3 UTF-8 check (JSON_VIEW_USE_SSSE3)
|
||||||
|
json_test_set_test_options(test-json_view_builder_portable
|
||||||
|
COMPILE_DEFINITIONS JSON_VIEW_NO_SIMD
|
||||||
|
)
|
||||||
|
json_test_add_test_for(src/unit-json_view_builder.cpp
|
||||||
|
NAME test-json_view_builder_portable
|
||||||
|
MAIN test_main CXX_STANDARDS ${test_cxx_standards} ${test_force}
|
||||||
|
)
|
||||||
|
if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86_64|AMD64|amd64)$" AND NOT MSVC)
|
||||||
|
json_test_set_test_options(test-json_view_builder_ssse3
|
||||||
|
COMPILE_DEFINITIONS JSON_VIEW_USE_SSSE3
|
||||||
|
COMPILE_OPTIONS -mssse3
|
||||||
|
)
|
||||||
|
json_test_add_test_for(src/unit-json_view_builder.cpp
|
||||||
|
NAME test-json_view_builder_ssse3
|
||||||
|
MAIN test_main CXX_STANDARDS ${test_cxx_standards} ${test_force}
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
# *DO NOT* use json_test_set_test_options() below this line
|
# *DO NOT* use json_test_set_test_options() below this line
|
||||||
|
|
||||||
#############################################################################
|
#############################################################################
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
#endif
|
#endif
|
||||||
using nlohmann::json;
|
using nlohmann::json;
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <map>
|
#include <map>
|
||||||
@@ -142,6 +143,32 @@ void check_same(const std::string& text)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// a string value and a key must be accepted or rejected as json::parse does,
|
||||||
|
// and give its value (cheaper than check_same: the options do not matter)
|
||||||
|
void check_string(const std::string& content)
|
||||||
|
{
|
||||||
|
for (const std::string& text :
|
||||||
|
{
|
||||||
|
"[\"" + content + "\"]", "{\"" + content + "\":1}"
|
||||||
|
})
|
||||||
|
{
|
||||||
|
const bool accepted = json::accept(text);
|
||||||
|
for (const bool sentinel :
|
||||||
|
{
|
||||||
|
true, false
|
||||||
|
})
|
||||||
|
{
|
||||||
|
const built b = build(text, false, false, sentinel);
|
||||||
|
if (b.ok != accepted || (accepted && value_of(b) != json::parse(text)))
|
||||||
|
{
|
||||||
|
CAPTURE(text);
|
||||||
|
CHECK(b.ok == accepted);
|
||||||
|
CHECK((b.ok && accepted ? value_of(b) == json::parse(text) : true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// a small deterministic generator of documents
|
// a small deterministic generator of documents
|
||||||
struct generator
|
struct generator
|
||||||
{
|
{
|
||||||
@@ -401,3 +428,81 @@ TEST_CASE("json_view builder")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("json_view builder: strings across vector blocks")
|
||||||
|
{
|
||||||
|
// Strings are scanned 8 or 16 bytes at a time (NEON, SSE2, or SWAR) and
|
||||||
|
// non-ASCII text with the vector UTF-8 check (NEON, SSSE3) or one sequence
|
||||||
|
// at a time. Sequences are placed so that they start at every offset
|
||||||
|
// around the block boundaries of keys (16, 32) and values (8, 24), with
|
||||||
|
// text of several lengths after them.
|
||||||
|
const std::array<std::size_t, 16> prefixes = {{0, 6, 7, 8, 13, 14, 15, 16, 21, 22, 23, 24, 29, 30, 31, 32}};
|
||||||
|
const std::array<std::size_t, 3> suffixes = {{0, 3, 17}};
|
||||||
|
const auto around = [&](const std::string & seq, std::size_t prefix, std::size_t suffix)
|
||||||
|
{
|
||||||
|
return std::string(prefix, 'a') + seq + std::string(suffix, 'b');
|
||||||
|
};
|
||||||
|
|
||||||
|
SECTION("every two-byte sequence")
|
||||||
|
{
|
||||||
|
for (unsigned lead = 0x80; lead <= 0xFF; ++lead)
|
||||||
|
{
|
||||||
|
for (unsigned second = 0; second <= 0xFF; ++second)
|
||||||
|
{
|
||||||
|
if (second == '"' || second == '\\')
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const std::string seq = {static_cast<char>(lead), static_cast<char>(second)};
|
||||||
|
check_string(around(seq, prefixes[(lead + second) % 16], suffixes[second % 3]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("three- and four-byte sequences")
|
||||||
|
{
|
||||||
|
const std::array<unsigned, 8> conts = {{0x7F, 0x80, 0x8F, 0x90, 0x9F, 0xA0, 0xBF, 0xC0}};
|
||||||
|
for (unsigned lead = 0xE0; lead <= 0xF7; ++lead)
|
||||||
|
{
|
||||||
|
for (const unsigned b2 : conts)
|
||||||
|
{
|
||||||
|
for (const unsigned b3 : conts)
|
||||||
|
{
|
||||||
|
for (const std::size_t prefix : prefixes)
|
||||||
|
{
|
||||||
|
std::string seq = {static_cast<char>(lead), static_cast<char>(b2), static_cast<char>(b3)};
|
||||||
|
if (lead >= 0xF0)
|
||||||
|
{
|
||||||
|
seq += static_cast<char>(prefix % 2 == 0 ? 0x80 : 0xBF);
|
||||||
|
}
|
||||||
|
check_string(around(seq, prefix, suffixes[prefix % 3]));
|
||||||
|
// cut short before the end of the string
|
||||||
|
check_string(around(seq.substr(0, seq.size() - 1), prefix, suffixes[prefix % 3]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("long runs of text with one damaged byte")
|
||||||
|
{
|
||||||
|
const std::array<const char*, 7> chars = {{"a", "\xc3\xa9", "\xe3\x81\x82", "\xf0\x9f\x98\x80", "\xed\x9f\xbf", "\xef\xbf\xbf", "\xf4\x8f\xbf\xbf"}};
|
||||||
|
const std::array<char, 11> damage = {{'\x80', '\xbf', '\xc0', '\xc1', '\xe0', '\xed', '\xf5', '\xff', '\x1f', '"', '\\'}};
|
||||||
|
std::mt19937 rng(5295); // NOLINT(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed): reproducible
|
||||||
|
for (int i = 0; i < 4000; ++i)
|
||||||
|
{
|
||||||
|
std::string text;
|
||||||
|
const auto n = rng() % 60;
|
||||||
|
for (unsigned k = 0; k < n; ++k)
|
||||||
|
{
|
||||||
|
text += chars[rng() % chars.size()];
|
||||||
|
}
|
||||||
|
check_string(text);
|
||||||
|
if (!text.empty())
|
||||||
|
{
|
||||||
|
text[rng() % text.size()] = damage[rng() % damage.size()];
|
||||||
|
check_string(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user