Compare commits

..

42 Commits

Author SHA1 Message Date
Francisco Geiman Thiesen 9fb26db309 Fixing typo 2024-11-27 12:06:42 -08:00
Francisco Geiman Thiesen 058907cffe Attempt at fixing failing serialization tests. (#2292) 2024-11-27 09:00:20 -05:00
Daniel Lemire 6c0b2a4af2 Merge branch 'json_builder_init' of https://github.com/simdjson/simdjson into json_builder_init 2024-11-26 18:27:19 -05:00
Daniel Lemire a0fb1400b8 another missing store 2024-11-26 18:26:56 -05:00
Daniel Lemire c5174b2213 missing store 2024-11-26 18:20:22 -05:00
Daniel Lemire 94214d1a9a minor fix 2024-11-25 21:12:13 -05:00
Daniel Lemire db5323a972 tweaking the approach 2024-11-25 21:10:25 -05:00
Daniel Lemire a14b63e654 fix typo 2024-11-22 19:43:14 -05:00
Daniel Lemire e32b2a744f tweaking 2024-11-22 19:35:12 -05:00
Daniel Lemire 19ebe94962 adding tests. we still specialized write_string_escaped 2024-11-21 16:34:12 -05:00
Daniel Lemire c61063ebfc dropping vs arm (missing support) 2024-11-21 14:27:21 -05:00
Daniel Lemire 19214fdd8e minor edits 2024-11-20 20:24:27 -05:00
Daniel Lemire b297927fe7 update 2024-11-05 17:36:30 -05:00
Daniel Lemire a309515c2f more later 2024-10-31 19:56:45 -04:00
Daniel Lemire ec03d814e7 tweak 2024-10-31 16:31:09 -04:00
Daniel Lemire 15434adff2 moving the files back to ondemand for now. 2024-10-30 01:10:00 -04:00
Daniel Lemire 6b3e55fc92 Initial work on JSON builder 2024-10-28 20:27:26 -04:00
Daniel Lemire 3c0d032ded Candidate for C++20 deserialization features (#2276)
* tag_invoke based custom types (#2219)

* tag_invoke based custom types

Now you can use tag_invoke to add a custom type or a group of custom types.

* Fixing macro usage + Fixing noexcept

* Fixing the usage of #include

We don't need <concepts> at all seems like it

* Fixing tag_invoke impl for MSVC

* Making `tag_invoke` to support `ondemand::document` as well + docs (#2228)

* Making `tag_invoke` to support `ondemand::document` as well + docs

* Fix typos and doc update by @lemire

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

* Better docs by @lemire

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

* Preserving the old, disallowing in the new

I'm disabling `document::get() &&` if the user has provided a `tag_invoke`d version; otherwise, we retain the compatibility.

---------

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

* fix: correct small issues with deserialize (#2232)

* Extending the deserialization code with more defaults + docs (#2233)

* Make custom types easier with some predefined cases + docs

* missing include

* adding Ubuntu 24 CXX 20

* using concepts all the way

* minor tweak

* tiny tweak

* tweaks

* more tweaking

* saving

---------

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

* Making `tag_invoke` a "put" as opposed to a "get" (#2256)

* fix: add tests related to issue 2227 (#2229)

* fix: add tests related to issue 2227

* avoiding name clash

* pedantic fix

* deprecate rvalue get on document

* selectively deprecating

* Fix ndjson spec link (#2234)

* fix ndjson spec link

The link in the readme of parse_many links to a casino spam site

* fix link

* [no-ci] Update README.md

* Make simdjson compile again

* Enable SIMDJSON_SINGLEHEADER=OFF in VS Code

With singleheader on, clangd can't find the right
include files.

* Add missing include directives to static build targets of simdjson. (#2240)

* adding a warning

* adding warning regarding SIMDJSON_BUILD_STATIC_LIB

* release candidate

* pedantic viable size

* Making tag_invoke a feeder instead of a producer

* adding missing undef silencer (#2253)

* Ignore pragma once when amalgamating source files (#2248)

With gcc it causes an error in `simdjson.cpp`:
```
simdjson.cpp:548:9: warning: #pragma once in main file
  548 | #pragma once
      |         ^~~~
```

It had previously been commented out in:
https://github.com/simdjson/simdjson/commit/6ef555e6fb79363fae057a9a46b52cd208d9e305

However, this was lost in an upgrade:
https://github.com/simdjson/simdjson/commit/2a4ff7346813b120f2b5b40e95d69352b593cc9c

* Update CI (#2254)

* adding missing undef silencer

* Updating CI

* more fixes

* fix

* big endian fix

* Moving to the new tag_invoke signature

* Fix nlohmann ambiguity on C++23-enabled clang

* Revert "Merge branch 'master' of https://github.com/simdjson/simdjson into builder_development_branch_extra"

This reverts commit 3eeecbab34, reversing
changes made to 6858b208b4.

---------

Co-authored-by: Daniel Lemire <daniel@lemire.me>
Co-authored-by: Sasha Lopoukhine <superlopuh@gmail.com>
Co-authored-by: John Keiser <john@johnkeiser.com>
Co-authored-by: Tan Li Boon <undisputed-seraphim@users.noreply.github.com>
Co-authored-by: tobil4sk <tobil4sk@outlook.com>

* update CI on the builder_development_branch (no code change) (#2262)

* typo

* General madness simpler, no simpler!!! (#2267)

* Minimal tag_invokes for STL types

* simpler madness

* adding a comment

* missing file

* minor tweaks to style

* fixing incorrect max/min usage

* updating single

* simplify

* validating the idea

* putting back the concept

* moving the include

* guarding

* Cheap General Madness (#2268)

* Some General Concepts and their deserializations

* Resolving ambiguity

* Add missing #include

* C++20 custom deserializer: better documentation (#2269)

* mostly a documentation update.

* missing cpp

* [no-ci] fix comment

* various minor fixes

---------

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

---------

Co-authored-by: M. Bahoosh <12122474+the-moisrex@users.noreply.github.com>
Co-authored-by: Daniel Lemire <dlemire@lemire.me>
Co-authored-by: M. Bahoosh <moisrex@gmail.com>

* minor update

* More documentation regarding builder (#2270)

* minor update

* more improvment to our documentation (builder branch)

* putting back missing functions

* merge candidate

---------

Co-authored-by: M. Bahoosh <moisrex@gmail.com>
Co-authored-by: Daniel Lemire <dlemire@lemire.me>
Co-authored-by: Sasha Lopoukhine <superlopuh@gmail.com>
Co-authored-by: John Keiser <john@johnkeiser.com>
Co-authored-by: Tan Li Boon <undisputed-seraphim@users.noreply.github.com>
Co-authored-by: tobil4sk <tobil4sk@outlook.com>
Co-authored-by: M. Bahoosh <12122474+the-moisrex@users.noreply.github.com>
2024-10-18 20:37:38 -04:00
Daniel Lemire 36f5dbcb75 documentation for threading... (#2277)
* documentation for threading...

* remove const

* doc
2024-10-11 12:34:11 -04:00
Daniel Lemire 49901fb254 doc 2024-10-10 21:32:51 -04:00
Daniel Lemire c066b5421b Jsonpath (#2272)
* Add support for JSONPath with '$' prefix and integrate into dom::at_path (#2266)

- Updated `json_path_to_pointer_conversion` to support JSONPath starting with the '$' prefix, while maintaining compatibility with the existing implementation.
- Moved `json_path_to_pointer_conversion` to a separate header file for better modularity and to support JSONPath queries in `dom` mode.
- Implemented `at_path` functionality in `dom` mode to enable querying JSON using JSONPath.
- Added unit tests to validate the new JSONPath support in `dom` mode and ensure compatibility with both standard and existing JSONPath formats.

* two minor fixes

* more tests and documentation

* damn compiler warnings

* more documentation fixes

---------

Co-authored-by: Zhengguo Yang <yangzhgg@gmail.com>
2024-10-08 10:37:59 -04:00
Daniel Lemire ec7550a70e fixes issue 2271 (#2273) 2024-10-08 10:36:12 -04:00
Daniel Lemire 3ef3078e51 documentation for issue 2259 (clarifying) (#2260) 2024-09-21 09:10:35 -04:00
tobil4sk fd06782c97 Make document::is_null behavior consistent with documentation (#2258)
* Make null-like value test consistent with docs

According to the docs: `INCORRECT_TYPE If the JSON value begins with 'n'
and is not 'null'.`

* Match document::is_null behavior to documentation

* Test other token beginning with n with is_null
2024-09-20 20:58:41 -04:00
Daniel Lemire 9303efbd0c [no-ci] Update basics.md 2024-09-20 20:56:11 -04:00
Daniel Lemire 6c979f15cc deps: switch to CPM (#2257)
* deps: switch to CPM

* missing file

* setting the dependencies on URL download

* using fixed version of cxxopts
2024-09-20 13:29:47 -04:00
Daniel Lemire 09ccabbe6c enable BMI, but only when LLVM is the compiler (#2255) 2024-09-20 10:59:54 -04:00
Daniel Lemire e00cc8c6dc Update CI (#2254)
* adding missing undef silencer

* Updating CI

* more fixes

* fix

* big endian fix
2024-09-18 09:09:11 -04:00
tobil4sk c10b32d463 Ignore pragma once when amalgamating source files (#2248)
With gcc it causes an error in `simdjson.cpp`:
```
simdjson.cpp:548:9: warning: #pragma once in main file
  548 | #pragma once
      |         ^~~~
```

It had previously been commented out in:
https://github.com/simdjson/simdjson/commit/6ef555e6fb79363fae057a9a46b52cd208d9e305

However, this was lost in an upgrade:
https://github.com/simdjson/simdjson/commit/2a4ff7346813b120f2b5b40e95d69352b593cc9c
2024-09-17 15:41:12 -04:00
Daniel Lemire 025a44348a adding missing undef silencer (#2253) 2024-09-16 21:04:09 -04:00
Daniel Lemire 70a68da941 pedantic viable size 2024-09-06 01:19:47 -04:00
Daniel Lemire e341c8b438 release candidate 2024-08-26 09:37:33 -04:00
Daniel Lemire 0ac0a80e28 Merge branch 'master' of github.com:simdjson/simdjson 2024-08-26 09:37:03 -04:00
Daniel Lemire 6b9117c029 adding warning regarding SIMDJSON_BUILD_STATIC_LIB 2024-08-26 09:36:44 -04:00
Daniel Lemire dd92151971 adding a warning 2024-08-26 09:34:39 -04:00
Tan Li Boon 0679c247f4 Add missing include directives to static build targets of simdjson. (#2240) 2024-08-26 09:24:01 -04:00
John Keiser 615218a3ad Merge pull request #2237 from simdjson/jkeiser/simdjson-vscode
Reduce errors in vscode
2024-08-18 22:04:20 -07:00
John Keiser 4c1b0a41d8 Enable SIMDJSON_SINGLEHEADER=OFF in VS Code
With singleheader on, clangd can't find the right
include files.
2024-08-18 14:21:00 -07:00
John Keiser ef563a4b09 Make simdjson compile again 2024-08-18 11:21:26 -07:00
Daniel Lemire 7a9ff93388 [no-ci] Update README.md 2024-08-15 12:52:47 -04:00
Sasha Lopoukhine fc61d7c7ba Fix ndjson spec link (#2234)
* fix ndjson spec link

The link in the readme of parse_many links to a casino spam site

* fix link
2024-08-10 10:11:05 -04:00
Daniel Lemire d506af0a79 fix: add tests related to issue 2227 (#2229)
* fix: add tests related to issue 2227

* avoiding name clash

* pedantic fix

* deprecate rvalue get on document

* selectively deprecating
2024-08-07 20:15:06 -04:00
110 changed files with 7696 additions and 2013 deletions
+1
View File
@@ -25,6 +25,7 @@ CompileFlags:
Diagnostics:
Suppress:
- pp_including_mainfile_in_preamble
- unused-includes
---
# Amalgamated files that require or partly define an implementation
If:
-1
View File
@@ -10,7 +10,6 @@ jobs:
fail-fast: false
matrix:
include:
- {arch: ARM}
- {arch: ARM64}
- {arch: ARM64EC}
steps:
+18 -8
View File
@@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.14)
project(
simdjson
# The version number is modified by tools/release.py
VERSION 3.10.0
VERSION 3.10.1
DESCRIPTION "Parsing gigabytes of JSON per second"
HOMEPAGE_URL "https://simdjson.org/"
LANGUAGES CXX C
@@ -23,7 +23,11 @@ string(
set(SIMDJSON_LIB_VERSION "23.0.0" CACHE STRING "simdjson library version")
set(SIMDJSON_LIB_SOVERSION "23" CACHE STRING "simdjson library soversion")
option(SIMDJSON_BUILD_STATIC_LIB "Build simdjson_static library along with simdjson" OFF)
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)
message(WARNING "SIMDJSON_BUILD_STATIC_LIB only makes sense if BUILD_SHARED_LIBS is set to ON")
message(WARNING "You might be building and installing a two identical static libraries.")
endif()
option(SIMDJSON_ENABLE_THREADS "Link with thread support" ON)
@@ -51,6 +55,7 @@ endif()
if(is_top_project)
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)
endif()
include(cmake/handle-deprecations.cmake)
@@ -155,11 +160,13 @@ endif()
include(CMakePackageConfigHelpers)
include(GNUInstallDirs)
install(
FILES singleheader/simdjson.h
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
COMPONENT simdjson_Development
)
if(SIMDJSON_SINGLEHEADER)
install(
FILES singleheader/simdjson.h
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
COMPONENT simdjson_Development
)
endif()
install(
TARGETS simdjson
@@ -203,6 +210,7 @@ if(SIMDJSON_BUILD_STATIC_LIB)
TARGETS simdjson_static
EXPORT simdjson_staticTargets
ARCHIVE COMPONENT simdjson_Development
INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
)
install(
EXPORT simdjson_staticTargets
@@ -279,6 +287,7 @@ enable_testing()
add_custom_target(all_tests)
add_subdirectory(windows)
include(cmake/CPM.cmake)
add_subdirectory(dependencies) ## This needs to be before tools because of cxxopts
add_subdirectory(tools) ## This needs to be before tests because of cxxopts
@@ -286,8 +295,9 @@ add_subdirectory(tools) ## This needs to be before tests because of cxxopts
# most of the data has been moved to https://github.com/simdjson/simdjson-data
add_subdirectory(jsonexamples)
if(SIMDJSON_SINGLEHEADER)
add_subdirectory(singleheader)
endif()
+1 -1
View File
@@ -38,7 +38,7 @@ PROJECT_NAME = simdjson
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = "3.10.0"
PROJECT_NUMBER = "3.10.1"
# 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
+2 -2
View File
@@ -201,8 +201,8 @@ For the video inclined, <br />
Funding
-------
The work is supported by the Natural Sciences and Engineering Research Council of Canada under grant
number RGPIN-2017-03910.
The work is supported by the Natural Sciences and Engineering Research Council of Canada under grants
RGPIN-2017-03910 and RGPIN-2024-03787.
[license]: LICENSE
[license img]: https://img.shields.io/badge/License-Apache%202-blue.svg
+24
View File
@@ -0,0 +1,24 @@
# SPDX-License-Identifier: MIT
#
# SPDX-FileCopyrightText: Copyright (c) 2019-2023 Lars Melchior and contributors
set(CPM_DOWNLOAD_VERSION 0.40.2)
set(CPM_HASH_SUM "c8cdc32c03816538ce22781ed72964dc864b2a34a310d3b7104812a5ca2d835d")
if(CPM_SOURCE_CACHE)
set(CPM_DOWNLOAD_LOCATION "${CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake")
elseif(DEFINED ENV{CPM_SOURCE_CACHE})
set(CPM_DOWNLOAD_LOCATION "$ENV{CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake")
else()
set(CPM_DOWNLOAD_LOCATION "${CMAKE_BINARY_DIR}/cmake/CPM_${CPM_DOWNLOAD_VERSION}.cmake")
endif()
# Expand relative path. This is important if the provided path contains a tilde (~)
get_filename_component(CPM_DOWNLOAD_LOCATION ${CPM_DOWNLOAD_LOCATION} ABSOLUTE)
file(DOWNLOAD
https://github.com/cpm-cmake/CPM.cmake/releases/download/v${CPM_DOWNLOAD_VERSION}/CPM.cmake
${CPM_DOWNLOAD_LOCATION} EXPECTED_HASH SHA256=${CPM_HASH_SUM}
)
include(${CPM_DOWNLOAD_LOCATION})
+76 -37
View File
@@ -1,5 +1,4 @@
include(CMakeDependentOption)
include(import.cmake)
option(SIMDJSON_ALLOW_DOWNLOADS
"Allow dependencies to be downloaded during configure time"
@@ -11,17 +10,21 @@ cmake_dependent_option(SIMDJSON_GOOGLE_BENCHMARKS "compile the Google Benchmark
SIMDJSON_ALLOW_DOWNLOADS OFF)
if(SIMDJSON_GOOGLE_BENCHMARKS)
set_off(BENCHMARK_ENABLE_TESTING)
set_off(BENCHMARK_ENABLE_INSTALL)
set_off(BENCHMARK_ENABLE_WERROR)
import_dependency(google_benchmarks google/benchmark v1.7.1)
add_dependency(google_benchmarks)
CPMAddPackage(
NAME google_benchmarks
URL https://github.com/google/benchmark/archive/refs/tags/v1.7.1.zip
OPTIONS
"BENCHMARK_ENABLE_TESTING OFF"
"BENCHMARK_ENABLE_INSTALL OFF"
"BENCHMARK_ENABLE_WERROR OFF"
)
endif()
# The bulk of our benchmarking and testing data has been moved simdjson/simdjson-data
import_dependency(simdjson-data simdjson/simdjson-data a5b13babe65c1bba7186b41b43d4cbdc20a5c470)
add_dependency(simdjson-data)
CPMAddPackage(
NAME simdjson-data
URL https://github.com/simdjson/simdjson-data/archive/a5b13babe65c1bba7186b41b43d4cbdc20a5c470.zip
)
option(SIMDJSON_USE_BOOST_JSON "Try to include BOOST_JSON, this may break your binaries under some systems." OFF)
# This prevents variables declared with set() from unnecessarily escaping and
# should not be called more than once
@@ -38,20 +41,30 @@ function(competition_scope_)
int main() {}
]] SIMDJSON_FOUND_STRING_VIEW)
if(SIMDJSON_FOUND_STRING_VIEW AND SIMDJSON_USE_BOOST_JSON)
import_dependency(boostjson boostorg/json ee8d72d)
CPMAddPackage(
NAME boostjson
URL https://github.com/boostorg/json/archive/ee8d72d8502b409b5561200299cad30ccdb91415.zip
)
add_library(boostjson STATIC "${boostjson_SOURCE_DIR}/src/src.cpp")
target_compile_definitions(boostjson PUBLIC BOOST_JSON_STANDALONE)
target_include_directories(boostjson SYSTEM PUBLIC
"${boostjson_SOURCE_DIR}/include")
target_compile_definitions(boostjson INTERFACE SIMDJSON_COMPETITION_BOOSTJSON)
endif()
import_dependency(cjson DaveGamble/cJSON c69134d)
CPMAddPackage(
NAME cjson
URL https://github.com/DaveGamble/cJSON/archive/c69134d01746dcf551dd7724b4edb12f922eb0d1.zip
DOWNLOAD_ONLY YES
)
add_library(cjson STATIC "${cjson_SOURCE_DIR}/cJSON.c")
target_include_directories(cjson SYSTEM PUBLIC "${cjson_SOURCE_DIR}")
target_compile_definitions(cjson INTERFACE SIMDJSON_COMPETITION_CJSON)
import_dependency(fastjson mikeando/fastjson 485f994)
CPMAddPackage(
NAME fastjson
URL https://github.com/mikeando/fastjson/archive/485f994a61a64ac73fa6a40d4d639b99b463563b.zip
DOWNLOAD_ONLY YES
)
add_library(fastjson STATIC
"${fastjson_SOURCE_DIR}/src/fastjson.cpp"
"${fastjson_SOURCE_DIR}/src/fastjson2.cpp"
@@ -60,28 +73,36 @@ int main() {}
"${fastjson_SOURCE_DIR}/include")
target_compile_definitions(fastjson INTERFACE SIMDJSON_COMPETITION_FASTJSON)
import_dependency(gason vivkin/gason 7aee524)
CPMAddPackage(
NAME gason
URL https://github.com/vivkin/gason/archive/7aee524189da1c1ecd19f67981e3d903dae25470.zip
DOWNLOAD_ONLY YES
)
add_library(gason STATIC "${gason_SOURCE_DIR}/src/gason.cpp")
target_include_directories(gason SYSTEM PUBLIC "${gason_SOURCE_DIR}/src")
target_compile_definitions(gason INTERFACE SIMDJSON_COMPETITION_GASON)
import_dependency(jsmn zserge/jsmn 18e9fe4)
CPMAddPackage(
NAME jsmn
URL https://github.com/zserge/jsmn/archive/18e9fe42cbfe21d65076f5c77ae2be379ad1270f.zip
DOWNLOAD_ONLY YES
)
add_library(jsmn STATIC "${jsmn_SOURCE_DIR}/jsmn.c")
target_include_directories(jsmn SYSTEM PUBLIC "${jsmn_SOURCE_DIR}")
target_compile_definitions(jsmn INTERFACE SIMDJSON_COMPETITION_JSMN)
message(STATUS "Importing json (nlohmann/json@v3.10.5)")
set(nlohmann_json_SOURCE_DIR "${dep_root}/json")
if(NOT EXISTS "${nlohmann_json_SOURCE_DIR}")
file(DOWNLOAD
"https://github.com/nlohmann/json/releases/download/v3.10.5/json.hpp"
"${nlohmann_json_SOURCE_DIR}/nlohmann/json.hpp")
endif()
add_library(nlohmann_json INTERFACE)
target_include_directories(nlohmann_json SYSTEM INTERFACE "${nlohmann_json_SOURCE_DIR}")
target_compile_definitions(nlohmann_json INTERFACE SIMDJSON_COMPETITION_NLOHMANN_JSON)
CPMAddPackage(
NAME nlohmann_json
URL https://github.com/nlohmann/json/archive/refs/tags/v3.10.5.zip
)
import_dependency(json11 dropbox/json11 ec4e452)
set_property(TARGET nlohmann_json APPEND PROPERTY INTERFACE_COMPILE_DEFINITIONS SIMDJSON_COMPETITION_NLOHMANN_JSON)
CPMAddPackage(
NAME json11
URL https://github.com/dropbox/json11/archive/ec4e45219af1d7cde3d58b49ed762376fccf1ace.zip
DOWNLOAD_ONLY YES
)
add_library(json11 STATIC "${json11_SOURCE_DIR}/json11.cpp")
target_include_directories(json11 SYSTEM PUBLIC "${json11_SOURCE_DIR}")
target_compile_definitions(json11 INTERFACE SIMDJSON_COMPETITION_JSON11)
@@ -91,7 +112,11 @@ int main() {}
target_include_directories(jsoncpp SYSTEM PUBLIC "${jsoncpp_SOURCE_DIR}")
target_compile_definitions(jsoncpp INTERFACE SIMDJSON_COMPETITION_JSONCPP)
import_dependency(rapidjson Tencent/rapidjson f54b0e4)
CPMAddPackage(
NAME rapidjson
URL https://github.com/Tencent/rapidjson/archive/f54b0e47a08782a6131cc3d60f94d038fa6e0a51.zip
DOWNLOAD_ONLY YES
)
add_library(rapidjson INTERFACE)
target_compile_definitions(rapidjson INTERFACE RAPIDJSON_HAS_STDSTRING)
include (TestBigEndian)
@@ -110,14 +135,22 @@ int main() {}
target_compile_definitions(rapidjson INTERFACE SIMDJSON_COMPETITION_RAPIDJSON)
endif()
import_dependency(sajson chadaustin/sajson 2dcfd35)
CPMAddPackage(
NAME sajson
URL https://github.com/chadaustin/sajson/archive/2dcfd350586375f9910f74821d4f07d67ae455ba.zip
DOWNLOAD_ONLY YES
)
add_library(sajson INTERFACE)
target_compile_definitions(sajson INTERFACE SAJSON_UNSORTED_OBJECT_KEYS)
target_include_directories(sajson SYSTEM INTERFACE
"${sajson_SOURCE_DIR}/include")
target_compile_definitions(sajson INTERFACE SIMDJSON_COMPETITION_SAJSON)
import_dependency(ujson4c esnme/ujson4c e14f3fd)
CPMAddPackage(
NAME ujson4c
URL https://github.com/esnme/ujson4c/archive/e14f3fd5207fe30d1bdea723f260609e69d1abfa.zip
DOWNLOAD_ONLY YES
)
add_library(ujson4c STATIC
"${ujson4c_SOURCE_DIR}/src/ujdecode.c"
"${ujson4c_SOURCE_DIR}/3rdparty/ultrajsondec.c")
@@ -126,7 +159,11 @@ int main() {}
"${ujson4c_SOURCE_DIR}/3rdparty")
target_compile_definitions(ujson4c INTERFACE SIMDJSON_COMPETITION_UJSON4C)
import_dependency(yyjson ibireme/yyjson c385651)
CPMAddPackage(
NAME yyjson
URL https://github.com/ibireme/yyjson/archive/c3856514de0a67d7b66939bf3ed491a2d6e61277.zip
DOWNLOAD_ONLY YES
)
add_library(yyjson STATIC "${yyjson_SOURCE_DIR}/src/yyjson.c")
target_include_directories(yyjson SYSTEM PUBLIC "${yyjson_SOURCE_DIR}/src")
target_compile_definitions(yyjson INTERFACE SIMDJSON_COMPETITION_YYJSON)
@@ -151,10 +188,12 @@ cmake_dependent_option(SIMDJSON_CXXOPTS "Download cxxopts (necessary for tools)"
SIMDJSON_ALLOW_DOWNLOADS OFF)
if(SIMDJSON_CXXOPTS)
set_off(CXXOPTS_BUILD_EXAMPLES)
set_off(CXXOPTS_BUILD_TESTS)
set_off(CXXOPTS_ENABLE_INSTALL)
import_dependency(cxxopts jarro2783/cxxopts 5965670)
add_dependency(cxxopts)
CPMAddPackage(
NAME cxxopts
URL https://github.com/jarro2783/cxxopts/archive/59656709c0c58fcd0ed18b38e02938dbe05284c5.zip
OPTIONS
"CXXOPTS_BUILD_EXAMPLES OFF"
"CXXOPTS_BUILD_TESTS OFF"
"CXXOPTS_ENABLE_INSTALL OFF"
)
endif()
-48
View File
@@ -1,48 +0,0 @@
set(dep_root "${simdjson_SOURCE_DIR}/dependencies/.cache")
if(DEFINED ENV{simdjson_DEPENDENCY_CACHE_DIR})
set(dep_root "$ENV{simdjson_DEPENDENCY_CACHE_DIR}")
endif()
function(import_dependency NAME GITHUB_REPO COMMIT)
message(STATUS "Importing ${NAME} (${GITHUB_REPO}@${COMMIT})")
set(target "${dep_root}/${NAME}")
# If the folder exists in the cache, then we assume that everything is as
# should be and do nothing
if(EXISTS "${target}")
set("${NAME}_SOURCE_DIR" "${target}" PARENT_SCOPE)
return()
endif()
set(zip_url "https://github.com/${GITHUB_REPO}/archive/${COMMIT}.zip")
set(archive "${dep_root}/archive.zip")
set(dest "${dep_root}/_extract")
file(DOWNLOAD "${zip_url}" "${archive}")
file(MAKE_DIRECTORY "${dest}")
execute_process(
WORKING_DIRECTORY "${dest}"
COMMAND "${CMAKE_COMMAND}" -E tar xf "${archive}")
file(REMOVE "${archive}")
# GitHub archives only ever have one folder component at the root, so this
# will always match that single folder
file(GLOB dir LIST_DIRECTORIES YES "${dest}/*")
file(RENAME "${dir}" "${target}")
set("${NAME}_SOURCE_DIR" "${target}" PARENT_SCOPE)
endfunction()
# Delegates to the dependency
macro(add_dependency NAME)
if(NOT DEFINED "${NAME}_SOURCE_DIR")
message(FATAL_ERROR "Missing ${NAME}_SOURCE_DIR variable")
endif()
add_subdirectory("${${NAME}_SOURCE_DIR}" "${PROJECT_BINARY_DIR}/_deps/${NAME}" EXCLUDE_FROM_ALL)
endmacro()
function(set_off NAME)
set("${NAME}" OFF CACHE INTERNAL "")
endfunction()
+201 -186
View File
@@ -17,7 +17,7 @@ An overview of what you need to know to use simdjson, with examples.
- [Using the parsed JSON](#using-the-parsed-json)
- [Using the parsed JSON: additional examples](#using-the-parsed-json-additional-examples)
- [Adding support for custom types](#adding-support-for-custom-types)
- [1. Specialize `simdjson::ondemand::value::get` to get custom types](#1-specialize-simdjsonondemandvalueget-to-get-custom-types)
- [1. Specialize `simdjson::ondemand::value::get` to get custom types (pre-C++20)](#1-specialize-simdjsonondemandvalueget-to-get-custom-types-pre-c20)
- [2. Use `tag_invoke` for custom types (C++20)](#2-use-tag_invoke-for-custom-types-c20)
- [Minifying JSON strings without parsing](#minifying-json-strings-without-parsing)
- [UTF-8 validation (alone)](#utf-8-validation-alone)
@@ -249,7 +249,7 @@ documents.
For code safety, you should keep (1) the `parser` instance, (2) the input string and (3) the document instance alive throughout your parsing. Additionally, you should follow the following rules:
- A `parser` may have at most one document open at a time, since it holds allocated memory used for the parsing.
- By design, you should only have one `document` instance per JSON document. Thus, if you must pass a document instance to a function, you should avoid passing it by value: choose to pass it by reference instance to avoid the copy. (We also provide a `document_reference` class if you need to pass by value.)
- By design, you should only have one `document` instance per JSON document. Thus, if you must pass a document instance to a function, you should avoid passing it by value: choose to pass it by reference instance to avoid the copy. In any case, the `document` class does not have a copy constructor.
During the `iterate` call, the original JSON text is never modified--only read. After you are done
with the document, the source (whether file or string) can be safely discarded.
@@ -528,7 +528,7 @@ support for users who avoid exceptions. See [the simdjson error handling documen
double value;
auto doc = parser.iterate(abstract_json);
auto error = doc["str"]["123"]["abc"].get(value);
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
if (error) { std::cerr << simdjson::error_message(error) << std::endl; return EXIT_FAILURE; }
cout << value << endl; // Prints 3.14
```
This examples also show how we can string several operations and only check for the error once, a strategy we call *error chaining*.
@@ -797,17 +797,10 @@ There are 2 main ways provided by simdjson to deserialize a value into a custom
2. Specialize `simdjson::ondemand::value::get` for each value
2. Using `tag_invoke` *(the recommended way if your system supports C++20 or better)*
The differences between the two approaches are as follows:
We describe both of them in the following sections. Most users who have systems compatible with
C++20 or better should skip ahead to [using `tag_invoke` for custom types (C++20)](#2-use-tag_invoke-for-custom-types-c20) as it is more powerful and simpler.
1. Your compiler needs to support C++20 for `tag_invoke`.
2. The first way limits you to know your type fully, but in the `tag_invoke` way, you can use C++20 concepts and template meta programming as well.
3. Another difference between the two ways is that in `tag_invoke` way you may or may not mark your deserialization function (`tag_invoke` itself) as `noexcept`, but in the
*template specialization* way (the first way) you HAVE TO mark your specialization as `noexcept` which means that you may need to catch exceptions so
that your function does not throw any exception all the while it potentially can (for example any type like `std::string`, `std::vector`, etc. that has an allocator can potentially throw exceptions).
4. Another difference is that the `tag_invoke` way can work on both `document` and `value` but in the first way you have to provide 2 distinct specializations.
5. If you by mistake use both way with each other, the *specialization way* will be used silently due to backward-compatibility.
### 1. Specialize `simdjson::ondemand::value::get` to get custom types
### 1. Specialize `simdjson::ondemand::value::get` to get custom types (pre-C++20)
Suppose you have your own types, such as a `Car` struct:
@@ -843,13 +836,14 @@ type:
We may do so by providing additional template definitions to the `ondemand::value` type.
We may start by providing a definition for `std::vector<double>` as follows. Observe
how we guard the code with `#ifndef __cpp_concepts`: that is because the necessary code
how we guard the code with `#if !SIMDJSON_SUPPORTS_DESERIALIZATION`: that is because the necessary code
is automatically provided by simdjson if C++20 (and concepts) are available.
See [Use `tag_invoke` for custom types](#2-use-tag_invoke-for-custom-types-c20) if you have
C++20 support.
```c++
#ifndef __cpp_concepts // because the code is unnecessary with C++20
#if !SIMDJSON_SUPPORTS_DESERIALIZATION
// The code is unnecessary with C++20:
template <>
simdjson_inline simdjson_result<std::vector<double>>
simdjson::ondemand::value::get() noexcept {
@@ -861,7 +855,7 @@ simdjson::ondemand::value::get() noexcept {
double val;
error = v.get_double().get(val);
if (error) { return error; }
try { vec.push_back(val); } catch (...) { return simdjson::UNEXPECTED_ERROR; }
vec.push_back(val);
}
return vec;
}
@@ -877,27 +871,10 @@ simdjson_inline simdjson_result<Car> simdjson::ondemand::value::get() noexcept {
auto error = get_object().get(obj);
if (error) { return error; }
Car car;
// Instead of repeatedly obj["something"], we iterate through the object which
// we expect to be faster.
for (auto field : obj) {
raw_json_string key;
error = field.key().get(key);
if (error) { return error; }
if (key == "make") {
error = field.value().get_string(car.make);
if (error) { return error; }
} else if (key == "model") {
error = field.value().get_string(car.model);
if (error) { return error; }
} else if (key == "year") {
error = field.value().get_int64().get(car.year);
if (error) { return error; }
} else if (key == "tire_pressure") {
error = field.value().get<std::vector<double>>().get(car.tire_pressure);
if (error) { return error; }
}
}
if((error = obj["make"].get_string(car.make))) { return error; }
if((error = obj["model"].get_string(car.model))) { return error; }
if((error = obj["year"].get_int64().get(car.year))) { return error; }
if((error = obj["tire_pressure"].get<std::vector<double>>().get(car.tire_pressure))) { return error; }
return car;
}
```
@@ -921,7 +898,8 @@ struct Car {
std::vector<double> tire_pressure;
};
#ifndef __cpp_concepts // because the code is unnecessary with C++20
#if !SIMDJSON_SUPPORTS_DESERIALIZATION
// This code is not necessary if you have a C++20 compliant system:
template <>
simdjson_inline simdjson_result<std::vector<double>>
simdjson::ondemand::value::get() noexcept {
@@ -939,33 +917,16 @@ simdjson::ondemand::value::get() noexcept {
}
#endif
template <>
simdjson_inline simdjson_result<Car> simdjson::ondemand::value::get() noexcept {
ondemand::object obj;
auto error = get_object().get(obj);
if (error) { return error; }
Car car;
// Instead of repeatedly obj["something"], we iterate through the object which
// we expect to be faster.
for (auto field : obj) {
raw_json_string key;
error = field.key().get(key);
if (error) { return error; }
if (key == "make") {
error = field.value().get_string(car.make);
if (error) { return error; }
} else if (key == "model") {
error = field.value().get_string(car.model);
if (error) { return error; }
} else if (key == "year") {
error = field.value().get_int64().get(car.year);
if (error) { return error; }
} else if (key == "tire_pressure") {
error = field.value().get<std::vector<double>>().get(car.tire_pressure);
if (error) { return error; }
}
}
if((error = obj["make"].get_string(car.make))) { return error; }
if((error = obj["model"].get_string(car.model))) { return error; }
if((error = obj["year"].get_int64().get(car.year))) { return error; }
if((error = obj["tire_pressure"].get<std::vector<double>>().get(car.tire_pressure))) { return error; }
return car;
}
@@ -983,7 +944,6 @@ int main(void) {
Car c(val); // an exception may be thrown
std::cout << c.make << std::endl;
}
direct();
return EXIT_SUCCESS;
}
```
@@ -1004,11 +964,11 @@ int main(void) {
ondemand::parser parser;
ondemand::document doc;
auto error = parser.iterate(json).get(doc);
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
if (error) { std::cerr << simdjson::error_message(error) << std::endl; return EXIT_FAILURE; }
for (auto val : doc) {
Car c;
error = val.get<Car>().get(c);
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
if (error) { std::cerr << simdjson::error_message(error) << std::endl; return EXIT_FAILURE; }
std::cout << c.make << std::endl;
}
return EXIT_SUCCESS;
@@ -1039,16 +999,19 @@ struct Car {
std::vector<double> tire_pressure;
};
#ifndef __cpp_concepts // because the code is unnecessary with C++20
#if !SIMDJSON_SUPPORTS_DESERIALIZATION
// This code is not necessary if you have a C++20 compliant system:
template <>
simdjson_inline simdjson_result<std::vector<double>>
simdjson::ondemand::value::get() noexcept {
ondemand::array array;
if (auto error = get_array().get(array); error) { return error; }
auto error = get_array().get(array);
if (error) { return error; }
std::vector<double> vec;
for (auto v : array) {
double val;
if (auto error = v.get_double().get(val); error) { return error; }
error = v.get_double().get(val);
if (error) { return error; }
vec.push_back(val);
}
return vec;
@@ -1059,30 +1022,26 @@ template <>
simdjson_inline simdjson_result<Car> simdjson::ondemand::document::get() & noexcept {
ondemand::object obj;
auto error = get_object().get(obj);
if (error) {
return error;
}
if (error) { return error; }
Car car;
// Instead of repeatedly obj["something"], we iterate through the object which
// we expect to be faster.
for (auto field : obj) {
raw_json_string key;
error = field.key().get(key);
if (error) { return error; }
if (key == "make") {
error = field.value().get_string(car.make);
if (error) { return error; }
} else if (key == "model") {
error = field.value().get_string(car.model);
if (error) { return error; }
} else if (key == "year") {
error = field.value().get_int64().get(car.year);
if (error) { return error; }
} else if (key == "tire_pressure") {
error = field.value().get<std::vector<double>>().get(car.tire_pressure);
if (error) { return error; }
}
}
if((error = obj["make"].get_string(car.make))) { return error; }
if((error = obj["model"].get_string(car.model))) { return error; }
if((error = obj["year"].get_int64().get(car.year))) { return error; }
if((error = obj["tire_pressure"].get<std::vector<double>>().get(car.tire_pressure))) { return error; }
return car;
}
template <>
simdjson_inline simdjson_result<Car> simdjson::ondemand::document::get() noexcept {
ondemand::object obj;
auto error = get_object().get(obj);
if (error) { return error; }
Car car;
if((error = obj["make"].get_string(car.make))) { return error; }
if((error = obj["model"].get_string(car.model))) { return error; }
if((error = obj["year"].get_int64().get(car.year))) { return error; }
if((error = obj["tire_pressure"].get<std::vector<double>>().get(car.tire_pressure))) { return error; }
return car;
}
@@ -1099,68 +1058,75 @@ int main(void) {
### 2. Use `tag_invoke` for custom types (C++20)
A standard approach to provide support for deserialization is to add a `tag_invoke` function
in your own class:
In C++20, the standard introduced the notion of *customization point*.
A customization point is a function or function object that can be customized for different types. It allows library authors to provide default behavior while giving users the ability to override this behavior for specific types.
A tag_invoke function serves as a mechanism for customization points. It is not directly part of the C++ standard library but is often used in libraries that implement customization points.
The tag_invoke function is typically a generic function that takes a tag type and additional arguments.
The first argument is usually a tag type (often an empty struct) that uniquely identifies the customization point (e.g., deserialization of custom types in simdjson). Users or library providers can specialize tag_invoke for their types by defining it in the appropriate namespace, often inline namespace.
If your system supports C++20, we recommend that you adopt the `tag_invoke` approach
instead to deserialize custom types. It may prove to be considerably simpler. When
simdjson detects the necessary support, it sets the `SIMDJSON_SUPPORTS_DESERIALIZATION` macro
to 1, otherwise it is set to 0.
Consider a custom class `Car`:
```C++
/**
* A custom type that we want to parse.
*/
struct Car {
std::string make;
std::string model;
int year = 0;
std::vector<double> tire_pressure;
friend simdjson_result<Car>
tag_invoke(simdjson::deserialize_tag, std::type_identity<Car>, auto &val) {
simdjson::ondemand::object obj;
auto error = val.get_object().get(obj);
if (error) {
return error;
}
Car car{};
// Instead of repeatedly obj["something"], we iterate through the object
// which we expect to be faster.
for (auto field : obj) {
simdjson::ondemand::raw_json_string key;
error = field.key().get(key);
if (error) {
return error;
}
if (key == "make") {
error = field.value().get_string(car.make);
if (error) {
return error;
}
} else if (key == "model") {
error = field.value().get_string(car.model);
if (error) {
return error;
}
} else if (key == "year") {
error = field.value().get(car.year);
if (error) {
return error;
}
} else if (key == "tire_pressure") {
error = field.value().get(car.tire_pressure);
if (error) {
return error;
}
}
}
return car;
}
int year;
std::vector<float> tire_pressure;
};
```
Let us explain each argument of `tag_invoke`:
Observe how we defined the class to use types that simdjson does not directly support (`float`, `int`).
With C++20 support, the library grabs from the JSON the generic type (`double`, `int`) and then it
casts it automatically.
- `simdjson::deserialize_tag`: it is the tag for Customization Point Object (CPO)
- `std::type_identity<T>`: We specify the custom type we want here
- `simdjson::ondemand::value&` or `simdjson::ondemand::document&`: the value or document that we want to deserialize from; you may want to just specify `auto&` to capture both of them.
You may support deserializing directly from a JSON value or document to your own `Car` instance
by defining a single `tag_invoke` function:
```C++
namespace simdjson {
// This tag_invoke MUST be inside simdjson namespace
template <typename simdjson_value>
auto tag_invoke(deserialize_tag, simdjson_value &val, Car& car) {
ondemand::object obj;
auto error = val.get_object().get(obj);
if (error) {
return error;
}
if ((error = obj["make"].get_string(car.make))) {
return error;
}
if ((error = obj["model"].get_string(car.model))) {
return error;
}
if ((error = obj["year"].get(car.year))) {
return error;
}
if ((error = obj["tire_pressure"].get<std::vector<float>>().get(
car.tire_pressure))) {
return error;
}
return simdjson::SUCCESS;
}
} // namespace simdjson
```
Observe how we call `get<std::vector<float>>()` even though we never defined support
for `std::vector<float>` in the simdjson library: it is all automated thanks to C++20 concepts.
Importantly, the `tag_invoke` function must be inside the `simdjson` namespace.
Let us explain each argument of `tag_invoke` function.
- `simdjson::deserialize_tag`: it is the tag for Customization Point Object (CPO). You may often ignore this parameter. It is used to indicate that you mean to provide a deserialization function for simdjson.
- `var`: It receives automatically a `simdjson` value type (document, value, document_reference).
- The third parameter is an instance of the type that you want to support.
You can use it like so:
@@ -1179,11 +1145,6 @@ Observe how we first get an instance of `document` and then we cast.
You can also handle errors explicitly:
```cpp
simdjson::padded_string json =
R"( { "make": "Toyota", "model": "Camry", "year": 2018,
"tire_pressure": [ 40.1, 39.9 ] })"_padded;
simdjson::ondemand::parser parser;
simdjson::ondemand::document doc = parser.iterate(json);
Car c;
auto error = doc.get(c);
if(error) { std::cerr << simdjson::error_message(error); return false; }
@@ -1215,8 +1176,6 @@ explicit casting. The cast may throw an exception.
Once more, you can handle errors explicitly:
```cpp
simdjson::ondemand::parser parser;
simdjson::ondemand::document doc = parser.iterate(json);
for (auto val : doc) {
Car c;
auto error = val.get(c);
@@ -1239,25 +1198,13 @@ You can also use the custom `Car` type as part of a template such as `std::vecto
}
```
You can also extend support to arbitrary templates.
Suppose your custom types are `std::unique_ptr<any type>`,
you could add the following `tag_invoke`:
By default, we support a wide range of standard templates such as
`std::vector`, `std::list`, `std::set`, `std::stack`, `std:queue`,
`std:deque`, `std::priority_queue`, `std::unordered_set`, `std::multiset`,
`std::unordered_multiset`, `std::unique_ptr`, `std::shared_ptr`, `std::optional`,
etc. They are handled automatically.
```C++
namespace simdjson {
// This tag_invoke MUST be inside simdjson namespace since we can't make it a friend of unique_ptr
template <typename T>
auto tag_invoke(deserialize_tag, std::type_identity<std::unique_ptr<T>>, auto &val) {
return simdjson_result{std::make_unique<T>(val.template get<T>())};
}
} // namespace simdjson
```
You can also mark your `tag_invoke` function as `noexcept` and we will obey that, unlike the *template specialization* way.
You would use it like this:
E.g., you can recover an `std::unique_ptr<Car>` like so:
```C++
int main() {
auto const json = R"( { "make": "Toyota", "model": "Camry", "year": 2018,
@@ -1270,6 +1217,59 @@ int main() {
}
```
You may also conditionally fill in `std::optional` values.
```C++
padded_string json =
R"( { "car1": { "make": "Toyota", "model": "Camry", "year": 2018,
"tire_pressure": [ 40.1, 39.9 ] }
})"_padded;
ondemand::parser parser;
ondemand::document doc = parser.iterate(json);
std::optional<Car> car;
error = doc["key not found"].get<std::optional<Car>>().get(car);
// car has no value, error != simdjson::SUCCESS
error = doc["car1"].get<std::optional<Car>>().get(car);
// car has value Car{"Toyota", "Camry", 2018, {40.1f, 39.9f}}
// error is simdjson::SUCCESS
```
And so forth.
Advanced users may want to overwrite the defaults provided by the simdjson library.
Suppose for example that you want to construct an instance of `std::list<Car>`, but
you also want to filter out any car made by Toyota. You may provide your own
`tag_invoke` function:
```c++
namespace simdjson {
// suppose we want to filter out all Toyotas
template <typename simdjson_value>
auto tag_invoke(deserialize_tag, simdjson_value &val, std::list<Car>& car) {
ondemand::array arr;
auto error = val.get_array().get(arr);
if (error) {
return error;
}
for (auto v : arr) {
Car c;
if ((error = v.get<Car>().get(c))) {
return error;
}
if(c.make != "Toyota") {
car.push_back(c);
}
}
return simdjson::SUCCESS;
}
}
```
With this code, deserializing an `std::list<Car>` instance would capture only the cars
that are not made by Toyota.
Minifying JSON strings without parsing
----------------------
@@ -1284,6 +1284,7 @@ In some cases, you may have valid JSON strings that you do not wish to parse but
std::unique_ptr<char[]> buffer{new char[length]};
size_t new_length{}; // It will receive the minified length.
auto error = simdjson::minify(some_string, length, buffer.get(), new_length);
if(error) { std::cerr << simdjson::error_message(error); }
// The buffer variable now has "[1,2,3,4]" and new_length has value 9.
```
@@ -1427,9 +1428,11 @@ be represented as `value` instances. You can check that a document is a scalar w
JSONPath
------------
The simdjson library now supports a subset of [JSONPath](https://datatracker.ietf.org/doc/html/draft-normington-jsonpath-00) through the `at_path()` method, allowing you to reach further into the document in a single call. The subset of JSONPath that is implemented is the subset that is trivially convertible into the JSON Pointer format, using `.` to access a field and `[]` to access a specific index.
The simdjson library supports a subset of [JSONPath](https://datatracker.ietf.org/doc/html/draft-normington-jsonpath-00) through the `at_path()` method, allowing you to reach further into the document in a single call. The subset of JSONPath that is implemented is the subset that is trivially convertible into the JSON Pointer format, using `.` to access a field and `[]` to access a specific index.
This implementation relies on `at_path()` converting its argument to JSON Pointer and then calling `at_pointer`, which makes use of [`rewind`](#rewind) to reset the parser at the beginning of the document. Hence, it invalidates all previously parsed values, objects and arrays: make sure to consume the values between each call to `at_path`.
This implementation relies on `at_path()` converting its argument to JSON Pointer and then calling `at_pointer`, which makes use of
[`rewind`](#rewind) to reset the parser at the beginning of the document. Hence, it invalidates all previously parsed values, objects
and arrays: make sure to consume the values between each call to `at_path`.
Consider the following example:
@@ -1470,6 +1473,18 @@ doc.at_path(".\\u00E9") == 123; // true
doc.at_path((const char*)u8".\u00E9") // returns an error (NO_SUCH_FIELD)
```
We also support the `$` prefix. When you start a JSONPath expression with $, you are indicating that the path starts from the root of the JSON document. E.g.,
```c++
auto json = R"( { "c" :{ "foo": { "a": [ 10, 20, 30 ] }}, "d": { "foo2": { "a": [ 10, 20, 30 ] }} , "e": 120 })"_padded;
ondemand::parser parser;
ondemand::document doc = parser.iterate(json);
ondemand::object obj = doc.get_object();
int64_t x = obj.at_path("$.c.foo.a[1]"); // 20
x = obj.at_path("$.d.foo2.a.2"); // 30
```
Error handling
--------------
@@ -1481,7 +1496,7 @@ pair. You can retrieve the value with .get() without generating an exception, li
```c++
ondemand::document doc;
auto error = parser.iterate(json).get(doc);
if (error) { cerr << error << endl; exit(1); }
if(error) { std::cerr << simdjson::error_message(error); exit(1); }
```
When there is no error, the error code `simdjson::SUCCESS`is returned: it evaluates as false as a Boolean.
@@ -1512,7 +1527,7 @@ bool simple_error_example() {
auto error = doc["bad number"].get_double().get(x);
// returns "simdjson::NUMBER_ERROR"
if (error != SUCCESS) {
std::cout << error << std::endl;
std::cerr << simdjson::error_message(error) << std::endl;
return false;
}
std::cout << "Got " << x << std::endl;
@@ -1565,7 +1580,6 @@ We can write a "quick start" example where we attempt to parse the following JSO
Our program loads the file, selects value corresponding to key `"search_metadata"` which expected to be an object, and then
it selects the key `"count"` within that object.
```C++
#include <iostream>
#include "simdjson.h"
@@ -1573,10 +1587,10 @@ it selects the key `"count"` within that object.
int main(void) {
simdjson::ondemand::parser parser;
auto error = padded_string::load("twitter.json").get(json);
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
if (error) { std::cerr << simdjson::error_message(error) << std::endl; return EXIT_FAILURE; }
simdjson::ondemand::document tweets;
error = parser.iterate(json).get(tweets);
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
if (error) { std::cerr << simdjson::error_message(error) << std::endl; return EXIT_FAILURE; }
simdjson::ondemand::value res;
error = tweets["search_metadata"]["count"].get(res);
if (error != SUCCESS) {
@@ -1607,12 +1621,12 @@ int main(void) {
simdjson::ondemand::document tweets;
padded_string json;
auto error = padded_string::load("twitter.json").get(json);
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
if (error) { std::cerr << simdjson::error_message(error) << std::endl; return EXIT_FAILURE; }
error = parser.iterate(json).get(tweets);
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
if (error) { std::cerr << simdjson::error_message(error) << std::endl; return EXIT_FAILURE; }
uint64_t identifier;
error = tweets["statuses"].at(0)["id"].get(identifier);
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
if (error) { std::cerr << simdjson::error_message(error) << std::endl; return EXIT_FAILURE; }
std::cout << identifier << std::endl;
}
```
@@ -1636,40 +1650,40 @@ bool parse() {
// Iterating through an array of objects
auto error = parser.iterate(cars_json).get(doc);
if (error) { std::cerr << error << std::endl; return false; }
if (error) { std::cerr << simdjson::error_message(error) << std::endl; return false; }
ondemand::array cars; // invalid until the get() succeeds
error = doc.get_array().get(cars);
for (auto car_value : cars) {
ondemand::object car; // invalid until the get() succeeds
error = car_value.get_object().get(car);
if (error) { std::cerr << error << std::endl; return false; }
if (error) { std::cerr << simdjson::error_message(error) << std::endl; return false; }
// Accessing a field by name
std::string_view make;
std::string_view model;
error = car["make"].get(make);
if (error) { std::cerr << error << std::endl; return false; }
if (error) { std::cerr << simdjson::error_message(error) << std::endl; return false; }
error = car["model"].get(model);
if (error) { std::cerr << error << std::endl; return false; }
if (error) { std::cerr << simdjson::error_message(error) << std::endl; return false; }
cout << "Make/Model: " << make << "/" << model << endl;
// Casting a JSON element to an integer
uint64_t year{};
error = car["year"].get(year);
if (error) { std::cerr << error << std::endl; return false; }
if (error) { std::cerr << simdjson::error_message(error) << std::endl; return false; }
cout << "- This car is " << 2020 - year << " years old." << endl;
// Iterating through an array of floats
double total_tire_pressure = 0;
ondemand::array pressures;
error = car["tire_pressure"].get_array().get(pressures);
if (error) { std::cerr << error << std::endl; return false; }
if (error) { std::cerr << simdjson::error_message(error) << std::endl; return false; }
for (auto tire_pressure_value : pressures) {
double tire_pressure;
error = tire_pressure_value.get_double().get(tire_pressure);
if (error) { std::cerr << error << std::endl; return false; }
if (error) { std::cerr << simdjson::error_message(error) << std::endl; return false; }
total_tire_pressure += tire_pressure;
}
cout << "- Average tire pressure: " << (total_tire_pressure / 4) << endl;
@@ -1791,7 +1805,7 @@ auto doc = parser.iterate(broken_json);
int64_t i;
auto error = doc["integer"].get_int64().get(i); // Expect to get integer from "integer" key, but get TAPE_ERROR
if (error) {
std::cout << error << std::endl; // Prints TAPE_ERROR error message
std::cerr << simdjson::error_message(error) << std::endl; // Prints TAPE_ERROR error message
// Recover a pointer to the location of the first error:
const char * ptr;
doc.current_location().get(ptr);
@@ -1828,7 +1842,7 @@ auto doc = parser.iterate(json);
int64_t i;
auto error = doc["integer"].get_int64().get(i); // Incorrect call on array, INCORRECT_TYPE error
if (error) {
std::cout << error << std::endl; // Prints INCORRECT_TYPE error message
std::cerr << simdjson::error_message(error) << std::endl; // Prints INCORRECT_TYPE error message
std::cout<< doc.current_location() << std::endl; // Prints "[1,2,3] " (location of INCORRECT_TYPE error)
}
```
@@ -2001,7 +2015,7 @@ for (auto doc: stream) {
error = doc.at_pointer("/4").get(val);
// error == simdjson::CAPACITY
if (error) {
std::cerr << error << std::endl;
std::cerr << simdjson::error_message(error) << std::endl;
// We left 293 bytes unprocessed at the tail end of the input.
std::cout << " unprocessed bytes at the end: " << stream.truncated_bytes() << std::endl;
break;
@@ -2876,6 +2890,7 @@ Performance tips
std::string_view year = data["year"];
std::string_view rating = data["rating"];
```
- You will get better performance if you seek the keys in the order in which they appear in the document. So if processing `{"a":1, "b":2, "c":3}`, do `value1 = data["a"]; value2 = data["b"]; value3 data["c"];` and not `value2 = data["b"]; value1 = data["a"]; value3 data["c"];`. Of course, it is not always possible to know for sure in which order the keys appear.
+44
View File
@@ -8,6 +8,7 @@ An overview of what you need to know to use simdjson, with examples.
* [Using the Parsed JSON](#using-the-parsed-json)
* [C++17 Support](#c17-support)
* [JSON Pointer](#json-pointer)
* [JSONPath](#jsonpath)
* [Error Handling](#error-handling)
* [Error Handling Example](#error-handling-example)
* [Exceptions](#exceptions)
@@ -257,6 +258,49 @@ for (dom::element car_element : cars) {
}
```
JSONPath
------------
The simdjson library supports a subset of [JSONPath](https://datatracker.ietf.org/doc/html/draft-normington-jsonpath-00) through the `at_path()` method, allowing you to reach further into the document in a single call. The subset of JSONPath that is implemented is the subset that is trivially convertible into the JSON Pointer format, using `.` to access a field and `[]` to access a specific index.
Consider the following example:
```c++
auto cars_json = R"( [
{ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] },
{ "make": "Kia", "model": "Soul", "year": 2012, "tire_pressure": [ 30.1, 31.0, 28.6, 28.7 ] },
{ "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
] )"_padded;
dom::parser parser;
dom::element doc;
auto error = parser.parse(cars_json).get(doc);
if(error) { /*won't happen*/ }
double p;
error = doc.at_path("[0].tire_pressure[1]").get(p);
if(error) { /*won't happen*/ }
cout << p << endl; // Prints 39.9
```
We also support the `$` prefix. When you start a JSONPath expression with $, you are indicating that the path starts from the root of the JSON document. E.g.,
```c++
auto json = R"( { "c" :{ "foo": { "a": [ 10, 20, 30 ] }}, "d": { "foo2": { "a": [ 10, 20, 30 ] }} , "e": 120 })"_padded;
dom::parser parser;
dom::element doc;
auto error = parser.parse(json).get(doc);
if(error) { /*won't happen*/ }
dom::object obj;
error = doc.get_object().get(obj);
if(error) { /*won't happen*/ }
int64_t x;
error = obj.at_path("$[3].foo.a[1]").get(x);
if(error) { /*won't happen*/ }
if(x != 20) { /*won't happen*/ }
x = obj.at_path("$.d.foo2.a.2");
if(error) { /*won't happen*/ }
```
Error Handling
+115 -1
View File
@@ -25,6 +25,7 @@ Contents
- [Use cases](#use-cases)
- [Tracking your position](#tracking-your-position)
- [Incomplete streams](#incomplete-streams)
- [C++20 features](#c20-features)
Motivation
-----------
@@ -102,7 +103,12 @@ remove almost entirely its cost and replaces it by the overhead of a thread, whi
cheaper. Ain't that awesome!
Thread support is only active if thread supported is detected in which case the macro
SIMDJSON_THREADS_ENABLED is set. Otherwise the library runs in single-thread mode.
SIMDJSON_THREADS_ENABLED is set. You can also manually pass `SIMDJSON_THREADS_ENABLED=1` flag
to the library. Otherwise the library runs in single-thread mode.
You should be consistent. If you link against the simdjson library built for multithreading
(i.e., with `SIMDJSON_THREADS_ENABLED`), then you should build your application with multithreading
system (setting `SIMDJSON_THREADS_ENABLED=1` and linking against a thread library).
A `document_stream` instance uses at most two threads: there is a main thread and a worker thread.
@@ -288,3 +294,111 @@ string
object
array
```
C++20 features
--------------------
In C++20, the standard introduced the notion of *customization point*.
A customization point is a function or function object that can be customized for different types. It allows library authors to provide default behavior while giving users the ability to override this behavior for specific types.
A tag_invoke function serves as a mechanism for customization points. It is not directly part of the C++ standard library but is often used in libraries that implement customization points.
The tag_invoke function is typically a generic function that takes a tag type and additional arguments.
The first argument is usually a tag type (often an empty struct) that uniquely identifies the customization point (e.g., deserialization of custom types in simdjson). Users or library providers can specialize tag_invoke for their types by defining it in the appropriate namespace, often inline namespace.
You can deserialize you own data structures conveniently if your system supports C++20.
When it is the case, the macro `SIMDJSON_SUPPORTS_DESERIALIZATION` will be set to 1 by
the simdjson library.
Consider a custom class `Car`:
```C++
struct Car {
std::string make;
std::string model;
int year;
std::vector<float> tire_pressure;
};
```
You may support deserializing directly from a JSON value or document to your own `Car` instance
by defining a single `tag_invoke` function:
```C++
namespace simdjson {
// This tag_invoke MUST be inside simdjson namespace
template <typename simdjson_value>
auto tag_invoke(deserialize_tag, simdjson_value &val, Car& car) {
ondemand::object obj;
auto error = val.get_object().get(obj);
if (error) {
return error;
}
if ((error = obj["make"].get_string(car.make))) {
return error;
}
if ((error = obj["model"].get_string(car.model))) {
return error;
}
if ((error = obj["year"].get(car.year))) {
return error;
}
if ((error = obj["tire_pressure"].get<std::vector<float>>().get(
car.tire_pressure))) {
return error;
}
return simdjson::SUCCESS;
}
} // namespace simdjson
```
Importantly, the `tag_invoke` function must be inside the `simdjson` namespace.
Let us explain each argument of `tag_invoke` function.
- `simdjson::deserialize_tag`: it is the tag for Customization Point Object (CPO). You may often ignore this parameter. It is used to indicate that you mean to provide a deserialization function for simdjson.
- `var`: It receives automatically a `simdjson` value type (document, value, document_reference).
- The third parameter is an instance of the type that you want to support.
Please see our main documentation (`basics.md`) under
"Use `tag_invoke` for custom types (C++20)" for details about
tag_invoke functions.
Given a stream of JSON documents, you can add them to a data struture
such as a `std::vector<Car>` like so if you support exceptions:
```C++
padded_string json =
R"( { "make": "Toyota", "model": "Camry", "year": 2018,
"tire_pressure": [ 40.1, 39.9 ] }
{ "make": "Kia", "model": "Soul", "year": 2012,
"tire_pressure": [ 30.1, 31.0 ] }
{ "make": "Toyota", "model": "Tercel", "year": 1999,
"tire_pressure": [ 29.8, 30.0 ] }
)"_padded;
ondemand::parser parser;
ondemand::document_stream stream;
[[maybe_unused]] auto error = parser.iterate_many(json).get(stream);
std::vector<Car> cars;
for(auto doc : stream) {
cars.push_back((Car)doc); // an exception may be thrown
}
```
Otherwise you may use this longer version for explicit handling of errors:
```C++
std::vector<Car> cars;
for(auto doc : stream) {
Car c;
if ((error = doc.get<Car>().get(c))) {
std::cerr << simdjson::error_message(error); << std::endl;
return EXIT_FAILURE;
}
cars.push_back(c);
}
```
+7 -2
View File
@@ -103,7 +103,12 @@ cases, remove almost entirely its cost and replaces it by the overhead of a thre
of magnitude cheaper. Ain't that awesome!
Thread support is only active if thread supported is detected in which case the macro
SIMDJSON_THREADS_ENABLED is set. Otherwise the library runs in single-thread mode.
SIMDJSON_THREADS_ENABLED is set. You can also manually pass `SIMDJSON_THREADS_ENABLED=1` flag
to the library. Otherwise the library runs in single-thread mode.
You should be consistent. If you link against the simdjson library built for multithreading
(i.e., with `SIMDJSON_THREADS_ENABLED`), then you should build your application with multithreading
system (setting `SIMDJSON_THREADS_ENABLED=1` and linking against a thread library).
A `document_stream` instance uses at most two threads: there is a main thread and a worker thread.
You should expect the main thread to be fully occupied while the worker thread is partially busy
@@ -125,7 +130,7 @@ Whitespace Characters:
- **Nothing**
Some official formats **(non-exhaustive list)**:
- [Newline-Delimited JSON (NDJSON)](http://ndjson.org/)
- [Newline-Delimited JSON (NDJSON)](https://github.com/ndjson/ndjson-spec)
- [JSON lines (JSONL)](http://jsonlines.org/)
- [Record separator-delimited JSON (RFC 7464)](https://tools.ietf.org/html/rfc7464) <- Not supported by JsonStream!
- [More on Wikipedia...](https://en.wikipedia.org/wiki/JSON_streaming)
+2 -2
View File
@@ -241,8 +241,8 @@ long page_size() {
// Returns true if the buffer + len + simdjson::SIMDJSON_PADDING crosses the
// page boundary.
bool need_allocation(const char *buf, size_t len) {
return ((reinterpret_cast<uintptr_t>(buf + len - 1) % page_size()) <
simdjson::SIMDJSON_PADDING);
return ((reinterpret_cast<uintptr_t>(buf + len - 1) % page_size())
+ simdjson::SIMDJSON_PADDING > static_cast<uintptr_t>(page_size()));
}
simdjson::padded_string_view
-1
View File
@@ -53,5 +53,4 @@
#include "simdjson/dom.h"
#include "simdjson/ondemand.h"
#endif // SIMDJSON_H
+6
View File
@@ -35,6 +35,12 @@ simdjson_inline uint64_t clear_lowest_bit(uint64_t input_num) {
return input_num & (input_num-1);
}
// We sometimes call leading_zeroes on inputs that are zero,
// but the algorithms do not end up using the returned value.
// Sadly, sanitizers are not smart enough to figure it out.
// Applies only when SIMDJSON_PREFER_REVERSE_BITS is defined and true.
// (See below.)
SIMDJSON_NO_SANITIZE_UNDEFINED
/* result might be undefined when input_num is zero */
simdjson_inline int leading_zeroes(uint64_t input_num) {
#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO
+1
View File
@@ -23,6 +23,7 @@ public:
) const noexcept final;
simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final;
simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final;
simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) const noexcept final;
};
} // namespace arm64
@@ -51,6 +51,12 @@ simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t
} // namespace arm64
} // namespace simdjson
#ifndef SIMDJSON_SWAR_NUMBER_PARSING
#if SIMDJSON_IS_BIG_ENDIAN
#define SIMDJSON_SWAR_NUMBER_PARSING 0
#else
#define SIMDJSON_SWAR_NUMBER_PARSING 1
#endif
#endif
#endif // SIMDJSON_ARM64_NUMBERPARSING_DEFS_H
+13 -2
View File
@@ -134,6 +134,12 @@ namespace {
tmp = vpaddq_u8(tmp, tmp);
return vgetq_lane_u16(vreinterpretq_u16_u8(tmp), 0);
}
// Returns 4-bit out of each byte, alternating between the high 4 bits and low
// bits result it is 64 bit.
simdjson_inline uint64_t to_bitmask64() const {
return vget_lane_u64(
vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(*this), 4)), 0);
}
simdjson_inline bool any() const { return vmaxvq_u32(vreinterpretq_u32_u8(*this)) != 0; }
};
@@ -210,7 +216,7 @@ namespace {
// Bit-specific operations
simdjson_inline simd8<bool> any_bits_set(simd8<uint8_t> bits) const { return vtstq_u8(*this, bits); }
simdjson_inline bool any_bits_set_anywhere() const { return this->max_val() != 0; }
simdjson_inline bool any_bits_set_anywhere() const { return vmaxvq_u32(vreinterpretq_u32_u8(*this)) != 0; }
simdjson_inline bool any_bits_set_anywhere(simd8<uint8_t> bits) const { return (*this & bits).any_bits_set_anywhere(); }
template<int N>
simdjson_inline simd8<uint8_t> shr() const { return vshrq_n_u8(*this, N); }
@@ -223,7 +229,12 @@ namespace {
return lookup_table.apply_lookup_16_to(*this);
}
// Returns 4-bit out of each byte, alternating between the high 4 bits and low
// bits result it is 64 bit.
simdjson_inline uint64_t to_bitmask64() const {
return vget_lane_u64(
vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(*this), 4)), 0);
}
// Copies to 'output" all bytes corresponding to a 0 in the mask (interpreted as a bitset).
// Passing a 0 value for mask would be equivalent to writing out every byte to output.
// Only the first 16 - count_ones(mask) bytes of the result are significant but 16 bytes
@@ -46,6 +46,32 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
};
}
struct escaping {
static constexpr uint32_t BYTES_PROCESSED = 16;
simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_escape() { return escape_bits != 0; }
simdjson_inline int escape_index() { return trailing_zeroes(escape_bits) / 4; }
uint64_t escape_bits;
}; // struct escaping
simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v(src);
v.store(dst);
simd8<bool> is_quote = (v == '"');
simd8<bool> is_backslash = (v == '\\');
simd8<bool> is_control = (v < 32);
return {
(is_backslash | is_quote | is_control).to_bitmask64()
};
}
} // unnamed namespace
} // namespace arm64
} // namespace simdjson
+1
View File
@@ -9,6 +9,7 @@
#include "simdjson/compiler_check.h"
#include "simdjson/error.h"
#include "simdjson/portability.h"
#include "simdjson/concepts.h"
/**
* @brief The top level simdjson namespace, containing everything the library provides.
+4
View File
@@ -50,6 +50,8 @@ double from_chars(const char *first, const char* end) noexcept;
#define SIMDJSON_ISALIGNED_N(ptr, n) (((uintptr_t)(ptr) & ((n)-1)) == 0)
#if SIMDJSON_REGULAR_VISUAL_STUDIO
// We could use [[deprecated]] but it requires C++14
#define simdjson_deprecated __declspec(deprecated)
#define simdjson_really_inline __forceinline
#define simdjson_never_inline __declspec(noinline)
@@ -88,6 +90,8 @@ double from_chars(const char *first, const char* end) noexcept;
#define SIMDJSON_POP_DISABLE_UNUSED_WARNINGS
#else // SIMDJSON_REGULAR_VISUAL_STUDIO
// We could use [[deprecated]] but it requires C++14
#define simdjson_deprecated __attribute__((deprecated))
#define simdjson_really_inline inline __attribute__((always_inline))
#define simdjson_never_inline inline __attribute__((noinline))
+113
View File
@@ -0,0 +1,113 @@
#ifndef SIMDJSON_CONCEPTS_H
#define SIMDJSON_CONCEPTS_H
#if SIMDJSON_SUPPORTS_DESERIALIZATION
#include <concepts>
#include <type_traits>
namespace simdjson {
namespace concepts {
namespace details {
#define SIMDJSON_IMPL_CONCEPT(name, method) \
template <typename T> \
concept supports_##name = !std::is_const_v<T> && requires { \
typename std::remove_cvref_t<T>::value_type; \
requires requires(typename std::remove_cvref_t<T>::value_type &&val, \
T obj) { \
obj.method(std::move(val)); \
requires !requires { obj = std::move(val); }; \
}; \
};
SIMDJSON_IMPL_CONCEPT(emplace_back, emplace_back);
SIMDJSON_IMPL_CONCEPT(emplace, emplace);
SIMDJSON_IMPL_CONCEPT(push_back, push_back);
SIMDJSON_IMPL_CONCEPT(add, add);
SIMDJSON_IMPL_CONCEPT(push, push);
SIMDJSON_IMPL_CONCEPT(append, append);
SIMDJSON_IMPL_CONCEPT(insert, insert);
SIMDJSON_IMPL_CONCEPT(op_append, operator+=);
#undef SIMDJSON_IMPL_CONCEPT
} // namespace details
/// Check if T is a container that we can append to, including:
/// std::vector, std::deque, std::list, std::string, ...
template <typename T>
concept appendable_containers =
details::supports_emplace_back<T> || details::supports_emplace<T> ||
details::supports_push_back<T> || details::supports_push<T> ||
details::supports_add<T> || details::supports_append<T> ||
details::supports_insert<T>;
/// Insert into the container however possible
template <appendable_containers T, typename... Args>
constexpr decltype(auto) emplace_one(T &vec, Args &&...args) {
if constexpr (details::supports_emplace_back<T>) {
return vec.emplace_back(std::forward<Args>(args)...);
} else if constexpr (details::supports_emplace<T>) {
return vec.emplace(std::forward<Args>(args)...);
} else if constexpr (details::supports_push_back<T>) {
return vec.push_back(std::forward<Args>(args)...);
} else if constexpr (details::supports_push<T>) {
return vec.push(std::forward<Args>(args)...);
} else if constexpr (details::supports_add<T>) {
return vec.add(std::forward<Args>(args)...);
} else if constexpr (details::supports_append<T>) {
return vec.append(std::forward<Args>(args)...);
} else if constexpr (details::supports_insert<T>) {
return vec.insert(std::forward<Args>(args)...);
} else if constexpr (details::supports_op_append<T> && sizeof...(Args) == 1) {
return vec.operator+=(std::forward<Args>(args)...);
} else {
static_assert(!sizeof(T *),
"We don't know how to add things to this container");
}
}
/// This checks if the container will return a reference to the newly added
/// element after an insert which for example `std::vector::emplace_back` does
/// since C++17; this will allow some optimizations.
template <typename T>
concept returns_reference = appendable_containers<T> && requires {
typename std::remove_cvref_t<T>::reference;
requires requires(typename std::remove_cvref_t<T>::value_type &&val, T obj) {
{
emplace_one(obj, std::move(val))
} -> std::same_as<typename std::remove_cvref_t<T>::reference>;
};
};
template <typename T>
concept smart_pointer = requires(std::remove_cvref_t<T> ptr) {
// Check if T has a member type named element_type
typename std::remove_cvref_t<T>::element_type;
// Check if T has a get() member function
{
ptr.get()
} -> std::same_as<typename std::remove_cvref_t<T>::element_type *>;
// Check if T can be dereferenced
{ *ptr } -> std::same_as<typename std::remove_cvref_t<T>::element_type &>;
};
template <typename T>
concept optional_type = requires(std::remove_cvref_t<T> obj) {
typename std::remove_cvref_t<T>::value_type;
{ obj.value() } -> std::same_as<typename std::remove_cvref_t<T>::value_type&>;
requires requires(typename std::remove_cvref_t<T>::value_type &&val) {
obj.emplace(std::move(val));
obj = std::move(val);
{
obj.value_or(val)
} -> std::convertible_to<typename std::remove_cvref_t<T>::value_type>;
};
{ static_cast<bool>(obj) } -> std::same_as<bool>; // convertible to bool
};
} // namespace concepts
} // namespace simdjson
#endif // SIMDJSON_SUPPORTS_DESERIALIZATION
#endif // SIMDJSON_CONCEPTS_H
+14
View File
@@ -7,6 +7,7 @@
#include "simdjson/dom/array.h"
#include "simdjson/dom/element.h"
#include "simdjson/error-inl.h"
#include "simdjson/jsonpathutil.h"
#include "simdjson/internal/tape_ref-inl.h"
#include <limits>
@@ -44,6 +45,13 @@ inline simdjson_result<dom::element> simdjson_result<dom::array>::at_pointer(std
if (error()) { return error(); }
return first.at_pointer(json_pointer);
}
inline simdjson_result<dom::element> simdjson_result<dom::array>::at_path(std::string_view json_path) const noexcept {
auto json_pointer = json_path_to_pointer_conversion(json_path);
if (json_pointer == "-1") { return INVALID_JSON_POINTER; }
return at_pointer(json_pointer);
}
inline simdjson_result<dom::element> simdjson_result<dom::array>::at(size_t index) const noexcept {
if (error()) { return error(); }
return first.at(index);
@@ -113,6 +121,12 @@ inline simdjson_result<element> array::at_pointer(std::string_view json_pointer)
return child;
}
inline simdjson_result<element> array::at_path(std::string_view json_path) const noexcept {
auto json_pointer = json_path_to_pointer_conversion(json_path);
if (json_pointer == "-1") { return INVALID_JSON_POINTER; }
return at_pointer(json_pointer);
}
inline simdjson_result<element> array::at(size_t index) const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
size_t i=0;
+16
View File
@@ -108,6 +108,21 @@ public:
*/
inline simdjson_result<element> at_pointer(std::string_view json_pointer) const noexcept;
/**
* Get the value associated with the given JSONPath expression. We only support
* JSONPath queries that trivially convertible to JSON Pointer queries: key
* names and array indices.
*
* https://datatracker.ietf.org/doc/html/draft-normington-jsonpath-00
*
* @return The value associated with the given JSONPath expression, or:
* - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails
* - NO_SUCH_FIELD if a field does not exist in an object
* - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length
* - INCORRECT_TYPE if a non-integer is used to access an array
*/
inline simdjson_result<element> at_path(std::string_view json_path) const noexcept;
/**
* Get the value at the given index. This function has linear-time complexity and
* is equivalent to the following:
@@ -152,6 +167,7 @@ public:
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
inline simdjson_result<dom::element> at_pointer(std::string_view json_pointer) const noexcept;
inline simdjson_result<dom::element> at_path(std::string_view json_path) const noexcept;
inline simdjson_result<dom::element> at(size_t index) const noexcept;
#if SIMDJSON_EXCEPTIONS
+11
View File
@@ -9,6 +9,7 @@
#include "simdjson/dom/object-inl.h"
#include "simdjson/error-inl.h"
#include "simdjson/jsonpathutil.h"
#include <ostream>
#include <limits>
@@ -122,6 +123,11 @@ simdjson_inline simdjson_result<dom::element> simdjson_result<dom::element>::at_
if (error()) { return error(); }
return first.at_pointer(json_pointer);
}
simdjson_inline simdjson_result<dom::element> simdjson_result<dom::element>::at_path(const std::string_view json_path) const noexcept {
auto json_pointer = json_path_to_pointer_conversion(json_path);
if (json_pointer == "-1") { return INVALID_JSON_POINTER; }
return at_pointer(json_pointer);
}
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
[[deprecated("For standard compliance, use at_pointer instead, and prefix your pointers with a slash '/', see RFC6901 ")]]
simdjson_inline simdjson_result<dom::element> simdjson_result<dom::element>::at(const std::string_view json_pointer) const noexcept {
@@ -412,6 +418,11 @@ inline simdjson_result<element> element::at_pointer(std::string_view json_pointe
}
}
}
inline simdjson_result<element> element::at_path(std::string_view json_path) const noexcept {
auto json_pointer = json_path_to_pointer_conversion(json_path);
if (json_pointer == "-1") { return INVALID_JSON_POINTER; }
return at_pointer(json_pointer);
}
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
[[deprecated("For standard compliance, use at_pointer instead, and prefix your pointers with a slash '/', see RFC6901 ")]]
inline simdjson_result<element> element::at(std::string_view json_pointer) const noexcept {
+16
View File
@@ -397,6 +397,21 @@ public:
*/
inline simdjson_result<element> at_pointer(const std::string_view json_pointer) const noexcept;
/**
* Get the value associated with the given JSONPath expression. We only support
* JSONPath queries that trivially convertible to JSON Pointer queries: key
* names and array indices.
*
* https://datatracker.ietf.org/doc/html/draft-normington-jsonpath-00
*
* @return The value associated with the given JSONPath expression, or:
* - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails
* - NO_SUCH_FIELD if a field does not exist in an object
* - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length
* - INCORRECT_TYPE if a non-integer is used to access an array
*/
inline simdjson_result<element> at_path(std::string_view json_path) const noexcept;
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
/**
*
@@ -526,6 +541,7 @@ public:
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;
simdjson_inline simdjson_result<dom::element> at_pointer(const std::string_view json_pointer) const noexcept;
simdjson_inline simdjson_result<dom::element> at_path(const std::string_view json_path) const noexcept;
[[deprecated("For standard compliance, use at_pointer instead, and prefix your pointers with a slash '/', see RFC6901 ")]]
simdjson_inline simdjson_result<dom::element> at(const std::string_view json_pointer) const noexcept;
simdjson_inline simdjson_result<dom::element> at(size_t index) const noexcept;
+12
View File
@@ -7,6 +7,7 @@
#include "simdjson/dom/element-inl.h"
#include "simdjson/error-inl.h"
#include "simdjson/jsonpathutil.h"
#include <cstring>
@@ -34,6 +35,11 @@ inline simdjson_result<dom::element> simdjson_result<dom::object>::at_pointer(st
if (error()) { return error(); }
return first.at_pointer(json_pointer);
}
inline simdjson_result<dom::element> simdjson_result<dom::object>::at_path(std::string_view json_path) const noexcept {
auto json_pointer = json_path_to_pointer_conversion(json_path);
if (json_pointer == "-1") { return INVALID_JSON_POINTER; }
return at_pointer(json_pointer);
}
inline simdjson_result<dom::element> simdjson_result<dom::object>::at_key(std::string_view key) const noexcept {
if (error()) { return error(); }
return first.at_key(key);
@@ -131,6 +137,12 @@ inline simdjson_result<element> object::at_pointer(std::string_view json_pointer
return child;
}
inline simdjson_result<element> object::at_path(std::string_view json_path) const noexcept {
auto json_pointer = json_path_to_pointer_conversion(json_path);
if (json_pointer == "-1") { return INVALID_JSON_POINTER; }
return at_pointer(json_pointer);
}
inline simdjson_result<element> object::at_key(std::string_view key) const noexcept {
iterator end_field = end();
for (iterator field = begin(); field != end_field; ++field) {
+16
View File
@@ -171,6 +171,21 @@ public:
*/
inline simdjson_result<element> at_pointer(std::string_view json_pointer) const noexcept;
/**
* Get the value associated with the given JSONPath expression. We only support
* JSONPath queries that trivially convertible to JSON Pointer queries: key
* names and array indices.
*
* https://datatracker.ietf.org/doc/html/draft-normington-jsonpath-00
*
* @return The value associated with the given JSONPath expression, or:
* - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails
* - NO_SUCH_FIELD if a field does not exist in an object
* - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length
* - INCORRECT_TYPE if a non-integer is used to access an array
*/
inline simdjson_result<element> at_path(std::string_view json_path) const noexcept;
/**
* Get the value associated with the given key.
*
@@ -244,6 +259,7 @@ public:
inline simdjson_result<dom::element> operator[](std::string_view key) const noexcept;
inline simdjson_result<dom::element> operator[](const char *key) const noexcept;
inline simdjson_result<dom::element> at_pointer(std::string_view json_pointer) const noexcept;
inline simdjson_result<dom::element> at_path(std::string_view json_path) const noexcept;
inline simdjson_result<dom::element> at_key(std::string_view key) const noexcept;
inline simdjson_result<dom::element> at_key_case_insensitive(std::string_view key) const noexcept;
+6 -1
View File
@@ -549,9 +549,14 @@ public:
/**
* The parser instance can use threads when they are available to speed up some
* operations. It is enabled by default. Changing this attribute will change the
* behavior of the parser for future operations.
* behavior of the parser for future operations. Set to true by default.
*/
bool threaded{true};
#else
/**
* When SIMDJSON_THREADS_ENABLED is not defined, the parser instance cannot use threads.
*/
bool threaded{false};
#endif
/** @private Use the new DOM API instead */
class Iterator;
+2 -1
View File
@@ -49,7 +49,8 @@ enum error_code {
SCALAR_DOCUMENT_AS_VALUE, ///< A scalar document is treated as a value.
OUT_OF_BOUNDS, ///< Attempted to access location outside of document.
TRAILING_CONTENT, ///< Unexpected trailing content in the JSON input
NUM_ERROR_CODES
OUT_OF_CAPACITY, ///< The capacity was exceeded, we cannot allocate enough memory.
NUM_ERROR_CODES ///< Placeholder for end of error code list.
};
/**
@@ -26,6 +26,7 @@ public:
) const noexcept final;
simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final;
simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final;
simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) const noexcept final;
};
} // namespace fallback
@@ -75,6 +75,12 @@ simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t
} // namespace fallback
} // namespace simdjson
#ifndef SIMDJSON_SWAR_NUMBER_PARSING
#if SIMDJSON_IS_BIG_ENDIAN
#define SIMDJSON_SWAR_NUMBER_PARSING 0
#else
#define SIMDJSON_SWAR_NUMBER_PARSING 1
#endif
#endif
#endif // SIMDJSON_FALLBACK_NUMBERPARSING_DEFS_H
@@ -29,6 +29,24 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
return { src[0] };
}
struct escaping {
static constexpr uint32_t BYTES_PROCESSED = 1;
simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_escape() { return escape_bits; }
simdjson_inline int escape_index() { return 0; }
bool escape_bits;
}; // struct escaping
simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
dst[0] = src[0];
return { (src[0] == '\\') || (src[0] == '"') || (src[0] < 32) };
}
} // unnamed namespace
} // namespace fallback
} // namespace simdjson
-1
View File
@@ -16,5 +16,4 @@
#include "simdjson/internal/jsoncharutils_tables.h"
#include "simdjson/internal/numberparsing_tables.h"
#include "simdjson/internal/simdprune_tables.h"
#endif // SIMDJSON_GENERIC_DEPENDENCIES_H
-1
View File
@@ -574,7 +574,6 @@ simdjson_unused simdjson_inline simdjson_result<number_type> get_number_type(con
// Our objective is accurate parsing (ULP of 0) at high speed.
template<typename W>
simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) {
//
// Check for minus sign
//
@@ -45,4 +45,8 @@
#include "simdjson/generic/ondemand/token_iterator-inl.h"
#include "simdjson/generic/ondemand/value_iterator-inl.h"
// JSON builder, ideally they should not be part of the ondemand directory
// but it is convenient for now to have them here.
#include "simdjson/generic/ondemand/json_string_builder.h"
#include "simdjson/generic/ondemand/json_string_builder-inl.h"
+1 -47
View File
@@ -2,6 +2,7 @@
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
#define SIMDJSON_GENERIC_ONDEMAND_ARRAY_INL_H
#include "simdjson/jsonpathutil.h"
#include "simdjson/generic/ondemand/base.h"
#include "simdjson/generic/ondemand/array.h"
#include "simdjson/generic/ondemand/array_iterator-inl.h"
@@ -163,53 +164,6 @@ inline simdjson_result<value> array::at_pointer(std::string_view json_pointer) n
return child;
}
inline std::string json_path_to_pointer_conversion(std::string_view json_path) {
if (json_path.empty() || (json_path.front() != '.' &&
json_path.front() != '[')) {
return "-1"; // This is just a sentinel value, the caller should check for this and return an error.
}
std::string result;
// Reserve space to reduce allocations, adjusting for potential increases due
// to escaping.
result.reserve(json_path.size() * 2);
size_t i = 0;
while (i < json_path.length()) {
if (json_path[i] == '.') {
result += '/';
} else if (json_path[i] == '[') {
result += '/';
++i; // Move past the '['
while (i < json_path.length() && json_path[i] != ']') {
if (json_path[i] == '~') {
result += "~0";
} else if (json_path[i] == '/') {
result += "~1";
} else {
result += json_path[i];
}
++i;
}
if (i == json_path.length() || json_path[i] != ']') {
return "-1"; // Using sentinel value that will be handled as an error by the caller.
}
} else {
if (json_path[i] == '~') {
result += "~0";
} else if (json_path[i] == '/') {
result += "~1";
} else {
result += json_path[i];
}
}
++i;
}
return result;
}
inline simdjson_result<value> array::at_path(std::string_view json_path) noexcept {
auto json_pointer = json_path_to_pointer_conversion(json_path);
if (json_pointer == "-1") { return INVALID_JSON_POINTER; }
@@ -13,5 +13,6 @@
#include "simdjson/padded_string.h"
#include "simdjson/padded_string_view.h"
#include "simdjson/internal/dom_parser_implementation.h"
#include "simdjson/jsonpathutil.h"
#endif // SIMDJSON_GENERIC_ONDEMAND_DEPENDENCIES_H
@@ -90,6 +90,7 @@ concept nothrow_deserializable = nothrow_custom_deserializable<T, ValT> || is_bu
inline constexpr struct deserialize_tag {
using value_type = SIMDJSON_IMPLEMENTATION::ondemand::value;
using document_type = SIMDJSON_IMPLEMENTATION::ondemand::document;
using document_reference_type = SIMDJSON_IMPLEMENTATION::ondemand::document_reference;
// Customization Point for value
template <typename T>
@@ -105,6 +106,14 @@ inline constexpr struct deserialize_tag {
return tag_invoke(*this, object, output);
}
// Customization Point for document reference
template <typename T>
requires custom_deserializable<T, document_reference_type>
[[nodiscard]] constexpr /* error_code */ auto operator()(document_reference_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, document_reference_type>) {
return tag_invoke(*this, object, output);
}
} deserialize{};
} // namespace simdjson
@@ -3,17 +3,15 @@
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
#define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H
#include "simdjson/generic/ondemand/base.h"
#include "simdjson/generic/ondemand/array-inl.h"
#include "simdjson/generic/ondemand/array_iterator.h"
#include "simdjson/generic/ondemand/document.h"
#include "simdjson/generic/ondemand/json_iterator-inl.h"
#include "simdjson/generic/ondemand/json_path_to_pointer_conversion.h"
#include "simdjson/generic/ondemand/json_path_to_pointer_conversion-inl.h"
#include "simdjson/generic/ondemand/json_type.h"
#include "simdjson/generic/ondemand/object-inl.h"
#include "simdjson/generic/ondemand/raw_json_string.h"
#include "simdjson/generic/ondemand/value.h"
#include "simdjson/generic/ondemand/value-inl.h"
#include "simdjson/generic/ondemand/array-inl.h"
#include "simdjson/generic/ondemand/json_iterator-inl.h"
#include "simdjson/generic/ondemand/object-inl.h"
#include "simdjson/generic/ondemand/value_iterator-inl.h"
#include "simdjson/generic/ondemand/deserialize.h"
#endif // SIMDJSON_CONDITIONAL_INCLUDE
@@ -179,10 +177,19 @@ template<> simdjson_inline error_code document::get(int64_t& out) & noexcept { r
template<> simdjson_inline error_code document::get(bool& out) & noexcept { return get_bool().get(out); }
template<> simdjson_inline error_code document::get(value& out) & noexcept { return get_value().get(out); }
template<> simdjson_deprecated simdjson_inline simdjson_result<raw_json_string> document::get() && noexcept { return get_raw_json_string(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<std::string_view> document::get() && noexcept { return get_string(false); }
template<> simdjson_deprecated simdjson_inline simdjson_result<double> document::get() && noexcept { return std::forward<document>(*this).get_double(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<uint64_t> document::get() && noexcept { return std::forward<document>(*this).get_uint64(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<int64_t> document::get() && noexcept { return std::forward<document>(*this).get_int64(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<bool> document::get() && noexcept { return std::forward<document>(*this).get_bool(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<value> document::get() && noexcept { return get_value(); }
#if SIMDJSON_EXCEPTIONS
template <class T>
simdjson_inline document::operator T() noexcept(false) { return get<T>(); }
simdjson_deprecated simdjson_inline document::operator T() && noexcept(false) { return get<T>(); }
template <class T>
simdjson_inline document::operator T() & noexcept(false) { return get<T>(); }
simdjson_inline document::operator array() & noexcept(false) { return get_array(); }
simdjson_inline document::operator object() & noexcept(false) { return get_object(); }
simdjson_inline document::operator uint64_t() noexcept(false) { return get_uint64(); }
@@ -469,7 +476,7 @@ simdjson_inline simdjson_result<T> simdjson_result<SIMDJSON_IMPLEMENTATION::onde
return first.get<T>();
}
template<typename T>
simdjson_inline simdjson_result<T> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::get() && noexcept {
simdjson_deprecated simdjson_inline simdjson_result<T> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::get() && noexcept {
if (error()) { return error(); }
return std::forward<SIMDJSON_IMPLEMENTATION::ondemand::document>(first).get<T>();
}
@@ -485,7 +492,7 @@ simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::do
}
template<> simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::get<SIMDJSON_IMPLEMENTATION::ondemand::document>() & noexcept = delete;
template<> simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::get<SIMDJSON_IMPLEMENTATION::ondemand::document>() && noexcept {
template<> simdjson_deprecated simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::get<SIMDJSON_IMPLEMENTATION::ondemand::document>() && noexcept {
if (error()) { return error(); }
return std::forward<SIMDJSON_IMPLEMENTATION::ondemand::document>(first);
}
@@ -816,6 +823,26 @@ simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::o
if (error()) { return error(); }
return first.is_null();
}
template<typename T>
simdjson_inline simdjson_result<T> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get() & noexcept {
if (error()) { return error(); }
return first.get<T>();
}
template<typename T>
simdjson_inline simdjson_result<T> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get() && noexcept {
if (error()) { return error(); }
return std::forward<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>(first).get<T>();
}
template <class T>
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get(T &out) & noexcept {
if (error()) { return error(); }
return first.get<T>(out);
}
template <class T>
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get(T &out) && noexcept {
if (error()) { return error(); }
return std::forward<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>(first).get<T>(out);
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::json_type> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::type() noexcept {
if (error()) { return error(); }
return first.type();
@@ -828,6 +855,18 @@ simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::o
if (error()) { return error(); }
return first.is_string();
}
template <>
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get(SIMDJSON_IMPLEMENTATION::ondemand::document_reference &out) & noexcept {
if (error()) { return error(); }
out = first;
return SUCCESS;
}
template <>
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get(SIMDJSON_IMPLEMENTATION::ondemand::document_reference &out) && noexcept {
if (error()) { return error(); }
out = first;
return SUCCESS;
}
simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::is_negative() noexcept {
if (error()) { return error(); }
return first.is_negative();
@@ -845,10 +884,12 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::number> simdj
return first.get_number();
}
#if SIMDJSON_EXCEPTIONS
template <class T, typename std::enable_if<std::is_same<T, SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::value == false>::type>
template <class T>
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::operator T() noexcept(false) {
static_assert(std::is_same<T, SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::value == false, "You should not call get<T> when T is a document");
static_assert(std::is_same<T, SIMDJSON_IMPLEMENTATION::ondemand::document>::value == false, "You should not call get<T> when T is a document");
if (error()) { throw simdjson_error(error()); }
return first;
return first.get<T>();
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::operator SIMDJSON_IMPLEMENTATION::ondemand::array() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
+82 -5
View File
@@ -254,7 +254,7 @@ public:
#endif
}
/** @overload template<typename T> error_code get(T &out) & noexcept */
template<typename T> simdjson_inline error_code get(T &out) && noexcept;
template<typename T> simdjson_deprecated simdjson_inline error_code get(T &out) && noexcept;
#if SIMDJSON_EXCEPTIONS
/**
@@ -267,7 +267,10 @@ public:
* @returns An instance of type T
*/
template <class T>
explicit simdjson_inline operator T() noexcept(false);
explicit simdjson_inline operator T() & noexcept(false);
template <class T>
explicit simdjson_deprecated simdjson_inline operator T() && noexcept(false);
/**
* Cast this JSON value to an array.
*
@@ -731,6 +734,11 @@ protected:
/**
* A document_reference is a thin wrapper around a document reference instance.
* The document_reference instances are used primarily/solely for streams of JSON
* documents. They differ from document instances when parsing a scalar value
* (a document that is not an array or an object). In the case of a document,
* we expect the document to be fully consumed. In the case of a document_reference,
* we allow trailing content.
*/
class document_reference {
public:
@@ -756,7 +764,70 @@ public:
simdjson_inline simdjson_result<value> get_value() noexcept;
simdjson_inline simdjson_result<bool> is_null() noexcept;
template<typename T> simdjson_inline simdjson_result<T> get() & noexcept;
template <typename T>
simdjson_inline simdjson_result<T> get() &
#if SIMDJSON_SUPPORTS_DESERIALIZATION
noexcept(custom_deserializable<T, document> ? nothrow_custom_deserializable<T, document> : true)
#else
noexcept
#endif
{
static_assert(std::is_default_constructible<T>::value, "Cannot initialize the specified type.");
T out{};
SIMDJSON_TRY(get<T>(out));
return out;
}
template<typename T>
simdjson_inline simdjson_result<T> get() &&
#if SIMDJSON_SUPPORTS_DESERIALIZATION
noexcept(custom_deserializable<T, document> ? nothrow_custom_deserializable<T, document> : true)
#else
noexcept
#endif
{
static_assert(!std::is_same<T, array>::value && !std::is_same<T, object>::value, "You should never hold either an ondemand::array or ondemand::object without a corresponding ondemand::document_reference being alive; that would be Undefined Behaviour.");
return static_cast<document&>(*this).get<T>();
}
/**
* Get this value as the given type.
*
* Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool, value
*
* Be mindful that the document instance must remain in scope while you are accessing object, array and value instances.
*
* @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized.
* @returns INCORRECT_TYPE If the JSON value is not an object.
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
*/
template<typename T>
simdjson_inline error_code get(T &out) &
#if SIMDJSON_SUPPORTS_DESERIALIZATION
noexcept(custom_deserializable<T, document> ? nothrow_custom_deserializable<T, document_reference> : true)
#else
noexcept
#endif
{
#if SIMDJSON_SUPPORTS_DESERIALIZATION
if constexpr (custom_deserializable<T, document_reference>) {
return deserialize(*this, out);
} else {
#endif // SIMDJSON_SUPPORTS_DESERIALIZATION
// Unless the simdjson library or the user provides an inline implementation, calling this method should
// immediately fail.
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
"The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), "
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template."
" You may also add support for custom types, see our documentation.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
#if SIMDJSON_SUPPORTS_DESERIALIZATION
}
#endif
}
/** @overload template<typename T> error_code get(T &out) & noexcept */
template<typename T> simdjson_inline error_code get(T &out) && noexcept;
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
simdjson_inline operator document&() const noexcept;
#if SIMDJSON_EXCEPTIONS
@@ -833,7 +904,7 @@ public:
simdjson_inline simdjson_result<bool> is_null() noexcept;
template<typename T> simdjson_inline simdjson_result<T> get() & noexcept;
template<typename T> simdjson_inline simdjson_result<T> get() && noexcept;
template<typename T> simdjson_deprecated simdjson_inline simdjson_result<T> get() && noexcept;
template<typename T> simdjson_inline error_code get(T &out) & noexcept;
template<typename T> simdjson_inline error_code get(T &out) && noexcept;
@@ -908,8 +979,14 @@ public:
simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> get_value() noexcept;
simdjson_inline simdjson_result<bool> is_null() noexcept;
template<typename T> simdjson_inline simdjson_result<T> get() & noexcept;
template<typename T> simdjson_inline simdjson_result<T> get() && noexcept;
template<typename T> simdjson_inline error_code get(T &out) & noexcept;
template<typename T> simdjson_inline error_code get(T &out) && noexcept;
#if SIMDJSON_EXCEPTIONS
template <class T, typename std::enable_if<std::is_same<T, SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::value == false>::type>
template <class T>
explicit simdjson_inline operator T() noexcept(false);
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::array() & noexcept(false);
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::object() & noexcept(false);
@@ -152,7 +152,6 @@ simdjson_inline document_stream::iterator::iterator(document_stream* _stream, bo
}
simdjson_inline simdjson_result<ondemand::document_reference> document_stream::iterator::operator*() noexcept {
//if(stream->error) { return stream->error; }
return simdjson_result<ondemand::document_reference>(stream->doc, stream->error);
}
@@ -316,7 +316,7 @@ private:
friend class document;
friend class json_iterator;
friend struct simdjson_result<ondemand::document_stream>;
friend struct internal::simdjson_result_base<ondemand::document_stream>;
friend struct simdjson::internal::simdjson_result_base<ondemand::document_stream>;
}; // document_stream
} // namespace ondemand
@@ -1,67 +0,0 @@
#pragma once
#ifndef SIMDJSON_ONDEMAND_GENERIC_JSON_PATH_TO_POINTER_CONVERSION_INL_H
#define SIMDJSON_ONDEMAND_GENERIC_JSON_PATH_TO_POINTER_CONVERSION_INL_H
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
#define SIMDJSON_GENERIC_ONDEMAND_JSON_PATH_TO_POINTER_CONVERSION_INL_H
#include "simdjson/generic/ondemand/json_path_to_pointer_conversion.h"
#endif // SIMDJSON_CONDITIONAL_INCLUDE
namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {}
namespace ondemand {
simdjson_inline std::string json_path_to_pointer_conversion(std::string_view json_path) {
if (json_path.empty() || (json_path.front() != '.' && json_path.front() != '[') {
return "-1"; // Sentinel value to be handled as an error by the caller.
}
std::string result;
// Reserve space to reduce allocations, adjusting for potential increases due
// to escaping.
result.reserve(json_path.size() * 2);
// Skip the initial '.' as it's assumed every path starts with it.
size_t i = 0;
while (i < json_path.length()) {
if (json_path[i] == '.') {
result += '/';
} else if (json_path[i] == '[') {
result += '/';
++i; // Move past the '['
while (i < json_path.length() && json_path[i] != ']') {
if (json_path[i] == '~') {
result += "~0";
} else if (json_path[i] == '/') {
result += "~1";
} else {
result += json_path[i];
}
++i;
}
if (i == json_path.length() || json_path[i] != ']') {
return "-1"; // Returning sentinel value that will be handled as an error by the caller
}
} else {
if (json_path[i] == '~') {
result += "~0";
} else if (json_path[i] == '/') {
result += "~1";
} else {
result += json_path[i];
}
}
++i;
}
return simdjson_result<std::string>(result);
}
} // namespace ondemand
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
#endif // SIMDJSON_ONDEMAND_GENERIC_JSON_PATH_TO_POINTER_CONVERSION_INL_H
@@ -1,22 +0,0 @@
#pragma once
#ifndef SIMDJSON_ONDEMAND_GENERIC_JSON_PATH_TO_POINTER_CONVERSION_H
#define SIMDJSON_ONDEMAND_GENERIC_JSON_PATH_TO_POINTER_CONVERSION_H
namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
namespace internal {
/**
* Converts JSONPath to JSON Pointer.
* @param json_path The JSONPath string to be converted.
* @return A string containing the equivalent JSON Pointer.
* @throws simdjson_error If the conversion fails.
*/
simdjson_inline std::string json_path_to_pointer_conversion(std::string_view json_path);
} // namespace internal
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
#endif // SIMDJSON_JSON_PATH_TO_POINTER_CONVERSION_H
@@ -0,0 +1,281 @@
/**
* This file is part of the builder API. It is temporarily in the ondemand directory
* but we will move it to a builder directory later.
*/
#include <type_traits>
#ifndef SIMDJSON_GENERIC_BUILDER_INL_H
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
#define SIMDJSON_GENERIC_BUILDER_INL_H
#include "simdjson/generic/builder/json_string_builder.h"
#endif // SIMDJSON_CONDITIONAL_INCLUDE
namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
namespace builder {
simdjson_inline string_builder::string_builder(size_t initial_capacity) :
buffer(new (std::nothrow) char[initial_capacity]),
position(0), capacity(buffer.get() != nullptr ? initial_capacity : 0),
is_valid(buffer.get() != nullptr) {}
simdjson_inline bool string_builder::capacity_check(size_t upcoming_bytes) {
// We use the convention that when is_valid is false, then the capacity and
// the position are 0.
// Most of the time, this function will return true.
if (simdjson_likely(upcoming_bytes <= capacity - position)) { return true; }
// check for overflow, most of the time there is no overflow
if (simdjson_likely(position + upcoming_bytes < position)) { return false; }
// We will rarely get here.
grow_buffer((std::max)(capacity * 2, position + upcoming_bytes));
// If the buffer allocation failed, we set is_valid to false.
return is_valid;
}
simdjson_inline void string_builder::grow_buffer(size_t desired_capacity) {
if (!is_valid) { return; }
std::unique_ptr<char[]> new_buffer(new (std::nothrow) char[desired_capacity]);
if (new_buffer.get() == nullptr) {
set_valid(false);
return;
}
std::memcpy(new_buffer.get(), buffer.get(), position);
buffer.swap(new_buffer);
capacity = desired_capacity;
}
simdjson_inline void string_builder::set_valid(bool valid) noexcept {
if(!valid) {
is_valid = false;
capacity = 0;
position = 0;
buffer.reset();
} else {
is_valid = true;
}
}
simdjson_inline size_t string_builder::size() const noexcept {
return position;
}
simdjson_inline void string_builder::append(char c) noexcept {
if(capacity_check(1)) {
buffer.get()[position++] = c;
}
}
simdjson_inline void string_builder::append_null() noexcept {
constexpr char null_literal[] = "null";
constexpr size_t null_len = sizeof(null_literal) - 1;
if(capacity_check(null_len)) {
std::memcpy(buffer.get() + position, null_literal, null_len);
position += null_len;
}
}
simdjson_inline void string_builder::clear() noexcept {
position = 0;
// if it was invalid, we should try to repair it
if(!is_valid) {
capacity = 0;
buffer.reset();
is_valid = true;
}
}
namespace internal {
// We could specialize further for 32-bit integers.
int int_log2(uint32_t x) { return (63 - leading_zeroes(x | 1)); }
int fast_digit_count_32(uint32_t x) {
static uint64_t table[] = {
4294967296, 8589934582, 8589934582, 8589934582, 12884901788,
12884901788, 12884901788, 17179868184, 17179868184, 17179868184,
21474826480, 21474826480, 21474826480, 21474826480, 25769703776,
25769703776, 25769703776, 30063771072, 30063771072, 30063771072,
34349738368, 34349738368, 34349738368, 34349738368, 38554705664,
38554705664, 38554705664, 41949672960, 41949672960, 41949672960,
42949672960, 42949672960};
return uint32_t((x + table[int_log2(x)]) >> 32);
}
int int_log2(uint64_t x) { return 63 - leading_zeroes(x | 1); }
int digit_count_64(uint64_t x) {
static uint64_t table[] = {9,
99,
999,
9999,
99999,
999999,
9999999,
99999999,
999999999,
9999999999,
99999999999,
999999999999,
9999999999999,
99999999999999,
999999999999999ULL,
9999999999999999ULL,
99999999999999999ULL,
999999999999999999ULL,
9999999999999999999ULL};
int y = (19 * int_log2(x) >> 6);
y += x > table[y];
return y + 1;
}
template<typename number_type,
typename = typename std::enable_if<std::is_unsigned<number_type>::value>::type>
simdjson_inline size_t digit_count(number_type v) noexcept {
static_assert(sizeof(number_type) == 8
|| sizeof(number_type) == 4
|| sizeof(number_type) == 2
|| sizeof(number_type) == 1, "We only support 8-bit, 16-bit, 32-bit and 64-bit numbers");
if (sizeof(number_type) <= 4) {
return fast_digit_count_32(v);
} else {
return digit_count_64(v);
}
}
} // internal
template<typename number_type, typename>
simdjson_inline void string_builder::append(number_type v) noexcept {
static_assert(std::is_same<number_type, bool>::value
|| std::is_integral<number_type>::value || std::is_floating_point<number_type>::value, "Unsupported number type");
// If C++17 is available, we can 'if constexpr' here.
if constexpr (std::is_same<number_type, bool>::value) {
if (v) {
constexpr char true_literal[] = "true";
constexpr size_t true_len = sizeof(true_literal) - 1;
if(capacity_check(true_len)) {
std::memcpy(buffer.get() + position, true_literal, true_len);
position += true_len;
}
} else {
constexpr char false_literal[] = "false";
constexpr size_t false_len = sizeof(false_literal) - 1;
if(capacity_check(false_len)) {
std::memcpy(buffer.get() + position, false_literal, false_len);
position += false_len;
}
}
} else if constexpr (std::is_unsigned<number_type>::value) {
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;
// optimization opportunity: if v is large, we can do better.
while(pv >= 10) {
*write_pointer-- = char('0' + (pv % 10));
pv /= 10;
}
*write_pointer = char('0' + pv);
position += dc;
}
} else if constexpr (std::is_integral<number_type>::value) {
constexpr size_t max_number_size = 20;
if(capacity_check(max_number_size)) {
using unsigned_type = typename std::make_unsigned<number_type>::type;
bool negative = v < 0;
unsigned_type pv = static_cast<unsigned_type>(negative ? -v : v);
size_t dc = internal::digit_count(pv);
if(negative) {
buffer.get()[position++] = '-';
}
char *write_pointer = buffer.get() + position + dc - 1;
// optimization opportunity: if v is large, we can do better.
while(pv >= 10) {
*write_pointer-- = char('0' + (pv % 10));
pv /= 10;
}
*write_pointer = char('0' + pv);
position += dc;
}
} else if constexpr (std::is_floating_point<number_type>::value) {
constexpr size_t max_number_size = 24;
if(capacity_check(max_number_size)) {
// We could specialize for float.
char *end = simdjson::internal::to_chars(buffer.get() + position, nullptr, double(v));
position = end - buffer.get();
}
}
}
simdjson_inline void string_builder::escape_and_append(std::string_view input) noexcept {
// escaping might turn a control character into \x00xx so 6 characters.
if(capacity_check(6 * input.size())) {
position += simdjson::write_string_escaped(input, buffer.get() + position);
}
}
simdjson_inline void string_builder::escape_and_append_with_quotes(std::string_view input) noexcept {
// escaping might turn a control character into \x00xx so 6 characters.
if(capacity_check(2 + 6 * input.size())) {
buffer.get()[position++] = '"';
position += simdjson::write_string_escaped(input, buffer.get() + position);
buffer.get()[position++] = '"';
}
}
simdjson_inline void string_builder::append_raw(const char *c) noexcept {
if(capacity_check(1)) {
buffer.get()[position++] = *c;
}
}
simdjson_inline void string_builder::append_raw(std::string_view input) noexcept {
if(capacity_check(input.size())) {
std::memcpy(buffer.get() + position, input.data(), input.size());
position += input.size();
}
}
simdjson_inline void string_builder::append_raw(const char *str, size_t len) noexcept {
if(capacity_check(len)) {
std::memcpy(buffer.get() + position, str, len);
position += len;
}
}
#if SIMDJSON_EXCEPTIONS
simdjson_inline string_builder::operator std::string() const noexcept(false) {
return std::string(std::string_view());
}
simdjson_inline string_builder::operator std::string_view() const noexcept(false) {
return view();
}
#endif
simdjson_inline simdjson_result<std::string_view> string_builder::view() const noexcept {
if (!is_valid) { return simdjson::OUT_OF_CAPACITY; }
return std::string_view(buffer.get(), position);
}
simdjson_inline simdjson_result<const char *> string_builder::c_str() noexcept {
if(capacity_check(1)) {
buffer.get()[position] = '\0';
return buffer.get();
}
return simdjson::OUT_OF_CAPACITY;
}
simdjson_inline bool string_builder::validate_unicode() const noexcept {
return simdjson::validate_utf8(buffer.get(), position);
}
} // namespace builder
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
#endif // SIMDJSON_GENERIC_BUILDER_INL_H
@@ -0,0 +1,167 @@
/**
* This file is part of the builder API. It is temporarily in the ondemand directory
* but we will move it to a builder directory later.
*/
#ifndef SIMDJSON_GENERIC_BUILDER_H
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
#define SIMDJSON_GENERIC_BUILDER_H
#include "simdjson/generic/implementation_simdjson_result_base.h"
#endif // SIMDJSON_CONDITIONAL_INCLUDE
namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
namespace builder {
/**
* A builder for JSON strings representing documents. This is a low-level
* builder that is not meant to be used directly by end-users. Though it
* supports atomic types (Booleans, strings), it does not support composed
* types (arrays and objects).
*
* Ultimately, this class should support kernel-specific optimizations. E.g.,
* it may make use of SIMD instructions to escape strings faster.
*/
class string_builder {
public:
simdjson_inline string_builder(size_t initial_capacity = 1024);
/**
* Append number (includes Booleans). Booleans are mapped to the strings
* false and true. Numbers are converted to strings abiding by the JSON standard.
* Floating-point numbers are converted to the shortest string that 'correctly'
* represents the number.
*/
template<typename number_type,
typename = typename std::enable_if<std::is_arithmetic<number_type>::value>::type>
simdjson_inline void append(number_type v) noexcept;
/**
* Append character c.
*/
simdjson_inline void append(char c) noexcept;
/**
* Append the string 'null'.
*/
simdjson_inline void append_null() noexcept;
/**
* Clear the content.
*/
simdjson_inline void clear() noexcept;
/**
* Append the std::string_view, after escaping it.
* There is no UTF-8 validation.
*/
simdjson_inline void escape_and_append(std::string_view input) noexcept;
/**
* Append the std::string_view surrounded by double quotes, after escaping it.
* There is no UTF-8 validation.
*/
simdjson_inline void escape_and_append_with_quotes(std::string_view input) noexcept;
/**
* Append the C string directly, without escaping.
* There is no UTF-8 validation.
*/
simdjson_inline void append_raw(const char *c) noexcept;
/**
* Append the std::string_view directly, without escaping.
* There is no UTF-8 validation.
*/
simdjson_inline void append_raw(std::string_view input) noexcept;
/**
* Append len characters from str.
* There is no UTF-8 validation.
*/
simdjson_inline void append_raw(const char *str, size_t len) noexcept;
#if SIMDJSON_EXCEPTIONS
/**
* Creates an std::string from the written JSON buffer.
* Throws if memory allocation failed
*
* The result may not be valid UTF-8 if some of your content was not valid UTF-8.
* Use validate_unicode() to check the content if needed.
*/
simdjson_inline operator std::string() const noexcept(false);
/**
* Creates an std::string_view from the written JSON buffer.
* Throws if memory allocation failed.
*
* The result may not be valid UTF-8 if some of your content was not valid UTF-8.
* Use validate_unicode() to check the content if needed.
*/
simdjson_inline operator std::string_view() const noexcept(false);
#endif
/**
* Returns a view on the written JSON buffer. Returns an error
* if memory allocation failed.
*
* The result may not be valid UTF-8 if some of your content was not valid UTF-8.
* Use validate_unicode() to check the content.
*/
simdjson_inline simdjson_result<std::string_view> view() const noexcept;
/**
* Appends the null character to the buffer and returns
* a pointer to the beginning of the written JSON buffer.
* Returns an error if memory allocation failed.
* The result is null-terminated.
*
* The result may not be valid UTF-8 if some of your content was not valid UTF-8.
* Use validate_unicode() to check the content.
*/
simdjson_inline simdjson_result<const char *> c_str() noexcept;
/**
* Return true if the content is valid UTF-8.
*/
simdjson_inline bool validate_unicode() const noexcept;
/**
* Returns the current size of the written JSON buffer.
* If an error occurred, returns 0.
*/
simdjson_inline size_t size() const noexcept;
private:
/**
* Returns true if we can write at least upcoming_bytes bytes.
* The underlying buffer is reallocated if needed. It is designed
* to be called before writing to the buffer. It should be fast.
*/
simdjson_inline bool capacity_check(size_t upcoming_bytes);
/**
* Grow the buffer to at least desired_capacity bytes.
* If the allocation fails, is_valid is set to false. We expect
* that this function would not be repeatedly called.
*/
simdjson_inline void grow_buffer(size_t desired_capacity);
/**
* We use this helper function to make sure that is_valid is kept consistent.
*/
simdjson_inline void set_valid(bool valid) noexcept;
std::unique_ptr<char[]> buffer{};
size_t position{0};
size_t capacity{0};
bool is_valid{true};
};
}
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_BUILDER_H
+6 -2
View File
@@ -278,8 +278,12 @@ public:
* behavior of the parser for future operations.
*/
bool threaded{true};
#else
/**
* When SIMDJSON_THREADS_ENABLED is not defined, the parser instance cannot use threads.
*/
bool threaded{false};
#endif
/**
* Unescape this JSON string, replacing \\ with \, \n with newline, etc. to a user-provided buffer.
* The result must be valid UTF-8.
@@ -340,7 +344,7 @@ public:
private:
/** @private [for benchmarking access] The implementation to use */
std::unique_ptr<internal::dom_parser_implementation> implementation{};
std::unique_ptr<simdjson::internal::dom_parser_implementation> implementation{};
size_t _capacity{0};
size_t _max_capacity;
size_t _max_depth{DEFAULT_MAX_DEPTH};
@@ -3,24 +3,23 @@
#ifndef SIMDJSON_ONDEMAND_DESERIALIZE_H
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
#define SIMDJSON_ONDEMAND_DESERIALIZE_H
#include "simdjson/generic/ondemand/base.h"
#include "simdjson/generic/ondemand/array.h"
#include "simdjson/generic/ondemand/base.h"
#endif // SIMDJSON_CONDITIONAL_INCLUDE
#include <concepts>
#include <limits>
#include <list>
#include <memory>
#include <string>
#include <vector>
namespace simdjson {
template <typename T>
constexpr bool require_custom_serialization = false;
//////////////////////////////
// Number deserialization
//////////////////////////////
template <std::unsigned_integral T>
requires(!require_custom_serialization<T>)
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
using limits = std::numeric_limits<T>;
@@ -34,6 +33,7 @@ error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
}
template <std::floating_point T>
requires(!require_custom_serialization<T>)
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
double x;
SIMDJSON_TRY(val.get_double().get(x));
@@ -42,6 +42,7 @@ error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
}
template <std::signed_integral T>
requires(!require_custom_serialization<T>)
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
using limits = std::numeric_limits<T>;
@@ -54,66 +55,6 @@ error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
return SUCCESS;
}
//////////////////////////////
// STL list deserialization
//////////////////////////////
template <typename T, typename AllocT, typename ValT>
error_code tag_invoke(deserialize_tag, ValT &val,
std::list<T, AllocT> &out) noexcept(false) {
// For better error messages, don't use these as constraints on
// the tag_invoke CPO.
static_assert(
deserializable<T, ValT>,
"The specified type inside the list must itself be deserializable");
static_assert(
std::is_default_constructible_v<T>,
"The specified type inside the list must default constructible.");
SIMDJSON_IMPLEMENTATION::ondemand::array arr;
SIMDJSON_TRY(val.get_array().get(arr));
for (auto v : arr) {
if (auto const err = v.get<T>().get(out.emplace_back()); err) {
// If an error occurs, the empty element that we just inserted gets
// removed. We're not using a temp variable because if T is a heavy type,
// we want the valid path to be the fast path and the slow path be the
// path that has errors in it.
static_cast<void>(out.pop_back());
return err;
}
}
return SUCCESS;
}
//////////////////////////////
// std::string deserialization
//////////////////////////////
template <typename CharT,
typename TraitsT,
typename AllocT,
typename ValT>
error_code tag_invoke(deserialize_tag, ValT &val, std::basic_string<CharT, TraitsT, AllocT> &out) noexcept(false) {
using string_type = std::basic_string<CharT, TraitsT, AllocT>;
if constexpr (std::same_as<string_type, string_type>) {
SIMDJSON_TRY(val.get_string(out));
} else {
// todo: optimize performance
std::string tmp;
SIMDJSON_TRY(val.get_string(tmp));
for (auto const ch : tmp) {
out.push_back(ch);
}
}
return SUCCESS;
}
//////////////////////////////
// STL Vector deserialization
//////////////////////////////
/**
* STL containers have several constructors including one that takes a single
* size argument. Thus, some compilers (Visual Studio) will not be able to
@@ -121,75 +62,104 @@ error_code tag_invoke(deserialize_tag, ValT &val, std::basic_string<CharT, Trait
* explicitly specify the type of the container as needed: e.g.,
* doc.get<std::vector<int>>().
*/
template <typename T, typename AllocT, typename ValT>
error_code tag_invoke(deserialize_tag, ValT &val,
std::vector<T, AllocT> &out) noexcept(false) {
// For better error messages, don't use these as constraints on
// the tag_invoke CPO.
template <concepts::appendable_containers T, typename ValT>
requires(!require_custom_serialization<T>)
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
using value_type = typename std::remove_cvref_t<T>::value_type;
static_assert(
deserializable<T, ValT>,
"The specified type inside the vector must itself be deserializable");
deserializable<value_type, ValT>,
"The specified type inside the container must itself be deserializable");
static_assert(
std::is_default_constructible_v<T>,
"The specified type inside the vector must default constructible.");
std::is_default_constructible_v<value_type>,
"The specified type inside the container must default constructible.");
SIMDJSON_IMPLEMENTATION::ondemand::array arr;
SIMDJSON_TRY(val.get_array().get(arr));
for (auto v : arr) {
if (auto const err = v.get<T>().get(out.emplace_back()); err) {
// If an error occurs, the empty element that we just inserted gets
// removed. We're not using a temp variable because if T is a heavy type,
// we want the valid path to be the fast path and the slow path be the
// path that has errors in it.
static_cast<void>(out.pop_back());
return err;
if constexpr (concepts::returns_reference<T>) {
if (auto const err = v.get<value_type>().get(concepts::emplace_one(out));
err) {
// If an error occurs, the empty element that we just inserted gets
// removed. We're not using a temp variable because if T is a heavy
// type, we want the valid path to be the fast path and the slow path be
// the path that has errors in it.
if constexpr (requires { out.pop_back(); }) {
static_cast<void>(out.pop_back());
}
return err;
}
} else {
value_type temp;
if (auto const err = v.get<value_type>().get(temp); err) {
return err;
}
concepts::emplace_one(out, std::move(temp));
}
}
return SUCCESS;
}
//////////////////////////////
// std::unique_ptr deserialization
//////////////////////////////
/**
* This CPO (Customization Point Object) will help deserialize into
* `unique_ptr`s.
* smart pointers.
*
* If constructing T is nothrow, this conversion should be nothrow as well since
* we return MEMALLOC if we're not able to allocate memory instead of throwing
* the the error message.
* the error message.
*
* @tparam T The type inside the unique_ptr
* @tparam Deleter The Deleter of the unique_ptr
* @tparam T The type inside the smart pointer
* @tparam ValT document/value type
* @param val document/value
* @param out output unique_ptr
* @param out a reference to the smart pointer
* @return status of the conversion
*/
template <typename T, typename Deleter, typename ValT>
error_code tag_invoke(deserialize_tag, ValT &val,
std::unique_ptr<T, Deleter>
&out) noexcept(nothrow_deserializable<T, ValT>) {
template <concepts::smart_pointer T, typename ValT>
requires(!require_custom_serialization<T>)
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable<typename std::remove_cvref_t<T>::element_type, ValT>) {
using element_type = typename std::remove_cvref_t<T>::element_type;
// For better error messages, don't use these as constraints on
// the tag_invoke CPO.
static_assert(
deserializable<T, ValT>,
deserializable<element_type, ValT>,
"The specified type inside the unique_ptr must itself be deserializable");
static_assert(
std::is_default_constructible_v<T>,
std::is_default_constructible_v<element_type>,
"The specified type inside the unique_ptr must default constructible.");
auto ptr = new (std::nothrow) T();
auto ptr = new (std::nothrow) element_type();
if (ptr == nullptr) {
return MEMALLOC;
}
SIMDJSON_TRY(val.template get<T>(*ptr));
SIMDJSON_TRY(val.template get<element_type>(*ptr));
out.reset(ptr);
return SUCCESS;
}
/**
* This CPO (Customization Point Object) will help deserialize into optional types.
*/
template <concepts::optional_type T, typename ValT>
requires(!require_custom_serialization<T>)
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable<typename std::remove_cvref_t<T>::value_type, ValT>) {
using value_type = typename std::remove_cvref_t<T>::value_type;
static_assert(
deserializable<value_type, ValT>,
"The specified type inside the unique_ptr must itself be deserializable");
static_assert(
std::is_default_constructible_v<value_type>,
"The specified type inside the unique_ptr must default constructible.");
if (!out) {
out.emplace();
}
SIMDJSON_TRY(val.template get<value_type>(out.value()));
return SUCCESS;
}
} // namespace simdjson
#endif // SIMDJSON_ONDEMAND_DESERIALIZE_H
@@ -6,8 +6,6 @@
#include "simdjson/generic/ondemand/array.h"
#include "simdjson/generic/ondemand/array_iterator.h"
#include "simdjson/generic/ondemand/json_iterator.h"
#include "simdjson/generic/ondemand/json_path_to_pointer_conversion.h"
#include "simdjson/generic/ondemand/json_path_to_pointer_conversion-inl.h"
#include "simdjson/generic/ondemand/json_type.h"
#include "simdjson/generic/ondemand/object.h"
#include "simdjson/generic/ondemand/raw_json_string.h"
@@ -6,9 +6,9 @@
#include "simdjson/generic/atomparsing.h"
#include "simdjson/generic/numberparsing.h"
#include "simdjson/generic/ondemand/json_iterator.h"
#include "simdjson/generic/ondemand/value_iterator.h"
#include "simdjson/generic/ondemand/json_type-inl.h"
#include "simdjson/generic/ondemand/raw_json_string-inl.h"
#include "simdjson/generic/ondemand/value_iterator.h"
#endif // SIMDJSON_CONDITIONAL_INCLUDE
namespace simdjson {
@@ -799,6 +799,8 @@ simdjson_inline simdjson_result<bool> value_iterator::is_root_null(bool check_tr
if(result) { // we have something that looks like a null.
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("null");
} else if (json[0] == 'n') {
return incorrect_type_error("Not a null but starts with n");
}
return result;
}
+6
View File
@@ -4,8 +4,14 @@
#include "simdjson/haswell/intrinsics.h"
#if !SIMDJSON_CAN_ALWAYS_RUN_HASWELL
// We enable bmi2 only if LLVM/clang is used, because GCC may not
// make good use of it. See https://github.com/simdjson/simdjson/pull/2243
#if defined(__clang__)
SIMDJSON_TARGET_REGION("avx2,bmi,bmi2,pclmul,lzcnt,popcnt")
#else
SIMDJSON_TARGET_REGION("avx2,bmi,pclmul,lzcnt,popcnt")
#endif
#endif
#include "simdjson/haswell/bitmanipulation.h"
#include "simdjson/haswell/bitmask.h"
@@ -28,6 +28,7 @@ public:
) const noexcept final;
simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final;
simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final;
simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) const noexcept final;
};
} // namespace haswell
@@ -41,6 +41,31 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
};
}
struct escaping {
static constexpr uint32_t BYTES_PROCESSED = 32;
simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_escape() { return escape_bits != 0; }
simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); }
uint64_t escape_bits;
}; // struct escaping
simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v(src);
v.store(dst);
simd8<bool> is_quote = (v == '"');
simd8<bool> is_backslash = (v == '\\');
simd8<bool> is_control = (v < 32);
return {
uint64_t((is_backslash | is_quote | is_control).to_bitmask())
};
}
} // unnamed namespace
} // namespace haswell
} // namespace simdjson
@@ -28,6 +28,7 @@ public:
) const noexcept final;
simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final;
simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final;
simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) const noexcept final;
};
} // namespace icelake
-1
View File
@@ -77,7 +77,6 @@ namespace simd {
friend simdjson_really_inline uint64_t operator==(const simd8<T> lhs, const simd8<T> rhs) {
return _mm512_cmpeq_epi8_mask(lhs, rhs);
}
static const int SIZE = sizeof(base<T>::value);
template<int N=1>
@@ -41,6 +41,35 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
};
}
struct escaping {
static constexpr uint32_t BYTES_PROCESSED = 64;
simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_escape() { return escape_bits != 0; }
simdjson_inline int escape_index() { return trailing_zeroes(uint64_t(escape_bits)); }
__mmask64 escape_bits;
}; // struct escaping
simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v(src);
v.store(dst);
__mmask64 is_quote = _mm512_cmpeq_epi8_mask(v, _mm512_set1_epi8('"'));
__mmask64 is_backslash = _mm512_cmpeq_epi8_mask(v, _mm512_set1_epi8('\\'));
__mmask64 is_control = _mm512_cmplt_epi8_mask(v, _mm512_set1_epi8(32));
return {
(is_backslash | is_quote | is_control)
};
}
} // unnamed namespace
} // namespace icelake
} // namespace simdjson
+17
View File
@@ -26,6 +26,15 @@ simdjson_inline simdjson_warn_unused bool validate_utf8(const std::string_view s
return validate_utf8(sv.data(), sv.size());
}
/**
* Write the string to the output buffer while escaping double-quote, backlash and ascii control characters.
*
* @param input the string_view to escape
* @param out output buffer (for escaped string): to be safe, it should have 6 * input.size() allocated bytes.
* @return number of bytes written
*/
simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) noexcept;
/**
* Validate the UTF-8 string.
*
@@ -127,6 +136,14 @@ public:
*/
simdjson_warn_unused virtual bool validate_utf8(const char *buf, size_t len) const noexcept = 0;
/**
* Write the string to the output buffer while escaping double-quote, backlash and ascii control characters.
*
* @param input the string_view to escape
* @param out output buffer (for escaped string): to be safe, it should have 6 * input.size() allocated bytes.
* @return number of bytes written
*/
simdjson_warn_unused virtual size_t write_string_escaped(const std::string_view input, char *out) const noexcept = 0;
protected:
/** @private Construct an implementation with the given name and description. For subclasses. */
simdjson_inline implementation(
+64
View File
@@ -0,0 +1,64 @@
#ifndef SIMDJSON_JSONPATHUTIL_H
#define SIMDJSON_JSONPATHUTIL_H
#include <string>
#include <string_view>
namespace simdjson {
/**
* Converts JSONPath to JSON Pointer.
* @param json_path The JSONPath string to be converted.
* @return A string containing the equivalent JSON Pointer.
*/
inline std::string json_path_to_pointer_conversion(std::string_view json_path) {
size_t i = 0;
// if JSONPath starts with $, skip it
if (!json_path.empty() && json_path.front() == '$') {
i = 1;
}
if (json_path.empty() || (json_path[i] != '.' &&
json_path[i] != '[')) {
return "-1"; // This is just a sentinel value, the caller should check for this and return an error.
}
std::string result;
// Reserve space to reduce allocations, adjusting for potential increases due
// to escaping.
result.reserve(json_path.size() * 2);
while (i < json_path.length()) {
if (json_path[i] == '.') {
result += '/';
} else if (json_path[i] == '[') {
result += '/';
++i; // Move past the '['
while (i < json_path.length() && json_path[i] != ']') {
if (json_path[i] == '~') {
result += "~0";
} else if (json_path[i] == '/') {
result += "~1";
} else {
result += json_path[i];
}
++i;
}
if (i == json_path.length() || json_path[i] != ']') {
return "-1"; // Using sentinel value that will be handled as an error by the caller.
}
} else {
if (json_path[i] == '~') {
result += "~0";
} else if (json_path[i] == '/') {
result += "~1";
} else {
result += json_path[i];
}
}
++i;
}
return result;
}
} // namespace simdjson
#endif // SIMDJSON_JSONPATHUTIL_H
+1
View File
@@ -23,6 +23,7 @@ public:
) const noexcept final;
simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final;
simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final;
simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) const noexcept final;
};
} // namespace lasx
@@ -36,6 +36,12 @@ simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t
} // namespace lasx
} // namespace simdjson
#ifndef SIMDJSON_SWAR_NUMBER_PARSING
#if SIMDJSON_IS_BIG_ENDIAN
#define SIMDJSON_SWAR_NUMBER_PARSING 0
#else
#define SIMDJSON_SWAR_NUMBER_PARSING 1
#endif
#endif
#endif // SIMDJSON_LASX_NUMBERPARSING_DEFS_H
@@ -40,6 +40,31 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
};
}
struct escaping {
static constexpr uint32_t BYTES_PROCESSED = 16;
simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_escape() { return escape_bits != 0; }
simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); }
uint64_t escape_bits;
}; // struct escaping
simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v(src);
v.store(dst);
simd8<bool> is_quote = (v == '"');
simd8<bool> is_backslash = (v == '\\');
simd8<bool> is_control = (v < 32);
return {
(is_backslash | is_quote | is_control).to_bitmask()
};
}
} // unnamed namespace
} // namespace lasx
} // namespace simdjson
+1
View File
@@ -23,6 +23,7 @@ public:
) const noexcept final;
simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final;
simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final;
simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) const noexcept final;
};
} // namespace lsx
@@ -36,6 +36,12 @@ simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t
} // namespace lsx
} // namespace simdjson
#ifndef SIMDJSON_SWAR_NUMBER_PARSING
#if SIMDJSON_IS_BIG_ENDIAN
#define SIMDJSON_SWAR_NUMBER_PARSING 0
#else
#define SIMDJSON_SWAR_NUMBER_PARSING 1
#endif
#endif
#endif // SIMDJSON_LSX_NUMBERPARSING_DEFS_H
+25
View File
@@ -46,6 +46,31 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
};
}
struct escaping {
static constexpr uint32_t BYTES_PROCESSED = 16;
simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_escape() { return escape_bits != 0; }
simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); }
uint64_t escape_bits;
}; // struct escaping
simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v(src);
v.store(dst);
simd8<bool> is_quote = (v == '"');
simd8<bool> is_backslash = (v == '\\');
simd8<bool> is_control = (v < 32);
return {
(is_backslash | is_quote | is_control).to_bitmask()
};
}
} // unnamed namespace
} // namespace lsx
} // namespace simdjson
+4
View File
@@ -8,6 +8,10 @@ namespace simdjson {
* @copydoc simdjson::builtin::ondemand
*/
namespace ondemand = builtin::ondemand;
/**
* @copydoc simdjson::builtin::builder
*/
namespace builder = builtin::builder;
} // namespace simdjson
#endif // SIMDJSON_ONDEMAND_H
+9 -1
View File
@@ -53,6 +53,9 @@ inline padded_string::padded_string(const char *data, size_t length) noexcept
if ((data != nullptr) && (data_ptr != nullptr)) {
std::memcpy(data_ptr, data, length);
}
if (data_ptr == nullptr) {
viable_size = 0;
}
}
#ifdef __cpp_char8_t
inline padded_string::padded_string(const char8_t *data, size_t length) noexcept
@@ -60,12 +63,17 @@ inline padded_string::padded_string(const char8_t *data, size_t length) noexcept
if ((data != nullptr) && (data_ptr != nullptr)) {
std::memcpy(data_ptr, reinterpret_cast<const char *>(data), length);
}
if (data_ptr == nullptr) {
viable_size = 0;
}
}
#endif
// note: do not pass std::string arguments by value
inline padded_string::padded_string(const std::string & str_ ) noexcept
: viable_size(str_.size()), data_ptr(internal::allocate_padded_buffer(str_.size())) {
if (data_ptr != nullptr) {
if (data_ptr == nullptr) {
viable_size = 0;
} else {
std::memcpy(data_ptr, str_.data(), str_.size());
}
}
+39
View File
@@ -201,4 +201,43 @@ using std::size_t;
#endif
#if defined __BYTE_ORDER__ && defined __ORDER_BIG_ENDIAN__
#define SIMDJSON_IS_BIG_ENDIAN (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
#elif defined _WIN32
#define SIMDJSON_IS_BIG_ENDIAN 0
#else
#if defined(__APPLE__) || defined(__FreeBSD__)
#include <machine/endian.h>
#elif defined(sun) || defined(__sun)
#include <sys/byteorder.h>
#elif defined(__MVS__)
#include <sys/endian.h>
#else
#ifdef __has_include
#if __has_include(<endian.h>)
#include <endian.h>
#endif //__has_include(<endian.h>)
#endif //__has_include
#endif
#
#ifndef __BYTE_ORDER__
// safe choice
#define SIMDJSON_IS_BIG_ENDIAN 0
#endif
#
#ifndef __ORDER_LITTLE_ENDIAN__
// safe choice
#define SIMDJSON_IS_BIG_ENDIAN 0
#endif
#
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define SIMDJSON_IS_BIG_ENDIAN 0
#else
#define SIMDJSON_IS_BIG_ENDIAN 1
#endif
#endif
#endif // SIMDJSON_PORTABILITY_H
+1
View File
@@ -32,6 +32,7 @@ public:
size_t &dst_len) const noexcept final;
simdjson_warn_unused bool validate_utf8(const char *buf,
size_t len) const noexcept final;
simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) const noexcept final;
};
} // namespace ppc64
@@ -60,6 +60,12 @@ simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t
} // namespace ppc64
} // namespace simdjson
#ifndef SIMDJSON_SWAR_NUMBER_PARSING
#if SIMDJSON_IS_BIG_ENDIAN
#define SIMDJSON_SWAR_NUMBER_PARSING 0
#else
#define SIMDJSON_SWAR_NUMBER_PARSING 1
#endif
#endif
#endif // SIMDJSON_PPC64_NUMBERPARSING_DEFS_H
@@ -58,6 +58,31 @@ backslash_and_quote::copy_and_find(const uint8_t *src, uint8_t *dst) {
};
}
struct escaping {
static constexpr uint32_t BYTES_PROCESSED = 16;
simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_escape() { return escape_bits != 0; }
simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); }
uint64_t escape_bits;
}; // struct escaping
simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v(src);
v.store(dst);
simd8<bool> is_quote = (v == '"');
simd8<bool> is_backslash = (v == '\\');
simd8<bool> is_control = (v < 32);
return {
(is_backslash | is_quote | is_control).to_bitmask()
};
}
} // unnamed namespace
} // namespace ppc64
} // namespace simdjson
+2 -2
View File
@@ -4,7 +4,7 @@
#define SIMDJSON_SIMDJSON_VERSION_H
/** The version of simdjson being used (major.minor.revision) */
#define SIMDJSON_VERSION "3.10.0"
#define SIMDJSON_VERSION "3.10.1"
namespace simdjson {
enum {
@@ -19,7 +19,7 @@ enum {
/**
* The revision (major.minor.REVISION) of simdjson being used.
*/
SIMDJSON_VERSION_REVISION = 0
SIMDJSON_VERSION_REVISION = 1
};
} // namespace simdjson
@@ -24,6 +24,7 @@ public:
) const noexcept final;
simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final;
simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final;
simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) const noexcept final;
};
} // namespace westmere
@@ -40,6 +40,31 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
};
}
struct escaping {
static constexpr uint32_t BYTES_PROCESSED = 16;
simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_escape() { return escape_bits != 0; }
simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); }
uint64_t escape_bits;
}; // struct escaping
simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v(src);
v.store(dst);
simd8<bool> is_quote = (v == '"');
simd8<bool> is_backslash = (v == '\\');
simd8<bool> is_control = (v < 32);
return {
uint64_t((is_backslash | is_quote | is_control).to_bitmask())
};
}
} // unnamed namespace
} // namespace westmere
} // namespace simdjson
+67 -16
View File
@@ -10,12 +10,41 @@ import os
import re
import shutil
import datetime
import json
from typing import Dict, List, Optional, Set, TextIO, Union, cast
# 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")
sys.exit(1)
rules = """
We refer your to the HACKING.md file for more information on how the project is organized.
To help understand the error, here are the rules for including files in simdjson:
All implementation-specific files, including arm64.h, arm64/implementation.h and
arm64/ondemand.h, must be within SIMDJSON_CONDITIONAL_INCLUDE blocks.
Top-level headers must not be included in any SIMDJSON_CONDITIONAL_INCLUDE block.
Generic files must be included only in amalgamator files (arm64.h,
arm64/implementation.h, arm64/ondemand.h, generic/amalgamated.h).
We fail if an implementation-specific file is included more than once in the same block.
We fail if a generic file is included more than once per implementation in the same block.
Tip: generally, "file" will search the including file's source directory first, then
the search paths while <file> does it the other way around.
We prefer to use <> in simdjson headers to avoid accidentally including a file from the
wrong directory.
The amalgamate.py script checks that all files are included.
"""
SCRIPTPATH = os.path.dirname(os.path.abspath(sys.argv[0]))
PROJECTPATH = os.path.dirname(SCRIPTPATH)
print(f"SCRIPTPATH={SCRIPTPATH} PROJECTPATH={PROJECTPATH}")
@@ -62,6 +91,22 @@ class SimdjsonFile:
def __str__(self):
return self.include_path
def dump(self):
return {
'root': self.root,
'include_path': self.include_path,
'includes': [include.include_path for include in self.includes],
'included_from': [included_from.include_path for included_from in self.included_from],
'editor_only_includes': [editor_only_include.include_path for editor_only_include in self.editor_only_includes],
'editor_only_included_from': [editor_only_included_from.include_path for editor_only_included_from in self.editor_only_included_from],
'processed': self.processed,
'dependency_file': self.dependency_file.include_path if self.dependency_file else None,
'is_amalgamator': self.is_amalgamator,
'implementation': self.implementation,
}
def json(self):
return json.dumps(self.dump(), indent=4, sort_keys=True, ensure_ascii=False)
def __repr__(self):
return self.include_path
@@ -162,20 +207,21 @@ class SimdjsonFile:
def add_include(self, include: 'SimdjsonFile'):
if self.is_conditional_include:
assert include.is_conditional_include, f"{self} cannot include {include} without #ifndef SIMDJSON_CONDITIONAL_INCLUDE."
# If I have a dependency file, I can only include something that has a dependency file.
assert include.is_conditional_include, f"{self} cannot include {include} without #ifndef SIMDJSON_CONDITIONAL_INCLUDE. {rules}"
# TODO make sure we only include amalgamated files that are guaranteed to be included with us (or before us)
# if include.amalgamator_file:
# assert include.amalgamator_file == self, f"{self} cannot include {include}: it should be included from {include.amalgamator_file} instead."
else:
assert include.is_amalgamator or not include.is_conditional_include, f"{self} cannot include {include} because it is an amalgamated file."
assert include.is_amalgamator or not include.is_conditional_include, f"{self} cannot include {include} because it is an amalgamated file. {rules}"
self.includes.append(include)
include.included_from.add(self)
def add_editor_only_include(self, include: 'SimdjsonFile'):
assert self.is_conditional_include, f"Cannot use #ifndef SIMDJSON_CONDITIONAL_INCLUDE in {self} because it is not an amalgamated file."
assert self.is_conditional_include, f"Cannot use #ifndef SIMDJSON_CONDITIONAL_INCLUDE in {self} because it is not an amalgamated file. {rules}"
if not include.is_conditional_include:
assert self.dependency_file, f"{self} cannot include {include} without #ifndef SIMDJSON_CONDITIONAL_INCLUDE."
assert self.dependency_file, f"{self} cannot include {include} without #ifndef SIMDJSON_CONDITIONAL_INCLUDE. {rules}"
# TODO make sure we only include amalgamated files that are guaranteed to be included with us (or before us)
# elif include.amalgamator_file:
# assert self.is_amalgamated_before(self.amalgamator_file), f"{self} cannot include {include}: it should be included from {include.amalgamator_file} instead."
@@ -190,11 +236,11 @@ class SimdjsonFile:
if file.dependency_file == self:
for editor_only_include in file.editor_only_includes:
if not editor_only_include.is_conditional_include:
assert editor_only_include in self.includes, f"{file} includes {editor_only_include}, but it is not included from {self}. It must be added to {self}."
assert editor_only_include in self.includes, f"{file} includes {editor_only_include}, but it is not included from {self}. It must be added to {self}. {rules}"
if editor_only_include in extra_include_set:
extra_include_set.remove(editor_only_include)
assert len(extra_include_set) == 0, f"{self} unnecessarily includes {extra_include_set}. They are not included in the corresponding amalgamated files."
assert len(extra_include_set) == 0, f"{self} unnecessarily includes {extra_include_set}. They are not included in the corresponding amalgamated files. {rules}"
class SimdjsonRepository:
def __init__(self, project_path: str, relative_roots: List[RelativeRoot]):
@@ -320,14 +366,19 @@ class Amalgamator:
assert not self.editor_only_region
with open(file.absolute_path, 'r') as fid2:
print(f"including: {file}")
for line in fid2:
line = line.rstrip('\n')
# Ignore #pragma once, it causes warnings if it ends up in a .cpp file
if re.search(r'^#pragma once$', line):
continue
# Ignore lines inside #ifndef SIMDJSON_CONDITIONAL_INCLUDE
if re.search(r'^#ifndef\s+SIMDJSON_CONDITIONAL_INCLUDE\s*$', line):
assert file.is_conditional_include, f"{file} uses #ifndef SIMDJSON_CONDITIONAL_INCLUDE but is not an amalgamated file!"
assert self.in_conditional_include_block, f"{file} uses #ifndef SIMDJSON_CONDITIONAL_INCLUDE without a prior #define SIMDJSON_CONDITIONAL_INCLUDE: {self.include_stack}"
assert not self.editor_only_region, f"{file} uses #ifndef SIMDJSON_CONDITIONAL_INCLUDE twice in a row"
assert file.is_conditional_include, f"{file} uses #ifndef SIMDJSON_CONDITIONAL_INCLUDE but is not an amalgamated file! {rules}"
assert self.in_conditional_include_block, f"{file} uses #ifndef SIMDJSON_CONDITIONAL_INCLUDE without a prior #define SIMDJSON_CONDITIONAL_INCLUDE: {self.include_stack} {rules}"
assert not self.editor_only_region, f"{file} uses #ifndef SIMDJSON_CONDITIONAL_INCLUDE twice in a row {rules}"
self.editor_only_region = True
# Handle ignored lines (and ending ignore blocks)
@@ -345,7 +396,7 @@ class Amalgamator:
self.editor_only_region = False
continue
assert not end_ignore, f"{file} has #endif // SIMDJSON_CONDITIONAL_INCLUDE without #ifndef SIMDJSON_CONDITIONAL_INCLUDE"
assert not end_ignore, f"{file} has #endif // SIMDJSON_CONDITIONAL_INCLUDE without #ifndef SIMDJSON_CONDITIONAL_INCLUDE {rules}"
# Handle #include lines
included = re.search(r'^#include\s+["<]([^">]*)[">]', line)
@@ -372,26 +423,26 @@ class Amalgamator:
self.implementation = None
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"Use of SIMDJSON_IMPLEMENTATION while not defined in {file}: {line}"
assert self.implementation, f"Use of SIMDJSON_IMPLEMENTATION while not defined in {file}: {line}\n{rules}"
line = re.sub(r'\bSIMDJSON_IMPLEMENTATION\b',self.implementation,line)
# Handle defining and undefining SIMDJSON_CONDITIONAL_INCLUDE
defined = re.search(r'^#define\s+SIMDJSON_CONDITIONAL_INCLUDE\s*$', line)
if defined:
assert not file.is_conditional_include, "SIMDJSON_CONDITIONAL_INCLUDE defined in amalgamated file {file}! Not allowed."
assert not self.in_conditional_include_block, f"{file} redefines SIMDJSON_CONDITIONAL_INCLUDE"
assert not file.is_conditional_include, "SIMDJSON_CONDITIONAL_INCLUDE defined in amalgamated file {file}! Not allowed. {rules}"
assert not self.in_conditional_include_block, f"{file} redefines SIMDJSON_CONDITIONAL_INCLUDE {rules}"
self.in_conditional_include_block = True
self.found_includes_per_conditional_block.clear()
self.write(f'/* defining SIMDJSON_CONDITIONAL_INCLUDE */')
elif re.search(r'^#undef\s+SIMDJSON_CONDITIONAL_INCLUDE\s*$', line):
assert not file.is_conditional_include, "SIMDJSON_CONDITIONAL_INCLUDE undefined in amalgamated file {file}! Not allowed."
assert self.in_conditional_include_block, f"{file} undefines SIMDJSON_CONDITIONAL_INCLUDE without defining it"
assert not file.is_conditional_include, "SIMDJSON_CONDITIONAL_INCLUDE undefined in amalgamated file {file}! Not allowed. {rules}"
assert self.in_conditional_include_block, f"{file} undefines SIMDJSON_CONDITIONAL_INCLUDE without defining it {rules}"
self.write(f'/* undefining SIMDJSON_CONDITIONAL_INCLUDE */')
self.in_conditional_include_block = False
self.write(line)
assert not self.editor_only_region, f"{file} ended without #endif // SIMDJSON_CONDITIONAL_INCLUDE"
assert not self.editor_only_region, f"{file} ended without #endif // SIMDJSON_CONDITIONAL_INCLUDE {rules}"
self.write(f"/* end file {self.file_to_str(file)} */")
+246 -12
View File
@@ -1,4 +1,4 @@
/* auto-generated on 2024-07-15 08:51:57 -0400. Do not edit! */
/* auto-generated on 2024-10-28 20:27:26 -0400. Do not edit! */
/* including simdjson.cpp: */
/* begin file simdjson.cpp */
#define SIMDJSON_SRC_SIMDJSON_CPP
@@ -296,6 +296,45 @@ using std::size_t;
#endif
#if defined __BYTE_ORDER__ && defined __ORDER_BIG_ENDIAN__
#define SIMDJSON_IS_BIG_ENDIAN (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
#elif defined _WIN32
#define SIMDJSON_IS_BIG_ENDIAN 0
#else
#if defined(__APPLE__) || defined(__FreeBSD__)
#include <machine/endian.h>
#elif defined(sun) || defined(__sun)
#include <sys/byteorder.h>
#elif defined(__MVS__)
#include <sys/endian.h>
#else
#ifdef __has_include
#if __has_include(<endian.h>)
#include <endian.h>
#endif //__has_include(<endian.h>)
#endif //__has_include
#endif
#
#ifndef __BYTE_ORDER__
// safe choice
#define SIMDJSON_IS_BIG_ENDIAN 0
#endif
#
#ifndef __ORDER_LITTLE_ENDIAN__
// safe choice
#define SIMDJSON_IS_BIG_ENDIAN 0
#endif
#
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define SIMDJSON_IS_BIG_ENDIAN 0
#else
#define SIMDJSON_IS_BIG_ENDIAN 1
#endif
#endif
#endif // SIMDJSON_PORTABILITY_H
/* end file simdjson/portability.h */
@@ -344,6 +383,8 @@ double from_chars(const char *first, const char* end) noexcept;
#define SIMDJSON_ISALIGNED_N(ptr, n) (((uintptr_t)(ptr) & ((n)-1)) == 0)
#if SIMDJSON_REGULAR_VISUAL_STUDIO
// We could use [[deprecated]] but it requires C++14
#define simdjson_deprecated __declspec(deprecated)
#define simdjson_really_inline __forceinline
#define simdjson_never_inline __declspec(noinline)
@@ -382,6 +423,8 @@ double from_chars(const char *first, const char* end) noexcept;
#define SIMDJSON_POP_DISABLE_UNUSED_WARNINGS
#else // SIMDJSON_REGULAR_VISUAL_STUDIO
// We could use [[deprecated]] but it requires C++14
#define simdjson_deprecated __attribute__((deprecated))
#define simdjson_really_inline inline __attribute__((always_inline))
#define simdjson_never_inline inline __attribute__((noinline))
@@ -557,7 +600,6 @@ SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#pragma once
#ifndef NONSTD_SV_LITE_H_INCLUDED
#define NONSTD_SV_LITE_H_INCLUDED
@@ -2401,7 +2443,8 @@ enum error_code {
SCALAR_DOCUMENT_AS_VALUE, ///< A scalar document is treated as a value.
OUT_OF_BOUNDS, ///< Attempted to access location outside of document.
TRAILING_CONTENT, ///< Unexpected trailing content in the JSON input
NUM_ERROR_CODES
OUT_OF_CAPACITY, ///< The capacity was exceeded, we cannot allocate enough memory.
NUM_ERROR_CODES ///< Placeholder for end of error code list.
};
/**
@@ -2670,6 +2713,122 @@ inline const std::string error_message(int error) noexcept;
#endif // SIMDJSON_ERROR_H
/* end file simdjson/error.h */
/* skipped duplicate #include "simdjson/portability.h" */
/* including simdjson/concepts.h: #include "simdjson/concepts.h" */
/* begin file simdjson/concepts.h */
#ifndef SIMDJSON_CONCEPTS_H
#define SIMDJSON_CONCEPTS_H
#if SIMDJSON_SUPPORTS_DESERIALIZATION
#include <concepts>
#include <type_traits>
namespace simdjson {
namespace concepts {
namespace details {
#define SIMDJSON_IMPL_CONCEPT(name, method) \
template <typename T> \
concept supports_##name = !std::is_const_v<T> && requires { \
typename std::remove_cvref_t<T>::value_type; \
requires requires(typename std::remove_cvref_t<T>::value_type &&val, \
T obj) { \
obj.method(std::move(val)); \
requires !requires { obj = std::move(val); }; \
}; \
};
SIMDJSON_IMPL_CONCEPT(emplace_back, emplace_back);
SIMDJSON_IMPL_CONCEPT(emplace, emplace);
SIMDJSON_IMPL_CONCEPT(push_back, push_back);
SIMDJSON_IMPL_CONCEPT(add, add);
SIMDJSON_IMPL_CONCEPT(push, push);
SIMDJSON_IMPL_CONCEPT(append, append);
SIMDJSON_IMPL_CONCEPT(insert, insert);
SIMDJSON_IMPL_CONCEPT(op_append, operator+=);
#undef SIMDJSON_IMPL_CONCEPT
} // namespace details
/// Check if T is a container that we can append to, including:
/// std::vector, std::deque, std::list, std::string, ...
template <typename T>
concept appendable_containers =
details::supports_emplace_back<T> || details::supports_emplace<T> ||
details::supports_push_back<T> || details::supports_push<T> ||
details::supports_add<T> || details::supports_append<T> ||
details::supports_insert<T>;
/// Insert into the container however possible
template <appendable_containers T, typename... Args>
constexpr decltype(auto) emplace_one(T &vec, Args &&...args) {
if constexpr (details::supports_emplace_back<T>) {
return vec.emplace_back(std::forward<Args>(args)...);
} else if constexpr (details::supports_emplace<T>) {
return vec.emplace(std::forward<Args>(args)...);
} else if constexpr (details::supports_push_back<T>) {
return vec.push_back(std::forward<Args>(args)...);
} else if constexpr (details::supports_push<T>) {
return vec.push(std::forward<Args>(args)...);
} else if constexpr (details::supports_add<T>) {
return vec.add(std::forward<Args>(args)...);
} else if constexpr (details::supports_append<T>) {
return vec.append(std::forward<Args>(args)...);
} else if constexpr (details::supports_insert<T>) {
return vec.insert(std::forward<Args>(args)...);
} else if constexpr (details::supports_op_append<T> && sizeof...(Args) == 1) {
return vec.operator+=(std::forward<Args>(args)...);
} else {
static_assert(!sizeof(T *),
"We don't know how to add things to this container");
}
}
/// This checks if the container will return a reference to the newly added
/// element after an insert which for example `std::vector::emplace_back` does
/// since C++17; this will allow some optimizations.
template <typename T>
concept returns_reference = appendable_containers<T> && requires {
typename std::remove_cvref_t<T>::reference;
requires requires(typename std::remove_cvref_t<T>::value_type &&val, T obj) {
{
emplace_one(obj, std::move(val))
} -> std::same_as<typename std::remove_cvref_t<T>::reference>;
};
};
template <typename T>
concept smart_pointer = requires(std::remove_cvref_t<T> ptr) {
// Check if T has a member type named element_type
typename std::remove_cvref_t<T>::element_type;
// Check if T has a get() member function
{
ptr.get()
} -> std::same_as<typename std::remove_cvref_t<T>::element_type *>;
// Check if T can be dereferenced
{ *ptr } -> std::same_as<typename std::remove_cvref_t<T>::element_type &>;
};
template <typename T>
concept optional_type = requires(std::remove_cvref_t<T> obj) {
typename std::remove_cvref_t<T>::value_type;
{ obj.value() } -> std::same_as<typename std::remove_cvref_t<T>::value_type&>;
requires requires(typename std::remove_cvref_t<T>::value_type &&val) {
obj.emplace(std::move(val));
obj = std::move(val);
{
obj.value_or(val)
} -> std::convertible_to<typename std::remove_cvref_t<T>::value_type>;
};
{ static_cast<bool>(obj) } -> std::same_as<bool>; // convertible to bool
};
} // namespace concepts
} // namespace simdjson
#endif // SIMDJSON_SUPPORTS_DESERIALIZATION
#endif // SIMDJSON_CONCEPTS_H
/* end file simdjson/concepts.h */
/**
* @brief The top level simdjson namespace, containing everything the library provides.
@@ -6455,7 +6614,6 @@ extern SIMDJSON_DLLIMPORTEXPORT const uint64_t thintable_epi8[256];
#endif // SIMDJSON_INTERNAL_SIMDPRUNE_TABLES_H
/* end file simdjson/internal/simdprune_tables.h */
#endif // SIMDJSON_GENERIC_DEPENDENCIES_H
/* end file simdjson/generic/dependencies.h */
/* including generic/dependencies.h: #include <generic/dependencies.h> */
@@ -7647,6 +7805,12 @@ simdjson_inline uint64_t clear_lowest_bit(uint64_t input_num) {
return input_num & (input_num-1);
}
// We sometimes call leading_zeroes on inputs that are zero,
// but the algorithms do not end up using the returned value.
// Sadly, sanitizers are not smart enough to figure it out.
// Applies only when SIMDJSON_PREFER_REVERSE_BITS is defined and true.
// (See below.)
SIMDJSON_NO_SANITIZE_UNDEFINED
/* result might be undefined when input_num is zero */
simdjson_inline int leading_zeroes(uint64_t input_num) {
#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO
@@ -7819,7 +7983,13 @@ simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t
} // namespace arm64
} // namespace simdjson
#ifndef SIMDJSON_SWAR_NUMBER_PARSING
#if SIMDJSON_IS_BIG_ENDIAN
#define SIMDJSON_SWAR_NUMBER_PARSING 0
#else
#define SIMDJSON_SWAR_NUMBER_PARSING 1
#endif
#endif
#endif // SIMDJSON_ARM64_NUMBERPARSING_DEFS_H
/* end file simdjson/arm64/numberparsing_defs.h */
@@ -9436,7 +9606,6 @@ simdjson_unused simdjson_inline simdjson_result<number_type> get_number_type(con
// Our objective is accurate parsing (ULP of 0) at high speed.
template<typename W>
simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) {
//
// Check for minus sign
//
@@ -10403,6 +10572,12 @@ simdjson_inline uint64_t clear_lowest_bit(uint64_t input_num) {
return input_num & (input_num-1);
}
// We sometimes call leading_zeroes on inputs that are zero,
// but the algorithms do not end up using the returned value.
// Sadly, sanitizers are not smart enough to figure it out.
// Applies only when SIMDJSON_PREFER_REVERSE_BITS is defined and true.
// (See below.)
SIMDJSON_NO_SANITIZE_UNDEFINED
/* result might be undefined when input_num is zero */
simdjson_inline int leading_zeroes(uint64_t input_num) {
#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO
@@ -10575,7 +10750,13 @@ simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t
} // namespace arm64
} // namespace simdjson
#ifndef SIMDJSON_SWAR_NUMBER_PARSING
#if SIMDJSON_IS_BIG_ENDIAN
#define SIMDJSON_SWAR_NUMBER_PARSING 0
#else
#define SIMDJSON_SWAR_NUMBER_PARSING 1
#endif
#endif
#endif // SIMDJSON_ARM64_NUMBERPARSING_DEFS_H
/* end file simdjson/arm64/numberparsing_defs.h */
@@ -14123,8 +14304,14 @@ static_assert(sizeof(__m256i) <= simdjson::SIMDJSON_PADDING, "insufficient paddi
/* end file simdjson/haswell/intrinsics.h */
#if !SIMDJSON_CAN_ALWAYS_RUN_HASWELL
// We enable bmi2 only if LLVM/clang is used, because GCC may not
// make good use of it. See https://github.com/simdjson/simdjson/pull/2243
#if defined(__clang__)
SIMDJSON_TARGET_REGION("avx2,bmi,bmi2,pclmul,lzcnt,popcnt")
#else
SIMDJSON_TARGET_REGION("avx2,bmi,pclmul,lzcnt,popcnt")
#endif
#endif
/* including simdjson/haswell/bitmanipulation.h: #include "simdjson/haswell/bitmanipulation.h" */
/* begin file simdjson/haswell/bitmanipulation.h */
@@ -15778,7 +15965,6 @@ simdjson_unused simdjson_inline simdjson_result<number_type> get_number_type(con
// Our objective is accurate parsing (ULP of 0) at high speed.
template<typename W>
simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) {
//
// Check for minus sign
//
@@ -16756,8 +16942,14 @@ static_assert(sizeof(__m256i) <= simdjson::SIMDJSON_PADDING, "insufficient paddi
/* end file simdjson/haswell/intrinsics.h */
#if !SIMDJSON_CAN_ALWAYS_RUN_HASWELL
// We enable bmi2 only if LLVM/clang is used, because GCC may not
// make good use of it. See https://github.com/simdjson/simdjson/pull/2243
#if defined(__clang__)
SIMDJSON_TARGET_REGION("avx2,bmi,bmi2,pclmul,lzcnt,popcnt")
#else
SIMDJSON_TARGET_REGION("avx2,bmi,pclmul,lzcnt,popcnt")
#endif
#endif
/* including simdjson/haswell/bitmanipulation.h: #include "simdjson/haswell/bitmanipulation.h" */
/* begin file simdjson/haswell/bitmanipulation.h */
@@ -21992,7 +22184,6 @@ simdjson_unused simdjson_inline simdjson_result<number_type> get_number_type(con
// Our objective is accurate parsing (ULP of 0) at high speed.
template<typename W>
simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) {
//
// Check for minus sign
//
@@ -26758,7 +26949,13 @@ simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t
} // namespace ppc64
} // namespace simdjson
#ifndef SIMDJSON_SWAR_NUMBER_PARSING
#if SIMDJSON_IS_BIG_ENDIAN
#define SIMDJSON_SWAR_NUMBER_PARSING 0
#else
#define SIMDJSON_SWAR_NUMBER_PARSING 1
#endif
#endif
#endif // SIMDJSON_PPC64_NUMBERPARSING_DEFS_H
/* end file simdjson/ppc64/numberparsing_defs.h */
@@ -28362,7 +28559,6 @@ simdjson_unused simdjson_inline simdjson_result<number_type> get_number_type(con
// Our objective is accurate parsing (ULP of 0) at high speed.
template<typename W>
simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) {
//
// Check for minus sign
//
@@ -29502,7 +29698,13 @@ simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t
} // namespace ppc64
} // namespace simdjson
#ifndef SIMDJSON_SWAR_NUMBER_PARSING
#if SIMDJSON_IS_BIG_ENDIAN
#define SIMDJSON_SWAR_NUMBER_PARSING 0
#else
#define SIMDJSON_SWAR_NUMBER_PARSING 1
#endif
#endif
#endif // SIMDJSON_PPC64_NUMBERPARSING_DEFS_H
/* end file simdjson/ppc64/numberparsing_defs.h */
@@ -35098,7 +35300,6 @@ simdjson_unused simdjson_inline simdjson_result<number_type> get_number_type(con
// Our objective is accurate parsing (ULP of 0) at high speed.
template<typename W>
simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) {
//
// Check for minus sign
//
@@ -40184,7 +40385,13 @@ simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t
} // namespace lsx
} // namespace simdjson
#ifndef SIMDJSON_SWAR_NUMBER_PARSING
#if SIMDJSON_IS_BIG_ENDIAN
#define SIMDJSON_SWAR_NUMBER_PARSING 0
#else
#define SIMDJSON_SWAR_NUMBER_PARSING 1
#endif
#endif
#endif // SIMDJSON_LSX_NUMBERPARSING_DEFS_H
/* end file simdjson/lsx/numberparsing_defs.h */
@@ -41658,7 +41865,6 @@ simdjson_unused simdjson_inline simdjson_result<number_type> get_number_type(con
// Our objective is accurate parsing (ULP of 0) at high speed.
template<typename W>
simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) {
//
// Check for minus sign
//
@@ -42713,7 +42919,13 @@ simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t
} // namespace lsx
} // namespace simdjson
#ifndef SIMDJSON_SWAR_NUMBER_PARSING
#if SIMDJSON_IS_BIG_ENDIAN
#define SIMDJSON_SWAR_NUMBER_PARSING 0
#else
#define SIMDJSON_SWAR_NUMBER_PARSING 1
#endif
#endif
#endif // SIMDJSON_LSX_NUMBERPARSING_DEFS_H
/* end file simdjson/lsx/numberparsing_defs.h */
@@ -46167,7 +46379,13 @@ simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t
} // namespace lasx
} // namespace simdjson
#ifndef SIMDJSON_SWAR_NUMBER_PARSING
#if SIMDJSON_IS_BIG_ENDIAN
#define SIMDJSON_SWAR_NUMBER_PARSING 0
#else
#define SIMDJSON_SWAR_NUMBER_PARSING 1
#endif
#endif
#endif // SIMDJSON_LASX_NUMBERPARSING_DEFS_H
/* end file simdjson/lasx/numberparsing_defs.h */
@@ -47657,7 +47875,6 @@ simdjson_unused simdjson_inline simdjson_result<number_type> get_number_type(con
// Our objective is accurate parsing (ULP of 0) at high speed.
template<typename W>
simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) {
//
// Check for minus sign
//
@@ -48712,7 +48929,13 @@ simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t
} // namespace lasx
} // namespace simdjson
#ifndef SIMDJSON_SWAR_NUMBER_PARSING
#if SIMDJSON_IS_BIG_ENDIAN
#define SIMDJSON_SWAR_NUMBER_PARSING 0
#else
#define SIMDJSON_SWAR_NUMBER_PARSING 1
#endif
#endif
#endif // SIMDJSON_LASX_NUMBERPARSING_DEFS_H
/* end file simdjson/lasx/numberparsing_defs.h */
@@ -52196,7 +52419,13 @@ simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t
} // namespace fallback
} // namespace simdjson
#ifndef SIMDJSON_SWAR_NUMBER_PARSING
#if SIMDJSON_IS_BIG_ENDIAN
#define SIMDJSON_SWAR_NUMBER_PARSING 0
#else
#define SIMDJSON_SWAR_NUMBER_PARSING 1
#endif
#endif
#endif // SIMDJSON_FALLBACK_NUMBERPARSING_DEFS_H
/* end file simdjson/fallback/numberparsing_defs.h */
@@ -53255,7 +53484,6 @@ simdjson_unused simdjson_inline simdjson_result<number_type> get_number_type(con
// Our objective is accurate parsing (ULP of 0) at high speed.
template<typename W>
simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) {
//
// Check for minus sign
//
@@ -54330,7 +54558,13 @@ simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t
} // namespace fallback
} // namespace simdjson
#ifndef SIMDJSON_SWAR_NUMBER_PARSING
#if SIMDJSON_IS_BIG_ENDIAN
#define SIMDJSON_SWAR_NUMBER_PARSING 0
#else
#define SIMDJSON_SWAR_NUMBER_PARSING 1
#endif
#endif
#endif // SIMDJSON_FALLBACK_NUMBERPARSING_DEFS_H
/* end file simdjson/fallback/numberparsing_defs.h */
+3845 -1352
View File
File diff suppressed because it is too large Load Diff
+4
View File
@@ -164,6 +164,10 @@ simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *
return stage2(_doc);
}
simdjson_warn_unused size_t implementation::write_string_escaped(const std::string_view input, char *out) const noexcept {
return arm64::stringparsing::write_string_escaped(input, out);
}
} // namespace arm64
} // namespace simdjson
+4
View File
@@ -402,6 +402,10 @@ simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *
return stage2(_doc);
}
simdjson_warn_unused size_t implementation::write_string_escaped(const std::string_view input, char *out) const noexcept {
return fallback::stringparsing::write_string_escaped(input, out);
}
} // namespace fallback
} // namespace simdjson
+95
View File
@@ -236,6 +236,101 @@ simdjson_warn_unused simdjson_inline uint8_t *parse_wobbly_string(const uint8_t
}
}
simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) noexcept {
// We are making the following assumption: most strings will either be very short or they will not
// need escaping.
size_t i = 0;
size_t pos = 0;
if(input.size() >= escaping::BYTES_PROCESSED) {
auto vec_processing = [input,out]() -> size_t {
size_t i = 0;
size_t pos = 0;
for(;input.size() - i >= escaping::BYTES_PROCESSED; i += escaping::BYTES_PROCESSED) {
escaping vinput = escaping::copy_and_find(reinterpret_cast<const uint8_t *>(input.data()) + i, reinterpret_cast<uint8_t *>(out) + pos);
if(vinput.has_escape()) {
return i + vinput.escape_index(); // We have a character that needs escaping
}
pos += escaping::BYTES_PROCESSED;
}
if(i == input.size()) { return input.size(); }
// We virtually backtrack so we can load a full vector register
i = input.size() - escaping::BYTES_PROCESSED;
pos = i;
escaping vinput = escaping::copy_and_find(reinterpret_cast<const uint8_t *>(input.data()) + i, reinterpret_cast<uint8_t *>(out) + pos);
if(vinput.has_escape()) {
return i + vinput.escape_index(); // We have a character that needs escaping
}
return input.size();
};
i = vec_processing();
pos = i;
if(i == input.size()) { return pos; }
// Here we only continue if there was a character that needed escaping.
}
static std::string_view control_chars[] = {
"\\x0000", "\\x0001", "\\x0002", "\\x0003", "\\x0004", "\\x0005", "\\x0006",
"\\x0007", "\\x0008", "\\t", "\\n", "\\x000b", "\\f", "\\r",
"\\x000e", "\\x000f", "\\x0010", "\\x0011", "\\x0012", "\\x0013", "\\x0014",
"\\x0015", "\\x0016", "\\x0017", "\\x0018", "\\x0019", "\\x001a", "\\x001b",
"\\x001c", "\\x001d", "\\x001e", "\\x001f"};
static std::array<uint8_t, 256> json_quotable_character =
[]() constexpr {
std::array<uint8_t, 256> result{};
for (int i = 0; i < 32; i++) {
result[i] = 1;
}
for (int i : {'"', '\\'}) {
result[i] = 1;
}
return result;
}();
// The rest could possibly be vectorized, but consider that we expect most strings
// to be short or not to require escaping.
for (; i < input.size(); i++) {
uint8_t c = static_cast<uint8_t>(input[i]);
if(json_quotable_character[c]) {
switch (c) {
case '"':
out[pos++] = '\\';
out[pos++] = '"';
break;
case '\\':
out[pos++] = '\\';
out[pos++] = '\\';
break;
case '\b':
out[pos++] = '\\';
out[pos++] = 'b';
break;
case '\f':
out[pos++] = '\\';
out[pos++] = 'f';
break;
case '\n':
out[pos++] = '\\';
out[pos++] = 'n';
break;
case '\r':
out[pos++] = '\\';
out[pos++] = 'r';
break;
case '\t':
out[pos++] = '\\';
out[pos++] = 't';
break;
default:
control_chars[c].copy(out + pos, 6);
pos += 6;
}
} else {
out[pos++] = c;
}
}
return pos;
}
} // namespace stringparsing
} // unnamed namespace
} // namespace SIMDJSON_IMPLEMENTATION
+4
View File
@@ -161,6 +161,10 @@ simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *
return stage2(_doc);
}
simdjson_warn_unused size_t implementation::write_string_escaped(const std::string_view input, char *out) const noexcept {
return haswell::stringparsing::write_string_escaped(input, out);
}
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
+4
View File
@@ -207,6 +207,10 @@ simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *
return stage2(_doc);
}
simdjson_warn_unused size_t implementation::write_string_escaped(const std::string_view input, char *out) const noexcept {
return icelake::stringparsing::write_string_escaped(input, out);
}
} // namespace icelake
} // namespace simdjson
+9
View File
@@ -186,6 +186,9 @@ public:
simdjson_warn_unused bool validate_utf8(const char * buf, size_t len) const noexcept final override {
return set_best()->validate_utf8(buf, len);
}
simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) const noexcept final {
return set_best()->write_string_escaped(input, out);
}
simdjson_inline detect_best_supported_implementation_on_first_use() noexcept : implementation("best_supported_detector", "Detects the best supported implementation and sets it", 0) {}
private:
const implementation *set_best() const noexcept;
@@ -236,6 +239,9 @@ public:
simdjson_warn_unused error_code minify(const uint8_t *, size_t, uint8_t *, size_t &) const noexcept final override {
return UNSUPPORTED_ARCHITECTURE;
}
simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) const noexcept final {
return 0;
}
simdjson_warn_unused bool validate_utf8(const char *, size_t) const noexcept final override {
return false; // Just refuse to validate. Given that we have a fallback implementation
// it seems unlikely that unsupported_implementation will ever be used. If it is used,
@@ -319,6 +325,9 @@ simdjson_warn_unused error_code minify(const char *buf, size_t len, char *dst, s
simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) noexcept {
return get_active_implementation()->validate_utf8(buf, len);
}
simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) noexcept {
return get_active_implementation()->write_string_escaped(input, out);
}
const implementation * builtin_implementation() {
static const implementation * builtin_impl = get_available_implementations()[SIMDJSON_STRINGIFY(SIMDJSON_BUILTIN_IMPLEMENTATION)];
assert(builtin_impl);
+4
View File
@@ -124,6 +124,10 @@ simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *
return stage2(_doc);
}
simdjson_warn_unused size_t implementation::write_string_escaped(const std::string_view input, char *out) const noexcept {
return lasx::stringparsing::write_string_escaped(input, out);
}
} // namespace lasx
} // namespace simdjson
+4
View File
@@ -128,6 +128,10 @@ simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *
return stage2(_doc);
}
simdjson_warn_unused size_t implementation::write_string_escaped(const std::string_view input, char *out) const noexcept {
return lsx::stringparsing::write_string_escaped(input, out);
}
} // namespace lsx
} // namespace simdjson
+4
View File
@@ -134,6 +134,10 @@ simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *
return stage2(_doc);
}
simdjson_warn_unused size_t implementation::write_string_escaped(const std::string_view input, char *out) const noexcept {
return ppc64::stringparsing::write_string_escaped(input, out);
}
} // namespace ppc64
} // namespace simdjson
+4
View File
@@ -166,6 +166,10 @@ simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *
return stage2(_doc);
}
simdjson_warn_unused size_t implementation::write_string_escaped(const std::string_view input, char *out) const noexcept {
return westmere::stringparsing::write_string_escaped(input, out);
}
} // namespace westmere
} // namespace simdjson
+1
View File
@@ -26,3 +26,4 @@ endif()
add_cpp_test(checkimplementation LABELS other per_implementation)
add_subdirectory(compilation_failure_tests)
add_subdirectory(builder)
+12
View File
@@ -0,0 +1,12 @@
# All remaining tests link with simdjson proper
link_libraries(simdjson)
include_directories(..)
add_cpp_test(builder_string_builder_tests LABELS ondemand acceptance per_implementation)
# Copy the simdjson dll into the tests directory
if(MSVC AND BUILD_SHARED_LIBS)
add_custom_command(TARGET builder_string_builder_tests POST_BUILD # Adds a post-build event
COMMAND ${CMAKE_COMMAND} -E copy_if_different # which executes "cmake -E copy_if_different..."
"$<TARGET_FILE:simdjson>" # <--this is in-file
"$<TARGET_FILE_DIR:builder_string_builder_tests>") # <--this is out-file path
endif(MSVC AND BUILD_SHARED_LIBS)
@@ -0,0 +1,178 @@
#include "simdjson.h"
#include "test_builder.h"
#include <string_view>
using namespace simdjson;
namespace builder_tests {
using namespace std;
#if SIMDJSON_EXCEPTIONS
bool string_convertion_except() {
TEST_START();
simdjson::ondemand::parser p;
simdjson::builder::string_builder sb;
sb.append('a');
std::string r(sb);
ASSERT_EQUAL(r, "a");
TEST_SUCCEED();
}
#endif
bool append_char() {
TEST_START();
simdjson::builder::string_builder sb;
sb.append('a');
ASSERT_EQUAL(sb.size(), 1);
std::string_view p;
auto result = sb.view().get(p);
ASSERT_EQUAL(result, SUCCESS);
ASSERT_EQUAL(p, "a");
TEST_SUCCEED();
}
bool append_integer() {
TEST_START();
simdjson::builder::string_builder sb;
sb.append(42);
ASSERT_EQUAL(sb.size(), 2);
std::string_view p;
auto result = sb.view().get(p);
ASSERT_EQUAL(result, SUCCESS);
ASSERT_EQUAL(p, "42");
TEST_SUCCEED();
}
bool append_float() {
TEST_START();
simdjson::builder::string_builder sb;
sb.append(1.1);
ASSERT_EQUAL(sb.size(), 3);
std::string_view p;
auto result = sb.view().get(p);
ASSERT_EQUAL(result, SUCCESS);
ASSERT_EQUAL(p, "1.1");
TEST_SUCCEED();
}
bool append_null() {
TEST_START();
simdjson::builder::string_builder sb;
sb.append_null();
ASSERT_EQUAL(sb.size(), 4);
std::string_view p;
auto result = sb.view().get(p);
ASSERT_EQUAL(result, SUCCESS);
ASSERT_EQUAL(p, "null");
TEST_SUCCEED();
}
bool clear() {
TEST_START();
simdjson::builder::string_builder sb;
sb.append('a');
sb.clear();
ASSERT_EQUAL(sb.size(), 0);
TEST_SUCCEED();
}
bool escape_and_append() {
TEST_START();
simdjson::builder::string_builder sb;
sb.escape_and_append("Hello, \"world\"!");
std::string_view p;
auto result = sb.view().get(p);
ASSERT_EQUAL(result, SUCCESS);
ASSERT_EQUAL(p, "Hello, \"world\"!");
TEST_SUCCEED();
}
bool escape_and_append_with_quotes() {
TEST_START();
simdjson::builder::string_builder sb;
sb.escape_and_append_with_quotes("Hello, \"world\"!");
std::string_view p;
auto result = sb.view().get(p);
ASSERT_EQUAL(result, SUCCESS);
ASSERT_EQUAL(p, "\"Hello, \\\"world\\\"!\"");
TEST_SUCCEED();
}
bool append_raw() {
TEST_START();
simdjson::builder::string_builder sb;
sb.append_raw("Test");
std::string_view p;
auto result = sb.view().get(p);
ASSERT_EQUAL(result, SUCCESS);
ASSERT_EQUAL(p, "Test");
TEST_SUCCEED();
}
bool raw_with_length() {
TEST_START();
simdjson::builder::string_builder sb;
sb.append_raw("Test String", 4);
std::string_view p;
auto result = sb.view().get(p);
ASSERT_EQUAL(result, SUCCESS);
ASSERT_EQUAL(p, "Test");
TEST_SUCCEED();
}
bool string_convertion() {
TEST_START();
simdjson::builder::string_builder sb;
sb.append('a');
std::string_view p;
auto result = sb.view().get(p);
ASSERT_EQUAL(result, SUCCESS);
ASSERT_EQUAL(p, "a");
TEST_SUCCEED();
}
bool unicode_validation() {
TEST_START();
simdjson::builder::string_builder sb;
sb.append('a');
ASSERT_TRUE(sb.validate_unicode());
TEST_SUCCEED();
}
bool buffer_growth() {
TEST_START();
simdjson::builder::string_builder sb;
for(int i = 0; i < 3; ++i) {
sb.append('a');
}
ASSERT_EQUAL(sb.size(), 3);
TEST_SUCCEED();
}
bool run() {
return
#if SIMDJSON_EXCEPTIONS
string_convertion_except() &&
#endif
append_char() &&
append_integer() &&
append_float() &&
append_null() &&
clear() &&
escape_and_append() &&
escape_and_append_with_quotes() &&
append_raw() &&
raw_with_length() &&
string_convertion() &&
buffer_growth() &&
unicode_validation() &&
true;
}
} // namespace twitter_tests
int main(int argc, char *argv[]) {
return test_main(argc, argv, builder_tests::run);
}
+51
View File
@@ -0,0 +1,51 @@
#ifndef ONDEMAND_TEST_BUILDER_H
#define ONDEMAND_TEST_BUILDER_H
#include <iostream>
#include <unistd.h>
#include "simdjson.h"
#include "cast_tester.h"
#include "test_macros.h"
template<typename F>
int test_main(int argc, char *argv[], const F& test_function) {
std::cout << std::unitbuf;
int c;
while ((c = getopt(argc, argv, "a:")) != -1) {
switch (c) {
case 'a': {
const simdjson::implementation *impl = simdjson::get_available_implementations()[optarg];
if (!impl) {
std::fprintf(stderr, "Unsupported architecture value -a %s\n", optarg);
return EXIT_FAILURE;
}
simdjson::get_active_implementation() = impl;
break;
}
default:
std::fprintf(stderr, "Unexpected argument %c\n", c);
return EXIT_FAILURE;
}
}
// this is put here deliberately to check that the documentation is correct (README),
// should this fail to compile, you should update the documentation:
if (simdjson::get_active_implementation()->name() == "unsupported") {
std::printf("unsupported CPU\n");
std::abort();
}
// We want to know what we are testing.
std::cout << "builtin_implementation -- " << simdjson::builtin_implementation()->name() << std::endl;
std::cout << "------------------------------------------------------------" << std::endl;
std::cout << "Running tests." << std::endl;
if (test_function()) {
std::cout << "Success!" << std::endl;
return EXIT_SUCCESS;
} else {
std::cerr << "FAILED." << std::endl;
return EXIT_FAILURE;
}
}
#endif // ONDEMAND_TEST_BUILDER_H
+1
View File
@@ -12,6 +12,7 @@ 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(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)
add_cpp_test(numberparsingcheck LABELS dom acceptance per_implementation) # https://tools.ietf.org/html/rfc6901
add_cpp_test(parse_many_test LABELS dom acceptance per_implementation)
+350
View File
@@ -0,0 +1,350 @@
/**
* refer to pathcheck.cpp
*/
#include <iostream>
#include <string>
using namespace std::string_literals;
#include "simdjson.h"
#include "test_macros.h"
// we define our own asserts to get around NDEBUG
#ifndef ASSERT
#define ASSERT(x) \
{ \
if (!(x)) { \
std::cerr << "Failed assertion " << #x << std::endl; \
return false; \
} \
}
#endif
using namespace simdjson;
bool demo() {
#if SIMDJSON_EXCEPTIONS
std::cout << "demo test" << std::endl;
auto cars_json = R"( [
{ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] },
{ "make": "Kia", "model": "Soul", "year": 2012, "tire_pressure": [ 30.1, 31.0, 28.6, 28.7 ] },
{ "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
] )"_padded;
dom::parser parser;
dom::element cars = parser.parse(cars_json);
double x = cars.at_path("$[0].tire_pressure[1]");
if (x != 39.9)
return false;
// Iterating through an array of objects
std::vector<double> measured;
for (dom::element car_element : cars) {
dom::object car;
simdjson::error_code error;
if ((error = car_element.get(car))) {
std::cerr << error << std::endl;
return false;
}
double x3 = car.at_path("$.tire_pressure[1]");
measured.push_back(x3);
}
std::vector<double> expected = {39.9, 31, 30};
if (measured != expected) {
return false;
}
#endif
return true;
}
const padded_string TEST_JSON = R"(
{
"/~01abc": [
0,
{
"\\\" 0": [
"value0",
"value1"
]
}
],
"0": "0 ok",
"01": "01 ok",
"": "empty ok",
"arr": []
}
)"_padded;
const padded_string TEST_RFC_JSON = R"(
{
"foo": ["bar", "baz"],
"": 0,
"a/b": 1,
"c%d": 2,
"e^f": 3,
"g|h": 4,
"i\\j": 5,
"k\"l": 6,
" ": 7,
"m~n": 8
}
)"_padded;
bool run_success_test(const padded_string &source, const char *json_path,
std::string_view expected_value) {
std::cout << "Running successful JSONPath test '" << json_path << "' ..."
<< std::endl;
dom::parser parser;
dom::element doc;
auto error = parser.parse(source).get(doc);
if (error) {
std::cerr << "cannot parse: " << error << std::endl;
return false;
}
dom::element answer;
error = doc.at_path(json_path).get(answer);
if (error) {
std::cerr << "cannot access pointer: " << error << std::endl;
return false;
}
std::string str_answer = simdjson::minify(answer);
if (str_answer != expected_value) {
std::cerr << "They differ!!!" << std::endl;
std::cerr << " found '" << str_answer << "'" << std::endl;
std::cerr << " expected '" << expected_value << "'" << std::endl;
}
ASSERT_EQUAL(str_answer, expected_value);
return true;
}
bool run_failure_test(const padded_string &source, const char *json_path,
error_code expected_error) {
std::cout << "Running invalid JSONPath test '" << json_path << "' ..."
<< std::endl;
dom::parser parser;
ASSERT_ERROR(parser.parse(source).at_path(json_path).error(), expected_error);
return true;
}
bool demo_relative_path() {
TEST_START();
auto cars_json = R"( [
{ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] },
{ "make": "Kia", "model": "Soul", "year": 2012, "tire_pressure": [ 30.1, 31.0, 28.6, 28.7 ] },
{ "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
] )"_padded;
dom::parser parser;
dom::element cars;
std::vector<double> measured;
auto error = parser.parse(cars_json).get(cars);
if (error) {
std::cerr << "cannot parse: " << error << std::endl;
return false;
}
dom::array cars_array;
error = cars.get(cars_array);
if (error) {
std::cerr << "cannot get array: " << error << std::endl;
return false;
}
for (auto car_element : cars_array) {
double x;
ASSERT_SUCCESS(car_element.at_path(".tire_pressure[1]").get(x));
measured.push_back(x);
}
std::vector<double> expected = {39.9, 31, 30};
if (measured != expected) {
return false;
}
TEST_SUCCEED();
}
bool many_json_paths() {
TEST_START();
auto cars_json = R"( [
{ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] },
{ "make": "Kia", "model": "Soul", "year": 2012, "tire_pressure": [ 30.1, 31.0, 28.6, 28.7 ] },
{ "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
] )"_padded;
dom::parser parser;
dom::element cars;
std::vector<double> measured;
ASSERT_SUCCESS(parser.parse(cars_json).get(cars));
for (int i = 0; i < 3; i++) {
double x;
std::string json_path = std::string("$[") + std::to_string(i) +
std::string("].tire_pressure[1]");
ASSERT_SUCCESS(cars.at_path(json_path).get(x));
measured.push_back(x);
}
std::vector<double> expected = {39.9, 31, 30};
if (measured != expected) {
return false;
}
TEST_SUCCEED();
}
bool many_json_paths_object_array() {
TEST_START();
auto dogcatpotato =
R"( { "dog" : [1,2,3], "cat" : [5, 6, 7], "potato" : [1234]})"_padded;
dom::parser parser;
dom::element doc;
ASSERT_SUCCESS(parser.parse(dogcatpotato).get(doc));
dom::object obj;
ASSERT_SUCCESS(doc.get_object().get(obj));
int64_t x;
ASSERT_SUCCESS(obj.at_path("$.dog[1]").get(x));
ASSERT_EQUAL(x, 2);
ASSERT_SUCCESS(obj.at_path("$.potato[0]").get(x));
ASSERT_EQUAL(x, 1234);
TEST_SUCCEED();
}
bool many_json_paths_object() {
TEST_START();
auto cfoofoo2 =
R"( { "c" :{ "foo": { "a": [ 10, 20, 30 ] }}, "d": { "foo2": { "a": [ 10, 20, 30 ] }} , "e": 120 })"_padded;
dom::parser parser;
dom::element doc;
ASSERT_SUCCESS(parser.parse(cfoofoo2).get(doc));
dom::object obj;
ASSERT_SUCCESS(doc.get_object().get(obj));
int64_t x;
ASSERT_SUCCESS(obj.at_path("$.c.foo.a[1]").get(x));
ASSERT_EQUAL(x, 20);
ASSERT_SUCCESS(obj.at_path("$.d.foo2.a.2").get(x));
ASSERT_EQUAL(x, 30);
ASSERT_SUCCESS(obj.at_path("$.e").get(x));
ASSERT_EQUAL(x, 120);
TEST_SUCCEED();
}
bool many_json_paths_array() {
TEST_START();
auto cfoofoo2 =
R"( [ 111, 2, 3, { "foo": { "a": [ 10, 20, 33 ] }}, { "foo2": { "a": [ 10, 20, 30 ] }}, 1001 ])"_padded;
dom::parser parser;
dom::element doc;
ASSERT_SUCCESS(parser.parse(cfoofoo2).get(doc));
dom::array arr;
ASSERT_SUCCESS(doc.get_array().get(arr));
int64_t x;
ASSERT_SUCCESS(arr.at_path("$[3].foo.a[1]").get(x));
ASSERT_EQUAL(x, 20);
TEST_SUCCEED();
}
struct car_type {
std::string make;
std::string model;
uint64_t year;
std::vector<double> tire_pressure;
car_type(std::string_view _make, std::string_view _model, uint64_t _year,
std::vector<double> &&_tire_pressure)
: make{_make}, model{_model}, year(_year), tire_pressure(_tire_pressure) {
}
};
bool json_path_invalidation() {
TEST_START();
auto cars_json = R"( [
{ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] },
{ "make": "Kia", "model": "Soul", "year": 2012, "tire_pressure": [ 30.1, 31.0, 28.6, 28.7 ] },
{ "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
] )"_padded;
dom::parser parser;
dom::element cars;
std::vector<double> measured;
ASSERT_SUCCESS(parser.parse(cars_json).get(cars));
std::vector<car_type> content;
for (int i = 0; i < 3; i++) {
dom::object obj;
std::string json_path =
std::string("$[") + std::to_string(i) + std::string("]");
// Each successive at_path call invalidates
// previously parsed values, strings, objects and array.
ASSERT_SUCCESS(cars.at_path(json_path).get(obj));
// We materialize the object.
std::string_view make;
ASSERT_SUCCESS(obj["make"].get(make));
std::string_view model;
ASSERT_SUCCESS(obj["model"].get(model));
uint64_t year;
ASSERT_SUCCESS(obj["year"].get(year));
// We materialize the array.
dom::array arr;
ASSERT_SUCCESS(obj["tire_pressure"].get(arr));
std::vector<double> values;
for (auto x : arr) {
double value_double;
ASSERT_SUCCESS(x.get(value_double));
values.push_back(value_double);
}
content.emplace_back(make, model, year, std::move(values));
}
std::string expected[] = {"Toyota", "Kia", "Toyota"};
int i = 0;
for (car_type c : content) {
std::cout << c.make << " " << c.model << " " << c.year << "\n";
ASSERT_EQUAL(expected[i++], c.make);
}
TEST_SUCCEED();
}
// for 0.5 version and following (standard compliant)
bool modern_support() {
#if SIMDJSON_EXCEPTIONS
std::cout << "modern test" << std::endl;
auto example_json = R"({"key": "value", "array": [0, 1, 2]})"_padded;
dom::parser parser;
dom::element example = parser.parse(example_json);
std::string_view value_str = example.at_path("$.key");
ASSERT_EQUAL(value_str, "value");
int64_t array0 = example.at_path("$.array[0]");
ASSERT_EQUAL(array0, 0);
array0 = example.at_path("$.array").at_path("$[0]");
ASSERT_EQUAL(array0, 0);
ASSERT_ERROR(example.at_path("$.no_such_key").error(), NO_SUCH_FIELD);
ASSERT_ERROR(example.at_path("$.array[9]").error(), INDEX_OUT_OF_BOUNDS);
ASSERT_ERROR(example.at_path("$.array.not_a_num").error(), INCORRECT_TYPE);
ASSERT_ERROR(example.at_path("$.array.").error(), INVALID_JSON_POINTER);
#endif
return true;
}
int main() {
if (true && demo() && modern_support() &&
run_success_test(TEST_RFC_JSON, "$.foo", "[\"bar\",\"baz\"]") &&
run_success_test(TEST_RFC_JSON, "$.foo[0]", "\"bar\"") &&
run_success_test(TEST_RFC_JSON, "$.", "0") &&
run_success_test(TEST_RFC_JSON, "$.a/b", "1") &&
run_success_test(TEST_RFC_JSON, "$.c%d", "2") &&
run_success_test(TEST_RFC_JSON, "$.e^f", "3") &&
run_success_test(TEST_RFC_JSON, "$.g|h", "4") &&
run_success_test(TEST_RFC_JSON, "$.i\\j", "5") &&
run_success_test(TEST_RFC_JSON, "$.k\"l", "6") &&
run_success_test(TEST_RFC_JSON, "$. ", "7") &&
run_success_test(TEST_RFC_JSON, "$.m~n", "8") &&
run_success_test(TEST_JSON, "$./~01abc",
R"([0,{"\\\" 0":["value0","value1"]}])") &&
run_success_test(TEST_JSON, "$./~01abc[1]",
R"({"\\\" 0":["value0","value1"]})") &&
run_success_test(TEST_JSON, "$./~01abc[1].\\\" 0",
R"(["value0","value1"])") &&
run_success_test(TEST_JSON, "$.arr", R"([])") // get array
&&
run_failure_test(TEST_JSON, R"($./~01abc[1].\\\" 0[2])", NO_SUCH_FIELD) &&
run_failure_test(TEST_JSON, "$.arr[0]", INDEX_OUT_OF_BOUNDS) &&
run_failure_test(TEST_JSON, "/~01abc", INVALID_JSON_POINTER) &&
run_failure_test(TEST_JSON, ".~1abc", NO_SUCH_FIELD) &&
run_failure_test(TEST_JSON, "./~01abc.01", INVALID_JSON_POINTER) &&
run_failure_test(TEST_JSON, "./~01abc.", INVALID_JSON_POINTER) &&
run_failure_test(TEST_JSON, "./~01abc.-", INDEX_OUT_OF_BOUNDS)) {
std::cout << "Success!" << std::endl;
return 0;
} else {
std::cerr << "Failed!" << std::endl;
return 1;
}
}
+37
View File
@@ -461,7 +461,44 @@ void parse_documentation_lowlevel() {
(void)element;
}
void jsondollar() {
dom::parser parser;
auto json = R"( { "c" :{ "foo": { "a": [ 10, 20, 30 ] }}, "d": { "foo2": { "a": [ 10, 20, 30 ] }} , "e": 120 })"_padded;
dom::element doc;
auto error = parser.parse(json).get(doc);
if(error) { exit(-1); }
dom::object obj;
error = doc.get_object().get(obj);
if(error) { exit(-1); }
int64_t x = 0; // initialization to silence unwarranted compiler warning
error = obj.at_path("$[3].foo.a[1]").get(x);
if(error) { exit(-1); }
if(x != 20) { exit(-1); }
x = obj.at_path("$.d.foo2.a.2");
if(x != 30) { exit(-1); }
if(error) { exit(-1); }
}
void jsonpath() {
auto cars_json = R"( [
{ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] },
{ "make": "Kia", "model": "Soul", "year": 2012, "tire_pressure": [ 30.1, 31.0, 28.6, 28.7 ] },
{ "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
] )"_padded;
dom::parser parser;
dom::element doc;
auto error = parser.parse(cars_json).get(doc);
if(error) { exit(-1); }
double p = 0.0; // initialization to silence unwarranted compiler warning
error = doc.at_path("[0].tire_pressure[1]").get(p);
if(error) { exit(-1); }
if(p != 39.9) { exit(-1); }
}
int main() {
jsonpath();
jsondollar();
basics_dom_1();
basics_dom_2();
basics_dom_3();
+2
View File
@@ -2,6 +2,8 @@
link_libraries(simdjson)
include_directories(..)
add_subdirectory(compilation_failure_tests)
add_cpp_test(ondemand_car_tag_invoke_deserialization LABELS ondemand acceptance per_implementation)
add_cpp_test(ondemand_car_deserialization LABELS ondemand acceptance per_implementation)
add_cpp_test(ondemand_log_tests LABELS ondemand acceptance per_implementation)
add_cpp_test(ondemand_log_error_tests LABELS ondemand acceptance per_implementation)
add_cpp_test(ondemand_tostring_tests LABELS ondemand acceptance per_implementation)
@@ -13,7 +13,8 @@ function(add_dual_compile_test TEST_NAME)
target_compile_definitions(${TEST_NAME}_should_not_compile PRIVATE COMPILATION_TEST_USE_FAILING_CODE=1)
endfunction(add_dual_compile_test)
add_dual_compile_test(iterate_object)
add_dual_compile_test(iterate_array)
add_dual_compile_test(iterate_char_star)
add_dual_compile_test(iterate_string_view)
add_dual_compile_test(iterate_temporary_buffer)
@@ -0,0 +1,34 @@
#include <iostream>
#include "simdjson.h"
using namespace simdjson;
int main() {
auto json = "[1]"_padded;
ondemand::parser parser;
auto f = [](ondemand::parser& p, simdjson::padded_string& jsons) -> ondemand::document {
ondemand::document doc;
auto error = p.iterate(jsons).get(doc);
if(error) { std::abort(); }
return doc;
};
ondemand::array arrayv;
#if COMPILATION_TEST_USE_FAILING_CODE
// Not allowed as this would be unsafe, the document must remain alive.
auto error = f(parser).get_array().get(arrayv);
#else
ondemand::document doc = f(parser, json);
auto error = doc.get_array().get(arrayv);
#endif
if(error) {
std::cout << "Failure" << std::endl;
}
int64_t a = 0;
error = arrayv.at(0).get_int64().get(a);
if(error) {
std::cout << "failure" << std::endl;
}
printf("a = %d\n", (int)a);
return EXIT_SUCCESS;
}
@@ -0,0 +1,34 @@
#include <iostream>
#include "simdjson.h"
using namespace simdjson;
int main() {
auto json = "{\"a\":1}"_padded;
ondemand::parser parser;
auto f = [](ondemand::parser& p, simdjson::padded_string& jsons) -> ondemand::document {
ondemand::document doc;
auto error = p.iterate(jsons).get(doc);
if(error) { std::abort(); }
return doc;
};
ondemand::object objv;
#if COMPILATION_TEST_USE_FAILING_CODE
// Not allowed as this would be unsafe, the document must remain alive.
auto error = f(parser).get_object().get(objv);
#else
ondemand::document doc = f(parser, json);
auto error = doc.get_object().get(objv);
#endif
if(error) {
std::cout << "Failure" << std::endl;
}
int64_t a = 0;
error = objv["a"].get_int64().get(a);
if(error) {
std::cout << "failure" << std::endl;
}
printf("a = %d\n", (int)a);
return EXIT_SUCCESS;
}
+22 -2
View File
@@ -21,8 +21,24 @@ long page_size() {
// Returns true if the buffer + len + simdjson::SIMDJSON_PADDING crosses the
// page boundary.
bool need_allocation(const char *buf, size_t len) {
return ((reinterpret_cast<uintptr_t>(buf + len - 1) % page_size()) <
simdjson::SIMDJSON_PADDING);
return ((reinterpret_cast<uintptr_t>(buf + len - 1) % page_size())
+ simdjson::SIMDJSON_PADDING > static_cast<uintptr_t>(page_size()));
}
bool check_need_allocation() {
long ps = page_size();
std::vector<std::tuple<uintptr_t,size_t,bool>> test_cases = {
{2*ps, 5, false}, {2*ps, ps, true}, {2*ps, 100, false},
};
for(auto t : test_cases) {
uintptr_t buf = std::get<0>(t);
size_t len = std::get<1>(t);
bool expected = std::get<2>(t);
if(need_allocation(reinterpret_cast<const char *>(buf), len) != expected) {
return false;
}
}
return true;
}
simdjson::padded_string_view
@@ -39,6 +55,10 @@ get_padded_string_view(const char *buf, size_t len,
int main() {
printf("page_size: %ld\n", page_size());
if(!check_need_allocation()) {
printf("Bug in the need_allocation function.\n");
return EXIT_FAILURE;
}
const char *jsonpoiner = R"(
{
"key": "value"
@@ -0,0 +1,263 @@
#include "simdjson.h"
#include "test_ondemand.h"
#include <iostream>
#include <vector>
using namespace simdjson;
/**
* A custom type that we want to parse.
*/
struct Car {
std::string make{};
std::string model{};
int64_t year{};
std::vector<double> tire_pressure{};
bool operator==(const Car &other) const {
return make == other.make && model == other.model && year == other.year &&
tire_pressure == other.tire_pressure;
}
};
std::ostream &operator<<(std::ostream &os, const Car &c) {
os << c.make << " " << c.model << " " << c.year << " ";
for (auto p : c.tire_pressure) {
os << p << " ";
}
return os;
}
#if !SIMDJSON_SUPPORTS_DESERIALIZATION
// This code is not necessary if you have a C++20 compliant system:
template <>
simdjson_inline simdjson_result<std::vector<double>>
simdjson::ondemand::value::get() noexcept {
ondemand::array array;
auto error = get_array().get(array);
if (error) {
return error;
}
std::vector<double> vec;
for (auto v : array) {
double val;
error = v.get_double().get(val);
if (error) {
return error;
}
vec.push_back(val);
}
return vec;
}
#endif
template <>
simdjson_inline simdjson_result<Car> simdjson::ondemand::value::get() noexcept {
ondemand::object obj;
auto error = get_object().get(obj);
if (error) {
return error;
}
Car car;
if ((error = obj["make"].get_string(car.make))) {
return error;
}
if ((error = obj["model"].get_string(car.model))) {
return error;
}
if ((error = obj["year"].get_int64().get(car.year))) {
return error;
}
if ((error = obj["tire_pressure"].get<std::vector<double>>().get(
car.tire_pressure))) {
return error;
}
return car;
}
template <>
simdjson_inline simdjson_result<Car>
simdjson::ondemand::document::get() &noexcept {
ondemand::object obj;
auto error = get_object().get(obj);
if (error) {
return error;
}
Car car;
if ((error = obj["make"].get_string(car.make))) {
return error;
}
if ((error = obj["model"].get_string(car.model))) {
return error;
}
if ((error = obj["year"].get_int64().get(car.year))) {
return error;
}
if ((error = obj["tire_pressure"].get<std::vector<double>>().get(
car.tire_pressure))) {
return error;
}
return car;
}
template <>
simdjson_inline simdjson_result<Car>
simdjson::ondemand::document_reference::get() &noexcept {
ondemand::object obj;
auto error = get_object().get(obj);
if (error) {
return error;
}
Car car;
if ((error = obj["make"].get_string(car.make))) {
return error;
}
if ((error = obj["model"].get_string(car.model))) {
return error;
}
if ((error = obj["year"].get_int64().get(car.year))) {
return error;
}
if ((error = obj["tire_pressure"].get<std::vector<double>>().get(
car.tire_pressure))) {
return error;
}
return car;
}
int main_should_compile(void) {
padded_string json =
R"( [ { "make": "Toyota", "model": "Camry", "year": 2018,
"tire_pressure": [ 40.1, 39.9 ] },
{ "make": "Kia", "model": "Soul", "year": 2012,
"tire_pressure": [ 30.1, 31.0 ] },
{ "make": "Toyota", "model": "Tercel", "year": 1999,
"tire_pressure": [ 29.8, 30.0 ] }
])"_padded;
ondemand::parser parser;
ondemand::document doc;
[[maybe_unused]] auto doc_error = parser.iterate(json).get(doc);
for (auto val : doc) {
#if SIMDJSON_EXCEPTIONS
Car c(val); // an exception may be thrown
#else
Car c;
if (auto error = val.get<Car>().get(c)) {
std::cerr << error << std::endl;
return EXIT_FAILURE;
}
#endif
}
return EXIT_SUCCESS;
}
namespace car_error_tests {
bool car_deserialize() {
TEST_START();
padded_string json =
R"( [ { "make": "Toyota", "model": "Camry", "year": 2018,
"tire_pressure": [ 40.1, 39.9 ] },
{ "make": "Kia", "model": "Soul", "year": 2012,
"tire_pressure": [ 30.1, 31.0 ] },
{ "make": "Toyota", "model": "Tercel", "year": 1999,
"tire_pressure": [ 29.8, 30.0 ] }
])"_padded;
ondemand::parser parser;
ondemand::document doc;
[[maybe_unused]] auto doc_error = parser.iterate(json).get(doc);
std::vector<Car> cars;
for (auto val : doc) {
#if SIMDJSON_EXCEPTIONS
Car c(val); // an exception may be thrown
#else
Car c;
if (auto error = val.get<Car>().get(c)) {
std::cerr << error << std::endl;
return EXIT_FAILURE;
}
#endif
cars.push_back(c);
std::cout << c.make << std::endl;
}
std::vector<Car> expected = {{"Toyota", "Camry", 2018, {40.1, 39.9}},
{"Kia", "Soul", 2012, {30.1, 31.0}},
{"Toyota", "Tercel", 1999, {29.8, 30.0}}};
ASSERT_EQUAL(cars.size(), expected.size());
for (size_t i = 0; i < cars.size(); i++) {
ASSERT_EQUAL(cars[i], expected[i]);
}
TEST_SUCCEED();
}
bool car_doc_deserialize() {
TEST_START();
padded_string json = R"( { "make": "Toyota", "model": "Camry", "year": 2018,
"tire_pressure": [ 40.1, 39.9 ] }
)"_padded;
ondemand::parser parser;
ondemand::document doc;
[[maybe_unused]] auto doc_error = parser.iterate(json).get(doc);
#if SIMDJSON_EXCEPTIONS
Car c(doc); // an exception may be thrown
#else
Car c;
if (auto error = doc.get<Car>().get(c)) {
std::cerr << error << std::endl;
return EXIT_FAILURE;
}
#endif
Car expected = {"Toyota", "Camry", 2018, {40.1, 39.9}};
ASSERT_EQUAL(c, expected);
TEST_SUCCEED();
}
bool car_stream_deserialize() {
TEST_START();
padded_string json =
R"( { "make": "Toyota", "model": "Camry", "year": 2018,
"tire_pressure": [ 40.1, 39.9 ] }
{ "make": "Kia", "model": "Soul", "year": 2012,
"tire_pressure": [ 30.1, 31.0 ] }
{ "make": "Toyota", "model": "Tercel", "year": 1999,
"tire_pressure": [ 29.8, 30.0 ] }
)"_padded;
ondemand::parser parser;
ondemand::document_stream stream;
auto error = parser.iterate_many(json).get(stream);
if(error) {
std::cerr << error << std::endl;
return EXIT_FAILURE;
}
std::vector<Car> cars;
#if SIMDJSON_EXCEPTIONS
for(ondemand::document_reference doc : stream) {
//Car c(doc); // an exception may be thrown
cars.push_back(Car(doc)); // an exception may be thrown
}
#else
for(auto doc : stream) {
Car c;
if ((error = doc.get<Car>().get(c))) {
std::cerr << error << std::endl;
return EXIT_FAILURE;
}
cars.push_back(c);
}
#endif
std::vector<Car> expected = {{"Toyota", "Camry", 2018, {40.1, 39.9}},
{"Kia", "Soul", 2012, {30.1, 31.0}},
{"Toyota", "Tercel", 1999, {29.8, 30.0}}};
ASSERT_EQUAL(cars.size(), expected.size());
for (size_t i = 0; i < cars.size(); i++) {
ASSERT_EQUAL(cars[i], expected[i]);
}
TEST_SUCCEED();
}
bool run() { return car_stream_deserialize() && car_doc_deserialize() && car_deserialize(); }
} // namespace car_error_tests
int main(int argc, char *argv[]) {
return test_main(argc, argv, car_error_tests::run);
}

Some files were not shown because too many files have changed in this diff Show More