mirror of
https://github.com/simdjson/simdjson
synced 2026-06-08 17:27:07 +00:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9b71acf803 | |||
| 325db34d41 |
@@ -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,18 @@ 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}
|
||||
# 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: Win32, shared: ON, build_type: Release}
|
||||
- {gen: Visual Studio 17 2022, arch: Win32, shared: OFF, build_type: Release}
|
||||
- {gen: Visual Studio 17 2022, arch: x64, shared: ON, build_type: Release}
|
||||
- {gen: Visual Studio 17 2022, arch: x64, shared: OFF, build_type: Debug}
|
||||
- {gen: Visual Studio 17 2022, arch: x64, shared: OFF, build_type: Release}
|
||||
- {gen: Visual Studio 17 2022, arch: x64, shared: OFF, build_type: RelWithDebInfo}
|
||||
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}} -B build
|
||||
- name: Build Debug
|
||||
run: cmake --build build --config ${{matrix.build_type}} --verbose
|
||||
- name: Run tests
|
||||
@@ -41,4 +37,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}}
|
||||
+14
-41
@@ -52,6 +52,20 @@ if(SIMDJSON_DISABLE_DEPRECATED_API)
|
||||
)
|
||||
endif()
|
||||
|
||||
option(SIMDJSON_FEATURE_ONDEMAND_API "Enable ondemand API" ON)
|
||||
option(SIMDJSON_FEATURE_DOM_API "Enable DOM API" ON)
|
||||
option(SIMDJSON_FEATURE_BUILDER_API "Enable builder API" ON)
|
||||
|
||||
if(NOT SIMDJSON_FEATURE_ONDEMAND_API)
|
||||
add_compile_definitions(SIMDJSON_FEATURE_ONDEMAND_API=0)
|
||||
endif()
|
||||
if(NOT SIMDJSON_FEATURE_DOM_API)
|
||||
add_compile_definitions(SIMDJSON_FEATURE_DOM_API=0)
|
||||
endif()
|
||||
if(NOT SIMDJSON_FEATURE_BUILDER_API)
|
||||
add_compile_definitions(SIMDJSON_FEATURE_BUILDER_API=0)
|
||||
endif()
|
||||
|
||||
if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.25.0")
|
||||
option(SIMDJSON_STATIC_REFLECTION "Enables static reflection (experimental), requires C++26" OFF)
|
||||
else()
|
||||
@@ -75,41 +89,6 @@ if(SIMDJSON_DEVELOPMENT_CHECKS)
|
||||
)
|
||||
endif()
|
||||
|
||||
# padded_memory_map is always available on POSIX. On Windows it is disabled
|
||||
# by default because it depends on the `CreateFileMapping2` / `MapViewOfFile3`
|
||||
# APIs, which require Windows 10 version 1803 or later and are exported via
|
||||
# onecore.lib rather than the default kernel32.lib. Turn this option ON to
|
||||
# opt into the feature on Windows; simdjson will then set the appropriate
|
||||
# Windows version macros and link onecore, so everything that links
|
||||
# simdjson picks up both the compile-time declarations and the import
|
||||
# library automatically. The option is a no-op on POSIX (where the feature
|
||||
# is unconditionally enabled).
|
||||
option(SIMDJSON_ENABLE_MEMORY_FILE_MAPPING_ON_WINDOWS
|
||||
"Enable simdjson::padded_memory_map on Windows (requires Windows 10 \
|
||||
version 1803 or later). Always enabled on POSIX." OFF)
|
||||
if(SIMDJSON_ENABLE_MEMORY_FILE_MAPPING_ON_WINDOWS)
|
||||
simdjson_add_props(
|
||||
target_compile_definitions PUBLIC
|
||||
SIMDJSON_ENABLE_MEMORY_FILE_MAPPING_ON_WINDOWS=1
|
||||
)
|
||||
if(WIN32)
|
||||
# Raise the Windows version floor so that <windows.h> declares the
|
||||
# modern memory-mapping APIs, and link the import library that
|
||||
# actually exports them. _WIN32_WINNT / WINVER / NTDDI_VERSION together
|
||||
# tell <sdkddkver.h> which APIs to light up.
|
||||
simdjson_add_props(
|
||||
target_compile_definitions PUBLIC
|
||||
_WIN32_WINNT=0x0A00
|
||||
WINVER=0x0A00
|
||||
NTDDI_VERSION=0x0A000006 # NTDDI_WIN10_RS5, Windows 10 version 1809
|
||||
)
|
||||
simdjson_add_props(
|
||||
target_link_libraries PUBLIC
|
||||
onecore
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(is_top_project)
|
||||
option(SIMDJSON_INSTALL "Enable target install" ON)
|
||||
option(SIMDJSON_DEVELOPER_MODE "Enable targets for developing simdjson" OFF)
|
||||
@@ -254,12 +233,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
|
||||
|
||||
+18
@@ -32,6 +32,24 @@ It is similar for Visual Studio users, please see the CMake or Visual Studio doc
|
||||
|
||||
By default the library is built in Release mode.
|
||||
|
||||
## Optional APIs (Feature Flags)
|
||||
|
||||
simdjson provides three APIs:
|
||||
- DOM (`simdjson::dom`)
|
||||
- OnDemand (`simdjson::ondemand`)
|
||||
- Builder (`simdjson::builder`) — requires DOM
|
||||
|
||||
You can enable or disable these APIs at compile time to reduce binary size and compile time.
|
||||
At least one of DOM or OnDemand must be enabled.
|
||||
|
||||
### Configuration
|
||||
|
||||
Using CMake:
|
||||
|
||||
```bash
|
||||
cmake -DSIMDJSON_FEATURE_DOM_API=OFF \
|
||||
-DSIMDJSON_FEATURE_ONDEMAND_API=ON \
|
||||
-DSIMDJSON_FEATURE_BUILDER_API=OFF ..
|
||||
|
||||
Assertions and development checks
|
||||
------------------------------
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
add_subdirectory(dom)
|
||||
|
||||
|
||||
include_directories( . )
|
||||
include_directories( . linux )
|
||||
link_libraries(simdjson-windows-headers test-data)
|
||||
link_libraries(simdjson)
|
||||
link_libraries(counters)
|
||||
if(SIMDJSON_STATIC_REFLECTION)
|
||||
add_compile_definitions(SIMDJSON_STATIC_REFLECTION=1)
|
||||
endif(SIMDJSON_STATIC_REFLECTION)
|
||||
@@ -16,7 +15,6 @@ if (TARGET benchmark::benchmark)
|
||||
link_libraries(benchmark::benchmark)
|
||||
add_executable(bench_parse_call bench_parse_call.cpp)
|
||||
add_executable(bench_dom_api bench_dom_api.cpp)
|
||||
add_executable(bench_stream_formats bench_stream_formats.cpp)
|
||||
if(SIMDJSON_EXCEPTIONS)
|
||||
add_executable(bench_ondemand bench_ondemand.cpp)
|
||||
if(TARGET yyjson)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -124,7 +124,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,216 +0,0 @@
|
||||
#include <benchmark/benchmark.h>
|
||||
#include <string>
|
||||
#include "simdjson.h"
|
||||
|
||||
using namespace simdjson;
|
||||
|
||||
namespace {
|
||||
|
||||
enum class stream_case {
|
||||
ndjson_small,
|
||||
ndjson_large,
|
||||
rfc7464_small,
|
||||
rfc7464_large,
|
||||
comma_delimited_small,
|
||||
comma_delimited_large
|
||||
};
|
||||
|
||||
constexpr size_t TARGET_BYTES = 128 * 1000 * 1000;
|
||||
constexpr size_t SMALL_PAYLOAD = 16;
|
||||
constexpr size_t LARGE_PAYLOAD = 4096;
|
||||
constexpr size_t BATCH_SIZE = 1 << 20;
|
||||
|
||||
struct stream_dataset {
|
||||
padded_string json;
|
||||
size_t count{};
|
||||
};
|
||||
|
||||
std::string make_document(size_t id, size_t payload_size) {
|
||||
return std::string{"{\"id\":"} + std::to_string(id) +
|
||||
",\"name\":\"aaaaaaaa\",\"payload\":\"" +
|
||||
std::string(payload_size, 'x') + "\",\"flag\":true}";
|
||||
}
|
||||
|
||||
stream_dataset build_dataset(stream_case which) {
|
||||
const bool small = which == stream_case::ndjson_small ||
|
||||
which == stream_case::rfc7464_small ||
|
||||
which == stream_case::comma_delimited_small;
|
||||
const bool rfc = which == stream_case::rfc7464_small ||
|
||||
which == stream_case::rfc7464_large;
|
||||
const bool comma = which == stream_case::comma_delimited_small ||
|
||||
which == stream_case::comma_delimited_large;
|
||||
const size_t payload_size = small ? SMALL_PAYLOAD : LARGE_PAYLOAD;
|
||||
const size_t count = TARGET_BYTES / (payload_size + 48);
|
||||
std::string out;
|
||||
out.reserve(count * (payload_size + 64));
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
if (rfc) {
|
||||
out += char(0x1E);
|
||||
}
|
||||
if (comma && i > 0) {
|
||||
out += ',';
|
||||
}
|
||||
out += make_document(i, payload_size);
|
||||
if (!comma) {
|
||||
out += '\n';
|
||||
}
|
||||
}
|
||||
return {padded_string(out), count};
|
||||
}
|
||||
|
||||
const stream_dataset &get_dataset(stream_case which) {
|
||||
static const stream_dataset ndjson_small =
|
||||
build_dataset(stream_case::ndjson_small);
|
||||
static const stream_dataset ndjson_large =
|
||||
build_dataset(stream_case::ndjson_large);
|
||||
static const stream_dataset rfc_small =
|
||||
build_dataset(stream_case::rfc7464_small);
|
||||
static const stream_dataset rfc_large =
|
||||
build_dataset(stream_case::rfc7464_large);
|
||||
static const stream_dataset comma_small =
|
||||
build_dataset(stream_case::comma_delimited_small);
|
||||
static const stream_dataset comma_large =
|
||||
build_dataset(stream_case::comma_delimited_large);
|
||||
switch (which) {
|
||||
case stream_case::ndjson_small:
|
||||
return ndjson_small;
|
||||
case stream_case::ndjson_large:
|
||||
return ndjson_large;
|
||||
case stream_case::rfc7464_small:
|
||||
return rfc_small;
|
||||
case stream_case::rfc7464_large:
|
||||
return rfc_large;
|
||||
case stream_case::comma_delimited_small:
|
||||
return comma_small;
|
||||
case stream_case::comma_delimited_large:
|
||||
return comma_large;
|
||||
}
|
||||
return ndjson_small;
|
||||
}
|
||||
|
||||
void set_counters(benchmark::State &state, const stream_dataset &dataset) {
|
||||
state.SetBytesProcessed(int64_t(state.iterations()) * int64_t(dataset.json.size()));
|
||||
state.SetItemsProcessed(int64_t(state.iterations()) * int64_t(dataset.count));
|
||||
}
|
||||
|
||||
template <stream_case which, bool threaded = true>
|
||||
static void bench_ondemand(benchmark::State &state) {
|
||||
const auto &dataset = get_dataset(which);
|
||||
ondemand::parser parser;
|
||||
parser.threaded = threaded;
|
||||
stream_format format = stream_format::whitespace_delimited;
|
||||
if constexpr (which == stream_case::rfc7464_small ||
|
||||
which == stream_case::rfc7464_large) {
|
||||
format = stream_format::json_sequence;
|
||||
} else if constexpr (which == stream_case::comma_delimited_small ||
|
||||
which == stream_case::comma_delimited_large) {
|
||||
format = stream_format::comma_delimited;
|
||||
}
|
||||
for (const auto _ : state) {
|
||||
ondemand::document_stream docs;
|
||||
auto error = parser.iterate_many(dataset.json, BATCH_SIZE, format).get(docs);
|
||||
if (error) {
|
||||
state.SkipWithError(error_message(error));
|
||||
return;
|
||||
}
|
||||
uint64_t sum = 0;
|
||||
for (auto doc : docs) {
|
||||
ondemand::object obj;
|
||||
if ((error = doc.get_object().get(obj))) {
|
||||
state.SkipWithError(error_message(error));
|
||||
return;
|
||||
}
|
||||
uint64_t id;
|
||||
if ((error = obj["id"].get_uint64().get(id))) {
|
||||
state.SkipWithError(error_message(error));
|
||||
return;
|
||||
}
|
||||
sum += id;
|
||||
}
|
||||
benchmark::DoNotOptimize(sum);
|
||||
}
|
||||
set_counters(state, dataset);
|
||||
}
|
||||
|
||||
template <stream_case which>
|
||||
static void bench_dom(benchmark::State &state) {
|
||||
const auto &dataset = get_dataset(which);
|
||||
dom::parser parser;
|
||||
parser.threaded = true;
|
||||
stream_format format = stream_format::whitespace_delimited;
|
||||
if constexpr (which == stream_case::rfc7464_small ||
|
||||
which == stream_case::rfc7464_large) {
|
||||
format = stream_format::json_sequence;
|
||||
} else if constexpr (which == stream_case::comma_delimited_small ||
|
||||
which == stream_case::comma_delimited_large) {
|
||||
format = stream_format::comma_delimited;
|
||||
}
|
||||
for (const auto _ : state) {
|
||||
dom::document_stream docs;
|
||||
auto error = parser.parse_many(dataset.json, BATCH_SIZE, format).get(docs);
|
||||
if (error) {
|
||||
state.SkipWithError(error_message(error));
|
||||
return;
|
||||
}
|
||||
uint64_t sum = 0;
|
||||
for (auto doc : docs) {
|
||||
uint64_t id;
|
||||
if ((error = doc["id"].get(id))) {
|
||||
state.SkipWithError(error_message(error));
|
||||
return;
|
||||
}
|
||||
sum += id;
|
||||
}
|
||||
benchmark::DoNotOptimize(sum);
|
||||
}
|
||||
set_counters(state, dataset);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
BENCHMARK(bench_ondemand<stream_case::ndjson_small>)
|
||||
->UseRealTime()
|
||||
->DisplayAggregatesOnly(true);
|
||||
BENCHMARK(bench_ondemand<stream_case::ndjson_large>)
|
||||
->UseRealTime()
|
||||
->DisplayAggregatesOnly(true);
|
||||
BENCHMARK(bench_ondemand<stream_case::rfc7464_small>)
|
||||
->UseRealTime()
|
||||
->DisplayAggregatesOnly(true);
|
||||
BENCHMARK(bench_ondemand<stream_case::rfc7464_large>)
|
||||
->UseRealTime()
|
||||
->DisplayAggregatesOnly(true);
|
||||
BENCHMARK(bench_ondemand<stream_case::comma_delimited_small>)
|
||||
->UseRealTime()
|
||||
->DisplayAggregatesOnly(true);
|
||||
BENCHMARK(bench_ondemand<stream_case::comma_delimited_large>)
|
||||
->UseRealTime()
|
||||
->DisplayAggregatesOnly(true);
|
||||
// Non-threaded comma_delimited for comparison
|
||||
BENCHMARK(bench_ondemand<stream_case::comma_delimited_small, false>)
|
||||
->UseRealTime()
|
||||
->DisplayAggregatesOnly(true);
|
||||
BENCHMARK(bench_ondemand<stream_case::comma_delimited_large, false>)
|
||||
->UseRealTime()
|
||||
->DisplayAggregatesOnly(true);
|
||||
|
||||
BENCHMARK(bench_dom<stream_case::ndjson_small>)
|
||||
->UseRealTime()
|
||||
->DisplayAggregatesOnly(true);
|
||||
BENCHMARK(bench_dom<stream_case::ndjson_large>)
|
||||
->UseRealTime()
|
||||
->DisplayAggregatesOnly(true);
|
||||
BENCHMARK(bench_dom<stream_case::rfc7464_small>)
|
||||
->UseRealTime()
|
||||
->DisplayAggregatesOnly(true);
|
||||
BENCHMARK(bench_dom<stream_case::rfc7464_large>)
|
||||
->UseRealTime()
|
||||
->DisplayAggregatesOnly(true);
|
||||
BENCHMARK(bench_dom<stream_case::comma_delimited_small>)
|
||||
->UseRealTime()
|
||||
->DisplayAggregatesOnly(true);
|
||||
BENCHMARK(bench_dom<stream_case::comma_delimited_large>)
|
||||
->UseRealTime()
|
||||
->DisplayAggregatesOnly(true);
|
||||
|
||||
BENCHMARK_MAIN();
|
||||
@@ -1,5 +1,4 @@
|
||||
#include <counters/event_counter.h>
|
||||
using namespace counters;
|
||||
#include "event_counter.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cctype>
|
||||
@@ -26,6 +25,7 @@ using namespace counters;
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "linux-perf-events.h"
|
||||
#ifdef __linux__
|
||||
#include <libgen.h>
|
||||
#endif
|
||||
@@ -204,8 +204,12 @@ struct feature_benchmarker {
|
||||
}
|
||||
// Rate of 1-7-structural misses per 8-structural flip
|
||||
double struct1_7_miss_rate(BenchmarkStage stage) const {
|
||||
#if SIMDJSON_SIMPLE_PERFORMANCE_COUNTERS
|
||||
return 1;
|
||||
#else
|
||||
if (!has_events()) { return 1; }
|
||||
return struct7_miss[stage].best.branch_misses() - struct7[stage].best.branch_misses() / double(struct7_miss.stats->blocks_with_1_structural_flipped);
|
||||
#endif
|
||||
}
|
||||
// Extra cost of an 8-15 structural block over a 1-7 structural block
|
||||
double struct8_15_cost(BenchmarkStage stage) const {
|
||||
@@ -217,8 +221,12 @@ struct feature_benchmarker {
|
||||
}
|
||||
// Rate of 8-15-structural misses per 8-structural flip
|
||||
double struct8_15_miss_rate(BenchmarkStage stage) const {
|
||||
#if SIMDJSON_SIMPLE_PERFORMANCE_COUNTERS
|
||||
return 1;
|
||||
#else
|
||||
if (!has_events()) { return 1; }
|
||||
return double(struct15_miss[stage].best.branch_misses() - struct15[stage].best.branch_misses()) / double(struct15_miss.stats->blocks_with_8_structurals_flipped);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Extra cost of a 16+-structural block over an 8-15 structural block (actual varies based on # of structurals!)
|
||||
@@ -231,8 +239,12 @@ struct feature_benchmarker {
|
||||
}
|
||||
// Rate of 16-structural misses per 16-structural flip
|
||||
double struct16_miss_rate(BenchmarkStage stage) const {
|
||||
#if SIMDJSON_SIMPLE_PERFORMANCE_COUNTERS
|
||||
return 1;
|
||||
#else
|
||||
if (!has_events()) { return 1; }
|
||||
return double(struct23_miss[stage].best.branch_misses() - struct23[stage].best.branch_misses()) / double(struct23_miss.stats->blocks_with_16_structurals_flipped);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -246,8 +258,12 @@ struct feature_benchmarker {
|
||||
}
|
||||
// Rate of UTF-8 misses per UTF-8 flip
|
||||
double utf8_miss_rate(BenchmarkStage stage) const {
|
||||
#if SIMDJSON_SIMPLE_PERFORMANCE_COUNTERS
|
||||
return 1;
|
||||
#else
|
||||
if (!has_events()) { return 1; }
|
||||
return double(utf8_miss[stage].best.branch_misses() - utf8[stage].best.branch_misses()) / double(utf8_miss.stats->blocks_with_utf8_flipped);
|
||||
#endif
|
||||
}
|
||||
// Extra cost of having escapes in a block
|
||||
double escape_cost(BenchmarkStage stage) const {
|
||||
@@ -259,8 +275,12 @@ struct feature_benchmarker {
|
||||
}
|
||||
// Rate of escape misses per escape flip
|
||||
double escape_miss_rate(BenchmarkStage stage) const {
|
||||
#if SIMDJSON_SIMPLE_PERFORMANCE_COUNTERS
|
||||
return 1;
|
||||
#else
|
||||
if (!has_events()) { return 1; }
|
||||
return double(escape_miss[stage].best.branch_misses() - escape[stage].best.branch_misses()) / double(escape_miss.stats->blocks_with_escapes_flipped);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -358,6 +378,22 @@ struct feature_benchmarker {
|
||||
}
|
||||
};
|
||||
|
||||
#if SIMDJSON_SIMPLE_PERFORMANCE_COUNTERS
|
||||
void print_file_effectiveness(BenchmarkStage stage, const char* filename, const benchmarker& results, const feature_benchmarker& features) {
|
||||
double actual = results[stage].best.elapsed_ns() / double(results.stats->blocks);
|
||||
double calc = features.calc_expected(stage, results);
|
||||
double calc_misses = features.calc_expected_misses(stage, results);
|
||||
double calc_miss_cost = features.calc_expected_miss_cost(stage, results);
|
||||
printf(" | %-8s ", benchmark_stage_name(stage));
|
||||
printf("| %-15s ", filename);
|
||||
printf("| %8.3g ", features.calc_expected_feature_cost(stage, results));
|
||||
printf("| %8.3g ", calc_miss_cost);
|
||||
printf("| %8.3g ", calc);
|
||||
printf("| %8.3g ", actual);
|
||||
printf("| %+8.3g ", actual - calc);
|
||||
printf("| %13llu ", (long long unsigned)(calc_misses));
|
||||
}
|
||||
#else
|
||||
void print_file_effectiveness(BenchmarkStage stage, const char* filename, const benchmarker& results, const feature_benchmarker& features) {
|
||||
double actual = results[stage].best.elapsed_ns() / double(results.stats->blocks);
|
||||
double calc = features.calc_expected(stage, results);
|
||||
@@ -381,6 +417,7 @@ void print_file_effectiveness(BenchmarkStage stage, const char* filename, const
|
||||
}
|
||||
printf("|\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
// Read options
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
#ifndef _BENCHMARK_H_
|
||||
#define _BENCHMARK_H_
|
||||
|
||||
#include <counters/event_counter.h>
|
||||
using namespace counters;
|
||||
#include "event_counter.h"
|
||||
|
||||
/*
|
||||
* Prints the best number of operations per cycle where
|
||||
|
||||
+11
-4
@@ -1,8 +1,7 @@
|
||||
#ifndef __BENCHMARKER_H
|
||||
#define __BENCHMARKER_H
|
||||
|
||||
#include <counters/event_counter.h>
|
||||
using namespace counters;
|
||||
#include "event_counter.h"
|
||||
#include "simdjson.h"
|
||||
|
||||
#include <cassert>
|
||||
@@ -29,9 +28,11 @@ using namespace counters;
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "linux-perf-events.h"
|
||||
#ifdef __linux__
|
||||
#include <libgen.h>
|
||||
#endif
|
||||
#include "simdjson.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
@@ -422,12 +423,18 @@ struct benchmarker {
|
||||
stage.instructions() / static_cast<double>(stats->structurals),
|
||||
stage.instructions() / static_cast<double>(stage.cycles())
|
||||
);
|
||||
printf("%s%-13s: %7.0f branch misses (%6.2f%%)\n",
|
||||
#if !SIMDJSON_SIMPLE_PERFORMANCE_COUNTERS
|
||||
// NOTE: removed cycles/miss because it is a somewhat misleading stat
|
||||
printf("%s%-13s: %7.0f branch misses (%6.2f%%) - %.0f cache misses (%6.2f%%) - %.2f cache references\n",
|
||||
prefix,
|
||||
"Misses",
|
||||
stage.branch_misses(),
|
||||
percent(stage.branch_misses(), all_stages_without_allocation.branch_misses())
|
||||
percent(stage.branch_misses(), all_stages_without_allocation.branch_misses()),
|
||||
stage.cache_misses(),
|
||||
percent(stage.cache_misses(), all_stages_without_allocation.cache_misses()),
|
||||
stage.cache_references()
|
||||
);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#include <counters/event_counter.h>
|
||||
using namespace counters;
|
||||
#include "event_counter.h"
|
||||
#include <random>
|
||||
#include <vector>
|
||||
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
include_directories( .. )
|
||||
include_directories( .. ../linux )
|
||||
link_libraries(simdjson-windows-headers test-data)
|
||||
link_libraries(simdjson)
|
||||
link_libraries(counters)
|
||||
|
||||
add_executable(perfdiff perfdiff.cpp)
|
||||
add_executable(parse parse.cpp)
|
||||
add_executable(parse_stream parse_stream.cpp)
|
||||
|
||||
add_executable(statisticalmodel statisticalmodel.cpp)
|
||||
|
||||
add_executable(parse_noutf8validation parse.cpp)
|
||||
target_compile_definitions(parse_noutf8validation PRIVATE SIMDJSON_SKIPUTF8VALIDATION)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#include <counters/event_counter.h>
|
||||
using namespace counters;
|
||||
#include "event_counter.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cctype>
|
||||
@@ -25,6 +24,7 @@ using namespace counters;
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "linux-perf-events.h"
|
||||
#ifdef __linux__
|
||||
#include <libgen.h>
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,207 @@
|
||||
#include <iostream>
|
||||
#include <unistd.h>
|
||||
#include "simdjson.h"
|
||||
#ifdef __linux__
|
||||
#include "linux-perf-events.h"
|
||||
#endif
|
||||
|
||||
size_t count_nonasciibytes(const uint8_t *input, size_t length) {
|
||||
size_t count = 0;
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
count += input[i] >> 7;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
size_t count_backslash(const uint8_t *input, size_t length) {
|
||||
size_t count = 0;
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
count += (input[i] == '\\') ? 1 : 0;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
struct stat_s {
|
||||
size_t integer_count;
|
||||
size_t float_count;
|
||||
size_t string_count;
|
||||
size_t backslash_count;
|
||||
size_t non_ascii_byte_count;
|
||||
size_t object_count;
|
||||
size_t array_count;
|
||||
size_t null_count;
|
||||
size_t true_count;
|
||||
size_t false_count;
|
||||
size_t byte_count;
|
||||
size_t structural_indexes_count;
|
||||
bool valid;
|
||||
};
|
||||
|
||||
using stat_t = struct stat_s;
|
||||
|
||||
|
||||
|
||||
simdjson_inline void simdjson_process_atom(stat_t &s,
|
||||
simdjson::dom::element element) {
|
||||
if (element.is<int64_t>()) {
|
||||
s.integer_count++;
|
||||
} else if(element.is<std::string_view>()) {
|
||||
s.string_count++;
|
||||
} else if(element.is<double>()) {
|
||||
s.float_count++;
|
||||
} else if (element.is<bool>()) {
|
||||
bool v;
|
||||
simdjson::error_code error;
|
||||
if ((error = element.get(v))) { std::cerr << error << std::endl; abort(); }
|
||||
if (v) {
|
||||
s.true_count++;
|
||||
} else {
|
||||
s.false_count++;
|
||||
}
|
||||
} else if (element.is_null()) {
|
||||
s.null_count++;
|
||||
}
|
||||
}
|
||||
|
||||
void simdjson_recurse(stat_t &s, simdjson::dom::element element) {
|
||||
simdjson::error_code error;
|
||||
if (element.is<simdjson::dom::array>()) {
|
||||
s.array_count++;
|
||||
simdjson::dom::array array;
|
||||
if ((error = element.get(array))) { std::cerr << error << std::endl; abort(); }
|
||||
for (auto child : array) {
|
||||
if (child.is<simdjson::dom::array>() || child.is<simdjson::dom::object>()) {
|
||||
simdjson_recurse(s, child);
|
||||
} else {
|
||||
simdjson_process_atom(s, child);
|
||||
}
|
||||
}
|
||||
} else if (element.is<simdjson::dom::object>()) {
|
||||
s.object_count++;
|
||||
simdjson::dom::object object;
|
||||
if ((error = element.get(object))) { std::cerr << error << std::endl; abort(); }
|
||||
for (auto field : object) {
|
||||
s.string_count++; // for key
|
||||
if (field.value.is<simdjson::dom::array>() || field.value.is<simdjson::dom::object>()) {
|
||||
simdjson_recurse(s, field.value);
|
||||
} else {
|
||||
simdjson_process_atom(s, field.value);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
simdjson_process_atom(s, element);
|
||||
}
|
||||
}
|
||||
|
||||
stat_t simdjson_compute_stats(const simdjson::padded_string &p) {
|
||||
stat_t answer{};
|
||||
simdjson::dom::parser parser;
|
||||
simdjson::dom::element doc;
|
||||
auto error = parser.parse(p).get(doc);
|
||||
if (error) {
|
||||
answer.valid = false;
|
||||
return answer;
|
||||
}
|
||||
answer.valid = true;
|
||||
answer.backslash_count =
|
||||
count_backslash(reinterpret_cast<const uint8_t *>(p.data()), p.size());
|
||||
answer.non_ascii_byte_count = count_nonasciibytes(
|
||||
reinterpret_cast<const uint8_t *>(p.data()), p.size());
|
||||
answer.byte_count = p.size();
|
||||
answer.structural_indexes_count = parser.implementation->n_structural_indexes;
|
||||
simdjson_recurse(answer, doc);
|
||||
return answer;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
#ifndef _MSC_VER
|
||||
int c;
|
||||
while ((c = getopt(argc, argv, "")) != -1) {
|
||||
switch (c) {
|
||||
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
#else
|
||||
int optind = 1;
|
||||
#endif
|
||||
if (optind >= argc) {
|
||||
std::cerr << "Reads json, prints stats. " << std::endl;
|
||||
std::cerr << "Usage: " << argv[0] << " <jsonfile>" << std::endl;
|
||||
|
||||
exit(1);
|
||||
}
|
||||
const char *filename = argv[optind];
|
||||
if (optind + 1 < argc) {
|
||||
std::cerr << "warning: ignoring everything after " << argv[optind + 1]
|
||||
<< std::endl;
|
||||
}
|
||||
simdjson::padded_string p;
|
||||
auto error = simdjson::padded_string::load(filename).get(p);
|
||||
if (error) {
|
||||
std::cerr << "Could not load the file " << filename << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
stat_t s = simdjson_compute_stats(p);
|
||||
if (!s.valid) {
|
||||
std::cerr << "not a valid JSON" << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
printf("# integer_count float_count string_count backslash_count "
|
||||
"non_ascii_byte_count object_count array_count null_count true_count "
|
||||
"false_count byte_count structural_indexes_count ");
|
||||
#ifdef __linux__
|
||||
printf(" stage1_cycle_count stage1_instruction_count stage2_cycle_count "
|
||||
" stage2_instruction_count stage3_cycle_count "
|
||||
"stage3_instruction_count ");
|
||||
#else
|
||||
printf("(you are not under linux, so perf counters are disaabled)");
|
||||
#endif
|
||||
printf("\n");
|
||||
printf("%zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu ", s.integer_count,
|
||||
s.float_count, s.string_count, s.backslash_count,
|
||||
s.non_ascii_byte_count, s.object_count, s.array_count, s.null_count,
|
||||
s.true_count, s.false_count, s.byte_count, s.structural_indexes_count);
|
||||
#ifdef __linux__
|
||||
simdjson::dom::parser parser;
|
||||
simdjson::error_code alloc_error = parser.allocate(p.size());
|
||||
if (alloc_error) {
|
||||
std::cerr << alloc_error << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
const uint32_t iterations = p.size() < 1 * 1000 * 1000 ? 1000 : 50;
|
||||
std::vector<int> evts;
|
||||
evts.push_back(PERF_COUNT_HW_CPU_CYCLES);
|
||||
evts.push_back(PERF_COUNT_HW_INSTRUCTIONS);
|
||||
LinuxEvents<PERF_TYPE_HARDWARE> unified(evts);
|
||||
unsigned long cy1 = 0, cy2 = 0;
|
||||
unsigned long cl1 = 0, cl2 = 0;
|
||||
std::vector<unsigned long long> results;
|
||||
results.resize(evts.size());
|
||||
for (uint32_t i = 0; i < iterations; i++) {
|
||||
unified.start();
|
||||
// The default template is simdjson::architecture::NATIVE.
|
||||
bool isok = (parser.implementation->stage1((const uint8_t *)p.data(), p.size(), simdjson::stage1_mode::regular) == simdjson::SUCCESS);
|
||||
unified.end(results);
|
||||
|
||||
cy1 += results[0];
|
||||
cl1 += results[1];
|
||||
|
||||
unified.start();
|
||||
isok = isok && (parser.implementation->stage2(parser.doc) == simdjson::SUCCESS);
|
||||
unified.end(results);
|
||||
|
||||
cy2 += results[0];
|
||||
cl2 += results[1];
|
||||
if (!isok) {
|
||||
std::cerr << "failure?" << std::endl;
|
||||
}
|
||||
}
|
||||
printf("%f %f %f %f ", static_cast<double>(cy1) / static_cast<double>(iterations), static_cast<double>(cl1) / static_cast<double>(iterations),
|
||||
static_cast<double>(cy2) / static_cast<double>(iterations), static_cast<double>(cl2) / static_cast<double>(iterations));
|
||||
#endif // __linux__
|
||||
printf("\n");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
#ifndef __EVENT_COUNTER_H
|
||||
#define __EVENT_COUNTER_H
|
||||
|
||||
#ifndef SIMDJSON_SIMPLE_PERFORMANCE_COUNTERS
|
||||
#ifdef __aarch64__
|
||||
// on ARM, we use just cycles and instructions
|
||||
#define SIMDJSON_SIMPLE_PERFORMANCE_COUNTERS 1
|
||||
#else
|
||||
// elsewhere, we try to use four counters.
|
||||
#define SIMDJSON_SIMPLE_PERFORMANCE_COUNTERS 0
|
||||
#endif
|
||||
#endif
|
||||
#include <cassert>
|
||||
#include <cctype>
|
||||
#ifndef _MSC_VER
|
||||
#include <dirent.h>
|
||||
#endif
|
||||
#include <unistd.h>
|
||||
#include <cinttypes>
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#ifdef __linux__
|
||||
#include "linux-perf-events.h"
|
||||
#include <libgen.h>
|
||||
#endif
|
||||
|
||||
#if __APPLE__ && __aarch64__
|
||||
#include "apple/apple_arm_events.h"
|
||||
#endif
|
||||
|
||||
#include "simdjson.h"
|
||||
|
||||
using std::string;
|
||||
using std::vector;
|
||||
using std::chrono::steady_clock;
|
||||
using std::chrono::time_point;
|
||||
using std::chrono::duration;
|
||||
|
||||
struct event_count {
|
||||
duration<double> elapsed;
|
||||
vector<unsigned long long> event_counts;
|
||||
event_count() : elapsed(0), event_counts{0,0,0,0,0} {}
|
||||
event_count(const duration<double> _elapsed, const vector<unsigned long long> _event_counts) : elapsed(_elapsed), event_counts(_event_counts) {}
|
||||
event_count(const event_count& other): elapsed(other.elapsed), event_counts(other.event_counts) { }
|
||||
|
||||
// The types of counters (so we can read the getter more easily)
|
||||
#if SIMDJSON_SIMPLE_PERFORMANCE_COUNTERS
|
||||
enum event_counter_types {
|
||||
CPU_CYCLES,
|
||||
INSTRUCTIONS
|
||||
};
|
||||
#else
|
||||
enum event_counter_types {
|
||||
CPU_CYCLES,
|
||||
INSTRUCTIONS,
|
||||
BRANCH_MISSES,
|
||||
CACHE_REFERENCES,
|
||||
CACHE_MISSES
|
||||
};
|
||||
#endif
|
||||
double elapsed_sec() const { return duration<double>(elapsed).count(); }
|
||||
double elapsed_ns() const { return duration<double, std::nano>(elapsed).count(); }
|
||||
double cycles() const { return static_cast<double>(event_counts[CPU_CYCLES]); }
|
||||
double instructions() const { return static_cast<double>(event_counts[INSTRUCTIONS]); }
|
||||
#if !SIMDJSON_SIMPLE_PERFORMANCE_COUNTERS
|
||||
double branch_misses() const { return static_cast<double>(event_counts[BRANCH_MISSES]); }
|
||||
double cache_references() const { return static_cast<double>(event_counts[CACHE_REFERENCES]); }
|
||||
double cache_misses() const { return static_cast<double>(event_counts[CACHE_MISSES]); }
|
||||
#endif
|
||||
event_count& operator=(const event_count& other) {
|
||||
this->elapsed = other.elapsed;
|
||||
this->event_counts = other.event_counts;
|
||||
return *this;
|
||||
}
|
||||
event_count operator+(const event_count& other) const {
|
||||
return event_count(elapsed+other.elapsed, {
|
||||
event_counts[0]+other.event_counts[0],
|
||||
event_counts[1]+other.event_counts[1],
|
||||
event_counts[2]+other.event_counts[2],
|
||||
event_counts[3]+other.event_counts[3],
|
||||
event_counts[4]+other.event_counts[4],
|
||||
});
|
||||
}
|
||||
|
||||
void operator+=(const event_count& other) {
|
||||
*this = *this + other;
|
||||
}
|
||||
};
|
||||
|
||||
struct event_aggregate {
|
||||
int iterations = 0;
|
||||
event_count total{};
|
||||
event_count best{};
|
||||
event_count worst{};
|
||||
|
||||
event_aggregate() {}
|
||||
|
||||
void operator<<(const event_count& other) {
|
||||
if (iterations == 0 || other.elapsed < best.elapsed) {
|
||||
best = other;
|
||||
}
|
||||
if (iterations == 0 || other.elapsed > worst.elapsed) {
|
||||
worst = other;
|
||||
}
|
||||
iterations++;
|
||||
total += other;
|
||||
}
|
||||
|
||||
double elapsed_sec() const { return total.elapsed_sec() / iterations; }
|
||||
double total_elapsed_ns() const { return total.elapsed_ns(); }
|
||||
double elapsed_ns() const { return total.elapsed_ns() / iterations; }
|
||||
double cycles() const { return total.cycles() / iterations; }
|
||||
double instructions() const { return total.instructions() / iterations; }
|
||||
#if !SIMDJSON_SIMPLE_PERFORMANCE_COUNTERS
|
||||
double branch_misses() const { return total.branch_misses() / iterations; }
|
||||
double cache_references() const { return total.cache_references() / iterations; }
|
||||
double cache_misses() const { return total.cache_misses() / iterations; }
|
||||
#endif
|
||||
};
|
||||
|
||||
struct event_collector {
|
||||
event_count count{};
|
||||
time_point<steady_clock> start_clock{};
|
||||
|
||||
#if defined(__linux__)
|
||||
LinuxEvents<PERF_TYPE_HARDWARE> linux_events;
|
||||
event_collector() : linux_events(vector<int>{
|
||||
#if SIMDJSON_SIMPLE_PERFORMANCE_COUNTERS
|
||||
PERF_COUNT_HW_CPU_CYCLES,
|
||||
PERF_COUNT_HW_INSTRUCTIONS,
|
||||
#else
|
||||
PERF_COUNT_HW_CPU_CYCLES,
|
||||
PERF_COUNT_HW_INSTRUCTIONS,
|
||||
PERF_COUNT_HW_BRANCH_MISSES,
|
||||
PERF_COUNT_HW_CACHE_REFERENCES,
|
||||
PERF_COUNT_HW_CACHE_MISSES
|
||||
#endif
|
||||
}) {}
|
||||
bool has_events() {
|
||||
return linux_events.is_working();
|
||||
}
|
||||
#elif __APPLE__ && __aarch64__
|
||||
AppleEvents apple_events;
|
||||
performance_counters diff;
|
||||
event_collector() : diff(0) {
|
||||
apple_events.setup_performance_counters();
|
||||
}
|
||||
bool has_events() {
|
||||
return apple_events.setup_performance_counters();
|
||||
}
|
||||
#else
|
||||
event_collector() {}
|
||||
bool has_events() {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
simdjson_inline void start() {
|
||||
#if defined(__linux)
|
||||
linux_events.start();
|
||||
#elif __APPLE__ && __aarch64__
|
||||
if(has_events()) { diff = apple_events.get_counters(); }
|
||||
#endif
|
||||
start_clock = steady_clock::now();
|
||||
}
|
||||
simdjson_inline event_count& end() {
|
||||
time_point<steady_clock> end_clock = steady_clock::now();
|
||||
#if defined(__linux)
|
||||
linux_events.end(count.event_counts);
|
||||
#elif __APPLE__ && __aarch64__
|
||||
if(has_events()) {
|
||||
performance_counters end = apple_events.get_counters();
|
||||
diff = end - diff;
|
||||
}
|
||||
count.event_counts[0] = diff.cycles;
|
||||
count.event_counts[1] = diff.instructions;
|
||||
count.event_counts[2] = diff.missed_branches;
|
||||
count.event_counts[3] = 0;
|
||||
count.event_counts[4] = 0;
|
||||
#endif
|
||||
count.elapsed = end_clock - start_clock;
|
||||
return count;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,8 +1,7 @@
|
||||
#ifndef BENCHMARK_HELPERS_H
|
||||
#define BENCHMARK_HELPERS_H
|
||||
|
||||
#include <counters/event_counter.h>
|
||||
using namespace counters;
|
||||
#include "event_counter.h"
|
||||
#include <atomic>
|
||||
|
||||
event_collector collector;
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "simdjson.h"
|
||||
#include <counters/event_counter.h>
|
||||
using namespace counters;
|
||||
#include "event_counter.h"
|
||||
#include <iostream>
|
||||
|
||||
namespace json_benchmark {
|
||||
@@ -59,7 +58,11 @@ template<typename B, typename R> static void run_json_benchmark(benchmark::State
|
||||
if (collector.has_events()) {
|
||||
state.counters["instructions"] = events.instructions();
|
||||
state.counters["cycles"] = events.cycles();
|
||||
#if !SIMDJSON_SIMPLE_PERFORMANCE_COUNTERS
|
||||
state.counters["branch_miss"] = events.branch_misses();
|
||||
state.counters["cache_miss"] = events.cache_misses();
|
||||
state.counters["cache_ref"] = events.cache_references();
|
||||
#endif
|
||||
state.counters["instructions_per_byte"] = events.instructions() / double(bench.bytes_per_iteration());
|
||||
state.counters["instructions_per_cycle"] = events.instructions() / events.cycles();
|
||||
state.counters["cycles_per_byte"] = events.cycles() / double(bench.bytes_per_iteration());
|
||||
@@ -67,7 +70,11 @@ template<typename B, typename R> static void run_json_benchmark(benchmark::State
|
||||
|
||||
state.counters["best_instructions"] = events.best.instructions();
|
||||
state.counters["best_cycles"] = events.best.cycles();
|
||||
#if !SIMDJSON_SIMPLE_PERFORMANCE_COUNTERS
|
||||
state.counters["best_branch_miss"] = events.best.branch_misses();
|
||||
state.counters["best_cache_miss"] = events.best.cache_misses();
|
||||
state.counters["best_cache_ref"] = events.best.cache_references();
|
||||
#endif
|
||||
|
||||
state.counters["best_instructions_per_byte"] = events.best.instructions() / double(bench.bytes_per_iteration());
|
||||
state.counters["best_instructions_per_cycle"] = events.best.instructions() / events.best.cycles();
|
||||
@@ -88,7 +95,11 @@ template<typename B, typename R> static void run_json_benchmark(benchmark::State
|
||||
if (collector.has_events()) {
|
||||
label << " instructions=" << setw(12) << uint64_t(events.best.instructions()) << setw(0);
|
||||
label << " cycles=" << setw(12) << uint64_t(events.best.cycles()) << setw(0);
|
||||
#if !SIMDJSON_SIMPLE_PERFORMANCE_COUNTERS
|
||||
label << " branch_miss=" << setw(8) << uint64_t(events.best.branch_misses()) << setw(0);
|
||||
label << " cache_miss=" << setw(8) << uint64_t(events.best.cache_misses()) << setw(0);
|
||||
label << " cache_ref=" << setw(10) << uint64_t(events.best.cache_references()) << setw(0);
|
||||
#endif
|
||||
}
|
||||
|
||||
label << " items=" << setw(10) << bench.items_per_iteration() << setw(0);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "json_benchmark/string_runner.h"
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
#pragma once
|
||||
#ifdef __linux__
|
||||
|
||||
#include <asm/unistd.h> // for __NR_perf_event_open
|
||||
#include <linux/perf_event.h> // for perf event constants
|
||||
#include <sys/ioctl.h> // for ioctl
|
||||
#include <unistd.h> // for syscall
|
||||
|
||||
#include <cerrno> // for errno
|
||||
#include <cstring> // for memset
|
||||
#include <stdexcept>
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
template <int TYPE = PERF_TYPE_HARDWARE> class LinuxEvents {
|
||||
int fd;
|
||||
bool working;
|
||||
perf_event_attr attribs{};
|
||||
size_t num_events{};
|
||||
std::vector<uint64_t> temp_result_vec{};
|
||||
std::vector<uint64_t> ids{};
|
||||
|
||||
public:
|
||||
explicit LinuxEvents(std::vector<int> config_vec) : fd(0), working(true) {
|
||||
memset(&attribs, 0, sizeof(attribs));
|
||||
attribs.type = TYPE;
|
||||
attribs.size = sizeof(attribs);
|
||||
attribs.disabled = 1;
|
||||
attribs.exclude_kernel = 1;
|
||||
attribs.exclude_hv = 1;
|
||||
|
||||
attribs.sample_period = 0;
|
||||
attribs.read_format = PERF_FORMAT_GROUP | PERF_FORMAT_ID;
|
||||
const int pid = 0; // the current process
|
||||
const int cpu = -1; // all CPUs
|
||||
const unsigned long flags = 0;
|
||||
|
||||
int group = -1; // no group
|
||||
num_events = config_vec.size();
|
||||
ids.resize(config_vec.size());
|
||||
uint32_t i = 0;
|
||||
for (auto config : config_vec) {
|
||||
attribs.config = config;
|
||||
int _fd = static_cast<int>(syscall(__NR_perf_event_open, &attribs, pid, cpu, group, flags));
|
||||
if (_fd == -1) {
|
||||
report_error("perf_event_open");
|
||||
}
|
||||
ioctl(_fd, PERF_EVENT_IOC_ID, &ids[i++]);
|
||||
if (group == -1) {
|
||||
group = _fd;
|
||||
fd = _fd;
|
||||
}
|
||||
}
|
||||
|
||||
temp_result_vec.resize(num_events * 2 + 1);
|
||||
}
|
||||
|
||||
~LinuxEvents() { if (fd != -1) { close(fd); } }
|
||||
|
||||
inline void start() {
|
||||
if (fd != -1) {
|
||||
if (ioctl(fd, PERF_EVENT_IOC_RESET, PERF_IOC_FLAG_GROUP) == -1) {
|
||||
report_error("ioctl(PERF_EVENT_IOC_RESET)");
|
||||
}
|
||||
|
||||
if (ioctl(fd, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP) == -1) {
|
||||
report_error("ioctl(PERF_EVENT_IOC_ENABLE)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void end(std::vector<unsigned long long> &results) {
|
||||
if (fd != -1) {
|
||||
if (ioctl(fd, PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP) == -1) {
|
||||
report_error("ioctl(PERF_EVENT_IOC_DISABLE)");
|
||||
}
|
||||
|
||||
if (read(fd, temp_result_vec.data(), temp_result_vec.size() * 8) == -1) {
|
||||
report_error("read");
|
||||
}
|
||||
}
|
||||
// our actual results are in slots 1,3,5, ... of this structure
|
||||
for (uint32_t i = 1; i < temp_result_vec.size(); i += 2) {
|
||||
results[i / 2] = temp_result_vec[i];
|
||||
}
|
||||
for (uint32_t i = 2; i < temp_result_vec.size(); i += 2) {
|
||||
if(ids[i/2-1] != temp_result_vec[i]) {
|
||||
report_error("event mismatch");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
bool is_working() {
|
||||
return working;
|
||||
}
|
||||
|
||||
private:
|
||||
void report_error(const std::string &) {
|
||||
working = false;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
@@ -1,7 +1,6 @@
|
||||
#ifndef BENCHMARK_HELPER_HPP
|
||||
#define BENCHMARK_HELPER_HPP
|
||||
#include <counters/event_counter.h>
|
||||
using namespace counters;
|
||||
#include "event_counter.h"
|
||||
#include <atomic>
|
||||
|
||||
inline event_collector &get_collector() {
|
||||
|
||||
Vendored
-8
@@ -20,14 +20,6 @@ if(SIMDJSON_GOOGLE_BENCHMARKS)
|
||||
)
|
||||
endif()
|
||||
|
||||
CPMAddPackage(
|
||||
NAME counters
|
||||
URL https://github.com/lemire/counters/archive/refs/tags/v3.1.0.zip
|
||||
OPTIONS
|
||||
"COUNTERS_BUILD_TESTS OFF"
|
||||
"COUNTERS_INSTALL OFF"
|
||||
)
|
||||
|
||||
CPMAddPackage(
|
||||
NAME simdjson-data
|
||||
URL https://github.com/simdjson/simdjson-data/archive/351949906abde446f0314bf79606fb5d884f5be7.zip
|
||||
|
||||
+27
-120
@@ -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.
|
||||
@@ -272,35 +269,10 @@ ondemand::document doc = parser.iterate(simdjson::pad(json));
|
||||
We recommend against creating many `std::string` or many `std::padded_string` instances in your application to store your JSON data.
|
||||
Consider reusing the same buffers and limiting memory allocations.
|
||||
|
||||
**Memory-file mapping.** You can use `simdjson::padded_memory_map` to create a
|
||||
`simdjson::padded_string_view` from a file on disk. On POSIX systems (Linux,
|
||||
macOS, BSD, ...) it uses `mmap` for true zero-copy access and is always
|
||||
available. On Windows it is an **opt-in** feature because it relies on the
|
||||
`CreateFileMapping2` / `MapViewOfFile3` APIs (Windows 10, version 1803 or
|
||||
later) which are exported from `onecore.lib` rather than the default
|
||||
`kernel32.lib`. To enable it, you must satisfy **all** of the following:
|
||||
|
||||
1. Building simdjson with `-DSIMDJSON_ENABLE_MEMORY_FILE_MAPPING_ON_WINDOWS=ON`, or
|
||||
defining `SIMDJSON_ENABLE_MEMORY_FILE_MAPPING_ON_WINDOWS=1` and raising
|
||||
`NTDDI_VERSION` to at least `NTDDI_WIN10_RS4` (Windows 10, version 1803)
|
||||
and linking `onecore.lib` manually if you are consuming simdjson as a
|
||||
pre-built library.
|
||||
2. `#include <windows.h>` before including simdjson, in every translation
|
||||
unit that uses `padded_memory_map`.
|
||||
|
||||
The Windows implementation then uses `CreateFileMapping2` / `MapViewOfFile3`
|
||||
for true zero-copy access whenever possible, with a transparent
|
||||
buffered-read fallback for files that end too close to a page boundary.
|
||||
|
||||
The availability of the class can be tested with the preprocessor macro
|
||||
`SIMDJSON_HAS_PADDED_MEMORY_MAP`.
|
||||
**Memory-file mapping (non-Windows).** You can use memory-file mapping to create a `simdjson::padded_string_view`
|
||||
from a file on disk:
|
||||
|
||||
```cpp
|
||||
#ifdef _WIN32
|
||||
#include <windows.h> // Must come BEFORE <simdjson.h> on Windows
|
||||
#endif
|
||||
#include "simdjson.h"
|
||||
// ...
|
||||
simdjson::padded_memory_map map(myfilename);
|
||||
if (!map.is_valid()) { /* handle error */ }
|
||||
simdjson::padded_string_view view = map.view();
|
||||
@@ -333,7 +305,7 @@ Some users may want to browse code along with the compiled assembly:
|
||||
| `simdjson::pad(std::string&)` | Adds padding if needed | Returns `padded_string_view` pointing to the (possibly resized) string | References original string | Recommended to silence sanitizers when using `std::string`. |
|
||||
| `padded_string(data, length)` or `padded_string(std::string)` | Automatic (copies into padded buffer) | Explicit copy into owned padded buffer | Owned by `padded_string` | Safe when you want full ownership and padding guaranteed. |
|
||||
| `padded_string_view` (manual) | User guarantees `SIMDJSON_PADDING` extra bytes after the viewed length | User provides pointer + length + capacity | Non-owning view | Low-level; requires careful buffer management. |
|
||||
| Memory-mapped file (`padded_memory_map`) | Automatic via mapping / padded read | Creates view with sufficient padding | Non-owning (tied to map lifetime) | Always available on POSIX (zero-copy `mmap`). On Windows, opt-in via `-DSIMDJSON_ENABLE_MEMORY_FILE_MAPPING_ON_WINDOWS=ON` (requires Windows 10 1803+ and links `onecore.lib`) and `#include <windows.h>` before simdjson; uses `CreateFileMapping2` + `MapViewOfFile3`. |
|
||||
| Memory-mapped file (`padded_memory_map`) | Automatic via mapping (non-Windows only) | Creates view with sufficient padding | Non-owning (tied to map lifetime) | Advanced; efficient for large files on Linux/macOS/etc. |
|
||||
|
||||
|
||||
Documents are iterators
|
||||
@@ -1443,8 +1415,6 @@ With this code, deserializing an `std::list<Car>` instance would capture only th
|
||||
that are not made by Toyota.
|
||||
|
||||
|
||||
**Performance tip**: You will get better performance if you order the attributes (make, model)
|
||||
in the order they appear in the JSON document.
|
||||
|
||||
|
||||
### 3. Using static reflection (C++26)
|
||||
@@ -1521,10 +1491,6 @@ void f() {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
**Performance tip**: You will get better performance if you order the attributes (make, model)
|
||||
in the order they appear in the JSON document.
|
||||
|
||||
#### Special cases
|
||||
|
||||
However, there are instances where the construction cannot
|
||||
@@ -1602,10 +1568,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 +1808,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 +1845,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
|
||||
|
||||
+3
-26
@@ -127,31 +127,11 @@ codepage, and they may call SetFileApisToOEM accordingly.
|
||||
|
||||
|
||||
**Advanced feature:**
|
||||
You can use `simdjson::padded_memory_map` to create a `simdjson::padded_string_view`
|
||||
from a file on disk without copying the file contents into your own buffer.
|
||||
On POSIX systems (Linux, macOS, BSD, ...) it uses `mmap` for true zero-copy
|
||||
access. On Windows it is available as an **opt-in** feature and requires:
|
||||
|
||||
1. Building simdjson with `-DSIMDJSON_ENABLE_MEMORY_FILE_MAPPING_ON_WINDOWS=ON`, or
|
||||
defining `SIMDJSON_ENABLE_MEMORY_FILE_MAPPING_ON_WINDOWS=1` and raising
|
||||
`NTDDI_VERSION` to at least `NTDDI_WIN10_RS4` (Windows 10, version 1803)
|
||||
and linking `onecore.lib` manually if you are consuming simdjson as a
|
||||
pre-built library.
|
||||
2. `#include <windows.h>` before `#include "simdjson.h"` in every
|
||||
translation unit where you want to use `padded_memory_map`.
|
||||
|
||||
When enabled on Windows, the implementation uses `CreateFileMapping2` and
|
||||
`MapViewOfFile3` for true zero-copy mapping whenever the file does not end
|
||||
within `SIMDJSON_PADDING` bytes of a page boundary; otherwise it falls back
|
||||
to reading the file into a padded heap buffer. If those requirements are
|
||||
not met, the class is not declared and the code below will fail to compile.
|
||||
On non-Windows systems, you can use memory-file mapping to create a `simdjson::padded_string_view`
|
||||
from a file on disk.
|
||||
|
||||
```cpp
|
||||
#ifdef _WIN32
|
||||
#include <windows.h> // Must come BEFORE <simdjson.h> on Windows
|
||||
#endif
|
||||
#include "simdjson.h"
|
||||
// ...
|
||||
// if the macro _WIN32 is defined, this will not work since we do not support Windows
|
||||
simdjson::padded_memory_map map(TWITTER_JSON);
|
||||
if (!map.is_valid()) { /* handle error */ }
|
||||
simdjson::padded_string_view view = map.view(); // view is usable while padded_memory_map is in scope
|
||||
@@ -790,9 +770,6 @@ void basics_treewalk_1() {
|
||||
}
|
||||
```
|
||||
|
||||
Notice that we do not include `dom::element_type::BIGINT` in this example
|
||||
as `dom::element_type::BIGINT` type is only generated if the parser was
|
||||
set to support big integers (`parser.number_as_string(true)`).
|
||||
|
||||
|
||||
Reusing the parser for maximum efficiency
|
||||
|
||||
+34
-352
@@ -22,12 +22,10 @@ Contents
|
||||
- [Threads](#threads)
|
||||
- [Support](#support)
|
||||
- [API](#api)
|
||||
- [Streaming directly from a memory-mapped file](#streaming-directly-from-a-memory-mapped-file)
|
||||
- [Use cases](#use-cases)
|
||||
- [Tracking your position](#tracking-your-position)
|
||||
- [Incomplete streams](#incomplete-streams)
|
||||
- [C++20 features](#c20-features)
|
||||
- [C++26 features (static reflection)](#c26-features-static-reflection)
|
||||
|
||||
Motivation
|
||||
-----------
|
||||
@@ -134,7 +132,7 @@ E.g., `[1,2]{"32":1}` is recognized as two documents.
|
||||
Some official formats **(non-exhaustive list)**:
|
||||
- [Newline-Delimited JSON (NDJSON)](https://github.com/ndjson/ndjson-spec/)
|
||||
- [JSON lines (JSONL)](http://jsonlines.org/)
|
||||
- [Record separator-delimited JSON (RFC 7464)](https://tools.ietf.org/html/rfc7464)
|
||||
- [Record separator-delimited JSON (RFC 7464)](https://tools.ietf.org/html/rfc7464) <- Not supported by simdjson!
|
||||
- [More on Wikipedia...](https://en.wikipedia.org/wiki/JSON_streaming)
|
||||
|
||||
API
|
||||
@@ -157,79 +155,13 @@ for (auto doc : docs) {
|
||||
See [basics.md](basics.md#newline-delimited-json-ndjson-and-json-lines) for an overview of the API.
|
||||
|
||||
|
||||
Streaming directly from a memory-mapped file
|
||||
--------------------------------------------
|
||||
|
||||
When your input is a large NDJSON / JSON-lines file on disk, the most efficient
|
||||
way to feed `iterate_many` is to use `simdjson::padded_memory_map`. It returns
|
||||
a `padded_string_view` with the right amount of trailing padding, so you can
|
||||
hand it straight to `iterate_many` without ever copying the file contents into
|
||||
your own buffer.
|
||||
|
||||
`padded_memory_map` is available on POSIX systems (Linux, macOS, BSD, ...) by
|
||||
default. On Windows it is an **opt-in** feature with the following
|
||||
requirements:
|
||||
|
||||
1. Build simdjson with `-DSIMDJSON_ENABLE_MEMORY_FILE_MAPPING_ON_WINDOWS=ON`, or — if
|
||||
you consume simdjson as a pre-built library — define
|
||||
`SIMDJSON_ENABLE_MEMORY_FILE_MAPPING_ON_WINDOWS=1`, raise `NTDDI_VERSION` to at
|
||||
least `NTDDI_WIN10_RS4` (`0x0A000005`, Windows 10 version 1803), and
|
||||
add `onecore.lib` to your link line yourself. The Windows
|
||||
implementation uses the modern memory APIs `CreateFileMapping2` /
|
||||
`MapViewOfFile3`, which are available starting with that version of
|
||||
Windows and are exported by `onecore.lib`.
|
||||
2. `#include <windows.h>` before `#include "simdjson.h"` in every
|
||||
translation unit where you want to use `padded_memory_map`. simdjson
|
||||
deliberately does not pull in `<windows.h>` itself, so the class is
|
||||
only declared when the Win32 types are already visible.
|
||||
|
||||
If either requirement is not met on Windows, the `padded_memory_map` class is
|
||||
not declared at all and any code that references it fails to compile with an
|
||||
"unknown identifier" error. The availability of the class can be tested with
|
||||
the macro `SIMDJSON_HAS_PADDED_MEMORY_MAP`.
|
||||
|
||||
On POSIX, `padded_memory_map` uses `mmap` to map the file directly into
|
||||
memory with zero copies. On Windows (when enabled), it uses
|
||||
`CreateFileMapping2` + `MapViewOfFile3` for true zero-copy mapping
|
||||
whenever the file does not end within `SIMDJSON_PADDING` bytes of a page
|
||||
boundary; for those rare cases, it transparently falls back to reading
|
||||
the file into a heap-allocated padded buffer so that the returned view
|
||||
always has `SIMDJSON_PADDING` accessible zero bytes after the file content.
|
||||
|
||||
```cpp
|
||||
#ifdef _WIN32
|
||||
#include <windows.h> // Must come BEFORE <simdjson.h> on Windows
|
||||
#endif
|
||||
#include "simdjson.h"
|
||||
|
||||
// ...
|
||||
|
||||
simdjson::padded_memory_map map("huge_stream.ndjson");
|
||||
if (!map.is_valid()) { /* file missing, unreadable, too large, ... */ return; }
|
||||
|
||||
simdjson::ondemand::parser parser;
|
||||
simdjson::ondemand::document_stream stream;
|
||||
auto error = parser.iterate_many(map.view()).get(stream);
|
||||
if (error) { std::cerr << error << std::endl; return; }
|
||||
|
||||
for (auto doc : stream) {
|
||||
// process each JSON document in the stream
|
||||
std::cout << doc << std::endl;
|
||||
}
|
||||
```
|
||||
|
||||
Important lifetime rule: the `padded_string_view` returned by `map.view()` is
|
||||
only valid while the `padded_memory_map` instance is alive, so keep `map`
|
||||
alive for as long as you are iterating the stream.
|
||||
|
||||
The file must not be modified while the memory map is in use. If you need a
|
||||
fully independent copy of the data, use `simdjson::padded_string::load(...)`
|
||||
instead.
|
||||
|
||||
If you prefer single-document parsing on a memory-mapped file, the same
|
||||
pattern applies to `parser.iterate(...)`:
|
||||
**Advanced feature:**
|
||||
On non-Windows systems, you can use memory-file mapping to create a `simdjson::padded_string_view`
|
||||
from a file on disk.
|
||||
|
||||
```cpp
|
||||
// If the macro _WIN32 is defined, this will not work since we do not support memory-file mapping
|
||||
// under Windows at this time.
|
||||
simdjson::padded_memory_map map(myfilename);
|
||||
if (!map.is_valid()) { /* handle error */ }
|
||||
simdjson::padded_string_view view = map.view(); // view is usable while padded_memory_map is in scope
|
||||
@@ -346,131 +278,39 @@ Importantly, you should only call `truncated_bytes()` after iterating through al
|
||||
Comma-separated documents
|
||||
-----------
|
||||
|
||||
To parse comma-separated documents like `{"a":1},{"b":2},{"c":3}`, use the `stream_format::comma_delimited` parameter:
|
||||
We also support comma-separated documents, but with some performance limitations. The `iterate_many` function takes in an option to allow parsing of comma separated documents (which defaults on false). In this mode, the entire buffer is processed in one batch. Therefore, the total size of the document should not exceed the maximal capacity of the parser (4 GB). This mode also effectively disallow multithreading. It is therefore mostly suitable for not "very large" inputs. In this mode, the batch_size parameter
|
||||
is effectively ignored, as it is set to at least the document size.
|
||||
|
||||
Example:
|
||||
|
||||
```cpp
|
||||
auto json = R"({"a":1},{"b":2},{"c":3})"_padded;
|
||||
ondemand::parser parser;
|
||||
ondemand::document_stream stream;
|
||||
auto error = parser.iterate_many(json, ondemand::DEFAULT_BATCH_SIZE,
|
||||
simdjson::stream_format::comma_delimited).get(stream);
|
||||
if (error) { std::cerr << error << std::endl; return; }
|
||||
for (auto doc : stream) {
|
||||
std::cout << doc << std::endl;
|
||||
}
|
||||
// Prints: {"a":1}
|
||||
// {"b":2}
|
||||
// {"c":3}
|
||||
auto json = R"( 1, 2, 3, 4, "a", "b", "c", {"hello": "world"} , [1, 2, 3])"_padded;
|
||||
ondemand::parser parser;
|
||||
ondemand::document_stream doc_stream;
|
||||
// We pass '32' as the batch size, but it is a bogus parameter because, since
|
||||
// we pass 'true' to the allow_comma parameter, the batch size will be set to at least
|
||||
// the document size.
|
||||
auto error = parser.iterate_many(json, 32, true).get(doc_stream);
|
||||
if (error) { std::cerr << error << std::endl; return; }
|
||||
for (auto doc : doc_stream) {
|
||||
std::cout << doc.type() << std::endl;
|
||||
}
|
||||
```
|
||||
|
||||
This will print:
|
||||
|
||||
```
|
||||
|
||||
Whitespace around the commas is allowed:
|
||||
```cpp
|
||||
auto json = R"({"a":1} , {"b":2} , {"c":3})"_padded; // Also works
|
||||
number
|
||||
number
|
||||
number
|
||||
number
|
||||
string
|
||||
string
|
||||
string
|
||||
object
|
||||
array
|
||||
```
|
||||
|
||||
Nested commas inside objects and arrays are preserved:
|
||||
```cpp
|
||||
auto json = R"({"arr":[1,2,3]},{"obj":{"x":1,"y":2}})"_padded;
|
||||
// Correctly parses as 2 documents, not 6
|
||||
```
|
||||
|
||||
Mixed document types are supported:
|
||||
```cpp
|
||||
auto json = R"(1, 2, 3, 4, "a", "b", "c", {"hello": "world"}, [1, 2, 3])"_padded;
|
||||
ondemand::parser parser;
|
||||
ondemand::document_stream doc_stream;
|
||||
auto error = parser.iterate_many(json, ondemand::DEFAULT_BATCH_SIZE,
|
||||
simdjson::stream_format::comma_delimited).get(doc_stream);
|
||||
if (error) { std::cerr << error << std::endl; return; }
|
||||
for (auto doc : doc_stream) {
|
||||
std::cout << doc.type() << std::endl;
|
||||
}
|
||||
// Prints: number number number number string string string object array
|
||||
```
|
||||
|
||||
Extra top-level separators are tolerated for compatibility with the legacy
|
||||
`allow_comma_separated` behavior. For example, leading commas, trailing commas,
|
||||
and repeated commas are treated as empty separators rather than documents.
|
||||
|
||||
### Legacy `allow_comma_separated` parameter (deprecated)
|
||||
|
||||
The `allow_comma_separated` boolean parameter is deprecated. When set to `true`, it now internally maps to `stream_format::comma_delimited`.
|
||||
|
||||
The old single-batch limitation no longer applies - comma-delimited parsing now supports multi-batch processing and threading for optimal performance on large files.
|
||||
|
||||
JSON Text Sequences (RFC 7464)
|
||||
------------------------------
|
||||
|
||||
[RFC 7464](https://tools.ietf.org/html/rfc7464) defines a format for streaming JSON values using ASCII Record Separator (RS, 0x1E) as a delimiter. Each JSON text is preceded by RS and optionally followed by ASCII Line Feed (LF, 0x0A).
|
||||
|
||||
Example input:
|
||||
```
|
||||
<RS>{"name":"doc1"}<LF>
|
||||
<RS>{"name":"doc2"}<LF>
|
||||
<RS>{"name":"doc3"}<LF>
|
||||
```
|
||||
|
||||
To parse JSON text sequences, use the `stream_format::json_sequence` parameter:
|
||||
|
||||
```cpp
|
||||
// Build input with RS (0x1E) and LF (0x0A) delimiters
|
||||
std::string input_str;
|
||||
input_str += '\x1e'; input_str += "{\"a\":1}"; input_str += '\x0a';
|
||||
input_str += '\x1e'; input_str += "{\"b\":2}"; input_str += '\x0a';
|
||||
input_str += '\x1e'; input_str += "{\"c\":3}"; input_str += '\x0a';
|
||||
simdjson::padded_string input(input_str);
|
||||
|
||||
ondemand::parser parser;
|
||||
ondemand::document_stream stream;
|
||||
auto error = parser.iterate_many(input, ondemand::DEFAULT_BATCH_SIZE,
|
||||
simdjson::stream_format::json_sequence).get(stream);
|
||||
if (error) { std::cerr << error << std::endl; return; }
|
||||
for (auto doc : stream) {
|
||||
std::cout << doc << std::endl;
|
||||
}
|
||||
```
|
||||
|
||||
The `stream_format` enum has the following values:
|
||||
- `stream_format::whitespace_delimited` (default): Standard NDJSON/JSON Lines format
|
||||
- `stream_format::json_sequence`: RFC 7464 format with RS delimiters
|
||||
- `stream_format::comma_delimited`: Comma-separated JSON documents
|
||||
- `stream_format::comma_delimited_array`: A single JSON array whose elements are iterated as comma-delimited documents (see below)
|
||||
|
||||
The trailing LF after each JSON text is optional but recommended by the RFC for robustness.
|
||||
|
||||
JSON Array As A Document Stream
|
||||
-------------------------------
|
||||
|
||||
Sometimes an input is a single, well-formed JSON array — `[{"a":1},{"b":2},{"c":3}]` — but you want to iterate its elements one at a time without materializing the whole array. Use `stream_format::comma_delimited_array`:
|
||||
|
||||
```cpp
|
||||
auto json = R"([{"a":1},{"b":2},{"c":3}])"_padded;
|
||||
ondemand::parser parser;
|
||||
ondemand::document_stream stream;
|
||||
auto error = parser.iterate_many(json, ondemand::DEFAULT_BATCH_SIZE,
|
||||
simdjson::stream_format::comma_delimited_array).get(stream);
|
||||
if (error) { std::cerr << error << std::endl; return; }
|
||||
for (auto doc : stream) {
|
||||
std::cout << doc << std::endl;
|
||||
}
|
||||
// Prints: {"a":1}
|
||||
// {"b":2}
|
||||
// {"c":3}
|
||||
```
|
||||
|
||||
The parser strips the outer `[` and `]` plus any surrounding JSON whitespace (space, tab, LF, CR) and then behaves exactly like `stream_format::comma_delimited` over the remaining bytes. All comma-delimited features are inherited: multi-batch processing, threading, mixed scalar types, and nested commas preserved inside inner objects and arrays.
|
||||
|
||||
```cpp
|
||||
// All of these work:
|
||||
auto a = R"([1, "x", true, null, {"k":"v"}, [1,2]])"_padded; // mixed scalars
|
||||
auto b = R"( [ 1, 2, 3 ] )"_padded; // whitespace
|
||||
auto c = R"([])"_padded; // empty array → 0 docs
|
||||
```
|
||||
|
||||
If the input is not a well-formed outer array (missing `[`, missing `]`, or empty / all-whitespace), `iterate_many` returns `TAPE_ERROR`. Content **inside** the array is not validated up front — individual document parse errors surface when you iterate, just like `comma_delimited`.
|
||||
|
||||
Positions reported via `current_index()` are relative to the **stripped** buffer (the bytes between `[` and `]`), not the original input, for consistency with the existing BOM-stripping behavior.
|
||||
|
||||
|
||||
C++20 features
|
||||
--------------------
|
||||
@@ -578,161 +418,3 @@ Otherwise you may use this longer version for explicit handling of errors:
|
||||
cars.push_back(c);
|
||||
}
|
||||
```
|
||||
|
||||
**Performance tip**: You will get better performance if you order the attributes (make, model)
|
||||
in the order they appear in the JSON document.
|
||||
|
||||
C++26 features (static reflection)
|
||||
-----------------------------------
|
||||
|
||||
If you have a C++26 compatible compiler with [P2996](https://wg21.link/P2996)
|
||||
static reflection support, you can compile the simdjson library with the
|
||||
`SIMDJSON_STATIC_REFLECTION` macro set to `1`. When this is the case, simdjson
|
||||
can deserialize a stream of JSON documents directly into your own structures
|
||||
**without** writing any `tag_invoke` function. The library inspects the
|
||||
non-static public members of your type at compile time and produces the
|
||||
parsing code automatically.
|
||||
|
||||
```cpp
|
||||
#define SIMDJSON_STATIC_REFLECTION 1
|
||||
#include "simdjson.h"
|
||||
```
|
||||
|
||||
Consider the same `Car` structure used in the C++20 example, but **without**
|
||||
any `tag_invoke` glue:
|
||||
|
||||
```cpp
|
||||
struct Car {
|
||||
std::string make;
|
||||
std::string model;
|
||||
int year;
|
||||
std::vector<double> tire_pressure;
|
||||
};
|
||||
```
|
||||
|
||||
With C++26 static reflection enabled, you can iterate a stream of cars and
|
||||
push them into a `std::vector<Car>` directly:
|
||||
|
||||
```cpp
|
||||
auto json = R"( { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||
"tire_pressure": [ 40.1, 39.9 ] }
|
||||
{ "make": "Kia", "model": "Soul", "year": 2012,
|
||||
"tire_pressure": [ 30.1, 31.0 ] }
|
||||
{ "make": "Toyota", "model": "Tercel", "year": 1999,
|
||||
"tire_pressure": [ 29.8, 30.0 ] } )"_padded;
|
||||
ondemand::parser parser;
|
||||
ondemand::document_stream stream;
|
||||
auto error = parser.iterate_many(json).get(stream);
|
||||
if (error) { /* handle error */ }
|
||||
std::vector<Car> cars;
|
||||
for (auto doc : stream) {
|
||||
Car c;
|
||||
if ((error = doc.get<Car>().get(c))) { /* handle error */ }
|
||||
cars.push_back(c);
|
||||
}
|
||||
```
|
||||
|
||||
This works for every `stream_format` value supported by `iterate_many`. The
|
||||
following examples each parse the same three cars, but laid out using a
|
||||
different streaming convention.
|
||||
|
||||
### Whitespace-delimited (default, NDJSON / JSON Lines)
|
||||
|
||||
```cpp
|
||||
auto json = R"( { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||
"tire_pressure": [ 40.1, 39.9 ] }
|
||||
{ "make": "Kia", "model": "Soul", "year": 2012,
|
||||
"tire_pressure": [ 30.1, 31.0 ] }
|
||||
{ "make": "Toyota", "model": "Tercel", "year": 1999,
|
||||
"tire_pressure": [ 29.8, 30.0 ] } )"_padded;
|
||||
ondemand::parser parser;
|
||||
ondemand::document_stream stream;
|
||||
auto error = parser.iterate_many(json, ondemand::DEFAULT_BATCH_SIZE,
|
||||
simdjson::stream_format::whitespace_delimited).get(stream);
|
||||
if (error) { /* handle error */ }
|
||||
std::vector<Car> cars;
|
||||
for (auto doc : stream) {
|
||||
cars.push_back((Car)doc); // throws on error
|
||||
}
|
||||
```
|
||||
|
||||
### Comma-delimited documents
|
||||
|
||||
```cpp
|
||||
auto json = R"( { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||
"tire_pressure": [ 40.1, 39.9 ] },
|
||||
{ "make": "Kia", "model": "Soul", "year": 2012,
|
||||
"tire_pressure": [ 30.1, 31.0 ] },
|
||||
{ "make": "Toyota", "model": "Tercel", "year": 1999,
|
||||
"tire_pressure": [ 29.8, 30.0 ] } )"_padded;
|
||||
ondemand::parser parser;
|
||||
ondemand::document_stream stream;
|
||||
auto error = parser.iterate_many(json, ondemand::DEFAULT_BATCH_SIZE,
|
||||
simdjson::stream_format::comma_delimited).get(stream);
|
||||
if (error) { /* handle error */ }
|
||||
std::vector<Car> cars;
|
||||
for (auto doc : stream) {
|
||||
Car c;
|
||||
if ((error = doc.get<Car>().get(c))) { /* handle error */ }
|
||||
cars.push_back(c);
|
||||
}
|
||||
```
|
||||
|
||||
### A single JSON array as a stream of documents
|
||||
|
||||
When the input is a single JSON array, you can stream its elements one at a
|
||||
time without materializing the entire array as a `std::vector` upfront:
|
||||
|
||||
```cpp
|
||||
auto json = R"( [ { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||
"tire_pressure": [ 40.1, 39.9 ] },
|
||||
{ "make": "Kia", "model": "Soul", "year": 2012,
|
||||
"tire_pressure": [ 30.1, 31.0 ] },
|
||||
{ "make": "Toyota", "model": "Tercel", "year": 1999,
|
||||
"tire_pressure": [ 29.8, 30.0 ] } ] )"_padded;
|
||||
ondemand::parser parser;
|
||||
ondemand::document_stream stream;
|
||||
auto error = parser.iterate_many(json, ondemand::DEFAULT_BATCH_SIZE,
|
||||
simdjson::stream_format::comma_delimited_array).get(stream);
|
||||
if (error) { /* handle error */ }
|
||||
std::vector<Car> cars;
|
||||
for (auto doc : stream) {
|
||||
Car c;
|
||||
if ((error = doc.get<Car>().get(c))) { /* handle error */ }
|
||||
cars.push_back(c);
|
||||
}
|
||||
```
|
||||
|
||||
### JSON Text Sequences (RFC 7464)
|
||||
|
||||
```cpp
|
||||
// Build input with RS (0x1E) and LF (0x0A) delimiters
|
||||
std::string input_str;
|
||||
auto append = [&](std::string_view doc) {
|
||||
input_str += '\x1e'; input_str += doc; input_str += '\x0a';
|
||||
};
|
||||
append(R"({ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9 ] })");
|
||||
append(R"({ "make": "Kia", "model": "Soul", "year": 2012, "tire_pressure": [ 30.1, 31.0 ] })");
|
||||
append(R"({ "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0 ] })");
|
||||
simdjson::padded_string input(input_str);
|
||||
|
||||
ondemand::parser parser;
|
||||
ondemand::document_stream stream;
|
||||
auto error = parser.iterate_many(input, ondemand::DEFAULT_BATCH_SIZE,
|
||||
simdjson::stream_format::json_sequence).get(stream);
|
||||
if (error) { /* handle error */ }
|
||||
std::vector<Car> cars;
|
||||
for (auto doc : stream) {
|
||||
Car c;
|
||||
if ((error = doc.get<Car>().get(c))) { /* handle error */ }
|
||||
cars.push_back(c);
|
||||
}
|
||||
```
|
||||
|
||||
In every case, the user-defined type (`Car` here) does not need a hand-written
|
||||
`tag_invoke` overload: the library generates the deserialization code from the
|
||||
type's public data members at compile time.
|
||||
|
||||
|
||||
**Performance tip**: You will get better performance if you order the attributes (make, model)
|
||||
in the order they appear in the JSON document.
|
||||
+7
-186
@@ -18,7 +18,6 @@ Contents
|
||||
- [How it works](#how-it-works)
|
||||
- [Support](#support)
|
||||
- [API](#api)
|
||||
- [Streaming directly from a memory-mapped file](#streaming-directly-from-a-memory-mapped-file)
|
||||
- [Use cases](#use-cases)
|
||||
- [Tracking your position](#tracking-your-position)
|
||||
- [Incomplete streams](#incomplete-streams)
|
||||
@@ -133,7 +132,7 @@ Whitespace Characters:
|
||||
Some official formats **(non-exhaustive list)**:
|
||||
- [Newline-Delimited JSON (NDJSON)](https://github.com/ndjson/ndjson-spec)
|
||||
- [JSON lines (JSONL)](http://jsonlines.org/)
|
||||
- [Record separator-delimited JSON (RFC 7464)](https://tools.ietf.org/html/rfc7464)
|
||||
- [Record separator-delimited JSON (RFC 7464)](https://tools.ietf.org/html/rfc7464) <- Not supported by simdjson!
|
||||
- [More on Wikipedia...](https://en.wikipedia.org/wiki/JSON_streaming)
|
||||
|
||||
API
|
||||
@@ -219,83 +218,17 @@ got full document at 29
|
||||
|
||||
|
||||
|
||||
Streaming directly from a memory-mapped file
|
||||
--------------------------------------------
|
||||
|
||||
When your input is a large NDJSON / JSON-lines file on disk, the most
|
||||
efficient way to feed `parse_many` is to use `simdjson::padded_memory_map`.
|
||||
It returns a `padded_string_view` with the right amount of trailing padding,
|
||||
so you can pass it directly to `parse_many` without copying the file content
|
||||
into your own buffer first.
|
||||
|
||||
`padded_memory_map` is available on POSIX systems (Linux, macOS, BSD, ...) by
|
||||
default. On Windows it is an **opt-in** feature with the following
|
||||
requirements:
|
||||
|
||||
1. Build simdjson with `-DSIMDJSON_ENABLE_MEMORY_FILE_MAPPING_ON_WINDOWS=ON`, or — if
|
||||
you consume simdjson as a pre-built library — define
|
||||
`SIMDJSON_ENABLE_MEMORY_FILE_MAPPING_ON_WINDOWS=1`, raise `NTDDI_VERSION` to at
|
||||
least `NTDDI_WIN10_RS4` (`0x0A000005`, Windows 10 version 1803), and
|
||||
add `onecore.lib` to your link line yourself. The Windows
|
||||
implementation uses the modern memory APIs `CreateFileMapping2` /
|
||||
`MapViewOfFile3`, which are available starting with that version of
|
||||
Windows and are exported by `onecore.lib`.
|
||||
2. `#include <windows.h>` before `#include "simdjson.h"` in every
|
||||
translation unit where you want to use `padded_memory_map`. simdjson
|
||||
deliberately does not pull in `<windows.h>` itself, so the class is
|
||||
only declared when the Win32 types are already visible.
|
||||
|
||||
If either requirement is not met on Windows, the `padded_memory_map` class is
|
||||
not declared at all and any code that references it fails to compile with an
|
||||
"unknown identifier" error. The availability of the class can be tested with
|
||||
the macro `SIMDJSON_HAS_PADDED_MEMORY_MAP`.
|
||||
|
||||
On POSIX, `padded_memory_map` uses `mmap` to map the file directly into
|
||||
memory with zero copies. On Windows (when enabled), it uses
|
||||
`CreateFileMapping2` + `MapViewOfFile3` for true zero-copy mapping
|
||||
whenever the file does not end within `SIMDJSON_PADDING` bytes of a page
|
||||
boundary; for those rare cases, it transparently falls back to reading
|
||||
the file into a heap-allocated padded buffer so that the returned view
|
||||
always has `SIMDJSON_PADDING` accessible zero bytes after the file content.
|
||||
|
||||
```cpp
|
||||
#ifdef _WIN32
|
||||
#include <windows.h> // Must come BEFORE <simdjson.h> on Windows
|
||||
#endif
|
||||
#include "simdjson.h"
|
||||
|
||||
// ...
|
||||
|
||||
simdjson::padded_memory_map map("huge_stream.ndjson");
|
||||
if (!map.is_valid()) { /* file missing, unreadable, too large, ... */ return; }
|
||||
|
||||
simdjson::dom::parser parser;
|
||||
simdjson::dom::document_stream stream;
|
||||
auto error = parser.parse_many(map.view()).get(stream);
|
||||
if (error) { std::cerr << error << std::endl; return; }
|
||||
|
||||
for (auto doc : stream) {
|
||||
// process each JSON document in the stream
|
||||
std::cout << doc << std::endl;
|
||||
}
|
||||
```
|
||||
|
||||
Important lifetime rule: the `padded_string_view` returned by `map.view()` is
|
||||
only valid while the `padded_memory_map` instance is alive, so keep `map`
|
||||
alive for as long as you are iterating the stream.
|
||||
|
||||
The file must not be modified while the memory map is in use. If you need a
|
||||
fully independent copy of the data, use `simdjson::padded_string::load(...)`
|
||||
instead.
|
||||
|
||||
If you prefer single-document parsing on a memory-mapped file, the same
|
||||
pattern applies to `parser.parse(...)`:
|
||||
**Advanced feature:**
|
||||
On non-Windows systems, you can use memory-file mapping to create a `simdjson::padded_string_view`
|
||||
from a file on disk.
|
||||
|
||||
```cpp
|
||||
// If the macro _WIN32 is defined, this will not work since we do not support memory-file mapping
|
||||
// under Windows at this time.
|
||||
simdjson::padded_memory_map map(myfilename);
|
||||
if (!map.is_valid()) { /* handle error */ }
|
||||
simdjson::padded_string_view view = map.view(); // view is usable while padded_memory_map is in scope
|
||||
simdjson::dom::element doc = parser.parse(view); // parse the JSON
|
||||
ondemand::document doc = parser.iterate(view); // parse the JSON
|
||||
```
|
||||
|
||||
Incomplete streams
|
||||
@@ -320,115 +253,3 @@ Consider the following example where a truncated document (`{"key":"intentionall
|
||||
|
||||
|
||||
Importantly, you should only call `truncated_bytes()` after iterating through all of the documents since the stream cannot tell whether there are truncated documents at the very end when it may not have accessed that part of the data yet.
|
||||
|
||||
JSON Text Sequences (RFC 7464)
|
||||
------------------------------
|
||||
|
||||
[RFC 7464](https://tools.ietf.org/html/rfc7464) defines a format for streaming JSON values using ASCII Record Separator (RS, 0x1E) as a delimiter. Each JSON text is preceded by RS and optionally followed by ASCII Line Feed (LF, 0x0A).
|
||||
|
||||
Example input:
|
||||
```
|
||||
<RS>{"name":"doc1"}<LF>
|
||||
<RS>{"name":"doc2"}<LF>
|
||||
<RS>{"name":"doc3"}<LF>
|
||||
```
|
||||
|
||||
To parse JSON text sequences, use the `stream_format::json_sequence` parameter:
|
||||
|
||||
```cpp
|
||||
// Build input with RS (0x1E) and LF (0x0A) delimiters
|
||||
std::string input_str;
|
||||
input_str += '\x1e'; input_str += "{\"a\":1}"; input_str += '\x0a';
|
||||
input_str += '\x1e'; input_str += "{\"b\":2}"; input_str += '\x0a';
|
||||
input_str += '\x1e'; input_str += "{\"c\":3}"; input_str += '\x0a';
|
||||
simdjson::padded_string input(input_str);
|
||||
|
||||
simdjson::dom::parser parser;
|
||||
simdjson::dom::document_stream stream;
|
||||
auto error = parser.parse_many(input, simdjson::dom::DEFAULT_BATCH_SIZE,
|
||||
simdjson::stream_format::json_sequence).get(stream);
|
||||
if (error) { std::cerr << error << std::endl; return; }
|
||||
for (auto doc : stream) {
|
||||
std::cout << doc << std::endl;
|
||||
}
|
||||
```
|
||||
|
||||
The `stream_format` enum has the following values:
|
||||
- `stream_format::whitespace_delimited` (default): Standard NDJSON/JSON Lines format
|
||||
- `stream_format::json_sequence`: RFC 7464 format with RS delimiters
|
||||
- `stream_format::comma_delimited`: Comma-separated JSON documents
|
||||
- `stream_format::comma_delimited_array`: A single JSON array whose elements are iterated as comma-delimited documents (see below)
|
||||
|
||||
The trailing LF after each JSON text is optional but recommended by the RFC for robustness.
|
||||
|
||||
Comma-Separated Documents
|
||||
-------------------------
|
||||
|
||||
Some systems produce JSON documents separated by commas, like `{"a":1},{"b":2},{"c":3}`. This is common when extracting elements from a JSON array or when APIs return comma-separated results.
|
||||
|
||||
To parse comma-separated documents, use the `stream_format::comma_delimited` parameter:
|
||||
|
||||
```cpp
|
||||
auto json = R"({"a":1},{"b":2},{"c":3})"_padded;
|
||||
simdjson::dom::parser parser;
|
||||
simdjson::dom::document_stream stream;
|
||||
auto error = parser.parse_many(json, simdjson::dom::DEFAULT_BATCH_SIZE,
|
||||
simdjson::stream_format::comma_delimited).get(stream);
|
||||
if (error) { std::cerr << error << std::endl; return; }
|
||||
for (auto doc : stream) {
|
||||
std::cout << doc << std::endl;
|
||||
}
|
||||
// Prints: {"a":1}
|
||||
// {"b":2}
|
||||
// {"c":3}
|
||||
```
|
||||
|
||||
Whitespace around the commas is allowed:
|
||||
```cpp
|
||||
auto json = R"({"a":1} , {"b":2} , {"c":3})"_padded; // Also works
|
||||
```
|
||||
|
||||
Nested commas inside objects and arrays are preserved:
|
||||
```cpp
|
||||
auto json = R"({"arr":[1,2,3]},{"obj":{"x":1,"y":2}})"_padded;
|
||||
// Correctly parses as 2 documents, not 6
|
||||
```
|
||||
|
||||
Extra top-level separators are tolerated for compatibility with the legacy
|
||||
On-Demand comma-separated mode. Leading commas, trailing commas, and repeated
|
||||
commas are treated as empty separators rather than documents.
|
||||
|
||||
Unlike the legacy `allow_comma_separated` parameter, `stream_format::comma_delimited` supports multi-batch processing and threading for optimal performance on large files.
|
||||
|
||||
JSON Array As A Document Stream
|
||||
-------------------------------
|
||||
|
||||
Sometimes an input is a single, well-formed JSON array — `[{"a":1},{"b":2},{"c":3}]` — but you want to iterate its elements one at a time without materializing the whole array. Use `stream_format::comma_delimited_array`:
|
||||
|
||||
```cpp
|
||||
auto json = R"([{"a":1},{"b":2},{"c":3}])"_padded;
|
||||
simdjson::dom::parser parser;
|
||||
simdjson::dom::document_stream stream;
|
||||
auto error = parser.parse_many(json, simdjson::dom::DEFAULT_BATCH_SIZE,
|
||||
simdjson::stream_format::comma_delimited_array).get(stream);
|
||||
if (error) { std::cerr << error << std::endl; return; }
|
||||
for (auto doc : stream) {
|
||||
std::cout << doc << std::endl;
|
||||
}
|
||||
// Prints: {"a":1}
|
||||
// {"b":2}
|
||||
// {"c":3}
|
||||
```
|
||||
|
||||
The parser strips the outer `[` and `]` plus any surrounding JSON whitespace (space, tab, LF, CR) and then behaves exactly like `stream_format::comma_delimited` over the remaining bytes. All comma-delimited features are inherited: multi-batch processing, threading, mixed scalar types, and nested commas preserved inside inner objects and arrays.
|
||||
|
||||
```cpp
|
||||
// All of these work:
|
||||
auto a = R"([1, "x", true, null, {"k":"v"}, [1,2]])"_padded; // mixed scalars
|
||||
auto b = R"( [ 1, 2, 3 ] )"_padded; // whitespace
|
||||
auto c = R"([])"_padded; // empty array → 0 docs
|
||||
```
|
||||
|
||||
If the input is not a well-formed outer array (missing `[`, missing `]`, or empty / all-whitespace), `parse_many` returns `TAPE_ERROR`. Content **inside** the array is not validated up front — individual document parse errors surface when you iterate, just like `comma_delimited`.
|
||||
|
||||
Positions reported via `current_index()` are relative to the **stripped** buffer (the bytes between `[` and `]`), not the original input, for consistency with the existing BOM-stripping behavior.
|
||||
|
||||
+1
-7
@@ -208,7 +208,7 @@ You can still make sure of this capability in your code if you are an expert
|
||||
programmer and you are willing to silence sanitizer warnings.
|
||||
|
||||
If you are building simdjson with C++17 or better, you can use `simdjson::padded_input`.
|
||||
The `padded_input` struct automatically manages padding for you. It can be constructed from a `std::string_view`, a C-style string with length, or a `std::string`. For `std::string`, it takes into account the reserved capacity when determining if sufficient padding exists. If the input already has sufficient padding (up to the end of the memory page), it creates a view without copying. Otherwise, it copies the data into a `padded_string` with proper padding.
|
||||
The `padded_input` struct automatically manages padding for you. It can be constructed from a `std::string_view` or a C-style string with length. If the input already has sufficient padding (up to the end of the memory page), it creates a view without copying. Otherwise, it copies the data into a `padded_string` with proper padding.
|
||||
|
||||
Example usage:
|
||||
|
||||
@@ -216,12 +216,6 @@ Example usage:
|
||||
std::string_view json = get_json_data();
|
||||
simdjson::padded_input input(json); // Automatically pads if needed
|
||||
auto result = parser.parse(input);
|
||||
|
||||
// Also works with std::string, considering capacity
|
||||
std::string json_str = get_json_string();
|
||||
json_str.reserve(json_str.size() + 100); // Reserve extra space
|
||||
simdjson::padded_input input2(json_str); // May avoid copying if capacity is sufficient
|
||||
auto result2 = parser.parse(input2);
|
||||
```
|
||||
|
||||
This simplifies padding management compared to manually checking and allocating.
|
||||
|
||||
+12
-1
@@ -35,6 +35,7 @@
|
||||
*/
|
||||
|
||||
#include "simdjson/common_defs.h"
|
||||
#include "simdjson/feature_macros.h"
|
||||
|
||||
// This provides the public API for simdjson.
|
||||
// DOM and ondemand are amalgamated separately, in simdjson.h
|
||||
@@ -45,19 +46,29 @@
|
||||
#include "simdjson/error.h"
|
||||
#include "simdjson/error-inl.h"
|
||||
#include "simdjson/implementation.h"
|
||||
#include "simdjson/builtin.h"
|
||||
#include "simdjson/minify.h"
|
||||
#include "simdjson/padded_string.h"
|
||||
#include "simdjson/padded_string-inl.h"
|
||||
#include "simdjson/padded_string_view.h"
|
||||
#include "simdjson/padded_string_view-inl.h"
|
||||
|
||||
#if SIMDJSON_FEATURE_DOM_API
|
||||
#include "simdjson/dom.h"
|
||||
#if SIMDJSON_FEATURE_BUILDER_API
|
||||
#include "simdjson/builder.h"
|
||||
#endif // SIMDJSON_FEATURE_BUILDER_API
|
||||
#endif // SIMDJSON_FEATURE_DOM_API
|
||||
|
||||
#if SIMDJSON_FEATURE_ONDEMAND_API
|
||||
#include "simdjson/ondemand.h"
|
||||
#endif // SIMDJSON_FEATURE_ONDEMAND_API
|
||||
|
||||
#if SIMDJSON_FEATURE_DOM_API && SIMDJSON_FEATURE_ONDEMAND_API
|
||||
#include "simdjson/convert.h"
|
||||
#include "simdjson/convert-inl.h"
|
||||
#endif // SIMDJSON_FEATURE_DOM_API && SIMDJSON_FEATURE_ONDEMAND_API
|
||||
|
||||
// Compile-time JSON parsing (C++26 P2996 reflection)
|
||||
#include "simdjson/compile_time_json.h"
|
||||
#include "simdjson/compile_time_json-inl.h"
|
||||
|
||||
|
||||
@@ -21,10 +21,6 @@ SIMDJSON_PUSH_DISABLE_UNUSED_WARNINGS
|
||||
|
||||
/** The maximum document size supported by simdjson. */
|
||||
constexpr size_t SIMDJSON_MAXSIZE_BYTES = 0xFFFFFFFF;
|
||||
/** The maximum depth of nested objects and arrays supported by simdjson.
|
||||
A depth of SIMDJSON_MAXSIZE_BYTES/2 is not reasonable and would be
|
||||
adversarial, but it serves as an upper bound for validation purposes. */
|
||||
constexpr size_t SIMDJSON_MAX_DEPTH = SIMDJSON_MAXSIZE_BYTES/2;
|
||||
|
||||
/**
|
||||
* The amount of padding needed in a buffer to parse JSON.
|
||||
@@ -50,21 +46,6 @@ struct padded_string;
|
||||
class padded_string_view;
|
||||
enum class stage1_mode;
|
||||
|
||||
/**
|
||||
* Stream format for parse_many/iterate_many.
|
||||
*/
|
||||
enum class stream_format {
|
||||
whitespace_delimited, ///< Whitespace-delimited JSON documents (default, includes NDJSON/JSONL)
|
||||
json_sequence, ///< RFC 7464 JSON text sequences (RS-delimited)
|
||||
comma_delimited, ///< Comma-separated JSON documents (e.g., `{...},{...},{...}`)
|
||||
comma_delimited_array ///< A single JSON array whose elements are iterated as
|
||||
///< comma-separated documents (e.g., `[{...},{...},{...}]`).
|
||||
///< The parser strips the outer `[` / `]` plus any
|
||||
///< surrounding JSON whitespace (space, tab, LF, CR)
|
||||
///< and then behaves like `comma_delimited` over the
|
||||
///< remaining bytes.
|
||||
};
|
||||
|
||||
namespace internal {
|
||||
|
||||
template<typename T>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
*
|
||||
|
||||
@@ -33,25 +33,16 @@ inline error_code document::allocate(size_t capacity) noexcept {
|
||||
allocated_capacity = 0;
|
||||
return SUCCESS;
|
||||
}
|
||||
if (capacity > SIMDJSON_MAXSIZE_BYTES) {
|
||||
return CAPACITY;
|
||||
}
|
||||
|
||||
// a pathological input like "[[[[..." would generate capacity tape elements, so
|
||||
// need a capacity of at least capacity + 1, but it is also possible to do
|
||||
// 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)) {
|
||||
|
||||
@@ -89,14 +89,12 @@ simdjson_inline document_stream::document_stream(
|
||||
dom::parser &_parser,
|
||||
const uint8_t *_buf,
|
||||
size_t _len,
|
||||
size_t _batch_size,
|
||||
stream_format _format
|
||||
size_t _batch_size
|
||||
) noexcept
|
||||
: parser{&_parser},
|
||||
buf{_buf},
|
||||
len{_len},
|
||||
batch_size{_batch_size <= MINIMAL_BATCH_SIZE ? MINIMAL_BATCH_SIZE : _batch_size},
|
||||
format{_format},
|
||||
error{SUCCESS}
|
||||
#ifdef SIMDJSON_THREADS_ENABLED
|
||||
, use_thread(_parser.threaded) // we need to make a copy because _parser.threaded can change
|
||||
@@ -114,7 +112,6 @@ simdjson_inline document_stream::document_stream() noexcept
|
||||
buf{nullptr},
|
||||
len{0},
|
||||
batch_size{0},
|
||||
format{stream_format::whitespace_delimited},
|
||||
error{UNINITIALIZED}
|
||||
#ifdef SIMDJSON_THREADS_ENABLED
|
||||
, use_thread(false)
|
||||
@@ -227,14 +224,7 @@ simdjson_inline std::string_view document_stream::iterator::source() const noexc
|
||||
} else {
|
||||
size_t next_doc_index = stream->batch_start + stream->parser->implementation->structural_indexes[stream->parser->implementation->next_structural_index];
|
||||
size_t svlen = next_doc_index - current_index();
|
||||
// Trim trailing whitespace, NUL, and RS (0x1E). In RFC 7464 json_sequence
|
||||
// mode the scanner classifies RS as a scalar character, so an RS-prefixed
|
||||
// scalar document (number/true/false/null/string) has no closing structural
|
||||
// index and the slice runs all the way up to the next document's RS. RS
|
||||
// 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')) {
|
||||
svlen--;
|
||||
}
|
||||
return std::string_view(start, svlen);
|
||||
@@ -284,35 +274,10 @@ inline size_t document_stream::next_batch_start() const noexcept {
|
||||
|
||||
inline error_code document_stream::run_stage1(dom::parser &p, size_t _batch_start) noexcept {
|
||||
size_t remaining = len - _batch_start;
|
||||
stage1_mode mode;
|
||||
if (remaining <= batch_size) {
|
||||
// Final batch
|
||||
switch (format) {
|
||||
case stream_format::json_sequence:
|
||||
mode = stage1_mode::json_sequence_final;
|
||||
break;
|
||||
case stream_format::comma_delimited:
|
||||
mode = stage1_mode::comma_delimited_final;
|
||||
break;
|
||||
default:
|
||||
mode = stage1_mode::streaming_final;
|
||||
break;
|
||||
}
|
||||
return p.implementation->stage1(&buf[_batch_start], remaining, mode);
|
||||
return p.implementation->stage1(&buf[_batch_start], remaining, stage1_mode::streaming_final);
|
||||
} else {
|
||||
// Partial batch
|
||||
switch (format) {
|
||||
case stream_format::json_sequence:
|
||||
mode = stage1_mode::json_sequence_partial;
|
||||
break;
|
||||
case stream_format::comma_delimited:
|
||||
mode = stage1_mode::comma_delimited_partial;
|
||||
break;
|
||||
default:
|
||||
mode = stage1_mode::streaming_partial;
|
||||
break;
|
||||
}
|
||||
return p.implementation->stage1(&buf[_batch_start], batch_size, mode);
|
||||
return p.implementation->stage1(&buf[_batch_start], batch_size, stage1_mode::streaming_partial);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -206,14 +206,12 @@ private:
|
||||
* @param buf is the raw byte buffer we need to process
|
||||
* @param len is the length of the raw byte buffer in bytes
|
||||
* @param batch_size is the size of the windows (must be strictly greater or equal to the largest JSON document)
|
||||
* @param format is the stream format
|
||||
*/
|
||||
simdjson_inline document_stream(
|
||||
dom::parser &parser,
|
||||
const uint8_t *buf,
|
||||
size_t len,
|
||||
size_t batch_size,
|
||||
stream_format format = stream_format::whitespace_delimited
|
||||
size_t batch_size
|
||||
) noexcept;
|
||||
|
||||
/**
|
||||
@@ -263,8 +261,6 @@ private:
|
||||
const uint8_t *buf;
|
||||
size_t len;
|
||||
size_t batch_size;
|
||||
/** The stream format. */
|
||||
stream_format format;
|
||||
/** The error (or lack thereof) from the current document. */
|
||||
error_code error;
|
||||
size_t batch_start{0};
|
||||
|
||||
@@ -22,8 +22,6 @@ enum class element_type {
|
||||
STRING = '"', ///< std::string_view
|
||||
BOOL = 't', ///< bool
|
||||
NULL_VALUE = 'n', ///< null
|
||||
/// The BIGINT type is for integers that do not fit in 64 bits. It is only present
|
||||
// if you set parser.number_as_string(true).
|
||||
BIGINT = 'Z' ///< std::string_view: big integer stored as raw digit string
|
||||
};
|
||||
|
||||
|
||||
@@ -170,7 +170,12 @@ simdjson_inline simdjson_result<element> parser::parse(const padded_string_view
|
||||
}
|
||||
|
||||
inline simdjson_result<document_stream> parser::parse_many(const uint8_t *buf, size_t len, size_t batch_size) noexcept {
|
||||
return parse_many(buf, len, batch_size, stream_format::whitespace_delimited);
|
||||
if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; }
|
||||
if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) {
|
||||
buf += 3;
|
||||
len -= 3;
|
||||
}
|
||||
return document_stream(*this, buf, len, batch_size);
|
||||
}
|
||||
inline simdjson_result<document_stream> parser::parse_many(const char *buf, size_t len, size_t batch_size) noexcept {
|
||||
return parse_many(reinterpret_cast<const uint8_t *>(buf), len, batch_size);
|
||||
@@ -181,48 +186,6 @@ inline simdjson_result<document_stream> parser::parse_many(const std::string &s,
|
||||
inline simdjson_result<document_stream> parser::parse_many(const padded_string &s, size_t batch_size) noexcept {
|
||||
return parse_many(s.data(), s.length(), batch_size);
|
||||
}
|
||||
inline simdjson_result<document_stream> parser::parse_many(const padded_string_view &v, size_t batch_size) noexcept {
|
||||
return parse_many(v.data(), v.length(), batch_size);
|
||||
}
|
||||
|
||||
inline simdjson_result<document_stream> parser::parse_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) noexcept {
|
||||
if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; }
|
||||
if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) {
|
||||
buf += 3;
|
||||
len -= 3;
|
||||
}
|
||||
if (format == stream_format::comma_delimited_array) {
|
||||
// Strip leading JSON whitespace.
|
||||
while (len > 0 && (buf[0] == ' ' || buf[0] == '\t' || buf[0] == '\n' || buf[0] == '\r')) {
|
||||
buf++; len--;
|
||||
}
|
||||
// Expect the opening '['.
|
||||
if (len == 0 || buf[0] != '[') { return TAPE_ERROR; }
|
||||
buf++; len--;
|
||||
// Strip trailing JSON whitespace.
|
||||
while (len > 0 && (buf[len-1] == ' ' || buf[len-1] == '\t' || buf[len-1] == '\n' || buf[len-1] == '\r')) {
|
||||
len--;
|
||||
}
|
||||
// Expect the closing ']'.
|
||||
if (len == 0 || buf[len-1] != ']') { return TAPE_ERROR; }
|
||||
len--;
|
||||
// Fall through to comma_delimited over the array contents.
|
||||
format = stream_format::comma_delimited;
|
||||
}
|
||||
return document_stream(*this, buf, len, batch_size, format);
|
||||
}
|
||||
inline simdjson_result<document_stream> parser::parse_many(const char *buf, size_t len, size_t batch_size, stream_format format) noexcept {
|
||||
return parse_many(reinterpret_cast<const uint8_t *>(buf), len, batch_size, format);
|
||||
}
|
||||
inline simdjson_result<document_stream> parser::parse_many(const std::string &s, size_t batch_size, stream_format format) noexcept {
|
||||
return parse_many(s.data(), s.length(), batch_size, format);
|
||||
}
|
||||
inline simdjson_result<document_stream> parser::parse_many(const padded_string &s, size_t batch_size, stream_format format) noexcept {
|
||||
return parse_many(s.data(), s.length(), batch_size, format);
|
||||
}
|
||||
inline simdjson_result<document_stream> parser::parse_many(const padded_string_view &v, size_t batch_size, stream_format format) noexcept {
|
||||
return parse_many(v.data(), v.length(), batch_size, format);
|
||||
}
|
||||
|
||||
simdjson_inline size_t parser::capacity() const noexcept {
|
||||
return implementation ? implementation->capacity() : 0;
|
||||
|
||||
@@ -490,39 +490,10 @@ public:
|
||||
/** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */
|
||||
inline simdjson_result<document_stream> parse_many(const padded_string &s, size_t batch_size = dom::DEFAULT_BATCH_SIZE) noexcept;
|
||||
inline simdjson_result<document_stream> parse_many(const padded_string &&s, size_t batch_size) = delete;// unsafe
|
||||
/** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size)
|
||||
*
|
||||
* Because padded_string_view guarantees SIMDJSON_PADDING trailing bytes, this
|
||||
* overload is safe to use with buffers that the caller owns elsewhere (for
|
||||
* example, a padded_memory_map), with no extra copy. Without this overload,
|
||||
* passing a padded_string_view would silently bind to the padded_string
|
||||
* overload via an implicit conversion, allocating and copying the input, and
|
||||
* — because that temporary is destroyed at the end of the full-expression —
|
||||
* leaving the returned document_stream pointing at freed memory. */
|
||||
inline simdjson_result<document_stream> parse_many(const padded_string_view &v, size_t batch_size = dom::DEFAULT_BATCH_SIZE) noexcept;
|
||||
|
||||
/** @private We do not want to allow implicit conversion from C string to std::string. */
|
||||
simdjson_result<document_stream> parse_many(const char *buf, size_t batch_size = dom::DEFAULT_BATCH_SIZE) noexcept = delete;
|
||||
|
||||
/**
|
||||
* Parse a stream of JSON documents with explicit format specification.
|
||||
*
|
||||
* @param buf The concatenated JSON documents.
|
||||
* @param len The length of the buffer.
|
||||
* @param batch_size The batch size to use.
|
||||
* @param format The stream format.
|
||||
* @return A stream of documents, or an error.
|
||||
*/
|
||||
inline simdjson_result<document_stream> parse_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) noexcept;
|
||||
/** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
|
||||
inline simdjson_result<document_stream> parse_many(const char *buf, size_t len, size_t batch_size, stream_format format) noexcept;
|
||||
/** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
|
||||
inline simdjson_result<document_stream> parse_many(const std::string &s, size_t batch_size, stream_format format) noexcept;
|
||||
/** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
|
||||
inline simdjson_result<document_stream> parse_many(const padded_string &s, size_t batch_size, stream_format format) noexcept;
|
||||
/** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
|
||||
inline simdjson_result<document_stream> parse_many(const padded_string_view &v, size_t batch_size, stream_format format) noexcept;
|
||||
|
||||
/**
|
||||
* Ensure this parser has enough memory to process JSON documents up to `capacity` bytes in length
|
||||
* and `max_depth` depth.
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#if !defined(SIMDJSON_FEATURE_DOM_API)
|
||||
#define SIMDJSON_FEATURE_DOM_API 1
|
||||
#endif
|
||||
|
||||
#if !defined(SIMDJSON_FEATURE_ONDEMAND_API)
|
||||
#define SIMDJSON_FEATURE_ONDEMAND_API 1
|
||||
#endif
|
||||
|
||||
#if !defined(SIMDJSON_FEATURE_BUILDER_API)
|
||||
#define SIMDJSON_FEATURE_BUILDER_API 1
|
||||
#endif
|
||||
|
||||
#if !SIMDJSON_FEATURE_DOM_API && !SIMDJSON_FEATURE_ONDEMAND_API
|
||||
#error "simdjson: at least one of SIMDJSON_FEATURE_DOM_API or SIMDJSON_FEATURE_ONDEMAND_API must be enabled."
|
||||
#endif
|
||||
@@ -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;
|
||||
@@ -78,7 +74,6 @@ inline simdjson_warn_unused error_code dom_parser_implementation::set_capacity(s
|
||||
}
|
||||
|
||||
inline simdjson_warn_unused error_code dom_parser_implementation::set_max_depth(size_t max_depth) noexcept {
|
||||
if(max_depth > SIMDJSON_MAX_DEPTH) { return CAPACITY; }
|
||||
// Stage 2 stacks
|
||||
open_containers.reset(new (std::nothrow) open_container[max_depth]);
|
||||
is_array.reset(new (std::nothrow) bool[max_depth]);
|
||||
|
||||
@@ -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; }
|
||||
|
||||
//
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_GENERIC_BUILDER_DEPENDENCIES_H)
|
||||
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
|
||||
#include "simdjson/feature_macros.h"
|
||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||
|
||||
#if SIMDJSON_FEATURE_ONDEMAND_API
|
||||
|
||||
#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_GENERIC_ONDEMAND_DEPENDENCIES_H)
|
||||
#error simdjson/generic/ondemand/dependencies.h must be included before simdjson/generic/ondemand/amalgamated.h!
|
||||
#endif
|
||||
|
||||
@@ -22,7 +28,6 @@
|
||||
#include "simdjson/generic/ondemand/field.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 +45,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"
|
||||
@@ -50,3 +54,5 @@
|
||||
// JSON path accessor (compile-time) - must be after inline definitions
|
||||
#include "simdjson/generic/ondemand/compile_time_accessors.h"
|
||||
|
||||
#endif // SIMDJSON_FEATURE_ONDEMAND_API
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
// Internal headers needed for ondemand generics.
|
||||
// All includes not under simdjson/generic/ondemand must be here!
|
||||
// Otherwise, amalgamation will fail.
|
||||
#include "simdjson/feature_macros.h"
|
||||
#include "simdjson/dom/base.h" // for MINIMAL_DOCUMENT_CAPACITY
|
||||
#include "simdjson/implementation.h"
|
||||
#include "simdjson/padded_string.h"
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -95,15 +95,13 @@ simdjson_inline document_stream::document_stream(
|
||||
const uint8_t *_buf,
|
||||
size_t _len,
|
||||
size_t _batch_size,
|
||||
bool _allow_comma_separated,
|
||||
stream_format _format
|
||||
bool _allow_comma_separated
|
||||
) noexcept
|
||||
: parser{&_parser},
|
||||
buf{_buf},
|
||||
len{_len},
|
||||
batch_size{_batch_size <= MINIMAL_BATCH_SIZE ? MINIMAL_BATCH_SIZE : _batch_size},
|
||||
allow_comma_separated{_allow_comma_separated},
|
||||
format{_format},
|
||||
error{SUCCESS}
|
||||
#ifdef SIMDJSON_THREADS_ENABLED
|
||||
, use_thread(_parser.threaded) // we need to make a copy because _parser.threaded can change
|
||||
@@ -122,7 +120,6 @@ simdjson_inline document_stream::document_stream() noexcept
|
||||
len{0},
|
||||
batch_size{0},
|
||||
allow_comma_separated{false},
|
||||
format{stream_format::whitespace_delimited},
|
||||
error{UNINITIALIZED}
|
||||
#ifdef SIMDJSON_THREADS_ENABLED
|
||||
, use_thread(false)
|
||||
@@ -222,10 +219,7 @@ inline void document_stream::start() noexcept {
|
||||
error = run_stage1(*parser, batch_start);
|
||||
}
|
||||
if (error) { return; }
|
||||
// For json_sequence mode, structural_indexes[0] points to the actual JSON value
|
||||
// after the RS delimiter and any following whitespace. For regular mode, it is
|
||||
// the offset from batch_start to the first document in the batch.
|
||||
doc_index = batch_start + parser->implementation->structural_indexes[0];
|
||||
doc_index = batch_start;
|
||||
doc = document(json_iterator(&buf[batch_start], parser));
|
||||
doc.iter._streaming = true;
|
||||
|
||||
@@ -306,7 +300,7 @@ inline void document_stream::next() noexcept {
|
||||
*/
|
||||
|
||||
if (error) { continue; } // If the error was EMPTY, we may want to load another batch.
|
||||
doc_index = batch_start + parser->implementation->structural_indexes[0];
|
||||
doc_index = batch_start;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -335,35 +329,10 @@ inline error_code document_stream::run_stage1(ondemand::parser &p, size_t _batch
|
||||
// This code only updates the structural index in the parser, it does not update any json_iterator
|
||||
// instance.
|
||||
size_t remaining = len - _batch_start;
|
||||
stage1_mode mode;
|
||||
if (remaining <= batch_size) {
|
||||
// Final batch
|
||||
switch (format) {
|
||||
case stream_format::json_sequence:
|
||||
mode = stage1_mode::json_sequence_final;
|
||||
break;
|
||||
case stream_format::comma_delimited:
|
||||
mode = stage1_mode::comma_delimited_final;
|
||||
break;
|
||||
default:
|
||||
mode = stage1_mode::streaming_final;
|
||||
break;
|
||||
}
|
||||
return p.implementation->stage1(&buf[_batch_start], remaining, mode);
|
||||
return p.implementation->stage1(&buf[_batch_start], remaining, stage1_mode::streaming_final);
|
||||
} else {
|
||||
// Partial batch
|
||||
switch (format) {
|
||||
case stream_format::json_sequence:
|
||||
mode = stage1_mode::json_sequence_partial;
|
||||
break;
|
||||
case stream_format::comma_delimited:
|
||||
mode = stage1_mode::comma_delimited_partial;
|
||||
break;
|
||||
default:
|
||||
mode = stage1_mode::streaming_partial;
|
||||
break;
|
||||
}
|
||||
return p.implementation->stage1(&buf[_batch_start], batch_size, mode);
|
||||
return p.implementation->stage1(&buf[_batch_start], batch_size, stage1_mode::streaming_partial);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -384,21 +353,14 @@ simdjson_inline std::string_view document_stream::iterator::source() const noexc
|
||||
depth--;
|
||||
break;
|
||||
default: // Scalar value document
|
||||
// TODO: We could remove trailing whitespaces
|
||||
// This returns a string spanning from start of value to the beginning of the next document (excluded)
|
||||
{
|
||||
auto next_index = stream->parser->implementation->structural_indexes[++cur_struct_index];
|
||||
// normally the length would be next_index - current_index() - 1, except for the last document
|
||||
size_t svlen = next_index - current_index();
|
||||
const char *start = reinterpret_cast<const char*>(stream->buf) + current_index();
|
||||
// Trim trailing whitespace, NUL, and RS (0x1E). In RFC 7464
|
||||
// json_sequence mode the scanner classifies RS as a scalar
|
||||
// character, so an RS-prefixed scalar document (number / true /
|
||||
// false / null / string) has no closing structural index and the
|
||||
// slice runs all the way up to the next document's RS. RS 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')) {
|
||||
svlen--;
|
||||
}
|
||||
return std::string_view(start, svlen);
|
||||
@@ -479,4 +441,4 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_stre
|
||||
|
||||
}
|
||||
|
||||
#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H
|
||||
#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H
|
||||
@@ -229,16 +229,13 @@ private:
|
||||
* @param buf is the raw byte buffer we need to process
|
||||
* @param len is the length of the raw byte buffer in bytes
|
||||
* @param batch_size is the size of the windows (must be strictly greater or equal to the largest JSON document)
|
||||
* @param allow_comma_separated whether to allow comma-separated documents
|
||||
* @param format the stream format
|
||||
*/
|
||||
simdjson_inline document_stream(
|
||||
ondemand::parser &parser,
|
||||
const uint8_t *buf,
|
||||
size_t len,
|
||||
size_t batch_size,
|
||||
bool allow_comma_separated,
|
||||
stream_format format = stream_format::whitespace_delimited
|
||||
bool allow_comma_separated
|
||||
) noexcept;
|
||||
|
||||
/**
|
||||
@@ -287,7 +284,6 @@ private:
|
||||
size_t len;
|
||||
size_t batch_size;
|
||||
bool allow_comma_separated;
|
||||
stream_format format;
|
||||
/**
|
||||
* We are going to use just one document instance. The document owns
|
||||
* the json_iterator. It implies that we only ever pass a reference
|
||||
|
||||
@@ -177,13 +177,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 +189,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -339,15 +347,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 {
|
||||
|
||||
@@ -172,21 +172,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
|
||||
@@ -336,13 +328,7 @@ public:
|
||||
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]);
|
||||
@@ -136,34 +133,6 @@ simdjson_warn_unused simdjson_inline simdjson_result<json_iterator> parser::iter
|
||||
return json_iterator(reinterpret_cast<const uint8_t *>(json.data()), this);
|
||||
}
|
||||
|
||||
inline simdjson_result<document_stream> parser::iterate_many(const uint8_t *buf, size_t len, size_t batch_size) noexcept {
|
||||
if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; }
|
||||
if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) {
|
||||
buf += 3;
|
||||
len -= 3;
|
||||
}
|
||||
return document_stream(*this, buf, len, batch_size, false, stream_format::whitespace_delimited);
|
||||
}
|
||||
inline simdjson_result<document_stream> parser::iterate_many(const char *buf, size_t len, size_t batch_size) noexcept {
|
||||
return iterate_many(reinterpret_cast<const uint8_t *>(buf), len, batch_size);
|
||||
}
|
||||
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size) noexcept {
|
||||
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
|
||||
return iterate_many(s.data(), s.length(), batch_size);
|
||||
}
|
||||
inline simdjson_result<document_stream> parser::iterate_many(const padded_string &s, size_t batch_size) noexcept {
|
||||
return iterate_many(padded_string_view(s), batch_size);
|
||||
}
|
||||
inline simdjson_result<document_stream> parser::iterate_many(const std::string &s, size_t batch_size) noexcept {
|
||||
return iterate_many(padded_string_view(s), batch_size);
|
||||
}
|
||||
inline simdjson_result<document_stream> parser::iterate_many(std::string &s, size_t batch_size) noexcept {
|
||||
return iterate_many(pad(s), batch_size);
|
||||
}
|
||||
|
||||
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
|
||||
SIMDJSON_PUSH_DISABLE_WARNINGS
|
||||
SIMDJSON_DISABLE_DEPRECATED_WARNING
|
||||
inline simdjson_result<document_stream> parser::iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept {
|
||||
// Warning: no check is done on the buffer padding. We trust the user.
|
||||
if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; }
|
||||
@@ -171,11 +140,8 @@ inline simdjson_result<document_stream> parser::iterate_many(const uint8_t *buf,
|
||||
buf += 3;
|
||||
len -= 3;
|
||||
}
|
||||
// Map allow_comma_separated to stream_format::comma_delimited
|
||||
if (allow_comma_separated) {
|
||||
return document_stream(*this, buf, len, batch_size, false, stream_format::comma_delimited);
|
||||
}
|
||||
return document_stream(*this, buf, len, batch_size, false, stream_format::whitespace_delimited);
|
||||
if(allow_comma_separated && batch_size < len) { batch_size = len; }
|
||||
return document_stream(*this, buf, len, batch_size, allow_comma_separated);
|
||||
}
|
||||
|
||||
inline simdjson_result<document_stream> parser::iterate_many(const char *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept {
|
||||
@@ -195,48 +161,6 @@ inline simdjson_result<document_stream> parser::iterate_many(const std::string &
|
||||
inline simdjson_result<document_stream> parser::iterate_many(std::string &s, size_t batch_size, bool allow_comma_separated) noexcept {
|
||||
return iterate_many(pad(s), batch_size, allow_comma_separated);
|
||||
}
|
||||
SIMDJSON_POP_DISABLE_WARNINGS
|
||||
#endif // SIMDJSON_DISABLE_DEPRECATED_API
|
||||
|
||||
inline simdjson_result<document_stream> parser::iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) noexcept {
|
||||
if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; }
|
||||
if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) {
|
||||
buf += 3;
|
||||
len -= 3;
|
||||
}
|
||||
if (format == stream_format::comma_delimited_array) {
|
||||
// Strip leading JSON whitespace.
|
||||
while (len > 0 && (buf[0] == ' ' || buf[0] == '\t' || buf[0] == '\n' || buf[0] == '\r')) {
|
||||
buf++; len--;
|
||||
}
|
||||
// Expect the opening '['.
|
||||
if (len == 0 || buf[0] != '[') { return TAPE_ERROR; }
|
||||
buf++; len--;
|
||||
// Strip trailing JSON whitespace.
|
||||
while (len > 0 && (buf[len-1] == ' ' || buf[len-1] == '\t' || buf[len-1] == '\n' || buf[len-1] == '\r')) {
|
||||
len--;
|
||||
}
|
||||
// Expect the closing ']'.
|
||||
if (len == 0 || buf[len-1] != ']') { return TAPE_ERROR; }
|
||||
len--;
|
||||
// Fall through to comma_delimited over the array contents.
|
||||
format = stream_format::comma_delimited;
|
||||
}
|
||||
return document_stream(*this, buf, len, batch_size, false, format);
|
||||
}
|
||||
inline simdjson_result<document_stream> parser::iterate_many(const char *buf, size_t len, size_t batch_size, stream_format format) noexcept {
|
||||
return iterate_many(reinterpret_cast<const uint8_t *>(buf), len, batch_size, format);
|
||||
}
|
||||
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size, stream_format format) noexcept {
|
||||
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
|
||||
return iterate_many(s.data(), s.length(), batch_size, format);
|
||||
}
|
||||
inline simdjson_result<document_stream> parser::iterate_many(const std::string &s, size_t batch_size, stream_format format) noexcept {
|
||||
return iterate_many(padded_string_view(s), batch_size, format);
|
||||
}
|
||||
inline simdjson_result<document_stream> parser::iterate_many(const padded_string &s, size_t batch_size, stream_format format) noexcept {
|
||||
return iterate_many(padded_string_view(s), batch_size, format);
|
||||
}
|
||||
simdjson_pure simdjson_inline size_t parser::capacity() const noexcept {
|
||||
return _capacity;
|
||||
}
|
||||
@@ -275,7 +199,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();
|
||||
|
||||
@@ -244,66 +244,32 @@ public:
|
||||
* spot is cache-related: small enough to fit in cache, yet big enough to
|
||||
* parse as many documents as possible in one tight loop.
|
||||
* Defaults to 10MB, which has been a reasonable sweet spot in our tests.
|
||||
* @param allow_comma_separated @deprecated Use stream_format::comma_delimited instead.
|
||||
* When true, maps internally to stream_format::comma_delimited.
|
||||
* Defaults to false.
|
||||
* @param allow_comma_separated (defaults on false) This allows a mode where the documents are
|
||||
* separated by commas instead of whitespace. It comes with a performance
|
||||
* penalty because the entire document is indexed at once (and the document must be
|
||||
* less than 4 GB), and there is no multithreading. In this mode, the batch_size parameter
|
||||
* is effectively ignored, as it is set to at least the document size.
|
||||
* @return The stream, or an error. An empty input will yield 0 documents rather than an EMPTY error. Errors:
|
||||
* - MEMALLOC if the parser does not have enough capacity and memory allocation fails
|
||||
* - CAPACITY if the parser does not have enough capacity and batch_size > max_capacity.
|
||||
* - other json errors if parsing fails. You should not rely on these errors to always the same for the
|
||||
* same document: they may vary under runtime dispatch (so they may vary depending on your system and hardware).
|
||||
*/
|
||||
inline simdjson_result<document_stream> iterate_many(const uint8_t *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
|
||||
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size) */
|
||||
inline simdjson_result<document_stream> iterate_many(padded_string_view json, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
|
||||
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size) */
|
||||
inline simdjson_result<document_stream> iterate_many(const char *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
|
||||
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size) */
|
||||
inline simdjson_result<document_stream> iterate_many(const std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
|
||||
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size)
|
||||
inline simdjson_result<document_stream> iterate_many(const uint8_t *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept;
|
||||
/** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */
|
||||
inline simdjson_result<document_stream> iterate_many(padded_string_view json, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept;
|
||||
/** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */
|
||||
inline simdjson_result<document_stream> iterate_many(const char *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept;
|
||||
/** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */
|
||||
inline simdjson_result<document_stream> iterate_many(const std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept;
|
||||
/** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size)
|
||||
the string might be automatically padded with up to SIMDJSON_PADDING whitespace characters */
|
||||
inline simdjson_result<document_stream> iterate_many(std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
|
||||
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size) */
|
||||
inline simdjson_result<document_stream> iterate_many(const padded_string &s, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
|
||||
inline simdjson_result<document_stream> iterate_many(std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept;
|
||||
/** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */
|
||||
inline simdjson_result<document_stream> iterate_many(const padded_string &s, size_t batch_size = DEFAULT_BATCH_SIZE, bool allow_comma_separated = false) noexcept;
|
||||
/** @private We do not want to allow implicit conversion from C string to std::string. */
|
||||
simdjson_result<document_stream> iterate_many(const char *buf, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept = delete;
|
||||
|
||||
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
|
||||
/**
|
||||
* @deprecated Use iterate_many with stream_format::comma_delimited instead.
|
||||
*/
|
||||
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept;
|
||||
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
|
||||
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(padded_string_view json, size_t batch_size, bool allow_comma_separated) noexcept;
|
||||
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
|
||||
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(const char *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept;
|
||||
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
|
||||
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(const std::string &s, size_t batch_size, bool allow_comma_separated) noexcept;
|
||||
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
|
||||
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(std::string &s, size_t batch_size, bool allow_comma_separated) noexcept;
|
||||
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
|
||||
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(const padded_string &s, size_t batch_size, bool allow_comma_separated) noexcept;
|
||||
#endif // SIMDJSON_DISABLE_DEPRECATED_API
|
||||
|
||||
/**
|
||||
* Parse a stream of JSON documents with explicit format specification.
|
||||
*
|
||||
* @param buf The concatenated JSON documents.
|
||||
* @param len The length of the buffer.
|
||||
* @param batch_size The batch size to use.
|
||||
* @param format The stream format (whitespace_delimited, json_sequence, or comma_delimited).
|
||||
* @return A stream of documents, or an error.
|
||||
*/
|
||||
inline simdjson_result<document_stream> iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) noexcept;
|
||||
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
|
||||
inline simdjson_result<document_stream> iterate_many(const char *buf, size_t len, size_t batch_size, stream_format format) noexcept;
|
||||
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
|
||||
inline simdjson_result<document_stream> iterate_many(padded_string_view s, size_t batch_size, stream_format format) noexcept;
|
||||
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
|
||||
inline simdjson_result<document_stream> iterate_many(const std::string &s, size_t batch_size, stream_format format) noexcept;
|
||||
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
|
||||
inline simdjson_result<document_stream> iterate_many(const padded_string &s, size_t batch_size, stream_format format) noexcept;
|
||||
|
||||
/** The capacity of this parser (the largest document it can process). */
|
||||
simdjson_pure simdjson_inline size_t capacity() const noexcept;
|
||||
/** The maximum capacity of this parser (the largest document it is allowed to process). */
|
||||
@@ -422,6 +388,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 */
|
||||
@@ -457,4 +425,4 @@ public:
|
||||
|
||||
} // namespace simdjson
|
||||
|
||||
#endif // SIMDJSON_GENERIC_ONDEMAND_PARSER_H
|
||||
#endif // SIMDJSON_GENERIC_ONDEMAND_PARSER_H
|
||||
@@ -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
|
||||
|
||||
@@ -19,12 +19,7 @@ class document;
|
||||
* 3) The stream_final mode allows us to truncate final
|
||||
* unterminated strings. It is useful in conjunction with streaming_partial.
|
||||
*/
|
||||
enum class stage1_mode {
|
||||
regular,
|
||||
streaming_partial, streaming_final,
|
||||
json_sequence_partial, json_sequence_final,
|
||||
comma_delimited_partial, comma_delimited_final
|
||||
};
|
||||
enum class stage1_mode { regular, streaming_partial, streaming_final};
|
||||
|
||||
/**
|
||||
* Returns true if mode == streaming_partial or mode == streaming_final
|
||||
@@ -36,6 +31,7 @@ inline bool is_streaming(stage1_mode mode) {
|
||||
// return (mode == stage1_mode::streaming_partial || mode == stage1_mode::streaming_final);
|
||||
}
|
||||
|
||||
|
||||
namespace internal {
|
||||
|
||||
|
||||
|
||||
@@ -8,12 +8,14 @@ namespace simdjson {
|
||||
* @copydoc simdjson::builtin::ondemand
|
||||
*/
|
||||
namespace ondemand = builtin::ondemand;
|
||||
#if SIMDJSON_FEATURE_BUILDER_API
|
||||
/**
|
||||
* @copydoc simdjson::builtin::builder
|
||||
*/
|
||||
namespace builder = builtin::builder;
|
||||
#endif // SIMDJSON_FEATURE_BUILDER_API
|
||||
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
#if SIMDJSON_STATIC_REFLECTION && SIMDJSON_FEATURE_BUILDER_API
|
||||
/**
|
||||
* Create a JSON string from any user-defined type using static reflection.
|
||||
* Only available when SIMDJSON_STATIC_REFLECTION is enabled.
|
||||
|
||||
@@ -10,18 +10,13 @@
|
||||
#include <climits>
|
||||
#include <cwchar>
|
||||
|
||||
#if SIMDJSON_HAS_UNISTD_H
|
||||
#ifndef _WIN32
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
// On Windows, `padded_memory_map` (when it is enabled) depends on types and
|
||||
// functions declared in <windows.h>. We deliberately do NOT include that
|
||||
// header here: users of simdjson who want `padded_memory_map` on Windows
|
||||
// must include <windows.h> themselves *before* including this header. See
|
||||
// padded_string.h for the detection logic.
|
||||
|
||||
namespace simdjson {
|
||||
namespace internal {
|
||||
@@ -390,9 +385,7 @@ inline bool padded_string_builder::reserve(size_t additional) noexcept {
|
||||
}
|
||||
|
||||
|
||||
#if SIMDJSON_HAS_PADDED_MEMORY_MAP
|
||||
|
||||
#if SIMDJSON_HAS_UNISTD_H
|
||||
#ifndef _WIN32
|
||||
simdjson_inline padded_memory_map::padded_memory_map(const char *filename) noexcept {
|
||||
|
||||
int fd = open(filename, O_RDONLY);
|
||||
@@ -428,132 +421,7 @@ simdjson_inline padded_memory_map::~padded_memory_map() noexcept {
|
||||
munmap(const_cast<char *>(data), size + simdjson::SIMDJSON_PADDING);
|
||||
}
|
||||
}
|
||||
#elif defined(_WIN32)
|
||||
// Windows zero-copy implementation using placeholder virtual memory.
|
||||
//
|
||||
// We use the modern Windows memory APIs (VirtualAlloc2, CreateFileMapping2,
|
||||
// MapViewOfFile3 — available since Windows 10 1803) to map the file into a
|
||||
// contiguous virtual address range that includes at least SIMDJSON_PADDING
|
||||
// zero bytes after the file content, with no data copies.
|
||||
//
|
||||
// Strategy:
|
||||
// 1. If rounding the file size up to the allocation granularity already
|
||||
// exceeds file_size + SIMDJSON_PADDING, the OS page zero-fill provides
|
||||
// the padding and we use a simple MapViewOfFile3 call.
|
||||
// 2. Otherwise we reserve a contiguous placeholder region via VirtualAlloc2,
|
||||
// split it at the granularity-aligned file boundary, map the file into
|
||||
// the first part, and commit zero pages for the second part (padding).
|
||||
simdjson_inline padded_memory_map::padded_memory_map(const char *filename) noexcept {
|
||||
HANDLE file_handle = ::CreateFileA(
|
||||
filename, GENERIC_READ,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (file_handle == INVALID_HANDLE_VALUE) {
|
||||
return;
|
||||
}
|
||||
LARGE_INTEGER file_size_li;
|
||||
if (!::GetFileSizeEx(file_handle, &file_size_li) || file_size_li.QuadPart < 0) {
|
||||
::CloseHandle(file_handle);
|
||||
return;
|
||||
}
|
||||
#if SIMDJSON_IS_32BITS
|
||||
if (static_cast<unsigned long long>(file_size_li.QuadPart) >
|
||||
static_cast<unsigned long long>(SIZE_MAX - simdjson::SIMDJSON_PADDING)) {
|
||||
::CloseHandle(file_handle);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
size = static_cast<size_t>(file_size_li.QuadPart);
|
||||
if (size == 0) {
|
||||
::CloseHandle(file_handle);
|
||||
return;
|
||||
}
|
||||
|
||||
HANDLE section = ::CreateFileMapping2(
|
||||
file_handle, NULL, FILE_MAP_READ, PAGE_READONLY,
|
||||
0, 0, NULL, NULL, 0);
|
||||
::CloseHandle(file_handle);
|
||||
if (section == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
SYSTEM_INFO si;
|
||||
::GetSystemInfo(&si);
|
||||
const size_t granularity = static_cast<size_t>(si.dwAllocationGranularity);
|
||||
const size_t file_region = (size + granularity - 1) & ~(granularity - 1);
|
||||
const size_t total_needed = size + simdjson::SIMDJSON_PADDING;
|
||||
|
||||
if (file_region >= total_needed) {
|
||||
// The zero-fill in the last page already covers the padding.
|
||||
PVOID view = ::MapViewOfFile3(
|
||||
section, ::GetCurrentProcess(), NULL, 0, 0,
|
||||
0, PAGE_READONLY, NULL, 0);
|
||||
::CloseHandle(section);
|
||||
if (view != NULL) {
|
||||
data = static_cast<const char *>(view);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// We need extra zero pages beyond the file region. Use the placeholder API
|
||||
// to get a contiguous virtual address range spanning both the file mapping
|
||||
// and the zero-filled padding.
|
||||
const size_t padding_region =
|
||||
((total_needed - file_region) + granularity - 1) & ~(granularity - 1);
|
||||
const size_t reserve_size = file_region + padding_region;
|
||||
|
||||
// Reserve a contiguous placeholder.
|
||||
PVOID placeholder = ::VirtualAlloc2(
|
||||
::GetCurrentProcess(), NULL, reserve_size,
|
||||
MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, PAGE_NOACCESS, NULL, 0);
|
||||
if (placeholder == NULL) {
|
||||
::CloseHandle(section);
|
||||
return;
|
||||
}
|
||||
|
||||
// Split into two placeholders at the file_region boundary.
|
||||
if (!::VirtualFree(placeholder, file_region,
|
||||
MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER)) {
|
||||
::VirtualFree(placeholder, 0, MEM_RELEASE);
|
||||
::CloseHandle(section);
|
||||
return;
|
||||
}
|
||||
|
||||
// Map the file into the first placeholder.
|
||||
PVOID file_view = ::MapViewOfFile3(
|
||||
section, ::GetCurrentProcess(), placeholder, 0, file_region,
|
||||
MEM_REPLACE_PLACEHOLDER, PAGE_READONLY, NULL, 0);
|
||||
::CloseHandle(section);
|
||||
if (file_view == NULL) {
|
||||
::VirtualFree(placeholder, 0, MEM_RELEASE);
|
||||
::VirtualFree(static_cast<char *>(placeholder) + file_region,
|
||||
0, MEM_RELEASE);
|
||||
return;
|
||||
}
|
||||
|
||||
// Commit zero pages in the second placeholder (the padding).
|
||||
void *pad = static_cast<char *>(placeholder) + file_region;
|
||||
PVOID padding_ptr = ::VirtualAlloc2(
|
||||
::GetCurrentProcess(), pad, padding_region,
|
||||
MEM_REPLACE_PLACEHOLDER | MEM_COMMIT, PAGE_READONLY, NULL, 0);
|
||||
if (padding_ptr == NULL) {
|
||||
::UnmapViewOfFile(file_view);
|
||||
::VirtualFree(pad, 0, MEM_RELEASE);
|
||||
return;
|
||||
}
|
||||
|
||||
data = static_cast<const char *>(file_view);
|
||||
padding_view_ = padding_ptr;
|
||||
}
|
||||
|
||||
simdjson_inline padded_memory_map::~padded_memory_map() noexcept {
|
||||
if (data == nullptr) { return; }
|
||||
::UnmapViewOfFile(data);
|
||||
if (padding_view_ != nullptr) {
|
||||
::VirtualFree(padding_view_, 0, MEM_RELEASE);
|
||||
}
|
||||
}
|
||||
#endif // POSIX or _WIN32
|
||||
|
||||
simdjson_inline simdjson::padded_string_view padded_memory_map::view() const noexcept simdjson_lifetime_bound {
|
||||
if(!is_valid()) {
|
||||
@@ -565,8 +433,7 @@ simdjson_inline simdjson::padded_string_view padded_memory_map::view() const noe
|
||||
simdjson_inline bool padded_memory_map::is_valid() const noexcept {
|
||||
return data != nullptr;
|
||||
}
|
||||
|
||||
#endif // SIMDJSON_HAS_PADDED_MEMORY_MAP
|
||||
#endif // _WIN32
|
||||
|
||||
} // namespace simdjson
|
||||
|
||||
|
||||
@@ -277,26 +277,11 @@ inline std::ostream& operator<<(std::ostream& out, const padded_string& s) { ret
|
||||
inline std::ostream& operator<<(std::ostream& out, simdjson_result<padded_string> &s) noexcept(false) { return out << s.value(); }
|
||||
#endif
|
||||
|
||||
#if SIMDJSON_HAS_PADDED_MEMORY_MAP
|
||||
|
||||
#ifndef _WIN32
|
||||
/**
|
||||
* A class representing a memory-mapped file with padding.
|
||||
*
|
||||
* On POSIX systems (Linux, macOS, BSD, ...), this uses `mmap` to map the file
|
||||
* contents directly into memory, which is efficient for large files (no copy).
|
||||
*
|
||||
* On Windows, this class is disabled by default and must be opted into at
|
||||
* build time by defining `SIMDJSON_ENABLE_MEMORY_FILE_MAPPING_ON_WINDOWS=1`. When
|
||||
* enabled, `<windows.h>` must also be included before `<simdjson.h>` and
|
||||
* the compilation must target Windows 10, version 1803 or later. The
|
||||
* Windows implementation uses the modern memory APIs (`VirtualAlloc2`,
|
||||
* `CreateFileMapping2`, `MapViewOfFile3`) with the placeholder virtual
|
||||
* memory mechanism to always achieve true zero-copy mapping with
|
||||
* contiguous zero-filled padding.
|
||||
*
|
||||
* Either way, the resulting `padded_string_view` carries at least
|
||||
* `SIMDJSON_PADDING` bytes of accessible zero-filled padding after the file
|
||||
* content, so it can be consumed directly by the simdjson parsers (including
|
||||
* `parse_many` / `iterate_many`).
|
||||
* It is only available on non-Windows platforms, as Windows has different APIs for memory mapping.
|
||||
*/
|
||||
class padded_memory_map {
|
||||
public:
|
||||
@@ -304,11 +289,9 @@ public:
|
||||
* Create a new padded memory map for the given file.
|
||||
* After creating the memory map, you can call view() to get a padded_string_view of the file content.
|
||||
* The memory map will be automatically released when the padded_memory_map instance is destroyed.
|
||||
* On POSIX systems, the file content is not copied, so this is efficient for large files.
|
||||
* On Windows, the file is mapped into memory via `MapViewOfFile3` (zero-copy).
|
||||
* In all cases, the file must remain unchanged while the memory map is in use.
|
||||
* In case of error (e.g., file not found, permission denied, etc.), the memory map will be
|
||||
* invalid and view() will return an empty view.
|
||||
* Note that the file content is not copied, so this is efficient for large files. However,
|
||||
* the file must remain unchanged while the memory map is in use. In case of error (e.g., file not found,
|
||||
* permission denied, etc.), the memory map will be invalid and view() will return an empty view.
|
||||
* You can check if the memory map is valid by calling is_valid() before using view().
|
||||
*
|
||||
* @param filename the path to the file to memory-map.
|
||||
@@ -345,14 +328,8 @@ private:
|
||||
padded_memory_map &operator=(const padded_memory_map &) = delete;
|
||||
const char *data{nullptr};
|
||||
size_t size{0};
|
||||
#ifdef _WIN32
|
||||
// When the file ends near an allocation-granularity boundary, we use the
|
||||
// placeholder API to append zero-filled padding pages. This pointer tracks
|
||||
// that region so the destructor can release it with VirtualFree.
|
||||
void *padding_view_{nullptr};
|
||||
#endif
|
||||
};
|
||||
#endif // SIMDJSON_HAS_PADDED_MEMORY_MAP
|
||||
#endif // _WIN32
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include <cstring> /* memcmp */
|
||||
|
||||
// for page size computation.
|
||||
#if SIMDJSON_HAS_UNISTD_H
|
||||
#if defined(__unix__) || defined(__APPLE__) || defined(__linux__)
|
||||
#include <unistd.h>
|
||||
#if defined(__APPLE__)
|
||||
#include <sys/sysctl.h>
|
||||
@@ -113,7 +113,7 @@ inline uint32_t get_page_size() noexcept {
|
||||
return static_cast<std::uint32_t>(si.dwPageSize);
|
||||
}();
|
||||
return cached;
|
||||
#elif SIMDJSON_HAS_UNISTD_H
|
||||
#elif defined(__unix__) || defined(__APPLE__) || defined(__linux__)
|
||||
static const uint32_t cached = []() -> uint32_t {
|
||||
long page_size = sysconf(_SC_PAGESIZE);
|
||||
if (page_size > 0) {
|
||||
|
||||
@@ -285,53 +285,5 @@ using std::size_t;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef SIMDJSON_HAS_UNISTD_H
|
||||
#if defined(__unix__) || defined(__APPLE__) || defined(__linux__)
|
||||
#define SIMDJSON_HAS_UNISTD_H 1
|
||||
#else
|
||||
#define SIMDJSON_HAS_UNISTD_H 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// padded_memory_map availability.
|
||||
//
|
||||
// On POSIX platforms the class is always available: the implementation uses
|
||||
// `mmap` (and a trailing anonymous page for padding) from <sys/mman.h>.
|
||||
//
|
||||
// On Windows the class is disabled by default and must be explicitly
|
||||
// opted into by defining `SIMDJSON_ENABLE_MEMORY_FILE_MAPPING_ON_WINDOWS=1`. Enabling
|
||||
// it requires:
|
||||
// 1. `<windows.h>` has been included *before* `<simdjson.h>` (so that
|
||||
// this header can see the Win32 types and the `_WINDOWS_` include
|
||||
// guard),
|
||||
// 2. the compilation targets Windows 10, version 1803 or later
|
||||
// (i.e. `NTDDI_VERSION >= NTDDI_WIN10_RS4`, `0x0A000005`). This is
|
||||
// required because the implementation relies on the modern memory
|
||||
// APIs introduced with that version (`CreateFileMapping2` /
|
||||
// `MapViewOfFile3`),
|
||||
// 3. the link step pulls in an import library that exports those APIs,
|
||||
// typically `onecore.lib` (or `mincore.lib`).
|
||||
//
|
||||
// The `SIMDJSON_ENABLE_MEMORY_FILE_MAPPING_ON_WINDOWS` CMake option arranges (1)-(3)
|
||||
// automatically when building simdjson with its own CMake. Consumers using
|
||||
// simdjson as a pre-built library are responsible for setting the macro,
|
||||
// the Windows version macros, and the link library themselves.
|
||||
//
|
||||
// If the opt-in conditions are not met on Windows, `padded_memory_map`
|
||||
// simply does not exist — any attempt to use it fails at compile time
|
||||
// with an "unknown identifier" diagnostic rather than silently degrading.
|
||||
//
|
||||
// The SIMDJSON_HAS_PADDED_MEMORY_MAP macro reflects whether the class is
|
||||
// available in the current translation unit. Users may test this macro to
|
||||
// conditionally compile code that depends on padded_memory_map.
|
||||
#ifndef SIMDJSON_HAS_PADDED_MEMORY_MAP
|
||||
#if defined(__unix__) || defined(__APPLE__) || defined(__linux__)
|
||||
#define SIMDJSON_HAS_PADDED_MEMORY_MAP 1
|
||||
#elif defined(_WINDOWS_) && defined(SIMDJSON_ENABLE_MEMORY_FILE_MAPPING_ON_WINDOWS) && SIMDJSON_ENABLE_MEMORY_FILE_MAPPING_ON_WINDOWS
|
||||
#define SIMDJSON_HAS_PADDED_MEMORY_MAP 1
|
||||
#else
|
||||
#define SIMDJSON_HAS_PADDED_MEMORY_MAP 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif // SIMDJSON_PORTABILITY_H
|
||||
|
||||
Executable
+226
@@ -0,0 +1,226 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Amalgamates simdjson into temporary files, then builds and runs 7 feature-flag
|
||||
test programs. Reports compile time and binary size for each configuration.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
import tempfile
|
||||
import time
|
||||
import shutil
|
||||
|
||||
SCRIPTPATH = os.path.dirname(os.path.abspath(__file__))
|
||||
PROJECTPATH = os.path.dirname(SCRIPTPATH)
|
||||
TESTSRC = os.path.join(PROJECTPATH, "tests", "feature_flag_tests")
|
||||
|
||||
# ── feature-flag configurations ──────────────────────────────────────────────
|
||||
CONFIGS = [
|
||||
{
|
||||
"name": "dom_only",
|
||||
"flags": {"SIMDJSON_FEATURE_DOM_API": 1,
|
||||
"SIMDJSON_FEATURE_ONDEMAND_API": 0,
|
||||
"SIMDJSON_FEATURE_BUILDER_API": 0},
|
||||
},
|
||||
{
|
||||
"name": "ondemand_only",
|
||||
"flags": {"SIMDJSON_FEATURE_DOM_API": 1,
|
||||
"SIMDJSON_FEATURE_ONDEMAND_API": 1,
|
||||
"SIMDJSON_FEATURE_BUILDER_API": 0},
|
||||
},
|
||||
{
|
||||
"name": "dom_and_ondemand",
|
||||
"flags": {"SIMDJSON_FEATURE_DOM_API": 1,
|
||||
"SIMDJSON_FEATURE_ONDEMAND_API": 1,
|
||||
"SIMDJSON_FEATURE_BUILDER_API": 0},
|
||||
},
|
||||
{
|
||||
"name": "builder_and_dom",
|
||||
"flags": {"SIMDJSON_FEATURE_DOM_API": 1,
|
||||
"SIMDJSON_FEATURE_ONDEMAND_API": 0,
|
||||
"SIMDJSON_FEATURE_BUILDER_API": 1},
|
||||
},
|
||||
{
|
||||
"name": "builder_and_ondemand",
|
||||
"flags": {"SIMDJSON_FEATURE_DOM_API": 1,
|
||||
"SIMDJSON_FEATURE_ONDEMAND_API": 1,
|
||||
"SIMDJSON_FEATURE_BUILDER_API": 1},
|
||||
},
|
||||
{
|
||||
"name": "builder_dom_and_ondemand",
|
||||
"flags": {"SIMDJSON_FEATURE_DOM_API": 1,
|
||||
"SIMDJSON_FEATURE_ONDEMAND_API": 1,
|
||||
"SIMDJSON_FEATURE_BUILDER_API": 1},
|
||||
},
|
||||
{
|
||||
"name": "builder_only",
|
||||
"flags": {"SIMDJSON_FEATURE_DOM_API": 1,
|
||||
"SIMDJSON_FEATURE_ONDEMAND_API": 0,
|
||||
"SIMDJSON_FEATURE_BUILDER_API": 1},
|
||||
},
|
||||
]
|
||||
|
||||
CXX = os.environ.get("CXX", "c++")
|
||||
CXXSTD = os.environ.get("CXXSTD", "-std=c++17")
|
||||
OPT = os.environ.get("OPT", "-O2")
|
||||
|
||||
|
||||
def amalgamate(output_dir):
|
||||
"""Run amalgamate.py, writing simdjson.h / simdjson.cpp into *output_dir*."""
|
||||
env = os.environ.copy()
|
||||
env["AMALGAMATE_OUTPUT_PATH"] = output_dir
|
||||
cmd = [sys.executable, os.path.join(SCRIPTPATH, "amalgamate.py")]
|
||||
result = subprocess.run(cmd, env=env, capture_output=True, text=True)
|
||||
if result.returncode != 0:
|
||||
print("amalgamate.py failed:")
|
||||
print(result.stdout)
|
||||
print(result.stderr)
|
||||
sys.exit(1)
|
||||
header = os.path.join(output_dir, "simdjson.h")
|
||||
source = os.path.join(output_dir, "simdjson.cpp")
|
||||
assert os.path.isfile(header), f"missing {header}"
|
||||
assert os.path.isfile(source), f"missing {source}"
|
||||
return header, source
|
||||
|
||||
|
||||
def compile_and_run(name, flags, amal_dir, build_dir):
|
||||
"""
|
||||
1. Compile simdjson.cpp -> simdjson.o (timed, size reported)
|
||||
2. Compile test .cpp -> test.o (timed, size reported)
|
||||
3. Link and run the test program
|
||||
|
||||
Returns (lib_time, lib_size, test_time, test_size) or Nones on failure.
|
||||
"""
|
||||
src = os.path.join(TESTSRC, f"{name}.cpp")
|
||||
simdjson_cpp = os.path.join(amal_dir, "simdjson.cpp")
|
||||
simdjson_o = os.path.join(build_dir, f"{name}_simdjson.o")
|
||||
test_o = os.path.join(build_dir, f"{name}.o")
|
||||
binary = os.path.join(build_dir, name)
|
||||
|
||||
defs = [f"-D{k}={v}" for k, v in flags.items()]
|
||||
|
||||
# ── compile simdjson.o (timed) ────────────────────────────────────────
|
||||
cmd_lib = [CXX, CXXSTD, OPT, "-c"] + defs + [
|
||||
f"-I{amal_dir}", "-o", simdjson_o, simdjson_cpp,
|
||||
]
|
||||
t0 = time.monotonic()
|
||||
result = subprocess.run(cmd_lib, capture_output=True, text=True)
|
||||
lib_time = time.monotonic() - t0
|
||||
|
||||
if result.returncode != 0:
|
||||
print(f" COMPILE FAILED (simdjson.o): {name}")
|
||||
print(f" command: {' '.join(cmd_lib)}")
|
||||
print(result.stderr)
|
||||
return None, None, None, None
|
||||
|
||||
lib_size = os.path.getsize(simdjson_o)
|
||||
|
||||
# ── compile test source (timed) ───────────────────────────────────────
|
||||
cmd_test = [CXX, CXXSTD, OPT, "-c"] + defs + [
|
||||
f"-I{amal_dir}", "-o", test_o, src,
|
||||
]
|
||||
t0 = time.monotonic()
|
||||
result = subprocess.run(cmd_test, capture_output=True, text=True)
|
||||
test_time = time.monotonic() - t0
|
||||
|
||||
if result.returncode != 0:
|
||||
print(f" COMPILE FAILED (test): {name}")
|
||||
print(f" command: {' '.join(cmd_test)}")
|
||||
print(result.stderr)
|
||||
return None, None, None, None
|
||||
|
||||
test_size = os.path.getsize(test_o)
|
||||
|
||||
# ── link ──────────────────────────────────────────────────────────────
|
||||
cmd_link = [CXX, "-o", binary, test_o, simdjson_o]
|
||||
result = subprocess.run(cmd_link, capture_output=True, text=True)
|
||||
if result.returncode != 0:
|
||||
print(f" LINK FAILED: {name}")
|
||||
print(f" command: {' '.join(cmd_link)}")
|
||||
print(result.stderr)
|
||||
return None, None, None, None
|
||||
|
||||
# ── run ───────────────────────────────────────────────────────────────
|
||||
result = subprocess.run([binary], capture_output=True, text=True)
|
||||
if result.returncode != 0:
|
||||
print(f" RUN FAILED: {name}")
|
||||
print(result.stdout)
|
||||
print(result.stderr)
|
||||
return lib_time, lib_size, test_time, test_size
|
||||
|
||||
print(f" {result.stdout.strip()}")
|
||||
return lib_time, lib_size, test_time, test_size
|
||||
|
||||
|
||||
def human_size(n):
|
||||
"""Return e.g. '1.23 MB' or '456 KB'."""
|
||||
if n is None:
|
||||
return "N/A"
|
||||
if n >= 1_000_000:
|
||||
return f"{n / 1_000_000:.2f} MB"
|
||||
if n >= 1_000:
|
||||
return f"{n / 1_000:.1f} KB"
|
||||
return f"{n} B"
|
||||
|
||||
|
||||
def main():
|
||||
tmpdir = tempfile.mkdtemp(prefix="simdjson_features_")
|
||||
amal_dir = os.path.join(tmpdir, "amalgamated")
|
||||
build_dir = os.path.join(tmpdir, "build")
|
||||
os.makedirs(amal_dir, exist_ok=True)
|
||||
os.makedirs(build_dir, exist_ok=True)
|
||||
|
||||
print(f"Working directory: {tmpdir}")
|
||||
print(f"Compiler: {CXX} flags: {CXXSTD} {OPT}")
|
||||
print()
|
||||
|
||||
# ── amalgamate ────────────────────────────────────────────────────────
|
||||
print("Amalgamating...")
|
||||
amalgamate(amal_dir)
|
||||
print("Amalgamation done.\n")
|
||||
|
||||
# ── build & run each configuration ────────────────────────────────────
|
||||
results = []
|
||||
for cfg in CONFIGS:
|
||||
name = cfg["name"]
|
||||
flags = cfg["flags"]
|
||||
enabled = [k.replace("SIMDJSON_FEATURE_", "").replace("_API", "")
|
||||
for k, v in flags.items() if v]
|
||||
label = " + ".join(enabled) if enabled else "none"
|
||||
print(f"[{name}] ({label})")
|
||||
lib_time, lib_size, test_time, test_size = \
|
||||
compile_and_run(name, flags, amal_dir, build_dir)
|
||||
results.append((name, label, lib_time, lib_size, test_time, test_size))
|
||||
|
||||
print()
|
||||
|
||||
# ── summary table ─────────────────────────────────────────────────────
|
||||
name_w = max(len(r[0]) for r in results)
|
||||
label_w = max(len(r[1]) for r in results)
|
||||
hdr = (f"{'Test':<{name_w}} {'APIs':<{label_w}}"
|
||||
f" {'lib time':>9} {'simdjson.o':>11}"
|
||||
f" {'test time':>10} {'test .o':>11}")
|
||||
print(hdr)
|
||||
print("-" * len(hdr))
|
||||
for name, label, lt, ls, tt, ts in results:
|
||||
lt_s = f"{lt:.2f} s" if lt is not None else "FAIL"
|
||||
ls_s = human_size(ls)
|
||||
tt_s = f"{tt:.2f} s" if tt is not None else "FAIL"
|
||||
ts_s = human_size(ts)
|
||||
print(f"{name:<{name_w}} {label:<{label_w}}"
|
||||
f" {lt_s:>9} {ls_s:>11}"
|
||||
f" {tt_s:>10} {ts_s:>11}")
|
||||
|
||||
# ── cleanup ───────────────────────────────────────────────────────────
|
||||
shutil.rmtree(tmpdir, ignore_errors=True)
|
||||
|
||||
# exit non-zero if anything failed
|
||||
if any(lt is None for _, _, lt, _, _, _ in results):
|
||||
print("\nSome tests FAILED.")
|
||||
sys.exit(1)
|
||||
print("\nAll tests passed.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
+2121
-2010
File diff suppressed because it is too large
Load Diff
+8515
-8453
File diff suppressed because it is too large
Load Diff
Binary file not shown.
+50
-33
@@ -2,6 +2,7 @@
|
||||
#define SIMDJSON_SRC_ARM64_CPP
|
||||
|
||||
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
|
||||
#include "simdjson/feature_macros.h"
|
||||
#include <base.h>
|
||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||
|
||||
@@ -38,43 +39,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 +133,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);
|
||||
@@ -130,6 +143,7 @@ simdjson_warn_unused bool implementation::validate_utf8(const char *buf, size_t
|
||||
return arm64::stage1::generic_validate_utf8(buf,len);
|
||||
}
|
||||
|
||||
#if SIMDJSON_FEATURE_DOM_API
|
||||
simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document &_doc) noexcept {
|
||||
return stage2::tape_builder::parse_document<false>(*this, _doc);
|
||||
}
|
||||
@@ -137,6 +151,7 @@ simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document
|
||||
simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::document &_doc) noexcept {
|
||||
return stage2::tape_builder::parse_document<true>(*this, _doc);
|
||||
}
|
||||
#endif // SIMDJSON_FEATURE_DOM_API
|
||||
|
||||
SIMDJSON_NO_SANITIZE_MEMORY
|
||||
simdjson_warn_unused uint8_t *dom_parser_implementation::parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) const noexcept {
|
||||
@@ -147,11 +162,13 @@ simdjson_warn_unused uint8_t *dom_parser_implementation::parse_wobbly_string(con
|
||||
return arm64::stringparsing::parse_wobbly_string(src, dst);
|
||||
}
|
||||
|
||||
#if SIMDJSON_FEATURE_DOM_API
|
||||
simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept {
|
||||
auto error = stage1(_buf, _len, stage1_mode::regular);
|
||||
if (error) { return error; }
|
||||
return stage2(_doc);
|
||||
}
|
||||
#endif // SIMDJSON_FEATURE_DOM_API
|
||||
|
||||
} // namespace arm64
|
||||
} // namespace simdjson
|
||||
|
||||
+10
-58
@@ -2,6 +2,7 @@
|
||||
#define SIMDJSON_SRC_FALLBACK_CPP
|
||||
|
||||
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
|
||||
#include "simdjson/feature_macros.h"
|
||||
#include <base.h>
|
||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||
|
||||
@@ -11,10 +12,13 @@
|
||||
#include <simdjson/fallback/begin.h>
|
||||
#include <generic/stage1/find_next_document_index.h>
|
||||
#include <generic/stage2/stringparsing.h>
|
||||
|
||||
#if SIMDJSON_FEATURE_DOM_API
|
||||
#include <generic/stage2/logger.h>
|
||||
#include <generic/stage2/json_iterator.h>
|
||||
#include <generic/stage2/tape_writer.h>
|
||||
#include <generic/stage2/tape_builder.h>
|
||||
#endif // SIMDJSON_FEATURE_DOM_API
|
||||
|
||||
//
|
||||
// Stage 1
|
||||
@@ -226,15 +230,9 @@ simdjson_warn_unused simdjson_inline error_code scan() {
|
||||
add_structural();
|
||||
// Primitive or invalid character (invalid characters will be checked in stage 2)
|
||||
} else {
|
||||
// Anything else, add the structural and go until we find the next one.
|
||||
// We also stop on RS (0x1E) so that RFC 7464 json_sequence inputs
|
||||
// like `\x1e"a"\x1e"b"` produce a separate structural for each RS
|
||||
// rather than being absorbed into a single primitive run. RS is a
|
||||
// control character that is invalid in normal JSON, so breaking
|
||||
// the run here has no effect on well-formed non-json_sequence
|
||||
// inputs.
|
||||
// Anything else, add the structural and go until we find the next one
|
||||
add_structural();
|
||||
while (idx+1<len && !char_is_space_or_operator(buf[idx+1]) && buf[idx+1] != 0x1e) {
|
||||
while (idx+1<len && !char_is_space_or_operator(buf[idx+1])) {
|
||||
idx++;
|
||||
};
|
||||
}
|
||||
@@ -289,56 +287,6 @@ simdjson_warn_unused simdjson_inline error_code scan() {
|
||||
// doing.
|
||||
parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len);
|
||||
if (parser.n_structural_indexes == 0) { return EMPTY; }
|
||||
} else if (partial == stage1_mode::json_sequence_partial) {
|
||||
// RFC 7464: use RS positions for batch boundaries
|
||||
if(unclosed_string) {
|
||||
parser.n_structural_indexes--;
|
||||
if (simdjson_unlikely(parser.n_structural_indexes == 0)) { return CAPACITY; }
|
||||
}
|
||||
uint32_t next_batch_start = uint32_t(len);
|
||||
auto new_structural_indexes = find_next_document_index_json_sequence(parser, len, false, next_batch_start);
|
||||
if (new_structural_indexes == DOCUMENT_TOO_LARGE) {
|
||||
return CAPACITY;
|
||||
}
|
||||
if (new_structural_indexes == 0) {
|
||||
parser.n_structural_indexes = 0;
|
||||
return EMPTY;
|
||||
}
|
||||
parser.n_structural_indexes = new_structural_indexes;
|
||||
parser.structural_indexes[parser.n_structural_indexes] = next_batch_start;
|
||||
} else if (partial == stage1_mode::json_sequence_final) {
|
||||
// RFC 7464: final batch, last document extends to EOF
|
||||
if(unclosed_string) { parser.n_structural_indexes--; }
|
||||
uint32_t next_batch_start = uint32_t(len);
|
||||
parser.n_structural_indexes = find_next_document_index_json_sequence(parser, len, true, next_batch_start);
|
||||
parser.structural_indexes[parser.n_structural_indexes + 1] = parser.structural_indexes[parser.n_structural_indexes];
|
||||
parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len);
|
||||
if (simdjson_unlikely(parser.n_structural_indexes == 0)) { return EMPTY; }
|
||||
} else if (partial == stage1_mode::comma_delimited_partial) {
|
||||
// Comma-delimited: filter root-level commas, use comma positions for batch boundaries
|
||||
if(unclosed_string) {
|
||||
parser.n_structural_indexes--;
|
||||
if (simdjson_unlikely(parser.n_structural_indexes == 0)) { return CAPACITY; }
|
||||
}
|
||||
uint32_t next_batch_start = uint32_t(len);
|
||||
auto new_structural_indexes = filter_comma_delimited(parser, len, false, next_batch_start);
|
||||
if (new_structural_indexes == DOCUMENT_TOO_LARGE) {
|
||||
return CAPACITY;
|
||||
}
|
||||
if (new_structural_indexes == 0) {
|
||||
parser.n_structural_indexes = 0;
|
||||
return EMPTY;
|
||||
}
|
||||
parser.n_structural_indexes = new_structural_indexes;
|
||||
parser.structural_indexes[parser.n_structural_indexes] = next_batch_start;
|
||||
} else if (partial == stage1_mode::comma_delimited_final) {
|
||||
// Comma-delimited: final batch, last document extends to EOF
|
||||
if(unclosed_string) { parser.n_structural_indexes--; }
|
||||
uint32_t next_batch_start = uint32_t(len);
|
||||
parser.n_structural_indexes = filter_comma_delimited(parser, len, true, next_batch_start);
|
||||
parser.structural_indexes[parser.n_structural_indexes + 1] = parser.structural_indexes[parser.n_structural_indexes];
|
||||
parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len);
|
||||
if (simdjson_unlikely(parser.n_structural_indexes == 0)) { return EMPTY; }
|
||||
} else if(unclosed_string) { error = UNCLOSED_STRING; }
|
||||
return error;
|
||||
}
|
||||
@@ -492,6 +440,7 @@ simdjson_warn_unused bool implementation::validate_utf8(const char *buf, size_t
|
||||
namespace simdjson {
|
||||
namespace fallback {
|
||||
|
||||
#if SIMDJSON_FEATURE_DOM_API
|
||||
simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document &_doc) noexcept {
|
||||
return stage2::tape_builder::parse_document<false>(*this, _doc);
|
||||
}
|
||||
@@ -499,6 +448,7 @@ simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document
|
||||
simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::document &_doc) noexcept {
|
||||
return stage2::tape_builder::parse_document<true>(*this, _doc);
|
||||
}
|
||||
#endif // SIMDJSON_FEATURE_DOM_API
|
||||
|
||||
SIMDJSON_NO_SANITIZE_MEMORY
|
||||
simdjson_warn_unused uint8_t *dom_parser_implementation::parse_string(const uint8_t *src, uint8_t *dst, bool replacement_char) const noexcept {
|
||||
@@ -509,11 +459,13 @@ simdjson_warn_unused uint8_t *dom_parser_implementation::parse_wobbly_string(con
|
||||
return fallback::stringparsing::parse_wobbly_string(src, dst);
|
||||
}
|
||||
|
||||
#if SIMDJSON_FEATURE_DOM_API
|
||||
simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept {
|
||||
auto error = stage1(_buf, _len, stage1_mode::regular);
|
||||
if (error) { return error; }
|
||||
return stage2(_doc);
|
||||
}
|
||||
#endif // SIMDJSON_FEATURE_DOM_API
|
||||
|
||||
} // namespace fallback
|
||||
} // namespace simdjson
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#ifndef SIMDJSON_SRC_GENERIC_DEPENDENCIES_H
|
||||
#define SIMDJSON_SRC_GENERIC_DEPENDENCIES_H
|
||||
|
||||
#include "simdjson/feature_macros.h"
|
||||
#include <base.h>
|
||||
|
||||
#endif // SIMDJSON_SRC_GENERIC_DEPENDENCIES_H
|
||||
@@ -97,280 +97,9 @@ simdjson_inline uint32_t find_next_document_index(dom_parser_implementation &par
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sentinel value returned to indicate a document started but didn't fit
|
||||
* (CAPACITY error), as opposed to 0 which means no document content found
|
||||
* (EMPTY).
|
||||
*/
|
||||
constexpr uint32_t DOCUMENT_TOO_LARGE = UINT32_MAX;
|
||||
|
||||
/**
|
||||
* For RFC 7464 JSON text sequences, filter RS from structural indexes and
|
||||
* find batch boundaries.
|
||||
*
|
||||
* In JSON sequence mode, RS (0x1E) marks the start of each JSON text.
|
||||
* RS bytes appear in structural_indexes as they are classified as scalars.
|
||||
* This function:
|
||||
* 1. Scans structural_indexes to find and count RS positions
|
||||
* 2. Filters RS out of structural_indexes in-place
|
||||
* 3. Determines batch boundaries based on RS positions
|
||||
*
|
||||
* @param parser The parser with structural_indexes and buf.
|
||||
* @param len The length of the current batch buffer.
|
||||
* @param is_final True if this is the final batch (no more data coming).
|
||||
* @param next_batch_start Output: offset where the next batch should start.
|
||||
* @return The number of structural indexes to keep (after RS filtering),
|
||||
* 0 if no document content found (EMPTY),
|
||||
* or DOCUMENT_TOO_LARGE if a document started but didn't fit (CAPACITY).
|
||||
*/
|
||||
simdjson_inline uint32_t find_next_document_index_json_sequence(
|
||||
dom_parser_implementation &parser,
|
||||
size_t len,
|
||||
bool is_final,
|
||||
uint32_t &next_batch_start) {
|
||||
// Default: next batch starts at end of buffer
|
||||
next_batch_start = uint32_t(len);
|
||||
|
||||
if (parser.n_structural_indexes == 0) { return 0; }
|
||||
|
||||
// Phase 1: Scan structural_indexes to find RS positions and handle them.
|
||||
// RS marks the start of a JSON text. For objects/arrays, the '{' or '[' after RS
|
||||
// is already in structural_indexes (it's an operator). For scalars like numbers,
|
||||
// the digit following RS is NOT in structural_indexes because the scanner sees
|
||||
// RS as a scalar, making the digit a scalar continuation, not a start.
|
||||
// We must: (1) remove RS from structural_indexes, and (2) for scalars, add the
|
||||
// actual value start position.
|
||||
uint32_t write_idx = 0;
|
||||
uint32_t last_rs_pos = 0;
|
||||
uint32_t rs_count = 0;
|
||||
|
||||
for (uint32_t read_idx = 0; read_idx < parser.n_structural_indexes; read_idx++) {
|
||||
const uint32_t pos = parser.structural_indexes[read_idx];
|
||||
if (parser.buf[pos] == 0x1E) {
|
||||
// This is an RS character - find the actual JSON value start.
|
||||
last_rs_pos = pos;
|
||||
rs_count++;
|
||||
// Skip past this RS and any whitespace *and any additional RSes*
|
||||
// to locate the real value. Consecutive RSes are degenerate
|
||||
// "empty records" per RFC 7464; we collapse them here. They do
|
||||
// not always appear as separate entries in structural_indexes
|
||||
// because the scanner groups runs of adjacent non-whitespace
|
||||
// scalar bytes (including RS) into a single scalar start.
|
||||
uint32_t value_start = pos + 1;
|
||||
while (value_start < len) {
|
||||
const uint8_t c = parser.buf[value_start];
|
||||
if (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
|
||||
value_start++;
|
||||
} else if (c == 0x1E) {
|
||||
// Collapsed empty record. Still count it so rs_count reflects
|
||||
// the true number of record markers and last_rs_pos tracks
|
||||
// the final one.
|
||||
last_rs_pos = value_start;
|
||||
rs_count++;
|
||||
value_start++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// If the scanner emitted additional structurals inside the
|
||||
// whitespace+RS run we just walked over (i.e., isolated RSes
|
||||
// separated by whitespace), skip past them so we do not
|
||||
// double-count or double-emit.
|
||||
while (read_idx + 1 < parser.n_structural_indexes &&
|
||||
parser.structural_indexes[read_idx + 1] < value_start) {
|
||||
read_idx++;
|
||||
}
|
||||
// Check if the value start is an operator (always present in
|
||||
// scanner structural_indexes) or a scalar-like start (which may
|
||||
// be missing from structural_indexes and must be added here).
|
||||
// Note: '"' is NOT always in structural_indexes. The scanner
|
||||
// classifies '"' as a scalar character and emits it as a
|
||||
// structural only when it is a *scalar start* (preceded by
|
||||
// whitespace or an operator). When '"' immediately follows an
|
||||
// RS (which the scanner also classifies as scalar), it is
|
||||
// treated as a scalar continuation and not emitted - so we
|
||||
// must add it here just like any other scalar value.
|
||||
if (value_start < len) {
|
||||
const uint8_t c = parser.buf[value_start];
|
||||
const bool is_operator =
|
||||
(c == '{' || c == '}' || c == '[' || c == ']' ||
|
||||
c == ':' || c == ',');
|
||||
// If the next scanner structural is exactly at value_start,
|
||||
// the scanner already emitted it (it followed whitespace) and
|
||||
// we must not add a duplicate - a subsequent iteration will
|
||||
// copy it into write_idx.
|
||||
const bool already_emitted =
|
||||
(read_idx + 1 < parser.n_structural_indexes &&
|
||||
parser.structural_indexes[read_idx + 1] == value_start);
|
||||
if (!is_operator && !already_emitted) {
|
||||
// Scalar value (number/true/false/null/string) - add its
|
||||
// position since scanner missed it.
|
||||
parser.structural_indexes[write_idx++] = value_start;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Not RS, copy to output
|
||||
parser.structural_indexes[write_idx++] = pos;
|
||||
}
|
||||
}
|
||||
|
||||
// Update structural index count
|
||||
parser.n_structural_indexes = write_idx;
|
||||
|
||||
if (parser.n_structural_indexes == 0) { return 0; }
|
||||
if (rs_count == 0) {
|
||||
// No RS found; for final batch, try generic boundary detection
|
||||
return is_final ? find_next_document_index(parser) : 0;
|
||||
}
|
||||
|
||||
// Phase 2: Determine batch boundaries based on RS positions
|
||||
|
||||
if (is_final) {
|
||||
// Final batch: all documents are complete (last one ends at EOF).
|
||||
// In json_sequence mode, RS markers define document boundaries, so all
|
||||
// remaining structurals form complete documents. Return them all directly.
|
||||
// (Calling find_next_document_index() would fail for scalar documents.)
|
||||
return parser.n_structural_indexes;
|
||||
}
|
||||
|
||||
// Partial batch: need to find complete documents only.
|
||||
// A document starting at an RS is complete if there is another RS after it.
|
||||
next_batch_start = last_rs_pos;
|
||||
|
||||
if (rs_count < 2) {
|
||||
// Only one RS, so we have at most one document that may be incomplete.
|
||||
// We cannot confirm it is complete without another RS.
|
||||
// Return DOCUMENT_TOO_LARGE if content was found (write_idx > 0), 0 if only separators.
|
||||
return (parser.n_structural_indexes > 0) ? DOCUMENT_TOO_LARGE : 0;
|
||||
}
|
||||
|
||||
// We have at least 2 RS markers. The last complete document ends before last_rs_pos.
|
||||
|
||||
// Find the structural index cutoff: keep only structurals < last_rs_pos.
|
||||
// Since we already filtered RS, all remaining structurals are valid.
|
||||
// We iterate backward to find the last structural before last_rs_pos.
|
||||
uint32_t keep_count = 0;
|
||||
for (uint32_t i = parser.n_structural_indexes; i > 0; i--) {
|
||||
if (parser.structural_indexes[i - 1] < last_rs_pos) {
|
||||
keep_count = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// No structurals before the last RS - no complete documents
|
||||
if (keep_count == 0) { return 0; }
|
||||
|
||||
// All documents before the last RS are complete by definition (the next RS
|
||||
// confirms their end). No need to call find_next_document_index() which
|
||||
// would fail for scalar documents like `1` or `"hello"`.
|
||||
return keep_count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter comma-delimited documents by removing root-level commas from
|
||||
* structural indexes.
|
||||
*
|
||||
* For comma-delimited format like `{...},{...},{...}`, we need to remove
|
||||
* the commas that separate documents (depth 0) while preserving commas
|
||||
* inside arrays and objects (depth > 0).
|
||||
*
|
||||
* After filtering, the structural indexes look like whitespace-delimited
|
||||
* documents, so find_next_document_index() works unchanged.
|
||||
*
|
||||
* @param parser The parser with structural_indexes and buf.
|
||||
* @param len The length of the current batch buffer.
|
||||
* @param is_final True if this is the final batch (no more data coming).
|
||||
* @param next_batch_start Output: offset where the next batch should start.
|
||||
* @return The number of structural indexes to keep,
|
||||
* 0 if no document content found (EMPTY),
|
||||
* or DOCUMENT_TOO_LARGE if a document started but didn't fit (CAPACITY).
|
||||
*/
|
||||
simdjson_inline uint32_t filter_comma_delimited(
|
||||
dom_parser_implementation &parser,
|
||||
size_t len,
|
||||
bool is_final,
|
||||
uint32_t &next_batch_start) {
|
||||
// Default: next batch starts at end of buffer
|
||||
next_batch_start = uint32_t(len);
|
||||
|
||||
if (parser.n_structural_indexes == 0) { return 0; }
|
||||
|
||||
// Track depth to identify root-level commas (depth 0)
|
||||
int depth = 0;
|
||||
uint32_t write_idx = 0;
|
||||
uint32_t last_root_comma_pos = 0;
|
||||
uint32_t root_comma_count = 0;
|
||||
|
||||
for (uint32_t i = 0; i < parser.n_structural_indexes; i++) {
|
||||
uint32_t idx = parser.structural_indexes[i];
|
||||
uint8_t c = parser.buf[idx];
|
||||
|
||||
switch (c) {
|
||||
case '{': case '[':
|
||||
depth++;
|
||||
parser.structural_indexes[write_idx++] = idx;
|
||||
break;
|
||||
case '}': case ']':
|
||||
depth--;
|
||||
parser.structural_indexes[write_idx++] = idx;
|
||||
break;
|
||||
case ',':
|
||||
if (depth == 0) {
|
||||
// Root-level comma = document boundary, skip it
|
||||
last_root_comma_pos = idx;
|
||||
root_comma_count++;
|
||||
continue;
|
||||
}
|
||||
parser.structural_indexes[write_idx++] = idx;
|
||||
break;
|
||||
default:
|
||||
// Colons, scalars, etc.
|
||||
parser.structural_indexes[write_idx++] = idx;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Update structural index count
|
||||
parser.n_structural_indexes = write_idx;
|
||||
|
||||
if (parser.n_structural_indexes == 0) { return 0; }
|
||||
|
||||
if (is_final) {
|
||||
// Final batch: use standard boundary detection on filtered indexes
|
||||
return find_next_document_index(parser);
|
||||
}
|
||||
|
||||
// Partial batch: need to find complete documents only.
|
||||
// A document ending with a root comma is complete.
|
||||
if (root_comma_count == 0) {
|
||||
// No root commas found; we cannot confirm any document is complete.
|
||||
// The whole batch might be one incomplete document.
|
||||
// Return DOCUMENT_TOO_LARGE if content was found (write_idx > 0), 0 if only commas.
|
||||
return (parser.n_structural_indexes > 0) ? DOCUMENT_TOO_LARGE : 0;
|
||||
}
|
||||
|
||||
// We have at least one root comma. Documents before the last comma are complete.
|
||||
next_batch_start = last_root_comma_pos + 1;
|
||||
|
||||
// Find the structural index cutoff: keep only structurals < last_root_comma_pos
|
||||
uint32_t keep_count = 0;
|
||||
for (uint32_t i = parser.n_structural_indexes; i > 0; i--) {
|
||||
if (parser.structural_indexes[i - 1] < last_root_comma_pos) {
|
||||
keep_count = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (keep_count == 0) { return 0; }
|
||||
|
||||
// Use standard boundary detection on the complete portion
|
||||
parser.n_structural_indexes = keep_count;
|
||||
return find_next_document_index(parser);
|
||||
}
|
||||
|
||||
} // namespace stage1
|
||||
} // unnamed namespace
|
||||
} // namespace SIMDJSON_IMPLEMENTATION
|
||||
} // namespace simdjson
|
||||
|
||||
#endif // SIMDJSON_SRC_GENERIC_STAGE1_FIND_NEXT_DOCUMENT_INDEX_H
|
||||
#endif // SIMDJSON_SRC_GENERIC_STAGE1_FIND_NEXT_DOCUMENT_INDEX_H
|
||||
@@ -314,6 +314,7 @@ simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementa
|
||||
return EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
parser.n_structural_indexes = new_structural_indexes;
|
||||
} else if (partial == stage1_mode::streaming_final) {
|
||||
if(have_unclosed_string) { parser.n_structural_indexes--; }
|
||||
@@ -341,56 +342,6 @@ simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementa
|
||||
// the trailing garbage.
|
||||
return EMPTY;
|
||||
}
|
||||
} else if (partial == stage1_mode::json_sequence_partial) {
|
||||
// RFC 7464: use RS positions for batch boundaries
|
||||
if(have_unclosed_string) {
|
||||
parser.n_structural_indexes--;
|
||||
if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { return CAPACITY; }
|
||||
}
|
||||
uint32_t next_batch_start = uint32_t(len);
|
||||
auto new_structural_indexes = find_next_document_index_json_sequence(parser, len, false, next_batch_start);
|
||||
if (new_structural_indexes == DOCUMENT_TOO_LARGE) {
|
||||
return CAPACITY;
|
||||
}
|
||||
if (new_structural_indexes == 0) {
|
||||
parser.n_structural_indexes = 0;
|
||||
return EMPTY;
|
||||
}
|
||||
parser.n_structural_indexes = new_structural_indexes;
|
||||
parser.structural_indexes[parser.n_structural_indexes] = next_batch_start;
|
||||
} else if (partial == stage1_mode::json_sequence_final) {
|
||||
// RFC 7464: final batch, last document extends to EOF
|
||||
if(have_unclosed_string) { parser.n_structural_indexes--; }
|
||||
uint32_t next_batch_start = uint32_t(len);
|
||||
parser.n_structural_indexes = find_next_document_index_json_sequence(parser, len, true, next_batch_start);
|
||||
parser.structural_indexes[parser.n_structural_indexes + 1] = parser.structural_indexes[parser.n_structural_indexes];
|
||||
parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len);
|
||||
if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { return EMPTY; }
|
||||
} else if (partial == stage1_mode::comma_delimited_partial) {
|
||||
// Comma-delimited: filter root-level commas, use comma positions for batch boundaries
|
||||
if(have_unclosed_string) {
|
||||
parser.n_structural_indexes--;
|
||||
if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { return CAPACITY; }
|
||||
}
|
||||
uint32_t next_batch_start = uint32_t(len);
|
||||
auto new_structural_indexes = filter_comma_delimited(parser, len, false, next_batch_start);
|
||||
if (new_structural_indexes == DOCUMENT_TOO_LARGE) {
|
||||
return CAPACITY;
|
||||
}
|
||||
if (new_structural_indexes == 0) {
|
||||
parser.n_structural_indexes = 0;
|
||||
return EMPTY;
|
||||
}
|
||||
parser.n_structural_indexes = new_structural_indexes;
|
||||
parser.structural_indexes[parser.n_structural_indexes] = next_batch_start;
|
||||
} else if (partial == stage1_mode::comma_delimited_final) {
|
||||
// Comma-delimited: final batch, last document extends to EOF
|
||||
if(have_unclosed_string) { parser.n_structural_indexes--; }
|
||||
uint32_t next_batch_start = uint32_t(len);
|
||||
parser.n_structural_indexes = filter_comma_delimited(parser, len, true, next_batch_start);
|
||||
parser.structural_indexes[parser.n_structural_indexes + 1] = parser.structural_indexes[parser.n_structural_indexes];
|
||||
parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len);
|
||||
if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { return EMPTY; }
|
||||
}
|
||||
checker.check_eof();
|
||||
return checker.errors();
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
#include <generic/stage2/stringparsing.h>
|
||||
|
||||
#if SIMDJSON_FEATURE_DOM_API
|
||||
|
||||
// Stuff other things depend on
|
||||
#include <generic/stage2/base.h>
|
||||
#include <generic/stage2/tape_writer.h>
|
||||
@@ -5,6 +9,7 @@
|
||||
|
||||
// All other declarations
|
||||
#include <generic/stage2/json_iterator.h>
|
||||
#include <generic/stage2/stringparsing.h>
|
||||
#include <generic/stage2/structural_iterator.h>
|
||||
#include <generic/stage2/tape_builder.h>
|
||||
|
||||
#endif // SIMDJSON_FEATURE_DOM_API
|
||||
|
||||
@@ -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
|
||||
+6
-1
@@ -2,6 +2,7 @@
|
||||
#define SIMDJSON_SRC_HASWELL_CPP
|
||||
|
||||
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
|
||||
#include "simdjson/feature_macros.h"
|
||||
#include <base.h>
|
||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||
|
||||
@@ -139,6 +140,7 @@ simdjson_warn_unused bool implementation::validate_utf8(const char *buf, size_t
|
||||
return haswell::stage1::generic_validate_utf8(buf,len);
|
||||
}
|
||||
|
||||
#if SIMDJSON_FEATURE_DOM_API
|
||||
simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document &_doc) noexcept {
|
||||
return stage2::tape_builder::parse_document<false>(*this, _doc);
|
||||
}
|
||||
@@ -146,6 +148,7 @@ simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document
|
||||
simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::document &_doc) noexcept {
|
||||
return stage2::tape_builder::parse_document<true>(*this, _doc);
|
||||
}
|
||||
#endif // SIMDJSON_FEATURE_DOM_API
|
||||
|
||||
SIMDJSON_NO_SANITIZE_MEMORY
|
||||
simdjson_warn_unused uint8_t *dom_parser_implementation::parse_string(const uint8_t *src, uint8_t *dst, bool replacement_char) const noexcept {
|
||||
@@ -156,15 +159,17 @@ simdjson_warn_unused uint8_t *dom_parser_implementation::parse_wobbly_string(con
|
||||
return haswell::stringparsing::parse_wobbly_string(src, dst);
|
||||
}
|
||||
|
||||
#if SIMDJSON_FEATURE_DOM_API
|
||||
simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept {
|
||||
auto error = stage1(_buf, _len, stage1_mode::regular);
|
||||
if (error) { return error; }
|
||||
return stage2(_doc);
|
||||
}
|
||||
#endif // SIMDJSON_FEATURE_DOM_API
|
||||
|
||||
} // namespace SIMDJSON_IMPLEMENTATION
|
||||
} // namespace simdjson
|
||||
|
||||
#include <simdjson/haswell/end.h>
|
||||
|
||||
#endif // SIMDJSON_SRC_HASWELL_CPP
|
||||
#endif // SIMDJSON_SRC_HASWELL_CPP
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define SIMDJSON_SRC_ICELAKE_CPP
|
||||
|
||||
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
|
||||
#include "simdjson/feature_macros.h"
|
||||
#include <base.h>
|
||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||
|
||||
@@ -185,6 +186,7 @@ simdjson_warn_unused bool implementation::validate_utf8(const char *buf, size_t
|
||||
return icelake::stage1::generic_validate_utf8(buf,len);
|
||||
}
|
||||
|
||||
#if SIMDJSON_FEATURE_DOM_API
|
||||
simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document &_doc) noexcept {
|
||||
return stage2::tape_builder::parse_document<false>(*this, _doc);
|
||||
}
|
||||
@@ -192,6 +194,7 @@ simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document
|
||||
simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::document &_doc) noexcept {
|
||||
return stage2::tape_builder::parse_document<true>(*this, _doc);
|
||||
}
|
||||
#endif // SIMDJSON_FEATURE_DOM_API
|
||||
|
||||
SIMDJSON_NO_SANITIZE_MEMORY
|
||||
simdjson_warn_unused uint8_t *dom_parser_implementation::parse_string(const uint8_t *src, uint8_t *dst, bool replacement_char) const noexcept {
|
||||
@@ -202,11 +205,13 @@ simdjson_warn_unused uint8_t *dom_parser_implementation::parse_wobbly_string(con
|
||||
return icelake::stringparsing::parse_wobbly_string(src, dst);
|
||||
}
|
||||
|
||||
#if SIMDJSON_FEATURE_DOM_API
|
||||
simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept {
|
||||
auto error = stage1(_buf, _len, stage1_mode::regular);
|
||||
if (error) { return error; }
|
||||
return stage2(_doc);
|
||||
}
|
||||
#endif // SIMDJSON_FEATURE_DOM_API
|
||||
|
||||
} // namespace icelake
|
||||
} // namespace simdjson
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define SIMDJSON_SRC_LASX_CPP
|
||||
|
||||
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
|
||||
#include "simdjson/feature_macros.h"
|
||||
#include <base.h>
|
||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||
|
||||
@@ -102,6 +103,7 @@ simdjson_warn_unused bool implementation::validate_utf8(const char *buf, size_t
|
||||
return lasx::stage1::generic_validate_utf8(buf,len);
|
||||
}
|
||||
|
||||
#if SIMDJSON_FEATURE_DOM_API
|
||||
simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document &_doc) noexcept {
|
||||
return stage2::tape_builder::parse_document<false>(*this, _doc);
|
||||
}
|
||||
@@ -109,6 +111,7 @@ simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document
|
||||
simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::document &_doc) noexcept {
|
||||
return stage2::tape_builder::parse_document<true>(*this, _doc);
|
||||
}
|
||||
#endif // SIMDJSON_FEATURE_DOM_API
|
||||
|
||||
SIMDJSON_NO_SANITIZE_MEMORY
|
||||
simdjson_warn_unused uint8_t *dom_parser_implementation::parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) const noexcept {
|
||||
@@ -119,11 +122,13 @@ simdjson_warn_unused uint8_t *dom_parser_implementation::parse_wobbly_string(con
|
||||
return lasx::stringparsing::parse_wobbly_string(src, dst);
|
||||
}
|
||||
|
||||
#if SIMDJSON_FEATURE_DOM_API
|
||||
simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept {
|
||||
auto error = stage1(_buf, _len, stage1_mode::regular);
|
||||
if (error) { return error; }
|
||||
return stage2(_doc);
|
||||
}
|
||||
#endif // SIMDJSON_FEATURE_DOM_API
|
||||
|
||||
} // namespace lasx
|
||||
} // namespace simdjson
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define SIMDJSON_SRC_LSX_CPP
|
||||
|
||||
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
|
||||
#include "simdjson/feature_macros.h"
|
||||
#include <base.h>
|
||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||
|
||||
@@ -106,6 +107,7 @@ simdjson_warn_unused bool implementation::validate_utf8(const char *buf, size_t
|
||||
return lsx::stage1::generic_validate_utf8(buf,len);
|
||||
}
|
||||
|
||||
#if SIMDJSON_FEATURE_DOM_API
|
||||
simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document &_doc) noexcept {
|
||||
return stage2::tape_builder::parse_document<false>(*this, _doc);
|
||||
}
|
||||
@@ -113,6 +115,7 @@ simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document
|
||||
simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::document &_doc) noexcept {
|
||||
return stage2::tape_builder::parse_document<true>(*this, _doc);
|
||||
}
|
||||
#endif // SIMDJSON_FEATURE_DOM_API
|
||||
|
||||
SIMDJSON_NO_SANITIZE_MEMORY
|
||||
simdjson_warn_unused uint8_t *dom_parser_implementation::parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) const noexcept {
|
||||
@@ -123,11 +126,13 @@ simdjson_warn_unused uint8_t *dom_parser_implementation::parse_wobbly_string(con
|
||||
return lsx::stringparsing::parse_wobbly_string(src, dst);
|
||||
}
|
||||
|
||||
#if SIMDJSON_FEATURE_DOM_API
|
||||
simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept {
|
||||
auto error = stage1(_buf, _len, stage1_mode::regular);
|
||||
if (error) { return error; }
|
||||
return stage2(_doc);
|
||||
}
|
||||
#endif // SIMDJSON_FEATURE_DOM_API
|
||||
|
||||
} // namespace lsx
|
||||
} // namespace simdjson
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define SIMDJSON_SRC_PPC64_CPP
|
||||
|
||||
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
|
||||
#include "simdjson/feature_macros.h"
|
||||
#include <base.h>
|
||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||
|
||||
@@ -112,6 +113,7 @@ simdjson_warn_unused bool implementation::validate_utf8(const char *buf, size_t
|
||||
return ppc64::stage1::generic_validate_utf8(buf,len);
|
||||
}
|
||||
|
||||
#if SIMDJSON_FEATURE_DOM_API
|
||||
simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document &_doc) noexcept {
|
||||
return stage2::tape_builder::parse_document<false>(*this, _doc);
|
||||
}
|
||||
@@ -119,6 +121,7 @@ simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document
|
||||
simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::document &_doc) noexcept {
|
||||
return stage2::tape_builder::parse_document<true>(*this, _doc);
|
||||
}
|
||||
#endif // SIMDJSON_FEATURE_DOM_API
|
||||
|
||||
SIMDJSON_NO_SANITIZE_MEMORY
|
||||
simdjson_warn_unused uint8_t *dom_parser_implementation::parse_string(const uint8_t *src, uint8_t *dst, bool replacement_char) const noexcept {
|
||||
@@ -129,11 +132,13 @@ simdjson_warn_unused uint8_t *dom_parser_implementation::parse_wobbly_string(con
|
||||
return ppc64::stringparsing::parse_wobbly_string(src, dst);
|
||||
}
|
||||
|
||||
#if SIMDJSON_FEATURE_DOM_API
|
||||
simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept {
|
||||
auto error = stage1(_buf, _len, stage1_mode::regular);
|
||||
if (error) { return error; }
|
||||
return stage2(_doc);
|
||||
}
|
||||
#endif // SIMDJSON_FEATURE_DOM_API
|
||||
|
||||
} // namespace ppc64
|
||||
} // namespace simdjson
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define SIMDJSON_SRC_RVV_VLS_CPP
|
||||
|
||||
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
|
||||
#include "simdjson/feature_macros.h"
|
||||
#include <base.h>
|
||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||
|
||||
@@ -101,6 +102,7 @@ simdjson_warn_unused bool implementation::validate_utf8(const char *buf, size_t
|
||||
return rvv_vls::stage1::generic_validate_utf8(buf,len);
|
||||
}
|
||||
|
||||
#if SIMDJSON_FEATURE_DOM_API
|
||||
simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document &_doc) noexcept {
|
||||
return stage2::tape_builder::parse_document<false>(*this, _doc);
|
||||
}
|
||||
@@ -108,6 +110,7 @@ simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document
|
||||
simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::document &_doc) noexcept {
|
||||
return stage2::tape_builder::parse_document<true>(*this, _doc);
|
||||
}
|
||||
#endif // SIMDJSON_FEATURE_DOM_API
|
||||
|
||||
SIMDJSON_NO_SANITIZE_MEMORY
|
||||
simdjson_warn_unused uint8_t *dom_parser_implementation::parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) const noexcept {
|
||||
@@ -118,11 +121,13 @@ simdjson_warn_unused uint8_t *dom_parser_implementation::parse_wobbly_string(con
|
||||
return rvv_vls::stringparsing::parse_wobbly_string(src, dst);
|
||||
}
|
||||
|
||||
#if SIMDJSON_FEATURE_DOM_API
|
||||
simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept {
|
||||
auto error = stage1(_buf, _len, stage1_mode::regular);
|
||||
if (error) { return error; }
|
||||
return stage2(_doc);
|
||||
}
|
||||
#endif // SIMDJSON_FEATURE_DOM_API
|
||||
|
||||
} // namespace rvv_vls
|
||||
} // namespace simdjson
|
||||
|
||||
+5
-1
@@ -1,5 +1,7 @@
|
||||
#define SIMDJSON_SRC_SIMDJSON_CPP
|
||||
|
||||
#include "simdjson/feature_macros.h"
|
||||
|
||||
#include <base.h>
|
||||
|
||||
SIMDJSON_PUSH_DISABLE_UNUSED_WARNINGS
|
||||
@@ -13,8 +15,11 @@ SIMDJSON_PUSH_DISABLE_UNUSED_WARNINGS
|
||||
|
||||
#include <simdjson/generic/dependencies.h>
|
||||
#include <generic/dependencies.h>
|
||||
|
||||
#if SIMDJSON_FEATURE_DOM_API
|
||||
#include <generic/stage1/dependencies.h>
|
||||
#include <generic/stage2/dependencies.h>
|
||||
#endif
|
||||
|
||||
#include <implementation.cpp>
|
||||
|
||||
@@ -50,4 +55,3 @@ SIMDJSON_PUSH_DISABLE_UNUSED_WARNINGS
|
||||
#undef SIMDJSON_CONDITIONAL_INCLUDE
|
||||
|
||||
SIMDJSON_POP_DISABLE_UNUSED_WARNINGS
|
||||
|
||||
|
||||
+6
-1
@@ -2,6 +2,7 @@
|
||||
#define SIMDJSON_SRC_WESTMERE_CPP
|
||||
|
||||
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
|
||||
#include "simdjson/feature_macros.h"
|
||||
#include <base.h>
|
||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||
|
||||
@@ -144,6 +145,7 @@ simdjson_warn_unused bool implementation::validate_utf8(const char *buf, size_t
|
||||
return westmere::stage1::generic_validate_utf8(buf,len);
|
||||
}
|
||||
|
||||
#if SIMDJSON_FEATURE_DOM_API
|
||||
simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document &_doc) noexcept {
|
||||
return stage2::tape_builder::parse_document<false>(*this, _doc);
|
||||
}
|
||||
@@ -151,6 +153,7 @@ simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document
|
||||
simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::document &_doc) noexcept {
|
||||
return stage2::tape_builder::parse_document<true>(*this, _doc);
|
||||
}
|
||||
#endif // SIMDJSON_FEATURE_DOM_API
|
||||
|
||||
SIMDJSON_NO_SANITIZE_MEMORY
|
||||
simdjson_warn_unused uint8_t *dom_parser_implementation::parse_string(const uint8_t *src, uint8_t *dst, bool replacement_char) const noexcept {
|
||||
@@ -161,14 +164,16 @@ simdjson_warn_unused uint8_t *dom_parser_implementation::parse_wobbly_string(con
|
||||
return westmere::stringparsing::parse_wobbly_string(src, dst);
|
||||
}
|
||||
|
||||
#if SIMDJSON_FEATURE_DOM_API
|
||||
simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept {
|
||||
auto error = stage1(_buf, _len, stage1_mode::regular);
|
||||
if (error) { return error; }
|
||||
return stage2(_doc);
|
||||
}
|
||||
#endif // SIMDJSON_FEATURE_DOM_API
|
||||
} // namespace westmere
|
||||
} // namespace simdjson
|
||||
|
||||
#include <simdjson/westmere/end.h>
|
||||
|
||||
#endif // SIMDJSON_SRC_WESTMERE_CPP
|
||||
#endif // SIMDJSON_SRC_WESTMERE_CPP
|
||||
|
||||
@@ -4,10 +4,6 @@ include(${PROJECT_SOURCE_DIR}/cmake/add_cpp_test.cmake)
|
||||
add_subdirectory(dom)
|
||||
add_subdirectory(ondemand)
|
||||
|
||||
# compilation_failure_tests is added before the global link_libraries(simdjson) so that
|
||||
# multiple_include/myexe does not receive a duplicate simdjson entry alongside the one
|
||||
# already propagated by mylib PUBLIC simdjson::simdjson.
|
||||
add_subdirectory(compilation_failure_tests)
|
||||
|
||||
# All remaining tests link with simdjson proper
|
||||
link_libraries(simdjson)
|
||||
@@ -31,5 +27,7 @@ endif()
|
||||
# SIMDJSON_FORCE_IMPLEMENTATION, so we know we're testing what we think we're testing
|
||||
add_cpp_test(checkimplementation LABELS other per_implementation)
|
||||
|
||||
add_subdirectory(compilation_failure_tests)
|
||||
add_subdirectory(builder)
|
||||
add_subdirectory(compile_time)
|
||||
add_subdirectory(compile_time)
|
||||
add_subdirectory(feature_flag_tests)
|
||||
@@ -1,5 +1,8 @@
|
||||
# All remaining tests link with simdjson proper
|
||||
include_directories(..)
|
||||
if(NOT SIMDJSON_FEATURE_BUILDER_API)
|
||||
return()
|
||||
endif()
|
||||
add_cpp_test(builder_string_builder_tests LABELS ondemand acceptance per_implementation)
|
||||
if(SIMDJSON_STATIC_REFLECTION)
|
||||
add_cpp_test(static_reflection_custom_tests LABELS ondemand acceptance per_implementation)
|
||||
|
||||
@@ -14,17 +14,6 @@ function(add_dual_compile_test TEST_NAME)
|
||||
endfunction(add_dual_compile_test)
|
||||
|
||||
|
||||
if(NOT BUILD_SHARED_LIBS)
|
||||
# Add multiple_include BEFORE link_libraries(simdjson) so that myexe only receives
|
||||
# simdjson once (through mylib PUBLIC simdjson::simdjson) and not a second time from
|
||||
# a directory-wide link entry.
|
||||
add_subdirectory(multiple_include)
|
||||
endif()
|
||||
|
||||
# Dual-compile tests build executables that reference simdjson symbols, so they need
|
||||
# the library linked in.
|
||||
link_libraries(simdjson)
|
||||
|
||||
add_dual_compile_test(example_compiletest)
|
||||
# These don't compile with exceptions off
|
||||
if (SIMDJSON_EXCEPTIONS)
|
||||
@@ -35,4 +24,9 @@ if (SIMDJSON_EXCEPTIONS)
|
||||
add_dual_compile_test(dangling_parser_parse_stdstring)
|
||||
add_dual_compile_test(dangling_parser_parse_padstring)
|
||||
add_dual_compile_test(unsafe_parse_many)
|
||||
endif()
|
||||
|
||||
if(NOT BUILD_SHARED_LIBS)
|
||||
# We only check that it builds
|
||||
add_subdirectory(multiple_include)
|
||||
endif()
|
||||
@@ -1,230 +0,0 @@
|
||||
#ifndef SIMDJSON_TESTS_DOCUMENT_STREAM_FUZZ_TEST_COMMON_H
|
||||
#define SIMDJSON_TESTS_DOCUMENT_STREAM_FUZZ_TEST_COMMON_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <random>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "simdjson.h"
|
||||
|
||||
namespace document_stream_fuzz {
|
||||
|
||||
constexpr uint64_t fixed_seed = 0x5eed1234ULL;
|
||||
constexpr size_t batch_size = 512;
|
||||
constexpr size_t minimum_total_bytes = 4096;
|
||||
constexpr size_t minimum_document_count = 128;
|
||||
|
||||
struct stream_case {
|
||||
const char *name;
|
||||
simdjson::stream_format format;
|
||||
std::string input;
|
||||
std::vector<std::string> expected_documents;
|
||||
};
|
||||
|
||||
inline char random_char(std::mt19937_64 &rng) {
|
||||
static constexpr char alphabet[] =
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"0123456789 _-/.";
|
||||
std::uniform_int_distribution<size_t> dist(0, sizeof(alphabet) - 2);
|
||||
return alphabet[dist(rng)];
|
||||
}
|
||||
|
||||
inline std::string make_ascii_text(std::mt19937_64 &rng, size_t min_length, size_t max_length) {
|
||||
std::uniform_int_distribution<size_t> length_dist(min_length, max_length);
|
||||
const size_t length = length_dist(rng);
|
||||
std::string text;
|
||||
text.reserve(length);
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
text.push_back(random_char(rng));
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
inline std::string quote_json_string(const std::string &text) {
|
||||
std::string quoted;
|
||||
quoted.reserve(text.size() + 2);
|
||||
quoted.push_back('"');
|
||||
for (char ch : text) {
|
||||
if (ch == '\\' || ch == '"') {
|
||||
quoted.push_back('\\');
|
||||
}
|
||||
quoted.push_back(ch);
|
||||
}
|
||||
quoted.push_back('"');
|
||||
return quoted;
|
||||
}
|
||||
|
||||
inline std::string make_integer_literal(std::mt19937_64 &rng) {
|
||||
std::uniform_int_distribution<int64_t> dist(-1000000, 1000000);
|
||||
return std::to_string(dist(rng));
|
||||
}
|
||||
|
||||
inline std::string make_float_literal(std::mt19937_64 &rng) {
|
||||
std::uniform_int_distribution<int64_t> whole_dist(-250000, 250000);
|
||||
std::uniform_int_distribution<int64_t> fraction_dist(1, 9999);
|
||||
return std::to_string(whole_dist(rng)) + "." + std::to_string(fraction_dist(rng));
|
||||
}
|
||||
|
||||
inline std::string make_scalar(std::mt19937_64 &rng) {
|
||||
std::uniform_int_distribution<int> type_dist(0, 4);
|
||||
switch (type_dist(rng)) {
|
||||
case 0: return quote_json_string(make_ascii_text(rng, 0, 20));
|
||||
case 1: return make_integer_literal(rng);
|
||||
case 2: return make_float_literal(rng);
|
||||
case 3: return (rng() & 1) ? "true" : "false";
|
||||
default: return "null";
|
||||
}
|
||||
}
|
||||
|
||||
inline std::string make_value(std::mt19937_64 &rng, int depth);
|
||||
|
||||
inline std::string make_array(std::mt19937_64 &rng, int depth) {
|
||||
std::uniform_int_distribution<int> count_dist(0, depth == 0 ? 5 : 3);
|
||||
const int count = count_dist(rng);
|
||||
std::string out = "[";
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (i > 0) {
|
||||
out.push_back(',');
|
||||
}
|
||||
out += make_value(rng, depth + 1);
|
||||
}
|
||||
out.push_back(']');
|
||||
return out;
|
||||
}
|
||||
|
||||
inline std::string make_object(std::mt19937_64 &rng, int depth) {
|
||||
std::uniform_int_distribution<int> count_dist(0, depth == 0 ? 5 : 3);
|
||||
const int count = count_dist(rng);
|
||||
std::string out = "{";
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (i > 0) {
|
||||
out.push_back(',');
|
||||
}
|
||||
std::string key = "k";
|
||||
key += std::to_string(depth);
|
||||
key += '_';
|
||||
key += std::to_string(i);
|
||||
key += '_';
|
||||
key += make_ascii_text(rng, 1, 6);
|
||||
out += quote_json_string(key);
|
||||
out.push_back(':');
|
||||
out += make_value(rng, depth + 1);
|
||||
}
|
||||
out.push_back('}');
|
||||
return out;
|
||||
}
|
||||
|
||||
inline std::string make_value(std::mt19937_64 &rng, int depth) {
|
||||
if (depth >= 3) {
|
||||
return make_scalar(rng);
|
||||
}
|
||||
std::uniform_int_distribution<int> type_dist(0, 6);
|
||||
switch (type_dist(rng)) {
|
||||
case 0: return quote_json_string(make_ascii_text(rng, 0, 20));
|
||||
case 1: return make_integer_literal(rng);
|
||||
case 2: return make_float_literal(rng);
|
||||
case 3: return (rng() & 1) ? "true" : "false";
|
||||
case 4: return "null";
|
||||
case 5: return make_array(rng, depth);
|
||||
default: return make_object(rng, depth);
|
||||
}
|
||||
}
|
||||
|
||||
inline std::vector<std::string> make_documents() {
|
||||
std::vector<std::string> docs = {
|
||||
"0",
|
||||
"-17",
|
||||
"3.125",
|
||||
"true",
|
||||
"false",
|
||||
"null",
|
||||
"\"alpha beta\"",
|
||||
"[]",
|
||||
"[1,true,\"x\"]",
|
||||
"{}",
|
||||
"{\"a\":1,\"b\":[2,3],\"c\":{\"d\":false}}"
|
||||
};
|
||||
|
||||
size_t total_bytes = 0;
|
||||
for (const auto &doc : docs) {
|
||||
total_bytes += doc.size();
|
||||
}
|
||||
|
||||
std::mt19937_64 rng(fixed_seed);
|
||||
while (docs.size() < minimum_document_count || total_bytes < minimum_total_bytes) {
|
||||
docs.push_back(make_value(rng, 0));
|
||||
total_bytes += docs.back().size();
|
||||
}
|
||||
return docs;
|
||||
}
|
||||
|
||||
inline std::vector<std::string> make_wrapped_documents(const std::vector<std::string> &docs) {
|
||||
std::vector<std::string> wrapped;
|
||||
wrapped.reserve(docs.size());
|
||||
for (size_t i = 0; i < docs.size(); i++) {
|
||||
wrapped.push_back("{\"id\":" + std::to_string(i) + ",\"value\":" + docs[i] + "}");
|
||||
}
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
inline std::string build_whitespace_input(const std::vector<std::string> &docs) {
|
||||
static const char *separators[] = {" ", "\n", "\r\n", "\t", " \n\t", "\r\t "};
|
||||
std::string out = " \n\t";
|
||||
for (size_t i = 0; i < docs.size(); i++) {
|
||||
out += docs[i];
|
||||
if (i + 1 < docs.size()) {
|
||||
out += separators[i % (sizeof(separators) / sizeof(separators[0]))];
|
||||
}
|
||||
}
|
||||
out += "\n\t ";
|
||||
return out;
|
||||
}
|
||||
|
||||
inline std::string build_json_sequence_input(const std::vector<std::string> &docs) {
|
||||
std::string out;
|
||||
for (size_t i = 0; i < docs.size(); i++) {
|
||||
out.push_back('\x1e');
|
||||
out += docs[i];
|
||||
out.push_back('\n');
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
inline std::string build_comma_delimited_input(const std::vector<std::string> &docs) {
|
||||
std::string out;
|
||||
for (size_t i = 0; i < docs.size(); i++) {
|
||||
if (i > 0) {
|
||||
out += (i % 3 == 0) ? ",\n" : ", ";
|
||||
}
|
||||
out += docs[i];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
inline std::string build_comma_delimited_array_input(const std::vector<std::string> &docs) {
|
||||
std::string out = " \t\n[";
|
||||
for (size_t i = 0; i < docs.size(); i++) {
|
||||
if (i > 0) {
|
||||
out += (i % 4 == 0) ? ",\n" : ", ";
|
||||
}
|
||||
out += docs[i];
|
||||
}
|
||||
out += "]\r\n";
|
||||
return out;
|
||||
}
|
||||
|
||||
inline std::vector<stream_case> make_stream_cases(const std::vector<std::string> &docs) {
|
||||
std::vector<std::string> whitespace_docs = make_wrapped_documents(docs);
|
||||
return {
|
||||
{"whitespace_delimited", simdjson::stream_format::whitespace_delimited, build_whitespace_input(whitespace_docs), whitespace_docs},
|
||||
{"json_sequence", simdjson::stream_format::json_sequence, build_json_sequence_input(whitespace_docs), whitespace_docs},
|
||||
{"comma_delimited", simdjson::stream_format::comma_delimited, build_comma_delimited_input(whitespace_docs), whitespace_docs},
|
||||
{"comma_delimited_array", simdjson::stream_format::comma_delimited_array, build_comma_delimited_array_input(whitespace_docs), whitespace_docs}
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace document_stream_fuzz
|
||||
|
||||
#endif
|
||||
@@ -7,13 +7,11 @@ if(NOT SIMDJSON_LEGACY_VISUAL_STUDIO AND NOT SIMDJSON_WINDOWS_DLL)
|
||||
endif()
|
||||
add_cpp_test(basictests LABELS dom acceptance per_implementation)
|
||||
add_cpp_test(document_stream_tests LABELS dom acceptance per_implementation)
|
||||
add_cpp_test(document_stream_fuzz_tests LABELS dom acceptance per_implementation)
|
||||
add_cpp_test(document_tests LABELS dom acceptance per_implementation)
|
||||
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 +126,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()
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "simdjson.h"
|
||||
#include "test_macros.h"
|
||||
#include "test_main.h"
|
||||
#include "document_stream_fuzz_test_common.h"
|
||||
|
||||
namespace document_stream_fuzz_tests {
|
||||
|
||||
std::string strip_stream_artifacts(simdjson::stream_format format, std::string_view text) {
|
||||
size_t start = 0;
|
||||
size_t end = text.size();
|
||||
while (start < end && (text[start] == ' ' || text[start] == '\t' || text[start] == '\n' || text[start] == '\r' || text[start] == '\x1e')) {
|
||||
start++;
|
||||
}
|
||||
while (end > start && (text[end - 1] == ' ' || text[end - 1] == '\t' || text[end - 1] == '\n' || text[end - 1] == '\r' || text[end - 1] == '\x1e')) {
|
||||
end--;
|
||||
}
|
||||
std::string cleaned(text.substr(start, end - start));
|
||||
if (format == simdjson::stream_format::comma_delimited || format == simdjson::stream_format::comma_delimited_array) {
|
||||
while (!cleaned.empty() && cleaned.back() == ',') {
|
||||
cleaned.pop_back();
|
||||
while (!cleaned.empty() && (cleaned.back() == ' ' || cleaned.back() == '\t' || cleaned.back() == '\n' || cleaned.back() == '\r')) {
|
||||
cleaned.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
return cleaned;
|
||||
}
|
||||
|
||||
std::string canonicalize_document(simdjson::stream_format format, std::string_view text) {
|
||||
std::string cleaned = strip_stream_artifacts(format, text);
|
||||
simdjson::dom::parser parser;
|
||||
simdjson::dom::element doc;
|
||||
if (parser.parse(cleaned).get(doc)) {
|
||||
return std::string("PARSE_ERROR:") + cleaned;
|
||||
}
|
||||
return simdjson::minify(doc);
|
||||
}
|
||||
|
||||
const std::vector<std::string> &expected_documents() {
|
||||
static const std::vector<std::string> docs = document_stream_fuzz::make_documents();
|
||||
return docs;
|
||||
}
|
||||
|
||||
const std::vector<document_stream_fuzz::stream_case> &stream_cases() {
|
||||
static const std::vector<document_stream_fuzz::stream_case> cases =
|
||||
document_stream_fuzz::make_stream_cases(expected_documents());
|
||||
return cases;
|
||||
}
|
||||
|
||||
bool verify_case(const document_stream_fuzz::stream_case &test_case) {
|
||||
TEST_START();
|
||||
const auto &expected = test_case.expected_documents;
|
||||
ASSERT_TRUE(test_case.input.size() > document_stream_fuzz::batch_size * 4);
|
||||
|
||||
simdjson::padded_string input(test_case.input);
|
||||
simdjson::dom::parser parser;
|
||||
|
||||
for (int pass = 0; pass < 2; pass++) {
|
||||
simdjson::dom::document_stream stream;
|
||||
ASSERT_SUCCESS(parser.parse_many(input, document_stream_fuzz::batch_size, test_case.format).get(stream));
|
||||
|
||||
size_t index = 0;
|
||||
for (auto doc : stream) {
|
||||
ASSERT_SUCCESS(doc.error());
|
||||
ASSERT_TRUE(index < expected.size());
|
||||
simdjson::dom::element el;
|
||||
ASSERT_SUCCESS(doc.get(el));
|
||||
ASSERT_EQUAL(
|
||||
canonicalize_document(test_case.format, simdjson::minify(el)),
|
||||
canonicalize_document(test_case.format, expected[index])
|
||||
);
|
||||
index++;
|
||||
}
|
||||
ASSERT_EQUAL(index, expected.size());
|
||||
}
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool run() {
|
||||
for (const auto &test_case : stream_cases()) {
|
||||
std::cout << "Running fuzz corpus against stream format: " << test_case.name << std::endl;
|
||||
if (!verify_case(test_case)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace document_stream_fuzz_tests
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
return test_main(argc, argv, document_stream_fuzz_tests::run);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user