mirror of
https://github.com/simdjson/simdjson
synced 2026-06-08 17:27:07 +00:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5900ac42d9 | |||
| 5bb9c8eed5 | |||
| 122bea262a | |||
| e48f7bf98b |
@@ -42,14 +42,3 @@ jobs:
|
||||
echo -e '#include <simdjson.h>\nint main(int argc,char**argv) {simdjson::dom::parser parser;simdjson::dom::element tweets = parser.load(argv[1]); }' > tmp.cpp && c++ -Idestination/include -Ldestination/lib -std=c++17 -Wl,-rpath,destination/lib -o linkandrun tmp.cpp -lsimdjson && ./linkandrun jsonexamples/twitter.json &&
|
||||
cd ../tests/installation_tests/find &&
|
||||
mkdir buildshared && cd buildshared && cmake -DCMAKE_INSTALL_PREFIX:PATH=../../../buildshared/destination .. && cmake --build .
|
||||
- name: Use cmake (parsing for NaN/Infinity enabled)
|
||||
run: |
|
||||
mkdir build_nan_inf &&
|
||||
cd build_nan_inf &&
|
||||
cmake -DSIMDJSON_ENABLE_NAN_INF=ON -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
|
||||
cmake --build . &&
|
||||
ctest --output-on-failure -LE explicitonly -j &&
|
||||
cmake --install . &&
|
||||
echo -e '#include <simdjson.h>\nint main(int argc,char**argv) {simdjson::dom::parser parser;simdjson::dom::element tweets = parser.load(argv[1]); }' > tmp.cpp && c++ -Idestination/include -Ldestination/lib -std=c++17 -Wl,-rpath,destination/lib -o linkandrun tmp.cpp -lsimdjson && ./linkandrun jsonexamples/twitter.json &&
|
||||
cd ../tests/installation_tests/find &&
|
||||
mkdir build_nan_inf && cd build_nan_inf && cmake -DCMAKE_INSTALL_PREFIX:PATH=../../../build_nan_inf/destination .. && cmake --build .
|
||||
|
||||
@@ -21,13 +21,6 @@ jobs:
|
||||
cmake -DSIMDJSON_SANITIZE=ON -DCMAKE_BUILD_TYPE=Debug -DSIMDJSON_GOOGLE_BENCHMARKS=OFF -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF .. &&
|
||||
cmake --build . &&
|
||||
ctest --output-on-failure -LE explicitonly -j
|
||||
- name: Use cmake with address sanitizer (Parsing of NaN/Infinity enabled)
|
||||
run: |
|
||||
mkdir builddebug_nan_inf &&
|
||||
cd builddebug_nan_inf &&
|
||||
cmake -DSIMDJSON_SANITIZE=ON -DCMAKE_BUILD_TYPE=Debug -DSIMDJSON_GOOGLE_BENCHMARKS=OFF -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF -DSIMDJSON_ENABLE_NAN_INF=ON .. &&
|
||||
cmake --build . &&
|
||||
ctest --output-on-failure -LE explicitonly -j
|
||||
ubuntu-build-undefined-sanitizer:
|
||||
if: >-
|
||||
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
|
||||
@@ -46,10 +39,3 @@ jobs:
|
||||
cmake -DSIMDJSON_SANITIZE_UNDEFINED=ON -DCMAKE_BUILD_TYPE=Debug -DSIMDJSON_GOOGLE_BENCHMARKS=OFF -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF .. &&
|
||||
cmake --build . &&
|
||||
ctest --output-on-failure -LE explicitonly -j
|
||||
- name: Use cmake with undefined sanitizer (Parsing of NaN/Infinity enabled)
|
||||
run: |
|
||||
mkdir builddebugundefsani_nan_inf &&
|
||||
cd builddebugundefsani_nan_inf &&
|
||||
cmake -DSIMDJSON_SANITIZE_UNDEFINED=ON -DCMAKE_BUILD_TYPE=Debug -DSIMDJSON_GOOGLE_BENCHMARKS=OFF -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF -DSIMDJSON_ENABLE_NAN_INF=ON .. &&
|
||||
cmake --build . &&
|
||||
ctest --output-on-failure -LE explicitonly -j
|
||||
|
||||
@@ -12,12 +12,11 @@ jobs:
|
||||
shared: [ON, OFF]
|
||||
cxx: [g++-13, clang++-16]
|
||||
sanitizer: [ON, OFF]
|
||||
nan_inf: [ON, OFF]
|
||||
build_type: [RelWithDebInfo, Debug, Release]
|
||||
steps:
|
||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
- name: Prepare
|
||||
run: cmake -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_SANITIZE=${{matrix.sanitizer}} -DBUILD_SHARED_LIBS=${{matrix.shared}} -DSIMDJSON_ENABLE_NAN_INF=${{matrix.nan_inf}} -B build
|
||||
run: cmake -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_SANITIZE=${{matrix.sanitizer}} -DBUILD_SHARED_LIBS=${{matrix.shared}} -B build
|
||||
env:
|
||||
CXX: ${{matrix.cxx}}
|
||||
- name: Build
|
||||
|
||||
@@ -13,22 +13,20 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- {gen: Visual Studio 17 2022, arch: Win32, shared: ON, build_type: Release, memory_map: OFF, nan_inf: OFF}
|
||||
- {gen: Visual Studio 17 2022, arch: Win32, shared: OFF, build_type: Release, memory_map: OFF, nan_inf: OFF}
|
||||
- {gen: Visual Studio 17 2022, arch: x64, shared: ON, build_type: Release, memory_map: OFF, nan_inf: OFF}
|
||||
- {gen: Visual Studio 17 2022, arch: x64, shared: OFF, build_type: Debug, memory_map: OFF, nan_inf: OFF}
|
||||
- {gen: Visual Studio 17 2022, arch: x64, shared: OFF, build_type: Release, memory_map: OFF, nan_inf: OFF}
|
||||
- {gen: Visual Studio 17 2022, arch: x64, shared: OFF, build_type: RelWithDebInfo, memory_map: OFF, nan_inf: OFF}
|
||||
- {gen: Visual Studio 17 2022, arch: x64, shared: OFF, build_type: Debug, memory_map: OFF, nan_inf: ON}
|
||||
- {gen: Visual Studio 17 2022, arch: x64, shared: OFF, build_type: Release, memory_map: OFF, nan_inf: ON}
|
||||
- {gen: Visual Studio 17 2022, arch: Win32, shared: ON, build_type: Release, memory_map: OFF}
|
||||
- {gen: Visual Studio 17 2022, arch: Win32, shared: OFF, build_type: Release, memory_map: OFF}
|
||||
- {gen: Visual Studio 17 2022, arch: x64, shared: ON, build_type: Release, memory_map: OFF}
|
||||
- {gen: Visual Studio 17 2022, arch: x64, shared: OFF, build_type: Debug, memory_map: OFF}
|
||||
- {gen: Visual Studio 17 2022, arch: x64, shared: OFF, build_type: Release, memory_map: OFF}
|
||||
- {gen: Visual Studio 17 2022, arch: x64, shared: OFF, build_type: RelWithDebInfo, memory_map: OFF}
|
||||
# Exercise the opt-in Windows memory-file mapping path at least once in CI.
|
||||
- {gen: Visual Studio 17 2022, arch: x64, shared: OFF, build_type: Release, memory_map: ON, nan_inf: OFF}
|
||||
- {gen: Visual Studio 17 2022, arch: x64, shared: OFF, build_type: Release, memory_map: ON}
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Configure
|
||||
run: |
|
||||
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_COMPETITION=OFF -DBUILD_SHARED_LIBS=${{matrix.shared}} -DSIMDJSON_ENABLE_MEMORY_FILE_MAPPING_ON_WINDOWS=${{matrix.memory_map}} -DSIMDJSON_ENABLE_NAN_INF=${{matrix.nan_inf}} -B build
|
||||
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_COMPETITION=OFF -DBUILD_SHARED_LIBS=${{matrix.shared}} -DSIMDJSON_ENABLE_MEMORY_FILE_MAPPING_ON_WINDOWS=${{matrix.memory_map}} -B build
|
||||
- name: Build Debug
|
||||
run: cmake --build build --config ${{matrix.build_type}} --verbose
|
||||
- name: Run tests
|
||||
@@ -41,4 +39,4 @@ jobs:
|
||||
- name: Test Installation
|
||||
run: |
|
||||
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -B build_install_test tests/installation_tests/find
|
||||
cmake --build build_install_test --config ${{matrix.build_type}}
|
||||
cmake --build build_install_test --config ${{matrix.build_type}}
|
||||
@@ -254,12 +254,6 @@ if(SIMDJSON_MINUS_ZERO_AS_FLOAT)
|
||||
simdjson_add_props(target_compile_definitions PRIVATE SIMDJSON_MINUS_ZERO_AS_FLOAT=1)
|
||||
endif(SIMDJSON_MINUS_ZERO_AS_FLOAT)
|
||||
|
||||
option(SIMDJSON_ENABLE_NAN_INF "Allow parsing of NaN and Infinity JSON values" OFF)
|
||||
if(SIMDJSON_ENABLE_NAN_INF)
|
||||
message(STATUS "simdjson NaN and Infinity parsing is enabled.")
|
||||
simdjson_add_props(target_compile_definitions PUBLIC SIMDJSON_ENABLE_NAN_INF=1)
|
||||
endif()
|
||||
|
||||
# GCC and Clang have horrendous Debug builds when using SIMD.
|
||||
# A common fix is to use '-Og' instead.
|
||||
# bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54412
|
||||
|
||||
@@ -40,6 +40,7 @@ SIMDJSON_POP_DISABLE_WARNINGS
|
||||
#include "json2msgpack/boostjson.h"
|
||||
|
||||
#include "partial_tweets/simdjson_ondemand.h"
|
||||
#include "partial_tweets/simdjson_ondemand_key_selector.h"
|
||||
#include "partial_tweets/simdjson_dom.h"
|
||||
#include "partial_tweets/yyjson.h"
|
||||
#if SIMDJSON_COMPETITION_ONDEMAND_SAJSON
|
||||
@@ -124,7 +125,6 @@ SIMDJSON_POP_DISABLE_WARNINGS
|
||||
#include "kostya/boostjson.h"
|
||||
|
||||
#include "large_random/simdjson_ondemand.h"
|
||||
#include "large_random/simdjson_ondemand_ranges.h"
|
||||
#if SIMDJSON_COMPETITION_ONDEMAND_UNORDERED
|
||||
#include "large_random/simdjson_ondemand_unordered.h"
|
||||
#endif // SIMDJSON_COMPETITION_ONDEMAND_UNORDERED
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#if SIMDJSON_EXCEPTIONS && SIMDJSON_SUPPORTS_RANGES
|
||||
|
||||
#include "large_random.h"
|
||||
|
||||
namespace large_random {
|
||||
|
||||
using namespace simdjson;
|
||||
|
||||
// Identical to simdjson_ondemand but uses get_range() for iteration.
|
||||
// Demonstrates that the ranges wrapper has zero per-element overhead.
|
||||
struct simdjson_ondemand_ranges {
|
||||
static constexpr diff_flags DiffFlags = diff_flags::NONE;
|
||||
|
||||
ondemand::parser parser{};
|
||||
|
||||
bool run(simdjson::padded_string &json, std::vector<point> &result) {
|
||||
auto doc = parser.iterate(json);
|
||||
for (auto coord_result : ondemand::get_range(doc.get_array())) {
|
||||
ondemand::object coord = coord_result;
|
||||
result.emplace_back(json_benchmark::point{coord.find_field("x"), coord.find_field("y"), coord.find_field("z")});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
BENCHMARK_TEMPLATE(large_random, simdjson_ondemand_ranges)->UseManualTime();
|
||||
|
||||
} // namespace large_random
|
||||
|
||||
#endif // SIMDJSON_EXCEPTIONS && SIMDJSON_SUPPORTS_RANGES
|
||||
@@ -103,12 +103,7 @@ error_code Sax::RunNoExcept(const padded_string &json) noexcept {
|
||||
|
||||
error_code Sax::Allocate(size_t new_capacity) {
|
||||
// string_capacity copied from document::allocate
|
||||
// a document with only zero-length strings... could have capacity/3 string
|
||||
// and we would need capacity/3 * 5 bytes on the string buffer
|
||||
if(5 * (new_capacity / 3) + SIMDJSON_PADDING < SIMDJSON_PADDING) {
|
||||
return CAPACITY; // overflow, only happen on legacy 32-bit systems with very large capacity
|
||||
}
|
||||
size_t string_capacity = SIMDJSON_ROUNDUP_N(5 * (new_capacity / 3) + SIMDJSON_PADDING, 64);
|
||||
size_t string_capacity = SIMDJSON_ROUNDUP_N(5 * new_capacity / 3 + SIMDJSON_PADDING, 64);
|
||||
string_buf.reset(new (std::nothrow) uint8_t[string_capacity]);
|
||||
if (auto error = dom_parser.set_capacity(new_capacity)) { return error; }
|
||||
if (capacity == 0) { // set max depth the first time only
|
||||
|
||||
+24
-86
@@ -28,9 +28,6 @@ separate document](https://github.com/simdjson/simdjson/blob/master/doc/builder.
|
||||
- [UTF-8 validation (alone)](#utf-8-validation-alone)
|
||||
- [JSON Pointer](#json-pointer)
|
||||
- [JSONPath](#jsonpath)
|
||||
* [Using `for_each_at_path_with_wildcard` for JSONPath Queries (On-Demand)](#using-for_each_at_path_with_wildcard-for-jsonpath-queries-on-demand)
|
||||
+ [Example Usage](#example-usage)
|
||||
- [C++20 Ranges Support](#c20-ranges-support)
|
||||
- [Compile-Time JSONPath and JSON Pointer (C++26 Reflection)](#compile-time-jsonpath-and-json-pointer-c26-reflection)
|
||||
- [Error handling](#error-handling)
|
||||
* [Error handling examples without exceptions](#error-handling-examples-without-exceptions)
|
||||
@@ -59,7 +56,7 @@ Requirements
|
||||
The simdjson library is widely deployed in popular systems such as the Node.js runtime
|
||||
environment.
|
||||
|
||||
- A recent compiler (LLVM clang 6 or better, GNU GCC 7.4 or better, Xcode 11 or better) on POSIX systems such as macOS, FreeBSD or Linux. We require that the compiler supports the C++11 standard or better. We test the library on a big-endian system (IBM s390x with Linux). We support [Fil-C, the memory-safe C/C++ compiler](https://fil-c.org).
|
||||
- A recent compiler (LLVM clang 6 or better, GNU GCC 7.4 or better, Xcode 11 or better) on POSIX systems such as macOS, FreeBSD or Linux. We require that the compiler supports the C++11 standard or better. We test the library on a big-endian system (IBM s390x with Linux).
|
||||
- Visual Studio 2017 or better. We support the LLVM clang compiler under Visual Studio (clang-cl) as well as the regular Visual Studio compiler. For better release performance (both compile time and execution time), we recommend Visual Studio users adopt LLVM (clang-cl). We discourage using GCC under Windows: there [is a long-running bug with GCC under Windows](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54412).
|
||||
|
||||
Support for AVX-512 require a processor with AVX512-VBMI2 support (Ice Lake or better, AMD Zen 4 or better) under a 64-bit system and a recent compiler (LLVM clang 6 or better, GCC 8 or better, Visual Studio 2019 or better). You need a correspondingly recent assembler such as gas (2.30+) or nasm (2.14+): recent compilers usually come with recent assemblers. If you mix a recent compiler with an incompatible/old assembler (e.g., when using a recent compiler with an old Linux distribution), you may get errors at build time because the compiler produces instructions that the assembler does not recognize: you should update your assembler to match your compiler (e.g., upgrade binutils to version 2.30 or better under Linux) or use an older compiler matching the capabilities of your assembler.
|
||||
@@ -1602,10 +1599,6 @@ type without a document instance like so:
|
||||
Car car = simdjson::from(json);
|
||||
```
|
||||
|
||||
The string must be a `simdjson::padded_string_view`, which can be created from an std::string
|
||||
instance with `simdjson::pad()` function, from a `simdjson::padded_string` instance, or string literal using the `_padded` user-defined literal.
|
||||
|
||||
|
||||
You can also use the `simdjson::from` syntax without exceptions, like so:
|
||||
```cpp
|
||||
Car car;
|
||||
@@ -1846,15 +1839,15 @@ int64_t x = obj.at_path("$.c.foo.a[1]"); // 20
|
||||
x = obj.at_path("$.d.foo2.a.2"); // 30
|
||||
```
|
||||
|
||||
## Using `for_each_at_path_with_wildcard` for JSONPath Queries (On-Demand)
|
||||
## Using `at_path_with_wildcard` for JSONPath Queries (On-Demand)
|
||||
|
||||
The `for_each_at_path_with_wildcard` function in simdjson extends the JSONPath querying capabilities by supporting wildcard expressions (`*`) in JSON paths. It calls a user-provided callback for each matching element, avoiding the need to materialize all results into a vector. For example, you can use `$.address.*` to fetch all fields within the `address` object or `$.phoneNumbers[*].numbers[*]` to retrieve all phone numbers across multiple objects in an array.
|
||||
The `at_path_with_wildcard` function in simdjson extends the JSONPath querying capabilities by supporting wildcard expressions (`*`) in JSON paths. This allows users to retrieve multiple elements from a JSON document in a single query. For example, you can use `$.address.*` to fetch all fields within the `address` object or `$.phoneNumbers[*].numbers[*]` to retrieve all phone numbers across multiple objects in an array.
|
||||
|
||||
The `*` wildcard matches all elements at a specific level. For instance, `$.address.*` retrieves all key-value pairs in the `address` object, while `$.*.streetAddress` fetches all `streetAddress` fields across objects at the root level. You can combine wildcards with array indexing. For example, `$.phoneNumbers[*].numbers[1]` retrieves the second number from each `numbers` array in the `phoneNumbers` array. If no elements match the wildcard query, the callback is simply never called. For instance, querying `$.empty_object.*` or `$.empty_array.*` will yield no callbacks.
|
||||
The `*` wildcard matches all elements at a specific level. For instance, `$.address.*` retrieves all key-value pairs in the `address` object, while `$.*.streetAddress` fetches all `streetAddress` fields across objects at the root level. You can combine wildcards with array indexing. For example, `$.phoneNumbers[*].numbers[1]` retrieves the second number from each `numbers` array in the `phoneNumbers` array. If no elements match the wildcard query, the function returns an empty result. For instance, querying `$.empty_object.*` or `$.empty_array.*` will yield an empty set.
|
||||
|
||||
### Example Usage
|
||||
|
||||
Here is an example demonstrating the use of `for_each_at_path_with_wildcard`:
|
||||
Here is an example demonstrating the use of `at_path_with_wildcard`:
|
||||
|
||||
```cpp
|
||||
simdjson::padded_string json_string = R"(
|
||||
@@ -1883,86 +1876,31 @@ ondemand::parser parser;
|
||||
ondemand::document doc = parser.iterate(json_string);
|
||||
|
||||
// Fetch all fields in the address object
|
||||
auto error = doc.for_each_at_path_with_wildcard("$.address.*",
|
||||
[](ondemand::value value) {
|
||||
std::string_view field;
|
||||
if (value.get(field) == SUCCESS) {
|
||||
std::cout << field << std::endl;
|
||||
}
|
||||
});
|
||||
std::vector<ondemand::value> values;
|
||||
auto error = doc.at_path_with_wildcard("$.address.*").get(values);
|
||||
if (!error) {
|
||||
for (auto value : values) {
|
||||
std::string_view field;
|
||||
if (value.get(field) == SUCCESS) {
|
||||
std::cout << field << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch all phone numbers
|
||||
doc.for_each_at_path_with_wildcard("$.phoneNumbers[*].numbers[*]",
|
||||
[](ondemand::value value) {
|
||||
std::string_view number;
|
||||
if (value.get(number) == SUCCESS) {
|
||||
std::cout << number << std::endl;
|
||||
}
|
||||
});
|
||||
error = doc.at_path_with_wildcard("$.phoneNumbers[*].numbers[*]").get(values);
|
||||
if (!error) {
|
||||
for (auto value : values) {
|
||||
std::string_view number;
|
||||
if (value.get(number) == SUCCESS) {
|
||||
std::cout << number << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This function is particularly useful for extracting data from complex JSON structures with nested arrays and objects. By leveraging wildcards, you can simplify your queries and reduce the need for multiple iterations.
|
||||
|
||||
## C++20 Ranges Support
|
||||
|
||||
When compiling with C++20 (or later), you can use `std::ranges` with the On-Demand API
|
||||
via the `get_range()` helper. This enables use of range adaptors such as `std::views::transform`.
|
||||
|
||||
```cpp
|
||||
#include "simdjson.h"
|
||||
#include <ranges>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
auto json = R"([
|
||||
{ "name": "Alice", "age": 30 },
|
||||
{ "name": "Bob", "age": 25 },
|
||||
{ "name": "Carol", "age": 35 }
|
||||
])"_padded;
|
||||
|
||||
ondemand::parser parser;
|
||||
auto doc = parser.iterate(json);
|
||||
auto arr = doc.get_array();
|
||||
|
||||
// Use std::views::transform to extract names
|
||||
auto names = ondemand::get_range(arr)
|
||||
| std::views::transform([](auto elem) -> std::string {
|
||||
return std::string(std::string_view(elem["name"]));
|
||||
});
|
||||
|
||||
for (auto name : names) {
|
||||
std::cout << name << std::endl; // Alice, Bob, Carol
|
||||
}
|
||||
```
|
||||
|
||||
The `get_range()` and `get_key_value_range()` functions wrap an `ondemand::array`
|
||||
or `ondemand::object` in a `std::ranges::view` that satisfies `std::ranges::input_range`.
|
||||
They work with both exception and non-exception code:
|
||||
|
||||
```cpp
|
||||
// With exceptions:
|
||||
auto range = ondemand::get_range(doc.get_array());
|
||||
|
||||
// Without exceptions:
|
||||
ondemand::array arr;
|
||||
if (doc.get_array().get(arr) == SUCCESS) {
|
||||
auto range = ondemand::get_range(arr);
|
||||
for (auto elem : range) { /* ... */ }
|
||||
}
|
||||
```
|
||||
|
||||
Object iteration uses `get_key_value_range()` and yields `simdjson_result<ondemand::field>` elements:
|
||||
|
||||
```cpp
|
||||
auto obj = doc.get_object();
|
||||
for (auto field_result : ondemand::get_key_value_range(obj)) {
|
||||
std::cout << field_result.key() << std::endl;
|
||||
}
|
||||
```
|
||||
|
||||
The range wrappers are zero-cost: they forward directly to the underlying
|
||||
On-Demand iterators with no value buffering or extra per-element overhead.
|
||||
|
||||
## Compile-Time JSONPath and JSON Pointer (C++26 Reflection)
|
||||
|
||||
The simdjson library provides **compile-time validated** JSONPath and JSON Pointer accessors when using C++26 Static Reflection. These accessors validate paths against struct definitions at compile time and generate optimized code with zero runtime overhead. In some cases, we find that it is much faster. Furthermore, it is safer in the sense that the expression
|
||||
|
||||
@@ -30,10 +30,6 @@ double from_chars(const char *first, const char* end) noexcept;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef SIMDJSON_ENABLE_NAN_INF
|
||||
#define SIMDJSON_ENABLE_NAN_INF 0
|
||||
#endif
|
||||
|
||||
} // namespace simdjson
|
||||
|
||||
#if defined(__GNUC__)
|
||||
@@ -49,6 +45,9 @@ double from_chars(const char *first, const char* end) noexcept;
|
||||
|
||||
// Align to N-byte boundary
|
||||
#define SIMDJSON_ROUNDUP_N(a, n) (((a) + ((n)-1)) & ~((n)-1))
|
||||
#define SIMDJSON_ROUNDDOWN_N(a, n) ((a) & ~((n)-1))
|
||||
|
||||
#define SIMDJSON_ISALIGNED_N(ptr, n) (((uintptr_t)(ptr) & ((n)-1)) == 0)
|
||||
|
||||
#if SIMDJSON_REGULAR_VISUAL_STUDIO
|
||||
// We could use [[deprecated]] but it requires C++14
|
||||
@@ -56,7 +55,6 @@ double from_chars(const char *first, const char* end) noexcept;
|
||||
|
||||
#define simdjson_really_inline __forceinline
|
||||
#define simdjson_never_inline __declspec(noinline)
|
||||
#define simdjson_really_flatten [[msvc::flatten]]
|
||||
|
||||
#define simdjson_unused
|
||||
#define simdjson_warn_unused
|
||||
@@ -97,7 +95,6 @@ double from_chars(const char *first, const char* end) noexcept;
|
||||
|
||||
#define simdjson_really_inline inline __attribute__((always_inline))
|
||||
#define simdjson_never_inline inline __attribute__((noinline))
|
||||
#define simdjson_really_flatten [[gnu::flatten]]
|
||||
|
||||
#define simdjson_unused __attribute__((unused))
|
||||
#define simdjson_warn_unused __attribute__((warn_unused_result))
|
||||
@@ -174,15 +171,6 @@ double from_chars(const char *first, const char* end) noexcept;
|
||||
#define simdjson_inline simdjson_really_inline
|
||||
#endif
|
||||
|
||||
#if defined(simdjson_flatten)
|
||||
// Prefer the user's definition of simdjson_flatten; don't define it ourselves.
|
||||
#elif (defined(__GNUC__) && !defined(__OPTIMIZE__)) || (defined(_DEBUG) && _MSC_VER )
|
||||
// Flattening can lead to significant code bloat and high compile times. Don't use it for unoptimized builds.
|
||||
#define simdjson_flatten
|
||||
#else
|
||||
#define simdjson_flatten simdjson_really_flatten
|
||||
#endif
|
||||
|
||||
#if SIMDJSON_VISUAL_STUDIO
|
||||
/**
|
||||
* Windows users need to do some extra work when building
|
||||
|
||||
@@ -59,6 +59,14 @@ concept appendable_containers =
|
||||
details::supports_add<T> || details::supports_append<T> ||
|
||||
details::supports_insert<T>) && !string_view_keyed_map<T>;
|
||||
|
||||
/// Check if T is a key_selector type for efficient JSON field lookup
|
||||
template <typename T>
|
||||
concept key_selector_type = requires(T selector) {
|
||||
{ selector.size() } -> std::same_as<std::size_t>;
|
||||
{ selector.index_of(std::string_view{}) } -> std::same_as<std::size_t>;
|
||||
{ selector.get_key(std::size_t{}) } -> std::same_as<std::string_view>;
|
||||
};
|
||||
|
||||
/// Insert into the container however possible
|
||||
template <appendable_containers T, typename... Args>
|
||||
constexpr decltype(auto) emplace_one(T &vec, Args &&...args) {
|
||||
|
||||
@@ -37,6 +37,9 @@ inline auto_parser<parser_type>::auto_parser(parser_type parser, ondemand::docum
|
||||
: auto_parser{*parser, std::move(doc)} {}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
template <typename parser_type>
|
||||
inline std::remove_pointer_t<parser_type> &auto_parser<parser_type>::parser() noexcept {
|
||||
if constexpr (std::is_pointer_v<parser_type>) {
|
||||
@@ -115,6 +118,16 @@ template <typename T>
|
||||
inline auto to_adaptor<T>::operator()(ondemand::parser &parser, padded_string_view const str) const noexcept {
|
||||
return auto_parser<ondemand::parser *>{parser, str};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline auto to_adaptor<T>::operator()(std::string str) const noexcept {
|
||||
return auto_parser<ondemand::parser *>{pad_with_reserve(str)};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline auto to_adaptor<T>::operator()(ondemand::parser &parser, std::string str) const noexcept {
|
||||
return auto_parser<ondemand::parser *>{parser, pad_with_reserve(str)};
|
||||
}
|
||||
} // namespace internal
|
||||
} // namespace convert
|
||||
} // namespace simdjson
|
||||
|
||||
+12
-12
@@ -36,12 +36,12 @@ public:
|
||||
explicit auto_parser(std::remove_pointer_t<parser_type> &parser, padded_string_view const str) noexcept requires(std::is_pointer_v<parser_type>);
|
||||
explicit auto_parser(padded_string_view const str) noexcept requires(std::is_pointer_v<parser_type>);
|
||||
explicit auto_parser(parser_type parser, ondemand::document &&doc) noexcept requires(std::is_pointer_v<parser_type>);
|
||||
auto_parser(auto_parser const &) = delete;
|
||||
auto_parser &operator=(auto_parser const &) = delete;
|
||||
~auto_parser() = default;
|
||||
// Prevent moving
|
||||
auto_parser(auto_parser&&) = delete;
|
||||
auto_parser &operator=(auto_parser &&) noexcept = delete;
|
||||
auto_parser(auto_parser const &) = delete;
|
||||
auto_parser &operator=(auto_parser const &) = delete;
|
||||
auto_parser(auto_parser &&) noexcept = default;
|
||||
auto_parser &operator=(auto_parser &&) noexcept = default;
|
||||
~auto_parser() = default;
|
||||
|
||||
simdjson_warn_unused std::remove_pointer_t<parser_type> &parser() noexcept;
|
||||
|
||||
template <typename T>
|
||||
@@ -75,6 +75,11 @@ struct to_adaptor {
|
||||
T operator()(simdjson_result<ondemand::value> &val) const noexcept;
|
||||
auto operator()(padded_string_view const str) const noexcept;
|
||||
auto operator()(ondemand::parser &parser, padded_string_view const str) const noexcept;
|
||||
// The std::string is padded with reserve to ensure there is enough space for padding.
|
||||
// Some sanitizers may not like this, so you can use simdjson::pad instead.
|
||||
// simdjson::from(simdjson::pad(str))
|
||||
auto operator()(std::string str) const noexcept;
|
||||
auto operator()(ondemand::parser &parser, std::string str) const noexcept;
|
||||
};
|
||||
// deduction guide
|
||||
auto_parser(padded_string_view const str) -> auto_parser<ondemand::parser*>;
|
||||
@@ -85,12 +90,7 @@ auto_parser(padded_string_view const str) -> auto_parser<ondemand::parser*>;
|
||||
* The simdjson::from instance is EXPERIMENTAL AND SUBJECT TO CHANGES.
|
||||
*
|
||||
* The `from` instance is a utility adaptor for parsing JSON strings into objects.
|
||||
*
|
||||
* The string must be a simdjson::padded_string_view, which can be created from a std::string
|
||||
* with simdjson::pad(), from a simdjson::padded_string, or string literal using the `_padded`
|
||||
* user-defined literal.
|
||||
*
|
||||
* The `from` instance provides a convenient way to convert JSON data into C++ objects using the `auto_parser`.
|
||||
* It provides a convenient way to convert JSON data into C++ objects using the `auto_parser`.
|
||||
*
|
||||
* Example usage:
|
||||
*
|
||||
|
||||
@@ -42,16 +42,10 @@ inline error_code document::allocate(size_t capacity) noexcept {
|
||||
// worse with "[7,7,7,7,6,7,7,7,6,7,7,6,[7,7,7,7,6,7,7,7,6,7,7,6,7,7,7,7,7,7,6"
|
||||
//where capacity + 1 tape elements are
|
||||
// generated, see issue https://github.com/simdjson/simdjson/issues/345
|
||||
if(capacity + 3 < capacity) {
|
||||
return CAPACITY; // overflow, only happen on legacy 32-bit systems with very large capacity
|
||||
}
|
||||
size_t tape_capacity = SIMDJSON_ROUNDUP_N(capacity + 3, 64);
|
||||
// a document with only zero-length strings... could have capacity/3 string
|
||||
// and we would need capacity/3 * 5 bytes on the string buffer
|
||||
if(5 * (capacity / 3) + SIMDJSON_PADDING < SIMDJSON_PADDING) {
|
||||
return CAPACITY; // overflow, only happen on legacy 32-bit systems with very large capacity
|
||||
}
|
||||
size_t string_capacity = SIMDJSON_ROUNDUP_N(5 * (capacity / 3) + SIMDJSON_PADDING, 64);
|
||||
size_t string_capacity = SIMDJSON_ROUNDUP_N(5 * capacity / 3 + SIMDJSON_PADDING, 64);
|
||||
string_buf.reset( new (std::nothrow) uint8_t[string_capacity]);
|
||||
tape.reset(new (std::nothrow) uint64_t[tape_capacity]);
|
||||
if(!(string_buf && tape)) {
|
||||
|
||||
@@ -234,7 +234,7 @@ simdjson_inline std::string_view document_stream::iterator::source() const noexc
|
||||
// cannot legally appear in a JSON value at the source level (control
|
||||
// characters in strings must be escaped as \u001E), so stripping it is
|
||||
// safe in every stream_format.
|
||||
while(svlen > 1 && (std::isspace(static_cast<unsigned char>(start[svlen-1])) || start[svlen-1] == '\0' || static_cast<uint8_t>(start[svlen-1]) == 0x1E || (stream->format == stream_format::comma_delimited && start[svlen-1] == ','))) {
|
||||
while(svlen > 1 && (std::isspace(start[svlen-1]) || start[svlen-1] == '\0' || static_cast<uint8_t>(start[svlen-1]) == 0x1E)) {
|
||||
svlen--;
|
||||
}
|
||||
return std::string_view(start, svlen);
|
||||
|
||||
@@ -22,9 +22,6 @@ namespace atomparsing {
|
||||
// to the compile-time constant 1936482662.
|
||||
simdjson_inline uint32_t string_to_uint32(const char* str) { uint32_t val; std::memcpy(&val, str, sizeof(uint32_t)); return val; }
|
||||
|
||||
// Acts on the same principle as string_to_uint32, but on an 8-byte block of memory
|
||||
simdjson_inline uint64_t string_to_uint64(const char* str) { uint64_t val; std::memcpy(&val, str, sizeof(uint64_t)); return val; }
|
||||
|
||||
|
||||
// Again in str4ncmp we use a memcpy to avoid undefined behavior. The memcpy may appear expensive.
|
||||
// Yet all decent optimizing compilers will compile memcpy to a single instruction, just about.
|
||||
@@ -36,28 +33,6 @@ simdjson_inline uint32_t str4ncmp(const uint8_t *src, const char* atom) {
|
||||
return srcval ^ string_to_uint32(atom);
|
||||
}
|
||||
|
||||
// Checks that the first 8 characters of the input string match the given atom in a case-insensitive manner.
|
||||
//
|
||||
// 'atom' must consist of only lowercase letters.
|
||||
simdjson_warn_unused
|
||||
simdjson_inline uint64_t str8ncmp_case_insensitive(const uint8_t *src, const char* atom) {
|
||||
uint64_t srcval; // we want to avoid unaligned 32-bit loads (undefined in C/C++)
|
||||
static_assert(sizeof(uint64_t) <= SIMDJSON_PADDING, "SIMDJSON_PADDING must be larger than 8 bytes");
|
||||
std::memcpy(&srcval, src, sizeof(uint64_t));
|
||||
|
||||
return (srcval | 0x2020202020202020ull) ^ string_to_uint64(atom);
|
||||
}
|
||||
|
||||
// Checks that the first 3 characters of 'src' match 'atom' in a case-insensitive way.
|
||||
//
|
||||
// 'atom' must consist of only lowercase letters.
|
||||
simdjson_warn_unused
|
||||
simdjson_inline uint32_t str3ncmp_case_insensitive(const uint8_t *src, const char* atom) {
|
||||
return ((src[0] | 0x20) ^ atom[0]) //
|
||||
| ((src[1] | 0x20) ^ atom[1]) //
|
||||
| ((src[2] | 0x20) ^ atom[2]);
|
||||
}
|
||||
|
||||
simdjson_warn_unused
|
||||
simdjson_inline bool is_valid_true_atom(const uint8_t *src) {
|
||||
return (str4ncmp(src, "true") | jsoncharutils::is_not_structural_or_whitespace(src[4])) == 0;
|
||||
@@ -94,72 +69,9 @@ simdjson_inline bool is_valid_null_atom(const uint8_t *src, size_t len) {
|
||||
else { return false; }
|
||||
}
|
||||
|
||||
#if SIMDJSON_ENABLE_NAN_INF
|
||||
// "nan" is 3 bytes; we check characters and then verify the next
|
||||
// character is structural or whitespace. We accept both "nan" and "NaN".
|
||||
simdjson_warn_unused
|
||||
simdjson_inline bool is_valid_nan_atom(const uint8_t *src) {
|
||||
return (str3ncmp_case_insensitive(src, "nan")
|
||||
| jsoncharutils::is_not_structural_or_whitespace(src[3])) == 0;
|
||||
}
|
||||
|
||||
// checks that the next four characters of a string are 'nan"', where the 'nan'
|
||||
// is checked in a case-insensitive way.
|
||||
simdjson_warn_unused
|
||||
simdjson_inline bool is_valid_nan_in_string(const uint8_t *src) {
|
||||
return (str3ncmp_case_insensitive(src, "nan") | (src[3] ^ '"')) == 0;
|
||||
}
|
||||
|
||||
simdjson_warn_unused
|
||||
simdjson_inline bool is_valid_nan_atom(const uint8_t *src, size_t len) {
|
||||
if (len > 3) { return is_valid_nan_atom(src); }
|
||||
if (len == 3) { return str3ncmp_case_insensitive(src, "nan") == 0; }
|
||||
return false;
|
||||
}
|
||||
|
||||
// This function will accept any case-insensitive 3-character spelling of
|
||||
// infinity: 'inf', 'INF', and 'Inf' are all accepted.
|
||||
//
|
||||
// Any capitalization of 'infinity' is also accepted.
|
||||
simdjson_warn_unused
|
||||
simdjson_inline bool is_valid_inf_atom(const uint8_t *src) {
|
||||
bool is_short_inf = (str3ncmp_case_insensitive(src, "inf")
|
||||
| jsoncharutils::is_not_structural_or_whitespace(src[3])) == 0;
|
||||
if(is_short_inf) return true;
|
||||
|
||||
// Check for 'infinity' (any capitalization)
|
||||
return (str8ncmp_case_insensitive(src, "infinity") | jsoncharutils::is_not_structural_or_whitespace(src[8])) == 0;
|
||||
}
|
||||
|
||||
simdjson_warn_unused
|
||||
simdjson_inline bool is_valid_inf_in_string(const uint8_t *src) {
|
||||
bool is_short_inf = (str3ncmp_case_insensitive(src, "inf") | (src[3] ^ '"')) == 0;
|
||||
if(is_short_inf) return true;
|
||||
|
||||
return (str8ncmp_case_insensitive(src, "infinity") | (src[8] ^ '"')) == 0;
|
||||
}
|
||||
|
||||
|
||||
// This function will accept any case-insensitive 3-character spelling of
|
||||
// infinity: 'inf', 'INF', and 'Inf' are all accepted.
|
||||
//
|
||||
// Any capitalization of 'infinity' is also accepted.
|
||||
simdjson_warn_unused
|
||||
simdjson_inline bool is_valid_inf_atom(const uint8_t *src, size_t len) {
|
||||
if (len > 8) { return is_valid_inf_atom(src); }
|
||||
if (len == 8) { return str8ncmp_case_insensitive(src, "infinity") == 0; }
|
||||
if (len > 3) {
|
||||
return (str3ncmp_case_insensitive(src, "inf")
|
||||
| jsoncharutils::is_not_structural_or_whitespace(src[3])) == 0;
|
||||
}
|
||||
if (len == 3) { return str3ncmp_case_insensitive(src, "inf") == 0; }
|
||||
return false;
|
||||
}
|
||||
#endif // SIMDJSON_ENABLE_NAN_INF
|
||||
|
||||
} // namespace atomparsing
|
||||
} // unnamed namespace
|
||||
} // namespace SIMDJSON_IMPLEMENTATION
|
||||
} // namespace simdjson
|
||||
|
||||
#endif // SIMDJSON_GENERIC_ATOMPARSING_H
|
||||
#endif // SIMDJSON_GENERIC_ATOMPARSING_H
|
||||
@@ -23,7 +23,7 @@ namespace builder {
|
||||
|
||||
template <class T>
|
||||
requires(concepts::container_but_not_string<T> && ! concepts::optional_type<T> && !require_custom_serialization<T>)
|
||||
simdjson_really_inline constexpr void atom(string_builder &b, const T &t) {
|
||||
constexpr void atom(string_builder &b, const T &t) {
|
||||
auto it = t.begin();
|
||||
auto end = t.end();
|
||||
if (it == end) {
|
||||
@@ -45,13 +45,13 @@ template <class T>
|
||||
std::is_same_v<T, std::string_view> ||
|
||||
std::is_same_v<T, const char *> ||
|
||||
std::is_same_v<T, char>)
|
||||
simdjson_really_inline constexpr void atom(string_builder &b, const T &t) {
|
||||
constexpr void atom(string_builder &b, const T &t) {
|
||||
b.escape_and_append_with_quotes(t);
|
||||
}
|
||||
|
||||
template <concepts::string_view_keyed_map T>
|
||||
requires(!require_custom_serialization<T>)
|
||||
simdjson_really_inline constexpr void atom(string_builder &b, const T &m) {
|
||||
constexpr void atom(string_builder &b, const T &m) {
|
||||
if (m.empty()) {
|
||||
b.append_raw("{}");
|
||||
return;
|
||||
@@ -74,7 +74,7 @@ simdjson_really_inline constexpr void atom(string_builder &b, const T &m) {
|
||||
|
||||
template<typename number_type,
|
||||
typename = typename std::enable_if<std::is_arithmetic<number_type>::value && !std::is_same_v<number_type, char>>::type>
|
||||
simdjson_really_inline constexpr void atom(string_builder &b, const number_type t) {
|
||||
constexpr void atom(string_builder &b, const number_type t) {
|
||||
b.append(t);
|
||||
}
|
||||
|
||||
@@ -88,24 +88,15 @@ template <class T>
|
||||
!std::is_same_v<T, std::string_view> &&
|
||||
!std::is_same_v<T, const char*> &&
|
||||
!std::is_same_v<T, char> && !require_custom_serialization<T>)
|
||||
simdjson_really_inline constexpr void atom(string_builder &b, const T &t) {
|
||||
// Coalesce the per-field separator+key+colon writes into a single
|
||||
// append_raw, so each field does one capacity_check + one memcpy instead of
|
||||
// three. The leading-comma variant is selected at runtime by the compile-time
|
||||
// peeled `i` counter, which clang folds away after the template-for unroll.
|
||||
constexpr void atom(string_builder &b, const T &t) {
|
||||
int i = 0;
|
||||
b.append('{');
|
||||
template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
|
||||
constexpr auto first_key = std::define_static_string(
|
||||
constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(dm)) + ":");
|
||||
constexpr auto rest_key = std::define_static_string(
|
||||
std::string(",") + constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(dm)) + ":");
|
||||
// Pass size as template parameter so memcpy is fully inlined with
|
||||
// a compile-time-constant size.
|
||||
constexpr size_t first_key_len = std::char_traits<char>::length(first_key);
|
||||
constexpr size_t rest_key_len = std::char_traits<char>::length(rest_key);
|
||||
if (i == 0) b.template append_raw_n<first_key_len>(first_key);
|
||||
else b.template append_raw_n<rest_key_len>(rest_key);
|
||||
if (i != 0)
|
||||
b.append(',');
|
||||
constexpr auto key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(dm)));
|
||||
b.append_raw(key);
|
||||
b.append(':');
|
||||
atom(b, t.[:dm:]);
|
||||
i++;
|
||||
};
|
||||
@@ -115,7 +106,7 @@ simdjson_really_inline constexpr void atom(string_builder &b, const T &t) {
|
||||
// Support for optional types (std::optional, etc.)
|
||||
template <concepts::optional_type T>
|
||||
requires(!require_custom_serialization<T>)
|
||||
simdjson_really_inline constexpr void atom(string_builder &b, const T &opt) {
|
||||
constexpr void atom(string_builder &b, const T &opt) {
|
||||
if (opt) {
|
||||
atom(b, opt.value());
|
||||
} else {
|
||||
@@ -126,7 +117,7 @@ simdjson_really_inline constexpr void atom(string_builder &b, const T &opt) {
|
||||
// Support for smart pointers (std::unique_ptr, std::shared_ptr, etc.)
|
||||
template <concepts::smart_pointer T>
|
||||
requires(!require_custom_serialization<T>)
|
||||
simdjson_really_inline constexpr void atom(string_builder &b, const T &ptr) {
|
||||
constexpr void atom(string_builder &b, const T &ptr) {
|
||||
if (ptr) {
|
||||
atom(b, *ptr);
|
||||
} else {
|
||||
@@ -137,7 +128,7 @@ simdjson_really_inline constexpr void atom(string_builder &b, const T &ptr) {
|
||||
// Support for enums - serialize as string representation using expand approach from P2996R12
|
||||
template <typename T>
|
||||
requires(std::is_enum_v<T> && !require_custom_serialization<T>)
|
||||
simdjson_really_inline void atom(string_builder &b, const T &e) {
|
||||
void atom(string_builder &b, const T &e) {
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
static constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T));
|
||||
template for (constexpr auto enum_val : enumerators) {
|
||||
@@ -161,7 +152,7 @@ template <concepts::appendable_containers T>
|
||||
!concepts::optional_type<T> && !concepts::smart_pointer<T> &&
|
||||
!std::is_same_v<T, std::string> &&
|
||||
!std::is_same_v<T, std::string_view> && !std::is_same_v<T, const char*> && !require_custom_serialization<T>)
|
||||
simdjson_really_inline constexpr void atom(string_builder &b, const T &container) {
|
||||
constexpr void atom(string_builder &b, const T &container) {
|
||||
if (container.empty()) {
|
||||
b.append_raw("[]");
|
||||
return;
|
||||
@@ -233,15 +224,14 @@ template <class Z>
|
||||
!std::is_same_v<Z, const char*> &&
|
||||
!std::is_same_v<Z, char> && !require_custom_serialization<Z>)
|
||||
void append(string_builder &b, const Z &z) {
|
||||
// Same coalescing as the atom() overload above.
|
||||
int i = 0;
|
||||
b.append('{');
|
||||
template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^Z, std::meta::access_context::unchecked()))) {
|
||||
constexpr auto first_key = std::define_static_string(
|
||||
constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(dm)) + ":");
|
||||
constexpr auto rest_key = std::define_static_string(
|
||||
std::string(",") + constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(dm)) + ":");
|
||||
b.append_raw(i == 0 ? first_key : rest_key);
|
||||
if (i != 0)
|
||||
b.append(',');
|
||||
constexpr auto key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(dm)));
|
||||
b.append_raw(key);
|
||||
b.append(':');
|
||||
atom(b, z.[:dm:]);
|
||||
i++;
|
||||
};
|
||||
@@ -314,14 +304,16 @@ void extract_from(string_builder &b, const T &obj) {
|
||||
|
||||
// Only serialize this field if it's in our list of requested fields
|
||||
if constexpr (((FieldNames.view() == key) || ...)) {
|
||||
// Same coalescing as the atom() / append() struct overloads.
|
||||
static constexpr auto first_key = std::define_static_string(
|
||||
constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(mem)) + ":");
|
||||
static constexpr auto rest_key = std::define_static_string(
|
||||
std::string(",") + constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(mem)) + ":");
|
||||
b.append_raw(first ? first_key : rest_key);
|
||||
if (!first) {
|
||||
b.append(',');
|
||||
}
|
||||
first = false;
|
||||
|
||||
// Serialize the key
|
||||
static constexpr auto quoted_key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(mem)));
|
||||
b.append_raw(quoted_key);
|
||||
b.append(':');
|
||||
|
||||
// Serialize the value
|
||||
atom(b, obj.[:mem:]);
|
||||
}
|
||||
|
||||
@@ -561,6 +561,62 @@ simdjson_inline void string_builder::clear() noexcept {
|
||||
|
||||
namespace internal {
|
||||
|
||||
template <typename number_type, typename = typename std::enable_if<
|
||||
std::is_unsigned<number_type>::value>::type>
|
||||
simdjson_really_inline int int_log2(number_type x) {
|
||||
return 63 - leading_zeroes(uint64_t(x) | 1);
|
||||
}
|
||||
|
||||
simdjson_really_inline int fast_digit_count_32(uint32_t x) {
|
||||
static uint64_t table[] = {
|
||||
4294967296, 8589934582, 8589934582, 8589934582, 12884901788,
|
||||
12884901788, 12884901788, 17179868184, 17179868184, 17179868184,
|
||||
21474826480, 21474826480, 21474826480, 21474826480, 25769703776,
|
||||
25769703776, 25769703776, 30063771072, 30063771072, 30063771072,
|
||||
34349738368, 34349738368, 34349738368, 34349738368, 38554705664,
|
||||
38554705664, 38554705664, 41949672960, 41949672960, 41949672960,
|
||||
42949672960, 42949672960};
|
||||
return uint32_t((x + table[int_log2(x)]) >> 32);
|
||||
}
|
||||
|
||||
simdjson_really_inline int fast_digit_count_64(uint64_t x) {
|
||||
static uint64_t table[] = {9,
|
||||
99,
|
||||
999,
|
||||
9999,
|
||||
99999,
|
||||
999999,
|
||||
9999999,
|
||||
99999999,
|
||||
999999999,
|
||||
9999999999,
|
||||
99999999999,
|
||||
999999999999,
|
||||
9999999999999,
|
||||
99999999999999,
|
||||
999999999999999ULL,
|
||||
9999999999999999ULL,
|
||||
99999999999999999ULL,
|
||||
999999999999999999ULL,
|
||||
9999999999999999999ULL};
|
||||
int y = (19 * int_log2(x) >> 6);
|
||||
y += x > table[y];
|
||||
return y + 1;
|
||||
}
|
||||
|
||||
template <typename number_type, typename = typename std::enable_if<
|
||||
std::is_unsigned<number_type>::value>::type>
|
||||
simdjson_really_inline size_t digit_count(number_type v) noexcept {
|
||||
static_assert(sizeof(number_type) == 8 || sizeof(number_type) == 4 ||
|
||||
sizeof(number_type) == 2 || sizeof(number_type) == 1,
|
||||
"We only support 8-bit, 16-bit, 32-bit and 64-bit numbers");
|
||||
SIMDJSON_IF_CONSTEXPR(sizeof(number_type) <= 4) {
|
||||
return fast_digit_count_32(static_cast<uint32_t>(v));
|
||||
}
|
||||
else {
|
||||
return fast_digit_count_64(static_cast<uint64_t>(v));
|
||||
}
|
||||
}
|
||||
static const char decimal_table[200] = {
|
||||
0x30, 0x30, 0x30, 0x31, 0x30, 0x32, 0x30, 0x33, 0x30, 0x34, 0x30, 0x35,
|
||||
0x30, 0x36, 0x30, 0x37, 0x30, 0x38, 0x30, 0x39, 0x31, 0x30, 0x31, 0x31,
|
||||
@@ -580,78 +636,6 @@ static const char decimal_table[200] = {
|
||||
0x39, 0x30, 0x39, 0x31, 0x39, 0x32, 0x39, 0x33, 0x39, 0x34, 0x39, 0x35,
|
||||
0x39, 0x36, 0x39, 0x37, 0x39, 0x38, 0x39, 0x39,
|
||||
};
|
||||
|
||||
// Forward unsigned-int writer (cascade-on-magnitude, no upfront digit_count).
|
||||
// Built from a non-recursive DAG of always_inline helpers — gcc and MSVC
|
||||
// refuse to inline recursive `always_inline`/`__forceinline` functions.
|
||||
// Caller must guarantee at least 20 bytes available at p. All helpers
|
||||
// return pointer past the last digit written.
|
||||
|
||||
// Caller guarantees v < 100. Writes 1-2 digits.
|
||||
simdjson_really_inline char* write_lt100(char* p, uint64_t v) noexcept {
|
||||
if (v < 10) { *p++ = char('0' + v); return p; }
|
||||
std::memcpy(p, &decimal_table[v * 2], 2);
|
||||
return p + 2;
|
||||
}
|
||||
|
||||
// Caller guarantees v < 10000. Writes 1-4 digits.
|
||||
simdjson_really_inline char* write_lt10000(char* p, uint64_t v) noexcept {
|
||||
if (v < 100) return write_lt100(p, v);
|
||||
uint64_t hi = v / 100, lo = v % 100;
|
||||
if (v < 1000) {
|
||||
*p++ = char('0' + hi);
|
||||
} else {
|
||||
std::memcpy(p, &decimal_table[hi * 2], 2);
|
||||
p += 2;
|
||||
}
|
||||
std::memcpy(p, &decimal_table[lo * 2], 2);
|
||||
return p + 2;
|
||||
}
|
||||
|
||||
// Caller guarantees v < 10000. Always writes exactly 4 digits.
|
||||
simdjson_really_inline void write_4_digits(char* p, uint64_t v) noexcept {
|
||||
uint64_t hi = v / 100, lo = v % 100;
|
||||
std::memcpy(p, &decimal_table[hi * 2], 2);
|
||||
std::memcpy(p + 2, &decimal_table[lo * 2], 2);
|
||||
}
|
||||
|
||||
// Caller guarantees v < 10^8. Writes 1-8 digits.
|
||||
simdjson_really_inline char* write_lt1e8(char* p, uint64_t v) noexcept {
|
||||
if (v < 10000) return write_lt10000(p, v);
|
||||
uint64_t hi = v / 10000, lo = v % 10000;
|
||||
p = write_lt10000(p, hi);
|
||||
write_4_digits(p, lo);
|
||||
return p + 4;
|
||||
}
|
||||
|
||||
simdjson_really_inline char* write_uint_jeaiii(char* p, uint64_t v) noexcept {
|
||||
if (v < 10000ULL) return write_lt10000(p, v);
|
||||
if (v < 100000000ULL) { // 5-8 digits
|
||||
uint64_t hi = v / 10000, lo = v % 10000;
|
||||
p = write_lt10000(p, hi);
|
||||
write_4_digits(p, lo);
|
||||
return p + 4;
|
||||
}
|
||||
if (v < 10000000000000000ULL) { // 9-16 digits
|
||||
uint64_t hi = v / 100000000ULL, lo = v % 100000000ULL;
|
||||
p = write_lt1e8(p, hi);
|
||||
uint64_t lo_hi = lo / 10000, lo_lo = lo % 10000;
|
||||
write_4_digits(p, lo_hi);
|
||||
write_4_digits(p + 4, lo_lo);
|
||||
return p + 8;
|
||||
}
|
||||
// 17-20 digits
|
||||
uint64_t hi = v / 10000000000000000ULL, lo = v % 10000000000000000ULL;
|
||||
p = write_lt10000(p, hi);
|
||||
uint64_t lo_a = lo / 100000000ULL, lo_b = lo % 100000000ULL;
|
||||
uint64_t lo_a_hi = lo_a / 10000, lo_a_lo = lo_a % 10000;
|
||||
uint64_t lo_b_hi = lo_b / 10000, lo_b_lo = lo_b % 10000;
|
||||
write_4_digits(p, lo_a_hi);
|
||||
write_4_digits(p + 4, lo_a_lo);
|
||||
write_4_digits(p + 8, lo_b_hi);
|
||||
write_4_digits(p + 12, lo_b_lo);
|
||||
return p + 16;
|
||||
}
|
||||
} // namespace internal
|
||||
|
||||
template <typename number_type, typename>
|
||||
@@ -679,31 +663,82 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
|
||||
}
|
||||
}
|
||||
else SIMDJSON_IF_CONSTEXPR(std::is_unsigned<number_type>::value) {
|
||||
// Process 4 digits at a time instead of 2, reducing store operations
|
||||
// and divisions by approximately half for large numbers.
|
||||
constexpr size_t max_number_size = 20;
|
||||
if (capacity_check(max_number_size)) {
|
||||
using unsigned_type = typename std::make_unsigned<number_type>::type;
|
||||
char* end = internal::write_uint_jeaiii(
|
||||
buffer.get() + position,
|
||||
static_cast<uint64_t>(static_cast<unsigned_type>(v)));
|
||||
position = end - buffer.get();
|
||||
unsigned_type pv = static_cast<unsigned_type>(v);
|
||||
size_t dc = internal::digit_count(pv);
|
||||
char *write_pointer = buffer.get() + position + dc - 1;
|
||||
|
||||
// Process 4 digits per iteration for large numbers
|
||||
while (pv >= 10000) {
|
||||
unsigned_type q = pv / 10000;
|
||||
unsigned_type r = pv % 10000;
|
||||
unsigned_type r_hi = r / 100; // High 2 digits of remainder
|
||||
unsigned_type r_lo = r % 100; // Low 2 digits of remainder
|
||||
// Write low 2 digits first (rightmost), then high 2 digits
|
||||
memcpy(write_pointer - 1, &internal::decimal_table[r_lo * 2], 2);
|
||||
memcpy(write_pointer - 3, &internal::decimal_table[r_hi * 2], 2);
|
||||
write_pointer -= 4;
|
||||
pv = q;
|
||||
}
|
||||
|
||||
// Handle remaining 1-4 digits with original 2-digit loop
|
||||
while (pv >= 100) {
|
||||
memcpy(write_pointer - 1, &internal::decimal_table[(pv % 100) * 2], 2);
|
||||
write_pointer -= 2;
|
||||
pv /= 100;
|
||||
}
|
||||
if (pv >= 10) {
|
||||
*write_pointer-- = char('0' + (pv % 10));
|
||||
pv /= 10;
|
||||
}
|
||||
*write_pointer = char('0' + pv);
|
||||
position += dc;
|
||||
}
|
||||
}
|
||||
else SIMDJSON_IF_CONSTEXPR(std::is_integral<number_type>::value) {
|
||||
// 19 digits (max abs value of int64_t) + optional minus sign.
|
||||
// Same 4-digit batching as unsigned path for signed integers
|
||||
constexpr size_t max_number_size = 20;
|
||||
if (capacity_check(max_number_size)) {
|
||||
using unsigned_type = typename std::make_unsigned<number_type>::type;
|
||||
bool negative = v < 0;
|
||||
// 0 - pv (rather than -pv) avoids an MSVC unary-minus warning.
|
||||
unsigned_type pv = negative
|
||||
? unsigned_type(0) - static_cast<unsigned_type>(v)
|
||||
: static_cast<unsigned_type>(v);
|
||||
// Branchless: always write '-', advance only if negative.
|
||||
unsigned_type pv = static_cast<unsigned_type>(v);
|
||||
if (negative) {
|
||||
pv = 0 - pv; // the 0 is for Microsoft
|
||||
}
|
||||
size_t dc = internal::digit_count(pv);
|
||||
// by always writing the minus sign, we avoid the branch.
|
||||
buffer.get()[position] = '-';
|
||||
position += negative;
|
||||
char* end = internal::write_uint_jeaiii(
|
||||
buffer.get() + position, static_cast<uint64_t>(pv));
|
||||
position = end - buffer.get();
|
||||
position += negative ? 1 : 0;
|
||||
char *write_pointer = buffer.get() + position + dc - 1;
|
||||
|
||||
// Process 4 digits per iteration for large numbers
|
||||
while (pv >= 10000) {
|
||||
unsigned_type q = pv / 10000;
|
||||
unsigned_type r = pv % 10000;
|
||||
unsigned_type r_hi = r / 100;
|
||||
unsigned_type r_lo = r % 100;
|
||||
memcpy(write_pointer - 1, &internal::decimal_table[r_lo * 2], 2);
|
||||
memcpy(write_pointer - 3, &internal::decimal_table[r_hi * 2], 2);
|
||||
write_pointer -= 4;
|
||||
pv = q;
|
||||
}
|
||||
|
||||
// Handle remaining 1-4 digits
|
||||
while (pv >= 100) {
|
||||
memcpy(write_pointer - 1, &internal::decimal_table[(pv % 100) * 2], 2);
|
||||
write_pointer -= 2;
|
||||
pv /= 100;
|
||||
}
|
||||
if (pv >= 10) {
|
||||
*write_pointer-- = char('0' + (pv % 10));
|
||||
pv /= 10;
|
||||
}
|
||||
*write_pointer = char('0' + pv);
|
||||
position += dc;
|
||||
}
|
||||
}
|
||||
else SIMDJSON_IF_CONSTEXPR(std::is_floating_point<number_type>::value) {
|
||||
@@ -759,9 +794,7 @@ simdjson_inline void string_builder::escape_and_append_with_quotes() noexcept {
|
||||
#endif
|
||||
|
||||
simdjson_inline void string_builder::append_raw(const char *c) noexcept {
|
||||
// char_traits::length is constexpr; lets the compiler fold the length
|
||||
// when called with a pointer to a compile-time-constant string.
|
||||
size_t len = std::char_traits<char>::length(c);
|
||||
size_t len = std::strlen(c);
|
||||
append_raw(c, len);
|
||||
}
|
||||
|
||||
@@ -780,14 +813,6 @@ simdjson_inline void string_builder::append_raw(const char *str,
|
||||
position += len;
|
||||
}
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
simdjson_inline void string_builder::append_raw_n(const char *str) noexcept {
|
||||
if (capacity_check(N)) {
|
||||
std::memcpy(buffer.get() + position, str, N);
|
||||
position += N;
|
||||
}
|
||||
}
|
||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
||||
// Support for optional types (std::optional, etc.)
|
||||
template <concepts::optional_type T>
|
||||
|
||||
@@ -52,7 +52,7 @@ class string_builder {
|
||||
public:
|
||||
simdjson_inline string_builder(size_t initial_capacity = DEFAULT_INITIAL_CAPACITY);
|
||||
|
||||
static constexpr size_t DEFAULT_INITIAL_CAPACITY = 262144;
|
||||
static constexpr size_t DEFAULT_INITIAL_CAPACITY = 1024;
|
||||
|
||||
/**
|
||||
* Append number (includes Booleans). Booleans are mapped to the strings
|
||||
@@ -185,15 +185,6 @@ requires (!std::is_convertible<R, std::string_view>::value && !concepts::optiona
|
||||
* There is no UTF-8 validation.
|
||||
*/
|
||||
simdjson_inline void append_raw(const char *str, size_t len) noexcept;
|
||||
|
||||
/**
|
||||
* Append exactly N characters from str. The length is a template parameter
|
||||
* so the compiler can fully inline the memcpy with a compile-time-constant
|
||||
* size, avoiding the libc call. Used for compile-time-constant keys in the
|
||||
* reflection struct atom.
|
||||
*/
|
||||
template <size_t N>
|
||||
simdjson_inline void append_raw_n(const char *str) noexcept;
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
/**
|
||||
* Creates an std::string from the written JSON buffer.
|
||||
|
||||
@@ -63,11 +63,7 @@ inline dom_parser_implementation &dom_parser_implementation::operator=(dom_parse
|
||||
inline simdjson_warn_unused error_code dom_parser_implementation::set_capacity(size_t capacity) noexcept {
|
||||
if(capacity > SIMDJSON_MAXSIZE_BYTES) { return CAPACITY; }
|
||||
// Stage 1 index output
|
||||
size_t rounded_capacity = SIMDJSON_ROUNDUP_N(capacity, 64);
|
||||
if(rounded_capacity + 9 < rounded_capacity) {
|
||||
return CAPACITY; // overflow, only happen on legacy 32-bit systems with very large capacity
|
||||
}
|
||||
size_t max_structures = rounded_capacity + 9;
|
||||
size_t max_structures = SIMDJSON_ROUNDUP_N(capacity, 64) + 2 + 7;
|
||||
structural_indexes.reset( new (std::nothrow) uint32_t[max_structures] );
|
||||
if (!structural_indexes) { _capacity = 0; return MEMALLOC; }
|
||||
structural_indexes[0] = 0;
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#define SIMDJSON_GENERIC_NUMBERPARSING_H
|
||||
#include "simdjson/generic/base.h"
|
||||
#include "simdjson/generic/jsoncharutils.h"
|
||||
#include "simdjson/generic/atomparsing.h"
|
||||
#include "simdjson/internal/numberparsing_tables.h"
|
||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||
|
||||
@@ -300,24 +299,6 @@ simdjson_inline bool compute_float_64(int64_t power, uint64_t i, bool negative,
|
||||
return true;
|
||||
}
|
||||
|
||||
#if SIMDJSON_ENABLE_NAN_INF
|
||||
// Parses a nan or infinity. Returns true on success, false on failure.
|
||||
simdjson_unused simdjson_inline bool compute_nan_inf(const uint8_t* src, bool negative, double& d) noexcept {
|
||||
if (atomparsing::is_valid_inf_atom(src)) {
|
||||
double inf = std::numeric_limits<double>::infinity();
|
||||
d = negative ? -inf : inf;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (atomparsing::is_valid_nan_atom(src)) {
|
||||
d = std::numeric_limits<double>::quiet_NaN();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
// We call a fallback floating-point parser that might be slow. Note
|
||||
// it will accept JSON numbers, but the JSON spec. is more restrictive so
|
||||
// before you call parse_float_fallback, you need to have validated the input
|
||||
@@ -619,21 +600,7 @@ simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *cons
|
||||
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
|
||||
// Optimization note: size_t is expected to be unsigned.
|
||||
size_t digit_count = size_t(p - start_digits);
|
||||
if (digit_count == 0 || ('0' == *start_digits && digit_count > 1)) {
|
||||
|
||||
#if SIMDJSON_ENABLE_NAN_INF
|
||||
// By this point, we know that our input does not begin with a digit. We will attempt
|
||||
// to handle NaN/Infinity.
|
||||
|
||||
double d;
|
||||
if (compute_nan_inf(p, negative, d)) {
|
||||
writer.append_double(d);
|
||||
return SUCCESS;
|
||||
}
|
||||
#endif
|
||||
|
||||
return INVALID_NUMBER(src);
|
||||
}
|
||||
if (digit_count == 0 || ('0' == *start_digits && digit_count > 1)) { return INVALID_NUMBER(src); }
|
||||
|
||||
//
|
||||
// Handle floats if there is a . or e (or both)
|
||||
@@ -1064,17 +1031,7 @@ simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8
|
||||
bool leading_zero = (i == 0);
|
||||
while (parse_digit(*p, i)) { p++; }
|
||||
// no integer digits, or 0123 (zero must be solo)
|
||||
if ( p == src ) {
|
||||
|
||||
#if SIMDJSON_ENABLE_NAN_INF
|
||||
// If there are no loading digits, the number may be nan or infinity.
|
||||
// Attempt to compute those, and return on success.
|
||||
double d;
|
||||
if (compute_nan_inf(p, negative, d)) { return d; }
|
||||
#endif
|
||||
|
||||
return INCORRECT_TYPE;
|
||||
}
|
||||
if ( p == src ) { return INCORRECT_TYPE; }
|
||||
if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
|
||||
|
||||
//
|
||||
@@ -1292,22 +1249,7 @@ simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(c
|
||||
bool leading_zero = (i == 0);
|
||||
while (parse_digit(*p, i)) { p++; }
|
||||
// no integer digits, or 0123 (zero must be solo)
|
||||
if ( p == src ) {
|
||||
#if SIMDJSON_ENABLE_NAN_INF
|
||||
// If there are no leading digits, attempt to parse numbers that are either
|
||||
// NaN or Infinity
|
||||
if (atomparsing::is_valid_inf_in_string(src)) {
|
||||
double inf = std::numeric_limits<double>::infinity();
|
||||
return negative ? -inf : inf;
|
||||
}
|
||||
|
||||
if (atomparsing::is_valid_nan_in_string(src)) {
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
}
|
||||
#endif
|
||||
|
||||
return INCORRECT_TYPE;
|
||||
}
|
||||
if ( p == src ) { return INCORRECT_TYPE; }
|
||||
if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
|
||||
|
||||
//
|
||||
|
||||
@@ -20,9 +20,9 @@
|
||||
#include "simdjson/generic/ondemand/document.h"
|
||||
#include "simdjson/generic/ondemand/document_stream.h"
|
||||
#include "simdjson/generic/ondemand/field.h"
|
||||
#include "simdjson/generic/ondemand/key_selector.h"
|
||||
#include "simdjson/generic/ondemand/object.h"
|
||||
#include "simdjson/generic/ondemand/object_iterator.h"
|
||||
#include "simdjson/generic/ondemand/ranges.h"
|
||||
#include "simdjson/generic/ondemand/serialization.h"
|
||||
|
||||
// Deserialization for standard types
|
||||
@@ -40,7 +40,6 @@
|
||||
#include "simdjson/generic/ondemand/logger-inl.h"
|
||||
#include "simdjson/generic/ondemand/object-inl.h"
|
||||
#include "simdjson/generic/ondemand/object_iterator-inl.h"
|
||||
#include "simdjson/generic/ondemand/ranges-inl.h"
|
||||
#include "simdjson/generic/ondemand/parser-inl.h"
|
||||
#include "simdjson/generic/ondemand/raw_json_string-inl.h"
|
||||
#include "simdjson/generic/ondemand/token_iterator-inl.h"
|
||||
|
||||
@@ -170,34 +170,46 @@ inline simdjson_result<value> array::at_path(std::string_view json_path) noexcep
|
||||
return at_pointer(json_pointer);
|
||||
}
|
||||
|
||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
||||
template <typename Func>
|
||||
requires std::invocable<Func, value>
|
||||
#else
|
||||
template <typename Func>
|
||||
#endif
|
||||
inline error_code array::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
|
||||
inline simdjson_result<std::vector<value>> array::at_path_with_wildcard(std::string_view json_path) noexcept {
|
||||
std::vector<value> result;
|
||||
|
||||
auto result_pair = get_next_key_and_json_path(json_path);
|
||||
std::string_view key = result_pair.first;
|
||||
std::string_view remaining_path = result_pair.second;
|
||||
// Wildcard case
|
||||
if (key=="*"){
|
||||
for(auto element: *this) {
|
||||
value val;
|
||||
SIMDJSON_TRY(element.get(val));
|
||||
if (remaining_path.empty()) {
|
||||
callback(val);
|
||||
} else {
|
||||
error_code err = element.for_each_at_path_with_wildcard(remaining_path, callback);
|
||||
if(err) { return err; }
|
||||
if(key=="*"){
|
||||
for(auto element: *this){
|
||||
|
||||
if(element.error()){
|
||||
return element.error();
|
||||
}
|
||||
|
||||
if(remaining_path.empty()){
|
||||
// Use value_unsafe() because we've already checked for errors above.
|
||||
// The 'element' is a simdjson_result<value> wrapper, and we need to extract
|
||||
// the underlying value. value_unsafe() is safe here because error() returned false.
|
||||
result.push_back(std::move(element).value_unsafe());
|
||||
|
||||
}else{
|
||||
auto nested_result = element.at_path_with_wildcard(remaining_path);
|
||||
|
||||
if(nested_result.error()){
|
||||
return nested_result.error();
|
||||
}
|
||||
// Same logic as above.
|
||||
std::vector<value> nested_matches = std::move(nested_result).value_unsafe();
|
||||
|
||||
result.insert(result.end(),
|
||||
std::make_move_iterator(nested_matches.begin()),
|
||||
std::make_move_iterator(nested_matches.end()));
|
||||
}
|
||||
}
|
||||
return SUCCESS;
|
||||
} else {
|
||||
return result;
|
||||
}else{
|
||||
// Specific index case in which we access the element at the given index
|
||||
size_t idx = 0;
|
||||
size_t idx=0;
|
||||
|
||||
for (char c : key) {
|
||||
for(char c:key){
|
||||
if(c < '0' || c > '9'){
|
||||
return INVALID_JSON_POINTER;
|
||||
}
|
||||
@@ -205,13 +217,16 @@ inline error_code array::for_each_at_path_with_wildcard(std::string_view json_pa
|
||||
}
|
||||
|
||||
auto element = at(idx);
|
||||
value val;
|
||||
SIMDJSON_TRY(element.get(val));
|
||||
if (remaining_path.empty()){
|
||||
callback(val);
|
||||
return SUCCESS;
|
||||
} else {
|
||||
return element.for_each_at_path_with_wildcard(remaining_path, callback);
|
||||
|
||||
if(element.error()){
|
||||
return element.error();
|
||||
}
|
||||
|
||||
if(remaining_path.empty()){
|
||||
result.push_back(std::move(element).value_unsafe());
|
||||
return result;
|
||||
}else{
|
||||
return element.at_path_with_wildcard(remaining_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -274,15 +289,9 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdj
|
||||
if (error()) { return error(); }
|
||||
return first.at_path(json_path);
|
||||
}
|
||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
||||
template <typename Func>
|
||||
requires std::invocable<Func, SIMDJSON_IMPLEMENTATION::ondemand::value>
|
||||
#else
|
||||
template <typename Func>
|
||||
#endif
|
||||
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array>::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
|
||||
simdjson_inline simdjson_result<std::vector<SIMDJSON_IMPLEMENTATION::ondemand::value>> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array>::at_path_with_wildcard(std::string_view json_path) noexcept {
|
||||
if (error()) { return error(); }
|
||||
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
|
||||
return first.at_path_with_wildcard(json_path);
|
||||
}
|
||||
simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array>::raw_json() noexcept {
|
||||
if (error()) { return error(); }
|
||||
|
||||
@@ -119,21 +119,13 @@ public:
|
||||
inline simdjson_result<value> at_path(std::string_view json_path) noexcept;
|
||||
|
||||
/**
|
||||
* Call the provided callback for each value matching the given JSONPath
|
||||
* expression with wildcard support.
|
||||
* Get all values matching the given JSONPath expression with wildcard support.
|
||||
* Supports wildcard patterns like "[*]" to match all array elements.
|
||||
*
|
||||
* @param json_path JSONPath expression with wildcards
|
||||
* @param callback Function called for each matching value
|
||||
* @return error_code indicating success or failure
|
||||
* @return Vector of values matching the wildcard pattern
|
||||
*/
|
||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
||||
template <typename Func>
|
||||
requires std::invocable<Func, value>
|
||||
#else
|
||||
template <typename Func>
|
||||
#endif
|
||||
inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
|
||||
inline simdjson_result<std::vector<value>> at_path_with_wildcard(std::string_view json_path) noexcept;
|
||||
|
||||
/**
|
||||
* Consumes the array and returns a string_view instance corresponding to the
|
||||
@@ -257,13 +249,7 @@ public:
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at(size_t index) noexcept;
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_path(std::string_view json_path) noexcept;
|
||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
||||
template <typename Func>
|
||||
requires std::invocable<Func, SIMDJSON_IMPLEMENTATION::ondemand::value>
|
||||
#else
|
||||
template <typename Func>
|
||||
#endif
|
||||
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
|
||||
simdjson_inline simdjson_result<std::vector<SIMDJSON_IMPLEMENTATION::ondemand::value>> at_path_with_wildcard(std::string_view json_path) noexcept;
|
||||
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
|
||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
||||
// TODO: move this code into object-inl.h
|
||||
|
||||
@@ -40,13 +40,6 @@ class token_iterator;
|
||||
class value;
|
||||
class value_iterator;
|
||||
|
||||
#if SIMDJSON_SUPPORTS_RANGES
|
||||
class array_range;
|
||||
class array_range_iterator;
|
||||
class object_range;
|
||||
class object_range_iterator;
|
||||
#endif // SIMDJSON_SUPPORTS_RANGES
|
||||
|
||||
} // namespace ondemand
|
||||
} // namespace SIMDJSON_IMPLEMENTATION
|
||||
} // namespace simdjson
|
||||
|
||||
@@ -365,14 +365,8 @@ simdjson_inline simdjson_result<value> document::at_path(std::string_view json_p
|
||||
}
|
||||
}
|
||||
|
||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
||||
template <typename Func>
|
||||
requires std::invocable<Func, value>
|
||||
#else
|
||||
template <typename Func>
|
||||
#endif
|
||||
simdjson_inline error_code document::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
|
||||
rewind(); // Rewind the document each time for_each_at_path_with_wildcard is called
|
||||
simdjson_inline simdjson_result<std::vector<value>> document::at_path_with_wildcard(std::string_view json_path) noexcept {
|
||||
rewind(); // Rewind the document each time at_path_with_wildcard is called
|
||||
if (json_path.empty()) {
|
||||
return INVALID_JSON_POINTER;
|
||||
}
|
||||
@@ -380,9 +374,9 @@ simdjson_inline error_code document::for_each_at_path_with_wildcard(std::string_
|
||||
SIMDJSON_TRY(type().get(t));
|
||||
switch (t) {
|
||||
case json_type::array:
|
||||
return (*this).get_array().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
|
||||
return (*this).get_array().at_path_with_wildcard(json_path);
|
||||
case json_type::object:
|
||||
return (*this).get_object().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
|
||||
return (*this).get_object().at_path_with_wildcard(json_path);
|
||||
default:
|
||||
return INVALID_JSON_POINTER;
|
||||
}
|
||||
@@ -719,15 +713,9 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjs
|
||||
return first.at_path(json_path);
|
||||
}
|
||||
|
||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
||||
template <typename Func>
|
||||
requires std::invocable<Func, SIMDJSON_IMPLEMENTATION::ondemand::value>
|
||||
#else
|
||||
template <typename Func>
|
||||
#endif
|
||||
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
|
||||
simdjson_inline simdjson_result<std::vector<SIMDJSON_IMPLEMENTATION::ondemand::value>> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::at_path_with_wildcard(std::string_view json_path) noexcept {
|
||||
if (error()) { return error(); }
|
||||
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
|
||||
return first.at_path_with_wildcard(json_path);
|
||||
}
|
||||
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
@@ -838,13 +826,7 @@ simdjson_inline simdjson_result<number> document_reference::get_number() noexcep
|
||||
simdjson_inline simdjson_result<std::string_view> document_reference::raw_json_token() noexcept { return doc->raw_json_token(); }
|
||||
simdjson_inline simdjson_result<value> document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); }
|
||||
simdjson_inline simdjson_result<value> document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); }
|
||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
||||
template <typename Func>
|
||||
requires std::invocable<Func, value>
|
||||
#else
|
||||
template <typename Func>
|
||||
#endif
|
||||
simdjson_inline error_code document_reference::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept { return doc->for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback)); }
|
||||
simdjson_inline simdjson_result<std::vector<value>> document_reference::at_path_with_wildcard(std::string_view json_path) noexcept { return doc->at_path_with_wildcard(json_path); }
|
||||
simdjson_inline simdjson_result<std::string_view> document_reference::raw_json() noexcept { return doc->raw_json();}
|
||||
simdjson_inline document_reference::operator document&() const noexcept { return *doc; }
|
||||
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
|
||||
@@ -1109,17 +1091,11 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjs
|
||||
}
|
||||
return first.at_path(json_path);
|
||||
}
|
||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
||||
template <typename Func>
|
||||
requires std::invocable<Func, SIMDJSON_IMPLEMENTATION::ondemand::value>
|
||||
#else
|
||||
template <typename Func>
|
||||
#endif
|
||||
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
|
||||
simdjson_inline simdjson_result<std::vector<SIMDJSON_IMPLEMENTATION::ondemand::value>> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::at_path_with_wildcard(std::string_view json_path) noexcept {
|
||||
if (error()) {
|
||||
return error();
|
||||
}
|
||||
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
|
||||
return first.at_path_with_wildcard(json_path);
|
||||
}
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
template<constevalutil::fixed_string... FieldNames, typename T>
|
||||
|
||||
@@ -744,24 +744,21 @@ public:
|
||||
simdjson_inline simdjson_result<value> at_path(std::string_view json_path) noexcept;
|
||||
|
||||
/**
|
||||
* Call the provided callback for each value matching the given JSONPath
|
||||
* expression with wildcard support.
|
||||
* Get all values matching the given JSONPath expression with wildcard support.
|
||||
*
|
||||
* Supports wildcard patterns like "$.array[*]" or "$.object.*" to match multiple elements.
|
||||
*
|
||||
* This method materializes all matching values into a vector.
|
||||
* The document will be consumed after this call.
|
||||
*
|
||||
* @param json_path JSONPath expression with wildcards
|
||||
* @param callback Function called for each matching value
|
||||
* @return error_code indicating success or failure
|
||||
* @return Vector of values matching the wildcard pattern, or:
|
||||
* - INVALID_JSON_POINTER if the JSONPath cannot be parsed
|
||||
* - NO_SUCH_FIELD if a field does not exist
|
||||
* - INDEX_OUT_OF_BOUNDS if an array index is out of bounds
|
||||
* - INCORRECT_TYPE if path traversal encounters wrong type
|
||||
*/
|
||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
||||
template <typename Func>
|
||||
requires std::invocable<Func, value>
|
||||
#else
|
||||
template <typename Func>
|
||||
#endif
|
||||
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
|
||||
simdjson_inline simdjson_result<std::vector<value>> at_path_with_wildcard(std::string_view json_path) noexcept;
|
||||
|
||||
/**
|
||||
* Consumes the document and returns a string_view instance corresponding to the
|
||||
@@ -982,13 +979,7 @@ public:
|
||||
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
|
||||
simdjson_inline simdjson_result<value> at_pointer(std::string_view json_pointer) noexcept;
|
||||
simdjson_inline simdjson_result<value> at_path(std::string_view json_path) noexcept;
|
||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
||||
template <typename Func>
|
||||
requires std::invocable<Func, value>
|
||||
#else
|
||||
template <typename Func>
|
||||
#endif
|
||||
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
|
||||
simdjson_inline simdjson_result<std::vector<value>> at_path_with_wildcard(std::string_view json_path) noexcept;
|
||||
|
||||
private:
|
||||
document *doc{nullptr};
|
||||
@@ -1074,13 +1065,7 @@ public:
|
||||
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_path(std::string_view json_path) noexcept;
|
||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
||||
template <typename Func>
|
||||
requires std::invocable<Func, SIMDJSON_IMPLEMENTATION::ondemand::value>
|
||||
#else
|
||||
template <typename Func>
|
||||
#endif
|
||||
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
|
||||
simdjson_inline simdjson_result<std::vector<SIMDJSON_IMPLEMENTATION::ondemand::value>> at_path_with_wildcard(std::string_view json_path) noexcept;
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
template<constevalutil::fixed_string... FieldNames, typename T>
|
||||
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
|
||||
@@ -1165,13 +1150,7 @@ public:
|
||||
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_path(std::string_view json_path) noexcept;
|
||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
||||
template <typename Func>
|
||||
requires std::invocable<Func, SIMDJSON_IMPLEMENTATION::ondemand::value>
|
||||
#else
|
||||
template <typename Func>
|
||||
#endif
|
||||
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
|
||||
simdjson_inline simdjson_result<std::vector<SIMDJSON_IMPLEMENTATION::ondemand::value>> at_path_with_wildcard(std::string_view json_path) noexcept;
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
template<constevalutil::fixed_string... FieldNames, typename T>
|
||||
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
|
||||
|
||||
@@ -398,7 +398,7 @@ simdjson_inline std::string_view document_stream::iterator::source() const noexc
|
||||
// legally appear in a JSON value at the source level (control
|
||||
// characters in strings must be escaped as \u001E), so stripping
|
||||
// it is safe in every stream_format.
|
||||
while(svlen > 1 && (std::isspace(static_cast<unsigned char>(start[svlen-1])) || start[svlen-1] == '\0' || static_cast<uint8_t>(start[svlen-1]) == 0x1E || (stream->format == stream_format::comma_delimited && start[svlen-1] == ','))) {
|
||||
while(svlen > 1 && (std::isspace(start[svlen-1]) || start[svlen-1] == '\0' || static_cast<uint8_t>(start[svlen-1]) == 0x1E)) {
|
||||
svlen--;
|
||||
}
|
||||
return std::string_view(start, svlen);
|
||||
|
||||
@@ -0,0 +1,339 @@
|
||||
#ifndef SIMDJSON_GENERIC_ONDEMAND_KEY_SELECTOR_H
|
||||
#define SIMDJSON_GENERIC_ONDEMAND_KEY_SELECTOR_H
|
||||
|
||||
#include "simdjson/base.h"
|
||||
#include "simdjson/common_defs.h"
|
||||
#include <array>
|
||||
#include <string_view>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
||||
|
||||
namespace simdjson {
|
||||
namespace SIMDJSON_IMPLEMENTATION {
|
||||
namespace ondemand {
|
||||
|
||||
|
||||
|
||||
// Forward declaration
|
||||
class object;
|
||||
|
||||
/**
|
||||
* A compile-time key selector for efficient JSON object field lookup.
|
||||
* Uses perfect hashing (gperf-style) to map keys to identifiers.
|
||||
*/
|
||||
template <std::size_t N>
|
||||
class key_selector {
|
||||
static_assert(N > 0, "key_selector requires at least one key");
|
||||
static_assert(N <= 100, "key_selector supports at most 100 keys");
|
||||
|
||||
// Perfect hash table data (gperf-style)
|
||||
static constexpr std::size_t MAX_POSITIONS = 16;
|
||||
static constexpr std::size_t POS_LAST_CHAR = std::size_t(-1);
|
||||
static constexpr std::size_t MAX_TABLE_SIZE = 256; // Power of 2, fits in uint8_t
|
||||
|
||||
std::array<std::array<std::uint8_t, 256>, MAX_POSITIONS> asso_values_{};
|
||||
std::uint8_t num_positions_{};
|
||||
std::array<std::size_t, MAX_POSITIONS> positions_{};
|
||||
std::array<std::uint8_t, MAX_TABLE_SIZE> slot_to_key_{};
|
||||
std::array<std::uint8_t, N> key_to_slot_{};
|
||||
std::array<std::array<char, 64>, N> key_data_{};
|
||||
std::array<std::uint8_t, N> key_lengths_{};
|
||||
std::size_t table_size_{};
|
||||
|
||||
public:
|
||||
// Validate keys at compile time
|
||||
constexpr void validate_keys(const std::array<std::string_view, N>& keys) {
|
||||
for (std::size_t i = 0; i < N; ++i) {
|
||||
auto key = keys[i];
|
||||
if (key.empty()) {
|
||||
throw "Empty keys are not allowed in key_selector";
|
||||
}
|
||||
if (key.size() > SIMDJSON_PADDING) {
|
||||
throw "Key length exceeds SIMDJSON_PADDING (64 bytes)";
|
||||
}
|
||||
for (char c : key) {
|
||||
if (c == '\\') {
|
||||
throw "Escape characters (\\) are not allowed in key_selector keys";
|
||||
}
|
||||
if (c == '\0') {
|
||||
throw "Null characters are not allowed in key_selector keys";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Gperf-style perfect hash generation using partition-based algorithm
|
||||
constexpr void generate_hash_table(const std::array<std::string_view, N>& keys) {
|
||||
// Try power-of-two table sizes starting from next_power_of_2(N)
|
||||
constexpr std::size_t START_M = next_power_of_2(N);
|
||||
if constexpr (START_M <= MAX_TABLE_SIZE) {
|
||||
if (try_compute_phf<START_M>(keys)) return;
|
||||
if constexpr (START_M * 2 <= MAX_TABLE_SIZE) {
|
||||
if (try_compute_phf<START_M * 2>(keys)) return;
|
||||
if constexpr (START_M * 4 <= MAX_TABLE_SIZE) {
|
||||
if (try_compute_phf<START_M * 4>(keys)) return;
|
||||
if constexpr (START_M * 8 <= MAX_TABLE_SIZE) {
|
||||
if (try_compute_phf<START_M * 8>(keys)) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: linear table
|
||||
table_size_ = N;
|
||||
num_positions_ = 0;
|
||||
std::fill(slot_to_key_.begin(), slot_to_key_.begin() + MAX_TABLE_SIZE, static_cast<std::uint8_t>(N));
|
||||
for (std::size_t i = 0; i < N; ++i) {
|
||||
slot_to_key_[i] = static_cast<std::uint8_t>(i);
|
||||
key_to_slot_[i] = static_cast<std::uint8_t>(i);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// Helper functions for gperf algorithm
|
||||
static constexpr std::size_t next_power_of_2(std::size_t n) {
|
||||
if (n == 0) return 1;
|
||||
std::size_t p = 1;
|
||||
while (p < n) p <<= 1;
|
||||
return p;
|
||||
}
|
||||
|
||||
static constexpr std::size_t char_at(std::string_view key, std::size_t pos) {
|
||||
if (pos == POS_LAST_CHAR) {
|
||||
return key.empty() ? 256 : static_cast<unsigned char>(key.back());
|
||||
}
|
||||
return (pos < key.size()) ? static_cast<unsigned char>(key[pos]) : 256;
|
||||
}
|
||||
|
||||
template <std::size_t M>
|
||||
constexpr bool try_compute_phf(const std::array<std::string_view, N>& keys) {
|
||||
// Initialize
|
||||
std::array<std::array<std::size_t, 256>, MAX_POSITIONS> asso{};
|
||||
std::size_t npos = 0;
|
||||
std::array<std::size_t, MAX_POSITIONS> pos{};
|
||||
std::array<std::size_t, M> s2k{};
|
||||
|
||||
// Try to generate gperf
|
||||
if (try_generate_gperf<M>(keys, asso, npos, pos, s2k)) {
|
||||
table_size_ = M;
|
||||
num_positions_ = static_cast<std::uint8_t>(npos);
|
||||
for (std::size_t p = 0; p < MAX_POSITIONS; ++p) {
|
||||
positions_[p] = pos[p];
|
||||
for (std::size_t c = 0; c < 256; ++c) {
|
||||
asso_values_[p][c] = static_cast<std::uint8_t>(asso[p][c]);
|
||||
}
|
||||
}
|
||||
for (std::size_t i = 0; i < M; ++i) {
|
||||
slot_to_key_[i] = static_cast<std::uint8_t>(s2k[i]);
|
||||
}
|
||||
// Fill remaining slots with sentinel
|
||||
for (std::size_t i = M; i < MAX_TABLE_SIZE; ++i) {
|
||||
slot_to_key_[i] = static_cast<std::uint8_t>(N);
|
||||
}
|
||||
|
||||
// Build key_to_slot mapping
|
||||
for (std::size_t i = 0; i < N; ++i) {
|
||||
key_to_slot_[i] = static_cast<std::uint8_t>(N); // Initialize
|
||||
}
|
||||
for (std::size_t slot = 0; slot < M; ++slot) {
|
||||
std::size_t key_idx = s2k[slot];
|
||||
if (key_idx < N) {
|
||||
key_to_slot_[key_idx] = static_cast<std::uint8_t>(slot);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <std::size_t M>
|
||||
static constexpr bool try_generate_gperf(
|
||||
const std::array<std::string_view, N>& keys,
|
||||
std::array<std::array<std::size_t, 256>, MAX_POSITIONS>& asso_values,
|
||||
std::size_t& num_positions,
|
||||
std::array<std::size_t, MAX_POSITIONS>& positions,
|
||||
std::array<std::size_t, M>& slot_to_key)
|
||||
{
|
||||
// Initialize
|
||||
for (std::size_t p = 0; p < MAX_POSITIONS; ++p) {
|
||||
for (std::size_t c = 0; c < 256; ++c) {
|
||||
asso_values[p][c] = 0;
|
||||
}
|
||||
}
|
||||
for (std::size_t i = 0; i < M; ++i) {
|
||||
slot_to_key[i] = N;
|
||||
}
|
||||
|
||||
// Try length-only hashing first
|
||||
bool success = true;
|
||||
for (std::size_t i = 0; i < N && success; ++i) {
|
||||
std::size_t slot = keys[i].size() % M;
|
||||
if (slot_to_key[slot] != N) {
|
||||
success = false;
|
||||
} else {
|
||||
slot_to_key[slot] = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (success) {
|
||||
num_positions = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Try with position 0
|
||||
positions[0] = 0;
|
||||
num_positions = 1;
|
||||
|
||||
// Find a working assignment of asso_values for position 0
|
||||
// Use a simple approach: try different offsets
|
||||
for (std::size_t offset = 0; offset < M; ++offset) {
|
||||
// Reset
|
||||
for (std::size_t i = 0; i < M; ++i) {
|
||||
slot_to_key[i] = N;
|
||||
}
|
||||
|
||||
// Assign asso_values based on offset
|
||||
for (std::size_t c = 0; c < 256; ++c) {
|
||||
asso_values[0][c] = (c + offset) % M;
|
||||
}
|
||||
|
||||
success = true;
|
||||
for (std::size_t i = 0; i < N && success; ++i) {
|
||||
std::size_t h = keys[i].size();
|
||||
std::size_t ch = char_at(keys[i], 0);
|
||||
if (ch < 256) h += asso_values[0][ch];
|
||||
std::size_t slot = h % M;
|
||||
|
||||
if (slot_to_key[slot] != N) {
|
||||
success = false;
|
||||
} else {
|
||||
slot_to_key[slot] = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (success) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Try with positions {0, last_char}
|
||||
if (N <= 50) { // Only for smaller N to avoid complexity
|
||||
positions[0] = 0;
|
||||
positions[1] = POS_LAST_CHAR;
|
||||
num_positions = 2;
|
||||
|
||||
for (std::size_t offset1 = 0; offset1 < 4 && !success; ++offset1) {
|
||||
for (std::size_t offset2 = 0; offset2 < 4 && !success; ++offset2) {
|
||||
// Reset
|
||||
for (std::size_t i = 0; i < M; ++i) {
|
||||
slot_to_key[i] = N;
|
||||
}
|
||||
|
||||
// Assign asso_values
|
||||
for (std::size_t c = 0; c < 256; ++c) {
|
||||
asso_values[0][c] = (c + offset1) % M;
|
||||
asso_values[1][c] = (c + offset2) % M;
|
||||
}
|
||||
|
||||
success = true;
|
||||
for (std::size_t i = 0; i < N && success; ++i) {
|
||||
std::size_t h = keys[i].size();
|
||||
std::size_t ch1 = char_at(keys[i], 0);
|
||||
if (ch1 < 256) h += asso_values[0][ch1];
|
||||
std::size_t ch2 = char_at(keys[i], POS_LAST_CHAR);
|
||||
if (ch2 < 256) h += asso_values[1][ch2];
|
||||
std::size_t slot = h % M;
|
||||
|
||||
if (slot_to_key[slot] != N) {
|
||||
success = false;
|
||||
} else {
|
||||
slot_to_key[slot] = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (success) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public:
|
||||
constexpr key_selector(const std::array<std::string_view, N>& keys) {
|
||||
validate_keys(keys);
|
||||
|
||||
// Store key data
|
||||
for (std::size_t i = 0; i < N; ++i) {
|
||||
key_lengths_[i] = static_cast<std::uint8_t>(keys[i].size());
|
||||
std::copy(keys[i].begin(), keys[i].end(), key_data_[i].begin());
|
||||
}
|
||||
|
||||
generate_hash_table(keys);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr std::size_t size() const noexcept { return N; }
|
||||
|
||||
[[nodiscard]] constexpr simdjson_really_inline std::size_t compute_hash(std::string_view key) const noexcept {
|
||||
std::size_t h = key.size();
|
||||
const char* kp = key.data();
|
||||
for (std::uint8_t i = 0; i < num_positions_; ++i) {
|
||||
std::size_t pos = positions_[i];
|
||||
std::size_t ch;
|
||||
if (pos == POS_LAST_CHAR) {
|
||||
ch = static_cast<unsigned char>(key.back());
|
||||
} else {
|
||||
ch = static_cast<unsigned char>(kp[pos]);
|
||||
}
|
||||
h += asso_values_[i][ch];
|
||||
}
|
||||
return h & (table_size_ - 1);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr simdjson_really_inline bool contains(std::string_view key) const noexcept {
|
||||
std::size_t slot = compute_hash(key);
|
||||
if (slot >= table_size_) return false;
|
||||
|
||||
std::uint8_t key_idx = slot_to_key_[slot];
|
||||
if (key_idx >= N) return false;
|
||||
|
||||
// Compare key
|
||||
if (key_lengths_[key_idx] != key.size()) return false;
|
||||
return std::equal(key.begin(), key.end(), key_data_[key_idx].begin());
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr simdjson_really_inline std::size_t index_of(std::string_view key) const noexcept {
|
||||
std::size_t slot = compute_hash(key);
|
||||
if (slot >= table_size_) return N; // Invalid index
|
||||
|
||||
std::uint8_t key_idx = slot_to_key_[slot];
|
||||
if (key_idx >= N) return N;
|
||||
|
||||
// Compare key
|
||||
if (key_lengths_[key_idx] != key.size()) return N;
|
||||
if (!std::equal(key.begin(), key.end(), key_data_[key_idx].begin())) return N;
|
||||
|
||||
return key_idx;
|
||||
}
|
||||
|
||||
// Accessors for key data (used by object::find_field)
|
||||
[[nodiscard]] constexpr std::string_view get_key(std::size_t index) const noexcept {
|
||||
if (index >= N) return {};
|
||||
return std::string_view(key_data_[index].data(), key_lengths_[index]);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace ondemand
|
||||
} // namespace SIMDJSON_IMPLEMENTATION
|
||||
} // namespace simdjson
|
||||
|
||||
#endif // SIMDJSON_SUPPORTS_CONCEPTS
|
||||
|
||||
#endif // SIMDJSON_GENERIC_ONDEMAND_KEY_SELECTOR_H
|
||||
@@ -63,6 +63,42 @@ simdjson_inline simdjson_result<value> object::find_field(const std::string_view
|
||||
return value(iter.child());
|
||||
}
|
||||
|
||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
||||
template <concepts::key_selector_type Selector>
|
||||
simdjson_inline std::pair<std::size_t, simdjson_result<value>> object::find_field(const Selector& selector) & noexcept {
|
||||
// Try to find any of the keys in the selector
|
||||
for (std::size_t i = 0; i < selector.size(); ++i) {
|
||||
std::string_view key = selector.get_key(i);
|
||||
auto result = iter.find_field_unordered_raw(key);
|
||||
if (result.error()) {
|
||||
return {selector.size(), result.error()};
|
||||
}
|
||||
bool has_value = result.value();
|
||||
if (has_value) {
|
||||
return {i, value(iter.child())};
|
||||
}
|
||||
}
|
||||
return {selector.size(), NO_SUCH_FIELD}; // Return size() as invalid index
|
||||
}
|
||||
|
||||
template <concepts::key_selector_type Selector>
|
||||
simdjson_inline std::pair<std::size_t, simdjson_result<value>> object::find_field(const Selector& selector) && noexcept {
|
||||
// Try to find any of the keys in the selector
|
||||
for (std::size_t i = 0; i < selector.size(); ++i) {
|
||||
std::string_view key = selector.get_key(i);
|
||||
auto result = iter.find_field_unordered_raw(key);
|
||||
if (result.error()) {
|
||||
return {selector.size(), result.error()};
|
||||
}
|
||||
bool has_value = result.value();
|
||||
if (has_value) {
|
||||
return {i, value(iter.child())};
|
||||
}
|
||||
}
|
||||
return {selector.size(), NO_SUCH_FIELD}; // Return size() as invalid index
|
||||
}
|
||||
#endif
|
||||
|
||||
simdjson_inline simdjson_result<object> object::start(value_iterator &iter) noexcept {
|
||||
SIMDJSON_TRY( iter.start_object().error() );
|
||||
return object(iter);
|
||||
@@ -177,13 +213,9 @@ inline simdjson_result<value> object::at_path(std::string_view json_path) noexce
|
||||
return at_pointer(json_pointer);
|
||||
}
|
||||
|
||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
||||
template <typename Func>
|
||||
requires std::invocable<Func, value>
|
||||
#else
|
||||
template <typename Func>
|
||||
#endif
|
||||
inline error_code object::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
|
||||
inline simdjson_result<std::vector<value>> object::at_path_with_wildcard(std::string_view json_path) noexcept {
|
||||
std::vector<value> result;
|
||||
|
||||
auto result_pair = get_next_key_and_json_path(json_path);
|
||||
std::string_view key = result_pair.first;
|
||||
std::string_view remaining_path = result_pair.second;
|
||||
@@ -193,22 +225,34 @@ inline error_code object::for_each_at_path_with_wildcard(std::string_view json_p
|
||||
for (auto field : *this) {
|
||||
value val;
|
||||
SIMDJSON_TRY(field.value().get(val));
|
||||
|
||||
if (remaining_path.empty()) {
|
||||
callback(val);
|
||||
result.push_back(std::move(val));
|
||||
} else {
|
||||
SIMDJSON_TRY(val.for_each_at_path_with_wildcard(remaining_path, callback));
|
||||
auto nested_result = val.at_path_with_wildcard(remaining_path);
|
||||
|
||||
if (nested_result.error()) {
|
||||
return nested_result.error();
|
||||
}
|
||||
// Extract and append all nested matches to our result
|
||||
std::vector<value> nested_vec;
|
||||
SIMDJSON_TRY(std::move(nested_result).get(nested_vec));
|
||||
|
||||
result.insert(result.end(),
|
||||
std::make_move_iterator(nested_vec.begin()),
|
||||
std::make_move_iterator(nested_vec.end()));
|
||||
}
|
||||
}
|
||||
return SUCCESS;
|
||||
return result;
|
||||
} else {
|
||||
value val;
|
||||
SIMDJSON_TRY(find_field(key).get(val));
|
||||
|
||||
if (remaining_path.empty()) {
|
||||
callback(val);
|
||||
return SUCCESS;
|
||||
result.push_back(std::move(val));
|
||||
return result;
|
||||
} else {
|
||||
return val.for_each_at_path_with_wildcard(remaining_path, callback);
|
||||
return val.at_path_with_wildcard(remaining_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -326,6 +370,20 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjs
|
||||
return std::forward<SIMDJSON_IMPLEMENTATION::ondemand::object>(first).find_field(key);
|
||||
}
|
||||
|
||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
||||
template <concepts::key_selector_type Selector>
|
||||
simdjson_inline std::pair<std::size_t, simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object>::find_field(const Selector& selector) & noexcept {
|
||||
if (error()) { return {0, error()}; }
|
||||
return first.find_field(selector);
|
||||
}
|
||||
|
||||
template <concepts::key_selector_type Selector>
|
||||
simdjson_inline std::pair<std::size_t, simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object>::find_field(const Selector& selector) && noexcept {
|
||||
if (error()) { return {0, error()}; }
|
||||
return std::forward<SIMDJSON_IMPLEMENTATION::ondemand::object>(first).find_field(selector);
|
||||
}
|
||||
#endif
|
||||
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object>::at_pointer(std::string_view json_pointer) noexcept {
|
||||
if (error()) { return error(); }
|
||||
return first.at_pointer(json_pointer);
|
||||
@@ -339,15 +397,9 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjs
|
||||
return first.at_path(json_path);
|
||||
}
|
||||
|
||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
||||
template <typename Func>
|
||||
requires std::invocable<Func, SIMDJSON_IMPLEMENTATION::ondemand::value>
|
||||
#else
|
||||
template <typename Func>
|
||||
#endif
|
||||
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object>::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
|
||||
simdjson_inline simdjson_result<std::vector<SIMDJSON_IMPLEMENTATION::ondemand::value>> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object>::at_path_with_wildcard(std::string_view json_path) noexcept {
|
||||
if (error()) { return error(); }
|
||||
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
|
||||
return first.at_path_with_wildcard(json_path);
|
||||
}
|
||||
|
||||
inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object>::reset() noexcept {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "simdjson/generic/ondemand/base.h"
|
||||
#include "simdjson/generic/implementation_simdjson_result_base.h"
|
||||
#include "simdjson/generic/ondemand/value_iterator.h"
|
||||
#include "simdjson/generic/ondemand/key_selector.h"
|
||||
#include <vector>
|
||||
#if SIMDJSON_STATIC_REFLECTION && SIMDJSON_SUPPORTS_CONCEPTS
|
||||
#include "simdjson/generic/ondemand/json_string_builder.h" // for constevalutil::fixed_string
|
||||
@@ -122,6 +123,23 @@ public:
|
||||
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
|
||||
simdjson_inline simdjson_result<value> operator[](std::string_view key) && noexcept;
|
||||
|
||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
||||
/**
|
||||
* Look up a field by name using a key_selector. This method is similar to find_field_unordered()
|
||||
* but uses a compile-time generated perfect hash table for efficient lookup.
|
||||
*
|
||||
* @tparam Selector The key_selector type
|
||||
* @param selector The key selector instance
|
||||
* @returns A pair containing the key identifier (index in the selector) and the value,
|
||||
* or NO_SUCH_FIELD if the field is not in the object.
|
||||
*/
|
||||
template <concepts::key_selector_type Selector>
|
||||
simdjson_inline std::pair<std::size_t, simdjson_result<value>> find_field(const Selector& selector) & noexcept;
|
||||
/** @overload template <concepts::key_selector_type Selector> simdjson_inline std::pair<std::size_t, simdjson_result<value>> find_field(const Selector& selector) & noexcept; */
|
||||
template <concepts::key_selector_type Selector>
|
||||
simdjson_inline std::pair<std::size_t, simdjson_result<value>> find_field(const Selector& selector) && noexcept;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Get the value associated with the given JSON pointer. We use the RFC 6901
|
||||
* https://tools.ietf.org/html/rfc6901 standard, interpreting the current node
|
||||
@@ -172,21 +190,13 @@ public:
|
||||
inline simdjson_result<value> at_path(std::string_view json_path) noexcept;
|
||||
|
||||
/**
|
||||
* Call the provided callback for each value matching the given JSONPath
|
||||
* expression with wildcard support.
|
||||
* Get all values matching the given JSONPath expression with wildcard support.
|
||||
* Supports wildcard patterns like ".*" to match all object fields.
|
||||
*
|
||||
* @param json_path JSONPath expression with wildcards
|
||||
* @param callback Function called for each matching value
|
||||
* @return error_code indicating success or failure
|
||||
* @return Vector of values matching the wildcard pattern
|
||||
*/
|
||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
||||
template <typename Func>
|
||||
requires std::invocable<Func, value>
|
||||
#else
|
||||
template <typename Func>
|
||||
#endif
|
||||
inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
|
||||
inline simdjson_result<std::vector<value>> at_path_with_wildcard(std::string_view json_path) noexcept;
|
||||
|
||||
/**
|
||||
* Reset the iterator so that we are pointing back at the
|
||||
@@ -332,17 +342,17 @@ public:
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field(std::string_view key) && noexcept;
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field_unordered(std::string_view key) & noexcept;
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field_unordered(std::string_view key) && noexcept;
|
||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
||||
template <concepts::key_selector_type Selector>
|
||||
simdjson_inline std::pair<std::size_t, simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>> find_field(const Selector& selector) & noexcept;
|
||||
template <concepts::key_selector_type Selector>
|
||||
simdjson_inline std::pair<std::size_t, simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>> find_field(const Selector& selector) && noexcept;
|
||||
#endif
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> operator[](std::string_view key) & noexcept;
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> operator[](std::string_view key) && noexcept;
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_path(std::string_view json_path) noexcept;
|
||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
||||
template <typename Func>
|
||||
requires std::invocable<Func, SIMDJSON_IMPLEMENTATION::ondemand::value>
|
||||
#else
|
||||
template <typename Func>
|
||||
#endif
|
||||
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
|
||||
simdjson_inline simdjson_result<std::vector<SIMDJSON_IMPLEMENTATION::ondemand::value>> at_path_with_wildcard(std::string_view json_path) noexcept;
|
||||
inline simdjson_result<bool> reset() noexcept;
|
||||
inline simdjson_result<bool> is_empty() noexcept;
|
||||
inline simdjson_result<size_t> count_fields() & noexcept;
|
||||
|
||||
@@ -27,10 +27,7 @@ simdjson_warn_unused simdjson_inline error_code parser::allocate(size_t new_capa
|
||||
|
||||
// string_capacity copied from document::allocate
|
||||
_capacity = 0;
|
||||
if(5 * (new_capacity / 3) + SIMDJSON_PADDING < SIMDJSON_PADDING) {
|
||||
return CAPACITY; // overflow, only happen on legacy 32-bit systems with very large capacity
|
||||
}
|
||||
size_t string_capacity = SIMDJSON_ROUNDUP_N(5 * (new_capacity / 3) + SIMDJSON_PADDING, 64);
|
||||
size_t string_capacity = SIMDJSON_ROUNDUP_N(5 * new_capacity / 3 + SIMDJSON_PADDING, 64);
|
||||
string_buf.reset(new (std::nothrow) uint8_t[string_capacity]);
|
||||
#if SIMDJSON_DEVELOPMENT_CHECKS
|
||||
start_positions.reset(new (std::nothrow) token_position[new_max_depth]);
|
||||
@@ -275,7 +272,7 @@ simdjson_inline simdjson_warn_unused ondemand::parser& parser::get_parser() {
|
||||
return *parser::get_parser_instance();
|
||||
}
|
||||
|
||||
simdjson_inline bool parser::release_parser() {
|
||||
simdjson_inline bool release_parser() {
|
||||
auto &parser_instance = parser::get_threadlocal_parser_if_exists();
|
||||
if (parser_instance) {
|
||||
parser_instance.reset();
|
||||
|
||||
@@ -422,6 +422,8 @@ public:
|
||||
static simdjson_inline bool release_parser();
|
||||
|
||||
private:
|
||||
friend bool release_parser();
|
||||
friend ondemand::parser& get_parser();
|
||||
/** Get the thread-local parser instance, allocates it if needed */
|
||||
static simdjson_inline simdjson_warn_unused std::unique_ptr<ondemand::parser>& get_parser_instance();
|
||||
/** Get the thread-local parser instance, it might be null */
|
||||
|
||||
@@ -1,138 +0,0 @@
|
||||
#ifndef SIMDJSON_GENERIC_ONDEMAND_RANGES_INL_H
|
||||
|
||||
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
|
||||
#define SIMDJSON_GENERIC_ONDEMAND_RANGES_INL_H
|
||||
#include "simdjson/generic/ondemand/base.h"
|
||||
#include "simdjson/generic/ondemand/ranges.h"
|
||||
#include "simdjson/generic/ondemand/array-inl.h"
|
||||
#include "simdjson/generic/ondemand/array_iterator-inl.h"
|
||||
#include "simdjson/generic/ondemand/object-inl.h"
|
||||
#include "simdjson/generic/ondemand/object_iterator-inl.h"
|
||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||
|
||||
#if SIMDJSON_SUPPORTS_RANGES
|
||||
|
||||
namespace simdjson {
|
||||
namespace SIMDJSON_IMPLEMENTATION {
|
||||
namespace ondemand {
|
||||
|
||||
//
|
||||
// array_range_iterator
|
||||
//
|
||||
|
||||
simdjson_inline array_range_iterator::array_range_iterator(array_iterator iter) noexcept
|
||||
: iter_{iter} {}
|
||||
|
||||
simdjson_inline simdjson_result<value> array_range_iterator::operator*() const noexcept {
|
||||
return *iter_;
|
||||
}
|
||||
|
||||
simdjson_inline array_range_iterator& array_range_iterator::operator++() noexcept {
|
||||
++iter_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
|
||||
simdjson_inline void array_range_iterator::operator++(int) noexcept {
|
||||
++*this;
|
||||
}
|
||||
SIMDJSON_POP_DISABLE_WARNINGS
|
||||
|
||||
//
|
||||
// array_range
|
||||
//
|
||||
|
||||
simdjson_inline array_range::array_range(array& arr) noexcept {
|
||||
auto b = arr.begin();
|
||||
if (b.error()) { error_ = b.error(); return; }
|
||||
begin_ = b.value_unsafe();
|
||||
end_ = arr.end().value_unsafe();
|
||||
}
|
||||
|
||||
simdjson_inline array_range_iterator array_range::begin() noexcept {
|
||||
return array_range_iterator(begin_);
|
||||
}
|
||||
|
||||
simdjson_inline array_range_iterator array_range::end() noexcept {
|
||||
return array_range_iterator(end_);
|
||||
}
|
||||
|
||||
//
|
||||
// object_range_iterator
|
||||
//
|
||||
|
||||
simdjson_inline object_range_iterator::object_range_iterator(object_iterator iter) noexcept
|
||||
: iter_{iter} {}
|
||||
|
||||
simdjson_inline simdjson_result<field> object_range_iterator::operator*() const noexcept {
|
||||
return *iter_;
|
||||
}
|
||||
|
||||
simdjson_inline object_range_iterator& object_range_iterator::operator++() noexcept {
|
||||
++iter_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
|
||||
simdjson_inline void object_range_iterator::operator++(int) noexcept {
|
||||
++*this;
|
||||
}
|
||||
SIMDJSON_POP_DISABLE_WARNINGS
|
||||
|
||||
|
||||
//
|
||||
// object_range
|
||||
//
|
||||
|
||||
simdjson_inline object_range::object_range(object& obj) noexcept {
|
||||
auto b = obj.begin();
|
||||
if (b.error()) { error_ = b.error(); return; }
|
||||
begin_ = b.value_unsafe();
|
||||
end_ = obj.end().value_unsafe();
|
||||
}
|
||||
|
||||
simdjson_inline object_range_iterator object_range::begin() noexcept {
|
||||
return object_range_iterator(begin_);
|
||||
}
|
||||
|
||||
simdjson_inline object_range_iterator object_range::end() noexcept {
|
||||
return object_range_iterator(end_);
|
||||
}
|
||||
|
||||
//
|
||||
// Free functions
|
||||
//
|
||||
|
||||
simdjson_inline array_range get_range(array& arr) noexcept {
|
||||
return array_range(arr);
|
||||
}
|
||||
|
||||
simdjson_inline object_range get_key_value_range(object& obj) noexcept {
|
||||
return object_range(obj);
|
||||
}
|
||||
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
simdjson_inline array_range get_range(simdjson_result<array> result) {
|
||||
return array_range(result.value());
|
||||
}
|
||||
|
||||
simdjson_inline object_range get_key_value_range(simdjson_result<object> result) {
|
||||
return object_range(result.value());
|
||||
}
|
||||
#endif // SIMDJSON_EXCEPTIONS
|
||||
|
||||
} // namespace ondemand
|
||||
} // namespace SIMDJSON_IMPLEMENTATION
|
||||
} // namespace simdjson
|
||||
|
||||
// Verify the range wrapper types satisfy the expected C++20 concepts.
|
||||
static_assert(std::input_iterator<simdjson::SIMDJSON_IMPLEMENTATION::ondemand::array_range_iterator>);
|
||||
static_assert(std::input_iterator<simdjson::SIMDJSON_IMPLEMENTATION::ondemand::object_range_iterator>);
|
||||
static_assert(std::ranges::input_range<simdjson::SIMDJSON_IMPLEMENTATION::ondemand::array_range>);
|
||||
static_assert(std::ranges::input_range<simdjson::SIMDJSON_IMPLEMENTATION::ondemand::object_range>);
|
||||
static_assert(std::ranges::view<simdjson::SIMDJSON_IMPLEMENTATION::ondemand::array_range>);
|
||||
static_assert(std::ranges::view<simdjson::SIMDJSON_IMPLEMENTATION::ondemand::object_range>);
|
||||
|
||||
#endif // SIMDJSON_SUPPORTS_RANGES
|
||||
|
||||
#endif // SIMDJSON_GENERIC_ONDEMAND_RANGES_INL_H
|
||||
@@ -1,182 +0,0 @@
|
||||
#ifndef SIMDJSON_GENERIC_ONDEMAND_RANGES_H
|
||||
|
||||
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
|
||||
#define SIMDJSON_GENERIC_ONDEMAND_RANGES_H
|
||||
#include "simdjson/generic/ondemand/base.h"
|
||||
#include "simdjson/generic/ondemand/array.h"
|
||||
#include "simdjson/generic/ondemand/array_iterator.h"
|
||||
#include "simdjson/generic/ondemand/object.h"
|
||||
#include "simdjson/generic/ondemand/object_iterator.h"
|
||||
#include "simdjson/generic/ondemand/field.h"
|
||||
#include "simdjson/generic/ondemand/value.h"
|
||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||
|
||||
#if SIMDJSON_SUPPORTS_RANGES
|
||||
|
||||
namespace simdjson {
|
||||
namespace SIMDJSON_IMPLEMENTATION {
|
||||
namespace ondemand {
|
||||
|
||||
/**
|
||||
* A ranges-compatible iterator adapter for JSON arrays.
|
||||
*
|
||||
* Wraps array_iterator to satisfy std::input_iterator by providing:
|
||||
* - const operator* (via mutable internal state)
|
||||
* - post-increment operator
|
||||
* - iterator_concept tag
|
||||
*
|
||||
* The mutable approach is standard for single-pass input iterators that
|
||||
* read from external sources (similar to std::istream_iterator).
|
||||
*/
|
||||
class array_range_iterator {
|
||||
public:
|
||||
using iterator_concept = std::input_iterator_tag;
|
||||
using value_type = simdjson_result<value>;
|
||||
using reference = simdjson_result<value>;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
|
||||
simdjson_inline array_range_iterator() noexcept = default;
|
||||
simdjson_inline explicit array_range_iterator(array_iterator iter) noexcept;
|
||||
|
||||
/**
|
||||
* Get the current element. Const-qualified for std::indirectly_readable;
|
||||
* internally delegates to the mutable wrapped iterator.
|
||||
*/
|
||||
simdjson_inline simdjson_result<value> operator*() const noexcept;
|
||||
simdjson_inline array_range_iterator& operator++() noexcept;
|
||||
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
|
||||
simdjson_inline void operator++(int) noexcept;
|
||||
SIMDJSON_POP_DISABLE_WARNINGS
|
||||
/**
|
||||
* Comparison delegates to array_iterator::operator==, which checks
|
||||
* whether the underlying parser has finished the array (depth-based).
|
||||
*/
|
||||
simdjson_inline friend bool operator==(const array_range_iterator& a,
|
||||
const array_range_iterator& b) noexcept {
|
||||
return a.iter_ == b.iter_;
|
||||
}
|
||||
|
||||
private:
|
||||
mutable array_iterator iter_{};
|
||||
};
|
||||
|
||||
/**
|
||||
* A std::ranges::view over a JSON array.
|
||||
*
|
||||
* Wraps an ondemand::array and exposes begin()/end() that return
|
||||
* array_range_iterator (satisfying std::input_iterator), enabling
|
||||
* use with std::views::transform and other range adaptors.
|
||||
*
|
||||
* If the array's begin() returns an error (only possible under
|
||||
* SIMDJSON_DEVELOPMENT_CHECKS), the range will be empty and error()
|
||||
* will return the error code.
|
||||
*
|
||||
* Usage:
|
||||
* ondemand::parser parser;
|
||||
* auto doc = parser.iterate(json);
|
||||
* auto arr = doc.get_array().value();
|
||||
* for (auto elem : ondemand::get_range(arr)) { ... }
|
||||
*/
|
||||
class array_range {
|
||||
public:
|
||||
simdjson_inline array_range() noexcept = default;
|
||||
simdjson_inline explicit array_range(array& arr) noexcept;
|
||||
|
||||
simdjson_inline array_range_iterator begin() noexcept;
|
||||
simdjson_inline array_range_iterator end() noexcept;
|
||||
|
||||
/** Returns SUCCESS if the range was created successfully, or the error code otherwise. */
|
||||
simdjson_inline error_code error() const noexcept { return error_; }
|
||||
|
||||
private:
|
||||
array_iterator begin_{};
|
||||
array_iterator end_{};
|
||||
error_code error_{SUCCESS};
|
||||
};
|
||||
|
||||
/**
|
||||
* A ranges-compatible iterator adapter for JSON objects.
|
||||
*
|
||||
* Wraps object_iterator to satisfy std::input_iterator, yielding
|
||||
* simdjson_result<field> elements (key-value pairs).
|
||||
*/
|
||||
class object_range_iterator {
|
||||
public:
|
||||
using iterator_concept = std::input_iterator_tag;
|
||||
using value_type = simdjson_result<field>;
|
||||
using reference = simdjson_result<field>;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
|
||||
simdjson_inline object_range_iterator() noexcept = default;
|
||||
simdjson_inline explicit object_range_iterator(object_iterator iter) noexcept;
|
||||
|
||||
simdjson_inline simdjson_result<field> operator*() const noexcept;
|
||||
simdjson_inline object_range_iterator& operator++() noexcept;
|
||||
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
|
||||
simdjson_inline void operator++(int) noexcept;
|
||||
SIMDJSON_POP_DISABLE_WARNINGS
|
||||
simdjson_inline friend bool operator==(const object_range_iterator& a,
|
||||
const object_range_iterator& b) noexcept {
|
||||
return a.iter_ == b.iter_;
|
||||
}
|
||||
|
||||
private:
|
||||
mutable object_iterator iter_{};
|
||||
};
|
||||
|
||||
/**
|
||||
* A std::ranges::view over a JSON object.
|
||||
*
|
||||
* Wraps an ondemand::object and exposes begin()/end() that return
|
||||
* object_range_iterator, enabling use with range adaptors.
|
||||
*
|
||||
* If the object's begin() returns an error, the range will be empty
|
||||
* and error() will return the error code.
|
||||
*/
|
||||
class object_range {
|
||||
public:
|
||||
simdjson_inline object_range() noexcept = default;
|
||||
simdjson_inline explicit object_range(object& obj) noexcept;
|
||||
|
||||
simdjson_inline object_range_iterator begin() noexcept;
|
||||
simdjson_inline object_range_iterator end() noexcept;
|
||||
|
||||
/** Returns SUCCESS if the range was created successfully, or the error code otherwise. */
|
||||
simdjson_inline error_code error() const noexcept { return error_; }
|
||||
|
||||
private:
|
||||
object_iterator begin_{};
|
||||
object_iterator end_{};
|
||||
error_code error_{SUCCESS};
|
||||
};
|
||||
|
||||
/** Get a std::ranges compatible view over a JSON array. */
|
||||
simdjson_inline array_range get_range(array& arr) noexcept;
|
||||
|
||||
/** Get a std::ranges compatible view over a JSON object (key-value pairs). */
|
||||
simdjson_inline object_range get_key_value_range(object& obj) noexcept;
|
||||
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
/** Get a std::ranges compatible view, unwrapping the simdjson_result (throws on error). */
|
||||
simdjson_inline array_range get_range(simdjson_result<array> result);
|
||||
|
||||
/** Get a std::ranges compatible view, unwrapping the simdjson_result (throws on error). */
|
||||
simdjson_inline object_range get_key_value_range(simdjson_result<object> result);
|
||||
#endif // SIMDJSON_EXCEPTIONS
|
||||
|
||||
} // namespace ondemand
|
||||
} // namespace SIMDJSON_IMPLEMENTATION
|
||||
} // namespace simdjson
|
||||
|
||||
namespace std {
|
||||
namespace ranges {
|
||||
template<>
|
||||
inline constexpr bool enable_view<simdjson::SIMDJSON_IMPLEMENTATION::ondemand::array_range> = true;
|
||||
template<>
|
||||
inline constexpr bool enable_view<simdjson::SIMDJSON_IMPLEMENTATION::ondemand::object_range> = true;
|
||||
} // namespace ranges
|
||||
} // namespace std
|
||||
|
||||
#endif // SIMDJSON_SUPPORTS_RANGES
|
||||
|
||||
#endif // SIMDJSON_GENERIC_ONDEMAND_RANGES_H
|
||||
@@ -314,20 +314,14 @@ simdjson_inline simdjson_result<value> value::at_path(std::string_view json_path
|
||||
}
|
||||
}
|
||||
|
||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
||||
template <typename Func>
|
||||
requires std::invocable<Func, value>
|
||||
#else
|
||||
template <typename Func>
|
||||
#endif
|
||||
inline error_code value::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
|
||||
inline simdjson_result<std::vector<value>> value::at_path_with_wildcard(std::string_view json_path) noexcept {
|
||||
json_type t;
|
||||
SIMDJSON_TRY(type().get(t));
|
||||
switch (t) {
|
||||
case json_type::array:
|
||||
return (*this).get_array().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
|
||||
return (*this).get_array().at_path_with_wildcard(json_path);
|
||||
case json_type::object:
|
||||
return (*this).get_object().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
|
||||
return (*this).get_object().at_path_with_wildcard(json_path);
|
||||
default:
|
||||
return INVALID_JSON_POINTER;
|
||||
}
|
||||
@@ -591,18 +585,12 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjs
|
||||
return first.at_path(json_path);
|
||||
}
|
||||
|
||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
||||
template <typename Func>
|
||||
requires std::invocable<Func, SIMDJSON_IMPLEMENTATION::ondemand::value>
|
||||
#else
|
||||
template <typename Func>
|
||||
#endif
|
||||
inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::for_each_at_path_with_wildcard(
|
||||
std::string_view json_path, Func&& callback) noexcept {
|
||||
inline simdjson_result<std::vector<SIMDJSON_IMPLEMENTATION::ondemand::value>> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::at_path_with_wildcard(
|
||||
std::string_view json_path) noexcept {
|
||||
if (error()) {
|
||||
return error();
|
||||
}
|
||||
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
|
||||
return first.at_path_with_wildcard(json_path);
|
||||
}
|
||||
|
||||
} // namespace simdjson
|
||||
|
||||
@@ -698,21 +698,13 @@ public:
|
||||
simdjson_inline simdjson_result<value> at_path(std::string_view at_path) noexcept;
|
||||
|
||||
/**
|
||||
* Call the provided callback for each value matching the given JSONPath
|
||||
* expression with wildcard support.
|
||||
* Get all values matching the given JSONPath expression with wildcard support.
|
||||
* Supports wildcard character (*) for arrays or ".*" for objects.
|
||||
*
|
||||
* @param json_path JSONPath expression with wildcards
|
||||
* @param callback Function called for each matching value
|
||||
* @return error_code indicating success or failure
|
||||
* @return Vector of values matching the wildcard pattern
|
||||
*/
|
||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
||||
template <typename Func>
|
||||
requires std::invocable<Func, value>
|
||||
#else
|
||||
template <typename Func>
|
||||
#endif
|
||||
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
|
||||
simdjson_inline simdjson_result<std::vector<value>> at_path_with_wildcard(std::string_view json_path) noexcept;
|
||||
|
||||
protected:
|
||||
/**
|
||||
@@ -905,23 +897,9 @@ public:
|
||||
simdjson_inline simdjson_result<int32_t> current_depth() const noexcept;
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_path(std::string_view json_path) noexcept;
|
||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
||||
template <typename Func>
|
||||
requires std::invocable<Func, SIMDJSON_IMPLEMENTATION::ondemand::value>
|
||||
#else
|
||||
template <typename Func>
|
||||
#endif
|
||||
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
|
||||
simdjson_inline simdjson_result<std::vector<SIMDJSON_IMPLEMENTATION::ondemand::value>> at_path_with_wildcard(std::string_view json_path) noexcept;
|
||||
};
|
||||
|
||||
// Forward-declare explicit specializations so MSVC /permissive- sees them before
|
||||
// any template instantiation that would resolve element.get(val) to the primary.
|
||||
template<> simdjson_inline error_code
|
||||
simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::get<SIMDJSON_IMPLEMENTATION::ondemand::value>(
|
||||
SIMDJSON_IMPLEMENTATION::ondemand::value &out) noexcept;
|
||||
template<> simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>
|
||||
simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::get<SIMDJSON_IMPLEMENTATION::ondemand::value>() noexcept;
|
||||
|
||||
} // namespace simdjson
|
||||
|
||||
#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_H
|
||||
|
||||
+45
-33
@@ -38,43 +38,55 @@ namespace {
|
||||
using namespace simd;
|
||||
|
||||
simdjson_inline json_character_block json_character_block::classify(const simd::simd8x64<uint8_t>& in) {
|
||||
const uint8x16_t op_table = simd8<uint8_t>(
|
||||
0xff, 0, ',', ':', 0, '[', ']', '{', '}', 0, 0, 0, 0, 0, 0, 0
|
||||
);
|
||||
const uint8x16_t ws_table = simd8<uint8_t>(
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0, 0, 0xff, 0, 0
|
||||
// Functional programming causes trouble with Visual Studio.
|
||||
// Keeping this version in comments since it is much nicer:
|
||||
// auto v = in.map<uint8_t>([&](simd8<uint8_t> chunk) {
|
||||
// auto nib_lo = chunk & 0xf;
|
||||
// auto nib_hi = chunk.shr<4>();
|
||||
// auto shuf_lo = nib_lo.lookup_16<uint8_t>(16, 0, 0, 0, 0, 0, 0, 0, 0, 8, 12, 1, 2, 9, 0, 0);
|
||||
// auto shuf_hi = nib_hi.lookup_16<uint8_t>(8, 0, 18, 4, 0, 1, 0, 1, 0, 0, 0, 3, 2, 1, 0, 0);
|
||||
// return shuf_lo & shuf_hi;
|
||||
// });
|
||||
const simd8<uint8_t> table1(16, 0, 0, 0, 0, 0, 0, 0, 0, 8, 12, 1, 2, 9, 0, 0);
|
||||
const simd8<uint8_t> table2(8, 0, 18, 4, 0, 1, 0, 1, 0, 0, 0, 3, 2, 1, 0, 0);
|
||||
|
||||
simd8x64<uint8_t> v(
|
||||
(in.chunks[0] & 0xf).lookup_16(table1) & (in.chunks[0].shr<4>()).lookup_16(table2),
|
||||
(in.chunks[1] & 0xf).lookup_16(table1) & (in.chunks[1].shr<4>()).lookup_16(table2),
|
||||
(in.chunks[2] & 0xf).lookup_16(table1) & (in.chunks[2].shr<4>()).lookup_16(table2),
|
||||
(in.chunks[3] & 0xf).lookup_16(table1) & (in.chunks[3].shr<4>()).lookup_16(table2)
|
||||
);
|
||||
|
||||
const uint8x16_t d0_0 = in.chunks[0];
|
||||
const uint8x16_t d0_1 = in.chunks[1];
|
||||
const uint8x16_t d0_2 = in.chunks[2];
|
||||
const uint8x16_t d0_3 = in.chunks[3];
|
||||
|
||||
const uint8x16_t match_op_0 = vceqq_u8(vqtbl1q_u8(op_table, vshrq_n_u8(vaddq_u8(d0_0, vdupq_n_u8(3)), 4)), d0_0);
|
||||
const uint8x16_t match_op_1 = vceqq_u8(vqtbl1q_u8(op_table, vshrq_n_u8(vaddq_u8(d0_1, vdupq_n_u8(3)), 4)), d0_1);
|
||||
const uint8x16_t match_op_2 = vceqq_u8(vqtbl1q_u8(op_table, vshrq_n_u8(vaddq_u8(d0_2, vdupq_n_u8(3)), 4)), d0_2);
|
||||
const uint8x16_t match_op_3 = vceqq_u8(vqtbl1q_u8(op_table, vshrq_n_u8(vaddq_u8(d0_3, vdupq_n_u8(3)), 4)), d0_3);
|
||||
// We compute whitespace and op separately. If the code later only use one or the
|
||||
// other, given the fact that all functions are aggressively inlined, we can
|
||||
// hope that useless computations will be omitted. This is namely case when
|
||||
// minifying (we only need whitespace). *However* if we only need spaces,
|
||||
// it is likely that we will still compute 'v' above with two lookup_16: one
|
||||
// could do it a bit cheaper. This is in contrast with the x64 implementations
|
||||
// where we can, efficiently, do the white space and structural matching
|
||||
// separately. One reason for this difference is that on ARM NEON, the table
|
||||
// lookups either zero or leave unchanged the characters exceeding 0xF whereas
|
||||
// on x64, the equivalent instruction (pshufb) automatically applies a mask,
|
||||
// ignoring the 4 most significant bits. Thus the x64 implementation is
|
||||
// optimized differently. This being said, if you use this code strictly
|
||||
// just for minification (or just to identify the structural characters),
|
||||
// there is a small untaken optimization opportunity here. We deliberately
|
||||
// do not pick it up.
|
||||
|
||||
const uint8x16_t match_ws_0 = vqtbx1q_u8(vceqq_u8(d0_0, vdupq_n_u8(' ')), ws_table, d0_0);
|
||||
const uint8x16_t match_ws_1 = vqtbx1q_u8(vceqq_u8(d0_1, vdupq_n_u8(' ')), ws_table, d0_1);
|
||||
const uint8x16_t match_ws_2 = vqtbx1q_u8(vceqq_u8(d0_2, vdupq_n_u8(' ')), ws_table, d0_2);
|
||||
const uint8x16_t match_ws_3 = vqtbx1q_u8(vceqq_u8(d0_3, vdupq_n_u8(' ')), ws_table, d0_3);
|
||||
uint64_t op = simd8x64<bool>(
|
||||
v.chunks[0].any_bits_set(0x7),
|
||||
v.chunks[1].any_bits_set(0x7),
|
||||
v.chunks[2].any_bits_set(0x7),
|
||||
v.chunks[3].any_bits_set(0x7)
|
||||
).to_bitmask();
|
||||
|
||||
const uint8x16_t bit_mask = simd8<uint8_t>(
|
||||
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80,
|
||||
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80
|
||||
);
|
||||
|
||||
uint8x16_t op_sum0 = vpaddq_u8(vandq_u8(match_op_0, bit_mask), vandq_u8(match_op_1, bit_mask));
|
||||
uint8x16_t ws_sum0 = vpaddq_u8(vandq_u8(match_ws_0, bit_mask), vandq_u8(match_ws_1, bit_mask));
|
||||
uint8x16_t op_sum1 = vpaddq_u8(vandq_u8(match_op_2, bit_mask), vandq_u8(match_op_3, bit_mask));
|
||||
uint8x16_t ws_sum1 = vpaddq_u8(vandq_u8(match_ws_2, bit_mask), vandq_u8(match_ws_3, bit_mask));
|
||||
op_sum0 = vpaddq_u8(op_sum0, op_sum1);
|
||||
ws_sum0 = vpaddq_u8(ws_sum0, ws_sum1);
|
||||
op_sum0 = vpaddq_u8(op_sum0, op_sum0);
|
||||
ws_sum0 = vpaddq_u8(ws_sum0, ws_sum0);
|
||||
const uint64_t op = vgetq_lane_u64(vreinterpretq_u64_u8(op_sum0), 0);
|
||||
const uint64_t whitespace = vgetq_lane_u64(vreinterpretq_u64_u8(ws_sum0), 0);
|
||||
uint64_t whitespace = simd8x64<bool>(
|
||||
v.chunks[0].any_bits_set(0x18),
|
||||
v.chunks[1].any_bits_set(0x18),
|
||||
v.chunks[2].any_bits_set(0x18),
|
||||
v.chunks[3].any_bits_set(0x18)
|
||||
).to_bitmask();
|
||||
|
||||
return { whitespace, op };
|
||||
}
|
||||
@@ -120,7 +132,7 @@ simdjson_warn_unused error_code implementation::minify(const uint8_t *buf, size_
|
||||
return arm64::stage1::json_minifier::minify<64>(buf, len, dst, dst_len);
|
||||
}
|
||||
|
||||
simdjson_flatten simdjson_warn_unused error_code dom_parser_implementation::stage1(const uint8_t *_buf, size_t _len, stage1_mode streaming) noexcept {
|
||||
simdjson_warn_unused error_code dom_parser_implementation::stage1(const uint8_t *_buf, size_t _len, stage1_mode streaming) noexcept {
|
||||
this->buf = _buf;
|
||||
this->len = _len;
|
||||
return arm64::stage1::json_structural_indexer::index<64>(buf, len, *this, streaming);
|
||||
|
||||
@@ -291,20 +291,7 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::visit_root_primit
|
||||
case '"': return visitor.visit_root_string(*this, value);
|
||||
case 't': return visitor.visit_root_true_atom(*this, value);
|
||||
case 'f': return visitor.visit_root_false_atom(*this, value);
|
||||
#if SIMDJSON_ENABLE_NAN_INF
|
||||
case 'n': {
|
||||
auto err = visitor.visit_root_null_atom(*this, value);
|
||||
if (err == SUCCESS) { return err; }
|
||||
// propagate the error value returned by a bad 'null' atom if parsing 'nan' fails
|
||||
return visitor.visit_root_nan_atom(*this, value, err);
|
||||
}
|
||||
// 'N' isn't a canonically recognized atom, so we return a TAPE_ERROR if failure occurs
|
||||
case 'N': return visitor.visit_root_nan_atom(*this, value, TAPE_ERROR);
|
||||
case 'i':
|
||||
case 'I': return visitor.visit_root_inf_atom(*this, value);
|
||||
#else
|
||||
case 'n': return visitor.visit_root_null_atom(*this, value);
|
||||
#endif
|
||||
case '-':
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
@@ -326,20 +313,7 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::visit_primitive(V
|
||||
switch (*value) {
|
||||
case 't': return visitor.visit_true_atom(*this, value);
|
||||
case 'f': return visitor.visit_false_atom(*this, value);
|
||||
#if SIMDJSON_ENABLE_NAN_INF
|
||||
case 'n': {
|
||||
auto err = visitor.visit_null_atom(*this, value);
|
||||
if (err == SUCCESS) { return err; }
|
||||
// propagate the error value returned by a bad 'null' atom if parsing 'nan' fails
|
||||
return visitor.visit_nan_atom(*this, value, err);
|
||||
}
|
||||
// 'N' isn't a canonically recognized atom, so we return a TAPE_ERROR if failure occurs
|
||||
case 'N': return visitor.visit_nan_atom(*this, value, TAPE_ERROR);
|
||||
case 'i':
|
||||
case 'I': return visitor.visit_inf_atom(*this, value);
|
||||
#else
|
||||
case 'n': return visitor.visit_null_atom(*this, value);
|
||||
#endif
|
||||
default:
|
||||
log_error("Non-value found when value was expected!");
|
||||
return TAPE_ERROR;
|
||||
@@ -351,4 +325,4 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::visit_primitive(V
|
||||
} // namespace SIMDJSON_IMPLEMENTATION
|
||||
} // namespace simdjson
|
||||
|
||||
#endif // SIMDJSON_SRC_GENERIC_STAGE2_JSON_ITERATOR_H
|
||||
#endif // SIMDJSON_SRC_GENERIC_STAGE2_JSON_ITERATOR_H
|
||||
@@ -76,15 +76,6 @@ struct tape_builder {
|
||||
simdjson_warn_unused simdjson_inline error_code visit_root_false_atom(json_iterator &iter, const uint8_t *value) noexcept;
|
||||
simdjson_warn_unused simdjson_inline error_code visit_root_null_atom(json_iterator &iter, const uint8_t *value) noexcept;
|
||||
|
||||
#if SIMDJSON_ENABLE_NAN_INF
|
||||
simdjson_warn_unused simdjson_inline error_code visit_nan_atom(json_iterator &iter, const uint8_t *value, error_code errc) noexcept;
|
||||
simdjson_warn_unused simdjson_inline error_code visit_root_nan_atom(json_iterator &iter, const uint8_t *value, error_code errc) noexcept;
|
||||
// Attempts to parse 'inf' or 'infinity' (case insensitive). Because neither are canonical atoms,
|
||||
// this returns a tape error on failure.
|
||||
simdjson_warn_unused simdjson_inline error_code visit_inf_atom(json_iterator &iter, const uint8_t *value) noexcept;
|
||||
simdjson_warn_unused simdjson_inline error_code visit_root_inf_atom(json_iterator &iter, const uint8_t *value) noexcept;
|
||||
#endif
|
||||
|
||||
/** Called each time a new field or element in an array or object is found. */
|
||||
simdjson_warn_unused simdjson_inline error_code increment_count(json_iterator &iter) noexcept;
|
||||
|
||||
@@ -264,38 +255,6 @@ simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_null_at
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
#if SIMDJSON_ENABLE_NAN_INF
|
||||
simdjson_warn_unused simdjson_inline error_code tape_builder::visit_nan_atom(json_iterator &iter, const uint8_t *value, error_code errc) noexcept {
|
||||
iter.log_value("nan");
|
||||
if (!atomparsing::is_valid_nan_atom(value)) { return errc; }
|
||||
tape.append_double(std::numeric_limits<double>::quiet_NaN());
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_nan_atom(json_iterator &iter, const uint8_t *value, error_code errc) noexcept {
|
||||
iter.log_value("nan");
|
||||
if (!atomparsing::is_valid_nan_atom(value, iter.remaining_len())) { return errc; }
|
||||
tape.append_double(std::numeric_limits<double>::quiet_NaN());
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
simdjson_warn_unused simdjson_inline error_code tape_builder::visit_inf_atom(json_iterator &iter, const uint8_t *value) noexcept {
|
||||
iter.log_value("inf");
|
||||
// Because 'inf' is an extension, non a canonical atom, a tape error should be returned on failure
|
||||
if (!atomparsing::is_valid_inf_atom(value)) { return TAPE_ERROR; }
|
||||
tape.append_double(std::numeric_limits<double>::infinity());
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_inf_atom(json_iterator &iter, const uint8_t *value) noexcept {
|
||||
iter.log_value("inf");
|
||||
// Because 'inf' is an extension, non a canonical atom, a tape error should be returned on failure
|
||||
if (!atomparsing::is_valid_inf_atom(value, iter.remaining_len())) { return TAPE_ERROR; }
|
||||
tape.append_double(std::numeric_limits<double>::infinity());
|
||||
return SUCCESS;
|
||||
}
|
||||
#endif // SIMDJSON_ENABLE_NAN_INF
|
||||
|
||||
// private:
|
||||
|
||||
simdjson_inline uint32_t tape_builder::next_tape_index(json_iterator &iter) const noexcept {
|
||||
@@ -351,4 +310,4 @@ simdjson_inline void tape_builder::on_end_string(uint8_t *dst) noexcept {
|
||||
} // namespace SIMDJSON_IMPLEMENTATION
|
||||
} // namespace simdjson
|
||||
|
||||
#endif // SIMDJSON_SRC_GENERIC_STAGE2_TAPE_BUILDER_H
|
||||
#endif // SIMDJSON_SRC_GENERIC_STAGE2_TAPE_BUILDER_H
|
||||
@@ -52,17 +52,13 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||
#include <cstdlib>
|
||||
#if defined(_MSC_VER)
|
||||
#include <intrin.h>
|
||||
#elif (defined(HAVE_GCC_GET_CPUID) && defined(USE_GCC_GET_CPUID)) || defined(__FILC__)
|
||||
#elif defined(HAVE_GCC_GET_CPUID) && defined(USE_GCC_GET_CPUID)
|
||||
#include <cpuid.h>
|
||||
#endif
|
||||
#if defined(__loongarch__) && defined(__linux__)
|
||||
#include <sys/auxv.h>
|
||||
#endif
|
||||
|
||||
#ifdef __FILC__
|
||||
#include <stdfil.h>
|
||||
#endif
|
||||
|
||||
namespace simdjson {
|
||||
namespace internal {
|
||||
|
||||
@@ -113,7 +109,7 @@ static inline void cpuid(uint32_t *eax, uint32_t *ebx, uint32_t *ecx,
|
||||
*ebx = cpu_info[1];
|
||||
*ecx = cpu_info[2];
|
||||
*edx = cpu_info[3];
|
||||
#elif (defined(HAVE_GCC_GET_CPUID) && defined(USE_GCC_GET_CPUID)) || defined(__FILC__)
|
||||
#elif defined(HAVE_GCC_GET_CPUID) && defined(USE_GCC_GET_CPUID)
|
||||
uint32_t level = *eax;
|
||||
__get_cpuid(level, eax, ebx, ecx, edx);
|
||||
#else
|
||||
@@ -130,8 +126,6 @@ static inline void cpuid(uint32_t *eax, uint32_t *ebx, uint32_t *ecx,
|
||||
static inline uint64_t xgetbv() {
|
||||
#if defined(_MSC_VER)
|
||||
return _xgetbv(0);
|
||||
#elif defined(__FILC__)
|
||||
return zxgetbv();
|
||||
#else
|
||||
uint32_t xcr0_lo, xcr0_hi;
|
||||
asm volatile("xgetbv\n\t" : "=a" (xcr0_lo), "=d" (xcr0_hi) : "c" (0));
|
||||
|
||||
@@ -13,7 +13,6 @@ add_cpp_test(errortests LABELS dom acceptance per_implementation
|
||||
add_cpp_test(extracting_values_example LABELS dom acceptance per_implementation)
|
||||
add_cpp_test(integer_tests LABELS dom acceptance per_implementation)
|
||||
add_cpp_test(big_integer_tests LABELS dom acceptance per_implementation)
|
||||
add_cpp_test(nan_inf_tests LABELS dom acceptance per_implementation)
|
||||
add_cpp_test(jsoncheck LABELS dom acceptance per_implementation)
|
||||
add_cpp_test(json_path_tests LABELS dom acceptance per_implementation)
|
||||
add_cpp_test(minefieldcheck LABELS dom acceptance per_implementation)
|
||||
@@ -128,16 +127,14 @@ endif()
|
||||
# 1. Visual Studio 2022 v17.6 or later
|
||||
# 2. GCC v14.0.0 or later (GCC v13.0.0 cannot handle pipe operator of lambda)
|
||||
# 3. Clang v15.0.0 or later (certain version C++ headers occur error when compiling)
|
||||
# 4. or if we are targeting C++20 or better
|
||||
if(
|
||||
(MSVC AND MSVC_VERSION GREATER_EQUAL 1930) OR
|
||||
(MSVC AND MSVC_VERSION LESS 1930) OR
|
||||
(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "14.0.0") OR
|
||||
(CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "15.0.0") OR
|
||||
(CMAKE_CXX_STANDARD GREATER_EQUAL 20)
|
||||
(CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "15.0.0")
|
||||
)
|
||||
message(STATUS "compiler id: ${CMAKE_CXX_COMPILER_ID} version: ${CMAKE_CXX_COMPILER_VERSION}")
|
||||
add_cpp_test(ranges_test LABELS dom acceptance per_implementation)
|
||||
if(NOT SIMDJSON_STATIC_REFLECTION AND NOT CMAKE_CXX_STANDARD GREATER_EQUAL 20)
|
||||
if(NOT SIMDJSON_STATIC_REFLECTION)
|
||||
set_target_properties(ranges_test PROPERTIES CXX_STANDARD 20 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -270,25 +270,6 @@ namespace document_stream_tests {
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool issue_non_ascii_separator_source() {
|
||||
TEST_START();
|
||||
std::string bytes = "1 ";
|
||||
bytes.push_back(char(0xFF));
|
||||
bytes += " 2";
|
||||
simdjson::padded_string json(bytes);
|
||||
|
||||
simdjson::dom::parser parser;
|
||||
simdjson::dom::document_stream stream;
|
||||
ASSERT_SUCCESS(parser.parse_many(json).get(stream));
|
||||
|
||||
auto i = stream.begin();
|
||||
ASSERT_TRUE(i != stream.end());
|
||||
std::string_view source = i.source();
|
||||
ASSERT_TRUE(!source.empty());
|
||||
ASSERT_EQUAL(source.front(), '1');
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
bool issue1310() {
|
||||
std::cout << "Running " << __func__ << std::endl;
|
||||
// hex : 20 20 5B 20 33 2C 31 5D 20 22 22 22 22 22 22 22 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
|
||||
@@ -1771,36 +1752,41 @@ namespace document_stream_tests {
|
||||
// Note on source(): the comma_delimited filter strips root-level
|
||||
// commas from structural_indexes but the bytes remain in the buffer.
|
||||
// source() for a scalar slices [current_index, next_doc_index) and
|
||||
// strips trailing whitespace; trailing delimiter is stripped for
|
||||
// consistency with json_sequence. The assertions below lock down
|
||||
// current_index() and parsed values strictly, and source() returns the
|
||||
// JSON value without trailing delimiter.
|
||||
// strips trailing whitespace, so it currently includes the trailing
|
||||
// separator comma (e.g. doc 0 source = "1,"). This is the analogous
|
||||
// bug to the json_sequence RS-trailing source bug. The assertions
|
||||
// below lock down current_index() and parsed values strictly, and
|
||||
// accept source() either with or without a single trailing comma.
|
||||
auto input = R"(1,2,"x",true,null)"_padded;
|
||||
ASSERT_SUCCESS(parser.parse_many(input, simdjson::dom::DEFAULT_BATCH_SIZE, simdjson::stream_format::comma_delimited).get(stream));
|
||||
auto it = stream.begin();
|
||||
|
||||
auto src_matches = [](std::string_view src, std::string_view want) {
|
||||
return src == want || src == std::string(want) + ",";
|
||||
};
|
||||
|
||||
// doc 0: 1
|
||||
ASSERT_EQUAL(it.current_index(), size_t(0));
|
||||
int64_t i1; ASSERT_SUCCESS((*it).get(i1)); ASSERT_EQUAL(i1, int64_t(1));
|
||||
ASSERT_EQUAL(it.source(), std::string_view("1"));
|
||||
ASSERT_TRUE(src_matches(it.source(), "1"));
|
||||
++it;
|
||||
|
||||
// doc 1: 2
|
||||
ASSERT_EQUAL(it.current_index(), size_t(2));
|
||||
int64_t i2; ASSERT_SUCCESS((*it).get(i2)); ASSERT_EQUAL(i2, int64_t(2));
|
||||
ASSERT_EQUAL(it.source(), std::string_view("2"));
|
||||
ASSERT_TRUE(src_matches(it.source(), "2"));
|
||||
++it;
|
||||
|
||||
// doc 2: "x"
|
||||
ASSERT_EQUAL(it.current_index(), size_t(4));
|
||||
std::string_view sv; ASSERT_SUCCESS((*it).get(sv)); ASSERT_EQUAL(sv, std::string_view("x"));
|
||||
ASSERT_EQUAL(it.source(), std::string_view("\"x\""));
|
||||
ASSERT_TRUE(src_matches(it.source(), "\"x\""));
|
||||
++it;
|
||||
|
||||
// doc 3: true
|
||||
ASSERT_EQUAL(it.current_index(), size_t(8));
|
||||
bool b{}; ASSERT_SUCCESS((*it).get(b)); ASSERT_TRUE(b);
|
||||
ASSERT_EQUAL(it.source(), std::string_view("true"));
|
||||
ASSERT_TRUE(src_matches(it.source(), "true"));
|
||||
++it;
|
||||
|
||||
// doc 4: null (last doc, no trailing comma in source either way)
|
||||
@@ -2111,7 +2097,6 @@ namespace document_stream_tests {
|
||||
comma_delimited_tests() &&
|
||||
issue2181() &&
|
||||
issue2170() &&
|
||||
issue_non_ascii_separator_source() &&
|
||||
skipbom() &&
|
||||
fuzzaccess() &&
|
||||
baby_fuzzer() &&
|
||||
|
||||
@@ -60,26 +60,6 @@ bool validate_minefield(const char *dirname) {
|
||||
char *fullpath = static_cast<char *>(malloc(fullpathlen));
|
||||
snprintf(fullpath, fullpathlen, "%s%s%s", dirname, needsep ? "/" : "", name);
|
||||
|
||||
// Skip any files that have invalid names
|
||||
if (namelen < 2 || name[1] != '_') {
|
||||
printf("warning: file %s should begin with 'y_', 'n_', or 'i_' (skipping)\n", name);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Determines if the file should pass.
|
||||
// 'y' for 'expected to pass', 'n' for 'expected to fail', 'i' for 'ignore'
|
||||
char should_pass = name[0];
|
||||
|
||||
#if SIMDJSON_ENABLE_NAN_INF
|
||||
// If nan/infinity are enabled, these files should pass (rather than failing).
|
||||
//
|
||||
// Therefore, we mark them as 'should_pass'
|
||||
bool is_nan_inf_test = contains("NaN", name) || contains("_Inf", name) || contains("_infinity", name);
|
||||
if (is_nan_inf_test && should_pass == 'n') {
|
||||
should_pass = 'y';
|
||||
}
|
||||
#endif
|
||||
|
||||
simdjson::padded_string p;
|
||||
auto error = simdjson::padded_string::load(fullpath).get(p);
|
||||
if (error) {
|
||||
@@ -92,16 +72,16 @@ bool validate_minefield(const char *dirname) {
|
||||
auto errorcode = parser.parse(p).error();
|
||||
++how_many;
|
||||
printf("%s\n", errorcode == simdjson::error_code::SUCCESS ? "ok" : "invalid");
|
||||
if (should_pass == 'i') {
|
||||
if (starts_with("i_", name) ) {
|
||||
// skipping
|
||||
how_many--;
|
||||
} else if (should_pass == 'y' && errorcode != simdjson::error_code::SUCCESS) {
|
||||
} else if (starts_with("y_", name) && errorcode != simdjson::error_code::SUCCESS) {
|
||||
is_file_as_expected[i] = false;
|
||||
printf("warning: file %s should pass but it fails. Error is: %s\n",
|
||||
name, simdjson::error_message(errorcode));
|
||||
printf("size of file in bytes: %zu \n", p.size());
|
||||
everything_fine = false;
|
||||
} else if (should_pass == 'n' && errorcode == simdjson::error_code::SUCCESS) {
|
||||
} else if (starts_with("n_", name) && errorcode == simdjson::error_code::SUCCESS) {
|
||||
is_file_as_expected[i] = false;
|
||||
printf("warning: file %s should fail but it passes.\n", name);
|
||||
printf("size of file in bytes: %zu \n", p.size());
|
||||
|
||||
@@ -1,311 +0,0 @@
|
||||
#include "simdjson.h"
|
||||
#include "test_macros.h"
|
||||
#include "test_main.h"
|
||||
#include <cmath>
|
||||
#include <string>
|
||||
|
||||
using namespace simdjson;
|
||||
|
||||
namespace nan_inf_tests {
|
||||
#if SIMDJSON_ENABLE_NAN_INF
|
||||
|
||||
bool parse_nan() {
|
||||
TEST_START();
|
||||
for (auto json_str : {"NaN", "nan", "NAN"}) {
|
||||
dom::parser parser;
|
||||
dom::element doc;
|
||||
ASSERT_SUCCESS(
|
||||
parser.parse(padded_string(json_str, strlen(json_str))).get(doc));
|
||||
double value;
|
||||
ASSERT_SUCCESS(doc.get_double().get(value));
|
||||
ASSERT_TRUE(std::isnan(value));
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool parse_infinity() {
|
||||
TEST_START();
|
||||
for (auto json_str :
|
||||
{"infinity", "Infinity", "INFINITY", "inf", "Inf", "INF"}) {
|
||||
dom::parser parser;
|
||||
dom::element doc;
|
||||
ASSERT_SUCCESS(
|
||||
parser.parse(padded_string(json_str, strlen(json_str))).get(doc));
|
||||
double value;
|
||||
ASSERT_SUCCESS(doc.get_double().get(value));
|
||||
ASSERT_TRUE(std::isinf(value));
|
||||
ASSERT_TRUE(value > 0);
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool parse_negative_infinity() {
|
||||
TEST_START();
|
||||
for (auto json_str :
|
||||
{"-infinity", "-Infinity", "-INFINITY", "-inf", "-Inf", "-INF"}) {
|
||||
dom::parser parser;
|
||||
dom::element doc;
|
||||
ASSERT_SUCCESS(
|
||||
parser.parse(padded_string(json_str, strlen(json_str))).get(doc));
|
||||
double value;
|
||||
ASSERT_SUCCESS(doc.get_double().get(value));
|
||||
ASSERT_TRUE(std::isinf(value));
|
||||
ASSERT_TRUE(value < 0);
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool nan_in_array() {
|
||||
TEST_START();
|
||||
dom::parser parser;
|
||||
dom::element doc;
|
||||
auto json = R"([5, NaN, NAN, nan, -nan, -NAN, -NaN, 1.25])"_padded;
|
||||
ASSERT_SUCCESS(parser.parse(json).get(doc));
|
||||
dom::array arr;
|
||||
ASSERT_SUCCESS(doc.get_array().get(arr));
|
||||
|
||||
double nan = std::numeric_limits<double>::quiet_NaN();
|
||||
std::array<double, 8> expected_values{5, nan, nan, nan, nan, nan, nan, 1.25};
|
||||
|
||||
size_t index = 0;
|
||||
for (auto val : arr) {
|
||||
if (index == expected_values.size()) {
|
||||
TEST_FAIL("Array contained more values than expected");
|
||||
}
|
||||
|
||||
double parsed;
|
||||
ASSERT_SUCCESS(val.get_double().get(parsed));
|
||||
double expected = expected_values[index];
|
||||
if (std::isnan(expected)) {
|
||||
ASSERT_TRUE(std::isnan(parsed))
|
||||
} else {
|
||||
ASSERT_EQUAL(parsed, expected_values[index]);
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
ASSERT_EQUAL(index, expected_values.size());
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool infinity_in_array() {
|
||||
TEST_START();
|
||||
dom::parser parser;
|
||||
auto json = R"([1,
|
||||
infinity,
|
||||
INFINITY,
|
||||
Infinity,
|
||||
inf,
|
||||
Inf,
|
||||
INF,
|
||||
-infinity,
|
||||
-INFINITY,
|
||||
-Infinity,
|
||||
-inf,
|
||||
-Inf,
|
||||
-INF,
|
||||
6.5])"_padded;
|
||||
dom::element doc;
|
||||
ASSERT_SUCCESS(parser.parse(json).get(doc));
|
||||
dom::array arr;
|
||||
ASSERT_SUCCESS(doc.get_array().get(arr));
|
||||
|
||||
double inf = std::numeric_limits<double>::infinity();
|
||||
std::array<double, 14> expected_values{
|
||||
1, inf, inf, inf, inf, inf, inf, -inf, -inf, -inf, -inf, -inf, -inf, 6.5};
|
||||
|
||||
size_t index = 0;
|
||||
for (auto val : arr) {
|
||||
if (index == expected_values.size()) {
|
||||
TEST_FAIL("Array contained more values than expected");
|
||||
}
|
||||
|
||||
double parsed;
|
||||
ASSERT_SUCCESS(val.get_double().get(parsed));
|
||||
ASSERT_EQUAL(parsed, expected_values[index]);
|
||||
index++;
|
||||
}
|
||||
|
||||
ASSERT_EQUAL(index, expected_values.size());
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool nan_in_object() {
|
||||
TEST_START();
|
||||
dom::parser parser;
|
||||
dom::element doc;
|
||||
ASSERT_SUCCESS(parser.parse(R"({"a": NaN, "b": nan})"_padded).get(doc));
|
||||
double a;
|
||||
ASSERT_SUCCESS(doc["a"].get_double().get(a));
|
||||
ASSERT_TRUE(std::isnan(a));
|
||||
double b;
|
||||
ASSERT_SUCCESS(doc["b"].get_double().get(b));
|
||||
ASSERT_TRUE(std::isnan(b));
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool infinity_in_object() {
|
||||
TEST_START();
|
||||
dom::parser parser;
|
||||
dom::element doc;
|
||||
ASSERT_SUCCESS(parser.parse(R"({"a": Infinity, "b": -inf})"_padded).get(doc));
|
||||
double a;
|
||||
ASSERT_SUCCESS(doc["a"].get_double().get(a));
|
||||
ASSERT_TRUE(std::isinf(a));
|
||||
ASSERT_TRUE(a > 0);
|
||||
double b;
|
||||
ASSERT_SUCCESS(doc["b"].get_double().get(b));
|
||||
ASSERT_TRUE(std::isinf(b));
|
||||
ASSERT_TRUE(b < 0);
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
// Bad 'Infinity' atoms should yield TAPE_ERROR (extension, not canonical).
|
||||
// Bad 'NaN' atoms (capital 'N') should yield TAPE_ERROR.
|
||||
// Bad 'nan' atoms (lowercase 'n') should yield N_ATOM_ERROR (shares the
|
||||
// 'null' dispatch, so a bad 'nan' reports the same error as bad 'null').
|
||||
// Bad negative atoms (any case) should yield NUMBER_ERROR: a leading '-'
|
||||
// routes through parse_number, which falls back to compute_nan_inf and
|
||||
// returns NUMBER_ERROR if that fails.
|
||||
//
|
||||
// Each reject_* test runs the cases in three contexts: at the document root
|
||||
// (visit_root_primitive), inside an array, and inside an object
|
||||
// (visit_primitive). Both dispatch paths must agree on error codes.
|
||||
|
||||
padded_string wrap_in_array(const char *atom) {
|
||||
return padded_string(std::string("[") + atom + "]");
|
||||
}
|
||||
|
||||
padded_string wrap_in_object(const char *atom) {
|
||||
return padded_string(std::string("{\"key\": ") + atom + "}");
|
||||
}
|
||||
|
||||
bool reject_trailing_junk() {
|
||||
TEST_START();
|
||||
struct {
|
||||
const char *json;
|
||||
error_code expected;
|
||||
} cases[] = {
|
||||
{"NaNa", TAPE_ERROR}, {"NaN1", TAPE_ERROR},
|
||||
{"nana", N_ATOM_ERROR}, {"InfX", TAPE_ERROR},
|
||||
{"Inf_", TAPE_ERROR}, {"Infinityy", TAPE_ERROR},
|
||||
{"InfinityX", TAPE_ERROR}, {"-NaNa", NUMBER_ERROR},
|
||||
{"-nana", NUMBER_ERROR}, {"-InfX", NUMBER_ERROR},
|
||||
{"-Infinityy", NUMBER_ERROR},
|
||||
};
|
||||
for (auto c : cases) {
|
||||
dom::parser parser;
|
||||
dom::element doc;
|
||||
auto err = parser.parse(padded_string(c.json, strlen(c.json))).get(doc);
|
||||
ASSERT_ERROR(err, c.expected);
|
||||
}
|
||||
for (auto c : cases) {
|
||||
dom::parser parser;
|
||||
dom::element doc;
|
||||
auto err = parser.parse(wrap_in_array(c.json)).get(doc);
|
||||
ASSERT_ERROR(err, c.expected);
|
||||
}
|
||||
for (auto c : cases) {
|
||||
dom::parser parser;
|
||||
dom::element doc;
|
||||
auto err = parser.parse(wrap_in_object(c.json)).get(doc);
|
||||
ASSERT_ERROR(err, c.expected);
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool reject_similar_prefix() {
|
||||
TEST_START();
|
||||
struct {
|
||||
const char *json;
|
||||
error_code expected;
|
||||
} cases[] = {
|
||||
{"Nope", TAPE_ERROR}, {"Napalm", TAPE_ERROR},
|
||||
{"nope", N_ATOM_ERROR}, {"Infant", TAPE_ERROR},
|
||||
{"Inform", TAPE_ERROR}, {"Information", TAPE_ERROR},
|
||||
{"-Nope", NUMBER_ERROR}, {"-nope", NUMBER_ERROR},
|
||||
{"-Infant", NUMBER_ERROR}, {"-Information", NUMBER_ERROR},
|
||||
};
|
||||
for (auto c : cases) {
|
||||
dom::parser parser;
|
||||
dom::element doc;
|
||||
auto err = parser.parse(padded_string(c.json, strlen(c.json))).get(doc);
|
||||
ASSERT_ERROR(err, c.expected);
|
||||
}
|
||||
for (auto c : cases) {
|
||||
dom::parser parser;
|
||||
dom::element doc;
|
||||
auto err = parser.parse(wrap_in_array(c.json)).get(doc);
|
||||
ASSERT_ERROR(err, c.expected);
|
||||
}
|
||||
for (auto c : cases) {
|
||||
dom::parser parser;
|
||||
dom::element doc;
|
||||
auto err = parser.parse(wrap_in_object(c.json)).get(doc);
|
||||
ASSERT_ERROR(err, c.expected);
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool reject_truncated_atoms() {
|
||||
TEST_START();
|
||||
struct {
|
||||
const char *json;
|
||||
error_code expected;
|
||||
} cases[] = {
|
||||
{"N", TAPE_ERROR}, {"Na", TAPE_ERROR}, {"na", N_ATOM_ERROR},
|
||||
{"I", TAPE_ERROR}, {"In", TAPE_ERROR}, {"Infinit", TAPE_ERROR},
|
||||
{"-N", NUMBER_ERROR}, {"-Na", NUMBER_ERROR}, {"-na", NUMBER_ERROR},
|
||||
{"-I", NUMBER_ERROR}, {"-In", NUMBER_ERROR}, {"-Infinit", NUMBER_ERROR},
|
||||
};
|
||||
for (auto c : cases) {
|
||||
dom::parser parser;
|
||||
dom::element doc;
|
||||
auto err = parser.parse(padded_string(c.json, strlen(c.json))).get(doc);
|
||||
ASSERT_ERROR(err, c.expected);
|
||||
}
|
||||
for (auto c : cases) {
|
||||
dom::parser parser;
|
||||
dom::element doc;
|
||||
auto err = parser.parse(wrap_in_array(c.json)).get(doc);
|
||||
ASSERT_ERROR(err, c.expected);
|
||||
}
|
||||
for (auto c : cases) {
|
||||
dom::parser parser;
|
||||
dom::element doc;
|
||||
auto err = parser.parse(wrap_in_object(c.json)).get(doc);
|
||||
ASSERT_ERROR(err, c.expected);
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool run() {
|
||||
return parse_nan() //
|
||||
&& parse_infinity() //
|
||||
&& parse_negative_infinity() //
|
||||
&& nan_in_array() //
|
||||
&& infinity_in_array() //
|
||||
&& nan_in_object() //
|
||||
&& infinity_in_object() //
|
||||
&& reject_trailing_junk() //
|
||||
&& reject_similar_prefix() //
|
||||
&& reject_truncated_atoms() //
|
||||
;
|
||||
}
|
||||
|
||||
#else // !SIMDJSON_ENABLE_NAN_INF
|
||||
|
||||
bool run() {
|
||||
std::cout << "NaN/Infinity parsing is disabled (SIMDJSON_ENABLE_NAN_INF=0), "
|
||||
"skipping tests."
|
||||
<< std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // SIMDJSON_ENABLE_NAN_INF
|
||||
} // namespace nan_inf_tests
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
return test_main(argc, argv, nan_inf_tests::run);
|
||||
}
|
||||
@@ -22,10 +22,10 @@ add_cpp_test(compile_time_json_pointer_tests LABELS ondemand acceptance
|
||||
add_cpp_test(compile_time_no_validation_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_key_string_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_misc_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_nan_inf_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_number_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_number_in_string_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_object_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_object_find_field_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_object_error_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_ordering_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_parse_api_tests LABELS ondemand acceptance per_implementation)
|
||||
@@ -46,25 +46,6 @@ if(NOT SIMDJSON_SANITIZE)
|
||||
add_cpp_test(ondemand_cacheline LABELS ondemand acceptance per_implementation)
|
||||
endif()
|
||||
|
||||
# Add the tests if we're on:
|
||||
# 1. Visual Studio 2022 v17.6 or later
|
||||
# 2. GCC v14.0.0 or later (GCC v13.0.0 cannot handle pipe operator of lambda)
|
||||
# 3. Clang v15.0.0 or later (certain version C++ headers occur error when compiling)
|
||||
# 4. or if we are targeting C++20 or better
|
||||
if(
|
||||
(MSVC AND MSVC_VERSION GREATER_EQUAL 1930) OR
|
||||
(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "14.0.0") OR
|
||||
(CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "15.0.0") OR
|
||||
(CMAKE_CXX_STANDARD GREATER_EQUAL 20)
|
||||
)
|
||||
message(STATUS "compiler id: ${CMAKE_CXX_COMPILER_ID} version: ${CMAKE_CXX_COMPILER_VERSION}")
|
||||
add_cpp_test(ondemand_ranges_tests LABELS ondemand acceptance per_implementation)
|
||||
if(NOT SIMDJSON_STATIC_REFLECTION AND NOT CMAKE_CXX_STANDARD GREATER_EQUAL 20)
|
||||
set_target_properties(ondemand_ranges_tests PROPERTIES CXX_STANDARD 20 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
if(HAVE_POSIX_FORK AND HAVE_POSIX_WAIT) # assert tests use fork and wait, which aren't on MSVC
|
||||
add_cpp_test(ondemand_assert_out_of_order_values LABELS assert per_implementation explicitonly ondemand)
|
||||
endif()
|
||||
|
||||
@@ -1,12 +1,3 @@
|
||||
#ifdef __FILC__
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
int main() {
|
||||
printf("This test is not relevant for FILC.\n");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
#else // This test is not relevant for FILC
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <sysinfoapi.h>
|
||||
@@ -95,6 +86,4 @@ int main() {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
#endif // This test is not relevant for FILC
|
||||
}
|
||||
@@ -230,7 +230,7 @@ simdjson::padded_string json_cars =
|
||||
.location = "Denver",
|
||||
.is_recurring = true
|
||||
});
|
||||
Meeting m2 = simdjson::from(simdjson::pad(json));
|
||||
Meeting m2 = simdjson::from(json);
|
||||
std::cout << m2.title << std::endl;
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
@@ -245,7 +245,7 @@ simdjson::padded_string json_cars =
|
||||
.is_recurring = true
|
||||
});
|
||||
std::cout << json << std::endl;
|
||||
MeetingTime m2 = simdjson::from(simdjson::pad(json));
|
||||
MeetingTime m2 = simdjson::from(json);
|
||||
//ASSERT_EQUAL(m2.start_time, start_time);
|
||||
std::cout << m2.title << std::endl;
|
||||
TEST_SUCCEED();
|
||||
@@ -523,4 +523,4 @@ int main(int argc, char *argv[]) {
|
||||
}
|
||||
#else
|
||||
int main() { return 0; }
|
||||
#endif
|
||||
#endif
|
||||
@@ -238,25 +238,6 @@ namespace document_stream_tests {
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool issue_non_ascii_separator_source() {
|
||||
TEST_START();
|
||||
std::string bytes = "1 ";
|
||||
bytes.push_back(char(0xFF));
|
||||
bytes += " 2";
|
||||
simdjson::padded_string json(bytes);
|
||||
|
||||
ondemand::parser parser;
|
||||
ondemand::document_stream stream;
|
||||
ASSERT_SUCCESS(parser.iterate_many(json).get(stream));
|
||||
|
||||
auto i = stream.begin();
|
||||
ASSERT_TRUE(i != stream.end());
|
||||
std::string_view source = i.source();
|
||||
ASSERT_TRUE(!source.empty());
|
||||
ASSERT_EQUAL(source.front(), '1');
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool issue1977() {
|
||||
TEST_START();
|
||||
std::string json = R"( 1111 })";
|
||||
@@ -1829,18 +1810,22 @@ namespace document_stream_tests {
|
||||
// Note on source(): the comma_delimited filter strips root-level
|
||||
// commas from structural_indexes but the bytes remain in the
|
||||
// buffer. source() for a scalar slices [current_index,
|
||||
// next_doc_index) and strips trailing whitespace; trailing
|
||||
// delimiter is stripped for consistency with json_sequence. The
|
||||
// assertions below lock down current_index() and parsed values
|
||||
// strictly, and source() returns the JSON value without trailing
|
||||
// delimiter.
|
||||
// next_doc_index) and strips trailing whitespace, so it
|
||||
// currently includes the trailing separator comma (e.g. doc 0
|
||||
// source = "1,"). The assertions below lock down current_index()
|
||||
// and parsed values strictly, and accept source() either with
|
||||
// or without a single trailing comma.
|
||||
auto input = R"(1,2,"x",true,null)"_padded;
|
||||
ASSERT_SUCCESS(parser.iterate_many(input, ondemand::DEFAULT_BATCH_SIZE, simdjson::stream_format::comma_delimited).get(stream));
|
||||
auto it = stream.begin();
|
||||
|
||||
auto src_matches = [](std::string_view src, std::string_view want) {
|
||||
return src == want || src == std::string(want) + ",";
|
||||
};
|
||||
|
||||
// doc 0: 1
|
||||
ASSERT_EQUAL(it.current_index(), size_t(0));
|
||||
ASSERT_EQUAL(it.source(), std::string_view("1"));
|
||||
ASSERT_TRUE(src_matches(it.source(), "1"));
|
||||
{
|
||||
ondemand::document_reference doc;
|
||||
ASSERT_SUCCESS((*it).get(doc));
|
||||
@@ -1851,7 +1836,7 @@ namespace document_stream_tests {
|
||||
|
||||
// doc 1: 2
|
||||
ASSERT_EQUAL(it.current_index(), size_t(2));
|
||||
ASSERT_EQUAL(it.source(), std::string_view("2"));
|
||||
ASSERT_TRUE(src_matches(it.source(), "2"));
|
||||
{
|
||||
ondemand::document_reference doc;
|
||||
ASSERT_SUCCESS((*it).get(doc));
|
||||
@@ -1862,7 +1847,7 @@ namespace document_stream_tests {
|
||||
|
||||
// doc 2: "x"
|
||||
ASSERT_EQUAL(it.current_index(), size_t(4));
|
||||
ASSERT_EQUAL(it.source(), std::string_view("\"x\""));
|
||||
ASSERT_TRUE(src_matches(it.source(), "\"x\""));
|
||||
{
|
||||
ondemand::document_reference doc;
|
||||
ASSERT_SUCCESS((*it).get(doc));
|
||||
@@ -1873,7 +1858,7 @@ namespace document_stream_tests {
|
||||
|
||||
// doc 3: true
|
||||
ASSERT_EQUAL(it.current_index(), size_t(8));
|
||||
ASSERT_EQUAL(it.source(), std::string_view("true"));
|
||||
ASSERT_TRUE(src_matches(it.source(), "true"));
|
||||
{
|
||||
ondemand::document_reference doc;
|
||||
ASSERT_SUCCESS((*it).get(doc));
|
||||
@@ -2360,7 +2345,6 @@ namespace document_stream_tests {
|
||||
comma_delimited_tests() &&
|
||||
issue2181() &&
|
||||
issue2170() &&
|
||||
issue_non_ascii_separator_source() &&
|
||||
issue2137() &&
|
||||
skipbom() &&
|
||||
issue1977() &&
|
||||
|
||||
@@ -1,352 +0,0 @@
|
||||
#include "simdjson.h"
|
||||
#include "test_ondemand.h"
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
|
||||
using namespace simdjson;
|
||||
|
||||
namespace nan_inf_tests {
|
||||
#if SIMDJSON_ENABLE_NAN_INF
|
||||
|
||||
bool parse_nan() {
|
||||
TEST_START();
|
||||
for (auto json_str : {"NaN", "nan", "-nan", "-NAN"}) {
|
||||
ondemand::parser parser;
|
||||
padded_string json(json_str, strlen(json_str));
|
||||
ondemand::document doc;
|
||||
ASSERT_SUCCESS(parser.iterate(json).get(doc));
|
||||
double value;
|
||||
ASSERT_SUCCESS(doc.get_double().get(value));
|
||||
ASSERT_TRUE(std::isnan(value));
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool parse_infinity() {
|
||||
TEST_START();
|
||||
for (auto json_str : {"Infinity", "inf", "INF", "Inf"}) {
|
||||
ondemand::parser parser;
|
||||
padded_string json(json_str, strlen(json_str));
|
||||
ondemand::document doc;
|
||||
ASSERT_SUCCESS(parser.iterate(json).get(doc));
|
||||
double value;
|
||||
ASSERT_SUCCESS(doc.get_double().get(value));
|
||||
ASSERT_TRUE(std::isinf(value));
|
||||
ASSERT_TRUE(value > 0);
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool parse_negative_infinity() {
|
||||
TEST_START();
|
||||
for (auto json_str : {"-Infinity", "-inf", "-INF", "-Inf"}) {
|
||||
ondemand::parser parser;
|
||||
padded_string json(json_str, strlen(json_str));
|
||||
ondemand::document doc;
|
||||
ASSERT_SUCCESS(parser.iterate(json).get(doc));
|
||||
double value;
|
||||
ASSERT_SUCCESS(doc.get_double().get(value));
|
||||
ASSERT_TRUE(std::isinf(value));
|
||||
ASSERT_TRUE(value < 0);
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool nan_in_array() {
|
||||
TEST_START();
|
||||
ondemand::parser parser;
|
||||
auto json = R"([5, NaN, NAN, nan, -nan, -NAN, -NaN, 1.25])"_padded;
|
||||
ondemand::document doc;
|
||||
ASSERT_SUCCESS(parser.iterate(json).get(doc));
|
||||
ondemand::array arr;
|
||||
ASSERT_SUCCESS(doc.get_array().get(arr));
|
||||
|
||||
double nan = std::numeric_limits<double>::quiet_NaN();
|
||||
std::array<double, 8> expected_values{5, nan, nan, nan, nan, nan, nan, 1.25};
|
||||
|
||||
size_t index = 0;
|
||||
for (auto val : arr) {
|
||||
if (index == expected_values.size()) {
|
||||
TEST_FAIL("Array contained more values than expected");
|
||||
}
|
||||
|
||||
double parsed;
|
||||
ASSERT_SUCCESS(val.get_double().get(parsed));
|
||||
double expected = expected_values[index];
|
||||
if (std::isnan(expected)) {
|
||||
ASSERT_TRUE(std::isnan(parsed))
|
||||
} else {
|
||||
ASSERT_EQUAL(parsed, expected_values[index]);
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
ASSERT_EQUAL(index, expected_values.size());
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool infinity_in_array() {
|
||||
TEST_START();
|
||||
ondemand::parser parser;
|
||||
auto json = R"([1,
|
||||
infinity,
|
||||
INFINITY,
|
||||
Infinity,
|
||||
inf,
|
||||
Inf,
|
||||
INF,
|
||||
-infinity,
|
||||
-INFINITY,
|
||||
-Infinity,
|
||||
-inf,
|
||||
-Inf,
|
||||
-INF,
|
||||
6.5])"_padded;
|
||||
ondemand::document doc;
|
||||
ASSERT_SUCCESS(parser.iterate(json).get(doc));
|
||||
ondemand::array arr;
|
||||
ASSERT_SUCCESS(doc.get_array().get(arr));
|
||||
|
||||
double inf = std::numeric_limits<double>::infinity();
|
||||
std::array<double, 14> expected_values{
|
||||
1, inf, inf, inf, inf, inf, inf, -inf, -inf, -inf, -inf, -inf, -inf, 6.5};
|
||||
|
||||
size_t index = 0;
|
||||
for (auto val : arr) {
|
||||
if (index == expected_values.size()) {
|
||||
TEST_FAIL("Array contained more values than expected");
|
||||
}
|
||||
|
||||
double parsed;
|
||||
ASSERT_SUCCESS(val.get_double().get(parsed));
|
||||
ASSERT_EQUAL(parsed, expected_values[index]);
|
||||
index++;
|
||||
}
|
||||
|
||||
ASSERT_EQUAL(index, expected_values.size());
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool nan_in_object() {
|
||||
TEST_START();
|
||||
ondemand::parser parser;
|
||||
auto json = R"({"a": NaN, "b": nan})"_padded;
|
||||
ondemand::document doc;
|
||||
ASSERT_SUCCESS(parser.iterate(json).get(doc));
|
||||
double a;
|
||||
ASSERT_SUCCESS(doc["a"].get_double().get(a));
|
||||
ASSERT_TRUE(std::isnan(a));
|
||||
// rewind to access second field
|
||||
doc.rewind();
|
||||
double b;
|
||||
ASSERT_SUCCESS(doc["b"].get_double().get(b));
|
||||
ASSERT_TRUE(std::isnan(b));
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool infinity_in_object() {
|
||||
TEST_START();
|
||||
ondemand::parser parser;
|
||||
auto json = R"({"a": Infinity, "b": -inf})"_padded;
|
||||
ondemand::document doc;
|
||||
ASSERT_SUCCESS(parser.iterate(json).get(doc));
|
||||
double a;
|
||||
ASSERT_SUCCESS(doc["a"].get_double().get(a));
|
||||
ASSERT_TRUE(std::isinf(a));
|
||||
ASSERT_TRUE(a > 0);
|
||||
doc.rewind();
|
||||
double b;
|
||||
ASSERT_SUCCESS(doc["b"].get_double().get(b));
|
||||
ASSERT_TRUE(std::isinf(b));
|
||||
ASSERT_TRUE(b < 0);
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool nan_in_string() {
|
||||
TEST_START();
|
||||
for (auto json_str : {R"("NaN")", R"("nan")"}) {
|
||||
ondemand::parser parser;
|
||||
padded_string json(json_str, strlen(json_str));
|
||||
ondemand::document doc;
|
||||
ASSERT_SUCCESS(parser.iterate(json).get(doc));
|
||||
double value;
|
||||
ASSERT_SUCCESS(doc.get_double_in_string().get(value));
|
||||
ASSERT_TRUE(std::isnan(value));
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool infinity_in_string() {
|
||||
TEST_START();
|
||||
for (auto json_str : {R"("Infinity")", R"("inf")", R"("INF")", R"("Inf")"}) {
|
||||
ondemand::parser parser;
|
||||
padded_string json(json_str, strlen(json_str));
|
||||
ondemand::document doc;
|
||||
ASSERT_SUCCESS(parser.iterate(json).get(doc));
|
||||
double value;
|
||||
ASSERT_SUCCESS(doc.get_double_in_string().get(value));
|
||||
ASSERT_TRUE(std::isinf(value));
|
||||
ASSERT_TRUE(value > 0);
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool negative_infinity_in_string() {
|
||||
TEST_START();
|
||||
for (auto json_str :
|
||||
{R"("-Infinity")", R"("-inf")", R"("-INF")", R"("-Inf")"}) {
|
||||
ondemand::parser parser;
|
||||
padded_string json(json_str, strlen(json_str));
|
||||
ondemand::document doc;
|
||||
ASSERT_SUCCESS(parser.iterate(json).get(doc));
|
||||
double value;
|
||||
ASSERT_SUCCESS(doc.get_double_in_string().get(value));
|
||||
ASSERT_TRUE(std::isinf(value));
|
||||
ASSERT_TRUE(value < 0);
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool nan_inf_in_string_in_array() {
|
||||
TEST_START();
|
||||
ondemand::parser parser;
|
||||
auto json = R"(["NaN", "inf", "-Infinity"])"_padded;
|
||||
ondemand::document doc;
|
||||
ASSERT_SUCCESS(parser.iterate(json).get(doc));
|
||||
ondemand::array arr;
|
||||
ASSERT_SUCCESS(doc.get_array().get(arr));
|
||||
size_t index = 0;
|
||||
for (auto val : arr) {
|
||||
double parsed;
|
||||
ASSERT_SUCCESS(val.get_double_in_string().get(parsed));
|
||||
if (index == 0) {
|
||||
ASSERT_TRUE(std::isnan(parsed));
|
||||
} else if (index == 1) {
|
||||
ASSERT_TRUE(std::isinf(parsed));
|
||||
ASSERT_TRUE(parsed > 0);
|
||||
} else {
|
||||
ASSERT_TRUE(std::isinf(parsed));
|
||||
ASSERT_TRUE(parsed < 0);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
ASSERT_EQUAL(index, 3);
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool nan_inf_in_string_in_object() {
|
||||
TEST_START();
|
||||
ondemand::parser parser;
|
||||
auto json = R"({"a": "NaN", "b": "inf", "c": "-Infinity"})"_padded;
|
||||
ondemand::document doc;
|
||||
ASSERT_SUCCESS(parser.iterate(json).get(doc));
|
||||
double a;
|
||||
ASSERT_SUCCESS(doc["a"].get_double_in_string().get(a));
|
||||
ASSERT_TRUE(std::isnan(a));
|
||||
doc.rewind();
|
||||
double b;
|
||||
ASSERT_SUCCESS(doc["b"].get_double_in_string().get(b));
|
||||
ASSERT_TRUE(std::isinf(b));
|
||||
ASSERT_TRUE(b > 0);
|
||||
doc.rewind();
|
||||
double c;
|
||||
ASSERT_SUCCESS(doc["c"].get_double_in_string().get(c));
|
||||
ASSERT_TRUE(std::isinf(c));
|
||||
ASSERT_TRUE(c < 0);
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
// Bad atom tokens should not be parseable as doubles, and the raw JSON
|
||||
// token should still be extractable via raw_json_token().
|
||||
bool reject_trailing_junk() {
|
||||
TEST_START();
|
||||
for (auto atom :
|
||||
{"NaNa", "NaN1", "nana", "InfX", "Inf_", "Infinityy", "InfinityX"}) {
|
||||
std::string wrapped = std::string("{\"key\": ") + atom + "}";
|
||||
ondemand::parser parser;
|
||||
padded_string json(wrapped);
|
||||
ondemand::document doc;
|
||||
ASSERT_SUCCESS(parser.iterate(json).get(doc));
|
||||
auto val = doc["key"];
|
||||
double num = 0.0;
|
||||
ASSERT_ERROR(val.get(num), INCORRECT_TYPE);
|
||||
std::string_view str;
|
||||
ASSERT_SUCCESS(val.raw_json_token().get(str));
|
||||
ASSERT_EQUAL(str, atom);
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool reject_similar_prefix() {
|
||||
TEST_START();
|
||||
for (auto atom :
|
||||
{"Nope", "Napalm", "nope", "Infant", "Inform", "Information"}) {
|
||||
std::string wrapped = std::string("{\"key\": ") + atom + "}";
|
||||
ondemand::parser parser;
|
||||
padded_string json(wrapped);
|
||||
ondemand::document doc;
|
||||
ASSERT_SUCCESS(parser.iterate(json).get(doc));
|
||||
auto val = doc["key"];
|
||||
double num = 0.0;
|
||||
ASSERT_ERROR(val.get(num), INCORRECT_TYPE);
|
||||
std::string_view str;
|
||||
ASSERT_SUCCESS(val.raw_json_token().get(str));
|
||||
ASSERT_EQUAL(str, atom);
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool reject_truncated_atoms() {
|
||||
TEST_START();
|
||||
for (auto atom : {"N", "Na", "na", "I", "In", "Infinit"}) {
|
||||
std::string wrapped = std::string("{\"key\": ") + atom + "}";
|
||||
ondemand::parser parser;
|
||||
padded_string json(wrapped);
|
||||
ondemand::document doc;
|
||||
ASSERT_SUCCESS(parser.iterate(json).get(doc));
|
||||
auto val = doc["key"];
|
||||
double num = 0.0;
|
||||
ASSERT_ERROR(val.get(num), INCORRECT_TYPE);
|
||||
std::string_view str;
|
||||
ASSERT_SUCCESS(val.raw_json_token().get(str));
|
||||
ASSERT_EQUAL(str, atom);
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool run() {
|
||||
return parse_nan() //
|
||||
&& parse_infinity() //
|
||||
&& parse_negative_infinity() //
|
||||
&& nan_in_array() //
|
||||
&& infinity_in_array() //
|
||||
&& nan_in_object() //
|
||||
&& infinity_in_object() //
|
||||
&& nan_in_string() //
|
||||
&& infinity_in_string() //
|
||||
&& negative_infinity_in_string() //
|
||||
&& nan_inf_in_string_in_array() //
|
||||
&& nan_inf_in_string_in_object() //
|
||||
&& reject_trailing_junk() //
|
||||
&& reject_similar_prefix() //
|
||||
&& reject_truncated_atoms() //
|
||||
;
|
||||
}
|
||||
|
||||
#else // !SIMDJSON_ENABLE_NAN_INF
|
||||
|
||||
bool run() {
|
||||
std::cout << "NaN/Infinity parsing is disabled (SIMDJSON_ENABLE_NAN_INF=0), "
|
||||
"skipping tests."
|
||||
<< std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // SIMDJSON_ENABLE_NAN_INF
|
||||
} // namespace nan_inf_tests
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
return test_main(argc, argv, nan_inf_tests::run);
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "simdjson.h"
|
||||
#include "test_ondemand.h"
|
||||
#include <limits>
|
||||
#include <string>
|
||||
|
||||
using namespace simdjson;
|
||||
@@ -336,12 +335,7 @@ namespace number_in_string_tests {
|
||||
double d;
|
||||
std::string_view view;
|
||||
ASSERT_SUCCESS(doc.find_field("ticker").find_field("change").get(value));
|
||||
#if SIMDJSON_ENABLE_NAN_INF
|
||||
ASSERT_SUCCESS(value.get_double_in_string().get(d));
|
||||
ASSERT_EQUAL(d, std::numeric_limits<double>::infinity());
|
||||
#else
|
||||
ASSERT_ERROR(value.get_double_in_string().get(d), INCORRECT_TYPE);
|
||||
#endif
|
||||
ASSERT_SUCCESS(value.get_string().get(view));
|
||||
ASSERT_EQUAL(view,"Infinity");
|
||||
TEST_SUCCEED();
|
||||
@@ -371,4 +365,4 @@ namespace number_in_string_tests {
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
return test_main(argc, argv, number_in_string_tests::run);
|
||||
}
|
||||
}
|
||||
@@ -173,6 +173,36 @@ namespace object_tests {
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
||||
bool object_find_field_key_selector() {
|
||||
TEST_START();
|
||||
auto json = R"({ "name": "John", "age": 30, "city": "New York" })"_padded;
|
||||
constexpr std::array<std::string_view, 3> keys = {"name", "age", "city"};
|
||||
constexpr auto selector = ondemand::key_selector<3>(keys);
|
||||
|
||||
SUBTEST("ondemand::object with key_selector", test_ondemand_doc(json, [&](auto doc_result) {
|
||||
ondemand::object object;
|
||||
ASSERT_SUCCESS( doc_result.get(object) );
|
||||
|
||||
auto [index, value_result] = object.find_field(selector);
|
||||
ASSERT_TRUE(index < 3);
|
||||
ASSERT_SUCCESS(value_result);
|
||||
std::string_view str_val;
|
||||
ASSERT_SUCCESS(value_result.get(str_val));
|
||||
ASSERT_EQUAL(str_val, "John");
|
||||
|
||||
// Test that we can find different keys
|
||||
ASSERT_EQUAL(selector.index_of("name"), 0);
|
||||
ASSERT_EQUAL(selector.index_of("age"), 1);
|
||||
ASSERT_EQUAL(selector.index_of("city"), 2);
|
||||
ASSERT_EQUAL(selector.index_of("invalid"), 3); // Not found
|
||||
|
||||
return true;
|
||||
}));
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
#endif
|
||||
|
||||
bool run() {
|
||||
return
|
||||
object_find_field_unordered() &&
|
||||
@@ -181,6 +211,9 @@ namespace object_tests {
|
||||
object_find_field() &&
|
||||
document_object_find_field() &&
|
||||
value_object_find_field() &&
|
||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
||||
object_find_field_key_selector() &&
|
||||
#endif
|
||||
true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,3 @@
|
||||
#ifdef __FILC__
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
int main() {
|
||||
printf("This test is not relevant for FILC.\n");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
#else // This test is not relevant for FILC
|
||||
|
||||
#include "simdjson.h"
|
||||
#include "simdjson/padded_string_view.h"
|
||||
#include <cstdio>
|
||||
@@ -204,5 +195,3 @@ int main() {
|
||||
}
|
||||
|
||||
#endif // SIMDJSON_CPLUSPLUS17
|
||||
|
||||
#endif // This test is not relevant for FILC
|
||||
@@ -1,319 +0,0 @@
|
||||
#include <iostream>
|
||||
#include "simdjson.h"
|
||||
#include "test_macros.h"
|
||||
#include "test_main.h"
|
||||
|
||||
#if SIMDJSON_SUPPORTS_RANGES
|
||||
|
||||
#include <algorithm>
|
||||
#include <ranges>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace simdjson;
|
||||
|
||||
namespace ondemand_ranges_tests {
|
||||
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
|
||||
bool array_get_range_basic() {
|
||||
TEST_START();
|
||||
auto json = R"([10, 20, 30])"_padded;
|
||||
ondemand::parser parser;
|
||||
auto doc = parser.iterate(json);
|
||||
auto arr = doc.get_array();
|
||||
auto range = ondemand::get_range(arr);
|
||||
|
||||
std::vector<int64_t> values;
|
||||
for (auto elem : range) {
|
||||
values.push_back(int64_t(elem));
|
||||
}
|
||||
ASSERT_EQUAL(values.size(), size_t(3));
|
||||
ASSERT_EQUAL(values[0], int64_t(10));
|
||||
ASSERT_EQUAL(values[1], int64_t(20));
|
||||
ASSERT_EQUAL(values[2], int64_t(30));
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool array_range_with_transform() {
|
||||
TEST_START();
|
||||
auto json = R"([1, 2, 3, 4, 5])"_padded;
|
||||
ondemand::parser parser;
|
||||
auto doc = parser.iterate(json);
|
||||
auto arr = doc.get_array();
|
||||
|
||||
auto doubled = ondemand::get_range(arr)
|
||||
| std::views::transform([](auto v) -> int64_t { return int64_t(v) * 2; });
|
||||
|
||||
std::vector<int64_t> values;
|
||||
for (auto val : doubled) {
|
||||
values.push_back(val);
|
||||
}
|
||||
ASSERT_EQUAL(values.size(), size_t(5));
|
||||
ASSERT_EQUAL(values[0], int64_t(2));
|
||||
ASSERT_EQUAL(values[1], int64_t(4));
|
||||
ASSERT_EQUAL(values[2], int64_t(6));
|
||||
ASSERT_EQUAL(values[3], int64_t(8));
|
||||
ASSERT_EQUAL(values[4], int64_t(10));
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool array_range_strings() {
|
||||
TEST_START();
|
||||
auto json = R"(["alpha", "beta", "gamma"])"_padded;
|
||||
ondemand::parser parser;
|
||||
auto doc = parser.iterate(json);
|
||||
auto arr = doc.get_array();
|
||||
|
||||
auto to_string = [](auto v) -> std::string {
|
||||
return std::string(std::string_view(v));
|
||||
};
|
||||
auto strings = ondemand::get_range(arr) | std::views::transform(to_string);
|
||||
|
||||
std::vector<std::string> values;
|
||||
for (auto s : strings) {
|
||||
values.push_back(s);
|
||||
}
|
||||
ASSERT_EQUAL(values.size(), size_t(3));
|
||||
ASSERT_TRUE(values[0] == "alpha");
|
||||
ASSERT_TRUE(values[1] == "beta");
|
||||
ASSERT_TRUE(values[2] == "gamma");
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool array_range_empty() {
|
||||
TEST_START();
|
||||
auto json = R"([])"_padded;
|
||||
ondemand::parser parser;
|
||||
auto doc = parser.iterate(json);
|
||||
auto arr = doc.get_array();
|
||||
auto range = ondemand::get_range(arr);
|
||||
|
||||
int count = 0;
|
||||
for (simdjson_unused auto elem : range) {
|
||||
count++;
|
||||
}
|
||||
ASSERT_EQUAL(count, 0);
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool array_range_nested() {
|
||||
TEST_START();
|
||||
auto json = R"([{"name": "Alice", "age": 30}, {"name": "Bob", "age": 25}])"_padded;
|
||||
ondemand::parser parser;
|
||||
auto doc = parser.iterate(json);
|
||||
auto arr = doc.get_array();
|
||||
|
||||
auto get_name = [](auto v) -> std::string {
|
||||
return std::string(std::string_view(v["name"]));
|
||||
};
|
||||
auto names = ondemand::get_range(arr) | std::views::transform(get_name);
|
||||
|
||||
std::vector<std::string> values;
|
||||
for (auto name : names) {
|
||||
values.push_back(name);
|
||||
}
|
||||
ASSERT_EQUAL(values.size(), size_t(2));
|
||||
ASSERT_TRUE(values[0] == "Alice");
|
||||
ASSERT_TRUE(values[1] == "Bob");
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool object_get_range_basic() {
|
||||
TEST_START();
|
||||
auto json = R"({"a": 1, "b": 2, "c": 3})"_padded;
|
||||
ondemand::parser parser;
|
||||
auto doc = parser.iterate(json);
|
||||
auto obj = doc.get_object();
|
||||
auto range = ondemand::get_key_value_range(obj);
|
||||
|
||||
std::vector<std::string> keys;
|
||||
std::vector<int64_t> vals;
|
||||
for (auto field_result : range) {
|
||||
keys.push_back(std::string(std::string_view(field_result.escaped_key())));
|
||||
vals.push_back(int64_t(field_result.value()));
|
||||
}
|
||||
ASSERT_EQUAL(keys.size(), size_t(3));
|
||||
ASSERT_TRUE(keys[0] == "a");
|
||||
ASSERT_TRUE(keys[1] == "b");
|
||||
ASSERT_TRUE(keys[2] == "c");
|
||||
ASSERT_EQUAL(vals[0], int64_t(1));
|
||||
ASSERT_EQUAL(vals[1], int64_t(2));
|
||||
ASSERT_EQUAL(vals[2], int64_t(3));
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool object_range_with_transform() {
|
||||
TEST_START();
|
||||
auto json = R"({"x": 10, "y": 20, "z": 30})"_padded;
|
||||
ondemand::parser parser;
|
||||
auto doc = parser.iterate(json);
|
||||
auto obj = doc.get_object();
|
||||
|
||||
auto get_key = [](auto field_result) -> std::string {
|
||||
return std::string(std::string_view(field_result.escaped_key()));
|
||||
};
|
||||
auto keys = ondemand::get_key_value_range(obj) | std::views::transform(get_key);
|
||||
|
||||
std::vector<std::string> values;
|
||||
for (auto k : keys) {
|
||||
values.push_back(k);
|
||||
}
|
||||
ASSERT_EQUAL(values.size(), size_t(3));
|
||||
ASSERT_TRUE(values[0] == "x");
|
||||
ASSERT_TRUE(values[1] == "y");
|
||||
ASSERT_TRUE(values[2] == "z");
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool object_range_empty() {
|
||||
TEST_START();
|
||||
auto json = R"({})"_padded;
|
||||
ondemand::parser parser;
|
||||
auto doc = parser.iterate(json);
|
||||
auto obj = doc.get_object();
|
||||
auto range = ondemand::get_key_value_range(obj);
|
||||
|
||||
int count = 0;
|
||||
for (simdjson_unused auto elem : range) {
|
||||
count++;
|
||||
}
|
||||
ASSERT_EQUAL(count, 0);
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool get_range_from_result() {
|
||||
TEST_START();
|
||||
auto json = R"([100, 200, 300])"_padded;
|
||||
ondemand::parser parser;
|
||||
auto doc = parser.iterate(json);
|
||||
|
||||
// get_range with simdjson_result<array> - unwraps automatically
|
||||
auto range = ondemand::get_range(doc.get_array());
|
||||
|
||||
std::vector<int64_t> values;
|
||||
for (auto elem : range) {
|
||||
values.push_back(int64_t(elem));
|
||||
}
|
||||
ASSERT_EQUAL(values.size(), size_t(3));
|
||||
ASSERT_EQUAL(values[0], int64_t(100));
|
||||
ASSERT_EQUAL(values[1], int64_t(200));
|
||||
ASSERT_EQUAL(values[2], int64_t(300));
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool object_range_key_iteration() {
|
||||
TEST_START();
|
||||
auto json = R"({"name": "Alice", "age": 30, "city": "New York"})"_padded;
|
||||
ondemand::parser parser;
|
||||
auto doc = parser.iterate(json);
|
||||
auto obj = doc.get_object();
|
||||
|
||||
// Test the specific pattern: iterating over field_result.key()
|
||||
std::vector<std::string> keys;
|
||||
for (auto field_result : ondemand::get_key_value_range(obj)) {
|
||||
keys.push_back(std::string(field_result.escaped_key().value()));
|
||||
}
|
||||
|
||||
ASSERT_EQUAL(keys.size(), size_t(3));
|
||||
bool has_name = false, has_age = false, has_city = false;
|
||||
for (const auto& key : keys) {
|
||||
if (key == "name") has_name = true;
|
||||
else if (key == "age") has_age = true;
|
||||
else if (key == "city") has_city = true;
|
||||
}
|
||||
ASSERT_TRUE(has_name);
|
||||
ASSERT_TRUE(has_age);
|
||||
ASSERT_TRUE(has_city);
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
// Verify that the types satisfy the expected C++20 concepts.
|
||||
bool concept_checks() {
|
||||
TEST_START();
|
||||
static_assert(std::input_iterator<ondemand::array_range_iterator>);
|
||||
static_assert(std::input_iterator<ondemand::object_range_iterator>);
|
||||
static_assert(std::ranges::input_range<ondemand::array_range>);
|
||||
static_assert(std::ranges::input_range<ondemand::object_range>);
|
||||
static_assert(std::ranges::view<ondemand::array_range>);
|
||||
static_assert(std::ranges::view<ondemand::object_range>);
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
#endif // SIMDJSON_EXCEPTIONS
|
||||
|
||||
// These tests work without exceptions.
|
||||
bool array_range_noexcept_basic() {
|
||||
TEST_START();
|
||||
auto json = R"([1, 2, 3])"_padded;
|
||||
ondemand::parser parser;
|
||||
auto doc = parser.iterate(json);
|
||||
ondemand::array arr;
|
||||
ASSERT_SUCCESS(doc.get_array().get(arr));
|
||||
auto range = ondemand::get_range(arr);
|
||||
ASSERT_SUCCESS(range.error());
|
||||
|
||||
int count = 0;
|
||||
for (auto elem : range) {
|
||||
int64_t val;
|
||||
ASSERT_SUCCESS(elem.get_int64().get(val));
|
||||
count++;
|
||||
}
|
||||
ASSERT_EQUAL(count, 3);
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool object_range_noexcept_basic() {
|
||||
TEST_START();
|
||||
auto json = R"({"a": 1, "b": 2})"_padded;
|
||||
ondemand::parser parser;
|
||||
auto doc = parser.iterate(json);
|
||||
ondemand::object obj;
|
||||
ASSERT_SUCCESS(doc.get_object().get(obj));
|
||||
auto range = ondemand::get_key_value_range(obj);
|
||||
ASSERT_SUCCESS(range.error());
|
||||
|
||||
int count = 0;
|
||||
for (auto field_result : range) {
|
||||
simdjson_unused ondemand::field f;
|
||||
ASSERT_SUCCESS(std::move(field_result).get(f));
|
||||
count++;
|
||||
}
|
||||
ASSERT_EQUAL(count, 2);
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool run() {
|
||||
return
|
||||
array_range_noexcept_basic() &&
|
||||
object_range_noexcept_basic() &&
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
concept_checks() &&
|
||||
array_get_range_basic() &&
|
||||
array_range_with_transform() &&
|
||||
array_range_strings() &&
|
||||
array_range_empty() &&
|
||||
array_range_nested() &&
|
||||
object_get_range_basic() &&
|
||||
object_range_with_transform() &&
|
||||
object_range_empty() &&
|
||||
get_range_from_result() &&
|
||||
object_range_key_iteration() &&
|
||||
#endif // SIMDJSON_EXCEPTIONS
|
||||
true;
|
||||
}
|
||||
|
||||
} // namespace ondemand_ranges_tests
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
return test_main(argc, argv, ondemand_ranges_tests::run);
|
||||
}
|
||||
|
||||
#else // !SIMDJSON_SUPPORTS_RANGES
|
||||
|
||||
int main() {
|
||||
std::cout << "Ranges tests require C++20 ranges support, skipping." << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif // SIMDJSON_SUPPORTS_RANGES
|
||||
@@ -205,68 +205,6 @@ simdjson_inline simdjson_result<Car> simdjson::ondemand::document::get() & noexc
|
||||
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
|
||||
// Test matching the doc example in doc/basics.md
|
||||
// "Using for_each_at_path_with_wildcard for JSONPath Queries (On-Demand)"
|
||||
bool wildcard_basics_example() {
|
||||
TEST_START();
|
||||
simdjson::padded_string json_string = R"(
|
||||
{
|
||||
"firstName": "John",
|
||||
"lastName": "doe",
|
||||
"age": 26,
|
||||
"address": {
|
||||
"streetAddress": "naist street",
|
||||
"city": "Nara",
|
||||
"postalCode": "630-0192"
|
||||
},
|
||||
"phoneNumbers": [
|
||||
{
|
||||
"type": "iPhone",
|
||||
"numbers": ["0123-4567-8888", "0123-4567-8788"]
|
||||
},
|
||||
{
|
||||
"type": "home",
|
||||
"numbers": ["0123-4567-8910"]
|
||||
}
|
||||
]
|
||||
})"_padded;
|
||||
|
||||
ondemand::parser parser;
|
||||
ondemand::document doc = parser.iterate(json_string);
|
||||
|
||||
// Fetch all fields in the address object
|
||||
std::vector<std::string_view> fields;
|
||||
auto error = doc.for_each_at_path_with_wildcard("$.address.*",
|
||||
[&](ondemand::value value) {
|
||||
std::string_view field;
|
||||
if (value.get(field) == SUCCESS) {
|
||||
fields.push_back(field);
|
||||
}
|
||||
});
|
||||
ASSERT_SUCCESS(error);
|
||||
ASSERT_EQUAL(fields.size(), 3);
|
||||
ASSERT_EQUAL(fields[0], "naist street");
|
||||
ASSERT_EQUAL(fields[1], "Nara");
|
||||
ASSERT_EQUAL(fields[2], "630-0192");
|
||||
|
||||
// Fetch all phone numbers
|
||||
std::vector<std::string_view> numbers;
|
||||
error = doc.for_each_at_path_with_wildcard("$.phoneNumbers[*].numbers[*]",
|
||||
[&](ondemand::value value) {
|
||||
std::string_view number;
|
||||
if (value.get(number) == SUCCESS) {
|
||||
numbers.push_back(number);
|
||||
}
|
||||
});
|
||||
ASSERT_SUCCESS(error);
|
||||
ASSERT_EQUAL(numbers.size(), 3);
|
||||
ASSERT_EQUAL(numbers[0], "0123-4567-8888");
|
||||
ASSERT_EQUAL(numbers[1], "0123-4567-8788");
|
||||
ASSERT_EQUAL(numbers[2], "0123-4567-8910");
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
void main_capture() {
|
||||
padded_string json_padded = "{\"a\":[1,2,3], \"b\": 2, \"c\": \"hello\"}"_padded;
|
||||
std::vector<std::string_view> fields;
|
||||
@@ -2103,7 +2041,6 @@ bool value_raw_json_object() {
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
bool run() {
|
||||
return true
|
||||
&& fatal_error()
|
||||
@@ -2163,7 +2100,6 @@ bool run() {
|
||||
&& current_location_no_error()
|
||||
&& to_string_example_no_except()
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
&& wildcard_basics_example()
|
||||
&& issue2215()
|
||||
&& to_string_example()
|
||||
&& raw_string()
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
#include "simdjson.h"
|
||||
#include "test_ondemand.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
using namespace simdjson;
|
||||
|
||||
namespace unknown_tests {
|
||||
@@ -25,7 +23,7 @@ namespace unknown_tests {
|
||||
bool object_value_type() {
|
||||
TEST_START();
|
||||
padded_string json = "{\"key\": NaN}"_padded;
|
||||
auto do_test = [&](auto doc_result) {
|
||||
ASSERT_TRUE(test_ondemand_doc(json, [&](auto doc_result) {
|
||||
simdjson::ondemand::object object;
|
||||
ASSERT_SUCCESS( doc_result.get_object().get(object) );
|
||||
|
||||
@@ -33,42 +31,33 @@ namespace unknown_tests {
|
||||
auto val = object["key"];
|
||||
ASSERT_SUCCESS( val.type().get(type) );
|
||||
ASSERT_EQUAL( type, simdjson::ondemand::json_type::unknown );
|
||||
double num = 0.0;
|
||||
#if SIMDJSON_ENABLE_NAN_INF
|
||||
ASSERT_SUCCESS( val.get(num) );
|
||||
ASSERT_TRUE( std::isnan(num) );
|
||||
#else
|
||||
double num;
|
||||
ASSERT_EQUAL( val.get(num), simdjson::error_code::INCORRECT_TYPE);
|
||||
#endif
|
||||
std::string_view str;
|
||||
ASSERT_SUCCESS( val.raw_json_token().get(str) );
|
||||
ASSERT_EQUAL( str, "NaN");
|
||||
return true;
|
||||
};
|
||||
ASSERT_TRUE(test_ondemand_doc(json, do_test));
|
||||
}));
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
bool object_value_type_exception() {
|
||||
TEST_START();
|
||||
padded_string json = "{\"key\": ThisIsNotANumber}"_padded;
|
||||
padded_string json = "{\"key\": NaN}"_padded;
|
||||
simdjson::ondemand::parser parser;
|
||||
simdjson::ondemand::document doc = parser.iterate(json);
|
||||
simdjson::ondemand::object object = doc.get_object();
|
||||
simdjson::ondemand::value val = object["key"];
|
||||
simdjson::ondemand::json_type type = val.type();
|
||||
ASSERT_EQUAL( type, simdjson::ondemand::json_type::unknown);
|
||||
bool exception_caught = false;
|
||||
try {
|
||||
double num = val.get_double();
|
||||
(void)num;
|
||||
} catch (const simdjson::simdjson_error& e) {
|
||||
exception_caught = true;
|
||||
ASSERT_EQUAL( e.error(), simdjson::error_code::INCORRECT_TYPE);
|
||||
}
|
||||
ASSERT_TRUE( exception_caught );
|
||||
std::string_view str = val.raw_json_token();
|
||||
ASSERT_EQUAL( str, "ThisIsNotANumber");
|
||||
ASSERT_EQUAL( str, "NaN");
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -21,16 +21,18 @@ namespace wildcard_tests {
|
||||
ondemand::parser parser;
|
||||
auto doc = parser.iterate(json);
|
||||
|
||||
std::vector<std::string_view> titles;
|
||||
ASSERT_SUCCESS(doc.for_each_at_path_with_wildcard("$.books[*].title", [&](ondemand::value v) {
|
||||
std::string_view sv;
|
||||
if (v.get_string().get(sv) == SUCCESS) { titles.push_back(sv); }
|
||||
}));
|
||||
std::vector<ondemand::value> titles;
|
||||
ASSERT_SUCCESS(doc.at_path_with_wildcard("$.books[*].title").get(titles));
|
||||
|
||||
ASSERT_EQUAL(titles.size(), 3);
|
||||
ASSERT_EQUAL(titles[0], "Book A");
|
||||
ASSERT_EQUAL(titles[1], "Book B");
|
||||
ASSERT_EQUAL(titles[2], "Book C");
|
||||
|
||||
std::string_view title;
|
||||
ASSERT_SUCCESS(titles[0].get_string().get(title));
|
||||
ASSERT_EQUAL(title, "Book A");
|
||||
ASSERT_SUCCESS(titles[1].get_string().get(title));
|
||||
ASSERT_EQUAL(title, "Book B");
|
||||
ASSERT_SUCCESS(titles[2].get_string().get(title));
|
||||
ASSERT_EQUAL(title, "Book C");
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
@@ -44,16 +46,18 @@ namespace wildcard_tests {
|
||||
ondemand::parser parser;
|
||||
auto doc = parser.iterate(json);
|
||||
|
||||
std::vector<uint64_t> prices;
|
||||
ASSERT_SUCCESS(doc.for_each_at_path_with_wildcard("$.prices[*]", [&](ondemand::value v) {
|
||||
uint64_t p;
|
||||
if (v.get_uint64().get(p) == SUCCESS) { prices.push_back(p); }
|
||||
}));
|
||||
std::vector<ondemand::value> prices;
|
||||
ASSERT_SUCCESS(doc.at_path_with_wildcard("$.prices[*]").get(prices));
|
||||
|
||||
ASSERT_EQUAL(prices.size(), 5);
|
||||
ASSERT_EQUAL(prices[0], 10);
|
||||
ASSERT_EQUAL(prices[2], 30);
|
||||
ASSERT_EQUAL(prices[4], 50);
|
||||
|
||||
uint64_t price;
|
||||
ASSERT_SUCCESS(prices[0].get_uint64().get(price));
|
||||
ASSERT_EQUAL(price, 10);
|
||||
ASSERT_SUCCESS(prices[2].get_uint64().get(price));
|
||||
ASSERT_EQUAL(price, 30);
|
||||
ASSERT_SUCCESS(prices[4].get_uint64().get(price));
|
||||
ASSERT_EQUAL(price, 50);
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
@@ -71,14 +75,19 @@ namespace wildcard_tests {
|
||||
ondemand::parser parser;
|
||||
auto doc = parser.iterate(json);
|
||||
|
||||
std::set<std::string_view> name_set;
|
||||
auto error = doc.for_each_at_path_with_wildcard("$.users.*.name", [&](ondemand::value v) {
|
||||
std::string_view sv;
|
||||
if (v.get_string().get(sv) == SUCCESS) { name_set.insert(sv); }
|
||||
});
|
||||
std::vector<ondemand::value> names;
|
||||
auto error = doc.at_path_with_wildcard("$.users.*.name").get(names);
|
||||
ASSERT_SUCCESS(error);
|
||||
|
||||
ASSERT_EQUAL(name_set.size(), 3);
|
||||
ASSERT_EQUAL(names.size(), 3);
|
||||
|
||||
// Verify we got all three names (order may vary)
|
||||
std::set<std::string_view> name_set;
|
||||
for (auto& name : names) {
|
||||
std::string_view view;
|
||||
ASSERT_SUCCESS(name.get_string().get(view));
|
||||
name_set.insert(view);
|
||||
}
|
||||
ASSERT_TRUE(name_set.count("Alice") > 0);
|
||||
ASSERT_TRUE(name_set.count("Bob") > 0);
|
||||
ASSERT_TRUE(name_set.count("Charlie") > 0);
|
||||
@@ -108,15 +117,19 @@ namespace wildcard_tests {
|
||||
ondemand::parser parser;
|
||||
auto doc = parser.iterate(json);
|
||||
|
||||
uint64_t total = 0;
|
||||
size_t count = 0;
|
||||
auto error = doc.for_each_at_path_with_wildcard("$.departments.*.employees[*].salary", [&](ondemand::value v) {
|
||||
uint64_t s;
|
||||
if (v.get_uint64().get(s) == SUCCESS) { total += s; count++; }
|
||||
});
|
||||
std::vector<ondemand::value> salaries;
|
||||
auto error = doc.at_path_with_wildcard("$.departments.*.employees[*].salary").get(salaries);
|
||||
ASSERT_SUCCESS(error);
|
||||
|
||||
ASSERT_EQUAL(count, 4);
|
||||
ASSERT_EQUAL(salaries.size(), 4);
|
||||
|
||||
// Check sum of all salaries
|
||||
uint64_t total = 0;
|
||||
for (auto& salary : salaries) {
|
||||
uint64_t value;
|
||||
ASSERT_SUCCESS(salary.get_uint64().get(value));
|
||||
total += value;
|
||||
}
|
||||
ASSERT_EQUAL(total, 360000);
|
||||
|
||||
TEST_SUCCEED();
|
||||
@@ -129,12 +142,11 @@ namespace wildcard_tests {
|
||||
ondemand::parser parser;
|
||||
auto doc = parser.iterate(json);
|
||||
|
||||
size_t count = 0;
|
||||
auto error = doc.for_each_at_path_with_wildcard("$.items[*].name", [&](ondemand::value) {
|
||||
count++;
|
||||
});
|
||||
std::vector<ondemand::value> items;
|
||||
auto error = doc.at_path_with_wildcard("$.items[*].name").get(items);
|
||||
ASSERT_SUCCESS(error);
|
||||
ASSERT_EQUAL(count, 0);
|
||||
|
||||
ASSERT_EQUAL(items.size(), 0);
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
@@ -146,12 +158,11 @@ namespace wildcard_tests {
|
||||
ondemand::parser parser;
|
||||
auto doc = parser.iterate(json);
|
||||
|
||||
size_t count = 0;
|
||||
auto error = doc.for_each_at_path_with_wildcard("$.users.*.name", [&](ondemand::value) {
|
||||
count++;
|
||||
});
|
||||
std::vector<ondemand::value> names;
|
||||
auto error = doc.at_path_with_wildcard("$.users.*.name").get(names);
|
||||
ASSERT_SUCCESS(error);
|
||||
ASSERT_EQUAL(count, 0);
|
||||
|
||||
ASSERT_EQUAL(names.size(), 0);
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
@@ -164,7 +175,8 @@ namespace wildcard_tests {
|
||||
auto doc = parser.iterate(json);
|
||||
|
||||
// Can't use array wildcard on scalar
|
||||
auto error = doc.for_each_at_path_with_wildcard("$.data[*]", [](ondemand::value) {});
|
||||
std::vector<ondemand::value> values;
|
||||
auto error = doc.at_path_with_wildcard("$.data[*]").get(values);
|
||||
ASSERT_ERROR(error, INVALID_JSON_POINTER);
|
||||
|
||||
TEST_SUCCEED();
|
||||
@@ -183,12 +195,12 @@ namespace wildcard_tests {
|
||||
ondemand::parser parser;
|
||||
auto doc = parser.iterate(json);
|
||||
|
||||
size_t count = 0;
|
||||
ASSERT_SUCCESS(doc.for_each_at_path_with_wildcard("$.matrix[*]", [&](ondemand::value) {
|
||||
count++;
|
||||
}));
|
||||
// Get all nested arrays
|
||||
std::vector<ondemand::value> arrays;
|
||||
ASSERT_SUCCESS(doc.at_path_with_wildcard("$.matrix[*]").get(arrays));
|
||||
|
||||
ASSERT_EQUAL(count, 3);
|
||||
// Verify we got 3 arrays
|
||||
ASSERT_EQUAL(arrays.size(), 3);
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
@@ -206,16 +218,18 @@ namespace wildcard_tests {
|
||||
ondemand::parser parser;
|
||||
auto doc = parser.iterate(json);
|
||||
|
||||
std::vector<std::string_view> statuses;
|
||||
ASSERT_SUCCESS(doc.for_each_at_path_with_wildcard("$.data[*].info.status", [&](ondemand::value v) {
|
||||
std::string_view sv;
|
||||
if (v.get_string().get(sv) == SUCCESS) { statuses.push_back(sv); }
|
||||
}));
|
||||
std::vector<ondemand::value> statuses;
|
||||
ASSERT_SUCCESS(doc.at_path_with_wildcard("$.data[*].info.status").get(statuses));
|
||||
|
||||
ASSERT_EQUAL(statuses.size(), 3);
|
||||
ASSERT_EQUAL(statuses[0], "active");
|
||||
ASSERT_EQUAL(statuses[1], "inactive");
|
||||
ASSERT_EQUAL(statuses[2], "active");
|
||||
|
||||
std::string_view status;
|
||||
ASSERT_SUCCESS(statuses[0].get_string().get(status));
|
||||
ASSERT_EQUAL(status, "active");
|
||||
ASSERT_SUCCESS(statuses[1].get_string().get(status));
|
||||
ASSERT_EQUAL(status, "inactive");
|
||||
ASSERT_SUCCESS(statuses[2].get_string().get(status));
|
||||
ASSERT_EQUAL(status, "active");
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
@@ -236,12 +250,13 @@ namespace wildcard_tests {
|
||||
ondemand::parser parser;
|
||||
auto doc = parser.iterate(json);
|
||||
|
||||
size_t count = 0;
|
||||
ASSERT_SUCCESS(doc.for_each_at_path_with_wildcard("$.mixed[*]", [&](ondemand::value) {
|
||||
count++;
|
||||
}));
|
||||
std::vector<ondemand::value> values;
|
||||
ASSERT_SUCCESS(doc.at_path_with_wildcard("$.mixed[*]").get(values));
|
||||
|
||||
ASSERT_EQUAL(count, 6);
|
||||
// Just verify we got all 6 elements
|
||||
ASSERT_EQUAL(values.size(), 6);
|
||||
|
||||
// Don't try to access the values - they've been consumed by the OnDemand API
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
@@ -260,11 +275,8 @@ namespace wildcard_tests {
|
||||
auto doc = parser.iterate(json);
|
||||
|
||||
// Third element doesn't have "name" field
|
||||
std::vector<std::string_view> names;
|
||||
auto error = doc.for_each_at_path_with_wildcard("$.data[*].name", [&](ondemand::value v) {
|
||||
std::string_view sv;
|
||||
if (v.get_string().get(sv) == SUCCESS) { names.push_back(sv); }
|
||||
});
|
||||
std::vector<ondemand::value> names;
|
||||
auto error = doc.at_path_with_wildcard("$.data[*].name").get(names);
|
||||
|
||||
// This might error or return partial results
|
||||
if (error) {
|
||||
@@ -289,47 +301,18 @@ namespace wildcard_tests {
|
||||
ondemand::parser parser;
|
||||
auto doc = parser.iterate(json);
|
||||
|
||||
std::vector<std::string_view> names;
|
||||
ASSERT_SUCCESS(doc.for_each_at_path_with_wildcard("$[*].name", [&](ondemand::value v) {
|
||||
std::string_view sv;
|
||||
if (v.get_string().get(sv) == SUCCESS) { names.push_back(sv); }
|
||||
}));
|
||||
std::vector<ondemand::value> names;
|
||||
ASSERT_SUCCESS(doc.at_path_with_wildcard("$[*].name").get(names));
|
||||
|
||||
ASSERT_EQUAL(names.size(), 3);
|
||||
ASSERT_EQUAL(names[0], "Item 1");
|
||||
ASSERT_EQUAL(names[1], "Item 2");
|
||||
ASSERT_EQUAL(names[2], "Item 3");
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
// https://github.com/simdjson/simdjson/issues/2684
|
||||
bool wildcard_raw_json_issue_2684() {
|
||||
TEST_START();
|
||||
auto json = R"([{"tag_meta":{"meta_code":2000211,"meta_value":""},"tag_value":""}])"_padded;
|
||||
|
||||
ondemand::parser parser;
|
||||
auto doc = parser.iterate(json);
|
||||
|
||||
size_t count = 0;
|
||||
bool raw_json_ok = true;
|
||||
ASSERT_SUCCESS(doc.for_each_at_path_with_wildcard("$[*].tag_meta", [&](ondemand::value v) {
|
||||
count++;
|
||||
std::string_view raw;
|
||||
if (v.raw_json().get(raw) != SUCCESS) {
|
||||
raw_json_ok = false;
|
||||
return;
|
||||
}
|
||||
// raw_json() must not leak sibling fields or outer array delimiters
|
||||
std::string_view expected = R"({"meta_code":2000211,"meta_value":""})";
|
||||
if (raw != expected || raw.find("tag_value") != std::string_view::npos) {
|
||||
std::cerr << " raw_json() returned: " << raw << std::endl;
|
||||
raw_json_ok = false;
|
||||
}
|
||||
}));
|
||||
|
||||
ASSERT_EQUAL(count, 1);
|
||||
ASSERT_TRUE(raw_json_ok);
|
||||
std::string_view name;
|
||||
ASSERT_SUCCESS(names[0].get_string().get(name));
|
||||
ASSERT_EQUAL(name, "Item 1");
|
||||
ASSERT_SUCCESS(names[1].get_string().get(name));
|
||||
ASSERT_EQUAL(name, "Item 2");
|
||||
ASSERT_SUCCESS(names[2].get_string().get(name));
|
||||
ASSERT_EQUAL(name, "Item 3");
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
@@ -346,11 +329,10 @@ namespace wildcard_tests {
|
||||
wildcard_with_nested_objects() &&
|
||||
mixed_types_in_array() &&
|
||||
wildcard_nonexistent_field() &&
|
||||
root_array_wildcard() &&
|
||||
wildcard_raw_json_issue_2684();
|
||||
root_array_wildcard();
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
return test_main(argc, argv, wildcard_tests::run);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user