Compare commits

..

32 Commits

Author SHA1 Message Date
Daniel Lemire 3d0fffaa7d forgot these lifebounds 2025-07-18 13:00:53 -04:00
evbse 6029af75b3 Improve fallback implementation (#2393) 2025-07-15 14:39:09 -04:00
Daniel Lemire c806e955c4 C++26 static reflection (#2282)
* Initial work on JSON builder

* moving the files back to ondemand for now.

* tweak

* more later

* update

* minor edits

* dropping vs arm (missing support)

* adding tests. we still specialized write_string_escaped

* tweaking

* fix typo

* tweaking the approach

* minor fix

* missing store

* another missing store

* Attempt at fixing failing serialization tests. (#2292)

* Fixing appeand_float typo (#2294)

* applying a couple of fixes

* updating single header

* fix for pre C++17 if constexpr

* Fixing unused argument problem and updating the singleheader file

* various pedantic fixes

* Sketch of builder

* reordering.

* simplify

* Adding draft of static reflection based deserialization

* Updating simdjson singleheader

* patching the automated deserialization.

* automated

* Adding support for smart pointers of user defined types.

* Adding specialization for smart pointers for basic types. I think it is highly likely that this can be done in a more generic way.

* Referncing a later version of rapidjson that fixed the issue related with assignment attempt of a const variable for GenericStringRef class.

* guarding the tests

* adding documentation for string_builder

* saving

* rename to 'append'

* saving

* non-functional benchmarks (#2342)

* non-functional benchmarks

* Fix typo

* various fixes

* tweaking

---------

Co-authored-by: Daniel Lemire <dlemire@lemire.me>
Co-authored-by: Francisco Geiman Thiesen <franciscogthiesen@gmail.com>

* tuning

* various minor fixes

* minor tweak

* minor simplification

* updating amal

* adding a cast

* update

* fancy casting

* removing dead code

* Pushing latest changes. CITM benchmark is still not working.

* Still not working, but now I am getting only 10 errors.

* add static reflection benchmark to 'large random' benchmark and allows (#2349)

deserialization (with static reflection) from objects and arrays.

Co-authored-by: Daniel Lemire <dlemire@lemire.me>

* Removing std::map from CitmCatalog definition, since that is not currently supported.

* Added free to rust bench, segfault is still happening..

* The syntax changed: ^E became ^^E. (#2350)

* The syntax changed: ^E became ^^E.

* guarding

---------

Co-authored-by: Daniel Lemire <dlemire@lemire.me>

* Adding support for string_view_keyed_map types.

* Adding concepts as a conditional include.

* updating single-header

* Adding concepts to ondemand deps

* rust benchmark is finally working

* Fixing small typo in docs.

* adding docker config and instructions so that our users can test the static reflection (#2358)

* adding docker config and instructions so that our users can test the
static reflection

* completing the instructions

* pruning white spaces

---------

Co-authored-by: Daniel Lemire <dlemire@lemire.me>

* minor optimizations on the JSON builder branch

* avoiding undef behaviour

* saving

* somewhat nicer builder

* make it possible to run just one benchmark

* adding linux perf

* fixing minor issue

* updating swar

* Adding real world compilation benchmark (#2379)

* Adding compilation benchmark for json parsing with and without reflection

* Moving it to the benchmark folder, also reducing a bit the number of iterations.

* Removing script from root folder.

* Reducing number of iterations

* Update benchmark/benchmark_reflection_usage_compilation.sh

Co-authored-by: Daniel Lemire <daniel@lemire.me>

* Update benchmark/benchmark_reflection_usage_compilation.sh

Co-authored-by: Daniel Lemire <daniel@lemire.me>

* Update benchmark/benchmark_reflection_usage_compilation.sh

Co-authored-by: Daniel Lemire <daniel@lemire.me>

* Making the script more customizable and also test whether the compiler being used supports reflection before actually running the benchmark

---------

Co-authored-by: Daniel Lemire <daniel@lemire.me>

* Using define_static_string from  https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p3491r2.html (#2389)

* Applying changes needed after latest reflection paper updates.

* Working, but no template for yet.

* Updating single-header to incldue the use of define_static_string.

* copying over master

---------

Co-authored-by: Daniel Lemire <dlemire@lemire.me>
Co-authored-by: Francisco Geiman Thiesen <franciscogthiesen@gmail.com>
2025-07-14 15:43:52 -04:00
Daniel Lemire 4acaf2ea1c improving DOM ranges test and silencing a warning (#2385)
* improving DOM ranges test and silencing a warning

* moving test_main to macros (even though it is not a macro)

* moving test_main
2025-07-04 15:46:50 -04:00
Daniel Lemire 86adda06d0 clarifying NDEBUG usage (#2388)
* clarifying NDEBUG usage

* init
2025-07-04 15:46:37 -04:00
Daniel Lemire 8df5f96eba guarding the POSITION_INDEPENDENT_CODE (#2386) 2025-06-30 16:41:49 -04:00
xkszltl 937667796c Replace -fPIC with POSITION_INDEPENDENT_CODE. (#2383) 2025-06-30 11:26:27 -04:00
Cuda Chen 7d86b04247 Add document and tests of std::ranges support of DOM API (#2381)
* Add document and tests of std::ranges support of DOM API

* Alter lambda

* Fix require of range
2025-06-24 15:13:44 -04:00
Daniel Lemire 265022ad8f Fixing MSYS2 issue (#2380)
* Google Benchmarks does not support MSYS2.

* narrowing it down
2025-06-21 21:26:52 -04:00
Daniel Lemire 0c0ce1bd48 3.13.0 2025-06-04 00:46:03 -04:00
Daniel Lemire f64c004cb7 making it easier to convert to std::string (#2378)
* making it easier to convert to std::string

* typo

* guarding C++20

* bad semi-colon
2025-06-04 00:22:10 -04:00
Daniel Lemire d7d19db6ae Emscripten (#2377)
* emscripten

* cmake_policy

* space
2025-06-03 09:10:59 -04:00
Daniel Lemire a260c967ed adding test for issue 2375 (#2376)
* adding test for issue 2375

* update

* removing space
2025-06-02 09:24:40 -04:00
Daniel Lemire 16e390d81a fix for issue 2373 (#2374)
* fix for issue 2373

* bumping google bench

* removing json11 since dropbox retired it

* removing json11 as a dependency
2025-05-31 00:56:19 -04:00
Daniel Lemire 8d34e14000 Update dom.md 2025-05-31 00:27:28 -04:00
shuiyisong b717136fd9 chore: add greptimedb in real world usage (#2372) 2025-05-07 16:33:24 -04:00
Daniel Lemire f3ac74caf8 slightly better documemntation of the C++20 features (#2371)
* slightly better documemntation of the C++20 features

* adding another remark
2025-04-23 18:32:51 -04:00
Daniel Lemire 2369927661 Moving from Ubuntu 20 to 24 (#2366) 2025-04-18 17:56:46 -04:00
Daniel Lemire 818c0491a1 Treat -0 as -0.0 when SIMDJSON_MINUS_ZERO_AS_FLOAT is set (#2364)
* In the DOM API, treat -0 as -0.0

* documenting...

* adding it to On-Demand
2025-04-18 13:56:02 -04:00
Daniel Lemire 9f14f90a64 [documentation] We run on practically all systems. (#2365)
* We run on practically all systems.

* Update doc/basics.md

Co-authored-by: Antoine Pitrou <pitrou@free.fr>

---------

Co-authored-by: Antoine Pitrou <pitrou@free.fr>
2025-04-18 13:48:13 -04:00
Daniel Lemire 00843d2711 better documentation 2025-04-16 11:45:52 -04:00
Daniel Lemire 2887a17bab bumping up nlohmann/json 2025-04-13 14:43:30 -04:00
huangqinjin d84c934768 do not try to include <string_view> without C++17 for MSVC (#2360)
Doing that will emit warning at https://github.com/microsoft/STL/blob/vs-2022-17.13/stl/inc/string_view#L12.
2025-03-28 19:23:15 -04:00
huangqinjin 7cec7c7ae4 fix VS2017 exception detection (#2359)
MSVC option /EHsc defines __cpp_exceptions only in VS2019 and above,
_CPPUNWIND should be used before VS2019.

https://learn.microsoft.com/en-us/cpp/preprocessor/predefined-macros#microsoft-specific-predefined-macros
2025-03-28 19:22:56 -04:00
Daniel Lemire c52b010a57 missing string_view include (#2361) 2025-03-28 19:22:38 -04:00
Daniel Lemire ee301599b1 Update basics.md 2025-03-28 11:53:07 -04:00
Daniel Lemire 7382dc2be8 preparing patch release 3.12.3 (#2357) 2025-03-28 11:26:19 -04:00
Daniel Lemire 8c14e0c56f fixing issue 2354: get_bool() on document with trailing spaces (#2356) 2025-03-27 15:01:10 -04:00
Daniel Lemire a9a62feb75 Map proto (#2353)
* std::map support

* completed

---------

Co-authored-by: Daniel Lemire <dlemire@lemire.me>
2025-03-23 16:32:48 -04:00
Ezequiel Ramis Folberg b9228b4d3c Update README.md (#2352) 2025-03-22 15:49:02 -04:00
Dirk Stolle 726c3eb611 fix several typos (#2348) 2025-03-17 09:57:08 -04:00
Daniel Lemire 4cdc4f18ef documenting fatal errors 2025-03-13 13:26:50 -04:00
84 changed files with 54776 additions and 12053 deletions
+17
View File
@@ -0,0 +1,17 @@
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
- uses: mymindstorm/setup-emsdk@6ab9eb1bda2574c4ddb79809fc9247783eaf9021 # v14
- name: Verify
run: emcc -v
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v3.6.0
- name: Configure
run: emcmake cmake -B build
- name: Build # We build but do not test
run: cmake --build build
@@ -4,7 +4,7 @@ on: [push, pull_request]
jobs:
whitespace:
runs-on: ubuntu-20.04
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: Remove whitespace and check the diff
-38
View File
@@ -1,38 +0,0 @@
name: Ubuntu 20.04 CI (GCC 8)
on: [push, pull_request]
jobs:
ubuntu-build:
if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]')
runs-on: ubuntu-20.04
env:
CXX: g++-8
CC: gcc-8
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v4
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
- name: Install GCC 8
run: sudo apt-get install -y g++-8
- name: Use cmake
run: |
mkdir builddebug &&
cd builddebug &&
cmake -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 &&
cd .. &&
mkdir build &&
cd build &&
cmake -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF -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 && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../../../build/destination .. && cmake --build .
-47
View File
@@ -1,47 +0,0 @@
name: Ubuntu 20.04 CI (GCC 9)
on: [push, pull_request]
jobs:
ubuntu-build:
if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]')
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v4
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
- name: Use cmake to build just the library
run: |
mkdir buildjustlib &&
cd buildjustlib &&
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF -DSIMDJSON_DEVELOPER_MODE=OFF -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
cmake --build . &&
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 &&
cd ../tests/installation_tests/find &&
mkdir buildjustlib &&
cd buildjustlib &&
cmake -DCMAKE_INSTALL_PREFIX:PATH=../../../buildjustlib/destination .. &&
cmake --build .
- name: Use cmake
run: |
mkdir builddebug &&
cd builddebug &&
cmake -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 &&
cd .. &&
mkdir build &&
cd build &&
cmake -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF -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 && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../../../build/destination .. && cmake --build .
@@ -13,7 +13,7 @@ jobs:
if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]')
runs-on: ubuntu-20.04
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v4
@@ -7,7 +7,7 @@ jobs:
if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]')
runs-on: ubuntu-20.04
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v4
@@ -7,7 +7,7 @@ jobs:
if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]')
runs-on: ubuntu-20.04
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v4
@@ -7,7 +7,7 @@ jobs:
if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]')
runs-on: ubuntu-20.04
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v4
@@ -25,7 +25,7 @@ jobs:
if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]')
runs-on: ubuntu-20.04
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v4
+12 -4
View File
@@ -1,9 +1,11 @@
cmake_minimum_required(VERSION 3.14)
cmake_policy(VERSION 3.5) # For doctest
project(
simdjson
# The version number is modified by tools/release.py
VERSION 3.12.2
VERSION 3.13.0
DESCRIPTION "Parsing gigabytes of JSON per second"
HOMEPAGE_URL "https://simdjson.org/"
LANGUAGES CXX C
@@ -20,8 +22,8 @@ string(
# ---- Options, variables ----
# These version numbers are modified by tools/release.py
set(SIMDJSON_LIB_VERSION "25.0.0" CACHE STRING "simdjson library version")
set(SIMDJSON_LIB_SOVERSION "25" CACHE STRING "simdjson library soversion")
set(SIMDJSON_LIB_VERSION "26.0.0" CACHE STRING "simdjson library version")
set(SIMDJSON_LIB_SOVERSION "26" CACHE STRING "simdjson library soversion")
option(SIMDJSON_BUILD_STATIC_LIB "Build simdjson_static library along with simdjson (only makes sense if BUILD_SHARED_LIBS=ON)" OFF)
if(SIMDJSON_BUILD_STATIC_LIB AND NOT BUILD_SHARED_LIBS)
@@ -98,7 +100,7 @@ set_target_properties(
)
# FIXME: Use proper CMake integration for exports
if(MSVC AND BUILD_SHARED_LIBS)
if(WIN32 AND BUILD_SHARED_LIBS)
target_compile_definitions(
simdjson
PRIVATE SIMDJSON_BUILDING_WINDOWS_DYNAMIC_LIBRARY=1
@@ -134,6 +136,12 @@ if(
)
endif()
option(SIMDJSON_MINUS_ZERO_AS_FLOAT "Treat -0 as a floating-point value" OFF)
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)
if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(loongarch64)$")
option(SIMDJSON_PREFER_LSX "Prefer LoongArch SX" ON)
include(CheckCXXCompilerFlag)
+1 -1
View File
@@ -92,7 +92,7 @@ We welcome contributions from women and less represented groups. If you need hel
Consider the following points when engaging with the project:
- We discourage arguments from authority: ideas are discusssed on their own merits and not based on who stated it.
- We discourage arguments from authority: ideas are discussed on their own merits and not based on who stated it.
- Be mindful that what you may view as an aggression is maybe merely a difference of opinion or a misunderstanding.
- Be mindful that a collection of small aggressions, even if mild in isolation, can become harmful.
+1 -1
View File
@@ -38,7 +38,7 @@ PROJECT_NAME = simdjson
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = "3.12.2"
PROJECT_NUMBER = "3.13.0"
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
+3 -1
View File
@@ -63,6 +63,7 @@ Real-world usage
- [fastgron](https://github.com/adamritter/fastgron)
- [WasmEdge](https://wasmedge.org)
- [RonDB](https://github.com/logicalclocks/rondb)
- [GreptimeDB](https://github.com/GreptimeTeam/greptimedb)
If you are planning to use simdjson in a product, please work from one of our releases.
@@ -174,7 +175,8 @@ We distinguish between "bindings" (which just wrap the C++ code) and a port to a
- [simdjsone](https://github.com/saleyn/simdjsone): erlang bindings.
- [lua-simdjson](https://github.com/FourierTransformer/lua-simdjson): lua bindings.
- [hermes-json](https://hackage.haskell.org/package/hermes-json): haskell bindings.
- [simdjzon](https://github.com/travisstaloch/simdjzon): zig port.
- [zimdjson](https://github.com/EzequielRamis/zimdjson): Zig port.
- [simdjzon](https://github.com/travisstaloch/simdjzon): Zig port.
- [JSON-Simd](https://github.com/rawleyfowler/JSON-simd): Raku bindings.
- [JSON::SIMD](https://metacpan.org/pod/JSON::SIMD): Perl bindings; fully-featured JSON module that uses simdjson for decoding.
- [gemmaJSON](https://github.com/sainttttt/gemmaJSON): Nim JSON parser based on simdjson bindings.
+618
View File
@@ -0,0 +1,618 @@
#!/bin/bash
# JSON Parsing Compilation Benchmark: Reflection Usage vs Manual Parsing
# Compares compilation times when ACTUALLY USING reflection for parsing vs manual parsing
# This measures the compile-time cost of reflection-based automatic deserialization
set -e
echo "=== simdjson Reflection Usage Compilation Benchmark ==="
echo "Measuring compilation impact of ACTUALLY USING reflection for parsing"
echo "Starting at: $(date)"
echo
echo "╔════════════════════════════════════════════════════════════════════════════╗"
echo "║ METHODOLOGY ║"
echo "╠════════════════════════════════════════════════════════════════════════════╣"
echo "║ ║"
echo "║ FAIR COMPARISON STRATEGY: ║"
echo "║ This benchmark compares two DIFFERENT approaches to parsing the same JSON: ║"
echo "║ ║"
echo "║ • MANUAL PARSING: Traditional simdjson with explicit .get() calls ║"
echo "║ - Uses doc[\"field\"].get(variable) for each field ║"
echo "║ - No reflection involved ║"
echo "║ ║"
echo "║ • REFLECTION PARSING: Automatic deserialization with reflection ║"
echo "║ - Uses doc.get<MyStruct>() for automatic field mapping ║"
echo "║ - Relies on compile-time reflection to generate parsing code ║"
echo "║ ║"
echo "║ WHAT WE'RE MEASURING: ║"
echo "║ • Compile-time cost of reflection-based automatic deserialization ║"
echo "║ • Template instantiation overhead for reflection parsing ║"
echo "║ • Code generation complexity from using reflection features ║"
echo "║ ║"
echo "║ TEST SCENARIOS: ║"
echo "║ ║"
echo "║ 1. SIMPLE STRUCT: Basic fields (string, int, bool) ║"
echo "║ - Measures baseline reflection overhead ║"
echo "║ ║"
echo "║ 2. NESTED STRUCT: Multiple levels of nested objects ║"
echo "║ - Measures reflection complexity scaling ║"
echo "║ ║"
echo "║ 3. COMPLEX STRUCT: Arrays, optional fields, mixed types ║"
echo "║ - Measures real-world reflection usage impact ║"
echo "║ ║"
echo "║ WHY THIS IS MEANINGFUL: ║"
echo "║ • Shows actual cost of using reflection features ║"
echo "║ • Measures compile-time code generation overhead ║"
echo "║ • Helps developers understand reflection's compilation impact ║"
echo "║ • Compares equivalent functionality implemented two different ways ║"
echo "║ ║"
echo "╚════════════════════════════════════════════════════════════════════════════╝"
echo
# Configuration
ITERATIONS=10
JOBS=4
# ───────────────────────────── BOX-PRINT HELPER ────────────────────────────
BOX_WIDTH=74 # characters between the pipes
print_box_line() { # usage: print_box_line "text"
printf "║ %-*s ║\n" "${BOX_WIDTH}" "$1"
}
# Function to test if a compiler supports reflection with debug output
test_reflection_support() {
local compiler="$1"
echo " → Testing compiler: $compiler"
if [ ! -x "$compiler" ] && ! command -v "$compiler" >/dev/null 2>&1; then
echo " → Compiler not found or not executable"
return 1
fi
# Check compiler version first
echo " → Compiler version: $("$compiler" --version 2>/dev/null | head -n1 || echo "version check failed")"
# Simple test: check if compiler accepts reflection flags
local test_file=$(mktemp /tmp/reflection_test_XXXXXX.cpp)
cat > "$test_file" << 'EOF'
int main() {
return 0;
}
EOF
echo " → Testing basic reflection flags..."
local test_exe=$(mktemp /tmp/reflection_test_XXXXXX)
local basic_result=$("$compiler" -freflection -fexpansion-statements -std=c++26 "$test_file" -o "$test_exe" 2>&1)
local basic_exit_code=$?
if [ $basic_exit_code -ne 0 ]; then
echo " → Basic flags FAILED with exit code $basic_exit_code"
echo " → Error output: $basic_result"
rm -f "$test_file" "$test_exe"
return 1
fi
echo " → Basic flags: OK"
rm -f "$test_exe"
# Test reflection syntax
echo " → Testing reflection syntax..."
cat > "$test_file" << 'EOF'
struct Test {
int x;
};
int main() {
auto refl = ^^Test;
return 0;
}
EOF
local syntax_result=$("$compiler" -freflection -fexpansion-statements -std=c++26 "$test_file" -o "$test_exe" 2>&1)
local syntax_exit_code=$?
if [ $syntax_exit_code -eq 0 ]; then
echo " → Reflection syntax: OK"
echo " → ✓ REFLECTION SUPPORT CONFIRMED"
rm -f "$test_file" "$test_exe"
return 0
else
echo " → Reflection syntax FAILED with exit code $syntax_exit_code"
echo " → Error output: $syntax_result"
rm -f "$test_file" "$test_exe"
return 1
fi
}
# Find a compiler with reflection support
echo "Searching for clang++ with reflection support..."
REFLECTION_CXX=""
REFLECTION_CC=""
# List of potential clang++ locations to check
POTENTIAL_COMPILERS=(
"/usr/local/bin/clang++"
"/opt/clang/bin/clang++"
"/usr/bin/clang++"
"clang++"
)
# If CXX is already set, test it first
if [ -n "$CXX" ]; then
echo "Testing user-specified compiler: $CXX"
if test_reflection_support "$CXX"; then
REFLECTION_CXX="$CXX"
echo "✓ User-specified compiler supports reflection: $CXX"
else
echo "✗ User-specified compiler does not support reflection: $CXX"
echo "Will search for alternative..."
fi
fi
# If we don't have a working compiler yet, search for one
if [ -z "$REFLECTION_CXX" ]; then
for compiler in "${POTENTIAL_COMPILERS[@]}"; do
echo "Testing: $compiler"
if test_reflection_support "$compiler"; then
REFLECTION_CXX="$compiler"
echo "✓ Found reflection-enabled compiler: $compiler"
break
else
echo "✗ No reflection support: $compiler"
fi
done
fi
# Check if we found a working compiler
if [ -z "$REFLECTION_CXX" ]; then
echo
echo "╔════════════════════════════════════════════════════════════════════════════╗"
echo "║ ERROR ║"
echo "╠════════════════════════════════════════════════════════════════════════════╣"
echo "║ ║"
echo "║ No clang++ compiler with reflection support found! ║"
echo "║ ║"
echo "║ This benchmark requires a compiler that supports C++26 reflection. ║"
echo "║ ║"
echo "║ Options: ║"
echo "║ 1. Use the Docker container: ./p2996/run_docker.sh ║"
echo "║ 2. Build clang with reflection from: https://github.com/bloomberg/clang-p2996 ║"
echo "║ 3. Set CXX environment variable to point to reflection-enabled clang++ ║"
echo "║ ║"
echo "║ Example: CXX=/path/to/reflection-clang++ ./benchmark_script.sh ║"
echo "║ ║"
echo "╚════════════════════════════════════════════════════════════════════════════╝"
exit 1
fi
# Set the compilers
export CXX="$REFLECTION_CXX"
# Find corresponding C compiler
if [ -n "$CC" ]; then
REFLECTION_CC="$CC"
elif [ "$REFLECTION_CXX" = "/usr/local/bin/clang++" ]; then
REFLECTION_CC="/usr/local/bin/clang"
elif [ "$REFLECTION_CXX" = "/opt/clang/bin/clang++" ]; then
REFLECTION_CC="/opt/clang/bin/clang"
elif [ "$REFLECTION_CXX" = "/usr/bin/clang++" ]; then
REFLECTION_CC="/usr/bin/clang"
else
REFLECTION_CC="clang"
fi
export CC="$REFLECTION_CC"
echo
echo "Using reflection-enabled compiler: $($CXX --version | head -n1)"
echo "Using C compiler: $($CC --version | head -n1)"
echo
# Function to create manual parsing test
create_manual_parsing_test() {
local test_name="$1"
local struct_complexity="$2"
cat > "${test_name}_manual.cpp" << 'EOF'
#include <simdjson.h>
#include <iostream>
#include <string>
#include <vector>
#include <optional>
// Test structures
struct Person {
std::string name;
int age;
bool active;
};
struct Address {
std::string street;
std::string city;
int zipcode;
};
struct Employee {
Person person;
Address address;
std::vector<std::string> skills;
std::optional<std::string> department;
double salary;
};
// Manual parsing functions
bool parse_person_manual(simdjson::ondemand::value& val, Person& person) {
auto obj = val.get_object();
if (obj.error()) return false;
for (auto field : obj) {
std::string_view key = field.unescaped_key();
if (key == "name") {
std::string_view name_val;
if (field.value().get(name_val)) return false;
person.name = name_val;
} else if (key == "age") {
if (field.value().get(person.age)) return false;
} else if (key == "active") {
if (field.value().get(person.active)) return false;
}
}
return true;
}
bool parse_address_manual(simdjson::ondemand::value& val, Address& address) {
auto obj = val.get_object();
if (obj.error()) return false;
for (auto field : obj) {
std::string_view key = field.unescaped_key();
if (key == "street") {
std::string_view street_val;
if (field.value().get(street_val)) return false;
address.street = street_val;
} else if (key == "city") {
std::string_view city_val;
if (field.value().get(city_val)) return false;
address.city = city_val;
} else if (key == "zipcode") {
if (field.value().get(address.zipcode)) return false;
}
}
return true;
}
bool parse_employee_manual(simdjson::ondemand::document& doc, Employee& employee) {
auto obj = doc.get_object();
if (obj.error()) return false;
for (auto field : obj) {
std::string_view key = field.unescaped_key();
if (key == "person") {
auto person_val = field.value();
if (!parse_person_manual(person_val, employee.person)) return false;
} else if (key == "address") {
auto addr_val = field.value();
if (!parse_address_manual(addr_val, employee.address)) return false;
} else if (key == "skills") {
auto skills_array = field.value().get_array();
if (skills_array.error()) return false;
for (auto skill : skills_array) {
std::string_view skill_val;
if (skill.get(skill_val)) return false;
employee.skills.emplace_back(skill_val);
}
} else if (key == "department") {
std::string_view dept_val;
if (!field.value().get(dept_val)) {
employee.department = dept_val;
}
} else if (key == "salary") {
if (field.value().get(employee.salary)) return false;
}
}
return true;
}
int main() {
simdjson::ondemand::parser parser;
std::string json_str = R"({
"person": {
"name": "John Doe",
"age": 30,
"active": true
},
"address": {
"street": "123 Main St",
"city": "Anytown",
"zipcode": 12345
},
"skills": ["C++", "JSON", "Programming"],
"department": "Engineering",
"salary": 85000.50
})";
simdjson::ondemand::document doc;
auto error = parser.iterate(simdjson::pad(json_str)).get(doc);
if (error) {
std::cerr << "Parse error" << std::endl;
return 1;
}
Employee employee;
if (!parse_employee_manual(doc, employee)) {
std::cerr << "Manual parsing failed" << std::endl;
return 1;
}
std::cout << "Manual parsing successful: " << employee.person.name
<< ", age " << employee.person.age << std::endl;
return 0;
}
EOF
}
# Function to create reflection parsing test
create_reflection_parsing_test() {
local test_name="$1"
local struct_complexity="$2"
cat > "${test_name}_reflection.cpp" << 'EOF'
#include <simdjson.h>
#include <iostream>
#include <string>
#include <vector>
#include <optional>
// Test structures (same as manual version)
struct Person {
std::string name;
int age;
bool active;
};
struct Address {
std::string street;
std::string city;
int zipcode;
};
struct Employee {
Person person;
Address address;
std::vector<std::string> skills;
std::optional<std::string> department;
double salary;
};
int main() {
simdjson::ondemand::parser parser;
std::string json_str = R"({
"person": {
"name": "John Doe",
"age": 30,
"active": true
},
"address": {
"street": "123 Main St",
"city": "Anytown",
"zipcode": 12345
},
"skills": ["C++", "JSON", "Programming"],
"department": "Engineering",
"salary": 85000.50
})";
simdjson::ondemand::document doc;
auto error = parser.iterate(simdjson::pad(json_str)).get(doc);
if (error) {
std::cerr << "Parse error" << std::endl;
return 1;
}
// Use reflection-based automatic deserialization
Employee employee;
auto result = doc.get<Employee>();
if (result.error()) {
std::cerr << "Reflection parsing failed" << std::endl;
return 1;
}
employee = result.value();
std::cout << "Reflection parsing successful: " << employee.person.name
<< ", age " << employee.person.age << std::endl;
return 0;
}
EOF
}
# Function to time compilation of parsing approach
time_parsing_compilation() {
local description="$1"
local test_file="$2"
local use_reflection="$3"
local iteration="$4"
echo "[$iteration] $description"
# Clean build
rm -rf build_parsing_test
mkdir build_parsing_test
cd build_parsing_test
# Copy test file
cp "../$test_file" .
echo " Configuring..."
if [ "$use_reflection" = "true" ]; then
cmake -DCMAKE_CXX_COMPILER="$CXX" \
-DSIMDJSON_DEVELOPER_MODE=ON \
-DSIMDJSON_STATIC_REFLECTION=ON \
-DBUILD_SHARED_LIBS=OFF \
../.. >/dev/null 2>&1
else
cmake -DCMAKE_CXX_COMPILER="$CXX" \
-DSIMDJSON_DEVELOPER_MODE=ON \
-DSIMDJSON_STATIC_REFLECTION=OFF \
-DBUILD_SHARED_LIBS=OFF \
../.. >/dev/null 2>&1
fi
echo " Building simdjson..."
cmake --build . --target simdjson >/dev/null 2>&1
echo " Compiling parsing test..."
# Time just the test compilation
start_time=$(date +%s.%N)
"$CXX" -std=c++17 -I../../include "$test_file" -L. -lsimdjson -o parsing_test >/dev/null 2>&1
end_time=$(date +%s.%N)
# Calculate time duration
time_taken=$(echo "$end_time $start_time" | awk '{printf "%.3f", $1 - $2}')
echo " Completed in: ${time_taken}s"
cd ..
rm -rf build_parsing_test
echo "$time_taken"
}
# Create test files
echo "Creating test files..."
create_manual_parsing_test "complex" "complex"
create_reflection_parsing_test "complex" "complex"
# Arrays to store times
times_manual=""
times_reflection=""
echo
echo "=== MANUAL PARSING COMPILATION ==="
echo "Testing traditional simdjson parsing with explicit .get() calls"
echo
for i in $(seq 1 $ITERATIONS); do
time_result=$(time_parsing_compilation "Compiling manual parsing test" "complex_manual.cpp" "false" "$i" | tail -n1)
times_manual="$times_manual $time_result"
done
echo
echo "=== REFLECTION PARSING COMPILATION ==="
echo "Testing automatic deserialization with doc.get<Struct>()"
echo
for i in $(seq 1 $ITERATIONS); do
time_result=$(time_parsing_compilation "Compiling reflection parsing test" "complex_reflection.cpp" "true" "$i" | tail -n1)
times_reflection="$times_reflection $time_result"
done
echo
echo "╔════════════════════════════════════════════════════════════════════════════╗"
echo "║ REFLECTION USAGE COMPILATION RESULTS ║"
echo "╠════════════════════════════════════════════════════════════════════════════╣"
echo "║ ║"
echo "║ MANUAL PARSING (explicit .get() calls): ║"
count=1
for t in $times_manual; do
if [[ "$t" =~ ^[0-9]+\.?[0-9]*$ ]]; then
line=$(printf "Run %2d: %7.3f seconds" "$count" "$t")
print_box_line "$line"
count=$((count + 1))
fi
done
echo "║ ║"
echo "║ REFLECTION PARSING (automatic doc.get<Struct>()): ║"
count=1
for t in $times_reflection; do
if [[ "$t" =~ ^[0-9]+\.?[0-9]*$ ]]; then
line=$(printf "Run %2d: %7.3f seconds" "$count" "$t")
print_box_line "$line"
count=$((count + 1))
fi
done
echo "╚════════════════════════════════════════════════════════════════════════════╝"
echo
echo "╔════════════════════════════════════════════════════════════════════════════╗"
echo "║ ANALYSIS SUMMARY ║"
echo "╠════════════════════════════════════════════════════════════════════════════╣"
echo "║ ║"
# Calculate averages and percentages - filter to only numeric values first
manual_numbers=""
reflection_numbers=""
for t in $times_manual; do
if [[ "$t" =~ ^[0-9]+\.?[0-9]*$ ]]; then
manual_numbers="$manual_numbers $t"
fi
done
for t in $times_reflection; do
if [[ "$t" =~ ^[0-9]+\.?[0-9]*$ ]]; then
reflection_numbers="$reflection_numbers $t"
fi
done
if [ -n "$manual_numbers" ] && [ -n "$reflection_numbers" ]; then
manual_avg=$(echo "$manual_numbers" | awk '{sum=0; for(i=1;i<=NF;i++) sum+=$i; print sum/NF}')
reflection_avg=$(echo "$reflection_numbers" | awk '{sum=0; for(i=1;i<=NF;i++) sum+=$i; print sum/NF}')
overhead=$(echo "$reflection_avg $manual_avg" | awk '{printf "%.3f", $1 - $2}')
if [ $(echo "$manual_avg > 0" | awk '{print ($1 > 0)}') -eq 1 ]; then
percent=$(echo "$reflection_avg $manual_avg" | awk '{printf "%.1f", ($1 - $2) / $2 * 100}')
else
percent="0"
fi
print_box_line "MANUAL PARSING RESULTS:"
print_box_line "$(printf "Average compilation time: %.3fs" "$manual_avg")"
print_box_line ""
print_box_line "REFLECTION PARSING RESULTS:"
print_box_line "$(printf "Average compilation time: %.3fs" "$reflection_avg")"
print_box_line ""
print_box_line "REFLECTION OVERHEAD:"
print_box_line "$(printf "Additional time: %.3fs (%+.1f%%)" "$overhead" "$percent")"
print_box_line ""
else
echo "║ ERROR: Could not extract valid timing data ║"
echo "║ Manual times: $times_manual"
echo "║ Reflection times: $times_reflection"
echo "║ ║"
fi
echo "╠════════════════════════════════════════════════════════════════════════════╣"
echo "║ INTERPRETATION ║"
echo "╠════════════════════════════════════════════════════════════════════════════╣"
echo "║ ║"
echo "║ WHAT THESE RESULTS SHOW: ║"
echo "║ ║"
echo "║ • COMPILE-TIME COST: How much longer reflection parsing takes to compile ║"
echo "║ - Higher % = more expensive template instantiation and codegen ║"
echo "║ ║"
echo "║ • CODE GENERATION OVERHEAD: Reflection creates parsing code at compile ║"
echo "║ time, which requires more template processing than manual parsing ║"
echo "║ ║"
echo "║ • DEVELOPER TRADE-OFF: Reflection provides automatic deserialization ║"
echo "║ but at the cost of increased compilation time ║"
echo "║ ║"
echo "║ EVALUATION: ║"
echo "║ • Low overhead (0-20%): Reflection is compile-time efficient ║"
echo "║ • Medium overhead (20-50%): Noticeable but potentially acceptable ║"
echo "║ • High overhead (50%+): Significant compilation cost for reflection ║"
echo "║ ║"
echo "║ REAL-WORLD IMPACT: ║"
echo "║ • Small projects: Absolute time matters more than percentage ║"
echo "║ • Large projects: Percentage overhead compounds across many files ║"
echo "║ • CI/CD pipelines: Longer builds affect development velocity ║"
echo "║ ║"
echo "╚════════════════════════════════════════════════════════════════════════════╝"
# Clean up test files
rm -f complex_manual.cpp complex_reflection.cpp
echo
echo "Completed at: $(date)"
+4 -5
View File
@@ -32,12 +32,11 @@ struct simdjson_ondemand_static_reflect {
bool run(simdjson::padded_string &json, std::vector<point> &result) {
auto doc = parser.iterate(json);
//ondemand::array arr = doc.get_array();
//arr.get<std::vector<point>>(result);
if(auto e = doc.get_array().get<std::vector<point>>(result); e) { return false; }
// We can also do it like so:
for (ondemand::object coord : doc) {
result.emplace_back(coord.get<point>());
}
//for (ondemand::object coord : doc) {
// result.emplace_back(coord.get<point>());
//}
// It seems that doing the reflection is slower than doing the manual lookup.
// E.g., it is faster if we do result.emplace_back(coord["x"], coord["y"], coord["z"]);
return true;
+3 -3
View File
@@ -39,6 +39,6 @@ else()
endif()
endif()
add_subdirectory(twitter_data)
# Add the benchmark executable targets
add_subdirectory(twitter_benchmark)
add_subdirectory(citm_catalog_benchmark)
@@ -0,0 +1,14 @@
add_executable(benchmark_serialization_citm_catalog benchmark_serialization_citm_catalog.cpp)
# Link with Rust benchmarking code if available
if(TARGET serde-benchmark)
message(STATUS "serde-benchmark target was created. Linking CITM catalog benchmark with serde-benchmark.")
target_link_libraries(benchmark_serialization_citm_catalog PRIVATE serde-benchmark)
target_compile_definitions(benchmark_serialization_citm_catalog PRIVATE SIMDJSON_RUST_VERSION="${Rust_VERSION}")
endif()
target_link_libraries(benchmark_serialization_citm_catalog PRIVATE simdjson::simdjson nlohmann_json)
target_link_libraries(benchmark_serialization_citm_catalog PRIVATE reflectcpp)
target_compile_definitions(benchmark_serialization_citm_catalog PRIVATE SIMDJSON_BENCH_CPP_REFLECT=1)
target_compile_definitions(benchmark_serialization_citm_catalog PRIVATE JSON_FILE="${BENCH_CITM_JSON}")
@@ -0,0 +1,177 @@
#include <cassert>
#include <cstdlib>
#include <ctime>
#include <format>
#include <fstream>
#include <iostream>
#include <nlohmann/json.hpp>
#include <simdjson.h>
#include <string>
#include "citm_catalog_data.h"
#include "nlohmann_citm_catalog_data.h"
#include "../benchmark_utils/benchmark_helper.h"
#if SIMDJSON_BENCH_CPP_REFLECT
#include <rfl.hpp>
#include <rfl/json.hpp>
void bench_reflect_cpp(CitmCatalog &data) {
std::string output = rfl::json::write(data);
size_t output_volume = output.size();
printf("# output volume: %zu bytes\n", output_volume);
volatile size_t measured_volume = 0;
pretty_print(1, output_volume, "bench_reflect_cpp",
bench([&data, &measured_volume, &output_volume]() {
std::string output = rfl::json::write(data);
measured_volume = output.size();
if (measured_volume != output_volume) {
printf("mismatch\n");
}
}));
}
#endif // SIMDJSON_BENCH_CPP_REFLECT
#ifdef SIMDJSON_RUST_VERSION
#include "../serde-benchmark/serde_benchmark.h"
void bench_rust(serde_benchmark::CitmCatalog *data) {
const char * output = serde_benchmark::str_from_citm(data);
size_t output_volume = strlen(output);
printf("# output volume: %zu bytes\n", output_volume);
volatile size_t measured_volume = 0;
pretty_print(1, output_volume, "bench_rust",
bench([&data, &measured_volume, &output_volume]() {
const char * output = serde_benchmark::str_from_citm(data);
measured_volume = strlen(output);
if (measured_volume != output_volume) {
printf("mismatch\n");
}
serde_benchmark::free_str(const_cast<char*>(output));
}));
serde_benchmark::free_str(const_cast<char*>(output));
}
#endif // SIMDJSON_RUST_VERSION
void bench_nlohmann(CitmCatalog &data) {
std::string output = nlohmann_serialize(data);
size_t output_volume = output.size();
printf("# output volume: %zu bytes\n", output_volume);
volatile size_t measured_volume = 0;
pretty_print(1, output_volume, "bench_nlohmann",
bench([&data, &measured_volume, &output_volume]() {
std::string output = nlohmann_serialize(data);
measured_volume = output.size();
if (measured_volume != output_volume) {
printf("mismatch\n");
}
}));
}
void bench_simdjson_static_reflection(CitmCatalog &data) {
simdjson::builder::string_builder sb;
simdjson::builder::append(sb, data);
std::string_view p;
if(sb.view().get(p)) {
std::cerr << "Error!" << std::endl;
}
size_t output_volume = p.size();
sb.clear();
printf("# output volume: %zu bytes\n", output_volume);
volatile size_t measured_volume = 0;
pretty_print(sizeof(data), output_volume, "bench_simdjson_static_reflection",
bench([&data, &measured_volume, &output_volume, &sb]() {
sb.clear();
simdjson::builder::append(sb, data);
std::string_view p;
if(sb.view().get(p)) {
std::cerr << "Error!" << std::endl;
}
measured_volume = sb.size();
if (measured_volume != output_volume) {
printf("mismatch\n");
}
}));
}
std::string read_file(const std::string &file_path, size_t read_size = 65536) {
std::ifstream stream(file_path, std::ios::binary);
if(!stream) {
std::cerr << "Could not open file '" << file_path << "'" << std::endl;
exit(EXIT_FAILURE);
}
stream.exceptions(std::ios_base::badbit);
std::string out;
std::string buf(read_size, '\0');
while (stream.read(&buf[0], read_size)) {
out.append(buf, 0, size_t(stream.gcount()));
}
out.append(buf, 0, size_t(stream.gcount()));
return out;
}
// Function to check if benchmark name contains filter substring
bool matches_filter(const std::string& benchmark_name, const std::string& filter) {
return filter.empty() || benchmark_name.find(filter) != std::string::npos;
}
int main(int argc, char* argv[]) {
std::string filter;
// Parse command-line arguments
for (int i = 1; i < argc; ++i) {
if (strcmp(argv[i], "-f") == 0 || strcmp(argv[i], "--filter") == 0) {
if (i + 1 < argc) {
filter = argv[++i];
} else {
std::cerr << "Error: -f/--filter requires an argument" << std::endl;
return EXIT_FAILURE;
}
}
}
// Testing correctness of round-trip (serialization + deserialization)
std::string json_str = read_file(JSON_FILE);
// Loading up the data into a structure.
simdjson::ondemand::parser parser;
simdjson::ondemand::document doc;
if(parser.iterate(simdjson::pad(json_str)).get(doc)) {
std::cerr << "Error loading the document!" << std::endl;
return EXIT_FAILURE;
}
CitmCatalog my_struct;
if(doc.get<CitmCatalog>().get(my_struct)) {
std::cerr << "Error loading CitmCatalog!" << std::endl;
return EXIT_FAILURE;
}
// Benchmarking the serialization
if (matches_filter("nlohmann", filter)) {
bench_nlohmann(my_struct);
}
if (matches_filter("simdjson_static_reflection", filter)) {
bench_simdjson_static_reflection(my_struct);
}
#ifdef SIMDJSON_RUST_VERSION
if (matches_filter("rust", filter)) {
printf("# WARNING: The Rust benchmark may not be directly comparable since it does not use an equivalent data structure.\n");
// Create a Rust-compatible CitmCatalog structure from the JSON string
serde_benchmark::CitmCatalog* rust_data =
serde_benchmark::citm_from_str(json_str.c_str(), json_str.size());
if (rust_data == nullptr) {
printf("# Failed to initialize Rust data structure\n");
} else {
bench_rust(rust_data);
serde_benchmark::free_citm(rust_data);
}
}
#endif
#if SIMDJSON_BENCH_CPP_REFLECT
if (matches_filter("reflect_cpp", filter)) {
bench_reflect_cpp(my_struct);
}
#endif
return EXIT_SUCCESS;
}
@@ -0,0 +1,82 @@
#ifndef CITM_CATALOG_DATA_H
#define CITM_CATALOG_DATA_H
#include <string>
#include <vector>
#include <map>
struct Area {
int64_t id;
std::string name;
int64_t parent;
std::vector<int64_t> childAreas;
bool operator==(const Area &other) const = default;
};
struct AudienceSubCategory {
int64_t id;
std::string name;
int64_t parent;
bool operator==(const AudienceSubCategory &other) const = default;
};
struct Event {
int64_t id;
std::string name;
std::string description;
int64_t subTopic;
int64_t topic;
std::vector<int64_t> audience;
bool operator==(const Event &other) const = default;
};
struct Performance {
int64_t id;
std::string name;
int64_t event;
std::string start;
int64_t venueCode;
bool operator==(const Performance &other) const = default;
};
struct SeatCategory {
int64_t id;
std::string name;
std::vector<int64_t> areas;
bool operator==(const SeatCategory &other) const = default;
};
struct SubTopic {
int64_t id;
std::string name;
int64_t parent;
bool operator==(const SubTopic &other) const = default;
};
struct Topic {
int64_t id;
std::string name;
bool operator==(const Topic &other) const = default;
};
struct Venue {
int64_t id;
std::string name;
int64_t address;
bool operator==(const Venue &other) const = default;
};
struct CitmCatalog {
std::map<std::string, Area> areas;
std::map<std::string, AudienceSubCategory> audienceSubCategory;
std::map<std::string, Event> events;
std::map<std::string, Performance> performances;
std::map<std::string, SeatCategory> seatCategory;
std::map<std::string, SubTopic> subTopic;
std::map<std::string, Topic> topic;
std::map<std::string, Venue> venue;
bool operator==(const CitmCatalog &other) const = default;
};
#endif
@@ -0,0 +1,171 @@
// nlohmann_citm_catalog_data.h
#ifndef NLOHMANN_CITM_CATALOG_DATA_H
#define NLOHMANN_CITM_CATALOG_DATA_H
#include "citm_catalog_data.h"
#include <nlohmann/json.hpp>
#include <string>
using json = nlohmann::json;
// ---- Area ----
inline void to_json(json &j, const Area &a) {
j = json{
{"id", a.id},
{"name", a.name},
{"parent", a.parent},
{"childAreas", a.childAreas}
};
}
inline void from_json(const json &j, Area &a) {
j.at("id").get_to(a.id);
j.at("name").get_to(a.name);
j.at("parent").get_to(a.parent);
j.at("childAreas").get_to(a.childAreas);
}
// ---- AudienceSubCategory ----
inline void to_json(json &j, const AudienceSubCategory &asc) {
j = json{
{"id", asc.id},
{"name", asc.name},
{"parent", asc.parent}
};
}
inline void from_json(const json &j, AudienceSubCategory &asc) {
j.at("id").get_to(asc.id);
j.at("name").get_to(asc.name);
j.at("parent").get_to(asc.parent);
}
// ---- Event ----
inline void to_json(json &j, const Event &e) {
j = json{
{"id", e.id},
{"name", e.name},
{"description", e.description},
{"subTopic", e.subTopic},
{"topic", e.topic},
{"audience", e.audience}
};
}
inline void from_json(const json &j, Event &e) {
j.at("id").get_to(e.id);
j.at("name").get_to(e.name);
j.at("description").get_to(e.description);
j.at("subTopic").get_to(e.subTopic);
j.at("topic").get_to(e.topic);
j.at("audience").get_to(e.audience);
}
// ---- Performance ----
inline void to_json(json &j, const Performance &p) {
j = json{
{"id", p.id},
{"name", p.name},
{"event", p.event},
{"start", p.start},
{"venueCode", p.venueCode}
};
}
inline void from_json(const json &j, Performance &p) {
j.at("id").get_to(p.id);
j.at("name").get_to(p.name);
j.at("event").get_to(p.event);
j.at("start").get_to(p.start);
j.at("venueCode").get_to(p.venueCode);
}
// ---- SeatCategory ----
inline void to_json(json &j, const SeatCategory &sc) {
j = json{
{"id", sc.id},
{"name", sc.name},
{"areas", sc.areas}
};
}
inline void from_json(const json &j, SeatCategory &sc) {
j.at("id").get_to(sc.id);
j.at("name").get_to(sc.name);
j.at("areas").get_to(sc.areas);
}
// ---- SubTopic ----
inline void to_json(json &j, const SubTopic &st) {
j = json{
{"id", st.id},
{"name", st.name},
{"parent", st.parent}
};
}
inline void from_json(const json &j, SubTopic &st) {
j.at("id").get_to(st.id);
j.at("name").get_to(st.name);
j.at("parent").get_to(st.parent);
}
// ---- Topic ----
inline void to_json(json &j, const Topic &t) {
j = json{
{"id", t.id},
{"name", t.name}
};
}
inline void from_json(const json &j, Topic &t) {
j.at("id").get_to(t.id);
j.at("name").get_to(t.name);
}
// ---- Venue ----
inline void to_json(json &j, const Venue &v) {
j = json{
{"id", v.id},
{"name", v.name},
{"address", v.address}
};
}
inline void from_json(const json &j, Venue &v) {
j.at("id").get_to(v.id);
j.at("name").get_to(v.name);
j.at("address").get_to(v.address);
}
// ---- CitmCatalog ----
inline void to_json(json &j, const CitmCatalog &c) {
j = json{
{"areas", c.areas},
{"audienceSubCategory", c.audienceSubCategory},
{"events", c.events},
{"performances", c.performances},
{"seatCategory", c.seatCategory},
{"subTopic", c.subTopic},
{"topic", c.topic},
{"venue", c.venue}
};
}
inline void from_json(const json &j, CitmCatalog &c) {
j.at("areas").get_to(c.areas);
j.at("audienceSubCategory").get_to(c.audienceSubCategory);
j.at("events").get_to(c.events);
j.at("performances").get_to(c.performances);
j.at("seatCategory").get_to(c.seatCategory);
j.at("subTopic").get_to(c.subTopic);
j.at("topic").get_to(c.topic);
j.at("venue").get_to(c.venue);
}
// Optional convenience functions for benchmarking
inline std::string nlohmann_serialize(const CitmCatalog &catalog) {
json j = catalog;
return j.dump();
}
inline bool nlohmann_deserialize(const std::string &json_in, CitmCatalog &catalog) {
try {
catalog = json::parse(json_in);
return false; // success
} catch(...) {
return true; // failure
}
}
#endif // NLOHMANN_CITM_CATALOG_DATA_H
+314 -2
View File
@@ -1,11 +1,11 @@
extern crate serde;
extern crate serde_json;
extern crate libc;
use std::slice;
use libc::{c_char, size_t};
use serde::{Serialize, Deserialize};
use std::{collections::HashMap, ffi::CString, ptr, slice};
use serde::de::{self, Deserializer};
/******************************************************/
/******************************************************/
/**
@@ -168,3 +168,315 @@ pub unsafe extern "C" fn free_twitter(raw: *mut TwitterData) {
pub unsafe extern fn free_string(ptr: *const c_char) {
let _ = std::ffi::CString::from_raw(ptr as *mut _);
}
// Functions associated with the CitmCatalog benchmark
#[derive(Serialize, Deserialize)]
pub struct Area {
pub id: i64,
pub name: Option<String>, // Changed to Option
pub parent: i64,
#[serde(rename = "childAreas")]
pub child_areas: Vec<i64>,
}
#[derive(Serialize, Deserialize)]
pub struct AudienceSubCategory {
pub id: i64,
pub name: Option<String>, // Changed to Option
pub parent: i64,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Event {
#[serde(default)]
pub description: Option<String>,
pub id: i64,
#[serde(default)]
pub logo: Option<String>,
#[serde(default)]
pub name: Option<String>,
#[serde(default)]
pub subTopicIds: Vec<i64>,
#[serde(default)]
pub subjectCode: Option<String>,
#[serde(default)]
pub subtitle: Option<String>,
#[serde(default)]
pub topicIds: Vec<i64>,
// Add a catch-all for any other fields
#[serde(flatten)]
pub extra: HashMap<String, serde_json::Value>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Performance {
#[serde(default)]
pub id: i64,
#[serde(default)]
pub name: Option<String>,
#[serde(default)]
pub event: i64,
// This is the key fix - accept any JSON value type for timestamps
// This allows both string dates and integer timestamps (line 3511)
#[serde(default)]
pub start: serde_json::Value,
#[serde(rename = "venueCode")]
pub venue_code: String,
// Add a catch-all for any other fields
#[serde(flatten)]
pub extra: HashMap<String, serde_json::Value>,
}
#[derive(Serialize, Deserialize)]
pub struct SeatCategory {
pub id: i64,
pub name: Option<String>, // Changed to Option
pub areas: Vec<i64>,
}
#[derive(Serialize, Deserialize)]
pub struct SubTopic {
pub id: i64,
pub name: Option<String>, // Changed to Option
pub parent: i64,
}
#[derive(Serialize, Deserialize)]
pub struct Topic {
pub id: i64,
pub name: Option<String>, // Changed to Option
}
#[derive(Serialize, Deserialize)]
pub struct Venue {
pub id: i64,
pub name: Option<String>, // Changed to Option
pub address: i64,
}
// Custom deserializers
fn deserialize_string_to_area<'de, D>(deserializer: D) -> Result<HashMap<String, Area>, D::Error>
where D: Deserializer<'de> {
let string_map: HashMap<String, String> = HashMap::deserialize(deserializer)?;
let mut result = HashMap::new();
for (id, name) in string_map {
let id_num = id.parse::<i64>().unwrap_or(0);
result.insert(id.clone(), Area {
id: id_num,
name: Some(name),
parent: 0,
child_areas: Vec::new(),
});
}
Ok(result)
}
fn deserialize_string_to_audience_subcategory<'de, D>(deserializer: D) -> Result<HashMap<String, AudienceSubCategory>, D::Error>
where D: Deserializer<'de> {
let string_map: HashMap<String, String> = HashMap::deserialize(deserializer)?;
let mut result = HashMap::new();
for (id, name) in string_map {
let id_num = id.parse::<i64>().unwrap_or(0);
result.insert(id.clone(), AudienceSubCategory {
id: id_num,
name: Some(name),
parent: 0,
});
}
Ok(result)
}
fn deserialize_string_to_seat_category<'de, D>(deserializer: D) -> Result<HashMap<String, SeatCategory>, D::Error>
where D: Deserializer<'de> {
let string_map: HashMap<String, String> = HashMap::deserialize(deserializer)?;
let mut result = HashMap::new();
for (id, name) in string_map {
let id_num = id.parse::<i64>().unwrap_or(0);
result.insert(id.clone(), SeatCategory {
id: id_num,
name: Some(name),
areas: Vec::new(),
});
}
Ok(result)
}
fn deserialize_string_to_subtopic<'de, D>(deserializer: D) -> Result<HashMap<String, SubTopic>, D::Error>
where D: Deserializer<'de> {
let string_map: HashMap<String, String> = HashMap::deserialize(deserializer)?;
let mut result = HashMap::new();
for (id, name) in string_map {
let id_num = id.parse::<i64>().unwrap_or(0);
result.insert(id.clone(), SubTopic {
id: id_num,
name: Some(name),
parent: 0,
});
}
Ok(result)
}
fn deserialize_string_to_topic<'de, D>(deserializer: D) -> Result<HashMap<String, Topic>, D::Error>
where D: Deserializer<'de> {
let string_map: HashMap<String, String> = HashMap::deserialize(deserializer)?;
let mut result = HashMap::new();
for (id, name) in string_map {
let id_num = id.parse::<i64>().unwrap_or(0);
result.insert(id.clone(), Topic {
id: id_num,
name: Some(name),
});
}
Ok(result)
}
fn deserialize_string_to_venue<'de, D>(deserializer: D) -> Result<HashMap<String, Venue>, D::Error>
where D: Deserializer<'de> {
let string_map: HashMap<String, String> = HashMap::deserialize(deserializer)?;
let mut result = HashMap::new();
for (id, name) in string_map {
result.insert(id.clone(), Venue {
id: 0,
name: Some(name),
address: 0,
});
}
Ok(result)
}
#[derive(Serialize, Deserialize, Debug)]
pub struct CitmCatalog {
#[serde(rename = "areaNames")]
pub area_names: HashMap<String, String>,
#[serde(rename = "audienceSubCategoryNames")]
pub audience_subcategory_names: HashMap<String, String>,
#[serde(default)]
#[serde(rename = "blockNames")]
pub block_names: HashMap<String, String>,
pub events: HashMap<String, Event>,
#[serde(default)]
pub performances: Vec<Performance>,
#[serde(rename = "seatCategoryNames")]
pub seat_category_names: HashMap<String, String>,
#[serde(rename = "subTopicNames")]
pub subtopic_names: HashMap<String, String>,
#[serde(default)]
#[serde(rename = "subjectNames")]
pub subject_names: HashMap<String, String>,
#[serde(rename = "topicNames")]
pub topic_names: HashMap<String, String>,
#[serde(rename = "topicSubTopics")]
pub topic_subtopics: HashMap<String, Vec<i64>>,
#[serde(rename = "venueNames")]
pub venue_names: HashMap<String, String>,
// Catch-all for other fields
#[serde(flatten)]
pub extra: HashMap<String, serde_json::Value>,
}
/// Creates a CitmCatalog from a JSON string (UTF-8 encoded).
#[no_mangle]
pub unsafe extern "C" fn citm_from_str(
raw_input: *const c_char,
raw_input_length: usize
) -> *mut CitmCatalog {
if raw_input.is_null() {
eprintln!("Error: Input pointer is null");
return ptr::null_mut();
}
// Convert the raw pointer + length into a Rust slice
let bytes = slice::from_raw_parts(raw_input as *const u8, raw_input_length);
let input_str = match std::str::from_utf8(bytes) {
Ok(s) => s,
Err(e) => {
eprintln!("Error: Invalid UTF-8 string: {}", e);
return ptr::null_mut();
}
};
// Try deserializing the input string into CitmCatalog
match serde_json::from_str::<CitmCatalog>(input_str) {
Ok(catalog) => Box::into_raw(Box::new(catalog)),
Err(e) => {
eprintln!("Error deserializing JSON: {}", e);
eprintln!("JSON snippet (first 200 chars): {:.200}...", input_str);
ptr::null_mut()
}
}
}
/// Serializes a CitmCatalog into a JSON string (UTF-8).
#[no_mangle]
pub unsafe extern "C" fn str_from_citm(raw_catalog: *mut CitmCatalog) -> *mut c_char {
if raw_catalog.is_null() {
eprintln!("Error: Catalog pointer is null");
return ptr::null_mut();
}
// Fix: Actually serialize the catalog
let catalog = &*raw_catalog;
match serde_json::to_string(catalog) {
Ok(serialized) => {
match CString::new(serialized) {
Ok(cstr) => cstr.into_raw(),
Err(e) => {
eprintln!("Error creating CString: {}", e);
ptr::null_mut()
}
}
},
Err(e) => {
eprintln!("Error serializing catalog to JSON: {}", e);
ptr::null_mut()
}
}
}
/// Frees the CitmCatalog pointer.
#[no_mangle]
pub unsafe extern "C" fn free_citm(raw_catalog: *mut CitmCatalog) {
if !raw_catalog.is_null() {
drop(Box::from_raw(raw_catalog));
}
}
#[no_mangle]
pub extern "C" fn free_str(ptr: *mut c_char) {
if !ptr.is_null() {
unsafe {
// Convert back into a CString, which automatically frees the memory
let _ = CString::from_raw(ptr);
}
}
}
@@ -1,7 +1,7 @@
#ifndef serde_benchmark_ffi_h
#define serde_benchmark_ffi_h
/* Generated with cbindgen:0.24.3 */
/* Generated with cbindgen:0.28.0 */
/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */
@@ -13,6 +13,8 @@
namespace serde_benchmark {
struct CitmCatalog;
struct TwitterData;
extern "C" {
@@ -25,8 +27,19 @@ void free_twitter(TwitterData *raw);
void free_string(const char *ptr);
} // extern "C"
/// Creates a CitmCatalog from a JSON string (UTF-8 encoded).
CitmCatalog *citm_from_str(const char *raw_input, uintptr_t raw_input_length);
} // namespace serde_benchmark
/// Serializes a CitmCatalog into a JSON string (UTF-8).
char *str_from_citm(CitmCatalog *raw_catalog);
#endif // serde_benchmark_ffi_h
/// Frees the CitmCatalog pointer.
void free_citm(CitmCatalog *raw_catalog);
void free_str(char *ptr);
} // extern "C"
} // namespace serde_benchmark
#endif // serde_benchmark_ffi_h
@@ -8,7 +8,6 @@ add_executable(benchmark_serialization_twitter benchmark_serialization_twitter.c
target_compile_definitions(benchmark_serialization_twitter PRIVATE SIMDJSON_RUST_VERSION="${Rust_VERSION}")
endif()
target_link_libraries(benchmark_serialization_twitter PRIVATE simdjson::simdjson nlohmann_json)
target_link_libraries(benchmark_serialization_twitter PRIVATE reflectcpp)
target_compile_definitions(benchmark_serialization_twitter PRIVATE SIMDJSON_BENCH_CPP_REFLECT=1)
@@ -9,7 +9,7 @@
#include <string>
#include "twitter_data.h"
#include "nlohmann_twitter_data.h"
#include "benchmark_helper.h"
#include "../benchmark_utils/benchmark_helper.h"
#if SIMDJSON_BENCH_CPP_REFLECT
#include <rfl.hpp>
#include <rfl/json.hpp>
@@ -109,7 +109,25 @@ std::string read_file(std::string filename) {
return out;
}
int main() {
// Function to check if benchmark name contains filter substring
bool matches_filter(const std::string& benchmark_name, const std::string& filter) {
return filter.empty() || benchmark_name.find(filter) != std::string::npos;
}
int main(int argc, char* argv[]) {
std::string filter;
// Parse command-line arguments
for (int i = 1; i < argc; ++i) {
if (strcmp(argv[i], "-f") == 0 || strcmp(argv[i], "--filter") == 0) {
if (i + 1 < argc) {
filter = argv[++i];
} else {
std::cerr << "Error: -f/--filter requires an argument" << std::endl;
return EXIT_FAILURE;
}
}
}
// Testing correctness of round-trip (serialization + deserialization)
std::string json_str = read_file(JSON_FILE);
@@ -127,16 +145,24 @@ int main() {
}
// Benchmarking the serialization
bench_nlohmann(my_struct);
bench_simdjson_static_reflection(my_struct);
if (matches_filter("nlohmann", filter)) {
bench_nlohmann(my_struct);
}
if (matches_filter("simdjson_static_reflection", filter)) {
bench_simdjson_static_reflection(my_struct);
}
#ifdef SIMDJSON_RUST_VERSION
printf("# WARNING: The Rust benchmark may not be directly comparable since it does not use an equivalent data structure.\n");
serde_benchmark::TwitterData * td = serde_benchmark::twitter_from_str(json_str.c_str(), json_str.size());
bench_rust(td);
serde_benchmark::free_twitter(td);
if (matches_filter("rust", filter)) {
printf("# WARNING: The Rust benchmark may not be directly comparable since it does not use an equivalent data structure.\n");
serde_benchmark::TwitterData * td = serde_benchmark::twitter_from_str(json_str.c_str(), json_str.size());
bench_rust(td);
serde_benchmark::free_twitter(td);
}
#endif
#if SIMDJSON_BENCH_CPP_REFLECT
bench_reflect_cpp(my_struct);
if (matches_filter("reflect_cpp", filter)) {
bench_reflect_cpp(my_struct);
}
#endif
return EXIT_SUCCESS;
}
@@ -105,7 +105,6 @@ std::string nlohmann_serialize(const std::vector<Status>& v) {
return a.dump();
}
void to_json(nlohmann::json &j, const TwitterData &t) {
j = nlohmann::json{{"statuses", t.statuses}};
}
+4 -5
View File
@@ -2,7 +2,10 @@
# Flags used by exes and by the simdjson library (project-wide flags)
#
add_library(simdjson-internal-flags INTERFACE)
if(NOT DEFINED CMAKE_POSITION_INDEPENDENT_CODE)
# We default to ON for all targets, so that we can use the library in shared libraries.
set_target_properties(simdjson-internal-flags PROPERTIES POSITION_INDEPENDENT_CODE ON)
endif(NOT DEFINED CMAKE_POSITION_INDEPENDENT_CODE)
option(SIMDJSON_CHECK_EOF "Check for the end of the input buffer. The setting is unnecessary since we require padding of the inputs. You should expect tests to fail with this option turned on." OFF)
if(SIMDJSON_CHECK_EOF)
@@ -52,7 +55,6 @@ endif()
option(SIMDJSON_SANITIZE_MEMORY "Sanitize memory" OFF)
if(SIMDJSON_SANITIZE_MEMORY)
message(STATUS "Setting the memory sanitizer.")
add_compile_options(
@@ -164,9 +166,6 @@ We recommend Visual Studio 2019 or better on a 64-bit system.")
add_compile_options(/Zi)
endif()
else()
if(NOT WIN32)
target_compile_options(simdjson-internal-flags INTERFACE -fPIC)
endif()
target_compile_options(
simdjson-internal-flags INTERFACE
-Werror -Wall -Wextra -Weffc++ -Wsign-compare -Wshadow -Wwrite-strings
+5 -13
View File
@@ -7,12 +7,12 @@ option(SIMDJSON_ALLOW_DOWNLOADS
cmake_dependent_option(SIMDJSON_COMPETITION "Compile competitive benchmarks" ON
SIMDJSON_ALLOW_DOWNLOADS OFF)
cmake_dependent_option(SIMDJSON_GOOGLE_BENCHMARKS "compile the Google Benchmark benchmarks" ON
SIMDJSON_ALLOW_DOWNLOADS OFF)
"SIMDJSON_ALLOW_DOWNLOADS OR MINGW" OFF)
if(SIMDJSON_GOOGLE_BENCHMARKS)
CPMAddPackage(
NAME google_benchmarks
URL https://github.com/google/benchmark/archive/refs/tags/v1.7.1.zip
URL https://github.com/google/benchmark/archive/refs/tags/v1.9.4.zip
OPTIONS
"BENCHMARK_ENABLE_TESTING OFF"
"BENCHMARK_ENABLE_INSTALL OFF"
@@ -22,7 +22,7 @@ endif()
CPMAddPackage(
NAME simdjson-data
URL https://github.com/simdjson/simdjson-data/archive/a5b13babe65c1bba7186b41b43d4cbdc20a5c470.zip
URL https://github.com/simdjson/simdjson-data/archive/351949906abde446f0314bf79606fb5d884f5be7.zip
)
option(SIMDJSON_USE_BOOST_JSON "Try to include BOOST_JSON, this may break your binaries under some systems." OFF)
@@ -93,19 +93,11 @@ int main() {}
CPMAddPackage(
NAME nlohmann_json
URL https://github.com/nlohmann/json/archive/refs/tags/v3.10.5.zip
URL https://github.com/nlohmann/json/archive/refs/tags/v3.12.0.zip
)
set_property(TARGET nlohmann_json APPEND PROPERTY INTERFACE_COMPILE_DEFINITIONS SIMDJSON_COMPETITION_NLOHMANN_JSON)
CPMAddPackage(
NAME json11
URL https://github.com/dropbox/json11/archive/ec4e45219af1d7cde3d58b49ed762376fccf1ace.zip
DOWNLOAD_ONLY YES
)
add_library(json11 STATIC "${json11_SOURCE_DIR}/json11.cpp")
target_include_directories(json11 SYSTEM PUBLIC "${json11_SOURCE_DIR}")
target_compile_definitions(json11 INTERFACE SIMDJSON_COMPETITION_JSON11)
set(jsoncpp_SOURCE_DIR "${simdjson_SOURCE_DIR}/dependencies/jsoncppdist")
add_library(jsoncpp STATIC "${jsoncpp_SOURCE_DIR}/jsoncpp.cpp")
@@ -177,7 +169,7 @@ int main() {}
endif()
add_library(competition-all INTERFACE)
target_link_libraries(competition-all INTERFACE competition-core jsoncpp json11 fastjson gason ujson4c)
target_link_libraries(competition-all INTERFACE competition-core jsoncpp fastjson gason ujson4c)
endfunction()
if(SIMDJSON_COMPETITION)
+111 -13
View File
@@ -47,13 +47,15 @@ An overview of what you need to know to use simdjson, with examples.
Requirements
------------------
- A recent compiler (LLVM clang 6 or better, GNU GCC 7.4 or better, Xcode 11 or better) on a 64-bit (PPC, ARM or x64 Intel/AMD) POSIX systems such as macOS, freeBSD or Linux. We require that the compiler supports the C++11 standard or better.
- Visual Studio 2017 or better under 64-bit Windows. Users should target a 64-bit build (x64 or ARM64) instead of a 32-bit build (x86). We support the LLVM clang compiler under Visual Studio (clang-cl) as well as 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 also support MinGW 64-bit under Windows.
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).
- Visual Studio 2017 or better. We support the LLVM clang compiler under Visual Studio (clang-cl) as well as 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 also support MinGW 64-bit under Windows.
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.
We test the library on a big-endian system (IBM s390x with Linux).
Including simdjson
------------------
@@ -320,13 +322,15 @@ transcode the UTF-8 strings produced by the simdjson library to other formats. S
Avoiding pitfalls: enable development checks
--------------------
We recommend that you first compile and run your code in Debug mode:
We recommend that you first compile and run your code in debug mode:
- under Visual Studio, it means having the `_DEBUG` macro defined,
- for other compilers, it means leaving the `__OPTIMIZE__` macro undefined.
- for many other compilers, it means leaving the `__OPTIMIZE__` macro undefined.
The simdjson code will set `SIMDJSON_DEVELOPMENT_CHECKS=1` in debug mode.
Alternatively, you can set the macro `SIMDJSON_DEVELOPMENT_CHECKS` to 1 prior to including
The simdjson code will set `SIMDJSON_DEVELOPMENT_CHECKS=1` in debug mode. Because
the C++ standard does not provide a direct way of checking for a debug build, and
because you may want the checks while building with otimizations, you can set
the macro `SIMDJSON_DEVELOPMENT_CHECKS` to 1 prior to including
the `simdjson.h` header to enable these additional checks: just make sure you remove the
definition once your code has been tested. When `SIMDJSON_DEVELOPMENT_CHECKS` is set to 1, the
simdjson library runs additional (expensive) tests on your code to help ensure that you are
@@ -335,9 +339,12 @@ using the library in a safe manner.
Once your code has been tested, you can then run it in
Release mode: under Visual Studio, it means having the `_DEBUG` macro undefined, and, for other
compilers, it means setting `__OPTIMIZE__` to a positive integer. You can also forcefully
disable these checks by setting `SIMDJSON_DEVELOPMENT_CHECKS` to 0. Once your code is tested, we
further encourage you to define `NDEBUG` in your Release builds to disable additional runtime
testing and get the best performance.
disable these checks by setting `SIMDJSON_DEVELOPMENT_CHECKS` to 0.
Once your code is tested, we further encourage you to define `NDEBUG` in your release
builds to disable additional runtime testing and get the best performance. We
disable these checks on a best-effort basis but the C++ standard does not provide
a direct way to check for a release build.
Using the parsed JSON
---------------------
@@ -1083,6 +1090,32 @@ int main(void) {
### 2. Use `tag_invoke` for custom types (C++20)
The simdjson library takes advantage of C++20. An immediate benefit
is that you can deserialize JSON data directly in standard containers
and other standard value types:
```C++
simdjson::padded_string json = R"({"data" : [1,2,3,4]})"_padded;
simdjson::ondemand::parser parser;
simdjson::ondemand::document d = parser.iterate(json);
std::vector<uint8_t> array = d["data"].get<std::vector<uint8_t>>();
```
Appending to an existing container is just as easy:
```C++
std::vector<uint32_t> array = {0, 0};
simdjson::padded_string json = R"({"data" : [1,2,3,4]})"_padded;
simdjson::ondemand::parser parser;
simdjson::ondemand::document d = parser.iterate(json);
d["data"].get<std::vector<uint32_t>>(array);
// array is now {0,0,1,2,3,4}
```
In C++20, the standard introduced the notion of *customization point*.
A customization point is a function or function object that can be customized for different types. It allows library authors to provide default behavior while giving users the ability to override this behavior for specific types.
@@ -1245,7 +1278,6 @@ int main() {
You may also conditionally fill in `std::optional` values.
```C++
padded_string json =
R"( { "car1": { "make": "Toyota", "model": "Camry", "year": 2018,
"tire_pressure": [ 40.1, 39.9 ] }
@@ -1260,6 +1292,23 @@ You may also conditionally fill in `std::optional` values.
// error is simdjson::SUCCESS
```
You can also deserialize to map-like types with keys that can be constructed
from `std::string_view` instances:
```C++
padded_string json =
R"( { "car1": { "make": "Toyota", "model": "Camry", "year": 2018,
"tire_pressure": [ 40.1, 39.9 ] }
})"_padded;
ondemand::parser parser;
ondemand::document doc = parser.iterate(json);
std::map<std::string,Car> cars;
error = doc.get<std::map<std::string,Car>>().get(cars);
// car has value car1->Car{"Toyota", "Camry", 2018, {40.1f, 39.9f}}
// error is simdjson::SUCCESS
```
And so forth.
Advanced users may want to overwrite the defaults provided by the simdjson library.
@@ -1556,7 +1605,20 @@ Some errors are recoverable:
* You may get the error `simdjson::INCORRECT_TYPE` after trying to convert a value to an incorrect type: e.g., you expected a number and try to convert the value to a number, but it is an array.
* You may query a key from an object, but the key is missing in which case you get the error `simdjson::NO_SUCH_FIELD`: e.g., you call `obj["myname"]` and the object does not have a key `"myname"`.
Other errors (e.g., `simdjson::INCOMPLETE_ARRAY_OR_OBJECT`) may indicate a fatal error and often follow from the fact that the document is not valid JSON. In which case, it is no longer possible to continue accessing the document: calling the method `is_alive()` on the document instance returns false. All following accesses will keep returning the same fatal error (e.g., `simdjson::INCOMPLETE_ARRAY_OR_OBJECT`).
Other errors (`simdjson::INCOMPLETE_ARRAY_OR_OBJECT` and `simdjson::TAPE_ERROR`) indicate a fatal error and follow from the fact that the document is not valid JSON. These errors are not recoverable: you cannot continue. In which case, it is no longer safe to continue accessing the document: calling the method `is_alive()` on the document instance returns false. It is your responsibility as a user to stop using the simdjson
document after encountering these fatal errors. Consider the following example, after
the fatal error, the document instance cannot be used. Observe how the JSON input is invalid.
```cpp
simdjson::padded_string badjson = R"( { "make": "Toyota", "model": "Camry", "year"})"_padded;
simdjson::ondemand::parser parser;
simdjson::ondemand::document doc;
auto errordoc = parser.iterate(badjson).get(doc);
// errordoc == simdjson::SUCCESS
simdjson::ondemand::value v;
auto error = doc.get_object()["year"].get(v);
// simdjson::is_fatal(error)) is true!
// doc.is_alive() is false
```
When you use the code without exceptions, it is your responsibility to check for error before using the
result: if there is an error, the result value will not be valid and using it will caused undefined behavior. Most compilers should be able to help you if you activate the right
@@ -1945,6 +2007,8 @@ conclude that you have trailing content and that your document is not valid JSON
You may then use `doc.current_location()` to obtain a pointer to the start of the trailing
content.
Example 1.
```C++
auto json = R"([1, 2] foo ])"_padded;
ondemand::parser parser;
@@ -1959,6 +2023,21 @@ content.
}
```
Example 2.
```cpp
auto json = R"(["extra close"]])"_padded;
ondemand::parser parser;
ondemand::document doc = parser.iterate(json);
ondemand::array array = doc.get_array();
for (std::string_view values : array) {
std::cout << values << std::endl;
}
if(!doc.at_end()) {
std::cerr << "trailing content at byte index " << doc.current_location() - json.data() << std::endl;
}
```
The `at_end()` method is equivalent to `doc.current_location().error() == simdjson::SUCCESS` but
more convenient.
@@ -2225,6 +2304,11 @@ Thus it is a dynamically typed number. Before accessing the value, you must dete
such a number of `get_number()`, you get the error `BIGINT_ERROR`. You can access the underlying string of digits with the function `raw_json_token()` which returns a `std::string_view` instance starting at the beginning of the digit. You can also call `get_double()` to get a floating-point approximation.
By default, the string `-0` is parsed as the integer 0 as in Python or C++. If you set the macro
`SIMDJSON_MINUS_ZERO_AS_FLOAT` to `1` when building simdjson, you can get that `-0` is mapped to `-0.0`
as in JavaScript. You can get the desired effect by building simdjson with cmake setting the
`SIMDJSON_MINUS_ZERO_AS_FLOAT` to on: `cmake -B build -D SIMDJSON_MINUS_ZERO_AS_FLOAT=ON`.
You must check the type before accessing the value: it is an error to call `get_int64()` when `number.get_number_type()` is not `number_type::signed_integer` and when `number.is_int64()` is false. You are responsible for this check as the user of the library.
The `get_number()` function is designed with performance in mind. When calling `get_number()`, you scan the number string only once, determining efficiently the type and storing it in an efficient manner.
@@ -2552,6 +2636,20 @@ can use it with features such as `std::optional`:
// value was populated with "3.1416"
```
You can generally convert any answer that would return an `std::string_view`.
```cpp
auto json = R"({"\u0062\u0065\u0062\u0065": 2} })"_padded;
ondemand::parser parser;
ondemand::document doc = parser.iterate(json);
ondemand::object object = doc.get_object();
for (auto field : object) {
std::string key;
error = field.unescaped_key().get(key);
if(error) { /* */ }
}
```
You should be mindful of the trade-off: allocating multiple
`std::string` instances can become expensive.
@@ -2570,7 +2668,7 @@ The CPU detection, which runs the first time parsing is attempted and switches t
parser for your CPU, is transparent and thread-safe.
Our runtime dispatching is based on global objects that are instantiated at the beginning of the
main thread and may be discarded at the end of the main thread. If you have multiple threads running
and some threads use the library while the main thread is cleaning up ressources, you may encounter
and some threads use the library while the main thread is cleaning up resources, you may encounter
issues. If you expect such problems, you may consider using [std::quick_exit](https://en.cppreference.com/w/cpp/utility/program/quick_exit).
In a threaded environment, stack space is often limited. Running code like simdjson in debug mode may require hundreds of kilobytes of stack memory. Thus stack overflows are a possibility. We recommend you turn on optimization when working in an environment where stack space is limited. If you must run your code in debug mode, we recommend you configure your system to have more stack space. We discourage you from running production code based on a debug build.
+12 -21
View File
@@ -47,42 +47,33 @@ Example: string_builder
void serialize_car(const Car& car, simdjson::builder::string_builder& builder) {
// start of JSON
builder.append_raw("{");
builder.start_object();
// "make"
builder.escape_and_append_with_quotes("make");
builder.append_raw(":");
builder.escape_and_append_with_quotes(car.make);
builder.append_key_value("make", car.make);
builder.append_comma();
// "model"
builder.append_raw(",");
builder.escape_and_append_with_quotes("model");
builder.append_raw(":");
builder.escape_and_append_with_quotes(car.model);
builder.append_key_value("model", car.model);
builder.append_comma();
// "year"
builder.append_raw(",");
builder.escape_and_append_with_quotes("year");
builder.append_raw(":");
builder.append(car.year);
builder.append_key_value("year", car.year);
builder.append_comma();
// "tire_pressure"
builder.append_raw(",");
builder.escape_and_append_with_quotes("tire_pressure");
builder.append_raw(":[");
builder.append_colon();
builder.start_array();
// vector tire_pressure
for (size_t i = 0; i < car.tire_pressure.size(); ++i) {
builder.append(car.tire_pressure[i]);
if (i < car.tire_pressure.size() - 1) {
builder.append_raw(",");
builder.append_comma();
}
}
// end of array
builder.append_raw("]");
// end of object
builder.append_raw("}");
builder.end_array();
builder.end_object();
}
bool car_test() {
+26 -2
View File
@@ -7,6 +7,7 @@ An overview of what you need to know to use simdjson, with examples.
* [The Basics: Loading and Parsing JSON Documents](#the-basics-loading-and-parsing-json-documents-using-the-dom-front-end)
* [Using the Parsed JSON](#using-the-parsed-json)
* [C++17 Support](#c17-support)
* [C++20 Support](#c20-support)
* [JSON Pointer](#json-pointer)
* [JSONPath](#jsonpath)
* [Error Handling](#error-handling)
@@ -90,7 +91,7 @@ During the`load` or `parse` calls, neither the input file nor the input string a
For best performance, a `parser` instance should be reused over several files: otherwise you will needlessly reallocate memory, an expensive process. It is also possible to avoid entirely memory allocations during parsing when using simdjson. [See our performance notes for details](performance.md).
If you need a lower-level interface, you may call the function `parser.parse(const char * p, size_t l)` on a pointer `p` while specifying the
length of your input `l` in bytes. To see how to get the very best performance from a low-level approach, you way want to read our [performance notes](https://github.com/simdjson/simdjson/blob/master/doc/performance.md#padding-and-temporary-copies) on this topic (see the Padding and Temporary Copies section).
length of your input `l` in bytes.
*Windows-specific*: Windows users who need to read files with
non-ANSI characters in the name should set their code page to
@@ -119,6 +120,12 @@ Once you have an element, you can navigate it with idiomatic C++ iterators, oper
std::cout << "I parsed " << value << " from " << numberstring.data() << std::endl;
```
The strings contain unescaped valid UTF-8 strings: no unmatched surrogate is allowed.
Internally, numbers are stored as either 64-bit integers or 64-bit floating-point numbers.
Thus it is possible to get the full 64-bit integer range (either signed or unsigned).
By default, the string `-0` is parsed as the integer 0 as in Pytho or C++. If you set the macro
`SIMDJSON_MINUS_ZERO_AS_FLOAT` to `1` when building simdjson, you can get that `-0` is mapped to `-0.0`
as in JavaScript. You can get the desired effect by building simdjson with cmake setting the
`SIMDJSON_MINUS_ZERO_AS_FLOAT` to on: `cmake -B build -D SIMDJSON_MINUS_ZERO_AS_FLOAT=ON`.
* **Field Access:** To get the value of the "foo" field in an object, use `object["foo"]`.
* **Array Iteration:** To iterate through an array, use `for (auto value : array) { ... }`. If you
know the type of the value, you can cast it right there, too! `for (double value : array) { ... }`
@@ -232,6 +239,23 @@ for (dom::key_value_pair field : object) {
}
```
C++20 Support
------------
simdjson library also supports some C++20 feature including `std::ranges`:
```c++
auto cars_json = R"( [
{ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] },
{ "make": "Kia", "model": "Soul", "year": 2012, "tire_pressure": [ 30.1, 31.0, 28.6, 28.7 ] },
{ "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
] )"_padded;
dom::parser parser;
auto justmodel = [](auto car) { return car["model"]; };
for (auto car : parser.parse(cars_json).get_array() | std::views::transform(justmodel)) {
std::cout << car << std::endl;
}
```
JSON Pointer
------------
@@ -733,5 +757,5 @@ Setting the `realloc_if_needed` parameter `false` in this manner may lead to bet
Performance Tips
---------------------
- For release builds, we recommend setting `NDEBUG` pre-processor directive when compiling the `simdjson` library. Importantly, using the optimization flags `-O2` or `-O3` under GCC and LLVM clang does not set the `NDEBUG` directrive, you must set it manually (e.g., `-DNDEBUG`).
- For release builds, we recommend setting `NDEBUG` pre-processor directive when compiling the `simdjson` library. Importantly, using the optimization flags `-O2` or `-O3` under GCC and LLVM clang does not set the `NDEBUG` directive, you must set it manually (e.g., `-DNDEBUG`).
- For long streams of JSON documents, consider [`iterate_many`](iterate_many.md) and [`parse_many`](parse_many.md) for better performance.
+1 -1
View File
@@ -367,7 +367,7 @@ Please see our main documentation (`basics.md`) under
"Use `tag_invoke` for custom types (C++20)" for details about
tag_invoke functions.
Given a stream of JSON documents, you can add them to a data struture
Given a stream of JSON documents, you can add them to a data structure
such as a `std::vector<Car>` like so if you support exceptions:
```C++
+20 -9
View File
@@ -4,9 +4,9 @@ Performance Notes
simdjson strives to be at its fastest *without tuning*, and generally achieves this. However, there
are still some scenarios where tuning can enhance performance.
Once your code is tested, we
further encourage you to define `NDEBUG` in your Release builds to disable additional runtime
further encourage you to define `NDEBUG` in your release builds to disable additional runtime
testing and get the best performance.
* [NDEBUG directive](#ndebug-directive)
* [NDEBUG macro](#ndebug-macro)
* [Reusing the parser for maximum efficiency](#reusing-the-parser-for-maximum-efficiency)
* [Reusing string buffers](#reusing-string-buffers)
* [Server Loops: Long-Running Processes and Memory Capacity](#server-loops-long-running-processes-and-memory-capacity)
@@ -17,16 +17,27 @@ testing and get the best performance.
* [Free Padding](#free-padding)
NDEBUG directive
NDEBUG macro
-------------
In C/C++, the `NDEBUG` pre-processor directive is not set by default. When it is not set, the simdjson library does
many additional checks that may impact negatively the performance. We recommend that, once your code
is well tested, you define `NDEBUG` directive in your Release builds. The `NDEBUG` directive should be defined
prior to including the `simdjson.h` header.
We recommend that you set `NDEBUG` macro in your release builds.
The `NDEBUG` directive is generally independent from optimization flags. For example, setting `-O3` under
GCC does not set the `NDEBUG` directive.
In C/C++, the `NDEBUG` macro is not set by default.
When it is not set, the software may do many additional checks that may impact
negatively the performance. We recommend that, once your code
is well tested, you define `NDEBUG` directive in your release builds.
The `NDEBUG` directive is generally independent from optimization flags.
For example, setting `-O3` under GCC does not set the `NDEBUG` directive.
However, tools like `CMake` automatically
set `NDEBUG` for release builds.
In the simdjson library, we check the `NDEBUG` macro as well as other
macros to make performant release builds. However, the C++ standard
does not provide a definitive approach to determine whether you are
compiling for a release build. Thus we recommend that you follow
the practice of setting the `NDEBUG` macro in release mode to make sure
that you do not get undesirable expensive checks.
Reusing the parser for maximum efficiency
-----------------------------------------
+15 -3
View File
@@ -23,7 +23,7 @@ double from_chars(const char *first, const char* end) noexcept;
}
#ifndef SIMDJSON_EXCEPTIONS
#if __cpp_exceptions
#if defined(__cpp_exceptions) || defined(_CPPUNWIND)
#define SIMDJSON_EXCEPTIONS 1
#else
#define SIMDJSON_EXCEPTIONS 0
@@ -226,12 +226,14 @@ double from_chars(const char *first, const char* end) noexcept;
// even if we do not have C++17 support.
#ifdef __cpp_lib_string_view
#define SIMDJSON_HAS_STRING_VIEW
#include <string_view>
#endif
// Some systems have string_view even if we do not have C++17 support,
// and even if __cpp_lib_string_view is undefined, it is the case
// with Apple clang version 11.
// We must handle it. *This is important.*
#ifndef _MSC_VER
#ifndef SIMDJSON_HAS_STRING_VIEW
#if defined __has_include
// do not combine the next #if with the previous one (unsafe)
@@ -247,6 +249,7 @@ double from_chars(const char *first, const char* end) noexcept;
#endif // __has_include (<string_view>)
#endif // defined __has_include
#endif // def SIMDJSON_HAS_STRING_VIEW
#endif // def _MSC_VER
// end of complicated but important routine to try to detect string_view.
//
@@ -279,16 +282,25 @@ namespace std {
// It could also wrongly set SIMDJSON_DEVELOPMENT_CHECKS (e.g., if the programmer
// sets _DEBUG in a release build under Visual Studio, or if some compiler fails to
// set the __OPTIMIZE__ macro).
// We make it so that if NDEBUG is defined, then SIMDJSON_DEVELOPMENT_CHECKS
// is not defined, irrespective of the compiler.
// We recommend that users set NDEBUG in release builds, so that
// SIMDJSON_DEVELOPMENT_CHECKS is not defined in release builds by default,
// irrespective of the compiler.
#ifndef SIMDJSON_DEVELOPMENT_CHECKS
#ifdef _MSC_VER
// Visual Studio seems to set _DEBUG for debug builds.
#ifdef _DEBUG
// We set SIMDJSON_DEVELOPMENT_CHECKS to 1 if _DEBUG is defined
// and NDEBUG is not defined.
#if defined(_DEBUG) && !defined(NDEBUG)
#define SIMDJSON_DEVELOPMENT_CHECKS 1
#endif // _DEBUG
#else // _MSC_VER
// All other compilers appear to set __OPTIMIZE__ to a positive integer
// when the compiler is optimizing.
#ifndef __OPTIMIZE__
// We only set SIMDJSON_DEVELOPMENT_CHECKS if both __OPTIMIZE__
// and NDEBUG are not defined.
#if !defined(__OPTIMIZE__) && !defined(NDEBUG)
#define SIMDJSON_DEVELOPMENT_CHECKS 1
#endif // __OPTIMIZE__
#endif // _MSC_VER
+19 -2
View File
@@ -32,14 +32,30 @@ SIMDJSON_IMPL_CONCEPT(op_append, operator+=)
#undef SIMDJSON_IMPL_CONCEPT
} // namespace details
template <typename T>
concept string_view_like = std::is_convertible_v<T, std::string_view> &&
!std::is_convertible_v<T, const char*>;
template<typename T>
concept constructible_from_string_view = std::is_constructible_v<T, std::string_view>
&& !std::is_same_v<T, std::string_view>
&& std::is_default_constructible_v<T>;
template<typename M>
concept string_view_keyed_map = string_view_like<typename M::key_type>
&& requires(std::remove_cvref_t<M>& m, typename M::key_type sv, typename M::mapped_type v) {
{ m.emplace(sv, v) } -> std::same_as<std::pair<typename M::iterator, bool>>;
};
/// Check if T is a container that we can append to, including:
/// std::vector, std::deque, std::list, std::string, ...
template <typename T>
concept appendable_containers =
details::supports_emplace_back<T> || details::supports_emplace<T> ||
(details::supports_emplace_back<T> || details::supports_emplace<T> ||
details::supports_push_back<T> || details::supports_push<T> ||
details::supports_add<T> || details::supports_append<T> ||
details::supports_insert<T>;
details::supports_insert<T>) && !string_view_keyed_map<T>;
/// Insert into the container however possible
template <appendable_containers T, typename... Args>
@@ -108,6 +124,7 @@ concept optional_type = requires(std::remove_cvref_t<T> obj) {
};
} // namespace concepts
} // namespace simdjson
#endif // SIMDJSON_SUPPORTS_DESERIALIZATION
+2 -2
View File
@@ -57,7 +57,7 @@ public:
iterator& operator=(const iterator&) noexcept = default;
private:
simdjson_inline iterator(const internal::tape_ref &tape) noexcept;
internal::tape_ref tape;
internal::tape_ref tape{};
friend class array;
};
@@ -148,7 +148,7 @@ public:
private:
simdjson_inline array(const internal::tape_ref &tape) noexcept;
internal::tape_ref tape;
internal::tape_ref tape{};
friend class element;
friend struct simdjson_result<element>;
template<typename T>
+1 -1
View File
@@ -70,7 +70,7 @@ public:
* The memory allocation is strict: you
* can you use this function to increase
* or lower the amount of allocated memory.
* Passsing zero clears the memory.
* Passing zero clears the memory.
*/
error_code allocate(size_t len) noexcept;
/** @private Capacity in bytes, in terms
+1 -1
View File
@@ -492,7 +492,7 @@ public:
private:
simdjson_inline element(const internal::tape_ref &tape) noexcept;
internal::tape_ref tape;
internal::tape_ref tape{};
friend class document;
friend class object;
friend class array;
+2 -2
View File
@@ -89,7 +89,7 @@ public:
private:
simdjson_inline iterator(const internal::tape_ref &tape) noexcept;
internal::tape_ref tape;
internal::tape_ref tape{};
friend class object;
};
@@ -224,7 +224,7 @@ public:
private:
simdjson_inline object(const internal::tape_ref &tape) noexcept;
internal::tape_ref tape;
internal::tape_ref tape{};
friend class element;
friend struct simdjson_result<element>;
+2 -2
View File
@@ -340,7 +340,7 @@ public:
* arrays or objects) MUST be separated with whitespace.
*
* The documents must not exceed batch_size bytes (by default 1MB) or they will fail to parse.
* Setting batch_size to excessively large or excesively small values may impact negatively the
* Setting batch_size to excessively large or excessively small values may impact negatively the
* performance.
*
* ### Error Handling
@@ -434,7 +434,7 @@ public:
* arrays or objects) MUST be separated with whitespace.
*
* The documents must not exceed batch_size bytes (by default 1MB) or they will fail to parse.
* Setting batch_size to excessively large or excesively small values may impact negatively the
* Setting batch_size to excessively large or excessively small values may impact negatively the
* performance.
*
* ### Error Handling
+22
View File
@@ -6,6 +6,11 @@
#include <iostream>
namespace simdjson {
inline bool is_fatal(error_code error) noexcept {
return error == TAPE_ERROR || error == INCOMPLETE_ARRAY_OR_OBJECT;
}
namespace internal {
// We store the error code so we can validate the error message is associated with the right code
struct error_code_info {
@@ -127,6 +132,23 @@ simdjson_warn_unused simdjson_inline error_code simdjson_result<T>::get(T &value
return std::forward<internal::simdjson_result_base<T>>(*this).get(value);
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code
simdjson_result<T>::get(std::string &value) && noexcept
#if SIMDJSON_SUPPORTS_DESERIALIZATION
requires (!std::is_same_v<T, std::string>)
#endif // SIMDJSON_SUPPORTS_DESERIALIZATION
{
// SFINAE : n'active que pour T = std::string_view
static_assert(std::is_same<T, std::string_view>::value, "simdjson_result<T>::get(std::string&) n'est disponible que pour T = std::string_view");
std::string_view v;
error_code error = std::forward<simdjson_result<T>>(*this).get(v);
if (!error) {
value.assign(v.data(), v.size());
}
return error;
}
template<typename T>
simdjson_inline error_code simdjson_result<T>::error() const noexcept {
return internal::simdjson_result_base<T>::error();
+22 -3
View File
@@ -20,7 +20,7 @@ enum error_code {
SUCCESS = 0, ///< No error
CAPACITY, ///< This parser can't support a document that big
MEMALLOC, ///< Error allocating memory, most likely out of memory
TAPE_ERROR, ///< Something went wrong, this is a generic error
TAPE_ERROR, ///< Something went wrong, this is a generic error. Fatal/unrecoverable error.
DEPTH_ERROR, ///< Your document exceeds the user-specified depth limitation
STRING_ERROR, ///< Problem while parsing a string
T_ATOM_ERROR, ///< Problem while parsing an atom starting with the letter 't'
@@ -45,7 +45,7 @@ enum error_code {
PARSER_IN_USE, ///< parser is already in use.
OUT_OF_ORDER_ITERATION, ///< tried to iterate an array or object out of order (checked when SIMDJSON_DEVELOPMENT_CHECKS=1)
INSUFFICIENT_PADDING, ///< The JSON doesn't have enough padding for simdjson to safely parse it.
INCOMPLETE_ARRAY_OR_OBJECT, ///< The document ends early.
INCOMPLETE_ARRAY_OR_OBJECT, ///< The document ends early. Fatal/unrecoverable error.
SCALAR_DOCUMENT_AS_VALUE, ///< A scalar document is treated as a value.
OUT_OF_BOUNDS, ///< Attempted to access location outside of document.
TRAILING_CONTENT, ///< Unexpected trailing content in the JSON input
@@ -53,6 +53,14 @@ enum error_code {
NUM_ERROR_CODES ///< Placeholder for end of error code list.
};
/**
* Some errors are fatal and invalidate the document. This function returns true if the
* error is fatal. It returns true for TAPE_ERROR and INCOMPLETE_ARRAY_OR_OBJECT.
* Once a fatal error is encountered, the on-demand document is no longer valid and
* processing should stop.
*/
inline bool is_fatal(error_code error) noexcept;
/**
* It is the convention throughout the code that the macro SIMDJSON_DEVELOPMENT_CHECKS determines whether
* we check for OUT_OF_ORDER_ITERATION. The logic behind it is that these errors only occurs when the code
@@ -189,6 +197,7 @@ struct simdjson_result_base : protected std::pair<T, error_code> {
* @throw simdjson_error if there was an error.
*/
simdjson_inline operator T&&() && noexcept(false);
#endif // SIMDJSON_EXCEPTIONS
/**
@@ -245,7 +254,17 @@ struct simdjson_result : public internal::simdjson_result_base<T> {
* @param value The variable to assign the value to. May not be set if there is an error.
*/
simdjson_warn_unused simdjson_inline error_code get(T &value) && noexcept;
//
/**
* Copy the value to a provided std::string, only enabled for std::string_view.
*
* @param value The variable to assign the value to. May not be set if there is an error.
*/
simdjson_warn_unused simdjson_inline error_code get(std::string &value) && noexcept
#if SIMDJSON_SUPPORTS_DESERIALIZATION
requires (!std::is_same_v<T, std::string>)
#endif // SIMDJSON_SUPPORTS_DESERIALIZATION
;
/**
* The error.
*/
+17 -2
View File
@@ -137,7 +137,7 @@ simdjson_inline bool compute_float_64(int64_t power, uint64_t i, bool negative,
// floor(log(5**power)/log(2))
//
// Note that this is not magic: 152170/(1<<16) is
// approximatively equal to log(5)/log(2).
// approximately equal to log(5)/log(2).
// The 1<<16 value is a power of two; we could use a
// larger power of 2 if we wanted to.
//
@@ -647,7 +647,16 @@ simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) {
if (i > uint64_t(INT64_MAX)) {
WRITE_UNSIGNED(i, src, writer);
} else {
WRITE_INTEGER(negative ? (~i+1) : i, src, writer);
#if SIMDJSON_MINUS_ZERO_AS_FLOAT
if(i == 0 && negative) {
// We have to write -0.0 instead of 0
WRITE_DOUBLE(-0.0, src, writer);
} else {
WRITE_INTEGER(negative ? (~i+1) : i, src, writer);
}
#else
WRITE_INTEGER(negative ? (~i+1) : i, src, writer);
#endif
}
if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return INVALID_NUMBER(src); }
return SUCCESS;
@@ -1108,6 +1117,12 @@ simdjson_unused simdjson_inline simdjson_result<number_type> get_number_type(con
if (simdjson_unlikely(digit_count == 19 && memcmp(src, smaller_big_integer, 19) > 0)) {
return number_type::big_integer;
}
#if SIMDJSON_MINUS_ZERO_AS_FLOAT
if(digit_count == 1 && src[0] == '0') {
// We have to write -0.0 instead of 0
return number_type::floating_point_number;
}
#endif
return number_type::signed_integer;
}
// Let us check if we have a big integer (>=2**64).
@@ -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/concepts.h"
#include "simdjson/dom/base.h" // for MINIMAL_DOCUMENT_CAPACITY
#include "simdjson/implementation.h"
#include "simdjson/padded_string.h"
+34 -20
View File
@@ -239,20 +239,27 @@ public:
if constexpr (custom_deserializable<T, document>) {
return deserialize(*this, out);
} else {
#endif // SIMDJSON_SUPPORTS_DESERIALIZATION
// Unless the simdjson library or the user provides an inline implementation, calling this method should
// immediately fail.
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
"The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), "
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template."
" You may also add support for custom types, see our documentation.");
static_assert(!sizeof(T), "The get<T> method with type T is not implemented by the simdjson library. "
"And you do not seem to have added support for it. Indeed, we have that "
"simdjson::custom_deserializable<T> is false and the type T is not a default type "
"such as ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, double, or bool.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
#if SIMDJSON_SUPPORTS_DESERIALIZATION
}
#endif
#else // SIMDJSON_SUPPORTS_DESERIALIZATION
// Unless the simdjson library or the user provides an inline implementation, calling this method should
// immediately fail.
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
"The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), "
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template."
" You may also add support for custom types, see our documentation.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
#endif // SIMDJSON_SUPPORTS_DESERIALIZATION
}
/** @overload template<typename T> error_code get(T &out) & noexcept */
template<typename T> simdjson_deprecated simdjson_inline error_code get(T &out) && noexcept;
@@ -814,20 +821,27 @@ public:
if constexpr (custom_deserializable<T, document_reference>) {
return deserialize(*this, out);
} else {
#endif // SIMDJSON_SUPPORTS_DESERIALIZATION
// Unless the simdjson library or the user provides an inline implementation, calling this method should
// immediately fail.
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
"The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), "
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template."
" You may also add support for custom types, see our documentation.");
static_assert(!sizeof(T), "The get<T> method with type T is not implemented by the simdjson library. "
"And you do not seem to have added support for it. Indeed, we have that "
"simdjson::custom_deserializable<T> is false and the type T is not a default type "
"such as ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, double, or bool.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
#if SIMDJSON_SUPPORTS_DESERIALIZATION
}
#endif
#else // SIMDJSON_SUPPORTS_DESERIALIZATION
// Unless the simdjson library or the user provides an inline implementation, calling this method should
// immediately fail.
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
"The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), "
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template."
" You may also add support for custom types, see our documentation.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
#endif // SIMDJSON_SUPPORTS_DESERIALIZATION
}
/** @overload template<typename T> error_code get(T &out) & noexcept */
template<typename T> simdjson_inline error_code get(T &out) && noexcept;
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
@@ -7,6 +7,7 @@
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
#define SIMDJSON_GENERIC_STRING_BUILDER_H
#include "simdjson/generic/builder/json_string_builder.h"
#include "simdjson/concepts.h"
#endif // SIMDJSON_CONDITIONAL_INCLUDE
#if SIMDJSON_STATIC_REFLECTION
@@ -16,6 +17,7 @@
#include <string_view>
#include <type_traits>
#include <utility>
// #include <static_reflection> // for std::define_static_string - header not available yet
namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
@@ -58,6 +60,27 @@ constexpr void atom(string_builder &b, const T &t) {
b.escape_and_append_with_quotes(t);
}
template <concepts::string_view_keyed_map T>
constexpr void atom(string_builder &b, const T &m) {
if (m.empty()) {
b.append_raw("{}");
return;
}
b.append('{');
bool first = true;
for (const auto& [key, value] : m) {
if (!first) {
b.append(',');
}
first = false;
// Keys must be convertible to string_view per the concept
b.escape_and_append_with_quotes(key);
b.append(':');
atom(b, value);
}
b.append('}');
}
template<typename number_type,
typename = typename std::enable_if<std::is_arithmetic<number_type>::value && !std::is_same_v<number_type, char>>::type>
@@ -70,46 +93,37 @@ consteval std::string consteval_to_quoted_escaped(std::string_view input);
template <class T>
requires(std::is_class_v<T> && !container_but_not_string<T> &&
!concepts::string_view_keyed_map<T> &&
!std::is_same_v<T, std::string> &&
!std::is_same_v<T, std::string_view>)
constexpr void atom(string_builder &b, const T &t) {
int i = 0;
b.append('{');
template for (constexpr auto mem : std::meta::nonstatic_data_members_of(^^T)) {
constexpr std::string_view key = std::string_view(std::meta::identifier_of(mem));
[:expand(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked())):] >> [&]<auto dm>() {
if (i != 0)
b.append(',');
b.append_raw(consteval_to_quoted_escaped(key));
b.append(':');
atom(b, t.*mem);
i++;
}
b.append('}');
/*[:expand(std::meta::nonstatic_data_members_of(^T)):] >> [&]<auto dm> {
if (i != 0)
b.append(',');
constexpr auto key = consteval_to_quoted_escaped(std::meta::identifier_of(dm));
constexpr auto key = std::define_static_string(consteval_to_quoted_escaped(std::meta::identifier_of(dm)));
b.append_raw(key);
b.append(':');
atom(b, t.[:dm:]);
i++;
};
b.append('}');*/
b.append('}');
}
// works for struct
template <class Z> void append(string_builder &b, const Z &z) {
int i = 0;
b.append('{');
template for (constexpr auto dm : std::meta::nonstatic_data_members_of(^^Z)) {
[:expand(std::meta::nonstatic_data_members_of(^^Z, std::meta::access_context::unchecked())):] >> [&]<auto dm>() {
if (i != 0)
b.append(',');
constexpr auto key = consteval_to_quoted_escaped(std::meta::identifier_of(dm));
constexpr auto key = std::define_static_string(consteval_to_quoted_escaped(std::meta::identifier_of(dm)));
b.append_raw(key);
b.append(':');
atom(b, z.[:dm:]);
i++;
}
};
b.append('}');
}
@@ -42,10 +42,41 @@ namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
namespace builder {
static SIMDJSON_CONSTEXPR_LAMBDA std::array<uint8_t, 256> json_quotable_character = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
/**
A possible SWAR implementation of has_json_escapable_byte. It is not used because
it is slower than the current implementation. It is kept here for reference (to show
that we tried it).
inline bool has_json_escapable_byte(uint64_t x) {
uint64_t is_ascii = 0x8080808080808080ULL & ~x;
uint64_t xor2 = x ^ 0x0202020202020202ULL;
uint64_t lt32_or_eq34 = xor2 - 0x2121212121212121ULL;
uint64_t sub92 = x ^ 0x5C5C5C5C5C5C5C5CULL;
uint64_t eq92 = (sub92 - 0x0101010101010101ULL);
return ((lt32_or_eq34 | eq92) & is_ascii) != 0;
}
**/
SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline bool
simple_needs_escaping(std::string_view v) {
for (char c : v) {
if ((uint8_t(c) < 32) | (c == '"') | (c == '\\')) {
// a table lookup is faster than a series of comparisons
if(json_quotable_character[static_cast<uint8_t>(c)]) {
return true;
}
}
@@ -110,18 +141,6 @@ simdjson_inline bool fast_needs_escaping(std::string_view view) {
}
#endif
static SIMDJSON_CONSTEXPR_LAMBDA std::array<uint8_t, 256> json_quotable_character = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
SIMDJSON_CONSTEXPR_LAMBDA inline size_t
find_next_json_quotable_character(const std::string_view view,
@@ -333,7 +352,25 @@ simdjson_inline size_t digit_count(number_type v) noexcept {
"We only support 8-bit, 16-bit, 32-bit and 64-bit numbers");
return fast_digit_count(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,
0x31, 0x32, 0x31, 0x33, 0x31, 0x34, 0x31, 0x35, 0x31, 0x36, 0x31, 0x37,
0x31, 0x38, 0x31, 0x39, 0x32, 0x30, 0x32, 0x31, 0x32, 0x32, 0x32, 0x33,
0x32, 0x34, 0x32, 0x35, 0x32, 0x36, 0x32, 0x37, 0x32, 0x38, 0x32, 0x39,
0x33, 0x30, 0x33, 0x31, 0x33, 0x32, 0x33, 0x33, 0x33, 0x34, 0x33, 0x35,
0x33, 0x36, 0x33, 0x37, 0x33, 0x38, 0x33, 0x39, 0x34, 0x30, 0x34, 0x31,
0x34, 0x32, 0x34, 0x33, 0x34, 0x34, 0x34, 0x35, 0x34, 0x36, 0x34, 0x37,
0x34, 0x38, 0x34, 0x39, 0x35, 0x30, 0x35, 0x31, 0x35, 0x32, 0x35, 0x33,
0x35, 0x34, 0x35, 0x35, 0x35, 0x36, 0x35, 0x37, 0x35, 0x38, 0x35, 0x39,
0x36, 0x30, 0x36, 0x31, 0x36, 0x32, 0x36, 0x33, 0x36, 0x34, 0x36, 0x35,
0x36, 0x36, 0x36, 0x37, 0x36, 0x38, 0x36, 0x39, 0x37, 0x30, 0x37, 0x31,
0x37, 0x32, 0x37, 0x33, 0x37, 0x34, 0x37, 0x35, 0x37, 0x36, 0x37, 0x37,
0x37, 0x38, 0x37, 0x39, 0x38, 0x30, 0x38, 0x31, 0x38, 0x32, 0x38, 0x33,
0x38, 0x34, 0x38, 0x35, 0x38, 0x36, 0x38, 0x37, 0x38, 0x38, 0x38, 0x39,
0x39, 0x30, 0x39, 0x31, 0x39, 0x32, 0x39, 0x33, 0x39, 0x34, 0x39, 0x35,
0x39, 0x36, 0x39, 0x37, 0x39, 0x38, 0x39, 0x39,
};
} // namespace internal
template <typename number_type, typename>
@@ -367,8 +404,12 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
unsigned_type pv = static_cast<unsigned_type>(v);
size_t dc = internal::digit_count(pv);
char *write_pointer = buffer.get() + position + dc - 1;
// optimization opportunity: if v is large, we can do better.
while (pv >= 10) {
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;
}
@@ -381,14 +422,21 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
if (capacity_check(max_number_size)) {
using unsigned_type = typename std::make_unsigned<number_type>::type;
bool negative = v < 0;
unsigned_type pv = static_cast<unsigned_type>(negative ? -v : v);
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);
if (negative) {
buffer.get()[position++] = '-';
}
char *write_pointer = buffer.get() + position + dc - 1;
// optimization opportunity: if v is large, we can do better.
while (pv >= 10) {
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;
}
@@ -436,6 +484,11 @@ string_builder::escape_and_append_with_quotes(char input) noexcept {
}
}
simdjson_inline void string_builder::escape_and_append_with_quotes(const char* input) noexcept {
std::string_view cinput(input);
escape_and_append_with_quotes(cinput);
}
simdjson_inline void string_builder::append_raw(const char *c) noexcept {
size_t len = std::strlen(c);
append_raw(c, len);
@@ -488,6 +541,75 @@ simdjson_inline bool string_builder::validate_unicode() const noexcept {
return simdjson::validate_utf8(buffer.get(), position);
}
simdjson_inline void string_builder::start_object() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = '{';
}
}
simdjson_inline void string_builder::end_object() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = '}';
}
}
simdjson_inline void string_builder::start_array() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = '[';
}
}
simdjson_inline void string_builder::end_array() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = ']';
}
}
simdjson_inline void string_builder::append_comma() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = ',';
}
}
simdjson_inline void string_builder::append_colon() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = ':';
}
}
template<typename key_type, typename value_type>
simdjson_inline void string_builder::append_key_value(key_type key, value_type value) noexcept {
static_assert(
std::is_arithmetic<value_type>::value ||
std::is_same<value_type, char>::value ||
std::is_same<value_type, const char*>::value ||
std::is_convertible<value_type, std::string_view>::value ||
std::is_same<value_type, std::nullptr_t>::value,
"Unsupported value type");
static_assert(
std::is_same<key_type, const char*>::value ||
std::is_convertible<key_type, std::string_view>::value,
"Unsupported key type");
escape_and_append_with_quotes(key);
append_colon();
SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, std::nullptr_t>::value) {
append_null();
} else SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, char>::value) {
escape_and_append_with_quotes(value);
} else SIMDJSON_IF_CONSTEXPR(std::is_convertible<value_type, std::string_view>::value) {
escape_and_append_with_quotes(value);
} else SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, const char*>::value) {
escape_and_append_with_quotes(value);
} else {
append(value);
}
}
} // namespace builder
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
@@ -68,12 +68,56 @@ public:
* There is no UTF-8 validation.
*/
simdjson_inline void escape_and_append_with_quotes(char input) noexcept;
/**
* Append the character surrounded by double quotes, after escaping it.
* There is no UTF-8 validation.
*/
simdjson_inline void escape_and_append_with_quotes(const char* input) noexcept;
/**
* Append the C string directly, without escaping.
* There is no UTF-8 validation.
*/
simdjson_inline void append_raw(const char *c) noexcept;
/**
* Append "{" to the buffer.
*/
simdjson_inline void start_object() noexcept;
/**
* Append "}" to the buffer.
*/
simdjson_inline void end_object() noexcept;
/**
* Append "[" to the buffer.
*/
simdjson_inline void start_array() noexcept;
/**
* Append "]" to the buffer.
*/
simdjson_inline void end_array() noexcept;
/**
* Append "," to the buffer.
*/
simdjson_inline void append_comma() noexcept;
/**
* Append ":" to the buffer.
*/
simdjson_inline void append_colon() noexcept;
/**
* Append a key-value pair to the buffer.
* The key is escaped and surrounded by double quotes.
* The value is escaped if it is a string.
*/
template<typename key_type, typename value_type>
simdjson_inline void append_key_value(key_type key, value_type value) noexcept;
/**
* Append the std::string_view directly, without escaping.
* There is no UTF-8 validation.
@@ -156,7 +200,6 @@ private:
*/
simdjson_inline void set_valid(bool valid) noexcept;
std::unique_ptr<char[]> buffer{};
size_t position{0};
size_t capacity{0};
@@ -12,6 +12,7 @@
#include <limits>
#if SIMDJSON_STATIC_REFLECTION
#include <experimental/meta>
// #include <static_reflection> // for std::define_static_string - header not available yet
#endif
namespace simdjson {
@@ -63,6 +64,7 @@ error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
// String deserialization
//////////////////////////////
// just a character!
error_code tag_invoke(deserialize_tag, auto &val, char &out) noexcept {
std::string_view x;
SIMDJSON_TRY(val.get_string().get(x));
@@ -73,14 +75,17 @@ error_code tag_invoke(deserialize_tag, auto &val, char &out) noexcept {
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::string &out) noexcept {
std::string_view x;
SIMDJSON_TRY(val.get_string().get(x));
out.assign(x.data(), x.size());
// any string-like type (can be constructed from std::string_view)
template <concepts::constructible_from_string_view T, typename ValT>
requires(!require_custom_serialization<T>)
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(std::is_nothrow_constructible_v<T, std::string_view>) {
std::string_view str;
SIMDJSON_TRY(val.get_string().get(str));
out = T{str};
return SUCCESS;
}
/**
* STL containers have several constructors including one that takes a single
* size argument. Thus, some compilers (Visual Studio) will not be able to
@@ -131,6 +136,77 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
}
/**
* We want to support std::map and std::unordered_map but only for
* string-keyed types.
*/
template <concepts::string_view_keyed_map T, typename ValT>
requires(!require_custom_serialization<T>)
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
using value_type = typename std::remove_cvref_t<T>::mapped_type;
static_assert(
deserializable<value_type, ValT>,
"The specified value type inside the container must itself be deserializable");
static_assert(
std::is_default_constructible_v<value_type>,
"The specified value type inside the container must default constructible.");
SIMDJSON_IMPLEMENTATION::ondemand::object obj;
SIMDJSON_TRY(val.get_object().get(obj));
for (auto field : obj) {
std::string_view key;
SIMDJSON_TRY(field.unescaped_key().get(key));
value_type this_value;
SIMDJSON_TRY(field.value().get<value_type>().get(this_value));
[[maybe_unused]] std::pair<typename T::iterator, bool> result = out.emplace(key, this_value);
// unclear what to do if the key already exists
// if (result.second == false) {
// // key already exists
// }
}
(void)out;
return SUCCESS;
}
template <concepts::string_view_keyed_map T>
error_code tag_invoke(deserialize_tag, SIMDJSON_IMPLEMENTATION::ondemand::object &obj, T &out) noexcept {
using value_type = typename std::remove_cvref_t<T>::mapped_type;
out.clear();
for (auto field : obj) {
std::string_view key;
SIMDJSON_TRY(field.unescaped_key().get(key));
SIMDJSON_IMPLEMENTATION::ondemand::value value_obj;
SIMDJSON_TRY(field.value().get(value_obj));
value_type this_value;
SIMDJSON_TRY(value_obj.get(this_value));
out.emplace(typename T::key_type(key), std::move(this_value));
}
return SUCCESS;
}
template <concepts::string_view_keyed_map T>
error_code tag_invoke(deserialize_tag, SIMDJSON_IMPLEMENTATION::ondemand::value &val, T &out) noexcept {
SIMDJSON_IMPLEMENTATION::ondemand::object obj;
SIMDJSON_TRY(val.get_object().get(obj));
return simdjson::deserialize(obj, out);
}
template <concepts::string_view_keyed_map T>
error_code tag_invoke(deserialize_tag, SIMDJSON_IMPLEMENTATION::ondemand::document &doc, T &out) noexcept {
SIMDJSON_IMPLEMENTATION::ondemand::object obj;
SIMDJSON_TRY(doc.get_object().get(obj));
return simdjson::deserialize(obj, out);
}
template <concepts::string_view_keyed_map T>
error_code tag_invoke(deserialize_tag, SIMDJSON_IMPLEMENTATION::ondemand::document_reference &doc, T &out) noexcept {
SIMDJSON_IMPLEMENTATION::ondemand::object obj;
SIMDJSON_TRY(doc.get_object().get(obj));
return simdjson::deserialize(obj, out);
}
/**
* This CPO (Customization Point Object) will help deserialize into
@@ -202,46 +278,55 @@ constexpr bool user_defined_type = (std::is_class_v<T>
// workaround from
// https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p2996r3.html#back-and-forth
// https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p2996r10.html#back-and-forth
// for missing expansion statements
/*namespace __impl {
template <auto... vals> struct replicator_type {
template <typename F> constexpr void operator>>(F body) const {
(body.template operator()<vals>(), ...);
}
};
namespace __impl {
template<auto... vals>
struct replicator_type {
template<typename F>
constexpr void operator>>(F body) const {
(body.template operator()<vals>(), ...);
}
};
template <auto... vals> replicator_type<vals...> replicator = {};
} // namespace __impl
template<auto... vals>
replicator_type<vals...> replicator = {};
}
template <typename R> consteval auto expand(R range) {
template<typename R>
consteval auto expand(R range) {
std::vector<std::meta::info> args;
for (auto r : range) {
args.push_back(std::meta::reflect_value(r));
args.push_back(reflect_value(r));
}
return substitute(^__impl::replicator, args);
}*/
return substitute(^^__impl::replicator, args);
}
// end of workaround
template <typename T, typename ValT>
requires(user_defined_type<T> && std::is_class_v<T>)
constexpr error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept {
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept {
SIMDJSON_IMPLEMENTATION::ondemand::object obj;
if constexpr (std::is_same_v<std::remove_cvref_t<ValT>, SIMDJSON_IMPLEMENTATION::ondemand::object>) {
obj = val;
} else {
SIMDJSON_TRY(val.get_object().get(obj));
}
template for (constexpr auto mem : std::meta::nonstatic_data_members_of(^^T)) {
if(std::meta::is_const(mem)) {
continue;
error_code e = simdjson::SUCCESS;
[:expand(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked())):] >> [&]<auto mem>() {
if constexpr (!std::meta::is_const(mem) && std::meta::is_public(mem)) {
constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem));
static_assert(
deserializable<decltype(out.[:mem:]), SIMDJSON_IMPLEMENTATION::ondemand::object>,
"The specified type inside the class must itself be deserializable");
// as long we are succesful or the field is not found, we continue
if(e == simdjson::SUCCESS || e == simdjson::NO_SUCH_FIELD) {
obj[key].get(out.[:mem:]);
}
}
std::string_view key = std::string_view(std::meta::identifier_of(mem));
if(error_code e = obj[key].get(out.[:mem:]); e != simdjson::SUCCESS && e != simdjson::NO_SUCH_FIELD) {
return e;
}
}
return simdjson::SUCCESS;
};
return e;
}
template <typename simdjson_value, typename T>
requires(user_defined_type<std::remove_cvref_t<T>>)
+21 -15
View File
@@ -72,22 +72,28 @@ public:
noexcept
#endif
{
#if SIMDJSON_SUPPORTS_DESERIALIZATION
if constexpr (custom_deserializable<T, value>) {
#if SIMDJSON_SUPPORTS_DESERIALIZATION
if constexpr (custom_deserializable<T, value>) {
return deserialize(*this, out);
} else {
#endif // SIMDJSON_SUPPORTS_DESERIALIZATION
// Unless the simdjson library or the user provides an inline implementation, calling this method should
// immediately fail.
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
"The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), "
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template."
" You may also add support for custom types, see our documentation.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
#if SIMDJSON_SUPPORTS_DESERIALIZATION
}
} else {
static_assert(!sizeof(T), "The get<T> method with type T is not implemented by the simdjson library. "
"And you do not seem to have added support for it. Indeed, we have that "
"simdjson::custom_deserializable<T> is false and the type T is not a default type "
"such as ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, double, or bool.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
}
#else // SIMDJSON_SUPPORTS_DESERIALIZATION
// Unless the simdjson library or the user provides an inline implementation, calling this method should
// immediately fail.
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
"The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), "
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template."
" You may also add support for custom types, see our documentation.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
#endif
}
@@ -778,19 +778,23 @@ simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::get_root_bool(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("bool");
uint8_t tmpbuf[5+1+1]; // +1 for null termination
tmpbuf[5+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 5+1)) { return incorrect_type_error("Not a boolean"); }
auto result = parse_bool(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("bool");
}
return result;
// We have a boolean if we have either "true" or "false" and the next character is either
// a structural character or whitespace. We also check that the length is correct:
// "true" and "false" are 4 and 5 characters long, respectively.
bool value_true = (max_len >= 4 && !atomparsing::str4ncmp(json, "true") &&
(max_len == 4 || jsoncharutils::is_structural_or_whitespace(json[4])));
bool value_false = (max_len >= 5 && !atomparsing::str4ncmp(json, "false") &&
(max_len == 5 || jsoncharutils::is_structural_or_whitespace(json[5])));
if(value_true == false && value_false == false) { return incorrect_type_error("Not a boolean"); }
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("bool");
return value_true;
}
simdjson_inline simdjson_result<bool> value_iterator::is_root_null(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("null");
@@ -125,7 +125,8 @@ public:
*
* @returns Whether the object had any fields (returns false for empty).
* @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent*
* array or object is incomplete).
* array or object is incomplete). An INCOMPLETE_ARRAY_OR_OBJECT is an unrecoverable error that
* invalidates the document.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> started_object() noexcept;
/**
@@ -135,7 +136,8 @@ public:
*
* @returns Whether the object had any fields (returns false for empty).
* @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent*
* array or object is incomplete).
* array or object is incomplete). An INCOMPLETE_ARRAY_OR_OBJECT is an unrecoverable error that
* invalidates the document.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> started_root_object() noexcept;
@@ -257,7 +259,8 @@ public:
*
* @returns Whether the array had any elements (returns false for empty).
* @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent*
* array or object is incomplete).
* array or object is incomplete). An INCOMPLETE_ARRAY_OR_OBJECT is an unrecoverable error that
* invalidates the document.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> started_array() noexcept;
/**
@@ -267,7 +270,8 @@ public:
*
* @returns Whether the array had any elements (returns false for empty).
* @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent*
* array or object is incomplete).
* array or object is incomplete). An INCOMPLETE_ARRAY_OR_OBJECT is an unrecoverable error that
* invalidates the document.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> started_root_array() noexcept;
+1 -1
View File
@@ -2,7 +2,7 @@
#define SIMDJSON_JSONPATHUTIL_H
#include <string>
#include <string_view>
#include "simdjson/common_defs.h"
namespace simdjson {
/**
+2 -2
View File
@@ -101,12 +101,12 @@ struct padded_string final {
/**
* Create a std::string_view with the same content.
*/
operator std::string_view() const;
operator std::string_view() const simdjson_lifetime_bound;
/**
* Create a padded_string_view with the same content.
*/
operator padded_string_view() const noexcept;
operator padded_string_view() const noexcept simdjson_lifetime_bound;
/**
* Load this padded string from a file.
+1
View File
@@ -191,6 +191,7 @@ using std::size_t;
#if defined(NDEBUG) || defined(__OPTIMIZE__) || (defined(_MSC_VER) && !defined(_DEBUG))
// If NDEBUG is set, or __OPTIMIZE__ is set, or we are under MSVC in release mode,
// then do away with asserts and use __assume.
// We still recommend that our users set NDEBUG in release mode.
#if SIMDJSON_VISUAL_STUDIO
#define SIMDJSON_UNREACHABLE() __assume(0)
#define SIMDJSON_ASSUME(COND) __assume(COND)
+3 -3
View File
@@ -4,7 +4,7 @@
#define SIMDJSON_SIMDJSON_VERSION_H
/** The version of simdjson being used (major.minor.revision) */
#define SIMDJSON_VERSION "3.12.2"
#define SIMDJSON_VERSION "3.13.0"
namespace simdjson {
enum {
@@ -15,11 +15,11 @@ enum {
/**
* The minor version (major.MINOR.revision) of simdjson being used.
*/
SIMDJSON_VERSION_MINOR = 12,
SIMDJSON_VERSION_MINOR = 13,
/**
* The revision (major.minor.REVISION) of simdjson being used.
*/
SIMDJSON_VERSION_REVISION = 2
SIMDJSON_VERSION_REVISION = 0
};
} // namespace simdjson
+1
View File
@@ -1,6 +1,7 @@
# The bulk of our data files is found in https://github.com/simdjson/simdjson-data
set(EXAMPLE_JSON ${CMAKE_CURRENT_BINARY_DIR}/twitter.json PARENT_SCOPE)
set(EXAMPLE_NDJSON ${CMAKE_CURRENT_BINARY_DIR}/amazon_cellphones.ndjson PARENT_SCOPE)
set(BENCH_CITM_JSON ${CMAKE_CURRENT_BINARY_DIR}/citm_catalog.json PARENT_SCOPE)
# Copy static files to the build dir so they live alongside the generated ones
file(GLOB_RECURSE example_files RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.json *.ndjson)
File diff suppressed because it is too large Load Diff
+42
View File
@@ -0,0 +1,42 @@
# Stage 1. Check out LLVM source code and run the build.
FROM debian:12 AS builder
# First, Update the apt's source list and include the sources of the packages.
RUN grep deb /etc/apt/sources.list | \
sed 's/^deb/deb-src /g' >> /etc/apt/sources.list
# Install compiler, python and subversion.
RUN apt-get update && \
apt-get install -y --no-install-recommends ca-certificates gnupg \
build-essential cmake make python3 zlib1g wget subversion unzip ninja-build git linux-perf && \
rm -rf /var/lib/apt/lists/*
RUN git clone --depth=1 --branch p2996 https://github.com/bloomberg/clang-p2996.git /tmp/clang-source
RUN cmake -S /tmp/clang-source/llvm -B /tmp/clang-source/build-llvm -DCMAKE_BUILD_TYPE=Release \
-DLLVM_ENABLE_ASSERTIONS=ON \
-DLLVM_UNREACHABLE_OPTIMIZE=ON \
-DLLVM_ENABLE_RUNTIMES="libcxx;libcxxabi;libunwind" \
-DCLANG_DEFAULT_CXX_STDLIB=libc++ \
-DLLVM_ENABLE_PROJECTS=clang -G Ninja
RUN cmake --build /tmp/clang-source/build-llvm -j
RUN cmake --install /tmp/clang-source/build-llvm --prefix /tmp/clang-install
# Stage 2. Produce a minimal release image with build results.
FROM debian:12
ARG USER_NAME
ARG USER_ID
ARG GROUP_ID
LABEL maintainer "LLVM Developers"
# Install packages for minimal useful image.
RUN apt-get update && \
apt-get install -y --no-install-recommends build-essential ca-certificates rust-all libcurl4-openssl-dev cmake make wget python3 python3-dev sudo curl ninja-build vim git binutils linux-perf && \
rm -rf /var/lib/apt/lists/*
# Copy build results of stage 1 to /usr/local.
COPY --from=builder /tmp/clang-install/ /usr/local/
RUN P=`/usr/local/bin/clang++ -v 2>&1 | grep Target | cut -d' ' -f2-`; echo /usr/local/lib/$P > /etc/ld.so.conf.d/$P.conf
RUN ldconfig
RUN addgroup --gid $GROUP_ID user; exit 0
RUN adduser --disabled-password --gecos '' --uid $USER_ID --gid $GROUP_ID $USER_NAME; exit 0
RUN echo "$USER_NAME:$USER_NAME" | chpasswd && adduser $USER_NAME sudo
RUN echo '----->'
RUN echo 'root:Docker!' | chpasswd
ENV TERM xterm-256color
USER $USER_NAME
+79
View File
@@ -0,0 +1,79 @@
# Building the experimental LLVM compiler for static reflection
The simdjson has experimental support for static reflection, when the compiler supports it.
We focus on the [latest p2996 paper](https://isocpp.org/files/papers/P2996R10.html) that is targeting C++26.
## Current status
There are 2 versions of compiler that aim to support the C++26 reflection paper ([p2996](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p2996r10.html)).
1. [Clang-p2996 llvm branch](https://github.com/bloomberg/clang-p2996/tree/p2996) that open-source and available in the [Compiler Explorer](https://godbolt.org/z/eoEej3E6j).
2. EDG reflection branch that is only publicly available in the [Compiler Explorer](https://godbolt.org).
For now, we will resort to #1, since it is open-source.
We are excited to hear that this reflection proposal seems to be on-track for C++26. [As per Herb Sutter](https://herbsutter.com/2024/03/22/trip-report-winter-iso-c-standards-meeting-tokyo-japan/).
## Instructions for building simdjson for static reflection (using docker)
We are assuming that you are running Linux or macOS. We recommend that Windows users rely on WSL.
Follow the following two steps:
1. Clone the project
```bash
git clone https://github.com/simdjson/simdjson.git
cd simdjson
```
As of March 2025, support for static reflection is available only in the `json_builder_init` branch, so you should switch:
```bash
git checkout -B json_builder_init
```
2. Make sure that you have [docker installed and running](https://docs.docker.com/engine/install/) on your system. Most Linux distributions support docker though some (like RedHat) have the equivalent (Podman). Users of Apple systems may want to [consider OrbStack](https://orbstack.dev). You do not need to familiar with docker, you just need to make sure that you are have it running.
3. While inside the `simdjson` repository, run the following bash script:
```bash
bash ./p2996/run_docker.sh bash
```
This will enter a bash shell with access to the repo directory. Note that this will take some time when running it for the first time, since the specific container image has to be built.
This step builds and executes a docker container defined by our Dockerfile which provides the necessary environment.
Importantly, we build the experimental LLVM compiler based on the current state of the
`p2996` branch of the `https://github.com/bloomberg/clang-p2996.git` repository. It is possible that the build could fail. Furthermore, you may need to refresh the build from time to time. We provide a script which allows you to delete the docker image you have built (`bash ./p2996/remove_docker.sh`) so you can start anew.
4. While inside the docker shell, configure the build system with cmake:
```bash
CXX=clang++ cmake -B buildreflect -D SIMDJSON_STATIC_REFLECTION=ON -DSIMDJSON_DEVELOPER_MODE=ON
```
This only needs to be done once.
5. Build the code...
```bash
cmake --build buildreflect --target benchmark_serialization_citm_catalog benchmark_serialization_twitter
```
6. Run the tests...
```bash
ctest --test-dir buildreflect --output-on-failure
```
7. Run the benchmarks.
```bash
./buildreflect/benchmark/static_reflect/citm_catalog_benchmark/benchmark_serialization_citm_catalog
./buildreflect/benchmark/static_reflect/twitter_benchmark/benchmark_serialization_twitter
```
You can modify the source code with your favorite editor and run again steps 5 (Build the code) and 6 (Run the tests) and 7 (Run the benchmark). Importantly, you should remain in the docker shell.
You can create a new docker shell at any time by running step 3 (bash script).
+8
View File
@@ -0,0 +1,8 @@
#!/usr/bin/env bash
tuser=$(echo $USER | tr -dc 'a-z')
container_name=${CONTAINER_NAME:-"debian12-clang-p2996-programming_station-for-$tuser-simdjson"}
echo $container_name
echo " Removing the container "
docker ps -a | awk '{ print $1,$2 }' | grep $container_name| awk '{print $1 }' | xargs -I {} docker stop -t 1 {} | xargs docker rm
# removing image
docker image rm $container_name
+30
View File
@@ -0,0 +1,30 @@
#!/usr/bin/env bash
if [ -z "$1" ]
then
echo "The run-docker-station script takes a command as a argument. E.g., try ./run-docker-station 'ls' "
exit 1
fi
set -o noglob
SCRIPTPATH="$( cd "$(dirname "$0")" ; pwd -P )"
COMMAND=$@
tuser=$(echo $USER | tr -dc 'a-z')
container_name=${CONTAINER_NAME:-"debian12-clang-p2996-programming_station-for-$tuser-simdjson"}
command -v docker >/dev/null 2>&1 || { echo >&2 "Please install docker. E.g., go to https://www.docker.com/products/docker-desktop Type 'docker' to diagnose the problem."; exit 1; }
docker info >/dev/null 2>&1 || { echo >&2 "Docker server is not running? type 'docker info'."; exit 1; }
SSHPARAM=""
[ -e "${SSH_AUTH_SOCK}" ] && SSHPARAM="--volume /run/host-services/ssh-auth.sock:/run/host-services/ssh-auth.sock --env SSH_AUTH_SOCK=/run/host-services/ssh-auth.sock"
[ -e "${HOME}/.ssh" ] && SSHPARAM="--volume ${HOME}/.ssh:/home/$tuser/.ssh:ro"
docker image inspect $container_name >/dev/null 2>&1 || ( echo "instantiating the container" ; docker build --no-cache -t $container_name -f $SCRIPTPATH/Dockerfile --build-arg USER_NAME="$tuser" --build-arg USER_ID=$(id -u) --build-arg GROUP_ID=$(id -g) . )
if [ -t 0 ]; then DOCKER_ARGS=-it; fi
docker run --rm $DOCKER_ARGS -h $container_name $SSHPARAM -v $(pwd):$(pwd):Z --privileged -w $(pwd) $container_name sh -c "$COMMAND"
+1 -1
View File
@@ -52,7 +52,7 @@ if (Python3_Interpreter_FOUND AND (NOT WIN32))
# file regeneration in the source directory without the user's knowledge.
# OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/simdjson.cpp ${CMAKE_CURRENT_SOURCE_DIR}/simdjson.h ${CMAKE_CURRENT_SOURCE_DIR}/amalgamate_demo.cpp ${CMAKE_CURRENT_SOURCE_DIR}/README.md
COMMAND ${CMAKE_COMMAND} -E copy ${SINGLEHEADER_FILES} ${CMAKE_CURRENT_SOURCE_DIR}
DEPENDS ${SINGLEHEADER_FILES}
POST_BUILD ${SINGLEHEADER_FILES}
)
endif()
+214 -1893
View File
File diff suppressed because it is too large Load Diff
+1159 -9721
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.
+88 -32
View File
@@ -121,10 +121,78 @@ simdjson_inline void validate_utf8_character() {
idx += 4;
}
static const uint8_t CHAR_TYPE_SPACE = 1 << 0;
static const uint8_t CHAR_TYPE_OPERATOR = 1 << 1;
static const uint8_t CHAR_TYPE_ESC_ASCII = 1 << 2;
static const uint8_t CHAR_TYPE_NON_ASCII = 1 << 3;
const uint8_t char_table[256] = {
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
0x04, 0x05, 0x05, 0x04, 0x04, 0x05, 0x04, 0x04,
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02, 0x04, 0x02, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00,
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08
};
simdjson_inline bool char_is_type(uint8_t c, uint8_t type) {
return (char_table[c] & type);
}
simdjson_inline bool char_is_space(uint8_t c) {
return char_is_type(c, CHAR_TYPE_SPACE);
}
simdjson_inline bool char_is_operator(uint8_t c) {
return char_is_type(c, CHAR_TYPE_OPERATOR);
}
simdjson_inline bool char_is_space_or_operator(uint8_t c) {
return char_is_type(c, CHAR_TYPE_SPACE | CHAR_TYPE_OPERATOR);
}
simdjson_inline bool char_is_ascii_stop(uint8_t c) {
return char_is_type(c, CHAR_TYPE_ESC_ASCII | CHAR_TYPE_NON_ASCII);
}
// Returns true if the string is unclosed.
simdjson_inline bool validate_string() {
idx++; // skip first quote
while (idx < len && buf[idx] != '"') {
while (idx < len) {
do {
if (char_is_ascii_stop(buf[idx])) { break; }
idx++;
} while (idx < len);
if (idx >= len) { return true; }
if (buf[idx] == '"') {
return false;
}
if (buf[idx] == '\\') {
idx += 2;
} else if (simdjson_unlikely(buf[idx] & 0x80)) {
@@ -138,43 +206,31 @@ simdjson_inline bool validate_string() {
return false;
}
simdjson_inline bool is_whitespace_or_operator(uint8_t c) {
switch (c) {
case '{': case '}': case '[': case ']': case ',': case ':':
case ' ': case '\r': case '\n': case '\t':
return true;
default:
return false;
}
}
//
// Parse the entire input in STEP_SIZE-byte chunks.
//
simdjson_inline error_code scan() {
bool unclosed_string = false;
for (;idx<len;idx++) {
switch (buf[idx]) {
// String
case '"':
add_structural();
unclosed_string |= validate_string();
break;
// Operator
case '{': case '}': case '[': case ']': case ',': case ':':
add_structural();
break;
// Whitespace
case ' ': case '\r': case '\n': case '\t':
break;
// Primitive or invalid character (invalid characters will be checked in stage 2)
default:
// Anything else, add the structural and go until we find the next one
add_structural();
while (idx+1<len && !is_whitespace_or_operator(buf[idx+1])) {
idx++;
};
break;
do {
if (!char_is_space(buf[idx])) { break; }
idx++;
} while (idx < len);
if (idx >= len) { break; }
// String
if (buf[idx] == '"') {
add_structural();
unclosed_string |= validate_string();
// Operator
} else if (char_is_operator(buf[idx])) {
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
add_structural();
while (idx+1<len && !char_is_space_or_operator(buf[idx+1])) {
idx++;
};
}
}
// We pad beyond.
+2 -2
View File
@@ -11,7 +11,7 @@ namespace internal {
{ SUCCESS, "SUCCESS: No error" },
{ CAPACITY, "CAPACITY: This parser can't support a document that big" },
{ MEMALLOC, "MEMALLOC: Error allocating memory, we're most likely out of memory" },
{ TAPE_ERROR, "TAPE_ERROR: The JSON document has an improper structure: missing or superfluous commas, braces, missing keys, etc." },
{ TAPE_ERROR, "TAPE_ERROR: The JSON document has an improper structure: missing or superfluous commas, braces, missing keys, etc. This is a fatal and unrecoverable error." },
{ DEPTH_ERROR, "DEPTH_ERROR: The JSON document was too deep (too many nested objects and arrays)" },
{ STRING_ERROR, "STRING_ERROR: Problem while parsing a string" },
{ T_ATOM_ERROR, "T_ATOM_ERROR: Problem while parsing an atom starting with the letter 't'" },
@@ -36,7 +36,7 @@ namespace internal {
{ PARSER_IN_USE, "PARSER_IN_USE: Cannot parse a new document while a document is still in use." },
{ OUT_OF_ORDER_ITERATION, "OUT_OF_ORDER_ITERATION: Objects and arrays can only be iterated when they are first encountered." },
{ INSUFFICIENT_PADDING, "INSUFFICIENT_PADDING: simdjson requires the input JSON string to have at least SIMDJSON_PADDING extra bytes allocated, beyond the string's length. Consider using the simdjson::padded_string class if needed." },
{ INCOMPLETE_ARRAY_OR_OBJECT, "INCOMPLETE_ARRAY_OR_OBJECT: JSON document ended early in the middle of an object or array." },
{ INCOMPLETE_ARRAY_OR_OBJECT, "INCOMPLETE_ARRAY_OR_OBJECT: JSON document ended early in the middle of an object or array. This is a fatal and unrecoverable error." },
{ SCALAR_DOCUMENT_AS_VALUE, "SCALAR_DOCUMENT_AS_VALUE: A JSON document made of a scalar (number, Boolean, null or string) is treated as a value. Use get_bool(), get_double(), etc. on the document instead. "},
{ OUT_OF_BOUNDS, "OUT_OF_BOUNDS: Attempt to access location outside of document."},
{ TRAILING_CONTENT, "TRAILING_CONTENT: Unexpected trailing content in the JSON input."}
+1 -1
View File
@@ -13,7 +13,7 @@ add_cpp_test(minify_tests LABELS other acceptance per_implementation)
add_cpp_test(padded_string_tests LABELS other acceptance )
add_cpp_test(prettify_tests LABELS other acceptance per_implementation)
if(MSVC AND BUILD_SHARED_LIBS)
if(WIN32 AND BUILD_SHARED_LIBS)
# Copy the simdjson dll into the tests directory
add_custom_command(TARGET unicode_tests POST_BUILD # Adds a post-build event
COMMAND ${CMAKE_COMMAND} -E copy_if_different # which executes "cmake -E copy_if_different..."
+112 -2
View File
@@ -107,6 +107,65 @@ namespace builder_tests {
TEST_SUCCEED();
}
bool various_integers() {
TEST_START();
std::vector<std::pair<int64_t, std::string_view>> test_cases = {
{0, "0"},
{1, "1"},
{-1, "-1"},
{42, "42"},
{-42, "-42"},
{100, "100"},
{-100, "-100"},
{999, "999"},
{-999, "-999"},
{2147483647, "2147483647"}, // max 32-bit integer
{-2147483648, "-2147483648"}, // min 32-bit integer
{4294967296ULL, "4294967296"}, // 2^32
{-4294967296LL, "-4294967296"},
{10000000000LL, "10000000000"}, // 10 billion
{-10000000000LL, "-10000000000"},
{9223372036854775807LL, "9223372036854775807"}, // max 64-bit integer
{-9223372036854775807LL-1, "-9223372036854775808"}, // min 64-bit integer
{1234567890123LL, "1234567890123"},
{-1234567890123LL, "-1234567890123"},
};
for (const auto& [value, expected] : test_cases) {
simdjson::builder::string_builder sb;
sb.append(value);
std::string_view p;
auto result = sb.view().get(p);
ASSERT_SUCCESS(result);
ASSERT_EQUAL(p, expected);
}
TEST_SUCCEED();
}
bool various_unsigned_integers() {
TEST_START();
std::vector<std::pair<uint64_t, std::string_view>> test_cases = {
{0, "0"},
{1, "1"},
{42, "42"},
{100, "100"},
{999, "999"},
{2147483647, "2147483647"}, // max 32-bit integer
{4294967296ULL, "4294967296"}, // 2^32
{10000000000LL, "10000000000"}, // 10 billion
{9223372036854775807LL, "9223372036854775807"}, // max 64-bit integer
{1234567890123LL, "1234567890123"},
};
for (const auto& [value, expected] : test_cases) {
simdjson::builder::string_builder sb;
sb.append(value);
std::string_view p;
auto result = sb.view().get(p);
ASSERT_SUCCESS(result);
ASSERT_EQUAL(p, expected);
}
TEST_SUCCEED();
}
bool append_raw() {
TEST_START();
simdjson::builder::string_builder sb;
@@ -158,7 +217,7 @@ namespace builder_tests {
TEST_SUCCEED();
}
void serialize_car(const Car& car, simdjson::builder::string_builder& builder) {
void serialize_car_long(const Car& car, simdjson::builder::string_builder& builder) {
// start of JSON
builder.append_raw("{");
@@ -198,6 +257,55 @@ namespace builder_tests {
builder.append_raw("}");
}
bool car_test_long() {
TEST_START();
simdjson::builder::string_builder sb;
Car c = {"Toyota", "Corolla", 2017, {30.0,30.2,30.513,30.79}};
serialize_car_long(c, sb);
std::string_view p;
auto result = sb.view().get(p);
ASSERT_SUCCESS(result);
ASSERT_EQUAL(p, "{\"make\":\"Toyota\",\"model\":\"Corolla\",\"year\":2017,\"tire_pressure\":[30.0,30.2,30.513,30.79]}");
TEST_SUCCEED();
}
void serialize_car(const Car& car, simdjson::builder::string_builder& builder) {
// start of JSON
builder.start_object();
// "make"
builder.append_key_value("make", car.make);
builder.append_comma();
// "model"
builder.append_key_value("model", car.model);
builder.append_comma();
// "year"
builder.append_key_value("year", car.year);
builder.append_comma();
// "tire_pressure"
builder.escape_and_append_with_quotes("tire_pressure");
builder.append_colon();
builder.start_array();
// vector tire_pressure
for (size_t i = 0; i < car.tire_pressure.size(); ++i) {
builder.append(car.tire_pressure[i]);
if (i < car.tire_pressure.size() - 1) {
builder.append_comma();
}
}
// end of array
builder.end_array();
// end of object
builder.end_object();
}
bool car_test() {
TEST_START();
simdjson::builder::string_builder sb;
@@ -210,9 +318,11 @@ namespace builder_tests {
TEST_SUCCEED();
}
bool run() {
return
various_integers() &&
various_unsigned_integers() &&
car_test_long() &&
car_test() &&
#if SIMDJSON_EXCEPTIONS
string_convertion_except() &&
+19 -2
View File
@@ -117,8 +117,25 @@ if(NOT (MSVC AND MSVC_VERSION LESS 1920))
target_compile_definitions(readme_examples_will_fail_with_exceptions_off PRIVATE SIMDJSON_EXCEPTIONS=0)
endif()
#
# Test C++20 features
#
if(MSVC AND BUILD_SHARED_LIBS)
# Add the tests if we're on:
# 1. Visual Studio 2022 v17.6 or later
# 2. GCC v14.0.0 or later (GCC v13.0.0 cannot handle pipe oprator of lambda)
# 3. Clang v15.0.0 or later (certain version C++ headers occur error when compiling)
if(
(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")
)
message(STATUS "compiler id: ${CMAKE_CXX_COMPILER_ID} version: ${CMAKE_CXX_COMPILER_VERSION}")
add_cpp_test(ranges_test LABELS dom acceptance per_implementation)
set_target_properties(ranges_test PROPERTIES CXX_STANDARD 20 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF)
endif()
if(WIN32 AND BUILD_SHARED_LIBS)
add_custom_command(TARGET basictests POST_BUILD # Adds a post-build event
COMMAND ${CMAKE_COMMAND} -E copy_if_different # which executes "cmake -E copy_if_different..."
"$<TARGET_FILE:simdjson>" # <--this is in-file
@@ -139,4 +156,4 @@ if(NOT MSVC)
if(NOT ${REPORTED_SIMDJSON_VERSION} STREQUAL ${SIMDJSON_LIB_VERSION})
message(FATAL_ERROR "The library target does not have the proper version information." )
endif()
endif()
endif()
-8
View File
@@ -12,8 +12,6 @@ SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
#include "gason.h"
#include "json11.hpp"
#include "rapidjson/document.h"
#include "rapidjson/reader.h" // you have to check in the submodule
#include "rapidjson/stringbuffer.h"
@@ -122,10 +120,6 @@ int main(int argc, char *argv[]) {
return EXIT_SUCCESS;
}
bool rapid_correct = (d.Parse((const char *)buffer).HasParseError() == false);
std::string json11err;
bool dropbox_correct = ((json11::Json::parse(buffer, json11err).is_null()) ||
(!json11err.empty())) == false;
bool fastjson_correct = fastjson_parse(buffer);
JsonValue value;
JsonAllocator allocator;
@@ -174,8 +168,6 @@ int main(int argc, char *argv[]) {
rapid_correct_checkencoding ? "correct" : "invalid");
printf("sajson : %s \n",
sajson_correct ? "correct" : "invalid");
printf("dropbox : %s \n",
dropbox_correct ? "correct" : "invalid");
printf("fastjson : %s \n",
fastjson_correct ? "correct" : "invalid");
printf("gason : %s \n",
+31 -2
View File
@@ -399,7 +399,11 @@ namespace number_tests {
bool specific_tests() {
std::cout << __func__ << std::endl;
return basic_test_64bit("-1e-999", -0.0) &&
return
#if SIMDJSON_MINUS_ZERO_AS_FLOAT
basic_test_64bit("-0", -0.0) &&
#endif // SIMDJSON_MINUS_ZERO_AS_FLOAT
basic_test_64bit("-1e-999", -0.0) &&
basic_test_64bit("-2402844368454405395.2",-2402844368454405395.2) &&
basic_test_64bit("4503599627370496.5", 4503599627370496.5) &&
basic_test_64bit("4503599627475352.5", 4503599627475352.5) &&
@@ -458,6 +462,31 @@ namespace parse_api_tests {
return true;
}
#if SIMDJSON_EXCEPTIONS
bool issue_2375() {
TEST_START();
std::string jsonData =
R"eos({"asset":{"version":"1.0"},"geometricError":100,"root":{"boundingVolume":{"region":[10,0,10,10,0,109]},"geometricError":100,"refine":"ADD","children":[{"boundingVolume":{"region":[20,0,20,0,0,20]},"geometricError":70,"content":{"url":"city/tileset.json"}},{"transform":[4,1,0,0,-0.7,3.1,3.8,0,0.9,-3.7,3.21,0,12,-47,40,1],"boundingVolume":{"region":[-1.3,0.6,-1.39,0.6,0,20]},"geometricError":0,"content":{"url":"building.b3dm"}},{"transform":[0.9,0.2,0,0,-0.15,0.62,0.76,0,0.19,-0.74,0.64,0,12,-47,40,1],"viewerRequestVolume":{"region":[-1.3,0.6,-1.31,0.6,0,20]},"boundingVolume":{"region":[-1.31,0.6,-1.3,0.6,0,20]},"geometricError":0,"content":{"url":"points.pnts"}}]}})eos";
simdjson::dom::parser parser;
auto json = parser.parse(jsonData.data(), jsonData.size());
const simdjson::dom::element jsonElement = json.value();
const simdjson::dom::element rootElement = jsonElement["root"];
if (jsonElement["asset"]["gltfUpAxis"].is_string()) {
if (jsonElement["asset"]["gltfUpAxis"].get_string().value_unsafe() == std::string_view("Z")) {
printf("up\n");
}
}
if (rootElement.is_object()) {
printf("I can confirm that root is an object!\n");
return true;
}
printf("root is not an object!\n");
return false;
}
bool issue679() {
std::cout << "Running " << __func__ << std::endl;
auto input = "[1, 2, 3]"_padded;
@@ -742,6 +771,7 @@ namespace parse_api_tests {
parser_load_exception() &&
parser_load_many_exception() &&
issue679() &&
issue_2375() &&
#endif
true;
}
@@ -1245,7 +1275,6 @@ namespace dom_api_tests {
bool numeric_values_exception() {
std::cout << "Running " << __func__ << std::endl;
dom::parser parser;
ASSERT_EQUAL( uint64_t(parser.parse("0"_padded)), 0);
ASSERT_EQUAL( int64_t(parser.parse("0"_padded)), 0);
ASSERT_EQUAL( double(parser.parse("0"_padded)), 0);
+59
View File
@@ -0,0 +1,59 @@
#include <iostream>
#include <ranges>
#include <string_view>
#include "simdjson.h"
#include "test_macros.h"
#include "test_main.h"
using namespace simdjson;
namespace ranges_test {
bool printout() {
TEST_START();
auto cars_json = R"( [
{ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] },
{ "make": "Kia", "model": "Soul", "year": 2012, "tire_pressure": [ 30.1, 31.0, 28.6, 28.7 ] },
{ "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
] )"_padded;
dom::parser parser;
auto justmodel = [](auto car) { return car["model"]; };
for (auto car :
parser.parse(cars_json).get_array() | std::views::transform(justmodel)) {
std::cout << car << std::endl;
}
TEST_SUCCEED();
}
bool checkvalues() {
TEST_START();
auto cars_json = R"( [
{ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] },
{ "make": "Kia", "model": "Soul", "year": 2012, "tire_pressure": [ 30.1, 31.0, 28.6, 28.7 ] },
{ "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
] )"_padded;
dom::parser parser;
auto justmodel = [](auto car) -> std::string_view {
return car["model"].get_string();
};
auto cars =
parser.parse(cars_json).get_array() | std::views::transform(justmodel);
const std::vector<std::string_view> expected{"Camry", "Soul", "Tercel"};
ASSERT_TRUE(std::ranges::equal(cars, expected));
TEST_SUCCEED();
}
bool run() {
return
#if SIMDJSON_EXCEPTIONS
printout() && checkvalues() &&
#endif // SIMDJSON_EXCEPTIONS
true;
}
} // namespace ranges_test
int main(int argc, char *argv[]) {
return test_main(argc, argv, ranges_test::run);
}
+2 -2
View File
@@ -42,9 +42,9 @@ if(HAVE_POSIX_FORK AND HAVE_POSIX_WAIT) # assert tests use fork and wait, which
endif()
# Copy the simdjson dll into the tests directory
if(MSVC AND BUILD_SHARED_LIBS)
if(WIN32 AND BUILD_SHARED_LIBS)
add_custom_command(TARGET ondemand_parse_api_tests POST_BUILD # Adds a post-build event
COMMAND ${CMAKE_COMMAND} -E copy_if_different # which executes "cmake -E copy_if_different..."
"$<TARGET_FILE:simdjson>" # <--this is in-file
"$<TARGET_FILE_DIR:ondemand_parse_api_tests>") # <--this is out-file path
endif(MSVC AND BUILD_SHARED_LIBS)
endif(WIN32 AND BUILD_SHARED_LIBS)
@@ -5,6 +5,8 @@
#include <vector>
#include <list>
#include <optional>
#include <map>
#include <unordered_map>
using namespace simdjson;
@@ -238,6 +240,40 @@ bool optional_car_deserialize() {
TEST_SUCCEED();
}
bool car_deserialize_to_map() {
TEST_START();
std::map<std::string,std::string> expected = { {"make", "Toyota"}, {"model", "Camry"}};
padded_string json =
R"( { "make": "Toyota", "model": "Camry" })"_padded;
ondemand::parser parser;
ondemand::document doc;
[[maybe_unused]] auto error = parser.iterate(json).get(doc);
static_assert(simdjson::concepts::string_view_keyed_map<std::map<std::string, std::string>>, "should be custom deserializable");
using map_type = typename std::map<std::string,std::string>;
static_assert(simdjson::custom_deserializable<map_type,simdjson::ondemand::value>, "should be custom deserializable");
map_type brands;
error = doc.get<map_type>().get(brands);
ASSERT_TRUE(brands == expected);
TEST_SUCCEED();
}
bool car_deserialize_with_map() {
TEST_START();
padded_string json =
R"( { "car1": { "make": "Toyota", "model": "Camry", "year": 2018,
"tire_pressure": [ 40.1, 39.9 ] }
})"_padded;
ondemand::parser parser;
ondemand::document doc;
[[maybe_unused]] auto error = parser.iterate(json).get(doc);
std::map<std::string,Car> car;
error = doc.get<std::map<std::string,Car>>().get(car);
std::optional<Car> expected = Car{"Toyota", "Camry", 2018, {40.1f, 39.9f}};
ASSERT_TRUE(car["car1"] == expected);
TEST_SUCCEED();
}
bool car_doc_deserialize() {
TEST_START();
padded_string json = R"( { "make": "Toyota", "model": "Camry", "year": 2018,
@@ -320,7 +356,9 @@ bool car_unique_ptr_deserialize() {
TEST_SUCCEED();
}
bool run() { return list_car_deserialize()
bool run() { return car_deserialize_with_map()
&& car_deserialize_to_map()
&& list_car_deserialize()
&& optional_car_deserialize()
&& car_unique_ptr_deserialize()
&& vector_car_deserialize()
@@ -83,6 +83,31 @@ bool custom_test() {
TEST_SUCCEED();
}
bool uint8_t_test() {
TEST_START();
simdjson::ondemand::parser parser;
const simdjson::padded_string json =
R"({"data" : [1,2,3,4]})"_padded;
simdjson::ondemand::document d = parser.iterate(json);
std::vector<uint8_t> array = d["data"].get<std::vector<uint8_t>>();
std::vector<uint8_t> expected = {1, 2, 3, 4};
ASSERT_EQUAL(array, expected);
TEST_SUCCEED();
}
bool append_test() {
TEST_START();
simdjson::ondemand::parser parser;
const simdjson::padded_string json =
R"({"data" : [1,2,3,4]})"_padded;
simdjson::ondemand::document d = parser.iterate(json);
std::vector<uint32_t> array = {0, 0};
d["data"].get<std::vector<uint32_t>>(array);
std::vector<uint32_t> expected = {0, 0, 1, 2, 3, 4};
ASSERT_EQUAL(array, expected);
TEST_SUCCEED();
}
bool readme_test() {
TEST_START();
auto const json = R"( { "make": "Toyota", "model": "Camry", "year": 2018,
@@ -162,6 +187,8 @@ bool simple_document_test_no_except() {
bool run() {
return
#if SIMDJSON_EXCEPTIONS && defined(__cpp_concepts)
append_test() &&
uint8_t_test() &&
readme_test() &&
custom_test() &&
simple_document_test() &&
+46 -16
View File
@@ -5,23 +5,51 @@ using namespace simdjson;
namespace misc_tests {
using namespace std;
bool issue2322() {
TEST_START();
std::vector<std::pair<std::string, bool>> examples = {{R"("hello")", false},
{R"(\"hello)", true},
{R"("hello\")", true},
{R"("hel\"lo")", false},
{R"("hel\\lo")", false},
{R"(\"hel\\\"lo\")", true},
{R"(\\"hel\\\"lo\")", false}};
for (std::pair<std::string, bool> v : examples) {
ASSERT_EQUAL(ondemand::raw_json_string::is_free_from_unescaped_quote(v.first),
v.second);
ASSERT_EQUAL(ondemand::raw_json_string::is_free_from_unescaped_quote(v.first.c_str()),
v.second);
bool issue2354() {
TEST_START();
auto json = "true "_padded;
ondemand::parser parser;
ondemand::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
bool b;
ASSERT_SUCCESS(doc.get_bool().get(b));
ASSERT_TRUE(b);
TEST_SUCCEED();
}
bool issue2355() {
TEST_START();
simdjson::ondemand::parser parser;
auto json = "[\"extra close\"]]"_padded;
ondemand::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
simdjson::ondemand::array test;
ASSERT_SUCCESS(doc.get_array().get(test));
for(auto vale : test) {
std::string_view str;
ASSERT_SUCCESS(vale.get_string().get(str));
ASSERT_EQUAL(str, "extra close");
}
ASSERT_FALSE(doc.at_end());
TEST_SUCCEED();
}
bool issue2322() {
TEST_START();
std::vector<std::pair<std::string, bool>> examples = {{R"("hello")", false},
{R"(\"hello)", true},
{R"("hello\")", true},
{R"("hel\"lo")", false},
{R"("hel\\lo")", false},
{R"(\"hel\\\"lo\")", true},
{R"(\\"hel\\\"lo\")", false}};
for (std::pair<std::string, bool> v : examples) {
ASSERT_EQUAL(ondemand::raw_json_string::is_free_from_unescaped_quote(v.first),
v.second);
ASSERT_EQUAL(ondemand::raw_json_string::is_free_from_unescaped_quote(v.first.c_str()),
v.second);
}
TEST_SUCCEED();
}
TEST_SUCCEED();
}
#if SIMDJSON_EXCEPTIONS
// user reported an asan error:
bool issue2199() {
@@ -655,6 +683,8 @@ bool issue2322() {
bool run() {
return
issue2355() &&
issue2354() &&
issue2322() &&
issue2312() &&
#if SIMDJSON_EXCEPTIONS
+26 -2
View File
@@ -312,7 +312,30 @@ namespace number_tests {
ASSERT_EQUAL(number.get_number_type(), ondemand::number_type::floating_point_number);
ASSERT_EQUAL(number.get_double(), 1e9);
TEST_SUCCEED();
}
}
bool minus_zero() {
TEST_START();
ondemand::parser parser;
auto json = "-0"_padded;
ondemand::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
ondemand::number number;
ASSERT_SUCCESS(doc.get_number().get(number));
#if SIMDJSON_MINUS_ZERO_AS_FLOAT
ASSERT_EQUAL(number.get_number_type(), ondemand::number_type::floating_point_number);
#else
ASSERT_EQUAL(number.get_number_type(), ondemand::number_type::signed_integer);
#endif
ondemand::number_type nt{};
ASSERT_SUCCESS(doc.get_number_type().get(nt));
#if SIMDJSON_MINUS_ZERO_AS_FLOAT
ASSERT_EQUAL(nt, ondemand::number_type::floating_point_number);
#else
ASSERT_EQUAL(nt, ondemand::number_type::signed_integer);
#endif
TEST_SUCCEED();
}
bool issue1878() {
TEST_START();
@@ -504,7 +527,8 @@ namespace number_tests {
}
bool run() {
return gigantic_big_int() &&
return minus_zero() &&
gigantic_big_int() &&
big_int_not_zero() &&
negative_big_int() &&
issue2099() &&
+34 -1
View File
@@ -23,6 +23,24 @@ namespace object_tests {
TEST_SUCCEED();
}
bool testing_to_string() {
TEST_START();
auto json = R"({"\u0062\u0065\u0062\u0065": 2} })"_padded;
ondemand::parser parser;
ondemand::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
ondemand::object object;
ASSERT_SUCCESS(doc.get_object().get(object));
for (auto field : object) {
std::string key;
ASSERT_SUCCESS(field.unescaped_key().get(key));
ASSERT_EQUAL(key, "bebe");
}
TEST_SUCCEED();
}
bool issue1977() {
TEST_START();
auto json = R"({"1": 2} foo })"_padded;
@@ -258,6 +276,19 @@ namespace object_tests {
#if SIMDJSON_EXCEPTIONS
bool testing_to_string_exception() {
TEST_START();
auto json = R"({"\u0062\u0065\u0062\u0065": 2} })"_padded;
ondemand::parser parser;
ondemand::document doc = parser.iterate(json);
ondemand::object object = doc.get_object();
for (auto field : object) {
std::string key;
ASSERT_SUCCESS(field.unescaped_key().get(key));
}
TEST_SUCCEED();
}
bool issue1965() {
TEST_START();
std::string str = "{\"query\":\"ah\"}";
@@ -1310,9 +1341,11 @@ namespace object_tests {
}
bool run() {
return issue1979() &&
return testing_to_string() &&
issue1979() &&
issue1977() &&
#if SIMDJSON_EXCEPTIONS
testing_to_string_exception() &&
issue1965() &&
#endif
issue1974a() &&
+52 -3
View File
@@ -10,6 +10,37 @@ using namespace std;
using namespace simdjson;
using error_code = simdjson::error_code;
bool fatal_error() {
TEST_START();
padded_string badjson = R"( { "make": "Toyota", "model": "Camry", "year"})"_padded;
ondemand::parser parser;
ondemand::document doc;
auto errordoc = parser.iterate(badjson).get(doc);
if(errordoc != simdjson::SUCCESS) { return false; }
simdjson::ondemand::value v;
auto error = doc.get_object()["year"].get(v);
ASSERT_TRUE(simdjson::is_fatal(error));
ASSERT_FALSE(doc.is_alive());
TEST_SUCCEED();
}
bool to_string_object() {
TEST_START();
auto json = R"({"\u0062\u0065\u0062\u0065": 2} })"_padded;
ondemand::parser parser;
ondemand::document doc;
auto error = parser.iterate(json).get(doc);
if(error) { return false; }
ondemand::object object;
error = doc.get_object().get(object);
if(error) { return false; }
for (auto field : object) {
std::string key;
error = field.unescaped_key().get(key);
if(error) { return false; }
}
return true;
}
bool simplepad() {
std::string json = "[1]";
ondemand::parser parser;
@@ -22,14 +53,14 @@ bool string1() {
const char * data = "my data"; // 7 bytes
simdjson::padded_string my_padded_data(data, 7); // copies to a padded buffer
std::cout << my_padded_data << std::endl;
return true;
TEST_SUCCEED();
}
bool string2() {
std::string data = "my data";
simdjson::padded_string my_padded_data(data); // copies to a padded buffer
std::cout << my_padded_data << std::endl;
return true;
TEST_SUCCEED();
}
bool to_string_example_no_except() {
@@ -272,6 +303,22 @@ bool at_end() {
TEST_SUCCEED();
}
bool at_end_array() {
TEST_START();
auto json = R"(["extra close"]])"_padded;
ondemand::parser parser;
ondemand::document doc = parser.iterate(json);
ondemand::array array = doc.get_array();
for (std::string_view values : array) {
std::cout << values << std::endl;
}
if(!doc.at_end()) {
std::cerr << "trailing content at byte index " << doc.current_location() - json.data() << std::endl;
}
TEST_SUCCEED();
}
bool examplecrt() {
TEST_START();
padded_string padded_input_json = R"([
@@ -1893,6 +1940,7 @@ bool value_raw_json_object() {
#endif
bool run() {
return true
&& fatal_error()
#if SIMDJSON_EXCEPTIONS
#if SIMDJSON_CPLUSPLUS17
&& big_int_array()
@@ -1956,8 +2004,9 @@ bool run() {
&& current_location_tape_error_with_except()
&& examplecrt()
&& examplecrt_realloc()
&& at_end_array()
#endif
;
;
}
int main(int argc, char *argv[]) {
+1 -40
View File
@@ -6,6 +6,7 @@
#include "simdjson.h"
#include "cast_tester.h"
#include "test_macros.h"
#include "test_main.h"
template<typename T, typename F>
bool test_ondemand(simdjson::ondemand::parser &parser, const simdjson::padded_string &json, const F& f) {
@@ -43,45 +44,5 @@ bool test_ondemand_doc(const simdjson::padded_string &json, const F& f) {
const size_t AMAZON_CELLPHONES_NDJSON_DOC_COUNT = 793;
#define SIMDJSON_SHOW_DEFINE(x) printf("%s=%s\n", #x, SIMDJSON_STRINGIFY(x))
template<typename F>
int test_main(int argc, char *argv[], const F& test_function) {
std::cout << std::unitbuf;
int c;
while ((c = getopt(argc, argv, "a:")) != -1) {
switch (c) {
case 'a': {
const simdjson::implementation *impl = simdjson::get_available_implementations()[optarg];
if (!impl) {
std::fprintf(stderr, "Unsupported architecture value -a %s\n", optarg);
return EXIT_FAILURE;
}
simdjson::get_active_implementation() = impl;
break;
}
default:
std::fprintf(stderr, "Unexpected argument %c\n", c);
return EXIT_FAILURE;
}
}
// this is put here deliberately to check that the documentation is correct (README),
// should this fail to compile, you should update the documentation:
if (simdjson::get_active_implementation()->name() == "unsupported") {
std::printf("unsupported CPU\n");
std::abort();
}
// We want to know what we are testing.
std::cout << "builtin_implementation -- " << simdjson::builtin_implementation()->name() << std::endl;
std::cout << "------------------------------------------------------------" << std::endl;
std::cout << "Running tests." << std::endl;
if (test_function()) {
std::cout << "Success!" << std::endl;
return EXIT_SUCCESS;
} else {
std::cerr << "FAILED." << std::endl;
return EXIT_FAILURE;
}
}
#endif // ONDEMAND_TEST_ONDEMAND_H
+15 -1
View File
@@ -46,6 +46,21 @@ simdjson_inline simdjson::error_code to_error_code(const simdjson::simdjson_resu
return result.error();
}
template <typename T>
std::ostream &operator<<(std::ostream &os, const std::vector<T> &vec) {
os << "["; // Start with opening bracket
if (!vec.empty()) {
// Print first element without leading comma
os << static_cast<uint16_t>(vec[0]);
// Print remaining elements with commas
for (size_t i = 1; i < vec.size(); ++i) {
os << ", " << static_cast<uint16_t>(vec[i]);
}
}
os << "]"; // End with closing bracket
return os;
}
template<typename T>
simdjson_inline bool assert_success(const T &actual, const char *operation = "result") {
simdjson::error_code error = to_error_code(actual);
@@ -124,5 +139,4 @@ simdjson_inline bool assert_iterate_error(T &arr, simdjson::error_code expected,
#define RUN_TEST(ACTUAL) do { if (!(ACTUAL)) { return false; } } while (0);
#define TEST_FAIL(MESSAGE) do { std::cerr << "FAIL: " << (MESSAGE) << std::endl; return false; } while (0);
#define TEST_SUCCEED() do { return true; } while (0);
#endif // TEST_MACROS_H
+50
View File
@@ -0,0 +1,50 @@
#ifndef TEST_MAIN_H
#define TEST_MAIN_H
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <unistd.h>
#include "simdjson.h"
// technically not a macro, but we want to share this function across tests for both ondemand and dom.
template<typename F>
int test_main(int argc, char *argv[], const F& test_function) {
std::cout << std::unitbuf;
int c;
while ((c = getopt(argc, argv, "a:")) != -1) {
switch (c) {
case 'a': {
const simdjson::implementation *impl = simdjson::get_available_implementations()[optarg];
if (!impl) {
std::fprintf(stderr, "Unsupported architecture value -a %s\n", optarg);
return EXIT_FAILURE;
}
simdjson::get_active_implementation() = impl;
break;
}
default:
std::fprintf(stderr, "Unexpected argument %c\n", c);
return EXIT_FAILURE;
}
}
// this is put here deliberately to check that the documentation is correct (README),
// should this fail to compile, you should update the documentation:
if (simdjson::get_active_implementation()->name() == "unsupported") {
std::printf("unsupported CPU\n");
std::abort();
}
// We want to know what we are testing.
std::cout << "builtin_implementation -- " << simdjson::builtin_implementation()->name() << std::endl;
std::cout << "------------------------------------------------------------" << std::endl;
std::cout << "Running tests." << std::endl;
if (test_function()) {
std::cout << "Success!" << std::endl;
return EXIT_SUCCESS;
} else {
std::cerr << "FAILED." << std::endl;
return EXIT_FAILURE;
}
}
#endif // TEST_MAIN_H