Compare commits

..

1 Commits

Author SHA1 Message Date
Daniel Lemire a7fbb17ac1 Patch release. 2021-01-28 09:47:45 -05:00
2232 changed files with 335032 additions and 25726 deletions
+6 -6
View File
@@ -15,29 +15,29 @@ environment:
- job_name: VS2019
CMAKE_ARGS: -A %Platform%
- job_name: VS2019ARM
CMAKE_ARGS: -A ARM64 -DSIMDJSON_DEVELOPER_MODE=ON -DCMAKE_CROSSCOMPILING=1 -D SIMDJSON_GOOGLE_BENCHMARKS=OFF # Does Google Benchmark builds under VS ARM?
CMAKE_ARGS: -A ARM64 -DCMAKE_CROSSCOMPILING=1 -D SIMDJSON_GOOGLE_BENCHMARKS=OFF # Does Google Benchmark builds under VS ARM?
- job_name: VS2017 (Static, No Threads)
image: Visual Studio 2017
CMAKE_ARGS: -A %Platform% -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF -DSIMDJSON_ENABLE_THREADS=OFF
CMAKE_ARGS: -A %Platform% -DSIMDJSON_BUILD_STATIC=ON -DSIMDJSON_ENABLE_THREADS=OFF
CTEST_ARGS: -LE explicitonly
- job_name: VS2019 (Win32)
platform: Win32
CMAKE_ARGS: -A %Platform% -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=ON -DSIMDJSON_ENABLE_THREADS=ON # This should be the default. Testing anyway.
CMAKE_ARGS: -A %Platform% -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_ENABLE_THREADS=ON # This should be the default. Testing anyway.
CTEST_ARGS: -LE explicitonly
- job_name: VS2019 (Win32, No Exceptions)
platform: Win32
CMAKE_ARGS: -A %Platform% -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=ON -DSIMDJSON_ENABLE_THREADS=ON -DSIMDJSON_EXCEPTIONS=OFF
CMAKE_ARGS: -A %Platform% -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_ENABLE_THREADS=ON -DSIMDJSON_EXCEPTIONS=OFF
CTEST_ARGS: -LE explicitonly
- job_name: VS2015
image: Visual Studio 2015
CMAKE_ARGS: -A %Platform% -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF -DSIMDJSON_ENABLE_THREADS=OFF
CMAKE_ARGS: -A %Platform% -DSIMDJSON_BUILD_STATIC=ON -DSIMDJSON_ENABLE_THREADS=OFF
CTEST_ARGS: -LE explicitonly
build_script:
- mkdir build
- cd build
- cmake --version
- cmake %CMAKE_ARGS% ..
- cmake %CMAKE_ARGS% --parallel ..
- cmake -LH ..
- cmake --build . --config %Configuration% --verbose --parallel
+27 -35
View File
@@ -1,7 +1,7 @@
version: 2.1
# We constantly run out of memory so please do not use parallelism (-j, -j4).
# We constantly run out of memory so please do not use parallelism (-j, -j4).
# Reusable image / compiler definitions
executors:
@@ -11,7 +11,7 @@ executors:
environment:
CXX: g++-8
CC: gcc-8
CMAKE_BUILD_FLAGS:
BUILD_FLAGS:
CTEST_FLAGS: --output-on-failure
gcc9:
@@ -20,7 +20,7 @@ executors:
environment:
CXX: g++-9
CC: gcc-9
CMAKE_BUILD_FLAGS:
BUILD_FLAGS:
CTEST_FLAGS: --output-on-failure
gcc10:
@@ -29,7 +29,7 @@ executors:
environment:
CXX: g++-10
CC: gcc-10
CMAKE_BUILD_FLAGS:
BUILD_FLAGS:
CTEST_FLAGS: --output-on-failure
clang10:
@@ -38,7 +38,7 @@ executors:
environment:
CXX: clang++-10
CC: clang-10
CMAKE_BUILD_FLAGS:
BUILD_FLAGS:
CTEST_FLAGS: --output-on-failure
clang9:
@@ -47,8 +47,8 @@ executors:
environment:
CXX: clang++-9
CC: clang-9
CMAKE_BUILD_FLAGS:
CTEST_FLAGS: --output-on-failure
BUILD_FLAGS:
CTEST_FLAGS: --output-on-failure
clang6:
docker:
@@ -56,7 +56,7 @@ executors:
environment:
CXX: clang++-6.0
CC: clang-6.0
CMAKE_BUILD_FLAGS:
BUILD_FLAGS:
CTEST_FLAGS: --output-on-failure
# Reusable test commands (and initializer for clang 6)
@@ -88,13 +88,13 @@ commands:
steps:
- cmake_prep
- dependency_restore
- run: cmake -DSIMDJSON_DEVELOPER_MODE=ON $CMAKE_FLAGS -DCMAKE_INSTALL_PREFIX:PATH=destination -B build .
- run: cmake $CMAKE_FLAGS -DCMAKE_INSTALL_PREFIX:PATH=destination -B build .
- dependency_cache # dependencies are produced in the configure step
cmake_build:
steps:
- cmake_build_cache
- run: cmake --build build
- run: cmake --build build
cmake_test:
steps:
@@ -129,8 +129,8 @@ commands:
steps:
- cmake_build_cache
- run: |
cmake -DSIMDJSON_ENABLE_DOM_CHECKPERF=ON --build build --target checkperf &&
cd build &&
cmake --build build --target checkperf &&
cd build &&
ctest --output-on-failure -R checkperf
# we not only want cmake to build and run tests, but we want also a successful installation from which we can build, link and run programs
@@ -164,68 +164,60 @@ jobs:
gcc10-perftest:
description: Build and run performance tests on GCC 10 and AVX 2 with a cmake static build, this test performance regression
executor: gcc10
environment: { CMAKE_FLAGS: -DSIMDJSON_GOOGLE_BENCHMARKS=OFF -DBUILD_SHARED_LIBS=OFF }
environment: { CMAKE_FLAGS: -DSIMDJSON_GOOGLE_BENCHMARKS=OFF -DSIMDJSON_BUILD_STATIC=ON }
steps: [ cmake_perftest ]
gcc10:
description: Build and run tests on GCC 10 and AVX 2 with a cmake static build
executor: gcc10
environment: { CMAKE_FLAGS: -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DBUILD_SHARED_LIBS=OFF }
environment: { CMAKE_FLAGS: -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_BUILD_STATIC=ON }
steps: [ cmake_test, cmake_install_test, cmake_installed_test_cxx20 ]
clang6:
description: Build and run tests on clang 6 and AVX 2 with a cmake static build
executor: clang6
environment: { CMAKE_FLAGS: -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DBUILD_SHARED_LIBS=OFF }
environment: { CMAKE_FLAGS: -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_BUILD_STATIC=ON }
steps: [ cmake_test, cmake_install_test ]
clang10:
description: Build and run tests on clang 10 and AVX 2 with a cmake static build
executor: clang10
environment: { CMAKE_FLAGS: -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DBUILD_SHARED_LIBS=OFF }
environment: { CMAKE_FLAGS: -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_BUILD_STATIC=ON }
steps: [ cmake_test, cmake_install_test, cmake_installed_test_cxx20 ]
# libcpp
libcpp-clang10:
description: Build and run tests on clang 10 and AVX 2 with a cmake static build and libc++
executor: clang10
environment: { CMAKE_FLAGS: -DSIMDJSON_USE_LIBCPP=ON -DBUILD_SHARED_LIBS=OFF }
environment: { CMAKE_FLAGS: -DSIMDJSON_USE_LIBCPP=ON -DSIMDJSON_BUILD_STATIC=ON }
steps: [ cmake_test, cmake_install_test, cmake_installed_test_cxx20 ]
# sanitize
sanitize-gcc10:
description: Build and run tests on GCC 10 and AVX 2 with a cmake sanitize build
executor: gcc10
environment: { CMAKE_FLAGS: -DCMAKE_BUILD_TYPE=Debug -DBUILD_SHARED_LIBS=ON -DSIMDJSON_SANITIZE=ON, CTEST_FLAGS: --output-on-failure -LE explicitonly }
environment: { CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_SANITIZE=ON, BUILD_FLAGS: "", CTEST_FLAGS: --output-on-failure -LE explicitonly }
steps: [ cmake_test ]
sanitize-clang10:
description: Build and run tests on clang 10 and AVX 2 with a cmake sanitize build
executor: clang10
environment: { CMAKE_FLAGS: -DBUILD_SHARED_LIBS=ON -DSIMDJSON_NO_FORCE_INLINING=ON -DSIMDJSON_SANITIZE=ON, CTEST_FLAGS: --output-on-failure -LE explicitonly }
environment: { CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_SANITIZE=ON, CTEST_FLAGS: --output-on-failure -LE explicitonly }
steps: [ cmake_test ]
threadsanitize-gcc10:
description: Build and run tests on GCC 10 and AVX 2 with a cmake sanitize build
executor: gcc10
environment: { CMAKE_FLAGS: -DBUILD_SHARED_LIBS=ON -DSIMDJSON_SANITIZE_THREADS=ON, CTEST_FLAGS: --output-on-failure -LE explicitonly }
environment: { CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_SANITIZE_THREADS=ON, BUILD_FLAGS: "", CTEST_FLAGS: --output-on-failure -LE explicitonly }
steps: [ cmake_test ]
threadsanitize-clang10:
description: Build and run tests on clang 10 and AVX 2 with a cmake sanitize build
executor: clang10
environment: { CMAKE_FLAGS: -DBUILD_SHARED_LIBS=ON -DSIMDJSON_NO_FORCE_INLINING=ON -DSIMDJSON_SANITIZE_THREADS=ON, CTEST_FLAGS: --output-on-failure -LE explicitonly }
steps: [ cmake_test ]
nocheckeof-clang10:
description: Validate that when __SIMDJSON_CHECK_EOF=0, everything still succeeds
environment:
CXXFLAGS: -D__SIMDJSON_CHECK_EOF=0
CMAKE_BUILD_FLAGS: --target ondemand_tests
CTEST_FLAGS: --output-on-failure -R ondemand_
executor: clang10
environment: { CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_SANITIZE_THREADS=ON, CTEST_FLAGS: --output-on-failure -LE explicitonly }
steps: [ cmake_test ]
# dynamic
dynamic-gcc10:
description: Build and run tests on GCC 10 and AVX 2 with a cmake dynamic build
executor: gcc10
environment: { CMAKE_FLAGS: -DBUILD_SHARED_LIBS=ON }
environment: { CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF }
steps: [ cmake_test, cmake_install_test ]
dynamic-clang10:
description: Build and run tests on clang 10 and AVX 2 with a cmake dynamic build
executor: clang10
environment: { CMAKE_FLAGS: -DBUILD_SHARED_LIBS=ON }
environment: { CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF }
steps: [ cmake_test, cmake_install_test ]
# unthreaded
@@ -270,12 +262,12 @@ jobs:
sanitize-haswell-gcc10:
description: Build and run tests on GCC 10 and AVX 2 with a cmake sanitize build
executor: gcc10
environment: { CXXFLAGS: -march=haswell, CMAKE_FLAGS: -DCMAKE_BUILD_TYPE=Debug -DBUILD_SHARED_LIBS=ON -DSIMDJSON_SANITIZE=ON, CTEST_FLAGS: --output-on-failure -LE explicitonly }
environment: { CXXFLAGS: -march=haswell, CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_SANITIZE=ON, BUILD_FLAGS: "", CTEST_FLAGS: --output-on-failure -LE explicitonly }
steps: [ cmake_test ]
sanitize-haswell-clang10:
description: Build and run tests on clang 10 and AVX 2 with a cmake sanitize build
executor: clang10
environment: { CXXFLAGS: -march=haswell, CMAKE_FLAGS: -DBUILD_SHARED_LIBS=ON -DSIMDJSON_NO_FORCE_INLINING=ON -DSIMDJSON_SANITIZE=ON, CTEST_FLAGS: --output-on-failure -LE explicitonly }
environment: { CXXFLAGS: -march=haswell, CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_SANITIZE=ON, CTEST_FLAGS: --output-on-failure -LE explicitonly }
steps: [ cmake_test ]
workflows:
@@ -292,7 +284,7 @@ workflows:
- libcpp-clang10
# full single-implementation tests
# - sanitize-gcc10 # gcc10 + sanitize uses a lot of memory?
- sanitize-gcc10
- sanitize-clang10
- threadsanitize-gcc10
- threadsanitize-clang10
@@ -311,7 +303,7 @@ workflows:
# sanitized single-implementation tests
# - sanitize-haswell-gcc10 # gcc10 + sanitize uses a lot of memory?
- sanitize-haswell-gcc10
- sanitize-haswell-clang10
# testing "just the library"
+2 -2
View File
@@ -19,8 +19,8 @@ task:
build_script:
- mkdir build
- cd build
- cmake -DSIMDJSON_BASH=OFF -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_GIT=OFF ..
- cmake --build .
- cmake -DSIMDJSON_BASH=OFF -DSIMDJSON_GIT=OFF ..
- make
test_script:
- cd build
- ctest --output-on-failure -LE explicitonly
+25 -25
View File
@@ -8,8 +8,8 @@ steps:
CC: gcc
CXX: g++
BUILD_FLAGS: -- -j
CMAKE_FLAGS: -DBUILD_SHARED_LIBS=OFF -DSIMDJSON_DEVELOPER_MODE=ON
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=ON
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
commands:
- scripts/addcmakeppa.sh "$(env -i sh -c '. /etc/os-release; echo $VERSION_CODENAME')"
- apt-get install -y g++ cmake gcc git
@@ -17,7 +17,7 @@ steps:
- cd build
- cmake $CMAKE_FLAGS ..
- cmake --build . $BUILD_FLAGS
- ctest $CTEST_FLAGS
- ctest $CTEST_FLAGS
---
kind: pipeline
name: i386-clang # we do not support 32-bit systems, but we run tests
@@ -29,8 +29,8 @@ steps:
CC: clang-6.0
CXX: clang++-6.0
BUILD_FLAGS: -- -j
CMAKE_FLAGS: -DBUILD_SHARED_LIBS=OFF -DSIMDJSON_DEVELOPER_MODE=ON
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=ON
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
commands:
- scripts/addcmakeppa.sh "$(env -i sh -c '. /etc/os-release; echo $VERSION_CODENAME')"
- apt-get install -y clang++-6.0 cmake git
@@ -38,7 +38,7 @@ steps:
- cd build
- cmake $CMAKE_FLAGS ..
- cmake --build . $BUILD_FLAGS
- ctest $CTEST_FLAGS
- ctest $CTEST_FLAGS
---
kind: pipeline
name: gcc9
@@ -50,7 +50,7 @@ steps:
CC: gcc
CXX: g++
BUILD_FLAGS: -- -j
CMAKE_FLAGS: -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF -DSIMDJSON_IMPLEMENTATION=haswell;westmere;fallback
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=ON -DSIMDJSON_IMPLEMENTATION=haswell;westmere;fallback
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
commands:
- echo "deb http://deb.debian.org/debian buster-backports main" >> /etc/apt/sources.list
@@ -77,7 +77,7 @@ steps:
CC: clang-6.0
CXX: clang++-6.0
BUILD_FLAGS: -- -j
CMAKE_FLAGS: -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF -DSIMDJSON_IMPLEMENTATION=haswell;westmere;fallback
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=ON -DSIMDJSON_IMPLEMENTATION=haswell;westmere;fallback
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
commands:
- mkdir build
@@ -100,7 +100,7 @@ steps:
CC: gcc
CXX: g++
BUILD_FLAGS: -- -j
CMAKE_FLAGS: -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=ON
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
commands:
- echo "deb http://deb.debian.org/debian buster-backports main" >> /etc/apt/sources.list
@@ -122,7 +122,7 @@ steps:
environment:
CC: clang-9
CXX: clang++-9
CMAKE_FLAGS: -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=ON
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF
BUILD_FLAGS: -- -j
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
commands:
@@ -142,7 +142,7 @@ steps:
CC: gcc
CXX: g++
BUILD_FLAGS: -- -j
CMAKE_FLAGS: -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF -DSIMDJSON_IMPLEMENTATION=haswell;westmere;fallback
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=ON -DSIMDJSON_IMPLEMENTATION=haswell;westmere;fallback
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
commands:
- echo "deb http://deb.debian.org/debian buster-backports main" >> /etc/apt/sources.list
@@ -168,7 +168,7 @@ steps:
environment:
CC: clang-9
CXX: clang++-9
CMAKE_FLAGS: -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_NO_FORCE_INLINING=ON -DSIMDJSON_SANITIZE=ON -DSIMDJSON_IMPLEMENTATION=haswell;westmere;fallback
CMAKE_FLAGS: -DSIMDJSON_SANITIZE=ON -DSIMDJSON_IMPLEMENTATION=haswell;westmere;fallback
BUILD_FLAGS: -- -j
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
commands:
@@ -192,7 +192,7 @@ steps:
environment:
CC: clang-11
CXX: clang++-11
CMAKE_FLAGS: -DSIMDJSON_DEVELOPER_MODE=ON -GNinja
CMAKE_FLAGS: -GNinja
BUILD_FLAGS:
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
CXXFLAGS: -std=c++20 -stdlib=libc++
@@ -213,7 +213,7 @@ steps:
CC: gcc
CXX: g++
BUILD_FLAGS: -- -j
CMAKE_FLAGS: -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF -DSIMDJSON_IMPLEMENTATION=arm64;fallback
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=ON -DSIMDJSON_IMPLEMENTATION=arm64;fallback
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
commands:
- echo "deb http://deb.debian.org/debian buster-backports main" >> /etc/apt/sources.list
@@ -237,7 +237,7 @@ steps:
environment:
CC: clang-6.0
CXX: clang++-6.0
CMAKE_FLAGS: -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=ON
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF
BUILD_FLAGS: -- -j
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
commands:
@@ -260,7 +260,7 @@ steps:
CC: gcc
CXX: g++
BUILD_FLAGS: -- -j
CMAKE_FLAGS: -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=ON
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
commands:
- echo "deb http://deb.debian.org/debian buster-backports main" >> /etc/apt/sources.list
@@ -281,7 +281,7 @@ steps:
environment:
CC: clang-6.0
CXX: clang++-6.0
CMAKE_FLAGS: -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=ON
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF
BUILD_FLAGS: -- -j
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
commands:
@@ -302,7 +302,7 @@ steps:
image: gcc:8
environment:
BUILD_FLAGS: -- -j
CMAKE_FLAGS: -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF -DSIMDJSON_IMPLEMENTATION=arm64;fallback
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=ON -DSIMDJSON_IMPLEMENTATION=arm64;fallback
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
CC: gcc
CXX: g++
@@ -329,7 +329,7 @@ steps:
environment:
CC: clang-6.0
CXX: clang++-6.0
CMAKE_FLAGS: -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_NO_FORCE_INLINING=ON -DSIMDJSON_SANITIZE=ON -DSIMDJSON_IMPLEMENTATION=arm64;fallback
CMAKE_FLAGS: -DSIMDJSON_SANITIZE=ON -DSIMDJSON_IMPLEMENTATION=arm64;fallback
BUILD_FLAGS: -- -j
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
commands:
@@ -356,7 +356,7 @@ steps:
CC: clang-9
CXX: clang++-9
BUILD_FLAGS: -- -j 4
CMAKE_FLAGS: -DSIMDJSON_DEVELOPER_MODE=ON -GNinja -DBUILD_SHARED_LIBS=OFF
CMAKE_FLAGS: -GNinja -DSIMDJSON_BUILD_STATIC=ON
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
CXXFLAGS: -stdlib=libc++
commands:
@@ -377,7 +377,7 @@ steps:
CC: clang-9
CXX: clang++-9
BUILD_FLAGS: -- -j
CMAKE_FLAGS: -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=ON
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
CXXFLAGS: -stdlib=libc++
commands:
@@ -398,7 +398,7 @@ steps:
CC: clang-7
CXX: clang++-7
BUILD_FLAGS: -- -j
CMAKE_FLAGS: -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=ON
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
CXXFLAGS: -stdlib=libc++
commands:
@@ -418,7 +418,7 @@ steps:
CC: gcc
CXX: g++
BUILD_FLAGS: -- -j
CMAKE_FLAGS: -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_EXCEPTIONS=OFF
CMAKE_FLAGS: -DSIMDJSON_EXCEPTIONS=OFF
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
commands:
- echo "deb http://deb.debian.org/debian buster-backports main" >> /etc/apt/sources.list
@@ -444,8 +444,8 @@ steps:
commands:
- apt-get -qq update
- apt-get install -q -y clang cmake git wget zip ninja-build
- wget -O corpus.tar.gz https://readonly:readonly@www.pauldreik.se/fuzzdata/index.php?project=simdjson
- tar xf corpus.tar.gz && rm corpus.tar.gz
- wget --quiet https://dl.bintray.com/pauldreik/simdjson-fuzz-corpus/corpus/corpus.tar
- tar xf corpus.tar && rm corpus.tar
- fuzz/build_like_ossfuzz.sh
- mkdir -p common_out
- for fuzzer in build/fuzz/fuzz_* ; do echo $fuzzer;$fuzzer common_out out/* -max_total_time=40; done
+1 -1
View File
@@ -3,7 +3,7 @@
* text=auto
# we don't want json files to be modified for this project
*.json binary diff=astextplain
*.json binary
# Common settings that generally should always be used with your language specific settings
+4 -19
View File
@@ -18,34 +18,19 @@ Before submitting an issue, please ensure that you have read the documentation:
**Describe the bug**
A clear and concise description of what the bug is.
A compiler or static-analyzer warning is not a bug.
We are committed to providing good documentation. We accept the lack of documentation or a misleading documentation as a bug (a 'documentation bug').
We accept the identification of an issue by a sanitizer or some checker tool (e.g., valgrind) as a bug, but you must first ensure that it is not a false positive.
We recommend that you run your tests using different optimization levels.
Before reporting a bug, please ensure that you have read our documentation.
Note that a compiler warning is not a bug.
**To Reproduce**
Steps to reproduce the behaviour: provide a code sample if possible.
If we cannot reproduce the issue, then we cannot address it. Note that a stack trace from your own program is not enough. A sample of your source code is insufficient: please provide a complete test for us to reproduce the issue. Please reduce the issue: use as small and as simple an example of the bug as possible.
If we cannot reproduce the issue, then we cannot address it.
It should be possible to trigger the bug by using solely simdjson with our default build setup. If you can only observe the bug within some specific context, with some other software, please reduce the issue first.
Note that a stack trace from your own program is not enough.
**simjson release**
Unless you plan to contribute to simdjson, you should only work from releases. Please be mindful that our main branch may have additional features, bugs and documentation items.
It is fine to report bugs against our main branch, but if that is what you are doing, please be explicit.
**Configuration (please complete the following information if relevant)**
**Configuration (please complete the following information if relevant):**
- OS: [e.g. Ubuntu 16.04.6 LTS]
- Compiler [e.g. Apple clang version 11.0.3 (clang-1103.0.32.59) x86_64-apple-darwin19.4.0]
- Version [e.g. 22]
- Optimization setting (e.g., -O3)
We support up-to-date 64-bit ARM and x64 FreeBSD, macOS, Windows and Linux systems. Please ensure that your configuration is supported before labelling the issue as a bug. In particular, we do not support legacy 32-bit systems.
+8 -2
View File
@@ -1,6 +1,12 @@
name: Alpine Linux
on: [push, pull_request]
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
ubuntu-build:
@@ -25,7 +31,7 @@ jobs:
./alpine.sh apk add build-base cmake g++ linux-headers git bash
- name: cmake
run: |
./alpine.sh cmake -DSIMDJSON_DEVELOPER_MODE=ON -B build_for_alpine
./alpine.sh cmake -B build_for_alpine
- name: build
run: |
./alpine.sh cmake --build build_for_alpine
@@ -1,6 +1,9 @@
name: Detect trailing whitespace
on: [push, pull_request]
on:
pull_request:
branches:
- master
jobs:
whitespace:
+16 -18
View File
@@ -44,18 +44,6 @@ jobs:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
- uses: actions/cache@v2
id: cache-corpus
with:
path: out/
key: corpus-${{ github.run_id }}
restore-keys: corpus-
- name: show statistics for the cached corpus
run: |
echo number of files in github action corpus cache:
find out -type f |wc -l
- name: Create and prepare the initial seed corpus
run: |
fuzz/build_corpus.sh
@@ -63,6 +51,12 @@ jobs:
mkdir seedcorpus
unzip -q -d seedcorpus seed_corpus.zip
- name: Download the corpus from the last run
run: |
wget --quiet https://dl.bintray.com/pauldreik/simdjson-fuzz-corpus/corpus/corpus.tar
tar xf corpus.tar
rm corpus.tar
- name: List clang versions
run: |
ls /usr/bin/clang*
@@ -76,7 +70,7 @@ jobs:
run: |
set -eux
for fuzzer in $defaultimplfuzzers $implfuzzers; do
mkdir -p out/$fuzzer # in case this is a new fuzzer, or the github action cached corpus is broken
mkdir -p out/$fuzzer # in case this is a new fuzzer, or corpus.tar is broken
# get input from everyone else (corpus cross pollination)
others=$(find out -type d -not -name $fuzzer -not -name out -not -name cmin)
build-fast/fuzz/fuzz_$fuzzer out/$fuzzer $others seedcorpus -max_total_time=30 $MAXLEN
@@ -129,11 +123,6 @@ jobs:
name: corpus
path: corpus.tar
- name: Store the corpus externally
run: |
gzip --keep corpus.tar
curl -F"filedata=@corpus.tar.gz" https://simdjson:${{ secrets.fuzzdatapassword }}@www.pauldreik.se/fuzzdata/index.php
# This takes a subset of the minimized corpus and run it through valgrind. It is slow,
# therefore take a "random" subset. The random selection is accomplished by sorting on filenames,
# which are hashes of the content.
@@ -154,6 +143,15 @@ jobs:
path: valgrind.tar
if-no-files-found: ignore
- name: Upload the corpus and results to bintray if we are on master
if: ${{ github.event_name == 'schedule' }}
run: |
echo uploading each artifact twice, otherwise it will not be published
curl -T corpus.tar -upauldreik:${{ secrets.bintrayApiKey }} https://api.bintray.com/content/pauldreik/simdjson-fuzz-corpus/corpus/0/corpus/corpus.tar";publish=1;override=1"
curl -T corpus.tar -upauldreik:${{ secrets.bintrayApiKey }} https://api.bintray.com/content/pauldreik/simdjson-fuzz-corpus/corpus/0/corpus/corpus.tar";publish=1;override=1"
curl -T valgrind.tar -upauldreik:${{ secrets.bintrayApiKey }} https://api.bintray.com/content/pauldreik/simdjson-fuzz-corpus/corpus/0/corpus/valgrind.tar";publish=1;override=1"
curl -T valgrind.tar -upauldreik:${{ secrets.bintrayApiKey }} https://api.bintray.com/content/pauldreik/simdjson-fuzz-corpus/corpus/0/corpus/valgrind.tar";publish=1;override=1"
- name: Archive any crashes as an artifact
uses: actions/upload-artifact@v2
if: always()
+10 -5
View File
@@ -1,7 +1,12 @@
name: MinGW32-CI
on: [push, pull_request]
on:
push:
branches:
- master
pull_request:
branches:
- master
# Important: scoop will either install 32-bit GCC or 64-bit GCC, not both.
@@ -17,7 +22,7 @@ jobs:
runs-on: windows-2016
env:
CMAKE_GENERATOR: Ninja # This is critical, try ' cmake -GNinja-DBUILD_SHARED_LIBS=OFF .. ' if using the command line
CMAKE_GENERATOR: Ninja # This is critical, try ' cmake -GNinja-DSIMDJSON_BUILD_STATIC=ON .. ' if using the command line
CC: gcc
CXX: g++
@@ -42,7 +47,7 @@ jobs:
run: |
Invoke-Expression (New-Object System.Net.WebClient).DownloadString('https://get.scoop.sh')
scoop install sudo --global
sudo scoop install git --global
sudo scoop install git --global
sudo scoop install ninja --global
sudo scoop install cmake --global
sudo scoop install gcc --arch 32bit --global
@@ -58,6 +63,6 @@ jobs:
git --version
mkdir build32
cd build32
cmake -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF -DSIMDJSON_COMPETITION=OFF -DSIMDJSON_GOOGLE_BENCHMARKS=OFF -DSIMDJSON_ENABLE_THREADS=OFF ..
cmake -DSIMDJSON_BUILD_STATIC=ON -DSIMDJSON_COMPETITION=OFF -DSIMDJSON_GOOGLE_BENCHMARKS=OFF -DSIMDJSON_ENABLE_THREADS=OFF ..
cmake --build . --target acceptance_tests --verbose
ctest -L acceptance --output-on-failure
+10 -5
View File
@@ -1,7 +1,12 @@
name: MinGW64-CI
on: [push, pull_request]
on:
push:
branches:
- master
pull_request:
branches:
- master
# Important: scoop will either install 32-bit GCC or 64-bit GCC, not both.
@@ -17,7 +22,7 @@ jobs:
runs-on: windows-2016
env:
CMAKE_GENERATOR: Ninja # This is critical, try ' cmake -GNinja-DBUILD_SHARED_LIBS=OFF .. ' if using the command line
CMAKE_GENERATOR: Ninja # This is critical, try ' cmake -GNinja-DSIMDJSON_BUILD_STATIC=ON .. ' if using the command line
CC: gcc
CXX: g++
@@ -58,12 +63,12 @@ jobs:
git --version
mkdir build64
cd build64
cmake -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF -DSIMDJSON_COMPETITION=OFF -DSIMDJSON_GOOGLE_BENCHMARKS=OFF -DSIMDJSON_ENABLE_THREADS=OFF ..
cmake -DSIMDJSON_BUILD_STATIC=ON -DSIMDJSON_COMPETITION=OFF -DSIMDJSON_GOOGLE_BENCHMARKS=OFF -DSIMDJSON_ENABLE_THREADS=OFF ..
cmake --build . --target acceptance_tests --verbose
ctest -L acceptance --output-on-failure
cd ..
mkdir build64debug
cd build64debug
cmake -DSIMDJSON_DEVELOPER_MODE=ON -DCMAKE_BUILD_TYPE=Debug -DBUILD_SHARED_LIBS=OFF -DSIMDJSON_COMPETITION=OFF -DSIMDJSON_GOOGLE_BENCHMARKS=OFF -DSIMDJSON_ENABLE_THREADS=OFF ..
cmake -DCMAKE_BUILD_TYPE=Debug -DSIMDJSON_BUILD_STATIC=ON -DSIMDJSON_COMPETITION=OFF -DSIMDJSON_GOOGLE_BENCHMARKS=OFF -DSIMDJSON_ENABLE_THREADS=OFF ..
cmake --build . --target acceptance_tests --verbose
ctest -L acceptance --output-on-failure
+12 -7
View File
@@ -1,7 +1,12 @@
name: MSYS2-CLANG-CI
on: [push, pull_request]
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
windows-mingw:
@@ -15,16 +20,16 @@ jobs:
matrix:
include:
- msystem: "MINGW64"
install: mingw-w64-x86_64-libxml2 mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-clang
install: mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-clang
type: Release
- msystem: "MINGW32"
install: mingw-w64-i686-libxml2 mingw-w64-i686-cmake mingw-w64-i686-ninja mingw-w64-i686-clang
install: mingw-w64-i686-cmake mingw-w64-i686-ninja mingw-w64-i686-clang
type: Release
- msystem: "MINGW64"
install: mingw-w64-x86_64-libxml2 mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-clang
install: mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-clang
type: Debug
- msystem: "MINGW32"
install: mingw-w64-i686-libxml2 mingw-w64-i686-cmake mingw-w64-i686-ninja mingw-w64-i686-clang
install: mingw-w64-i686-cmake mingw-w64-i686-ninja mingw-w64-i686-clang
type: Debug
env:
CMAKE_GENERATOR: Ninja
@@ -44,6 +49,6 @@ jobs:
run: |
mkdir build
cd build
cmake -DSIMDJSON_DEVELOPER_MODE=ON -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_BUILD_TYPE=${{ matrix.type }} -DSIMDJSON_BUILD_STATIC=ON -DSIMDJSON_DO_NOT_USE_THREADS_NO_MATTER_WHAT=ON ..
cmake -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_BUILD_TYPE=${{ matrix.type }} -DSIMDJSON_BUILD_STATIC=ON -DSIMDJSON_DO_NOT_USE_THREADS_NO_MATTER_WHAT=ON ..
cmake --build . --verbose
ctest -j4 --output-on-failure -LE explicitonly
+8 -2
View File
@@ -1,6 +1,12 @@
name: MSYS2-CI
on: [push, pull_request]
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
windows-mingw:
@@ -46,6 +52,6 @@ jobs:
run: |
mkdir build
cd build
cmake -DSIMDJSON_DEVELOPER_MODE=ON -DCMAKE_BUILD_TYPE=${{ matrix.type }} -DBUILD_SHARED_LIBS=OFF -DSIMDJSON_DO_NOT_USE_THREADS_NO_MATTER_WHAT=ON ..
cmake -DCMAKE_BUILD_TYPE=${{ matrix.type }} -DSIMDJSON_BUILD_STATIC=ON -DSIMDJSON_DO_NOT_USE_THREADS_NO_MATTER_WHAT=ON ..
cmake --build . --verbose
ctest -j4 --output-on-failure -LE explicitonly
+5 -11
View File
@@ -32,19 +32,13 @@ jobs:
run: |
export CLANGSUFFIX="-7"
apt-get -qq update
apt-get install -q -y clang-7 libfuzzer-7-dev git wget zip ninja-build gnupg software-properties-common
wget -q -O - "https://raw.githubusercontent.com/simdjson/debian-ppa/master/key.gpg" | apt-key add -
apt-add-repository "deb https://raw.githubusercontent.com/simdjson/debian-ppa/master simdjson main"
apt-get -qq update
apt-get purge cmake cmake-data
apt-get -t simdjson -y install cmake
apt-get install -q -y clang-7 libfuzzer-7-dev cmake git wget zip ninja-build
mkdir -p build ; cd build
cmake .. -GNinja \
-DCMAKE_CXX_COMPILER=clang++$CLANGSUFFIX \
-DCMAKE_C_COMPILER=clang$CLANGSUFFIX \
-DBUILD_SHARED_LIBS=OFF \
-DSIMDJSON_DEVELOPER_MODE=ON \
-DSIMDJSON_ENABLE_FUZZING=On \
-DSIMDJSON_BUILD_STATIC=Off \
-DENABLE_FUZZING=On \
-DSIMDJSON_COMPETITION=OFF \
-DSIMDJSON_GOOGLE_BENCHMARKS=OFF \
-DSIMDJSON_DISABLE_DEPRECATED_API=On \
@@ -56,8 +50,8 @@ jobs:
cd ..
builddir=build
cmake --build $builddir
wget -O corpus.tar.gz https://readonly:readonly@www.pauldreik.se/fuzzdata/index.php?project=simdjson
tar xf corpus.tar.gz
wget --quiet https://dl.bintray.com/pauldreik/simdjson-fuzz-corpus/corpus/corpus.tar
tar xf corpus.tar && rm corpus.tar
fuzzernames=$(cmake --build $builddir --target print_all_fuzzernames |tail -n1)
for fuzzer in $fuzzernames ; do
exe=$builddir/fuzz/$fuzzer
+1 -1
View File
@@ -24,6 +24,6 @@ jobs:
run: |
mkdir build &&
cd build &&
cmake -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_ENABLE_DOM_CHECKPERF=ON -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
cmake -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_BUILD_STATIC=ON -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
cmake --build . --target checkperf &&
ctest --output-on-failure -R checkperf ubuntu18-checkperf.yml
+9 -4
View File
@@ -1,7 +1,12 @@
name: Ubuntu 18.04 CI (GCC 7) with Thread Sanitizer
on: [push, pull_request]
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
ubuntu-build:
@@ -19,7 +24,7 @@ jobs:
run: |
mkdir build &&
cd build &&
cmake -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_SANITIZE_THREADS=ON .. &&
cmake --build . --target document_stream_tests --target ondemand_document_stream_tests --target parse_many_test &&
cmake -DSIMDJSON_SANITIZE_THREADS=ON .. &&
cmake --build . --target document_stream_tests --target parse_many_test &&
ctest --output-on-failure -R parse_many_test &&
ctest --output-on-failure -R document_stream_tests
+8 -2
View File
@@ -1,6 +1,12 @@
name: Ubuntu 18.04 CI (GCC 7)
on: [push, pull_request]
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
ubuntu-build:
@@ -18,7 +24,7 @@ jobs:
run: |
mkdir build &&
cd build &&
cmake -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
cmake -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_BUILD_STATIC=ON -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
cmake --build . &&
ctest -j --output-on-failure -LE explicitonly &&
make install &&
+2 -2
View File
@@ -24,6 +24,6 @@ jobs:
run: |
mkdir build &&
cd build &&
cmake -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_ENABLE_DOM_CHECKPERF=ON -DCMAKE_CXX_FLAGS="-Werror=old-style-cast -pedantic -Wpedantic" -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
cmake -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_BUILD_STATIC=ON -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
cmake --build . --target checkperf &&
ctest --output-on-failure -R checkperf
ctest --output-on-failure -R checkperf
-34
View File
@@ -1,34 +0,0 @@
name: Ubuntu 20.04 CI (GCC 9) without exceptions
on: [push, pull_request]
jobs:
ubuntu-build:
if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]')
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- uses: actions/cache@v2
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
- name: Use cmake
run: |
mkdir builddebug &&
cd builddebug &&
cmake -DCMAKE_BUILD_TYPE=Debug -DSIMDJSON_GOOGLE_BENCHMARKS=OFF -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_EXCEPTIONS=OFF -DBUILD_SHARED_LIBS=OFF .. &&
cmake --build . &&
ctest -j --output-on-failure -LE explicitonly &&
cd .. &&
mkdir build &&
cd build &&
cmake -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_EXCEPTIONS=OFF -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
cmake --build . &&
ctest -j --output-on-failure -LE explicitonly &&
make install &&
echo -e '#include <simdjson.h>\nint main(int argc,char**argv) {simdjson::dom::parser parser;simdjson::dom::element tweets = parser.load(argv[1]); }' > tmp.cpp && c++ -Idestination/include -Ldestination/lib -std=c++17 -Wl,-rpath,destination/lib -o linkandrun tmp.cpp -lsimdjson && ./linkandrun jsonexamples/twitter.json &&
mkdir testfindpackage &&
cd testfindpackage &&
echo -e 'cmake_minimum_required(VERSION 3.1)\nproject(simdjsontester)\nset(CMAKE_CXX_STANDARD 17)\nfind_package(simdjson REQUIRED)'> CMakeLists.txt && mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../destination .. && cmake --build .
-34
View File
@@ -1,34 +0,0 @@
name: Ubuntu 20.04 CI (GCC 9) Without Threads
on: [push, pull_request]
jobs:
ubuntu-build:
if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]')
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- uses: actions/cache@v2
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
- name: Use cmake
run: |
mkdir builddebug &&
cd builddebug &&
cmake -DCMAKE_BUILD_TYPE=Debug -DSIMDJSON_GOOGLE_BENCHMARKS=OFF -DSIMDJSON_ENABLE_THREADS=OFF -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF .. &&
cmake --build . &&
ctest -j --output-on-failure -LE explicitonly &&
cd .. &&
mkdir build &&
cd build &&
cmake -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_ENABLE_THREADS=OFF -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
cmake --build . &&
ctest -j --output-on-failure -LE explicitonly &&
make install &&
echo -e '#include <simdjson.h>\nint main(int argc,char**argv) {simdjson::dom::parser parser;simdjson::dom::element tweets = parser.load(argv[1]); }' > tmp.cpp && c++ -Idestination/include -Ldestination/lib -std=c++17 -Wl,-rpath,destination/lib -o linkandrun tmp.cpp -lsimdjson && ./linkandrun jsonexamples/twitter.json &&
mkdir testfindpackage &&
cd testfindpackage &&
echo -e 'cmake_minimum_required(VERSION 3.1)\nproject(simdjsontester)\nset(CMAKE_CXX_STANDARD 17)\nfind_package(simdjson REQUIRED)'> CMakeLists.txt && mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../destination .. && cmake --build .
-23
View File
@@ -1,23 +0,0 @@
name: Ubuntu 20.04 CI (GCC 9) With Memory Sanitizer
on: [push, pull_request]
jobs:
ubuntu-build:
if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]')
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- uses: actions/cache@v2
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
- name: Use cmake
run: |
mkdir builddebug &&
cd builddebug &&
cmake -DSIMDJSON_SANITIZE=ON -DCMAKE_BUILD_TYPE=Debug -DSIMDJSON_GOOGLE_BENCHMARKS=OFF -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF .. &&
cmake --build . &&
ctest -j --output-on-failure -LE explicitonly
+9 -3
View File
@@ -1,6 +1,12 @@
name: Ubuntu 20.04 CI (GCC 9) with Thread Sanitizer
on: [push, pull_request]
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
ubuntu-build:
@@ -18,7 +24,7 @@ jobs:
run: |
mkdir build &&
cd build &&
cmake -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_SANITIZE_THREADS=ON .. &&
cmake --build . --target document_stream_tests --target ondemand_document_stream_tests --target parse_many_test &&
cmake -DSIMDJSON_SANITIZE_THREADS=ON .. &&
cmake --build . --target document_stream_tests --target parse_many_test &&
ctest --output-on-failure -R parse_many_test &&
ctest --output-on-failure -R document_stream_tests
+10 -12
View File
@@ -1,6 +1,12 @@
name: Ubuntu 20.04 CI (GCC 9)
on: [push, pull_request]
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
ubuntu-build:
@@ -16,18 +22,10 @@ jobs:
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
- name: Use cmake
run: |
mkdir builddebug &&
cd builddebug &&
cmake -DCMAKE_BUILD_TYPE=Debug -DSIMDJSON_GOOGLE_BENCHMARKS=OFF -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF .. &&
cmake --build . &&
ctest -j --output-on-failure -LE explicitonly &&
cd .. &&
mkdir build &&
cd build &&
cmake -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
cmake -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_BUILD_STATIC=ON -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
cmake --build . &&
ctest -j --output-on-failure -LE explicitonly &&
cmake --install . &&
echo -e '#include <simdjson.h>\nint main(int argc,char**argv) {simdjson::dom::parser parser;simdjson::dom::element tweets = parser.load(argv[1]); }' > tmp.cpp && c++ -Idestination/include -Ldestination/lib -std=c++17 -Wl,-rpath,destination/lib -o linkandrun tmp.cpp -lsimdjson && ./linkandrun jsonexamples/twitter.json &&
cd ../tests/installation_tests/find &&
mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../../../build/destination .. && cmake --build .
make install &&
echo -e '#include <simdjson.h>\nint main(int argc,char**argv) {simdjson::dom::parser parser;simdjson::dom::element tweets = parser.load(argv[1]); }' > tmp.cpp && c++ -Idestination/include -Ldestination/lib -std=c++17 -Wl,-rpath,destination/lib -o linkandrun tmp.cpp -lsimdjson && ./linkandrun jsonexamples/twitter.json
-44
View File
@@ -1,44 +0,0 @@
name: VS15-CI
on: [push, pull_request]
jobs:
ci:
if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]')
name: windows-vs15
runs-on: windows-2016
strategy:
fail-fast: false
matrix:
include:
- {gen: Visual Studio 15 2017, arch: Win32, static: ON}
- {gen: Visual Studio 15 2017, arch: Win32, static: OFF}
- {gen: Visual Studio 15 2017, arch: x64, static: ON}
- {gen: Visual Studio 15 2017, arch: x64, static: OFF}
steps:
- name: checkout
uses: actions/checkout@v2
- name: Configure
run: |
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -DSIMDJSON_COMPETITION=OFF -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_BUILD_STATIC=${{matrix.static}} -B build
- name: Build Debug
run: cmake --build build --config Debug --verbose
- name: Build Release
run: cmake --build build --config Release --verbose
- name: Run Release tests
run: |
cd build
ctest -C Release -LE explicitonly --output-on-failure
- name: Run Debug tests
run: |
cd build
ctest -C Debug -LE explicitonly --output-on-failure
- name: Install
run: |
cmake --install build --config Release
- name: Test Installation
run: |
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -B build_install_test tests/installation_tests/find
cmake --build build_install_test --config Release
-21
View File
@@ -1,21 +0,0 @@
name: VS16-ARM-CI
on: [push, pull_request]
jobs:
ci:
name: windows-vs16
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
include:
- {arch: ARM}
- {arch: ARM64}
steps:
- name: checkout
uses: actions/checkout@v2
- name: Use cmake
run: |
cmake -A ${{ matrix.arch }} -DCMAKE_CROSSCOMPILING=1 -DSIMDJSON_DEVELOPER_MODE=ON -D SIMDJSON_GOOGLE_BENCHMARKS=OFF -DSIMDJSON_EXCEPTIONS=OFF -B build &&
cmake --build build --verbose
+28 -33
View File
@@ -1,6 +1,12 @@
name: VS16-CI
on: [push, pull_request]
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
ci:
@@ -9,36 +15,25 @@ jobs:
! contains(toJSON(github.event.commits.*.message), '[skip github]')
name: windows-vs16
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
include:
- {gen: Visual Studio 16 2019, arch: Win32, static: ON}
- {gen: Visual Studio 16 2019, arch: Win32, static: OFF}
- {gen: Visual Studio 16 2019, arch: x64, static: ON}
- {gen: Visual Studio 16 2019, arch: x64, static: OFF}
steps:
- name: checkout
uses: actions/checkout@v2
- name: Configure
run: |
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_COMPETITION=OFF -DSIMDJSON_BUILD_STATIC=${{matrix.static}} -B build
- name: Build Debug
run: cmake --build build --config Debug --verbose
- name: Build Release
run: cmake --build build --config Release --verbose
- name: Run Release tests
run: |
cd build
ctest -C Release -LE explicitonly --output-on-failure
- name: Run Debug tests
run: |
cd build
ctest -C Debug -LE explicitonly --output-on-failure
- name: Install
run: |
cmake --install build --config Release
- name: Test Installation
run: |
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -B build_install_test tests/installation_tests/find
cmake --build build_install_test --config Release
- uses: actions/checkout@v2
- uses: actions/cache@v2
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
- name: 'Run CMake with VS16'
uses: lukka/run-cmake@v2
with:
cmakeListsOrSettingsJson: CMakeListsTxtAdvanced
cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt'
buildDirectory: "${{ github.workspace }}/../../_temp/windows"
cmakeBuildType: Release
buildWithCMake: true
cmakeGenerator: VS16Win64
cmakeAppendedArgs: -DSIMDJSON_COMPETITION=OFF
buildWithCMakeArgs: --config Release
- name: 'Run CTest'
run: ctest -C Release -LE explicitonly --output-on-failure
working-directory: "${{ github.workspace }}/../../_temp/windows"
+16 -9
View File
@@ -1,6 +1,12 @@
name: VS16-CLANG-CI
on: [push, pull_request]
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
ci:
@@ -15,17 +21,18 @@ jobs:
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
- name: 'Run CMake with VS16 Clang'
uses: lukka/run-cmake@v3
- name: 'Run CMake with VS16'
uses: lukka/run-cmake@v2
with:
cmakeListsOrSettingsJson: CMakeListsTxtAdvanced
cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt'
buildDirectory: "${{ github.workspace }}/../../_temp/windows"
cmakeBuildType: Release
cmakeBuildType: Release
buildWithCMake: true
cmakeAppendedArgs: -T ClangCL -DSIMDJSON_COMPETITION=OFF -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF
buildWithCMakeArgs: --config Release
cmakeGenerator: VS16Win64
cmakeAppendedArgs: -T ClangCL -DSIMDJSON_COMPETITION=OFF -DSIMDJSON_BUILD_STATIC=ON
buildWithCMakeArgs: --config Release
- name: 'Run CTest'
run: ctest -C Release -LE explicitonly --output-on-failure
working-directory: "${{ github.workspace }}/../../_temp/windows"
run: ctest -C Release -LE explicitonly --output-on-failure
working-directory: "${{ github.workspace }}/../../_temp/windows"
+14 -20
View File
@@ -1,6 +1,12 @@
name: VS16-Ninja-CI
on: [push, pull_request]
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
ci:
@@ -21,25 +27,13 @@ jobs:
cmakeListsOrSettingsJson: CMakeListsTxtAdvanced
cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt'
buildDirectory: "${{ github.workspace }}/../../_temp/windows"
cmakeBuildType: Release
cmakeBuildType: Release
buildWithCMake: true
cmakeAppendedArgs: -G Ninja -DSIMDJSON_COMPETITION=OFF -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF
buildWithCMakeArgs: --config Release
cmakeGenerator: VS16Win64
cmakeAppendedArgs: -G Ninja -DSIMDJSON_COMPETITION=OFF -DSIMDJSON_BUILD_STATIC=ON
buildWithCMakeArgs: --config Release
- name: 'Run CTest'
run: ctest -C Release -LE explicitonly --output-on-failure
run: ctest -C Release -LE explicitonly --output-on-failure
working-directory: "${{ github.workspace }}/../../_temp/windows"
- name: 'Install with CMake'
uses: lukka/run-cmake@v3
with:
cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt'
buildWithCMakeArgs: '--target install'
- name: 'Test Installation with CMake'
uses: lukka/run-cmake@v3
with:
cmakeListsOrSettingsJson: CMakeListsTxtAdvanced
cmakeListsTxtPath: '${{ github.workspace }}/tests/installation_tests/find/CMakeLists.txt'
cmakeBuildType: Release
buildWithCMake: true
buildDirectory: '${{ github.workspace }}/tests/installation_tests/find/buildDirectory'
cmakeAppendedArgs: -G Ninja
buildWithCMakeArgs: '--config Release --verbose'
+15 -9
View File
@@ -1,6 +1,12 @@
name: VS16-NoExcept-CI
on: [push, pull_request]
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
ci:
@@ -13,18 +19,18 @@ jobs:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
- name: 'Run CMake with VS16'
uses: lukka/run-cmake@v3
uses: lukka/run-cmake@v2
with:
cmakeListsOrSettingsJson: CMakeListsTxtAdvanced
cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt'
buildDirectory: "${{ github.workspace }}/../../_temp/windows"
cmakeBuildType: Release
cmakeBuildType: Release
buildWithCMake: true
cmakeGenerator: VS16Win64
cmakeAppendedArgs: -DSIMDJSON_COMPETITION=OFF -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_EXCEPTIONS=OFF
buildWithCMakeArgs: --config Release
cmakeGenerator: VS16Win64
cmakeAppendedArgs: -DSIMDJSON_COMPETITION=OFF -DSIMDJSON_EXCEPTIONS=OFF
buildWithCMakeArgs: --config Release
- name: 'Run CTest'
run: ctest -C Release -LE explicitonly --output-on-failure
run: ctest -C Release -LE explicitonly --output-on-failure
working-directory: "${{ github.workspace }}/../../_temp/windows"
-7
View File
@@ -97,10 +97,3 @@ objs
# Generated docs
/doc/api
*.orig
# VSCode workspace files
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
+27 -29
View File
@@ -48,18 +48,17 @@ matrix:
- COMPILER="CC=gcc-10 && CXX=g++-10"
compiler: gcc-10
# The sanitizer runs fail systematically
# - os: linux
# addons:
# apt:
# sources:
# - ubuntu-toolchain-r-test
# packages:
# - g++-10
# env:
# - COMPILER="CC=gcc-10 && CXX=g++-10"
# - SANITIZE="on"
# compiler: gcc-10-sanitize
- os: linux
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-10
env:
- COMPILER="CC=gcc-10 && CXX=g++-10"
- SANITIZE="on"
compiler: gcc-10-sanitize
- os: linux
addons:
@@ -144,20 +143,19 @@ matrix:
- STATIC="on"
compiler: clang-10-static
# The clang sanitizer runs fail frequently at setup time
# - os: linux
# addons:
# apt:
# packages:
# - clang-10
# sources:
# - ubuntu-toolchain-r-test
# - sourceline: 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-10 main'
# key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key'
# env:
# - COMPILER="CC=clang-10 && CXX=clang++-10"
# - SANITIZE="on"
# compiler: clang-10-sanitize
- os: linux
addons:
apt:
packages:
- clang-10
sources:
- ubuntu-toolchain-r-test
- sourceline: 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-10 main'
key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key'
env:
- COMPILER="CC=clang-10 && CXX=clang++-10"
- SANITIZE="on"
compiler: clang-10-sanitize
before_install:
- eval "${COMPILER}"
@@ -172,18 +170,18 @@ install:
- export CMAKE_C_FLAGS="${CMAKE_CXX_FLAGS}"
- export CMAKE_FLAGS="-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS} -DCMAKE_C_FLAGS=${CMAKE_C_FLAGS} -DSIMDJSON_IMPLEMENTATION=ppc64;fallback";
- if [[ "${SANITIZE}" == "on" ]]; then
export CMAKE_FLAGS="${CMAKE_FLAGS} -DSIMDJSON_NO_FORCE_INLINING=ON -DSIMDJSON_SANITIZE=ON";
export CMAKE_FLAGS="${CMAKE_FLAGS} -DSIMDJSON_SANITIZE=ON";
export ASAN_OPTIONS="detect_leaks=0";
fi
- if [[ "${STATIC}" == "on" ]]; then
export CMAKE_FLAGS="${CMAKE_FLAGS} -DBUILD_SHARED_LIBS=OFF";
export CMAKE_FLAGS="${CMAKE_FLAGS} -DSIMDJSON_BUILD_STATIC=ON";
fi
- export CTEST_FLAGS="-j4 --output-on-failure -LE explicitonly"
script:
- mkdir build
- cd build
- cmake -DSIMDJSON_DEVELOPER_MODE=ON $CMAKE_FLAGS ..
- cmake $CMAKE_FLAGS ..
- cmake --build . -- -j2
- SIMDJSON_FORCE_IMPLEMENTATION=ppc64 ctest $CTEST_FLAGS -L per_implementation
- SIMDJSON_FORCE_IMPLEMENTATION=fallback ctest $CTEST_FLAGS -L per_implementation
-17
View File
@@ -1,17 +0,0 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
// List of extensions which should be recommended for users of this workspace.
"recommendations": [
// Syntax
"ms-vscode.cpptools",
"ms-vscode.cmake-tools",
"ms-python.python",
"twxs.cmake"
],
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
"unwantedRecommendations": [
]
}
-90
View File
@@ -1,90 +0,0 @@
{
"editor.rulers": [
{"column": 95 },
{"column": 120 }
],
"files.trimTrailingWhitespace": true,
"files.associations": {
"array": "cpp",
"iterator": "cpp",
"chrono": "cpp",
"optional": "cpp",
"__locale": "cpp",
"__tuple": "cpp",
"__bit_reference": "cpp",
"__config": "cpp",
"__debug": "cpp",
"__errc": "cpp",
"__functional_base": "cpp",
"__hash_table": "cpp",
"__mutex_base": "cpp",
"__node_handle": "cpp",
"__nullptr": "cpp",
"__split_buffer": "cpp",
"__string": "cpp",
"__threading_support": "cpp",
"__tree": "cpp",
"algorithm": "cpp",
"atomic": "cpp",
"bit": "cpp",
"bitset": "cpp",
"cctype": "cpp",
"cinttypes": "cpp",
"clocale": "cpp",
"cmath": "cpp",
"codecvt": "cpp",
"complex": "cpp",
"condition_variable": "cpp",
"cstdarg": "cpp",
"cstddef": "cpp",
"cstdint": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"cstring": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"cwctype": "cpp",
"deque": "cpp",
"exception": "cpp",
"forward_list": "cpp",
"fstream": "cpp",
"functional": "cpp",
"initializer_list": "cpp",
"iomanip": "cpp",
"ios": "cpp",
"iosfwd": "cpp",
"iostream": "cpp",
"istream": "cpp",
"limits": "cpp",
"list": "cpp",
"locale": "cpp",
"map": "cpp",
"memory": "cpp",
"mutex": "cpp",
"new": "cpp",
"numeric": "cpp",
"ostream": "cpp",
"random": "cpp",
"ratio": "cpp",
"regex": "cpp",
"set": "cpp",
"sstream": "cpp",
"stack": "cpp",
"stdexcept": "cpp",
"streambuf": "cpp",
"string": "cpp",
"string_view": "cpp",
"system_error": "cpp",
"thread": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"typeinfo": "cpp",
"unordered_map": "cpp",
"unordered_set": "cpp",
"utility": "cpp",
"valarray": "cpp",
"vector": "cpp",
"*.ipp": "cpp",
"filesystem": "cpp"
}
}
+79 -268
View File
@@ -1,230 +1,71 @@
cmake_minimum_required(VERSION 3.14)
cmake_minimum_required(VERSION 3.13)
project(
simdjson
# The version number is modified by tools/release.py
VERSION 0.9.1
DESCRIPTION "Parsing gigabytes of JSON per second"
HOMEPAGE_URL "https://simdjson.org/"
LANGUAGES CXX C
project(simdjson
DESCRIPTION "Parsing gigabytes of JSON per second"
LANGUAGES CXX C
)
set(SIMDJSON_GITHUB_REPOSITORY "https://github.com/simdjson/simdjson")
set(PROJECT_VERSION_MAJOR 0)
set(PROJECT_VERSION_MINOR 8)
set(PROJECT_VERSION_PATCH 1)
set(SIMDJSON_SEMANTIC_VERSION "0.8.1" CACHE STRING "simdjson semantic version")
set(SIMDJSON_LIB_VERSION "7.0.0" CACHE STRING "simdjson library version")
set(SIMDJSON_LIB_SOVERSION "7" CACHE STRING "simdjson library soversion")
set(SIMDJSON_GITHUB_REPOSITORY https://github.com/simdjson/simdjson)
string(
COMPARE EQUAL
"${CMAKE_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}"
is_top_project
)
include(GNUInstallDirs)
include(cmake/simdjson-flags.cmake)
include(cmake/simdjson-user-cmakecache.cmake)
# ---- Options, variables ----
# These version numbers are modified by tools/release.py
set(SIMDJSON_LIB_VERSION "8.0.0" CACHE STRING "simdjson library version")
set(SIMDJSON_LIB_SOVERSION "8" CACHE STRING "simdjson library soversion")
if(SIMDJSON_JUST_LIBRARY)
message( STATUS "Building just the library, omitting all tests, tools and benchmarks." )
else(SIMDJSON_JUST_LIBRARY)
# Setup tests
enable_testing()
add_subdirectory(jsonchecker)
add_subdirectory(jsonexamples)
add_library(test-data INTERFACE)
target_link_libraries(test-data INTERFACE jsonchecker-data jsonchecker-minefield-data jsonexamples-data)
endif(SIMDJSON_JUST_LIBRARY)
# Create the top level simdjson library (must be done at this level to use both src/ and include/
# directories) and tools
#
add_subdirectory(include)
add_subdirectory(src)
add_subdirectory(windows)
if(NOT(SIMDJSON_JUST_LIBRARY))
add_subdirectory(dependencies) ## This needs to be before tools because of cxxopts
add_subdirectory(tools) ## This needs to be before tests because of cxxopts
add_subdirectory(singleheader)
endif()
install(FILES singleheader/simdjson.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
option(SIMDJSON_ENABLE_THREADS "Link with thread support" ON)
include(cmake/simdjson-props.cmake)
include(cmake/implementation-flags.cmake)
include(cmake/exception-flags.cmake)
option(SIMDJSON_DISABLE_DEPRECATED_API "Disables deprecated APIs" OFF)
if(SIMDJSON_DISABLE_DEPRECATED_API)
simdjson_add_props(
target_compile_definitions PUBLIC
SIMDJSON_DISABLE_DEPRECATED_API=1
)
endif()
option(SIMDJSON_DEVELOPMENT_CHECKS "Enable development-time aids, such as \
checks for incorrect API usage. Enabled by default in DEBUG." OFF)
if(SIMDJSON_DEVELOPMENT_CHECKS)
simdjson_add_props(
target_compile_definitions PUBLIC
SIMDJSON_DEVELOPMENT_CHECKS
)
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)
if("$ENV{CI}")
set(SIMDJSON_DEVELOPER_MODE ON CACHE INTERNAL "")
endif()
endif()
include(cmake/handle-deprecations.cmake)
if(SIMDJSON_DEVELOPER_MODE)
include(cmake/developer-options.cmake)
else()
message(STATUS "Building only the library. Advanced users may want to turn SIMDJSON_DEVELOPER_MODE to ON, e.g., via -D SIMDJSON_DEVELOPER_MODE=ON.")
endif()
# ---- simdjson library ----
add_library(simdjson src/simdjson.cpp)
add_library(simdjson::simdjson ALIAS simdjson)
set_target_properties(
simdjson PROPERTIES
VERSION "${SIMDJSON_LIB_VERSION}"
SOVERSION "${SIMDJSON_LIB_SOVERSION}"
# FIXME: symbols should be hidden by default
WINDOWS_EXPORT_ALL_SYMBOLS YES
)
# FIXME: Use proper CMake integration for exports
if(MSVC AND BUILD_SHARED_LIBS)
target_compile_definitions(
simdjson
PRIVATE SIMDJSON_BUILDING_WINDOWS_DYNAMIC_LIBRARY=1
INTERFACE SIMDJSON_USING_WINDOWS_DYNAMIC_LIBRARY=1
)
endif()
simdjson_add_props(
target_include_directories
PUBLIC "$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>"
PRIVATE "$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/src>"
)
simdjson_add_props(target_compile_features PUBLIC cxx_std_11)
# workaround for GNU GCC poor AVX load/store code generation
if(
CMAKE_CXX_COMPILER_ID STREQUAL "GNU"
AND CMAKE_SYSTEM_PROCESSOR MATCHES "^(i.86|x86(_64)?)$"
)
simdjson_add_props(
target_compile_options PRIVATE
-mno-avx256-split-unaligned-load -mno-avx256-split-unaligned-store
)
endif()
if(SIMDJSON_ENABLE_THREADS)
find_package(Threads REQUIRED)
simdjson_add_props(target_link_libraries PUBLIC Threads::Threads)
simdjson_add_props(target_compile_definitions PUBLIC SIMDJSON_THREADS_ENABLED=1)
endif()
simdjson_apply_props(simdjson)
# ---- Install rules ----
include(CMakePackageConfigHelpers)
include(GNUInstallDirs)
install(
FILES singleheader/simdjson.h
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
COMPONENT simdjson_Development
)
install(
TARGETS simdjson
EXPORT simdjsonTargets
RUNTIME COMPONENT simdjson_Runtime
LIBRARY COMPONENT simdjson_Runtime
NAMELINK_COMPONENT simdjson_Development
ARCHIVE COMPONENT simdjson_Development
INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
)
configure_file(cmake/simdjson-config.cmake.in simdjson-config.cmake @ONLY)
configure_package_config_file("${PROJECT_SOURCE_DIR}/cmake/simdjson-config.cmake.in"
"${PROJECT_BINARY_DIR}/simdjson-config.cmake"
INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/simdjson"
NO_SET_AND_CHECK_MACRO
NO_CHECK_REQUIRED_COMPONENTS_MACRO)
write_basic_package_version_file(
simdjson-config-version.cmake
COMPATIBILITY SameMinorVersion
)
set(
SIMDJSON_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/simdjson"
CACHE STRING "CMake package config location relative to the install prefix"
)
mark_as_advanced(SIMDJSON_INSTALL_CMAKEDIR)
install(
FILES
"${PROJECT_BINARY_DIR}/simdjson-config.cmake"
"${PROJECT_BINARY_DIR}/simdjson-config-version.cmake"
DESTINATION "${SIMDJSON_INSTALL_CMAKEDIR}"
COMPONENT simdjson_Development
)
install(
EXPORT simdjsonTargets
NAMESPACE simdjson::
DESTINATION "${SIMDJSON_INSTALL_CMAKEDIR}"
COMPONENT example_Development
)
#
# CPack
#
if(is_top_project)
set(CPACK_PACKAGE_VENDOR "Daniel Lemire")
set(CPACK_PACKAGE_CONTACT "lemire@gmail.com")
set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE")
set(CPACK_RESOURCE_FILE_README "${PROJECT_SOURCE_DIR}/README.md")
set(CPACK_RPM_PACKAGE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE")
set(CPACK_SOURCE_GENERATOR "TGZ;ZIP")
include(CPack)
endif()
# ---- Developer mode extras ----
if(NOT SIMDJSON_DEVELOPER_MODE)
return()
elseif(NOT is_top_project)
message(AUTHOR_WARNING "Developer mode is intended for developers of simdjson")
endif()
simdjson_apply_props(simdjson-internal-flags)
set(
SIMDJSON_USER_CMAKECACHE
"${CMAKE_BINARY_DIR}/.simdjson-user-CMakeCache.txt"
)
add_custom_target(
simdjson-user-cmakecache
COMMAND "${CMAKE_COMMAND}"
-D "BINARY_DIR=${CMAKE_BINARY_DIR}"
-D "USER_CMAKECACHE=${SIMDJSON_USER_CMAKECACHE}"
-P "${PROJECT_SOURCE_DIR}/cmake/simdjson-user-cmakecache.cmake"
VERBATIM
)
# Setup tests
enable_testing()
# So we can build just tests with "make all_tests"
add_custom_target(all_tests)
add_subdirectory(windows)
add_subdirectory(dependencies) ## This needs to be before tools because of cxxopts
add_subdirectory(tools) ## This needs to be before tests because of cxxopts
# Data: jsonexamples is left with only the bare essential.
# most of the data has been moved to https://github.com/simdjson/simdjson-data
add_subdirectory(jsonexamples)
add_subdirectory(singleheader)
"${PROJECT_BINARY_DIR}/simdjson-config-version.cmake"
VERSION ${SIMDJSON_SEMANTIC_VERSION}
COMPATIBILITY SameMinorVersion)
install(FILES "${PROJECT_BINARY_DIR}/simdjson-config.cmake"
"${PROJECT_BINARY_DIR}/simdjson-config-version.cmake"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/simdjson")
#
# Compile tools / tests / benchmarks
#
add_subdirectory(tests)
add_subdirectory(examples)
add_subdirectory(benchmark)
add_subdirectory(fuzz)
if(NOT(SIMDJSON_JUST_LIBRARY))
add_subdirectory(tests)
add_subdirectory(examples)
add_subdirectory(benchmark)
add_subdirectory(fuzz)
endif()
#
# Source files should be just ASCII
@@ -232,59 +73,29 @@ add_subdirectory(fuzz)
find_program(FIND find)
find_program(FILE file)
find_program(GREP grep)
if(FIND AND FILE AND GREP)
add_test(
NAME just_ascii
COMMAND sh -c "\
${FIND} include src windows tools singleheader tests examples benchmark \
-path benchmark/checkperf-reference -prune -name '*.h' -o -name '*.cpp' \
-type f -exec ${FILE} '{}' \; | ${GREP} -qv ASCII || exit 0 && exit 1"
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
)
if((FIND) AND (FILE) AND (GREP))
add_test(
NAME "just_ascii"
COMMAND sh -c "${FIND} include src windows tools singleheader tests examples benchmark -path benchmark/checkperf-reference -prune -name '*.h' -o -name '*.cpp' -type f -exec ${FILE} '{}' \; |${GREP} -v ASCII || exit 0 && exit 1"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
endif()
##
## In systems like R, libraries must not use stderr or abort to be acceptable.
## Thus we make it a hard rule that one is not allowed to call abort or stderr.
## The sanitized builds are allowed to abort.
##
if(NOT SIMDJSON_SANITIZE)
find_program(GREP grep)
find_program(NM nm)
if((NOT GREP) OR (NOT NM))
message("grep and nm are unavailable on this system.")
else()
add_test(
NAME "avoid_abort"
# Under FreeBSD, the __cxa_guard_abort symbol may appear but it is fine.
# So we want to look for <space><possibly _>abort as a test.
COMMAND sh -c "${NM} $<TARGET_FILE_NAME:simdjson> | ${GREP} ' _*abort' || exit 0 && exit 1"
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
)
add_test(
NAME "avoid_cout"
COMMAND sh -c "${NM} $<TARGET_FILE_NAME:simdjson> | ${GREP} ' _*cout' || exit 0 && exit 1"
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
)
add_test(
NAME "avoid_cerr"
COMMAND sh -c "${NM} $<TARGET_FILE_NAME:simdjson> | ${GREP} ' _*cerr' || exit 0 && exit 1"
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
)
add_test(
NAME "avoid_printf"
COMMAND sh -c "${NM} $<TARGET_FILE_NAME:simdjson> | ${GREP} ' _*printf' || exit 0 && exit 1"
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
)
add_test(
NAME "avoid_stdout"
COMMAND sh -c "${NM} $<TARGET_FILE_NAME:simdjson> | ${GREP} stdout || exit 0 && exit 1"
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
)
add_test(
NAME "avoid_stderr"
COMMAND sh -c "${NM} $<TARGET_FILE_NAME:simdjson> | ${GREP} stderr || exit 0 && exit 1"
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
)
endif()
endif()
#
# CPack
#
set(CPACK_PACKAGE_VENDOR "Daniel Lemire")
set(CPACK_PACKAGE_CONTACT "lemire@gmail.com")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Parsing gigabytes of JSON per second")
set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR})
set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR})
set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH})
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")
set(CPACK_RESOURCE_FILE_README "${CMAKE_CURRENT_SOURCE_DIR}/README.md")
set(CPACK_RPM_PACKAGE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")
set(CPACK_SOURCE_GENERATOR "TGZ;ZIP")
include(CPack)
+1 -1
View File
@@ -69,7 +69,7 @@ Pull requests are always invited. However, we ask that you follow these guidelin
3. Other types of changes must be clearly motivated. We openly discourage changes with no identifiable benefits.
- Changes should be focused and minimal. You should change as few lines of code as possible. Please do not reformat or touch files needlessly.
- New features must be accompanied of new tests, in general.
- Your code should pass our continuous-integration tests. It is your responsibility to ensure that your proposal pass the tests. We do not merge pull requests that would break our build.
- Your code should pass our continuous-integration tests. It is your responsability to ensure that your proposal pass the tests. We do not merge pull requests that would break our build.
- An exception to this would be changes to non-code files, such as documentation and assets, or trivial changes to code, such as comments, where it is encouraged to explicitly ask for skipping a CI run using the `[skip ci]` prefix in your Pull Request title **and** in the first line of the most recent commit in a push. Example for such a commit: `[skip ci] Fixed typo in power_of_ten's docs`
This benefits the project in such a way that the CI pipeline is not burdened by running jobs on changes that don't change any behavior in the code, which reduces wait times for other Pull Requests that do change behavior and require testing.
-1
View File
@@ -36,6 +36,5 @@ Nong Li
Furkan Taşkale
Brendan Knapp
Danila Kutenin
Pavel Pavlov
# if you have contributed to the project and your name does not
# appear in this list, please let us know!
+88
View File
@@ -0,0 +1,88 @@
###
#
# Though simdjson requires only commonly available compilers and tools, it can
# be convenient to build it and test it inside a docker container: it makes it
# possible to test and benchmark simdjson under even relatively out-of-date
# Linux servers. It should also work under macOS and Windows, though not
# at native speeds, maybe.
#
# Assuming that you have a working docker server, this file
# allows you to build, test and benchmark simdjson.
#
# We build the library and associated files in the dockerbuild subdirectory.
# It may be necessary to delete it before creating the image:
#
# rm -r -f dockerbuild
#
# The need to delete the directory has nothing to do with docker per se: it is
# simply cleaner in CMake to start from a fresh directory. This is important: if you
# reuse the same directory with different configurations, you may get broken builds.
#
#
# Then you can build the image as follows:
#
# docker build -t simdjson --build-arg USER_ID=$(id -u) --build-arg GROUP_ID=$(id -g) .
#
# Please note that the image does not contain a copy of the code. However, the image will contain the
# the compiler and the build system. This means that if you change the source code, after you have built
# the image, you won't need to rebuild the image. In fact, unless you want to try a different compiler, you
# do not need to ever rebuild the image, even if you do a lot of work on the source code.
#
# We specify the users to avoid having files owned by a privileged user (root) in our directory. Some
# people like to run their machine as the "root" user. We do not think it is cool.
#
# Then you need to build the project:
#
# docker run -v $(pwd):/project:Z simdjson
#
# Should you change a source file, you may need to call this command again. Because the output
# files are persistent between calls to this command (they reside in the dockerbuild directory),
# this command can be fast.
#
# Next you can test it as follows:
#
# docker run -it -v $(pwd):/project:Z simdjson sh -c "cd dockerbuild && ctest . --output-on-failure -LE explicitonly"
#
# The run the complete tests requires you to have built all of simdjson.
#
# Building all of simdjson takes a long time. Instead, you can build just one target:
#
# docker run -it -v $(pwd):/project:Z simdjson sh -c "[ -d dockerbuild ] || mkdir dockerbuild && cd dockerbuild && cmake .. && cmake --build . --target parse"
#
# Note that it is safe to remove dockerbuild before call the previous command, as the repository gets rebuild. It is also possible, by changing the command, to use a different directory name.
#
# You can run performance tests:
#
# docker run -it --privileged -v $(pwd):/project:Z simdjson sh -c "cd dockerbuild && for i in ../jsonexamples/*.json; do echo \$i; ./benchmark/parse \$i; done"
#
# The "--privileged" is recommended so you can get performance counters under Linux.
#
# You can also grab a fresh copy of simdjson and rebuild it, to make comparisons:
#
# docker run -it -v $(pwd):/project:Z simdjson sh -c "git clone https://github.com/simdjson/simdjson.git && cd simdjson && mkdir build && cd build && cmake .. && cmake --build . --target parse "
#
# Then you can run comparisons:
#
# docker run -it --privileged -v $(pwd):/project:Z simdjson sh -c "for i in jsonexamples/*.json; do echo \$i; dockerbuild/benchmark/parse \$i| grep GB| head -n 1; simdjson/build/benchmark/parse \$i | grep GB |head -n 1; done"
#
####
FROM ubuntu:20.10
################
# We would prefer to use the conan io images but they do not support 64-bit ARM? The small gcc images appear to
# be broken on ARM.
# Furthermore, we would not expect users to frequently rebuild the container, so using ubuntu is probably fine.
###############
ARG USER_ID
ARG GROUP_ID
RUN apt-get update -qq
RUN DEBIAN_FRONTEND="noninteractive" apt-get -y install tzdata
RUN apt-get install -y cmake g++ git
RUN mkdir project
RUN addgroup --gid $GROUP_ID user; exit 0
RUN adduser --disabled-password --gecos '' --uid $USER_ID --gid $GROUP_ID user; exit 0
USER user
RUN gcc --version
WORKDIR /project
CMD ["sh","-c","[ -d dockerbuild ] || mkdir dockerbuild && cd dockerbuild && cmake .. && cmake --build . "]
+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 = "0.9.1"
PROJECT_NUMBER = "0.8.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
+15 -27
View File
@@ -32,18 +32,6 @@ Stage 1 also does unicode validation.
Stage 2 handles all of the rest: number parsings, recognizing atoms like true, false, null, and so forth.
Developer mode
--------------
Build system targets that are only useful for developers of the simdjson
library are behind the `SIMDJSON_DEVELOPER_MODE` option. Enabling this option
makes tests, examples, benchmarks and other developer targets available. Not
enabling this option means that you are a consumer of simdjson and thus you
only get the library targets and options.
Developer mode is forced to be on when the `CI` environment variable is set to
a value that CMake recognizes as "on", which is set to `true` in all of the CI
workflows used by simdjson.
Directory Structure and Source
------------------------------
@@ -86,19 +74,19 @@ Other important files and directories:
```bash
mkdir build
cd build
cmake -D SIMDJSON_DEVELOPER_MODE=ON ..
cmake ..
cmake --build . --config Release
benchmark/dom/parse ../jsonexamples/twitter.json
benchmark/parse ../jsonexamples/twitter.json
```
The last line becomes `./benchmark/Release/parse.exe ../jsonexample/twitter.json` under Windows. You may also use Google Benchmark:
```bash
mkdir build
cd build
cmake -D SIMDJSON_DEVELOPER_MODE=ON ..
cmake ..
cmake --build . --target bench_parse_call --config Release
./benchmark/bench_parse_call
```
The last line becomes `./benchmark/Release/bench_parse_call.exe` under Windows. Under Windows, you can also build with the clang compiler by adding `-T ClangCL` to the call to `cmake ..`: `cmake -T ClangCL ..`.
The last line becomes `./benchmark/Release/bench_parse_call.exe` under Windows. Under Windows, you can also build with the clang compiler by adding `-T ClangCL` to the call to `cmake ..`: `cmake .. - TClangCL`.
* **fuzz:** The source for fuzz testing. This lets us explore important edge and middle cases
* **fuzz:** The source for fuzz testing. This lets us explore important edge and middle cases
automatically, and is run in CI.
@@ -159,7 +147,7 @@ processor.
At this point, we are require to use one of two main strategies.
1. On POSIX systems, the main compilers (LLVM clang, GNU gcc) allow us to use any intrinsic function after including the header, but they fail to inline the resulting instruction if the target processor does not support them. Because we compile for a generic processor, we would not be able to use most intrinsic functions. Thankfully, more recent versions of these compilers allow us to flag a region of code with a specific target, so that we can compile only some of the code with support for advanced instructions. Thus in our C++, one might notice macros like `TARGET_HASWELL`. It is then our responsibility, at runtime, to only run the regions of code (that we call kernels) matching the properties of the runtime processor. The benefit of this approach is that the compiler not only let us use intrinsic functions, but it can also optimize the rest of the code in the kernel with advanced instructions we enabled.
1. On POSIX systems, the main compilers (LLVM clang, GNU gcc) allow us to use any intrinsic function after including the header, but they fail to inline the resulting instruction if the target processor does not support them. Because we compile for a generic processor, we would not be able to use most intrinsic functions. Thankfully, more recent versions of these compilers allow us to flag a region of code with a specific target, so that we can compile only some of the code with support for advanced instructions. Thus in our C++, one might notice macros like `TARGET_HASWELL`. It is then our responsability, at runtime, to only run the regions of code (that we call kernels) matching the properties of the runtime processor. The benefit of this approach is that the compiler not only let us use intrinsic functions, but it can also optimize the rest of the code in the kernel with advanced instructions we enabled.
2. Under Visual Studio, the problem is somewhat simpler. Visual Studio will not only provide the intrinsic functions, but it will also allow us to use them. They will compile just fine. It is at runtime that they may cause a crash. So we do not need to mark regions of code for compilation toward advanced processors (e.g., with `TARGET_HASWELL` macros). The downside of the Visual Studio approach is that the compiler is not allowed to use advanced instructions others than those we specify. In principle, this means that Visual Studio has weaker optimization opportunities.
@@ -180,7 +168,7 @@ systematically regenerated on releases. To ensure you have the latest code, you
```bash
mkdir build
cd build
cmake -D SIMDJSON_DEVELOPER_MODE=ON ..
cmake ..
cmake --build . # needed, because currently dependencies do not work fully for the amalgamate target
cmake --build . --target amalgamate
```
@@ -221,31 +209,31 @@ Building: While in the project repository, do the following:
```
mkdir build
cd build
cmake -D SIMDJSON_DEVELOPER_MODE=ON ..
cmake ..
cmake --build .
ctest
```
CMake will build a library. By default, it builds a static library (e.g., libsimdjson.a on Linux).
CMake will build a library. By default, it builds a shared library (e.g., libsimdjson.so on Linux).
You can build a shared library:
You can build a static library:
```
mkdir buildshared
cd buildshared
cmake -D BUILD_SHARED_LIBS=ON -D SIMDJSON_DEVELOPER_MODE=ON ..
mkdir buildstatic
cd buildstatic
cmake -DSIMDJSON_BUILD_STATIC=ON ..
cmake --build .
ctest
```
In some cases, you may want to specify your compiler, especially if the default compiler on your system is too old. You need to tell cmake which compiler you wish to use by setting the CC and CXX variables. Under bash, you can do so with commands such as `export CC=gcc-7` and `export CXX=g++-7`. You can also do it as part of the `cmake` command: `cmake -DCMAKE_CXX_COMPILER=g++ ..`. You may proceed as follows:
In some cases, you may want to specify your compiler, especially if the default compiler on your system is too old. You need to tell cmake which compiler you wish to use by setting the CC and CXX variables. Under bash, you can do so with commands such as `export CC=gcc-7` and `export CXX=g++-7`. You can also do it as part of the `cmake` command: `cmake .. -DCMAKE_CXX_COMPILER=g++`. You may proceed as follows:
```
brew install gcc@8
mkdir build
cd build
export CXX=g++-8 CC=gcc-8
cmake -D SIMDJSON_DEVELOPER_MODE=ON ..
cmake ..
cmake --build .
ctest
```
@@ -281,7 +269,7 @@ Furthermore, if you have installed LLVM clang on Windows, for example as a compo
- `mkdir build`
- `cd build`
- `cmake -T ClangCL ..`
- `cmake .. -T ClangCL`
- `cmake --build . -config Release`
+79 -30
View File
@@ -1,9 +1,9 @@
[![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/simdjson.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&q=proj%3Asimdjson&can=2)
![Ubuntu 18.04 CI](https://github.com/simdjson/simdjson/workflows/Ubuntu%2018.04%20CI%20(GCC%207)/badge.svg)
[![Ubuntu 20.04 CI](https://github.com/simdjson/simdjson/workflows/Ubuntu%2020.04%20CI%20(GCC%209)/badge.svg)](https://simdjson.org/plots.html)
![VS16-CI](https://github.com/simdjson/simdjson/workflows/VS16-CI/badge.svg)
![MinGW64-CI](https://github.com/simdjson/simdjson/workflows/MinGW64-CI/badge.svg)
[![][license img]][license] [![Doxygen Documentation](https://img.shields.io/badge/docs-doxygen-green.svg)](https://simdjson.org/api/0.9.0/index.html)
[![][license img]][license] [![Doxygen Documentation](https://img.shields.io/badge/docs-doxygen-green.svg)](https://simdjson.org/api/0.8.0/index.html)
simdjson : Parsing gigabytes of JSON per second
===============================================
@@ -11,11 +11,12 @@ simdjson : Parsing gigabytes of JSON per second
<img src="images/logo.png" width="10%" style="float: right">
JSON is everywhere on the Internet. Servers spend a *lot* of time parsing it. We need a fresh
approach. The simdjson library uses commonly available SIMD instructions and microparallel algorithms
to parse JSON 4x faster than RapidJSON and 25x faster than JSON for Modern C++.
to parse JSON 2.5x faster than RapidJSON and 25x faster than JSON for Modern C++.
* **Fast:** Over 4x faster than commonly used production-grade JSON parsers.
* **Fast:** Over 2.5x faster than commonly used production-grade JSON parsers.
* **Record Breaking Features:** Minify JSON at 6 GB/s, validate UTF-8 at 13 GB/s, NDJSON at 3.5 GB/s.
* **Easy:** First-class, easy to use and carefully documented APIs.
* **Beyond DOM:** Try the new On Demand API for twice the speed (>4GB/s).
* **Strict:** Full JSON and UTF-8 validation, lossless parsing. Performance with no compromises.
* **Automatic:** Selects a CPU-tailored parser at runtime. No configuration needed.
* **Reliable:** From memory allocation to error handling, simdjson's design avoids surprises.
@@ -27,6 +28,7 @@ Table of Contents
-----------------
* [Quick Start](#quick-start)
* [On Demand](#on-demand)
* [Documentation](#documentation)
* [Performance results](#performance-results)
* [Real-world usage](#real-world-usage)
@@ -52,16 +54,13 @@ The simdjson library is easily consumable with a single .h and .cpp file.
```
2. Create `quickstart.cpp`:
```c++
#include "simdjson.h"
using namespace simdjson;
int main(void) {
ondemand::parser parser;
padded_string json = padded_string::load("twitter.json");
ondemand::document tweets = parser.iterate(json);
std::cout << uint64_t(tweets["search_metadata"]["count"]) << " results." << std::endl;
}
```c++
#include "simdjson.h"
int main(void) {
simdjson::dom::parser parser;
simdjson::dom::element tweets = parser.load("twitter.json");
std::cout << tweets["search_metadata"]["count"] << " results." << std::endl;
}
```
3. `c++ -o quickstart quickstart.cpp simdjson.cpp`
4. `./quickstart`
@@ -69,6 +68,34 @@ int main(void) {
100 results.
```
### On Demand
The new On Demand JSON parser is just as easy, but much faster due to just-in-time parsing. It is in
alpha right now. More information can be found in the [On Demand Guide](doc/ondemand.md).
1. Do step 1 of the [Quick Start](#quick-start).
2. Create `quickstart.cpp`:
```c++
#include "simdjson.h"
using namespace simdjson;
int main(void) {
ondemand::parser parser;
padded_string json = padded_string::load("twitter.json");
ondemand::document tweets = parser.iterate(json);
std::cout << uint64_t(tweets["search_metadata"]["count"]) << " results." << std::endl;
}
```
3. `c++ -march=native -o quickstart quickstart.cpp simdjson.cpp`
4. `./quickstart`
```
100 results.
```
You'll notice that the code here is very similar to the [main Quick Start code](#quick-start) (and
indeed, it does the same thing). However, if you compare the performance, you should find On
Demand much faster.
Documentation
-------------
@@ -78,31 +105,59 @@ Usage documentation is available:
* [Performance](doc/performance.md) shows some more advanced scenarios and how to tune for them.
* [Implementation Selection](doc/implementation-selection.md) describes runtime CPU detection and
how you can work with it.
* [API](https://simdjson.org/api/0.9.0/annotated.html) contains the automatically generated API documentation.
* [API](https://simdjson.org/api/0.8.0/annotated.html) contains the automatically generated API documentation.
Performance results
-------------------
The simdjson library uses three-quarters less instructions than state-of-the-art parser [RapidJSON](https://rapidjson.org). To our knowledge, simdjson is the first fully-validating JSON parser
The simdjson library uses three-quarters less instructions than state-of-the-art parser [RapidJSON](https://rapidjson.org) and
fifty percent less than sajson. To our knowledge, simdjson is the first fully-validating JSON parser
to run at [gigabytes per second](https://en.wikipedia.org/wiki/Gigabyte) (GB/s) on commodity processors. It can parse millions of JSON documents per second on a single core.
The following figure represents parsing speed in GB/s for parsing various files
on an Intel Skylake processor (3.4 GHz) using the GNU GCC 10 compiler (with the -O3 flag).
We compare against the best and fastest C++ libraries on benchmarks that load and process the data.
on an Intel Skylake processor (3.4 GHz) using the GNU GCC 9 compiler (with the -O3 flag).
We compare against the best and fastest C++ libraries.
The simdjson library offers full unicode ([UTF-8](https://en.wikipedia.org/wiki/UTF-8)) validation and exact
number parsing.
number parsing. The RapidJSON library is tested in two modes: fast and
exact number parsing. The sajson library offers fast (but not exact)
number parsing and partial unicode validation. In this data set, the file
sizes range from 65KB (github_events) all the way to 3.3GB (gsoc-2018).
Many files are mostly made of numbers: canada, mesh.pretty, mesh, random
and numbers: in such instances, we see lower JSON parsing speeds due to the
high cost of number parsing. The simdjson library uses exact number parsing which
is particular taxing.
<img src="doc/gbps.png" width="90%">
On a Skylake processor, the parsing speeds (in GB/s) of various processors on the twitter.json file are as follows, using again GNU GCC 9.1 (with the -O3 flag). The popular JSON for Modern C++ library is particularly slow: it obviously trades parsing speed for other desirable features.
| parser | GB/s |
| ------------------------------------- | ---- |
| simdjson | 2.5 |
| RapidJSON UTF8-validation | 0.29 |
| RapidJSON UTF8-valid., exact numbers | 0.28 |
| RapidJSON insitu, UTF8-validation | 0.41 |
| RapidJSON insitu, UTF8-valid., exact | 0.39 |
| sajson (insitu, dynamic) | 0.62 |
| sajson (insitu, static) | 0.88 |
| dropbox | 0.13 |
| fastjson | 0.27 |
| gason | 0.59 |
| ultrajson | 0.34 |
| jsmn | 0.25 |
| cJSON | 0.31 |
| JSON for Modern C++ (nlohmann/json) | 0.11 |
<img src="doc/rome.png" width="60%">
The simdjson library offers high speed whether it processes tiny files (e.g., 300 bytes)
or larger files (e.g., 3MB). The following plot presents parsing
speed for [synthetic files over various sizes generated with a script](https://github.com/simdjson/simdjson_experiments_vldb2019/blob/master/experiments/growing/gen.py) on a 3.4 GHz Skylake processor (GNU GCC 9, -O3).
<img src="doc/growing.png" width="60%">
<img src="doc/growing.png" width="90%">
[All our experiments are reproducible](https://github.com/simdjson/simdjson_experiments_vldb2019).
You can go beyond 4 GB/s with our new [On Demand API](https://github.com/simdjson/simdjson/blob/master/doc/ondemand.md).
For NDJSON files, we can exceed 3 GB/s with [our multithreaded parsing functions](https://github.com/simdjson/simdjson/blob/master/doc/parse_many.md).
@@ -125,7 +180,6 @@ We distinguish between "bindings" (which just wrap the C++ code) and a port to a
- [ZippyJSON](https://github.com/michaeleisel/zippyjson): Swift bindings for the simdjson project.
- [libpy_simdjson](https://github.com/gerrymanoim/libpy_simdjson/): high-speed Python bindings for simdjson using [libpy](https://github.com/quantopian/libpy).
- [pysimdjson](https://github.com/TkTech/pysimdjson): Python bindings for the simdjson project.
- [cysimdjson](https://github.com/TeskaLabs/cysimdjson): high-speed Python bindings for the simdjson project.
- [simdjson-rs](https://github.com/simd-lite): Rust port.
- [simdjson-rust](https://github.com/SunDoge/simdjson-rust): Rust wrapper (bindings).
- [SimdJsonSharp](https://github.com/EgorBo/SimdJsonSharp): C# version for .NET Core (bindings and full port).
@@ -136,7 +190,6 @@ We distinguish between "bindings" (which just wrap the C++ code) and a port to a
- [simdjson-go](https://github.com/minio/simdjson-go): Go port using Golang assembly.
- [rcppsimdjson](https://github.com/eddelbuettel/rcppsimdjson): R bindings.
- [simdjson_erlang](https://github.com/ChomperT/simdjson_erlang): erlang bindings.
- [lua-simdjson](https://github.com/FourierTransformer/lua-simdjson): lua bindings.
About simdjson
@@ -152,7 +205,7 @@ and implementation of simdjson is in our research article:
We have an in-depth paper focused on the UTF-8 validation:
- John Keiser, Daniel Lemire, [Validating UTF-8 In Less Than One Instruction Per Byte](https://arxiv.org/abs/2010.03090), Software: Practice & Experience 51 (5), 2021.
- John Keiser, Daniel Lemire, [Validating UTF-8 In Less Than One Instruction Per Byte](https://arxiv.org/abs/2010.03090), Software: Practice & Experience (to appear)
We also have an informal [blog post providing some background and context](https://branchfree.org/2019/02/25/paper-parsing-gigabytes-of-json-per-second/).
@@ -182,8 +235,4 @@ This code is made available under the [Apache License 2.0](https://www.apache.or
Under Windows, we build some tools using the windows/dirent_portable.h file (which is outside our library code): it under the liberal (business-friendly) MIT license.
For compilers that do not support [C++17](https://en.wikipedia.org/wiki/C%2B%2B17), we bundle the string-view library which is published under the [Boost license](http://www.boost.org/LICENSE_1_0.txt). Like the Apache license, the Boost license is a permissive license allowing commercial redistribution.
For efficient number serialization, we bundle Florian Loitsch's implementation of the Grisu2 algorithm for binary to decimal floating-point numbers. The implementation was slightly modified by JSON for Modern C++ library. Both Florian Loitsch's implementation and JSON for Modern C++ are provided under the MIT license.
For runtime dispatching, we use some code from the PyTorch project licensed under 3-clause BSD.
For compilers that do not support [C++17](https://en.wikipedia.org/wiki/C%2B%2B17), we bundle the string-view library which is published under the Boost license (http://www.boost.org/LICENSE_1_0.txt). Like the Apache license, the Boost license is a permissive license allowing commercial redistribution.
+13 -4
View File
@@ -1,12 +1,20 @@
add_subdirectory(dom)
include_directories( . linux )
link_libraries(simdjson-windows-headers test-data)
link_libraries(simdjson)
link_libraries(simdjson simdjson-flags)
add_executable(benchfeatures benchfeatures.cpp)
add_executable(get_corpus_benchmark get_corpus_benchmark.cpp)
add_executable(perfdiff perfdiff.cpp)
add_executable(parse parse.cpp)
add_executable(parse_stream parse_stream.cpp)
add_executable(statisticalmodel statisticalmodel.cpp)
add_executable(parse_noutf8validation parse.cpp)
target_compile_definitions(parse_noutf8validation PRIVATE SIMDJSON_SKIPUTF8VALIDATION)
add_executable(parse_nonumberparsing parse.cpp)
target_compile_definitions(parse_nonumberparsing PRIVATE SIMDJSON_SKIPNUMBERPARSING)
add_executable(parse_nostringparsing parse.cpp)
target_compile_definitions(parse_nostringparsing PRIVATE SIMDJSON_SKIPSTRINGPARSING)
if (TARGET benchmark::benchmark)
link_libraries(benchmark::benchmark)
@@ -29,3 +37,4 @@ if (TARGET benchmark::benchmark)
endif()
endif()
include(checkperf.cmake)
+19
View File
@@ -0,0 +1,19 @@
# From the ROOT, run:
# docker build -t simdjsonbench -f benchmark/Dockerfile . && docker run --privileged -t simdjsonbench
FROM gcc:8.3
# # Build latest
# ENV latest_release=v0.2.1
# WORKDIR /usr/src/$latest_release/
# RUN git clone --depth 1 https://github.com/lemire/simdjson/ -b $latest_release .
# RUN make parse
# # Build master
# WORKDIR /usr/src/master/
# RUN git clone --depth 1 https://github.com/lemire/simdjson/ .
# RUN make parse
# Build the current source
COPY . /usr/src/current/
WORKDIR /usr/src/current/
RUN make checkperf
@@ -1,68 +0,0 @@
#pragma once
#include "json_benchmark/file_runner.h"
#include <map>
#include <string>
namespace amazon_cellphones {
using namespace json_benchmark;
struct brand {
double cumulative_rating;
uint64_t reviews_count;
simdjson_really_inline bool operator==(const brand &other) const {
return cumulative_rating == other.cumulative_rating &&
reviews_count == other.reviews_count;
}
simdjson_really_inline bool operator!=(const brand &other) const { return !(*this == other); }
};
simdjson_unused static std::ostream &operator<<(std::ostream &o, const brand &b) {
o << "cumulative_rating: " << b.cumulative_rating << std::endl;
o << "reviews_count: " << b.reviews_count << std::endl;
return o;
}
template<typename StringType>
simdjson_unused static std::ostream &operator<<(std::ostream &o, const std::pair<const StringType, brand> &p) {
o << "brand: " << p.first << std::endl;
o << p.second;
return o;
}
template<typename I>
struct runner : public file_runner<I> {
std::map<typename I::StringType, brand> result{};
bool setup(benchmark::State &state) {
return this->load_json(state, AMAZON_CELLPHONES_NDJSON);
}
bool before_run(benchmark::State &state) {
if (!file_runner<I>::before_run(state)) { return false; }
result.clear();
return true;
}
bool run(benchmark::State &) {
return this->implementation.run(this->json, result);
}
template<typename R>
bool diff(benchmark::State &state, runner<R> &reference) {
return diff_results(state, result, reference.result, diff_flags::NONE);
}
size_t items_per_iteration() {
return result.size();
}
};
struct simdjson_dom;
template<typename I> simdjson_really_inline static void amazon_cellphones(benchmark::State &state) {
run_json_benchmark<runner<I>, runner<simdjson_dom>>(state);
}
} // namespace amazon_cellphones
@@ -1,44 +0,0 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
#include "amazon_cellphones.h"
namespace amazon_cellphones {
using namespace simdjson;
struct simdjson_dom {
using StringType = std::string;
dom::parser parser{};
bool run(simdjson::padded_string &json, std::map<StringType, brand> &result) {
auto stream = parser.parse_many(json);
auto i = stream.begin();
++i; // Skip first line
for (;i != stream.end(); ++i) {
auto doc = *i;
StringType copy(std::string_view(doc.at(1)));
auto x = result.find(copy);
if (x == result.end()) { // If key not found, add new key
result.emplace(copy, amazon_cellphones::brand{
double(doc.at(5)) * uint64_t(doc.at(7)),
uint64_t(doc.at(7))
});
} else { // Otherwise, update key data
x->second.cumulative_rating += double(doc.at(5)) * uint64_t(doc.at(7));
x->second.reviews_count += uint64_t(doc.at(7));
}
}
return true;
}
};
BENCHMARK_TEMPLATE(amazon_cellphones, simdjson_dom)->UseManualTime();
} // namespace amazon_cellphones
#endif // SIMDJSON_EXCEPTIONS
@@ -1,65 +0,0 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
#include "amazon_cellphones.h"
namespace amazon_cellphones {
using namespace simdjson;
struct simdjson_ondemand {
using StringType = std::string;
ondemand::parser parser{};
bool run(simdjson::padded_string &json, std::map<StringType, brand> &result) {
ondemand::document_stream stream = parser.iterate_many(json);
ondemand::document_stream::iterator i = stream.begin();
++i; // Skip first line
for (;i != stream.end(); ++i) {
auto & doc = *i;
size_t index{0};
StringType copy;
double rating;
uint64_t reviews;
for ( auto value : doc ) {
switch (index)
{
case 1:
copy = StringType(std::string_view(value));
break;
case 5:
rating = double(value);
break;
case 7:
reviews = uint64_t(value);
break;
default:
break;
}
index++;
}
auto x = result.find(copy);
if (x == result.end()) { // If key not found, add new key
result.emplace(copy, amazon_cellphones::brand{
rating * reviews,
reviews
});
} else { // Otherwise, update key data
x->second.cumulative_rating += rating * reviews;
x->second.reviews_count += reviews;
}
}
return true;
}
};
BENCHMARK_TEMPLATE(amazon_cellphones, simdjson_ondemand)->UseManualTime();
} // namespace amazon_cellphones
#endif // SIMDJSON_EXCEPTIONS
+1 -1
View File
@@ -522,7 +522,7 @@ static void twitter_image_sizes(State& state) {
dom::array media;
if (not (error = tweet["entities"]["media"].get(media))) {
for (dom::object image : media) {
for (auto size : image["sizes"].get_object()) {
for (auto size : image["sizes"].get<dom::object>()) {
image_sizes.emplace(size.value["w"], size.value["h"]);
}
}
-20
View File
@@ -26,20 +26,12 @@ SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
SIMDJSON_POP_DISABLE_WARNINGS
#include "amazon_cellphones/simdjson_dom.h"
#include "amazon_cellphones/simdjson_ondemand.h"
#include "large_amazon_cellphones/simdjson_dom.h"
#include "large_amazon_cellphones/simdjson_ondemand.h"
#include "partial_tweets/simdjson_dom.h"
#include "partial_tweets/simdjson_ondemand.h"
#include "partial_tweets/yyjson.h"
#include "partial_tweets/sajson.h"
#include "partial_tweets/rapidjson.h"
#include "partial_tweets/rapidjson_sax.h"
#include "partial_tweets/nlohmann_json.h"
#include "partial_tweets/nlohmann_json_sax.h"
#include "large_random/simdjson_dom.h"
#include "large_random/simdjson_ondemand.h"
@@ -47,46 +39,34 @@ SIMDJSON_POP_DISABLE_WARNINGS
#include "large_random/yyjson.h"
#include "large_random/sajson.h"
#include "large_random/rapidjson.h"
#include "large_random/rapidjson_sax.h"
#include "large_random/nlohmann_json.h"
#include "large_random/nlohmann_json_sax.h"
#include "kostya/simdjson_dom.h"
#include "kostya/simdjson_ondemand.h"
#include "kostya/yyjson.h"
#include "kostya/sajson.h"
#include "kostya/rapidjson.h"
#include "kostya/rapidjson_sax.h"
#include "kostya/nlohmann_json.h"
#include "kostya/nlohmann_json_sax.h"
#include "distinct_user_id/simdjson_dom.h"
#include "distinct_user_id/simdjson_dom_json_pointer.h"
#include "distinct_user_id/simdjson_ondemand.h"
#include "distinct_user_id/simdjson_ondemand_json_pointer.h"
#include "distinct_user_id/yyjson.h"
#include "distinct_user_id/sajson.h"
#include "distinct_user_id/rapidjson.h"
#include "distinct_user_id/rapidjson_sax.h"
#include "distinct_user_id/nlohmann_json.h"
#include "distinct_user_id/nlohmann_json_sax.h"
#include "find_tweet/simdjson_dom.h"
#include "find_tweet/simdjson_ondemand.h"
#include "find_tweet/yyjson.h"
#include "find_tweet/sajson.h"
#include "find_tweet/rapidjson.h"
#include "find_tweet/rapidjson_sax.h"
#include "find_tweet/nlohmann_json.h"
#include "find_tweet/nlohmann_json_sax.h"
#include "top_tweet/simdjson_dom.h"
#include "top_tweet/simdjson_ondemand.h"
#include "top_tweet/yyjson.h"
#include "top_tweet/sajson.h"
#include "top_tweet/rapidjson.h"
#include "top_tweet/rapidjson_sax.h"
#include "top_tweet/nlohmann_json.h"
#include "top_tweet/nlohmann_json_sax.h"
BENCHMARK_MAIN();
-58
View File
@@ -10,64 +10,6 @@ const char *GSOC_JSON = SIMDJSON_BENCHMARK_DATA_DIR "gsoc-2018.json";
static void fast_minify_twitter(State& state) {
dom::parser parser;
padded_string docdata;
auto error = padded_string::load(TWITTER_JSON).get(docdata);
if(error) {
cerr << "could not parse twitter.json" << error << endl;
return;
}
std::unique_ptr<char[]> buffer{new char[docdata.size()]};
size_t bytes = 0;
for (simdjson_unused auto _ : state) {
size_t new_length{}; // It will receive the minified length.
auto error = simdjson::minify(docdata.data(), docdata.size(), buffer.get(), new_length);
bytes += docdata.size();
benchmark::DoNotOptimize(error);
}
// Gigabyte: https://en.wikipedia.org/wiki/Gigabyte
state.counters["Gigabytes"] = benchmark::Counter(
double(bytes), benchmark::Counter::kIsRate,
benchmark::Counter::OneK::kIs1000); // For GiB : kIs1024
state.counters["docs"] = Counter(double(state.iterations()), benchmark::Counter::kIsRate);
}
BENCHMARK(fast_minify_twitter)->Repetitions(10)->ComputeStatistics("max", [](const std::vector<double>& v) -> double {
return *(std::max_element(std::begin(v), std::end(v)));
})->DisplayAggregatesOnly(true);
static void fast_minify_gsoc(State& state) {
dom::parser parser;
padded_string docdata;
auto error = padded_string::load(GSOC_JSON).get(docdata);
if(error) {
cerr << "could not parse gsoc-2018.json" << error << endl;
return;
}
std::unique_ptr<char[]> buffer{new char[docdata.size()]};
size_t bytes = 0;
for (simdjson_unused auto _ : state) {
size_t new_length{}; // It will receive the minified length.
auto error = simdjson::minify(docdata.data(), docdata.size(), buffer.get(), new_length);
bytes += docdata.size();
benchmark::DoNotOptimize(error);
}
// Gigabyte: https://en.wikipedia.org/wiki/Gigabyte
state.counters["Gigabytes"] = benchmark::Counter(
double(bytes), benchmark::Counter::kIsRate,
benchmark::Counter::OneK::kIs1000); // For GiB : kIs1024
state.counters["docs"] = Counter(double(state.iterations()), benchmark::Counter::kIsRate);
}
BENCHMARK(fast_minify_gsoc)->Repetitions(10)->ComputeStatistics("max", [](const std::vector<double>& v) -> double {
return *(std::max_element(std::begin(v), std::end(v)));
})->DisplayAggregatesOnly(true);
static void unicode_validate_twitter(State& state) {
dom::parser parser;
padded_string docdata;
+5 -5
View File
@@ -321,7 +321,7 @@ struct benchmarker {
allocate_stage << allocate_count;
// Run it once to get hot buffers
if(hotbuffers) {
auto result = parser.parse(reinterpret_cast<const uint8_t *>(json.data()), json.size());
auto result = parser.parse((const uint8_t *)json.data(), json.size());
if (result.error()) {
exit_error(string("Failed to parse ") + filename + string(":") + error_message(result.error()));
}
@@ -331,7 +331,7 @@ struct benchmarker {
// Stage 1 (find structurals)
collector.start();
error = parser.implementation->stage1(reinterpret_cast<const uint8_t *>(json.data()), json.size(), stage1_mode::regular);
error = parser.implementation->stage1((const uint8_t *)json.data(), json.size(), false);
event_count stage1_count = collector.end();
stage1 << stage1_count;
if (error) {
@@ -367,7 +367,7 @@ struct benchmarker {
void run_loop(size_t iterations) {
dom::parser parser;
auto firstresult = parser.parse(reinterpret_cast<const uint8_t *>(json.data()), json.size());
auto firstresult = parser.parse((const uint8_t *)json.data(), json.size());
if (firstresult.error()) {
exit_error(string("Failed to parse ") + filename + string(":") + error_message(firstresult.error()));
}
@@ -375,7 +375,7 @@ struct benchmarker {
collector.start();
// some users want something closer to "number of documents per second"
for(size_t i = 0; i < iterations; i++) {
auto result = parser.parse(reinterpret_cast<const uint8_t *>(json.data()), json.size());
auto result = parser.parse((const uint8_t *)json.data(), json.size());
if (result.error()) {
exit_error(string("Failed to parse ") + filename + string(":") + error_message(result.error()));
}
@@ -446,7 +446,7 @@ struct benchmarker {
void print(bool tabbed_output) const {
if (tabbed_output) {
char* filename_copy = reinterpret_cast<char*>(malloc(strlen(filename)+1));
char* filename_copy = (char*)malloc(strlen(filename)+1);
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_DEPRECATED_WARNING // Validated CRT_SECURE safe here
strcpy(filename_copy, filename);
@@ -5,12 +5,9 @@
# checkperf-repo: initialize and sync reference repository (first time only)
# TEST checkperf: runs the actual checkperf test
option(SIMDJSON_ENABLE_DOM_CHECKPERF "Enable DOM performance comparison with main branch" OFF)
# Clone the repository if it's not there
find_package(Git QUIET)
if (SIMDJSON_ENABLE_DOM_CHECKPERF AND Git_FOUND AND (GIT_VERSION_STRING VERSION_GREATER "2.1.4") AND (NOT CMAKE_GENERATOR MATCHES Ninja) AND (NOT MSVC) ) # We use "-C" which requires a recent git
if (Git_FOUND AND (GIT_VERSION_STRING VERSION_GREATER "2.1.4") AND (NOT CMAKE_GENERATOR MATCHES Ninja) ) # We use "-C" which requires a recent git
message(STATUS "Git is available and it is recent. We are enabling checkperf targets.")
# sync_git_repository(myrepo ...) creates two targets:
# myrepo - if the repo does not exist, creates and syncs it against the origin branch
@@ -64,13 +61,7 @@ if (SIMDJSON_ENABLE_DOM_CHECKPERF AND Git_FOUND AND (GIT_VERSION_STRING VERSION_
OUTPUT ${SIMDJSON_CHECKPERF_DIR}/build/cmake_install.cmake # We make many things but this seems the most cross-platform one we can depend on
COMMAND
${CMAKE_COMMAND} -E env CXX=${CMAKE_CXX_COMPILER} CC=${CMAKE_C_COMPILER}
${CMAKE_COMMAND}
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
-DSIMDJSON_GOOGLE_BENCHMARKS=OFF
-DSIMDJSON_COMPETITION=OFF
-DSIMDJSON_DEVELOPER_MODE=YES
-G ${CMAKE_GENERATOR}
..
${CMAKE_COMMAND} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DSIMDJSON_GOOGLE_BENCHMARKS=OFF -DSIMDJSON_COMPETITION=OFF -G ${CMAKE_GENERATOR} ..
WORKING_DIRECTORY ${SIMDJSON_CHECKPERF_DIR}/build
DEPENDS ${SIMDJSON_CHECKPERF_DIR}/build/CMakeCache.txt
)
@@ -79,7 +70,7 @@ if (SIMDJSON_ENABLE_DOM_CHECKPERF AND Git_FOUND AND (GIT_VERSION_STRING VERSION_
if (CMAKE_CONFIGURATION_TYPES)
set(CHECKPERF_PARSE ${SIMDJSON_CHECKPERF_DIR}/build/benchmark/$<CONFIGURATION>/parse)
else()
set(CHECKPERF_PARSE ${SIMDJSON_CHECKPERF_DIR}/build/benchmark/dom/parse)
set(CHECKPERF_PARSE ${SIMDJSON_CHECKPERF_DIR}/build/benchmark/parse)
endif()
add_custom_target(
checkperf-parse ALL # TODO is ALL necessary?
@@ -101,8 +92,6 @@ if (SIMDJSON_ENABLE_DOM_CHECKPERF AND Git_FOUND AND (GIT_VERSION_STRING VERSION_
set_property(TEST checkperf APPEND PROPERTY LABELS per_implementation explicitonly)
set_property(TEST checkperf APPEND PROPERTY DEPENDS parse perfdiff ${SIMDJSON_USER_CMAKECACHE})
set_property(TEST checkperf PROPERTY RUN_SERIAL TRUE)
add_dependencies(per_implementation_tests checkperf)
add_dependencies(explicitonly_tests checkperf)
else()
if (CMAKE_GENERATOR MATCHES Ninja)
message(STATUS "We disable the checkperf targets under Ninja.")
@@ -1,59 +0,0 @@
#pragma once
#ifdef SIMDJSON_COMPETITION_NLOHMANN_JSON
#include "distinct_user_id.h"
namespace distinct_user_id {
using json = nlohmann::json;
struct nlohmann_json_sax {
struct Handler : json::json_sax_t
{
std::vector<uint64_t>& result;
bool user = false;
bool user_id = false;
Handler(std::vector<uint64_t> &r) : result(r) { }
bool key(string_t& val) override {
// Assume that valid user/id pairs appear only once in main array of user objects
if (user) { // If already found user object, find id key
if (val.compare("id") == 0) { user_id = true; }
}
else if (val.compare("user") == 0) { user = true; } // Otherwise, find user object
return true;
}
bool number_unsigned(number_unsigned_t val) override {
if (user_id) {
result.emplace_back(val);
user = false;
user_id = false;
}
return true;
}
// Irrelevant events
bool null() override { return true; }
bool boolean(bool val) override { return true; }
bool number_float(number_float_t val, const string_t& s) override { return true; }
bool number_integer(number_integer_t val) override { return true; }
bool string(string_t& val) override { return true; }
bool start_object(std::size_t elements) override { return true; }
bool end_object() override { return true; }
bool start_array(std::size_t elements) override { return true; }
bool end_array() override { return true; }
bool binary(json::binary_t& val) override { return true; }
bool parse_error(std::size_t position, const std::string& last_token, const json::exception& ex) override { return false; }
}; // Handler
bool run(simdjson::padded_string &json, std::vector<uint64_t> &result) {
Handler handler(result);
json::sax_parse(json.data(), &handler);
return true;
}
}; // nlohmann_json_sax
BENCHMARK_TEMPLATE(distinct_user_id, nlohmann_json_sax)->UseManualTime();
} // namespace distinct_user_id
#endif // SIMDJSON_COMPETITION_NLOHMANN_JSON
@@ -1,61 +0,0 @@
#pragma once
#ifdef SIMDJSON_COMPETITION_RAPIDJSON
#include "distinct_user_id.h"
#include <string.h>
namespace distinct_user_id {
using namespace rapidjson;
struct rapidjson_sax {
struct Handler {
std::vector<uint64_t>& result;
bool user = false;
bool user_id = false;
Handler(std::vector<uint64_t> &r) : result(r) { }
bool Key(const char* key, SizeType length, bool copy) {
// Assume that valid user/id pairs appear only once in main array of user objects
if (user) { // If already found user object, find id key
if ((length == 2) && memcmp(key,"id",2) == 0) { user_id = true; }
}
else if ((length == 4) && memcmp(key,"user",4) == 0) { user = true; } // Otherwise, find user object
return true;
}
bool Uint(unsigned i) { // id values are treated as Uint (not Uint64) by the reader
if (user_id) { // Getting id if previous key was "id" for a user
result.emplace_back(i);
user_id = false;
user = false;
}
return true;
}
// Irrelevant events
bool Null() { return true; }
bool Bool(bool b) { return true; }
bool Double(double d) { return true; }
bool Int(int i) { return true; }
bool Int64(int64_t i) { return true; }
bool Uint64(uint64_t i) { return true; }
bool RawNumber(const char* str, SizeType length, bool copy) { return true; }
bool String(const char* str, SizeType length, bool copy) { return true; }
bool StartObject() { return true; }
bool EndObject(SizeType memberCount) { return true; }
bool StartArray() { return true; }
bool EndArray(SizeType elementCount) { return true; }
}; // handler
bool run(simdjson::padded_string &json, std::vector<uint64_t> &result) {
Reader reader;
Handler handler(result);
InsituStringStream ss(json.data());
reader.Parse<kParseInsituFlag | kParseValidateEncodingFlag | kParseFullPrecisionFlag>(ss,handler);
return true;
}
}; // rapid_jason_sax
BENCHMARK_TEMPLATE(distinct_user_id, rapidjson_sax)->UseManualTime();
} // namespace distinct_user_id
#endif // SIMDJSON_COMPETITION_RAPIDJSON
@@ -1,36 +0,0 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
#include "distinct_user_id.h"
namespace distinct_user_id {
using namespace simdjson;
struct simdjson_dom_json_pointer {
dom::parser parser{};
bool run(simdjson::padded_string &json, std::vector<uint64_t> &result) {
// Walk the document, parsing as we go
auto doc = parser.parse(json);
for (dom::object tweet : doc["statuses"]) {
// We believe that all statuses have a matching
// user, and we are willing to throw when they do not.
result.push_back(tweet.at_pointer("/user/id"));
// Not all tweets have a "retweeted_status", but when they do
// we want to go and find the user within.
auto retweet_id = tweet.at_pointer("/retweeted_status/user/id");
if (retweet_id.error() != NO_SUCH_FIELD) {
result.push_back(retweet_id);
}
}
return true;
}
};
BENCHMARK_TEMPLATE(distinct_user_id, simdjson_dom_json_pointer)->UseManualTime();
} // namespace distinct_user_id
#endif // SIMDJSON_EXCEPTIONS
@@ -1,37 +0,0 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
#include "distinct_user_id.h"
namespace distinct_user_id {
using namespace simdjson;
struct simdjson_ondemand_json_pointer {
ondemand::parser parser{};
bool run(simdjson::padded_string &json, std::vector<uint64_t> &result) {
// Walk the document, parsing as we go
auto doc = parser.iterate(json);
for (ondemand::object tweet : doc.find_field("statuses")) {
// We believe that all statuses have a matching
// user, and we are willing to throw when they do not.
result.push_back(tweet.at_pointer("/user/id"));
// Not all tweets have a "retweeted_status", but when they do
// we want to go and find the user within.
auto retweet_id = tweet.at_pointer("/retweeted_status/user/id");
if (retweet_id.error() != NO_SUCH_FIELD) {
result.push_back(retweet_id);
}
}
return true;
}
};
BENCHMARK_TEMPLATE(distinct_user_id, simdjson_ondemand_json_pointer)->UseManualTime();
} // namespace distinct_user_id
#endif // SIMDJSON_EXCEPTIONS
-16
View File
@@ -1,16 +0,0 @@
include_directories( .. ../linux )
link_libraries(simdjson-windows-headers test-data)
link_libraries(simdjson)
add_executable(perfdiff perfdiff.cpp)
add_executable(parse parse.cpp)
add_executable(parse_stream parse_stream.cpp)
add_executable(statisticalmodel statisticalmodel.cpp)
add_executable(parse_noutf8validation parse.cpp)
target_compile_definitions(parse_noutf8validation PRIVATE SIMDJSON_SKIPUTF8VALIDATION)
add_executable(parse_nonumberparsing parse.cpp)
target_compile_definitions(parse_nonumberparsing PRIVATE SIMDJSON_SKIPNUMBERPARSING)
add_executable(parse_nostringparsing parse.cpp)
target_compile_definitions(parse_nostringparsing PRIVATE SIMDJSON_SKIPSTRINGPARSING)
include(checkperf.cmake)
-68
View File
@@ -1,68 +0,0 @@
#pragma once
#ifdef SIMDJSON_COMPETITION_NLOHMANN_JSON
#include "find_tweet.h"
namespace find_tweet {
using json = nlohmann::json;
struct nlohmann_json_sax {
using StringType=std::string;
struct Handler : json::json_sax_t
{
bool text_key = false;
bool id_key = false;
bool found_id = false;
uint64_t find_id;
std::string &result;
Handler(std::string &r,uint64_t id): result(r), find_id(id) { }
// We assume id is found before text
bool key(string_t& val) override {
if (found_id) { // If have found id, find text key
if (val.compare("text") == 0) { text_key = true; }
}
else if (val.compare("id") == 0) { id_key = true; } // Otherwise, find id key
return true;
}
bool number_unsigned(number_unsigned_t val) override {
if (id_key && (val == find_id)) { // If id key, check if id value matches find_id
found_id = true;
}
return true;
}
bool string(string_t& val) override {
if (text_key) {
result = val;
return false; // End parsing
}
return true;
}
// Irrelevant events
bool null() override { return true; }
bool boolean(bool val) override { return true; }
bool number_float(number_float_t val, const string_t& s) override { return true; }
bool number_integer(number_integer_t val) override { return true; }
bool start_object(std::size_t elements) override { return true; }
bool end_object() override { return true; }
bool start_array(std::size_t elements) override { return true; }
bool end_array() override { return true; }
bool binary(json::binary_t& val) override { return true; }
bool parse_error(std::size_t position, const std::string& last_token, const json::exception& ex) override { return false; }
}; // Handler
bool run(simdjson::padded_string &json, uint64_t find_id, std::string &result) {
Handler handler(result,find_id);
json::sax_parse(json.data(), &handler);
return true;
}
}; // nlohmann_json_sax
BENCHMARK_TEMPLATE(find_tweet, nlohmann_json_sax)->UseManualTime();
} // namespace find_tweet
#endif // SIMDJSON_COMPETITION_NLOHMANN_JSON
+1 -1
View File
@@ -47,6 +47,6 @@ struct rapidjson_insitu : rapidjson_base {
};
BENCHMARK_TEMPLATE(find_tweet, rapidjson_insitu)->UseManualTime();
} // namespace find_tweet
} // namespace partial_tweets
#endif // SIMDJSON_COMPETITION_RAPIDJSON
-70
View File
@@ -1,70 +0,0 @@
#pragma once
#ifdef SIMDJSON_COMPETITION_RAPIDJSON
#include "find_tweet.h"
#include <string.h>
namespace find_tweet {
using namespace rapidjson;
struct rapidjson_sax {
using StringType=std::string_view;
struct Handler {
bool text_key = false;
bool id_key = false;
bool found_id = false;
uint64_t find_id;
std::string_view &result;
Handler(std::string_view &r,uint64_t id): result(r), find_id(id) { }
// We assume id is found before text
bool Key(const char* key, SizeType length, bool copy) {
if (found_id) { // If have found id, find text key
if ((length == 4) && (memcmp(key,"text",4) == 0)) { text_key = true; }
}
else if ((length == 2) && (memcmp(key,"id",2) == 0)) { id_key = true; } // Otherwise, find id key
return true;
}
bool Uint64(uint64_t i) {
if (id_key && (i == find_id)) { // If id key, check if id value matches find_id
found_id = true;
}
return true;
}
bool String(const char* str, SizeType length, bool copy) {
if (text_key) {
result = {str,length};
return false; // End parsing
}
return true;
}
// Irrelevant events
bool Null() { return true; }
bool Bool(bool b) { return true; }
bool Double(double d) { return true; }
bool Int(int i) { return true; }
bool Int64(int64_t i) { return true; }
bool Uint(unsigned i) { return true; }
bool RawNumber(const char* str, SizeType length, bool copy) { return true; }
bool StartObject() { return true; }
bool EndObject(SizeType memberCount) { return true; }
bool StartArray() { return true; }
bool EndArray(SizeType elementCount) { return true; }
}; // handler
bool run(simdjson::padded_string &json, uint64_t find_id, std::string_view &result) {
Reader reader;
Handler handler(result,find_id);
InsituStringStream ss(json.data());
reader.Parse<kParseInsituFlag | kParseValidateEncodingFlag | kParseFullPrecisionFlag>(ss,handler);
return true;
}
}; // rapidjson_sax
BENCHMARK_TEMPLATE(find_tweet, rapidjson_sax)->UseManualTime();
} // namespace find_tweet
#endif // SIMDJSON_COMPETITION_RAPIDJSON
-1
View File
@@ -4,6 +4,5 @@ namespace json_benchmark {
static constexpr const char *TWITTER_JSON = SIMDJSON_BENCHMARK_DATA_DIR "twitter.json";
static constexpr const char *NUMBERS_JSON = SIMDJSON_BENCHMARK_DATA_DIR "numbers.json";
static constexpr const char *AMAZON_CELLPHONES_NDJSON = SIMDJSON_BENCHMARK_DATA_DIR "amazon_cellphones.ndjson";
}
-51
View File
@@ -1,7 +1,6 @@
#pragma once
#include <vector>
#include <map>
#include <sstream>
#include <limits>
@@ -53,56 +52,6 @@ struct result_differ<std::vector<T>, std::vector<U>> {
}
};
template<typename T, typename U, typename StringType>
struct result_differ<std::map<StringType,T>, std::map<StringType,U>> {
static bool diff(benchmark::State &state, const std::map<StringType,T> &result, const std::map<StringType,U> &reference, diff_flags flags) {
auto result_iter = result.begin();
auto reference_iter = reference.begin();
while (result_iter != result.end() && reference_iter != reference.end()) {
if (!diff_results(state, *result_iter, *reference_iter, flags)) { return false; }
result_iter++;
reference_iter++;
}
if (result_iter != result.end()) {
std::stringstream str;
str << "extra results (got " << result.size() << ", expected " << reference.size() << "): first extra element: " << *result_iter;
state.SkipWithError(str.str().data());
return false;
} else if (reference_iter != reference.end()) {
std::stringstream str;
str << "missing results (got " << result.size() << ", expected " << reference.size() << "): first missing element: " << *reference_iter;
state.SkipWithError(str.str().data());
return false;
}
return true;
}
};
template<typename T, typename U>
struct result_differ<std::map<std::string_view,T>, std::vector<std::string_view,U>> {
static bool diff(benchmark::State &state, const std::map<std::string_view,T> &result, const std::map<std::string_view,U> &reference, diff_flags flags) {
auto result_iter = result.begin();
auto reference_iter = reference.begin();
while (result_iter != result.end() && reference_iter != reference.end()) {
if (!diff_results(state, *result_iter, *reference_iter, flags)) { return false; }
result_iter++;
reference_iter++;
}
if (result_iter != result.end()) {
std::stringstream str;
str << "extra results (got " << result.size() << ", expected " << reference.size() << "): first extra element: " << *result_iter;
state.SkipWithError(str.str().data());
return false;
} else if (reference_iter != reference.end()) {
std::stringstream str;
str << "missing results (got " << result.size() << ", expected " << reference.size() << "): first missing element: " << *reference_iter;
state.SkipWithError(str.str().data());
return false;
}
return true;
}
};
template<>
struct result_differ<double, double> {
static bool diff(benchmark::State &state, const double &result, const double &reference, diff_flags flags) {
@@ -11,8 +11,7 @@ void maybe_display_implementation() {
if(!displayed_implementation) {
displayed_implementation = true;
std::cout << "simdjson::dom implementation: " << simdjson::active_implementation->name() << std::endl;
std::cout << "simdjson::ondemand implementation (stage 1): " << simdjson::active_implementation->name() << std::endl;
std::cout << "simdjson::ondemand implementation (stage 2): " << simdjson::builtin_implementation()->name() << std::endl;
std::cout << "simdjson::ondemand implementation: " << simdjson::builtin_implementation()->name() << std::endl;
}
}
-74
View File
@@ -1,74 +0,0 @@
#pragma once
#ifdef SIMDJSON_COMPETITION_NLOHMANN_JSON
#include "kostya.h"
namespace kostya {
using json = nlohmann::json;
struct nlohmann_json_sax {
static constexpr diff_flags DiffFlags = diff_flags::NONE;
struct Handler : json::json_sax_t
{
size_t k{0};
double buffer[3];
std::vector<point>& result;
Handler(std::vector<point>& r) : result(r) { }
bool key(string_t& val) override {
switch(val[0]) {
case 'x':
k = 0;
break;
case 'y':
k = 1;
break;
case 'z':
k = 2;
break;
}
return true;
}
bool number_float(number_float_t val, const string_t& s) override {
buffer[k] = val;
if (k == 2) {
result.emplace_back(json_benchmark::point{buffer[0],buffer[1],buffer[2]});
k = 0;
}
return true;
}
bool number_unsigned(number_unsigned_t val) override { // Need this event because coordinate value can be equal to 1
buffer[k] = val;
if (k == 2) {
result.emplace_back(json_benchmark::point{buffer[0],buffer[1],buffer[2]});
k = 0;
}
return true;
}
// Irrelevant events
bool null() override { return true; }
bool boolean(bool val) override { return true; }
bool number_integer(number_integer_t val) override { return true; }
bool string(string_t& val) override { return true; }
bool start_object(std::size_t elements) override { return true; }
bool end_object() override { return true; }
bool start_array(std::size_t elements) override { return true; }
bool end_array() override { return true; }
bool binary(json::binary_t& val) override { return true; }
bool parse_error(std::size_t position, const std::string& last_token, const json::exception& ex) override { return false; }
}; // Handler
bool run(simdjson::padded_string &json, std::vector<point> &result) {
Handler handler(result);
json::sax_parse(json.data(), &handler);
return true;
}
}; // nlohmann_json_sax
BENCHMARK_TEMPLATE(kostya, nlohmann_json_sax)->UseManualTime();
} // namespace kostya
#endif // SIMDJSON_COMPETITION_NLOHMANN_JSON
-70
View File
@@ -1,70 +0,0 @@
#pragma once
#ifdef SIMDJSON_COMPETITION_RAPIDJSON
#include "kostya.h"
namespace kostya {
using namespace rapidjson;
struct rapidjson_sax {
static constexpr diff_flags DiffFlags = diff_flags::NONE;
struct Handler {
size_t k{0};
double buffer[3];
std::vector<point>& result;
Handler(std::vector<point> &r) : result(r) { }
bool Key(const char* key, SizeType length, bool copy) {
switch(key[0]) {
case 'x':
k = 0;
break;
case 'y':
k = 1;
break;
case 'z':
k = 2;
break;
}
return true;
}
bool Double(double d) {
buffer[k] = d;
if (k == 2) {
result.emplace_back(json_benchmark::point{buffer[0],buffer[1],buffer[2]});
k = 0;
}
return true;
}
bool Uint(unsigned i) { return Double(i); } // Need this event because coordinate value can be equal to 1
// Irrelevant events
bool Null() { return true; }
bool Bool(bool b) { return true; }
bool Int(int i) { return true; }
bool Int64(int64_t i) { return true; }
bool Uint64(uint64_t i) { return true; }
bool RawNumber(const char* str, SizeType length, bool copy) { return true; }
bool String(const char* str, SizeType length, bool copy) { return true; }
bool StartObject() { return true; }
bool EndObject(SizeType memberCount) { return true; }
bool StartArray() { return true; }
bool EndArray(SizeType elementCount) { return true; }
}; // handler
bool run(simdjson::padded_string &json, std::vector<point> &result) {
Reader reader;
Handler handler(result);
InsituStringStream ss(json.data());
reader.Parse<kParseInsituFlag | kParseValidateEncodingFlag | kParseFullPrecisionFlag>(ss,handler);
return true;
}
}; // rapid_jason_sax
BENCHMARK_TEMPLATE(kostya, rapidjson_sax)->UseManualTime();
} // namespace kostya
#endif // SIMDJSON_COMPETITION_RAPIDJSON
+2 -4
View File
@@ -16,16 +16,14 @@ struct yyjson_base {
switch (yyjson_get_subtype(val)) {
case YYJSON_SUBTYPE_UINT:
return double(yyjson_get_uint(val));
return yyjson_get_uint(val);
case YYJSON_SUBTYPE_SINT:
return double(yyjson_get_sint(val));
return yyjson_get_sint(val);
case YYJSON_SUBTYPE_REAL:
return yyjson_get_real(val);
default:
SIMDJSON_UNREACHABLE();
}
SIMDJSON_UNREACHABLE();
return 0.0; // unreachable
}
bool run(yyjson_doc *doc, std::vector<point> &result) {
@@ -1,91 +0,0 @@
#pragma once
#include "json_benchmark/string_runner.h"
#include <map>
#include <string>
namespace large_amazon_cellphones {
static const simdjson::padded_string &get_built_json();
using namespace json_benchmark;
struct brand {
double cumulative_rating;
uint64_t reviews_count;
simdjson_really_inline bool operator==(const brand &other) const {
return cumulative_rating == other.cumulative_rating &&
reviews_count == other.reviews_count;
}
simdjson_really_inline bool operator!=(const brand &other) const { return !(*this == other); }
};
simdjson_unused static std::ostream &operator<<(std::ostream &o, const brand &b) {
o << "cumulative_rating: " << b.cumulative_rating << std::endl;
o << "reviews_count: " << b.reviews_count << std::endl;
return o;
}
template<typename StringType>
simdjson_unused static std::ostream &operator<<(std::ostream &o, const std::pair<const StringType, brand> &p) {
o << "brand: " << p.first << std::endl;
o << p.second;
return o;
}
template<typename I>
struct runner : public string_runner<I> {
std::map<typename I::StringType, brand> result{};
runner() : string_runner<I>(get_built_json()) {}
bool before_run(benchmark::State &state) {
if (!string_runner<I>::before_run(state)) { return false; }
result.clear();
return true;
}
bool run(benchmark::State &) {
return this->implementation.run(this->json, result);
}
template<typename R>
bool diff(benchmark::State &state, runner<R> &reference) {
return diff_results(state, result, reference.result, diff_flags::NONE);
}
size_t items_per_iteration() {
return result.size();
}
};
static std::string build_json(size_t N) {
std::ifstream in(AMAZON_CELLPHONES_NDJSON);
std::string answer((std::istreambuf_iterator<char>(in)), std::istreambuf_iterator<char>());
// Find position of first line to exclude it in further copies
size_t first_line = answer.find('\n');
std::string copy(answer,first_line + 1);
size_t count{1};
while (answer.size() < N) {
answer.append(copy);
count++;
}
std::cout << "Creating a source file spanning " << (answer.size() + 512) / (1024*1024) << " MB (" << count << " copies of original file)" << std::endl;
return answer;
}
static const simdjson::padded_string &get_built_json() {
static simdjson::padded_string json = build_json(10*1024*1024);
return json;
}
struct simdjson_dom;
template<typename I> simdjson_really_inline static void large_amazon_cellphones(benchmark::State &state) {
run_json_benchmark<runner<I>, runner<simdjson_dom>>(state);
}
} // namespace large_amazon_cellphones
@@ -1,45 +0,0 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
#include "large_amazon_cellphones.h"
#include <algorithm>
namespace large_amazon_cellphones {
using namespace simdjson;
struct simdjson_dom {
using StringType = std::string;
dom::parser parser{};
bool run(simdjson::padded_string &json, std::map<StringType, brand> &result) {
auto stream = parser.parse_many(json);
auto i = stream.begin();
++i; // Skip first line
for (;i != stream.end(); ++i) {
auto doc = *i;
StringType copy(std::string_view(doc.at(1)));
auto x = result.find(copy);
if (x == result.end()) { // If key not found, add new key
result.emplace(copy, large_amazon_cellphones::brand{
double(doc.at(5)) * uint64_t(doc.at(7)),
uint64_t(doc.at(7))
});
} else { // Otherwise, update key data
x->second.cumulative_rating += double(doc.at(5)) * uint64_t(doc.at(7));
x->second.reviews_count += uint64_t(doc.at(7));
}
}
return true;
}
};
BENCHMARK_TEMPLATE(large_amazon_cellphones, simdjson_dom)->UseManualTime();
} // namespace large_amazon_cellphones
#endif // SIMDJSON_EXCEPTIONS
@@ -1,65 +0,0 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
#include "large_amazon_cellphones.h"
namespace large_amazon_cellphones {
using namespace simdjson;
struct simdjson_ondemand {
using StringType = std::string;
ondemand::parser parser{};
bool run(simdjson::padded_string &json, std::map<StringType, brand> &result) {
ondemand::document_stream stream = parser.iterate_many(json);
ondemand::document_stream::iterator i = stream.begin();
++i; // Skip first line
for (;i != stream.end(); ++i) {
auto & doc = *i;
size_t index{0};
StringType copy;
double rating;
uint64_t reviews;
for ( auto value : doc ) {
switch (index)
{
case 1:
copy = StringType(std::string_view(value));
break;
case 5:
rating = double(value);
break;
case 7:
reviews = uint64_t(value);
break;
default:
break;
}
index++;
}
auto x = result.find(copy);
if (x == result.end()) { // If key not found, add new key
result.emplace(copy, large_amazon_cellphones::brand{
rating * reviews,
reviews
});
} else { // Otherwise, update key data
x->second.cumulative_rating += rating * reviews;
x->second.reviews_count += reviews;
}
}
return true;
}
};
BENCHMARK_TEMPLATE(large_amazon_cellphones, simdjson_ondemand)->UseManualTime();
} // namespace amazon_cellphones
#endif // SIMDJSON_EXCEPTIONS
@@ -1,73 +0,0 @@
#pragma once
#ifdef SIMDJSON_COMPETITION_NLOHMANN_JSON
#include "large_random.h"
namespace large_random {
using json = nlohmann::json;
struct nlohmann_json_sax {
static constexpr diff_flags DiffFlags = diff_flags::NONE;
struct Handler : json::json_sax_t
{
size_t k{0};
double buffer[3];
std::vector<point>& result;
Handler(std::vector<point> &r) : result(r) { }
bool key(string_t& val) override {
switch(val[0]) {
case 'x':
k = 0;
break;
case 'y':
k = 1;
break;
case 'z':
k = 2;
break;
}
return true;
}
bool number_unsigned(number_unsigned_t val) override {
buffer[k] = val;
if (k == 2) {
result.emplace_back(json_benchmark::point{buffer[0],buffer[1],buffer[2]});
k = 0;
}
return true;
}
bool number_float(number_float_t val, const string_t& s) override {
buffer[k] = val;
if (k == 2) {
result.emplace_back(json_benchmark::point{buffer[0],buffer[1],buffer[2]});
k = 0;
}
return true;
}
// Irrelevant events
bool null() override { return true; }
bool boolean(bool val) override { return true; }
bool number_integer(number_integer_t val) override { return true; }
bool string(string_t& val) override { return true; }
bool start_object(std::size_t elements) override { return true; }
bool end_object() override { return true; }
bool start_array(std::size_t elements) override { return true; }
bool end_array() override { return true; }
bool binary(json::binary_t& val) override { return true; }
bool parse_error(std::size_t position, const std::string& last_token, const json::exception& ex) override { return false; }
}; // Handler
bool run(simdjson::padded_string &json, std::vector<point> &result) {
Handler handler(result);
json::sax_parse(json.data(), &handler);
return true;
}
}; // nlohmann_json_sax
BENCHMARK_TEMPLATE(large_random, nlohmann_json_sax)->UseManualTime();
} // namespace large_random
#endif // SIMDJSON_COMPETITION_NLOHMANN_JSON
-69
View File
@@ -1,69 +0,0 @@
#pragma once
#ifdef SIMDJSON_COMPETITION_RAPIDJSON
#include "large_random.h"
namespace large_random {
using namespace rapidjson;
struct rapidjson_sax {
static constexpr diff_flags DiffFlags = diff_flags::NONE;
struct Handler {
size_t k{0};
double buffer[3];
std::vector<point>& result;
Handler(std::vector<point> &r) : result(r) { }
bool Key(const char* key, SizeType length, bool copy) {
switch(key[0]) {
case 'x':
k = 0;
break;
case 'y':
k = 1;
break;
case 'z':
k = 2;
break;
}
return true;
}
bool Double(double d) {
buffer[k] = d;
if (k == 2) {
result.emplace_back(json_benchmark::point{buffer[0],buffer[1],buffer[2]});
k = 0;
}
return true;
}
bool Uint(unsigned i) { return Double(i); } // Need this event because coordinate value can be equal to 1
// Irrelevant events
bool Null() { return true; }
bool Bool(bool b) { return true; }
bool Int(int i) { return true; }
bool Int64(int64_t i) { return true; }
bool Uint64(uint64_t i) { return true; }
bool RawNumber(const char* str, SizeType length, bool copy) { return true; }
bool String(const char* str, SizeType length, bool copy) { return true; }
bool StartObject() { return true; }
bool EndObject(SizeType memberCount) { return true; }
bool StartArray() { return true; }
bool EndArray(SizeType elementCount) { return true; }
}; // handler
bool run(simdjson::padded_string &json, std::vector<point> &result) {
Reader reader;
Handler handler(result);
InsituStringStream ss(json.data());
reader.Parse<kParseInsituFlag | kParseValidateEncodingFlag | kParseFullPrecisionFlag>(ss,handler);
return true;
}
}; // rapid_jason_sax
BENCHMARK_TEMPLATE(large_random, rapidjson_sax)->UseManualTime();
} // namespace large_random
#endif // SIMDJSON_COMPETITION_RAPIDJSON
+2 -4
View File
@@ -16,16 +16,14 @@ struct yyjson_base {
switch (yyjson_get_subtype(val)) {
case YYJSON_SUBTYPE_UINT:
return double(yyjson_get_uint(val));
return yyjson_get_uint(val);
case YYJSON_SUBTYPE_SINT:
return double(yyjson_get_sint(val));
return yyjson_get_sint(val);
case YYJSON_SUBTYPE_REAL:
return yyjson_get_real(val);
default:
SIMDJSON_UNREACHABLE();
}
SIMDJSON_UNREACHABLE();
return 0.0; // unreachable
}
bool run(yyjson_doc *doc, std::vector<point> &result) {
+3 -10
View File
@@ -29,7 +29,6 @@ template <int TYPE = PERF_TYPE_HARDWARE> class LinuxEvents {
size_t num_events{};
std::vector<uint64_t> temp_result_vec{};
std::vector<uint64_t> result{};
std::vector<int> fds{};
bool quiet;
public:
@@ -53,12 +52,10 @@ public:
uint32_t i = 0;
for (auto config : config_vec) {
attribs.config = config;
int _fd = static_cast<int>(syscall(__NR_perf_event_open, &attribs, pid, cpu, group, flags));
if (_fd == -1) {
fd = static_cast<int>(syscall(__NR_perf_event_open, &attribs, pid, cpu, group, flags));
if (fd == -1) {
report_error("perf_event_open");
}
fd = _fd; // fd tracks the last _fd value.
fds.push_back(fd);
ioctl(fd, PERF_EVENT_IOC_ID, &result[i++]);
if (group == -1) {
group = fd;
@@ -68,11 +65,7 @@ public:
temp_result_vec.resize(num_events * 2 + 1);
}
~LinuxEvents() {
for (auto tfd : fds) {
if (tfd != -1) { close(tfd); }
}
}
~LinuxEvents() { if (fd != -1) { close(fd); } }
inline void start() {
if (fd != -1) {
@@ -1,159 +0,0 @@
#pragma once
#ifdef SIMDJSON_COMPETITION_NLOHMANN_JSON
#include "partial_tweets.h"
namespace partial_tweets {
using json = nlohmann::json;
struct nlohmann_json_sax {
using StringType=std::string;
struct Handler : json::json_sax_t
{
// 8 keys to parse for each tweet (in order of appearance): "created_at", "id", "text", "in_reply_status_id", "id"(user),
// "screen_name"(user), "retweet_count" and "favorite_count".
// Assume that the first valid key encountered will be the correct key to parse.
// Assume that each tweet/retweet start with a key "metadata" and has a key "retweeted" towards the end
// The previous assumption will be used to check for the beginning of a new tweet and the end of a retweet
enum state { // Bitset to store state of search
key_date = (1<<0),
key_id = (1<<1),
key_text = (1<<2),
key_reply = (1<<3),
key_userid = (1<<4),
key_screenname = (1<<5),
key_rt = (1<<6),
key_fav = (1<<7),
found_date = (1<<8),
found_id = (1<<9),
found_text = (1<<10),
found_reply = (1<<11),
found_userid = (1<<12),
found_screenname = (1<<13),
found_rt = (1<<14),
found_fav = (1<<15)
};
int values = state::key_date;
bool userobject_id = false; // If in a user object (to find user.id)
bool userobject_screen_name = false; // If in a user object (to find user.screen_name)
bool inretweet = false; // If in a retweet (all keys irrelevant in retweet object)
// Fields to store partial tweet info
uint64_t user_id;
uint64_t id;
uint64_t rt;
uint64_t fav;
uint64_t reply_status;
string_t screen_name;
string_t date;
string_t text;
std::vector<tweet<std::string>>& result;
Handler(std::vector<tweet<std::string>> &r) : result(r) { }
bool key(string_t& val) override {
if (!inretweet) { // If not in a retweet object, find relevant keys
if (val.compare("retweeted_status") == 0) { inretweet = true; } // Check if entering retweet
else if (val.compare("metadata") == 0) { values = 0; } // Reset
// Check if key has been found and if key matches a valid key
else if (!(values & found_date) && (val.compare("created_at") == 0)) { values |= (key_date); }
// Must also check if not in a user object
else if (!(values & found_id) && !userobject_id && (val.compare("id") == 0)) { values |= (key_id); }
else if (!(values & found_text) && (val.compare("text") == 0)) { values |= (key_text); }
else if (!(values & found_reply) && (val.compare("in_reply_to_status_id") == 0)) { values |= (key_reply); }
// Check if entering user object
else if ((val.compare("user") == 0)) { userobject_id = userobject_screen_name = true; }
// Must also check if in a user object
else if (!(values & found_userid) && userobject_id && (val.compare("id") == 0)) { values |= (key_userid); }
// Must also check if in a user object
else if (!(values & found_screenname) && userobject_screen_name && (val.compare("screen_name") == 0)) { values |= (key_screenname); }
else if (!(values & found_rt) && (val.compare("retweet_count") == 0)) { values |= (key_rt); }
else if (!(values & found_fav) && (val.compare("favorite_count") == 0)) { values |= (key_fav); }
}
else if (val.compare("retweeted") == 0) { inretweet = false; } // Check if end of retweet
return true;
}
bool number_unsigned(number_unsigned_t val) override {
if (values & key_id && !(values & found_id)) { // id
id = val;
values &= ~(key_id);
values |= (found_id);
}
else if (values & key_reply && !(values & found_reply)) { // in_reply_status_id
reply_status = val;
values &= ~(key_reply);
values |= (found_reply);
}
else if (values & key_userid && !(values & found_userid)) { // user.id
user_id = val;
userobject_id = false;
values &= ~(key_userid);
values |= (found_userid);
}
else if (values & key_rt && !(values & found_rt)) { // retweet_count
rt = val;
values &= ~(key_rt);
values |= (found_rt);
}
else if (values & key_fav && !(values & found_fav)) { // favorite_count
fav = val;
values &= ~(key_fav);
values |= (found_fav);
// Assume that this is last key required, so add the partial_tweet to result
result.emplace_back(partial_tweets::tweet<std::string>{
date,id,text,reply_status,{user_id,screen_name},rt,fav});
}
return true;
}
bool string(string_t& val) override {
if (values & key_date && !(values & found_date)) { // created_at
date = val;
values &= ~(key_date);
values |= (found_date);
}
else if (values & key_text && !(values & found_text)) { // text
text = val;
values &= ~(key_text);
values |= (found_text);
}
else if (values & key_screenname && !(values & found_screenname)) { // user.screen_name
screen_name = val;
userobject_screen_name = false;
values &= ~(key_screenname);
values |= (found_screenname);
}
return true;
}
bool null() override {
if (values & key_reply && !(values & found_reply)) { // in_reply_status (null case)
reply_status = 0;
values &= ~(key_reply);
values |= (found_reply);
}
return true;
}
// Irrelevant events
bool boolean(bool val) override { return true; }
bool number_float(number_float_t val, const string_t& s) override { return true; }
bool number_integer(number_integer_t val) override { return true; }
bool start_object(std::size_t elements) override { return true; }
bool end_object() override { return true; }
bool start_array(std::size_t elements) override { return true; }
bool end_array() override { return true; }
bool binary(json::binary_t& val) override { return true; }
bool parse_error(std::size_t position, const std::string& last_token, const json::exception& ex) override { return false; }
}; // Handler
bool run(simdjson::padded_string &json, std::vector<tweet<std::string>> &result) {
Handler handler(result);
json::sax_parse(json.data(), &handler);
return true;
}
}; // nlohmann_json_sax
BENCHMARK_TEMPLATE(partial_tweets, nlohmann_json_sax)->UseManualTime();
} // namespace partial_tweets
#endif // SIMDJSON_COMPETITION_NLOHMANN_JSON
-165
View File
@@ -1,165 +0,0 @@
#pragma once
#ifdef SIMDJSON_COMPETITION_RAPIDJSON
#include "partial_tweets.h"
#include <string.h>
#include <fstream>
namespace partial_tweets {
using namespace rapidjson;
struct rapidjson_sax {
using StringType=std::string_view;
// 8 keys to parse for each tweet (in order of appearance): "created_at", "id", "text", "in_reply_status_id", "id"(user),
// "screen_name"(user), "retweet_count" and "favorite_count".
// Assume that the first valid key encountered will be the correct key to parse.
// Assume that each tweet/retweet start with a key "metadata" and has a key "retweeted" towards the end
// The previous assumption will be used to check for the beginning of a new tweet and the end of a retweet
struct Handler {
enum state { // Bitset to store state of search
key_date = (1<<0),
key_id = (1<<1),
key_text = (1<<2),
key_reply = (1<<3),
key_userid = (1<<4),
key_screenname = (1<<5),
key_rt = (1<<6),
key_fav = (1<<7),
found_date = (1<<8),
found_id = (1<<9),
found_text = (1<<10),
found_reply = (1<<11),
found_userid = (1<<12),
found_screenname = (1<<13),
found_rt = (1<<14),
found_fav = (1<<15)
};
int values = state::key_date;
bool userobject_id = false; // If in a user object (to find user.id)
bool userobject_screen_name = false; // If in a user object (to find user.screen_name)
bool inretweet = false; // If in a retweet (all keys irrelevant in retweet object)
// Fields to store partial tweet info
uint64_t user_id;
uint64_t id;
uint64_t rt;
uint64_t fav;
uint64_t reply_status;
std::string_view screen_name;
std::string_view date;
std::string_view text;
std::vector<tweet<std::string_view>>& result;
Handler(std::vector<tweet<std::string_view>> &r) : result(r) { }
bool Key(const char* key, SizeType length, bool copy) {
if (!inretweet) { // If not in a retweet object, find relevant keys
if ((length == 16) && (memcmp(key,"retweeted_status",16) == 0)) { inretweet = true; } // Check if entering retweet
else if ((length == 8) && (memcmp(key,"metadata",8) == 0)) { values = 0; } // Reset
// Check if key has been found and if key matches a valid key
else if (!(values & found_date) && (length == 10) && (memcmp(key,"created_at",10) == 0)) { values |= (key_date); }
// Must also check if not in a user object
else if (!(values & found_id) && !userobject_id && (length == 2) && (memcmp(key,"id",2) == 0)) { values |= (key_id); }
else if (!(values & found_text) && (length == 4) && (memcmp(key,"text",4) == 0)) { values |= (key_text); }
else if (!(values & found_reply) && (length == 21) && (memcmp(key,"in_reply_to_status_id",21) == 0)) { values |= (key_reply); }
// Check if entering user object
else if ((length == 4) && (memcmp(key,"user",4) == 0)) { userobject_id = userobject_screen_name = true; }
// Must also check if in a user object
else if (!(values & found_userid) && userobject_id && (length == 2) && (memcmp(key,"id",2) == 0)) { values |= (key_userid); }
// Must also check if in a user object
else if (!(values & found_screenname) && userobject_screen_name && (length == 11) && (memcmp(key,"screen_name",11) == 0)) { values |= (key_screenname); }
else if (!(values & found_rt) && (length == 13) && (memcmp(key,"retweet_count",13) == 0)) { values |= (key_rt); }
else if (!(values & found_fav) && (length == 14) && (memcmp(key,"favorite_count",14) == 0)) { values |= (key_fav); }
}
else if ((length == 9) && (memcmp(key,"retweeted",9) == 0)) { inretweet = false; } // Check if end of retweet
return true;
}
bool Uint(unsigned i) {
if (values & key_userid && !(values & found_userid)) { // user.id
user_id = i;
userobject_id = false;
values &= ~(key_userid);
values |= (found_userid);
}
else if (values & key_rt && !(values & found_rt)) { // retweet_count
rt = i;
values &= ~(key_rt);
values |= (found_rt);
}
else if (values & key_fav && !(values & found_fav)) { // favorite_count
fav = i;
values &= ~(key_fav);
values |= (found_fav);
// Assume that this is last key required, so add the partial_tweet to result
result.emplace_back(partial_tweets::tweet<std::string_view>{
date,id,text,reply_status,{user_id,screen_name},rt,fav});
}
return true;
}
bool Uint64(uint64_t i) {
if (values & key_id && !(values & found_id)) { // id
id = i;
values &= ~(key_id);
values |= (found_id);
}
else if (values & key_reply && !(values & found_reply)) { // in_reply_status_id
reply_status = i;
values &= ~(key_reply);
values |= (found_reply);
}
return true;
}
bool String(const char* str, SizeType length, bool copy) {
if (values & key_date && !(values & found_date)) { // created_at
date = {str,length};
values &= ~(key_date);
values |= (found_date);
}
else if (values & key_text && !(values & found_text)) { // text
text = {str,length};
values &= ~(key_text);
values |= (found_text);
}
else if (values & key_screenname && !(values & found_screenname)) { // user.screen_name
screen_name = {str,length};
userobject_screen_name = false;
values &= ~(key_screenname);
values |= (found_screenname);
}
return true;
}
bool Null() {
if (values & key_reply && !(values & found_reply)) { // in_reply_status (null case)
reply_status = 0;
values &= ~(key_reply);
values |= (found_reply);
}
return true;
}
// Irrelevant events
bool Bool(bool b) { return true; }
bool Double(double d) { return true; }
bool Int(int i) { return true; }
bool Int64(int64_t i) { return true; }
bool RawNumber(const char* str, SizeType length, bool copy) { return true; }
bool StartObject() { return true; }
bool EndObject(SizeType memberCount) { return true; }
bool StartArray() { return true; }
bool EndArray(SizeType elementCount) { return true; }
}; // handler
bool run(simdjson::padded_string &json, std::vector<tweet<std::string_view>> &result) {
Reader reader;
Handler handler(result);
InsituStringStream ss(json.data());
reader.Parse<kParseInsituFlag | kParseValidateEncodingFlag | kParseFullPrecisionFlag>(ss,handler);
return true;
}
}; // rapid_jason_sax
BENCHMARK_TEMPLATE(partial_tweets, rapidjson_sax)->UseManualTime();
} // namespace partial_tweets
#endif // SIMDJSON_COMPETITION_RAPIDJSON
@@ -183,7 +183,7 @@ int main(int argc, char *argv[]) {
for (uint32_t i = 0; i < iterations; i++) {
unified.start();
// The default template is simdjson::architecture::NATIVE.
bool isok = (parser.implementation->stage1((const uint8_t *)p.data(), p.size(), simdjson::stage1_mode::regular) == simdjson::SUCCESS);
bool isok = (parser.implementation->stage1((const uint8_t *)p.data(), p.size(), false) == simdjson::SUCCESS);
unified.end(results);
cy1 += results[0];
-99
View File
@@ -1,99 +0,0 @@
#pragma once
#ifdef SIMDJSON_COMPETITION_NLOHMANN_JSON
#include "top_tweet.h"
namespace top_tweet {
using json = nlohmann::json;
struct nlohmann_json_sax {
using StringType=std::string;
struct Handler : json::json_sax_t
{
// Assume every tweet/retweet starts with "metadata" key and ends with "retweeted" key. Ignore everything in a retweet.
// Assume that the first valid key encountered outside a retweet is the correct key.
enum state { // Bitset to store state of search
key_text = (1<<0),
key_screen_name = (1<<1),
key_rt = (1<<2),
found_text = (1<<3),
found_screen_name = (1<<4),
found_rt = (1<<5)
};
int values = state::key_text;
bool userobject = false; // If in a user object
bool inretweet = false;
int64_t max_rt;
int rt;
string_t screen_name;
string_t text;
top_tweet_result<StringType>& result;
Handler(top_tweet_result<StringType> &r,int64_t m) : result(r), max_rt(m) { }
bool key(string_t& val) override {
if (!inretweet) { // If not in a retweet object, find relevant keys
if (val.compare("retweeted_status") == 0) { inretweet = true; } // Check if entering retweet
else if (val.compare("metadata") == 0) { values = 0; } // Reset
else if (!(values & found_text) && (val.compare("text") == 0)) { values |= (key_text); }
else if ((val.compare("user") == 0)) { userobject = true; }
else if (!(values & found_screen_name) && userobject && (val.compare("screen_name") == 0)) { values |= (key_screen_name); }
else if (!(values & found_rt) && (val.compare("retweet_count") == 0)) { values |= (key_rt); }
}
else if (val.compare("retweeted") == 0) { inretweet = false; } // Check if end of retweet
return true;
}
bool number_unsigned(number_unsigned_t val) override {
if (values & key_rt && !(values & found_rt)) { // retweet_count
rt = val;
values &= ~(key_rt);
values |= (found_rt);
if (rt <= max_rt && rt >= result.retweet_count) { // Check if current tweet has more retweet than previous top tweet
result.retweet_count = rt;
result.text = text;
result.screen_name = screen_name;
}
}
return true;
}
bool string(string_t& val) override {
if (values & key_text && !(values & found_text)) { // text
text = val;
values &= ~(key_text);
values |= (found_text);
}
else if (values & key_screen_name && !(values & found_screen_name)) { // user.screen_name
screen_name = val;
userobject = false;
values &= ~(key_screen_name);
values |= (found_screen_name);
}
return true;
}
// Irrelevant events
bool null() override { return true; }
bool boolean(bool val) override { return true; }
bool number_float(number_float_t val, const string_t& s) override { return true; }
bool number_integer(number_integer_t val) override { return true; }
bool start_object(std::size_t elements) override { return true; }
bool end_object() override { return true; }
bool start_array(std::size_t elements) override { return true; }
bool end_array() override { return true; }
bool binary(json::binary_t& val) override { return true; }
bool parse_error(std::size_t position, const std::string& last_token, const json::exception& ex) override { return false; }
}; // Handler
bool run(simdjson::padded_string &json, int64_t max_retweet_count, top_tweet_result<StringType> &result) {
result.retweet_count = -1;
Handler handler(result,max_retweet_count);
json::sax_parse(json.data(), &handler);
return true;
}
}; // nlohmann_json_sax
BENCHMARK_TEMPLATE(top_tweet, nlohmann_json_sax)->UseManualTime();
} // namespace top_tweet
#endif // SIMDJSON_COMPETITION_NLOHMANN_JSON
+1 -1
View File
@@ -64,6 +64,6 @@ struct rapidjson_insitu : rapidjson_base {
};
BENCHMARK_TEMPLATE(top_tweet, rapidjson_insitu)->UseManualTime();
} // namespace top_tweet
} // namespace partial_tweets
#endif // SIMDJSON_COMPETITION_RAPIDJSON
-101
View File
@@ -1,101 +0,0 @@
#pragma once
#ifdef SIMDJSON_COMPETITION_RAPIDJSON
#include "top_tweet.h"
#include <string.h>
namespace top_tweet {
using namespace rapidjson;
struct rapidjson_sax {
using StringType=std::string_view;
struct Handler {
// Assume every tweet/retweet starts with "metadata" key and ends with "retweeted" key. Ignore everything in a retweet.
// Assume that the first valid key encountered outside a retweet is the correct key.
enum state { // Bit set to keep track of state of search for keys
key_text = (1<<0),
key_screen_name = (1<<1),
key_rt = (1<<2),
found_text = (1<<3),
found_screen_name = (1<<4),
found_rt = (1<<5)
};
int values = state::key_text;
int rt;
StringType text;
StringType screen_name;
bool inretweet = false;
bool userobject = false;
top_tweet_result<StringType>& result;
int64_t max_rt;
Handler(top_tweet_result<StringType> &r,int64_t m) : result(r), max_rt(m) { }
bool Key(const char* key, SizeType length, bool copy) {
if (!inretweet) {
if ((length == 16) && (memcmp(key,"retweeted_status",16) == 0)) { inretweet = true; } // Check if entering retweet
else if ((length == 8) && (memcmp(key,"metadata",8) == 0)) { values = 0; } // Reset
else if (!(values & found_text) && (length == 4) && (memcmp(key,"text",4) == 0)) { values |= (key_text); }
else if ((length == 4) && (memcmp(key,"user",4) == 0)) { userobject = true; }
else if (!(values & found_screen_name) && userobject && (length == 11) && memcmp(key,"screen_name",11) == 0) { values |= (key_screen_name); }
else if (!(values & found_rt) && (length == 13) && (memcmp(key,"retweet_count",13) == 0)) { values |= (key_rt); }
}
else if ((length == 9) && (memcmp(key,"retweeted",9) == 0)) { inretweet = false; } // Check if end of retweet
return true;
}
bool String(const char* str, SizeType length, bool copy) {
if (values & key_text && !(values & found_text)) { // text
text = {str,length};
values &= ~(key_text);
values |= (found_text);
}
else if (values & key_screen_name && !(values & found_screen_name)) { // user.screen_name
screen_name = {str,length};
values &= ~(key_screen_name);
values |= (found_screen_name);
userobject = false;
}
return true;
}
bool Uint(unsigned i) {
if (values & key_rt && !(values & found_rt)) { // retweet_count
rt = i;
values &= ~(key_rt);
values |= (found_rt);
if (rt <= max_rt && rt >= result.retweet_count) { // Check if current tweet has more retweet than previous top tweet
result.retweet_count = rt;
result.text = text;
result.screen_name = screen_name;
}
}
return true;
}
// Irrelevant events
bool Null() { return true; }
bool Bool(bool b) { return true; }
bool Double(double d) { return true; }
bool Int(int i) { return true; }
bool Int64(int64_t i) { return true; }
bool Uint64(uint64_t i) { return true; }
bool RawNumber(const char* str, SizeType length, bool copy) { return true; }
bool StartObject() { return true; }
bool EndObject(SizeType memberCount) { return true; }
bool StartArray() { return true; }
bool EndArray(SizeType elementCount) { return true; }
}; // handler
bool run(simdjson::padded_string &json, int64_t max_retweet_count, top_tweet_result<StringType> &result) {
result.retweet_count = -1;
Reader reader;
Handler handler(result,max_retweet_count);
InsituStringStream ss(json.data());
reader.Parse<kParseInsituFlag | kParseValidateEncodingFlag | kParseFullPrecisionFlag>(ss,handler);
return true;
}
}; // rapidjson_sax
BENCHMARK_TEMPLATE(top_tweet, rapidjson_sax)->UseManualTime();
} // namespace top_tweet
#endif // SIMDJSON_COMPETITION_RAPIDJSON
-187
View File
@@ -1,187 +0,0 @@
#
# Flags used by exes and by the simdjson library (project-wide flags)
#
add_library(simdjson-internal-flags INTERFACE)
option(SIMDJSON_NO_FORCE_INLINING "Do not attempt to force function inlining" OFF)
if(SIMDJSON_NO_FORCE_INLINING)
add_compile_definitions(SIMDJSON_NO_FORCE_INLINING=1)
endif()
option(SIMDJSON_CHECK_EOF "Check for the end of the input buffer (to avoid buffer overflows). We expect this setting to be ON except for performance testing." ON)
if(SIMDJSON_CHECK_EOF)
add_compile_definitions(SIMDJSON_CHECK_EOF=1)
else()
add_compile_definitions(SIMDJSON_CHECK_EOF=0)
endif()
option(SIMDJSON_SANITIZE_UNDEFINED "Sanitize undefined behavior" OFF)
if(SIMDJSON_SANITIZE_UNDEFINED)
add_compile_options(-fsanitize=undefined -fno-sanitize-recover=all)
add_link_options(-fsanitize=undefined -fno-sanitize-recover=all)
endif()
option(SIMDJSON_SANITIZE "Sanitize addresses" OFF)
if(SIMDJSON_SANITIZE)
if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
message(STATUS "The address sanitizer under Apple's clang appears to be \
incompatible with the undefined-behavior sanitizer.")
message(STATUS "You may set SIMDJSON_SANITIZE_UNDEFINED to sanitize \
undefined behavior.")
add_compile_options(
-fsanitize=address -fno-omit-frame-pointer -fno-sanitize-recover=all
)
add_compile_definitions(ASAN_OPTIONS=detect_leaks=1)
link_libraries(
-fsanitize=address -fno-omit-frame-pointer -fno-sanitize-recover=all
)
else()
message(
STATUS
"Setting both the address sanitizer and the undefined sanitizer."
)
add_compile_options(
-fsanitize=address -fno-omit-frame-pointer
-fsanitize=undefined -fno-sanitize-recover=all
)
link_libraries(
-fsanitize=address -fno-omit-frame-pointer
-fsanitize=undefined -fno-sanitize-recover=all
)
endif()
# Ubuntu bug for GCC 5.0+ (safe for all versions)
if(CMAKE_COMPILER_IS_GNUCC)
link_libraries(-fuse-ld=gold)
endif()
endif()
if(SIMDJSON_SANITIZE_THREADS)
message(STATUS "Setting both the thread sanitizer \
and the undefined-behavior sanitizer.")
add_compile_options(
-fsanitize=thread -fsanitize=undefined -fno-sanitize-recover=all
)
link_libraries(
-fsanitize=thread -fsanitize=undefined -fno-sanitize-recover=all
)
# Ubuntu bug for GCC 5.0+ (safe for all versions)
if(CMAKE_COMPILER_IS_GNUCC)
link_libraries(-fuse-ld=gold)
endif()
endif()
get_cmake_property(is_multi_config GENERATOR_IS_MULTI_CONFIG)
if(NOT is_multi_config AND NOT CMAKE_BUILD_TYPE)
# Deliberately not including SIMDJSON_SANITIZE_THREADS since thread behavior
# depends on the build type.
if(SIMDJSON_SANITIZE OR SIMDJSON_SANITIZE_UNDEFINED)
message(STATUS "No build type selected and you have enabled the sanitizer, \
default to Debug. Consider setting CMAKE_BUILD_TYPE.")
set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build." FORCE)
else()
message(STATUS "No build type selected, default to Release")
set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE)
endif()
endif()
if(NOT MSVC)
option(SIMDJSON_USE_LIBCPP "Use the libc++ library" OFF)
endif()
if(MSVC AND BUILD_SHARED_LIBS)
# This will require special handling.
set(SIMDJSON_WINDOWS_DLL TRUE)
endif()
# We compile tools, tests, etc. with C++ 17. Override yourself if you need on a
# target.
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_MACOSX_RPATH OFF)
set(CMAKE_THREAD_PREFER_PTHREAD ON)
set(THREADS_PREFER_PTHREAD_FLAG ON)
# LTO seems to create all sorts of fun problems. Let us
# disable temporarily.
#include(CheckIPOSupported)
#check_ipo_supported(RESULT ltoresult)
#if(ltoresult)
# set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
#endif()
option(SIMDJSON_VISUAL_STUDIO_BUILD_WITH_DEBUG_INFO_FOR_PROFILING "\
Under Visual Studio, add Zi to the compile flag and DEBUG to the link file to \
add debugging information to the release build for easier profiling inside \
tools like VTune" OFF)
if(MSVC)
if(MSVC_TOOLSET_VERSION STRLESS "142")
set(SIMDJSON_LEGACY_VISUAL_STUDIO TRUE)
message (STATUS "A legacy Visual Studio version was detected. \
We recommend Visual Studio 2019 or better on a 64-bit system.")
endif()
if(MSVC_TOOLSET_VERSION STREQUAL "140")
# Visual Studio 2015 issues warnings and we tolerate it
# cmake -G "Visual Studio 14" ..
target_compile_options(simdjson-internal-flags INTERFACE /W0 /sdl)
else()
# Recent version of Visual Studio expected (2017, 2019...). Prior versions
# are unsupported.
# https://docs.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-4-c4714?view=vs-2019
target_compile_options(simdjson-internal-flags INTERFACE /WX /W3 /sdl /w34714)
if(MSVC_VERSION GREATER 1910)
target_compile_options(simdjson-internal-flags INTERFACE /permissive-)
endif()
endif()
if(SIMDJSON_VISUAL_STUDIO_BUILD_WITH_DEBUG_INFO_FOR_PROFILING)
add_link_options(/DEBUG)
add_compile_options(/Zi)
endif()
else()
if(NOT WIN32)
target_compile_options(simdjson-internal-flags INTERFACE -fPIC)
endif()
target_compile_options(
simdjson-internal-flags INTERFACE
-Werror -Wall -Wextra -Weffc++ -Wsign-compare -Wshadow -Wwrite-strings
-Wpointer-arith -Winit-self -Wconversion -Wno-sign-conversion
)
endif()
#
# Other optional flags
#
option(SIMDJSON_BASH "Allow usage of bash within CMake" ON)
option(
SIMDJSON_VERBOSE_LOGGING
"Enable verbose logging for internal simdjson library development."
OFF
)
if(SIMDJSON_VERBOSE_LOGGING)
add_compile_definitions(SIMDJSON_VERBOSE_LOGGING=1
)
endif()
if(SIMDJSON_USE_LIBCPP)
link_libraries(-stdlib=libc++ -lc++abi)
# instead of the above line, we could have used
# set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++
# -lc++abi")
# The next line is needed empirically.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
# we update CMAKE_SHARED_LINKER_FLAGS, this gets updated later as well
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -lc++abi")
endif()
# prevent shared libraries from depending on Intel provided libraries
if(CMAKE_C_COMPILER_ID MATCHES "Intel")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -static-intel")
endif()
include(CheckSymbolExists)
check_symbol_exists(fork unistd.h HAVE_POSIX_FORK)
check_symbol_exists(wait sys/wait.h HAVE_POSIX_WAIT)
-28
View File
@@ -1,28 +0,0 @@
option(SIMDJSON_EXCEPTIONS "Enable simdjson's exception-throwing interface" ON)
if(NOT SIMDJSON_EXCEPTIONS)
message(STATUS "simdjson exception interface turned off. \
Code that does not check error codes will not compile.")
simdjson_add_props(target_compile_definitions PUBLIC SIMDJSON_EXCEPTIONS=0)
if(MSVC)
if(NOT is_top_project)
message(AUTHOR_WARNING "Turning SIMDJSON_EXCEPTIONS off requires \
editing CMAKE_CXX_FLAGS")
endif()
# CMake currently /EHsc as a default flag in CMAKE_CXX_FLAGS on MSVC.
# Replacing this with a more general abstraction is a WIP
# (see https://gitlab.kitware.com/cmake/cmake/-/issues/20610)
# /EHs enables standard C++ stack unwinding when catching exceptions
# (non-structured exception handling)
# /EHc used in conjection with /EHs indicates that extern "C" functions
# never throw (terminate-on-throw)
# Here, we disable both with the - argument negation operator
string(REPLACE "/EHsc" "/EHs-c-" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
# Because we cannot change the flag above on an individual target (yet), the
# definition below must similarly be added globally
add_definitions(-D_HAS_EXCEPTIONS=0)
elseif(CMAKE_COMPILER_IS_GNUCC)
simdjson_add_props(target_link_libraries PRIVATE -fno-exceptions)
endif()
endif()
-23
View File
@@ -1,23 +0,0 @@
if(DEFINED SIMDJSON_BUILD_STATIC)
message(DEPRECATION "SIMDJSON_BUILD_STATIC is deprecated, setting \
BUILD_SHARED_LIBS with its value and unsetting it")
set(shared ON)
if(SIMDJSON_BUILD_STATIC)
set(shared OFF)
endif()
set(BUILD_SHARED_LIBS "${shared}" CACHE BOOL "" FORCE)
unset(SIMDJSON_BUILD_STATIC CACHE)
endif()
if(DEFINED SIMDJSON_JUST_LIBRARY)
message(DEPRECATION "SIMDJSON_JUST_LIBRARY is deprecated, setting \
SIMDJSON_DEVELOPER_MODE with its value and unsetting it")
set(dev_mode ON)
if(SIMDJSON_JUST_LIBRARY)
set(dev_mode OFF)
endif()
set(SIMDJSON_DEVELOPER_MODE "${dev_mode}" CACHE BOOL "" FORCE)
unset(SIMDJSON_JUST_LIBRARY CACHE)
endif()
-106
View File
@@ -1,106 +0,0 @@
#
# Implementation selection
#
set(SIMDJSON_ALL_IMPLEMENTATIONS fallback westmere haswell arm64 ppc64)
set(
SIMDJSON_IMPLEMENTATION ""
CACHE STRING "\
Semicolon-separated list of implementations to include \
(${SIMDJSON_ALL_IMPLEMENTATIONS}). If this is not set, any implementations \
that are supported at compile time and may be selected at runtime will be \
included."
)
set(
SIMDJSON_EXCLUDE_IMPLEMENTATION ""
CACHE STRING "\
Semicolon-separated list of implementations to exclude \
(haswell/westmere/arm64/ppc64/fallback). By default, excludes any \
implementations that are unsupported at compile time or cannot be selected at \
runtime."
)
foreach(var IN ITEMS IMPLEMENTATION EXCLUDE_IMPLEMENTATION)
set(var "SIMDJSON_${var}")
foreach(impl IN LISTS "${var}")
if(NOT impl IN_LIST SIMDJSON_ALL_IMPLEMENTATIONS)
message(ERROR "\
Implementation ${impl} found in ${var} not supported by simdjson. \
Possible implementations: ${SIMDJSON_ALL_IMPLEMENTATIONS}")
endif()
endforeach()
endforeach()
macro(flag_action action var val)
message(STATUS "${action} implementation ${impl} due to ${var}=${${var}}")
simdjson_add_props(
target_compile_definitions PUBLIC
"SIMDJSON_IMPLEMENTATION_${impl_upper}=${val}"
)
endmacro()
foreach(impl IN LISTS SIMDJSON_ALL_IMPLEMENTATIONS)
string(TOUPPER "${impl}" impl_upper)
if(impl IN_LIST SIMDJSON_EXCLUDE_IMPLEMENTATION)
flag_action(Excluding SIMDJSON_EXCLUDE_IMPLEMENTATION 0)
elseif(impl IN_LIST SIMDJSON_IMPLEMENTATION)
flag_action(Including SIMDJSON_IMPLEMENTATION 1)
elseif(SIMDJSON_IMPLEMENTATION)
flag_action(Excluding SIMDJSON_IMPLEMENTATION 0)
endif()
endforeach()
# TODO make it so this generates the necessary compiler flags to select the
# given impl as the builtin automatically!
set(
SIMDJSON_BUILTIN_IMPLEMENTATION ""
CACHE STRING "\
Select the implementation that will be used for user code. Defaults to the \
most universal implementation in SIMDJSON_IMPLEMENTATION (in the order \
${SIMDJSON_ALL_IMPLEMENTATIONS}) if specified; otherwise, by default the \
compiler will pick the best implementation that can always be selected given \
the compiler flags."
)
if(NOT SIMDJSON_BUILTIN_IMPLEMENTATION STREQUAL "")
simdjson_add_props(
target_compile_definitions PUBLIC
"SIMDJSON_BUILTIN_IMPLEMENTATION=${SIMDJSON_BUILTIN_IMPLEMENTATION}"
)
else()
# Pick the most universal implementation out of the selected implementations
# (if any)
foreach(impl IN LISTS SIMDJSON_ALL_IMPLEMENTATIONS)
if(
impl IN_LIST SIMDJSON_IMPLEMENTATION
AND NOT impl IN_LIST SIMDJSON_EXCLUDE_IMPLEMENTATION
)
message(STATUS "\
Selected implementation ${impl} as builtin implementation based on \
${SIMDJSON_IMPLEMENTATION}")
simdjson_add_props(
target_compile_definitions PUBLIC
"SIMDJSON_BUILTIN_IMPLEMENTATION=${impl}"
)
break()
endif()
endforeach()
endif()
foreach(impl IN LISTS SIMDJSON_ALL_IMPLEMENTATIONS)
string(TOUPPER "${impl}" impl_upper)
option(
"SIMDJSON_IMPLEMENTATION_${impl_upper}"
"Include the ${impl} implementation"
ON
)
mark_as_advanced("SIMDJSON_IMPLEMENTATION_${impl_upper}")
if(NOT "${SIMDJSON_IMPLEMENTATION_${impl_upper}}")
message(DEPRECATION "\
SIMDJSON_IMPLEMENTATION_${impl_upper} is deprecated. \
Use SIMDJSON_IMPLEMENTATION=-${impl} instead")
simdjson_add_props(
target_compile_definitions PUBLIC
"SIMDJSON_IMPLEMENTATION_${impl_upper}=0"
)
endif()
endforeach()
+8 -5
View File
@@ -1,6 +1,9 @@
include(CMakeFindDependencyMacro)
if("@SIMDJSON_ENABLE_THREADS@")
find_dependency(Threads)
endif()
@PACKAGE_INIT@
include("${CMAKE_CURRENT_LIST_DIR}/simdjsonTargets.cmake")
include(CMakeFindDependencyMacro)
if(@SIMDJSON_ENABLE_THREADS@)
set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
find_dependency(Threads)
endif()
# Import targets.
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@-targets.cmake")
+240
View File
@@ -0,0 +1,240 @@
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
message (STATUS "The simdjson repository appears to be standalone.")
option(SIMDJSON_JUST_LIBRARY "Build just the library, omit tests, tools and benchmarks" OFF)
message (STATUS "By default, we attempt to build everything.")
else()
message (STATUS "The simdjson repository appears to be used as a subdirectory.")
option(SIMDJSON_JUST_LIBRARY "Build just the library, omit tests, tools and benchmarks" ON)
message (STATUS "By default, we just build the library.")
endif()
#
# Flags used by exes and by the simdjson library (project-wide flags)
#
add_library(simdjson-flags INTERFACE)
add_library(simdjson-internal-flags INTERFACE)
target_link_libraries(simdjson-internal-flags INTERFACE simdjson-flags)
option(SIMDJSON_SANITIZE "Sanitize addresses" OFF)
if(SIMDJSON_SANITIZE)
target_compile_options(simdjson-flags INTERFACE -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover=all)
target_link_libraries(simdjson-flags INTERFACE -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover=all)
# Ubuntu bug for GCC 5.0+ (safe for all versions)
if (CMAKE_COMPILER_IS_GNUCC)
target_link_libraries(simdjson-flags INTERFACE -fuse-ld=gold)
endif()
endif()
if(SIMDJSON_SANITIZE_THREADS)
target_compile_options(simdjson-flags INTERFACE -fsanitize=thread -fsanitize=undefined -fno-sanitize-recover=all)
target_link_libraries(simdjson-flags INTERFACE -fsanitize=thread -fsanitize=undefined -fno-sanitize-recover=all)
# Ubuntu bug for GCC 5.0+ (safe for all versions)
if (CMAKE_COMPILER_IS_GNUCC)
target_link_libraries(simdjson-flags INTERFACE -fuse-ld=gold)
endif()
endif()
if (NOT CMAKE_BUILD_TYPE)
message(STATUS "No build type selected, default to Release")
set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE)
if(SIMDJSON_SANITIZE)
message(WARNING "No build type selected and you have enabled the sanitizer. Consider setting CMAKE_BUILD_TYPE to Debug to help identify the eventual problems.")
endif()
endif()
if(MSVC)
option(SIMDJSON_BUILD_STATIC "Build a static library" ON) # turning it on disables the production of a dynamic library
else()
option(SIMDJSON_BUILD_STATIC "Build a static library" OFF) # turning it on disables the production of a dynamic library
option(SIMDJSON_USE_LIBCPP "Use the libc++ library" OFF)
endif()
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/tools/cmake")
# We compile tools, tests, etc. with C++ 17. Override yourself if you need on a target.
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_MACOSX_RPATH OFF)
set(CMAKE_THREAD_PREFER_PTHREAD ON)
set(THREADS_PREFER_PTHREAD_FLAG ON)
# LTO seems to create all sorts of fun problems. Let us
# disable temporarily.
#include(CheckIPOSupported)
#check_ipo_supported(RESULT ltoresult)
#if(ltoresult)
# set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
#endif()
option(SIMDJSON_VISUAL_STUDIO_BUILD_WITH_DEBUG_INFO_FOR_PROFILING "Under Visual Studio, add Zi to the compile flag and DEBUG to the link file to add debugging information to the release build for easier profiling inside tools like VTune" OFF)
if(MSVC)
if("${MSVC_TOOLSET_VERSION}" STREQUAL "140")
# Visual Studio 2015 issues warnings and we tolerate it, cmake -G"Visual Studio 14" ..
target_compile_options(simdjson-internal-flags INTERFACE /W0 /sdl)
else()
# Recent version of Visual Studio expected (2017, 2019...). Prior versions are unsupported.
target_compile_options(simdjson-internal-flags INTERFACE /WX /W3 /sdl /w34714) # https://docs.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-4-c4714?view=vs-2019
endif()
if(SIMDJSON_VISUAL_STUDIO_BUILD_WITH_DEBUG_INFO_FOR_PROFILING)
target_link_options(simdjson-flags INTERFACE /DEBUG )
target_compile_options(simdjson-flags INTERFACE /Zi)
endif(SIMDJSON_VISUAL_STUDIO_BUILD_WITH_DEBUG_INFO_FOR_PROFILING)
else(MSVC)
if(NOT WIN32)
target_compile_options(simdjson-internal-flags INTERFACE -fPIC)
endif()
target_compile_options(simdjson-internal-flags INTERFACE -Werror -Wall -Wextra -Weffc++)
target_compile_options(simdjson-internal-flags INTERFACE -Wsign-compare -Wshadow -Wwrite-strings -Wpointer-arith -Winit-self -Wconversion -Wno-sign-conversion)
endif(MSVC)
#
# Optional flags
#
#
# Implementation selection
#
set(SIMDJSON_ALL_IMPLEMENTATIONS "fallback;westmere;haswell;arm64;ppc64")
set(SIMDJSON_IMPLEMENTATION "" CACHE STRING "Semicolon-separated list of implementations to include (${SIMDJSON_ALL_IMPLEMENTATIONS}). If this is not set, any implementations that are supported at compile time and may be selected at runtime will be included.")
foreach(implementation ${SIMDJSON_IMPLEMENTATION})
if(NOT (implementation IN_LIST SIMDJSON_ALL_IMPLEMENTATIONS))
message(ERROR "Implementation ${implementation} not supported by simdjson. Possible implementations: ${SIMDJSON_ALL_IMPLEMENTATIONS}")
endif()
endforeach(implementation)
set(SIMDJSON_EXCLUDE_IMPLEMENTATION "" CACHE STRING "Semicolon-separated list of implementations to exclude (haswell/westmere/arm64/ppc64/fallback). By default, excludes any implementations that are unsupported at compile time or cannot be selected at runtime.")
foreach(implementation ${SIMDJSON_EXCLUDE_IMPLEMENTATION})
if(NOT (implementation IN_LIST SIMDJSON_ALL_IMPLEMENTATIONS))
message(ERROR "Implementation ${implementation} not supported by simdjson. Possible implementations: ${SIMDJSON_ALL_IMPLEMENTATIONS}")
endif()
endforeach(implementation)
foreach(implementation ${SIMDJSON_ALL_IMPLEMENTATIONS})
string(TOUPPER ${implementation} implementation_upper)
if(implementation IN_LIST SIMDJSON_EXCLUDE_IMPLEMENTATION)
message(STATUS "Excluding implementation ${implementation} due to SIMDJSON_EXCLUDE_IMPLEMENTATION=${SIMDJSON_EXCLUDE_IMPLEMENTATION}")
target_compile_definitions(simdjson-flags INTERFACE "SIMDJSON_IMPLEMENTATION_${implementation_upper}=0")
elseif(implementation IN_LIST SIMDJSON_IMPLEMENTATION)
message(STATUS "Including implementation ${implementation} due to SIMDJSON_IMPLEMENTATION=${SIMDJSON_IMPLEMENTATION}")
target_compile_definitions(simdjson-flags INTERFACE "SIMDJSON_IMPLEMENTATION_${implementation_upper}=1")
elseif(SIMDJSON_IMPLEMENTATION)
message(STATUS "Excluding implementation ${implementation} due to SIMDJSON_IMPLEMENTATION=${SIMDJSON_IMPLEMENTATION}")
target_compile_definitions(simdjson-flags INTERFACE "SIMDJSON_IMPLEMENTATION_${implementation_upper}=0")
endif()
endforeach(implementation)
# TODO make it so this generates the necessary compiler flags to select the given implementation as the builtin automatically!
option(SIMDJSON_BUILTIN_IMPLEMENTATION "Select the implementation that will be used for user code. Defaults to the most universal implementation in SIMDJSON_IMPLEMENTATION (in the order ${SIMDJSON_ALL_IMPLEMENTATIONS}) if specified; otherwise, by default the compiler will pick the best implementation that can always be selected given the compiler flags." "")
if(SIMDJSON_BUILTIN_IMPLEMENTATION)
target_compile_definitions(simdjson-flags INTERFACE "SIMDJSON_BUILTIN_IMPLEMENTATION=${SIMDJSON_BUILTIN_IMPLEMENTATION}")
else()
# Pick the most universal implementation out of the selected implementations (if any)
foreach(implementation ${SIMDJSON_ALL_IMPLEMENTATIONS})
if(implementation IN_LIST SIMDJSON_IMPLEMENTATION AND NOT (implementation IN_LIST SIMDJSON_EXCLUDE_IMPLEMENTATION))
message(STATUS "Selected implementation ${implementation} as builtin implementation based on ${SIMDJSON_IMPLEMENTATION}.")
target_compile_definitions(simdjson-flags INTERFACE "SIMDJSON_BUILTIN_IMPLEMENTATION=${implementation}")
break()
endif()
endforeach(implementation)
endif(SIMDJSON_BUILTIN_IMPLEMENTATION)
option(SIMDJSON_IMPLEMENTATION_HASWELL "Include the haswell implementation" ON)
if(NOT SIMDJSON_IMPLEMENTATION_HASWELL)
message(DEPRECATION "SIMDJSON_IMPLEMENTATION_HASWELL is deprecated. Use SIMDJSON_IMPLEMENTATION=-haswell instead.")
target_compile_definitions(simdjson-flags INTERFACE SIMDJSON_IMPLEMENTATION_HASWELL=0)
endif()
option(SIMDJSON_IMPLEMENTATION_WESTMERE "Include the westmere implementation" ON)
if(NOT SIMDJSON_IMPLEMENTATION_WESTMERE)
message(DEPRECATION "SIMDJSON_IMPLEMENTATION_WESTMERE is deprecated. SIMDJSON_IMPLEMENTATION=-westmere instead.")
target_compile_definitions(simdjson-flags INTERFACE SIMDJSON_IMPLEMENTATION_WESTMERE=0)
endif()
option(SIMDJSON_IMPLEMENTATION_ARM64 "Include the arm64 implementation" ON)
if(NOT SIMDJSON_IMPLEMENTATION_ARM64)
message(DEPRECATION "SIMDJSON_IMPLEMENTATION_ARM64 is deprecated. Use SIMDJSON_IMPLEMENTATION=-arm64 instead.")
target_compile_definitions(simdjson-flags INTERFACE SIMDJSON_IMPLEMENTATION_ARM64=0)
endif()
option(SIMDJSON_IMPLEMENTATION_PPC64 "Include the arm64 implementation" ON)
if(NOT SIMDJSON_IMPLEMENTATION_PPC64)
message(DEPRECATION "SIMDJSON_IMPLEMENTATION_PPC64 is deprecated. Use SIMDJSON_IMPLEMENTATION=-ppc64 instead.")
target_compile_definitions(simdjson-flags INTERFACE SIMDJSON_IMPLEMENTATION_PPC64=0)
endif()
option(SIMDJSON_IMPLEMENTATION_FALLBACK "Include the fallback implementation" ON)
if(NOT SIMDJSON_IMPLEMENTATION_FALLBACK)
message(DEPRECATION "SIMDJSON_IMPLEMENTATION_FALLBACK is deprecated. Use SIMDJSON_IMPLEMENTATION=-fallback instead.")
target_compile_definitions(simdjson-flags INTERFACE SIMDJSON_IMPLEMENTATION_FALLBACK=0)
endif()
#
# Other optional flags
#
option(SIMDJSON_ONDEMAND_SAFETY_RAILS "Validate ondemand user code at runtime to ensure it is being used correctly. Defaults to ON for debug builds, OFF for release builds." $<IF:$<CONFIG:DEBUG>,ON,OFF>)
if(SIMDJSON_ONDEMAND_SAFETY_RAILS)
message(STATUS "Ondemand safety rails enabled. Ondemand user code will be checked at runtime. This will be slower than normal!")
target_compile_definitions(simdjson-flags INTERFACE SIMDJSON_ONDEMAND_SAFETY_RAILS)
endif(SIMDJSON_ONDEMAND_SAFETY_RAILS)
option(SIMDJSON_BASH "Allow usage of bash within CMake" ON)
option(SIMDJSON_EXCEPTIONS "Enable simdjson's exception-throwing interface" ON)
if(NOT SIMDJSON_EXCEPTIONS)
message(STATUS "simdjson exception interface turned off. Code that does not check error codes will not compile.")
target_compile_definitions(simdjson-flags INTERFACE SIMDJSON_EXCEPTIONS=0)
if(MSVC)
# CMake currently /EHsc as a default flag in CMAKE_CXX_FLAGS on MSVC. Replacing this with a more general abstraction is a WIP (see https://gitlab.kitware.com/cmake/cmake/-/issues/20610)
# /EHs enables standard C++ stack unwinding when catching exceptions (non-structured exception handling)
# /EHc used in conjection with /EHs indicates that extern "C" functions never throw (terminate-on-throw)
# Here, we disable both with the - argument negation operator
string(REPLACE "/EHsc" "/EHs-c-" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
# Because we cannot change the flag above on an invidual target (yet), the definition below must similarly be added globally
add_definitions(-D_HAS_EXCEPTIONS=0)
endif()
endif()
option(SIMDJSON_ENABLE_THREADS "Link with thread support" ON)
if(SIMDJSON_ENABLE_THREADS)
set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
set(THREADS_PREFER_PTHREAD_FLAG TRUE)
find_package(Threads REQUIRED)
target_link_libraries(simdjson-flags INTERFACE Threads::Threads)
target_compile_definitions(simdjson-flags INTERFACE SIMDJSON_THREADS_ENABLED=1) # This will be set in the code automatically.
endif()
option(SIMDJSON_VERBOSE_LOGGING, "Enable verbose logging for internal simdjson library development." OFF)
if (SIMDJSON_VERBOSE_LOGGING)
target_compile_definitions(simdjson-flags INTERFACE SIMDJSON_VERBOSE_LOGGING=1)
endif()
option(SIMDJSON_DISABLE_DEPRECATED_API "Disables deprecated APIs" Off)
if (SIMDJSON_DISABLE_DEPRECATED_API)
target_compile_definitions(simdjson-flags INTERFACE SIMDJSON_DISABLE_DEPRECATED_API=1)
endif()
if(SIMDJSON_USE_LIBCPP)
target_link_libraries(simdjson-flags INTERFACE -stdlib=libc++ -lc++abi)
# instead of the above line, we could have used
# set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++ -lc++abi")
# The next line is needed empirically.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
# we update CMAKE_SHARED_LINKER_FLAGS, this gets updated later as well
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -lc++abi")
endif(SIMDJSON_USE_LIBCPP)
# prevent shared libraries from depending on Intel provided libraries
if(${CMAKE_C_COMPILER_ID} MATCHES "Intel") # icc / icpc
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -static-intel")
endif()
include (CheckSymbolExists)
CHECK_SYMBOL_EXISTS(fork unistd.h HAVE_POSIX_FORK)
CHECK_SYMBOL_EXISTS(wait sys/wait.h HAVE_POSIX_WAIT)
install(TARGETS simdjson-flags EXPORT simdjson-config)
# I do not think we want to export our internal flags!
# install(TARGETS simdjson-internal-flags EXPORT simdjson-config)
-48
View File
@@ -1,48 +0,0 @@
#
# Accumulate flags
#
set(simdjson_props_script "${PROJECT_BINARY_DIR}/simdjson-props.cmake")
set(simdjson_props_content "")
set(simdjson_props_flushed NO)
function(simdjson_add_props command)
set(args "")
math(EXPR limit "${ARGC} - 1")
foreach(i RANGE 1 "${limit}")
set(value "${ARGV${i}}")
if(value MATCHES "^(PRIVATE|PUBLIC)$")
string(TOLOWER "${value}" value)
set(value "\${${value}}")
else()
set(value "[==[${value}]==]")
endif()
string(APPEND args " ${value}")
endforeach()
set(simdjson_props_flushed NO PARENT_SCOPE)
set(
simdjson_props_content
"${simdjson_props_content}${command}(\"\${target}\"${args})\n"
PARENT_SCOPE
)
endfunction()
macro(simdjson_flush_props)
if(NOT simdjson_props_flushed)
set(simdjson_props_flushed YES PARENT_SCOPE)
file(WRITE "${simdjson_props_script}" "${simdjson_props_content}")
endif()
endmacro()
function(simdjson_apply_props target)
set(private PRIVATE)
set(public PUBLIC)
get_target_property(TYPE "${target}" TYPE)
if(TYPE STREQUAL "INTERFACE_LIBRARY")
set(private INTERFACE)
set(public INTERFACE)
endif()
simdjson_flush_props()
include("${simdjson_props_script}")
endfunction()
+22 -17
View File
@@ -1,19 +1,24 @@
#
# ${USER_CMAKECACHE} contains the *user-specified* simdjson options so you can
# call cmake on another branch or repository with the same options.
# ${SIMDJSON_USER_CMAKECACHE} contains the *user-specified* simdjson options so you can call cmake on
# another branch or repository with the same options.
#
file(READ "${BINARY_DIR}/CMakeCache.txt" cache)
# Escape semicolons, so the lines can be safely iterated in CMake
string(REPLACE ";" "\\;" cache "${cache}")
# Turn the contents into a list
string(REPLACE "\n" ";" cache "${cache}")
message(STATUS "${USER_CMAKECACHE}")
file(REMOVE "${USER_CMAKECACHE}")
foreach(line IN LISTS cache)
if(line MATCHES "^SIMDJSON_" AND NOT line MATCHES "^SIMDJSON_LIB_")
file(APPEND "${USER_CMAKECACHE}" "${line}\n")
endif()
endforeach()
# Not supported on Windows at present, because the only thing that uses it is checkperf, which we
# don't run on Windows.
#
set(SIMDJSON_USER_CMAKECACHE ${CMAKE_CURRENT_BINARY_DIR}/.simdjson-user-CMakeCache.txt)
if (MSVC)
add_custom_command(
OUTPUT ${SIMDJSON_USER_CMAKECACHE}
COMMAND findstr SIMDJSON_ ${PROJECT_BINARY_DIR}/CMakeCache.txt > ${SIMDJSON_USER_CMAKECACHE}.tmp
COMMAND findstr /v SIMDJSON_LIB_ ${SIMDJSON_USER_CMAKECACHE}.tmp > ${SIMDJSON_USER_CMAKECACHE}
VERBATIM # Makes it not do weird escaping with the command
)
else()
add_custom_command(
OUTPUT ${SIMDJSON_USER_CMAKECACHE}
COMMAND grep SIMDJSON_ ${PROJECT_BINARY_DIR}/CMakeCache.txt > ${SIMDJSON_USER_CMAKECACHE}.tmp
COMMAND grep -v SIMDJSON_LIB_ ${SIMDJSON_USER_CMAKECACHE}.tmp > ${SIMDJSON_USER_CMAKECACHE}
VERBATIM # Makes it not do weird escaping with the command
)
endif()
add_custom_target(simdjson-user-cmakecache DEPENDS ${SIMDJSON_USER_CMAKECACHE})
-4
View File
@@ -18,10 +18,6 @@ if(SIMDJSON_GOOGLE_BENCHMARKS)
add_dependency(google_benchmarks)
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)
# This prevents variables declared with set() from unnecessarily escaping and
# should not be called more than once
function(competition_scope_)
+244 -566
View File
File diff suppressed because it is too large Load Diff
+697 -3
View File
@@ -1,3 +1,697 @@
* Basics: https://github.com/simdjson/simdjson/blob/master/doc/basics.md is an overview of how to use simdjson and its APIs.
* parse_many: https://github.com/simdjson/simdjson/blob/master/doc/parse_many.md describes an interface providing features to work with files or streams containing multiple small JSON documents. As fast and convenient as possible.
* Performance: https://github.com/simdjson/simdjson/blob/master/doc/performance.md shows some more advanced scenarios and how to tune for them.
The Basics
==========
An overview of what you need to know to use simdjson, with examples.
Requirements
------------------
- A recent compiler (LLVM clang6 or better, GNU GCC 7 or better) on a 64-bit (PPC, ARM or x64 Intel/AMD) POSIX systems such as macOS, freeBSD or Linux. We require that the compiler supports the C++11 standard or better.
- Visual Studio 2017 or better under 64-bit Windows. Users should target a 64-bit build (x64) instead of a 32-bit build (x86). We support the LLVM clang compiler under Visual Studio (clangcl) as well as as the regular Visual Studio compiler.
Including simdjson
------------------
To include simdjson, copy the simdjson.h and simdjson.cpp files from the singleheader directory
into your project. Then include the header file in your project with:
```
#include "simdjson.h"
using namespace simdjson; // optional
```
You can compile with:
```
c++ myproject.cpp simdjson.cpp
```
Note:
- Users on macOS and other platforms were default compilers do not provide C++11 compliant by default should request it with the appropriate flag (e.g., `c++ -std=c++17 myproject.cpp simdjson.cpp`).
- Visual Studio users should compile with the `_CRT_SECURE_NO_WARNINGS` flag to avoid warnings with respect to our use of standard C functions such as `fopen`.
Using simdjson with package managers
------------------
You can install the simdjson library on your system or in your project using multiple package managers such as MSYS2, the conan package manager, vcpkg, brew, the apt package manager (debian-based Linux systems), the FreeBSD package manager (FreeBSD), and so on. [Visit our wiki for more details](https://github.com/simdjson/simdjson/wiki/Installing-simdjson-with-a-package-manager).
Using simdjson as a CMake dependency
------------------
You can include the simdjson as a CMake dependency by including the following lines in your `CMakeLists.txt`:
```
include(FetchContent)
FetchContent_Declare(
simdjson
GIT_REPOSITORY https://github.com/simdjson/simdjson.git
GIT_TAG v0.6.1
GIT_SHALLOW TRUE)
set(SIMDJSON_JUST_LIBRARY ON CACHE INTERNAL "")
set(SIMDJSON_BUILD_STATIC ON CACHE INTERNAL "")
FetchContent_MakeAvailable(simdjson)
```
You should replace `GIT_TAG v0.6.1` by the version you need. If you omit `GIT_TAG v0.6.1`, you will work from the main branch of simdjson: we recommend that if you are working on production code,
Elsewhere in your project, you can declare dependencies on simdjson with lines such as these:
```
add_executable(myprogram myprogram.cpp)
target_link_libraries(myprogram simdjson)
```
We recommend CMake version 3.15 or better.
See [our CMake demonstration](https://github.com/simdjson/cmake_demo_single_file). It works under Linux, FreeBSD, macOS and Windows (including Visual Studio).
The CMake build in simdjson can be taylored with a few variables. You can see the available variables and their default values by entering the `cmake -LA` command.
The Basics: Loading and Parsing JSON Documents
----------------------------------------------
The simdjson library offers a simple DOM tree API, which you can access by creating a
`dom::parser` and calling the `load()` method:
```
dom::parser parser;
dom::element doc = parser.load(filename); // load and parse a file
```
Or by creating a padded string (for efficiency reasons, simdjson requires a string with
SIMDJSON_PADDING bytes at the end) and calling `parse()`:
```
dom::parser parser;
dom::element doc = parser.parse("[1,2,3]"_padded); // parse a string, the _padded suffix creates a simdjson::padded_string instance
```
The parsed document resulting from the `parser.load` and `parser.parse` calls depends on the `parser` instance. Thus the `parser` instance must remain in scope. Furthermore, you must have at most one parsed document in play per `parser` instance.
You cannot copy a `parser` instance, you may only move it.
If you need to keep a document around long term, you can keep or move the parser instance. Note that moving a parser instance, or keeping one in a movable data structure like vector or map, can cause any outstanding `element`, `object` or `array` instances to be invalidated. If you need to store a parser in a movable data structure, you should use a `std::unique_ptr` to avoid this invalidation(e.g., `std::unique_ptr<dom::parser> parser(new dom::parser{})`).
During the`load` or `parse` calls, neither the input file nor the input string are ever modified. After calling `load` or `parse`, the source (either a file or a string) can be safely discarded. All of the JSON data is stored in the `parser` instance. The parsed document is also immutable in simdjson: you do not modify it by accessing it.
For best performance, a `parser` instance should be reused over several files: otherwise you will needlessly reallocate memory, an expensive process. It is also possible to avoid entirely memory allocations during parsing when using simdjson.
If you need a lower-level interface, you may call the function `parser.parse(const char * p, size_t l)` on a pointer `p` while specifying the
length of your input `l` in bytes. To see how to get the very best performance from a low-level approach, you way want to read our [performance notes](https://github.com/simdjson/simdjson/blob/master/doc/performance.md#padding-and-temporary-copies) on this topic (see the Padding and Temporary Copies section).
Using the Parsed JSON
---------------------
Once you have an element, you can navigate it with idiomatic C++ iterators, operators and casts.
* **Extracting Values (with exceptions):** You can cast a JSON element to a native type: `double(element)` or
`double x = json_element`. This works for double, uint64_t, int64_t, bool,
dom::object and dom::array. An exception is thrown if the cast is not possible.
* **Extracting Values (without exceptions):** You can use a variant usage of `get()` with error codes to avoid exceptions. You first declare the variable of the appropriate type (`double`, `uint64_t`, `int64_t`, `bool`,
`dom::object` and `dom::array`) and pass it by reference to `get()` which gives you back an error code: e.g.,
```
simdjson::error_code error;
simdjson::padded_string numberstring = "1.2"_padded; // our JSON input ("1.2")
simdjson::dom::parser parser;
double value; // variable where we store the value to be parsed
error = parser.parse(numberstring).get(value);
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
std::cout << "I parsed " << value << " from " << numberstring.data() << std::endl;
```
* **Field Access:** To get the value of the "foo" field in an object, use `object["foo"]`.
* **Array Iteration:** To iterate through an array, use `for (auto value : array) { ... }`. If you
know the type of the value, you can cast it right there, too! `for (double value : array) { ... }`
* **Object Iteration:** You can iterate through an object's fields, too: `for (auto [key, value] : object)`
* **Array Index:** To get at an array value by index, use the at() method: `array.at(0)` gets the
first element.
> Note that array[0] does not compile, because implementing [] gives the impression indexing is a
> O(1) operation, which it is not presently in simdjson. Instead, you should iterate over the elements
> using a for-loop, as in our examples.
* **Array and Object size** Given an array or an object, you can get its size (number of elements or keys)
with the `size()` method.
* **Checking an Element Type:** You can check an element's type with `element.type()`. It
returns an `element_type` with values such as `simdjson::dom::element_type::ARRAY`, `simdjson::dom::element_type::OBJECT`, `simdjson::dom::element_type::INT64`, `simdjson::dom::element_type::UINT64`,`simdjson::dom::element_type::DOUBLE`, `simdjson::dom::element_type::BOOL` or, `simdjson::dom::element_type::NULL_VALUE`.
* **Output to Streams and Strings:** Given a document or an element (or node) out of a JSON document, you can output a minified string version using the C++ stream idiom (`out << element`). You can also request the construction of a minified string version (`simdjson::minify(element)`).
The following code illustrates all of the above:
```
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;
// Iterating through an array of objects
for (dom::object car : parser.parse(cars_json)) {
// Accessing a field by name
cout << "Make/Model: " << car["make"] << "/" << car["model"] << endl;
// Casting a JSON element to an integer
uint64_t year = car["year"];
cout << "- This car is " << 2020 - year << "years old." << endl;
// Iterating through an array of floats
double total_tire_pressure = 0;
for (double tire_pressure : car["tire_pressure"]) {
total_tire_pressure += tire_pressure;
}
cout << "- Average tire pressure: " << (total_tire_pressure / 4) << endl;
// Writing out all the information about the car
for (auto field : car) {
cout << "- " << field.key << ": " << field.value << endl;
}
}
```
Here is a different example illustrating the same ideas:
```
auto abstract_json = R"( [
{ "12345" : {"a":12.34, "b":56.78, "c": 9998877} },
{ "12545" : {"a":11.44, "b":12.78, "c": 11111111} }
] )"_padded;
dom::parser parser;
// Parse and iterate through an array of objects
for (dom::object obj : parser.parse(abstract_json)) {
for(const auto& key_value : obj) {
cout << "key: " << key_value.key << " : ";
dom::object innerobj = key_value.value;
cout << "a: " << double(innerobj["a"]) << ", ";
cout << "b: " << double(innerobj["b"]) << ", ";
cout << "c: " << int64_t(innerobj["c"]) << endl;
}
}
```
And another one:
```
auto abstract_json = R"(
{ "str" : { "123" : {"abc" : 3.14 } } } )"_padded;
dom::parser parser;
double v = parser.parse(abstract_json)["str"]["123"]["abc"];
cout << "number: " << v << endl;
```
C++11 Support and string_view
-------------
The simdjson library builds on compilers supporting the [C++11 standard](https://en.wikipedia.org/wiki/C%2B%2B11). It is also a strict requirement: we have no plan to support older C++ compilers.
We represent parsed strings in simdjson using the `std::string_view` class. It avoids
the need to copy the data, as would be necessary with the `std::string` class. It also
avoids the pitfalls of null-terminated C strings.
The `std::string_view` class has become standard as part of C++17 but it is not always available
on compilers which only supports C++11. When we detect that `string_view` is natively
available, we define the macro `SIMDJSON_HAS_STRING_VIEW`.
When we detect that it is unavailable,
we use [string-view-lite](https://github.com/martinmoene/string-view-lite) as a
substitute. In such cases, we use the type alias `using string_view = nonstd::string_view;` to
offer the same API, irrespective of the compiler and standard library. The macro
`SIMDJSON_HAS_STRING_VIEW` will be *undefined* to indicate that we emulate `string_view`.
C++17 Support
-------------
While the simdjson library can be used in any project using C++ 11 and above, field iteration has special support C++ 17's destructuring syntax. For example:
```
padded_string json = R"( { "foo": 1, "bar": 2 } )"_padded;
dom::parser parser;
dom::object object;
auto error = parser.parse(json).get(object);
if (error) { cerr << error << endl; return; }
for (auto [key, value] : object) {
cout << key << " = " << value << endl;
}
```
For comparison, here is the C++ 11 version of the same code:
```
// C++ 11 version for comparison
padded_string json = R"( { "foo": 1, "bar": 2 } )"_padded;
dom::parser parser;
dom::object object;
auto error = parser.parse(json).get(object);
if (!error) { cerr << error << endl; return; }
for (dom::key_value_pair field : object) {
cout << field.key << " = " << field.value << endl;
}
```
Minifying JSON strings without parsing
----------------------
In some cases, you may have valid JSON strings that you do not wish to parse but that you wish to minify. That is, you wish to remove all unnecessary spaces. We have a fast function for this purpose (`simdjson::minify(const char * input, size_t length, const char * output, size_t& new_length)`). This function does not validate your content, and it does not parse it. It is much faster than parsing the string and re-serializing it in minified form (`simdjson::minify(parser.parse())`). Usage is relatively simple. You must pass an input pointer with a length parameter, as well as an output pointer and an output length parameter (by reference). The output length parameter is not read, but written to. The output pointer should point to a valid memory region that is as large as the original string length. The input pointer and input length are read, but not written to.
```
// Starts with a valid JSON document as a string.
// It does not have to be null-terminated.
const char * some_string = "[ 1, 2, 3, 4] ";
size_t length = std::strlen(some_string);
// Create a buffer to receive the minified string. Make sure that there is enough room (length bytes).
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);
// The buffer variable now has "[1,2,3,4]" and new_length has value 9.
```
Though it does not validate the JSON input, it will detect when the document ends with an unterminated string. E.g., it would refuse to minify the string `"this string is not terminated` because of the missing final quote.
UTF-8 validation (alone)
----------------------
The simdjson library has fast functions to validate UTF-8 strings. They are many times faster than most functions commonly found in libraries. You can use our fast functions, even if you do not care about JSON.
```
const char * some_string = "[ 1, 2, 3, 4] ";
size_t length = std::strlen(some_string);
bool is_ok = simdjson::validate_utf8(some_string, length);
```
The UTF-8 validation function merely checks that the input is valid UTF-8: it works with strings in general, not just JSON strings.
Your input string does not need any padding. Any string will do. The `validate_utf8` function does not do any memory allocation on the heap, and it does not throw exceptions.
JSON Pointer
------------
The simdjson library also supports [JSON pointer](https://tools.ietf.org/html/rfc6901) through the
`at_pointer()` method, letting you reach further down into the document in a single call:
```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 cars = parser.parse(cars_json);
cout << cars.at_pointer("/0/tire_pressure/1") << endl; // Prints 39.9
```
A JSON Path is a sequence of segments each starting with the '/' character. Within arrays, an integer
index allows you to select the indexed node. Within objects, the string value of the key allows you to
select the value. If your keys contain the characters '/' or '~', they must be escaped as '~1' and
'~0' respectively. An empty JSON Path refers to the whole document.
We also extend the JSON Pointer support to include *relative* paths.
You can apply a JSON path to any node and the path gets interpreted relatively, as if the currrent node were a whole JSON document.
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 cars = parser.parse(cars_json);
cout << cars.at_pointer("/0/tire_pressure/1") << endl; // Prints 39.9
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; }
double x = car.at_pointer("/tire_pressure/1");
cout << x << endl; // Prints 39.9, 31 and 30
}
```
Error Handling
--------------
All simdjson APIs that can fail return `simdjson_result<T>`, which is a &lt;value, error_code&gt;
pair. You can retrieve the value with .get(), like so:
```
dom::element doc;
auto error = parser.parse(json).get(doc);
if (error) { cerr << error << endl; exit(1); }
```
When you use the code this way, it is your responsibility to check for error before using the
result: if there is an error, the result value will not be valid and using it will caused undefined
behavior.
We can write a "quick start" example where we attempt to parse the following JSON file and access some data, without triggering exceptions:
```JavaScript
{
"statuses": [
{
"id": 505874924095815700
},
{
"id": 505874922023837700
}
],
"search_metadata": {
"count": 100
}
}
```
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 "simdjson.h"
int main(void) {
simdjson::dom::parser parser;
simdjson::dom::element tweets;
auto error = parser.load("twitter.json").get(tweets);
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
simdjson::dom::element res;
if ((error = tweets["search_metadata"]["count"].get(res))) {
std::cerr << "could not access keys" << std::endl;
return EXIT_FAILURE;
}
std::cout << res << " results." << std::endl;
}
```
The following is a similar example where one wants to get the id of the first tweet without
triggering exceptions. To do this, we use `["statuses"].at(0)["id"]`. We break that expression down:
- Get the list of tweets (the `"statuses"` key of the document) using `["statuses"]`). The result is expected to be an array.
- Get the first tweet using `.at(0)`. The result is expected to be an object.
- Get the id of the tweet using ["id"]. We expect the value to be a non-negative integer.
Observe how we use the `at` method when querying an index into an array, and not the bracket operator.
```
#include "simdjson.h"
int main(void) {
simdjson::dom::parser parser;
simdjson::dom::element tweets;
auto error = parser.load("twitter.json").get(tweets);
if(error) { std::cerr << 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; }
std::cout << identifier << std::endl;
return EXIT_SUCCESS;
}
```
### Error Handling Example
This is how the example in "Using the Parsed JSON" could be written using only error code checking:
```
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::array cars;
auto error = parser.parse(cars_json).get(cars);
if (error) { cerr << error << endl; exit(1); }
// Iterating through an array of objects
for (dom::element car_element : cars) {
dom::object car;
if ((error = car_element.get(car))) { cerr << error << endl; exit(1); }
// Accessing a field by name
std::string_view make, model;
if ((error = car["make"].get(make))) { cerr << error << endl; exit(1); }
if ((error = car["model"].get(model))) { cerr << error << endl; exit(1); }
cout << "Make/Model: " << make << "/" << model << endl;
// Casting a JSON element to an integer
uint64_t year;
if ((error = car["year"].get(year))) { cerr << error << endl; exit(1); }
cout << "- This car is " << 2020 - year << "years old." << endl;
// Iterating through an array of floats
double total_tire_pressure = 0;
dom::array tire_pressure_array;
if ((error = car["tire_pressure"].get(tire_pressure_array))) { cerr << error << endl; exit(1); }
for (dom::element tire_pressure_element : tire_pressure_array) {
double tire_pressure;
if ((error = tire_pressure_element.get(tire_pressure))) { cerr << error << endl; exit(1); }
total_tire_pressure += tire_pressure;
}
cout << "- Average tire pressure: " << (total_tire_pressure / 4) << endl;
// Writing out all the information about the car
for (auto field : car) {
cout << "- " << field.key << ": " << field.value << endl;
}
}
```
Here is another example:
```
auto abstract_json = R"( [
{ "12345" : {"a":12.34, "b":56.78, "c": 9998877} },
{ "12545" : {"a":11.44, "b":12.78, "c": 11111111} }
] )"_padded;
dom::parser parser;
dom::array array;
auto error = parser.parse(abstract_json).get(array);
if (error) { cerr << error << endl; exit(1); }
// Iterate through an array of objects
for (dom::element elem : array) {
dom::object obj;
if ((error = elem.get(obj))) { cerr << error << endl; exit(1); }
for (auto & key_value : obj) {
cout << "key: " << key_value.key << " : ";
dom::object innerobj;
if ((error = key_value.value.get(innerobj))) { cerr << error << endl; exit(1); }
double va, vb;
if ((error = innerobj["a"].get(va))) { cerr << error << endl; exit(1); }
cout << "a: " << va << ", ";
if ((error = innerobj["b"].get(vc))) { cerr << error << endl; exit(1); }
cout << "b: " << vb << ", ";
int64_t vc;
if ((error = innerobj["c"].get(vc))) { cerr << error << endl; exit(1); }
cout << "c: " << vc << endl;
}
}
```
And another one:
```
auto abstract_json = R"(
{ "str" : { "123" : {"abc" : 3.14 } } } )"_padded;
dom::parser parser;
double v;
auto error = parser.parse(abstract_json)["str"]["123"]["abc"].get(v);
if (error) { cerr << error << endl; exit(1); }
cout << "number: " << v << endl;
```
Notice how we can string several operations (`parser.parse(abstract_json)["str"]["123"]["abc"].get(v)`) and only check for the error once, a strategy we call *error chaining*.
The next two functions will take as input a JSON document containing an array with a single element, either a string or a number. They return true upon success.
```
simdjson::dom::parser parser{};
bool parse_double(const char *j, double &d) {
auto error = parser.parse(j, std::strlen(j))
.at(0)
.get(d, error);
if (error) { return false; }
return true;
}
bool parse_string(const char *j, std::string &s) {
std::string_view answer;
auto error = parser.parse(j,strlen(j))
.at(0)
.get(answer, error);
if (error) { return false; }
s.assign(answer.data(), answer.size());
return true;
}
```
### Exceptions
Users more comfortable with an exception flow may choose to directly cast the `simdjson_result<T>` to the desired type:
```
dom::element doc = parser.parse(json); // Throws an exception if there was an error!
```
When used this way, a `simdjson_error` exception will be thrown if an error occurs, preventing the
program from continuing if there was an error.
If one is willing to trigger exceptions, it is possible to write simpler code:
```
#include "simdjson.h"
int main(void) {
simdjson::dom::parser parser;
simdjson::dom::element tweets = parser.load("twitter.json");
std::cout << "ID: " << tweets["statuses"].at(0)["id"] << std::endl;
return EXIT_SUCCESS;
}
```
Tree Walking and JSON Element Types
-----------------------------------
Sometimes you don't necessarily have a document with a known type, and are trying to generically
inspect or walk over JSON elements. To do that, you can use iterators and the type() method. For
example, here's a quick and dirty recursive function that verbosely prints the JSON document as JSON
(* ignoring nuances like trailing commas and escaping strings, for brevity's sake):
```
void print_json(dom::element element) {
switch (element.type()) {
case dom::element_type::ARRAY:
cout << "[";
for (dom::element child : dom::array(element)) {
print_json(child);
cout << ",";
}
cout << "]";
break;
case dom::element_type::OBJECT:
cout << "{";
for (dom::key_value_pair field : dom::object(element)) {
cout << "\"" << field.key << "\": ";
print_json(field.value);
}
cout << "}";
break;
case dom::element_type::INT64:
cout << int64_t(element) << endl;
break;
case dom::element_type::UINT64:
cout << uint64_t(element) << endl;
break;
case dom::element_type::DOUBLE:
cout << double(element) << endl;
break;
case dom::element_type::STRING:
cout << std::string_view(element) << endl;
break;
case dom::element_type::BOOL:
cout << bool(element) << endl;
break;
case dom::element_type::NULL_VALUE:
cout << "null" << endl;
break;
}
}
void basics_treewalk_1() {
dom::parser parser;
print_json(parser.load("twitter.json"));
}
```
Newline-Delimited JSON (ndjson) and JSON lines
----------------------------------------------
The simdjson library also support multithreaded JSON streaming through a large file containing many
smaller JSON documents in either [ndjson](http://ndjson.org) or [JSON lines](http://jsonlines.org)
format. If your JSON documents all contain arrays or objects, we even support direct file
concatenation without whitespace. The concatenated file has no size restrictions (including larger
than 4GB), though each individual document must be no larger than 4 GB.
Here is a simple example, given "x.json" with this content:
```
{ "foo": 1 }
{ "foo": 2 }
{ "foo": 3 }
```
```
dom::parser parser;
dom::document_stream docs = parser.load_many("x.json");
for (dom::element doc : docs) {
cout << doc["foo"] << endl;
}
// Prints 1 2 3
```
In-memory ndjson strings can be parsed as well, with `parser.parse_many(string)`:
```
dom::parser parser;
auto json = R"({ "foo": 1 }
{ "foo": 2 }
{ "foo": 3 })"_padded;
dom::document_stream docs = parser.parse_many(json);
for (dom::element doc : docs) {
cout << doc["foo"] << endl;
}
// Prints 1 2 3
```
Unlike `parser.parse`, both `parser.load_many(filename)` and `parser.parse_many(string)` may parse
"on demand" (lazily). That is, no parsing may have been done before you enter the loop
`for (dom::element doc : docs) {` and you should expect the parser to only ever fully parse one JSON
document at a time.
1. When calling `parser.load_many(filename)`, the file's content is loaded up in a memory buffer owned by the `parser`'s instance. Thus the file can be safely deleted after calling `parser.load_many(filename)` as the parser instance owns all of the data.
2. When calling `parser.parse_many(string)`, no copy is made of the provided string input. The provided memory buffer may be accessed each time a JSON document is parsed. Calling `parser.parse_many(string)` on a temporary string buffer (e.g., `docs = parser.parse_many("[1,2,3]"_padded)`) is unsafe (and will not compile) because the `document_stream` instance needs access to the buffer to return the JSON documents. In constrast, calling `doc = parser.parse("[1,2,3]"_padded)` is safe because `parser.parse` eagerly parses the input.
Both `load_many` and `parse_many` take an optional parameter `size_t batch_size` which defines the window processing size. It is set by default to a large value (`1000000` corresponding to 1 MB). None of your JSON documents should exceed this window size, or else you will get the error `simdjson::CAPACITY`. You cannot set this window size larger than 4 GB: you will get the error `simdjson::CAPACITY`. The smaller the window size is, the less memory the function will use. Setting the window size too small (e.g., less than 100 kB) may also impact performance negatively. Leaving it to 1 MB is expected to be a good choice, unless you have some larger documents.
Thread Safety
-------------
We built simdjson with thread safety in mind.
The simdjson library is single-threaded except for `parse_many` which may use secondary threads under its control when the library is compiled with thread support.
We recommend using one `dom::parser` object per thread in which case the library is thread-safe.
It is unsafe to reuse a `dom::parser` object between different threads.
The parsed results (`dom::document`, `dom::element`, `array`, `object`) depend on the `dom::parser`, etc. therefore it is also potentially unsafe to use the result of the parsing between different threads.
The CPU detection, which runs the first time parsing is attempted and switches to the fastest
parser for your CPU, is transparent and thread-safe.
Backwards Compatibility
-----------------------
The only header file supported by simdjson is `simdjson.h`. Older versions of simdjson published a
number of other include files such as `document.h` or `ParsedJson.h` alongside `simdjson.h`; these headers
may be moved or removed in future versions.
-610
View File
@@ -1,610 +0,0 @@
The Document-Object-Model (DOM) front-end
==========
An overview of what you need to know to use simdjson, with examples.
* [DOM vs On Demand](#dom-vs-ondemand)
* [The Basics: Loading and Parsing JSON Documents](#the-basics-loading-and-parsing-json-documents)
* [Using the Parsed JSON](#using-the-parsed-json)
* [C++17 Support](#c17-support)
* [JSON Pointer](#json-pointer)
* [Error Handling](#error-handling)
* [Error Handling Example](#error-handling-example)
* [Exceptions](#exceptions)
* [Tree Walking and JSON Element Types](#tree-walking-and-json-element-types)
* [Reusing the parser for maximum efficiency](#reusing-the-parser-for-maximum-efficiency)
* [Server Loops: Long-Running Processes and Memory Capacity](#server-loops-long-running-processes-and-memory-capacity)
* [Best Use of the DOM API](#best-use-of-the-dom-api)
DOM vs On Demand
----------------------------------------------
The simdjson library offers two distinct approaches on how to access a JSON document. We support
a conventional Document-Object-Model (DOM) front-end. In such a scenario, the JSON document is
entirely parsed, validated and materialized in memory as the first step. The programmer may
then access the parsed data using this in-memory model.
The Basics: Loading and Parsing JSON Documents using the DOM front-end
----------------------------------------------
The simdjson library offers a simple DOM tree API, which you can access by creating a
`dom::parser` and calling the `load()` method:
```c++
dom::parser parser;
dom::element doc = parser.load(filename); // load and parse a file
```
Or by creating a string and calling `parse()`:
```c++
dom::parser parser;
dom::element doc = parser.parse(std::string("[1,2,3]")); // parse a string
```
The parsed document resulting from the `parser.load` and `parser.parse` calls depends on the `parser` instance. Thus the `parser` instance must remain in scope. Furthermore, you must have at most one parsed document in play per `parser` instance.
You cannot copy a `parser` instance, you may only move it.
If you need to keep a document around long term, you can keep or move the parser instance. Note that moving a parser instance, or keeping one in a movable data structure like vector or map, can cause any outstanding `element`, `object` or `array` instances to be invalidated. If you need to store a parser in a movable data structure, you should use a `std::unique_ptr` to avoid this invalidation(e.g., `std::unique_ptr<dom::parser> parser(new dom::parser{})`).
During the`load` or `parse` calls, neither the input file nor the input string are ever modified. After calling `load` or `parse`, the source (either a file or a string) can be safely discarded. All of the JSON data is stored in the `parser` instance. The parsed document is also immutable in simdjson: you do not modify it by accessing it.
For best performance, a `parser` instance should be reused over several files: otherwise you will needlessly reallocate memory, an expensive process. It is also possible to avoid entirely memory allocations during parsing when using simdjson. [See our performance notes for details](performance.md).
If you need a lower-level interface, you may call the function `parser.parse(const char * p, size_t l)` on a pointer `p` while specifying the
length of your input `l` in bytes.
Using the Parsed JSON
---------------------
Once you have an element, you can navigate it with idiomatic C++ iterators, operators and casts.
* **Extracting Values (with exceptions):** You can cast a JSON element to a native type: `double(element)` or
`double x = json_element`. This works for double, uint64_t, int64_t, bool,
dom::object and dom::array. An exception is thrown if the cast is not possible.
* **Extracting Values (without exceptions):** You can use a variant usage of `get()` with error codes to avoid exceptions. You first declare the variable of the appropriate type (`double`, `uint64_t`, `int64_t`, `bool`,
`dom::object` and `dom::array`) and pass it by reference to `get()` which gives you back an error code: e.g.,
```c++
simdjson::error_code error;
std::string numberstring = "1.2"; // our JSON input ("1.2")
simdjson::dom::parser parser;
double value; // variable where we store the value to be parsed
error = parser.parse(numberstring).get(value);
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
std::cout << "I parsed " << value << " from " << numberstring.data() << std::endl;
```
* **Field Access:** To get the value of the "foo" field in an object, use `object["foo"]`.
* **Array Iteration:** To iterate through an array, use `for (auto value : array) { ... }`. If you
know the type of the value, you can cast it right there, too! `for (double value : array) { ... }`
* **Object Iteration:** You can iterate through an object's fields, too: `for (auto [key, value] : object)`
* **Array Index:** To get at an array value by index, use the at() method: `array.at(0)` gets the
first element.
> Note that array[0] does not compile, because implementing [] gives the impression indexing is a
> O(1) operation, which it is not presently in simdjson. Instead, you should iterate over the elements
> using a for-loop, as in our examples.
* **Array and Object size** Given an array or an object, you can get its size (number of elements or keys)
with the `size()` method.
* **Checking an Element Type:** You can check an element's type with `element.type()`. It
returns an `element_type` with values such as `simdjson::dom::element_type::ARRAY`, `simdjson::dom::element_type::OBJECT`, `simdjson::dom::element_type::INT64`, `simdjson::dom::element_type::UINT64`,`simdjson::dom::element_type::DOUBLE`, `simdjson::dom::element_type::BOOL` or, `simdjson::dom::element_type::NULL_VALUE`.
* **Output to streams and strings:** Given a document or an element (or node) out of a JSON document, you can output a minified string version using the C++ stream idiom (`out << element`). You can also request the construction of a minified string version (`simdjson::minify(element)`). Numbers are serialized as 64-bit floating-point numbers (`double`).
### Examples
The following code illustrates all of the above:
```c++
std::string 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 ] }
] )";
dom::parser parser;
// Iterating through an array of objects
for (dom::object car : parser.parse(cars_json)) {
// Accessing a field by name
cout << "Make/Model: " << car["make"] << "/" << car["model"] << endl;
// Casting a JSON element to an integer
uint64_t year = car["year"];
cout << "- This car is " << 2020 - year << "years old." << endl;
// Iterating through an array of floats
double total_tire_pressure = 0;
for (double tire_pressure : car["tire_pressure"]) {
total_tire_pressure += tire_pressure;
}
cout << "- Average tire pressure: " << (total_tire_pressure / 4) << endl;
// Writing out all the information about the car
for (auto field : car) {
cout << "- " << field.key << ": " << field.value << endl;
}
}
```
Here is a different example illustrating the same ideas:
```C++
std::string abstract_json = R"( [
{ "12345" : {"a":12.34, "b":56.78, "c": 9998877} },
{ "12545" : {"a":11.44, "b":12.78, "c": 11111111} }
] )";
dom::parser parser;
// Parse and iterate through an array of objects
for (dom::object obj : parser.parse(abstract_json)) {
for(const auto key_value : obj) {
cout << "key: " << key_value.key << " : ";
dom::object innerobj = key_value.value;
cout << "a: " << double(innerobj["a"]) << ", ";
cout << "b: " << double(innerobj["b"]) << ", ";
cout << "c: " << int64_t(innerobj["c"]) << endl;
}
}
```
And another one:
```C++
std::string abstract_json = R"(
{ "str" : { "123" : {"abc" : 3.14 } } } )";
dom::parser parser;
double v = parser.parse(abstract_json)["str"]["123"]["abc"];
cout << "number: " << v << endl;
```
C++17 Support
-------------
While the simdjson library can be used in any project using C++ 11 and above, field iteration has special support C++ 17's destructuring syntax. For example:
```c++
std::string json = R"( { "foo": 1, "bar": 2 } )";
dom::parser parser;
dom::object object;
auto error = parser.parse(json).get(object);
if (error) { cerr << error << endl; return; }
for (auto [key, value] : object) {
cout << key << " = " << value << endl;
}
```
For comparison, here is the C++ 11 version of the same code:
```c++
// C++ 11 version for comparison
std::string json = R"( { "foo": 1, "bar": 2 } )";
dom::parser parser;
dom::object object;
auto error = parser.parse(json).get(object);
if (error) { cerr << error << endl; return; }
for (dom::key_value_pair field : object) {
cout << field.key << " = " << field.value << endl;
}
```
JSON Pointer
------------
The simdjson library also supports [JSON pointer](https://tools.ietf.org/html/rfc6901) through the
`at_pointer()` method, letting you reach further down into the document in a single call:
```c++
std::string 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 ] }
] )";
dom::parser parser;
dom::element cars = parser.parse(cars_json);
cout << cars.at_pointer("/0/tire_pressure/1") << endl; // Prints 39.9
```
A JSON Path is a sequence of segments each starting with the '/' character. Within arrays, an integer
index allows you to select the indexed node. Within objects, the string value of the key allows you to
select the value. If your keys contain the characters '/' or '~', they must be escaped as '~1' and
'~0' respectively. An empty JSON Path refers to the whole document.
We also extend the JSON Pointer support to include *relative* paths.
You can apply a JSON path to any node and the path gets interpreted relatively, as if the current node were a whole JSON document.
Consider the following example:
```c++
std::string 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 ] }
] )";
dom::parser parser;
dom::element cars = parser.parse(cars_json);
cout << cars.at_pointer("/0/tire_pressure/1") << endl; // Prints 39.9
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; }
double x = car.at_pointer("/tire_pressure/1");
cout << x << endl; // Prints 39.9, 31 and 30
}
```
Error Handling
--------------
All simdjson APIs that can fail return `simdjson_result<T>`, which is a &lt;value, error_code&gt;
pair. You can retrieve the value with .get(), like so:
```c++
dom::element doc;
auto error = parser.parse(json).get(doc);
if (error) { cerr << error << endl; exit(1); }
```
When you use the code this way, it is your responsibility to check for error before using the
result: if there is an error, the result value will not be valid and using it will caused undefined
behavior.
We can write a "quick start" example where we attempt to parse the following JSON file and access some data, without triggering exceptions:
```JavaScript
{
"statuses": [
{
"id": 505874924095815700
},
{
"id": 505874922023837700
}
],
"search_metadata": {
"count": 100
}
}
```
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 "simdjson.h"
int main(void) {
simdjson::dom::parser parser;
simdjson::dom::element tweets;
auto error = parser.load("twitter.json").get(tweets);
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
simdjson::dom::element res;
if ((error = tweets["search_metadata"]["count"].get(res))) {
std::cerr << "could not access keys" << std::endl;
return EXIT_FAILURE;
}
std::cout << res << " results." << std::endl;
}
```
The following is a similar example where one wants to get the id of the first tweet without
triggering exceptions. To do this, we use `["statuses"].at(0)["id"]`. We break that expression down:
- Get the list of tweets (the `"statuses"` key of the document) using `["statuses"]`). The result is expected to be an array.
- Get the first tweet using `.at(0)`. The result is expected to be an object.
- Get the id of the tweet using ["id"]. We expect the value to be a non-negative integer.
Observe how we use the `at` method when querying an index into an array, and not the bracket operator.
```C++
#include "simdjson.h"
int main(void) {
simdjson::dom::parser parser;
simdjson::dom::element tweets;
auto error = parser.load("twitter.json").get(tweets);
if(error) { std::cerr << 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; }
std::cout << identifier << std::endl;
return EXIT_SUCCESS;
}
```
### Error Handling Example
This is how the example in "Using the Parsed JSON" could be written using only error code checking:
```c++
std::string 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 ] }
] )";
dom::parser parser;
dom::array cars;
auto error = parser.parse(cars_json).get(cars);
if (error) { cerr << error << endl; exit(1); }
// Iterating through an array of objects
for (dom::element car_element : cars) {
dom::object car;
if ((error = car_element.get(car))) { cerr << error << endl; exit(1); }
// Accessing a field by name
std::string_view make, model;
if ((error = car["make"].get(make))) { cerr << error << endl; exit(1); }
if ((error = car["model"].get(model))) { cerr << error << endl; exit(1); }
cout << "Make/Model: " << make << "/" << model << endl;
// Casting a JSON element to an integer
uint64_t year;
if ((error = car["year"].get(year))) { cerr << error << endl; exit(1); }
cout << "- This car is " << 2020 - year << "years old." << endl;
// Iterating through an array of floats
double total_tire_pressure = 0;
dom::array tire_pressure_array;
if ((error = car["tire_pressure"].get(tire_pressure_array))) { cerr << error << endl; exit(1); }
for (dom::element tire_pressure_element : tire_pressure_array) {
double tire_pressure;
if ((error = tire_pressure_element.get(tire_pressure))) { cerr << error << endl; exit(1); }
total_tire_pressure += tire_pressure;
}
cout << "- Average tire pressure: " << (total_tire_pressure / 4) << endl;
// Writing out all the information about the car
for (auto field : car) {
cout << "- " << field.key << ": " << field.value << endl;
}
}
```
Here is another example:
```C++
std::string abstract_json = R"( [
{ "12345" : {"a":12.34, "b":56.78, "c": 9998877} },
{ "12545" : {"a":11.44, "b":12.78, "c": 11111111} }
] )";
dom::parser parser;
dom::array array;
auto error = parser.parse(abstract_json).get(array);
if (error) { cerr << error << endl; exit(1); }
// Iterate through an array of objects
for (dom::element elem : array) {
dom::object obj;
if ((error = elem.get(obj))) { cerr << error << endl; exit(1); }
for (auto & key_value : obj) {
cout << "key: " << key_value.key << " : ";
dom::object innerobj;
if ((error = key_value.value.get(innerobj))) { cerr << error << endl; exit(1); }
double va, vb;
if ((error = innerobj["a"].get(va))) { cerr << error << endl; exit(1); }
cout << "a: " << va << ", ";
if ((error = innerobj["b"].get(vc))) { cerr << error << endl; exit(1); }
cout << "b: " << vb << ", ";
int64_t vc;
if ((error = innerobj["c"].get(vc))) { cerr << error << endl; exit(1); }
cout << "c: " << vc << endl;
}
}
```
And another one:
```C++
std::string abstract_json = R"(
{ "str" : { "123" : {"abc" : 3.14 } } } )";
dom::parser parser;
double v;
auto error = parser.parse(abstract_json)["str"]["123"]["abc"].get(v);
if (error) { cerr << error << endl; exit(1); }
cout << "number: " << v << endl;
```
Notice how we can string several operations (`parser.parse(abstract_json)["str"]["123"]["abc"].get(v)`) and only check for the error once, a strategy we call *error chaining*.
The next two functions will take as input a JSON document containing an array with a single element, either a string or a number. They return true upon success.
```C++
simdjson::dom::parser parser{};
bool parse_double(const char *j, double &d) {
auto error = parser.parse(j, std::strlen(j))
.at(0)
.get(d, error);
if (error) { return false; }
return true;
}
bool parse_string(const char *j, std::string &s) {
std::string_view answer;
auto error = parser.parse(j,strlen(j))
.at(0)
.get(answer, error);
if (error) { return false; }
s.assign(answer.data(), answer.size());
return true;
}
```
To ensure you don't write any code that uses exceptions, compile with `SIMDJSON_EXCEPTIONS=OFF`. For example, if including the project via cmake:
```cmake
target_compile_definitions(simdjson PUBLIC SIMDJSON_EXCEPTIONS=OFF)
```
### Exceptions
Users more comfortable with an exception flow may choose to directly cast the `simdjson_result<T>` to the desired type:
```c++
dom::element doc = parser.parse(json); // Throws an exception if there was an error!
```
When used this way, a `simdjson_error` exception will be thrown if an error occurs, preventing the
program from continuing if there was an error.
If one is willing to trigger exceptions, it is possible to write simpler code:
```C++
#include "simdjson.h"
int main(void) {
simdjson::dom::parser parser;
simdjson::dom::element tweets = parser.load("twitter.json");
std::cout << "ID: " << tweets["statuses"].at(0)["id"] << std::endl;
return EXIT_SUCCESS;
}
```
Tree Walking and JSON Element Types
-----------------------------------
Sometimes you don't necessarily have a document with a known type, and are trying to generically
inspect or walk over JSON elements. To do that, you can use iterators and the type() method. For
example, here's a quick and dirty recursive function that verbosely prints the JSON document as JSON
(* ignoring nuances like trailing commas and escaping strings, for brevity's sake):
```c++
void print_json(dom::element element) {
switch (element.type()) {
case dom::element_type::ARRAY:
cout << "[";
for (dom::element child : dom::array(element)) {
print_json(child);
cout << ",";
}
cout << "]";
break;
case dom::element_type::OBJECT:
cout << "{";
for (dom::key_value_pair field : dom::object(element)) {
cout << "\"" << field.key << "\": ";
print_json(field.value);
}
cout << "}";
break;
case dom::element_type::INT64:
cout << int64_t(element) << endl;
break;
case dom::element_type::UINT64:
cout << uint64_t(element) << endl;
break;
case dom::element_type::DOUBLE:
cout << double(element) << endl;
break;
case dom::element_type::STRING:
cout << std::string_view(element) << endl;
break;
case dom::element_type::BOOL:
cout << bool(element) << endl;
break;
case dom::element_type::NULL_VALUE:
cout << "null" << endl;
break;
}
}
void basics_treewalk_1() {
dom::parser parser;
print_json(parser.load("twitter.json"));
}
```
Reusing the parser for maximum efficiency
-----------------------------------------
If you're using simdjson to parse multiple documents, or in a loop, you should make a parser once
and reuse it. The simdjson library will allocate and retain internal buffers between parses, keeping
buffers hot in cache and keeping memory allocation and initialization to a minimum. In this manner,
you can parse terabytes of JSON data without doing any new allocation.
```c++
dom::parser parser;
// This initializes buffers and a document big enough to handle this JSON.
dom::element doc = parser.parse(std::string("[ true, false ]"));
// std::string("[ true, false ]") may be replaced by "[ true, false ]"s
cout << doc << endl;
// This reuses the existing buffers, and reuses and *overwrites* the old document
doc = parser.parse(std::string("[1, 2, 3]"));
cout << doc << endl;
// This also reuses the existing buffers, and reuses and *overwrites* the old document
dom::element doc2 = parser.parse(std::string("true"));
// Even if you keep the old reference around, doc and doc2 refer to the same document.
cout << doc << endl;
cout << doc2 << endl;
```
It's not just internal buffers though. The simdjson library reuses the document itself. The dom::element, dom::object and dom::array instances are *references* to the internal document.
You are only *borrowing* the document from simdjson, which purposely reuses and overwrites it each
time you call parse. This prevent wasteful and unnecessary memory allocation in 99% of cases where
JSON is just read, used, and converted to native values or thrown away.
> **You are only borrowing the document from the simdjson parser. Don't keep it long term!**
This is key: don't keep the `document&`, `dom::element`, `dom::array`, `dom::object`
or `string_view` objects you get back from the API. Convert them to C++ native values, structs and
arrays that you own.
Server Loops: Long-Running Processes and Memory Capacity
--------------------------------------------------------
The simdjson library automatically expands its memory capacity when larger documents are parsed, so
that you don't unexpectedly fail. In a short process that reads a bunch of files and then exits,
this works pretty flawlessly.
Server loops, though, are long-running processes that will keep the parser around forever. This
means that if you encounter a really, really large document, simdjson will not resize back down.
The simdjson library lets you adjust your allocation strategy to prevent your server from growing
without bound:
* You can set a *max capacity* when constructing a parser:
```c++
dom::parser parser(1000*1000); // Never grow past documents > 1MB
for (web_request request : listen()) {
dom::element doc;
auto error = parser.parse(request.body).get(doc);
// If the document was above our limit, emit 413 = payload too large
if (error == CAPACITY) { request.respond(413); continue; }
// ...
}
```
This parser will grow normally as it encounters larger documents, but will never pass 1MB.
* You can set a *fixed capacity* that never grows, as well, which can be excellent for
predictability and reliability, since simdjson will never call malloc after startup!
```c++
dom::parser parser(0); // This parser will refuse to automatically grow capacity
auto error = parser.allocate(1000*1000); // This allocates enough capacity to handle documents <= 1MB
if (error) { cerr << error << endl; exit(1); }
for (web_request request : listen()) {
dom::element doc;
error = parser.parse(request.body).get(doc);
// If the document was above our limit, emit 413 = payload too large
if (error == CAPACITY) { request.respond(413); continue; }
// ...
}
```
Best Use of the DOM API
-------------------------
The simdjson API provides access to the JSON DOM (document-object-model) content as a tree of `dom::element` instances, each representing an object, an array or an atomic type (null, true, false, number). These `dom::element` instances are lightweight objects (e.g., spanning 16 bytes) and it might be advantageous to pass them by value, as opposed to passing them by reference or by pointer.
-229
View File
@@ -1,229 +0,0 @@
iterate_many
==========
An interface providing features to work with files or streams containing multiple small JSON documents.
As fast and convenient as possible.
Contents
--------
- [Motivations](#motivations)
- [How it works](#how-it-works)
- [Support](#support)
- [API](#api)
- [Use cases](#use-cases)
- [Tracking your position](#tracking-your-position)
- [Incomplete streams](#incomplete-streams)
Motivation
-----------
The main motivation for this piece of software is to achieve maximum speed and offer a
better quality of life in parsing files containing multiple small JSON documents.
The JavaScript Object Notation (JSON) [RFC7159](https://tools.ietf.org/html/rfc7159) is a handy
serialization format. However, when serializing a large sequence of
values as an array, or a possibly indeterminate-length or never-
ending sequence of values, JSON may be inconvenient.
Consider a sequence of one million values, each possibly one kilobyte
when encoded -- roughly one gigabyte. It is often desirable to process such a dataset incrementally
without having to first read all of it before beginning to produce results.
How it works
------------
### Context
Before parsing anything, simdjson first preprocesses the JSON text by identifying all structural indexes
(i.e. the starting position of any JSON value, as well as any important operators like `,`, `:`, `]` or
`}`) and validating UTF8. This stage is referred to stage 1. However, during this process, simdjson has
no knowledge of whether parsed a valid document, multiple documents, or even if the document is complete.
Then, to iterate through the JSON text during parsing, we use what we call a JSON iterator that will navigate
through the text using these structural indexes. This JSON iterator is not visible though, but it is the
key component to make parsing work.
Prior to iterate_many, most people who had to parse a multiline JSON file would proceed by reading the
file line by line, using a utility function like `std::getline` or equivalent, and would then use
the `parse` on each of those lines. From a performance point of view, this process is highly
inefficient, in that it requires a lot of unnecessary memory allocation and makes use of the
`getline` function, which is fundamentally slow, slower than the act of parsing with simdjson
[(more on this here)](https://lemire.me/blog/2019/06/18/how-fast-is-getline-in-c/).
Unlike the popular parser RapidJson, our DOM does not require the buffer once the parsing job is
completed, the DOM and the buffer are completely independent. The drawback of this architecture is
that we need to allocate some additional memory to store our ParsedJson data, for every document
inside a given file. Memory allocation can be slow and become a bottleneck, therefore, we want to
minimize it as much as possible.
### Design
To achieve a minimum amount of allocations, we opted for a design where we create only one
parser object and therefore allocate its memory once, and then recycle it for every document in a
given file. But, knowing that they often have largely varying size, we need to make sure that we
allocate enough memory so that all the documents can fit. This value is what we call the batch size.
As of right now, we need to manually specify a value for this batch size, it has to be at least as
big as the biggest document in your file, but not too big so that it submerges the cached memory.
The bigger the batch size, the fewer we need to make allocations. We found that 1MB is somewhat a
sweet spot.
1. When the user calls `iterate_many`, we return a `document_stream` which the user can iterate over
to receive parsed documents.
2. We call stage 1 on the first batch_size bytes of JSON in the buffer, detecting structural
indexes for all documents in that batch.
3. The `document_stream` owns a `document` instance that keeps track of the current document position
in the stream using a JSON iterator. To obtain a valid document, the `document_stream` returns a
**reference** to its document instance.
4. Each time the user calls `++` to read the next document, the JSON iterator moves to the start the
next document.
5. When we reach the end of the batch, we call stage 1 on the next batch, starting from the end of
the last document, and go to step 3.
### Threads
But how can we make use of threads if they are available? We found a pretty cool algorithm that allows
us to quickly identify the position of the last JSON document in a given batch. Knowing exactly where
the end of the last document in the batch is, we can safely parse through the last document without any
worries that it might be incomplete. Therefore, we can run stage 1 on the next batch concurrently while
parsing the documents in the current batch. Running stage 1 in a different thread can, in best cases,
remove almost entirely its cost and replaces it by the overhead of a thread, which is orders 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.
A `document_stream` instance uses at most two threads: there is a main thread and a worker thread.
Support
-------
Since we want to offer flexibility and not restrict ourselves to a specific file
format, we support any file that contains any amount of valid JSON document, **separated by one
or more character that is considered whitespace** by the JSON spec. Anything that is
not whitespace will be parsed as a JSON document and could lead to failure.
Whitespace Characters:
- **Space**
- **Linefeed**
- **Carriage return**
- **Horizontal tab**
- **Nothing**
Some official formats **(non-exhaustive list)**:
- [Newline-Delimited JSON (NDJSON)](http://ndjson.org/)
- [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)
API
---
See [basics.md](basics.md#newline-delimited-json-ndjson-and-json-lines) for an overview of the API.
## Use cases
From [jsonlines.org](http://jsonlines.org/examples/):
- **Better than CSV**
```json
["Name", "Session", "Score", "Completed"]
["Gilbert", "2013", 24, true]
["Alexa", "2013", 29, true]
["May", "2012B", 14, false]
["Deloise", "2012A", 19, true]
```
CSV seems so easy that many programmers have written code to generate it themselves, and almost every implementation is
different. Handling broken CSV files is a common and frustrating task. CSV has no standard encoding, no standard column
separator and multiple character escaping standards. String is the only type supported for cell values, so some programs
attempt to guess the correct types.
JSON Lines handles tabular data cleanly and without ambiguity. Cells may use the standard JSON types.
The biggest missing piece is an import/export filter for popular spreadsheet programs so that non-programmers can use
this format.
- **Easy Nested Data**
```json
{"name": "Gilbert", "wins": [["straight", "7♣"], ["one pair", "10♥"]]}
{"name": "Alexa", "wins": [["two pair", "4♠"], ["two pair", "9♠"]]}
{"name": "May", "wins": []}
{"name": "Deloise", "wins": [["three of a kind", "5♣"]]}
```
JSON Lines' biggest strength is in handling lots of similar nested data structures. One .jsonl file is easier to
work with than a directory full of XML files.
Tracking your position
-----------
Some users would like to know where the document they parsed is in the input array of bytes.
It is possible to do so by accessing directly the iterator and calling its `current_index()`
method which reports the location (in bytes) of the current document in the input stream.
You may also call the `source()` method to get a `std::string_view` instance on the document
and `error()` to check if there were any error.
Let us illustrate the idea with code:
```C++
std::string json = R"([1,2,3] {"1":1,"2":3,"4":4} [1,2,3] )";
simdjson::ondemand::parser parser;
simdjson::ondemand::document_stream stream;
auto error = parser.iterate_many(json).get(stream);
if( error ) { /* do something */ }
auto i = stream.begin();
size_t count{0};
for(; i != stream.end(); ++i) {
auto & doc = *i;
if(!i.error()) {
std::cout << "got full document at " << i.current_index() << std::endl;
std::cout << i.source() << std::endl;
count++;
} else {
std::cout << "got broken document at " << i.current_index() << std::endl;
return false;
}
}
```
This code will print:
```
got full document at 0
[1,2,3]
got full document at 9
{"1":1,"2":3,"4":4}
got full document at 29
[1,2,3]
```
Incomplete streams
-----------
Some users may need to work with truncated streams. The simdjson may truncate documents at the very end of the stream that cannot possibly be valid JSON (e.g., they contain unclosed strings, unmatched brackets, unmatched braces). After iterating through the stream, you may query the `truncated_bytes()` method which tells you how many bytes were truncated. If the stream is made of full (whole) documents, then you should expect `truncated_bytes()` to return zero.
Consider the following example where a truncated document (`{"key":"intentionally unclosed string `) containing 39 bytes has been left within the stream. In such cases, the first two whole documents are parsed and returned, and the `truncated_bytes()` method returns 39.
```C++
std::string json = R"([1,2,3] {"1":1,"2":3,"4":4} {"key":"intentionally unclosed string )";
simdjson::ondemand::parser parser;
simdjson::ondemand::document_stream stream;
auto error = parser.iterate_many(json,json.size()).get(stream);
if(error) { std::cerr << error << std::endl; return; }
for(auto i = stream.begin(); i != stream.end(); ++i) {
std::cout << i.source() << std::endl;
}
std::cout << stream.truncated_bytes() << " bytes "<< std::endl; // returns 39 bytes
```
This will print:
```
[1,2,3]
{"1":1,"2":3,"4":4}
39 bytes
```
Importantly, you should only call `truncated_bytes()` after iterating through all of the documents since the stream cannot tell whether there are truncated documents at the very end when it may not have accessed that part of the data yet.
+230
View File
@@ -0,0 +1,230 @@
On Demand Basics
================
On Demand is a new, faster simdjson API with all the ease-of-use you are used to. While it provides a
familiar DOM interface, under the hood it is different: it is parsing values *as you use them.*
With On Demand, you do not waste time parsing JSON you do not use, and you do not pay the cost of generating
an intermediate DOM tree.
We provide an overview of what you need to know to use the simdjson On Demand API, with examples.
* [Including ondemand](#including-on-demand)
* [The Basics: Loading and Parsing JSON Documents](#the-basics-loading-and-parsing-json-documents)
* [Using the Parsed JSON](#using-the-parsed-json)
The On Demand API supports the same JSON standards and C++ compilers as simdjson's DOM API. Refer to the DOM docs for more information:
* [Requirements](basics.md##requirements)
* [Using simdjson as a CMake dependency](#using-simdjson-as-a-cmake-dependency)
* [Error Handling](basics.md#error-handling)
* [Error Handling Example](basics.md#error-handling-example)
* [Exceptions](basics.md#exceptions)
* [Thread Safety](basics.md#thread-safety)
* [Standard Compliance](basics.md#standard-compliance)
* [C++11 Support and string_view](basics.md#c11-support-and-string_view)
* [C++17 Support](basics.md#c17-support)
* [Backwards Compatibility](basics.md#backwards-compatibility)
For deeper information about the design and implementation of the simdjson On Demand API, refer to
the [design document](ondemand.md).
Including On Demand
------------------
To include simdjson, copy [simdjson.h](/singleheader/simdjson.h) and [simdjson.cpp](/singleheader/simdjson.cpp)
into your project. Then include it in your project with:
```c++
#include "simdjson.h"
using namespace simdjson; // optional
```
You can generally compile with:
```
c++ -O3 myproject.cpp simdjson.cpp
```
Note:
- Users on macOS and other platforms where compilers do not provide C++11 compliant by default
should request it with the appropriate flag (e.g., `c++ -march=native -std=c++17 myproject.cpp simdjson.cpp`).
The Basics: Loading and Parsing JSON Documents
----------------------------------------------
The simdjson library offers a simple DOM tree API, which you can access by creating a
`ondemand::parser` and calling the `iterate()` method:
```c++
ondemand::parser parser;
auto json = padded_string::load("twitter.json");
ondemand::document doc = parser.iterate(json); // load and parse a file
```
Or by creating a padded string (for efficiency reasons, simdjson requires a string with
SIMDJSON_PADDING bytes at the end) and calling `iterate()`:
```c++
ondemand::parser parser;
auto json = "[1,2,3]"_padded; // The _padded suffix creates a simdjson::padded_string instance
ondemand::document doc = parser.iterate(json); // parse a string
```
Documents Are Iterators
-----------------------
A `document` is *not* a fully-parsed JSON value; rather, it is an **iterator** over the JSON text.
This means that while you iterate an array, or search for a field in an object, it is actually
walking through the original JSON text, merrily reading commas and colons and brackets to make sure
you get where you are going. This is the key to On Demand's performance: since it's just an iterator,
it lets you parse values as you use them. And particularly, it lets you *skip* values you do not want
to use.
### Parser, Document and JSON Scope
Because a document is an iterator over the JSON text, both the JSON text and the parser must
remain alive (in scope) while you are using it. Further, a `parser` may have at most
one document open at a time, since it holds allocated memory used for the parsing.
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.
For best performance, a `parser` instance should be reused over several files: otherwise you will
needlessly reallocate memory, an expensive process. It is also possible to avoid entirely memory
allocations during parsing when using simdjson. [See our performance notes for details](performance.md).
Using the Parsed JSON
---------------------
Once you have a document, you can navigate it with idiomatic C++ iterators, operators and casts.
The following show how to use the JSON when exceptions are enabled, but simdjson has full, idiomatic
support for users who avoid exceptions. See [the simdjson DOM API's error handling documentation](basics.md#error-handling) for more.
* **Extracting Values:** You can cast a JSON element to a native type:
`double(element)` or `double x = json_element`. This works for double, uint64_t, int64_t, bool,
ondemand::object and ondemand::array. At this point, the number, string or boolean will be parsed,
or the initial `[` or `{` will be verified. An exception is thrown if the cast is not possible.
> IMPORTANT NOTE: values can only be parsed once. Since documents are *iterators*, once you have
> parsed a value (such as by casting to double), you cannot get at it again.
* **Field Access:** To get the value of the "foo" field in an object, use `object["foo"]`. This will
scan through the object looking for the field with the matching string.
> NOTE: simdjson does *not* unescape keys when matching. This is not generally a problem for
> applications with well-defined key names (which generally do not use escapes). If you do need this
> support, it's best to iterate through the object fields to find the field you are looking for.
>
> By default, field lookup is order-insensitive, so you can look up values in any order. However,
> we still encourage you to look up fields in the order you expect them in the JSON, as it is still
> much faster.
>
> If you want to enforce finding fields in order, you can use `object.find_field("foo")` instead.
> This will only look forward, and will fail to find fields in the wrong order: for example, this
> will fail:
>
> ```c++
> ondemand::parser parser;
> auto json = R"( { "x": 1, "y": 2 } )"_padded;
> auto doc = parser.iterate(json);
> double y = doc.find_field("y"); // The cursor is now after the 2 (at })
> double x = doc.find_field("x"); // This fails, because there are no more fields after "y"
> ```
>
> By contrast, using the default (order-insensitive) lookup succeeds:
>
> ```c++
> ondemand::parser parser;
> auto json = R"( { "x": 1, "y": 2 } )"_padded;
> auto doc = parser.iterate(json);
> double y = doc["y"]; // The cursor is now after the 2 (at })
> double x = doc["x"]; // Success: [] loops back around to find "x"
> ```
* **Array Iteration:** To iterate through an array, use `for (auto value : array) { ... }`. This will
step through each value in the JSON array.
If you know the type of the value, you can cast it right there, too! `for (double value : array) { ... }`.
* **Object Iteration:** You can iterate through an object's fields, as well: `for (auto field : object) { ... }`
- `field.unescaped_key()` will get you the key string.
- `field.value()` will get you the value, which you can then use all these other methods on.
* **Array Index:** Because it is forward-only, you cannot look up an array element by index. Instead,
you will need to iterate through the array and keep an index yourself.
### Examples
The following code illustrates many of the above concepts:
```c++
ondemand::parser parser;
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;
// Iterating through an array of objects
for (ondemand::object car : parser.iterate(cars_json)) {
// Accessing a field by name
cout << "Make/Model: " << std::string_view(car["make"]) << "/" << std::string_view(car["model"]) << endl;
// Casting a JSON element to an integer
uint64_t year = car["year"];
cout << "- This car is " << 2020 - year << "years old." << endl;
// Iterating through an array of floats
double total_tire_pressure = 0;
for (double tire_pressure : car["tire_pressure"]) {
total_tire_pressure += tire_pressure;
}
cout << "- Average tire pressure: " << (total_tire_pressure / 4) << endl;
}
```
Here is a different example illustrating the same ideas:
```C++
ondemand::parser parser;
auto points_json = R"( [
{ "12345" : {"x":12.34, "y":56.78, "z": 9998877} },
{ "12545" : {"x":11.44, "y":12.78, "z": 11111111} }
] )"_padded;
// Parse and iterate through an array of objects
for (ondemand::object points : parser.iterate(points_json)) {
for (auto point : points) {
cout << "id: " << std::string_view(point.unescaped_key()) << ": (";
cout << point.value()["x"].get_double() << ", ";
cout << point.value()["y"].get_double() << ", ";
cout << point.value()["z"].get_int64() << endl;
}
}
```
And another one:
```C++
auto abstract_json = R"(
{ "str" : { "123" : {"abc" : 3.14 } } }
)"_padded;
ondemand::parser parser;
auto doc = parser.iterate(abstract_json);
cout << doc["str"]["123"]["abc"].get_double() << endl; // Prints 3.14
```
* **Extracting Values (without exceptions):** You can use a variant usage of `get()` with error
codes to avoid exceptions. You first declare the variable of the appropriate type (`double`,
`uint64_t`, `int64_t`, `bool`, `ondemand::object` and `ondemand::array`) and pass it by reference
to `get()` which gives you back an error code: e.g.,
```c++
auto abstract_json = R"(
{ "str" : { "123" : {"abc" : 3.14 } } }
)"_padded;
ondemand::parser parser;
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; }
cout << value << endl; // Prints 3.14
```
+2 -43
View File
@@ -632,12 +632,7 @@ We iterate through object instances using `field` instances which represent key-
is accessible by the `value()` method whereas the key is accessible by the `key()` method.
The keys are treated differently than values are made available as as special type `raw_json_string`
which is a lightweight type that is meant to be used on a temporary basis, amost solely for
direct raw ASCII comparisons: `key().raw()` provides direct access to the unescaped string.
You can compare `key()` with unescaped C strings (e.g., `key()=="test"`). It is expected
that the provided string is a valid JSON string. Importantly,
the C string must not contain an unescaped quote character (`"`). For speed, the comparison is done byte-by-byte
without handling the escaped characters.
If you occasionally need to access and store the
direct raw ASCII comparisons (`field.key() == "mykey"`). If you occasionally need to access and store the
unescaped key values, you may use the `unescaped_key()` method. Once you have called `unescaped_key()` method,
neither the `key()` nor the `unescaped_key()` methods should be called: the current field instance
has no longer a key (that is by design). Like other strings, the resulting `std::string_view` generated
@@ -709,42 +704,6 @@ in production systems:
}
```
### Long-Running Processes and Memory Capacity
The On Demand approach also automatically expands its memory capacity when larger documents are parsed. However, for longer processes where very large files are processed (such as server loops), this capacity is not resized down. Similarly to the DOM-based approach (see [here](https://github.com/simdjson/simdjson/blob/master/doc/dom.md#server-loops-long-running-processes-and-memory-capacity)]), On Demand also lets you adjust the maximal capacity that the parser can process:
* You can set an upper bound (*max_capacity*) when construction the parser:
```C++
ondemand::parser parser(1000*1000); // Never grows past documents > 1 MB
auto doc = parser.iterate(json);
for (web_request request : listen()) {
padded_string json;
padded_string json = padded_string::load(request.body);
auto error = parser.iterate(json);
// If the document was above our limit, emit 413 = payload too large
if (error == CAPACITY) { request.respond(413); continue; }
// ...
}
```
The capacity will grow as the parser encounters larger documents up to 1 MB.
* You can also allocate a *fixed capacity* that will never grow:
```C++
ondemand::parser parser(1000*1000);
parser.allocate(1000*1000) // Fix the capacity to 1 MB
auto doc = parser.iterate(json);
for (web_request request : listen()) {
padded_string json;
padded_string json = padded_string::load(request.body);
auto error = parser.iterate(json);
// If the document was above our limit, emit 413 = payload too large
if (error == CAPACITY) { request.respond(413); continue; }
// ...
}
```
You can also manually set the maximal capacity using the method `set_max_capacity()`.
### Benefits of the On Demand Approach
We expect that the On Demand approach has many of the performance benefits of the schema-based approach, while providing a flexibility that is similar to that of the DOM-based approach.
@@ -816,4 +775,4 @@ Instead of specifying a specific microarchitecture, you can let your compiler do
Passing `-march=native` to the compiler may make On Demand faster by allowing it to use optimizations specific to your machine. You cannot do this, however, if you are compiling code that might be run on less advanced machines. That is, be mindful that when compiling with the `-march=native` flag, the resulting binary will run on the current system but may not run on other systems (e.g., on an old processor).
If you are compiling on an ARM or POWER system, you do not need to be concerned with CPU selection during compilation. The `-march=native` flag useful for best performance on x64 (e.g., Intel) systems but it is generally unsupported on some platforms such as ARM (aarch64) or POWER.
If you are compiling on an ARM or POWER system, you do not need to be concerned with CPU selection during compilation. The `-march=native` flag useful for best performance on x64 (e.g., Intel) systems but it is generally unsupported on some platforms such as ARM (aarch64) or POWER.
+18 -31
View File
@@ -168,63 +168,50 @@ Tracking your position
Some users would like to know where the document they parsed is in the input array of bytes.
It is possible to do so by accessing directly the iterator and calling its `current_index()`
method which reports the location (in bytes) of the current document in the input stream.
You may also call the `source()` method to get a `std::string_view` instance on the document.
Let us illustrate the idea with code:
```C++
std::string json = R"([1,2,3] {"1":1,"2":3,"4":4} [1,2,3] )";
auto json = R"([1,2,3] {"1":1,"2":3,"4":4} [1,2,3] )"_padded;
simdjson::dom::parser parser;
simdjson::dom::document_stream stream;
auto error = parser.parse_many(json).get(stream);
if( error ) { /* do something */ }
ASSERT_SUCCESS( parser.parse_many(json).get(stream) );
auto i = stream.begin();
size_t count{0};
for(; i != stream.end(); ++i) {
auto doc = *i;
if(!doc.error()) {
std::cout << "got full document at " << i.current_index() << std::endl;
std::cout << i.source() << std::endl;
count++;
} else {
std::cout << "got broken document at " << i.current_index() << std::endl;
return false;
}
}
size_t index = i.current_index();
if(index != 38) {
std::cerr << "Expected to stop after the three full documents " << std::endl;
std::cerr << "index = " << index << std::endl;
return false;
}
```
This code will print:
```
got full document at 0
[1,2,3]
got full document at 9
{"1":1,"2":3,"4":4}
got full document at 29
[1,2,3]
```
The last call to `i.current_index()` return the byte index 38, which is just beyond
the last document.
Incomplete streams
-----------
Some users may need to work with truncated streams. The simdjson may truncate documents at the very end of the stream that cannot possibly be valid JSON (e.g., they contain unclosed strings, unmatched brackets, unmatched braces). After iterating through the stream, you may query the `truncated_bytes()` method which tells you how many bytes were truncated. If the stream is made of full (whole) documents, then you should expect `truncated_bytes()` to return zero.
Some users may need to work with truncated streams while tracking their location in the stream.
The same code, with the `current_index()` will work. However, the last block (by default 1MB)
terminates with an unclosed string, then no JSON document within this last block will validate.
In particular, it means that if your input string is `[1,2,3] {"1":1,"2":3,"4":4} [1,2` then
no JSON document will be successfully parsed. The error `simdjson::UNCLOSED_STRING` will be
given (even with the first JSON document). It is then your responsability to terminate the input
maybe by appending the missing data at the end of the truncated string, or by copying the truncated
data before the continuing input.
Consider the following example where a truncated document (`{"key":"intentionally unclosed string `) containing 39 bytes has been left within the stream. In such cases, the first two whole documents are parsed and returned, and the `truncated_bytes()` method returns 39.
```C++
std::string json = R"([1,2,3] {"1":1,"2":3,"4":4} {"key":"intentionally unclosed string )";
simdjson::dom::parser parser;
simdjson::dom::document_stream stream;
auto error = parser.parse_many(json,json.size()).get(stream);
if(error) { std::cerr << error << std::endl; return; }
for(auto doc : stream) {
std::cout << doc << std::endl;
}
std::cout << stream.truncated_bytes() << " bytes "<< std::endl; // returns 39 bytes
```
Importantly, you should only call `truncated_bytes()` after iterating through all of the documents since the stream cannot tell whether there are truncated documents at the very end when it may not have accessed that part of the data yet.
+94 -20
View File
@@ -5,12 +5,15 @@ simdjson strives to be at its fastest *without tuning*, and generally achieves t
are still some scenarios where tuning can enhance performance.
* [Reusing the parser for maximum efficiency](#reusing-the-parser-for-maximum-efficiency)
* [Reusing string buffers](#reusing-string-buffers)
* [Keeping documents around for longer](#keeping-documents-around-for-longer)
* [Server Loops: Long-Running Processes and Memory Capacity](#server-loops-long-running-processes-and-memory-capacity)
* [Large files and huge page support](#large-files-and-huge-page-support)
* [Number parsing](#number-parsing)
* [Visual Studio](#visual-studio)
* [Downclocking](#downclocking)
* [Best Use of the DOM API](#best-use-of-the-dom-api)
* [Padding and Temporary Copies](#padding-and-temporary-copies)
Reusing the parser for maximum efficiency
-----------------------------------------
@@ -21,34 +24,77 @@ buffers hot in cache and keeping memory allocation and initialization to a minim
you can parse terabytes of JSON data without doing any new allocation.
```c++
ondemand::parser parser;
dom::parser parser;
// This initializes buffers big enough to handle this JSON.
std::string json = "[ true, false ]";
auto doc = parser.iterate(json);
for(bool i : doc.get_array()) {
cout << i << endl;
}
// This initializes buffers and a document big enough to handle this JSON.
dom::element doc = parser.parse("[ true, false ]"_padded);
cout << doc << endl;
// This reuses the existing buffers
std::string number_json = "[1, 2, 3]";
doc = parser.iterate(number_json);
for(int64_t i : doc.get_array()) {
cout << i << endl;
}
// This reuses the existing buffers, and reuses and *overwrites* the old document
doc = parser.parse("[1, 2, 3]"_padded);
cout << doc << endl;
// This also reuses the existing buffers, and reuses and *overwrites* the old document
dom::element doc2 = parser.parse("true"_padded);
// Even if you keep the old reference around, doc and doc2 refer to the same document.
cout << doc << endl;
cout << doc2 << endl;
```
It's not just internal buffers though. The simdjson library reuses the document itself. The dom::element, dom::object and dom::array instances are *references* to the internal document.
You are only *borrowing* the document from simdjson, which purposely reuses and overwrites it each
time you call parse. This prevent wasteful and unnecessary memory allocation in 99% of cases where
JSON is just read, used, and converted to native values or thrown away.
Reusing string buffers
-----------------------------------------
> **You are only borrowing the document from the simdjson parser. Don't keep it long term!**
We recommend against creating many `std::string` or `simdjson::padded_string` instances to store the JSON content in your application. [Creating many non-trivial objects is convenient but often surprisingly slow](https://lemire.me/blog/2020/08/08/performance-tip-constructing-many-non-trivial-objects-is-slow/). Instead, as much as possible, you should allocate (once or a few times) reusable memory buffers where you write your JSON content. If you have a buffer `json_str` (of type `char*`) allocated for `capacity` bytes and you store a JSON document spanning `length` bytes, you can pass it to simdjson as follows:
This is key: don't keep the `document&`, `dom::element`, `dom::array`, `dom::object`
or `string_view` objects you get back from the API. Convert them to C++ native values, structs and
arrays that you own.
```c++
auto doc = parser.iterate(json_str, length));
```
Server Loops: Long-Running Processes and Memory Capacity
--------------------------------------------------------
The simdjson library automatically expands its memory capacity when larger documents are parsed, so
that you don't unexpectedly fail. In a short process that reads a bunch of files and then exits,
this works pretty flawlessly.
Server loops, though, are long-running processes that will keep the parser around forever. This
means that if you encounter a really, really large document, simdjson will not resize back down.
The simdjson library lets you adjust your allocation strategy to prevent your server from growing
without bound:
* You can set a *max capacity* when constructing a parser:
```c++
dom::parser parser(1000*1000); // Never grow past documents > 1MB
for (web_request request : listen()) {
dom::element doc;
auto error = parser.parse(request.body).get(doc);
// If the document was above our limit, emit 413 = payload too large
if (error == CAPACITY) { request.respond(413); continue; }
// ...
}
```
This parser will grow normally as it encounters larger documents, but will never pass 1MB.
* You can set a *fixed capacity* that never grows, as well, which can be excellent for
predictability and reliability, since simdjson will never call malloc after startup!
```c++
dom::parser parser(0); // This parser will refuse to automatically grow capacity
auto error = parser.allocate(1000*1000); // This allocates enough capacity to handle documents <= 1MB
if (error) { cerr << error << endl; exit(1); }
for (web_request request : listen()) {
dom::element doc;
error = parser.parse(request.body).get(doc);
// If the document was above our limit, emit 413 = payload too large
if (error == CAPACITY) { request.respond(413); continue; }
// ...
}
```
Large files and huge page support
---------------------------------
@@ -124,3 +170,31 @@ On some Intel processors, using SIMD instructions in a sustained manner on the s
The simdjson library does not currently support AVX-512 instructions and it does not make use of heavy 256-bit instructions. We do use vectorized multiplications, but only using 128-bit registers. Thus there should be no downclocking due to simdjson on recent processors.
You may still be worried about which SIMD instruction set is used by simdjson. Thankfully, [you can always determine and change which architecture-specific implementation is used](implementation-selection.md) by simdjson. Thus even if your CPU supports AVX2, you do not need to use AVX2. You are in control.
Best Use of the DOM API
-------------------------
The simdjson API provides access to the JSON DOM (document-object-model) content as a tree of `dom::element` instances, each representing an object, an array or an atomic type (null, true, false, number). These `dom::element` instances are lightweight objects (e.g., spanning 16 bytes) and it might be advantageous to pass them by value, as opposed to passing them by reference or by pointer.
Padding and Temporary Copies
--------------
The simdjson function `parser.parse` reads data from a padded buffer, containing SIMDJSON_PADDING extra bytes added at the end.
If you are passing a `padded_string` to `parser.parse` or loading the JSON directly from
disk (`parser.load`), padding is automatically handled.
When calling `parser.parse` on a pointer (e.g., `parser.parse(my_char_pointer, my_length_in_bytes)`) a temporary copy is made by default with adequate padding and you, again, do not need to be concerned with padding.
Some users may not be able use our `padded_string` class or to load the data directly from disk (`parser.load`). They may need to pass data pointers to the library. If these users wish to avoid temporary copies and corresponding temporary memory allocations, they may want to call `parser.parse` with the `realloc_if_needed` parameter set to false (e.g., `parser.parse(my_char_pointer, my_length_in_bytes, false)`). In such cases, they need to ensure that there are at least SIMDJSON_PADDING extra bytes at the end that can be safely accessed and read. They do not need to initialize the padded bytes to any value in particular. The following example is safe:
```C++
const char *json = R"({"key":"value"})";
const size_t json_len = std::strlen(json);
std::unique_ptr<char[]> padded_json_copy{new char[json_len + SIMDJSON_PADDING]};
memcpy(padded_json_copy.get(), json, json_len);
memset(padded_json_copy.get() + json_len, 0, SIMDJSON_PADDING);
simdjson::dom::parser parser;
simdjson::dom::element element = parser.parse(padded_json_copy.get(), json_len, false);
````
Setting the `realloc_if_needed` parameter `false` in this manner may lead to better performance since copies are avoided, but it requires that the user takes more responsibilities: the simdjson library cannot verify that the input buffer was padded with SIMDJSON_PADDING extra bytes.
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

+3 -3
View File
@@ -31,7 +31,7 @@ The following is a dump of the content of the tape, with the first number of eac
### The Tape
| index | element (64 bit word) |
| ----- | ------------------------------------------------------------------- |
| 0 | r // pointing to 39 (right after last node) |
| 0 | r // pointing to 38 (right after last node) |
| 1 | { // pointing to next tape location 38 (first node after the scope) |
| 2 | string "Image" |
| 3 | { // pointing to next tape location 37 (first node after the scope) |
@@ -115,7 +115,7 @@ We store string values using UTF-8 encoding with null termination on a separate
JSON arrays are represented using two 64-bit tape elements.
- The first 64-bit tape element contains the value `('[' << 56) + (c << 32) + x` where the payload `x` is 1 + the index of the second 64-bit tape element on the tape as a 32-bit integer and where `c` is the count of the number of elements (immediate children) in the array, satured to a 24-bit value (meaning that it cannot exceed 16777215 and if the real count exceeds 16777215, 16777215 is stored). Note that the exact count of elements can always be computed by iterating (e.g., when it is 16777215 or higher).
- The first 64-bit tape element contains the value `('[' << 56) + x` where the payload `x` is 1 + the index of the second 64-bit tape element on the tape.
- The second 64-bit tape element contains the value `(']' << 56) + x` where the payload `x` contains the index of the first 64-bit tape element on the tape.
All the content of the array is located between these two tape elements, including arrays and objects.
@@ -126,7 +126,7 @@ Performance consideration: We can skip the content of an array entirely by acces
JSON objects are represented using two 64-bit tape elements.
- The first 64-bit tape element contains the value `('{' << 56) + (c << 32) + x` where the payload `x` is 1 + the index of the second 64-bit tape element on the tape as a 32-bit integer and where `c` is the count of the number of key-value pairs (immediate children) in the array, satured to a 24-bit value (meaning that it cannot exceed 16777215 and if the real count exceeds 16777215, 16777215 is stored). Note that the exact count of key-value pairs can always be computed by iterating (e.g., when it is 16777215 or higher).
- The first 64-bit tape element contains the value `('{' << 56) + x` where the payload `x` is 1 + the index of the second 64-bit tape element on the tape.
- The second 64-bit tape element contains the value `('}' << 56) + x` where the payload `x` contains the index of the first 64-bit tape element on the tape.
In-between these two tape elements, we alternate between key (which must be strings) and values. A value could be an object or an array.
+6 -10
View File
@@ -1,11 +1,6 @@
if(NOT SIMDJSON_LEGACY_VISUAL_STUDIO AND NOT SIMDJSON_WINDOWS_DLL)
option(SIMDJSON_ENABLE_FUZZING "enable building the fuzzers" ON)
else()
option(SIMDJSON_ENABLE_FUZZING "enable building the fuzzers" OFF)
endif()
option(ENABLE_FUZZING "enable building the fuzzers" ON)
if(SIMDJSON_ENABLE_FUZZING)
if(ENABLE_FUZZING)
# First attempt at a fuzzer, using libFuzzer.
#
@@ -17,7 +12,7 @@ if(SIMDJSON_ENABLE_FUZZING)
# export CFLAGS="-fsanitize=fuzzer-no-link,address,undefined"
# export CXX=clang++
# export CC=clang++
# cmake .. -GNinja -DCMAKE_BUILD_TYPE=Debug -DSIMDJSON_ENABLE_FUZZING=On -DSIMDJSON_FUZZ_LINKMAIN=Off -DSIMDJSON_FUZZ_LDFLAGS=-fsanitize=fuzzer
# cmake .. -GNinja -DCMAKE_BUILD_TYPE=Debug -DENABLE_FUZZING=On -DSIMDJSON_FUZZ_LINKMAIN=Off -DSIMDJSON_FUZZ_LDFLAGS=-fsanitize=fuzzer
# ninja
# settings this links in a main. useful for reproducing,
@@ -25,7 +20,7 @@ if(SIMDJSON_ENABLE_FUZZING)
# (note that libFuzzer can also reproduce, just pass it the files)
#
# Using this by default, means the fuzzers will be built as a part of the normal
# workflow, meaning they won't bitrot and will participate in refactoring etc.
# workflow, meaning they wont bitrot and will participate in refactoring etc.
#
option(SIMDJSON_FUZZ_LINKMAIN "links a main into fuzz targets for building reproducers" On)
@@ -35,10 +30,11 @@ if(SIMDJSON_ENABLE_FUZZING)
# Fuzzer build flags and libraries
add_library(simdjson-fuzzer INTERFACE)
target_link_libraries(simdjson-fuzzer INTERFACE simdjson)
if (SIMDJSON_FUZZ_LINKMAIN)
target_link_libraries(simdjson-fuzzer INTERFACE simdjson-source)
target_sources(simdjson-fuzzer INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>/main.cpp)
else ()
target_link_libraries(simdjson-fuzzer INTERFACE simdjson)
target_link_libraries(simdjson-fuzzer INTERFACE ${SIMDJSON_FUZZ_LDFLAGS})
endif ()
target_link_libraries(simdjson-fuzzer INTERFACE simdjson-internal-flags)

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