Compare commits

...

125 Commits

Author SHA1 Message Date
Daniel Lemire ee66fb1c60 Version 0.2.0. 2019-08-01 16:23:30 -04:00
Daniel Lemire ea212e4b50 Update CONTRIBUTORS 2019-08-01 16:12:18 -04:00
Daniel Lemire 038b18edf1 Adding style scripts. (#243)
* Adding style scripts.
2019-08-01 16:09:26 -04:00
Daniel Lemire 0610ebc514 We are adopting clang-format. 2019-08-01 15:40:07 -04:00
ioioioio 968117c940 preventing clang-format to move sysinfoapi.h (#244) 2019-08-01 15:06:50 -04:00
Daniel Lemire 6788b12d65 It is not beneficial to try to get clever with trailing zeroes. (Lead to major performance
regression under haswell+ for stage 1).
2019-08-01 14:44:04 -04:00
Daniel Lemire 66ffc1b2d6 Adding a remark. 2019-08-01 11:33:51 -04:00
Daniel Lemire 4c7d384e9a Update README.md 2019-07-31 21:53:32 -04:00
Daniel Lemire d83aef4e86 This should fix a warning in Visual Studio. 2019-07-31 18:12:58 -04:00
John Keiser bf59ba76f5 Fix most warnings on VS2019 (#241) 2019-07-31 17:43:45 -04:00
Daniel Lemire e8e78f8be6 Testing on arm (#240)
* This should enabled testing on ARM

* Adding cmake tests on drone.
2019-07-30 18:21:50 -04:00
Daniel Lemire 76da659977 Fixing amalgamate under ARM 2019-07-30 22:10:48 +00:00
ioioioio c2eea8abba Style uniformization (#238)
* massive clang-format -style=LLVM

* naming harmonization

* adding commentary about sysinfoapi.h
2019-07-30 17:18:10 -04:00
Daniel Lemire 065805d6e1 Fixing amalgamation. 2019-07-30 09:10:40 -04:00
Daniel Lemire 5fa79b2db2 Adding John Keiser as a contributor 2019-07-29 14:12:44 -04:00
ioioioio 5f20d3eb34 Merging No duplicate tail (PR#223) (#232)
* Use __forceinline on Windows for really_inline

https://docs.microsoft.com/en-us/cpp/cpp/inline-functions-cpp?view=vs-2019#inline-__inline-and-__forceinline

* Don't duplicate find_structural_bits for final chunk

* writing coherent macro definitions
2019-07-29 14:11:42 -04:00
Daniel Lemire 3c0f5a3fe4 Improving the documentation. 2019-07-29 14:10:49 -04:00
Daniel Lemire 771e9cd68a Trying again... (#235) 2019-07-29 13:55:13 -04:00
Daniel Lemire c328afee57 This should fix master. 2019-07-29 13:44:25 -04:00
Daniel Lemire 3dae86223d Changing intrinsic name. 2019-07-29 13:39:54 -04:00
Daniel Lemire dd07212f02 Adding more test files. 2019-07-29 13:34:13 -04:00
Daniel Lemire 85e31a5479 This fixes the "big exp" bug, although we need to assess the performance and maybe do some tuning. (#233) 2019-07-29 13:28:16 -04:00
Daniel Lemire a53d95099c Intrinsic-based flatten (#234)
* Providing a flatten function with intrinsics (for Visual Studio).
2019-07-29 13:28:02 -04:00
Daniel Lemire f76ee5e5ef Fixes issue 221 (#222)
https://github.com/lemire/simdjson/issues/221
2019-07-29 10:07:07 -04:00
Daniel Lemire eba02dc1b9 Runtime dispatch
* Attempt 1 - fn targeting

GCC won't work with templates with different targets, need to specialize all the way up the call stack.

* Compiles properly with cmake. Does not with the Makefile.

* Compilation works with Makefile

* instruction_set changes to architecture

* some aesthetic changes

* fix amalgation and tests + aesthetic changes

* This now compiles and passes tests under CLANG

* Minor correction.

* Trying to make it work on ARM

* Adding missing namespace

* Missing bracket

* Fixing minor compilation issues.

* Getting parse to use runtime dispatch

* Fixing amalgamation script.

* Making sure that NEON is supported.

* Fixing typo

* Merging https://github.com/lemire/simdjson/pull/229

* Manual merge of
https://github.com/lemire/simdjson/pull/229
by @jkeiser  (second part)

* Trying another way.

* Removing the paral.

* Fixing the make file

* Let us make the practice run long enough.

* Resolved the awful slowness.

* Cleaning the README.md

* With runtime dispatching, we should not need flags anymore.

* Changing isa detection file's name + fixing typos.
2019-07-28 22:46:33 -04:00
ioioioio bcabdfc1ae Json pointer (#220)
* json pointer support

* Addition of tests for the json pointer

* Adding a new tool for the JSON Pointer support, and some documentation.
2019-07-26 18:38:10 -04:00
Daniel Lemire cb44b3b9f2 Making sure everything builds. (#226) 2019-07-26 13:53:53 -04:00
AmoghSubhedar 9aa2cd71b2 Fix benchmark cpp files (#225) 2019-07-26 08:37:52 -04:00
Daniel Lemire abdf81b39b Making sure that it is clear that we generate a full DOM 2019-07-19 16:25:05 -04:00
Daniel Lemire 1176725af7 Link to Rust bindings 2019-07-19 16:19:29 -04:00
Daniel Lemire f668adcf11 iPhone 5s 2019-07-18 16:45:40 -04:00
Daniel Lemire a3beac8d13 This simplifies back the number parsing code... The extra work introduced recently is seemingly unnecessary. (#218) 2019-07-18 11:50:26 -04:00
Daniel Lemire e926b4b3c9 More accurate number parsing (#217)
* This drastically improves the accuracy (down to to a ULP of 1)

* More comments and documentation.
2019-07-15 22:17:49 -04:00
Daniel Lemire 6c168f046d Optimizing stage1 (#216)
* Optimizing stage 1-- avx edition

* Optimizing sse.

* Saving 0.5% in instruction count (NEON).
2019-07-11 20:59:21 -04:00
Daniel Lemire 37fa6affc8 Documenting Docker 2019-07-11 16:20:24 +00:00
Daniel Lemire 47c6490115 Tweaking makefile. 2019-07-11 11:54:17 -04:00
Daniel Lemire 467831e3d8 Refreshing the single header files. 2019-07-11 09:40:42 -04:00
Daniel Lemire 3c1638c046 We no longer need this old stuff. (#214) 2019-07-09 22:04:02 -04:00
Daniel Lemire 4b7e87ec7f Removing garbage. (#213) 2019-07-09 21:51:16 -04:00
Daniel Lemire 98b387aac3 Fixing a messed up interleaved #ifdef/namespace. (#211) 2019-07-09 19:48:20 -04:00
Daniel Lemire 5312c7ff31 Adding remark to the effect that we support ARM 2019-07-09 19:47:41 -04:00
Daniel Lemire be956654b2 Minor cleaning = annotating simdjson namespaces and making sure that we don't have headers all over. 2019-07-09 19:24:08 -04:00
Daniel Lemire 977f57fd37 We need to guard the simdutf8check files. 2019-07-09 16:53:28 -04:00
Daniel Lemire a1ea37c336 Updating the amalgamation. 2019-07-09 15:21:07 -04:00
ioioioio 7369339c88 Neon utf8validation (#207)
* utf8 validation on neon works
2019-07-09 15:14:34 -04:00
Daniel Lemire 8ace2ba194 tput may fail. (#210) 2019-07-09 13:24:50 -04:00
Daniel Lemire 3f79385160 Removing some fprintf. (#209) 2019-07-09 13:04:44 -04:00
Daniel Lemire 14ee003907 Silencing warning in instances with char is unsigned. (#208)
* Silencing warning in instances with char is unsigned.

* Damn it.
2019-07-09 12:07:28 -04:00
ioioioio 3bd3116cf8 Merge pull request #202 from lemire/sse_integration
SSE integration (PR#139)
2019-07-05 12:25:06 -04:00
ioioioio a1f692408d Adding Sunny Gleason to the contributors list 2019-07-05 11:38:32 -04:00
ioioioio b0d9c074e1 check_utf8_helper has a more meaningful name 2019-07-05 11:09:28 -04:00
Daniel Lemire 9238e15bb1 Update README.md 2019-07-04 21:31:00 -04:00
Daniel Lemire d7b9a29dc6 Adding comments. 2019-07-04 19:10:05 -04:00
Daniel Lemire 0c2f58e40c Extending the no-avx tests on circleci. 2019-07-04 17:58:45 -04:00
Daniel Lemire fba27ef4b9 I missed a few. Building up VS support. 2019-07-04 17:45:45 -04:00
Daniel Lemire 19cdc09928 Improving support for VS 2019-07-04 17:36:26 -04:00
Daniel Lemire 2b2d93b05f Various minor tweaks. 2019-07-04 17:19:05 -04:00
Daniel Lemire 36471c23ce Merge branch 'master' into sse_integration 2019-07-04 16:00:46 -04:00
Daniel Lemire bc88aaf8d7 Merge branch 'master' into sse_integration 2019-07-04 16:00:15 -04:00
Daniel Lemire f598a8666c Removing duplicate flag. 2019-07-04 15:59:43 -04:00
Daniel Lemire 85b5ccf5ae Removing swear word. 2019-07-04 15:57:51 -04:00
ioioioio a592199068 readme is aware of namespace (#206) 2019-07-04 11:50:35 -04:00
ioioioio f7ea2629e4 Fixing warnings and Microsoft intinsics. 2019-07-04 10:13:40 -04:00
ioioioio 83a9fa8913 Merge branch 'master' of https://github.com/lemire/simdjson into sse_integration 2019-07-04 09:56:51 -04:00
ioioioio 477b058f74 Merge pull request #201 from lemire/Multiple_implementation_refactoring_stage2
Stage2 refactored to simplify multiple implementations
2019-07-03 17:32:44 -04:00
ioioioio 861a6a17e4 SSE implementation integrated 2019-07-03 17:15:21 -04:00
ioioioio 0df6d83f08 deleting useless comments and namespace indications 2019-07-03 10:47:45 -04:00
ioioioio 036f9d5a45 Merge branch 'master' of https://github.com/lemire/simdjson into Multiple_implementation_refactoring_stage2 2019-07-03 10:34:58 -04:00
ioioioio 43143f6434 Merge pull request #200 from lemire/merging_namespace
Addition of a namespace
2019-07-03 09:50:44 -04:00
ioioioio 3f24879157 Stage2 refactored to simplify multiple implementations 2019-07-02 17:12:00 -04:00
ioioioio 40c098f78a updating single header 2019-07-02 16:37:05 -04:00
ioioioio b335af8507 Attemp to solve some singleheader's problems 2019-07-02 16:24:56 -04:00
ioioioio 9a6a146183 Fixing amalgation.sh 2019-07-02 15:32:48 -04:00
ioioioio 9230588ce8 conflicts are solved 2019-07-02 15:21:00 -04:00
ioioioio 78406ba954 Merge pull request #197 from lemire/multiple_implementation_refactoring
Partial refactoring (stage1) to facilitate multiple implementations.
2019-07-02 10:42:51 -04:00
Daniel Lemire aa78b70d69 Introducing a "native" instruction set so that you do not need to do #ifdef to select the right SIMD set all the time.
Fixing indentation.
Removing some obsolete WARN_UNUSED.
Fixing a weird warning with optind variable.
2019-07-01 14:18:30 -04:00
Daniel Lemire 1b81e7c928 Correcting ugly indentation. 2019-07-01 11:55:39 -04:00
ioioioio de08df6a7e Correction of identation. 2019-06-28 15:33:30 -04:00
ioioioio c2e4b8ca9a Merge branch 'master' of https://github.com/lemire/simdjson into HEAD 2019-06-28 15:18:05 -04:00
ioioioio 6723221a42 Refactoring stage1 to facilitate multiple implementations. 2019-06-28 15:14:42 -04:00
Daniel Lemire 5bd7fffb4c Link to actual users. 2019-06-27 19:16:42 -04:00
Daniel Lemire 471c71310b Various formatting issues in the tests directory. 2019-06-26 19:48:51 -04:00
Daniel Lemire 9e79acc25a Adding ack. 2019-06-26 14:16:32 -04:00
ioioioio cdb06a4c6e Merge pull request #196 from lemire/fixamalgamation
Adding missing cpp
2019-06-25 10:16:17 -04:00
ioioioio e1af3737f3 Adding missing cpp 2019-06-25 10:13:53 -04:00
Daniel Lemire d7f7f1b200 Fixing issue. (#193) 2019-06-20 18:49:47 -04:00
Daniel Lemire 8914b12db5 On ARM NEON, we need crypto support. 2019-06-19 16:45:09 -04:00
Kushal Kumaran 296777546c fix typo: padding_string (#192) 2019-06-18 16:53:21 -04:00
Daniel Lemire 3db8c5a0eb Fixing the issue (#191) 2019-06-12 16:32:46 -04:00
Daniel Lemire b0e6bfa84c Simpler iteration code (#190)
* Adding convenience method to simplify code.

* Simplifying the iteration code.
2019-06-12 16:29:24 -04:00
Daniel Lemire b1e8990654 Moving iterator functions in the header file (#189)
We want the compiler to inline hot functions in the iterators. Let us leave them in the header file. Please.
2019-06-11 21:09:58 -04:00
Daniel Lemire 463ef9b08f Adding reduced demo. 2019-06-05 21:41:13 -04:00
Daniel Lemire 14016743be fixing typo in comment. 2019-06-05 21:29:46 -04:00
Daniel Lemire 59194dcf4d Issue182: fixed (#183)
* Verifying issue 182.

* Fixing the corresponding bug.
2019-06-05 18:51:29 -04:00
Daniel Lemire b32c72f1fc Adding a new compile-time flag (SIMDJSON_NAIVE_STRUCTURAL) for research purposes. 2019-06-03 16:41:50 -04:00
Daniel Lemire e27a46973c Introducing a fallback for clmul. 2019-06-03 15:10:14 -04:00
Daniel Lemire 06461a465b Tweaking. 2019-06-03 14:25:22 -04:00
Daniel Lemire cf6f231be6 Allowing users to provide additional flags. 2019-06-03 14:17:42 -04:00
Daniel Lemire b1d5849bb5 Merge branch 'master' of github.com:lemire/simdjson 2019-06-03 13:46:08 -04:00
Daniel Lemire cdc75dec97 Adding GB/s to the table version of parse. 2019-06-03 13:45:34 -04:00
Daniel Lemire 9239f75123 Adding compile-time option to test the speed of the fast flatten (research-oriented). (#181) 2019-06-03 13:37:09 -04:00
Daniel Lemire f0bee2ac8b Ease diagnostic with GHz reporting. 2019-06-03 13:24:54 -04:00
Daniel Lemire 295e481a2e Getting more precise timings (avoiding the overhead of linux perf. counters). 2019-06-03 10:59:07 -04:00
Daniel Lemire 5aaca27cda Making it practical to benchmark large files. 2019-05-31 20:33:16 -04:00
Daniel Lemire f220c1e9eb Removing bogus doc. 2019-05-31 19:48:52 -04:00
Daniel Lemire 642132920f Fixing performance regression caused by helpful code contributions
that moved inlineable functions into the source file combined with
helpful compilers which aren't smart enough to do the inlinining in
any case.
2019-05-31 18:16:12 -04:00
Daniel Lemire 4e7e7d99cc We do not want to check unified_machine against a bool now that we return an integer. 2019-05-31 11:11:23 -04:00
Daniel Lemire ba8aa46cd0 Makefile: A git failure should not be considered a critical failure. 2019-05-29 12:03:07 -04:00
Daniel Lemire f00be30318 Being clearer as to what TAPE_ERROR means. 2019-05-28 19:32:56 -04:00
Daniel Lemire 6b5231f930 Just improving the look and feel of 'parsingcompetition'. 2019-05-24 20:08:06 -04:00
Daniel Lemire 2c7a9734af Updating parsingcompetition to the new API. 2019-05-24 19:28:21 -04:00
Daniel Lemire 8526387acb Improving error codes. (#176)
* This commit adds new error codes.
2019-05-24 17:28:56 -04:00
Daniel Lemire 17ac5c0525 This adds guards so that we can better detect the case where we have neither AVX2 nor ARM NEON. (#173) 2019-05-24 17:26:29 -04:00
Daniel Lemire bf82288ab1 Preventing implicit conversions for C strings to C++ strings (with evil results). (#172) 2019-05-21 13:32:23 -04:00
Daniel Lemire 2151ad7f34 Update CONTRIBUTORS
@alexey-milovidov spotted an issue we may not have otherwise detected.
2019-05-20 14:45:58 -04:00
alexey-milovidov 576914ed54 Remove MMX code (#170)
#169
2019-05-20 14:41:58 -04:00
Daniel Lemire 43dba8ac7f A slightly better "flatten"? (#166)
* This seems beneficial.
2019-05-19 12:33:45 -04:00
Daniel Lemire dcd0cb8080 Fix for https://github.com/lemire/simdjson/issues/58 (#168) 2019-05-19 12:25:27 -04:00
Daniel Lemire 47beaff152 Adding white-listing for memory sanitizer. 2019-05-19 11:18:54 -04:00
Daniel Lemire e4bae80f9b Merge branch 'master' into issue99 2019-05-16 15:03:26 -04:00
Daniel Lemire 1d531a9600 Fix for https://github.com/lemire/simdjson/issues/92 2019-05-16 15:01:58 -04:00
Dong Xie 14cd1f7a0b Fix Issue #164 (#165)
* Fix Issue #164.
2019-05-10 17:51:53 -04:00
Daniel Lemire 871fd20ee5 Fixing issue 99 (#163) 2019-05-09 21:20:14 -04:00
Daniel Lemire 58f0d81925 Fixing issue 99 2019-05-09 21:17:13 -04:00
Dong Xie b98454d213 Add explicit conversion for leading and tailing zeros. (#161) 2019-05-09 20:56:13 -04:00
235 changed files with 11007 additions and 7390 deletions
+6 -1
View File
@@ -9,9 +9,14 @@ clone_folder: c:\projects\simdjson
platform:
- x64
environment:
matrix:
- AVXFLAG: "OFF"
- AVXFLAG: "ON"
build_script:
- mkdir build
- cd build
- ps: cmake -DCMAKE_GENERATOR_PLATFORM=x64 ..
- ps: cmake -DSIMDJSON_DISABLE_AVX="$env:AVXFLAG" -DCMAKE_GENERATOR_PLATFORM=x64 ..
- cmake --build .
- ctest --verbose
+78
View File
@@ -38,6 +38,44 @@ jobs:
cd build
make test
"gccnoavx":
docker:
- image: ubuntu:18.04
environment:
CXX: g++-7
steps:
- checkout
- run: apt-get update -qq
- run: >
apt-get install -y
build-essential
cmake
g++-7
git
- run:
name: Building (gcc)
command: ARCHFLAGS="-march=nehalem" make
- run:
name: Running tests (gcc)
command: ARCHFLAGS="-march=nehalem" make quiettest amalgamate
- run:
name: Building (gcc, cmake)
command: |
mkdir build
cd build
cmake -DSIMDJSON_DISABLE_AVX=on ..
make
- run:
name: Running tests (gcc, cmake)
command: |
cd build
make test
"clang":
docker:
- image: ubuntu:18.04
@@ -76,9 +114,49 @@ jobs:
cd build
make test
"clangnoavx":
docker:
- image: ubuntu:18.04
environment:
CXX: clang++-6.0
steps:
- checkout
- run: apt-get update -qq
- run: >
apt-get install -y
build-essential
cmake
clang-6.0
git
- run:
name: Building (clang)
command: ARCHFLAGS="-march=nehalem" make
- run:
name: Running tests (clang)
command: ARCHFLAGS="-march=nehalem" make quiettest amalgamate
- run:
name: Building (clang, cmake)
command: |
mkdir build
cd build
cmake -DSIMDJSON_DISABLE_AVX=on ..
make
- run:
name: Running tests (clang, cmake)
command: |
cd build
make test
workflows:
version: 2
build_and_test:
jobs:
- "clang"
- "gcc"
- "clangnoavx"
- "gccnoavx"
+1
View File
@@ -0,0 +1 @@
BasedOnStyle: LLVM
+125 -3
View File
@@ -1,10 +1,132 @@
kind: pipeline
name: default
name: x64
platform:
os: linux
arch: amd64
steps:
- name: test
image: gcc:8
commands:
- make -j2
- make quiettest -j2
- make
- make quiettest
- make amalgamate
---
kind: pipeline
name: arm64
platform:
os: linux
arch: arm64
steps:
- name: test
image: gcc:8
commands:
- make
- make quiettest
- make amalgamate
---
kind: pipeline
name: stylecheck
platform:
os: linux
arch: amd64
steps:
- name: Build and Test
image: ubuntu:18.04
commands:
- apt-get update -y
- apt-get install -y python clang-format
- ./style/run-clang-format.py -r include/ benchmark/ src/ tests/
---
kind: pipeline
name: amd64_clang_cmake
platform:
os: linux
arch: amd64
steps:
- name: Build and Test
image: ubuntu:18.04
environment:
CC: clang
CXX: clang++
commands:
- apt-get update -y
- apt-get install -y make $CC g++ cmake
- $CC --version
- mkdir build && cd build
- cmake $CMAKE_FLAGS ..
- make -j
- ctest
---
kind: pipeline
name: amd64_gcc_cmake
platform:
os: linux
arch: amd64
steps:
- name: Build and Test
image: ubuntu:18.04
environment:
CC: gcc
CXX: g++
commands:
- apt-get update -y
- apt-get install -y make $CC g++ cmake
- $CC --version
- mkdir build && cd build
- cmake $CMAKE_FLAGS ..
- make -j
- ctest
---
kind: pipeline
name: arm64_clang_cmake
platform:
os: linux
arch: arm64
steps:
- name: Build and Test
image: ubuntu:18.04
environment:
CC: clang
CXX: clang++
commands:
- apt-get update -y
- apt-get install -y make $CC g++ cmake
- $CC --version
- mkdir build && cd build
- cmake $CMAKE_FLAGS ..
- make -j
- ctest
---
kind: pipeline
name: arm64_gcc_cmake
platform:
os: linux
arch: arm64
steps:
- name: Build and Test
image: ubuntu:18.04
environment:
CC: gcc
CXX: g++
commands:
- apt-get update -y
- apt-get install -y make $CC g++ cmake
- $CC --version
- mkdir build && cd build
- cmake $CMAKE_FLAGS ..
- make -j
- ctest
+10 -1
View File
@@ -7,7 +7,8 @@ addons:
packages:
- gcc-7
- g++-7
- clang-format
- python
branches:
only:
- master
@@ -17,5 +18,13 @@ script:
- export CC=gcc-7
- make
- make test
- make everything
- make amalgamate
- make clean
- make SANITIZEGOLD=1 test
- make clean
- ARCHFLAGS="-march=nehalem" make
- ARCHFLAGS="-march=nehalem" make test
- ARCHFLAGS="-march=nehalem" make everything
- ./style/run-clang-format.py -r include/ benchmark/ src/ tests/
+13 -6
View File
@@ -1,4 +1,13 @@
cmake_minimum_required(VERSION 3.8...3.13)
cmake_minimum_required(VERSION 3.9) # CMP0069 NEW
include(CheckIPOSupported)
check_ipo_supported(RESULT ltoresult)
if(ltoresult)
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
endif()
# usage: cmake -DSIMDJSON_DISABLE_AVX=on ..
option(SIMDJSON_DISABLE_AVX "Forcefully disable AVX even if hardware supports it" OFF)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_MACOSX_RPATH OFF)
@@ -10,9 +19,9 @@ endif()
project(simdjson)
set(SIMDJSON_LIB_NAME simdjson)
set(PROJECT_VERSION_MAJOR 0)
set(PROJECT_VERSION_MINOR 1)
set(PROJECT_VERSION_PATCH 2)
set(SIMDJSON_LIB_VERSION "0.1.2" CACHE STRING "simdjson library version")
set(PROJECT_VERSION_MINOR 2)
set(PROJECT_VERSION_PATCH 0)
set(SIMDJSON_LIB_VERSION "0.2.0" CACHE STRING "simdjson library version")
set(SIMDJSON_LIB_SOVERSION "0" CACHE STRING "simdjson library soversion")
if(NOT MSVC)
@@ -20,14 +29,12 @@ option(SIMDJSON_BUILD_STATIC "Build a static library" OFF) # turning it on disab
else()
option(SIMDJSON_BUILD_STATIC "Build a static library" ON) # turning it on disables the production of a dynamic library
endif()
option(SIMDJSON_BUILD_LTO "Build library with Link Time Optimization" OFF)
option(SIMDJSON_SANITIZE "Sanitize addresses" OFF)
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/tools/cmake")
find_package(CTargets)
find_package(Options)
find_package(LTO)
install(DIRECTORY include/${SIMDJSON_LIB_NAME} DESTINATION include)
set (TEST_DATA_DIR "${CMAKE_CURRENT_SOURCE_DIR}/jsonchecker/")
+27
View File
@@ -0,0 +1,27 @@
# contributors (in no particular order)
Thomas Navennec
Kai Wolf
Tyler Kennedy
Frank Wessels
George Fotopoulos
Heinz N. Gies
Emil Gedda
Wojciech Muła
Georgios Floros
Dong Xie
Nan Xiao
Egor Bogatov
Jinxi Wang
Luiz Fernando Peres
Wouter Bolsterlee
Anish Karandikar
Reini Urban
Tom Dyson
Ihor Dotsenko
Alexey Milovidov
Chang Liu
Sunny Gleason
John Keiser
Zach Bjornson
# if you have contributed to the project and your name does not
# appear in this list, please let us know!
+10
View File
@@ -0,0 +1,10 @@
# docker build -t simdjson . && docker run --privileged -t simdjson
FROM gcc:8.3
COPY . /usr/src/
WORKDIR /usr/src/
RUN make clean
RUN make amalgamate
RUN make
RUN make test
RUN make parsingcompetition
CMD ["bash", "scripts/selectparser.sh"]
+47 -22
View File
@@ -7,8 +7,23 @@
.PHONY: clean cleandist
COREDEPSINCLUDE = -Idependencies/rapidjson/include -Idependencies/sajson/include -Idependencies/cJSON -Idependencies/jsmn
EXTRADEPSINCLUDE = -Idependencies/jsoncppdist -Idependencies/json11 -Idependencies/fastjson/src -Idependencies/fastjson/include -Idependencies/gason/src -Idependencies/ujson4c/3rdparty -Idependencies/ujson4c/src
CXXFLAGS = -std=c++17 -march=native -Wall -Wextra -Wshadow -Iinclude -Ibenchmark/linux
CFLAGS = -march=native -Idependencies/ujson4c/3rdparty -Idependencies/ujson4c/src
# users can provide their own additional flags with make EXTRAFLAGS=something
architecture:=$(shell arch)
####
# If you want to specify your own target architecture,
# then define ARCHFLAGS. Otherwise, we set good default.
# E.g., type ' ARCHFLAGS="-march=nehalem" make parse '
###
ifeq ($(architecture),aarch64)
ARCHFLAGS ?= -march=armv8-a+crc+crypto
else
ARCHFLAGS ?= -msse4.2 -mpclmul # lowest supported feature set?
endif
CXXFLAGS = $(ARCHFLAGS) -std=c++17 -Wall -Wextra -Wshadow -Iinclude -Ibenchmark/linux $(EXTRAFLAGS)
CFLAGS = $(ARCHFLAGS) -Idependencies/ujson4c/3rdparty -Idependencies/ujson4c/src $(EXTRAFLAGS)
# This is a convenience flag
ifdef SANITIZEGOLD
@@ -23,6 +38,10 @@ endif
# SANITIZE *implies* DEBUG
ifeq ($(MEMSANITIZE),1)
CXXFLAGS += -g3 -O0 -fsanitize=memory -fno-omit-frame-pointer -fsanitize=undefined
CFLAGS += -g3 -O0 -fsanitize=memory -fno-omit-frame-pointer -fsanitize=undefined
else
ifeq ($(SANITIZE),1)
CXXFLAGS += -g3 -O0 -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined
CFLAGS += -g3 -O0 -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined
@@ -36,14 +55,15 @@ else
CFLAGS += -O3
endif # ifeq ($(DEBUG),1)
endif # ifeq ($(SANITIZE),1)
endif # ifeq ($(MEMSANITIZE),1)
MAINEXECUTABLES=parse minify json2json jsonstats statisticalmodel
TESTEXECUTABLES=jsoncheck numberparsingcheck stringparsingcheck
MAINEXECUTABLES=parse minify json2json jsonstats statisticalmodel jsonpointer
TESTEXECUTABLES=jsoncheck numberparsingcheck stringparsingcheck pointercheck
COMPARISONEXECUTABLES=minifiercompetition parsingcompetition parseandstatcompetition distinctuseridcompetition allparserscheckfile allparsingcompetition
SUPPLEMENTARYEXECUTABLES=parse_noutf8validation parse_nonumberparsing parse_nostringparsing
HEADERS= include/simdjson/simdutf8check.h include/simdjson/stringparsing.h include/simdjson/numberparsing.h include/simdjson/jsonparser.h include/simdjson/common_defs.h include/simdjson/jsonioutil.h benchmark/benchmark.h benchmark/linux/linux-perf-events.h include/simdjson/parsedjson.h include/simdjson/stage1_find_marks.h include/simdjson/stage2_build_tape.h include/simdjson/jsoncharutils.h include/simdjson/jsonformatutils.h
LIBFILES=src/jsonioutil.cpp src/jsonparser.cpp src/simdjson.cpp src/stage1_find_marks.cpp src/stage2_build_tape.cpp src/parsedjson.cpp src/parsedjsoniterator.cpp
HEADERS= include/simdjson/simdutf8check_haswell.h include/simdjson/simdutf8check_westmere.h include/simdjson/simdutf8check_arm64.h include/simdjson/stringparsing.h include/simdjson/stringparsing_arm64.h include/simdjson/stringparsing_haswell.h include/simdjson/stringparsing_macros.h include/simdjson/stringparsing_westmere.h include/simdjson/numberparsing.h include/simdjson/jsonparser.h include/simdjson/common_defs.h include/simdjson/jsonioutil.h benchmark/benchmark.h benchmark/linux/linux-perf-events.h include/simdjson/parsedjson.h include/simdjson/stage1_find_marks.h include/simdjson/stage1_find_marks_arm64.h include/simdjson/stage1_find_marks_haswell.h include/simdjson/stage1_find_marks_westmere.h include/simdjson/stage1_find_marks_macros.h include/simdjson/stage2_build_tape.h include/simdjson/jsoncharutils.h include/simdjson/jsonformatutils.h include/simdjson/stage1_find_marks_flatten.h include/simdjson/stage1_find_marks_flatten_haswell.h
LIBFILES=src/jsonioutil.cpp src/jsonparser.cpp src/simdjson.cpp src/stage1_find_marks.cpp src/stage2_build_tape.cpp src/parsedjson.cpp src/parsedjsoniterator.cpp
MINIFIERHEADERS=include/simdjson/jsonminifier.h include/simdjson/simdprune_tables.h
MINIFIERLIBFILES=src/jsonminifier.cpp
@@ -71,32 +91,32 @@ benchmark:
bash ./scripts/parser.sh
bash ./scripts/parseandstat.sh
test: jsoncheck numberparsingcheck stringparsingcheck basictests allparserscheckfile minify json2json
test: jsoncheck numberparsingcheck stringparsingcheck basictests allparserscheckfile minify json2json pointercheck
./basictests
./numberparsingcheck
./stringparsingcheck
./jsoncheck
./pointercheck
./scripts/testjson2json.sh
./scripts/issue150.sh
@echo
@tput setaf 2
@echo "It looks like the code is good!"
@tput sgr0
quiettest: jsoncheck numberparsingcheck stringparsingcheck basictests allparserscheckfile minify json2json
quiettest: jsoncheck numberparsingcheck stringparsingcheck basictests allparserscheckfile minify json2json pointercheck
./basictests
./numberparsingcheck
./stringparsingcheck
./jsoncheck
./pointercheck
./scripts/testjson2json.sh
./scripts/issue150.sh
amalgamate:
./amalgamation.sh
$(CXX) $(CXXFLAGS) -o singleheader/demo ./singleheader/amalgamation_demo.cpp -Isingleheader
submodules:
git submodule update --init --recursive
-git submodule update --init --recursive
-touch submodules
$(SAJSON_INCLUDE) $(RAPIDJSON_INCLUDE) $(JSON11_INCLUDE) $(FASTJSON_INCLUDE) $(GASON_INCLUDE) $(UJSON4C_INCLUDE) $(CJSON_INCLUDE) $(JSMN_INCLUDE) : submodules
@@ -131,8 +151,10 @@ numberparsingcheck:tests/numberparsingcheck.cpp $(HEADERS) $(LIBFILES)
stringparsingcheck:tests/stringparsingcheck.cpp $(HEADERS) $(LIBFILES)
$(CXX) $(CXXFLAGS) -o stringparsingcheck tests/stringparsingcheck.cpp src/jsonioutil.cpp src/jsonparser.cpp src/simdjson.cpp src/stage1_find_marks.cpp src/parsedjson.cpp -I. $(LIBFLAGS) -DJSON_TEST_STRINGS
pointercheck:tests/pointercheck.cpp $(HEADERS) $(LIBFILES)
$(CXX) $(CXXFLAGS) -o pointercheck tests/pointercheck.cpp src/stage2_build_tape.cpp src/jsonioutil.cpp src/jsonparser.cpp src/simdjson.cpp src/stage1_find_marks.cpp src/parsedjson.cpp src/parsedjsoniterator.cpp -I. $(LIBFLAGS)
minifiercompetition: benchmark/minifiercompetition.cpp $(HEADERS) $(LIBS) $(MINIFIERHEADERS) $(LIBFILES) $(MINIFIERLIBFILES)
minifiercompetition: benchmark/minifiercompetition.cpp $(HEADERS) submodules $(MINIFIERHEADERS) $(LIBFILES) $(MINIFIERLIBFILES)
$(CXX) $(CXXFLAGS) -o minifiercompetition $(LIBFILES) $(MINIFIERLIBFILES) benchmark/minifiercompetition.cpp -I. $(LIBFLAGS) $(COREDEPSINCLUDE)
minify: tools/minify.cpp $(HEADERS) $(MINIFIERHEADERS) $(LIBFILES) $(MINIFIERLIBFILES)
@@ -141,29 +163,32 @@ minify: tools/minify.cpp $(HEADERS) $(MINIFIERHEADERS) $(LIBFILES) $(MINIFIERLIB
json2json: tools/json2json.cpp $(HEADERS) $(LIBFILES)
$(CXX) $(CXXFLAGS) -o json2json $ tools/json2json.cpp $(LIBFILES) -I.
jsonpointer: tools/jsonpointer.cpp $(HEADERS) $(LIBFILES)
$(CXX) $(CXXFLAGS) -o jsonpointer $ tools/jsonpointer.cpp $(LIBFILES) -I.
jsonstats: tools/jsonstats.cpp $(HEADERS) $(LIBFILES)
$(CXX) $(CXXFLAGS) -o jsonstats $ tools/jsonstats.cpp $(LIBFILES) -I.
ujdecode.o: $(UJSON4C_INCLUDE)
$(CC) $(CFLAGS) -c dependencies/ujson4c/src/ujdecode.c
parseandstatcompetition: benchmark/parseandstatcompetition.cpp $(HEADERS) $(LIBFILES) $(LIBS)
parseandstatcompetition: benchmark/parseandstatcompetition.cpp $(HEADERS) $(LIBFILES) submodules
$(CXX) $(CXXFLAGS) -o parseandstatcompetition $(LIBFILES) benchmark/parseandstatcompetition.cpp -I. $(LIBFLAGS) $(COREDEPSINCLUDE)
distinctuseridcompetition: benchmark/distinctuseridcompetition.cpp $(HEADERS) $(LIBFILES) $(LIBS)
distinctuseridcompetition: benchmark/distinctuseridcompetition.cpp $(HEADERS) $(LIBFILES) submodules
$(CXX) $(CXXFLAGS) -o distinctuseridcompetition $(LIBFILES) benchmark/distinctuseridcompetition.cpp -I. $(LIBFLAGS) $(COREDEPSINCLUDE)
parsingcompetition: benchmark/parsingcompetition.cpp $(HEADERS) $(LIBFILES) $(LIBS)
parsingcompetition: benchmark/parsingcompetition.cpp $(HEADERS) $(LIBFILES) submodules
$(CXX) $(CXXFLAGS) -o parsingcompetition $(LIBFILES) benchmark/parsingcompetition.cpp -I. $(LIBFLAGS) $(COREDEPSINCLUDE)
allparsingcompetition: benchmark/parsingcompetition.cpp $(HEADERS) $(LIBFILES) $(EXTRAOBJECTS) $(LIBS)
allparsingcompetition: benchmark/parsingcompetition.cpp $(HEADERS) $(LIBFILES) $(EXTRAOBJECTS) submodules
$(CXX) $(CXXFLAGS) -o allparsingcompetition $(LIBFILES) benchmark/parsingcompetition.cpp $(EXTRAOBJECTS) -I. $(LIBFLAGS) $(COREDEPSINCLUDE) $(EXTRADEPSINCLUDE) -DALLPARSER
allparserscheckfile: tests/allparserscheckfile.cpp $(HEADERS) $(LIBFILES) $(EXTRAOBJECTS) $(LIBS)
allparserscheckfile: tests/allparserscheckfile.cpp $(HEADERS) $(LIBFILES) $(EXTRAOBJECTS) submodules
$(CXX) $(CXXFLAGS) -o allparserscheckfile $(LIBFILES) tests/allparserscheckfile.cpp $(EXTRAOBJECTS) -I. $(LIBFLAGS) $(COREDEPSINCLUDE) $(EXTRADEPSINCLUDE)
.PHONY: submodules clean cppcheck cleandist
.PHONY: clean cppcheck cleandist
cppcheck:
cppcheck --enable=all src/*.cpp benchmarks/*.cpp tests/*.cpp -Iinclude -I. -Ibenchmark/linux
@@ -171,7 +196,7 @@ cppcheck:
everything: $(MAINEXECUTABLES) $(EXTRA_EXECUTABLES) $(TESTEXECUTABLES) $(COMPARISONEXECUTABLES) $(SUPPLEMENTARYEXECUTABLES)
clean:
rm -f $(EXTRAOBJECTS) $(MAINEXECUTABLES) $(EXTRA_EXECUTABLES) $(TESTEXECUTABLES) $(COMPARISONEXECUTABLES) $(SUPPLEMENTARYEXECUTABLES)
rm -f submodules $(EXTRAOBJECTS) $(MAINEXECUTABLES) $(EXTRA_EXECUTABLES) $(TESTEXECUTABLES) $(COMPARISONEXECUTABLES) $(SUPPLEMENTARYEXECUTABLES)
cleandist:
rm -f $(EXTRAOBJECTS) $(MAINEXECUTABLES) $(EXTRA_EXECUTABLES) $(TESTEXECUTABLES) $(COMPARISONEXECUTABLES) $(SUPPLEMENTARYEXECUTABLES)
rm -f submodules $(EXTRAOBJECTS) $(MAINEXECUTABLES) $(EXTRA_EXECUTABLES) $(TESTEXECUTABLES) $(COMPARISONEXECUTABLES) $(SUPPLEMENTARYEXECUTABLES)
+99 -62
View File
@@ -3,6 +3,7 @@
[![CircleCI](https://circleci.com/gh/lemire/simdjson.svg?style=svg)](https://circleci.com/gh/lemire/simdjson)
[![Build Status](https://img.shields.io/appveyor/ci/lemire/simdjson.svg)](https://ci.appveyor.com/project/lemire/simdjson)
[![][license img]][license]
[![Code Quality: Cpp](https://img.shields.io/lgtm/grade/cpp/g/lemire/simdjson.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/lemire/simdjson/context:cpp)
## A C++ library to see how fast we can parse JSON with complete validation.
@@ -12,6 +13,11 @@ JSON documents are everywhere on the Internet. Servers spend a lot of time parsi
<img src="images/logo.png" width="10%">
## Real-world usage
- [Microsoft FishStore](https://github.com/microsoft/FishStore)
- [Yandex ClickHouse](https://github.com/yandex/ClickHouse)
## Paper
A description of the design and implementation of simdjson appears at https://arxiv.org/abs/1902.08318 and an informal blog post providing some background and context is at https://branchfree.org/2019/02/25/paper-parsing-gigabytes-of-json-per-second/.
@@ -46,7 +52,10 @@ On a Skylake processor, the parsing speeds (in GB/s) of various processors on th
## Requirements
- We support platforms like Linux or macOS, as well as Windows through Visual Studio 2017 or later.
- A processor with AVX2 (i.e., Intel processors starting with the Haswell microarchitecture released 2013 and AMD processors starting with the Zen microarchitecture released 2017).
- A processor with
- AVX2 (i.e., Intel processors starting with the Haswell microarchitecture released 2013 and AMD processors starting with the Zen microarchitecture released 2017),
- or SSE 4.2 and CLMUL (i.e., Intel processors going back to Westmere released in 2010 or AMD processors starting with the Jaguar used in the PS4 and XBox One)
- or a 64-bit ARM processor (ARMv8-A): this covers a wide range of mobile processors, including all Apple processors currently available for sale, going back as far back as the iPhone 5s (2013).
- A recent C++ compiler (e.g., GNU GCC or LLVM CLANG or Visual Studio 2017), we assume C++17. GNU GCC 7 or better or LLVM's clang 6 or better.
- Some benchmark scripts assume bash and other common utilities, but they are optional.
@@ -56,10 +65,13 @@ This code is made available under the Apache License 2.0.
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.
## Code example
## Code usage and example
The main API involves populating a `ParsedJson` object which hosts a fully navigable document-object-model (DOM) view of the JSON document. The DOM can be accessed using [JSON Pointer](https://tools.ietf.org/html/rfc6901) paths, for example. The main function is `json_parse` which takes a string containing the JSON document as well as a reference to pre-allocated `ParsedJson` object (which can be reused multiple time). Once you have populated the `ParsedJson` object you can navigate through the DOM with an iterator (e.g., created by `ParsedJson::Iterator pjh(pj)`, see 'Navigating the parsed document').
```C
#include "simdjson/jsonparser.h"
using namespace simdjson;
/...
@@ -68,12 +80,12 @@ const char * filename = ... //
// use whatever means you want to get a string (UTF-8) of your JSON document
padded_string p = get_corpus(filename);
ParsedJson pj;
pj.allocateCapacity(p.size()); // allocate memory for parsing up to p.size() bytes
pj.allocate_capacity(p.size()); // allocate memory for parsing up to p.size() bytes
const int res = json_parse(p, pj); // do the parsing, return 0 on success
// parsing is done!
if (res != 0) {
// You can use the "simdjson/simdjson.h" header to access the error message
std::cout << "Error parsing:" << simdjson::errorMsg(res) << std::endl;
std::cout << "Error parsing:" << simdjson::error_message(res) << std::endl;
}
// the ParsedJson document can be used here
// pj can be reused with other json_parse calls.
@@ -84,14 +96,16 @@ of memory allocation with each new JSON document:
```C
#include "simdjson/jsonparser.h"
using namespace simdjson;
/...
const char * filename = ... //
padding_string p = get_corpus(filename);
padded_string p = get_corpus(filename);
ParsedJson pj = build_parsed_json(p); // do the parsing
if( ! pj.isValid() ) {
if( ! pj.is_valid() ) {
// something went wrong
std::cout << pj.get_error_message() << std::endl;
}
```
@@ -100,17 +114,18 @@ Though the `padded_string` class is recommended for best performance, you can ca
```C
#include "simdjson/jsonparser.h"
using namespace simdjson;
/...
std::string mystring = ... //
ParsedJson pj;
pj.allocateCapacity(mystring.size()); // allocate memory for parsing up to p.size() bytes
pj.allocate_capacity(mystring.size()); // allocate memory for parsing up to p.size() bytes
// std::string may not overallocate so a copy will be needed
const int res = json_parse(mystring, pj); // do the parsing, return 0 on success
// parsing is done!
if (res != 0) {
// You can use the "simdjson/simdjson.h" header to access the error message
std::cout << "Error parsing:" << simdjson::errorMsg(res) << std::endl;
std::cout << "Error parsing:" << simdjson::error_message(res) << std::endl;
}
// pj can be reused with other json_parse calls.
```
@@ -119,14 +134,16 @@ or
```C
#include "simdjson/jsonparser.h"
using namespace simdjson;
/...
std::string mystring = ... //
// std::string may not overallocate so a copy will be needed
ParsedJson pj = build_parsed_json(mystring); // do the parsing
if( ! pj.isValid() ) {
if( ! pj.is_valid() ) {
// something went wrong
std::cout << pj.get_error_message() << std::endl;
}
```
@@ -142,12 +159,14 @@ copy the files in your project in your include path. You can then include them q
#include <iostream>
#include "simdjson.h"
#include "simdjson.cpp"
using namespace simdjson;
int main(int argc, char *argv[]) {
const char * filename = argv[1];
padded_string p = get_corpus(filename);
ParsedJson pj = build_parsed_json(p); // do the parsing
if( ! pj.isValid() ) {
if( ! pj.is_valid() ) {
std::cout << "not valid" << std::endl;
std::cout << pj.get_error_message() << std::endl;
} else {
std::cout << "valid" << std::endl;
}
@@ -155,16 +174,19 @@ int main(int argc, char *argv[]) {
}
```
We require hardware support for AVX2 instructions. You have to make sure that you instruct your
compiler to use these instructions as needed. Under compilers such as GNU GCC or LLVM clang, the
flag `-march=native` used on a recent Intel processor (Haswell or better) is sufficient. For portability
of the binary files you can also specify directly the Haswell processor (`-march=haswell`). You may
also use the flags `-mavx2 -mbmi2`. Under Visual Studio, you need to target x64 and add the
flag `/arch:AVX2`.
Note: In some settings, it might be desirable to precompile `simdjson.cpp` instead of including it.
## Runtime dispatch
On Intel and AMD processors, we get best performance by using the hardware support for AVX2 instructions. However, simdjson also
runs on older Intel and AMD processors. We require a minimum feature support of SSE 4.2 and CLMUL (2010 Intel Westmere or better).
The code automatically detects the feature set of your processor and switches to the right function at runtime (a technical
sometimes called runtime dispatch).
We also support 64-bit ARM. We assume NEON support, and if the cryptographic extension is available, we leverage it, at compile-time.
There is no runtime dispatch on ARM.
## Usage (old-school Makefile on platforms like Linux or macOS)
@@ -247,14 +269,15 @@ make test
## Usage (CMake on Windows using Visual Studio)
We assume you have a common Windows PC with at least Visual Studio 2017 and an x64 processor with AVX2 support (2013 Intel Haswell or later).
We assume you have a common Windows PC with at least Visual Studio 2017 and an x64 processor with AVX2 support (2013 Intel Haswell or later) or SSE 4.2 + CLMUL (2010 Westmere or later).
- Grab the simdjson code from GitHub, e.g., by cloning it using [GitHub Desktop](https://desktop.github.com/).
- Install [CMake](https://cmake.org/download/). When you install it, make sure to ask that `cmake` be made available from the command line. Please choose a recent version of cmake.
- Create a subdirectory within simdjson, such as `VisualStudio`.
- Using a shell, go to this newly created directory.
- Type `cmake -DCMAKE_GENERATOR_PLATFORM=x64 ..` in the shell while in the `VisualStudio` repository. (Alternatively, if you want to build a DLL, you may use the command line `cmake -DCMAKE_GENERATOR_PLATFORM=x64 -DSIMDJSON_BUILD_STATIC=OFF ..`.)
- This last command created a Visual Studio solution file in the newly created directory (e.g., `simdjson.sln`). Open this file in Visual Studio. You should now be able to build the project and run the tests. For example, in the `Solution Explorer` window (available from the `View` menu), right-click `ALL_BUILD` and select `Build`. To test the code, still in the `Solution Explorer` window, select `RUN_TESTS` and select `Build`.
- Type `cmake -DCMAKE_GENERATOR_PLATFORM=x64 ..` in the shell while in the `VisualStudio` repository. (Alternatively, if you want to build a DLL, you may use the command line `cmake -DCMAKE_GENERATOR_PLATFORM=x64 -DSIMDJSON_BUILD_STATIC=OFF ..`.)
- This last command (`cmake ...`) created a Visual Studio solution file in the newly created directory (e.g., `simdjson.sln`). Open this file in Visual Studio. You should now be able to build the project and run the tests. For example, in the `Solution Explorer` window (available from the `View` menu), right-click `ALL_BUILD` and select `Build`. To test the code, still in the `Solution Explorer` window, select `RUN_TESTS` and select `Build`.
## Usage (Using `vcpkg` on Windows, Linux and MacOS)
@@ -293,6 +316,7 @@ If you find the version of `simdjson` shipped with `vcpkg` is out-of-date, feel
- `json2json mydoc.json` parses the document, constructs a model and then dumps back the result to standard output.
- `json2json -d mydoc.json` parses the document, constructs a model and then dumps model (as a tape) to standard output. The tape format is described in the accompanying file `tape.md`.
- `minify mydoc.json` minifies the JSON document, outputting the result to standard output. Minifying means to remove the unneeded white space characters.
- `jsonpointer mydoc.json <jsonpath> <jsonpath> ... <jsonpath>` parses the document, constructs a model and then processes a series of [JSON Pointer paths](https://tools.ietf.org/html/rfc6901). The result is itself a JSON document.
## Scope
@@ -301,10 +325,8 @@ The parser builds a useful immutable (read-only) DOM (document-object model) whi
To simplify the engineering, we make some assumptions.
- We support UTF-8 (and thus ASCII), nothing else (no Latin, no UTF-16). We do not believe this is a genuine limitation, because we do not think there is any serious application that needs to process JSON data without an ASCII or UTF-8 encoding.
- We support UTF-8 (and thus ASCII), nothing else (no Latin, no UTF-16). We do not believe this is a genuine limitation, because we do not think there is any serious application that needs to process JSON data without an ASCII or UTF-8 encoding. If the UTF-8 contains a leading BOM, it should be omitted: the user is responsible for detecting and skipping the BOM; UTF-8 BOMs are discouraged.
- All strings in the JSON document may have up to 4294967295 bytes in UTF-8 (4GB). To enforce this constraint, we refuse to parse a document that contains more than 4294967295 bytes (4GB). This should accommodate most JSON documents.
- We assume AVX2 support, which is available in all recent mainstream x86 processors produced by AMD and Intel. No support for non-x86 processors is included, though it can be done. We plan to support ARM processors (help is invited).
- In cases of failure, we report a failure without any indication to the nature of the problem. (This can be easily improved without affecting performance.)
- As allowed by the specification, we allow repeated keys within an object (other parsers like sajson do the same).
- Performance is optimized for JSON documents spanning at least a tens kilobytes up to many megabytes: the performance issues with having to parse many tiny JSON documents or one truly enormous JSON document are different.
@@ -314,9 +336,13 @@ _We do not aim to provide a general-purpose JSON library._ A library like RapidJ
- The input string is unmodified. (Parsers like sajson and RapidJSON use the input string as a buffer.)
- We parse integers and floating-point numbers as separate types which allows us to support large 64-bit integers in [-9223372036854775808,9223372036854775808), like a Java `long` or a C/C++ `long long`. Among the parsers that differentiate between integers and floating-point numbers, not all support 64-bit integers. (For example, sajson rejects JSON files with integers larger than or equal to 2147483648. RapidJSON will parse a file containing an overly long integer like 18446744073709551616 as a floating-point number.) When we cannot represent exactly an integer as a signed 64-bit value, we reject the JSON document.
- We do full UTF-8 validation as part of the parsing. (Parsers like fastjson, gason and dropbox json11 do not do UTF-8 validation.)
- We support the full range of 64-bit floating-point numbers (binary64). The values range from ` std::numeric_limits<double>::lowest()` to `std::numeric_limits<double>::max()`, so from -1.7976e308 all the way to 1.7975e308. Extreme values (less or equal to -1e308, greater or equal to 1e308) are rejected: we refuse to parse the input document.
- We test for accurate float parsing with a bound on the [unit of least precision (ULP)](https://en.wikipedia.org/wiki/Unit_in_the_last_place) of one. Practically speaking, this implies 15 digits of accuracy or better.
- We do full UTF-8 validation as part of the parsing. (Parsers like fastjson, gason and dropbox json11 do not do UTF-8 validation. The sajson parser does incomplete UTF-8 validation, accepting code point
sequences like 0xb1 0x87.)
- We fully validate the numbers. (Parsers like gason and ultranjson will accept `[0e+]` as valid JSON.)
- We validate string content for unescaped characters. (Parsers like fastjson and ultrajson accept unescaped line breaks and tabs in strings.)
- We fully validate the white-space characters outside of the strings. Parsers like RapidJSON will accept JSON documents with null characters outside of strings.
## Architecture
@@ -325,13 +351,27 @@ The parser works in two stages:
- Stage 1. (Find marks) Identifies quickly structure elements, strings, and so forth. We validate UTF-8 encoding at that stage.
- Stage 2. (Structure building) Involves constructing a "tree" of sort (materialized as a tape) to navigate through the data. Strings and numbers are parsed at this stage.
## JSON Pointer
We can navigate the parsed JSON using JSON Pointers as per the [RFC6901 standard](https://tools.ietf.org/html/rfc6901).
You can build a tool (jsonpointer) to parse a JSON document and then issue an array of JSON Pointer queries:
```
make jsonpointer
./jsonpointer jsonexamples/small/demo.json /Image/Width /Image/Height /Image/IDs/2
./jsonpointer jsonexamples/twitter.json /statuses/0/id /statuses/1/id /statuses/2/id /statuses/3/id /statuses/4/id /statuses/5/id
```
In C++, given a `ParsedJson`, we can move to a node with the `move_to` method, passing a `std::string` representing the JSON Pointer query.
## Navigating the parsed document
Here is a code sample to dump back the parsed JSON to a string:
```c
ParsedJson::iterator pjh(pj);
if (!pjh.isOk()) {
ParsedJson::Iterator pjh(pj);
if (!pjh.is_ok()) {
std::cerr << " Could not iterate parsed result. " << std::endl;
return EXIT_FAILURE;
}
@@ -339,7 +379,7 @@ Here is a code sample to dump back the parsed JSON to a string:
//
// where compute_dump is :
void compute_dump(ParsedJson::iterator &pjh) {
void compute_dump(ParsedJson::Iterator &pjh) {
if (pjh.is_object()) {
std::cout << "{";
if (pjh.down()) {
@@ -377,46 +417,21 @@ void compute_dump(ParsedJson::iterator &pjh) {
The following function will find all user.id integers:
```C
void simdjson_traverse(std::vector<int64_t> &answer, ParsedJson::iterator &i) {
switch (i.get_type()) {
case '{':
if (i.down()) {
do {
bool founduser = equals(i.get_string(), "user");
i.next(); // move to value
if (i.is_object()) {
if (founduser && i.move_to_key("id")) {
void simdjson_scan(std::vector<int64_t> &answer, ParsedJson::Iterator &i) {
while(i.move_forward()) {
if(i.get_scope_type() == '{') {
bool found_user = (i.get_string_length() == 4) && (memcmp(i.get_string(), "user", 4) == 0);
i.move_to_value();
if(found_user) {
if(i.is_object() && i.move_to_key("id",2)) {
if (i.is_integer()) {
answer.push_back(i.get_integer());
}
i.up();
}
simdjson_traverse(answer, i);
} else if (i.is_array()) {
simdjson_traverse(answer, i);
}
} while (i.next());
i.up();
}
break;
case '[':
if (i.down()) {
do {
if (i.is_object_or_array()) {
simdjson_traverse(answer, i);
}
} while (i.next());
i.up();
}
break;
case 'l':
case 'd':
case 'n':
case 't':
case 'f':
default:
break;
}
}
}
}
}
```
@@ -443,12 +458,28 @@ make allparsingcompetition
./allparsingcompetition myfile.json
```
Both the `parsingcompetition` and `allparsingcompetition` tools take a `-t` flag which produces
a table-oriented output that can be conventiently parsed by other tools.
## Docker
One can run tests and benchmarks using docker. It especially makes sense under Linux. A privileged access may be needed to get performance counters.
```
git clone https://github.com/lemire/simdjson.git
cd simdjson
docker build -t simdjson .
docker run --privileged -t simdjson
```
## Other programming languages
We distinguish between "bindings" (which just wrap the C++ code) and a port to another programming language (which reimplements everything).
- [pysimdjson](https://github.com/TkTech/pysimdjson): Python bindings for the simdjson project.
- [simdjson-rs](https://github.com/Licenser/simdjson-rs): 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)
- [simdjson_nodejs](https://github.com/luizperes/simdjson_nodejs): Node.js bindings for the simdjson project.
- [simdjson_php](https://github.com/crazyxman/simdjson_php): PHP bindings for the simdjson project.
@@ -535,5 +566,11 @@ This helps as we redefine some new characters as pseudo-structural such as the c
- Yang, Shiyang. Validation of XML Document Based on Parallel Bit Stream Technology. Diss. Applied Sciences: School of Computing Science, 2013.
- N. Nakasato, "Implementation of a parallel tree method on a GPU", Journal of Computational Science, vol. 3, no. 3, pp. 132-141, 2012.
## Funding
The work is supported by the Natural Sciences and Engineering Research Council of Canada under grant number RGPIN-2017-03910.
[license]: LICENSE
[license img]: https://img.shields.io/badge/License-Apache%202-blue.svg
+25 -8
View File
@@ -13,6 +13,7 @@ AMAL_C="simdjson.cpp"
# order does not matter
ALLCFILES="
$SCRIPTPATH/src/simdjson.cpp
$SCRIPTPATH/src/jsonioutil.cpp
$SCRIPTPATH/src/jsonminifier.cpp
$SCRIPTPATH/src/jsonparser.cpp
@@ -25,19 +26,32 @@ $SCRIPTPATH/src/parsedjsoniterator.cpp
# order matters
ALLCHEADERS="
$SCRIPTPATH/include/simdjson/simdjson_version.h
$SCRIPTPATH/include/simdjson/simdjson.h
$SCRIPTPATH/include/simdjson/portability.h
$SCRIPTPATH/include/simdjson/isadetection.h
$SCRIPTPATH/include/simdjson/simdjson.h
$SCRIPTPATH/include/simdjson/common_defs.h
$SCRIPTPATH/include/simdjson/padded_string.h
$SCRIPTPATH/include/simdjson/jsoncharutils.h
$SCRIPTPATH/include/simdjson/jsonformatutils.h
$SCRIPTPATH/include/simdjson/jsonioutil.h
$SCRIPTPATH/include/simdjson/simdprune_tables.h
$SCRIPTPATH/include/simdjson/simdutf8check.h
$SCRIPTPATH/include/simdjson/simdutf8check_haswell.h
$SCRIPTPATH/include/simdjson/simdutf8check_westmere.h
$SCRIPTPATH/include/simdjson/simdutf8check_arm64.h
$SCRIPTPATH/include/simdjson/jsonminifier.h
$SCRIPTPATH/include/simdjson/parsedjson.h
$SCRIPTPATH/include/simdjson/stage1_find_marks.h
$SCRIPTPATH/include/simdjson/stage1_find_marks_flatten.h
$SCRIPTPATH/include/simdjson/stage1_find_marks_flatten_haswell.h
$SCRIPTPATH/include/simdjson/stage1_find_marks_macros.h
$SCRIPTPATH/include/simdjson/stage1_find_marks_westmere.h
$SCRIPTPATH/include/simdjson/stage1_find_marks_haswell.h
$SCRIPTPATH/include/simdjson/stage1_find_marks_arm64.h
$SCRIPTPATH/include/simdjson/stringparsing.h
$SCRIPTPATH/include/simdjson/stringparsing_macros.h
$SCRIPTPATH/include/simdjson/stringparsing_westmere.h
$SCRIPTPATH/include/simdjson/stringparsing_haswell.h
$SCRIPTPATH/include/simdjson/stringparsing_arm64.h
$SCRIPTPATH/include/simdjson/numberparsing.h
$SCRIPTPATH/include/simdjson/stage2_build_tape.h
$SCRIPTPATH/include/simdjson/jsonparser.h
@@ -100,10 +114,13 @@ cat <<< '
#include "simdjson.h"
#include "simdjson.cpp"
int main(int argc, char *argv[]) {
if(argc < 2) {
std::cerr << "Please specify a filename " << std::endl;
}
const char * filename = argv[1];
padded_string p = get_corpus(filename);
ParsedJson pj = build_parsed_json(p); // do the parsing
if( ! pj.isValid() ) {
simdjson::padded_string p = simdjson::get_corpus(filename);
simdjson::ParsedJson pj = simdjson::build_parsed_json(p); // do the parsing
if( ! pj.is_valid() ) {
std::cout << "not valid" << std::endl;
} else {
std::cout << "valid" << std::endl;
@@ -123,16 +140,16 @@ echo "Giving final instructions:"
CPPBIN=${DEMOCPP%%.*}
echo "Try :"
echo "c++ -march=native -O3 -std=c++17 -o ${CPPBIN} ${DEMOCPP} && ./${CPPBIN} ../jsonexamples/twitter.json "
echo "c++ -O3 -std=c++17 -o ${CPPBIN} ${DEMOCPP} && ./${CPPBIN} ../jsonexamples/twitter.json "
SINGLEHDR=$SCRIPTPATH/singleheader
echo "Copying files to $SCRIPTPATH/singleheader "
mkdir -p $SINGLEHDR
echo "c++ -march=native -O3 -std=c++17 -o ${CPPBIN} ${DEMOCPP} && ./${CPPBIN} ../jsonexamples/twitter.json " > $SINGLEHDR/README.md
echo "c++ -O3 -std=c++17 -o ${CPPBIN} ${DEMOCPP} && ./${CPPBIN} ../jsonexamples/twitter.json " > $SINGLEHDR/README.md
cp ${AMAL_C} ${AMAL_H} ${DEMOCPP} $SINGLEHDR
ls $SINGLEHDR
cd $SINGLEHDR && c++ -march=native -O3 -std=c++17 -o ${CPPBIN} ${DEMOCPP} && ./${CPPBIN} ../jsonexamples/twitter.json
cd $SINGLEHDR && c++ -O3 -std=c++17 -o ${CPPBIN} ${DEMOCPP} && ./${CPPBIN} ../jsonexamples/twitter.json
lowercase(){
echo "$1" | tr 'A-Z' 'a-z'
+10 -7
View File
@@ -18,7 +18,7 @@ const char *unitname = "cycles";
: \
: /* no read only */ \
"%rax", "%rbx", "%rcx", "%rdx" /* clobbers */ \
); \
); \
(cycles) = ((uint64_t)cyc_high << 32) | cyc_low; \
} while (0)
@@ -32,7 +32,7 @@ const char *unitname = "cycles";
: "=r"(cyc_high), "=r"(cyc_low) \
: /* no read only registers */ \
: "%rax", "%rbx", "%rcx", "%rdx" /* clobbers */ \
); \
); \
(cycles) = ((uint64_t)cyc_high << 32) | cyc_low; \
} while (0)
@@ -92,7 +92,7 @@ double diff(timespec start, timespec end) {
if (verbose) \
printf("%-40s\t: ", name); \
else \
printf("\"%s\"\t", name); \
printf("\"%-40s\"", name); \
fflush(NULL); \
uint64_t cycles_start, cycles_final, cycles_diff; \
uint64_t min_diff = (uint64_t)-1; \
@@ -106,7 +106,7 @@ double diff(timespec start, timespec end) {
clock_gettime(CLOCK_REALTIME, &time1); \
RDTSC_START(cycles_start); \
if (test != expected) { \
fprintf(stderr, "not expected (%d , %d )", (int)test, (int)expected); \
fprintf(stderr, "not expected (%d , %d )", (int)test, (int)expected); \
break; \
} \
RDTSC_STOP(cycles_final); \
@@ -128,11 +128,14 @@ double diff(timespec start, timespec end) {
double max_gb_per_s = \
((double)S) / ((min_sumclockdiff)*1000.0 * 1000.0 * 1000.0); \
if (verbose) \
printf(" %.3f %s per input byte (best) ", cycle_per_op, unitname); \
printf(" %7.3f %s per input byte (best) ", cycle_per_op, unitname); \
if (verbose) \
printf(" %.3f %s per input byte (avg) ", avg_cycle_per_op, unitname); \
printf(" %7.3f %s per input byte (avg) ", avg_cycle_per_op, unitname); \
if (verbose) \
printf(" %7.3f GB/s (error margin: %.3f GB/s)", max_gb_per_s, \
-avg_gb_per_s + max_gb_per_s); \
if (!verbose) \
printf(" %.3f %.3f %.3f %.3f ", cycle_per_op, \
printf(" %20.3f %20.3f %20.3f %20.3f ", cycle_per_op, \
avg_cycle_per_op - cycle_per_op, max_gb_per_s, \
-avg_gb_per_s + max_gb_per_s); \
printf("\n"); \
+145 -79
View File
@@ -30,61 +30,54 @@ void print_vec(const std::vector<int64_t> &v) {
std::cout << std::endl;
}
void simdjson_traverse(std::vector<int64_t> &answer, ParsedJson::iterator &i) {
switch (i.get_type()) {
case '{':
if (i.down()) {
do {
bool founduser = equals(i.get_string(), "user");
i.next(); // move to value
if (i.is_object()) {
if (founduser && i.move_to_key("id")) {
if (i.is_integer()) {
answer.push_back(i.get_integer());
}
i.up();
void simdjson_scan(std::vector<int64_t> &answer,
simdjson::ParsedJson::Iterator &i) {
while (i.move_forward()) {
if (i.get_scope_type() == '{') {
bool found_user = (i.get_string_length() == 4) &&
(memcmp(i.get_string(), "user", 4) == 0);
i.move_to_value();
if (found_user) {
if (i.is_object() && i.move_to_key("id", 2)) {
if (i.is_integer()) {
answer.push_back(i.get_integer());
}
simdjson_traverse(answer, i);
} else if (i.is_array()) {
simdjson_traverse(answer, i);
i.up();
}
} while (i.next());
i.up();
}
}
break;
case '[':
if (i.down()) {
do {
if (i.is_object_or_array()) {
simdjson_traverse(answer, i);
}
} while (i.next());
i.up();
}
break;
case 'l':
case 'd':
case 'n':
case 't':
case 'f':
default:
break;
}
}
std::vector<int64_t> simdjson_computestats(const padded_string &p) {
__attribute__((noinline)) std::vector<int64_t>
simdjson_just_dom(simdjson::ParsedJson &pj) {
std::vector<int64_t> answer;
ParsedJson pj = build_parsed_json(p);
if (!pj.isValid()) {
simdjson::ParsedJson::Iterator i(pj);
simdjson_scan(answer, i);
remove_duplicates(answer);
return answer;
}
__attribute__((noinline)) std::vector<int64_t>
simdjson_compute_stats(const simdjson::padded_string &p) {
std::vector<int64_t> answer;
simdjson::ParsedJson pj = simdjson::build_parsed_json(p);
if (!pj.is_valid()) {
return answer;
}
ParsedJson::iterator i(pj);
simdjson_traverse(answer, i);
simdjson::ParsedJson::Iterator i(pj);
simdjson_scan(answer, i);
remove_duplicates(answer);
return answer;
}
__attribute__((noinline)) bool
simdjson_just_parse(const simdjson::padded_string &p) {
simdjson::ParsedJson pj = simdjson::build_parsed_json(p);
bool answer = !pj.is_valid();
return answer;
}
void sajson_traverse(std::vector<int64_t> &answer, const sajson::value &node) {
using namespace sajson;
switch (node.get_type()) {
@@ -97,22 +90,26 @@ void sajson_traverse(std::vector<int64_t> &answer, const sajson::value &node) {
}
case TYPE_OBJECT: {
auto length = node.get_length();
// sajson has O(log n) find_object_key, but we still visit each node anyhow
// because we need to visit all values.
for (auto i = 0u; i < length; ++i) {
if (equals(node.get_object_key(i).data(), "user")) { // found a user!!!
auto uservalue = node.get_object_value(i); // get the value
if (uservalue.get_type() ==
auto key = node.get_object_key(i); // expected: sajson::string
bool found_user =
(key.length() == 4) && (memcmp(key.data(), "user", 4) == 0);
if (found_user) { // found a user!!!
auto user_value = node.get_object_value(i); // get the value
if (user_value.get_type() ==
TYPE_OBJECT) { // the value should be an object
auto uservaluelength = uservalue.get_length();
for (auto j = 0u; j < uservaluelength;
++j) { // go through the children
if (equals(uservalue.get_object_key(j).data(),
"id")) { // ah ah found id
auto v = uservalue.get_object_value(j);
if (v.get_type() == TYPE_INTEGER) { // check that it is an integer
answer.push_back(v.get_integer_value()); // record it!
} else if (v.get_type() == TYPE_DOUBLE) {
answer.push_back((int64_t)v.get_double_value()); // record it!
}
// now we know that we only need one value
auto user_value_length = user_value.get_length();
auto right_index =
user_value.find_object_key(sajson::string("id", 2));
if (right_index < user_value_length) {
auto v = user_value.get_object_value(right_index);
if (v.get_type() == TYPE_INTEGER) { // check that it is an integer
answer.push_back(v.get_integer_value()); // record it!
} else if (v.get_type() == TYPE_DOUBLE) {
answer.push_back((int64_t)v.get_double_value()); // record it!
}
}
}
@@ -133,13 +130,23 @@ void sajson_traverse(std::vector<int64_t> &answer, const sajson::value &node) {
}
}
std::vector<int64_t> sasjon_computestats(const padded_string &p) {
__attribute__((noinline)) std::vector<int64_t>
sasjon_just_dom(sajson::document &d) {
std::vector<int64_t> answer;
sajson_traverse(answer, d.get_root());
remove_duplicates(answer);
return answer;
}
__attribute__((noinline)) std::vector<int64_t>
sasjon_compute_stats(const simdjson::padded_string &p) {
std::vector<int64_t> answer;
char *buffer = (char *)malloc(p.size());
memcpy(buffer, p.data(), p.size());
auto d = sajson::parse(sajson::dynamic_allocation(),
sajson::mutable_string_view(p.size(), buffer));
if (!d.is_valid()) {
free(buffer);
return answer;
}
sajson_traverse(answer, d.get_root());
@@ -148,12 +155,25 @@ std::vector<int64_t> sasjon_computestats(const padded_string &p) {
return answer;
}
__attribute__((noinline)) bool
sasjon_just_parse(const simdjson::padded_string &p) {
char *buffer = (char *)malloc(p.size());
memcpy(buffer, p.data(), p.size());
auto d = sajson::parse(sajson::dynamic_allocation(),
sajson::mutable_string_view(p.size(), buffer));
bool answer = !d.is_valid();
free(buffer);
return answer;
}
void rapid_traverse(std::vector<int64_t> &answer, const rapidjson::Value &v) {
switch (v.GetType()) {
case kObjectType:
for (Value::ConstMemberIterator m = v.MemberBegin(); m != v.MemberEnd();
++m) {
if (equals(m->name.GetString(), "user")) {
bool found_user = (m->name.GetStringLength() == 4) &&
(memcmp(m->name.GetString(), "user", 4) == 0);
if (found_user) {
const rapidjson::Value &child = m->value;
if (child.GetType() == kObjectType) {
for (Value::ConstMemberIterator k = child.MemberBegin();
@@ -186,7 +206,16 @@ void rapid_traverse(std::vector<int64_t> &answer, const rapidjson::Value &v) {
}
}
std::vector<int64_t> rapid_computestats(const padded_string &p) {
__attribute__((noinline)) std::vector<int64_t>
rapid_just_dom(rapidjson::Document &d) {
std::vector<int64_t> answer;
rapid_traverse(answer, d);
remove_duplicates(answer);
return answer;
}
__attribute__((noinline)) std::vector<int64_t>
rapid_compute_stats(const simdjson::padded_string &p) {
std::vector<int64_t> answer;
char *buffer = (char *)malloc(p.size() + 1);
memcpy(buffer, p.data(), p.size());
@@ -194,6 +223,7 @@ std::vector<int64_t> rapid_computestats(const padded_string &p) {
rapidjson::Document d;
d.ParseInsitu<kParseValidateEncodingFlag>(buffer);
if (d.HasParseError()) {
free(buffer);
return answer;
}
rapid_traverse(answer, d);
@@ -202,15 +232,27 @@ std::vector<int64_t> rapid_computestats(const padded_string &p) {
return answer;
}
__attribute__((noinline)) bool
rapid_just_parse(const simdjson::padded_string &p) {
char *buffer = (char *)malloc(p.size() + 1);
memcpy(buffer, p.data(), p.size());
buffer[p.size()] = '\0';
rapidjson::Document d;
d.ParseInsitu<kParseValidateEncodingFlag>(buffer);
bool answer = d.HasParseError();
free(buffer);
return answer;
}
int main(int argc, char *argv[]) {
bool verbose = false;
bool justdata = false;
bool just_data = false;
int c;
while ((c = getopt(argc, argv, "vt")) != -1)
switch (c) {
case 't':
justdata = true;
just_data = true;
break;
case 'v':
verbose = true;
@@ -219,19 +261,22 @@ int main(int argc, char *argv[]) {
abort();
}
if (optind >= argc) {
std::cerr << "Using different parsers, we compute the content statistics of "
"JSON documents." << std::endl;
std::cerr
<< "Using different parsers, we compute the content statistics of "
"JSON documents."
<< std::endl;
std::cerr << "Usage: " << argv[0] << " <jsonfile>" << std::endl;
std::cerr << "Or " << argv[0] << " -v <jsonfile>" << std::endl;
exit(1);
}
const char *filename = argv[optind];
if (optind + 1 < argc) {
std::cerr << "warning: ignoring everything after " << argv[optind + 1] << std::endl;
std::cerr << "warning: ignoring everything after " << argv[optind + 1]
<< std::endl;
}
padded_string p;
simdjson::padded_string p;
try {
get_corpus(filename).swap(p);
simdjson::get_corpus(filename).swap(p);
} catch (const std::exception &e) { // caught by reference to base
std::cout << "Could not load the file " << filename << std::endl;
return EXIT_FAILURE;
@@ -247,17 +292,17 @@ int main(int argc, char *argv[]) {
std::cout << p.size() << " B ";
std::cout << std::endl;
}
std::vector<int64_t> s1 = simdjson_computestats(p);
std::vector<int64_t> s1 = simdjson_compute_stats(p);
if (verbose) {
printf("simdjson: ");
print_vec(s1);
}
std::vector<int64_t> s2 = rapid_computestats(p);
std::vector<int64_t> s2 = rapid_compute_stats(p);
if (verbose) {
printf("rapid: ");
print_vec(s2);
}
std::vector<int64_t> s3 = sasjon_computestats(p);
std::vector<int64_t> s3 = sasjon_compute_stats(p);
if (verbose) {
printf("sasjon: ");
print_vec(s3);
@@ -266,16 +311,37 @@ int main(int argc, char *argv[]) {
assert(s1 == s3);
size_t size = s1.size();
int repeat = 50;
int repeat = 500;
int volume = p.size();
if(justdata) {
printf("name cycles_per_byte cycles_per_byte_err gb_per_s gb_per_s_err \n");
if (just_data) {
printf(
"name cycles_per_byte cycles_per_byte_err gb_per_s gb_per_s_err \n");
}
BEST_TIME("simdjson ", simdjson_computestats(p).size(), size, , repeat,
volume, !justdata);
BEST_TIME("rapid ", rapid_computestats(p).size(), size, , repeat, volume,
!justdata);
BEST_TIME("sasjon ", sasjon_computestats(p).size(), size, , repeat, volume,
!justdata);
BEST_TIME("simdjson ", simdjson_compute_stats(p).size(), size, , repeat,
volume, !just_data);
BEST_TIME("rapid ", rapid_compute_stats(p).size(), size, , repeat, volume,
!just_data);
BEST_TIME("sasjon ", sasjon_compute_stats(p).size(), size, , repeat, volume,
!just_data);
BEST_TIME("simdjson (just parse) ", simdjson_just_parse(p), false, , repeat,
volume, !just_data);
BEST_TIME("rapid (just parse) ", rapid_just_parse(p), false, , repeat,
volume, !just_data);
BEST_TIME("sasjon (just parse) ", sasjon_just_parse(p), false, , repeat,
volume, !just_data);
simdjson::ParsedJson dsimdjson = simdjson::build_parsed_json(p);
BEST_TIME("simdjson (just dom) ", simdjson_just_dom(dsimdjson).size(), size,
, repeat, volume, !just_data);
char *buffer = (char *)malloc(p.size());
memcpy(buffer, p.data(), p.size());
rapidjson::Document drapid;
drapid.ParseInsitu<kParseValidateEncodingFlag>(buffer);
BEST_TIME("rapid (just dom) ", rapid_just_dom(drapid).size(), size, , repeat,
volume, !just_data);
memcpy(buffer, p.data(), p.size());
auto dsasjon = sajson::parse(sajson::dynamic_allocation(),
sajson::mutable_string_view(p.size(), buffer));
BEST_TIME("sasjon (just dom) ", sasjon_just_dom(dsasjon).size(), size, ,
repeat, volume, !just_data);
free(buffer);
}
+6 -4
View File
@@ -11,16 +11,17 @@
#include <cstring> // for memset
#include <stdexcept>
#include <vector>
#include <iostream>
#include <vector>
template <int TYPE = PERF_TYPE_HARDWARE> class LinuxEvents {
int fd;
bool working;
bool working;
perf_event_attr attribs;
int num_events;
std::vector<uint64_t> temp_result_vec;
std::vector<uint64_t> ids;
public:
explicit LinuxEvents(std::vector<int> config_vec) : fd(0), working(true) {
memset(&attribs, 0, sizeof(attribs));
@@ -84,8 +85,9 @@ public:
private:
void report_error(const std::string &context) {
if(working) std::cerr << (context + ": " + std::string(strerror(errno))) << std::endl;
working = false;
if (working)
std::cerr << (context + ": " + std::string(strerror(errno))) << std::endl;
working = false;
}
};
#endif
+86 -63
View File
@@ -1,5 +1,5 @@
#include <unistd.h>
#include <iostream>
#include <unistd.h>
#include "benchmark.h"
#include "simdjson/jsonioutil.h"
@@ -14,10 +14,10 @@
#include "rapidjson/writer.h"
#include "sajson.h"
using namespace simdjson;
using namespace rapidjson;
std::string rapidstringmeInsitu(char *json) {
std::string rapid_stringme_insitu(char *json) {
Document d;
d.ParseInsitu(json);
if (d.HasParseError()) {
@@ -30,7 +30,7 @@ std::string rapidstringmeInsitu(char *json) {
return buffer.GetString();
}
std::string rapidstringme(char *json) {
std::string rapid_stringme(char *json) {
Document d;
d.Parse(json);
if (d.HasParseError()) {
@@ -46,29 +46,28 @@ std::string rapidstringme(char *json) {
int main(int argc, char *argv[]) {
int c;
bool verbose = false;
bool justdata = false;
bool just_data = false;
while ((c = getopt (argc, argv, "vt")) != -1)
switch (c)
{
case 't':
justdata = true;
break;
case 'v':
verbose = true;
break;
default:
abort ();
}
while ((c = getopt(argc, argv, "vt")) != -1)
switch (c) {
case 't':
just_data = true;
break;
case 'v':
verbose = true;
break;
default:
abort();
}
if (optind >= argc) {
std::cerr << "Usage: " << argv[0] << " <jsonfile>" << std::endl;
exit(1);
}
const char * filename = argv[optind];
padded_string p;
const char *filename = argv[optind];
simdjson::padded_string p;
try {
get_corpus(filename).swap(p);
} catch (const std::exception& e) { // caught by reference to base
simdjson::get_corpus(filename).swap(p);
} catch (const std::exception &e) { // caught by reference to base
std::cout << "Could not load the file " << filename << std::endl;
return EXIT_FAILURE;
}
@@ -82,77 +81,101 @@ int main(int argc, char *argv[]) {
std::cout << p.size() << " B ";
std::cout << std::endl;
}
char *buffer = allocate_padded_buffer(p.size() + 1);
char *buffer = simdjson::allocate_padded_buffer(p.size() + 1);
memcpy(buffer, p.data(), p.size());
buffer[p.size()] = '\0';
int repeat = 50;
int volume = p.size();
if(justdata) {
printf("name cycles_per_byte cycles_per_byte_err gb_per_s gb_per_s_err \n");
if (just_data) {
printf(
"name cycles_per_byte cycles_per_byte_err gb_per_s gb_per_s_err \n");
}
size_t strlength = rapidstringme((char *)p.data()).size();
size_t strlength = rapid_stringme((char *)p.data()).size();
if (verbose)
std::cout << "input length is " << p.size() << " stringified length is "
<< strlength << std::endl;
BEST_TIME_NOCHECK("despacing with RapidJSON", rapidstringme((char *)p.data()), , repeat, volume, !justdata);
BEST_TIME_NOCHECK("despacing with RapidJSON Insitu", rapidstringmeInsitu((char *)buffer),
memcpy(buffer, p.data(), p.size()), repeat, volume, !justdata);
BEST_TIME_NOCHECK("despacing with RapidJSON",
rapid_stringme((char *)p.data()), , repeat, volume,
!just_data);
BEST_TIME_NOCHECK(
"despacing with RapidJSON Insitu", rapid_stringme_insitu((char *)buffer),
memcpy(buffer, p.data(), p.size()), repeat, volume, !just_data);
memcpy(buffer, p.data(), p.size());
size_t outlength =
jsonminify((const uint8_t *)buffer, p.size(), (uint8_t *)buffer);
size_t outlength = simdjson::json_minify((const uint8_t *)buffer, p.size(),
(uint8_t *)buffer);
if (verbose)
std::cout << "jsonminify length is " << outlength << std::endl;
std::cout << "json_minify length is " << outlength << std::endl;
uint8_t *cbuffer = (uint8_t *)buffer;
BEST_TIME("jsonminify", jsonminify(cbuffer, p.size(), cbuffer), outlength,
memcpy(buffer, p.data(), p.size()), repeat, volume, !justdata);
printf("minisize = %zu, original size = %zu (minified down to %.2f percent of original) \n", outlength, p.size(), outlength * 100.0 / p.size());
BEST_TIME("json_minify", simdjson::json_minify(cbuffer, p.size(), cbuffer),
outlength, memcpy(buffer, p.data(), p.size()), repeat, volume,
!just_data);
printf("minisize = %zu, original size = %zu (minified down to %.2f percent "
"of original) \n",
outlength, p.size(), outlength * 100.0 / p.size());
/***
* Is it worth it to minify before parsing?
***/
rapidjson::Document d;
BEST_TIME("RapidJSON Insitu orig", d.ParseInsitu(buffer).HasParseError(), false,
memcpy(buffer, p.data(), p.size()), repeat, volume, !justdata);
BEST_TIME("RapidJSON Insitu orig", d.ParseInsitu(buffer).HasParseError(),
false, memcpy(buffer, p.data(), p.size()), repeat, volume,
!just_data);
char *minibuffer = allocate_padded_buffer(p.size() + 1);
size_t minisize = jsonminify((const uint8_t *)p.data(), p.size(), (uint8_t*) minibuffer);
minibuffer[minisize] = '\0';
char *mini_buffer = simdjson::allocate_padded_buffer(p.size() + 1);
size_t minisize = simdjson::json_minify((const uint8_t *)p.data(), p.size(),
(uint8_t *)mini_buffer);
mini_buffer[minisize] = '\0';
BEST_TIME("RapidJSON Insitu despaced", d.ParseInsitu(buffer).HasParseError(), false,
memcpy(buffer, minibuffer, p.size()),
repeat, volume, !justdata);
BEST_TIME("RapidJSON Insitu despaced", d.ParseInsitu(buffer).HasParseError(),
false, memcpy(buffer, mini_buffer, p.size()), repeat, volume,
!just_data);
size_t astbuffersize = p.size() * 2;
size_t * ast_buffer = (size_t *) malloc(astbuffersize * sizeof(size_t));
size_t ast_buffer_size = p.size() * 2;
size_t *ast_buffer = (size_t *)malloc(ast_buffer_size * sizeof(size_t));
BEST_TIME("sajson orig", sajson::parse(sajson::bounded_allocation(ast_buffer, astbuffersize), sajson::mutable_string_view(p.size(), buffer)).is_valid(), true, memcpy(buffer, p.data(), p.size()), repeat, volume, !justdata);
BEST_TIME(
"sajson orig",
sajson::parse(sajson::bounded_allocation(ast_buffer, ast_buffer_size),
sajson::mutable_string_view(p.size(), buffer))
.is_valid(),
true, memcpy(buffer, p.data(), p.size()), repeat, volume, !just_data);
BEST_TIME(
"sajson despaced",
sajson::parse(sajson::bounded_allocation(ast_buffer, ast_buffer_size),
sajson::mutable_string_view(minisize, buffer))
.is_valid(),
true, memcpy(buffer, mini_buffer, p.size()), repeat, volume, !just_data);
BEST_TIME("sajson despaced", sajson::parse(sajson::bounded_allocation(ast_buffer, astbuffersize), sajson::mutable_string_view(minisize, buffer)).is_valid(), true, memcpy(buffer, minibuffer, p.size()), repeat, volume, !justdata);
ParsedJson pj;
bool isallocok = pj.allocateCapacity(p.size(), 1024);
if(!isallocok) {
simdjson::ParsedJson pj;
bool is_alloc_ok = pj.allocate_capacity(p.size(), 1024);
if (!is_alloc_ok) {
fprintf(stderr, "failed to allocate memory\n");
return EXIT_FAILURE;
}
bool automated_reallocation = false;
BEST_TIME("simdjson orig", json_parse((const uint8_t*)buffer, p.size(), pj, automated_reallocation), true, memcpy(buffer, p.data(), p.size()), repeat, volume, !justdata);
ParsedJson pj2;
bool isallocok2 = pj2.allocateCapacity(p.size(), 1024);
if(!isallocok2) {
}
bool automated_reallocation = false;
BEST_TIME("simdjson orig",
simdjson::json_parse((const uint8_t *)buffer, p.size(), pj,
automated_reallocation),
true, memcpy(buffer, p.data(), p.size()), repeat, volume,
!just_data);
simdjson::ParsedJson pj2;
bool is_alloc_ok2 = pj2.allocate_capacity(p.size(), 1024);
if (!is_alloc_ok2) {
fprintf(stderr, "failed to allocate memory\n");
return EXIT_FAILURE;
}
automated_reallocation = false;
BEST_TIME("simdjson despaced", json_parse((const uint8_t*)buffer, minisize, pj2, automated_reallocation), true, memcpy(buffer, minibuffer, p.size()), repeat, volume, !justdata);
}
automated_reallocation = false;
BEST_TIME("simdjson despaced",
simdjson::json_parse((const uint8_t *)buffer, minisize, pj2,
automated_reallocation),
true, memcpy(buffer, mini_buffer, p.size()), repeat, volume,
!just_data);
free(buffer);
free(ast_buffer);
free(minibuffer);
free(mini_buffer);
}
+173 -49
View File
@@ -28,25 +28,109 @@
#endif
//#define DEBUG
#include "simdjson/common_defs.h"
#include "simdjson/isadetection.h"
#include "simdjson/jsonioutil.h"
#include "simdjson/jsonparser.h"
#include "simdjson/parsedjson.h"
#include "simdjson/stage1_find_marks.h"
#include "simdjson/stage2_build_tape.h"
namespace simdjson {
Architecture _find_best_supported_implementation() {
constexpr uint32_t haswell_flags =
instruction_set::AVX2 | instruction_set::PCLMULQDQ |
instruction_set::BMI1 | instruction_set::BMI2;
constexpr uint32_t westmere_flags =
instruction_set::SSE42 | instruction_set::PCLMULQDQ;
uint32_t supports = detect_supported_architectures();
// Order from best to worst (within architecture)
if ((haswell_flags & supports) == haswell_flags) {
return Architecture::HASWELL;
}
if ((westmere_flags & supports) == westmere_flags) {
return Architecture::WESTMERE;
}
if (instruction_set::NEON)
return Architecture::ARM64;
return Architecture::NONE;
}
using unified_functype = int(const uint8_t *buf, size_t len, ParsedJson &pj);
using stage1_functype = int(const uint8_t *buf, size_t len, ParsedJson &pj);
extern unified_functype *unified_ptr;
extern stage1_functype *stage1_ptr;
int unified_machine_dispatch(const uint8_t *buf, size_t len, ParsedJson &pj) {
Architecture best_implementation = _find_best_supported_implementation();
// Selecting the best implementation
switch (best_implementation) {
#ifdef IS_X86_64
case Architecture::HASWELL:
unified_ptr = &unified_machine<Architecture::HASWELL>;
break;
case Architecture::WESTMERE:
unified_ptr = &unified_machine<Architecture::WESTMERE>;
break;
#endif
#ifdef IS_ARM64
case Architecture::ARM64:
unified_ptr = &unified_machine<Architecture::ARM64>;
break;
#endif
default:
std::cerr << "The processor is not supported by simdjson." << std::endl;
return simdjson::UNEXPECTED_ERROR;
}
return unified_ptr(buf, len, pj);
}
// Responsible to select the best json_parse implementation
int find_structural_bits_dispatch(const uint8_t *buf, size_t len,
ParsedJson &pj) {
Architecture best_implementation = _find_best_supported_implementation();
// Selecting the best implementation
switch (best_implementation) {
#ifdef IS_X86_64
case Architecture::HASWELL:
stage1_ptr = &find_structural_bits<Architecture::HASWELL>;
break;
case Architecture::WESTMERE:
stage1_ptr = &find_structural_bits<Architecture::WESTMERE>;
break;
#endif
#ifdef IS_ARM64
case Architecture::ARM64:
stage1_ptr = &find_structural_bits<Architecture::ARM64>;
break;
#endif
default:
std::cerr << "The processor is not supported by simdjson." << std::endl;
return simdjson::UNEXPECTED_ERROR;
}
return stage1_ptr(buf, len, pj);
}
stage1_functype *stage1_ptr = &find_structural_bits_dispatch;
unified_functype *unified_ptr = &unified_machine_dispatch;
} // namespace simdjson
int main(int argc, char *argv[]) {
bool verbose = false;
bool dump = false;
bool jsonoutput = false;
bool forceoneiteration = false;
bool justdata = false;
bool json_output = false;
bool force_one_iteration = false;
bool just_data = false;
#ifndef _MSC_VER
int c;
while ((c = getopt(argc, argv, "1vdt")) != -1) {
switch (c) {
case 't':
justdata = true;
just_data = true;
break;
case 'v':
verbose = true;
@@ -55,15 +139,15 @@ int main(int argc, char *argv[]) {
dump = true;
break;
case 'j':
jsonoutput = true;
json_output = true;
break;
case '1':
forceoneiteration = true;
force_one_iteration = true;
break;
default:
abort();
}
}
}
#else
int optind = 1;
#endif
@@ -73,38 +157,57 @@ int main(int argc, char *argv[]) {
}
const char *filename = argv[optind];
if (optind + 1 < argc) {
std::cerr << "warning: ignoring everything after " << argv[optind + 1] << std::endl;
std::cerr << "warning: ignoring everything after " << argv[optind + 1]
<< std::endl;
}
if (verbose) {
std::cout << "[verbose] loading " << filename << std::endl;
}
padded_string p;
simdjson::padded_string p;
try {
get_corpus(filename).swap(p);
} catch (const std::exception &e) { // caught by reference to base
simdjson::get_corpus(filename).swap(p);
} catch (const std::exception &) { // caught by reference to base
std::cout << "Could not load the file " << filename << std::endl;
return EXIT_FAILURE;
}
if (verbose) {
std::cout << "[verbose] loaded " << filename << " (" << p.size() << " bytes)"
<< std::endl;
}
std::cout << "[verbose] loaded " << filename << " (" << p.size()
<< " bytes)" << std::endl;
}
#if defined(DEBUG)
const uint32_t iterations = 1;
#else
const uint32_t iterations =
forceoneiteration ? 1 : (p.size() < 1 * 1000 * 1000 ? 1000 : 10);
force_one_iteration ? 1 : (p.size() < 1 * 1000 * 1000 ? 1000 : 10);
#endif
std::vector<double> res;
res.resize(iterations);
if (!just_data)
printf("number of iterations %u \n", iterations);
#if !defined(__linux__)
#define SQUASH_COUNTERS
if (justdata) {
printf("justdata (-t) flag only works under linux.\n");
if (just_data) {
printf("just_data (-t) flag only works under linux.\n");
}
#endif
{ // practice run
simdjson::ParsedJson pj;
bool allocok = pj.allocate_capacity(p.size());
if (allocok) {
simdjson::stage1_ptr((const uint8_t *)p.data(), p.size(), pj);
simdjson::unified_ptr(
(const uint8_t
*)(const uint8_t
*)(const uint8_t
*)(const uint8_t
*)(const uint8_t
*)(const uint8_t
*)(const uint8_t
*)(const uint8_t *)
p.data(),
p.size(), pj);
}
}
#ifndef SQUASH_COUNTERS
std::vector<int> evts;
evts.push_back(PERF_COUNT_HW_CPU_CYCLES);
@@ -122,38 +225,30 @@ int main(int argc, char *argv[]) {
unsigned long cmis0 = 0, cmis1 = 0, cmis2 = 0;
#endif
bool isok = true;
#ifndef SQUASH_COUNTERS
for (uint32_t i = 0; i < iterations; i++) {
if (verbose) {
std::cout << "[verbose] iteration # " << i << std::endl;
}
#ifndef SQUASH_COUNTERS
unified.start();
#endif
ParsedJson pj;
bool allocok = pj.allocateCapacity(p.size());
simdjson::ParsedJson pj;
bool allocok = pj.allocate_capacity(p.size());
if (!allocok) {
std::cerr << "failed to allocate memory" << std::endl;
return EXIT_FAILURE;
}
#ifndef SQUASH_COUNTERS
unified.end(results);
cy0 += results[0];
cl0 += results[1];
mis0 += results[2];
cref0 += results[3];
cmis0 += results[4];
#endif
if (verbose) {
std::cout << "[verbose] allocated memory for parsed JSON " << std::endl;
}
auto start = std::chrono::steady_clock::now();
#ifndef SQUASH_COUNTERS
}
unified.start();
#endif
isok = find_structural_bits(p.data(), p.size(), pj);
#ifndef SQUASH_COUNTERS
isok = (simdjson::stage1_ptr((const uint8_t *)p.data(), p.size(), pj) ==
simdjson::SUCCESS);
unified.end(results);
cy1 += results[0];
cl1 += results[1];
@@ -165,10 +260,9 @@ int main(int argc, char *argv[]) {
break;
}
unified.start();
#endif
isok = isok && !unified_machine(p.data(), p.size(), pj);
#ifndef SQUASH_COUNTERS
isok = isok &&
(simdjson::SUCCESS ==
simdjson::unified_ptr((const uint8_t *)p.data(), p.size(), pj));
unified.end(results);
cy2 += results[0];
cl2 += results[1];
@@ -179,20 +273,50 @@ int main(int argc, char *argv[]) {
std::cout << "Failed during stage 2" << std::endl;
break;
}
}
#endif
// we do it again, this time just measuring the elapsed time
for (uint32_t i = 0; i < iterations; i++) {
if (verbose) {
std::cout << "[verbose] iteration # " << i << std::endl;
}
simdjson::ParsedJson pj;
bool allocok = pj.allocate_capacity(p.size());
if (!allocok) {
std::cerr << "failed to allocate memory" << std::endl;
return EXIT_FAILURE;
}
if (verbose) {
std::cout << "[verbose] allocated memory for parsed JSON " << std::endl;
}
auto start = std::chrono::steady_clock::now();
isok = (simdjson::stage1_ptr((const uint8_t *)p.data(), p.size(), pj) ==
simdjson::SUCCESS);
isok = isok &&
(simdjson::SUCCESS ==
simdjson::unified_ptr((const uint8_t *)p.data(), p.size(), pj));
auto end = std::chrono::steady_clock::now();
std::chrono::duration<double> secs = end - start;
res[i] = secs.count();
if (!isok) {
std::cerr << pj.get_error_message() << std::endl;
std::cerr << "Could not parse. " << std::endl;
return EXIT_FAILURE;
}
}
ParsedJson pj = build_parsed_json(p); // do the parsing again to get the stats
if (!pj.isValid()) {
simdjson::ParsedJson pj =
build_parsed_json(p); // do the parsing again to get the stats
if (!pj.is_valid()) {
std::cerr << pj.get_error_message() << std::endl;
std::cerr << "Could not parse. " << std::endl;
return EXIT_FAILURE;
}
double min_result = *min_element(res.begin(), res.end());
double speedinGBs = (p.size()) / (min_result * 1000000000.0);
#ifndef SQUASH_COUNTERS
unsigned long total = cy0 + cy1 + cy2;
if (justdata) {
if (just_data) {
float cpb0 = (double)cy0 / (iterations * p.size());
float cpb1 = (double)cy1 / (iterations * p.size());
float cpb2 = (double)cy2 / (iterations * p.size());
@@ -210,8 +334,8 @@ int main(int argc, char *argv[]) {
break;
}
}
printf("\"%s\"\t%f\t%f\t%f\t%f\n", snewfile, cpb0, cpb1, cpb2,
cpbtotal);
printf("\"%s\"\t%f\t%f\t%f\t%f\t%f\n", snewfile, cpb0, cpb1, cpb2, cpbtotal,
speedinGBs);
free(newfile);
} else {
printf("number of bytes %ld number of structural chars %u ratio %.3f\n",
@@ -247,16 +371,16 @@ int main(int argc, char *argv[]) {
printf(" all stages: %.2f cycles per input byte.\n",
(double)total / (iterations * p.size()));
printf("Estimated average frequency: %.3f GHz.\n",
(double)total / (iterations * min_result * 1000000000.0));
}
#endif
double min_result = *min_element(res.begin(), res.end());
if (!justdata) {
if (!just_data) {
std::cout << "Min: " << min_result << " bytes read: " << p.size()
<< " Gigabytes/second: " << (p.size()) / (min_result * 1000000000.0)
<< std::endl;
}
if (jsonoutput) {
isok = isok && pj.printjson(std::cout);
<< " Gigabytes/second: " << speedinGBs << std::endl;
}
if (json_output) {
isok = isok && pj.print_json(std::cout);
}
if (dump) {
isok = isok && pj.dump_raw_tape(std::cout);
+39 -36
View File
@@ -12,7 +12,7 @@
#include "sajson.h"
using namespace rapidjson;
using namespace simdjson;
struct stat_s {
size_t number_count;
size_t object_count;
@@ -43,11 +43,11 @@ void print_stat(const stat_t &s) {
s.true_count, s.false_count);
}
__attribute__ ((noinline))
stat_t simdjson_computestats(const padded_string &p) {
__attribute__((noinline)) stat_t
simdjson_compute_stats(const simdjson::padded_string &p) {
stat_t answer;
ParsedJson pj = build_parsed_json(p);
answer.valid = pj.isValid();
simdjson::ParsedJson pj = build_parsed_json(p);
answer.valid = pj.is_valid();
if (!answer.valid) {
return answer;
}
@@ -57,24 +57,24 @@ stat_t simdjson_computestats(const padded_string &p) {
answer.null_count = 0;
answer.true_count = 0;
answer.false_count = 0;
size_t tapeidx = 0;
uint64_t tape_val = pj.tape[tapeidx++];
size_t tape_idx = 0;
uint64_t tape_val = pj.tape[tape_idx++];
uint8_t type = (tape_val >> 56);
size_t howmany = 0;
size_t how_many = 0;
assert(type == 'r');
howmany = tape_val & JSONVALUEMASK;
for (; tapeidx < howmany; tapeidx++) {
tape_val = pj.tape[tapeidx];
// uint64_t payload = tape_val & JSONVALUEMASK;
how_many = tape_val & JSON_VALUE_MASK;
for (; tape_idx < how_many; tape_idx++) {
tape_val = pj.tape[tape_idx];
// uint64_t payload = tape_val & JSON_VALUE_MASK;
type = (tape_val >> 56);
switch (type) {
case 'l': // we have a long int
answer.number_count++;
tapeidx++; // skipping the integer
tape_idx++; // skipping the integer
break;
case 'd': // we have a double
answer.number_count++;
tapeidx++; // skipping the double
tape_idx++; // skipping the double
break;
case 'n': // we have a null
answer.null_count++;
@@ -145,8 +145,8 @@ void sajson_traverse(stat_t &stats, const sajson::value &node) {
}
}
__attribute__ ((noinline))
stat_t sasjon_computestats(const padded_string &p) {
__attribute__((noinline)) stat_t
sasjon_compute_stats(const simdjson::padded_string &p) {
stat_t answer;
char *buffer = (char *)malloc(p.size());
memcpy(buffer, p.data(), p.size());
@@ -203,8 +203,8 @@ void rapid_traverse(stat_t &stats, const rapidjson::Value &v) {
}
}
__attribute__ ((noinline))
stat_t rapid_computestats(const padded_string &p) {
__attribute__((noinline)) stat_t
rapid_compute_stats(const simdjson::padded_string &p) {
stat_t answer;
char *buffer = (char *)malloc(p.size() + 1);
memcpy(buffer, p.data(), p.size());
@@ -228,13 +228,13 @@ stat_t rapid_computestats(const padded_string &p) {
int main(int argc, char *argv[]) {
bool verbose = false;
bool justdata = false;
bool just_data = false;
int c;
while ((c = getopt(argc, argv, "vt")) != -1)
switch (c) {
case 't':
justdata = true;
just_data = true;
break;
case 'v':
verbose = true;
@@ -243,19 +243,22 @@ int main(int argc, char *argv[]) {
abort();
}
if (optind >= argc) {
std::cerr << "Using different parsers, we compute the content statistics of "
"JSON documents." << std::endl;
std::cerr
<< "Using different parsers, we compute the content statistics of "
"JSON documents."
<< std::endl;
std::cerr << "Usage: " << argv[0] << " <jsonfile>" << std::endl;
std::cerr << "Or " << argv[0] << " -v <jsonfile>" << std::endl;
exit(1);
}
const char *filename = argv[optind];
if (optind + 1 < argc) {
std::cerr << "warning: ignoring everything after " << argv[optind + 1] << std::endl;
std::cerr << "warning: ignoring everything after " << argv[optind + 1]
<< std::endl;
}
padded_string p;
simdjson::padded_string p;
try {
get_corpus(filename).swap(p);
simdjson::get_corpus(filename).swap(p);
} catch (const std::exception &e) { // caught by reference to base
std::cout << "Could not load the file " << filename << std::endl;
return EXIT_FAILURE;
@@ -271,17 +274,17 @@ int main(int argc, char *argv[]) {
std::cout << p.size() << " B ";
std::cout << std::endl;
}
stat_t s1 = simdjson_computestats(p);
stat_t s1 = simdjson_compute_stats(p);
if (verbose) {
printf("simdjson: ");
print_stat(s1);
}
stat_t s2 = rapid_computestats(p);
stat_t s2 = rapid_compute_stats(p);
if (verbose) {
printf("rapid: ");
print_stat(s2);
}
stat_t s3 = sasjon_computestats(p);
stat_t s3 = sasjon_compute_stats(p);
if (verbose) {
printf("sasjon: ");
print_stat(s3);
@@ -290,13 +293,13 @@ int main(int argc, char *argv[]) {
assert(stat_equal(s1, s3));
int repeat = 50;
int volume = p.size();
if(justdata) {
printf("name cycles_per_byte cycles_per_byte_err gb_per_s gb_per_s_err \n");
if (just_data) {
printf("name cycles_per_byte cycles_per_byte_err gb_per_s gb_per_s_err \n");
}
BEST_TIME("simdjson ", simdjson_computestats(p).valid, true, , repeat,
volume, !justdata);
BEST_TIME("RapidJSON ", rapid_computestats(p).valid, true, , repeat, volume,
!justdata);
BEST_TIME("sasjon ", sasjon_computestats(p).valid, true, , repeat, volume,
!justdata);
BEST_TIME("simdjson ", simdjson_compute_stats(p).valid, true, , repeat,
volume, !just_data);
BEST_TIME("RapidJSON ", rapid_compute_stats(p).valid, true, , repeat, volume,
!just_data);
BEST_TIME("sasjon ", sasjon_compute_stats(p).valid, true, , repeat, volume,
!just_data);
}
+157 -128
View File
@@ -1,7 +1,7 @@
#include "simdjson/jsonparser.h"
#ifndef _MSC_VER
#include <unistd.h>
#include "linux-perf-events.h"
#include <unistd.h>
#ifdef __linux__
#include <libgen.h>
#endif //__linux__
@@ -11,7 +11,6 @@
#include "benchmark.h"
// #define RAPIDJSON_SSE2 // bad for performance
// #define RAPIDJSON_SSE42 // bad for performance
#include "rapidjson/document.h"
@@ -23,29 +22,27 @@
#ifdef ALLPARSER
#include "fastjson.cpp"
#include "fastjson_dom.cpp"
#include "gason.cpp"
#include "json11.cpp"
extern "C" {
#include "cJSON.c"
#include "cJSON.h"
#include "jsmn.c"
#include "jsmn.h"
#include "ujdecode.h"
#include "ultrajsondec.c"
#include "cJSON.h"
#include "cJSON.c"
#include "jsmn.h"
#include "jsmn.c"
}
#include "json/json.h"
#include "jsoncpp.cpp"
#include "json/json.h"
#endif
#endif
using namespace rapidjson;
#ifdef ALLPARSER
// fastjson has a tricky interface
void on_json_error(void *, const fastjson::ErrorContext &ec) {
@@ -62,12 +59,12 @@ bool fastjson_parse(const char *input) {
int main(int argc, char *argv[]) {
bool verbose = false;
bool justdata = false;
bool just_data = false;
int c;
while ((c = getopt(argc, argv, "vt")) != -1)
switch (c) {
case 't':
justdata = true;
just_data = true;
break;
case 'v':
verbose = true;
@@ -78,17 +75,17 @@ int main(int argc, char *argv[]) {
if (optind >= argc) {
std::cerr << "Usage: " << argv[0] << " <jsonfile>" << std::endl;
std::cerr << "Or " << argv[0] << " -v <jsonfile>" << std::endl;
std::cerr << "To enable parsers that are not standard compliant, use the -a "
"flag" << std::endl;
std::cerr << "The '-t' flag outputs a table. " << std::endl;
exit(1);
}
const char *filename = argv[optind];
if (optind + 1 < argc) {
std::cerr << "warning: ignoring everything after " << argv[optind + 1] << std::endl;
std::cerr << "warning: ignoring everything after " << argv[optind + 1]
<< std::endl;
}
padded_string p;
simdjson::padded_string p;
try {
get_corpus(filename).swap(p);
simdjson::get_corpus(filename).swap(p);
} catch (const std::exception &e) { // caught by reference to base
std::cout << "Could not load the file " << filename << std::endl;
return EXIT_FAILURE;
@@ -104,158 +101,190 @@ int main(int argc, char *argv[]) {
std::cout << p.size() << " B ";
std::cout << std::endl;
}
ParsedJson pj;
bool allocok = pj.allocateCapacity(p.size(), 1024);
simdjson::ParsedJson pj;
bool allocok = pj.allocate_capacity(p.size(), 1024);
if (!allocok) {
std::cerr << "can't allocate memory" << std::endl;
return EXIT_FAILURE;
}
int repeat = 50;
int repeat = (p.size() < 1 * 1000 * 1000 ? 1000 : 10);
int volume = p.size();
if(justdata) {
printf("name cycles_per_byte cycles_per_byte_err gb_per_s gb_per_s_err \n");
if (just_data) {
printf("%-42s %20s %20s %20s %20s \n", "name", "cycles_per_byte",
"cycles_per_byte_err", "gb_per_s", "gb_per_s_err");
}
if(!justdata) BEST_TIME("simdjson (dynamic mem) ", build_parsed_json(p).isValid(), true, ,
repeat, volume, !justdata);
// (static alloc)
BEST_TIME("simdjson ", json_parse(p, pj), simdjson::SUCCESS, , repeat,
volume, !justdata);
if (!just_data)
BEST_TIME("simdjson (dynamic mem) ", build_parsed_json(p).is_valid(), true,
, repeat, volume, !just_data);
// (static alloc)
BEST_TIME("simdjson ", json_parse(p, pj), simdjson::SUCCESS, , repeat, volume,
!just_data);
rapidjson::Document d;
char *buffer = (char *)malloc(p.size() + 1);
memcpy(buffer, p.data(), p.size());
buffer[p.size()] = '\0';
#ifndef ALLPARSER
if(!justdata)
#endif
BEST_TIME(
"RapidJSON ",
d.Parse<kParseValidateEncodingFlag>((const char *)buffer).HasParseError(),
false, memcpy(buffer, p.data(), p.size()), repeat, volume, !justdata);
if (!just_data)
#endif
BEST_TIME("RapidJSON ",
d.Parse<kParseValidateEncodingFlag>((const char *)buffer)
.HasParseError(),
false, memcpy(buffer, p.data(), p.size()), repeat, volume,
!just_data);
BEST_TIME("RapidJSON (insitu)",
d.ParseInsitu<kParseValidateEncodingFlag>(buffer).HasParseError(),
false, memcpy(buffer, p.data(), p.size()) && (buffer[p.size()] = '\0'), repeat, volume, !justdata);
false,
memcpy(buffer, p.data(), p.size()) && (buffer[p.size()] = '\0'),
repeat, volume, !just_data);
#ifndef ALLPARSER
if(!justdata)
#endif
BEST_TIME("sajson (dynamic mem)",
sajson::parse(sajson::dynamic_allocation(),
sajson::mutable_string_view(p.size(), buffer))
.is_valid(),
true, memcpy(buffer, p.data(), p.size()), repeat, volume, !justdata);
if (!just_data)
#endif
BEST_TIME("sajson (dynamic mem)",
sajson::parse(sajson::dynamic_allocation(),
sajson::mutable_string_view(p.size(), buffer))
.is_valid(),
true, memcpy(buffer, p.data(), p.size()), repeat, volume,
!just_data);
size_t astbuffersize = p.size();
size_t *ast_buffer = (size_t *)malloc(astbuffersize * sizeof(size_t));
size_t ast_buffer_size = p.size();
size_t *ast_buffer = (size_t *)malloc(ast_buffer_size * sizeof(size_t));
// (static alloc, insitu)
BEST_TIME("sajson",
sajson::parse(sajson::bounded_allocation(ast_buffer, astbuffersize),
sajson::mutable_string_view(p.size(), buffer))
.is_valid(),
true, memcpy(buffer, p.data(), p.size()), repeat, volume, !justdata);
#ifdef __linux__
if(!justdata) {
std::vector<int> evts;
evts.push_back(PERF_COUNT_HW_CPU_CYCLES);
evts.push_back(PERF_COUNT_HW_INSTRUCTIONS);
evts.push_back(PERF_COUNT_HW_BRANCH_MISSES);
evts.push_back(PERF_COUNT_HW_CACHE_REFERENCES);
evts.push_back(PERF_COUNT_HW_CACHE_MISSES);
LinuxEvents<PERF_TYPE_HARDWARE> unified(evts);
std::vector<unsigned long long> results;
std::vector<unsigned long long> stats;
results.resize(evts.size());
stats.resize(evts.size());
std::fill(stats.begin(), stats.end(), 0);// unnecessary
for(int i = 0; i < repeat; i++) {
unified.start();
if(json_parse(p, pj) != true) printf("bug\n");
unified.end(results);
std::transform (stats.begin(), stats.end(), results.begin(), stats.begin(), std::plus<unsigned long long>());
}
printf("simdjson : cycles %10.0f instructions %10.0f branchmisses %10.0f cacheref %10.0f cachemisses %10.0f bytespercachemiss %10.0f inspercycle %10.1f insperbyte %10.1f\n",
stats[0] * 1.0 / repeat, stats[1] * 1.0 / repeat, stats[2] * 1.0 / repeat, stats[3] * 1.0 / repeat, stats[4] * 1.0 / repeat, volume * repeat * 1.0 / stats[2], stats[1] *1.0 / stats[0], stats[1] * 1.0 / (volume * repeat));
std::fill(stats.begin(), stats.end(), 0);
for(int i = 0; i < repeat; i++) {
memcpy(buffer, p.data(), p.size());
buffer[p.size()] = '\0';
unified.start();
if(d.ParseInsitu<kParseValidateEncodingFlag>(buffer).HasParseError() != false) printf("bug\n");
unified.end(results);
std::transform (stats.begin(), stats.end(), results.begin(), stats.begin(), std::plus<unsigned long long>());
}
printf("RapidJSON: cycles %10.0f instructions %10.0f branchmisses %10.0f cacheref %10.0f cachemisses %10.0f bytespercachemiss %10.0f inspercycle %10.1f insperbyte %10.1f\n",
stats[0] * 1.0 / repeat, stats[1] * 1.0 / repeat, stats[2] * 1.0 / repeat, stats[3] * 1.0 / repeat, stats[4] * 1.0 / repeat, volume * repeat * 1.0 / stats[2], stats[1] *1.0 / stats[0], stats[1] * 1.0 / (volume * repeat));
std::fill(stats.begin(), stats.end(), 0);// unnecessary
for(int i = 0; i < repeat; i++) {
memcpy(buffer, p.data(), p.size());
unified.start();
if(sajson::parse(sajson::bounded_allocation(ast_buffer, astbuffersize),
sajson::mutable_string_view(p.size(), buffer))
.is_valid() != true) printf("bug\n");
unified.end(results);
std::transform (stats.begin(), stats.end(), results.begin(), stats.begin(), std::plus<unsigned long long>());
}
printf("sajson : cycles %10.0f instructions %10.0f branchmisses %10.0f cacheref %10.0f cachemisses %10.0f bytespercachemiss %10.0f inspercycle %10.1f insperbyte %10.1f\n",
stats[0] * 1.0 / repeat, stats[1] * 1.0 / repeat, stats[2] * 1.0 / repeat, stats[3] * 1.0 / repeat, stats[4] * 1.0 / repeat, volume * repeat * 1.0 / stats[2], stats[1] *1.0 / stats[0], stats[1] * 1.0 / (volume * repeat));
}
#endif// __linux__
BEST_TIME(
"sajson",
sajson::parse(sajson::bounded_allocation(ast_buffer, ast_buffer_size),
sajson::mutable_string_view(p.size(), buffer))
.is_valid(),
true, memcpy(buffer, p.data(), p.size()), repeat, volume, !just_data);
#ifdef ALLPARSER
std::string json11err;
BEST_TIME("dropbox (json11) ",
((json11::Json::parse(buffer, json11err).is_null()) ||
(!json11err.empty())),
false, memcpy(buffer, p.data(), p.size()), repeat, volume, !justdata);
BEST_TIME("dropbox (json11) ",
((json11::Json::parse(buffer, json11err).is_null()) ||
(!json11err.empty())),
false, memcpy(buffer, p.data(), p.size()), repeat, volume,
!just_data);
BEST_TIME("fastjson ", fastjson_parse(buffer), true,
memcpy(buffer, p.data(), p.size()), repeat, volume, !justdata);
BEST_TIME("fastjson ", fastjson_parse(buffer), true,
memcpy(buffer, p.data(), p.size()), repeat, volume, !just_data);
JsonValue value;
JsonAllocator allocator;
char *endptr;
BEST_TIME("gason ",
jsonParse(buffer, &endptr, &value, allocator), JSON_OK,
memcpy(buffer, p.data(), p.size()), repeat, volume, !justdata);
BEST_TIME("gason ", jsonParse(buffer, &endptr, &value, allocator),
JSON_OK, memcpy(buffer, p.data(), p.size()), repeat, volume,
!just_data);
void *state;
BEST_TIME("ultrajson ",
(UJDecode(buffer, p.size(), NULL, &state) == NULL), false,
memcpy(buffer, p.data(), p.size()), repeat, volume, !justdata);
BEST_TIME("ultrajson ",
(UJDecode(buffer, p.size(), NULL, &state) == NULL), false,
memcpy(buffer, p.data(), p.size()), repeat, volume, !just_data);
{
std::unique_ptr<jsmntok_t[]> tokens = std::make_unique<jsmntok_t[]>(p.size());
std::unique_ptr<jsmntok_t[]> tokens =
std::make_unique<jsmntok_t[]>(p.size());
jsmn_parser parser;
jsmn_init(&parser);
memcpy(buffer, p.data(), p.size());
buffer[p.size()] = '\0';
BEST_TIME("jsmn ",
(jsmn_parse(&parser, buffer, p.size(), tokens.get(), p.size()) > 0), true,
jsmn_init(&parser), repeat, volume, !justdata);
BEST_TIME(
"jsmn ",
(jsmn_parse(&parser, buffer, p.size(), tokens.get(), p.size()) > 0),
true, jsmn_init(&parser), repeat, volume, !just_data);
}
memcpy(buffer, p.data(), p.size());
buffer[p.size()] = '\0';
cJSON * tree = cJSON_Parse(buffer);
BEST_TIME("cJSON ",
((tree = cJSON_Parse(buffer)) != NULL ), true,
cJSON_Delete(tree), repeat, volume, !justdata);
cJSON *tree = cJSON_Parse(buffer);
BEST_TIME("cJSON ", ((tree = cJSON_Parse(buffer)) != NULL), true,
cJSON_Delete(tree), repeat, volume, !just_data);
cJSON_Delete(tree);
Json::CharReaderBuilder b;
Json::CharReader * jsoncppreader = b.newCharReader();
Json::CharReader *json_cpp_reader = b.newCharReader();
Json::Value root;
Json::String errs;
BEST_TIME("jsoncpp ",
jsoncppreader->parse(buffer,buffer+volume,&root,&errs), true,
, repeat, volume, !justdata);
delete jsoncppreader;
json_cpp_reader->parse(buffer, buffer + volume, &root, &errs), true,
, repeat, volume, !just_data);
delete json_cpp_reader;
#endif
if(!justdata) BEST_TIME("memcpy ",
(memcpy(buffer, p.data(), p.size()) == buffer), true, , repeat,
volume, !justdata);
if (!just_data)
BEST_TIME("memcpy ",
(memcpy(buffer, p.data(), p.size()) == buffer), true, , repeat,
volume, !just_data);
#ifdef __linux__
if (!just_data) {
printf("\n \n <doing additional analysis with performance counters (Linux "
"only)>\n");
std::vector<int> evts;
evts.push_back(PERF_COUNT_HW_CPU_CYCLES);
evts.push_back(PERF_COUNT_HW_INSTRUCTIONS);
evts.push_back(PERF_COUNT_HW_BRANCH_MISSES);
evts.push_back(PERF_COUNT_HW_CACHE_REFERENCES);
evts.push_back(PERF_COUNT_HW_CACHE_MISSES);
LinuxEvents<PERF_TYPE_HARDWARE> unified(evts);
std::vector<unsigned long long> results;
std::vector<unsigned long long> stats;
results.resize(evts.size());
stats.resize(evts.size());
std::fill(stats.begin(), stats.end(), 0); // unnecessary
for (int i = 0; i < repeat; i++) {
unified.start();
if (json_parse(p, pj) != simdjson::SUCCESS)
printf("bug\n");
unified.end(results);
std::transform(stats.begin(), stats.end(), results.begin(), stats.begin(),
std::plus<unsigned long long>());
}
printf("simdjson : cycles %10.0f instructions %10.0f branchmisses %10.0f "
"cacheref %10.0f cachemisses %10.0f bytespercachemiss %10.0f "
"inspercycle %10.1f insperbyte %10.1f\n",
stats[0] * 1.0 / repeat, stats[1] * 1.0 / repeat,
stats[2] * 1.0 / repeat, stats[3] * 1.0 / repeat,
stats[4] * 1.0 / repeat, volume * repeat * 1.0 / stats[2],
stats[1] * 1.0 / stats[0], stats[1] * 1.0 / (volume * repeat));
std::fill(stats.begin(), stats.end(), 0);
for (int i = 0; i < repeat; i++) {
memcpy(buffer, p.data(), p.size());
buffer[p.size()] = '\0';
unified.start();
if (d.ParseInsitu<kParseValidateEncodingFlag>(buffer).HasParseError() !=
false)
printf("bug\n");
unified.end(results);
std::transform(stats.begin(), stats.end(), results.begin(), stats.begin(),
std::plus<unsigned long long>());
}
printf("RapidJSON: cycles %10.0f instructions %10.0f branchmisses %10.0f "
"cacheref %10.0f cachemisses %10.0f bytespercachemiss %10.0f "
"inspercycle %10.1f insperbyte %10.1f\n",
stats[0] * 1.0 / repeat, stats[1] * 1.0 / repeat,
stats[2] * 1.0 / repeat, stats[3] * 1.0 / repeat,
stats[4] * 1.0 / repeat, volume * repeat * 1.0 / stats[2],
stats[1] * 1.0 / stats[0], stats[1] * 1.0 / (volume * repeat));
std::fill(stats.begin(), stats.end(), 0); // unnecessary
for (int i = 0; i < repeat; i++) {
memcpy(buffer, p.data(), p.size());
unified.start();
if (sajson::parse(sajson::bounded_allocation(ast_buffer, ast_buffer_size),
sajson::mutable_string_view(p.size(), buffer))
.is_valid() != true)
printf("bug\n");
unified.end(results);
std::transform(stats.begin(), stats.end(), results.begin(), stats.begin(),
std::plus<unsigned long long>());
}
printf("sajson : cycles %10.0f instructions %10.0f branchmisses %10.0f "
"cacheref %10.0f cachemisses %10.0f bytespercachemiss %10.0f "
"inspercycle %10.1f insperbyte %10.1f\n",
stats[0] * 1.0 / repeat, stats[1] * 1.0 / repeat,
stats[2] * 1.0 / repeat, stats[3] * 1.0 / repeat,
stats[4] * 1.0 / repeat, volume * repeat * 1.0 / stats[2],
stats[1] * 1.0 / stats[0], stats[1] * 1.0 / (volume * repeat));
}
#endif // __linux__
free(ast_buffer);
free(buffer);
}
+43 -39
View File
@@ -1,5 +1,5 @@
#include <iostream>
#ifndef _MSC_VER
#ifndef _MSC_VER
#include <unistd.h>
#endif
#include "simdjson/jsonioutil.h"
@@ -29,7 +29,7 @@ struct stat_s {
size_t float_count;
size_t string_count;
size_t backslash_count;
size_t nonasciibyte_count;
size_t non_ascii_byte_count;
size_t object_count;
size_t array_count;
size_t null_count;
@@ -42,16 +42,17 @@ struct stat_s {
using stat_t = struct stat_s;
stat_t simdjson_computestats(const padded_string &p) {
stat_t simdjson_compute_stats(const simdjson::padded_string &p) {
stat_t answer;
ParsedJson pj = build_parsed_json(p);
answer.valid = pj.isValid();
simdjson::ParsedJson pj = simdjson::build_parsed_json(p);
answer.valid = pj.is_valid();
if (!answer.valid) {
return answer;
}
answer.backslash_count = count_backslash(reinterpret_cast<const uint8_t *>(p.data()), p.size());
answer.nonasciibyte_count =
count_nonasciibytes(reinterpret_cast<const uint8_t *>(p.data()), p.size());
answer.backslash_count =
count_backslash(reinterpret_cast<const uint8_t *>(p.data()), p.size());
answer.non_ascii_byte_count = count_nonasciibytes(
reinterpret_cast<const uint8_t *>(p.data()), p.size());
answer.byte_count = p.size();
answer.integer_count = 0;
answer.float_count = 0;
@@ -62,24 +63,24 @@ stat_t simdjson_computestats(const padded_string &p) {
answer.false_count = 0;
answer.string_count = 0;
answer.structural_indexes_count = pj.n_structural_indexes;
size_t tapeidx = 0;
uint64_t tape_val = pj.tape[tapeidx++];
size_t tape_idx = 0;
uint64_t tape_val = pj.tape[tape_idx++];
uint8_t type = (tape_val >> 56);
size_t howmany = 0;
size_t how_many = 0;
assert(type == 'r');
howmany = tape_val & JSONVALUEMASK;
for (; tapeidx < howmany; tapeidx++) {
tape_val = pj.tape[tapeidx];
// uint64_t payload = tape_val & JSONVALUEMASK;
how_many = tape_val & JSON_VALUE_MASK;
for (; tape_idx < how_many; tape_idx++) {
tape_val = pj.tape[tape_idx];
// uint64_t payload = tape_val & JSON_VALUE_MASK;
type = (tape_val >> 56);
switch (type) {
case 'l': // we have a long int
answer.integer_count++;
tapeidx++; // skipping the integer
tape_idx++; // skipping the integer
break;
case 'd': // we have a double
answer.float_count++;
tapeidx++; // skipping the double
tape_idx++; // skipping the double
break;
case 'n': // we have a null
answer.null_count++;
@@ -112,14 +113,14 @@ stat_t simdjson_computestats(const padded_string &p) {
int main(int argc, char *argv[]) {
#ifndef _MSC_VER
int c;
while ((c = getopt(argc, argv, "")) != -1) {
int c;
while ((c = getopt(argc, argv, "")) != -1) {
switch (c) {
default:
abort();
}
}
}
#else
int optind = 1;
#endif
@@ -134,37 +135,37 @@ int main(int argc, char *argv[]) {
std::cerr << "warning: ignoring everything after " << argv[optind + 1]
<< std::endl;
}
padded_string p;
simdjson::padded_string p;
try {
get_corpus(filename).swap(p);
} catch (const std::exception &e) { // caught by reference to base
simdjson::get_corpus(filename).swap(p);
} catch (const std::exception &) { // caught by reference to base
std::cerr << "Could not load the file " << filename << std::endl;
return EXIT_FAILURE;
}
stat_t s = simdjson_computestats(p);
stat_t s = simdjson_compute_stats(p);
if (!s.valid) {
std::cerr << "not a valid JSON" << std::endl;
return EXIT_FAILURE;
}
printf("# integer_count float_count string_count backslash_count "
"nonasciibyte_count object_count array_count null_count true_count "
"non_ascii_byte_count object_count array_count null_count true_count "
"false_count byte_count structural_indexes_count ");
#ifdef __linux__
printf(
" stage1_cycle_count stage1_instruction_count stage2_cycle_count "
" stage2_instruction_count stage3_cycle_count stage3_instruction_count ");
printf(" stage1_cycle_count stage1_instruction_count stage2_cycle_count "
" stage2_instruction_count stage3_cycle_count "
"stage3_instruction_count ");
#else
printf("(you are not under linux, so perf counters are disaabled)");
#endif
printf("\n");
printf("%zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu ", s.integer_count,
s.float_count, s.string_count, s.backslash_count, s.nonasciibyte_count,
s.object_count, s.array_count, s.null_count, s.true_count,
s.false_count, s.byte_count, s.structural_indexes_count);
s.float_count, s.string_count, s.backslash_count,
s.non_ascii_byte_count, s.object_count, s.array_count, s.null_count,
s.true_count, s.false_count, s.byte_count, s.structural_indexes_count);
#ifdef __linux__
ParsedJson pj;
bool allocok = pj.allocateCapacity(p.size());
simdjson::ParsedJson pj;
bool allocok = pj.allocate_capacity(p.size());
if (!allocok) {
std::cerr << "failed to allocate memory" << std::endl;
return EXIT_FAILURE;
@@ -180,19 +181,22 @@ int main(int argc, char *argv[]) {
results.resize(evts.size());
for (uint32_t i = 0; i < iterations; i++) {
unified.start();
bool isok = find_structural_bits(p.data(), p.size(), pj);
// The default template is simdjson::Architecture::NATIVE.
bool isok = (simdjson::find_structural_bits<>(p.data(), p.size(), pj) ==
simdjson::SUCCESS);
unified.end(results);
cy1 += results[0];
cl1 += results[1];
unified.start();
isok = isok && unified_machine(p.data(), p.size(), pj);
isok =
isok && (simdjson::SUCCESS == unified_machine(p.data(), p.size(), pj));
unified.end(results);
cy2 += results[0];
cl2 += results[1];
if(!isok) {
if (!isok) {
std::cerr << "failure?" << std::endl;
}
}
+32 -16
View File
@@ -10,11 +10,11 @@
// the input buf should be readable up to buf + SIMDJSON_PADDING
#ifdef __AVX2__
#define SIMDJSON_PADDING sizeof(__m256i)
#define SIMDJSON_PADDING sizeof(__m256i)
#else
// this is a stopgap; there should be a better description of the
// main loop and its behavior that abstracts over this
#define SIMDJSON_PADDING 32
#define SIMDJSON_PADDING 32
#endif
#ifndef _MSC_VER
@@ -23,7 +23,6 @@
#define SIMDJSON_USE_COMPUTED_GOTO
#endif
// Align to N-byte boundary
#define ROUNDUP_N(a, n) (((a) + ((n)-1)) & ~((n)-1))
#define ROUNDDOWN_N(a, n) ((a) & ~((n)-1))
@@ -31,9 +30,7 @@
#define ISALIGNED_N(ptr, n) (((uintptr_t)(ptr) & ((n)-1)) == 0)
#ifdef _MSC_VER
// Visual Studio won't allow it:
//#define ALLOW_SAME_PAGE_BUFFER_OVERRUN
#define really_inline inline
#define really_inline __forceinline
#define never_inline __declspec(noinline)
#define UNUSED
@@ -46,23 +43,38 @@
#define unlikely(x) x
#endif
// For Visual Studio compilers, same-page buffer overrun is not fine.
#define ALLOW_SAME_PAGE_BUFFER_OVERRUN false
#else
// for non-Visual Studio compilers, we assume that same-page buffer overrun is fine:
#ifndef ALLOW_SAME_PAGE_BUFFER_OVERRUN
#define ALLOW_SAME_PAGE_BUFFER_OVERRUN
#endif
// For non-Visual Studio compilers, we may assume that same-page buffer overrun
// is fine. However, it will make it difficult to be "valgrind clean".
//#ifndef ALLOW_SAME_PAGE_BUFFER_OVERRUN
//#define ALLOW_SAME_PAGE_BUFFER_OVERRUN true
//#else
#define ALLOW_SAME_PAGE_BUFFER_OVERRUN false
//#endif
// The following is likely unnecessarily complex.
#ifdef __SANITIZE_ADDRESS__
// we have GCC, stuck with https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67368
#undef ALLOW_SAME_PAGE_BUFFER_OVERRUN
#define ALLOW_SAME_PAGE_BUFFER_OVERRUN false
#elif defined(__has_feature)
// we have CLANG?
# if (__has_feature(address_sanitizer))
#define ALLOW_SAME_PAGE_BUFFER_OVERRUN_QUALIFIER __attribute__((no_sanitize("address")))
# endif
#endif
// todo: if we're setting ALLOW_SAME_PAGE_BUFFER_OVERRUN to false, why do we
// have a non-empty qualifier?
#if (__has_feature(address_sanitizer))
#define ALLOW_SAME_PAGE_BUFFER_OVERRUN_QUALIFIER \
__attribute__((no_sanitize("address")))
#endif
#endif
#if defined(__has_feature)
#if (__has_feature(memory_sanitizer))
#define LENIENT_MEM_SANITIZER __attribute__((no_sanitize("memory")))
#endif
#endif
#define really_inline inline __attribute__((always_inline, unused))
#define never_inline inline __attribute__((noinline, unused))
@@ -77,10 +89,14 @@
#define unlikely(x) __builtin_expect(!!(x), 0)
#endif
#endif // MSC_VER
#endif // MSC_VER
// if it does not apply, make it an empty macro
#ifndef ALLOW_SAME_PAGE_BUFFER_OVERRUN_QUALIFIER
#define ALLOW_SAME_PAGE_BUFFER_OVERRUN_QUALIFIER
#endif
#ifndef LENIENT_MEM_SANITIZER
#define LENIENT_MEM_SANITIZER
#endif
#endif // SIMDJSON_COMMON_DEFS_H
+152
View File
@@ -0,0 +1,152 @@
/* From
https://github.com/endorno/pytorch/blob/master/torch/lib/TH/generic/simd/simd.h
Highly modified.
Copyright (c) 2016- Facebook, Inc (Adam Paszke)
Copyright (c) 2014- Facebook, Inc (Soumith Chintala)
Copyright (c) 2011-2014 Idiap Research Institute (Ronan Collobert)
Copyright (c) 2012-2014 Deepmind Technologies (Koray Kavukcuoglu)
Copyright (c) 2011-2012 NEC Laboratories America (Koray Kavukcuoglu)
Copyright (c) 2011-2013 NYU (Clement Farabet)
Copyright (c) 2006-2010 NEC Laboratories America (Ronan Collobert, Leon Bottou,
Iain Melvin, Jason Weston) Copyright (c) 2006 Idiap Research Institute
(Samy Bengio) Copyright (c) 2001-2004 Idiap Research Institute (Ronan Collobert,
Samy Bengio, Johnny Mariethoz)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the names of Facebook, Deepmind Technologies, NYU, NEC Laboratories
America and IDIAP Research Institute nor the names of its contributors may be
used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SIMDJSON_ISADETECTION_H
#define SIMDJSON_ISADETECTION_H
#include <stdint.h>
#include <stdlib.h>
#if defined(_MSC_VER)
#include <intrin.h>
#elif defined(HAVE_GCC_GET_CPUID) && defined(USE_GCC_GET_CPUID)
#include <cpuid.h>
#endif
namespace simdjson {
// Can be found on Intel ISA Reference for CPUID
constexpr uint32_t cpuid_avx2_bit = 1 << 5; // Bit 5 of EBX for EAX=0x7
constexpr uint32_t cpuid_bmi1_bit = 1 << 3; // bit 3 of EBX for EAX=0x7
constexpr uint32_t cpuid_bmi2_bit = 1 << 8; // bit 8 of EBX for EAX=0x7
constexpr uint32_t cpuid_sse42_bit = 1 << 20; // bit 20 of ECX for EAX=0x1
constexpr uint32_t cpuid_pclmulqdq_bit = 1 << 1; // bit 1 of ECX for EAX=0x1
enum instruction_set {
DEFAULT = 0x0,
NEON = 0x1,
AVX2 = 0x4,
SSE42 = 0x8,
PCLMULQDQ = 0x10,
BMI1 = 0x20,
BMI2 = 0x40
};
#if defined(__arm__) || defined(__aarch64__) // incl. armel, armhf, arm64
#if defined(__NEON__)
static inline uint32_t detect_supported_architectures() {
return instruction_set::NEON;
}
#else // ARM without NEON
static inline uint32_t detect_supported_architectures() {
return instruction_set::DEFAULT;
}
#endif
#else // x86
static inline void cpuid(uint32_t *eax, uint32_t *ebx, uint32_t *ecx,
uint32_t *edx) {
#if defined(_MSC_VER)
int cpu_info[4];
__cpuid(cpu_info, *eax);
*eax = cpu_info[0];
*ebx = cpu_info[1];
*ecx = cpu_info[2];
*edx = cpu_info[3];
#elif defined(HAVE_GCC_GET_CPUID) && defined(USE_GCC_GET_CPUID)
uint32_t level = *eax;
__get_cpuid(level, eax, ebx, ecx, edx);
#else
uint32_t a = *eax, b, c = *ecx, d;
asm volatile("cpuid\n\t" : "+a"(a), "=b"(b), "+c"(c), "=d"(d));
*eax = a;
*ebx = b;
*ecx = c;
*edx = d;
#endif
}
static inline uint32_t detect_supported_architectures() {
uint32_t eax, ebx, ecx, edx;
uint32_t host_isa = 0x0;
// ECX for EAX=0x7
eax = 0x7;
ecx = 0x0;
cpuid(&eax, &ebx, &ecx, &edx);
if (ebx & cpuid_avx2_bit) {
host_isa |= instruction_set::AVX2;
}
if (ebx & cpuid_bmi1_bit) {
host_isa |= instruction_set::BMI1;
}
if (ebx & cpuid_bmi2_bit) {
host_isa |= instruction_set::BMI2;
}
// EBX for EAX=0x1
eax = 0x1;
cpuid(&eax, &ebx, &ecx, &edx);
if (ecx & cpuid_sse42_bit) {
host_isa |= instruction_set::SSE42;
}
if (ecx & cpuid_pclmulqdq_bit) {
host_isa |= instruction_set::PCLMULQDQ;
}
return host_isa;
}
#endif // end SIMD extension detection code
} // namespace simdjson
#endif
+66 -20
View File
@@ -4,6 +4,7 @@
#include "simdjson/common_defs.h"
#include "simdjson/parsedjson.h"
namespace simdjson {
// structural chars here are
// they are { 0x7b } 0x7d : 0x3a [ 0x5b ] 0x5d , 0x2c (and NULL)
// we are also interested in the four whitespace characters
@@ -11,7 +12,7 @@
// these are the chars that can follow a true/false/null or number atom
// and nothing else
const uint32_t structural_or_whitespace_negated[256] = {
const uint32_t structural_or_whitespace_or_null_negated[256] = {
0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1,
@@ -28,13 +29,36 @@ const uint32_t structural_or_whitespace_negated[256] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
// return non-zero if not a structural or whitespace char
// zero otherwise
really_inline uint32_t is_not_structural_or_whitespace_or_null(uint8_t c) {
return structural_or_whitespace_or_null_negated[c];
}
const uint32_t structural_or_whitespace_negated[256] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
// return non-zero if not a structural or whitespace char
// zero otherwise
really_inline uint32_t is_not_structural_or_whitespace(uint8_t c) {
return structural_or_whitespace_negated[c];
}
const uint32_t structural_or_whitespace[256] = {
const uint32_t structural_or_whitespace_or_null[256] = {
1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -47,11 +71,28 @@ const uint32_t structural_or_whitespace[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
really_inline uint32_t is_structural_or_whitespace_or_null(uint8_t c) {
return structural_or_whitespace_or_null[c];
}
const uint32_t structural_or_whitespace[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
really_inline uint32_t is_structural_or_whitespace(uint8_t c) {
return structural_or_whitespace[c];
}
const uint32_t digittoval32[886] = {
const uint32_t digit_to_val32[886] = {
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
@@ -60,7 +101,7 @@ const uint32_t digittoval32[886] = {
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
0x0, 0x1, 0x2, 0x3, 0x4, 0x5,
0x0, 0x1, 0x2, 0x3, 0x4, 0x5,
0x6, 0x7, 0x8, 0x9, 0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xa,
0xb, 0xc, 0xd, 0xe, 0xf, 0xFFFFFFFF,
@@ -95,7 +136,7 @@ const uint32_t digittoval32[886] = {
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
0x0, 0x10, 0x20, 0x30, 0x40, 0x50,
0x0, 0x10, 0x20, 0x30, 0x40, 0x50,
0x60, 0x70, 0x80, 0x90, 0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xa0,
0xb0, 0xc0, 0xd0, 0xe0, 0xf0, 0xFFFFFFFF,
@@ -130,7 +171,7 @@ const uint32_t digittoval32[886] = {
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
0x0, 0x100, 0x200, 0x300, 0x400, 0x500,
0x0, 0x100, 0x200, 0x300, 0x400, 0x500,
0x600, 0x700, 0x800, 0x900, 0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xa00,
0xb00, 0xc00, 0xd00, 0xe00, 0xf00, 0xFFFFFFFF,
@@ -165,7 +206,7 @@ const uint32_t digittoval32[886] = {
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
0x0, 0x1000, 0x2000, 0x3000, 0x4000, 0x5000,
0x0, 0x1000, 0x2000, 0x3000, 0x4000, 0x5000,
0x6000, 0x7000, 0x8000, 0x9000, 0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xa000,
0xb000, 0xc000, 0xd000, 0xe000, 0xf000, 0xFFFFFFFF,
@@ -201,15 +242,17 @@ const uint32_t digittoval32[886] = {
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF};
// returns a value with the high 16 bits set if not valid
// otherwise returns the conversion of the 4 hex digits at src into the bottom 16 bits of the 32-bit
// return register
// otherwise returns the conversion of the 4 hex digits at src into the bottom
// 16 bits of the 32-bit return register
//
// see https://lemire.me/blog/2019/04/17/parsing-short-hexadecimal-strings-efficiently/
static inline uint32_t hex_to_u32_nocheck(const uint8_t *src) {// strictly speaking, static inline is a C-ism
uint32_t v1 = digittoval32[630 + src[0]];
uint32_t v2 = digittoval32[420 + src[1]];
uint32_t v3 = digittoval32[210 + src[2]];
uint32_t v4 = digittoval32[0 + src[3]];
// see
// https://lemire.me/blog/2019/04/17/parsing-short-hexadecimal-strings-efficiently/
static inline uint32_t hex_to_u32_nocheck(
const uint8_t *src) { // strictly speaking, static inline is a C-ism
uint32_t v1 = digit_to_val32[630 + src[0]];
uint32_t v2 = digit_to_val32[420 + src[1]];
uint32_t v3 = digit_to_val32[210 + src[2]];
uint32_t v4 = digit_to_val32[0 + src[3]];
return v1 | v2 | v3 | v4;
}
@@ -229,19 +272,21 @@ inline size_t codepoint_to_utf8(uint32_t cp, uint8_t *c) {
if (cp <= 0x7F) {
c[0] = cp;
return 1; // ascii
} if (cp <= 0x7FF) {
}
if (cp <= 0x7FF) {
c[0] = (cp >> 6) + 192;
c[1] = (cp & 63) + 128;
return 2; // universal plane
// Surrogates are treated elsewhere...
//} //else if (0xd800 <= cp && cp <= 0xdfff) {
// return 0; // surrogates // could put assert here
// Surrogates are treated elsewhere...
//} //else if (0xd800 <= cp && cp <= 0xdfff) {
// return 0; // surrogates // could put assert here
} else if (cp <= 0xFFFF) {
c[0] = (cp >> 12) + 224;
c[1] = ((cp >> 6) & 63) + 128;
c[2] = (cp & 63) + 128;
return 3;
} else if (cp <= 0x10FFFF) { // if you know you have a valid code point, this is not needed
} else if (cp <= 0x10FFFF) { // if you know you have a valid code point, this
// is not needed
c[0] = (cp >> 18) + 240;
c[1] = ((cp >> 12) & 63) + 128;
c[2] = ((cp >> 6) & 63) + 128;
@@ -251,5 +296,6 @@ inline size_t codepoint_to_utf8(uint32_t cp, uint8_t *c) {
// will return 0 when the code point was too large.
return 0; // bad r
}
} // namespace simdjson
#endif
+144 -142
View File
@@ -5,44 +5,45 @@
#include <iomanip>
#include <iostream>
namespace simdjson {
// ends with zero char
static inline void print_with_escapes(const unsigned char *src) {
while (*src) {
switch (*src) {
case '\b':
putchar('\\');
putchar('b');
break;
case '\f':
putchar('\\');
putchar('f');
break;
case '\n':
putchar('\\');
putchar('n');
break;
case '\r':
putchar('\\');
putchar('r');
break;
case '\"':
putchar('\\');
putchar('"');
break;
case '\t':
putchar('\\');
putchar('t');
break;
case '\\':
putchar('\\');
putchar('\\');
break;
default:
if (*src <= 0x1F) {
printf("\\u%04x", *src);
} else {
putchar(*src);
}
case '\b':
putchar('\\');
putchar('b');
break;
case '\f':
putchar('\\');
putchar('f');
break;
case '\n':
putchar('\\');
putchar('n');
break;
case '\r':
putchar('\\');
putchar('r');
break;
case '\"':
putchar('\\');
putchar('"');
break;
case '\t':
putchar('\\');
putchar('t');
break;
case '\\':
putchar('\\');
putchar('\\');
break;
default:
if (*src <= 0x1F) {
printf("\\u%04x", *src);
} else {
putchar(*src);
}
}
src++;
}
@@ -53,43 +54,43 @@ static inline void print_with_escapes(const unsigned char *src,
std::ostream &os) {
while (*src) {
switch (*src) {
case '\b':
os << '\\';
os << 'b';
break;
case '\f':
os << '\\';
os << 'f';
break;
case '\n':
os << '\\';
os << 'n';
break;
case '\r':
os << '\\';
os << 'r';
break;
case '\"':
os << '\\';
os << '"';
break;
case '\t':
os << '\\';
os << 't';
break;
case '\\':
os << '\\';
os << '\\';
break;
default:
if (*src <= 0x1F) {
std::ios::fmtflags f(os.flags());
os << std::hex << std::setw(4) << std::setfill('0')
<< static_cast<int>(*src);
os.flags(f);
} else {
os << *src;
}
case '\b':
os << '\\';
os << 'b';
break;
case '\f':
os << '\\';
os << 'f';
break;
case '\n':
os << '\\';
os << 'n';
break;
case '\r':
os << '\\';
os << 'r';
break;
case '\"':
os << '\\';
os << '"';
break;
case '\t':
os << '\\';
os << 't';
break;
case '\\':
os << '\\';
os << '\\';
break;
default:
if (*src <= 0x1F) {
std::ios::fmtflags f(os.flags());
os << std::hex << std::setw(4) << std::setfill('0')
<< static_cast<int>(*src);
os.flags(f);
} else {
os << *src;
}
}
src++;
}
@@ -100,40 +101,40 @@ static inline void print_with_escapes(const unsigned char *src, size_t len) {
const unsigned char *finalsrc = src + len;
while (src < finalsrc) {
switch (*src) {
case '\b':
putchar('\\');
putchar('b');
break;
case '\f':
putchar('\\');
putchar('f');
break;
case '\n':
putchar('\\');
putchar('n');
break;
case '\r':
putchar('\\');
putchar('r');
break;
case '\"':
putchar('\\');
putchar('"');
break;
case '\t':
putchar('\\');
putchar('t');
break;
case '\\':
putchar('\\');
putchar('\\');
break;
default:
if (*src <= 0x1F) {
printf("\\u%04x", *src);
} else {
putchar(*src);
}
case '\b':
putchar('\\');
putchar('b');
break;
case '\f':
putchar('\\');
putchar('f');
break;
case '\n':
putchar('\\');
putchar('n');
break;
case '\r':
putchar('\\');
putchar('r');
break;
case '\"':
putchar('\\');
putchar('"');
break;
case '\t':
putchar('\\');
putchar('t');
break;
case '\\':
putchar('\\');
putchar('\\');
break;
default:
if (*src <= 0x1F) {
printf("\\u%04x", *src);
} else {
putchar(*src);
}
}
src++;
}
@@ -145,43 +146,43 @@ static inline void print_with_escapes(const unsigned char *src,
const unsigned char *finalsrc = src + len;
while (src < finalsrc) {
switch (*src) {
case '\b':
os << '\\';
os << 'b';
break;
case '\f':
os << '\\';
os << 'f';
break;
case '\n':
os << '\\';
os << 'n';
break;
case '\r':
os << '\\';
os << 'r';
break;
case '\"':
os << '\\';
os << '"';
break;
case '\t':
os << '\\';
os << 't';
break;
case '\\':
os << '\\';
os << '\\';
break;
default:
if (*src <= 0x1F) {
std::ios::fmtflags f(os.flags());
os << std::hex << std::setw(4) << std::setfill('0')
<< static_cast<int>(*src);
os.flags(f);
} else {
os << *src;
}
case '\b':
os << '\\';
os << 'b';
break;
case '\f':
os << '\\';
os << 'f';
break;
case '\n':
os << '\\';
os << 'n';
break;
case '\r':
os << '\\';
os << 'r';
break;
case '\"':
os << '\\';
os << '"';
break;
case '\t':
os << '\\';
os << 't';
break;
case '\\':
os << '\\';
os << '\\';
break;
default:
if (*src <= 0x1F) {
std::ios::fmtflags f(os.flags());
os << std::hex << std::setw(4) << std::setfill('0')
<< static_cast<int>(*src);
os.flags(f);
} else {
os << *src;
}
}
src++;
}
@@ -195,6 +196,7 @@ static inline void print_with_escapes(const char *src, std::ostream &os,
size_t len) {
print_with_escapes(reinterpret_cast<const unsigned char *>(src), os, len);
}
} // namespace simdjson
#
#endif
+5 -7
View File
@@ -8,11 +8,9 @@
#include <sstream>
#include <string>
#include "simdjson/padded_string.h"
namespace simdjson {
// load a file in memory...
// get a corpus; pad out to cache line so we can always use SIMD
@@ -20,15 +18,15 @@
// first element of the pair is a string (null terminated)
// whereas the second element is the length.
// caller is responsible to free (aligned_free((void*)result.data())))
//
//
// throws an exception if the file cannot be opened, use try/catch
// try {
// p = get_corpus(filename);
// } catch (const std::exception& e) {
// } catch (const std::exception& e) {
// aligned_free((void*)p.data());
// std::cout << "Could not load the file " << filename << std::endl;
// }
padded_string get_corpus(const std::string& filename);
padded_string get_corpus(const std::string &filename);
} // namespace simdjson
#endif
+14 -10
View File
@@ -1,26 +1,30 @@
#ifndef SIMDJSON_JSONMINIFIER_H
#define SIMDJSON_JSONMINIFIER_H
#include "simdjson/padded_string.h"
#include <cstddef>
#include <cstdint>
#include <string_view>
namespace simdjson {
// Take input from buf and remove useless whitespace, write it to out; buf and
// out can be the same pointer. Result is null terminated,
// return the string length (minus the null termination).
size_t jsonminify(const uint8_t *buf, size_t len, uint8_t *out);
// The accelerated version of this function only runs on AVX2 hardware.
size_t json_minify(const uint8_t *buf, size_t len, uint8_t *out);
static inline size_t jsonminify(const char *buf, size_t len, char *out) {
return jsonminify(reinterpret_cast<const uint8_t *>(buf), len, reinterpret_cast<uint8_t *>(out));
static inline size_t json_minify(const char *buf, size_t len, char *out) {
return json_minify(reinterpret_cast<const uint8_t *>(buf), len,
reinterpret_cast<uint8_t *>(out));
}
static inline size_t jsonminify(const std::string_view & p, char *out) {
return jsonminify(p.data(), p.size(), out);
static inline size_t json_minify(const std::string_view &p, char *out) {
return json_minify(p.data(), p.size(), out);
}
static inline size_t jsonminify(const padded_string & p, char *out) {
return jsonminify(p.data(), p.size(), out);
static inline size_t json_minify(const padded_string &p, char *out) {
return json_minify(p.data(), p.size(), out);
}
} // namespace simdjson
#endif
+214 -83
View File
@@ -1,136 +1,267 @@
#ifndef SIMDJSON_JSONPARSER_H
#define SIMDJSON_JSONPARSER_H
#include <string>
#include "simdjson/common_defs.h"
#include "simdjson/padded_string.h"
#include "simdjson/jsonioutil.h"
#include "simdjson/padded_string.h"
#include "simdjson/parsedjson.h"
#include "simdjson/simdjson.h"
#include "simdjson/stage1_find_marks.h"
#include "simdjson/stage2_build_tape.h"
#include "simdjson/simdjson.h"
#include <string>
#ifdef _MSC_VER
#include <windows.h>
// must be included after windows.h
#include <sysinfoapi.h>
#else
#include <unistd.h>
#endif
// Parse a document found in buf.
// You need to preallocate ParsedJson with a capacity of len (e.g., pj.allocateCapacity(len)).
// Return 0 on success, an error code from simdjson/simdjson.h otherwise
// You can also check validity by calling pj.isValid(). The same ParsedJson can be reused for other documents.
//
// If reallocifneeded is true (default) then a temporary buffer is created when needed during processing
// (a copy of the input string is made).
// The input buf should be readable up to buf + len + SIMDJSON_PADDING if reallocifneeded is false,
// all bytes at and after buf + len are ignored (can be garbage).
// The ParsedJson object can be reused.
WARN_UNUSED
int json_parse(const uint8_t *buf, size_t len, ParsedJson &pj, bool reallocifneeded = true);
namespace simdjson {
// The function that users are expected to call is json_parse.
// We have more than one such function because we want to support several
// instruction sets.
// Parse a document found in buf.
// You need to preallocate ParsedJson with a capacity of len (e.g., pj.allocateCapacity(len)).
// Return SUCCESS (an integer = 1) in case of a success. You can also check validity
// by calling pj.isValid(). The same ParsedJson can be reused for other documents.
//
// If reallocifneeded is true (default) then a temporary buffer is created when needed during processing
// (a copy of the input string is made).
// The input buf should be readable up to buf + len + SIMDJSON_PADDING if reallocifneeded is false,
// all bytes at and after buf + len are ignored (can be garbage).
// The ParsedJson object can be reused.
WARN_UNUSED
inline int json_parse(const char * buf, size_t len, ParsedJson &pj, bool reallocifneeded = true) {
return json_parse(reinterpret_cast<const uint8_t *>(buf), len, pj, reallocifneeded);
// function pointer type for json_parse
using json_parse_functype = int(const uint8_t *buf, size_t len, ParsedJson &pj,
bool realloc_if_needed);
// Pointer that holds the json_parse implementation corresponding to the
// available SIMD instruction set
extern json_parse_functype *json_parse_ptr;
// json_parse_implementation is the generic function, it is specialized for
// various architectures, e.g., as
// json_parse_implementation<Architecture::HASWELL> or
// json_parse_implementation<Architecture::ARM64>
template <Architecture T>
int json_parse_implementation(const uint8_t *buf, size_t len, ParsedJson &pj,
bool realloc_if_needed = true) {
if (pj.byte_capacity < len) {
return simdjson::CAPACITY;
}
bool reallocated = false;
if (realloc_if_needed) {
#if ALLOW_SAME_PAGE_BUFFER_OVERRUN
// realloc is needed if the end of the memory crosses a page
#ifdef _MSC_VER
SYSTEM_INFO sysInfo;
GetSystemInfo(&sysInfo);
long page_size = sysInfo.dwPageSize;
#else
long page_size = sysconf(_SC_PAGESIZE);
#endif
//////////////
// We want to check that buf + len - 1 and buf + len - 1 + SIMDJSON_PADDING
// are in the same page.
// That is, we want to check that
// (buf + len - 1) / page_size == (buf + len - 1 + SIMDJSON_PADDING) /
// page_size That's true if (buf + len - 1) % page_size + SIMDJSON_PADDING <
// page_size.
///////////
if ((reinterpret_cast<uintptr_t>(buf + len - 1) % page_size) +
SIMDJSON_PADDING <
static_cast<uintptr_t>(page_size)) {
#else // SIMDJSON_SAFE_SAME_PAGE_READ_OVERRUN
if (true) { // if not SIMDJSON_SAFE_SAME_PAGE_READ_OVERRUN, we always
// reallocate
#endif
const uint8_t *tmp_buf = buf;
buf = (uint8_t *)allocate_padded_buffer(len);
if (buf == NULL)
return simdjson::MEMALLOC;
memcpy((void *)buf, tmp_buf, len);
reallocated = true;
} // if (true) OR if ( (reinterpret_cast<uintptr_t>(buf + len - 1) %
// page_size ) + SIMDJSON_PADDING < static_cast<uintptr_t>(page_size) ) {
} // if(realloc_if_needed) {
int stage1_is_ok = simdjson::find_structural_bits<T>(buf, len, pj);
if (stage1_is_ok != simdjson::SUCCESS) {
pj.error_code = stage1_is_ok;
return pj.error_code;
}
int res = unified_machine<T>(buf, len, pj);
if (reallocated) {
aligned_free((void *)buf);
}
return res;
}
// Parse a document found in buf.
// You need to preallocate ParsedJson with a capacity of len (e.g., pj.allocateCapacity(len)).
// Return SUCCESS (an integer = 1) in case of a success. You can also check validity
// by calling pj.isValid(). The same ParsedJson can be reused for other documents.
//
// If reallocifneeded is true (default) then a temporary buffer is created when needed during processing
// (a copy of the input string is made).
// The input s should be readable up to s.data() + s.size() + SIMDJSON_PADDING if reallocifneeded is false,
// all bytes at and after s.data()+s.size() are ignored (can be garbage).
// The ParsedJson object can be reused.
//WARN_UNUSED
//inline int json_parse(const std::string_view &s, ParsedJson &pj, bool reallocifneeded = true) {
// return json_parse(s.data(), s.size(), pj, reallocifneeded);
//}
// The content should be a valid JSON document encoded as UTF-8. If there is a
// UTF-8 BOM, the caller is responsible for omitting it, UTF-8 BOM are
// discouraged.
//
// You need to preallocate ParsedJson with a capacity of len (e.g.,
// pj.allocate_capacity(len)).
//
// The function returns simdjson::SUCCESS (an integer = 0) in case of a success
// or an error code from simdjson/simdjson.h in case of failure such as
// simdjson::CAPACITY, simdjson::MEMALLOC, simdjson::DEPTH_ERROR and so forth;
// the simdjson::error_message function converts these error codes into a
// string).
//
// You can also check validity by calling pj.is_valid(). The same ParsedJson can
// be reused for other documents.
//
// If realloc_if_needed is true (default) then a temporary buffer is created
// when needed during processing (a copy of the input string is made). The input
// buf should be readable up to buf + len + SIMDJSON_PADDING if
// realloc_if_needed is false, all bytes at and after buf + len are ignored
// (can be garbage). The ParsedJson object can be reused.
inline int json_parse(const uint8_t *buf, size_t len, ParsedJson &pj,
bool realloc_if_needed = true) {
return json_parse_ptr(buf, len, pj, realloc_if_needed);
}
// Parse a document found in buf.
//
// The content should be a valid JSON document encoded as UTF-8. If there is a
// UTF-8 BOM, the caller is responsible for omitting it, UTF-8 BOM are
// discouraged.
//
// You need to preallocate ParsedJson with a capacity of len (e.g.,
// pj.allocate_capacity(len)).
//
// The function returns simdjson::SUCCESS (an integer = 0) in case of a success
// or an error code from simdjson/simdjson.h in case of failure such as
// simdjson::CAPACITY, simdjson::MEMALLOC, simdjson::DEPTH_ERROR and so forth;
// the simdjson::error_message function converts these error codes into a
// string).
//
// You can also check validity
// by calling pj.is_valid(). The same ParsedJson can be reused for other
// documents.
//
// If realloc_if_needed is true (default) then a temporary buffer is created
// when needed during processing (a copy of the input string is made). The input
// buf should be readable up to buf + len + SIMDJSON_PADDING if
// realloc_if_needed is false, all bytes at and after buf + len are ignored
// (can be garbage). The ParsedJson object can be reused.
inline int json_parse(const char *buf, size_t len, ParsedJson &pj,
bool realloc_if_needed = true) {
return json_parse_ptr(reinterpret_cast<const uint8_t *>(buf), len, pj,
realloc_if_needed);
}
// We do not want to allow implicit conversion from C string to std::string.
int json_parse(const char *buf, ParsedJson &pj) = delete;
// Parse a document found in in string s.
// You need to preallocate ParsedJson with a capacity of len (e.g., pj.allocateCapacity(len)).
// Return SUCCESS (an integer = 1) in case of a success. You can also check validity
// by calling pj.isValid(). The same ParsedJson can be reused for other documents.
// You need to preallocate ParsedJson with a capacity of len (e.g.,
// pj.allocate_capacity(len)).
//
// The function returns simdjson::SUCCESS (an integer = 0) in case of a success
// or an error code from simdjson/simdjson.h in case of failure such as
// simdjson::CAPACITY, simdjson::MEMALLOC, simdjson::DEPTH_ERROR and so forth;
// the simdjson::error_message function converts these error codes into a
// string).
//
// A temporary buffer is created when needed during processing
// (a copy of the input string is made).
WARN_UNUSED
inline int json_parse(const std::string &s, ParsedJson &pj) {
return json_parse(s.data(), s.length(), pj, true);
}
// Parse a document found in in string s.
// You need to preallocate ParsedJson with a capacity of len (e.g., pj.allocateCapacity(len)).
// Return SUCCESS (an integer = 1) in case of a success. You can also check validity
// by calling pj.isValid(). The same ParsedJson can be reused for other documents.
WARN_UNUSED
//
// The content should be a valid JSON document encoded as UTF-8. If there is a
// UTF-8 BOM, the caller is responsible for omitting it, UTF-8 BOM are
// discouraged.
//
// You need to preallocate ParsedJson with a capacity of len (e.g.,
// pj.allocate_capacity(len)).
//
// The function returns simdjson::SUCCESS (an integer = 0) in case of a success
// or an error code from simdjson/simdjson.h in case of failure such as
// simdjson::CAPACITY, simdjson::MEMALLOC, simdjson::DEPTH_ERROR and so forth;
// the simdjson::error_message function converts these error codes into a
// string).
//
// You can also check validity
// by calling pj.is_valid(). The same ParsedJson can be reused for other
// documents.
inline int json_parse(const padded_string &s, ParsedJson &pj) {
return json_parse(s.data(), s.length(), pj, false);
}
// Build a ParsedJson object. You can check validity
// by calling pj.isValid(). This does the memory allocation needed for ParsedJson.
// If reallocifneeded is true (default) then a temporary buffer is created when needed during processing
// (a copy of the input string is made).
// by calling pj.is_valid(). This does the memory allocation needed for
// ParsedJson. If realloc_if_needed is true (default) then a temporary buffer is
// created when needed during processing (a copy of the input string is made).
//
// the input buf should be readable up to buf + len + SIMDJSON_PADDING if reallocifneeded is false,
// all bytes at and after buf + len are ignored (can be garbage).
// The input buf should be readable up to buf + len + SIMDJSON_PADDING if
// realloc_if_needed is false, all bytes at and after buf + len are ignored
// (can be garbage).
//
// The content should be a valid JSON document encoded as UTF-8. If there is a
// UTF-8 BOM, the caller is responsible for omitting it, UTF-8 BOM are
// discouraged.
//
// This is a convenience function which calls json_parse.
WARN_UNUSED
ParsedJson build_parsed_json(const uint8_t *buf, size_t len, bool reallocifneeded = true);
ParsedJson build_parsed_json(const uint8_t *buf, size_t len,
bool realloc_if_needed = true);
WARN_UNUSED
// Build a ParsedJson object. You can check validity
// by calling pj.isValid(). This does the memory allocation needed for ParsedJson.
// If reallocifneeded is true (default) then a temporary buffer is created when needed during processing
// (a copy of the input string is made).
// The input buf should be readable up to buf + len + SIMDJSON_PADDING if reallocifneeded is false,
// all bytes at and after buf + len are ignored (can be garbage).
inline ParsedJson build_parsed_json(const char * buf, size_t len, bool reallocifneeded = true) {
return build_parsed_json(reinterpret_cast<const uint8_t *>(buf), len, reallocifneeded);
// by calling pj.is_valid(). This does the memory allocation needed for
// ParsedJson. If realloc_if_needed is true (default) then a temporary buffer is
// created when needed during processing (a copy of the input string is made).
//
// The input buf should be readable up to buf + len + SIMDJSON_PADDING if
// realloc_if_needed is false, all bytes at and after buf + len are ignored
// (can be garbage).
//
//
// The content should be a valid JSON document encoded as UTF-8. If there is a
// UTF-8 BOM, the caller is responsible for omitting it, UTF-8 BOM are
// discouraged.
//
// This is a convenience function which calls json_parse.
inline ParsedJson build_parsed_json(const char *buf, size_t len,
bool realloc_if_needed = true) {
return build_parsed_json(reinterpret_cast<const uint8_t *>(buf), len,
realloc_if_needed);
}
// convenience function
WARN_UNUSED
// Build a ParsedJson object. You can check validity
// by calling pj.isValid(). This does the memory allocation needed for ParsedJson.
// If reallocifneeded is true (default) then a temporary buffer is created when needed during processing
// (a copy of the input string is made).
// The input s should be readable up to s.data() + s.size() + SIMDJSON_PADDING if reallocifneeded is false,
// all bytes at and after s.data()+s.size() are ignored (can be garbage).
//inline ParsedJson build_parsed_json(const std::string_view &s, bool reallocifneeded = true) {
// return build_parsed_json(s.data(), s.size(), reallocifneeded);
//}
// We do not want to allow implicit conversion from C string to std::string.
ParsedJson build_parsed_json(const char *buf) = delete;
// Parse a document found in in string s.
// You need to preallocate ParsedJson with a capacity of len (e.g., pj.allocateCapacity(len)).
// Return SUCCESS (an integer = 1) in case of a success. You can also check validity
// by calling pj.isValid(). The same ParsedJson can be reused for other documents.
// You need to preallocate ParsedJson with a capacity of len (e.g.,
// pj.allocate_capacity(len)). Return SUCCESS (an integer = 0) in case of a
// success. You can also check validity by calling pj.is_valid(). The same
// ParsedJson can be reused for other documents.
//
// A temporary buffer is created when needed during processing
// (a copy of the input string is made).
//
// The content should be a valid JSON document encoded as UTF-8. If there is a
// UTF-8 BOM, the caller is responsible for omitting it, UTF-8 BOM are
// discouraged.
//
// This is a convenience function which calls json_parse.
WARN_UNUSED
inline ParsedJson build_parsed_json(const std::string &s) {
return build_parsed_json(s.data(), s.length(), true);
}
// Parse a document found in in string s.
// You need to preallocate ParsedJson with a capacity of len (e.g., pj.allocateCapacity(len)).
// Return SUCCESS (an integer = 1) in case of a success. You can also check validity
// by calling pj.isValid(). The same ParsedJson can be reused for other documents.
// You need to preallocate ParsedJson with a capacity of len (e.g.,
// pj.allocate_capacity(len)). Return SUCCESS (an integer = 0) in case of a
// success. You can also check validity by calling pj.is_valid(). The same
// ParsedJson can be reused for other documents.
//
// The content should be a valid JSON document encoded as UTF-8. If there is a
// UTF-8 BOM, the caller is responsible for omitting it, UTF-8 BOM are
// discouraged.
//
// This is a convenience function which calls json_parse.
WARN_UNUSED
inline ParsedJson build_parsed_json(const padded_string &s) {
return build_parsed_json(s.data(), s.length(), false);
}
} // namespace simdjson
#endif
+194 -146
View File
@@ -6,6 +6,18 @@
#include "simdjson/parsedjson.h"
#include "simdjson/portability.h"
#ifdef JSON_TEST_NUMBERS // for unit testing
void found_invalid_number(const uint8_t *buf);
void found_integer(int64_t result, const uint8_t *buf);
void found_float(double result, const uint8_t *buf);
#endif
namespace simdjson {
// Allowable floating-point values range from
// std::numeric_limits<double>::lowest() to std::numeric_limits<double>::max(),
// so from -1.7976e308 all the way to 1.7975e308 in binary64. The lowest
// non-zero normal values is std::numeric_limits<double>::min() or
// about 2.225074e-308.
static const double power_of_ten[] = {
1e-308, 1e-307, 1e-306, 1e-305, 1e-304, 1e-303, 1e-302, 1e-301, 1e-300,
1e-299, 1e-298, 1e-297, 1e-296, 1e-295, 1e-294, 1e-293, 1e-292, 1e-291,
@@ -86,7 +98,7 @@ static inline bool is_integer(char c) {
// probably frequent and it is hard than it looks. We are building all of this
// just to differentiate between 0x1 (invalid), 0,1 (valid) 0e1 (valid)...
const bool structural_or_whitespace_or_exponent_or_decimal_negated[256] = {
0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1,
@@ -99,23 +111,23 @@ const bool structural_or_whitespace_or_exponent_or_decimal_negated[256] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
really_inline bool
is_not_structural_or_whitespace_or_exponent_or_decimal_or_null(unsigned char c) {
is_not_structural_or_whitespace_or_exponent_or_decimal(unsigned char c) {
return structural_or_whitespace_or_exponent_or_decimal_negated[c];
}
#ifdef __AVX2__
} // namespace simdjson
#ifndef SIMDJSON_DISABLE_SWAR_NUMBER_PARSING
#define SWAR_NUMBER_PARSING
#endif
#ifdef SWAR_NUMBER_PARSING
#ifdef _MSC_VER
namespace simdjson {
// check quickly whether the next 8 chars are made of digits
// at a glance, it looks better than Mula's
// http://0x80.pl/articles/swar-digits-validate.html
static inline bool is_made_of_eight_digits_fast(const char *chars) {
uint64_t val;
// this can read up to 7 bytes beyond the buffer size, but we require
// this can read up to 7 bytes beyond the buffer size, but we require
// SIMDJSON_PADDING of padding
static_assert(7 <= SIMDJSON_PADDING);
memcpy(&val, chars, 8);
@@ -127,37 +139,10 @@ static inline bool is_made_of_eight_digits_fast(const char *chars) {
(((val + 0x0606060606060606) & 0xF0F0F0F0F0F0F0F0) >> 4)) ==
0x3333333333333333);
}
#else
// this is more efficient apparently than the scalar code above (fewer instructions)
static inline bool is_made_of_eight_digits_fast(const char *chars) {
__m64 val;
// this can read up to 7 bytes beyond the buffer size, but we require
// SIMDJSON_PADDING of padding
static_assert(7 <= SIMDJSON_PADDING);
memcpy(&val, chars, 8);
__m64 base = _mm_sub_pi8(val,_mm_set1_pi8('0'));
__m64 basecmp = _mm_subs_pu8(base,_mm_set1_pi8(9));
return _mm_cvtm64_si64(basecmp) == 0;
}
#endif
// clang-format off
/***
Should parse_eight_digits_unrolled be out of the question, one could
use a standard approach like the following:
static inline uint32_t newparse_eight_digits_unrolled(const char *chars) {
uint64_t val;
memcpy(&val, chars, sizeof(uint64_t));
val = (val & 0x0F0F0F0F0F0F0F0F) * 2561 >> 8;
val = (val & 0x00FF00FF00FF00FF) * 6553601 >> 16;
return (val & 0x0000FFFF0000FFFF) * 42949672960001 >> 32;
}
credit: https://johnnylee-sde.github.io/Fast-numeric-string-to-int/
*/
// clang-format on
} // namespace simdjson
#ifdef IS_X86_64
TARGET_WESTMERE
namespace simdjson {
static inline uint32_t parse_eight_digits_unrolled(const char *chars) {
// this actually computes *16* values so we are being wasteful.
const __m128i ascii0 = _mm_set1_epi8('0');
@@ -166,7 +151,8 @@ static inline uint32_t parse_eight_digits_unrolled(const char *chars) {
const __m128i mul_1_100 = _mm_setr_epi16(100, 1, 100, 1, 100, 1, 100, 1);
const __m128i mul_1_10000 =
_mm_setr_epi16(10000, 1, 10000, 1, 10000, 1, 10000, 1);
const __m128i input = _mm_sub_epi8(_mm_loadu_si128(reinterpret_cast<const __m128i *>(chars)), ascii0);
const __m128i input = _mm_sub_epi8(
_mm_loadu_si128(reinterpret_cast<const __m128i *>(chars)), ascii0);
const __m128i t1 = _mm_maddubs_epi16(input, mul_1_10);
const __m128i t2 = _mm_madd_epi16(t1, mul_1_100);
const __m128i t3 = _mm_packus_epi32(t2, t2);
@@ -174,9 +160,33 @@ static inline uint32_t parse_eight_digits_unrolled(const char *chars) {
return _mm_cvtsi128_si32(
t4); // only captures the sum of the first 8 digits, drop the rest
}
} // namespace simdjson
UNTARGET_REGION
#endif
namespace simdjson {
#ifdef IS_ARM64
// we don't have SSE, so let us use a scalar function
// credit: https://johnnylee-sde.github.io/Fast-numeric-string-to-int/
static inline uint32_t parse_eight_digits_unrolled(const char *chars) {
uint64_t val;
memcpy(&val, chars, sizeof(uint64_t));
val = (val & 0x0F0F0F0F0F0F0F0F) * 2561 >> 8;
val = (val & 0x00FF00FF00FF00FF) * 6553601 >> 16;
return (val & 0x0000FFFF0000FFFF) * 42949672960001 >> 32;
}
#endif
#endif
//
// This function computes base * 10 ^ (- negative_exponent ).
// It is only even going to be used when negative_exponent is tiny.
static double subnormal_power10(double base, int negative_exponent) {
// this is probably not going to be fast
return base * 1e-308 * pow(10, negative_exponent + 308);
}
// called by parse_number when we know that the output is a float,
// but where there might be some integer overflow. The trick here is to
// parse using floats from the start.
@@ -187,17 +197,15 @@ static inline uint32_t parse_eight_digits_unrolled(const char *chars) {
//
// Note: a redesign could avoid this function entirely.
//
static never_inline bool
parse_float(const uint8_t *const buf,
ParsedJson &pj, const uint32_t offset,
bool found_minus) {
static never_inline bool parse_float(const uint8_t *const buf, ParsedJson &pj,
const uint32_t offset, bool found_minus) {
const char *p = reinterpret_cast<const char *>(buf + offset);
bool negative = false;
if (found_minus) {
++p;
negative = true;
}
double i;
long double i;
if (*p == '0') { // 0 cannot be followed by an integer
++p;
i = 0;
@@ -213,82 +221,102 @@ parse_float(const uint8_t *const buf,
}
if ('.' == *p) {
++p;
double fractionalweight = 1;
if(is_integer(*p)) {
int fractional_weight = 308;
if (is_integer(*p)) {
unsigned char digit = *p - '0';
++p;
fractionalweight *= 0.1;
i = i + digit * fractionalweight;
fractional_weight--;
i = i + digit * (fractional_weight >= 0 ? power_of_ten[fractional_weight]
: 0);
} else {
#ifdef JSON_TEST_NUMBERS // for unit testing
foundInvalidNumber(buf + offset);
found_invalid_number(buf + offset);
#endif
return false;
}
while (is_integer(*p)) {
unsigned char digit = *p - '0';
++p;
fractionalweight *= 0.1;
i = i + digit * fractionalweight;
fractional_weight--;
i = i + digit * (fractional_weight >= 0 ? power_of_ten[fractional_weight]
: 0);
}
}
if (('e' == *p) || ('E' == *p)) {
++p;
bool negexp = false;
bool neg_exp = false;
if ('-' == *p) {
negexp = true;
neg_exp = true;
++p;
} else if ('+' == *p) {
++p;
}
if (!is_integer(*p)) {
#ifdef JSON_TEST_NUMBERS // for unit testing
foundInvalidNumber(buf + offset);
found_invalid_number(buf + offset);
#endif
return false;
}
unsigned char digit = *p - '0';
int64_t expnumber = digit; // exponential part
int64_t exp_number = digit; // exponential part
p++;
if (is_integer(*p)) {
digit = *p - '0';
expnumber = 10 * expnumber + digit;
exp_number = 10 * exp_number + digit;
++p;
}
if (is_integer(*p)) {
digit = *p - '0';
expnumber = 10 * expnumber + digit;
exp_number = 10 * exp_number + digit;
++p;
}
if (is_integer(*p)) {
digit = *p - '0';
expnumber = 10 * expnumber + digit;
exp_number = 10 * exp_number + digit;
++p;
}
if (is_integer(*p)) {
while (is_integer(*p)) {
if (exp_number > 0x100000000) { // we need to check for overflows
// we refuse to parse this
#ifdef JSON_TEST_NUMBERS // for unit testing
foundInvalidNumber(buf + offset);
found_invalid_number(buf + offset);
#endif
return false;
return false;
}
digit = *p - '0';
exp_number = 10 * exp_number + digit;
++p;
}
if (expnumber > 308) {
if (unlikely(exp_number > 308)) {
// this path is unlikely
if (neg_exp) {
// We either have zero or a subnormal.
// We expect this to be uncommon so we go through a slow path.
i = subnormal_power10(i, -exp_number);
} else {
// We know for sure that we have a number that is too large,
// we refuse to parse this
#ifdef JSON_TEST_NUMBERS // for unit testing
foundInvalidNumber(buf + offset);
found_invalid_number(buf + offset);
#endif
return false;
return false;
}
} else {
int exponent = (neg_exp ? -exp_number : exp_number);
// we have that exp_number is [0,308] so that
// exponent is [-308,308] so that
// 308 + exponent is in [0, 2 * 308]
i *= power_of_ten[308 + exponent];
}
int exponent = (negexp ? -expnumber : expnumber);
i *= power_of_ten[308 + exponent];
}
if(is_not_structural_or_whitespace(*p)) {
if (is_not_structural_or_whitespace(*p)) {
return false;
}
double d = negative ? -i : i;
pj.write_tape_double(d);
#ifdef JSON_TEST_NUMBERS // for unit testing
foundFloat(d, buf + offset);
found_float(d, buf + offset);
#endif
return is_structural_or_whitespace(*p);
}
@@ -326,13 +354,13 @@ static never_inline bool parse_large_integer(const uint8_t *const buf,
digit = *p - '0';
if (mul_overflow(i, 10, &i)) {
#ifdef JSON_TEST_NUMBERS // for unit testing
foundInvalidNumber(buf + offset);
found_invalid_number(buf + offset);
#endif
return false; // overflow
}
if (add_overflow(i, digit, &i)) {
#ifdef JSON_TEST_NUMBERS // for unit testing
foundInvalidNumber(buf + offset);
found_invalid_number(buf + offset);
#endif
return false; // overflow
}
@@ -343,7 +371,7 @@ static never_inline bool parse_large_integer(const uint8_t *const buf,
if (i > 0x8000000000000000) {
// overflows!
#ifdef JSON_TEST_NUMBERS // for unit testing
foundInvalidNumber(buf + offset);
found_invalid_number(buf + offset);
#endif
return false; // overflow
}
@@ -351,31 +379,36 @@ static never_inline bool parse_large_integer(const uint8_t *const buf,
if (i >= 0x8000000000000000) {
// overflows!
#ifdef JSON_TEST_NUMBERS // for unit testing
foundInvalidNumber(buf + offset);
found_invalid_number(buf + offset);
#endif
return false; // overflow
}
}
int64_t signed_answer = negative ? -static_cast<int64_t>(i) : static_cast<int64_t>(i);
int64_t signed_answer =
negative ? -static_cast<int64_t>(i) : static_cast<int64_t>(i);
pj.write_tape_s64(signed_answer);
#ifdef JSON_TEST_NUMBERS // for unit testing
foundInteger(signed_answer, buf + offset);
found_integer(signed_answer, buf + offset);
#endif
return is_structural_or_whitespace(*p);
}
// parse the number at buf + offset
// define JSON_TEST_NUMBERS for unit testing
static really_inline bool parse_number(const uint8_t *const buf,
ParsedJson &pj,
//
// It is assumed that the number is followed by a structural ({,},],[) character
// or a white space character. If that is not the case (e.g., when the JSON
// document is made of a single number), then it is necessary to copy the
// content and append a space before calling this function.
//
// Our objective is accurate parsing (ULP of 0 or 1) at high speed.
static really_inline bool parse_number(const uint8_t *const buf, ParsedJson &pj,
const uint32_t offset,
bool found_minus) {
#ifdef SIMDJSON_SKIPNUMBERPARSING // for performance analysis, it is sometimes useful to skip parsing
pj.write_tape_s64(0); // always write zero
return true; // always succeeds
#ifdef SIMDJSON_SKIPNUMBERPARSING // for performance analysis, it is sometimes
// useful to skip parsing
pj.write_tape_s64(0); // always write zero
return true; // always succeeds
#else
const char *p = reinterpret_cast<const char *>(buf + offset);
bool negative = false;
@@ -383,28 +416,28 @@ static really_inline bool parse_number(const uint8_t *const buf,
++p;
negative = true;
if (!is_integer(*p)) { // a negative sign must be followed by an integer
#ifdef JSON_TEST_NUMBERS // for unit testing
foundInvalidNumber(buf + offset);
#ifdef JSON_TEST_NUMBERS // for unit testing
found_invalid_number(buf + offset);
#endif
return false;
}
}
const char *const startdigits = p;
const char *const start_digits = p;
uint64_t i; // an unsigned int avoids signed overflows (which are bad)
uint64_t i; // an unsigned int avoids signed overflows (which are bad)
if (*p == '0') { // 0 cannot be followed by an integer
++p;
if (is_not_structural_or_whitespace_or_exponent_or_decimal_or_null(*p)) {
if (is_not_structural_or_whitespace_or_exponent_or_decimal(*p)) {
#ifdef JSON_TEST_NUMBERS // for unit testing
foundInvalidNumber(buf + offset);
found_invalid_number(buf + offset);
#endif
return false;
}
i = 0;
} else {
if (!(is_integer(*p))) { // must start with an integer
#ifdef JSON_TEST_NUMBERS // for unit testing
foundInvalidNumber(buf + offset);
#ifdef JSON_TEST_NUMBERS // for unit testing
found_invalid_number(buf + offset);
#endif
return false;
}
@@ -415,25 +448,33 @@ static really_inline bool parse_number(const uint8_t *const buf,
// we rarely see large integer parts like 123456789
while (is_integer(*p)) {
digit = *p - '0';
i = 10 * i + digit; // might overflow
// a multiplication by 10 is cheaper than an arbitrary integer
// multiplication
i = 10 * i + digit; // might overflow, we will handle the overflow later
++p;
}
}
int64_t exponent = 0;
bool is_float = false;
if ('.' == *p) {
is_float = true; // At this point we know that we have a float
// we continue with the fiction that we have an integer. If the
// floating point number is representable as x * 10^z for some integer
// z that fits in 53 bits, then we will be able to convert back the
// the integer into a float in a lossless manner.
++p;
const char *const firstafterperiod = p;
if(is_integer(*p)) {
const char *const first_after_period = p;
if (is_integer(*p)) {
unsigned char digit = *p - '0';
++p;
i = i * 10 + digit;
i = i * 10 + digit; // might overflow + multiplication by 10 is likely
// cheaper than arbitrary mult.
// we will handle the overflow later
} else {
#ifdef JSON_TEST_NUMBERS // for unit testing
foundInvalidNumber(buf + offset);
found_invalid_number(buf + offset);
#endif
return false;
return false;
}
#ifdef SWAR_NUMBER_PARSING
// this helps if we have lots of decimals!
@@ -446,94 +487,101 @@ static really_inline bool parse_number(const uint8_t *const buf,
while (is_integer(*p)) {
unsigned char digit = *p - '0';
++p;
i = i * 10 + digit; // in rare cases, this will overflow, but that's ok because we have parse_highprecision_float later.
i = i * 10 + digit; // in rare cases, this will overflow, but that's ok
// because we have parse_highprecision_float later.
}
exponent = firstafterperiod - p;
exponent = first_after_period - p;
}
int digitcount = p - startdigits - 1;
int64_t expnumber = 0; // exponential part
int digit_count =
p - start_digits - 1; // used later to guard against overflows
int64_t exp_number = 0; // exponential part
if (('e' == *p) || ('E' == *p)) {
is_float = true;
++p;
bool negexp = false;
bool neg_exp = false;
if ('-' == *p) {
negexp = true;
neg_exp = true;
++p;
} else if ('+' == *p) {
++p;
}
if (!is_integer(*p)) {
#ifdef JSON_TEST_NUMBERS // for unit testing
foundInvalidNumber(buf + offset);
found_invalid_number(buf + offset);
#endif
return false;
}
unsigned char digit = *p - '0';
expnumber = digit;
exp_number = digit;
p++;
if (is_integer(*p)) {
digit = *p - '0';
expnumber = 10 * expnumber + digit;
exp_number = 10 * exp_number + digit;
++p;
}
if (is_integer(*p)) {
digit = *p - '0';
expnumber = 10 * expnumber + digit;
exp_number = 10 * exp_number + digit;
++p;
}
if (is_integer(*p)) {
// we refuse to parse this
while (is_integer(*p)) {
if (exp_number > 0x100000000) { // we need to check for overflows
// we refuse to parse this
#ifdef JSON_TEST_NUMBERS // for unit testing
foundInvalidNumber(buf + offset);
found_invalid_number(buf + offset);
#endif
return false;
return false;
}
digit = *p - '0';
exp_number = 10 * exp_number + digit;
++p;
}
if(expnumber > 308) {
// we refuse to parse this
#ifdef JSON_TEST_NUMBERS // for unit testing
foundInvalidNumber(buf + offset);
#endif
return false;
}
exponent += (negexp ? -expnumber : expnumber);
exponent += (neg_exp ? -exp_number : exp_number);
}
if ((exponent != 0) || (expnumber != 0)) {
if (unlikely(digitcount >= 19)) { // this is uncommon!!!
if (is_float) {
uint64_t power_index = 308 + exponent;
if (unlikely((digit_count >= 19))) { // this is uncommon
// It is possible that the integer had an overflow.
// We have to handle the case where we have 0.0000somenumber.
const char *start = start_digits;
while ((*start == '0') || (*start == '.')) {
start++;
}
// we over-decrement by one when there is a '.'
digit_count -= (start - start_digits);
if (digit_count >= 19) {
// Ok, chances are good that we had an overflow!
// this is almost never going to get called!!!
// we start anew, going slowly!!!
return parse_float(buf, pj, offset, found_minus);
}
}
if (unlikely((power_index > 2 * 308))) { // this is uncommon!!!
// this is almost never going to get called!!!
// we start anew, going slowly!!!
return parse_float(buf, pj, offset,
found_minus);
return parse_float(buf, pj, offset, found_minus);
}
///////////
// We want 0.1e1 to be a float.
//////////
if (i == 0) {
pj.write_tape_double(0.0);
double factor = power_of_ten[power_index];
factor = negative ? -factor : factor;
double d = i * factor;
pj.write_tape_double(d);
#ifdef JSON_TEST_NUMBERS // for unit testing
foundFloat(0.0, buf + offset);
found_float(d, buf + offset);
#endif
} else {
double d = i;
d = negative ? -d : d;
d *= power_of_ten[308 + exponent];
pj.write_tape_double(d);
#ifdef JSON_TEST_NUMBERS // for unit testing
foundFloat(d, buf + offset);
#endif
}
} else {
if (unlikely(digitcount >= 18)) { // this is uncommon!!!
return parse_large_integer(buf, pj, offset,
found_minus);
if (unlikely(digit_count >= 18)) { // this is uncommon!!!
// there is a good chance that we had an overflow, so we need
// need to recover: we parse the whole thing again.
return parse_large_integer(buf, pj, offset, found_minus);
}
i = negative ? 0-i : i;
i = negative ? 0 - i : i;
pj.write_tape_s64(i);
#ifdef JSON_TEST_NUMBERS // for unit testing
foundInteger(i, buf + offset);
found_integer(i, buf + offset);
#endif
}
return is_structural_or_whitespace(*p);
return is_structural_or_whitespace(*p);
#endif // SIMDJSON_SKIPNUMBERPARSING
}
} // simdjson
#endif
+4 -1
View File
@@ -1,8 +1,10 @@
#ifndef SIMDJSON_PADDING_STRING_H
#define SIMDJSON_PADDING_STRING_H
#include "simdjson/portability.h"
#include <memory>
#include <cstring>
#include <memory>
namespace simdjson {
// low-level function to allocate memory with padding so we can read passed the
// "length" bytes safely. if you must provide a pointer to some data, create it
// with this function: length is the max. size in bytes of the string caller is
@@ -63,5 +65,6 @@ private:
size_t viable_size;
char *data_ptr;
};
} // namespace simdjson
#endif
+368 -98
View File
@@ -1,40 +1,50 @@
#ifndef SIMDJSON_PARSEDJSON_H
#define SIMDJSON_PARSEDJSON_H
#include "simdjson/common_defs.h"
#include "simdjson/jsonformatutils.h"
#include "simdjson/portability.h"
#include "simdjson/simdjson.h"
#include <cinttypes>
#include <cmath>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <limits>
#include "simdjson/common_defs.h"
#include "simdjson/jsonformatutils.h"
#include "simdjson/portability.h"
#define JSONVALUEMASK 0xFFFFFFFFFFFFFF
#define DEFAULTMAXDEPTH 1024// a JSON document with a depth exceeding 1024 is probably de facto invalid
#define JSON_VALUE_MASK 0xFFFFFFFFFFFFFF
#define DEFAULT_MAX_DEPTH \
1024 // a JSON document with a depth exceeding 1024 is probably de facto
// invalid
namespace simdjson {
/************
* The JSON is parsed to a tape, see the accompanying tape.md file
* for documentation.
***********/
struct ParsedJson {
class ParsedJson {
public:
// create a ParsedJson container with zero capacity, call allocateCapacity to
// create a ParsedJson container with zero capacity, call allocate_capacity to
// allocate memory
ParsedJson();
~ParsedJson();
ParsedJson(ParsedJson && p);
ParsedJson(ParsedJson &&p);
// if needed, allocate memory so that the object is able to process JSON
// documents having up to len bytes and maxdepth "depth"
// documents having up to len bytes and max_depth "depth"
WARN_UNUSED
bool allocateCapacity(size_t len, size_t maxdepth = DEFAULTMAXDEPTH);
bool allocate_capacity(size_t len, size_t max_depth = DEFAULT_MAX_DEPTH);
bool isValid() const;
// returns true if the document parsed was valid
bool is_valid() const;
// return an error code corresponding to the last parsing attempt, see
// simdjson.h will return simdjson::UNITIALIZED if no parsing was attempted
int get_error_code() const;
// return the string equivalent of "get_error_code"
std::string get_error_message() const;
// deallocate memory and set capacity to zero, called automatically by the
// destructor
@@ -47,11 +57,10 @@ public:
// return false if the tape is likely wrong (e.g., you did not parse a valid
// JSON).
WARN_UNUSED
bool printjson(std::ostream &os);
bool print_json(std::ostream &os);
WARN_UNUSED
bool dump_raw_tape(std::ostream &os);
// all nodes are stored on the tape using a 64-bit word.
//
// strings, double and ints are stored as
@@ -68,167 +77,252 @@ public:
// this should be considered a private function
really_inline void write_tape(uint64_t val, uint8_t c) {
tape[current_loc++] = val | ((static_cast<uint64_t>(c)) << 56);
tape[current_loc++] = val | ((static_cast<uint64_t>(c)) << 56);
}
really_inline void write_tape_s64(int64_t i) {
write_tape(0, 'l');
tape[current_loc++] = *(reinterpret_cast<uint64_t *>(&i));
write_tape(0, 'l');
tape[current_loc++] = *(reinterpret_cast<uint64_t *>(&i));
}
really_inline void write_tape_double(double d) {
write_tape(0, 'd');
static_assert(sizeof(d) == sizeof(tape[current_loc]), "mismatch size");
memcpy(& tape[current_loc++], &d, sizeof(double));
//tape[current_loc++] = *((uint64_t *)&d);
memcpy(&tape[current_loc++], &d, sizeof(double));
// tape[current_loc++] = *((uint64_t *)&d);
}
really_inline uint32_t get_current_loc() { return current_loc; }
really_inline void annotate_previousloc(uint32_t saved_loc, uint64_t val) {
tape[saved_loc] |= val;
really_inline void annotate_previous_loc(uint32_t saved_loc, uint64_t val) {
tape[saved_loc] |= val;
}
struct InvalidJSON : public std::exception {
const char * what () const throw () {
return "JSON document is invalid";
}
class InvalidJSON : public std::exception {
const char *what() const throw() { return "JSON document is invalid"; }
};
struct iterator {
class Iterator {
// might throw InvalidJSON if ParsedJson is invalid
explicit iterator(ParsedJson &pj_);
~iterator();
public:
explicit Iterator(ParsedJson &pj_);
~Iterator();
iterator(const iterator &o);
Iterator(const Iterator &o) noexcept;
iterator(iterator &&o);
Iterator(Iterator &&o) noexcept;
bool isOk() const;
inline bool is_ok() const;
// useful for debuging purposes
size_t get_tape_location() const;
inline size_t get_tape_location() const;
// useful for debuging purposes
size_t get_tape_length() const;
inline size_t get_tape_length() const;
// returns the current depth (start at 1 with 0 reserved for the fictitious root node)
size_t get_depth() const;
// returns the current depth (start at 1 with 0 reserved for the fictitious
// root node)
inline size_t get_depth() const;
// A scope is a series of nodes at the same depth, typically it is either an object ({) or an array ([).
// The root node has type 'r'.
uint8_t get_scope_type() const;
// A scope is a series of nodes at the same depth, typically it is either an
// object ({) or an array ([). The root node has type 'r'.
inline uint8_t get_scope_type() const;
// move forward in document order
bool move_forward();
inline bool move_forward();
// retrieve the character code of what we're looking at:
// [{"sltfn are the possibilities
uint8_t get_type() const;
inline uint8_t get_type() const {
return current_type; // short functions should be inlined!
}
// get the int64_t value at this node; valid only if we're at "l"
int64_t get_integer() const;
inline int64_t get_integer() const {
if (location + 1 >= tape_length) {
return 0; // default value in case of error
}
return static_cast<int64_t>(pj.tape[location + 1]);
}
// get the string value at this node (NULL ended); valid only if we're at "
// note that tabs, and line endings are escaped in the returned value (see print_with_escapes)
// return value is valid UTF-8
// It may contain NULL chars within the string: get_string_length determines the true
// string length.
const char * get_string() const;
// note that tabs, and line endings are escaped in the returned value (see
// print_with_escapes) return value is valid UTF-8 It may contain NULL chars
// within the string: get_string_length determines the true string length.
inline const char *get_string() const {
return reinterpret_cast<const char *>(
pj.string_buf + (current_val & JSON_VALUE_MASK) + sizeof(uint32_t));
}
uint32_t get_string_length() const;
// return the length of the string in bytes
inline uint32_t get_string_length() const {
uint32_t answer;
memcpy(&answer,
reinterpret_cast<const char *>(pj.string_buf +
(current_val & JSON_VALUE_MASK)),
sizeof(uint32_t));
return answer;
}
// get the double value at this node; valid only if
// we're at "d"
double get_double() const;
inline double get_double() const {
if (location + 1 >= tape_length) {
return std::numeric_limits<double>::quiet_NaN(); // default value in
// case of error
}
double answer;
memcpy(&answer, &pj.tape[location + 1], sizeof(answer));
return answer;
}
bool is_object_or_array() const;
inline bool is_object_or_array() const { return is_object() || is_array(); }
bool is_object() const;
inline bool is_object() const { return get_type() == '{'; }
bool is_array() const;
inline bool is_array() const { return get_type() == '['; }
bool is_string() const;
inline bool is_string() const { return get_type() == '"'; }
bool is_integer() const;
inline bool is_integer() const { return get_type() == 'l'; }
bool is_double() const;
inline bool is_double() const { return get_type() == 'd'; }
bool is_true() const;
inline bool is_true() const { return get_type() == 't'; }
bool is_false() const;
inline bool is_false() const { return get_type() == 'f'; }
bool is_null() const;
inline bool is_null() const { return get_type() == 'n'; }
static bool is_object_or_array(uint8_t type);
static bool is_object_or_array(uint8_t type) {
return ((type == '[') || (type == '{'));
}
// when at {, go one level deep, looking for a given key
// if successful, we are left pointing at the value,
// if not, we are still pointing at the object ({)
// (in case of repeated keys, this only finds the first one)
// (in case of repeated keys, this only finds the first one).
// We seek the key using C's strcmp so if your JSON strings contain
// NULL chars, this would trigger a false positive: if you expect that
// to be the case, take extra precautions.
bool move_to_key(const char * key);
inline bool move_to_key(const char *key);
// when at {, go one level deep, looking for a given key
// if successful, we are left pointing at the value,
// if not, we are still pointing at the object ({)
// (in case of repeated keys, this only finds the first one).
// The string we search for can contain NULL values.
inline bool move_to_key(const char *key, uint32_t length);
// when at a key location within an object, this moves to the accompanying
// value (located next to it). this is equivalent but much faster than
// calling "next()".
inline void move_to_value();
// when at [, go one level deep, and advance to the given index.
// if successful, we are left pointing at the value,
// if not, we are still pointing at the array ([)
inline bool move_to_index(uint32_t index);
// Moves the iterator to the value correspoding to the json pointer.
// Always search from the root of the document.
// if successful, we are left pointing at the value,
// if not, we are still pointing the same value we were pointing before the
// call. The json pointer follows the rfc6901 standard's syntax:
// https://tools.ietf.org/html/rfc6901 However, the standard says "If a
// referenced member name is not unique in an object, the member that is
// referenced is undefined, and evaluation fails". Here we just return the
// first corresponding value. The length parameter is the length of the
// jsonpointer string ('pointer').
bool move_to(const char *pointer, uint32_t length);
// Moves the iterator to the value correspoding to the json pointer.
// Always search from the root of the document.
// if successful, we are left pointing at the value,
// if not, we are still pointing the same value we were pointing before the
// call. The json pointer implementation follows the rfc6901 standard's
// syntax: https://tools.ietf.org/html/rfc6901 However, the standard says
// "If a referenced member name is not unique in an object, the member that
// is referenced is undefined, and evaluation fails". Here we just return
// the first corresponding value.
inline bool move_to(const std::string &pointer) {
return move_to(pointer.c_str(), pointer.length());
}
private:
// Almost the same as move_to(), except it searchs from the current
// position. The pointer's syntax is identical, though that case is not
// handled by the rfc6901 standard. The '/' is still required at the
// beginning. However, contrary to move_to(), the URI Fragment Identifier
// Representation is not supported here. Also, in case of failure, we are
// left pointing at the closest value it could reach. For these reasons it
// is private. It exists because it is used by move_to().
bool relative_move_to(const char *pointer, uint32_t length);
public:
// throughout return true if we can do the navigation, false
// otherwise
// Withing a given scope (series of nodes at the same depth within either an
// array or an object), we move forward.
// Thus, given [true, null, {"a":1}, [1,2]], we would visit true, null, { and [.
// At the object ({) or at the array ([), you can issue a "down" to visit their content.
// valid if we're not at the end of a scope (returns true).
bool next();
// Thus, given [true, null, {"a":1}, [1,2]], we would visit true, null, {
// and [. At the object ({) or at the array ([), you can issue a "down" to
// visit their content. valid if we're not at the end of a scope (returns
// true).
inline bool next();
// Withing a given scope (series of nodes at the same depth within either an
// array or an object), we move backward.
// Thus, given [true, null, {"a":1}, [1,2]], we would visit ], }, null, true when starting at the end
// of the scope.
// At the object ({) or at the array ([), you can issue a "down" to visit their content.
bool prev();
// Thus, given [true, null, {"a":1}, [1,2]], we would visit ], }, null, true
// when starting at the end of the scope. At the object ({) or at the array
// ([), you can issue a "down" to visit their content.
inline bool prev();
// Moves back to either the containing array or object (type { or [) from
// within a contained scope.
// Valid unless we are at the first level of the document
bool up();
inline bool up();
// Valid if we're at a [ or { and it starts a non-empty scope; moves us to start of
// that deeper scope if it not empty.
// Thus, given [true, null, {"a":1}, [1,2]], if we are at the { node, we would move to the
// "a" node.
bool down();
// Valid if we're at a [ or { and it starts a non-empty scope; moves us to
// start of that deeper scope if it not empty. Thus, given [true, null,
// {"a":1}, [1,2]], if we are at the { node, we would move to the "a" node.
inline bool down();
// move us to the start of our current scope,
// a scope is a series of nodes at the same level
void to_start_scope();
inline void to_start_scope();
inline void rewind() {
while (up())
;
}
// void to_end_scope(); // move us to
// the start of our current scope; always succeeds
// print the thing we're currently pointing at
bool print(std::ostream &os, bool escape_strings = true) const;
typedef struct {size_t start_of_scope; uint8_t scope_type;} scopeindex_t;
typedef struct {
size_t start_of_scope;
uint8_t scope_type;
} scopeindex_t;
private:
iterator& operator=(const iterator& other) = delete ;
private:
Iterator &operator=(const Iterator &other) = delete;
ParsedJson &pj;
size_t depth;
size_t location; // our current location on a tape
size_t location; // our current location on a tape
size_t tape_length;
uint8_t current_type;
uint64_t current_val;
scopeindex_t *depthindex;
scopeindex_t *depth_index;
};
size_t bytecapacity{0}; // indicates how many bits are meant to be supported
size_t byte_capacity{0}; // indicates how many bits are meant to be supported
size_t depthcapacity{0}; // how deep we can go
size_t tapecapacity{0};
size_t stringcapacity{0};
size_t depth_capacity{0}; // how deep we can go
size_t tape_capacity{0};
size_t string_capacity{0};
uint32_t current_loc{0};
uint32_t n_structural_indexes{0};
@@ -238,23 +332,23 @@ private:
uint32_t *containing_scope_offset;
#ifdef SIMDJSON_USE_COMPUTED_GOTO
void **ret_address;
#else
#else
char *ret_address;
#endif
uint8_t *string_buf; // should be at least bytecapacity
uint8_t *string_buf; // should be at least byte_capacity
uint8_t *current_string_buf_loc;
bool isvalid{false};
bool valid{false};
int error_code{simdjson::UNITIALIZED};
private :
// we don't want the default constructor to be called
ParsedJson(const ParsedJson & p) = delete; // we don't want the default constructor to be called
// we don't want the assignment to be called
ParsedJson & operator=(const ParsedJson&o) = delete;
private:
// we don't want the default constructor to be called
ParsedJson(const ParsedJson &p) =
delete; // we don't want the default constructor to be called
// we don't want the assignment to be called
ParsedJson &operator=(const ParsedJson &o) = delete;
};
// dump bits low to high
inline void dumpbits_always(uint64_t v, const std::string &msg) {
for (uint32_t i = 0; i < 64; i++) {
@@ -270,5 +364,181 @@ inline void dumpbits32_always(uint32_t v, const std::string &msg) {
std::cout << " " << msg.c_str() << "\n";
}
WARN_UNUSED
bool ParsedJson::Iterator::is_ok() const { return location < tape_length; }
// useful for debuging purposes
size_t ParsedJson::Iterator::get_tape_location() const { return location; }
// useful for debuging purposes
size_t ParsedJson::Iterator::get_tape_length() const { return tape_length; }
// returns the current depth (start at 1 with 0 reserved for the fictitious root
// node)
size_t ParsedJson::Iterator::get_depth() const { return depth; }
// A scope is a series of nodes at the same depth, typically it is either an
// object ({) or an array ([). The root node has type 'r'.
uint8_t ParsedJson::Iterator::get_scope_type() const {
return depth_index[depth].scope_type;
}
bool ParsedJson::Iterator::move_forward() {
if (location + 1 >= tape_length) {
return false; // we are at the end!
}
if ((current_type == '[') || (current_type == '{')) {
// We are entering a new scope
depth++;
depth_index[depth].start_of_scope = location;
depth_index[depth].scope_type = current_type;
} else if ((current_type == ']') || (current_type == '}')) {
// Leaving a scope.
depth--;
} else if ((current_type == 'd') || (current_type == 'l')) {
// d and l types use 2 locations on the tape, not just one.
location += 1;
}
location += 1;
current_val = pj.tape[location];
current_type = (current_val >> 56);
return true;
}
void ParsedJson::Iterator::move_to_value() {
// assume that we are on a key, so move by 1.
location += 1;
current_val = pj.tape[location];
current_type = (current_val >> 56);
}
bool ParsedJson::Iterator::move_to_key(const char *key) {
if (down()) {
do {
assert(is_string());
bool right_key =
(strcmp(get_string(), key) == 0); // null chars would fool this
move_to_value();
if (right_key) {
return true;
}
} while (next());
assert(up()); // not found
}
return false;
}
bool ParsedJson::Iterator::move_to_key(const char *key, uint32_t length) {
if (down()) {
do {
assert(is_string());
bool right_key = ((get_string_length() == length) &&
(memcmp(get_string(), key, length) == 0));
move_to_value();
if (right_key) {
return true;
}
} while (next());
assert(up()); // not found
}
return false;
}
bool ParsedJson::Iterator::move_to_index(uint32_t index) {
assert(is_array());
if (down()) {
uint32_t i = 0;
for (; i < index; i++) {
if (!next()) {
break;
}
}
if (i == index) {
return true;
}
assert(up());
}
return false;
}
bool ParsedJson::Iterator::prev() {
if (location - 1 < depth_index[depth].start_of_scope) {
return false;
}
location -= 1;
current_val = pj.tape[location];
current_type = (current_val >> 56);
if ((current_type == ']') || (current_type == '}')) {
// we need to jump
size_t new_location = (current_val & JSON_VALUE_MASK);
if (new_location < depth_index[depth].start_of_scope) {
return false; // shoud never happen
}
location = new_location;
current_val = pj.tape[location];
current_type = (current_val >> 56);
}
return true;
}
bool ParsedJson::Iterator::up() {
if (depth == 1) {
return false; // don't allow moving back to root
}
to_start_scope();
// next we just move to the previous value
depth--;
location -= 1;
current_val = pj.tape[location];
current_type = (current_val >> 56);
return true;
}
bool ParsedJson::Iterator::down() {
if (location + 1 >= tape_length) {
return false;
}
if ((current_type == '[') || (current_type == '{')) {
size_t npos = (current_val & JSON_VALUE_MASK);
if (npos == location + 2) {
return false; // we have an empty scope
}
depth++;
location = location + 1;
depth_index[depth].start_of_scope = location;
depth_index[depth].scope_type = current_type;
current_val = pj.tape[location];
current_type = (current_val >> 56);
return true;
}
return false;
}
void ParsedJson::Iterator::to_start_scope() {
location = depth_index[depth].start_of_scope;
current_val = pj.tape[location];
current_type = (current_val >> 56);
}
bool ParsedJson::Iterator::next() {
size_t npos;
if ((current_type == '[') || (current_type == '{')) {
// we need to jump
npos = (current_val & JSON_VALUE_MASK);
} else {
npos = location + ((current_type == 'd' || current_type == 'l') ? 2 : 1);
}
uint64_t next_val = pj.tape[npos];
uint8_t next_type = (next_val >> 56);
if ((next_type == ']') || (next_type == '}')) {
return false; // we reached the end of the scope
}
location = npos;
current_val = next_val;
current_type = next_type;
return true;
}
} // namespace simdjson
#endif
+109 -80
View File
@@ -1,143 +1,172 @@
#ifndef SIMDJSON_PORTABILITY_H
#define SIMDJSON_PORTABILITY_H
#if defined(__x86_64__) || defined(_M_AMD64)
#define IS_X86_64 1
#endif
#if defined(__aarch64__) || defined(_M_ARM64)
#define IS_ARM64 1
#endif
// this is almost standard?
#define STRINGIFY(a) #a
// we are going to use runtime dispatch
#ifdef IS_X86_64
#ifdef __clang__
// clang does not have GCC push pop
// warning: clang attribute push can't be used within a namespace in clang up
// til 8.0 so TARGET_REGION and UNTARGET_REGION must be *outside* of a
// namespace.
#define TARGET_REGION(T) \
_Pragma(STRINGIFY( \
clang attribute push(__attribute__((target(T))), apply_to = function)))
#define UNTARGET_REGION _Pragma("clang attribute pop")
#elif defined(__GNUC__)
// GCC is easier
#define TARGET_REGION(T) \
_Pragma("GCC push_options") _Pragma(STRINGIFY(GCC target(T)))
#define UNTARGET_REGION _Pragma("GCC pop_options")
#else
#define TARGET_REGION(T)
#define UNTARGET_REGION
#endif // clang then gcc
// under GCC and CLANG, we use these two macros
#define TARGET_HASWELL TARGET_REGION("avx2,bmi,pclmul")
#define TARGET_WESTMERE TARGET_REGION("sse4.2,pclmul")
#endif // x86
#ifdef _MSC_VER
#include <intrin.h>
#else
#if IS_X86_64
#include <x86intrin.h>
#elif IS_ARM64
#include <arm_neon.h>
#endif
#endif
#ifdef _MSC_VER
/* Microsoft C/C++-compatible compiler */
#include <intrin.h>
#include <iso646.h>
#include <cstdint>
#include <iso646.h>
static inline bool add_overflow(uint64_t value1, uint64_t value2, uint64_t *result) {
return _addcarry_u64(0, value1, value2, reinterpret_cast<unsigned __int64 *>(result));
namespace simdjson {
static inline bool add_overflow(uint64_t value1, uint64_t value2,
uint64_t *result) {
return _addcarry_u64(0, value1, value2,
reinterpret_cast<unsigned __int64 *>(result));
}
# pragma intrinsic(_umul128)
static inline bool mul_overflow(uint64_t value1, uint64_t value2, uint64_t *result) {
uint64_t high;
*result = _umul128(value1, value2, &high);
return high;
#pragma intrinsic(_umul128)
static inline bool mul_overflow(uint64_t value1, uint64_t value2,
uint64_t *result) {
uint64_t high;
*result = _umul128(value1, value2, &high);
return high;
}
static inline int trailingzeroes(uint64_t input_num) {
return _tzcnt_u64(input_num);
static inline int trailing_zeroes(uint64_t input_num) {
return static_cast<int>(_tzcnt_u64(input_num));
}
static inline int leadingzeroes(uint64_t input_num) {
return _lzcnt_u64(input_num);
static inline int leading_zeroes(uint64_t input_num) {
return static_cast<int>(_lzcnt_u64(input_num));
}
static inline int hamming(uint64_t input_num) {
#ifdef _WIN64 // highly recommended!!!
return (int)__popcnt64(input_num);
#else // if we must support 32-bit Windows
return (int)(__popcnt((uint32_t)input_num) +
__popcnt((uint32_t)(input_num >> 32)));
#ifdef _WIN64 // highly recommended!!!
return (int)__popcnt64(input_num);
#else // if we must support 32-bit Windows
return (int)(__popcnt((uint32_t)input_num) +
__popcnt((uint32_t)(input_num >> 32)));
#endif
}
} // namespace simdjson
#else
#include <cstdint>
#include <cstdlib>
#if defined(__BMI2__) || defined(__POPCOUNT__) || defined(__AVX2__)
#include <x86intrin.h>
#endif
static inline bool add_overflow(uint64_t value1, uint64_t value2, uint64_t *result) {
return __builtin_uaddll_overflow(value1, value2, (unsigned long long*)result);
namespace simdjson {
static inline bool add_overflow(uint64_t value1, uint64_t value2,
uint64_t *result) {
return __builtin_uaddll_overflow(value1, value2,
(unsigned long long *)result);
}
static inline bool mul_overflow(uint64_t value1, uint64_t value2, uint64_t *result) {
return __builtin_umulll_overflow(value1, value2, (unsigned long long *)result);
static inline bool mul_overflow(uint64_t value1, uint64_t value2,
uint64_t *result) {
return __builtin_umulll_overflow(value1, value2,
(unsigned long long *)result);
}
/* result might be undefined when input_num is zero */
static inline int trailingzeroes(uint64_t input_num) {
#ifdef __BMI2__
return _tzcnt_u64(input_num);
static inline int trailing_zeroes(uint64_t input_num) {
#ifdef __BMI__ // tzcnt is BMI1
return _tzcnt_u64(input_num);
#else
return __builtin_ctzll(input_num);
return __builtin_ctzll(input_num);
#endif
}
/* result might be undefined when input_num is zero */
static inline int leadingzeroes(uint64_t input_num) {
static inline int leading_zeroes(uint64_t input_num) {
#ifdef __BMI2__
return _lzcnt_u64(input_num);
return _lzcnt_u64(input_num);
#else
return __builtin_clzll(input_num);
return __builtin_clzll(input_num);
#endif
}
/* result might be undefined when input_num is zero */
static inline int hamming(uint64_t input_num) {
#ifdef __POPCOUNT__
return _popcnt64(input_num);
return _popcnt64(input_num);
#else
return __builtin_popcountll(input_num);
return __builtin_popcountll(input_num);
#endif
}
} // namespace simdjson
#endif // _MSC_VER
namespace simdjson {
// portable version of posix_memalign
static inline void *aligned_malloc(size_t alignment, size_t size) {
void *p;
void *p;
#ifdef _MSC_VER
p = _aligned_malloc(size, alignment);
p = _aligned_malloc(size, alignment);
#elif defined(__MINGW32__) || defined(__MINGW64__)
p = __mingw_aligned_malloc(size, alignment);
p = __mingw_aligned_malloc(size, alignment);
#else
// somehow, if this is used before including "x86intrin.h", it creates an
// implicit defined warning.
if (posix_memalign(&p, alignment, size) != 0) { return nullptr; }
// somehow, if this is used before including "x86intrin.h", it creates an
// implicit defined warning.
if (posix_memalign(&p, alignment, size) != 0) {
return nullptr;
}
#endif
return p;
return p;
}
static inline char *aligned_malloc_char(size_t alignment, size_t size) {
return (char*)aligned_malloc(alignment, size);
return (char *)aligned_malloc(alignment, size);
}
#ifdef __AVX2__
#ifndef __clang__
#ifndef _MSC_VER
static __m256i inline _mm256_loadu2_m128i(__m128i const *__addr_hi,
__m128i const *__addr_lo) {
__m256i __v256 = _mm256_castsi128_si256(_mm_loadu_si128(__addr_lo));
return _mm256_insertf128_si256(__v256, _mm_loadu_si128(__addr_hi), 1);
}
static inline void _mm256_storeu2_m128i(__m128i *__addr_hi, __m128i *__addr_lo,
__m256i __a) {
__m128i __v128;
__v128 = _mm256_castsi256_si128(__a);
_mm_storeu_si128(__addr_lo, __v128);
__v128 = _mm256_extractf128_si256(__a, 1);
_mm_storeu_si128(__addr_hi, __v128);
}
#endif
#endif
#endif // AVX_2
static inline void aligned_free(void *memblock) {
if(memblock == nullptr) { return; }
static inline void aligned_free(void *mem_block) {
if (mem_block == nullptr) {
return;
}
#ifdef _MSC_VER
_aligned_free(memblock);
_aligned_free(mem_block);
#elif defined(__MINGW32__) || defined(__MINGW64__)
__mingw_aligned_free(memblock);
__mingw_aligned_free(mem_block);
#else
free(memblock);
free(mem_block);
#endif
}
static inline void aligned_free_char(char *memblock) {
aligned_free((void*)memblock);
static inline void aligned_free_char(char *mem_block) {
aligned_free((void *)mem_block);
}
} // namespace simdjson
#endif // SIMDJSON_PORTABILITY_H
+38 -11
View File
@@ -1,17 +1,44 @@
#ifndef SIMDJSON_ERR_H
# define SIMDJSON_ERR_H
#ifndef SIMDJSON_ERR_H
#define SIMDJSON_ERR_H
#include <string>
struct simdjson {
enum errorValues {
SUCCESS = 0,
CAPACITY, // This ParsedJson can't support a document that big
MEMALLOC, // Error allocating memory, most likely out of memory
TAPE_ERROR, // Something went wrong while writing to the tape
DEPTH_ERROR, // Your document exceeds the user-specified depth limitation
};
static const std::string& errorMsg(const int);
namespace simdjson {
// Represents the minimal architecture that would support an implementation
enum class Architecture {
WESTMERE,
HASWELL,
ARM64,
NONE,
// TODO remove 'native' in favor of runtime dispatch?
// the 'native' enum class value should point at a good default on the current
// machine
#ifdef IS_X86_64
NATIVE = WESTMERE
#elif defined(IS_ARM64)
NATIVE = ARM64
#endif
};
enum ErrorValues {
SUCCESS = 0,
CAPACITY, // This ParsedJson can't support a document that big
MEMALLOC, // Error allocating memory, most likely out of memory
TAPE_ERROR, // Something went wrong while writing to the tape (stage 2), this
// is a generic error
DEPTH_ERROR, // Your document exceeds the user-specified depth limitation
STRING_ERROR, // Problem while parsing a string
T_ATOM_ERROR, // Problem while parsing an atom starting with the letter 't'
F_ATOM_ERROR, // Problem while parsing an atom starting with the letter 'f'
N_ATOM_ERROR, // Problem while parsing an atom starting with the letter 'n'
NUMBER_ERROR, // Problem while parsing a number
UTF8_ERROR, // the input is not valid UTF-8
UNITIALIZED, // unknown error, or uninitialized document
EMPTY, // no structural document found
UNESCAPED_CHARS, // found unescaped characters in a string.
UNCLOSED_STRING, // missing quote at the end
UNEXPECTED_ERROR // indicative of a bug in simdjson
};
const std::string &error_message(const int);
} // namespace simdjson
#endif
+13 -10
View File
@@ -1,10 +1,13 @@
// /include/simdjson/simdjson_version.h automatically generated by release.py, do not change by hand
#ifndef SIMDJSON_INCLUDE_SIMDJSON_VERSION
#define SIMDJSON_INCLUDE_SIMDJSON_VERSION
#define SIMDJSON_VERSION 0.1.2
enum {
SIMDJSON_VERSION_MAJOR = 0,
SIMDJSON_VERSION_MINOR = 1,
SIMDJSON_VERSION_REVISION = 2
};
#endif // SIMDJSON_INCLUDE_SIMDJSON_VERSION
// /include/simdjson/simdjson_version.h automatically generated by release.py,
// do not change by hand
#ifndef SIMDJSON_INCLUDE_SIMDJSON_VERSION
#define SIMDJSON_INCLUDE_SIMDJSON_VERSION
#define SIMDJSON_VERSION 0.2.0
namespace simdjson {
enum {
SIMDJSON_VERSION_MAJOR = 0,
SIMDJSON_VERSION_MINOR = 2,
SIMDJSON_VERSION_REVISION = 0
};
}
#endif // SIMDJSON_INCLUDE_SIMDJSON_VERSION
+5 -10
View File
@@ -1,10 +1,10 @@
#ifndef SIMDJSON_SIMDPRUNE_TABLES_H
#define SIMDJSON_SIMDPRUNE_TABLES_H
#include <cstdint>
#include "simdjson/portability.h"
#ifdef __AVX__
namespace simdjson {
static const unsigned char mask128_epi8[] = {
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe,
0xf, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe,
@@ -34984,13 +34984,9 @@ static const unsigned char mask128_epi32[] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff,
};
} // namespace simdjson
#endif //__SSE3__
#ifdef __AVX2__
#include <cstdint>
namespace simdjson {
static const uint32_t mask256_epi32[] = {
0, 1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7, 7, 0, 2, 3, 4, 5, 6, 7, 7, 2,
3, 4, 5, 6, 7, 7, 7, 0, 1, 3, 4, 5, 6, 7, 7, 1, 3, 4, 5, 6, 7, 7, 7, 0, 3,
@@ -35074,6 +35070,5 @@ static const uint32_t mask256_epi32[] = {
3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2,
0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
#endif //__AVX2__
}
#endif
+180
View File
@@ -0,0 +1,180 @@
// From https://github.com/cyb70289/utf8/blob/master/lemire-neon.c
// Adapted from https://github.com/lemire/fastvalidate-utf-8
#ifndef SIMDJSON_SIMDUTF8CHECK_ARM64_H
#define SIMDJSON_SIMDUTF8CHECK_ARM64_H
#if defined(_ARM_NEON) || defined(__aarch64__) || \
(defined(_MSC_VER) && defined(_M_ARM64))
#include <arm_neon.h>
#include <cinttypes>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstring>
/*
* legal utf-8 byte sequence
* http://www.unicode.org/versions/Unicode6.0.0/ch03.pdf - page 94
*
* Code Points 1st 2s 3s 4s
* U+0000..U+007F 00..7F
* U+0080..U+07FF C2..DF 80..BF
* U+0800..U+0FFF E0 A0..BF 80..BF
* U+1000..U+CFFF E1..EC 80..BF 80..BF
* U+D000..U+D7FF ED 80..9F 80..BF
* U+E000..U+FFFF EE..EF 80..BF 80..BF
* U+10000..U+3FFFF F0 90..BF 80..BF 80..BF
* U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF
* U+100000..U+10FFFF F4 80..8F 80..BF 80..BF
*
*/
namespace simdjson {
// all byte values must be no larger than 0xF4
static inline void check_smaller_than_0xF4(int8x16_t current_bytes,
int8x16_t *has_error) {
// unsigned, saturates to 0 below max
*has_error = vorrq_s8(
*has_error, vreinterpretq_s8_u8(vqsubq_u8(
vreinterpretq_u8_s8(current_bytes), vdupq_n_u8(0xF4))));
}
static const int8_t _nibbles[] = {
1, 1, 1, 1, 1, 1, 1, 1, // 0xxx (ASCII)
0, 0, 0, 0, // 10xx (continuation)
2, 2, // 110x
3, // 1110
4, // 1111, next should be 0 (not checked here)
};
static inline int8x16_t continuation_lengths(int8x16_t high_nibbles) {
return vqtbl1q_s8(vld1q_s8(_nibbles), vreinterpretq_u8_s8(high_nibbles));
}
static inline int8x16_t carry_continuations(int8x16_t initial_lengths,
int8x16_t previous_carries) {
int8x16_t right1 = vreinterpretq_s8_u8(vqsubq_u8(
vreinterpretq_u8_s8(vextq_s8(previous_carries, initial_lengths, 16 - 1)),
vdupq_n_u8(1)));
int8x16_t sum = vaddq_s8(initial_lengths, right1);
int8x16_t right2 = vreinterpretq_s8_u8(
vqsubq_u8(vreinterpretq_u8_s8(vextq_s8(previous_carries, sum, 16 - 2)),
vdupq_n_u8(2)));
return vaddq_s8(sum, right2);
}
static inline void check_continuations(int8x16_t initial_lengths,
int8x16_t carries,
int8x16_t *has_error) {
// overlap || underlap
// carry > length && length > 0 || !(carry > length) && !(length > 0)
// (carries > length) == (lengths > 0)
uint8x16_t overunder = vceqq_u8(vcgtq_s8(carries, initial_lengths),
vcgtq_s8(initial_lengths, vdupq_n_s8(0)));
*has_error = vorrq_s8(*has_error, vreinterpretq_s8_u8(overunder));
}
// when 0xED is found, next byte must be no larger than 0x9F
// when 0xF4 is found, next byte must be no larger than 0x8F
// next byte must be continuation, ie sign bit is set, so signed < is ok
static inline void check_first_continuation_max(int8x16_t current_bytes,
int8x16_t off1_current_bytes,
int8x16_t *has_error) {
uint8x16_t maskED = vceqq_s8(off1_current_bytes, vdupq_n_s8(0xED));
uint8x16_t maskF4 = vceqq_s8(off1_current_bytes, vdupq_n_s8(0xF4));
uint8x16_t badfollowED =
vandq_u8(vcgtq_s8(current_bytes, vdupq_n_s8(0x9F)), maskED);
uint8x16_t badfollowF4 =
vandq_u8(vcgtq_s8(current_bytes, vdupq_n_s8(0x8F)), maskF4);
*has_error = vorrq_s8(
*has_error, vreinterpretq_s8_u8(vorrq_u8(badfollowED, badfollowF4)));
}
static const int8_t _initial_mins[] = {
-128, -128, -128, -128, -128, -128,
-128, -128, -128, -128, -128, -128, // 10xx => false
(int8_t)0xC2, -128, // 110x
(int8_t)0xE1, // 1110
(int8_t)0xF1,
};
static const int8_t _second_mins[] = {
-128, -128, -128, -128, -128, -128,
-128, -128, -128, -128, -128, -128, // 10xx => false
127, 127, // 110x => true
(int8_t)0xA0, // 1110
(int8_t)0x90,
};
// map off1_hibits => error condition
// hibits off1 cur
// C => < C2 && true
// E => < E1 && < A0
// F => < F1 && < 90
// else false && false
static inline void check_overlong(int8x16_t current_bytes,
int8x16_t off1_current_bytes,
int8x16_t hibits, int8x16_t previous_hibits,
int8x16_t *has_error) {
int8x16_t off1_hibits = vextq_s8(previous_hibits, hibits, 16 - 1);
int8x16_t initial_mins =
vqtbl1q_s8(vld1q_s8(_initial_mins), vreinterpretq_u8_s8(off1_hibits));
uint8x16_t initial_under = vcgtq_s8(initial_mins, off1_current_bytes);
int8x16_t second_mins =
vqtbl1q_s8(vld1q_s8(_second_mins), vreinterpretq_u8_s8(off1_hibits));
uint8x16_t second_under = vcgtq_s8(second_mins, current_bytes);
*has_error = vorrq_s8(
*has_error, vreinterpretq_s8_u8(vandq_u8(initial_under, second_under)));
}
struct processed_utf_bytes {
int8x16_t raw_bytes;
int8x16_t high_nibbles;
int8x16_t carried_continuations;
};
static inline void count_nibbles(int8x16_t bytes,
struct processed_utf_bytes *answer) {
answer->raw_bytes = bytes;
answer->high_nibbles =
vreinterpretq_s8_u8(vshrq_n_u8(vreinterpretq_u8_s8(bytes), 4));
}
// check whether the current bytes are valid UTF-8
// at the end of the function, previous gets updated
static inline struct processed_utf_bytes
check_utf8_bytes(int8x16_t current_bytes, struct processed_utf_bytes *previous,
int8x16_t *has_error) {
struct processed_utf_bytes pb;
count_nibbles(current_bytes, &pb);
check_smaller_than_0xF4(current_bytes, has_error);
int8x16_t initial_lengths = continuation_lengths(pb.high_nibbles);
pb.carried_continuations =
carry_continuations(initial_lengths, previous->carried_continuations);
check_continuations(initial_lengths, pb.carried_continuations, has_error);
int8x16_t off1_current_bytes =
vextq_s8(previous->raw_bytes, pb.raw_bytes, 16 - 1);
check_first_continuation_max(current_bytes, off1_current_bytes, has_error);
check_overlong(current_bytes, off1_current_bytes, pb.high_nibbles,
previous->high_nibbles, has_error);
return pb;
}
} // namespace simdjson
#endif
#endif
@@ -1,12 +1,12 @@
#ifndef SIMDJSON_SIMDUTF8CHECK_HASWELL_H
#define SIMDJSON_SIMDUTF8CHECK_HASWELL_H
#ifndef SIMDJSON_SIMDUTF8CHECK_H
#define SIMDJSON_SIMDUTF8CHECK_H
#include "simdjson/portability.h"
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include "simdjson/portability.h"
#ifdef IS_X86_64
/*
* legal utf-8 byte sequence
* http://www.unicode.org/versions/Unicode6.0.0/ch03.pdf - page 94
@@ -26,10 +26,8 @@
// all byte values must be no larger than 0xF4
#ifdef __AVX2__
/*****************************/
TARGET_HASWELL
namespace simdjson {
static inline __m256i push_last_byte_of_a_to_b(__m256i a, __m256i b) {
return _mm256_alignr_epi8(b, _mm256_permute2x128_si256(a, b, 0x21), 15);
}
@@ -39,14 +37,14 @@ static inline __m256i push_last_2bytes_of_a_to_b(__m256i a, __m256i b) {
}
// all byte values must be no larger than 0xF4
static inline void avxcheckSmallerThan0xF4(__m256i current_bytes,
__m256i *has_error) {
static inline void avx_check_smaller_than_0xF4(__m256i current_bytes,
__m256i *has_error) {
// unsigned, saturates to 0 below max
*has_error = _mm256_or_si256(
*has_error, _mm256_subs_epu8(current_bytes, _mm256_set1_epi8(0xF4)));
*has_error, _mm256_subs_epu8(current_bytes, _mm256_set1_epi8(0xF4u)));
}
static inline __m256i avxcontinuationLengths(__m256i high_nibbles) {
static inline __m256i avx_continuation_lengths(__m256i high_nibbles) {
return _mm256_shuffle_epi8(
_mm256_setr_epi8(1, 1, 1, 1, 1, 1, 1, 1, // 0xxx (ASCII)
0, 0, 0, 0, // 10xx (continuation)
@@ -62,8 +60,8 @@ static inline __m256i avxcontinuationLengths(__m256i high_nibbles) {
high_nibbles);
}
static inline __m256i avxcarryContinuations(__m256i initial_lengths,
__m256i previous_carries) {
static inline __m256i avx_carry_continuations(__m256i initial_lengths,
__m256i previous_carries) {
__m256i right1 = _mm256_subs_epu8(
push_last_byte_of_a_to_b(previous_carries, initial_lengths),
@@ -75,8 +73,9 @@ static inline __m256i avxcarryContinuations(__m256i initial_lengths,
return _mm256_add_epi8(sum, right2);
}
static inline void avxcheckContinuations(__m256i initial_lengths,
__m256i carries, __m256i *has_error) {
static inline void avx_check_continuations(__m256i initial_lengths,
__m256i carries,
__m256i *has_error) {
// overlap || underlap
// carry > length && length > 0 || !(carry > length) && !(length > 0)
@@ -91,18 +90,18 @@ static inline void avxcheckContinuations(__m256i initial_lengths,
// when 0xED is found, next byte must be no larger than 0x9F
// when 0xF4 is found, next byte must be no larger than 0x8F
// next byte must be continuation, ie sign bit is set, so signed < is ok
static inline void avxcheckFirstContinuationMax(__m256i current_bytes,
__m256i off1_current_bytes,
__m256i *has_error) {
static inline void avx_check_first_continuation_max(__m256i current_bytes,
__m256i off1_current_bytes,
__m256i *has_error) {
__m256i maskED =
_mm256_cmpeq_epi8(off1_current_bytes, _mm256_set1_epi8(0xED));
_mm256_cmpeq_epi8(off1_current_bytes, _mm256_set1_epi8(0xEDu));
__m256i maskF4 =
_mm256_cmpeq_epi8(off1_current_bytes, _mm256_set1_epi8(0xF4));
_mm256_cmpeq_epi8(off1_current_bytes, _mm256_set1_epi8(0xF4u));
__m256i badfollowED = _mm256_and_si256(
_mm256_cmpgt_epi8(current_bytes, _mm256_set1_epi8(0x9F)), maskED);
_mm256_cmpgt_epi8(current_bytes, _mm256_set1_epi8(0x9Fu)), maskED);
__m256i badfollowF4 = _mm256_and_si256(
_mm256_cmpgt_epi8(current_bytes, _mm256_set1_epi8(0x8F)), maskF4);
_mm256_cmpgt_epi8(current_bytes, _mm256_set1_epi8(0x8Fu)), maskF4);
*has_error =
_mm256_or_si256(*has_error, _mm256_or_si256(badfollowED, badfollowF4));
@@ -114,37 +113,37 @@ static inline void avxcheckFirstContinuationMax(__m256i current_bytes,
// E => < E1 && < A0
// F => < F1 && < 90
// else false && false
static inline void avxcheckOverlong(__m256i current_bytes,
__m256i off1_current_bytes, __m256i hibits,
__m256i previous_hibits,
__m256i *has_error) {
static inline void avx_check_overlong(__m256i current_bytes,
__m256i off1_current_bytes,
__m256i hibits, __m256i previous_hibits,
__m256i *has_error) {
__m256i off1_hibits = push_last_byte_of_a_to_b(previous_hibits, hibits);
__m256i initial_mins = _mm256_shuffle_epi8(
_mm256_setr_epi8(-128, -128, -128, -128, -128, -128, -128, -128,
-128, -128, -128, -128, // 10xx => false
0xC2, -128, // 110x
0xE1, // 1110
0xF1, // 1111
-128, -128, -128, -128, -128, -128, -128, -128,
-128, -128, -128, -128, // 10xx => false
0xC2, -128, // 110x
0xE1, // 1110
0xF1), // 1111
_mm256_setr_epi8(-128, -128, -128, -128, -128, -128, -128, -128, -128,
-128, -128, -128, // 10xx => false
0xC2u, -128, // 110x
0xE1u, // 1110
0xF1u, // 1111
-128, -128, -128, -128, -128, -128, -128, -128, -128,
-128, -128, -128, // 10xx => false
0xC2u, -128, // 110x
0xE1u, // 1110
0xF1u), // 1111
off1_hibits);
__m256i initial_under = _mm256_cmpgt_epi8(initial_mins, off1_current_bytes);
__m256i second_mins = _mm256_shuffle_epi8(
_mm256_setr_epi8(-128, -128, -128, -128, -128, -128, -128, -128,
-128, -128, -128, -128, // 10xx => false
127, 127, // 110x => true
0xA0, // 1110
0x90, // 1111
-128, -128, -128, -128, -128, -128, -128, -128,
-128, -128, -128, -128, // 10xx => false
127, 127, // 110x => true
0xA0, // 1110
0x90), // 1111
_mm256_setr_epi8(-128, -128, -128, -128, -128, -128, -128, -128, -128,
-128, -128, -128, // 10xx => false
127, 127, // 110x => true
0xA0u, // 1110
0x90u, // 1111
-128, -128, -128, -128, -128, -128, -128, -128, -128,
-128, -128, -128, // 10xx => false
127, 127, // 110x => true
0xA0u, // 1110
0x90u), // 1111
off1_hibits);
__m256i second_under = _mm256_cmpgt_epi8(second_mins, current_bytes);
*has_error = _mm256_or_si256(*has_error,
@@ -152,14 +151,14 @@ static inline void avxcheckOverlong(__m256i current_bytes,
}
struct avx_processed_utf_bytes {
__m256i rawbytes;
__m256i raw_bytes;
__m256i high_nibbles;
__m256i carried_continuations;
};
static inline void avx_count_nibbles(__m256i bytes,
struct avx_processed_utf_bytes *answer) {
answer->rawbytes = bytes;
answer->raw_bytes = bytes;
answer->high_nibbles =
_mm256_and_si256(_mm256_srli_epi16(bytes, 4), _mm256_set1_epi8(0x0F));
}
@@ -167,31 +166,33 @@ static inline void avx_count_nibbles(__m256i bytes,
// check whether the current bytes are valid UTF-8
// at the end of the function, previous gets updated
static inline struct avx_processed_utf_bytes
avxcheckUTF8Bytes(__m256i current_bytes,
struct avx_processed_utf_bytes *previous,
__m256i *has_error) {
struct avx_processed_utf_bytes pb{};
avx_check_utf8_bytes(__m256i current_bytes,
struct avx_processed_utf_bytes *previous,
__m256i *has_error) {
struct avx_processed_utf_bytes pb {};
avx_count_nibbles(current_bytes, &pb);
avxcheckSmallerThan0xF4(current_bytes, has_error);
avx_check_smaller_than_0xF4(current_bytes, has_error);
__m256i initial_lengths = avxcontinuationLengths(pb.high_nibbles);
__m256i initial_lengths = avx_continuation_lengths(pb.high_nibbles);
pb.carried_continuations =
avxcarryContinuations(initial_lengths, previous->carried_continuations);
avx_carry_continuations(initial_lengths, previous->carried_continuations);
avxcheckContinuations(initial_lengths, pb.carried_continuations, has_error);
avx_check_continuations(initial_lengths, pb.carried_continuations, has_error);
__m256i off1_current_bytes =
push_last_byte_of_a_to_b(previous->rawbytes, pb.rawbytes);
avxcheckFirstContinuationMax(current_bytes, off1_current_bytes, has_error);
push_last_byte_of_a_to_b(previous->raw_bytes, pb.raw_bytes);
avx_check_first_continuation_max(current_bytes, off1_current_bytes,
has_error);
avxcheckOverlong(current_bytes, off1_current_bytes, pb.high_nibbles,
previous->high_nibbles, has_error);
avx_check_overlong(current_bytes, off1_current_bytes, pb.high_nibbles,
previous->high_nibbles, has_error);
return pb;
}
} // namespace simdjson
UNTARGET_REGION // haswell
#endif // IS_X86_64
#else // __AVX2__
#warning "We require AVX2 support!"
#endif // __AVX2__
#endif
+169
View File
@@ -0,0 +1,169 @@
#ifndef SIMDJSON_SIMDUTF8CHECK_WESTMERE_H
#define SIMDJSON_SIMDUTF8CHECK_WESTMERE_H
#include "simdjson/portability.h"
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#ifdef IS_X86_64
/*
* legal utf-8 byte sequence
* http://www.unicode.org/versions/Unicode6.0.0/ch03.pdf - page 94
*
* Code Points 1st 2s 3s 4s
* U+0000..U+007F 00..7F
* U+0080..U+07FF C2..DF 80..BF
* U+0800..U+0FFF E0 A0..BF 80..BF
* U+1000..U+CFFF E1..EC 80..BF 80..BF
* U+D000..U+D7FF ED 80..9F 80..BF
* U+E000..U+FFFF EE..EF 80..BF 80..BF
* U+10000..U+3FFFF F0 90..BF 80..BF 80..BF
* U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF
* U+100000..U+10FFFF F4 80..8F 80..BF 80..BF
*
*/
// all byte values must be no larger than 0xF4
/********** sse code **********/
TARGET_WESTMERE
namespace simdjson {
// all byte values must be no larger than 0xF4
static inline void check_smaller_than_0xF4(__m128i current_bytes,
__m128i *has_error) {
// unsigned, saturates to 0 below max
*has_error = _mm_or_si128(*has_error,
_mm_subs_epu8(current_bytes, _mm_set1_epi8(0xF4u)));
}
static inline __m128i continuation_lengths(__m128i high_nibbles) {
return _mm_shuffle_epi8(
_mm_setr_epi8(1, 1, 1, 1, 1, 1, 1, 1, // 0xxx (ASCII)
0, 0, 0, 0, // 10xx (continuation)
2, 2, // 110x
3, // 1110
4), // 1111, next should be 0 (not checked here)
high_nibbles);
}
static inline __m128i carry_continuations(__m128i initial_lengths,
__m128i previous_carries) {
__m128i right1 =
_mm_subs_epu8(_mm_alignr_epi8(initial_lengths, previous_carries, 16 - 1),
_mm_set1_epi8(1));
__m128i sum = _mm_add_epi8(initial_lengths, right1);
__m128i right2 = _mm_subs_epu8(_mm_alignr_epi8(sum, previous_carries, 16 - 2),
_mm_set1_epi8(2));
return _mm_add_epi8(sum, right2);
}
static inline void check_continuations(__m128i initial_lengths, __m128i carries,
__m128i *has_error) {
// overlap || underlap
// carry > length && length > 0 || !(carry > length) && !(length > 0)
// (carries > length) == (lengths > 0)
__m128i overunder =
_mm_cmpeq_epi8(_mm_cmpgt_epi8(carries, initial_lengths),
_mm_cmpgt_epi8(initial_lengths, _mm_setzero_si128()));
*has_error = _mm_or_si128(*has_error, overunder);
}
// when 0xED is found, next byte must be no larger than 0x9F
// when 0xF4 is found, next byte must be no larger than 0x8F
// next byte must be continuation, ie sign bit is set, so signed < is ok
static inline void check_first_continuation_max(__m128i current_bytes,
__m128i off1_current_bytes,
__m128i *has_error) {
__m128i maskED = _mm_cmpeq_epi8(off1_current_bytes, _mm_set1_epi8(0xEDu));
__m128i maskF4 = _mm_cmpeq_epi8(off1_current_bytes, _mm_set1_epi8(0xF4u));
__m128i badfollowED = _mm_and_si128(
_mm_cmpgt_epi8(current_bytes, _mm_set1_epi8(0x9Fu)), maskED);
__m128i badfollowF4 = _mm_and_si128(
_mm_cmpgt_epi8(current_bytes, _mm_set1_epi8(0x8Fu)), maskF4);
*has_error = _mm_or_si128(*has_error, _mm_or_si128(badfollowED, badfollowF4));
}
// map off1_hibits => error condition
// hibits off1 cur
// C => < C2 && true
// E => < E1 && < A0
// F => < F1 && < 90
// else false && false
static inline void check_overlong(__m128i current_bytes,
__m128i off1_current_bytes, __m128i hibits,
__m128i previous_hibits, __m128i *has_error) {
__m128i off1_hibits = _mm_alignr_epi8(hibits, previous_hibits, 16 - 1);
__m128i initial_mins = _mm_shuffle_epi8(
_mm_setr_epi8(-128, -128, -128, -128, -128, -128, -128, -128, -128, -128,
-128, -128, // 10xx => false
0xC2u, -128, // 110x
0xE1u, // 1110
0xF1u),
off1_hibits);
__m128i initial_under = _mm_cmpgt_epi8(initial_mins, off1_current_bytes);
__m128i second_mins = _mm_shuffle_epi8(
_mm_setr_epi8(-128, -128, -128, -128, -128, -128, -128, -128, -128, -128,
-128, -128, // 10xx => false
127, 127, // 110x => true
0xA0u, // 1110
0x90u),
off1_hibits);
__m128i second_under = _mm_cmpgt_epi8(second_mins, current_bytes);
*has_error =
_mm_or_si128(*has_error, _mm_and_si128(initial_under, second_under));
}
struct processed_utf_bytes {
__m128i raw_bytes;
__m128i high_nibbles;
__m128i carried_continuations;
};
static inline void count_nibbles(__m128i bytes,
struct processed_utf_bytes *answer) {
answer->raw_bytes = bytes;
answer->high_nibbles =
_mm_and_si128(_mm_srli_epi16(bytes, 4), _mm_set1_epi8(0x0F));
}
// check whether the current bytes are valid UTF-8
// at the end of the function, previous gets updated
static struct processed_utf_bytes
check_utf8_bytes(__m128i current_bytes, struct processed_utf_bytes *previous,
__m128i *has_error) {
struct processed_utf_bytes pb;
count_nibbles(current_bytes, &pb);
check_smaller_than_0xF4(current_bytes, has_error);
__m128i initial_lengths = continuation_lengths(pb.high_nibbles);
pb.carried_continuations =
carry_continuations(initial_lengths, previous->carried_continuations);
check_continuations(initial_lengths, pb.carried_continuations, has_error);
__m128i off1_current_bytes =
_mm_alignr_epi8(pb.raw_bytes, previous->raw_bytes, 16 - 1);
check_first_continuation_max(current_bytes, off1_current_bytes, has_error);
check_overlong(current_bytes, off1_current_bytes, pb.high_nibbles,
previous->high_nibbles, has_error);
return pb;
}
} // namespace simdjson
UNTARGET_REGION // westmere
#endif // IS_X86_64
#endif
+112 -5
View File
@@ -2,13 +2,120 @@
#define SIMDJSON_STAGE1_FIND_MARKS_H
#include "simdjson/common_defs.h"
#include "simdjson/parsedjson.h"
#include "simdjson/portability.h"
#include "simdjson/simdjson.h"
#include <cassert>
struct ParsedJson;
namespace simdjson {
WARN_UNUSED
bool find_structural_bits(const uint8_t *buf, size_t len, ParsedJson &pj);
template <Architecture> struct simd_input;
WARN_UNUSED
bool find_structural_bits(const char *buf, size_t len, ParsedJson &pj);
template <Architecture> uint64_t compute_quote_mask(uint64_t quote_bits);
namespace {
// for when clmul is unavailable
[[maybe_unused]] uint64_t portable_compute_quote_mask(uint64_t quote_bits) {
uint64_t quote_mask = quote_bits ^ (quote_bits << 1);
quote_mask = quote_mask ^ (quote_mask << 2);
quote_mask = quote_mask ^ (quote_mask << 4);
quote_mask = quote_mask ^ (quote_mask << 8);
quote_mask = quote_mask ^ (quote_mask << 16);
quote_mask = quote_mask ^ (quote_mask << 32);
return quote_mask;
}
} // namespace
// Holds the state required to perform check_utf8().
template <Architecture> struct utf8_checking_state;
template <Architecture T>
void check_utf8(simd_input<T> in, utf8_checking_state<T> &state);
// Checks if the utf8 validation has found any error.
template <Architecture T>
ErrorValues check_utf8_errors(utf8_checking_state<T> &state);
// a straightforward comparison of a mask against input.
template <Architecture T>
uint64_t cmp_mask_against_input(simd_input<T> in, uint8_t m);
template <Architecture T> simd_input<T> fill_input(const uint8_t *ptr);
// find all values less than or equal than the content of maxval (using unsigned
// arithmetic)
template <Architecture T>
uint64_t unsigned_lteq_against_input(simd_input<T> in, uint8_t m);
template <Architecture T>
really_inline uint64_t find_odd_backslash_sequences(
simd_input<T> in, uint64_t &prev_iter_ends_odd_backslash);
template <Architecture T>
really_inline uint64_t find_quote_mask_and_bits(
simd_input<T> in, uint64_t odd_ends, uint64_t &prev_iter_inside_quote,
uint64_t &quote_bits, uint64_t &error_mask);
// do a 'shufti' to detect structural JSON characters
// they are { 0x7b } 0x7d : 0x3a [ 0x5b ] 0x5d , 0x2c
// these go into the first 3 buckets of the comparison (1/2/4)
// we are also interested in the four whitespace characters
// space 0x20, linefeed 0x0a, horizontal tab 0x09 and carriage return 0x0d
// these go into the next 2 buckets of the comparison (8/16)
template <Architecture T>
void find_whitespace_and_structurals(simd_input<T> in, uint64_t &whitespace,
uint64_t &structurals);
// return a updated structural bit vector with quoted contents cleared out and
// pseudo-structural characters added to the mask
// updates prev_iter_ends_pseudo_pred which tells us whether the previous
// iteration ended on a whitespace or a structural character (which means that
// the next iteration
// will have a pseudo-structural character at its start)
really_inline uint64_t finalize_structurals(
uint64_t structurals, uint64_t whitespace, uint64_t quote_mask,
uint64_t quote_bits, uint64_t &prev_iter_ends_pseudo_pred) {
// mask off anything inside quotes
structurals &= ~quote_mask;
// add the real quote bits back into our bit_mask as well, so we can
// quickly traverse the strings we've spent all this trouble gathering
structurals |= quote_bits;
// Now, establish "pseudo-structural characters". These are non-whitespace
// characters that are (a) outside quotes and (b) have a predecessor that's
// either whitespace or a structural character. This means that subsequent
// passes will get a chance to encounter the first character of every string
// of non-whitespace and, if we're parsing an atom like true/false/null or a
// number we can stop at the first whitespace or structural character
// following it.
// a qualified predecessor is something that can happen 1 position before an
// pseudo-structural character
uint64_t pseudo_pred = structurals | whitespace;
uint64_t shifted_pseudo_pred =
(pseudo_pred << 1) | prev_iter_ends_pseudo_pred;
prev_iter_ends_pseudo_pred = pseudo_pred >> 63;
uint64_t pseudo_structurals =
shifted_pseudo_pred & (~whitespace) & (~quote_mask);
structurals |= pseudo_structurals;
// now, we've used our close quotes all we need to. So let's switch them off
// they will be off in the quote mask and on in quote bits.
structurals &= ~(quote_bits & ~quote_mask);
return structurals;
}
template <Architecture T = Architecture::NATIVE>
int find_structural_bits(const uint8_t *buf, size_t len,
simdjson::ParsedJson &pj);
template <Architecture T = Architecture::NATIVE>
int find_structural_bits(const char *buf, size_t len,
simdjson::ParsedJson &pj) {
return find_structural_bits((const uint8_t *)buf, len, pj);
}
} // namespace simdjson
#endif
+210
View File
@@ -0,0 +1,210 @@
#ifndef SIMDJSON_STAGE1_FIND_MARKS_ARM64_H
#define SIMDJSON_STAGE1_FIND_MARKS_ARM64_H
#include "simdjson/simdutf8check_arm64.h"
#include "simdjson/stage1_find_marks.h"
#include "simdjson/stage1_find_marks_flatten.h"
#include "simdjson/stage1_find_marks_macros.h"
#ifdef IS_ARM64
namespace simdjson {
template <> struct simd_input<Architecture::ARM64> {
uint8x16_t i0;
uint8x16_t i1;
uint8x16_t i2;
uint8x16_t i3;
};
template <>
really_inline simd_input<Architecture::ARM64>
fill_input<Architecture::ARM64>(const uint8_t *ptr) {
struct simd_input<Architecture::ARM64> in;
in.i0 = vld1q_u8(ptr + 0);
in.i1 = vld1q_u8(ptr + 16);
in.i2 = vld1q_u8(ptr + 32);
in.i3 = vld1q_u8(ptr + 48);
return in;
}
really_inline uint16_t neon_movemask(uint8x16_t input) {
const uint8x16_t bit_mask = {0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80,
0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80};
uint8x16_t minput = vandq_u8(input, bit_mask);
uint8x16_t tmp = vpaddq_u8(minput, minput);
tmp = vpaddq_u8(tmp, tmp);
tmp = vpaddq_u8(tmp, tmp);
return vgetq_lane_u16(vreinterpretq_u16_u8(tmp), 0);
}
really_inline uint64_t neon_movemask_bulk(uint8x16_t p0, uint8x16_t p1,
uint8x16_t p2, uint8x16_t p3) {
const uint8x16_t bit_mask = {0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80,
0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80};
uint8x16_t t0 = vandq_u8(p0, bit_mask);
uint8x16_t t1 = vandq_u8(p1, bit_mask);
uint8x16_t t2 = vandq_u8(p2, bit_mask);
uint8x16_t t3 = vandq_u8(p3, bit_mask);
uint8x16_t sum0 = vpaddq_u8(t0, t1);
uint8x16_t sum1 = vpaddq_u8(t2, t3);
sum0 = vpaddq_u8(sum0, sum1);
sum0 = vpaddq_u8(sum0, sum0);
return vgetq_lane_u64(vreinterpretq_u64_u8(sum0), 0);
}
template <>
really_inline uint64_t
compute_quote_mask<Architecture::ARM64>(uint64_t quote_bits) {
#ifdef __ARM_FEATURE_CRYPTO // some ARM processors lack this extension
return vmull_p64(-1ULL, quote_bits);
#else
return portable_compute_quote_mask(quote_bits);
#endif
}
template <> struct utf8_checking_state<Architecture::ARM64> {
int8x16_t has_error{};
processed_utf_bytes previous{};
};
// Checks that all bytes are ascii
really_inline bool check_ascii_neon(simd_input<Architecture::ARM64> in) {
// checking if the most significant bit is always equal to 0.
uint8x16_t high_bit = vdupq_n_u8(0x80);
uint8x16_t t0 = vorrq_u8(in.i0, in.i1);
uint8x16_t t1 = vorrq_u8(in.i2, in.i3);
uint8x16_t t3 = vorrq_u8(t0, t1);
uint8x16_t t4 = vandq_u8(t3, high_bit);
uint64x2_t v64 = vreinterpretq_u64_u8(t4);
uint32x2_t v32 = vqmovn_u64(v64);
uint64x1_t result = vreinterpret_u64_u32(v32);
return vget_lane_u64(result, 0) == 0;
}
template <>
really_inline void check_utf8<Architecture::ARM64>(
simd_input<Architecture::ARM64> in,
utf8_checking_state<Architecture::ARM64> &state) {
if (check_ascii_neon(in)) {
// All bytes are ascii. Therefore the byte that was just before must be
// ascii too. We only check the byte that was just before simd_input. Nines
// are arbitrary values.
const int8x16_t verror =
(int8x16_t){9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 1};
state.has_error =
vorrq_s8(vreinterpretq_s8_u8(
vcgtq_s8(state.previous.carried_continuations, verror)),
state.has_error);
} else {
// it is not ascii so we have to do heavy work
state.previous = check_utf8_bytes(vreinterpretq_s8_u8(in.i0),
&(state.previous), &(state.has_error));
state.previous = check_utf8_bytes(vreinterpretq_s8_u8(in.i1),
&(state.previous), &(state.has_error));
state.previous = check_utf8_bytes(vreinterpretq_s8_u8(in.i2),
&(state.previous), &(state.has_error));
state.previous = check_utf8_bytes(vreinterpretq_s8_u8(in.i3),
&(state.previous), &(state.has_error));
}
}
template <>
really_inline ErrorValues check_utf8_errors<Architecture::ARM64>(
utf8_checking_state<Architecture::ARM64> &state) {
uint64x2_t v64 = vreinterpretq_u64_s8(state.has_error);
uint32x2_t v32 = vqmovn_u64(v64);
uint64x1_t result = vreinterpret_u64_u32(v32);
return vget_lane_u64(result, 0) != 0 ? simdjson::UTF8_ERROR
: simdjson::SUCCESS;
}
template <>
really_inline uint64_t cmp_mask_against_input<Architecture::ARM64>(
simd_input<Architecture::ARM64> in, uint8_t m) {
const uint8x16_t mask = vmovq_n_u8(m);
uint8x16_t cmp_res_0 = vceqq_u8(in.i0, mask);
uint8x16_t cmp_res_1 = vceqq_u8(in.i1, mask);
uint8x16_t cmp_res_2 = vceqq_u8(in.i2, mask);
uint8x16_t cmp_res_3 = vceqq_u8(in.i3, mask);
return neon_movemask_bulk(cmp_res_0, cmp_res_1, cmp_res_2, cmp_res_3);
}
template <>
really_inline uint64_t unsigned_lteq_against_input<Architecture::ARM64>(
simd_input<Architecture::ARM64> in, uint8_t m) {
const uint8x16_t mask = vmovq_n_u8(m);
uint8x16_t cmp_res_0 = vcleq_u8(in.i0, mask);
uint8x16_t cmp_res_1 = vcleq_u8(in.i1, mask);
uint8x16_t cmp_res_2 = vcleq_u8(in.i2, mask);
uint8x16_t cmp_res_3 = vcleq_u8(in.i3, mask);
return neon_movemask_bulk(cmp_res_0, cmp_res_1, cmp_res_2, cmp_res_3);
}
template <>
really_inline uint64_t find_odd_backslash_sequences<Architecture::ARM64>(
simd_input<Architecture::ARM64> in,
uint64_t &prev_iter_ends_odd_backslash) {
FIND_ODD_BACKSLASH_SEQUENCES(Architecture::ARM64, in,
prev_iter_ends_odd_backslash);
}
template <>
really_inline uint64_t find_quote_mask_and_bits<Architecture::ARM64>(
simd_input<Architecture::ARM64> in, uint64_t odd_ends,
uint64_t &prev_iter_inside_quote, uint64_t &quote_bits,
uint64_t &error_mask) {
FIND_QUOTE_MASK_AND_BITS(Architecture::ARM64, in, odd_ends,
prev_iter_inside_quote, quote_bits, error_mask)
}
template <>
really_inline void find_whitespace_and_structurals<Architecture::ARM64>(
simd_input<Architecture::ARM64> in, uint64_t &whitespace,
uint64_t &structurals) {
const uint8x16_t low_nibble_mask =
(uint8x16_t){16, 0, 0, 0, 0, 0, 0, 0, 0, 8, 12, 1, 2, 9, 0, 0};
const uint8x16_t high_nibble_mask =
(uint8x16_t){8, 0, 18, 4, 0, 1, 0, 1, 0, 0, 0, 3, 2, 1, 0, 0};
const uint8x16_t structural_shufti_mask = vmovq_n_u8(0x7);
const uint8x16_t whitespace_shufti_mask = vmovq_n_u8(0x18);
const uint8x16_t low_nib_and_mask = vmovq_n_u8(0xf);
uint8x16_t nib_0_lo = vandq_u8(in.i0, low_nib_and_mask);
uint8x16_t nib_0_hi = vshrq_n_u8(in.i0, 4);
uint8x16_t shuf_0_lo = vqtbl1q_u8(low_nibble_mask, nib_0_lo);
uint8x16_t shuf_0_hi = vqtbl1q_u8(high_nibble_mask, nib_0_hi);
uint8x16_t v_0 = vandq_u8(shuf_0_lo, shuf_0_hi);
uint8x16_t nib_1_lo = vandq_u8(in.i1, low_nib_and_mask);
uint8x16_t nib_1_hi = vshrq_n_u8(in.i1, 4);
uint8x16_t shuf_1_lo = vqtbl1q_u8(low_nibble_mask, nib_1_lo);
uint8x16_t shuf_1_hi = vqtbl1q_u8(high_nibble_mask, nib_1_hi);
uint8x16_t v_1 = vandq_u8(shuf_1_lo, shuf_1_hi);
uint8x16_t nib_2_lo = vandq_u8(in.i2, low_nib_and_mask);
uint8x16_t nib_2_hi = vshrq_n_u8(in.i2, 4);
uint8x16_t shuf_2_lo = vqtbl1q_u8(low_nibble_mask, nib_2_lo);
uint8x16_t shuf_2_hi = vqtbl1q_u8(high_nibble_mask, nib_2_hi);
uint8x16_t v_2 = vandq_u8(shuf_2_lo, shuf_2_hi);
uint8x16_t nib_3_lo = vandq_u8(in.i3, low_nib_and_mask);
uint8x16_t nib_3_hi = vshrq_n_u8(in.i3, 4);
uint8x16_t shuf_3_lo = vqtbl1q_u8(low_nibble_mask, nib_3_lo);
uint8x16_t shuf_3_hi = vqtbl1q_u8(high_nibble_mask, nib_3_hi);
uint8x16_t v_3 = vandq_u8(shuf_3_lo, shuf_3_hi);
uint8x16_t tmp_0 = vtstq_u8(v_0, structural_shufti_mask);
uint8x16_t tmp_1 = vtstq_u8(v_1, structural_shufti_mask);
uint8x16_t tmp_2 = vtstq_u8(v_2, structural_shufti_mask);
uint8x16_t tmp_3 = vtstq_u8(v_3, structural_shufti_mask);
structurals = neon_movemask_bulk(tmp_0, tmp_1, tmp_2, tmp_3);
uint8x16_t tmp_ws_0 = vtstq_u8(v_0, whitespace_shufti_mask);
uint8x16_t tmp_ws_1 = vtstq_u8(v_1, whitespace_shufti_mask);
uint8x16_t tmp_ws_2 = vtstq_u8(v_2, whitespace_shufti_mask);
uint8x16_t tmp_ws_3 = vtstq_u8(v_3, whitespace_shufti_mask);
whitespace = neon_movemask_bulk(tmp_ws_0, tmp_ws_1, tmp_ws_2, tmp_ws_3);
}
} // namespace simdjson
#endif // IS_ARM64
#endif // SIMDJSON_STAGE1_FIND_MARKS_ARM64_H
@@ -0,0 +1,93 @@
#ifndef SIMDJSON_STAGE1_FIND_MARKS_FLATTEN_H
#define SIMDJSON_STAGE1_FIND_MARKS_FLATTEN_H
namespace simdjson {
#ifdef SIMDJSON_NAIVE_FLATTEN // useful for benchmarking
//
// This is just a naive implementation. It should be normally
// disable, but can be used for research purposes to compare
// again our optimized version.
really_inline void flatten_bits(uint32_t *base_ptr, uint32_t &base,
uint32_t idx, uint64_t bits) {
uint32_t *out_ptr = base_ptr + base;
idx -= 64;
while (bits != 0) {
out_ptr[0] = idx + trailing_zeroes(bits);
bits = bits & (bits - 1);
out_ptr++;
}
base = (out_ptr - base_ptr);
}
#else
// flatten out values in 'bits' assuming that they are are to have values of idx
// plus their position in the bitvector, and store these indexes at
// base_ptr[base] incrementing base as we go
// will potentially store extra values beyond end of valid bits, so base_ptr
// needs to be large enough to handle this
really_inline void flatten_bits(uint32_t *base_ptr, uint32_t &base,
uint32_t idx, uint64_t bits) {
// In some instances, the next branch is expensive because it is mispredicted.
// Unfortunately, in other cases,
// it helps tremendously.
if (bits == 0)
return;
uint32_t cnt = hamming(bits);
uint32_t next_base = base + cnt;
idx -= 64;
base_ptr += base;
{
base_ptr[0] = idx + trailing_zeroes(bits);
bits = bits & (bits - 1);
base_ptr[1] = idx + trailing_zeroes(bits);
bits = bits & (bits - 1);
base_ptr[2] = idx + trailing_zeroes(bits);
bits = bits & (bits - 1);
base_ptr[3] = idx + trailing_zeroes(bits);
bits = bits & (bits - 1);
base_ptr[4] = idx + trailing_zeroes(bits);
bits = bits & (bits - 1);
base_ptr[5] = idx + trailing_zeroes(bits);
bits = bits & (bits - 1);
base_ptr[6] = idx + trailing_zeroes(bits);
bits = bits & (bits - 1);
base_ptr[7] = idx + trailing_zeroes(bits);
bits = bits & (bits - 1);
base_ptr += 8;
}
// We hope that the next branch is easily predicted.
if (cnt > 8) {
base_ptr[0] = idx + trailing_zeroes(bits);
bits = bits & (bits - 1);
base_ptr[1] = idx + trailing_zeroes(bits);
bits = bits & (bits - 1);
base_ptr[2] = idx + trailing_zeroes(bits);
bits = bits & (bits - 1);
base_ptr[3] = idx + trailing_zeroes(bits);
bits = bits & (bits - 1);
base_ptr[4] = idx + trailing_zeroes(bits);
bits = bits & (bits - 1);
base_ptr[5] = idx + trailing_zeroes(bits);
bits = bits & (bits - 1);
base_ptr[6] = idx + trailing_zeroes(bits);
bits = bits & (bits - 1);
base_ptr[7] = idx + trailing_zeroes(bits);
bits = bits & (bits - 1);
base_ptr += 8;
}
if (cnt > 16) { // unluckly: we rarely get here
// since it means having one structural or pseudo-structral element
// every 4 characters (possible with inputs like "","","",...).
do {
base_ptr[0] = idx + trailing_zeroes(bits);
bits = bits & (bits - 1);
base_ptr++;
} while (bits != 0);
}
base = next_base;
}
#endif // SIMDJSON_NAIVE_FLATTEN
} // namespace simdjson
#endif // SIMDJSON_STAGE1_FIND_MARKS_FLATTEN_H
@@ -0,0 +1,95 @@
#ifndef SIMDJSON_STAGE1_FIND_MARKS_FLATTEN_HASWELL_H
#define SIMDJSON_STAGE1_FIND_MARKS_FLATTEN_HASWELL_H
// This file provides the same function as
// stage1_find_marks_flatten.h, but uses Intel intrinsics.
// This should provide better performance on Visual Studio
// and other compilers that do a conservative optimization.
// Specifically, on x64 processors with BMI,
// x & (x - 1) should be mapped to
// the blsr instruction. By using the
// _blsr_u64 intrinsic, we
// ensure that this will happen.
/////////
#include "simdjson/common_defs.h"
#include "simdjson/portability.h"
#ifdef IS_X86_64
TARGET_HASWELL
namespace simdjson {
namespace haswell {
// flatten out values in 'bits' assuming that they are are to have values of idx
// plus their position in the bitvector, and store these indexes at
// base_ptr[base] incrementing base as we go
// will potentially store extra values beyond end of valid bits, so base_ptr
// needs to be large enough to handle this
really_inline void flatten_bits(uint32_t *base_ptr, uint32_t &base,
uint32_t idx, uint64_t bits) {
// In some instances, the next branch is expensive because it is mispredicted.
// Unfortunately, in other cases,
// it helps tremendously.
if (bits == 0)
return;
uint32_t cnt = _mm_popcnt_u64(bits);
uint32_t next_base = base + cnt;
idx -= 64;
base_ptr += base;
{
base_ptr[0] = idx + trailing_zeroes(bits);
bits = _blsr_u64(bits);
base_ptr[1] = idx + trailing_zeroes(bits);
bits = _blsr_u64(bits);
base_ptr[2] = idx + trailing_zeroes(bits);
bits = _blsr_u64(bits);
base_ptr[3] = idx + trailing_zeroes(bits);
bits = _blsr_u64(bits);
base_ptr[4] = idx + trailing_zeroes(bits);
bits = _blsr_u64(bits);
base_ptr[5] = idx + trailing_zeroes(bits);
bits = _blsr_u64(bits);
base_ptr[6] = idx + trailing_zeroes(bits);
bits = _blsr_u64(bits);
base_ptr[7] = idx + trailing_zeroes(bits);
bits = _blsr_u64(bits);
base_ptr += 8;
}
// We hope that the next branch is easily predicted.
if (cnt > 8) {
base_ptr[0] = idx + trailing_zeroes(bits);
bits = _blsr_u64(bits);
base_ptr[1] = idx + trailing_zeroes(bits);
bits = _blsr_u64(bits);
base_ptr[2] = idx + trailing_zeroes(bits);
bits = _blsr_u64(bits);
base_ptr[3] = idx + trailing_zeroes(bits);
bits = _blsr_u64(bits);
base_ptr[4] = idx + trailing_zeroes(bits);
bits = _blsr_u64(bits);
base_ptr[5] = idx + trailing_zeroes(bits);
bits = _blsr_u64(bits);
base_ptr[6] = idx + trailing_zeroes(bits);
bits = _blsr_u64(bits);
base_ptr[7] = idx + trailing_zeroes(bits);
bits = _blsr_u64(bits);
base_ptr += 8;
}
if (cnt > 16) { // unluckly: we rarely get here
// since it means having one structural or pseudo-structral element
// every 4 characters (possible with inputs like "","","",...).
do {
base_ptr[0] = idx + trailing_zeroes(bits);
bits = _blsr_u64(bits);
base_ptr++;
} while (bits != 0);
}
base = next_base;
}
} // namespace haswell
} // namespace simdjson
UNTARGET_REGION
#endif // IS_X86_64
#endif // SIMDJSON_STAGE1_FIND_MARKS_FLATTEN_H
@@ -0,0 +1,210 @@
#ifndef SIMDJSON_STAGE1_FIND_MARKS_HASWELL_H
#define SIMDJSON_STAGE1_FIND_MARKS_HASWELL_H
#include "simdjson/simdutf8check_haswell.h"
#include "simdjson/stage1_find_marks.h"
#include "simdjson/stage1_find_marks_flatten_haswell.h"
#include "simdjson/stage1_find_marks_macros.h"
#ifdef IS_X86_64
TARGET_HASWELL
namespace simdjson {
template <> struct simd_input<Architecture::HASWELL> {
__m256i lo;
__m256i hi;
};
template <>
really_inline simd_input<Architecture::HASWELL>
fill_input<Architecture::HASWELL>(const uint8_t *ptr) {
struct simd_input<Architecture::HASWELL> in;
in.lo = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(ptr + 0));
in.hi = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(ptr + 32));
return in;
}
template <>
really_inline uint64_t
compute_quote_mask<Architecture::HASWELL>(uint64_t quote_bits) {
// There should be no such thing with a processing supporting avx2
// but not clmul.
uint64_t quote_mask = _mm_cvtsi128_si64(_mm_clmulepi64_si128(
_mm_set_epi64x(0ULL, quote_bits), _mm_set1_epi8(0xFFu), 0));
return quote_mask;
}
template <> struct utf8_checking_state<Architecture::HASWELL> {
__m256i has_error;
avx_processed_utf_bytes previous;
utf8_checking_state() {
has_error = _mm256_setzero_si256();
previous.raw_bytes = _mm256_setzero_si256();
previous.high_nibbles = _mm256_setzero_si256();
previous.carried_continuations = _mm256_setzero_si256();
}
};
template <>
really_inline void check_utf8<Architecture::HASWELL>(
simd_input<Architecture::HASWELL> in,
utf8_checking_state<Architecture::HASWELL> &state) {
__m256i high_bit = _mm256_set1_epi8(0x80u);
if ((_mm256_testz_si256(_mm256_or_si256(in.lo, in.hi), high_bit)) == 1) {
// it is ascii, we just check continuation
state.has_error = _mm256_or_si256(
_mm256_cmpgt_epi8(state.previous.carried_continuations,
_mm256_setr_epi8(9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 1)),
state.has_error);
} else {
// it is not ascii so we have to do heavy work
state.previous =
avx_check_utf8_bytes(in.lo, &(state.previous), &(state.has_error));
state.previous =
avx_check_utf8_bytes(in.hi, &(state.previous), &(state.has_error));
}
}
template <>
really_inline ErrorValues check_utf8_errors<Architecture::HASWELL>(
utf8_checking_state<Architecture::HASWELL> &state) {
return _mm256_testz_si256(state.has_error, state.has_error) == 0
? simdjson::UTF8_ERROR
: simdjson::SUCCESS;
}
template <>
really_inline uint64_t cmp_mask_against_input<Architecture::HASWELL>(
simd_input<Architecture::HASWELL> in, uint8_t m) {
const __m256i mask = _mm256_set1_epi8(m);
__m256i cmp_res_0 = _mm256_cmpeq_epi8(in.lo, mask);
uint64_t res_0 = static_cast<uint32_t>(_mm256_movemask_epi8(cmp_res_0));
__m256i cmp_res_1 = _mm256_cmpeq_epi8(in.hi, mask);
uint64_t res_1 = _mm256_movemask_epi8(cmp_res_1);
return res_0 | (res_1 << 32);
}
template <>
really_inline uint64_t unsigned_lteq_against_input<Architecture::HASWELL>(
simd_input<Architecture::HASWELL> in, uint8_t m) {
const __m256i maxval = _mm256_set1_epi8(m);
__m256i cmp_res_0 = _mm256_cmpeq_epi8(_mm256_max_epu8(maxval, in.lo), maxval);
uint64_t res_0 = static_cast<uint32_t>(_mm256_movemask_epi8(cmp_res_0));
__m256i cmp_res_1 = _mm256_cmpeq_epi8(_mm256_max_epu8(maxval, in.hi), maxval);
uint64_t res_1 = _mm256_movemask_epi8(cmp_res_1);
return res_0 | (res_1 << 32);
}
template <>
really_inline uint64_t find_odd_backslash_sequences<Architecture::HASWELL>(
simd_input<Architecture::HASWELL> in,
uint64_t &prev_iter_ends_odd_backslash) {
FIND_ODD_BACKSLASH_SEQUENCES(Architecture::HASWELL, in,
prev_iter_ends_odd_backslash);
}
template <>
really_inline uint64_t find_quote_mask_and_bits<Architecture::HASWELL>(
simd_input<Architecture::HASWELL> in, uint64_t odd_ends,
uint64_t &prev_iter_inside_quote, uint64_t &quote_bits,
uint64_t &error_mask) {
FIND_QUOTE_MASK_AND_BITS(Architecture::HASWELL, in, odd_ends,
prev_iter_inside_quote, quote_bits, error_mask)
}
template <>
really_inline void find_whitespace_and_structurals<Architecture::HASWELL>(
simd_input<Architecture::HASWELL> in, uint64_t &whitespace,
uint64_t &structurals) {
#ifdef SIMDJSON_NAIVE_STRUCTURAL
// You should never need this naive approach, but it can be useful
// for research purposes
const __m256i mask_open_brace = _mm256_set1_epi8(0x7b);
__m256i struct_lo = _mm256_cmpeq_epi8(in.lo, mask_open_brace);
__m256i struct_hi = _mm256_cmpeq_epi8(in.hi, mask_open_brace);
const __m256i mask_close_brace = _mm256_set1_epi8(0x7d);
struct_lo =
_mm256_or_si256(struct_lo, _mm256_cmpeq_epi8(in.lo, mask_close_brace));
struct_hi =
_mm256_or_si256(struct_hi, _mm256_cmpeq_epi8(in.hi, mask_close_brace));
const __m256i mask_open_bracket = _mm256_set1_epi8(0x5b);
struct_lo =
_mm256_or_si256(struct_lo, _mm256_cmpeq_epi8(in.lo, mask_open_bracket));
struct_hi =
_mm256_or_si256(struct_hi, _mm256_cmpeq_epi8(in.hi, mask_open_bracket));
const __m256i mask_close_bracket = _mm256_set1_epi8(0x5d);
struct_lo =
_mm256_or_si256(struct_lo, _mm256_cmpeq_epi8(in.lo, mask_close_bracket));
struct_hi =
_mm256_or_si256(struct_hi, _mm256_cmpeq_epi8(in.hi, mask_close_bracket));
const __m256i mask_column = _mm256_set1_epi8(0x3a);
struct_lo = _mm256_or_si256(struct_lo, _mm256_cmpeq_epi8(in.lo, mask_column));
struct_hi = _mm256_or_si256(struct_hi, _mm256_cmpeq_epi8(in.hi, mask_column));
const __m256i mask_comma = _mm256_set1_epi8(0x2c);
struct_lo = _mm256_or_si256(struct_lo, _mm256_cmpeq_epi8(in.lo, mask_comma));
struct_hi = _mm256_or_si256(struct_hi, _mm256_cmpeq_epi8(in.hi, mask_comma));
uint64_t structural_res_0 =
static_cast<uint32_t>(_mm256_movemask_epi8(struct_lo));
uint64_t structural_res_1 = _mm256_movemask_epi8(struct_hi);
structurals = (structural_res_0 | (structural_res_1 << 32));
const __m256i mask_space = _mm256_set1_epi8(0x20);
__m256i space_lo = _mm256_cmpeq_epi8(in.lo, mask_space);
__m256i space_hi = _mm256_cmpeq_epi8(in.hi, mask_space);
const __m256i mask_linefeed = _mm256_set1_epi8(0x0a);
space_lo = _mm256_or_si256(space_lo, _mm256_cmpeq_epi8(in.lo, mask_linefeed));
space_hi = _mm256_or_si256(space_hi, _mm256_cmpeq_epi8(in.hi, mask_linefeed));
const __m256i mask_tab = _mm256_set1_epi8(0x09);
space_lo = _mm256_or_si256(space_lo, _mm256_cmpeq_epi8(in.lo, mask_tab));
space_hi = _mm256_or_si256(space_hi, _mm256_cmpeq_epi8(in.hi, mask_tab));
const __m256i mask_carriage = _mm256_set1_epi8(0x0d);
space_lo = _mm256_or_si256(space_lo, _mm256_cmpeq_epi8(in.lo, mask_carriage));
space_hi = _mm256_or_si256(space_hi, _mm256_cmpeq_epi8(in.hi, mask_carriage));
uint64_t ws_res_0 = static_cast<uint32_t>(_mm256_movemask_epi8(space_lo));
uint64_t ws_res_1 = _mm256_movemask_epi8(space_hi);
whitespace = (ws_res_0 | (ws_res_1 << 32));
// end of naive approach
#else // SIMDJSON_NAIVE_STRUCTURAL
// clang-format off
const __m256i structural_table =
_mm256_setr_epi8(44, 125, 0, 0, 0xc0u, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58, 123,
44, 125, 0, 0, 0xc0u, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58, 123);
const __m256i white_table = _mm256_setr_epi8(
32, 100, 100, 100, 17, 100, 113, 2, 100, 9, 10, 112, 100, 13, 100, 100,
32, 100, 100, 100, 17, 100, 113, 2, 100, 9, 10, 112, 100, 13, 100, 100);
// clang-format on
const __m256i struct_offset = _mm256_set1_epi8(0xd4u);
const __m256i struct_mask = _mm256_set1_epi8(32);
__m256i lo_white =
_mm256_cmpeq_epi8(in.lo, _mm256_shuffle_epi8(white_table, in.lo));
__m256i hi_white =
_mm256_cmpeq_epi8(in.hi, _mm256_shuffle_epi8(white_table, in.hi));
uint64_t ws_res_0 = static_cast<uint32_t>(_mm256_movemask_epi8(lo_white));
uint64_t ws_res_1 = _mm256_movemask_epi8(hi_white);
whitespace = (ws_res_0 | (ws_res_1 << 32));
__m256i lo_struct_r1 = _mm256_add_epi8(struct_offset, in.lo);
__m256i hi_struct_r1 = _mm256_add_epi8(struct_offset, in.hi);
__m256i lo_struct_r2 = _mm256_or_si256(in.lo, struct_mask);
__m256i hi_struct_r2 = _mm256_or_si256(in.hi, struct_mask);
__m256i lo_struct_r3 = _mm256_shuffle_epi8(structural_table, lo_struct_r1);
__m256i hi_struct_r3 = _mm256_shuffle_epi8(structural_table, hi_struct_r1);
__m256i lo_struct = _mm256_cmpeq_epi8(lo_struct_r2, lo_struct_r3);
__m256i hi_struct = _mm256_cmpeq_epi8(hi_struct_r2, hi_struct_r3);
uint64_t structural_res_0 =
static_cast<uint32_t>(_mm256_movemask_epi8(lo_struct));
uint64_t structural_res_1 = _mm256_movemask_epi8(hi_struct);
structurals = (structural_res_0 | (structural_res_1 << 32));
#endif // SIMDJSON_NAIVE_STRUCTURAL
}
} // namespace simdjson
UNTARGET_REGION
#endif // IS_X86_64
#endif // SIMDJSON_STAGE1_FIND_MARKS_HASWELL_H
+239
View File
@@ -0,0 +1,239 @@
#ifndef SIMDJSON_STAGE1_FIND_MARKS_MACROS_H
#define SIMDJSON_STAGE1_FIND_MARKS_MACROS_H
// return a bitvector indicating where we have characters that end an odd-length
// sequence of backslashes (and thus change the behavior of the next character
// to follow). A even-length sequence of backslashes, and, for that matter, the
// largest even-length prefix of our odd-length sequence of backslashes, simply
// modify the behavior of the backslashes themselves.
// We also update the prev_iter_ends_odd_backslash reference parameter to
// indicate whether we end an iteration on an odd-length sequence of
// backslashes, which modifies our subsequent search for odd-length
// sequences of backslashes in an obvious way.
// We need to compile that code for multiple architectures. However, target
// attributes can be used only once by function definition. Huge macro seemed
// better than huge code duplication. uint64_t
// FIND_ODD_BACKSLASH_SEQUENCES(Architecture T, simd_input<T> in, uint64_t
// &prev_iter_ends_odd_backslash)
#define FIND_ODD_BACKSLASH_SEQUENCES(T, in, prev_iter_ends_odd_backslash) \
{ \
const uint64_t even_bits = 0x5555555555555555ULL; \
const uint64_t odd_bits = ~even_bits; \
uint64_t bs_bits = cmp_mask_against_input<T>(in, '\\'); \
uint64_t start_edges = bs_bits & ~(bs_bits << 1); \
/* flip lowest if we have an odd-length run at the end of the prior \
* iteration */ \
uint64_t even_start_mask = even_bits ^ prev_iter_ends_odd_backslash; \
uint64_t even_starts = start_edges & even_start_mask; \
uint64_t odd_starts = start_edges & ~even_start_mask; \
uint64_t even_carries = bs_bits + even_starts; \
\
uint64_t odd_carries; \
/* must record the carry-out of our odd-carries out of bit 63; this \
* indicates whether the sense of any edge going to the next iteration \
* should be flipped */ \
bool iter_ends_odd_backslash = \
add_overflow(bs_bits, odd_starts, &odd_carries); \
\
odd_carries |= prev_iter_ends_odd_backslash; /* push in bit zero as a \
* potential end if we had an \
* odd-numbered run at the \
* end of the previous \
* iteration */ \
prev_iter_ends_odd_backslash = iter_ends_odd_backslash ? 0x1ULL : 0x0ULL; \
uint64_t even_carry_ends = even_carries & ~bs_bits; \
uint64_t odd_carry_ends = odd_carries & ~bs_bits; \
uint64_t even_start_odd_end = even_carry_ends & odd_bits; \
uint64_t odd_start_even_end = odd_carry_ends & even_bits; \
uint64_t odd_ends = even_start_odd_end | odd_start_even_end; \
return odd_ends; \
}
// return both the quote mask (which is a half-open mask that covers the first
// quote
// in an unescaped quote pair and everything in the quote pair) and the quote
// bits, which are the simple
// unescaped quoted bits. We also update the prev_iter_inside_quote value to
// tell the next iteration
// whether we finished the final iteration inside a quote pair; if so, this
// inverts our behavior of
// whether we're inside quotes for the next iteration.
// Note that we don't do any error checking to see if we have backslash
// sequences outside quotes; these
// backslash sequences (of any length) will be detected elsewhere.
// We need to compile that code for multiple architectures. However, target
// attributes can be used only once by function definition. Huge macro seemed
// better than huge code duplication. uint64_t
// FIND_QUOTE_MASK_AND_BITS(Architecture T, simd_input<T> in, uint64_t odd_ends,
// uint64_t &prev_iter_inside_quote, uint64_t &quote_bits, uint64_t
// &error_mask)
#define FIND_QUOTE_MASK_AND_BITS(T, in, odd_ends, prev_iter_inside_quote, \
quote_bits, error_mask) \
{ \
quote_bits = cmp_mask_against_input<T>(in, '"'); \
quote_bits = quote_bits & ~odd_ends; \
uint64_t quote_mask = compute_quote_mask<T>(quote_bits); \
quote_mask ^= prev_iter_inside_quote; \
/* All Unicode characters may be placed within the \
* quotation marks, except for the characters that MUST be escaped: \
* quotation mark, reverse solidus, and the control characters (U+0000 \
* through U+001F). \
* https://tools.ietf.org/html/rfc8259 */ \
uint64_t unescaped = unsigned_lteq_against_input<T>(in, 0x1F); \
error_mask |= quote_mask & unescaped; \
/* right shift of a signed value expected to be well-defined and standard \
* compliant as of C++20, \
* John Regher from Utah U. says this is fine code */ \
prev_iter_inside_quote = \
static_cast<uint64_t>(static_cast<int64_t>(quote_mask) >> 63); \
return quote_mask; \
}
// Find structural bits in a 64-byte chunk.
// We need to compile that code for multiple architectures. However, target
// attributes can be used only once by function definition. Huge macro seemed
// better than huge code duplication. void FIND_STRUCTURAL_BITS_64(
// Architecture T,
// const uint8_t *buf,
// size_t idx,
// uint32_t *base_ptr,
// uint32_t &base,
// uint64_t &prev_iter_ends_odd_backslash,
// uint64_t &prev_iter_inside_quote,
// uint64_t &prev_iter_ends_pseudo_pred,
// uint64_t &structurals,
// uint64_t &error_mask,
// utf8_checking_state<T> &utf8_state, flatten
// function)
#define FIND_STRUCTURAL_BITS_64( \
T, buf, idx, base_ptr, base, prev_iter_ends_odd_backslash, \
prev_iter_inside_quote, prev_iter_ends_pseudo_pred, structurals, \
error_mask, utf8_state, flat) \
{ \
simd_input<T> in = fill_input<T>(buf); \
check_utf8<T>(in, utf8_state); \
/* detect odd sequences of backslashes */ \
uint64_t odd_ends = \
find_odd_backslash_sequences<T>(in, prev_iter_ends_odd_backslash); \
\
/* detect insides of quote pairs ("quote_mask") and also our quote_bits \
* themselves */ \
uint64_t quote_bits; \
uint64_t quote_mask = find_quote_mask_and_bits<T>( \
in, odd_ends, prev_iter_inside_quote, quote_bits, error_mask); \
\
/* take the previous iterations structural bits, not our current \
* iteration, \
* and flatten */ \
flat(base_ptr, base, idx, structurals); \
\
uint64_t whitespace; \
find_whitespace_and_structurals<T>(in, whitespace, structurals); \
\
/* fixup structurals to reflect quotes and add pseudo-structural \
* characters */ \
structurals = \
finalize_structurals(structurals, whitespace, quote_mask, quote_bits, \
prev_iter_ends_pseudo_pred); \
}
// We need to compile that code for multiple architectures. However, target
// attributes can be used only once by function definition. Huge macro seemed
// better than huge code duplication. ErrorValues
// FIND_STRUCTURAL_BITS(Architecture T, const uint8_t *buf, size_t len,
// ParsedJson &pj, flatten function)
#define FIND_STRUCTURAL_BITS(T, buf, len, pj, flat) \
{ \
if (len > pj.byte_capacity) { \
std::cerr << "Your ParsedJson object only supports documents up to " \
<< pj.byte_capacity << " bytes but you are trying to process " \
<< len << " bytes" << std::endl; \
return simdjson::CAPACITY; \
} \
uint32_t *base_ptr = pj.structural_indexes; \
uint32_t base = 0; \
utf8_checking_state<T> utf8_state; \
\
/* we have padded the input out to 64 byte multiple with the remainder \
* being zeros persistent state across loop does the last iteration end \
* with an odd-length sequence of backslashes? */ \
\
/* either 0 or 1, but a 64-bit value */ \
uint64_t prev_iter_ends_odd_backslash = 0ULL; \
/* does the previous iteration end inside a double-quote pair? */ \
uint64_t prev_iter_inside_quote = \
0ULL; /* either all zeros or all ones \
* does the previous iteration end on something that is a \
* predecessor of a pseudo-structural character - i.e. \
* whitespace or a structural character effectively the very \
* first char is considered to follow "whitespace" for the \
* purposes of pseudo-structural character detection so we \
* initialize to 1 */ \
uint64_t prev_iter_ends_pseudo_pred = 1ULL; \
\
/* structurals are persistent state across loop as we flatten them on the \
* subsequent iteration into our array pointed to be base_ptr. \
* This is harmless on the first iteration as structurals==0 \
* and is done for performance reasons; we can hide some of the latency of \
* the \
* expensive carryless multiply in the previous step with this work */ \
uint64_t structurals = 0; \
\
size_t lenminus64 = len < 64 ? 0 : len - 64; \
size_t idx = 0; \
uint64_t error_mask = 0; /* for unescaped characters within strings (ASCII \
code points < 0x20) */ \
\
for (; idx < lenminus64; idx += 64) { \
FIND_STRUCTURAL_BITS_64( \
T, &buf[idx], idx, base_ptr, base, prev_iter_ends_odd_backslash, \
prev_iter_inside_quote, prev_iter_ends_pseudo_pred, structurals, \
error_mask, utf8_state, flat); \
} \
/* If we have a final chunk of less than 64 bytes, pad it to 64 with \
* spaces before processing it (otherwise, we risk invalidating the UTF-8 \
* checks). */ \
if (idx < len) { \
uint8_t tmp_buf[64]; \
memset(tmp_buf, 0x20, 64); \
memcpy(tmp_buf, buf + idx, len - idx); \
FIND_STRUCTURAL_BITS_64( \
T, &tmp_buf[0], idx, base_ptr, base, prev_iter_ends_odd_backslash, \
prev_iter_inside_quote, prev_iter_ends_pseudo_pred, structurals, \
error_mask, utf8_state, flat); \
idx += 64; \
} \
\
/* is last string quote closed? */ \
if (prev_iter_inside_quote) { \
return simdjson::UNCLOSED_STRING; \
} \
\
/* finally, flatten out the remaining structurals from the last iteration \
*/ \
flat(base_ptr, base, idx, structurals); \
\
pj.n_structural_indexes = base; \
/* a valid JSON file cannot have zero structural indexes - we should have \
* found something */ \
if (pj.n_structural_indexes == 0u) { \
return simdjson::EMPTY; \
} \
if (base_ptr[pj.n_structural_indexes - 1] > len) { \
return simdjson::UNEXPECTED_ERROR; \
} \
if (len != base_ptr[pj.n_structural_indexes - 1]) { \
/* the string might not be NULL terminated, but we add a virtual NULL \
* ending \
* character. */ \
base_ptr[pj.n_structural_indexes++] = len; \
} \
/* make it safe to dereference one beyond this array */ \
base_ptr[pj.n_structural_indexes] = 0; \
if (error_mask) { \
return simdjson::UNESCAPED_CHARS; \
} \
return check_utf8_errors<T>(utf8_state); \
}
#endif // SIMDJSON_STAGE1_FIND_MARKS_MACROS_H
@@ -0,0 +1,194 @@
#ifndef SIMDJSON_STAGE1_FIND_MARKS_WESTMERE_H
#define SIMDJSON_STAGE1_FIND_MARKS_WESTMERE_H
#include "simdjson/simdutf8check_westmere.h"
#include "simdjson/stage1_find_marks.h"
#include "simdjson/stage1_find_marks_flatten.h"
#include "simdjson/stage1_find_marks_macros.h"
#ifdef IS_X86_64
TARGET_WESTMERE
namespace simdjson {
template <> struct simd_input<Architecture::WESTMERE> {
__m128i v0;
__m128i v1;
__m128i v2;
__m128i v3;
};
template <>
really_inline simd_input<Architecture::WESTMERE>
fill_input<Architecture::WESTMERE>(const uint8_t *ptr) {
struct simd_input<Architecture::WESTMERE> in;
in.v0 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(ptr + 0));
in.v1 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(ptr + 16));
in.v2 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(ptr + 32));
in.v3 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(ptr + 48));
return in;
}
template <>
really_inline uint64_t
compute_quote_mask<Architecture::WESTMERE>(uint64_t quote_bits) {
return _mm_cvtsi128_si64(_mm_clmulepi64_si128(
_mm_set_epi64x(0ULL, quote_bits), _mm_set1_epi8(0xFFu), 0));
}
template <> struct utf8_checking_state<Architecture::WESTMERE> {
__m128i has_error = _mm_setzero_si128();
processed_utf_bytes previous{
_mm_setzero_si128(), // raw_bytes
_mm_setzero_si128(), // high_nibbles
_mm_setzero_si128() // carried_continuations
};
};
template <>
really_inline void check_utf8<Architecture::WESTMERE>(
simd_input<Architecture::WESTMERE> in,
utf8_checking_state<Architecture::WESTMERE> &state) {
__m128i high_bit = _mm_set1_epi8(0x80u);
if ((_mm_testz_si128(_mm_or_si128(in.v0, in.v1), high_bit)) == 1) {
// it is ascii, we just check continuation
state.has_error =
_mm_or_si128(_mm_cmpgt_epi8(state.previous.carried_continuations,
_mm_setr_epi8(9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 1)),
state.has_error);
} else {
// it is not ascii so we have to do heavy work
state.previous =
check_utf8_bytes(in.v0, &(state.previous), &(state.has_error));
state.previous =
check_utf8_bytes(in.v1, &(state.previous), &(state.has_error));
}
if ((_mm_testz_si128(_mm_or_si128(in.v2, in.v3), high_bit)) == 1) {
// it is ascii, we just check continuation
state.has_error =
_mm_or_si128(_mm_cmpgt_epi8(state.previous.carried_continuations,
_mm_setr_epi8(9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 1)),
state.has_error);
} else {
// it is not ascii so we have to do heavy work
state.previous =
check_utf8_bytes(in.v2, &(state.previous), &(state.has_error));
state.previous =
check_utf8_bytes(in.v3, &(state.previous), &(state.has_error));
}
}
template <>
really_inline ErrorValues check_utf8_errors<Architecture::WESTMERE>(
utf8_checking_state<Architecture::WESTMERE> &state) {
return _mm_testz_si128(state.has_error, state.has_error) == 0
? simdjson::UTF8_ERROR
: simdjson::SUCCESS;
}
template <>
really_inline uint64_t cmp_mask_against_input<Architecture::WESTMERE>(
simd_input<Architecture::WESTMERE> in, uint8_t m) {
const __m128i mask = _mm_set1_epi8(m);
__m128i cmp_res_0 = _mm_cmpeq_epi8(in.v0, mask);
uint64_t res_0 = _mm_movemask_epi8(cmp_res_0);
__m128i cmp_res_1 = _mm_cmpeq_epi8(in.v1, mask);
uint64_t res_1 = _mm_movemask_epi8(cmp_res_1);
__m128i cmp_res_2 = _mm_cmpeq_epi8(in.v2, mask);
uint64_t res_2 = _mm_movemask_epi8(cmp_res_2);
__m128i cmp_res_3 = _mm_cmpeq_epi8(in.v3, mask);
uint64_t res_3 = _mm_movemask_epi8(cmp_res_3);
return res_0 | (res_1 << 16) | (res_2 << 32) | (res_3 << 48);
}
template <>
really_inline uint64_t unsigned_lteq_against_input<Architecture::WESTMERE>(
simd_input<Architecture::WESTMERE> in, uint8_t m) {
const __m128i maxval = _mm_set1_epi8(m);
__m128i cmp_res_0 = _mm_cmpeq_epi8(_mm_max_epu8(maxval, in.v0), maxval);
uint64_t res_0 = _mm_movemask_epi8(cmp_res_0);
__m128i cmp_res_1 = _mm_cmpeq_epi8(_mm_max_epu8(maxval, in.v1), maxval);
uint64_t res_1 = _mm_movemask_epi8(cmp_res_1);
__m128i cmp_res_2 = _mm_cmpeq_epi8(_mm_max_epu8(maxval, in.v2), maxval);
uint64_t res_2 = _mm_movemask_epi8(cmp_res_2);
__m128i cmp_res_3 = _mm_cmpeq_epi8(_mm_max_epu8(maxval, in.v3), maxval);
uint64_t res_3 = _mm_movemask_epi8(cmp_res_3);
return res_0 | (res_1 << 16) | (res_2 << 32) | (res_3 << 48);
}
template <>
really_inline uint64_t find_odd_backslash_sequences<Architecture::WESTMERE>(
simd_input<Architecture::WESTMERE> in,
uint64_t &prev_iter_ends_odd_backslash) {
FIND_ODD_BACKSLASH_SEQUENCES(Architecture::WESTMERE, in,
prev_iter_ends_odd_backslash);
}
template <>
really_inline uint64_t find_quote_mask_and_bits<Architecture::WESTMERE>(
simd_input<Architecture::WESTMERE> in, uint64_t odd_ends,
uint64_t &prev_iter_inside_quote, uint64_t &quote_bits,
uint64_t &error_mask) {
FIND_QUOTE_MASK_AND_BITS(Architecture::WESTMERE, in, odd_ends,
prev_iter_inside_quote, quote_bits, error_mask)
}
template <>
really_inline void find_whitespace_and_structurals<Architecture::WESTMERE>(
simd_input<Architecture::WESTMERE> in, uint64_t &whitespace,
uint64_t &structurals) {
const __m128i structural_table =
_mm_setr_epi8(44, 125, 0, 0, 0xc0u, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58, 123);
const __m128i white_table = _mm_setr_epi8(32, 100, 100, 100, 17, 100, 113, 2,
100, 9, 10, 112, 100, 13, 100, 100);
const __m128i struct_offset = _mm_set1_epi8(0xd4u);
const __m128i struct_mask = _mm_set1_epi8(32);
__m128i white0 = _mm_cmpeq_epi8(in.v0, _mm_shuffle_epi8(white_table, in.v0));
__m128i white1 = _mm_cmpeq_epi8(in.v1, _mm_shuffle_epi8(white_table, in.v1));
__m128i white2 = _mm_cmpeq_epi8(in.v2, _mm_shuffle_epi8(white_table, in.v2));
__m128i white3 = _mm_cmpeq_epi8(in.v3, _mm_shuffle_epi8(white_table, in.v3));
uint64_t ws_res_0 = _mm_movemask_epi8(white0);
uint64_t ws_res_1 = _mm_movemask_epi8(white1);
uint64_t ws_res_2 = _mm_movemask_epi8(white2);
uint64_t ws_res_3 = _mm_movemask_epi8(white3);
whitespace =
(ws_res_0 | (ws_res_1 << 16) | (ws_res_2 << 32) | (ws_res_3 << 48));
__m128i struct1_r1 = _mm_add_epi8(struct_offset, in.v0);
__m128i struct2_r1 = _mm_add_epi8(struct_offset, in.v1);
__m128i struct3_r1 = _mm_add_epi8(struct_offset, in.v2);
__m128i struct4_r1 = _mm_add_epi8(struct_offset, in.v3);
__m128i struct1_r2 = _mm_or_si128(in.v0, struct_mask);
__m128i struct2_r2 = _mm_or_si128(in.v1, struct_mask);
__m128i struct3_r2 = _mm_or_si128(in.v2, struct_mask);
__m128i struct4_r2 = _mm_or_si128(in.v3, struct_mask);
__m128i struct1_r3 = _mm_shuffle_epi8(structural_table, struct1_r1);
__m128i struct2_r3 = _mm_shuffle_epi8(structural_table, struct2_r1);
__m128i struct3_r3 = _mm_shuffle_epi8(structural_table, struct3_r1);
__m128i struct4_r3 = _mm_shuffle_epi8(structural_table, struct4_r1);
__m128i struct1 = _mm_cmpeq_epi8(struct1_r2, struct1_r3);
__m128i struct2 = _mm_cmpeq_epi8(struct2_r2, struct2_r3);
__m128i struct3 = _mm_cmpeq_epi8(struct3_r2, struct3_r3);
__m128i struct4 = _mm_cmpeq_epi8(struct4_r2, struct4_r3);
uint64_t structural_res_0 = _mm_movemask_epi8(struct1);
uint64_t structural_res_1 = _mm_movemask_epi8(struct2);
uint64_t structural_res_2 = _mm_movemask_epi8(struct3);
uint64_t structural_res_3 = _mm_movemask_epi8(struct4);
structurals = (structural_res_0 | (structural_res_1 << 16) |
(structural_res_2 << 32) | (structural_res_3 << 48));
}
} // namespace simdjson
UNTARGET_REGION
#endif // IS_X86_64
#endif // SIMDJSON_STAGE1_FIND_MARKS_WESTMERE_H
+73 -4
View File
@@ -1,16 +1,85 @@
#ifndef SIMDJSON_STAGE2_BUILD_TAPE_H
#define SIMDJSON_STAGE2_BUILD_TAPE_H
#include <cassert>
#include <cstring>
#include <iostream>
#include "simdjson/common_defs.h"
#include "simdjson/jsoncharutils.h"
#include "simdjson/numberparsing.h"
#include "simdjson/parsedjson.h"
#include "simdjson/simdjson.h"
#include "simdjson/stringparsing.h"
struct ParsedJson;
namespace simdjson {
void init_state_machine();
WARN_UNUSED
int unified_machine(const uint8_t *buf, size_t len, ParsedJson &pj);
really_inline bool is_valid_true_atom(const uint8_t *loc) {
uint64_t tv = *reinterpret_cast<const uint64_t *>("true ");
uint64_t mask4 = 0x00000000ffffffff;
uint32_t error = 0;
uint64_t
locval; // we want to avoid unaligned 64-bit loads (undefined in C/C++)
// this can read up to 7 bytes beyond the buffer size, but we require
// SIMDJSON_PADDING of padding
static_assert(sizeof(uint64_t) - 1 <= SIMDJSON_PADDING);
std::memcpy(&locval, loc, sizeof(uint64_t));
error = (locval & mask4) ^ tv;
error |= is_not_structural_or_whitespace(loc[4]);
return error == 0;
}
WARN_UNUSED
int unified_machine(const char *buf, size_t len, ParsedJson &pj);
really_inline bool is_valid_false_atom(const uint8_t *loc) {
// We have to use an integer constant because the space in the cast
// below would lead to values illegally being qualified
// uint64_t fv = *reinterpret_cast<const uint64_t *>("false ");
// using this constant (that is the same false) but nulls out the
// unused bits solves that
uint64_t fv = 0x00000065736c6166; // takes into account endianness
uint64_t mask5 = 0x000000ffffffffff;
// we can't use the 32 bit value for checking for errors otherwise
// the last character of false (it being 5 byte long!) would be
// ignored
uint64_t error = 0;
uint64_t
locval; // we want to avoid unaligned 64-bit loads (undefined in C/C++)
// this can read up to 7 bytes beyond the buffer size, but we require
// SIMDJSON_PADDING of padding
static_assert(sizeof(uint64_t) - 1 <= SIMDJSON_PADDING);
std::memcpy(&locval, loc, sizeof(uint64_t));
error = (locval & mask5) ^ fv;
error |= is_not_structural_or_whitespace(loc[5]);
return error == 0;
}
WARN_UNUSED
really_inline bool is_valid_null_atom(const uint8_t *loc) {
uint64_t nv = *reinterpret_cast<const uint64_t *>("null ");
uint64_t mask4 = 0x00000000ffffffff;
uint32_t error = 0;
uint64_t
locval; // we want to avoid unaligned 64-bit loads (undefined in C/C++)
// this can read up to 7 bytes beyond the buffer size, but we require
// SIMDJSON_PADDING of padding
static_assert(sizeof(uint64_t) - 1 <= SIMDJSON_PADDING);
std::memcpy(&locval, loc, sizeof(uint64_t));
error = (locval & mask4) ^ nv;
error |= is_not_structural_or_whitespace(loc[4]);
return error == 0;
}
template <Architecture T = Architecture::NATIVE>
WARN_UNUSED ALLOW_SAME_PAGE_BUFFER_OVERRUN_QUALIFIER LENIENT_MEM_SANITIZER int
unified_machine(const uint8_t *buf, size_t len, ParsedJson &pj);
template <Architecture T = Architecture::NATIVE>
int unified_machine(const char *buf, size_t len, ParsedJson &pj) {
return unified_machine<T>(reinterpret_cast<const uint8_t *>(buf), len, pj);
}
} // namespace simdjson
#endif
+31 -132
View File
@@ -5,7 +5,13 @@
#include "simdjson/jsoncharutils.h"
#include "simdjson/parsedjson.h"
#ifdef JSON_TEST_STRINGS
void found_string(const uint8_t *buf, const uint8_t *parsed_begin,
const uint8_t *parsed_end);
void found_bad_string(const uint8_t *buf);
#endif
namespace simdjson {
// begin copypasta
// These chars yield themselves: " \ /
// b -> backspace, f -> formfeed, n -> newline, r -> cr, t -> horizontal tab
@@ -32,7 +38,6 @@ static const uint8_t escape_map[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
// handle a unicode codepoint
// write appropriate values into dest
// src will advance 6 bytes or 12 bytes
@@ -40,9 +45,10 @@ static const uint8_t escape_map[256] = {
// return true if the unicode codepoint was valid
// We work in little-endian then swap at write time
WARN_UNUSED
really_inline bool handle_unicode_codepoint(const uint8_t **src_ptr, uint8_t **dst_ptr) {
really_inline bool handle_unicode_codepoint(const uint8_t **src_ptr,
uint8_t **dst_ptr) {
// hex_to_u32_nocheck fills high 16 bits of the return value with 1s if the
// conversion isn't valid; we defer the check for this to inside the
// conversion isn't valid; we defer the check for this to inside the
// multilingual plane check
uint32_t code_point = hex_to_u32_nocheck(*src_ptr + 2);
*src_ptr += 6;
@@ -53,14 +59,14 @@ really_inline bool handle_unicode_codepoint(const uint8_t **src_ptr, uint8_t **d
return false;
}
uint32_t code_point_2 = hex_to_u32_nocheck(*src_ptr + 2);
// if the first code point is invalid we will get here, as we will go past
// the check for being outside the Basic Multilingual plane. If we don't
// find a \u immediately afterwards we fail out anyhow, but if we do,
// find a \u immediately afterwards we fail out anyhow, but if we do,
// this check catches both the case of the first code point being invalid
// or the second code point being invalid.
if ((code_point | code_point_2) >> 16) {
return false;
return false;
}
code_point =
@@ -72,135 +78,28 @@ really_inline bool handle_unicode_codepoint(const uint8_t **src_ptr, uint8_t **d
return offset > 0;
}
#ifdef __ARM_NEON
#include <arm_neon.h>
#endif
// Holds backslashes and quotes locations.
struct parse_string_helper {
uint32_t bs_bits;
uint32_t quote_bits;
};
WARN_UNUSED ALLOW_SAME_PAGE_BUFFER_OVERRUN_QUALIFIER
really_inline bool parse_string(UNUSED const uint8_t *buf, UNUSED size_t len,
ParsedJson &pj, UNUSED const uint32_t depth, UNUSED uint32_t offset) {
#ifdef SIMDJSON_SKIPSTRINGPARSING // for performance analysis, it is sometimes useful to skip parsing
pj.write_tape(0, '"');// don't bother with the string parsing at all
return true; // always succeeds
#else
pj.write_tape(pj.current_string_buf_loc - pj.string_buf, '"');
const uint8_t *src = &buf[offset + 1]; // we know that buf at offset is a "
uint8_t *dst = pj.current_string_buf_loc + sizeof(uint32_t);
const uint8_t *const start_of_string = dst;
while (1) {
#ifdef __AVX2__
// this can read up to 31 bytes beyond the buffer size, but we require
// SIMDJSON_PADDING of padding
static_assert(sizeof(__m256i) - 1 <= SIMDJSON_PADDING);
__m256i v = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(src));
// store to dest unconditionally - we can overwrite the bits we don't like
// later
_mm256_storeu_si256(reinterpret_cast<__m256i *>(dst), v);
auto bs_bits =
static_cast<uint32_t>(_mm256_movemask_epi8(_mm256_cmpeq_epi8(v, _mm256_set1_epi8('\\'))));
auto quote_mask = _mm256_cmpeq_epi8(v, _mm256_set1_epi8('"'));
auto quote_bits =
static_cast<uint32_t>(_mm256_movemask_epi8(quote_mask));
#else
// this can read up to 31 bytes beyond the buffer size, but we require
// SIMDJSON_PADDING of padding
static_assert(2 * sizeof(uint8x16_t) - 1 <= SIMDJSON_PADDING);
uint8x16_t v0 = vld1q_u8(src);
uint8x16_t v1 = vld1q_u8(src+16);
vst1q_u8(dst, v0);
vst1q_u8(dst+16, v1);
uint8x16_t bs_mask = vmovq_n_u8('\\');
uint8x16_t qt_mask = vmovq_n_u8('"');
const uint8x16_t bitmask = { 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80,
0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80};
uint8x16_t cmp_bs_0 = vceqq_u8(v0, bs_mask);
uint8x16_t cmp_bs_1 = vceqq_u8(v1, bs_mask);
uint8x16_t cmp_qt_0 = vceqq_u8(v0, qt_mask);
uint8x16_t cmp_qt_1 = vceqq_u8(v1, qt_mask);
cmp_bs_0 = vandq_u8(cmp_bs_0, bitmask);
cmp_bs_1 = vandq_u8(cmp_bs_1, bitmask);
cmp_qt_0 = vandq_u8(cmp_qt_0, bitmask);
cmp_qt_1 = vandq_u8(cmp_qt_1, bitmask);
// Finds where the backslashes and quotes are located.
template <Architecture>
parse_string_helper find_bs_bits_and_quote_bits(const uint8_t *src,
uint8_t *dst);
uint8x16_t sum0 = vpaddq_u8(cmp_bs_0, cmp_bs_1);
uint8x16_t sum1 = vpaddq_u8(cmp_qt_0, cmp_qt_1);
sum0 = vpaddq_u8(sum0, sum1);
sum0 = vpaddq_u8(sum0, sum0);
auto bs_bits = vgetq_lane_u32(vreinterpretq_u32_u8(sum0), 0);
auto quote_bits = vgetq_lane_u32(vreinterpretq_u32_u8(sum0), 1);
#endif
if(((bs_bits - 1) & quote_bits) != 0 ) {
// we encountered quotes first. Move dst to point to quotes and exit
template <Architecture T>
WARN_UNUSED ALLOW_SAME_PAGE_BUFFER_OVERRUN_QUALIFIER LENIENT_MEM_SANITIZER
really_inline bool
parse_string(UNUSED const uint8_t *buf, UNUSED size_t len, ParsedJson &pj,
UNUSED const uint32_t depth, UNUSED uint32_t offset);
// find out where the quote is...
uint32_t quote_dist = trailingzeroes(quote_bits);
// NULL termination is still handy if you expect all your strings to be NULL terminated?
// It comes at a small cost
dst[quote_dist] = 0;
uint32_t str_length = (dst - start_of_string) + quote_dist;
memcpy(pj.current_string_buf_loc,&str_length, sizeof(uint32_t));
///////////////////////
// Above, check for overflow in case someone has a crazy string (>=4GB?)
// But only add the overflow check when the document itself exceeds 4GB
// Currently unneeded because we refuse to parse docs larger or equal to 4GB.
////////////////////////
// we advance the point, accounting for the fact that we have a NULl termination
pj.current_string_buf_loc = dst + quote_dist + 1;
#ifdef JSON_TEST_STRINGS // for unit testing
foundString(buf + offset,start_of_string,pj.current_string_buf_loc - 1);
#endif // JSON_TEST_STRINGS
return true;
}
if(((quote_bits - 1) & bs_bits ) != 0 ) {
// find out where the backspace is
uint32_t bs_dist = trailingzeroes(bs_bits);
uint8_t escape_char = src[bs_dist + 1];
// we encountered backslash first. Handle backslash
if (escape_char == 'u') {
// move src/dst up to the start; they will be further adjusted
// within the unicode codepoint handling code.
src += bs_dist;
dst += bs_dist;
if (!handle_unicode_codepoint(&src, &dst)) {
#ifdef JSON_TEST_STRINGS // for unit testing
foundBadString(buf + offset);
#endif // JSON_TEST_STRINGS
return false;
}
} else {
// simple 1:1 conversion. Will eat bs_dist+2 characters in input and
// write bs_dist+1 characters to output
// note this may reach beyond the part of the buffer we've actually
// seen. I think this is ok
uint8_t escape_result = escape_map[escape_char];
if (escape_result == 0u) {
#ifdef JSON_TEST_STRINGS // for unit testing
foundBadString(buf + offset);
#endif // JSON_TEST_STRINGS
return false; // bogus escape value is an error
}
dst[bs_dist] = escape_result;
src += bs_dist + 2;
dst += bs_dist + 1;
}
} else {
// they are the same. Since they can't co-occur, it means we encountered
// neither.
src += 32;
dst += 32;
}
}
// can't be reached
return true;
#endif // SIMDJSON_SKIPSTRINGPARSING
}
} // namespace simdjson
/// Now include the specializations:
#include "simdjson/stringparsing_arm64.h"
#include "simdjson/stringparsing_haswell.h"
#include "simdjson/stringparsing_westmere.h"
#endif
+56
View File
@@ -0,0 +1,56 @@
#ifndef SIMDJSON_STRINGPARSING_ARM64_H
#define SIMDJSON_STRINGPARSING_ARM64_H
#include "simdjson/stringparsing.h"
#include "simdjson/stringparsing_macros.h"
#ifdef IS_ARM64
namespace simdjson {
template <>
really_inline parse_string_helper
find_bs_bits_and_quote_bits<Architecture::ARM64>(const uint8_t *src,
uint8_t *dst) {
// this can read up to 31 bytes beyond the buffer size, but we require
// SIMDJSON_PADDING of padding
static_assert(2 * sizeof(uint8x16_t) - 1 <= SIMDJSON_PADDING);
uint8x16_t v0 = vld1q_u8(src);
uint8x16_t v1 = vld1q_u8(src + 16);
vst1q_u8(dst, v0);
vst1q_u8(dst + 16, v1);
uint8x16_t bs_mask = vmovq_n_u8('\\');
uint8x16_t qt_mask = vmovq_n_u8('"');
const uint8x16_t bit_mask = {0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80,
0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80};
uint8x16_t cmp_bs_0 = vceqq_u8(v0, bs_mask);
uint8x16_t cmp_bs_1 = vceqq_u8(v1, bs_mask);
uint8x16_t cmp_qt_0 = vceqq_u8(v0, qt_mask);
uint8x16_t cmp_qt_1 = vceqq_u8(v1, qt_mask);
cmp_bs_0 = vandq_u8(cmp_bs_0, bit_mask);
cmp_bs_1 = vandq_u8(cmp_bs_1, bit_mask);
cmp_qt_0 = vandq_u8(cmp_qt_0, bit_mask);
cmp_qt_1 = vandq_u8(cmp_qt_1, bit_mask);
uint8x16_t sum0 = vpaddq_u8(cmp_bs_0, cmp_bs_1);
uint8x16_t sum1 = vpaddq_u8(cmp_qt_0, cmp_qt_1);
sum0 = vpaddq_u8(sum0, sum1);
sum0 = vpaddq_u8(sum0, sum0);
return {
vgetq_lane_u32(vreinterpretq_u32_u8(sum0), 0), // bs_bits
vgetq_lane_u32(vreinterpretq_u32_u8(sum0), 1) // quote_bits
};
}
template <>
WARN_UNUSED ALLOW_SAME_PAGE_BUFFER_OVERRUN_QUALIFIER LENIENT_MEM_SANITIZER
really_inline bool
parse_string<Architecture::ARM64>(UNUSED const uint8_t *buf,
UNUSED size_t len, ParsedJson &pj,
UNUSED const uint32_t depth,
UNUSED uint32_t offset) {
PARSE_STRING(Architecture::ARM64, buf, len, pj, depth, offset);
}
} // namespace simdjson
#endif
#endif
+43
View File
@@ -0,0 +1,43 @@
#ifndef SIMDJSON_STRINGPARSING_HASWELL_H
#define SIMDJSON_STRINGPARSING_HASWELL_H
#include "simdjson/stringparsing.h"
#include "simdjson/stringparsing_macros.h"
#ifdef IS_X86_64
TARGET_HASWELL
namespace simdjson {
template <>
really_inline parse_string_helper
find_bs_bits_and_quote_bits<Architecture::HASWELL>(const uint8_t *src,
uint8_t *dst) {
// this can read up to 31 bytes beyond the buffer size, but we require
// SIMDJSON_PADDING of padding
static_assert(sizeof(__m256i) - 1 <= SIMDJSON_PADDING);
__m256i v = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(src));
// store to dest unconditionally - we can overwrite the bits we don't like
// later
_mm256_storeu_si256(reinterpret_cast<__m256i *>(dst), v);
auto quote_mask = _mm256_cmpeq_epi8(v, _mm256_set1_epi8('"'));
return {
static_cast<uint32_t>(_mm256_movemask_epi8(
_mm256_cmpeq_epi8(v, _mm256_set1_epi8('\\')))), // bs_bits
static_cast<uint32_t>(_mm256_movemask_epi8(quote_mask)) // quote_bits
};
}
template <>
WARN_UNUSED ALLOW_SAME_PAGE_BUFFER_OVERRUN_QUALIFIER LENIENT_MEM_SANITIZER
really_inline bool
parse_string<Architecture::HASWELL>(UNUSED const uint8_t *buf,
UNUSED size_t len, ParsedJson &pj,
UNUSED const uint32_t depth,
UNUSED uint32_t offset) {
PARSE_STRING(Architecture::HASWELL, buf, len, pj, depth, offset);
}
} // namespace simdjson
UNTARGET_REGION
#endif
#endif
+88
View File
@@ -0,0 +1,88 @@
#ifndef SIMDJSON_STRINGPARSING_MACROS_H
#define SIMDJSON_STRINGPARSING_MACROS_H
// We need to compile that code for multiple architectures. However, target
// attributes can be used only once by function definition. Huge macro seemed
// better than huge code duplication.ç
// bool PARSE_STRING(Architecture T, const uint8_t *buf, size_t len, ParsedJson
// &pj,const uint32_t depth, uint32_t offset)
#define PARSE_STRING(T, buf, len, pj, depth, offset) \
{ \
pj.write_tape(pj.current_string_buf_loc - pj.string_buf, '"'); \
const uint8_t *src = \
&buf[offset + 1]; /* we know that buf at offset is a " */ \
uint8_t *dst = pj.current_string_buf_loc + sizeof(uint32_t); \
const uint8_t *const start_of_string = dst; \
while (1) { \
parse_string_helper helper = find_bs_bits_and_quote_bits<T>(src, dst); \
if (((helper.bs_bits - 1) & helper.quote_bits) != 0) { \
/* we encountered quotes first. Move dst to point to quotes and exit \
*/ \
\
/* find out where the quote is... */ \
uint32_t quote_dist = trailing_zeroes(helper.quote_bits); \
\
/* NULL termination is still handy if you expect all your strings to \
* be NULL terminated? */ \
/* It comes at a small cost */ \
dst[quote_dist] = 0; \
\
uint32_t str_length = (dst - start_of_string) + quote_dist; \
memcpy(pj.current_string_buf_loc, &str_length, sizeof(uint32_t)); \
/***************************** \
* Above, check for overflow in case someone has a crazy string \
* (>=4GB?) _ \
* But only add the overflow check when the document itself exceeds \
* 4GB \
* Currently unneeded because we refuse to parse docs larger or equal \
* to 4GB. \
****************************/ \
\
/* we advance the point, accounting for the fact that we have a NULL \
* termination */ \
pj.current_string_buf_loc = dst + quote_dist + 1; \
return true; \
} \
if (((helper.quote_bits - 1) & helper.bs_bits) != 0) { \
/* find out where the backspace is */ \
uint32_t bs_dist = trailing_zeroes(helper.bs_bits); \
uint8_t escape_char = src[bs_dist + 1]; \
/* we encountered backslash first. Handle backslash */ \
if (escape_char == 'u') { \
/* move src/dst up to the start; they will be further adjusted \
within the unicode codepoint handling code. */ \
src += bs_dist; \
dst += bs_dist; \
if (!handle_unicode_codepoint(&src, &dst)) { \
return false; \
} \
} else { \
/* simple 1:1 conversion. Will eat bs_dist+2 characters in input and \
* write bs_dist+1 characters to output \
* note this may reach beyond the part of the buffer we've actually \
* seen. I think this is ok */ \
uint8_t escape_result = escape_map[escape_char]; \
if (escape_result == 0u) { \
return false; /* bogus escape value is an error */ \
} \
dst[bs_dist] = escape_result; \
src += bs_dist + 2; \
dst += bs_dist + 1; \
} \
} else { \
/* they are the same. Since they can't co-occur, it means we \
* encountered neither. */ \
if constexpr (T == Architecture::WESTMERE) { \
src += 16; \
dst += 16; \
} else { \
src += 32; \
dst += 32; \
} \
} \
} \
/* can't be reached */ \
return true; \
}
#endif
+41
View File
@@ -0,0 +1,41 @@
#ifndef SIMDJSON_STRINGPARSING_WESTMERE_H
#define SIMDJSON_STRINGPARSING_WESTMERE_H
#include "simdjson/stringparsing.h"
#include "simdjson/stringparsing_macros.h"
#ifdef IS_X86_64
TARGET_WESTMERE
namespace simdjson {
template <>
really_inline parse_string_helper
find_bs_bits_and_quote_bits<Architecture::WESTMERE>(const uint8_t *src,
uint8_t *dst) {
// this can read up to 31 bytes beyond the buffer size, but we require
// SIMDJSON_PADDING of padding
__m128i v = _mm_loadu_si128(reinterpret_cast<const __m128i *>(src));
// store to dest unconditionally - we can overwrite the bits we don't like
// later
_mm_storeu_si128(reinterpret_cast<__m128i *>(dst), v);
auto quote_mask = _mm_cmpeq_epi8(v, _mm_set1_epi8('"'));
return {
static_cast<uint32_t>(
_mm_movemask_epi8(_mm_cmpeq_epi8(v, _mm_set1_epi8('\\')))), // bs_bits
static_cast<uint32_t>(_mm_movemask_epi8(quote_mask)) // quote_bits
};
}
template <>
WARN_UNUSED ALLOW_SAME_PAGE_BUFFER_OVERRUN_QUALIFIER LENIENT_MEM_SANITIZER
really_inline bool
parse_string<Architecture::WESTMERE>(UNUSED const uint8_t *buf,
UNUSED size_t len, ParsedJson &pj,
UNUSED const uint32_t depth,
UNUSED uint32_t offset) {
PARSE_STRING(Architecture::WESTMERE, buf, len, pj, depth, offset);
}
} // namespace simdjson
UNTARGET_REGION
#endif
#endif
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
44
Binary file not shown.
Binary file not shown.
View File
+1
View File
@@ -0,0 +1 @@
"a bad string±‡"
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
[1.0e-308,0.1e-308,0.01e-307,1.79769e+308,2.22507e-308,-1.79769e+308,-2.22507e-308]
+1
View File
@@ -0,0 +1 @@
[1000000000000000000e0,1000000000000000000e-0,1000000000000000000.0e0,1000000000000000000e10,"issue187"]
+1
View File
@@ -0,0 +1 @@
-1
+1
View File
@@ -0,0 +1 @@
1.2e000000010
+1
View File
@@ -0,0 +1 @@
{"Width":800,"Height":600,"Title":"View from my room","Url":"http://ex.com/img.png","Private":false,"Thumbnail":{"Url":"http://ex.com/th.png","Height":125,"Width":100},"array":[116,943,234],"Owner":null}
+1 -1
View File
@@ -15479,4 +15479,4 @@
"since_id": 0,
"since_id_str": "0"
}
}
}
-27
View File
@@ -1,27 +0,0 @@
set term pdfcairo fontscale 0.5
set output name
set boxwidth 0.8
set style fill solid
set ylabel "cycles per input byte"
set style line 80 lt rgb "#000000"
# Line style for grid
set style line 81 lt 0 # dashed
set style line 81 lt rgb "#808080" # grey
set grid back linestyle 81
set border 3 back linestyle 80 # Remove border on top and right. These
# borders are useless and make it harder
# to see plotted lines near the border.
# Also, put it in grey; no need for so much emphasis on a border.
set xtics nomirror
set ytics nomirror
set yrange [0:]
set format y "%0.1f";
set style line 1 lt rgb "#A0A0A0" lw 1 pt 1 ps 1
plot filename using 0:2:xtic(1) with boxes notitle ls 1, '' using 0:(1):(sprintf("%.2g", $2)) with labels notitle
@@ -1,12 +0,0 @@
name cycles_per_byte cycles_per_byte_err gb_per_s gb_per_s_err
"simdjson " 1.128 0.080 1.939 0.127
"RapidJSON " 4.996 0.201 0.441 0.017
"RapidJSON (insitu)" 3.910 0.122 0.563 0.017
"sajson (dynamic mem)" 3.149 0.063 0.699 0.014
"sajson" 2.116 0.058 1.038 0.028
"dropbox (json11) " 14.236 0.137 0.155 0.001
"fastjson " 9.305 0.429 0.237 0.010
"gason " 2.917 0.337 0.754 0.078
"ultrajson " 6.413 0.279 0.344 0.014
"jsmn " 37.170 0.232 0.059 0.000
"cJSON " 8.661 0.091 0.255 0.003
-12
View File
@@ -1,12 +0,0 @@
name cycles_per_byte cycles_per_byte_err gb_per_s gb_per_s_err
"simdjson " 2.452 0.050 0.900 0.018
"RapidJSON " 5.084 0.047 0.434 0.004
"RapidJSON (insitu)" 5.067 0.044 0.436 0.004
"sajson (dynamic mem)" 4.375 0.120 0.505 0.013
"sajson" 3.238 0.030 0.682 0.006
"dropbox (json11) " 35.101 0.229 0.063 0.000
"fastjson " 11.541 0.347 0.191 0.006
"gason " 3.489 0.667 0.633 0.102
"ultrajson " 5.596 0.027 0.394 0.002
"jsmn " 382.251 0.900 0.006 0.000
"cJSON " 32.573 0.793 0.068 0.002
@@ -1,12 +0,0 @@
name cycles_per_byte cycles_per_byte_err gb_per_s gb_per_s_err
"simdjson " 1.006 0.033 2.194 0.069
"RapidJSON " 3.265 0.039 0.676 0.008
"RapidJSON (insitu)" 3.100 0.026 0.712 0.006
"sajson (dynamic mem)" 3.344 0.065 0.660 0.013
"sajson" 2.068 0.052 1.067 0.026
"dropbox (json11) " 12.765 0.461 0.173 0.006
"fastjson " 6.849 0.988 0.322 0.041
"gason " 2.292 0.043 0.963 0.018
"ultrajson " 3.819 0.042 0.578 0.006
"jsmn " 12.229 0.078 0.181 0.001
"cJSON " 7.632 0.173 0.289 0.006
@@ -1,12 +0,0 @@
name cycles_per_byte cycles_per_byte_err gb_per_s gb_per_s_err
"simdjson " 1.016 0.089 2.130 0.174
"RapidJSON " 5.022 0.395 0.438 0.032
"RapidJSON (insitu)" 3.581 0.235 0.613 0.038
"sajson (dynamic mem)" 2.688 0.152 0.815 0.043
"sajson" 2.266 0.095 0.966 0.039
"dropbox (json11) " 16.039 0.246 0.137 0.002
"fastjson " 9.582 0.211 0.230 0.005
"gason " 2.957 0.229 0.741 0.052
"ultrajson " 6.861 0.232 0.321 0.011
"jsmn " 4.499 0.063 0.488 0.007
"cJSON " 8.840 0.163 0.249 0.004
-12
View File
@@ -1,12 +0,0 @@
name cycles_per_byte cycles_per_byte_err gb_per_s gb_per_s_err
"simdjson " 0.840 0.030 2.628 0.090
"RapidJSON " 4.617 0.118 0.478 0.012
"RapidJSON (insitu)" 2.835 0.024 0.779 0.006
"sajson (dynamic mem)" 2.356 0.025 0.937 0.010
"sajson" 1.943 0.017 1.136 0.010
"dropbox (json11) " 11.637 0.195 0.190 0.003
"fastjson " 8.641 1.068 0.255 0.028
"gason " 2.588 0.034 0.853 0.011
"ultrajson " 5.439 0.080 0.406 0.006
"jsmn " 14.814 0.094 0.149 0.001
"cJSON " 5.400 0.083 0.409 0.006
@@ -1,12 +0,0 @@
name cycles_per_byte cycles_per_byte_err gb_per_s gb_per_s_err
"simdjson " 1.236 0.083 1.778 0.112
"RapidJSON " 4.238 0.129 0.520 0.015
"RapidJSON (insitu)" 3.932 0.109 0.561 0.015
"sajson (dynamic mem)" 3.807 0.064 0.579 0.010
"sajson" 2.378 0.042 0.926 0.016
"dropbox (json11) " 17.152 0.202 0.129 0.001
"fastjson " 7.785 0.768 0.283 0.025
"gason " 2.822 0.375 0.781 0.092
"ultrajson " 6.106 0.122 0.361 0.007
"jsmn " 9.853 0.081 0.224 0.002
"cJSON " 9.924 0.088 0.222 0.002
-12
View File
@@ -1,12 +0,0 @@
name cycles_per_byte cycles_per_byte_err gb_per_s gb_per_s_err
"simdjson " 2.746 0.066 0.804 0.019
"RapidJSON " 6.105 0.050 0.362 0.003
"RapidJSON (insitu)" 5.867 0.039 0.376 0.003
"sajson (dynamic mem)" 5.258 0.333 0.420 0.025
"sajson" 3.555 0.063 0.621 0.011
"dropbox (json11) " 31.563 4.146 0.070 0.008
"fastjson " 12.260 0.409 0.180 0.006
"gason " 3.604 0.943 0.613 0.127
"ultrajson " 7.062 0.066 0.313 0.003
"jsmn " 15.600 0.488 0.142 0.004
"cJSON " 30.865 2.423 0.072 0.005
-12
View File
@@ -1,12 +0,0 @@
name cycles_per_byte cycles_per_byte_err gb_per_s gb_per_s_err
"simdjson " 2.742 0.109 0.805 0.031
"RapidJSON " 5.868 0.126 0.376 0.008
"RapidJSON (insitu)" 5.901 0.065 0.374 0.004
"sajson (dynamic mem)" 5.475 0.171 0.403 0.012
"sajson" 3.860 0.087 0.572 0.013
"dropbox (json11) " 28.076 0.352 0.079 0.001
"fastjson " 13.015 0.217 0.170 0.003
"gason " 3.717 0.940 0.594 0.120
"ultrajson " 6.710 0.092 0.329 0.004
"jsmn " 47.987 0.317 0.046 0.000
"cJSON " 35.302 0.433 0.063 0.001
@@ -1,12 +0,0 @@
name cycles_per_byte cycles_per_byte_err gb_per_s gb_per_s_err
"simdjson " 1.816 0.036 1.215 0.023
"RapidJSON " 3.584 0.070 0.616 0.012
"RapidJSON (insitu)" 3.589 0.032 0.615 0.005
"sajson (dynamic mem)" 4.337 0.248 0.509 0.027
"sajson" 2.588 0.036 0.853 0.012
"dropbox (json11) " 16.129 0.201 0.137 0.002
"fastjson " 7.866 0.249 0.281 0.009
"gason " 2.618 0.480 0.843 0.131
"ultrajson " 3.762 0.041 0.587 0.006
"jsmn " 23.841 0.197 0.093 0.001
"cJSON " 17.739 0.260 0.124 0.002
-12
View File
@@ -1,12 +0,0 @@
name cycles_per_byte cycles_per_byte_err gb_per_s gb_per_s_err
"simdjson " 2.292 0.125 0.960 0.050
"RapidJSON " 4.812 0.168 0.458 0.015
"RapidJSON (insitu)" 4.846 0.109 0.455 0.010
"sajson (dynamic mem)" 4.566 0.079 0.482 0.008
"sajson" 3.067 0.063 0.718 0.014
"dropbox (json11) " 25.393 0.190 0.087 0.001
"fastjson " 11.008 0.306 0.200 0.005
"gason " 3.189 0.701 0.690 0.124
"ultrajson " 5.492 0.108 0.401 0.008
"jsmn " 4.822 0.147 0.457 0.013
"cJSON " 29.293 0.500 0.075 0.001
-12
View File
@@ -1,12 +0,0 @@
name cycles_per_byte cycles_per_byte_err gb_per_s gb_per_s_err
"simdjson " 1.768 0.068 1.247 0.046
"RapidJSON " 6.532 0.105 0.338 0.005
"RapidJSON (insitu)" 5.397 0.032 0.409 0.002
"sajson (dynamic mem)" 5.900 0.073 0.374 0.005
"sajson" 3.095 0.062 0.713 0.014
"dropbox (json11) " 26.067 1.026 0.085 0.003
"fastjson " 10.810 0.261 0.204 0.005
"gason " 3.417 0.650 0.646 0.103
"ultrajson " 8.801 0.109 0.251 0.003
"jsmn " 79.479 0.333 0.028 0.000
"cJSON " 13.740 0.289 0.161 0.003
-12
View File
@@ -1,12 +0,0 @@
name cycles_per_byte cycles_per_byte_err gb_per_s gb_per_s_err
"simdjson " 1.132 0.066 1.948 0.108
"RapidJSON " 5.186 0.085 0.426 0.007
"RapidJSON (insitu)" 3.759 0.049 0.587 0.008
"sajson (dynamic mem)" 3.639 0.053 0.606 0.009
"sajson" 2.428 0.056 0.909 0.020
"dropbox (json11) " 17.214 0.473 0.128 0.003
"fastjson " 10.042 0.646 0.220 0.013
"gason " 3.227 0.071 0.684 0.015
"ultrajson " 6.217 0.066 0.355 0.004
"jsmn " 9.050 0.091 0.244 0.002
"cJSON " 7.565 0.088 0.292 0.003
@@ -1,12 +0,0 @@
name cycles_per_byte cycles_per_byte_err gb_per_s gb_per_s_err
"simdjson " 2.290 0.070 0.963 0.029
"RapidJSON " 6.236 0.110 0.354 0.006
"RapidJSON (insitu)" 4.400 0.066 0.501 0.007
"sajson (dynamic mem)" 4.176 0.046 0.528 0.006
"sajson" 2.886 0.055 0.764 0.014
"dropbox (json11) " 22.869 0.275 0.097 0.001
"fastjson " 10.673 0.288 0.207 0.005
"gason " 3.841 0.093 0.574 0.014
"ultrajson " 7.277 0.121 0.303 0.005
"jsmn " 9.810 0.100 0.225 0.002
"cJSON " 9.588 0.086 0.230 0.002
@@ -1,12 +0,0 @@
name cycles_per_byte cycles_per_byte_err gb_per_s gb_per_s_err
"simdjson " 1.430 0.071 1.542 0.073
"RapidJSON " 6.277 0.147 0.352 0.008
"RapidJSON (insitu)" 4.859 0.036 0.454 0.003
"sajson (dynamic mem)" 5.213 0.060 0.423 0.005
"sajson" 2.881 0.063 0.766 0.016
"dropbox (json11) " 22.860 0.369 0.097 0.002
"fastjson " 12.205 0.821 0.181 0.011
"gason " 3.683 0.156 0.599 0.024
"ultrajson " 8.590 0.140 0.257 0.004
"jsmn " 38.342 0.295 0.058 0.000
"cJSON " 10.162 0.127 0.217 0.003
@@ -1,4 +0,0 @@
name cycles_per_byte cycles_per_byte_err gb_per_s gb_per_s_err
"simdjson " 1.126 0.080 1.943 0.128
"RapidJSON (insitu)" 3.873 0.202 0.568 0.028
"sajson" 1.994 0.045 1.102 0.024
-36
View File
@@ -1,36 +0,0 @@
set term pdfcairo fontscale 0.65 noenhanced
! python pasteandrotate.py > gbps.txt
set output "gbps.pdf"
set boxwidth 0.8
set style fill solid
set ylabel "parsing speed (GB/s)"
#set style line 80 lt rgb "#000000"
# Line style for grid
set style line 81 lt 0 # dashed
set style line 81 lt rgb "#808080" # grey
set grid back linestyle 81
set border 3 back linestyle 80 # Remove border on top and right. These
# borders are useless and make it harder
# to see plotted lines near the border.
# Also, put it in grey; no need for so much emphasis on a border.
set xtics nomirror
set ytics nomirror
set yrange [0:]
set format y "%0.1f";
set style data histogram
set style histogram cluster gap 1
#set style line 1 lt rgb "#A0A0A0" lw 1 pt 1 ps 1
#set key autotitle columnhead
color1 = "#ff0c18";color2 = "#000000"; color3 = "#0c24ff";
set xtic rotate by 300 scale 1
plot "gbps.txt" using 2:xtic(1) title "simdjson" linecolor rgb color1, \
"" using 3 title "RapidJSON" linecolor rgb color2, \
"" using 4 title "sajson" linecolor rgb color3
-4
View File
@@ -1,4 +0,0 @@
name cycles_per_byte cycles_per_byte_err gb_per_s gb_per_s_err
"simdjson " 2.448 0.049 0.902 0.018
"RapidJSON (insitu)" 5.074 0.038 0.435 0.003
"sajson" 3.264 0.198 0.676 0.039
-4
View File
@@ -1,4 +0,0 @@
name cycles_per_byte cycles_per_byte_err gb_per_s gb_per_s_err
"simdjson " 1.005 0.030 2.195 0.065
"RapidJSON (insitu)" 3.055 0.087 0.723 0.020
"sajson" 1.999 0.036 1.104 0.019
Binary file not shown.
@@ -1,4 +0,0 @@
name cycles_per_byte cycles_per_byte_err gb_per_s gb_per_s_err
"simdjson " 1.031 0.097 2.100 0.177
"RapidJSON (insitu)" 3.551 0.340 0.618 0.054
"sajson" 2.218 0.175 0.987 0.072
-4
View File
@@ -1,4 +0,0 @@
name cycles_per_byte cycles_per_byte_err gb_per_s gb_per_s_err
"simdjson " 0.842 0.030 2.621 0.090
"RapidJSON (insitu)" 2.840 0.027 0.777 0.007
"sajson" 1.908 0.016 1.157 0.010
-4
View File
@@ -1,4 +0,0 @@
name cycles_per_byte cycles_per_byte_err gb_per_s gb_per_s_err
"simdjson " 1.236 0.092 1.777 0.122
"RapidJSON (insitu)" 3.853 0.133 0.572 0.019
"sajson" 2.345 0.054 0.939 0.021
-4
View File
@@ -1,4 +0,0 @@
name cycles_per_byte cycles_per_byte_err gb_per_s gb_per_s_err
"simdjson " 2.788 0.069 0.792 0.019
"RapidJSON (insitu)" 5.928 0.062 0.372 0.004
"sajson" 3.602 0.059 0.613 0.010
-4
View File
@@ -1,4 +0,0 @@
name cycles_per_byte cycles_per_byte_err gb_per_s gb_per_s_err
"simdjson " 2.777 0.122 0.795 0.033
"RapidJSON (insitu)" 5.898 0.116 0.374 0.007
"sajson" 3.774 0.101 0.585 0.015
-4
View File
@@ -1,4 +0,0 @@
name cycles_per_byte cycles_per_byte_err gb_per_s gb_per_s_err
"simdjson " 1.817 0.046 1.214 0.030
"RapidJSON (insitu)" 3.576 0.057 0.617 0.010
"sajson" 2.652 0.062 0.832 0.019
-4
View File
@@ -1,4 +0,0 @@
name cycles_per_byte cycles_per_byte_err gb_per_s gb_per_s_err
"simdjson " 2.453 0.157 0.897 0.054
"RapidJSON (insitu)" 4.856 0.185 0.454 0.017
"sajson" 3.084 0.074 0.714 0.017
-28
View File
@@ -1,28 +0,0 @@
import os
import csv
try: import pandas as pd
except ImportError:
import pip
pip.main(['install', '--user', 'pandas'])
import pandas as pd
def getdata(filename):
df = pd.read_csv(filename, delim_whitespace=True)
return (df["gb_per_s"].tolist())
ourdir=os.path.dirname(os.path.realpath(__file__))
answer = []
for file in os.listdir(ourdir):
if file.startswith("all"):
continue
if file.endswith(".table"):
fullpath = os.path.join(ourdir, file)
answer.append([file[:-11]]+getdata(fullpath))
print("#simdjson RapidJSON sajson")
answer.sort()
for l in answer:
print("\t".join(map(str,l)))
-4
View File
@@ -1,4 +0,0 @@
name cycles_per_byte cycles_per_byte_err gb_per_s gb_per_s_err
"simdjson " 1.782 0.084 1.237 0.055
"RapidJSON (insitu)" 5.366 0.090 0.411 0.007
"sajson" 2.978 0.060 0.741 0.015
-4
View File
@@ -1,4 +0,0 @@
name cycles_per_byte cycles_per_byte_err gb_per_s gb_per_s_err
"simdjson " 1.146 0.065 1.924 0.104
"RapidJSON (insitu)" 3.771 0.061 0.585 0.009
"sajson" 2.392 0.057 0.922 0.021
@@ -1,4 +0,0 @@
name cycles_per_byte cycles_per_byte_err gb_per_s gb_per_s_err
"simdjson " 2.294 0.069 0.962 0.028
"RapidJSON (insitu)" 4.394 0.064 0.502 0.007
"sajson" 2.827 0.056 0.780 0.015
@@ -1,4 +0,0 @@
name cycles_per_byte cycles_per_byte_err gb_per_s gb_per_s_err
"simdjson " 1.437 0.078 1.534 0.079
"RapidJSON (insitu)" 4.855 0.104 0.455 0.010
"sajson" 2.772 0.059 0.796 0.017
@@ -1,12 +0,0 @@
name cycles_per_byte cycles_per_byte_err gb_per_s gb_per_s_err
"simdjson " 1.523 0.034 2.222 0.049
"RapidJSON " 6.546 0.096 0.520 0.008
"RapidJSON (insitu)" 4.917 0.048 0.692 0.007
"sajson (dynamic mem)" 4.116 0.061 0.826 0.012
"sajson" 2.829 0.071 1.200 0.029
"dropbox (json11) " 20.658 0.097 0.165 0.001
"fastjson " 12.749 0.395 0.267 0.008
"gason " 3.639 0.363 0.934 0.084
"ultrajson " 8.329 0.223 0.409 0.011
"jsmn " 50.809 0.150 0.067 0.000
"cJSON " 11.417 0.066 0.298 0.002
-12
View File
@@ -1,12 +0,0 @@
name cycles_per_byte cycles_per_byte_err gb_per_s gb_per_s_err
"simdjson " 3.281 0.030 1.038 0.009
"RapidJSON " 6.705 0.017 0.508 0.001
"RapidJSON (insitu)" 6.771 0.011 0.503 0.001
"sajson (dynamic mem)" 5.812 0.100 0.586 0.010
"sajson" 4.250 0.027 0.802 0.005
"dropbox (json11) " 48.060 0.635 0.071 0.001
"fastjson " 16.785 0.175 0.203 0.002
"gason " 3.753 0.748 0.908 0.151
"ultrajson " 7.258 0.019 0.469 0.001
"jsmn " 535.513 0.171 0.006 0.000
"cJSON " 43.412 0.607 0.079 0.001
@@ -1,12 +0,0 @@
name cycles_per_byte cycles_per_byte_err gb_per_s gb_per_s_err
"simdjson " 1.349 0.015 2.524 0.029
"RapidJSON " 4.127 0.016 0.826 0.003
"RapidJSON (insitu)" 3.891 0.013 0.876 0.003
"sajson (dynamic mem)" 3.992 0.017 0.853 0.004
"sajson" 2.968 0.017 1.148 0.007
"dropbox (json11) " 14.293 0.085 0.238 0.001
"fastjson " 9.145 0.977 0.373 0.036
"gason " 2.925 0.069 1.165 0.027
"ultrajson " 4.778 0.024 0.713 0.004
"jsmn " 16.163 0.020 0.211 0.000
"cJSON " 8.935 0.028 0.381 0.001
@@ -1,12 +0,0 @@
name cycles_per_byte cycles_per_byte_err gb_per_s gb_per_s_err
"simdjson " 1.388 0.059 2.422 0.099
"RapidJSON " 6.727 0.114 0.505 0.008
"RapidJSON (insitu)" 4.529 0.112 0.749 0.018
"sajson (dynamic mem)" 3.526 0.128 0.961 0.034
"sajson" 3.184 0.099 1.064 0.032
"dropbox (json11) " 23.065 0.176 0.148 0.001
"fastjson " 13.145 0.139 0.259 0.003
"gason " 3.634 0.281 0.933 0.067
"ultrajson " 8.912 0.248 0.382 0.010
"jsmn " 5.908 0.076 0.575 0.007
"cJSON " 12.011 0.110 0.283 0.003
@@ -1,12 +0,0 @@
name cycles_per_byte cycles_per_byte_err gb_per_s gb_per_s_err
"simdjson " 1.136 0.022 2.998 0.056
"RapidJSON " 6.166 0.018 0.553 0.002
"RapidJSON (insitu)" 3.409 0.005 1.000 0.002
"sajson (dynamic mem)" 3.174 0.009 1.074 0.003
"sajson" 2.706 0.010 1.259 0.004
"dropbox (json11) " 15.026 0.075 0.227 0.001
"fastjson " 10.953 1.283 0.311 0.033
"gason " 3.121 0.043 1.092 0.015
"ultrajson " 7.697 0.023 0.443 0.001
"jsmn " 20.240 0.040 0.168 0.000
"cJSON " 6.304 0.025 0.541 0.002
@@ -1,12 +0,0 @@
name cycles_per_byte cycles_per_byte_err gb_per_s gb_per_s_err
"simdjson " 1.667 0.041 2.037 0.049
"RapidJSON " 5.548 0.041 0.614 0.004
"RapidJSON (insitu)" 4.909 0.042 0.693 0.006
"sajson (dynamic mem)" 5.102 0.060 0.667 0.008
"sajson" 3.420 0.026 0.995 0.008
"dropbox (json11) " 24.124 0.110 0.141 0.001
"fastjson " 10.926 0.143 0.312 0.004
"gason " 3.445 0.517 0.987 0.129
"ultrajson " 7.649 0.109 0.445 0.006
"jsmn " 13.093 0.050 0.260 0.001
"cJSON " 13.135 0.067 0.259 0.001

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