Compare commits

..

5 Commits

Author SHA1 Message Date
Daniel Lemire bfd8856913 less terrible maybe 2026-02-05 20:06:38 -05:00
Daniel Lemire 39bee282da implementing the backslash-free string optimization 2026-01-22 16:30:05 -05:00
Daniel Lemire d9956d7b3b Merge branch 'master' into optimization-get_string 2026-01-22 10:44:04 -05:00
Daniel Lemire 8e7ddb3155 Merge branch 'master' into optimization-get_string 2026-01-22 10:43:29 -05:00
Carlos Sousa e0ef3e7cec optimize get_string 2024-07-11 01:30:27 -03:00
92 changed files with 2217 additions and 38612 deletions
-4
View File
@@ -29,9 +29,6 @@ We accept the identification of an issue by a sanitizer or some checker tool (e.
We recommend that you run your tests using different optimization levels. In particular, we recommend your run tests with the simdjson library and you code compiled in debug mode. The simdjson then sets the SIMDJSON_DEVELOPMENT_CHECKS macro to 1, and this triggers additional checks on your code and on the internals of the library. If possible, we recommend that you run tests with sanitizers (e.g., see [No more leaks with sanitize flags in gcc and clang](https://lemire.me/blog/2016/04/20/no-more-leaks-with-sanitize-flags-in-gcc-and-clang/)). You can compile the library with sanitizers for debugging purposes (e.g., set SIMDJSON_SANITIZE to ON using CMake), but you should also turn on sanitizers on your own code. You may also use tools like valgrind or the commercial equivalent.
Mixing debug and release simdjson code is unsafe: you either build all your code using simdjson in
release mode or all of it in debug mode.
Before reporting a bug, please ensure that you have read our documentation.
**To Reproduce**
@@ -58,7 +55,6 @@ We support up-to-date 64-bit ARM and x64 FreeBSD, macOS, Windows and Linux syste
* We do not support unreleased or experimental compilers. If you encounter an issue with a
pre-release version of a compiler, do not report it as a bug to simdjson. However, we always
invite contributions either in the form an analysis or of a code contribution.
* Vendors (e.g., Apple and Microsoft) stop supporting old systems. Once a compiler system is no longer supported by its vendor, we no longer support it. We will gladly accept code contributions, but we do not consider it a *bug* if you have issues with an obsolete compiler systems. This policy extends to obsolete standard libraries, linkers and other build tools. Please do not report it as an issue. If you cannot resolve the issue yourself, we encourage you to reach out to the vendor for legacy support.
Under Windows, we support Visual Studio (both with LLVM and without). We do not support MinGW and other alternate compiler systems. Windows users should be aware that there [is a long-running bug with GCC under Windows](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54412).
+1 -1
View File
@@ -20,7 +20,7 @@ How to verify / test
Please read before contributing:
- CONTRIBUTING: https://github.com/simdjson/simdjson/blob/master/CONTRIBUTING.md
- HACKING: https://github.com/simdjson/simdjson/blob/master/HACKING.md
- AI Usage Policy: https://github.com/simdjson/simdjson/blob/master/AI_USAGE_POLICY.md
If you can, we recommend running our tests with the sanitizers turned on.
+1 -1
View File
@@ -1,4 +1,4 @@
name: Performance check on Ubuntu 24.04 CI (GCC 13)
name: Performance check on Ubuntu 20.04 CI (GCC 9)
on:
push:
+2 -2
View File
@@ -1,4 +1,4 @@
name: Ubuntu 24.04 CI (GCC 13) without exceptions
name: Ubuntu 20.04 CI (GCC 9) without exceptions
on: [push, pull_request]
@@ -24,7 +24,7 @@ jobs:
cd .. &&
mkdir build &&
cd build &&
cmake -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_EXCEPTIONS=OFF -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
cmake -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_EXCEPTIONS=OFF -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
cmake --build . &&
ctest --output-on-failure -LE explicitonly -j &&
make install &&
+1 -1
View File
@@ -1,4 +1,4 @@
name: Ubuntu 24.04 CI (GCC 13) Without Threads
name: Ubuntu 20.04 CI (GCC 9) Without Threads
on: [push, pull_request]
+2 -2
View File
@@ -1,9 +1,9 @@
name: Ubuntu 24.04 CI (GCC 13) With Memory Sanitizer
name: Ubuntu 20.04 CI (GCC 9) With Memory Sanitizer
on: [push, pull_request]
jobs:
ubuntu-build-address-sanitizer:
ubuntu-build-address-sanitizier:
if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]')
-56
View File
@@ -1,56 +0,0 @@
# AI Usage Policy
Contributors can use whatever tools they would like to
craft their contributions, but there must be a **human in the loop**.
**Contributors must read and review all LLM-generated code or text before they
ask other project members to review it.** The contributor is always the author
and is fully accountable for their contributions. Contributors should be
sufficiently confident that the contribution is high enough quality that asking
for a review is a good use of scarce maintainer time, and they should be **able
to answer questions about their work** during review.
We expect that new contributors will be less confident in their contributions,
and our guidance to them is to **start with small contributions** that they can
fully understand to build confidence. We aspire to be a welcoming community
that helps new contributors grow their expertise, but learning involves taking
small steps, getting feedback, and iterating. Passing maintainer feedback to an
LLM doesn't help anyone grow, and does not sustain our community.
This policy includes, but is not limited to, the following kinds of
contributions:
- Code, usually in the form of a pull request
- Issues or security vulnerabilities
- Comments and feedback on pull requests
## Extractive Contributions
The reason for our "human-in-the-loop" contribution policy is that processing
patches, PRs, RFCs, and comments is not free -- it takes a lot of
maintainer time and energy to review those contributions! Sending the
unreviewed output of an LLM to open source project maintainers *extracts* work
from them in the form of design and code review, so we call this kind of
contribution an "extractive contribution".
## Transparency
For contributions involving significant AI assistance, we encourage you to disclose
its use and explain your process. If a submission appears to rely heavily on AI
without disclosure, we may doubt that the **human-in-the-loop** requirement has
been met. Please show awareness of your use of AI.
## Copyright
Artificial intelligence systems raise many questions around copyright that have
yet to be answered. Our policy on AI tools is similar to our copyright policy:
Contributors are responsible for ensuring that they have the right to
contribute code under the terms of our license, typically meaning that either
they, their employer, or their collaborators hold the copyright. Using AI tools
to regenerate copyrighted material does not remove the copyright, and
contributors are responsible for ensuring that such material does not appear in
their contributions. Contributions found to violate this policy will be removed
just like any other offending contribution.
## Reference
- [LLVM AI Tool Use Policy](https://discourse.llvm.org/t/rfc-llvm-ai-tool-policy-human-in-the-loop/89159)
+83 -97
View File
@@ -12,7 +12,7 @@ endif()
project(
simdjson
# The version number is modified by tools/release.py
VERSION 4.6.3
VERSION 4.2.4
DESCRIPTION "Parsing gigabytes of JSON per second"
HOMEPAGE_URL "https://simdjson.org/"
LANGUAGES CXX C
@@ -29,8 +29,8 @@ string(
# ---- Options, variables ----
# These version numbers are modified by tools/release.py
set(SIMDJSON_LIB_VERSION "33.0.0" CACHE STRING "simdjson library version")
set(SIMDJSON_LIB_SOVERSION "33" CACHE STRING "simdjson library soversion")
set(SIMDJSON_LIB_VERSION "29.0.0" CACHE STRING "simdjson library version")
set(SIMDJSON_LIB_SOVERSION "29" 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)
@@ -76,12 +76,9 @@ if(SIMDJSON_DEVELOPMENT_CHECKS)
endif()
if(is_top_project)
option(SIMDJSON_INSTALL "Enable target install" ON)
option(SIMDJSON_DEVELOPER_MODE "Enable targets for developing simdjson" OFF)
option(BUILD_SHARED_LIBS "Build simdjson as a shared library" OFF)
option(SIMDJSON_SINGLEHEADER "Disable singleheader generation" ON)
else()
option(SIMDJSON_INSTALL "Enable target install" ${BUILD_SHARED_LIBS})
endif()
include(cmake/handle-deprecations.cmake)
@@ -95,16 +92,13 @@ add_library(simdjson ${SIMDJSON_SOURCES})
add_library(simdjson::simdjson ALIAS simdjson)
set(SIMDJSON_LIBRARIES simdjson)
# Check for <bit> header compatibility
include(CheckIncludeFileCXX)
check_include_file_cxx(bit SIMDJSON_HAS_BIT_HEADER)
# Enable precompiled headers for faster builds
if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.16")
set(SIMDJSON_PRECOMPILE_HEADERS
target_precompile_headers(simdjson PRIVATE
<algorithm>
<array>
<atomic>
<bit>
<cassert>
<cctype>
<cerrno>
@@ -117,12 +111,6 @@ if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.16")
<utility>
<vector>
)
if(SIMDJSON_HAS_BIT_HEADER)
list(APPEND SIMDJSON_PRECOMPILE_HEADERS <bit>)
endif()
target_precompile_headers(simdjson PRIVATE ${SIMDJSON_PRECOMPILE_HEADERS})
endif()
if(SIMDJSON_BUILD_STATIC_LIB)
@@ -228,89 +216,87 @@ endif()
# ---- Install rules ----
if(SIMDJSON_INSTALL)
include(CMakePackageConfigHelpers)
include(GNUInstallDirs)
include(CMakePackageConfigHelpers)
include(GNUInstallDirs)
if(SIMDJSON_SINGLEHEADER)
install(
FILES singleheader/simdjson.h
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
COMPONENT simdjson_Development
)
endif()
install(
TARGETS simdjson
EXPORT simdjsonTargets
RUNTIME COMPONENT simdjson_Runtime
LIBRARY COMPONENT simdjson_Runtime
NAMELINK_COMPONENT simdjson_Development
ARCHIVE COMPONENT simdjson_Development
INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
)
configure_file(cmake/simdjson-config.cmake.in simdjson-config.cmake @ONLY)
write_basic_package_version_file(
simdjson-config-version.cmake
COMPATIBILITY SameMinorVersion
)
set(
SIMDJSON_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/simdjson"
CACHE STRING "CMake package config location relative to the install prefix"
)
mark_as_advanced(SIMDJSON_INSTALL_CMAKEDIR)
install(
FILES
"${PROJECT_BINARY_DIR}/simdjson-config.cmake"
"${PROJECT_BINARY_DIR}/simdjson-config-version.cmake"
DESTINATION "${SIMDJSON_INSTALL_CMAKEDIR}"
COMPONENT simdjson_Development
)
install(
EXPORT simdjsonTargets
NAMESPACE simdjson::
DESTINATION "${SIMDJSON_INSTALL_CMAKEDIR}"
COMPONENT simdjson_Development
)
if(SIMDJSON_BUILD_STATIC_LIB)
install(
TARGETS simdjson_static
EXPORT simdjson_staticTargets
ARCHIVE COMPONENT simdjson_Development
INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
)
install(
EXPORT simdjson_staticTargets
NAMESPACE simdjson::
DESTINATION "${SIMDJSON_INSTALL_CMAKEDIR}"
COMPONENT simdjson_Development
)
endif()
# pkg-config
include(cmake/JoinPaths.cmake)
join_paths(PKGCONFIG_INCLUDEDIR "\${prefix}" "${CMAKE_INSTALL_INCLUDEDIR}")
join_paths(PKGCONFIG_LIBDIR "\${prefix}" "${CMAKE_INSTALL_LIBDIR}")
if(SIMDJSON_ENABLE_THREADS)
set(PKGCONFIG_CFLAGS "-DSIMDJSON_THREADS_ENABLED=1")
if(CMAKE_THREAD_LIBS_INIT)
set(PKGCONFIG_LIBS_PRIVATE "Libs.private: ${CMAKE_THREAD_LIBS_INIT}")
endif()
endif()
configure_file("simdjson.pc.in" "simdjson.pc" @ONLY)
install(
FILES "${CMAKE_CURRENT_BINARY_DIR}/simdjson.pc"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig"
)
if(SIMDJSON_SINGLEHEADER)
install(
FILES singleheader/simdjson.h
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
COMPONENT simdjson_Development
)
endif()
install(
TARGETS simdjson
EXPORT simdjsonTargets
RUNTIME COMPONENT simdjson_Runtime
LIBRARY COMPONENT simdjson_Runtime
NAMELINK_COMPONENT simdjson_Development
ARCHIVE COMPONENT simdjson_Development
INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
)
configure_file(cmake/simdjson-config.cmake.in simdjson-config.cmake @ONLY)
write_basic_package_version_file(
simdjson-config-version.cmake
COMPATIBILITY SameMinorVersion
)
set(
SIMDJSON_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/simdjson"
CACHE STRING "CMake package config location relative to the install prefix"
)
mark_as_advanced(SIMDJSON_INSTALL_CMAKEDIR)
install(
FILES
"${PROJECT_BINARY_DIR}/simdjson-config.cmake"
"${PROJECT_BINARY_DIR}/simdjson-config-version.cmake"
DESTINATION "${SIMDJSON_INSTALL_CMAKEDIR}"
COMPONENT simdjson_Development
)
install(
EXPORT simdjsonTargets
NAMESPACE simdjson::
DESTINATION "${SIMDJSON_INSTALL_CMAKEDIR}"
COMPONENT simdjson_Development
)
if(SIMDJSON_BUILD_STATIC_LIB)
install(
TARGETS simdjson_static
EXPORT simdjson_staticTargets
ARCHIVE COMPONENT simdjson_Development
INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
)
install(
EXPORT simdjson_staticTargets
NAMESPACE simdjson::
DESTINATION "${SIMDJSON_INSTALL_CMAKEDIR}"
COMPONENT simdjson_Development
)
endif()
# pkg-config
include(cmake/JoinPaths.cmake)
join_paths(PKGCONFIG_INCLUDEDIR "\${prefix}" "${CMAKE_INSTALL_INCLUDEDIR}")
join_paths(PKGCONFIG_LIBDIR "\${prefix}" "${CMAKE_INSTALL_LIBDIR}")
if(SIMDJSON_ENABLE_THREADS)
set(PKGCONFIG_CFLAGS "-DSIMDJSON_THREADS_ENABLED=1")
if(CMAKE_THREAD_LIBS_INIT)
set(PKGCONFIG_LIBS_PRIVATE "Libs.private: ${CMAKE_THREAD_LIBS_INIT}")
endif()
endif()
configure_file("simdjson.pc.in" "simdjson.pc" @ONLY)
install(
FILES "${CMAKE_CURRENT_BINARY_DIR}/simdjson.pc"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig"
)
#
# CPack
#
-7
View File
@@ -101,10 +101,3 @@ Getting Started Hacking
An overview of simdjson's directory structure, with pointers to architecture and design
considerations and other helpful notes, can be found at [HACKING.md](HACKING.md).
AI Usage Policy
---------------
Please also review our [AI Usage Policy](AI_USAGE_POLICY.md).
+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 = "4.6.3"
PROJECT_NUMBER = "4.2.4"
# 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
+1 -10
View File
@@ -64,8 +64,6 @@ Real-world usage
- [RonDB](https://github.com/logicalclocks/rondb)
- [GreptimeDB](https://github.com/GreptimeTeam/greptimedb)
- [mamba](https://github.com/mamba-org/mamba)
- [Ladybird Browser](https://ladybird.org)
- [SereneDB](https://github.com/serenedb/serenedb)
If you are planning to use simdjson in a product, please work from one of our releases.
@@ -189,7 +187,6 @@ We distinguish between "bindings" (which just wrap the C++ code) and a port to a
- [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.
- [simdjson-java](https://github.com/simdjson/simdjson-java): Java port.
- [mruby-fast-json](https://github.com/Asmod4n/mruby-fast-json): mruby binding with high API coverage.
About simdjson
--------------
@@ -212,16 +209,10 @@ We have an in-depth paper focused on the UTF-8 validation:
We also have an informal [blog post providing some background and context](https://branchfree.org/2019/02/25/paper-parsing-gigabytes-of-json-per-second/).
For the video inclined, we had a talk at QCon San Francisco 2019<br />
For the video inclined, <br />
[![simdjson at QCon San Francisco 2019](http://img.youtube.com/vi/wlvKAT7SZIQ/0.jpg)](http://www.youtube.com/watch?v=wlvKAT7SZIQ)<br />
(It was the best voted talk, we're kinda proud of it.)
We also had a CppCon 2025 talk. We show how C++26 reflection allows for one-line serialization (to_json(player)) or deserialization—without invasive macros or manual mapping—using nothing but the C++ standard library. Whether youre a performance junkie or simply interested in the roadmap for the next decade of C++ development, watch our full talk!
[![simdjson at CppCon 2025](http://img.youtube.com/vi/Mcgk3CxHYMs/0.jpg)](http://www.youtube.com/watch?v=Mcgk3CxHYMs)<br />
Citing this work
-----------------
+3 -3
View File
@@ -8,7 +8,7 @@ CPMAddPackage(
option(SIMDJSON_USE_RUST "Build the static_reflect benchmark" OFF)
if(SIMDJSON_USE_RUST)
if(SIMDJSON_USER_RUST)
if(NOT WIN32)
# We want the check whether Rust is available before trying to build a crate.
CPMAddPackage(
@@ -39,9 +39,9 @@ if(SIMDJSON_USE_RUST)
message(STATUS "curl https://sh.rustup.rs -sSf | sh")
endif()
endif()
else(SIMDJSON_USE_RUST)
else(SIMDJSON_USER_RUST)
message(STATUS "We will not benchmark serde-benchmark." )
endif(SIMDJSON_USE_RUST)
endif(SIMDJSON_USER_RUST)
# Add the benchmark executable targets
add_subdirectory(twitter_benchmark)
@@ -1,5 +1,4 @@
add_executable(benchmark_serialization_citm_catalog benchmark_serialization_citm_catalog.cpp)
add_executable(benchmark_parsing_citm benchmark_parsing_citm.cpp)
# Link with Rust benchmarking code if available
if(TARGET serde-benchmark)
@@ -12,29 +11,4 @@ target_link_libraries(benchmark_serialization_citm_catalog PRIVATE simdjson::sim
target_link_libraries(benchmark_serialization_citm_catalog PRIVATE reflectcpp)
target_compile_definitions(benchmark_serialization_citm_catalog PRIVATE SIMDJSON_BENCH_CPP_REFLECT=1)
if(TARGET yyjson)
target_link_libraries(benchmark_serialization_citm_catalog PRIVATE yyjson)
target_compile_definitions(benchmark_serialization_citm_catalog PRIVATE SIMDJSON_COMPETITION_YYJSON)
endif()
target_compile_definitions(benchmark_serialization_citm_catalog PRIVATE JSON_FILE="${BENCH_CITM_JSON}")
# Configuration for parsing benchmark
if(TARGET serde-benchmark)
target_link_libraries(benchmark_parsing_citm PRIVATE serde-benchmark)
target_compile_definitions(benchmark_parsing_citm PRIVATE SIMDJSON_RUST_VERSION="${Rust_VERSION}")
endif()
target_link_libraries(benchmark_parsing_citm PRIVATE simdjson::simdjson nlohmann_json)
if(TARGET rapidjson)
target_link_libraries(benchmark_parsing_citm PRIVATE rapidjson)
target_compile_definitions(benchmark_parsing_citm PRIVATE SIMDJSON_COMPETITION_RAPIDJSON)
endif()
if(TARGET yyjson)
target_link_libraries(benchmark_parsing_citm PRIVATE yyjson)
target_compile_definitions(benchmark_parsing_citm PRIVATE SIMDJSON_COMPETITION_YYJSON)
endif()
target_compile_definitions(benchmark_parsing_citm PRIVATE JSON_FILE="${BENCH_CITM_JSON}")
target_compile_definitions(benchmark_serialization_citm_catalog PRIVATE JSON_FILE="${BENCH_CITM_JSON}")
@@ -1,563 +0,0 @@
#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"
#ifdef SIMDJSON_COMPETITION_RAPIDJSON
#include "rapidjson_citm_catalog_data.h"
#endif
#ifdef SIMDJSON_COMPETITION_YYJSON
#include "yyjson_citm_catalog_data.h"
#endif
#ifdef SIMDJSON_RUST_VERSION
#include "../serde-benchmark/serde_benchmark.h"
void bench_rust_parsing(const std::string &json_str) {
size_t input_volume = json_str.size();
printf("# input volume: %zu bytes\n", input_volume);
volatile bool result = true;
pretty_print(1, input_volume, "bench_rust_parsing",
bench([&json_str, &result]() {
serde_benchmark::CitmCatalog *catalog = serde_benchmark::citm_from_str(json_str.c_str(), json_str.size());
result = (catalog != nullptr);
if (catalog) {
serde_benchmark::free_citm(catalog);
}
if (!result) {
printf("parse error\n");
}
}));
}
#endif
template <class T> void bench_simdjson_static_reflection_parsing(const std::string &json_str) {
size_t input_volume = json_str.size();
printf("# input volume: %zu bytes\n", input_volume);
// Pre-allocate padded buffer outside the benchmark loop
std::string mutable_json = json_str;
simdjson::pad(mutable_json);
volatile bool result = true;
pretty_print(1, input_volume, "bench_simdjson_static_reflection_parsing",
bench([&mutable_json, &result]() {
simdjson::ondemand::parser parser;
simdjson::ondemand::document doc;
if(parser.iterate(mutable_json).get(doc)) {
result = false;
return;
}
T my_struct;
if(doc.get<T>().get(my_struct)) {
result = false;
}
if (!result) {
printf("parse error\n");
}
}));
}
#if SIMDJSON_STATIC_REFLECTION
template <class T> void bench_simdjson_from_parsing(const std::string &json_str) {
size_t input_volume = json_str.size();
printf("# input volume: %zu bytes\n", input_volume);
// Pre-allocate padded buffer outside the benchmark loop
simdjson::padded_string padded = simdjson::padded_string(json_str);
volatile bool result = true;
pretty_print(1, input_volume, "bench_simdjson_from_parsing",
bench([&padded, &result]() {
T my_struct;
auto err = simdjson::from(padded).get(my_struct);
if (err) {
result = false;
printf("parse error: %s\n", simdjson::error_message(err));
return;
}
}));
}
#endif
// nlohmann::json deserialization functions
void from_json(const nlohmann::json &j, CITMPrice &p) {
j.at("amount").get_to(p.amount);
j.at("audienceSubCategoryId").get_to(p.audienceSubCategoryId);
j.at("seatCategoryId").get_to(p.seatCategoryId);
}
void from_json(const nlohmann::json &j, CITMArea &a) {
j.at("areaId").get_to(a.areaId);
j.at("blockIds").get_to(a.blockIds);
}
void from_json(const nlohmann::json &j, CITMSeatCategory &s) {
j.at("areas").get_to(s.areas);
j.at("seatCategoryId").get_to(s.seatCategoryId);
}
void from_json(const nlohmann::json &j, CITMPerformance &p) {
j.at("id").get_to(p.id);
j.at("eventId").get_to(p.eventId);
if (j.contains("logo") && !j["logo"].is_null()) {
p.logo = j["logo"].get<std::string>();
}
if (j.contains("name") && !j["name"].is_null()) {
p.name = j["name"].get<std::string>();
}
j.at("prices").get_to(p.prices);
j.at("seatCategories").get_to(p.seatCategories);
if (j.contains("seatMapImage") && !j["seatMapImage"].is_null()) {
p.seatMapImage = j["seatMapImage"].get<std::string>();
}
j.at("start").get_to(p.start);
j.at("venueCode").get_to(p.venueCode);
}
void from_json(const nlohmann::json &j, CITMEvent &e) {
j.at("id").get_to(e.id);
j.at("name").get_to(e.name);
if (j.contains("description") && !j["description"].is_null()) {
e.description = j["description"].get<std::string>();
}
if (j.contains("logo") && !j["logo"].is_null()) {
e.logo = j["logo"].get<std::string>();
}
j.at("subTopicIds").get_to(e.subTopicIds);
if (j.contains("subjectCode") && !j["subjectCode"].is_null()) {
e.subjectCode = j["subjectCode"].get<std::string>();
}
if (j.contains("subtitle") && !j["subtitle"].is_null()) {
e.subtitle = j["subtitle"].get<std::string>();
}
j.at("topicIds").get_to(e.topicIds);
}
void from_json(const nlohmann::json &j, CitmCatalog &c) {
j.at("events").get_to(c.events);
j.at("performances").get_to(c.performances);
}
CitmCatalog nlohmann_deserialize(const std::string &json_str) {
nlohmann::json j = nlohmann::json::parse(json_str);
return j.get<CitmCatalog>();
}
void bench_nlohmann_parsing(const std::string &json_str) {
size_t input_volume = json_str.size();
printf("# input volume: %zu bytes\n", input_volume);
volatile bool result = true;
pretty_print(1, input_volume, "bench_nlohmann_parsing",
bench([&json_str, &result]() {
try {
CitmCatalog data = nlohmann_deserialize(json_str);
result = true;
} catch (...) {
result = false;
printf("parse error\n");
}
}));
}
#ifdef SIMDJSON_COMPETITION_RAPIDJSON
CitmCatalog rapidjson_deserialize(const std::string &json_str) {
rapidjson::Document doc;
doc.Parse(json_str.c_str());
if (doc.HasParseError()) {
throw std::runtime_error("RapidJSON parse error");
}
CitmCatalog catalog;
// Parse events
if (doc.HasMember("events") && doc["events"].IsObject()) {
for (auto& m : doc["events"].GetObject()) {
CITMEvent event;
const auto& e = m.value;
event.id = e["id"].GetUint64();
event.name = e["name"].GetString();
if (e.HasMember("description") && !e["description"].IsNull()) {
event.description = e["description"].GetString();
}
if (e.HasMember("logo") && !e["logo"].IsNull()) {
event.logo = e["logo"].GetString();
}
event.subTopicIds.clear();
for (auto& id : e["subTopicIds"].GetArray()) {
event.subTopicIds.push_back(id.GetUint64());
}
if (e.HasMember("subjectCode") && !e["subjectCode"].IsNull()) {
event.subjectCode = e["subjectCode"].GetString();
}
if (e.HasMember("subtitle") && !e["subtitle"].IsNull()) {
event.subtitle = e["subtitle"].GetString();
}
event.topicIds.clear();
for (auto& id : e["topicIds"].GetArray()) {
event.topicIds.push_back(id.GetUint64());
}
catalog.events[m.name.GetString()] = event;
}
}
// Parse performances
if (doc.HasMember("performances") && doc["performances"].IsArray()) {
for (auto& p : doc["performances"].GetArray()) {
CITMPerformance perf;
perf.id = p["id"].GetUint64();
perf.eventId = p["eventId"].GetUint64();
if (p.HasMember("logo") && !p["logo"].IsNull()) {
perf.logo = p["logo"].GetString();
}
if (p.HasMember("name") && !p["name"].IsNull()) {
perf.name = p["name"].GetString();
}
// Parse prices
for (auto& price : p["prices"].GetArray()) {
CITMPrice pr;
pr.amount = price["amount"].GetUint64();
pr.audienceSubCategoryId = price["audienceSubCategoryId"].GetUint64();
pr.seatCategoryId = price["seatCategoryId"].GetUint64();
perf.prices.push_back(pr);
}
// Parse seat categories
for (auto& sc : p["seatCategories"].GetArray()) {
CITMSeatCategory seatCat;
seatCat.seatCategoryId = sc["seatCategoryId"].GetUint64();
for (auto& area : sc["areas"].GetArray()) {
CITMArea ar;
ar.areaId = area["areaId"].GetUint64();
for (auto& block : area["blockIds"].GetArray()) {
ar.blockIds.push_back(block.GetUint64());
}
seatCat.areas.push_back(ar);
}
perf.seatCategories.push_back(seatCat);
}
if (p.HasMember("seatMapImage") && !p["seatMapImage"].IsNull()) {
perf.seatMapImage = p["seatMapImage"].GetString();
}
perf.start = p["start"].GetUint64();
perf.venueCode = p["venueCode"].GetString();
catalog.performances.push_back(perf);
}
}
return catalog;
}
void bench_rapidjson_parsing(const std::string &json_str) {
size_t input_volume = json_str.size();
printf("# input volume: %zu bytes\n", input_volume);
volatile bool result = true;
pretty_print(1, input_volume, "bench_rapidjson_parsing",
bench([&json_str, &result]() {
try {
CitmCatalog data = rapidjson_deserialize(json_str);
result = true;
} catch (...) {
result = false;
printf("parse error\n");
}
}));
}
#endif
#ifdef SIMDJSON_COMPETITION_YYJSON
CitmCatalog yyjson_deserialize(const std::string &json_str) {
yyjson_doc *doc = yyjson_read(json_str.c_str(), json_str.size(), 0);
if (!doc) {
throw std::runtime_error("YYJson parse error");
}
yyjson_val *root = yyjson_doc_get_root(doc);
CitmCatalog catalog;
// Parse events
yyjson_val *events = yyjson_obj_get(root, "events");
if (events) {
size_t idx, max;
yyjson_val *key, *val;
yyjson_obj_foreach(events, idx, max, key, val) {
CITMEvent event;
event.id = yyjson_get_uint(yyjson_obj_get(val, "id"));
const char* name = yyjson_get_str(yyjson_obj_get(val, "name"));
if (name) event.name = name;
yyjson_val *desc = yyjson_obj_get(val, "description");
if (desc && !yyjson_is_null(desc)) {
const char* str = yyjson_get_str(desc);
if (str) event.description = str;
}
yyjson_val *logo = yyjson_obj_get(val, "logo");
if (logo && !yyjson_is_null(logo)) {
const char* str = yyjson_get_str(logo);
if (str) event.logo = str;
}
yyjson_val *subTopics = yyjson_obj_get(val, "subTopicIds");
if (subTopics) {
size_t sidx, smax;
yyjson_val *sval;
yyjson_arr_foreach(subTopics, sidx, smax, sval) {
event.subTopicIds.push_back(yyjson_get_uint(sval));
}
}
yyjson_val *subjectCode = yyjson_obj_get(val, "subjectCode");
if (subjectCode && !yyjson_is_null(subjectCode)) {
const char* str = yyjson_get_str(subjectCode);
if (str) event.subjectCode = str;
}
yyjson_val *subtitle = yyjson_obj_get(val, "subtitle");
if (subtitle && !yyjson_is_null(subtitle)) {
const char* str = yyjson_get_str(subtitle);
if (str) event.subtitle = str;
}
yyjson_val *topics = yyjson_obj_get(val, "topicIds");
if (topics) {
size_t tidx, tmax;
yyjson_val *tval;
yyjson_arr_foreach(topics, tidx, tmax, tval) {
event.topicIds.push_back(yyjson_get_uint(tval));
}
}
const char* keyStr = yyjson_get_str(key);
if (keyStr) {
catalog.events[keyStr] = event;
}
}
}
// Parse performances
yyjson_val *performances = yyjson_obj_get(root, "performances");
if (performances) {
size_t idx, max;
yyjson_val *val;
yyjson_arr_foreach(performances, idx, max, val) {
CITMPerformance perf;
perf.id = yyjson_get_uint(yyjson_obj_get(val, "id"));
perf.eventId = yyjson_get_uint(yyjson_obj_get(val, "eventId"));
yyjson_val *logo = yyjson_obj_get(val, "logo");
if (logo && !yyjson_is_null(logo)) {
const char* str = yyjson_get_str(logo);
if (str) perf.logo = str;
}
yyjson_val *name = yyjson_obj_get(val, "name");
if (name && !yyjson_is_null(name)) {
const char* str = yyjson_get_str(name);
if (str) perf.name = str;
}
// Parse prices
yyjson_val *prices = yyjson_obj_get(val, "prices");
if (prices) {
size_t pidx, pmax;
yyjson_val *pval;
yyjson_arr_foreach(prices, pidx, pmax, pval) {
CITMPrice price;
price.amount = yyjson_get_uint(yyjson_obj_get(pval, "amount"));
price.audienceSubCategoryId = yyjson_get_uint(yyjson_obj_get(pval, "audienceSubCategoryId"));
price.seatCategoryId = yyjson_get_uint(yyjson_obj_get(pval, "seatCategoryId"));
perf.prices.push_back(price);
}
}
// Parse seat categories
yyjson_val *seatCats = yyjson_obj_get(val, "seatCategories");
if (seatCats) {
size_t scidx, scmax;
yyjson_val *scval;
yyjson_arr_foreach(seatCats, scidx, scmax, scval) {
CITMSeatCategory seatCat;
seatCat.seatCategoryId = yyjson_get_uint(yyjson_obj_get(scval, "seatCategoryId"));
yyjson_val *areas = yyjson_obj_get(scval, "areas");
if (areas) {
size_t aidx, amax;
yyjson_val *aval;
yyjson_arr_foreach(areas, aidx, amax, aval) {
CITMArea area;
area.areaId = yyjson_get_uint(yyjson_obj_get(aval, "areaId"));
yyjson_val *blocks = yyjson_obj_get(aval, "blockIds");
if (blocks) {
size_t bidx, bmax;
yyjson_val *bval;
yyjson_arr_foreach(blocks, bidx, bmax, bval) {
area.blockIds.push_back(yyjson_get_uint(bval));
}
}
seatCat.areas.push_back(area);
}
}
perf.seatCategories.push_back(seatCat);
}
}
yyjson_val *seatMapImage = yyjson_obj_get(val, "seatMapImage");
if (seatMapImage && !yyjson_is_null(seatMapImage)) {
const char* str = yyjson_get_str(seatMapImage);
if (str) perf.seatMapImage = str;
}
perf.start = yyjson_get_uint(yyjson_obj_get(val, "start"));
const char* venueCode = yyjson_get_str(yyjson_obj_get(val, "venueCode"));
if (venueCode) perf.venueCode = venueCode;
catalog.performances.push_back(perf);
}
}
yyjson_doc_free(doc);
return catalog;
}
void bench_yyjson_parsing(const std::string &json_str) {
size_t input_volume = json_str.size();
printf("# input volume: %zu bytes\n", input_volume);
volatile bool result = true;
pretty_print(1, input_volume, "bench_yyjson_parsing",
bench([&json_str, &result]() {
try {
CitmCatalog data = yyjson_deserialize(json_str);
result = true;
} catch (...) {
result = false;
printf("parse error\n");
}
}));
}
#endif
std::string read_file(std::string filename) {
printf("# Reading file %s\n", filename.c_str());
constexpr size_t read_size = 4096;
auto stream = std::ifstream(filename);
stream.exceptions(std::ios_base::badbit);
if (!stream) {
std::cerr << "Error: Failed to open file " << filename << std::endl;
exit(EXIT_FAILURE);
}
std::string out;
auto buf = std::string(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 matches any of the comma-separated filters
bool matches_filter(const std::string& benchmark_name, const std::string& filter) {
if (filter.empty()) return true;
// Split filter by comma
size_t start = 0;
size_t end = filter.find(',');
while (end != std::string::npos) {
std::string token = filter.substr(start, end - start);
if (benchmark_name.find(token) != std::string::npos) {
return true;
}
start = end + 1;
end = filter.find(',', start);
}
// Check last token
std::string token = filter.substr(start);
return benchmark_name.find(token) != std::string::npos;
}
int main(int argc, char *argv[]) {
// Get the JSON file path from preprocessor or use default
std::string filename;
#ifdef JSON_FILE
filename = JSON_FILE;
#else
filename = "jsonexamples/citm_catalog.json";
#endif
std::string json_str = read_file(filename);
// Parse command-line arguments for filter
std::string filter;
for (int i = 1; i < argc; i++) {
std::string arg = argv[i];
if (arg == "-f" && i + 1 < argc) {
filter = argv[i + 1];
printf("# Filter: %s\n", filter.c_str());
i++;
}
}
// If no filter provided, run all benchmarks
if (filter.empty()) {
printf("# Running all benchmarks (use -f <filter> to run specific ones)\n");
}
// Benchmarking the parsing
if (matches_filter("nlohmann", filter)) {
bench_nlohmann_parsing(json_str);
}
#ifdef SIMDJSON_COMPETITION_RAPIDJSON
if (matches_filter("rapidjson", filter)) {
bench_rapidjson_parsing(json_str);
}
#endif
#ifdef SIMDJSON_COMPETITION_YYJSON
if (matches_filter("yyjson", filter)) {
bench_yyjson_parsing(json_str);
}
#endif
if (matches_filter("simdjson_static_reflection", filter)) {
bench_simdjson_static_reflection_parsing<CitmCatalog>(json_str);
}
#if SIMDJSON_STATIC_REFLECTION
if (matches_filter("simdjson_from", filter)) {
bench_simdjson_from_parsing<CitmCatalog>(json_str);
}
#endif
#ifdef SIMDJSON_RUST_VERSION
if (matches_filter("rust", filter)) {
printf("# Note: Rust/Serde parsing test\n");
bench_rust_parsing(json_str);
}
#endif
return EXIT_SUCCESS;
}
@@ -11,10 +11,6 @@
#include "nlohmann_citm_catalog_data.h"
#include "../benchmark_utils/benchmark_helper.h"
#ifdef SIMDJSON_COMPETITION_YYJSON
#include "yyjson_citm_catalog_data.h"
#endif
#if SIMDJSON_BENCH_CPP_REFLECT
#include <rfl.hpp>
#include <rfl/json.hpp>
@@ -39,15 +35,20 @@ void bench_reflect_cpp(CitmCatalog &data) {
#include "../serde-benchmark/serde_benchmark.h"
void bench_rust(serde_benchmark::CitmCatalog *data) {
serde_benchmark::set_citm_data(data);
size_t output_volume = serde_benchmark::serialize_citm_to_string();
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([&measured_volume, &output_volume]() {
measured_volume = serde_benchmark::serialize_citm_to_string();
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
@@ -67,55 +68,7 @@ void bench_nlohmann(CitmCatalog &data) {
}));
}
#ifdef SIMDJSON_COMPETITION_YYJSON
void bench_yyjson(CitmCatalog &data) {
std::string output = yyjson_serialize_citm(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_yyjson",
bench([&data, &measured_volume, &output_volume]() {
std::string output = yyjson_serialize_citm(data);
measured_volume = output.size();
if (measured_volume != output_volume) {
printf("mismatch\n");
}
}));
}
#endif
// Fair allocation variant: allocates fresh buffer each iteration (matches other libraries)
void bench_simdjson_static_reflection(CitmCatalog &data) {
// First run to determine expected size
simdjson::builder::string_builder sb_init;
simdjson::builder::append(sb_init, data);
std::string_view p_init;
if(sb_init.view().get(p_init)) {
std::cerr << "Error!" << std::endl;
}
size_t output_volume = p_init.size();
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]() {
// Fresh allocation each iteration - fair comparison
simdjson::builder::string_builder sb;
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");
}
}));
}
// Optimized variant: reuses buffer across iterations (shows API potential)
void bench_simdjson_static_reflection_reuse(CitmCatalog &data) {
simdjson::builder::string_builder sb;
simdjson::builder::append(sb, data);
std::string_view p;
@@ -127,7 +80,7 @@ void bench_simdjson_static_reflection_reuse(CitmCatalog &data) {
printf("# output volume: %zu bytes\n", output_volume);
volatile size_t measured_volume = 0;
pretty_print(sizeof(data), output_volume, "bench_simdjson_reuse_buffer",
pretty_print(sizeof(data), output_volume, "bench_simdjson_static_reflection",
bench([&data, &measured_volume, &output_volume, &sb]() {
sb.clear();
simdjson::builder::append(sb, data);
@@ -142,97 +95,25 @@ void bench_simdjson_static_reflection_reuse(CitmCatalog &data) {
}));
}
#if SIMDJSON_STATIC_REFLECTION
// Fair allocation variant: allocates fresh string each iteration
void bench_simdjson_to(CitmCatalog &data) {
// First run to determine size
std::string output_init;
if (simdjson::error_code err = simdjson::builder::to_json(data, output_init); err) {
std::cerr << "Error in to_json initialization!" << simdjson::error_message(err) << std::endl;
return;
}
size_t output_volume = output_init.size();
printf("# output volume: %zu bytes\n", output_volume);
volatile size_t measured_volume = 0;
pretty_print(sizeof(data), output_volume, "bench_simdjson_to",
bench([&data, &measured_volume, &output_volume]() {
// Fresh allocation each iteration - fair comparison
std::string output;
if (simdjson::error_code err = simdjson::builder::to_json(data, output); err) {
std::cerr << "Error in to_json!" << simdjson::error_message(err) << std::endl;
return;
}
measured_volume = output.size();
if (measured_volume != output_volume) {
printf("mismatch\n");
}
}));
}
// Optimized variant: reuses pre-allocated string
void bench_simdjson_to_reuse(CitmCatalog &data) {
std::string output;
if (simdjson::error_code err = simdjson::builder::to_json(data, output); err) {
std::cerr << "Error in to_json initialization!" << simdjson::error_message(err) << std::endl;
return;
}
size_t output_volume = output.size();
printf("# output volume: %zu bytes\n", output_volume);
// Pre-allocate string with sufficient capacity to avoid reallocation
output.reserve(output_volume * 2);
volatile size_t measured_volume = 0;
pretty_print(sizeof(data), output_volume, "bench_simdjson_to_reuse",
bench([&data, &measured_volume, &output_volume, &output]() {
// Reuse the pre-allocated string - avoids allocation
if (simdjson::error_code err = simdjson::builder::to_json(data, output); err) {
std::cerr << "Error in to_json!" << simdjson::error_message(err) << std::endl;
return;
}
measured_volume = output.size();
if (measured_volume != output_volume) {
printf("mismatch\n");
}
}));
}
#endif
simdjson::padded_string read_file(const std::string &file_path, size_t read_size = 65536) {
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);
simdjson::padded_string_builder builder;
std::string out;
std::string buf(read_size, '\0');
while (stream.read(&buf[0], read_size)) {
builder.append(buf.data(), size_t(stream.gcount()));
out.append(buf, 0, size_t(stream.gcount()));
}
builder.append(buf.data(), size_t(stream.gcount()));
return builder.convert();
out.append(buf, 0, size_t(stream.gcount()));
return out;
}
// Function to check if benchmark name matches any of the comma-separated filters
// Function to check if benchmark name contains filter substring
bool matches_filter(const std::string& benchmark_name, const std::string& filter) {
if (filter.empty()) return true;
// Split filter by comma
size_t start = 0;
size_t end = filter.find(',');
while (end != std::string::npos) {
std::string token = filter.substr(start, end - start);
if (benchmark_name.find(token) != std::string::npos) {
return true;
}
start = end + 1;
end = filter.find(',', start);
}
// Check last token
std::string token = filter.substr(start);
return benchmark_name.find(token) != std::string::npos;
return filter.empty() || benchmark_name.find(filter) != std::string::npos;
}
int main(int argc, char* argv[]) {
@@ -250,12 +131,12 @@ int main(int argc, char* argv[]) {
}
}
// Testing correctness of round-trip (serialization + deserialization)
simdjson::padded_string json_str = read_file(JSON_FILE);
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(json_str).get(doc)) {
if(parser.iterate(simdjson::pad(json_str)).get(doc)) {
std::cerr << "Error loading the document!" << std::endl;
return EXIT_FAILURE;
}
@@ -266,37 +147,18 @@ int main(int argc, char* argv[]) {
}
// Benchmarking the serialization
// Note: simdjson benchmarks include both "fair" (fresh allocation) and "reuse" (buffer reuse) variants
// The "fair" variants allocate fresh memory each iteration, matching other libraries' behavior
// The "reuse" variants demonstrate the API's potential when buffer reuse is possible
if (matches_filter("nlohmann", filter)) {
bench_nlohmann(my_struct);
}
#ifdef SIMDJSON_COMPETITION_YYJSON
if (matches_filter("yyjson", filter)) {
bench_yyjson(my_struct);
}
#endif
if (matches_filter("simdjson_static_reflection", filter)) {
bench_simdjson_static_reflection(my_struct);
}
if (matches_filter("simdjson_reuse", filter)) {
bench_simdjson_static_reflection_reuse(my_struct);
}
#if SIMDJSON_STATIC_REFLECTION
if (matches_filter("simdjson_to", filter)) {
bench_simdjson_to(my_struct);
}
if (matches_filter("simdjson_to_reuse", filter)) {
bench_simdjson_to_reuse(my_struct);
}
#endif
#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.data(), json_str.size());
serde_benchmark::citm_from_str(json_str.c_str(), json_str.size());
if (rust_data == nullptr) {
printf("# Failed to initialize Rust data structure\n");
@@ -4,65 +4,79 @@
#include <string>
#include <vector>
#include <map>
#include <optional>
#include <cstdint>
// Price structure - field names must match JSON keys for reflection
struct CITMPrice {
uint64_t amount;
uint64_t audienceSubCategoryId;
uint64_t seatCategoryId;
bool operator==(const CITMPrice&) const = default;
struct Area {
int64_t id;
std::string name;
int64_t parent;
std::vector<int64_t> childAreas;
bool operator==(const Area &other) const = default;
};
struct CITMArea {
uint64_t areaId;
std::vector<uint64_t> blockIds;
bool operator==(const CITMArea&) const = default;
struct AudienceSubCategory {
int64_t id;
std::string name;
int64_t parent;
bool operator==(const AudienceSubCategory &other) const = default;
};
struct CITMSeatCategory {
std::vector<CITMArea> areas;
uint64_t seatCategoryId;
bool operator==(const CITMSeatCategory&) 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 CITMPerformance {
uint64_t id;
uint64_t eventId;
std::optional<std::string> logo;
std::optional<std::string> name;
std::vector<CITMPrice> prices;
std::vector<CITMSeatCategory> seatCategories;
std::optional<std::string> seatMapImage;
uint64_t start;
std::string venueCode;
bool operator==(const CITMPerformance&) 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 CITMEvent {
uint64_t id;
std::string name;
std::optional<std::string> description;
std::optional<std::string> logo;
std::vector<uint64_t> subTopicIds;
std::optional<std::string> subjectCode;
std::optional<std::string> subtitle;
std::vector<uint64_t> topicIds;
bool operator==(const CITMEvent&) 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, CITMEvent> events;
std::vector<CITMPerformance> performances;
bool operator==(const CitmCatalog&) const = default;
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;
};
// Type aliases
using Event = CITMEvent;
using Performance = CITMPerformance;
using Price = CITMPrice;
using SeatArea = CITMArea;
using SeatCategoryInfo = CITMSeatCategory;
#endif
#endif
@@ -8,72 +8,164 @@
using json = nlohmann::json;
// ---- CITMPrice ----
inline void to_json(json &j, const CITMPrice &p) {
// ---- Area ----
inline void to_json(json &j, const Area &a) {
j = json{
{"amount", p.amount},
{"audienceSubCategoryId", p.audienceSubCategoryId},
{"seatCategoryId", p.seatCategoryId}
{"id", a.id},
{"name", a.name},
{"parent", a.parent},
{"childAreas", a.childAreas}
};
}
// ---- CITMArea ----
inline void to_json(json &j, const CITMArea &a) {
j = json{
{"areaId", a.areaId},
{"blockIds", a.blockIds}
};
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);
}
// ---- CITMSeatCategory ----
inline void to_json(json &j, const CITMSeatCategory &s) {
// ---- AudienceSubCategory ----
inline void to_json(json &j, const AudienceSubCategory &asc) {
j = json{
{"areas", s.areas},
{"seatCategoryId", s.seatCategoryId}
{"id", asc.id},
{"name", asc.name},
{"parent", asc.parent}
};
}
// ---- CITMPerformance ----
inline void to_json(json &j, const CITMPerformance &p) {
j = json{
{"id", p.id},
{"eventId", p.eventId},
{"logo", p.logo},
{"name", p.name},
{"prices", p.prices},
{"seatCategories", p.seatCategories},
{"seatMapImage", p.seatMapImage},
{"start", p.start},
{"venueCode", p.venueCode}
};
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);
}
// ---- CITMEvent ----
inline void to_json(json &j, const CITMEvent &e) {
// ---- Event ----
inline void to_json(json &j, const Event &e) {
j = json{
{"id", e.id},
{"name", e.name},
{"description", e.description},
{"logo", e.logo},
{"subTopicIds", e.subTopicIds},
{"subjectCode", e.subjectCode},
{"subtitle", e.subtitle},
{"topicIds", e.topicIds}
{"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}
{"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);
}
// Serialization function
// 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
@@ -1,189 +0,0 @@
#ifndef RAPIDJSON_CITM_CATALOG_DATA_H
#define RAPIDJSON_CITM_CATALOG_DATA_H
#include "citm_catalog_data.h"
#include <rapidjson/document.h>
#include <rapidjson/writer.h>
#include <rapidjson/stringbuffer.h>
#include <rapidjson/error/en.h>
using namespace rapidjson;
// RapidJSON deserialization for CITM Catalog data
CitmCatalog rapidjson_deserialize_citm(const std::string& json_str) {
Document doc;
doc.Parse(json_str.c_str());
if (doc.HasParseError()) {
throw std::runtime_error("RapidJSON parse error");
}
CitmCatalog catalog;
// Parse events
if (doc.HasMember("events") && doc["events"].IsObject()) {
const Value& events = doc["events"];
for (auto it = events.MemberBegin(); it != events.MemberEnd(); ++it) {
Event event;
const Value& ev = it->value;
if (ev.HasMember("description") && ev["description"].IsString())
event.description = ev["description"].GetString();
if (ev.HasMember("id") && ev["id"].IsUint64())
event.id = ev["id"].GetUint64();
if (ev.HasMember("logo") && ev["logo"].IsString())
event.logo = ev["logo"].GetString();
if (ev.HasMember("name") && ev["name"].IsString())
event.name = ev["name"].GetString();
if (ev.HasMember("subjectCode") && ev["subjectCode"].IsString())
event.subjectCode = ev["subjectCode"].GetString();
if (ev.HasMember("subtitle") && ev["subtitle"].IsString())
event.subtitle = ev["subtitle"].GetString();
if (ev.HasMember("topicIds") && ev["topicIds"].IsArray()) {
const Value& topics = ev["topicIds"];
for (SizeType j = 0; j < topics.Size(); j++) {
if (topics[j].IsUint64())
event.topicIds.push_back(topics[j].GetUint64());
}
}
if (ev.HasMember("subTopicIds") && ev["subTopicIds"].IsArray()) {
const Value& subtopics = ev["subTopicIds"];
for (SizeType j = 0; j < subtopics.Size(); j++) {
if (subtopics[j].IsUint64())
event.subTopicIds.push_back(subtopics[j].GetUint64());
}
}
catalog.events[it->name.GetString()] = event;
}
}
// Parse performances
if (doc.HasMember("performances") && doc["performances"].IsArray()) {
const Value& performances = doc["performances"];
for (SizeType i = 0; i < performances.Size(); i++) {
Performance perf;
const Value& p = performances[i];
if (p.HasMember("id") && p["id"].IsUint64())
perf.id = p["id"].GetUint64();
if (p.HasMember("eventId") && p["eventId"].IsUint64())
perf.eventId = p["eventId"].GetUint64();
if (p.HasMember("start") && p["start"].IsUint64())
perf.start = p["start"].GetUint64();
if (p.HasMember("venueCode") && p["venueCode"].IsString())
perf.venueCode = p["venueCode"].GetString();
if (p.HasMember("name") && p["name"].IsString())
perf.name = p["name"].GetString();
catalog.performances.push_back(perf);
}
}
// Parse other string maps
auto parseStringMap = [&doc](const char* key, std::map<std::string, std::string>& target) {
if (doc.HasMember(key) && doc[key].IsObject()) {
const Value& obj = doc[key];
for (auto it = obj.MemberBegin(); it != obj.MemberEnd(); ++it) {
if (it->value.IsString()) {
target[it->name.GetString()] = it->value.GetString();
}
}
}
};
return catalog;
}
// RapidJSON serialization for CITM Catalog data
std::string rapidjson_serialize_citm(const CitmCatalog& catalog) {
Document doc;
doc.SetObject();
Document::AllocatorType& allocator = doc.GetAllocator();
// Serialize events
Value events_obj(kObjectType);
for (const auto& [key, event] : catalog.events) {
Value event_obj(kObjectType);
if (event.description) {
Value desc;
desc.SetString(event.description->c_str(), allocator);
event_obj.AddMember("description", desc, allocator);
}
event_obj.AddMember("id", event.id, allocator);
if (event.logo) {
Value logo;
logo.SetString(event.logo->c_str(), allocator);
event_obj.AddMember("logo", logo, allocator);
}
Value name;
name.SetString(event.name.c_str(), allocator);
event_obj.AddMember("name", name, allocator);
if (event.subjectCode) {
Value subject;
subject.SetString(event.subjectCode->c_str(), allocator);
event_obj.AddMember("subjectCode", subject, allocator);
}
if (event.subtitle) {
Value subtitle;
subtitle.SetString(event.subtitle->c_str(), allocator);
event_obj.AddMember("subtitle", subtitle, allocator);
}
Value topicIds(kArrayType);
for (uint64_t id : event.topicIds) {
topicIds.PushBack(id, allocator);
}
event_obj.AddMember("topicIds", topicIds, allocator);
Value subTopicIds(kArrayType);
for (uint64_t id : event.subTopicIds) {
subTopicIds.PushBack(id, allocator);
}
event_obj.AddMember("subTopicIds", subTopicIds, allocator);
Value key_val;
key_val.SetString(key.c_str(), allocator);
events_obj.AddMember(key_val, event_obj, allocator);
}
doc.AddMember("events", events_obj, allocator);
// Serialize performances
Value performances_array(kArrayType);
for (const auto& perf : catalog.performances) {
Value perf_obj(kObjectType);
perf_obj.AddMember("id", perf.id, allocator);
perf_obj.AddMember("eventId", perf.eventId, allocator);
perf_obj.AddMember("start", perf.start, allocator);
Value venue;
venue.SetString(perf.venueCode.c_str(), allocator);
perf_obj.AddMember("venueCode", venue, allocator);
if (perf.name) {
Value name;
name.SetString(perf.name->c_str(), allocator);
perf_obj.AddMember("name", name, allocator);
}
performances_array.PushBack(perf_obj, allocator);
}
doc.AddMember("performances", performances_array, allocator);
StringBuffer buffer;
Writer<StringBuffer> writer(buffer);
doc.Accept(writer);
return buffer.GetString();
}
#endif // RAPIDJSON_CITM_CATALOG_DATA_H
@@ -1,231 +0,0 @@
#ifndef YYJSON_CITM_CATALOG_DATA_H
#define YYJSON_CITM_CATALOG_DATA_H
#include "citm_catalog_data.h"
#include <yyjson.h>
#include <string>
#include <stdexcept>
// yyjson deserialization for CITM Catalog data
// Matches C++ CitmCatalog struct (only events + performances)
CitmCatalog yyjson_deserialize_citm(const std::string &json_str) {
CitmCatalog catalog;
yyjson_doc *doc = yyjson_read(json_str.c_str(), json_str.size(), 0);
if (!doc) {
throw std::runtime_error("yyjson parse error");
}
yyjson_val *root = yyjson_doc_get_root(doc);
if (!root) {
yyjson_doc_free(doc);
return catalog;
}
// Parse events
yyjson_val *events_val = yyjson_obj_get(root, "events");
if (events_val && yyjson_is_obj(events_val)) {
size_t idx, max;
yyjson_val *key, *val;
yyjson_obj_foreach(events_val, idx, max, key, val) {
CITMEvent event;
yyjson_val *v;
v = yyjson_obj_get(val, "description");
if (v && yyjson_is_str(v)) event.description = yyjson_get_str(v);
v = yyjson_obj_get(val, "id");
if (v && yyjson_is_uint(v)) event.id = yyjson_get_uint(v);
v = yyjson_obj_get(val, "logo");
if (v && yyjson_is_str(v)) event.logo = yyjson_get_str(v);
v = yyjson_obj_get(val, "name");
if (v && yyjson_is_str(v)) event.name = yyjson_get_str(v);
v = yyjson_obj_get(val, "subjectCode");
if (v && yyjson_is_str(v)) event.subjectCode = yyjson_get_str(v);
v = yyjson_obj_get(val, "subtitle");
if (v && yyjson_is_str(v)) event.subtitle = yyjson_get_str(v);
// Parse topicIds array
v = yyjson_obj_get(val, "topicIds");
if (v && yyjson_is_arr(v)) {
size_t arr_idx, arr_max;
yyjson_val *arr_val;
yyjson_arr_foreach(v, arr_idx, arr_max, arr_val) {
if (yyjson_is_uint(arr_val))
event.topicIds.push_back(yyjson_get_uint(arr_val));
}
}
// Parse subTopicIds array
v = yyjson_obj_get(val, "subTopicIds");
if (v && yyjson_is_arr(v)) {
size_t arr_idx, arr_max;
yyjson_val *arr_val;
yyjson_arr_foreach(v, arr_idx, arr_max, arr_val) {
if (yyjson_is_uint(arr_val))
event.subTopicIds.push_back(yyjson_get_uint(arr_val));
}
}
if (yyjson_is_str(key))
catalog.events[yyjson_get_str(key)] = event;
}
}
// Parse performances (simplified - full parsing would need prices/seatCategories)
yyjson_val *performances_val = yyjson_obj_get(root, "performances");
if (performances_val && yyjson_is_arr(performances_val)) {
size_t idx, max;
yyjson_val *perf_val;
yyjson_arr_foreach(performances_val, idx, max, perf_val) {
CITMPerformance perf;
yyjson_val *v;
v = yyjson_obj_get(perf_val, "id");
if (v && yyjson_is_uint(v)) perf.id = yyjson_get_uint(v);
v = yyjson_obj_get(perf_val, "eventId");
if (v && yyjson_is_uint(v)) perf.eventId = yyjson_get_uint(v);
v = yyjson_obj_get(perf_val, "start");
if (v && yyjson_is_uint(v)) perf.start = yyjson_get_uint(v);
v = yyjson_obj_get(perf_val, "venueCode");
if (v && yyjson_is_str(v)) perf.venueCode = yyjson_get_str(v);
v = yyjson_obj_get(perf_val, "name");
if (v && yyjson_is_str(v)) perf.name = yyjson_get_str(v);
v = yyjson_obj_get(perf_val, "logo");
if (v && yyjson_is_str(v)) perf.logo = yyjson_get_str(v);
v = yyjson_obj_get(perf_val, "seatMapImage");
if (v && yyjson_is_str(v)) perf.seatMapImage = yyjson_get_str(v);
// Note: prices and seatCategories parsing omitted for brevity
// The serialization benchmark uses data loaded by simdjson
catalog.performances.push_back(perf);
}
}
yyjson_doc_free(doc);
return catalog;
}
// Helper to add optional string field
static inline void yyjson_add_optional_str(yyjson_mut_doc *doc, yyjson_mut_val *obj,
const char *key, const std::optional<std::string> &val) {
if (val.has_value()) {
yyjson_mut_obj_add_str(doc, obj, key, val->c_str());
} else {
yyjson_mut_obj_add_null(doc, obj, key);
}
}
// yyjson serialization for CITM Catalog data
// Matches C++ CitmCatalog struct exactly (only events + performances)
std::string yyjson_serialize_citm(const CitmCatalog &catalog) {
yyjson_mut_doc *doc = yyjson_mut_doc_new(NULL);
yyjson_mut_val *root = yyjson_mut_obj(doc);
yyjson_mut_doc_set_root(doc, root);
// Create events object
yyjson_mut_val *events_obj = yyjson_mut_obj(doc);
for (const auto& [key, event] : catalog.events) {
yyjson_mut_val *event_obj = yyjson_mut_obj(doc);
yyjson_add_optional_str(doc, event_obj, "description", event.description);
yyjson_mut_obj_add_uint(doc, event_obj, "id", event.id);
yyjson_add_optional_str(doc, event_obj, "logo", event.logo);
// name is not optional in CITMEvent
yyjson_mut_obj_add_str(doc, event_obj, "name", event.name.c_str());
// Add subTopicIds array
yyjson_mut_val *subtopic_ids = yyjson_mut_arr(doc);
for (uint64_t id : event.subTopicIds) {
yyjson_mut_arr_add_uint(doc, subtopic_ids, id);
}
yyjson_mut_obj_add_val(doc, event_obj, "subTopicIds", subtopic_ids);
yyjson_add_optional_str(doc, event_obj, "subjectCode", event.subjectCode);
yyjson_add_optional_str(doc, event_obj, "subtitle", event.subtitle);
// Add topicIds array
yyjson_mut_val *topic_ids = yyjson_mut_arr(doc);
for (uint64_t id : event.topicIds) {
yyjson_mut_arr_add_uint(doc, topic_ids, id);
}
yyjson_mut_obj_add_val(doc, event_obj, "topicIds", topic_ids);
yyjson_mut_obj_add_val(doc, events_obj, key.c_str(), event_obj);
}
yyjson_mut_obj_add_val(doc, root, "events", events_obj);
// Create performances array
yyjson_mut_val *performances_array = yyjson_mut_arr(doc);
for (const auto& perf : catalog.performances) {
yyjson_mut_val *perf_obj = yyjson_mut_obj(doc);
yyjson_mut_obj_add_uint(doc, perf_obj, "eventId", perf.eventId);
yyjson_mut_obj_add_uint(doc, perf_obj, "id", perf.id);
yyjson_add_optional_str(doc, perf_obj, "logo", perf.logo);
yyjson_add_optional_str(doc, perf_obj, "name", perf.name);
// Add prices array
yyjson_mut_val *prices_array = yyjson_mut_arr(doc);
for (const auto& price : perf.prices) {
yyjson_mut_val *price_obj = yyjson_mut_obj(doc);
yyjson_mut_obj_add_uint(doc, price_obj, "amount", price.amount);
yyjson_mut_obj_add_uint(doc, price_obj, "audienceSubCategoryId", price.audienceSubCategoryId);
yyjson_mut_obj_add_uint(doc, price_obj, "seatCategoryId", price.seatCategoryId);
yyjson_mut_arr_append(prices_array, price_obj);
}
yyjson_mut_obj_add_val(doc, perf_obj, "prices", prices_array);
// Add seatCategories array
yyjson_mut_val *seat_cats_array = yyjson_mut_arr(doc);
for (const auto& seatCat : perf.seatCategories) {
yyjson_mut_val *seat_cat_obj = yyjson_mut_obj(doc);
// Add areas array
yyjson_mut_val *areas_array = yyjson_mut_arr(doc);
for (const auto& area : seatCat.areas) {
yyjson_mut_val *area_obj = yyjson_mut_obj(doc);
yyjson_mut_obj_add_uint(doc, area_obj, "areaId", area.areaId);
yyjson_mut_val *block_ids = yyjson_mut_arr(doc);
for (uint64_t blockId : area.blockIds) {
yyjson_mut_arr_add_uint(doc, block_ids, blockId);
}
yyjson_mut_obj_add_val(doc, area_obj, "blockIds", block_ids);
yyjson_mut_arr_append(areas_array, area_obj);
}
yyjson_mut_obj_add_val(doc, seat_cat_obj, "areas", areas_array);
yyjson_mut_obj_add_uint(doc, seat_cat_obj, "seatCategoryId", seatCat.seatCategoryId);
yyjson_mut_arr_append(seat_cats_array, seat_cat_obj);
}
yyjson_mut_obj_add_val(doc, perf_obj, "seatCategories", seat_cats_array);
yyjson_add_optional_str(doc, perf_obj, "seatMapImage", perf.seatMapImage);
yyjson_mut_obj_add_uint(doc, perf_obj, "start", perf.start);
yyjson_mut_obj_add_str(doc, perf_obj, "venueCode", perf.venueCode.c_str());
yyjson_mut_arr_append(performances_array, perf_obj);
}
yyjson_mut_obj_add_val(doc, root, "performances", performances_array);
// Write to string
char *json_output = yyjson_mut_write(doc, 0, NULL);
std::string result(json_output);
free(json_output);
yyjson_mut_doc_free(doc);
return result;
}
#endif // YYJSON_CITM_CATALOG_DATA_H
+369 -238
View File
@@ -5,165 +5,405 @@ extern crate libc;
use libc::{c_char, size_t};
use serde::{Serialize, Deserialize};
use std::{collections::HashMap, ffi::CString, ptr, slice};
use serde::de::{self, Deserializer};
/******************************************************/
/******************************************************/
/**
* Warning: the C++ code may not generate the same JSON.
*/
/******************************************************/
/******************************************************/
//==============================================================================
// Twitter Benchmark Structures
// These match the C++ TwitterData structures exactly
//==============================================================================
// This has no equivalent in C++:
#[derive(Serialize, Deserialize)]
pub struct Metadata {
result_type: String,
iso_language_code: String,
}
#[derive(Serialize, Deserialize)]
pub struct User {
id: u64,
id: i64,
id_str: String,
name: String,
screen_name: String,
location: String,
description: String,
// C++ does not have those:
// url: Option<String>,
//protected: bool,
//listed_count: i64,
//created_at: String,
//favourites_count: i64,
//utc_offset: Option<i64>,
//time_zone: Option<String>,
//geo_enabled: bool,
verified: bool,
followers_count: u64,
friends_count: u64,
statuses_count: u64,
followers_count: i64,
friends_count: i64,
statuses_count: i64,
// C++ does not have those:
//lang: String,
//profile_background_color: String,
//profile_background_image_url: String,
//profile_background_image_url_https: String,
//profile_background_tile: bool,
//profile_image_url: String,
//profile_image_url_https: String,
//profile_banner_url: Option<String>,
//profile_link_color: String,
//profile_sidebar_border_color: String,
//profile_sidebar_fill_color: String,
//profile_text_color: String,
//profile_use_background_image: bool,
//default_profile: bool,
//default_profile_image: bool,
//following: bool,
//follow_request_sent: bool,
//notifications: bool,
}
#[derive(Serialize, Deserialize)]
pub struct Hashtag {
text: String,
// C++ has those but D. Lemire does not know what they are, they don't appear in the JSON:
// int64_t indices_start;
// int64_t indices_end;
}
#[derive(Serialize, Deserialize)]
pub struct Url {
url: String,
expanded_url: String,
display_url: String,
// C++ has those but D. Lemire does not know what they are, they don't appear in the JSON:
// int64_t indices_start;
// int64_t indices_end;
}
#[derive(Serialize, Deserialize)]
pub struct UserMention {
id: i64,
name: String,
screen_name: String,
// Not in the C++ equivalent:
//id_str: String,
//indices: Vec<i64>,
// C++ has those but D. Lemire does not know what they are, they don't appear in the JSON:
// int64_t indices_start;
// int64_t indices_end;
}
#[derive(Serialize, Deserialize)]
pub struct Entities {
hashtags: Vec<Hashtag>,
urls: Vec<Url>,
user_mentions: Vec<UserMention>,
}
#[derive(Serialize, Deserialize)]
pub struct Status {
created_at: String,
id: u64,
id: i64,
text: String,
user: User,
retweet_count: u64,
favorite_count: u64,
entities: Entities,
retweet_count: i64,
favorite_count: i64,
favorited: bool,
retweeted: bool,
// None of these are in the C++ equivalent:
/*
metadata: Metadata,
id_str: String,
source: String,
truncated: bool,
in_reply_to_status_id: Option<i64>,
in_reply_to_status_id_str: Option<String>,
in_reply_to_user_id: Option<i64>,
in_reply_to_user_id_str: Option<String>,
in_reply_to_screen_name: Option<String>,
geo: Option<String>,
coordinates: Option<String>,
place: Option<String>,
contributors: Option<String>,
lang: String,
*/
}
#[derive(Serialize, Deserialize)]
pub struct TwitterData {
statuses: Vec<Status>,
}
static mut TWITTER_DATA: *mut TwitterData = std::ptr::null_mut();
#[no_mangle]
pub unsafe extern "C" fn twitter_from_str(raw_input: *const c_char, raw_input_length: size_t) -> *mut TwitterData {
let input = std::str::from_utf8_unchecked(slice::from_raw_parts(raw_input as *const u8, raw_input_length));
match serde_json::from_str(&input) {
Ok(result) => Box::into_raw(Box::new(result)),
Err(_) => std::ptr::null_mut(),
}
pub unsafe extern "C" fn twitter_from_str(raw_input: *const c_char, raw_input_length: size_t) -> *mut TwitterData {
let input = std::str::from_utf8_unchecked(slice::from_raw_parts(raw_input as *const u8, raw_input_length));
match serde_json::from_str(&input) {
Ok(result) => Box::into_raw(Box::new(result)),
Err(_) => std::ptr::null_mut(),
}
}
#[no_mangle]
pub unsafe extern "C" fn set_twitter_data(raw: *mut TwitterData) {
TWITTER_DATA = raw;
pub unsafe extern "C" fn str_from_twitter(raw: *mut TwitterData) -> *const c_char {
let twitter_thing = { &*raw };
let serialized = serde_json::to_string(&twitter_thing).unwrap();
return std::ffi::CString::new(serialized.as_str()).unwrap().into_raw()
}
#[no_mangle]
pub unsafe extern "C" fn serialize_twitter_to_string() -> usize {
if TWITTER_DATA.is_null() {
return 0;
}
let data = &*TWITTER_DATA;
serde_json::to_string(data).unwrap().len()
}
#[no_mangle]
pub unsafe extern "C" fn free_twitter(raw: *mut TwitterData) {
if raw.is_null() {
return;
}
drop(Box::from_raw(raw))
if raw.is_null() {
return;
}
drop(Box::from_raw(raw))
}
#[no_mangle]
pub unsafe extern fn free_string(ptr: *const c_char) {
let _ = std::ffi::CString::from_raw(ptr as *mut _);
}
//==============================================================================
// CITM Catalog Benchmark Structures
// These match the C++ CitmCatalog structures EXACTLY for fair comparison
//==============================================================================
// Functions associated with the CitmCatalog benchmark
/// Matches C++ CITMPrice struct exactly
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct CITMPrice {
pub amount: u64,
#[serde(rename = "audienceSubCategoryId")]
pub audience_sub_category_id: u64,
#[serde(rename = "seatCategoryId")]
pub seat_category_id: u64,
#[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>,
}
/// Matches C++ CITMArea struct exactly
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct CITMArea {
#[serde(rename = "areaId")]
pub area_id: u64,
#[serde(rename = "blockIds")]
pub block_ids: Vec<u64>,
#[derive(Serialize, Deserialize)]
pub struct AudienceSubCategory {
pub id: i64,
pub name: Option<String>, // Changed to Option
pub parent: i64,
}
/// Matches C++ CITMSeatCategory struct exactly
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct CITMSeatCategory {
pub areas: Vec<CITMArea>,
#[serde(rename = "seatCategoryId")]
pub seat_category_id: u64,
}
/// Matches C++ CITMPerformance struct exactly
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct CITMPerformance {
pub id: u64,
#[serde(rename = "eventId")]
pub event_id: u64,
#[serde(default)]
pub logo: Option<String>,
#[serde(default)]
pub name: Option<String>,
pub prices: Vec<CITMPrice>,
#[serde(rename = "seatCategories")]
pub seat_categories: Vec<CITMSeatCategory>,
#[serde(default)]
#[serde(rename = "seatMapImage")]
pub seat_map_image: Option<String>,
pub start: u64,
#[serde(rename = "venueCode")]
pub venue_code: String,
}
/// Matches C++ CITMEvent struct exactly
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct CITMEvent {
pub id: u64,
#[serde(default)]
pub name: Option<String>,
#[derive(Serialize, Deserialize, Debug)]
pub struct Event {
#[serde(default)]
pub description: Option<String>,
pub id: i64,
#[serde(default)]
pub logo: Option<String>,
#[serde(default)]
#[serde(rename = "subTopicIds")]
pub sub_topic_ids: Vec<u64>,
pub name: Option<String>,
#[serde(default)]
#[serde(rename = "subjectCode")]
pub subject_code: Option<String>,
pub subTopicIds: Vec<i64>,
#[serde(default)]
pub subjectCode: Option<String>,
#[serde(default)]
pub subtitle: Option<String>,
#[serde(default)]
#[serde(rename = "topicIds")]
pub topic_ids: Vec<u64>,
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)
}
/// Matches C++ CitmCatalog struct exactly - ONLY events and performances
/// This is the key fix: we serialize only what C++ serializes
#[derive(Serialize, Deserialize, Debug)]
pub struct CitmCatalog {
pub events: HashMap<String, CITMEvent>,
pub performances: Vec<CITMPerformance>,
#[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>,
}
static mut CITM_DATA: *mut CitmCatalog = std::ptr::null_mut();
/// Creates a CitmCatalog from a JSON string (UTF-8 encoded).
/// Only extracts events and performances to match C++ behavior.
#[no_mangle]
pub unsafe extern "C" fn citm_from_str(
raw_input: *const c_char,
@@ -174,6 +414,7 @@ pub unsafe extern "C" fn citm_from_str(
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,
@@ -183,23 +424,12 @@ pub unsafe extern "C" fn citm_from_str(
}
};
// Parse the full JSON to extract only events and performances
match serde_json::from_str::<serde_json::Value>(input_str) {
Ok(full_json) => {
// Extract only the fields we need (matching C++ behavior)
let events: HashMap<String, CITMEvent> = full_json.get("events")
.and_then(|v| serde_json::from_value(v.clone()).ok())
.unwrap_or_default();
let performances: Vec<CITMPerformance> = full_json.get("performances")
.and_then(|v| serde_json::from_value(v.clone()).ok())
.unwrap_or_default();
let catalog = CitmCatalog { events, performances };
Box::into_raw(Box::new(catalog))
},
// 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()
}
}
@@ -207,17 +437,30 @@ pub unsafe extern "C" fn citm_from_str(
/// Serializes a CitmCatalog into a JSON string (UTF-8).
#[no_mangle]
pub unsafe extern "C" fn set_citm_data(raw: *mut CitmCatalog) {
CITM_DATA = raw;
}
#[no_mangle]
pub unsafe extern "C" fn serialize_citm_to_string() -> usize {
if CITM_DATA.is_null() {
return 0;
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()
}
}
let data = &*CITM_DATA;
return serde_json::to_string(data).unwrap().len();
}
/// Frees the CitmCatalog pointer.
@@ -232,120 +475,8 @@ pub unsafe extern "C" fn free_citm(raw_catalog: *mut CitmCatalog) {
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);
}
}
}
//==============================================================================
// FFI Overhead Measurement Functions
// These allow measuring the actual FFI overhead vs pure Rust serialization
//==============================================================================
/// Result structure for FFI overhead measurement
#[repr(C)]
pub struct FfiOverheadResult {
/// Time in nanoseconds for pure serde_json::to_string() (no FFI overhead)
pub pure_serde_ns: u64,
/// Time in nanoseconds for serde + CString conversion
pub serde_plus_cstring_ns: u64,
/// Number of iterations performed
pub iterations: u64,
/// Output size in bytes (for verification)
pub output_size: u64,
}
/// Prevents compiler from optimizing away the value
/// Works on stable Rust (unlike std::hint::black_box which is unstable)
#[inline(never)]
fn black_box<T>(dummy: T) -> T {
unsafe {
let ret = std::ptr::read_volatile(&dummy);
std::mem::forget(dummy);
ret
}
}
/// Measures FFI overhead for Twitter serialization.
/// Performs `iterations` serializations entirely in Rust and returns timing data.
/// This allows comparing against per-call FFI overhead.
#[no_mangle]
pub unsafe extern "C" fn measure_twitter_ffi_overhead(
raw: *mut TwitterData,
iterations: u64
) -> FfiOverheadResult {
use std::time::Instant;
let twitter_data = &*raw;
let output_size: u64;
// Warm-up run
let warmup = serde_json::to_string(&twitter_data).unwrap();
output_size = warmup.len() as u64;
// Measure pure serde_json::to_string() - no CString conversion
let start_pure = Instant::now();
for _ in 0..iterations {
let serialized = serde_json::to_string(&twitter_data).unwrap();
// Prevent optimization from eliminating the work
black_box(&serialized);
}
let pure_serde_ns = start_pure.elapsed().as_nanos() as u64;
// Measure serde + CString conversion (but not FFI return)
let start_cstring = Instant::now();
for _ in 0..iterations {
let serialized = serde_json::to_string(&twitter_data).unwrap();
let cstring = CString::new(serialized).unwrap();
// Prevent optimization from eliminating the work
black_box(&cstring);
}
let serde_plus_cstring_ns = start_cstring.elapsed().as_nanos() as u64;
FfiOverheadResult {
pure_serde_ns,
serde_plus_cstring_ns,
iterations,
output_size,
}
}
/// Measures FFI overhead for CITM serialization.
#[no_mangle]
pub unsafe extern "C" fn measure_citm_ffi_overhead(
raw: *mut CitmCatalog,
iterations: u64
) -> FfiOverheadResult {
use std::time::Instant;
let catalog = &*raw;
let output_size: u64;
// Warm-up run
let warmup = serde_json::to_string(&catalog).unwrap();
output_size = warmup.len() as u64;
// Measure pure serde_json::to_string() - no CString conversion
let start_pure = Instant::now();
for _ in 0..iterations {
let serialized = serde_json::to_string(&catalog).unwrap();
black_box(&serialized);
}
let pure_serde_ns = start_pure.elapsed().as_nanos() as u64;
// Measure serde + CString conversion
let start_cstring = Instant::now();
for _ in 0..iterations {
let serialized = serde_json::to_string(&catalog).unwrap();
let cstring = CString::new(serialized).unwrap();
black_box(&cstring);
}
let serde_plus_cstring_ns = start_cstring.elapsed().as_nanos() as u64;
FfiOverheadResult {
pure_serde_ns,
serde_plus_cstring_ns,
iterations,
output_size,
}
}
}
@@ -4,7 +4,6 @@
/* Generated with cbindgen:0.28.0 */
/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */
/* Note: FfiOverheadResult and measurement functions added manually */
#include <cstdarg>
#include <cstdint>
@@ -18,25 +17,11 @@ struct CitmCatalog;
struct TwitterData;
/// Result structure for FFI overhead measurement
struct FfiOverheadResult {
/// Time in nanoseconds for pure serde_json::to_string() (no FFI overhead)
uint64_t pure_serde_ns;
/// Time in nanoseconds for serde + CString conversion
uint64_t serde_plus_cstring_ns;
/// Number of iterations performed
uint64_t iterations;
/// Output size in bytes (for verification)
uint64_t output_size;
};
extern "C" {
TwitterData *twitter_from_str(const char *raw_input, size_t raw_input_length);
void set_twitter_data(TwitterData *raw);
size_t serialize_twitter_to_string();
const char *str_from_twitter(TwitterData *raw);
void free_twitter(TwitterData *raw);
@@ -45,22 +30,14 @@ void free_string(const char *ptr);
/// Creates a CitmCatalog from a JSON string (UTF-8 encoded).
CitmCatalog *citm_from_str(const char *raw_input, uintptr_t raw_input_length);
void set_citm_data(CitmCatalog *raw);
size_t serialize_citm_to_string();
/// Serializes a CitmCatalog into a JSON string (UTF-8).
char *str_from_citm(CitmCatalog *raw_catalog);
/// Frees the CitmCatalog pointer.
void free_citm(CitmCatalog *raw_catalog);
void free_str(char *ptr);
/// Measures FFI overhead for Twitter serialization.
/// Performs `iterations` serializations entirely in Rust and returns timing data.
FfiOverheadResult measure_twitter_ffi_overhead(TwitterData *raw, uint64_t iterations);
/// Measures FFI overhead for CITM serialization.
FfiOverheadResult measure_citm_ffi_overhead(CitmCatalog *raw, uint64_t iterations);
} // extern "C"
} // namespace serde_benchmark
@@ -1,31 +0,0 @@
use std::fs;
// Include the lib.rs content directly
include!("../lib.rs");
fn main() {
// Read the Twitter JSON file
let json_str = fs::read_to_string("/Users/random_person/Desktop/simdjson/build/jsonexamples/twitter.json")
.expect("Failed to read file");
// Parse it
let data: TwitterData = serde_json::from_str(&json_str)
.expect("Failed to parse JSON");
// Serialize it back
let output = serde_json::to_string(&data)
.expect("Failed to serialize");
// Write to file for comparison
fs::write("rust_output.json", &output)
.expect("Failed to write output");
println!("Output size: {} bytes", output.len());
println!("Written to rust_output.json");
// Also write pretty version for easier inspection
let pretty = serde_json::to_string_pretty(&data)
.expect("Failed to serialize pretty");
fs::write("rust_output_pretty.json", &pretty)
.expect("Failed to write pretty output");
}
@@ -1,36 +0,0 @@
use std::fs;
// Import from the parent lib.rs
include!("../lib.rs");
fn main() {
// Read the Twitter JSON file
let json_str = fs::read_to_string("/Users/random_person/Desktop/simdjson/build/jsonexamples/twitter.json")
.expect("Failed to read file");
// Parse it
let data: TwitterData = serde_json::from_str(&json_str)
.expect("Failed to parse JSON");
// Serialize it back (compact)
let output = serde_json::to_vec(&data)
.expect("Failed to serialize");
let output_str = String::from_utf8(output.clone()).unwrap();
// Write to file for comparison
fs::write("rust_output_test.json", &output)
.expect("Failed to write output");
println!("Output size: {} bytes", output.len());
// Count statuses
println!("Number of statuses: {}", data.statuses.len());
// Check what fields are in the first status
if let Some(first) = data.statuses.first() {
// Let's serialize just the first status to see what fields are included
let first_json = serde_json::to_string_pretty(first).unwrap();
println!("First status (pretty):\n{}", first_json);
}
}
@@ -1,32 +1,14 @@
# Add executable targets
add_executable(benchmark_serialization_twitter benchmark_serialization_twitter.cpp)
add_executable(benchmark_parsing_twitter benchmark_parsing_twitter.cpp)
if(TARGET serde-benchmark)
message(STATUS "serde-benchmark target was created. Linking benchmarks and serde-benchmark.")
target_link_libraries(benchmark_serialization_twitter PRIVATE serde-benchmark)
target_link_libraries(benchmark_parsing_twitter PRIVATE serde-benchmark)
target_compile_definitions(benchmark_serialization_twitter PRIVATE SIMDJSON_RUST_VERSION="${Rust_VERSION}")
target_compile_definitions(benchmark_parsing_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)
target_link_libraries(benchmark_parsing_twitter PRIVATE simdjson::simdjson nlohmann_json)
if(TARGET rapidjson)
target_link_libraries(benchmark_parsing_twitter PRIVATE rapidjson)
target_compile_definitions(benchmark_parsing_twitter PRIVATE SIMDJSON_COMPETITION_RAPIDJSON)
endif()
if(TARGET yyjson)
target_link_libraries(benchmark_parsing_twitter PRIVATE yyjson)
target_compile_definitions(benchmark_parsing_twitter PRIVATE SIMDJSON_COMPETITION_YYJSON)
target_link_libraries(benchmark_serialization_twitter PRIVATE yyjson)
target_compile_definitions(benchmark_serialization_twitter PRIVATE SIMDJSON_COMPETITION_YYJSON)
endif()
target_compile_definitions(benchmark_serialization_twitter PRIVATE JSON_FILE="${EXAMPLE_JSON}")
target_compile_definitions(benchmark_parsing_twitter PRIVATE JSON_FILE="${EXAMPLE_JSON}")
target_compile_definitions(benchmark_serialization_twitter PRIVATE JSON_FILE="${EXAMPLE_JSON}")
@@ -1,232 +0,0 @@
#include <cassert>
#include <cstdlib>
#include <ctime>
#include <format>
#include <fstream>
#include <iostream>
#include <nlohmann/json.hpp>
#include <simdjson.h>
#include <string>
#include "twitter_data.h"
#include "nlohmann_twitter_data.h"
#include "../benchmark_utils/benchmark_helper.h"
#ifdef SIMDJSON_COMPETITION_RAPIDJSON
#include "rapidjson_twitter_data.h"
#endif
#ifdef SIMDJSON_COMPETITION_YYJSON
#include "yyjson_twitter_data.h"
#endif
#ifdef SIMDJSON_RUST_VERSION
#include "../serde-benchmark/serde_benchmark.h"
void bench_rust_parsing(const std::string &json_str) {
size_t input_volume = json_str.size();
printf("# input volume: %zu bytes\n", input_volume);
volatile bool result = true;
pretty_print(1, input_volume, "bench_rust_parsing",
bench([&json_str, &result]() {
serde_benchmark::TwitterData *td = serde_benchmark::twitter_from_str(json_str.c_str(), json_str.size());
result = (td != nullptr);
if (td) {
serde_benchmark::free_twitter(td);
}
if (!result) {
printf("parse error\n");
}
}));
}
#endif
// OPTIMIZED VERSION: Reuses parser across iterations
template <class T>
void bench_simdjson_static_reflection_parsing(const std::string &json_str) {
size_t input_volume = json_str.size();
printf("# input volume: %zu bytes\n", input_volume);
// Pre-allocate padded buffer outside the benchmark loop
simdjson::padded_string padded = simdjson::padded_string(json_str);
// CRITICAL: Create parser OUTSIDE the loop for reuse
simdjson::ondemand::parser parser;
volatile bool result = true;
pretty_print(1, input_volume, "bench_simdjson_static_reflection_parsing",
bench([&padded, &result, &parser]() {
// Reuse the same parser instance
simdjson::ondemand::document doc;
if(parser.iterate(padded).get(doc)) {
result = false;
return;
}
T my_struct;
if(doc.get<T>().get(my_struct)) {
result = false;
}
if (!result) {
printf("parse error\n");
}
}));
}
#if SIMDJSON_STATIC_REFLECTION
template <class T>
void bench_simdjson_from_parsing(const std::string &json_str) {
size_t input_volume = json_str.size();
printf("# input volume: %zu bytes\n", input_volume);
// Pre-allocate padded buffer outside the benchmark loop
simdjson::padded_string padded = simdjson::padded_string(json_str);
volatile bool result = true;
pretty_print(1, input_volume, "bench_simdjson_from_parsing",
bench([&padded, &result]() {
T my_struct;
auto err = simdjson::from(padded).get(my_struct);
if (err) {
result = false;
printf("parse error: %s\n", simdjson::error_message(err));
return;
}
}));
}
#endif
void bench_nlohmann_parsing(const std::string &json_str) {
size_t input_volume = json_str.size();
printf("# input volume: %zu bytes\n", input_volume);
volatile bool result = true;
pretty_print(1, input_volume, "bench_nlohmann_parsing",
bench([&json_str, &result]() {
try {
TwitterData data = nlohmann_deserialize(json_str);
result = true;
} catch (...) {
result = false;
printf("parse error\n");
}
}));
}
#ifdef SIMDJSON_COMPETITION_RAPIDJSON
void bench_rapidjson_parsing(const std::string &json_str) {
size_t input_volume = json_str.size();
printf("# input volume: %zu bytes\n", input_volume);
volatile bool result = true;
pretty_print(1, input_volume, "bench_rapidjson_parsing",
bench([&json_str, &result]() {
try {
TwitterData data = rapidjson_deserialize(json_str);
result = true;
} catch (...) {
result = false;
printf("parse error\n");
}
}));
}
#endif
#ifdef SIMDJSON_COMPETITION_YYJSON
void bench_yyjson_parsing(const std::string &json_str) {
size_t input_volume = json_str.size();
printf("# input volume: %zu bytes\n", input_volume);
volatile bool result = true;
pretty_print(1, input_volume, "bench_yyjson_parsing",
bench([&json_str, &result]() {
try {
TwitterData data = yyjson_deserialize(json_str);
result = true;
} catch (...) {
result = false;
printf("parse error\n");
}
}));
}
#endif
std::string read_file(std::string filename) {
printf("# Reading file %s\n", filename.c_str());
constexpr size_t read_size = 4096;
auto stream = std::ifstream(filename.c_str());
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 matches any of the comma-separated filters
bool matches_filter(const std::string& benchmark_name, const std::string& filter) {
if (filter.empty()) return true;
// Split filter by comma
size_t start = 0;
size_t end = filter.find(',');
while (end != std::string::npos) {
std::string token = filter.substr(start, end - start);
if (benchmark_name.find(token) != std::string::npos) {
return true;
}
start = end + 1;
end = filter.find(',', start);
}
// Check last token
std::string token = filter.substr(start);
return benchmark_name.find(token) != 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;
}
}
}
// Load the JSON data
std::string json_str = read_file(JSON_FILE);
// Benchmarking the parsing
if (matches_filter("nlohmann", filter)) {
bench_nlohmann_parsing(json_str);
}
#ifdef SIMDJSON_COMPETITION_RAPIDJSON
if (matches_filter("rapidjson", filter)) {
bench_rapidjson_parsing(json_str);
}
#endif
#ifdef SIMDJSON_COMPETITION_YYJSON
if (matches_filter("yyjson", filter)) {
bench_yyjson_parsing(json_str);
}
#endif
if (matches_filter("simdjson_static_reflection", filter)) {
bench_simdjson_static_reflection_parsing<TwitterData>(json_str);
}
#if SIMDJSON_STATIC_REFLECTION
if (matches_filter("simdjson_from", filter)) {
bench_simdjson_from_parsing<TwitterData>(json_str);
}
#endif
#ifdef SIMDJSON_RUST_VERSION
if (matches_filter("rust", filter)) {
printf("# Note: Rust/Serde parsing test\n");
bench_rust_parsing(json_str);
}
#endif
return EXIT_SUCCESS;
}
@@ -1,5 +1,4 @@
#include <cassert>
#include <chrono>
#include <cstdlib>
#include <ctime>
#include <format>
@@ -11,9 +10,6 @@
#include "twitter_data.h"
#include "nlohmann_twitter_data.h"
#include "../benchmark_utils/benchmark_helper.h"
#ifdef SIMDJSON_COMPETITION_YYJSON
#include "yyjson_twitter_data.h"
#endif
#if SIMDJSON_BENCH_CPP_REFLECT
#include <rfl.hpp>
#include <rfl/json.hpp>
@@ -39,49 +35,19 @@ void bench_reflect_cpp(TwitterData &data) {
void bench_rust(serde_benchmark::TwitterData *data) {
serde_benchmark::set_twitter_data(data);
size_t output_volume = serde_benchmark::serialize_twitter_to_string();
const char * output = serde_benchmark::str_from_twitter(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([&measured_volume, &output_volume]() {
measured_volume = serde_benchmark::serialize_twitter_to_string();
bench([&data, &measured_volume, &output_volume]() {
const char * output = serde_benchmark::str_from_twitter(data);
serde_benchmark::free_string(output);
}));
}
#endif
// Fair allocation variant: allocates fresh buffer each iteration (matches other libraries)
template <class T> void bench_simdjson_static_reflection(T &data) {
// First run to determine expected size
simdjson::builder::string_builder sb_init;
simdjson::builder::append(sb_init, data);
std::string_view p_init;
if(sb_init.view().get(p_init)) {
std::cerr << "Error!" << std::endl;
}
size_t output_volume = p_init.size();
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]() {
// Fresh allocation each iteration - fair comparison
simdjson::builder::string_builder sb;
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");
}
}));
}
// Optimized variant: reuses buffer across iterations (shows API potential)
template <class T> void bench_simdjson_static_reflection_reuse(T &data) {
simdjson::builder::string_builder sb;
simdjson::builder::append(sb, data);
std::string_view p;
@@ -93,7 +59,7 @@ template <class T> void bench_simdjson_static_reflection_reuse(T &data) {
printf("# output volume: %zu bytes\n", output_volume);
volatile size_t measured_volume = 0;
pretty_print(sizeof(data), output_volume, "bench_simdjson_reuse_buffer",
pretty_print(sizeof(data), output_volume, "bench_simdjson_static_reflection",
bench([&data, &measured_volume, &output_volume, &sb]() {
sb.clear();
simdjson::builder::append(sb, data);
@@ -108,63 +74,6 @@ template <class T> void bench_simdjson_static_reflection_reuse(T &data) {
}));
}
#if SIMDJSON_STATIC_REFLECTION
// Fair allocation variant: allocates fresh string each iteration
template <class T> void bench_simdjson_to(T &data) {
// First run to determine size
std::string output_init;
if (simdjson::error_code err = simdjson::builder::to_json(data, output_init); err) {
std::cerr << "Error in to_json initialization!" << simdjson::error_message(err) << std::endl;
return;
}
size_t output_volume = output_init.size();
printf("# output volume: %zu bytes\n", output_volume);
volatile size_t measured_volume = 0;
pretty_print(sizeof(data), output_volume, "bench_simdjson_to",
bench([&data, &measured_volume, &output_volume]() {
// Fresh allocation each iteration - fair comparison
std::string output;
if (simdjson::error_code err = simdjson::builder::to_json(data, output); err) {
std::cerr << "Error in to_json!" << simdjson::error_message(err) << std::endl;
return;
}
measured_volume = output.size();
if (measured_volume != output_volume) {
printf("mismatch\n");
}
}));
}
// Optimized variant: reuses pre-allocated string
template <class T> void bench_simdjson_to_reuse(T &data) {
std::string output;
if (simdjson::error_code err = simdjson::builder::to_json(data, output); err) {
std::cerr << "Error in to_json initialization!" << simdjson::error_message(err) << std::endl;
return;
}
size_t output_volume = output.size();
printf("# output volume: %zu bytes\n", output_volume);
// Pre-allocate string with sufficient capacity to avoid reallocation
output.reserve(output_volume * 2);
volatile size_t measured_volume = 0;
pretty_print(sizeof(data), output_volume, "bench_simdjson_to_reuse",
bench([&data, &measured_volume, &output_volume, &output]() {
// Reuse the pre-allocated string - avoids allocation
if (simdjson::error_code err = simdjson::builder::to_json(data, output); err) {
std::cerr << "Error in to_json!" << simdjson::error_message(err) << std::endl;
return;
}
measured_volume = output.size();
if (measured_volume != output_volume) {
printf("mismatch\n");
}
}));
}
#endif
void bench_nlohmann(TwitterData &data) {
std::string output = nlohmann_serialize(data);
size_t output_volume = output.size();
@@ -181,61 +90,28 @@ void bench_nlohmann(TwitterData &data) {
}));
}
#ifdef SIMDJSON_COMPETITION_YYJSON
void bench_yyjson(TwitterData &data) {
std::string output = yyjson_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_yyjson",
bench([&data, &measured_volume, &output_volume]() {
std::string output = yyjson_serialize(data);
measured_volume = output.size();
if (measured_volume != output_volume) {
printf("mismatch\n");
}
}));
}
#endif
size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp) {
((std::string *)userp)->append((char *)contents, size * nmemb);
return size * nmemb;
}
simdjson::padded_string read_file(std::string filename) {
std::string read_file(std::string filename) {
printf("# Reading file %s\n", filename.c_str());
constexpr size_t read_size = 4096;
auto stream = std::ifstream(filename.c_str());
stream.exceptions(std::ios_base::badbit);
simdjson::padded_string_builder builder;
std::string out;
std::string buf(read_size, '\0');
while (stream.read(&buf[0], read_size)) {
builder.append(buf.data(), size_t(stream.gcount()));
out.append(buf, 0, size_t(stream.gcount()));
}
builder.append(buf.data(), size_t(stream.gcount()));
return builder.convert();
out.append(buf, 0, size_t(stream.gcount()));
return out;
}
// Function to check if benchmark name matches any of the comma-separated filters
// Function to check if benchmark name contains filter substring
bool matches_filter(const std::string& benchmark_name, const std::string& filter) {
if (filter.empty()) return true;
// Split filter by comma
size_t start = 0;
size_t end = filter.find(',');
while (end != std::string::npos) {
std::string token = filter.substr(start, end - start);
if (benchmark_name.find(token) != std::string::npos) {
return true;
}
start = end + 1;
end = filter.find(',', start);
}
// Check last token
std::string token = filter.substr(start);
return benchmark_name.find(token) != std::string::npos;
return filter.empty() || benchmark_name.find(filter) != std::string::npos;
}
int main(int argc, char* argv[]) {
@@ -253,12 +129,12 @@ int main(int argc, char* argv[]) {
}
}
// Testing correctness of round-trip (serialization + deserialization)
simdjson::padded_string json_str = read_file(JSON_FILE);
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(json_str).get(doc)) {
if(parser.iterate(simdjson::pad(json_str)).get(doc)) {
std::cerr << "Error loading the document!" << std::endl;
return EXIT_FAILURE;
}
@@ -269,41 +145,18 @@ int main(int argc, char* argv[]) {
}
// Benchmarking the serialization
// Note: simdjson benchmarks include both "fair" (fresh allocation) and "reuse" (buffer reuse) variants
// The "fair" variants allocate fresh memory each iteration, matching other libraries' behavior
// The "reuse" variants demonstrate the API's potential when buffer reuse is possible
if (matches_filter("nlohmann", filter)) {
bench_nlohmann(my_struct);
}
#ifdef SIMDJSON_COMPETITION_YYJSON
if (matches_filter("yyjson", filter)) {
bench_yyjson(my_struct);
}
#endif
if (matches_filter("simdjson_static_reflection", filter)) {
bench_simdjson_static_reflection(my_struct);
}
if (matches_filter("simdjson_reuse", filter)) {
bench_simdjson_static_reflection_reuse(my_struct);
}
#if SIMDJSON_STATIC_REFLECTION
if (matches_filter("simdjson_to", filter)) {
bench_simdjson_to(my_struct);
}
if (matches_filter("simdjson_to_reuse", filter)) {
bench_simdjson_to_reuse(my_struct);
}
#endif
#ifdef SIMDJSON_RUST_VERSION
if (matches_filter("rust", filter)) {
serde_benchmark::TwitterData * td = serde_benchmark::twitter_from_str(json_str.data(), json_str.size());
if (td == nullptr) {
printf("# Failed to parse Twitter data for Rust benchmark\n");
} else {
bench_rust(td);
serde_benchmark::free_twitter(td);
}
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
@@ -4,7 +4,6 @@
#include "twitter_data.h"
#include <nlohmann/json.hpp>
// Serialization functions for nlohmann
void to_json(nlohmann::json &j, const User &u) {
j = nlohmann::json{{"id", u.id},
{"name", u.name},
@@ -17,54 +16,101 @@ void to_json(nlohmann::json &j, const User &u) {
{"statuses_count", u.statuses_count}};
}
void to_json(nlohmann::json &j, const Hashtag &h) {
j = nlohmann::json{{"text", h.text},
{"indices_start", h.indices_start},
{"indices_end", h.indices_end}};
}
void to_json(nlohmann::json &j, const Url &u) {
j = nlohmann::json{{"url", u.url},
{"expanded_url", u.expanded_url},
{"display_url", u.display_url},
{"indices_start", u.indices_start},
{"indices_end", u.indices_end}};
}
void to_json(nlohmann::json &j, const UserMention &um) {
j = nlohmann::json{{"id", um.id},
{"name", um.name},
{"screen_name", um.screen_name},
{"indices_start", um.indices_start},
{"indices_end", um.indices_end}};
}
void to_json(nlohmann::json &j, const Entities &e) {
j = nlohmann::json{{"hashtags", e.hashtags},
{"urls", e.urls},
{"user_mentions", e.user_mentions}};
}
void to_json(nlohmann::json &j, const Status &s) {
j = nlohmann::json{{"created_at", s.created_at},
{"id", s.id},
{"text", s.text},
{"user", s.user},
{"entities", s.entities},
{"retweet_count", s.retweet_count},
{"favorite_count", s.favorite_count}};
{"favorite_count", s.favorite_count},
{"favorited", s.favorited},
{"retweeted", s.retweeted}};
}
std::string nlohmann_serialize(const std::vector<Hashtag>& v) {
nlohmann::json a = nlohmann::json::array();
for(const Hashtag & h : v) {
a.push_back(nlohmann::json{{"text", h.text},
{"indices_start", h.indices_start},
{"indices_end", h.indices_end}});
}
return a.dump();
}
std::string nlohmann_serialize(const std::vector<Url>& v) {
nlohmann::json a = nlohmann::json::array();
for(const Url & u : v) {
a.push_back(nlohmann::json{{"url", u.url},
{"expanded_url", u.expanded_url},
{"display_url", u.display_url},
{"indices_start", u.indices_start},
{"indices_end", u.indices_end}});
}
return a.dump();
}
std::string nlohmann_serialize(const std::vector<UserMention>& v) {
nlohmann::json a = nlohmann::json::array();
for(const UserMention & um : v) {
a.push_back(nlohmann::json{{"id", um.id},
{"name", um.name},
{"screen_name", um.screen_name},
{"indices_start", um.indices_start},
{"indices_end", um.indices_end}});
}
return a.dump();
}
std::string nlohmann_serialize(const std::vector<Status>& v) {
nlohmann::json a = nlohmann::json::array();
for(const Status & s : v) {
a.push_back(nlohmann::json{{"created_at", s.created_at},
{"id", s.id},
{"text", s.text},
{"user", s.user},
{"entities", s.entities},
{"retweet_count", s.retweet_count},
{"favorite_count", s.favorite_count},
{"favorited", s.favorited},
{"retweeted", s.retweeted}});
}
return a.dump();
}
void to_json(nlohmann::json &j, const TwitterData &t) {
j = nlohmann::json{{"statuses", t.statuses}};
}
// Deserialization functions for nlohmann
void from_json(const nlohmann::json &j, User &u) {
j.at("id").get_to(u.id);
j.at("name").get_to(u.name);
j.at("screen_name").get_to(u.screen_name);
j.at("location").get_to(u.location);
j.at("description").get_to(u.description);
j.at("verified").get_to(u.verified);
j.at("followers_count").get_to(u.followers_count);
j.at("friends_count").get_to(u.friends_count);
j.at("statuses_count").get_to(u.statuses_count);
}
void from_json(const nlohmann::json &j, Status &s) {
j.at("created_at").get_to(s.created_at);
j.at("id").get_to(s.id);
j.at("text").get_to(s.text);
j.at("user").get_to(s.user);
j.at("retweet_count").get_to(s.retweet_count);
j.at("favorite_count").get_to(s.favorite_count);
}
void from_json(const nlohmann::json &j, TwitterData &t) {
j.at("statuses").get_to(t.statuses);
}
// Helper functions for benchmarking
std::string nlohmann_serialize(const TwitterData &data) {
nlohmann::json j = data;
return j.dump();
}
TwitterData nlohmann_deserialize(const std::string &json_str) {
nlohmann::json j = nlohmann::json::parse(json_str);
return j.get<TwitterData>();
return nlohmann_serialize(data.statuses);
}
#endif // NLOHMANN_TWITTER_DATA_H
@@ -1,142 +0,0 @@
#ifndef RAPIDJSON_TWITTER_DATA_H
#define RAPIDJSON_TWITTER_DATA_H
#include "twitter_data.h"
#include <rapidjson/document.h>
#include <rapidjson/writer.h>
#include <rapidjson/stringbuffer.h>
#include <rapidjson/error/en.h>
using namespace rapidjson;
// RapidJSON deserialization for simplified Twitter data
TwitterData rapidjson_deserialize(const std::string& json_str) {
Document doc;
doc.Parse(json_str.c_str());
if (doc.HasParseError()) {
throw std::runtime_error("RapidJSON parse error");
}
TwitterData data;
if (!doc.HasMember("statuses") || !doc["statuses"].IsArray()) {
return data;
}
const Value& statuses = doc["statuses"];
data.statuses.reserve(statuses.Size());
for (SizeType i = 0; i < statuses.Size(); i++) {
const Value& status_json = statuses[i];
Status status;
// Parse status fields
if (status_json.HasMember("created_at") && status_json["created_at"].IsString())
status.created_at = status_json["created_at"].GetString();
if (status_json.HasMember("id") && status_json["id"].IsUint64())
status.id = status_json["id"].GetUint64();
if (status_json.HasMember("text") && status_json["text"].IsString())
status.text = status_json["text"].GetString();
if (status_json.HasMember("retweet_count") && status_json["retweet_count"].IsUint64())
status.retweet_count = status_json["retweet_count"].GetUint64();
if (status_json.HasMember("favorite_count") && status_json["favorite_count"].IsUint64())
status.favorite_count = status_json["favorite_count"].GetUint64();
// Parse user
if (status_json.HasMember("user") && status_json["user"].IsObject()) {
const Value& user_json = status_json["user"];
User user;
if (user_json.HasMember("id") && user_json["id"].IsUint64())
user.id = user_json["id"].GetUint64();
if (user_json.HasMember("name") && user_json["name"].IsString())
user.name = user_json["name"].GetString();
if (user_json.HasMember("screen_name") && user_json["screen_name"].IsString())
user.screen_name = user_json["screen_name"].GetString();
if (user_json.HasMember("location") && user_json["location"].IsString())
user.location = user_json["location"].GetString();
if (user_json.HasMember("description") && user_json["description"].IsString())
user.description = user_json["description"].GetString();
if (user_json.HasMember("verified") && user_json["verified"].IsBool())
user.verified = user_json["verified"].GetBool();
if (user_json.HasMember("followers_count") && user_json["followers_count"].IsUint64())
user.followers_count = user_json["followers_count"].GetUint64();
if (user_json.HasMember("friends_count") && user_json["friends_count"].IsUint64())
user.friends_count = user_json["friends_count"].GetUint64();
if (user_json.HasMember("statuses_count") && user_json["statuses_count"].IsUint64())
user.statuses_count = user_json["statuses_count"].GetUint64();
status.user = user;
}
data.statuses.push_back(status);
}
return data;
}
// RapidJSON serialization for simplified Twitter data
std::string rapidjson_serialize(const TwitterData& data) {
Document doc;
doc.SetObject();
Document::AllocatorType& allocator = doc.GetAllocator();
Value statuses_array(kArrayType);
for (const auto& status : data.statuses) {
Value status_obj(kObjectType);
Value created_at;
created_at.SetString(status.created_at.c_str(), allocator);
status_obj.AddMember("created_at", created_at, allocator);
status_obj.AddMember("id", status.id, allocator);
Value text;
text.SetString(status.text.c_str(), allocator);
status_obj.AddMember("text", text, allocator);
// Add user
Value user_obj(kObjectType);
user_obj.AddMember("id", status.user.id, allocator);
Value name;
name.SetString(status.user.name.c_str(), allocator);
user_obj.AddMember("name", name, allocator);
Value screen_name;
screen_name.SetString(status.user.screen_name.c_str(), allocator);
user_obj.AddMember("screen_name", screen_name, allocator);
Value location;
location.SetString(status.user.location.c_str(), allocator);
user_obj.AddMember("location", location, allocator);
Value description;
description.SetString(status.user.description.c_str(), allocator);
user_obj.AddMember("description", description, allocator);
user_obj.AddMember("verified", status.user.verified, allocator);
user_obj.AddMember("followers_count", status.user.followers_count, allocator);
user_obj.AddMember("friends_count", status.user.friends_count, allocator);
user_obj.AddMember("statuses_count", status.user.statuses_count, allocator);
status_obj.AddMember("user", user_obj, allocator);
status_obj.AddMember("retweet_count", status.retweet_count, allocator);
status_obj.AddMember("favorite_count", status.favorite_count, allocator);
statuses_array.PushBack(status_obj, allocator);
}
doc.AddMember("statuses", statuses_array, allocator);
StringBuffer buffer;
Writer<StringBuffer> writer(buffer);
doc.Accept(writer);
return buffer.GetString();
}
#endif // RAPIDJSON_TWITTER_DATA_H
@@ -4,31 +4,68 @@
#include <string>
#include <vector>
// Simplified Twitter structures for benchmarking
struct User {
uint64_t id;
int64_t id;
std::string id_str;
std::string name;
std::string screen_name;
std::string location;
std::string description;
bool verified;
uint64_t followers_count;
uint64_t friends_count;
uint64_t statuses_count;
int64_t followers_count;
int64_t friends_count;
int64_t statuses_count;
bool operator<=>(const User &other) const = default;
};
struct Hashtag {
std::string text;
int64_t indices_start;
int64_t indices_end;
bool operator<=>(const Hashtag &other) const = default;
};
struct Url {
std::string url;
std::string expanded_url;
std::string display_url;
int64_t indices_start;
int64_t indices_end;
bool operator<=>(const Url &other) const = default;
};
struct UserMention {
int64_t id;
std::string name;
std::string screen_name;
int64_t indices_start;
int64_t indices_end;
bool operator<=>(const UserMention &other) const = default;
};
struct Entities {
std::vector<Hashtag> hashtags;
std::vector<Url> urls;
std::vector<UserMention> user_mentions;
bool operator==(const Entities &other) const = default;
};
struct Status {
std::string created_at;
uint64_t id;
int64_t id;
std::string text;
User user;
uint64_t retweet_count;
uint64_t favorite_count;
Entities entities;
int64_t retweet_count;
int64_t favorite_count;
bool favorited;
bool retweeted;
bool operator==(const Status &other) const = default;
};
struct TwitterData {
std::vector<Status> statuses;
bool operator==(const TwitterData &other) const = default;
};
#endif // TWITTER_DATA_H
#endif
@@ -1,145 +0,0 @@
#ifndef YYJSON_TWITTER_DATA_H
#define YYJSON_TWITTER_DATA_H
#include "twitter_data.h"
#include <yyjson.h>
#include <string>
#include <stdexcept>
// yyjson deserialization for simplified Twitter data
TwitterData yyjson_deserialize(const std::string &json_str) {
TwitterData data;
yyjson_doc *doc = yyjson_read(json_str.c_str(), json_str.size(), 0);
if (!doc) {
throw std::runtime_error("yyjson parse error");
}
yyjson_val *root = yyjson_doc_get_root(doc);
if (!root) {
yyjson_doc_free(doc);
return data;
}
// Get statuses array
yyjson_val *statuses_val = yyjson_obj_get(root, "statuses");
if (!statuses_val || !yyjson_is_arr(statuses_val)) {
yyjson_doc_free(doc);
return data;
}
size_t idx, max;
yyjson_val *status_val;
yyjson_arr_foreach(statuses_val, idx, max, status_val) {
Status status;
// Parse status fields
yyjson_val *val;
val = yyjson_obj_get(status_val, "created_at");
if (val && yyjson_is_str(val)) status.created_at = yyjson_get_str(val);
val = yyjson_obj_get(status_val, "id");
if (val && yyjson_is_uint(val)) status.id = yyjson_get_uint(val);
val = yyjson_obj_get(status_val, "text");
if (val && yyjson_is_str(val)) status.text = yyjson_get_str(val);
val = yyjson_obj_get(status_val, "retweet_count");
if (val && yyjson_is_uint(val)) status.retweet_count = yyjson_get_uint(val);
val = yyjson_obj_get(status_val, "favorite_count");
if (val && yyjson_is_uint(val)) status.favorite_count = yyjson_get_uint(val);
// Parse user
yyjson_val *user_val = yyjson_obj_get(status_val, "user");
if (user_val && yyjson_is_obj(user_val)) {
User user;
val = yyjson_obj_get(user_val, "id");
if (val && yyjson_is_uint(val)) user.id = yyjson_get_uint(val);
val = yyjson_obj_get(user_val, "name");
if (val && yyjson_is_str(val)) user.name = yyjson_get_str(val);
val = yyjson_obj_get(user_val, "screen_name");
if (val && yyjson_is_str(val)) user.screen_name = yyjson_get_str(val);
val = yyjson_obj_get(user_val, "location");
if (val && yyjson_is_str(val)) user.location = yyjson_get_str(val);
val = yyjson_obj_get(user_val, "description");
if (val && yyjson_is_str(val)) user.description = yyjson_get_str(val);
val = yyjson_obj_get(user_val, "verified");
if (val && yyjson_is_bool(val)) user.verified = yyjson_get_bool(val);
val = yyjson_obj_get(user_val, "followers_count");
if (val && yyjson_is_uint(val)) user.followers_count = yyjson_get_uint(val);
val = yyjson_obj_get(user_val, "friends_count");
if (val && yyjson_is_uint(val)) user.friends_count = yyjson_get_uint(val);
val = yyjson_obj_get(user_val, "statuses_count");
if (val && yyjson_is_uint(val)) user.statuses_count = yyjson_get_uint(val);
status.user = user;
}
data.statuses.push_back(status);
}
yyjson_doc_free(doc);
return data;
}
// yyjson serialization for simplified Twitter data
std::string yyjson_serialize(const TwitterData &data) {
yyjson_mut_doc *doc = yyjson_mut_doc_new(NULL);
yyjson_mut_val *root = yyjson_mut_obj(doc);
yyjson_mut_doc_set_root(doc, root);
// Create statuses array
yyjson_mut_val *statuses_array = yyjson_mut_arr(doc);
for (const auto& status : data.statuses) {
yyjson_mut_val *status_obj = yyjson_mut_obj(doc);
// Add status fields
yyjson_mut_obj_add_str(doc, status_obj, "created_at", status.created_at.c_str());
yyjson_mut_obj_add_uint(doc, status_obj, "id", status.id);
yyjson_mut_obj_add_str(doc, status_obj, "text", status.text.c_str());
// User object
yyjson_mut_val *user_obj = yyjson_mut_obj(doc);
yyjson_mut_obj_add_uint(doc, user_obj, "id", status.user.id);
yyjson_mut_obj_add_str(doc, user_obj, "name", status.user.name.c_str());
yyjson_mut_obj_add_str(doc, user_obj, "screen_name", status.user.screen_name.c_str());
yyjson_mut_obj_add_str(doc, user_obj, "location", status.user.location.c_str());
yyjson_mut_obj_add_str(doc, user_obj, "description", status.user.description.c_str());
yyjson_mut_obj_add_bool(doc, user_obj, "verified", status.user.verified);
yyjson_mut_obj_add_uint(doc, user_obj, "followers_count", status.user.followers_count);
yyjson_mut_obj_add_uint(doc, user_obj, "friends_count", status.user.friends_count);
yyjson_mut_obj_add_uint(doc, user_obj, "statuses_count", status.user.statuses_count);
yyjson_mut_obj_add_val(doc, status_obj, "user", user_obj);
// Other fields
yyjson_mut_obj_add_uint(doc, status_obj, "retweet_count", status.retweet_count);
yyjson_mut_obj_add_uint(doc, status_obj, "favorite_count", status.favorite_count);
yyjson_mut_arr_append(statuses_array, status_obj);
}
// Add statuses array to root
yyjson_mut_obj_add_val(doc, root, "statuses", statuses_array);
// Write to string
char *json_output = yyjson_mut_write(doc, 0, NULL);
std::string result(json_output);
free(json_output);
yyjson_mut_doc_free(doc);
return result;
}
#endif // YYJSON_TWITTER_DATA_H
+1 -1
View File
@@ -12,7 +12,7 @@ cmake_dependent_option(SIMDJSON_GOOGLE_BENCHMARKS "compile the Google Benchmark
if(SIMDJSON_GOOGLE_BENCHMARKS)
CPMAddPackage(
NAME google_benchmarks
URL https://github.com/google/benchmark/archive/refs/tags/v1.9.5.zip
URL https://github.com/google/benchmark/archive/refs/tags/v1.9.4.zip
OPTIONS
"BENCHMARK_ENABLE_TESTING OFF"
"BENCHMARK_ENABLE_INSTALL OFF"
+27 -43
View File
@@ -28,8 +28,6 @@ separate document](https://github.com/simdjson/simdjson/blob/master/doc/builder.
- [UTF-8 validation (alone)](#utf-8-validation-alone)
- [JSON Pointer](#json-pointer)
- [JSONPath](#jsonpath)
* [Using `for_each_at_path_with_wildcard` for JSONPath Queries (On-Demand)](#using-for_each_at_path_with_wildcard-for-jsonpath-queries-on-demand)
+ [Example Usage](#example-usage)
- [Compile-Time JSONPath and JSON Pointer (C++26 Reflection)](#compile-time-jsonpath-and-json-pointer-c26-reflection)
- [Error handling](#error-handling)
* [Error handling examples without exceptions](#error-handling-examples-without-exceptions)
@@ -273,21 +271,6 @@ Further, they may use the AreFileApisANSI function to determine whether
the filename is interpreted using the ANSI or the system default OEM
codepage, and they may call SetFileApisToOEM accordingly.
**Advanced feature:**
On non-Windows systems, you can use memory-file mapping to create a `simdjson::padded_string_view`
from a file on disk.
```cpp
// If the macro _WIN32 is defined, this will not work since we do not support memory-file mapping
// under Windows at this time.
simdjson::padded_memory_map map(myfilename);
if (!map.is_valid()) { /* handle error */ }
simdjson::padded_string_view view = map.view(); // view is usable while padded_memory_map is in scope
ondemand::document doc = parser.iterate(view); // parse the JSON
```
Documents are iterators
-----------------------
@@ -391,10 +374,6 @@ 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.
Warnign: Mixing debug and release simdjson code is unsafe: you either build all your code
using simdjson in release mode or all of it in debug mode.
Using the parsed JSON
---------------------
@@ -438,7 +417,7 @@ support for users who avoid exceptions. See [the simdjson error handling documen
* **Extracting Values:** You can cast a JSON element to a native type:
`double(element)`. This works for `std::string_view`, double, uint64_t, int64_t, bool,
ondemand::object and ondemand::array. We also have explicit methods such as `get_string()`, `get_double()`,
`get_uint64()`, `get_int64()`, `get_uint32()`, `get_int32()`, `get_bool()`, `get_object()` and `get_array()`. After a cast or an explicit method,
`get_uint64()`, `get_int64()`, `get_bool()`, `get_object()` and `get_array()`. After a cast or an explicit method,
the number, string or boolean will be parsed, or the initial `{` or `[` will be verified for `ondemand::object` and `ondemand::array`. An exception may be thrown if
the cast is not possible: the error code is `simdjson::INCORRECT_TYPE` (see [Error handling](#error-handling)). Importantly, when getting an ondemand::object or ondemand::array instance, its content is
not validated: you are only guaranteed that the corresponding initial character (`{` or `[`) is present. Thus,
@@ -448,8 +427,8 @@ support for users who avoid exceptions. See [the simdjson error handling documen
pass `true` (`get_string(true)`) as a parameter to get replacement characters where errors
occur. If you somehow need to access non-UTF-8 strings in a lossless manner
(e.g., if you strings contain unpaired surrogates), you may use the `get_wobbly_string()` function to get a string in the [WTF-8 format](https://simonsapin.github.io/wtf-8).
When calling `get_uint64()`, `get_int64()`, `get_uint32()` or `get_int32()`, if the number does not fit in the
corresponding integer type, it is also considered an error (`NUMBER_OUT_OF_RANGE`). When parsing numbers or other scalar values, the library checks
When calling `get_uint64()` and `get_int64()`, if the number does not fit in a corresponding
64-bit integer type, it is also considered an error. When parsing numbers or other scalar values, the library checks
that the value is followed by an expected character, thus you *may* get a number parsing error when accessing the digits
as an integer in the following strings: `{"number":12332a`, `{"number":12332\0`, `{"number":12332` (the digits appear at the end). We always abide by the [RFC 8259](https://www.tbray.org/ongoing/When/201x/2017/12/14/rfc8259.html) JSON specification so that, for example, numbers prefixed by the `+` sign are in error.
@@ -490,7 +469,7 @@ support for users who avoid exceptions. See [the simdjson error handling documen
out of the array, you may use an array access (e.g., `array[1]`). You should never reset an array as you are iterating through it. The following is an anti-pattern: `for(auto value: myarray) {myarray.reset()}`.
* **Field Access:** To get the value of the "foo" field in an object, use `object["foo"]`. This will
scan through the object looking for the field with the matching string, doing a character-by-character
comparison. It may generate the error `simdjson::NO_SUCH_FIELD` if there is no such key in the object, it may throw an exception (see [Error handling](#error-handling)). The returned value is only valid so long as you do not access another field: normally, you should therefore grab the value right after accessing a key (i.e., convert it to number, string, object, array...). For efficiency reason, you should avoid looking up the same field repeatedly: e.g., do
comparison. It may generate the error `simdjson::NO_SUCH_FIELD` if there is no such key in the object, it may throw an exception (see [Error handling](#error-handling)). For efficiency reason, you should avoid looking up the same field repeatedly: e.g., do
not do `object["foo"]` followed by `object["foo"]` with the same `object` instance. Generally, you should not mix and match iterating through an object (`for(auto field : object) {...}`) and key accesses (`object["foo"]`): if you need to iterate through an object after a key access, you need to call `reset()` on the object. Whenever you call `reset()`, you need to keep in mind that though you can iterate over the array repeatedly, values should be consumedonly once (e.g., repeatedly calling `unescaped_key()` on the same key is forbidden). Keep in mind that On-Demand does not buffer or save the result of the parsing: if you repeatedly access `object["foo"]`, then it must repeatedly seek the key and parse the content. The library does not provide a distinct function to check if a key is present, instead we recommend you attempt to access the key: e.g., by doing `ondemand::value val{}; if (!object["foo"].get(val)) {...}`, you have that `val` contains the requested value inside the if clause. It is your responsibility as a user to temporarily keep a reference to the value (`auto v = object["foo"]`), or to consume the content and store it in your own data structures. If you consume an
object twice: `std::string_view(object["foo"]` followed by `std::string_view(object["foo"]` then your code
is in error. Furthermore, you can only consume one field at a time, on the same object. The
@@ -1792,15 +1771,15 @@ int64_t x = obj.at_path("$.c.foo.a[1]"); // 20
x = obj.at_path("$.d.foo2.a.2"); // 30
```
## Using `for_each_at_path_with_wildcard` for JSONPath Queries (On-Demand)
## Using `at_path_with_wildcard` for JSONPath Queries (On-Demand)
The `for_each_at_path_with_wildcard` function in simdjson extends the JSONPath querying capabilities by supporting wildcard expressions (`*`) in JSON paths. It calls a user-provided callback for each matching element, avoiding the need to materialize all results into a vector. For example, you can use `$.address.*` to fetch all fields within the `address` object or `$.phoneNumbers[*].numbers[*]` to retrieve all phone numbers across multiple objects in an array.
The `at_path_with_wildcard` function in simdjson extends the JSONPath querying capabilities by supporting wildcard expressions (`*`) in JSON paths. This allows users to retrieve multiple elements from a JSON document in a single query. For example, you can use `$.address.*` to fetch all fields within the `address` object or `$.phoneNumbers[*].numbers[*]` to retrieve all phone numbers across multiple objects in an array.
The `*` wildcard matches all elements at a specific level. For instance, `$.address.*` retrieves all key-value pairs in the `address` object, while `$.*.streetAddress` fetches all `streetAddress` fields across objects at the root level. You can combine wildcards with array indexing. For example, `$.phoneNumbers[*].numbers[1]` retrieves the second number from each `numbers` array in the `phoneNumbers` array. If no elements match the wildcard query, the callback is simply never called. For instance, querying `$.empty_object.*` or `$.empty_array.*` will yield no callbacks.
The `*` wildcard matches all elements at a specific level. For instance, `$.address.*` retrieves all key-value pairs in the `address` object, while `$.*.streetAddress` fetches all `streetAddress` fields across objects at the root level. You can combine wildcards with array indexing. For example, `$.phoneNumbers[*].numbers[1]` retrieves the second number from each `numbers` array in the `phoneNumbers` array. If no elements match the wildcard query, the function returns an empty result. For instance, querying `$.empty_object.*` or `$.empty_array.*` will yield an empty set.
### Example Usage
Here is an example demonstrating the use of `for_each_at_path_with_wildcard`:
Here is an example demonstrating the use of `at_path_with_wildcard`:
```cpp
simdjson::padded_string json_string = R"(
@@ -1829,22 +1808,27 @@ ondemand::parser parser;
ondemand::document doc = parser.iterate(json_string);
// Fetch all fields in the address object
auto error = doc.for_each_at_path_with_wildcard("$.address.*",
[](ondemand::value value) {
std::string_view field;
if (value.get(field) == SUCCESS) {
std::cout << field << std::endl;
}
});
std::vector<ondemand::value> values;
auto error = doc.at_path_with_wildcard("$.address.*").get(values);
if (!error) {
for (auto value : values) {
std::string_view field;
if (value.get(field) == SUCCESS) {
std::cout << field << std::endl;
}
}
}
// Fetch all phone numbers
doc.for_each_at_path_with_wildcard("$.phoneNumbers[*].numbers[*]",
[](ondemand::value value) {
std::string_view number;
if (value.get(number) == SUCCESS) {
std::cout << number << std::endl;
}
});
error = doc.at_path_with_wildcard("$.phoneNumbers[*].numbers[*]").get(values);
if (!error) {
for (auto value : values) {
std::string_view number;
if (value.get(number) == SUCCESS) {
std::cout << number << std::endl;
}
}
}
```
This function is particularly useful for extracting data from complex JSON structures with nested arrays and objects. By leveraging wildcards, you can simplify your queries and reduce the need for multiple iterations.
+1 -34
View File
@@ -125,22 +125,6 @@ Further, they may use the AreFileApisANSI function to determine whether
the filename is interpreted using the ANSI or the system default OEM
codepage, and they may call SetFileApisToOEM accordingly.
**Advanced feature:**
On non-Windows systems, you can use memory-file mapping to create a `simdjson::padded_string_view`
from a file on disk.
```cpp
// if the macro _WIN32 is defined, this will not work since we do not support Windows
simdjson::padded_memory_map map(TWITTER_JSON);
if (!map.is_valid()) { /* handle error */ }
simdjson::padded_string_view view = map.view(); // view is usable while padded_memory_map is in scope
ondemand::document doc = parser.iterate(view); // parse the JSON
```
Using memory-file mapping requires some care. The file should not be modified while you are
accessing it.
Using the Parsed JSON
---------------------
@@ -168,23 +152,6 @@ Once you have an element, you can navigate it with idiomatic C++ iterators, oper
`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`.
* **Big Integer Support (opt-in):** By default, integers that exceed the 64-bit range cause parsing to fail with `BIGINT_ERROR`. You can opt in to big integer support so that these numbers are stored as raw digit strings on the tape instead:
```cpp
simdjson::dom::parser parser;
parser.number_as_string(true); // opt-in, default false
simdjson::dom::element doc;
auto error = parser.parse("[1, 123456789012345678901]"_padded).get(doc);
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
for (simdjson::dom::element elem : doc) {
if (elem.is_bigint()) {
std::string_view digits;
error = elem.get_bigint().get(digits);
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
std::cout << "big integer: " << digits << std::endl;
}
}
```
When enabled, big integers have type `element_type::BIGINT`. Calling `get_int64()`, `get_uint64()`, or `get_double()` on a big integer returns `INCORRECT_TYPE`. Normal numbers (int64, uint64, double) are unaffected.
* **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) { ... }`
@@ -197,7 +164,7 @@ Once you have an element, you can navigate it with idiomatic C++ iterators, oper
* **Array and Object size** Given an array or an object, you can get its size (number of elements or keys)
with the `size()` method.
* **Checking an Element Type:** You can check an element's type with `element.type()`. It
returns an `element_type` with values such as `simdjson::dom::element_type::ARRAY`, `simdjson::dom::element_type::OBJECT`, `simdjson::dom::element_type::INT64`, `simdjson::dom::element_type::UINT64`,`simdjson::dom::element_type::DOUBLE`, `simdjson::dom::element_type::STRING`, `simdjson::dom::element_type::BOOL`, `simdjson::dom::element_type::NULL_VALUE` or, `simdjson::dom::element_type::BIGINT` (when big integer support is enabled).
returns an `element_type` with values such as `simdjson::dom::element_type::ARRAY`, `simdjson::dom::element_type::OBJECT`, `simdjson::dom::element_type::INT64`, `simdjson::dom::element_type::UINT64`,`simdjson::dom::element_type::DOUBLE`, `simdjson::dom::element_type::STRING`, `simdjson::dom::element_type::BOOL` or, `simdjson::dom::element_type::NULL_VALUE`.
* **Output to streams and strings:** Given a document or an element (or node) out of a JSON document, you can output a minified string version using the C++ stream idiom (`out << element`). You can also request the construction of a minified string version (`simdjson::minify(element)`) or a prettified string version (`simdjson::prettify(element)`). Numbers are serialized as 64-bit floating-point numbers (`double`).
### Examples
-3
View File
@@ -33,9 +33,6 @@ compiles *all* the implementations into the executable. On Intel, it will includ
(icelake, haswell, westmere and fallback), on 64-bit ARM it will include just one since running dispatching is unnecessary, and on PPC
it will include 2 (ppc64 and fallback).
On Loongson processors, LASX runtime dispatching is only enabled on GCC 15+, not on LLVM or older versions of GCC.
Thus unless you compile specifically for LASX or use GCC 15+, you will not benefit from LASX support.
If you know more about where you're going to run and want to save the space, you can disable any of
these implementations at compile time with `-DSIMDJSON_IMPLEMENTATION_X=0` (where X is ICELAKE, HASWELL,
WESTMERE, ARM64, PPC64, LSX, LASX and FALLBACK).
-14
View File
@@ -154,20 +154,6 @@ for (auto doc : docs) {
See [basics.md](basics.md#newline-delimited-json-ndjson-and-json-lines) for an overview of the API.
**Advanced feature:**
On non-Windows systems, you can use memory-file mapping to create a `simdjson::padded_string_view`
from a file on disk.
```cpp
// If the macro _WIN32 is defined, this will not work since we do not support memory-file mapping
// under Windows at this time.
simdjson::padded_memory_map map(myfilename);
if (!map.is_valid()) { /* handle error */ }
simdjson::padded_string_view view = map.view(); // view is usable while padded_memory_map is in scope
ondemand::document doc = parser.iterate(view); // parse the JSON
```
## Use cases
From [jsonlines.org](http://jsonlines.org/examples/):
-14
View File
@@ -217,20 +217,6 @@ got full document at 29
```
**Advanced feature:**
On non-Windows systems, you can use memory-file mapping to create a `simdjson::padded_string_view`
from a file on disk.
```cpp
// If the macro _WIN32 is defined, this will not work since we do not support memory-file mapping
// under Windows at this time.
simdjson::padded_memory_map map(myfilename);
if (!map.is_valid()) { /* handle error */ }
simdjson::padded_string_view view = map.view(); // view is usable while padded_memory_map is in scope
ondemand::document doc = parser.iterate(view); // parse the JSON
```
Incomplete streams
-----------
-8
View File
@@ -93,14 +93,6 @@ Float values are represented as two 64-bit tape elements:
Performance consideration: We store numbers of the main tape because we believe that locality of reference is helpful for performance.
## Big Integers
When a JSON integer exceeds the 64-bit range (both signed and unsigned), it is classified as a big integer. By default, parsing returns `BIGINT_ERROR`. When `parser.number_as_string(true)` is set, the raw digits are stored on the string tape using the same format as strings (4-byte little-endian length prefix, followed by the UTF-8 digit bytes, followed by a null terminator).
A big integer is represented on the main tape as the 64-bit tape element `('Z' << 56) + x` where the payload `x` is the location on the string tape of the null-terminated digit string.
For example, the JSON value `99999999999999999999` would be stored as the string `"99999999999999999999"` on the string tape, with a `'Z'` tag on the main tape. Negative big integers include the leading minus sign.
## Root node
Each JSON document will have two special 64-bit tape elements representing a root node, one at the beginning and one at the end.
-3
View File
@@ -54,9 +54,6 @@ static void print_json(std::ostream& os, simdjson::dom::element element) noexcep
case simdjson::dom::element_type::NULL_VALUE:
os << "null" << endl;
break;
case simdjson::dom::element_type::BIGINT:
os << element.get_bigint().value_unsafe() << endl;
break;
}
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+1
View File
@@ -119,4 +119,5 @@
#define SIMDJSON_CONSTEVAL 0
#endif // defined(__cpp_consteval) && __cpp_consteval >= 201811L && defined(__cpp_lib_constexpr_string) && __cpp_lib_constexpr_string >= 201907L
#endif // !defined(SIMDJSON_CONSTEVAL)
#endif // SIMDJSON_COMPILER_CHECK_H
-9
View File
@@ -141,15 +141,6 @@ inline bool document::dump_raw_tape(std::ostream &os) const noexcept {
case 'r': // we start and end with the root node
// should we be hitting the root node?
return false;
case 'Z': // we have a big integer
os << "bigint ";
std::memcpy(&string_length, string_buf.get() + payload, sizeof(uint32_t));
os << std::string_view(
reinterpret_cast<const char *>(string_buf.get() + payload + sizeof(uint32_t)),
string_length
);
os << '\n';
break;
default:
return false;
}
-22
View File
@@ -81,10 +81,6 @@ simdjson_inline simdjson_result<bool> simdjson_result<dom::element>::get_bool()
if (error()) { return error(); }
return first.get_bool();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<dom::element>::get_bigint() const noexcept {
if (error()) { return error(); }
return first.get_bigint();
}
simdjson_inline bool simdjson_result<dom::element>::is_array() const noexcept {
return !error() && first.is_array();
@@ -114,9 +110,6 @@ simdjson_inline bool simdjson_result<dom::element>::is_bool() const noexcept {
simdjson_inline bool simdjson_result<dom::element>::is_null() const noexcept {
return !error() && first.is_null();
}
simdjson_inline bool simdjson_result<dom::element>::is_bigint() const noexcept {
return !error() && first.is_bigint();
}
simdjson_inline simdjson_result<dom::element> simdjson_result<dom::element>::operator[](std::string_view key) const noexcept {
if (error()) { return error(); }
@@ -225,15 +218,6 @@ inline simdjson_result<bool> element::get_bool() const noexcept {
}
return INCORRECT_TYPE;
}
inline simdjson_result<std::string_view> element::get_bigint() const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable());
switch (tape.tape_ref_type()) {
case internal::tape_type::BIGINT:
return tape.get_string_view();
default:
return INCORRECT_TYPE;
}
}
inline simdjson_result<const char *> element::get_c_str() const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
switch (tape.tape_ref_type()) {
@@ -376,10 +360,6 @@ inline bool element::is_null() const noexcept {
return tape.is_null_on_tape();
}
inline bool element::is_bigint() const noexcept {
return tape.tape_ref_type() == internal::tape_type::BIGINT;
}
#if SIMDJSON_EXCEPTIONS
inline element::operator bool() const noexcept(false) { return get<bool>(); }
@@ -512,8 +492,6 @@ inline std::ostream& operator<<(std::ostream& out, element_type type) {
return out << "bool";
case element_type::NULL_VALUE:
return out << "null";
case element_type::BIGINT:
return out << "bigint";
default:
return out << "unexpected content!!!"; // abort() usage is forbidden in the library
}
+1 -17
View File
@@ -21,8 +21,7 @@ enum class element_type {
DOUBLE = 'd', ///< double: Any number with a "." or "e" that fits in double.
STRING = '"', ///< std::string_view
BOOL = 't', ///< bool
NULL_VALUE = 'n', ///< null
BIGINT = 'Z' ///< std::string_view: big integer stored as raw digit string
NULL_VALUE = 'n' ///< null
};
/**
@@ -121,14 +120,6 @@ public:
*/
inline simdjson_result<bool> get_bool() const noexcept;
/**
* Read this element as a big integer (raw digit string).
*
* @returns A string_view of the raw digits, or:
* INCORRECT_TYPE if the JSON element is not a big integer.
*/
inline simdjson_result<std::string_view> get_bigint() const noexcept;
/**
* Whether this element is a json array.
*
@@ -184,11 +175,6 @@ public:
*/
inline bool is_null() const noexcept;
/**
* Whether this element is a big integer (number exceeding 64-bit range).
*/
inline bool is_bigint() const noexcept;
/**
* Tell whether the value can be cast to provided type (T).
*
@@ -547,7 +533,6 @@ public:
simdjson_inline simdjson_result<uint64_t> get_uint64() const noexcept;
simdjson_inline simdjson_result<double> get_double() const noexcept;
simdjson_inline simdjson_result<bool> get_bool() const noexcept;
simdjson_inline simdjson_result<std::string_view> get_bigint() const noexcept;
simdjson_inline bool is_array() const noexcept;
simdjson_inline bool is_object() const noexcept;
@@ -558,7 +543,6 @@ public:
simdjson_inline bool is_number() const noexcept;
simdjson_inline bool is_bool() const noexcept;
simdjson_inline bool is_null() const noexcept;
simdjson_inline bool is_bigint() const noexcept;
simdjson_inline simdjson_result<dom::element> operator[](std::string_view key) const noexcept;
simdjson_inline simdjson_result<dom::element> operator[](const char *key) const noexcept;
+1 -3
View File
@@ -36,11 +36,10 @@ inline bool parser::dump_raw_tape(std::ostream &os) const noexcept {
}
inline simdjson_result<size_t> parser::read_file(std::string_view path) noexcept {
const std::string path_copy(path);
// Open the file
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_DEPRECATED_WARNING // Disable CRT_SECURE warning on MSVC: manually verified this is safe
std::FILE *fp = std::fopen(path_copy.c_str(), "rb");
std::FILE *fp = std::fopen(path.data(), "rb");
SIMDJSON_POP_DISABLE_WARNINGS
if (fp == nullptr) {
@@ -133,7 +132,6 @@ inline simdjson_result<element> parser::parse_into_document(document& provided_d
buf += 3;
len -= 3;
}
implementation->_number_as_string = _number_as_string;
_error = implementation->parse(buf, len, provided_doc);
if (_error) { return _error; }
-10
View File
@@ -610,13 +610,6 @@ public:
inline bool dump_raw_tape(std::ostream &os) const noexcept;
/**
* When enabled, big integers (exceeding uint64 range) are stored as strings
* in the tape instead of returning BIGINT_ERROR. Default: false.
*/
inline void number_as_string(bool enabled) noexcept { _number_as_string = enabled; }
inline bool number_as_string() const noexcept { return _number_as_string; }
private:
/**
* The maximum document length this parser will automatically support.
@@ -625,9 +618,6 @@ private:
*/
size_t _max_capacity;
/** Whether to store big integers as strings instead of returning BIGINT_ERROR */
bool _number_as_string{false};
/**
* The loaded buffer (reused each time load() is called)
*/
-6
View File
@@ -460,12 +460,6 @@ inline void string_builder<serializer>::append(simdjson::dom::element value) {
case tape_type::STRING:
format.string(iter.get_string_view());
break;
case tape_type::BIGINT: {
// Big integer stored as string — output raw digits (no quotes)
auto sv = iter.get_string_view();
format.chars(sv.data(), sv.data() + sv.size());
break;
}
case tape_type::INT64:
format.number(iter.next_tape_value<int64_t>());
iter.json_index++; // numbers take up 2 spots, so we need to increment
@@ -41,19 +41,6 @@ simdjson_inline int leading_zeroes(uint64_t input_num) {
#endif// _MSC_VER
}
simdjson_inline int trailing_zeroes(uint64_t input_num) {
#ifdef _MSC_VER
unsigned long trailing_zero = 0;
// Search the mask data from least significant bit (LSB)
// to most significant bit (MSB) for a set bit (1).
if (_BitScanForward64(&trailing_zero, input_num))
return (int)trailing_zero;
else return 64;
#else
return __builtin_ctzll(input_num);
#endif// _MSC_VER
}
} // unnamed namespace
} // namespace fallback
} // namespace simdjson
@@ -275,7 +275,7 @@ simdjson_warn_unused simdjson_result<std::string> to_json_string(const Z &z, siz
}
template <class Z>
simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
simdjson_warn_unused simdjson_error to_json(const Z &z, std::string &s, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
string_builder b(initial_capacity);
append(b, z);
std::string_view view;
@@ -352,7 +352,7 @@ simdjson_warn_unused simdjson_result<std::string> to_json(const Z &z, size_t ini
return std::string(s);
}
template <class Z>
simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = SIMDJSON_IMPLEMENTATION::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
simdjson_warn_unused simdjson_error to_json(const Z &z, std::string &s, size_t initial_capacity = SIMDJSON_IMPLEMENTATION::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
SIMDJSON_IMPLEMENTATION::builder::string_builder b(initial_capacity);
SIMDJSON_IMPLEMENTATION::builder::append(b, z);
std::string_view view;
@@ -27,50 +27,12 @@
#define SIMDJSON_EXPERIMENTAL_HAS_NEON 1
#endif
#endif
#if defined(__loongarch_sx)
#ifndef SIMDJSON_EXPERIMENTAL_HAS_LSX
#define SIMDJSON_EXPERIMENTAL_HAS_LSX 1
#endif
#endif
#if defined(__riscv_v_intrinsic) && __riscv_v_intrinsic >= 11000 && \
defined(__riscv_vector)
#ifndef SIMDJSON_EXPERIMENTAL_HAS_RVV
#define SIMDJSON_EXPERIMENTAL_HAS_RVV 1
#endif
#endif
#if (defined(__PPC64__) || defined(_M_PPC64)) && defined(__ALTIVEC__)
#ifndef SIMDJSON_EXPERIMENTAL_HAS_PPC64
#define SIMDJSON_EXPERIMENTAL_HAS_PPC64 1
#endif
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_NEON
#include <arm_neon.h>
#ifdef _MSC_VER
#include <intrin.h>
#endif
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_SSE2
#include <emmintrin.h>
#ifdef _MSC_VER
#include <intrin.h>
#endif
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_LSX
#include <lsxintrin.h>
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_RVV
#include <riscv_vector.h>
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_PPC64
#include <altivec.h>
#ifdef bool
#undef bool
#endif
#ifdef vector
#undef vector
#endif
#endif
namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
@@ -171,45 +133,16 @@ simdjson_inline bool fast_needs_escaping(std::string_view view) {
}
return _mm_movemask_epi8(running) != 0;
}
#elif SIMDJSON_EXPERIMENTAL_HAS_PPC64
simdjson_inline bool fast_needs_escaping(std::string_view view) {
if (view.size() < 16) {
return simple_needs_escaping(view);
}
size_t i = 0;
__vector unsigned char running = vec_splats((unsigned char)0);
__vector unsigned char v34 = vec_splats((unsigned char)34);
__vector unsigned char v92 = vec_splats((unsigned char)92);
__vector unsigned char v32 = vec_splats((unsigned char)32);
for (; i + 15 < view.size(); i += 16) {
__vector unsigned char word =
vec_vsx_ld(0, reinterpret_cast<const unsigned char *>(view.data() + i));
running = vec_or(running, (__vector unsigned char)vec_cmpeq(word, v34));
running = vec_or(running, (__vector unsigned char)vec_cmpeq(word, v92));
running = vec_or(running,
(__vector unsigned char)vec_cmplt(word, v32));
}
if (i < view.size()) {
__vector unsigned char word = vec_vsx_ld(
0, reinterpret_cast<const unsigned char *>(view.data() + view.length() - 16));
running = vec_or(running, (__vector unsigned char)vec_cmpeq(word, v34));
running = vec_or(running, (__vector unsigned char)vec_cmpeq(word, v92));
running = vec_or(running,
(__vector unsigned char)vec_cmplt(word, v32));
}
return !vec_all_eq(running, vec_splats((unsigned char)0));
}
#else
simdjson_inline bool fast_needs_escaping(std::string_view view) {
return simple_needs_escaping(view);
}
#endif
// Scalar fallback for finding next quotable character
SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline size_t
find_next_json_quotable_character_scalar(const std::string_view view,
size_t location) noexcept {
SIMDJSON_CONSTEXPR_LAMBDA inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
for (auto pos = view.begin() + location; pos != view.end(); ++pos) {
if (json_quotable_character[static_cast<uint8_t>(*pos)]) {
return pos - view.begin();
@@ -218,213 +151,6 @@ find_next_json_quotable_character_scalar(const std::string_view view,
return size_t(view.size());
}
// SIMD-accelerated position finding that directly locates the first quotable
// character, combining detection and position extraction in a single pass to
// minimize redundant work.
#if SIMDJSON_EXPERIMENTAL_HAS_NEON
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
// SIMD constants for characters requiring escape
uint8x16_t v34 = vdupq_n_u8(34); // '"'
uint8x16_t v92 = vdupq_n_u8(92); // '\\'
uint8x16_t v32 = vdupq_n_u8(32); // control char threshold
while (remaining >= 16) {
uint8x16_t word = vld1q_u8(ptr);
// Check for quotable characters: '"', '\\', or control chars (< 32)
uint8x16_t needs_escape = vceqq_u8(word, v34);
needs_escape = vorrq_u8(needs_escape, vceqq_u8(word, v92));
needs_escape = vorrq_u8(needs_escape, vcltq_u8(word, v32));
const uint8x8_t res = vshrn_n_u16(vreinterpretq_u16_u8(needs_escape), 4);
const uint64_t mask = vget_lane_u64(vreinterpret_u64_u8(res), 0);
if(mask != 0) {
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
auto trailing_zero = trailing_zeroes(mask);
return offset + (trailing_zero >> 2);
}
ptr += 16;
remaining -= 16;
}
// Scalar fallback for remaining bytes
size_t current = len - remaining;
return find_next_json_quotable_character_scalar(view, current);
}
#elif SIMDJSON_EXPERIMENTAL_HAS_SSE2
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
// SIMD constants
__m128i v34 = _mm_set1_epi8(34); // '"'
__m128i v92 = _mm_set1_epi8(92); // '\\'
__m128i v31 = _mm_set1_epi8(31); // for control char detection
while (remaining >= 16) {
__m128i word = _mm_loadu_si128(reinterpret_cast<const __m128i *>(ptr));
// Check for quotable characters
__m128i needs_escape = _mm_cmpeq_epi8(word, v34);
needs_escape = _mm_or_si128(needs_escape, _mm_cmpeq_epi8(word, v92));
needs_escape = _mm_or_si128(
needs_escape,
_mm_cmpeq_epi8(_mm_subs_epu8(word, v31), _mm_setzero_si128()));
int mask = _mm_movemask_epi8(needs_escape);
if (mask != 0) {
// Found quotable character - use trailing zero count to find position
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
return offset + trailing_zeroes(mask);
}
ptr += 16;
remaining -= 16;
}
// Scalar fallback for remaining bytes
size_t current = len - remaining;
return find_next_json_quotable_character_scalar(view, current);
}
#elif SIMDJSON_EXPERIMENTAL_HAS_LSX
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
//SIMD constants for characters requiring escape
__m128i v34 = __lsx_vreplgr2vr_b(34); // '"'
__m128i v92 = __lsx_vreplgr2vr_b(92); // '\\'
__m128i v32 = __lsx_vreplgr2vr_b(32); // control char threshold
while (remaining >= 16){
__m128i word = __lsx_vld(ptr, 0);
//Check for the quotable characters: '"', '\\', or control char (<32)
__m128i needs_escape = __lsx_vseq_b(word, v34);
needs_escape = __lsx_vor_v(needs_escape, __lsx_vseq_b(word, v92));
needs_escape = __lsx_vor_v(needs_escape, __lsx_vslt_bu(word, v32));
if (!__lsx_bz_v(needs_escape)){
//Found quotable character - extract exact byte position
uint64_t lo = __lsx_vpickve2gr_du(needs_escape,0);
uint64_t hi = __lsx_vpickve2gr_du(needs_escape,1);
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
if ( lo != 0) {
return offset + trailing_zeroes(lo) / 8;
} else {
return offset + 8 + trailing_zeroes(hi) / 8;
}
}
ptr += 16;
remaining -= 16;
}
size_t current = len - remaining;
return find_next_json_quotable_character_scalar(view, current);
}
#elif SIMDJSON_EXPERIMENTAL_HAS_RVV
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
while (remaining > 0) {
size_t vl = __riscv_vsetvl_e8m1(remaining);
vuint8m1_t word = __riscv_vle8_v_u8m1(ptr, vl);
// Check for quotable characters: '"', '\\', or control chars (< 32)
vbool8_t needs_escape = __riscv_vmseq(word, (uint8_t)34, vl);
needs_escape = __riscv_vmor(needs_escape,
__riscv_vmseq(word, (uint8_t)92, vl), vl);
needs_escape = __riscv_vmor(needs_escape,
__riscv_vmsltu(word, (uint8_t)32, vl), vl);
long first = __riscv_vfirst(needs_escape, vl);
if (first >= 0) {
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
return offset + first;
}
ptr += vl;
remaining -= vl;
}
return len;
}
#elif SIMDJSON_EXPERIMENTAL_HAS_PPC64
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
// SIMD constants for characters requiring escape
__vector unsigned char v34 = vec_splats((unsigned char)34); // '"'
__vector unsigned char v92 = vec_splats((unsigned char)92); // '\\'
__vector unsigned char v32 = vec_splats((unsigned char)32); // control char threshold
// Bitmask for vec_vbpermq to extract one bit per byte
const __vector unsigned char perm_mask = {0x78, 0x70, 0x68, 0x60, 0x58, 0x50,
0x48, 0x40, 0x38, 0x30, 0x28, 0x20,
0x18, 0x10, 0x08, 0x00};
while (remaining >= 16) {
__vector unsigned char word =
vec_vsx_ld(0, reinterpret_cast<const unsigned char *>(ptr));
// Check for quotable characters: '"', '\\', or control chars (< 32)
__vector unsigned char needs_escape =
(__vector unsigned char)vec_cmpeq(word, v34);
needs_escape = vec_or(needs_escape,
(__vector unsigned char)vec_cmpeq(word, v92));
needs_escape = vec_or(needs_escape,
(__vector unsigned char)vec_cmplt(word, v32));
__vector unsigned long long result =
(__vector unsigned long long)vec_vbpermq(needs_escape, perm_mask);
#ifdef __LITTLE_ENDIAN__
unsigned int mask = static_cast<unsigned int>(result[1]);
#else
unsigned int mask = static_cast<unsigned int>(result[0]);
#endif
if (mask != 0) {
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
return offset + __builtin_ctz(mask);
}
ptr += 16;
remaining -= 16;
}
// Scalar fallback for remaining bytes
size_t current = len - remaining;
return find_next_json_quotable_character_scalar(view, current);
}
#else
SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
return find_next_json_quotable_character_scalar(view, location);
}
#endif
SIMDJSON_CONSTEXPR_LAMBDA static std::string_view control_chars[] = {
"\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006",
"\\u0007", "\\b", "\\t", "\\n", "\\u000b", "\\f", "\\r",
@@ -437,7 +163,7 @@ SIMDJSON_CONSTEXPR_LAMBDA static std::string_view control_chars[] = {
// control characters (U+0000 through U+001F). There are two-character sequence
// escape representations of some popular characters:
// \", \\, \b, \f, \n, \r, \t.
SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline void escape_json_char(char c, char *&out) {
SIMDJSON_CONSTEXPR_LAMBDA void escape_json_char(char c, char *&out) {
if (c == '"') {
memcpy(out, "\\\"", 2);
out += 2;
@@ -451,20 +177,14 @@ SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline void escape_json_char(char c, char *&o
}
}
// Writes the escaped version of input to out, returning the number of bytes
// written. Uses SIMD position finding to locate quotable characters efficiently.
inline size_t write_string_escaped(const std::string_view input, char *out) {
size_t mysize = input.size();
// Use SIMD position finder directly - it returns mysize if no escape needed
size_t location = find_next_json_quotable_character(input, 0);
if (location == mysize) {
// Fast path: no escaping needed
if (!fast_needs_escaping(input)) { // fast path!
memcpy(out, input.data(), input.size());
return input.size();
}
const char *const initout = out;
size_t location = find_next_json_quotable_character(input, 0);
memcpy(out, input.data(), location);
out += location;
escape_json_char(input[location], out);
@@ -496,7 +216,7 @@ simdjson_inline bool string_builder::capacity_check(size_t upcoming_bytes) {
return true;
}
// check for overflow, most of the time there is no overflow
if (simdjson_unlikely(position + upcoming_bytes < position)) {
if (simdjson_likely(position + upcoming_bytes < position)) {
return false;
}
// We will rarely get here.
@@ -663,29 +383,12 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
}
}
else SIMDJSON_IF_CONSTEXPR(std::is_unsigned<number_type>::value) {
// Process 4 digits at a time instead of 2, reducing store operations
// and divisions by approximately half for large numbers.
constexpr size_t max_number_size = 20;
if (capacity_check(max_number_size)) {
using unsigned_type = typename std::make_unsigned<number_type>::type;
unsigned_type pv = static_cast<unsigned_type>(v);
size_t dc = internal::digit_count(pv);
char *write_pointer = buffer.get() + position + dc - 1;
// Process 4 digits per iteration for large numbers
while (pv >= 10000) {
unsigned_type q = pv / 10000;
unsigned_type r = pv % 10000;
unsigned_type r_hi = r / 100; // High 2 digits of remainder
unsigned_type r_lo = r % 100; // Low 2 digits of remainder
// Write low 2 digits first (rightmost), then high 2 digits
memcpy(write_pointer - 1, &internal::decimal_table[r_lo * 2], 2);
memcpy(write_pointer - 3, &internal::decimal_table[r_hi * 2], 2);
write_pointer -= 4;
pv = q;
}
// Handle remaining 1-4 digits with original 2-digit loop
while (pv >= 100) {
memcpy(write_pointer - 1, &internal::decimal_table[(pv % 100) * 2], 2);
write_pointer -= 2;
@@ -700,7 +403,6 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
}
}
else SIMDJSON_IF_CONSTEXPR(std::is_integral<number_type>::value) {
// Same 4-digit batching as unsigned path for signed integers
constexpr size_t max_number_size = 20;
if (capacity_check(max_number_size)) {
using unsigned_type = typename std::make_unsigned<number_type>::type;
@@ -714,20 +416,6 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
buffer.get()[position] = '-';
position += negative ? 1 : 0;
char *write_pointer = buffer.get() + position + dc - 1;
// Process 4 digits per iteration for large numbers
while (pv >= 10000) {
unsigned_type q = pv / 10000;
unsigned_type r = pv % 10000;
unsigned_type r_hi = r / 100;
unsigned_type r_lo = r % 100;
memcpy(write_pointer - 1, &internal::decimal_table[r_lo * 2], 2);
memcpy(write_pointer - 3, &internal::decimal_table[r_hi * 2], 2);
write_pointer -= 4;
pv = q;
}
// Handle remaining 1-4 digits
while (pv >= 100) {
memcpy(write_pointer - 1, &internal::decimal_table[(pv % 100) * 2], 2);
write_pointer -= 2;
@@ -280,7 +280,7 @@ simdjson_warn_unused simdjson_result<std::string> to_json(const Z &z, size_t ini
return std::string(s);
}
template <class Z>
simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = simdjson::SIMDJSON_IMPLEMENTATION::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
simdjson_warn_unused simdjson_error to_json(const Z &z, std::string &s, size_t initial_capacity = simdjson::SIMDJSON_IMPLEMENTATION::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
simdjson::SIMDJSON_IMPLEMENTATION::builder::string_builder b(initial_capacity);
b.append(z);
std::string_view sv;
+44 -35
View File
@@ -170,34 +170,46 @@ inline simdjson_result<value> array::at_path(std::string_view json_path) noexcep
return at_pointer(json_pointer);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
inline error_code array::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
inline simdjson_result<std::vector<value>> array::at_path_with_wildcard(std::string_view json_path) noexcept {
std::vector<value> result;
auto result_pair = get_next_key_and_json_path(json_path);
std::string_view key = result_pair.first;
std::string_view remaining_path = result_pair.second;
// Wildcard case
if (key=="*"){
for(auto element: *this) {
value val;
SIMDJSON_TRY(element.get(val));
if (remaining_path.empty()) {
callback(val);
} else {
error_code err = element.for_each_at_path_with_wildcard(remaining_path, callback);
if(err) { return err; }
if(key=="*"){
for(auto element: *this){
if(element.error()){
return element.error();
}
if(remaining_path.empty()){
// Use value_unsafe() because we've already checked for errors above.
// The 'element' is a simdjson_result<value> wrapper, and we need to extract
// the underlying value. value_unsafe() is safe here because error() returned false.
result.push_back(std::move(element).value_unsafe());
}else{
auto nested_result = element.at_path_with_wildcard(remaining_path);
if(nested_result.error()){
return nested_result.error();
}
// Same logic as above.
std::vector<value> nested_matches = std::move(nested_result).value_unsafe();
result.insert(result.end(),
std::make_move_iterator(nested_matches.begin()),
std::make_move_iterator(nested_matches.end()));
}
}
return SUCCESS;
} else {
return result;
}else{
// Specific index case in which we access the element at the given index
size_t idx = 0;
size_t idx=0;
for (char c : key) {
for(char c:key){
if(c < '0' || c > '9'){
return INVALID_JSON_POINTER;
}
@@ -205,13 +217,16 @@ inline error_code array::for_each_at_path_with_wildcard(std::string_view json_pa
}
auto element = at(idx);
value val;
SIMDJSON_TRY(element.get(val));
if (remaining_path.empty()){
callback(val);
return SUCCESS;
} else {
return element.for_each_at_path_with_wildcard(remaining_path, callback);
if(element.error()){
return element.error();
}
if(remaining_path.empty()){
result.push_back(std::move(element).value_unsafe());
return result;
}else{
return element.at_path_with_wildcard(remaining_path);
}
}
}
@@ -274,15 +289,9 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdj
if (error()) { return error(); }
return first.at_path(json_path);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, SIMDJSON_IMPLEMENTATION::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array>::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
simdjson_inline simdjson_result<std::vector<SIMDJSON_IMPLEMENTATION::ondemand::value>> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array>::at_path_with_wildcard(std::string_view json_path) noexcept {
if (error()) { return error(); }
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
return first.at_path_with_wildcard(json_path);
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array>::raw_json() noexcept {
if (error()) { return error(); }
+4 -18
View File
@@ -119,21 +119,13 @@ public:
inline simdjson_result<value> at_path(std::string_view json_path) noexcept;
/**
* Call the provided callback for each value matching the given JSONPath
* expression with wildcard support.
* Get all values matching the given JSONPath expression with wildcard support.
* Supports wildcard patterns like "[*]" to match all array elements.
*
* @param json_path JSONPath expression with wildcards
* @param callback Function called for each matching value
* @return error_code indicating success or failure
* @return Vector of values matching the wildcard pattern
*/
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
inline simdjson_result<std::vector<value>> at_path_with_wildcard(std::string_view json_path) noexcept;
/**
* Consumes the array and returns a string_view instance corresponding to the
@@ -257,13 +249,7 @@ public:
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at(size_t index) noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, SIMDJSON_IMPLEMENTATION::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
simdjson_inline simdjson_result<std::vector<SIMDJSON_IMPLEMENTATION::ondemand::value>> at_path_with_wildcard(std::string_view json_path) noexcept;
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
// TODO: move this code into object-inl.h
@@ -16,8 +16,6 @@
#include "simdjson/generic/ondemand/deserialize.h"
#endif // SIMDJSON_CONDITIONAL_INCLUDE
#include <limits>
namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
namespace ondemand {
@@ -133,18 +131,6 @@ simdjson_inline simdjson_result<int64_t> document::get_int64() noexcept {
simdjson_inline simdjson_result<int64_t> document::get_int64_in_string() noexcept {
return get_root_value_iterator().get_root_int64_in_string(true);
}
simdjson_inline simdjson_result<uint32_t> document::get_uint32() noexcept {
uint64_t result;
SIMDJSON_TRY(get_uint64().get(result));
if (result > (std::numeric_limits<uint32_t>::max)()) { return NUMBER_OUT_OF_RANGE; }
return static_cast<uint32_t>(result);
}
simdjson_inline simdjson_result<int32_t> document::get_int32() noexcept {
int64_t result;
SIMDJSON_TRY(get_int64().get(result));
if (result > (std::numeric_limits<int32_t>::max)() || result < (std::numeric_limits<int32_t>::min)()) { return NUMBER_OUT_OF_RANGE; }
return static_cast<int32_t>(result);
}
simdjson_inline simdjson_result<double> document::get_double() noexcept {
return get_root_value_iterator().get_root_double(true);
}
@@ -178,8 +164,6 @@ template<> simdjson_inline simdjson_result<std::string_view> document::get() & n
template<> simdjson_inline simdjson_result<double> document::get() & noexcept { return get_double(); }
template<> simdjson_inline simdjson_result<uint64_t> document::get() & noexcept { return get_uint64(); }
template<> simdjson_inline simdjson_result<int64_t> document::get() & noexcept { return get_int64(); }
template<> simdjson_inline simdjson_result<uint32_t> document::get() & noexcept { return get_uint32(); }
template<> simdjson_inline simdjson_result<int32_t> document::get() & noexcept { return get_int32(); }
template<> simdjson_inline simdjson_result<bool> document::get() & noexcept { return get_bool(); }
template<> simdjson_inline simdjson_result<value> document::get() & noexcept { return get_value(); }
@@ -190,8 +174,6 @@ template<> simdjson_warn_unused simdjson_inline error_code document::get(std::st
template<> simdjson_warn_unused simdjson_inline error_code document::get(double& out) & noexcept { return get_double().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(uint64_t& out) & noexcept { return get_uint64().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(int64_t& out) & noexcept { return get_int64().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(uint32_t& out) & noexcept { return get_uint32().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(int32_t& out) & noexcept { return get_int32().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(bool& out) & noexcept { return get_bool().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(value& out) & noexcept { return get_value().get(out); }
@@ -365,14 +347,8 @@ simdjson_inline simdjson_result<value> document::at_path(std::string_view json_p
}
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
simdjson_inline error_code document::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
rewind(); // Rewind the document each time for_each_at_path_with_wildcard is called
simdjson_inline simdjson_result<std::vector<value>> document::at_path_with_wildcard(std::string_view json_path) noexcept {
rewind(); // Rewind the document each time at_path_with_wildcard is called
if (json_path.empty()) {
return INVALID_JSON_POINTER;
}
@@ -380,9 +356,9 @@ simdjson_inline error_code document::for_each_at_path_with_wildcard(std::string_
SIMDJSON_TRY(type().get(t));
switch (t) {
case json_type::array:
return (*this).get_array().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
return (*this).get_array().at_path_with_wildcard(json_path);
case json_type::object:
return (*this).get_object().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
return (*this).get_object().at_path_with_wildcard(json_path);
default:
return INVALID_JSON_POINTER;
}
@@ -528,14 +504,6 @@ simdjson_inline simdjson_result<int64_t> simdjson_result<SIMDJSON_IMPLEMENTATION
if (error()) { return error(); }
return first.get_int64_in_string();
}
simdjson_inline simdjson_result<uint32_t> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::get_uint32() noexcept {
if (error()) { return error(); }
return first.get_uint32();
}
simdjson_inline simdjson_result<int32_t> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::get_int32() noexcept {
if (error()) { return error(); }
return first.get_int32();
}
simdjson_inline simdjson_result<double> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::get_double() noexcept {
if (error()) { return error(); }
return first.get_double();
@@ -719,15 +687,9 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjs
return first.at_path(json_path);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, SIMDJSON_IMPLEMENTATION::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
simdjson_inline simdjson_result<std::vector<SIMDJSON_IMPLEMENTATION::ondemand::value>> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::at_path_with_wildcard(std::string_view json_path) noexcept {
if (error()) { return error(); }
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
return first.at_path_with_wildcard(json_path);
}
#if SIMDJSON_STATIC_REFLECTION
@@ -769,18 +731,6 @@ simdjson_inline simdjson_result<uint64_t> document_reference::get_uint64() noexc
simdjson_inline simdjson_result<uint64_t> document_reference::get_uint64_in_string() noexcept { return doc->get_root_value_iterator().get_root_uint64_in_string(false); }
simdjson_inline simdjson_result<int64_t> document_reference::get_int64() noexcept { return doc->get_root_value_iterator().get_root_int64(false); }
simdjson_inline simdjson_result<int64_t> document_reference::get_int64_in_string() noexcept { return doc->get_root_value_iterator().get_root_int64_in_string(false); }
simdjson_inline simdjson_result<uint32_t> document_reference::get_uint32() noexcept {
uint64_t result;
SIMDJSON_TRY(doc->get_root_value_iterator().get_root_uint64(false).get(result));
if (result > (std::numeric_limits<uint32_t>::max)()) { return NUMBER_OUT_OF_RANGE; }
return static_cast<uint32_t>(result);
}
simdjson_inline simdjson_result<int32_t> document_reference::get_int32() noexcept {
int64_t result;
SIMDJSON_TRY(doc->get_root_value_iterator().get_root_int64(false).get(result));
if (result > (std::numeric_limits<int32_t>::max)() || result < (std::numeric_limits<int32_t>::min)()) { return NUMBER_OUT_OF_RANGE; }
return static_cast<int32_t>(result);
}
simdjson_inline simdjson_result<double> document_reference::get_double() noexcept { return doc->get_root_value_iterator().get_root_double(false); }
simdjson_inline simdjson_result<double> document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double(false); }
simdjson_inline simdjson_result<std::string_view> document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); }
@@ -798,8 +748,6 @@ template<> simdjson_inline simdjson_result<std::string_view> document_reference:
template<> simdjson_inline simdjson_result<double> document_reference::get() & noexcept { return get_double(); }
template<> simdjson_inline simdjson_result<uint64_t> document_reference::get() & noexcept { return get_uint64(); }
template<> simdjson_inline simdjson_result<int64_t> document_reference::get() & noexcept { return get_int64(); }
template<> simdjson_inline simdjson_result<uint32_t> document_reference::get() & noexcept { return get_uint32(); }
template<> simdjson_inline simdjson_result<int32_t> document_reference::get() & noexcept { return get_int32(); }
template<> simdjson_inline simdjson_result<bool> document_reference::get() & noexcept { return get_bool(); }
template<> simdjson_inline simdjson_result<value> document_reference::get() & noexcept { return get_value(); }
#if SIMDJSON_EXCEPTIONS
@@ -838,13 +786,7 @@ simdjson_inline simdjson_result<number> document_reference::get_number() noexcep
simdjson_inline simdjson_result<std::string_view> document_reference::raw_json_token() noexcept { return doc->raw_json_token(); }
simdjson_inline simdjson_result<value> document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); }
simdjson_inline simdjson_result<value> document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); }
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
simdjson_inline error_code document_reference::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept { return doc->for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback)); }
simdjson_inline simdjson_result<std::vector<value>> document_reference::at_path_with_wildcard(std::string_view json_path) noexcept { return doc->at_path_with_wildcard(json_path); }
simdjson_inline simdjson_result<std::string_view> document_reference::raw_json() noexcept { return doc->raw_json();}
simdjson_inline document_reference::operator document&() const noexcept { return *doc; }
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
@@ -937,14 +879,6 @@ simdjson_inline simdjson_result<int64_t> simdjson_result<SIMDJSON_IMPLEMENTATION
if (error()) { return error(); }
return first.get_int64_in_string();
}
simdjson_inline simdjson_result<uint32_t> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get_uint32() noexcept {
if (error()) { return error(); }
return first.get_uint32();
}
simdjson_inline simdjson_result<int32_t> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get_int32() noexcept {
if (error()) { return error(); }
return first.get_int32();
}
simdjson_inline simdjson_result<double> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get_double() noexcept {
if (error()) { return error(); }
return first.get_double();
@@ -1109,17 +1043,11 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjs
}
return first.at_path(json_path);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, SIMDJSON_IMPLEMENTATION::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
simdjson_inline simdjson_result<std::vector<SIMDJSON_IMPLEMENTATION::ondemand::value>> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::at_path_with_wildcard(std::string_view json_path) noexcept {
if (error()) {
return error();
}
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
return first.at_path_with_wildcard(json_path);
}
#if SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
+11 -58
View File
@@ -76,26 +76,6 @@ public:
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit integer.
*/
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
/**
* Cast this JSON value to a 32-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint32_t.
*
* @returns A 32-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint32_t.
*/
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
/**
* Cast this JSON value to a 32-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int32_t.
*
* @returns A 32-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int32_t.
*/
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
/**
* Cast this JSON value to a double.
*
@@ -744,24 +724,21 @@ public:
simdjson_inline simdjson_result<value> at_path(std::string_view json_path) noexcept;
/**
* Call the provided callback for each value matching the given JSONPath
* expression with wildcard support.
* Get all values matching the given JSONPath expression with wildcard support.
*
* Supports wildcard patterns like "$.array[*]" or "$.object.*" to match multiple elements.
*
* This method materializes all matching values into a vector.
* The document will be consumed after this call.
*
* @param json_path JSONPath expression with wildcards
* @param callback Function called for each matching value
* @return error_code indicating success or failure
* @return Vector of values matching the wildcard pattern, or:
* - INVALID_JSON_POINTER if the JSONPath cannot be parsed
* - NO_SUCH_FIELD if a field does not exist
* - INDEX_OUT_OF_BOUNDS if an array index is out of bounds
* - INCORRECT_TYPE if path traversal encounters wrong type
*/
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
simdjson_inline simdjson_result<std::vector<value>> at_path_with_wildcard(std::string_view json_path) noexcept;
/**
* Consumes the document and returns a string_view instance corresponding to the
@@ -852,8 +829,6 @@ public:
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
@@ -982,13 +957,7 @@ public:
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
simdjson_inline simdjson_result<value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
simdjson_inline simdjson_result<std::vector<value>> at_path_with_wildcard(std::string_view json_path) noexcept;
private:
document *doc{nullptr};
@@ -1013,8 +982,6 @@ public:
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
@@ -1074,13 +1041,7 @@ public:
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, SIMDJSON_IMPLEMENTATION::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
simdjson_inline simdjson_result<std::vector<SIMDJSON_IMPLEMENTATION::ondemand::value>> at_path_with_wildcard(std::string_view json_path) noexcept;
#if SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
@@ -1108,8 +1069,6 @@ public:
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
@@ -1165,13 +1124,7 @@ public:
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, SIMDJSON_IMPLEMENTATION::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
simdjson_inline simdjson_result<std::vector<SIMDJSON_IMPLEMENTATION::ondemand::value>> at_path_with_wildcard(std::string_view json_path) noexcept;
#if SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
+23 -21
View File
@@ -177,13 +177,9 @@ inline simdjson_result<value> object::at_path(std::string_view json_path) noexce
return at_pointer(json_pointer);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
inline error_code object::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
inline simdjson_result<std::vector<value>> object::at_path_with_wildcard(std::string_view json_path) noexcept {
std::vector<value> result;
auto result_pair = get_next_key_and_json_path(json_path);
std::string_view key = result_pair.first;
std::string_view remaining_path = result_pair.second;
@@ -193,22 +189,34 @@ inline error_code object::for_each_at_path_with_wildcard(std::string_view json_p
for (auto field : *this) {
value val;
SIMDJSON_TRY(field.value().get(val));
if (remaining_path.empty()) {
callback(val);
result.push_back(std::move(val));
} else {
SIMDJSON_TRY(val.for_each_at_path_with_wildcard(remaining_path, callback));
auto nested_result = val.at_path_with_wildcard(remaining_path);
if (nested_result.error()) {
return nested_result.error();
}
// Extract and append all nested matches to our result
std::vector<value> nested_vec;
SIMDJSON_TRY(std::move(nested_result).get(nested_vec));
result.insert(result.end(),
std::make_move_iterator(nested_vec.begin()),
std::make_move_iterator(nested_vec.end()));
}
}
return SUCCESS;
return result;
} else {
value val;
SIMDJSON_TRY(find_field(key).get(val));
if (remaining_path.empty()) {
callback(val);
return SUCCESS;
result.push_back(std::move(val));
return result;
} else {
return val.for_each_at_path_with_wildcard(remaining_path, callback);
return val.at_path_with_wildcard(remaining_path);
}
}
}
@@ -339,15 +347,9 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjs
return first.at_path(json_path);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, SIMDJSON_IMPLEMENTATION::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object>::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
simdjson_inline simdjson_result<std::vector<SIMDJSON_IMPLEMENTATION::ondemand::value>> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object>::at_path_with_wildcard(std::string_view json_path) noexcept {
if (error()) { return error(); }
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
return first.at_path_with_wildcard(json_path);
}
inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object>::reset() noexcept {
+4 -18
View File
@@ -172,21 +172,13 @@ public:
inline simdjson_result<value> at_path(std::string_view json_path) noexcept;
/**
* Call the provided callback for each value matching the given JSONPath
* expression with wildcard support.
* Get all values matching the given JSONPath expression with wildcard support.
* Supports wildcard patterns like ".*" to match all object fields.
*
* @param json_path JSONPath expression with wildcards
* @param callback Function called for each matching value
* @return error_code indicating success or failure
* @return Vector of values matching the wildcard pattern
*/
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
inline simdjson_result<std::vector<value>> at_path_with_wildcard(std::string_view json_path) noexcept;
/**
* Reset the iterator so that we are pointing back at the
@@ -336,13 +328,7 @@ public:
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> operator[](std::string_view key) && noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, SIMDJSON_IMPLEMENTATION::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
simdjson_inline simdjson_result<std::vector<SIMDJSON_IMPLEMENTATION::ondemand::value>> at_path_with_wildcard(std::string_view json_path) noexcept;
inline simdjson_result<bool> reset() noexcept;
inline simdjson_result<bool> is_empty() noexcept;
inline simdjson_result<size_t> count_fields() & noexcept;
@@ -172,7 +172,7 @@ simdjson_pure simdjson_inline size_t parser::max_depth() const noexcept {
}
simdjson_inline void parser::set_max_capacity(size_t max_capacity) noexcept {
if(max_capacity > dom::MINIMAL_DOCUMENT_CAPACITY) {
if(max_capacity < dom::MINIMAL_DOCUMENT_CAPACITY) {
_max_capacity = max_capacity;
} else {
_max_capacity = dom::MINIMAL_DOCUMENT_CAPACITY;
@@ -199,7 +199,7 @@ simdjson_inline simdjson_warn_unused ondemand::parser& parser::get_parser() {
return *parser::get_parser_instance();
}
simdjson_inline bool parser::release_parser() {
simdjson_inline bool release_parser() {
auto &parser_instance = parser::get_threadlocal_parser_if_exists();
if (parser_instance) {
parser_instance.reset();
@@ -388,6 +388,8 @@ public:
static simdjson_inline bool release_parser();
private:
friend bool release_parser();
friend ondemand::parser& get_parser();
/** Get the thread-local parser instance, allocates it if needed */
static simdjson_inline simdjson_warn_unused std::unique_ptr<ondemand::parser>& get_parser_instance();
/** Get the thread-local parser instance, it might be null */
+6 -44
View File
@@ -12,8 +12,6 @@
#include "simdjson/generic/ondemand/value.h"
#endif // SIMDJSON_CONDITIONAL_INCLUDE
#include <limits>
namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
namespace ondemand {
@@ -74,18 +72,6 @@ simdjson_inline simdjson_result<int64_t> value::get_int64() noexcept {
simdjson_inline simdjson_result<int64_t> value::get_int64_in_string() noexcept {
return iter.get_int64_in_string();
}
simdjson_inline simdjson_result<uint32_t> value::get_uint32() noexcept {
uint64_t result;
SIMDJSON_TRY(get_uint64().get(result));
if (result > (std::numeric_limits<uint32_t>::max)()) { return NUMBER_OUT_OF_RANGE; }
return static_cast<uint32_t>(result);
}
simdjson_inline simdjson_result<int32_t> value::get_int32() noexcept {
int64_t result;
SIMDJSON_TRY(get_int64().get(result));
if (result > (std::numeric_limits<int32_t>::max)() || result < (std::numeric_limits<int32_t>::min)()) { return NUMBER_OUT_OF_RANGE; }
return static_cast<int32_t>(result);
}
simdjson_inline simdjson_result<bool> value::get_bool() noexcept {
return iter.get_bool();
}
@@ -101,8 +87,6 @@ template<> simdjson_inline simdjson_result<number> value::get() noexcept { retur
template<> simdjson_inline simdjson_result<double> value::get() noexcept { return get_double(); }
template<> simdjson_inline simdjson_result<uint64_t> value::get() noexcept { return get_uint64(); }
template<> simdjson_inline simdjson_result<int64_t> value::get() noexcept { return get_int64(); }
template<> simdjson_inline simdjson_result<uint32_t> value::get() noexcept { return get_uint32(); }
template<> simdjson_inline simdjson_result<int32_t> value::get() noexcept { return get_int32(); }
template<> simdjson_inline simdjson_result<bool> value::get() noexcept { return get_bool(); }
@@ -114,8 +98,6 @@ template<> simdjson_warn_unused simdjson_inline error_code value::get(number& ou
template<> simdjson_warn_unused simdjson_inline error_code value::get(double& out) noexcept { return get_double().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(uint64_t& out) noexcept { return get_uint64().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(int64_t& out) noexcept { return get_int64().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(uint32_t& out) noexcept { return get_uint32().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(int32_t& out) noexcept { return get_int32().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(bool& out) noexcept { return get_bool().get(out); }
#if SIMDJSON_EXCEPTIONS
@@ -314,20 +296,14 @@ simdjson_inline simdjson_result<value> value::at_path(std::string_view json_path
}
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
inline error_code value::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
inline simdjson_result<std::vector<value>> value::at_path_with_wildcard(std::string_view json_path) noexcept {
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t) {
case json_type::array:
return (*this).get_array().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
return (*this).get_array().at_path_with_wildcard(json_path);
case json_type::object:
return (*this).get_object().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
return (*this).get_object().at_path_with_wildcard(json_path);
default:
return INVALID_JSON_POINTER;
}
@@ -425,14 +401,6 @@ simdjson_inline simdjson_result<int64_t> simdjson_result<SIMDJSON_IMPLEMENTATION
if (error()) { return error(); }
return first.get_int64_in_string();
}
simdjson_inline simdjson_result<uint32_t> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::get_uint32() noexcept {
if (error()) { return error(); }
return first.get_uint32();
}
simdjson_inline simdjson_result<int32_t> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::get_int32() noexcept {
if (error()) { return error(); }
return first.get_int32();
}
simdjson_inline simdjson_result<double> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::get_double() noexcept {
if (error()) { return error(); }
return first.get_double();
@@ -591,18 +559,12 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjs
return first.at_path(json_path);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, SIMDJSON_IMPLEMENTATION::ondemand::value>
#else
template <typename Func>
#endif
inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::for_each_at_path_with_wildcard(
std::string_view json_path, Func&& callback) noexcept {
inline simdjson_result<std::vector<SIMDJSON_IMPLEMENTATION::ondemand::value>> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::at_path_with_wildcard(
std::string_view json_path) noexcept {
if (error()) {
return error();
}
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
return first.at_path_with_wildcard(json_path);
}
} // namespace simdjson
+9 -52
View File
@@ -161,28 +161,6 @@ public:
*/
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
/**
* Cast this JSON value to a 32-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint32_t.
*
* @returns A 32-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint32_t.
*/
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
/**
* Cast this JSON value to a 32-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int32_t.
*
* @returns A 32-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int32_t.
*/
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
/**
* Cast this JSON value to a double.
*
@@ -220,14 +198,17 @@ public:
* simdjson::ondemand::document doc = parser.iterate(json);
* auto view = doc["deviceId"].get_string(true);
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns An UTF-8 string. The string is stored in the parser when escaping was needed
* and will be invalidated the next
* time it parses a document or when it is destroyed. If no escaping was needed,
* the string_view points directly into the original JSON buffer.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
/**
* Attempts to fill the provided std::string reference with the parsed value of the current string.
* The data is stored into the provided std::string instance.
*
* The string is guaranteed to be valid UTF-8.
*
@@ -698,21 +679,13 @@ public:
simdjson_inline simdjson_result<value> at_path(std::string_view at_path) noexcept;
/**
* Call the provided callback for each value matching the given JSONPath
* expression with wildcard support.
* Get all values matching the given JSONPath expression with wildcard support.
* Supports wildcard character (*) for arrays or ".*" for objects.
*
* @param json_path JSONPath expression with wildcards
* @param callback Function called for each matching value
* @return error_code indicating success or failure
* @return Vector of values matching the wildcard pattern
*/
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
simdjson_inline simdjson_result<std::vector<value>> at_path_with_wildcard(std::string_view json_path) noexcept;
protected:
/**
@@ -776,8 +749,6 @@ public:
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
@@ -905,23 +876,9 @@ public:
simdjson_inline simdjson_result<int32_t> current_depth() const noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, SIMDJSON_IMPLEMENTATION::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
simdjson_inline simdjson_result<std::vector<SIMDJSON_IMPLEMENTATION::ondemand::value>> at_path_with_wildcard(std::string_view json_path) noexcept;
};
// Forward-declare explicit specializations so MSVC /permissive- sees them before
// any template instantiation that would resolve element.get(val) to the primary.
template<> simdjson_inline error_code
simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::get<SIMDJSON_IMPLEMENTATION::ondemand::value>(
SIMDJSON_IMPLEMENTATION::ondemand::value &out) noexcept;
template<> simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>
simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::get<SIMDJSON_IMPLEMENTATION::ondemand::value>() noexcept;
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_H
@@ -510,6 +510,38 @@ simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::parse
}
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_string(bool allow_replacement) noexcept {
// Optimization strategy:
// We expect that most strings do not have escape characters, and most are short.
// So we can quickly check for backslashes and if there are none, we can just return a string_view
// into the original JSON buffer. There is no need to copy or unescape.
// Fast path: check for backslash in the string
// It may seem that this function is odd in that it scans the string even if a
// backslash is found early. However, we expect that in most strings there will be no backslash,
// so we optimize for that case. The compiler knows to expect a full scan and it can optimize for it.
auto has_backslash_fast = [](std::string_view s) noexcept {
for(const char c : s) {
if(c == '\\') {
return true;
}
}
return false;
};
std::string_view string_with_quotes(reinterpret_cast<const char*>(peek_start()), peek_start_length());
if(string_with_quotes.front() != '"') {
return incorrect_type_error("Not a string");
}
if(!has_backslash_fast(string_with_quotes)) {
// Find the ending quote
size_t len = string_with_quotes.size();
while(string_with_quotes[len - 1] != '"') {
len--;
}
// At this point len is 2 or more
return std::string_view(string_with_quotes.data() + 1, len - 2);
}
// Slow path: we have a backslash, so we need to unescape
return get_raw_json_string().unescape(json_iter(), allow_replacement);
}
template <typename string_type>
@@ -517,8 +549,7 @@ simdjson_warn_unused simdjson_inline error_code value_iterator::get_string(strin
std::string_view content;
// Save the string buffer location so that we can restore it after get_string
auto saved_string_buf_loc = _json_iter->string_buf_loc();
auto err = get_string(allow_replacement).get(content);
if (err) { return err; }
SIMDJSON_TRY(get_string(allow_replacement).get(content));
receiver = content;
// Restore the string buffer location, effectively discarding any temporary string storage
_json_iter->string_buf_loc() = saved_string_buf_loc;
@@ -656,6 +687,8 @@ simdjson_inline simdjson_result<number> value_iterator::get_root_number(bool che
return num;
}
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_root_string(bool check_trailing, bool allow_replacement) noexcept {
// We could optimize for the no-escape case here as well, but root strings
// are less common so we do the simple thing for now.
return get_root_raw_json_string(check_trailing).unescape(json_iter(), allow_replacement);
}
template <typename string_type>
@@ -663,8 +696,7 @@ simdjson_warn_unused simdjson_inline error_code value_iterator::get_root_string(
std::string_view content;
// Save the string buffer location so that we can restore it after get_string
auto saved_string_buf_loc = _json_iter->string_buf_loc();
auto err = get_root_string(check_trailing, allow_replacement).get(content);
if (err) { return err; }
SIMDJSON_TRY(get_root_string(check_trailing, allow_replacement).get(content));
receiver = content;
// Restore the string buffer location, effectively discarding any temporary string storage
_json_iter->string_buf_loc() = saved_string_buf_loc;
@@ -212,12 +212,6 @@ protected:
*/
size_t _max_depth{0};
public:
/** Whether to store big integers as strings instead of returning BIGINT_ERROR */
bool _number_as_string{false};
protected:
// Declaring these so that subclasses can use them to implement their constructors.
simdjson_inline dom_parser_implementation() noexcept;
simdjson_inline dom_parser_implementation(dom_parser_implementation &&other) noexcept;
+1 -2
View File
@@ -19,8 +19,7 @@ enum class tape_type {
DOUBLE = 'd',
TRUE_VALUE = 't',
FALSE_VALUE = 'f',
NULL_VALUE = 'n',
BIGINT = 'Z' // Big integer stored as string in string buffer
NULL_VALUE = 'n'
}; // enum class tape_type
} // namespace internal
+4 -74
View File
@@ -10,14 +10,6 @@
#include <climits>
#include <cwchar>
#ifndef _WIN32
#include <fcntl.h>
#include <stdio.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#endif
namespace simdjson {
namespace internal {
@@ -168,13 +160,10 @@ inline padded_string::operator padded_string_view() const noexcept simdjson_life
}
inline simdjson_result<padded_string> padded_string::load(std::string_view filename) noexcept {
// std::string_view is not guaranteed to be null-terminated, but std::fopen requires
// a null-terminated C string. Construct a temporary std::string to ensure null-termination.
const std::string null_terminated_filename(filename);
// Open the file
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_DEPRECATED_WARNING // Disable CRT_SECURE warning on MSVC: manually verified this is safe
std::FILE *fp = std::fopen(null_terminated_filename.c_str(), "rb");
std::FILE *fp = std::fopen(filename.data(), "rb");
SIMDJSON_POP_DISABLE_WARNINGS
if (fp == nullptr) {
@@ -226,13 +215,10 @@ inline simdjson_result<padded_string> padded_string::load(std::string_view filen
#if defined(_WIN32) && SIMDJSON_CPLUSPLUS17
inline simdjson_result<padded_string> padded_string::load(std::wstring_view filename) noexcept {
// std::wstring_view is not guaranteed to be null-terminated, but _wfopen requires
// a null-terminated wide C string. Construct a temporary std::wstring to ensure null-termination.
const std::wstring null_terminated_filename(filename);
// Open the file using the wide characters
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_DEPRECATED_WARNING // Disable CRT_SECURE warning on MSVC: manually verified this is safe
std::FILE *fp = _wfopen(null_terminated_filename.c_str(), L"rb");
std::FILE *fp = _wfopen(filename.data(), L"rb");
SIMDJSON_POP_DISABLE_WARNINGS
if (fp == nullptr) {
@@ -355,9 +341,6 @@ inline padded_string padded_string_builder::convert() noexcept {
}
inline bool padded_string_builder::reserve(size_t additional) noexcept {
if (simdjson_unlikely(additional + size < size)) {
return false; // overflow: cannot satisfy request
}
size_t needed = size + additional;
if (needed <= capacity) {
return true;
@@ -367,9 +350,8 @@ inline bool padded_string_builder::reserve(size_t additional) noexcept {
// repeated allocations.
if (new_capacity < 4096) {
new_capacity *= 2;
// overflow guard: ensure new_capacity + new_capacity/2 does not overflow
} else if (new_capacity + new_capacity / 2 > new_capacity) {
new_capacity += new_capacity / 2; // grow by 1.5x
} else {
new_capacity += new_capacity/2; // grow by 1.5x
}
char *new_data = internal::allocate_padded_buffer(new_capacity);
if (new_data == nullptr) {
@@ -384,57 +366,6 @@ inline bool padded_string_builder::reserve(size_t additional) noexcept {
return true;
}
#ifndef _WIN32
simdjson_inline padded_memory_map::padded_memory_map(const char *filename) noexcept {
int fd = open(filename, O_RDONLY);
if (fd == -1) {
return; // file not found or cannot be opened, data will be nullptr
}
struct stat st;
if (fstat(fd, &st) == -1) {
close(fd);
return; // failed to get file size, data will be nullptr
}
size = static_cast<size_t>(st.st_size);
size_t total_size = size + simdjson::SIMDJSON_PADDING;
void *anon_map =
mmap(NULL, total_size, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (anon_map == MAP_FAILED) {
close(fd);
return; // failed to create anonymous mapping, data will be nullptr
}
void *file_map =
mmap(anon_map, size, PROT_READ, MAP_SHARED | MAP_FIXED, fd, 0);
if (file_map == MAP_FAILED) {
munmap(anon_map, total_size);
close(fd);
return; // failed to mmap file, data will be nullptr
}
data = static_cast<const char *>(file_map);
close(fd); // no longer needed after mapping
}
simdjson_inline padded_memory_map::~padded_memory_map() noexcept {
if (data != nullptr) {
munmap(const_cast<char *>(data), size + simdjson::SIMDJSON_PADDING);
}
}
simdjson_inline simdjson::padded_string_view padded_memory_map::view() const noexcept simdjson_lifetime_bound {
if(!is_valid()) {
return simdjson::padded_string_view(); // return an empty view if mapping failed
}
return simdjson::padded_string_view(data, size, size + simdjson::SIMDJSON_PADDING);
}
simdjson_inline bool padded_memory_map::is_valid() const noexcept {
return data != nullptr;
}
#endif // _WIN32
} // namespace simdjson
inline simdjson::padded_string operator ""_padded(const char *str, size_t len) {
@@ -445,5 +376,4 @@ inline simdjson::padded_string operator ""_padded(const char8_t *str, size_t len
return simdjson::padded_string(reinterpret_cast<const char *>(str), len);
}
#endif
#endif // SIMDJSON_PADDED_STRING_INL_H
-56
View File
@@ -277,62 +277,6 @@ inline std::ostream& operator<<(std::ostream& out, const padded_string& s) { ret
inline std::ostream& operator<<(std::ostream& out, simdjson_result<padded_string> &s) noexcept(false) { return out << s.value(); }
#endif
#ifndef _WIN32
/**
* A class representing a memory-mapped file with padding.
* It is only available on non-Windows platforms, as Windows has different APIs for memory mapping.
*/
class padded_memory_map {
public:
/**
* Create a new padded memory map for the given file.
* After creating the memory map, you can call view() to get a padded_string_view of the file content.
* The memory map will be automatically released when the padded_memory_map instance is destroyed.
* Note that the file content is not copied, so this is efficient for large files. However,
* the file must remain unchanged while the memory map is in use. In case of error (e.g., file not found,
* permission denied, etc.), the memory map will be invalid and view() will return an empty view.
* You can check if the memory map is valid by calling is_valid() before using view().
*
* @param filename the path to the file to memory-map.
*/
simdjson_inline padded_memory_map(const char *filename) noexcept;
/**
* Destroy the padded memory map and release any resources.
*/
simdjson_inline ~padded_memory_map() noexcept;
// lifetime of the view is tied to the memory map, so we can return a view
// directly
/**
* Get a view of the memory-mapped file. It always succeeds, but the view may be empty
* if the memory map is invalid (e.g., due to file not found, permission denied, etc.).
* You can check if the memory map is valid by calling is_valid() before using the view.
*
* Lifetime of the view is tied to the memory map, so the view should not be used after the
* padded_memory_map instance is destroyed.
*
* @return a padded_string_view representing the memory-mapped file, or an empty view if the memory map is invalid.
*/
simdjson_inline simdjson::padded_string_view view() const noexcept simdjson_lifetime_bound;
/**
* Check if the memory map is valid.
*
* @return true if the memory map is valid, false otherwise.
*/
simdjson_inline bool is_valid() const noexcept;
private:
padded_memory_map() = delete;
padded_memory_map(const padded_memory_map &) = delete;
padded_memory_map &operator=(const padded_memory_map &) = delete;
const char *data{nullptr};
size_t size{0};
};
#endif // _WIN32
} // namespace simdjson
// This is deliberately outside of simdjson so that people get it without having to use the namespace
+1 -1
View File
@@ -17,7 +17,7 @@ namespace simdjson {
*/
class padded_string_view : public std::string_view {
private:
size_t _capacity{0};
size_t _capacity;
public:
/** Create an empty padded_string_view. */
-19
View File
@@ -66,25 +66,6 @@ using std::size_t;
#define SIMDJSON_IS_LASX 1 // We can always run both
#elif defined(__loongarch_sx)
#define SIMDJSON_IS_LSX 1
// Adjust for runtime dispatching support.
#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) && !defined(__NVCOMPILER)
#if __GNUC__ > 15 || (__GNUC__ == 15 && __GNUC_MINOR__ >= 0)
// We are ok, we will support runtime dispatch for LASX.
#else
// We disable runtime dispatch for LASX, which means that we will not be able to use LASX
// even if it is supported by the hardware.
// Loongson users should update to GCC 15 or better.
#define SIMDJSON_IMPLEMENTATION_LASX 0
#endif
#else
// We are not using GCC, so we assume that we can support runtime dispatch for LASX.
// https://godbolt.org/z/jcMnrjYhs
#define SIMDJSON_IMPLEMENTATION_LASX 0
#endif
#endif
#elif defined(__PPC64__) || defined(_M_PPC64)
#define SIMDJSON_IS_PPC64 1
+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 "4.6.3"
#define SIMDJSON_VERSION "4.2.4"
namespace simdjson {
enum {
@@ -15,11 +15,11 @@ enum {
/**
* The minor version (major.MINOR.revision) of simdjson being used.
*/
SIMDJSON_VERSION_MINOR = 6,
SIMDJSON_VERSION_MINOR = 2,
/**
* The revision (major.minor.REVISION) of simdjson being used.
*/
SIMDJSON_VERSION_REVISION = 3
SIMDJSON_VERSION_REVISION = 4
};
} // namespace simdjson
+4 -11
View File
@@ -54,15 +54,15 @@ Importantly, we build the experimental LLVM compiler based on the current state
```bash
CXX=clang++ cmake -B buildreflect -D SIMDJSON_STATIC_REFLECTION=ON -DSIMDJSON_DEVELOPER_MODE=ON
```
This only needs to be done once. To build the Rust code, add `-D SIMDJSON_USE_RUST=ON`. Note that you should have Rust on your system as a prerequisite for this option to be meaningful.
This only needs to be done once.
5. Build the code...
```bash
cmake --build buildreflect --target benchmark_serialization_citm_catalog benchmark_serialization_twitter benchmark_parsing_twitter benchmark_parsing_citm
cmake --build buildreflect --target benchmark_serialization_citm_catalog benchmark_serialization_twitter
```
This is sufficient if you only mean to run the benchmarks (and skip the tests).
6. Run the tests... (optional)
6. Run the tests...
```bash
cmake --build buildreflect
ctest --test-dir buildreflect --output-on-failure
@@ -71,16 +71,9 @@ 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/citm_catalog_benchmark/benchmark_parsing_citm
./buildreflect/benchmark/static_reflect/twitter_benchmark/benchmark_serialization_twitter
./buildreflect/benchmark/static_reflect/twitter_benchmark/benchmark_parsing_twitter
```
These benchmarks should print performance counters *if* run in privileged mode (e.g., run under sudo).
If you run the code inside a docker container, under Linux, it should have access to the performance
counters if they are enabled on the host. Note that they are usually disabled in a cloud setting
unless you have so-called metal access.
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).
+13 -26
View File
@@ -13,18 +13,6 @@ import datetime
import json
from typing import Dict, List, Optional, Set, TextIO, Union, cast
# Pre-compile regex patterns for performance
pragma_once_re = re.compile(r'^#pragma once$')
ifndef_conditional_re = re.compile(r'^#ifndef\s+SIMDJSON_CONDITIONAL_INCLUDE\s*$')
endif_conditional_re = re.compile(r'^#endif\s*//\s*SIMDJSON_CONDITIONAL_INCLUDE\s*$')
include_re = re.compile(r'^#include\s+["<]([^">]*)[">]')
define_implementation_re = re.compile(r'^#define\s+SIMDJSON_IMPLEMENTATION\s+(.+)$')
undef_implementation_re = re.compile(r'^#undef\s+SIMDJSON_IMPLEMENTATION\s*$')
simdjson_implementation_re = re.compile(r'\bSIMDJSON_IMPLEMENTATION\b')
define_conditional_re = re.compile(r'^#define\s+SIMDJSON_CONDITIONAL_INCLUDE\s*$')
undef_conditional_re = re.compile(r'^#undef\s+SIMDJSON_CONDITIONAL_INCLUDE\s*$')
version_re = re.compile(r'\d+\.\d+\.\d+')
# Check for Python 3, this does not actually work.
if sys.version_info < (3, 0):
sys.stdout.write("Sorry, requires Python 3.x or better\n")
@@ -121,7 +109,6 @@ RelativeRoot = str # Literal['src','include'] # Literal not supported in Python
RELATIVE_ROOTS: List[RelativeRoot] = ['src', 'include' ]
Implementation = str # Literal['arm64', 'fallback', 'haswell', 'icelake', 'ppc64', 'westmere', 'lsx', 'lasx'] # Literal not supported in Python 3.7 (CI)
IMPLEMENTATIONS: List[Implementation] = [ 'arm64', 'haswell', 'icelake', 'lasx', 'lsx', 'ppc64', 'rvv-vls', 'westmere', 'fallback' ]
implementation_re = re.compile(f'(^|/)({"|".join(IMPLEMENTATIONS)})')
GENERIC_INCLUDE = "simdjson/generic"
GENERIC_SRC = "generic"
BUILTIN = "simdjson/builtin"
@@ -212,7 +199,7 @@ class SimdjsonFile:
@property
def implementation(self) -> Optional[Implementation]:
match = implementation_re.search(self.include_path)
match = re.search(f'(^|/)({"|".join(IMPLEMENTATIONS)})', self.include_path)
if match:
return cast(Implementation, str(match.group(2)))
@@ -430,23 +417,23 @@ class Amalgamator:
line = line.rstrip('\n')
# Ignore #pragma once, it causes warnings if it ends up in a .cpp file
if pragma_once_re.search(line):
if re.search(r'^#pragma once$', line):
continue
# Ignore lines inside #ifndef SIMDJSON_CONDITIONAL_INCLUDE
if ifndef_conditional_re.search(line):
if re.search(r'^#ifndef\s+SIMDJSON_CONDITIONAL_INCLUDE\s*$', line):
assert file.is_conditional_include, f"Error: File '{file}' uses '#ifndef SIMDJSON_CONDITIONAL_INCLUDE', but it's not an amalgamated file. Conditional includes are only for amalgamated files. {rules}"
assert self.in_conditional_include_block, f"Error: File '{file}' uses '#ifndef SIMDJSON_CONDITIONAL_INCLUDE' without a prior '#define SIMDJSON_CONDITIONAL_INCLUDE'. Ensure the define comes first. Stack: {self.include_stack}. {rules}"
assert not self.editor_only_region, f"Error: File '{file}' uses '#ifndef SIMDJSON_CONDITIONAL_INCLUDE' twice in a row. Ensure conditional blocks are properly nested and closed. {rules}"
self.editor_only_region = True
# Handle ignored lines (and ending ignore blocks)
end_ignore = endif_conditional_re.search(line)
end_ignore = re.search(r'^#endif\s*//\s*SIMDJSON_CONDITIONAL_INCLUDE\s*$', line)
if self.editor_only_region:
self.write(f"/* amalgamation skipped (editor-only): {line} */")
# Add the editor-only include so we can check dependencies.h for completeness later
included = include_re.search(line)
included = re.search(r'^#include\s+["<]([^">]*)[">]', line)
if included:
included_file = self.repository[included.group(1)]
if included_file:
@@ -458,7 +445,7 @@ class Amalgamator:
assert not end_ignore, f"Error: File '{file}' has '#endif // SIMDJSON_CONDITIONAL_INCLUDE' without a matching '#ifndef'. Ensure proper conditional block structure. {rules}"
# Handle #include lines
included = include_re.search(line)
included = re.search(r'^#include\s+["<]([^">]*)[">]', line)
if included:
# we explicitly include simdjson headers, one time each (unless they are generic, in which case multiple times is fine)
included_file = self.repository[included.group(1)]
@@ -468,7 +455,7 @@ class Amalgamator:
continue
# Handle defining and replacing SIMDJSON_IMPLEMENTATION
defined = define_implementation_re.search(line)
defined = re.search(r'^#define\s+SIMDJSON_IMPLEMENTATION\s+(.+)$', line)
if defined:
old_implementation = self.implementation
self.implementation = defined.group(1)
@@ -476,24 +463,24 @@ class Amalgamator:
self.write(f'/* defining SIMDJSON_IMPLEMENTATION to "{self.implementation}" */')
else:
self.write(f'/* redefining SIMDJSON_IMPLEMENTATION from "{old_implementation}" to "{self.implementation}" */')
elif undef_implementation_re.search(line):
elif re.search(r'^#undef\s+SIMDJSON_IMPLEMENTATION\s*$', line):
# Don't include #undef SIMDJSON_IMPLEMENTATION since we're handling it ourselves
self.write(f'/* undefining SIMDJSON_IMPLEMENTATION from "{self.implementation}" */')
self.implementation = None
elif self.implementation and file.include_path != IMPLEMENTATION_DETECTION_H:
elif re.search(r'\bSIMDJSON_IMPLEMENTATION\b', line) and file.include_path != IMPLEMENTATION_DETECTION_H:
# copy the line, with SIMDJSON_IMPLEMENTATION replace to what it is currently defined to
assert self.implementation, f"Error: In '{file}', line '{line}' uses SIMDJSON_IMPLEMENTATION, but it's not defined. Ensure SIMDJSON_IMPLEMENTATION is set before use. {rules}"
line = simdjson_implementation_re.sub(self.implementation, line)
line = re.sub(r'\bSIMDJSON_IMPLEMENTATION\b',self.implementation,line)
# Handle defining and undefining SIMDJSON_CONDITIONAL_INCLUDE
defined = define_conditional_re.search(line)
defined = re.search(r'^#define\s+SIMDJSON_CONDITIONAL_INCLUDE\s*$', line)
if defined:
assert not file.is_conditional_include, f"Error: Amalgamated file '{file}' defines SIMDJSON_CONDITIONAL_INCLUDE, which is not allowed. Only non-amalgamated files can define it. {rules}"
assert not self.in_conditional_include_block, f"Error: File '{file}' redefines SIMDJSON_CONDITIONAL_INCLUDE while already in a conditional block. Avoid redefinition. {rules}"
self.in_conditional_include_block = True
self.found_includes_per_conditional_block.clear()
self.write(f'/* defining SIMDJSON_CONDITIONAL_INCLUDE */')
elif undef_conditional_re.search(line):
elif re.search(r'^#undef\s+SIMDJSON_CONDITIONAL_INCLUDE\s*$', line):
assert not file.is_conditional_include, f"Error: Amalgamated file '{file}' undefines SIMDJSON_CONDITIONAL_INCLUDE, which is not allowed. Only non-amalgamated files can undefine it. {rules}"
assert self.in_conditional_include_block, f"Error: File '{file}' undefines SIMDJSON_CONDITIONAL_INCLUDE without having defined it first. Ensure proper define/undefine pairing. {rules}"
self.write(f'/* undefining SIMDJSON_CONDITIONAL_INCLUDE */')
@@ -567,7 +554,7 @@ def validate_implementations():
def read_version():
with open(os.path.join(PROJECTPATH, 'include/simdjson/simdjson_version.h')) as f:
return version_re.search(f.read()).group(0)
return re.search(r'\d+\.\d+\.\d+', f.read()).group(0)
version = read_version()
if not validate_implementations():
+94 -7064
View File
File diff suppressed because it is too large Load Diff
+982 -27301
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.
+1 -17
View File
@@ -172,23 +172,7 @@ simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_string(
simdjson_warn_unused simdjson_inline error_code tape_builder::visit_number(json_iterator &iter, const uint8_t *value) noexcept {
iter.log_value("number");
error_code err = numberparsing::parse_number(value, tape);
if (simdjson_unlikely(err == BIGINT_ERROR &&
iter.dom_parser._number_as_string)) {
// Write big integer to string buffer using the same format as strings.
// Scan digits the same way parse_number does (skip optional '-', then digits).
const uint8_t *p = value;
if (*p == '-') p++;
while (numberparsing::is_digit(*p)) p++;
size_t len = size_t(p - value);
tape.append(current_string_buf_loc - iter.dom_parser.doc->string_buf.get(), internal::tape_type::BIGINT);
uint8_t *dst = current_string_buf_loc + sizeof(uint32_t);
memcpy(dst, value, len);
dst += len;
on_end_string(dst);
return SUCCESS;
}
return err;
return numberparsing::parse_number(value, tape);
}
simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_number(json_iterator &iter, const uint8_t *value) noexcept {
-15
View File
@@ -26,9 +26,6 @@ struct tape_writer {
/** Write a double value to tape. */
simdjson_inline void append_double(double value) noexcept;
/** Write a big integer (as string) to tape. src points to first digit, len is byte count. */
simdjson_inline void append_bigint(const uint8_t *src, size_t len, uint8_t *&string_buf) noexcept;
/**
* Append a tape entry (an 8-bit type,and 56 bits worth of value).
*/
@@ -112,18 +109,6 @@ simdjson_inline void tape_writer::write(uint64_t &tape_loc, uint64_t val, intern
tape_loc = val | ((uint64_t(char(t))) << 56);
}
simdjson_inline void tape_writer::append_bigint(const uint8_t *src, size_t len, uint8_t *&string_buf) noexcept {
// Write to string buffer: [4-byte LE length][digits][null]
uint32_t str_len = uint32_t(len);
memcpy(string_buf, &str_len, sizeof(uint32_t));
memcpy(string_buf + sizeof(uint32_t), src, len);
string_buf[sizeof(uint32_t) + len] = 0;
// Tape entry: offset into string buffer
// The caller must set the offset relative to doc.string_buf base
append(0, internal::tape_type::BIGINT); // placeholder offset, caller patches
string_buf += sizeof(uint32_t) + len + 1;
}
} // namespace stage2
} // unnamed namespace
} // namespace SIMDJSON_IMPLEMENTATION
+1 -1
View File
@@ -145,7 +145,7 @@ namespace internal {
+ SIMDJSON_IMPLEMENTATION_RVV_VLS + SIMDJSON_IMPLEMENTATION_FALLBACK == 1)
#if SIMDJSON_SINGLE_IMPLEMENTATION
simdjson_really_inline static const implementation* get_single_implementation() {
static const implementation* get_single_implementation() {
return
#if SIMDJSON_IMPLEMENTATION_ICELAKE
get_icelake_singleton();
-1
View File
@@ -11,7 +11,6 @@ link_libraries(simdjson)
add_cpp_test(unicode_tests LABELS dom acceptance per_implementation)
add_cpp_test(minify_tests LABELS other acceptance per_implementation)
add_cpp_test(padded_string_tests LABELS other acceptance )
add_cpp_test(memory_map_tests LABELS other acceptance )
add_cpp_test(prettify_tests LABELS other acceptance per_implementation)
add_cpp_test(fractured_json_tests LABELS other acceptance per_implementation)
@@ -25,8 +25,3 @@ if (SIMDJSON_EXCEPTIONS)
add_dual_compile_test(dangling_parser_parse_padstring)
add_dual_compile_test(unsafe_parse_many)
endif()
if(NOT BUILD_SHARED_LIBS)
# We only check that it builds
add_subdirectory(multiple_include)
endif()
@@ -1,5 +0,0 @@
add_library(mylib mylib.cpp)
target_link_libraries(mylib PUBLIC simdjson::simdjson)
target_include_directories(mylib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
add_executable(myexe main.cpp)
target_link_libraries(myexe PRIVATE mylib)
@@ -1,22 +0,0 @@
#include "mylib.h"
#include <simdjson.h>
#include <iostream>
#include <memory>
int main() {
simdjson::padded_string json = R"([ 1, 2, 3, 4 ])"_padded;
simdjson::padded_string minified = minify_json(json);
std::cout << "Minified: " << std::string_view(minified) << std::endl;
// Also directly use minify
size_t length = json.size();
std::unique_ptr<char[]> buffer{new char[length]};
size_t new_length{};
auto error = simdjson::minify(json.data(), length, buffer.get(), new_length);
if (error) {
std::cerr << "Error: " << simdjson::error_message(error) << std::endl;
} else {
std::cout << "Direct minified: " << std::string(buffer.get(), new_length) << std::endl;
}
return 0;
}
@@ -1,14 +0,0 @@
#include "mylib.h"
#include <simdjson.h>
#include <memory>
simdjson::padded_string minify_json(const simdjson::padded_string& json) {
size_t length = json.size();
std::unique_ptr<char[]> buffer{new char[length]};
size_t new_length{};
auto error = simdjson::minify(json.data(), length, buffer.get(), new_length);
if (error) {
return simdjson::padded_string();
}
return simdjson::padded_string(std::string_view(buffer.get(), new_length));
}
@@ -1,9 +0,0 @@
#ifndef MYLIB_H
#define MYLIB_H
#include <simdjson.h>
#include <string>
simdjson::padded_string minify_json(const simdjson::padded_string& json);
#endif // MYLIB_H
-1
View File
@@ -11,7 +11,6 @@ add_cpp_test(document_tests LABELS dom acceptance per_implementation
add_cpp_test(errortests LABELS dom acceptance per_implementation)
add_cpp_test(extracting_values_example LABELS dom acceptance per_implementation)
add_cpp_test(integer_tests LABELS dom acceptance per_implementation)
add_cpp_test(big_integer_tests LABELS dom acceptance per_implementation)
add_cpp_test(jsoncheck LABELS dom acceptance per_implementation)
add_cpp_test(json_path_tests LABELS dom acceptance per_implementation)
add_cpp_test(minefieldcheck LABELS dom acceptance per_implementation)
-151
View File
@@ -1,151 +0,0 @@
#include "simdjson.h"
#include "test_macros.h"
#include <sstream>
using namespace simdjson;
namespace big_integer_tests {
bool option_off_by_default() {
TEST_START();
dom::parser parser;
ASSERT_EQUAL(parser.number_as_string(), false);
TEST_SUCCEED();
}
bool default_returns_bigint_error() {
TEST_START();
dom::parser parser;
auto result = parser.parse(R"({"val":123456789012345678901})"_padded);
ASSERT_EQUAL(result.error(), BIGINT_ERROR);
TEST_SUCCEED();
}
bool opt_in_parses_big_integer() {
TEST_START();
dom::parser parser;
parser.number_as_string(true);
dom::element doc;
ASSERT_SUCCESS(parser.parse(R"({"val":123456789012345678901})"_padded).get(doc));
dom::element val;
ASSERT_SUCCESS(doc["val"].get(val));
ASSERT_EQUAL(val.type(), dom::element_type::BIGINT);
ASSERT_TRUE(val.is_bigint());
std::string_view digits;
ASSERT_SUCCESS(val.get_bigint().get(digits));
ASSERT_EQUAL(digits, "123456789012345678901");
// Verify streaming works (same pattern as readme_examples)
std::ostringstream os;
os << val.get_bigint().value_unsafe();
ASSERT_EQUAL(os.str(), "123456789012345678901");
TEST_SUCCEED();
}
bool negative_big_integer() {
TEST_START();
dom::parser parser;
parser.number_as_string(true);
dom::element doc;
ASSERT_SUCCESS(parser.parse(R"({"val":-12345678901234567890})"_padded).get(doc));
dom::element val;
ASSERT_SUCCESS(doc["val"].get(val));
ASSERT_EQUAL(val.type(), dom::element_type::BIGINT);
std::string_view digits;
ASSERT_SUCCESS(val.get_bigint().get(digits));
ASSERT_EQUAL(digits, "-12345678901234567890");
TEST_SUCCEED();
}
bool big_integer_in_array() {
TEST_START();
dom::parser parser;
parser.number_as_string(true);
dom::element doc;
ASSERT_SUCCESS(parser.parse(R"([1, 123456789012345678901, 3])"_padded).get(doc));
dom::array arr;
ASSERT_SUCCESS(doc.get_array().get(arr));
size_t count = 0;
for (auto elem : arr) { (void)elem; count++; }
ASSERT_EQUAL(count, 3);
TEST_SUCCEED();
}
bool serialization_preserves_digits() {
TEST_START();
dom::parser parser;
parser.number_as_string(true);
dom::element doc;
ASSERT_SUCCESS(parser.parse(R"({"val":123456789012345678901})"_padded).get(doc));
std::string output = simdjson::to_string(doc);
// Raw digits should appear
ASSERT_TRUE(output.find("123456789012345678901") != std::string::npos);
// Must not be quoted — it's a number, not a string
ASSERT_EQUAL(output.find("\"123456789012345678901\""), std::string::npos);
TEST_SUCCEED();
}
bool normal_numbers_unaffected() {
TEST_START();
dom::parser parser;
parser.number_as_string(true);
dom::element doc;
ASSERT_SUCCESS(parser.parse(R"({"a":42,"b":-1,"c":3.14,"d":18446744073709551615})"_padded).get(doc));
int64_t a;
ASSERT_SUCCESS(doc["a"].get_int64().get(a));
ASSERT_EQUAL(a, 42);
uint64_t d;
ASSERT_SUCCESS(doc["d"].get_uint64().get(d));
ASSERT_EQUAL(d, 18446744073709551615ULL);
TEST_SUCCEED();
}
bool type_checks() {
TEST_START();
dom::parser parser;
parser.number_as_string(true);
dom::element doc;
ASSERT_SUCCESS(parser.parse(R"({"val":123456789012345678901})"_padded).get(doc));
dom::element val;
ASSERT_SUCCESS(doc["val"].get(val));
// is_bigint should be true
ASSERT_TRUE(val.is_bigint());
// other type checks should be false
ASSERT_FALSE(val.is_int64());
ASSERT_FALSE(val.is_uint64());
ASSERT_FALSE(val.is_double());
ASSERT_FALSE(val.is_string());
ASSERT_FALSE(val.is_bool());
ASSERT_FALSE(val.is_null());
// get_int64/uint64/double should return INCORRECT_TYPE
ASSERT_ERROR(val.get_int64(), INCORRECT_TYPE);
ASSERT_ERROR(val.get_uint64(), INCORRECT_TYPE);
ASSERT_ERROR(val.get_double(), INCORRECT_TYPE);
ASSERT_ERROR(val.get_string(), INCORRECT_TYPE);
TEST_SUCCEED();
}
bool element_type_output() {
TEST_START();
std::ostringstream os;
os << dom::element_type::BIGINT;
ASSERT_EQUAL(os.str(), "bigint");
TEST_SUCCEED();
}
bool run() {
return option_off_by_default() &&
default_returns_bigint_error() &&
opt_in_parses_big_integer() &&
negative_big_integer() &&
big_integer_in_array() &&
serialization_preserves_digits() &&
normal_numbers_unaffected() &&
type_checks() &&
element_type_output();
}
} // namespace big_integer_tests
int main() {
return big_integer_tests::run() ? 0 : 1;
}
-15
View File
@@ -114,20 +114,6 @@ namespace parser_load {
TEST_SUCCEED();
}
bool parser_load_string_view_subpath() {
TEST_START();
const std::string valid_path(TWITTER_JSON);
const std::string backing = valid_path + ".extra";
const std::string_view view(backing.data(), valid_path.size());
uint64_t count_from_string = 0;
uint64_t count_from_view = 0;
dom::parser parser;
ASSERT_SUCCESS(parser.load(valid_path)["search_metadata"]["count"].get(count_from_string));
ASSERT_SUCCESS(parser.load(view)["search_metadata"]["count"].get(count_from_view));
ASSERT_EQUAL(count_from_view, count_from_string);
TEST_SUCCEED();
}
bool parser_load_chain() {
TEST_START();
dom::parser parser;
@@ -150,7 +136,6 @@ namespace parser_load {
&& parser_load_nonexistent()
&& parser_load_many_nonexistent()
&& padded_string_load_nonexistent()
&& parser_load_string_view_subpath()
&& parser_load_chain()
&& parser_load_many_chain()
&& parser_parse_many_documents_error_in_the_middle()
-3
View File
@@ -273,9 +273,6 @@ namespace treewalk_1 {
case dom::element_type::NULL_VALUE:
cout << "null" << endl;
break;
case dom::element_type::BIGINT:
cout << element.get_bigint().value_unsafe() << endl;
break;
}
}
-47
View File
@@ -1,47 +0,0 @@
#ifdef _WIN32
#include <cstdlib>
// This test is not supported on Windows because it relies on POSIX APIs like
// mmap. Please run it on a POSIX-compliant system.
int main() { return EXIT_SUCCESS; }
#else
#include "simdjson.h"
#include "test_macros.h"
#if SIMDJSON_EXCEPTIONS
bool test_memory_map_exception() {
TEST_START();
simdjson::padded_memory_map map(TWITTER_JSON);
ASSERT_TRUE(map.is_valid());
simdjson::padded_string_view view = map.view();
simdjson::ondemand::document doc = simdjson::ondemand::parser::get_parser().iterate(view);
ASSERT_TRUE(doc["search_metadata"]["count"].get_uint64() == 100);
TEST_SUCCEED();
}
#endif
bool test_memory_map_noexception() {
TEST_START();
simdjson::padded_memory_map map(TWITTER_JSON);
if (!map.is_valid()) {
std::cerr << "Failed to memory-map the file " << TWITTER_JSON << std::endl;
return false;
}
simdjson::padded_string_view view = map.view();
simdjson::ondemand::parser parser;
simdjson::ondemand::document doc;
ASSERT_SUCCESS( parser.iterate(view).get(doc) );
uint64_t count;
ASSERT_SUCCESS( doc["search_metadata"]["count"].get(count) );
ASSERT_EQUAL(count, 100);
TEST_SUCCEED();
}
int main() {
#if SIMDJSON_EXCEPTIONS
return (test_memory_map_exception() && test_memory_map_noexception()) ? EXIT_SUCCESS : EXIT_FAILURE;
#else
return test_memory_map_noexception() ? EXIT_SUCCESS : EXIT_FAILURE;
#endif
}
#endif
@@ -884,30 +884,6 @@ namespace document_stream_tests {
TEST_SUCCEED();
}
bool uint32_with_trailing() {
TEST_START();
auto json = R"( 133 badstuff )"_padded;
ondemand::parser parser;
ondemand::document_stream doc;
ASSERT_SUCCESS(parser.iterate_many(json).get(doc));
uint32_t x;
ASSERT_SUCCESS((*doc.begin()).get_uint32().get(x));
ASSERT_EQUAL(x,133);
TEST_SUCCEED();
}
bool int32_with_trailing() {
TEST_START();
auto json = R"( 133 badstuff )"_padded;
ondemand::parser parser;
ondemand::document_stream doc;
ASSERT_SUCCESS(parser.iterate_many(json).get(doc));
int32_t x;
ASSERT_SUCCESS((*doc.begin()).get_int32().get(x));
ASSERT_EQUAL(x,133);
TEST_SUCCEED();
}
bool double_with_trailing() {
TEST_START();
auto json = R"( 133 badstuff )"_padded;
@@ -955,8 +931,6 @@ namespace document_stream_tests {
string_with_trailing() &&
uint64_with_trailing() &&
int64_with_trailing() &&
uint32_with_trailing() &&
int32_with_trailing() &&
bool_with_trailing() &&
null_with_trailing() &&
truncated_utf8() &&
-44
View File
@@ -171,17 +171,6 @@ namespace error_tests {
TEST_SUCCEED();
}
bool parser_set_max_capacity() {
TEST_START();
ondemand::parser parser;
parser.set_max_capacity(1024 * 1024);
ASSERT_EQUAL(parser.max_capacity(), size_t(1024 * 1024));
auto json = R"({"k":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"})"_padded;
ondemand::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
TEST_SUCCEED();
}
bool simple_error_example() {
TEST_START();
ondemand::parser parser;
@@ -223,11 +212,7 @@ namespace error_tests {
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_uint32(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_int32(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_object(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_int32(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_uint32(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
@@ -250,11 +235,7 @@ namespace error_tests {
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_uint32(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_int32(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_object(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_int32(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_uint32(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
@@ -276,11 +257,7 @@ namespace error_tests {
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_uint32(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_int32(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_object(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_int32(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_uint32(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
@@ -302,11 +279,7 @@ namespace error_tests {
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_uint32(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_int32(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_object(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_int32(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_uint32(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
@@ -333,11 +306,7 @@ namespace error_tests {
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_uint32(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_int32(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_object(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_int32(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_uint32(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
@@ -360,11 +329,7 @@ namespace error_tests {
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_uint32(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_int32(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_object(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_int32(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_uint32(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
@@ -385,11 +350,7 @@ namespace error_tests {
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_uint32(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_int32(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_object(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_int32(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_uint32(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
@@ -410,11 +371,7 @@ namespace error_tests {
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_uint32(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_int32(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_object(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_int32(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_uint32(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_int64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_uint64(), INCORRECT_TYPE );
ASSERT_ERROR( val.get_double(), INCORRECT_TYPE );
@@ -475,7 +432,6 @@ namespace error_tests {
raw_json_string_error() &&
empty_document_error() &&
parser_max_capacity() &&
parser_set_max_capacity() &&
get_fail_then_succeed_bool() &&
get_fail_then_succeed_null() &&
simple_error_example() &&
+1 -104
View File
@@ -545,111 +545,8 @@ namespace number_tests {
TEST_SUCCEED();
}
bool get_int32_values() {
TEST_START();
ondemand::parser parser;
ondemand::document doc;
int32_t val;
padded_string docdata;
// Valid int32 values
docdata = "0"_padded;
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
ASSERT_SUCCESS(doc.get_int32().get(val));
ASSERT_EQUAL(val, 0);
docdata = "42"_padded;
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
ASSERT_SUCCESS(doc.get_int32().get(val));
ASSERT_EQUAL(val, 42);
docdata = "-1"_padded;
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
ASSERT_SUCCESS(doc.get_int32().get(val));
ASSERT_EQUAL(val, -1);
docdata = "2147483647"_padded; // INT32_MAX
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
ASSERT_SUCCESS(doc.get_int32().get(val));
ASSERT_EQUAL(val, 2147483647);
docdata = "-2147483648"_padded; // INT32_MIN
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
ASSERT_SUCCESS(doc.get_int32().get(val));
ASSERT_EQUAL(val, -2147483648);
// Out of range
docdata = "2147483648"_padded; // INT32_MAX + 1
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
ASSERT_ERROR(doc.get_int32(), NUMBER_OUT_OF_RANGE);
docdata = "-2147483649"_padded; // INT32_MIN - 1
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
ASSERT_ERROR(doc.get_int32(), NUMBER_OUT_OF_RANGE);
docdata = "9999999999"_padded;
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
ASSERT_ERROR(doc.get_int32(), NUMBER_OUT_OF_RANGE);
// Test via value path
auto json = R"({"x": 42})"_padded;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
ASSERT_SUCCESS(doc["x"].get_int32().get(val));
ASSERT_EQUAL(val, 42);
TEST_SUCCEED();
}
bool get_uint32_values() {
TEST_START();
ondemand::parser parser;
ondemand::document doc;
uint32_t val;
padded_string docdata;
// Valid uint32 values
docdata = "0"_padded;
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
ASSERT_SUCCESS(doc.get_uint32().get(val));
ASSERT_EQUAL(val, 0u);
docdata = "42"_padded;
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
ASSERT_SUCCESS(doc.get_uint32().get(val));
ASSERT_EQUAL(val, 42u);
docdata = "4294967295"_padded; // UINT32_MAX
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
ASSERT_SUCCESS(doc.get_uint32().get(val));
ASSERT_EQUAL(val, 4294967295u);
// Out of range
docdata = "4294967296"_padded; // UINT32_MAX + 1
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
ASSERT_ERROR(doc.get_uint32(), NUMBER_OUT_OF_RANGE);
docdata = "9999999999"_padded;
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
ASSERT_ERROR(doc.get_uint32(), NUMBER_OUT_OF_RANGE);
// Negative values should fail (get_uint64 will fail with INCORRECT_TYPE)
docdata = "-1"_padded;
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
ASSERT_ERROR(doc.get_uint32(), INCORRECT_TYPE);
// Test via value path
auto json = R"({"x": 42})"_padded;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
ASSERT_SUCCESS(doc["x"].get_uint32().get(val));
ASSERT_EQUAL(val, 42u);
TEST_SUCCEED();
}
bool run() {
return get_int32_values() &&
get_uint32_values() &&
minus_zero() &&
return minus_zero() &&
gigantic_big_int() &&
big_int_not_zero() &&
negative_big_int() &&
+1 -65
View File
@@ -177,68 +177,6 @@ simdjson_inline simdjson_result<Car> simdjson::ondemand::document::get() & noexc
#if SIMDJSON_EXCEPTIONS
// Test matching the doc example in doc/basics.md
// "Using for_each_at_path_with_wildcard for JSONPath Queries (On-Demand)"
bool wildcard_basics_example() {
TEST_START();
simdjson::padded_string json_string = R"(
{
"firstName": "John",
"lastName": "doe",
"age": 26,
"address": {
"streetAddress": "naist street",
"city": "Nara",
"postalCode": "630-0192"
},
"phoneNumbers": [
{
"type": "iPhone",
"numbers": ["0123-4567-8888", "0123-4567-8788"]
},
{
"type": "home",
"numbers": ["0123-4567-8910"]
}
]
})"_padded;
ondemand::parser parser;
ondemand::document doc = parser.iterate(json_string);
// Fetch all fields in the address object
std::vector<std::string_view> fields;
auto error = doc.for_each_at_path_with_wildcard("$.address.*",
[&](ondemand::value value) {
std::string_view field;
if (value.get(field) == SUCCESS) {
fields.push_back(field);
}
});
ASSERT_SUCCESS(error);
ASSERT_EQUAL(fields.size(), 3);
ASSERT_EQUAL(fields[0], "naist street");
ASSERT_EQUAL(fields[1], "Nara");
ASSERT_EQUAL(fields[2], "630-0192");
// Fetch all phone numbers
std::vector<std::string_view> numbers;
error = doc.for_each_at_path_with_wildcard("$.phoneNumbers[*].numbers[*]",
[&](ondemand::value value) {
std::string_view number;
if (value.get(number) == SUCCESS) {
numbers.push_back(number);
}
});
ASSERT_SUCCESS(error);
ASSERT_EQUAL(numbers.size(), 3);
ASSERT_EQUAL(numbers[0], "0123-4567-8888");
ASSERT_EQUAL(numbers[1], "0123-4567-8788");
ASSERT_EQUAL(numbers[2], "0123-4567-8910");
TEST_SUCCEED();
}
void main_capture() {
padded_string json_padded = "{\"a\":[1,2,3], \"b\": 2, \"c\": \"hello\"}"_padded;
std::vector<std::string_view> fields;
@@ -826,7 +764,7 @@ bool basics_1() {
TEST_START();
ondemand::parser parser;
auto json = padded_string::load("twitter.json"); // this is a simdjson_result<padded_string>
auto json = padded_string::load("twitter.json");
ondemand::document doc = parser.iterate(json); // load and parse a file
simdjson_unused auto unused_doc = doc.get_object();
@@ -2078,7 +2016,6 @@ bool value_raw_json_object() {
}
#endif
bool run() {
return true
&& fatal_error()
@@ -2138,7 +2075,6 @@ bool run() {
&& current_location_no_error()
&& to_string_example_no_except()
#if SIMDJSON_EXCEPTIONS
&& wildcard_basics_example()
&& issue2215()
&& to_string_example()
&& raw_string()
-21
View File
@@ -20,26 +20,6 @@ namespace twitter_tests {
TEST_SUCCEED();
}
#if SIMDJSON_EXCEPTIONS
bool twitter_result_example() {
TEST_START();
simdjson_result<padded_string> json = padded_string::load(TWITTER_JSON);
ondemand::parser parser;
auto doc = parser.iterate(json);
for (ondemand::object tweet : doc["statuses"]) {
uint64_t id = tweet["id"];
std::string_view text = tweet["text"];
std::string_view screen_name = tweet["user"]["screen_name"];
uint64_t retweets = tweet["retweet_count"];
uint64_t favorites = tweet["favorite_count"];
(void) id;
(void) text;
(void) retweets;
(void) favorites;
(void) screen_name;
}
TEST_SUCCEED();
}
bool twitter_example() {
TEST_START();
padded_string json;
@@ -202,7 +182,6 @@ namespace twitter_tests {
twitter_default_profile() &&
twitter_image_sizes() &&
#if SIMDJSON_EXCEPTIONS
twitter_result_example() &&
twitter_count_exception() &&
twitter_example() &&
twitter_default_profile_exception() &&
+86 -104
View File
@@ -21,16 +21,18 @@ namespace wildcard_tests {
ondemand::parser parser;
auto doc = parser.iterate(json);
std::vector<std::string_view> titles;
ASSERT_SUCCESS(doc.for_each_at_path_with_wildcard("$.books[*].title", [&](ondemand::value v) {
std::string_view sv;
if (v.get_string().get(sv) == SUCCESS) { titles.push_back(sv); }
}));
std::vector<ondemand::value> titles;
ASSERT_SUCCESS(doc.at_path_with_wildcard("$.books[*].title").get(titles));
ASSERT_EQUAL(titles.size(), 3);
ASSERT_EQUAL(titles[0], "Book A");
ASSERT_EQUAL(titles[1], "Book B");
ASSERT_EQUAL(titles[2], "Book C");
std::string_view title;
ASSERT_SUCCESS(titles[0].get_string().get(title));
ASSERT_EQUAL(title, "Book A");
ASSERT_SUCCESS(titles[1].get_string().get(title));
ASSERT_EQUAL(title, "Book B");
ASSERT_SUCCESS(titles[2].get_string().get(title));
ASSERT_EQUAL(title, "Book C");
TEST_SUCCEED();
}
@@ -44,16 +46,18 @@ namespace wildcard_tests {
ondemand::parser parser;
auto doc = parser.iterate(json);
std::vector<uint64_t> prices;
ASSERT_SUCCESS(doc.for_each_at_path_with_wildcard("$.prices[*]", [&](ondemand::value v) {
uint64_t p;
if (v.get_uint64().get(p) == SUCCESS) { prices.push_back(p); }
}));
std::vector<ondemand::value> prices;
ASSERT_SUCCESS(doc.at_path_with_wildcard("$.prices[*]").get(prices));
ASSERT_EQUAL(prices.size(), 5);
ASSERT_EQUAL(prices[0], 10);
ASSERT_EQUAL(prices[2], 30);
ASSERT_EQUAL(prices[4], 50);
uint64_t price;
ASSERT_SUCCESS(prices[0].get_uint64().get(price));
ASSERT_EQUAL(price, 10);
ASSERT_SUCCESS(prices[2].get_uint64().get(price));
ASSERT_EQUAL(price, 30);
ASSERT_SUCCESS(prices[4].get_uint64().get(price));
ASSERT_EQUAL(price, 50);
TEST_SUCCEED();
}
@@ -71,14 +75,19 @@ namespace wildcard_tests {
ondemand::parser parser;
auto doc = parser.iterate(json);
std::set<std::string_view> name_set;
auto error = doc.for_each_at_path_with_wildcard("$.users.*.name", [&](ondemand::value v) {
std::string_view sv;
if (v.get_string().get(sv) == SUCCESS) { name_set.insert(sv); }
});
std::vector<ondemand::value> names;
auto error = doc.at_path_with_wildcard("$.users.*.name").get(names);
ASSERT_SUCCESS(error);
ASSERT_EQUAL(name_set.size(), 3);
ASSERT_EQUAL(names.size(), 3);
// Verify we got all three names (order may vary)
std::set<std::string_view> name_set;
for (auto& name : names) {
std::string_view view;
ASSERT_SUCCESS(name.get_string().get(view));
name_set.insert(view);
}
ASSERT_TRUE(name_set.count("Alice") > 0);
ASSERT_TRUE(name_set.count("Bob") > 0);
ASSERT_TRUE(name_set.count("Charlie") > 0);
@@ -108,15 +117,19 @@ namespace wildcard_tests {
ondemand::parser parser;
auto doc = parser.iterate(json);
uint64_t total = 0;
size_t count = 0;
auto error = doc.for_each_at_path_with_wildcard("$.departments.*.employees[*].salary", [&](ondemand::value v) {
uint64_t s;
if (v.get_uint64().get(s) == SUCCESS) { total += s; count++; }
});
std::vector<ondemand::value> salaries;
auto error = doc.at_path_with_wildcard("$.departments.*.employees[*].salary").get(salaries);
ASSERT_SUCCESS(error);
ASSERT_EQUAL(count, 4);
ASSERT_EQUAL(salaries.size(), 4);
// Check sum of all salaries
uint64_t total = 0;
for (auto& salary : salaries) {
uint64_t value;
ASSERT_SUCCESS(salary.get_uint64().get(value));
total += value;
}
ASSERT_EQUAL(total, 360000);
TEST_SUCCEED();
@@ -129,12 +142,11 @@ namespace wildcard_tests {
ondemand::parser parser;
auto doc = parser.iterate(json);
size_t count = 0;
auto error = doc.for_each_at_path_with_wildcard("$.items[*].name", [&](ondemand::value) {
count++;
});
std::vector<ondemand::value> items;
auto error = doc.at_path_with_wildcard("$.items[*].name").get(items);
ASSERT_SUCCESS(error);
ASSERT_EQUAL(count, 0);
ASSERT_EQUAL(items.size(), 0);
TEST_SUCCEED();
}
@@ -146,12 +158,11 @@ namespace wildcard_tests {
ondemand::parser parser;
auto doc = parser.iterate(json);
size_t count = 0;
auto error = doc.for_each_at_path_with_wildcard("$.users.*.name", [&](ondemand::value) {
count++;
});
std::vector<ondemand::value> names;
auto error = doc.at_path_with_wildcard("$.users.*.name").get(names);
ASSERT_SUCCESS(error);
ASSERT_EQUAL(count, 0);
ASSERT_EQUAL(names.size(), 0);
TEST_SUCCEED();
}
@@ -164,7 +175,8 @@ namespace wildcard_tests {
auto doc = parser.iterate(json);
// Can't use array wildcard on scalar
auto error = doc.for_each_at_path_with_wildcard("$.data[*]", [](ondemand::value) {});
std::vector<ondemand::value> values;
auto error = doc.at_path_with_wildcard("$.data[*]").get(values);
ASSERT_ERROR(error, INVALID_JSON_POINTER);
TEST_SUCCEED();
@@ -183,12 +195,12 @@ namespace wildcard_tests {
ondemand::parser parser;
auto doc = parser.iterate(json);
size_t count = 0;
ASSERT_SUCCESS(doc.for_each_at_path_with_wildcard("$.matrix[*]", [&](ondemand::value) {
count++;
}));
// Get all nested arrays
std::vector<ondemand::value> arrays;
ASSERT_SUCCESS(doc.at_path_with_wildcard("$.matrix[*]").get(arrays));
ASSERT_EQUAL(count, 3);
// Verify we got 3 arrays
ASSERT_EQUAL(arrays.size(), 3);
TEST_SUCCEED();
}
@@ -206,16 +218,18 @@ namespace wildcard_tests {
ondemand::parser parser;
auto doc = parser.iterate(json);
std::vector<std::string_view> statuses;
ASSERT_SUCCESS(doc.for_each_at_path_with_wildcard("$.data[*].info.status", [&](ondemand::value v) {
std::string_view sv;
if (v.get_string().get(sv) == SUCCESS) { statuses.push_back(sv); }
}));
std::vector<ondemand::value> statuses;
ASSERT_SUCCESS(doc.at_path_with_wildcard("$.data[*].info.status").get(statuses));
ASSERT_EQUAL(statuses.size(), 3);
ASSERT_EQUAL(statuses[0], "active");
ASSERT_EQUAL(statuses[1], "inactive");
ASSERT_EQUAL(statuses[2], "active");
std::string_view status;
ASSERT_SUCCESS(statuses[0].get_string().get(status));
ASSERT_EQUAL(status, "active");
ASSERT_SUCCESS(statuses[1].get_string().get(status));
ASSERT_EQUAL(status, "inactive");
ASSERT_SUCCESS(statuses[2].get_string().get(status));
ASSERT_EQUAL(status, "active");
TEST_SUCCEED();
}
@@ -236,12 +250,13 @@ namespace wildcard_tests {
ondemand::parser parser;
auto doc = parser.iterate(json);
size_t count = 0;
ASSERT_SUCCESS(doc.for_each_at_path_with_wildcard("$.mixed[*]", [&](ondemand::value) {
count++;
}));
std::vector<ondemand::value> values;
ASSERT_SUCCESS(doc.at_path_with_wildcard("$.mixed[*]").get(values));
ASSERT_EQUAL(count, 6);
// Just verify we got all 6 elements
ASSERT_EQUAL(values.size(), 6);
// Don't try to access the values - they've been consumed by the OnDemand API
TEST_SUCCEED();
}
@@ -260,11 +275,8 @@ namespace wildcard_tests {
auto doc = parser.iterate(json);
// Third element doesn't have "name" field
std::vector<std::string_view> names;
auto error = doc.for_each_at_path_with_wildcard("$.data[*].name", [&](ondemand::value v) {
std::string_view sv;
if (v.get_string().get(sv) == SUCCESS) { names.push_back(sv); }
});
std::vector<ondemand::value> names;
auto error = doc.at_path_with_wildcard("$.data[*].name").get(names);
// This might error or return partial results
if (error) {
@@ -289,47 +301,18 @@ namespace wildcard_tests {
ondemand::parser parser;
auto doc = parser.iterate(json);
std::vector<std::string_view> names;
ASSERT_SUCCESS(doc.for_each_at_path_with_wildcard("$[*].name", [&](ondemand::value v) {
std::string_view sv;
if (v.get_string().get(sv) == SUCCESS) { names.push_back(sv); }
}));
std::vector<ondemand::value> names;
ASSERT_SUCCESS(doc.at_path_with_wildcard("$[*].name").get(names));
ASSERT_EQUAL(names.size(), 3);
ASSERT_EQUAL(names[0], "Item 1");
ASSERT_EQUAL(names[1], "Item 2");
ASSERT_EQUAL(names[2], "Item 3");
TEST_SUCCEED();
}
// https://github.com/simdjson/simdjson/issues/2684
bool wildcard_raw_json_issue_2684() {
TEST_START();
auto json = R"([{"tag_meta":{"meta_code":2000211,"meta_value":""},"tag_value":""}])"_padded;
ondemand::parser parser;
auto doc = parser.iterate(json);
size_t count = 0;
bool raw_json_ok = true;
ASSERT_SUCCESS(doc.for_each_at_path_with_wildcard("$[*].tag_meta", [&](ondemand::value v) {
count++;
std::string_view raw;
if (v.raw_json().get(raw) != SUCCESS) {
raw_json_ok = false;
return;
}
// raw_json() must not leak sibling fields or outer array delimiters
std::string_view expected = R"({"meta_code":2000211,"meta_value":""})";
if (raw != expected || raw.find("tag_value") != std::string_view::npos) {
std::cerr << " raw_json() returned: " << raw << std::endl;
raw_json_ok = false;
}
}));
ASSERT_EQUAL(count, 1);
ASSERT_TRUE(raw_json_ok);
std::string_view name;
ASSERT_SUCCESS(names[0].get_string().get(name));
ASSERT_EQUAL(name, "Item 1");
ASSERT_SUCCESS(names[1].get_string().get(name));
ASSERT_EQUAL(name, "Item 2");
ASSERT_SUCCESS(names[2].get_string().get(name));
ASSERT_EQUAL(name, "Item 3");
TEST_SUCCEED();
}
@@ -346,11 +329,10 @@ namespace wildcard_tests {
wildcard_with_nested_objects() &&
mixed_types_in_array() &&
wildcard_nonexistent_field() &&
root_array_wildcard() &&
wildcard_raw_json_issue_2684();
root_array_wildcard();
}
}
int main(int argc, char *argv[]) {
return test_main(argc, argv, wildcard_tests::run);
}
}