mirror of
https://github.com/simdjson/simdjson
synced 2026-06-08 17:27:07 +00:00
Compare commits
49 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3240d55bcc | |||
| 66fd28fc00 | |||
| 5f638951c6 | |||
| 3e94eea939 | |||
| 0e8311f812 | |||
| 3620e9d151 | |||
| d017cd7ca4 | |||
| d9d1ff5856 | |||
| eb8f2bce14 | |||
| 2a4ff73468 | |||
| 77fc2b8447 | |||
| ba8b66a633 | |||
| 66eec5feaf | |||
| 3964f3e5d2 | |||
| ee8515122d | |||
| 5d35e7ca1f | |||
| deefc88b9c | |||
| c80dda7c58 | |||
| d2954ef68b | |||
| ac719827ff | |||
| 6ea77392a7 | |||
| e2f879751c | |||
| bf7834179c | |||
| 69ab8848bf | |||
| 7288323e36 | |||
| 3f381cf3ee | |||
| 52406402ed | |||
| 799d8e3fc9 | |||
| 6c647d9f3b | |||
| 8519e24f12 | |||
| fffb62743c | |||
| c888075f8d | |||
| 9b8aac5f22 | |||
| 40b414d184 | |||
| c85e8a7db1 | |||
| 83f390f8e7 | |||
| 82563dcf70 | |||
| 646bd3f2c3 | |||
| 4b0a908bb2 | |||
| 4c98e51c53 | |||
| 58e3d5d9cc | |||
| a22a92dc72 | |||
| c4654553dd | |||
| 5997f82c37 | |||
| bee410b315 | |||
| b115e3ef61 | |||
| 669229df5d | |||
| b6a2286e62 | |||
| 3a97c57773 |
@@ -10,7 +10,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: dependencies/.cache
|
||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||
|
||||
@@ -34,18 +34,18 @@ jobs:
|
||||
sudo apt update
|
||||
sudo apt-get install --quiet ninja-build valgrind zip unzip lsb-release wget software-properties-common gnupg
|
||||
wget https://apt.llvm.org/llvm.sh
|
||||
sudo apt-get purge --auto-remove llvm python3-lldb-14 llvm-14
|
||||
sudo apt-get purge --auto-remove llvm python3-lldb-15 llvm-15
|
||||
chmod +x llvm.sh
|
||||
sudo ./llvm.sh $CLANGVERSION
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: dependencies/.cache
|
||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
id: cache-corpus
|
||||
with:
|
||||
path: out/
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
name: LoongArch64-CI
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
loongarch64:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform:
|
||||
- { toolchain-version: 2023.08.08 }
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install build requirements
|
||||
run: |
|
||||
sudo apt-get update -y
|
||||
sudo apt-get install -y --no-install-recommends cmake
|
||||
|
||||
- uses: actions/cache/restore@v4
|
||||
id: restore-cache
|
||||
with:
|
||||
path: /opt/cross-tools
|
||||
key: loongarch64-${{ matrix.platform.toolchain-version }}
|
||||
|
||||
- name: Download LoongArch64 gcc+glibc toolchain
|
||||
if: ${{ !steps.restore-cache.outputs.cache-hit }}
|
||||
run: |
|
||||
url="https://github.com/loongson/build-tools/releases/download/${{ matrix.platform.toolchain-version }}/x86_64-cross-tools-loongarch64-gcc-libc.tar.xz"
|
||||
|
||||
wget "$url" -O /tmp/toolchain.tar.xz
|
||||
|
||||
mkdir -p /opt
|
||||
tar -C /opt -x -f /tmp/toolchain.tar.xz
|
||||
|
||||
- uses: actions/cache/save@v3
|
||||
if: ${{ !steps.restore-cache.outputs.cache-hit }}
|
||||
with:
|
||||
path: /opt/cross-tools
|
||||
key: loongarch64-${{ matrix.platform.toolchain-version }}
|
||||
|
||||
- name: setup Loongarch64 build environment
|
||||
run: |
|
||||
echo "/opt/cross-tools/bin" >> $GITHUB_PATH
|
||||
echo "CC=loongarch64-unknown-linux-gnu-gcc" >> $GITHUB_ENV
|
||||
echo "CXX=loongarch64-unknown-linux-gnu-g++" >> $GITHUB_ENV
|
||||
- name: configure
|
||||
run: cmake -B build -DCMAKE_SYSTEM_PROCESSOR=loongarch64 -DARCH=lonngarch64 -DCMAKE_SYSTEM_NAME=Linux -DCMAKE_C_COMPILER=loongarch64-unknown-linux-gnu-gcc -DCMAKE_CXX_COMPILER=loongarch64-unknown-linux-gnu-g++
|
||||
- name: build
|
||||
run: cmake --build build
|
||||
@@ -10,7 +10,7 @@ jobs:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: dependencies/.cache
|
||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||
|
||||
@@ -20,12 +20,15 @@ jobs:
|
||||
- msystem: "MINGW64"
|
||||
install: mingw-w64-x86_64-libxml2 mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-clang
|
||||
type: Debug
|
||||
- msystem: "MINGW64"
|
||||
install: mingw-w64-x86_64-libxml2 mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-clang
|
||||
type: RelWithDebInfo
|
||||
env:
|
||||
CMAKE_GENERATOR: Ninja
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: dependencies/.cache
|
||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||
|
||||
@@ -22,12 +22,15 @@ jobs:
|
||||
- msystem: "MINGW64"
|
||||
install: mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-gcc
|
||||
type: Debug
|
||||
- msystem: "MINGW64"
|
||||
install: mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-gcc
|
||||
type: RelWithDebInfo
|
||||
env:
|
||||
CMAKE_GENERATOR: Ninja
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: dependencies/.cache
|
||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
name: short fuzz on the power arch
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
armv7_job:
|
||||
if: >-
|
||||
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
|
||||
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
||||
# The host should always be Linux
|
||||
runs-on: ubuntu-20.04
|
||||
name: Build on ubuntu-20.04 ppc64le
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: uraimo/run-on-arch-action@v2.0.5
|
||||
name: Run commands
|
||||
id: runcmd
|
||||
env:
|
||||
DEBIAN_FRONTEND: noninteractive
|
||||
with:
|
||||
arch: ppc64le
|
||||
distro: buster
|
||||
|
||||
# Not required, but speeds up builds by storing container images in
|
||||
# a GitHub package registry.
|
||||
githubToken: ${{ github.token }}
|
||||
|
||||
run: |
|
||||
export CLANGSUFFIX="-7"
|
||||
apt-get -qq update
|
||||
apt-get install -q -y clang-7 libfuzzer-7-dev git wget zip ninja-build gnupg software-properties-common
|
||||
wget -q -O - "https://raw.githubusercontent.com/simdjson/debian-ppa/master/key.gpg" | apt-key add -
|
||||
apt-add-repository "deb https://raw.githubusercontent.com/simdjson/debian-ppa/master simdjson main"
|
||||
apt-get -qq update
|
||||
apt-get purge cmake cmake-data
|
||||
apt-get -t simdjson -y install cmake
|
||||
mkdir -p build ; cd build
|
||||
cmake .. -GNinja \
|
||||
-DCMAKE_CXX_COMPILER=clang++$CLANGSUFFIX \
|
||||
-DCMAKE_C_COMPILER=clang$CLANGSUFFIX \
|
||||
-DBUILD_SHARED_LIBS=OFF \
|
||||
-DSIMDJSON_DEVELOPER_MODE=ON \
|
||||
-DSIMDJSON_ENABLE_FUZZING=On \
|
||||
-DSIMDJSON_COMPETITION=OFF \
|
||||
-DSIMDJSON_GOOGLE_BENCHMARKS=OFF \
|
||||
-DSIMDJSON_DISABLE_DEPRECATED_API=On \
|
||||
-DSIMDJSON_FUZZ_LDFLAGS=-lFuzzer \
|
||||
-DCMAKE_CXX_FLAGS="-fsanitize=fuzzer-no-link -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=" \
|
||||
-DCMAKE_C_FLAGS="-fsanitize=fuzzer-no-link" \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DSIMDJSON_FUZZ_LINKMAIN=Off
|
||||
cd ..
|
||||
builddir=build
|
||||
cmake --build $builddir
|
||||
wget -O corpus.tar.gz https://readonly:readonly@www.pauldreik.se/fuzzdata/index.php?project=simdjson
|
||||
tar xf corpus.tar.gz
|
||||
fuzzernames=$(cmake --build $builddir --target print_all_fuzzernames |tail -n1)
|
||||
for fuzzer in $fuzzernames ; do
|
||||
exe=$builddir/fuzz/$fuzzer
|
||||
shortname=$(echo $fuzzer |cut -f2- -d_)
|
||||
echo found fuzzer $shortname with executable $exe
|
||||
mkdir -p out/$shortname
|
||||
others=$(find out -type d -not -name $shortname -not -name out -not -name cmin)
|
||||
$exe -max_total_time=20 -max_len=4000 out/$shortname $others
|
||||
echo "*************************************************************************"
|
||||
done
|
||||
echo "all is good, no errors found in any of these fuzzers: $fuzzernames"
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: dependencies/.cache
|
||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||
|
||||
@@ -13,7 +13,7 @@ jobs:
|
||||
CC: gcc-8
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: dependencies/.cache
|
||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||
|
||||
@@ -10,7 +10,7 @@ jobs:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: dependencies/.cache
|
||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||
|
||||
@@ -10,7 +10,7 @@ jobs:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: dependencies/.cache
|
||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||
|
||||
@@ -10,7 +10,7 @@ jobs:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: dependencies/.cache
|
||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||
@@ -28,7 +28,7 @@ jobs:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: dependencies/.cache
|
||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||
|
||||
@@ -10,7 +10,7 @@ jobs:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: dependencies/.cache
|
||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||
|
||||
@@ -10,7 +10,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: dependencies/.cache
|
||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||
|
||||
@@ -10,7 +10,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: dependencies/.cache
|
||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||
|
||||
@@ -10,7 +10,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: dependencies/.cache
|
||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||
|
||||
@@ -10,7 +10,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: dependencies/.cache
|
||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
name: Ubuntu 22.04 CI (GCC 13)
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
ubuntu-build:
|
||||
if: >-
|
||||
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
|
||||
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: dependencies/.cache
|
||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||
- name: Use cmake
|
||||
run: |
|
||||
mkdir build &&
|
||||
cd build &&
|
||||
CXX=g++-13 cmake -DSIMDJSON_DEVELOPER_MODE=ON .. &&
|
||||
cmake --build . &&
|
||||
ctest --output-on-failure -LE explicitonly -j
|
||||
@@ -9,7 +9,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: dependencies/.cache
|
||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Ubuntu 22.04 CI (GCC 11) with Thread Sanitizer
|
||||
name: Ubuntu 22.04 CI (GCC 12) with Thread Sanitizer
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
@@ -10,7 +10,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: dependencies/.cache
|
||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||
@@ -18,7 +18,7 @@ jobs:
|
||||
run: |
|
||||
mkdir build &&
|
||||
cd build &&
|
||||
cmake -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_SANITIZE_THREADS=ON .. &&
|
||||
CXX=g++-12 cmake -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_SANITIZE_THREADS=ON .. &&
|
||||
cmake --build . --target document_stream_tests --target ondemand_document_stream_tests --target parse_many_test &&
|
||||
ctest --output-on-failure -R parse_many_test &&
|
||||
ctest --output-on-failure -R document_stream_tests
|
||||
@@ -10,7 +10,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: dependencies/.cache
|
||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
name: Ubuntu 24.04 CI
|
||||
|
||||
on: [push, pull_request]
|
||||
jobs:
|
||||
ubuntu-build:
|
||||
if: >-
|
||||
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
|
||||
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
||||
runs-on: ubuntu-24.04
|
||||
strategy:
|
||||
matrix:
|
||||
shared: [ON, OFF]
|
||||
cxx: [g++-13, clang++-16]
|
||||
sanitizer: [ON, OFF]
|
||||
build_type: [RelWithDebInfo, Debug, Release]
|
||||
steps:
|
||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
- name: Prepare
|
||||
run: cmake -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_SANITIZE=${{matrix.sanitizer}} -DBUILD_SHARED_LIBS=${{matrix.shared}} -B build
|
||||
env:
|
||||
CXX: ${{matrix.cxx}}
|
||||
- name: Build
|
||||
run: cmake --build build -j=2
|
||||
- name: Test
|
||||
run: ctest --output-on-failure --test-dir build
|
||||
@@ -12,10 +12,11 @@ jobs:
|
||||
include:
|
||||
- {arch: ARM}
|
||||
- {arch: ARM64}
|
||||
- {arch: ARM64EC}
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Use cmake
|
||||
run: |
|
||||
cmake -A ${{ matrix.arch }} -DCMAKE_CROSSCOMPILING=1 -DSIMDJSON_DEVELOPER_MODE=ON -D SIMDJSON_GOOGLE_BENCHMARKS=OFF -DSIMDJSON_EXCEPTIONS=OFF -B build &&
|
||||
cmake -A ${{ matrix.arch }} -DCMAKE_SYSTEM_VERSION="10.0.22621.0" -DCMAKE_CROSSCOMPILING=1 -DSIMDJSON_DEVELOPER_MODE=ON -D SIMDJSON_GOOGLE_BENCHMARKS=OFF -DSIMDJSON_EXCEPTIONS=OFF -B build &&
|
||||
cmake --build build --verbose
|
||||
@@ -13,10 +13,12 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- {gen: Visual Studio 17 2022, arch: Win32, shared: ON}
|
||||
- {gen: Visual Studio 17 2022, arch: Win32, shared: OFF}
|
||||
- {gen: Visual Studio 17 2022, arch: x64, shared: ON}
|
||||
- {gen: Visual Studio 17 2022, arch: x64, shared: OFF}
|
||||
- {gen: Visual Studio 17 2022, arch: Win32, shared: ON, build_type: Release}
|
||||
- {gen: Visual Studio 17 2022, arch: Win32, shared: OFF, build_type: Release}
|
||||
- {gen: Visual Studio 17 2022, arch: x64, shared: ON, build_type: Release}
|
||||
- {gen: Visual Studio 17 2022, arch: x64, shared: OFF, build_type: Debug}
|
||||
- {gen: Visual Studio 17 2022, arch: x64, shared: OFF, build_type: Release}
|
||||
- {gen: Visual Studio 17 2022, arch: x64, shared: OFF, build_type: RelWithDebInfo}
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
@@ -24,21 +26,15 @@ jobs:
|
||||
run: |
|
||||
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_COMPETITION=OFF -DBUILD_SHARED_LIBS=${{matrix.shared}} -B build
|
||||
- name: Build Debug
|
||||
run: cmake --build build --config Debug --verbose
|
||||
- name: Build Release
|
||||
run: cmake --build build --config Release --verbose
|
||||
- name: Run Release tests
|
||||
run: cmake --build build --config ${{build_type}} --verbose
|
||||
- name: Run tests
|
||||
run: |
|
||||
cd build
|
||||
ctest -C Release -LE explicitonly --output-on-failure
|
||||
- name: Run Debug tests
|
||||
run: |
|
||||
cd build
|
||||
ctest -C Debug -LE explicitonly --output-on-failure
|
||||
ctest -C ${{build_type}} -LE explicitonly --output-on-failure
|
||||
- name: Install
|
||||
run: |
|
||||
cmake --install build --config Release
|
||||
cmake --install build --config ${{build_type}}
|
||||
- name: Test Installation
|
||||
run: |
|
||||
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -B build_install_test tests/installation_tests/find
|
||||
cmake --build build_install_test --config Release
|
||||
cmake --build build_install_test --config ${{build_type}}
|
||||
@@ -13,29 +13,25 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- {gen: Visual Studio 17 2022, arch: x64}
|
||||
- {gen: Visual Studio 17 2022, arch: x64, build_type: Debug}
|
||||
- {gen: Visual Studio 17 2022, arch: x64, build_type: Release}
|
||||
- {gen: Visual Studio 17 2022, arch: x64, build_type: RelWithDebInfo}
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Configure
|
||||
run: |
|
||||
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -T ClangCL -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_COMPETITION=OFF -B build
|
||||
- name: Build Debug
|
||||
run: cmake --build build --config Debug --verbose
|
||||
- name: Build Release
|
||||
run: cmake --build build --config Release --verbose
|
||||
- name: Run Release tests
|
||||
- name: Build
|
||||
run: cmake --build build --config ${{matrix.build_type}} --verbose
|
||||
- name: Run tests
|
||||
run: |
|
||||
cd build
|
||||
ctest -C Release -LE explicitonly --output-on-failure
|
||||
- name: Run Debug tests
|
||||
run: |
|
||||
cd build
|
||||
ctest -C Debug -LE explicitonly --output-on-failure
|
||||
ctest -C ${{matrix.build_type}} -LE explicitonly --output-on-failure
|
||||
- name: Install
|
||||
run: |
|
||||
cmake --install build --config Release
|
||||
cmake --install build --config ${{matrix.build_type}}
|
||||
- name: Test Installation
|
||||
run: |
|
||||
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -B build_install_test tests/installation_tests/find
|
||||
cmake --build build_install_test --config Release
|
||||
cmake --build build_install_test --config ${{matrix.build_type}}
|
||||
@@ -8,7 +8,7 @@ jobs:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: dependencies/.cache
|
||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||
|
||||
+21
-3
@@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.14)
|
||||
project(
|
||||
simdjson
|
||||
# The version number is modified by tools/release.py
|
||||
VERSION 3.8.0
|
||||
VERSION 3.9.4
|
||||
DESCRIPTION "Parsing gigabytes of JSON per second"
|
||||
HOMEPAGE_URL "https://simdjson.org/"
|
||||
LANGUAGES CXX C
|
||||
@@ -20,8 +20,8 @@ string(
|
||||
# ---- Options, variables ----
|
||||
|
||||
# These version numbers are modified by tools/release.py
|
||||
set(SIMDJSON_LIB_VERSION "21.0.0" CACHE STRING "simdjson library version")
|
||||
set(SIMDJSON_LIB_SOVERSION "21" CACHE STRING "simdjson library soversion")
|
||||
set(SIMDJSON_LIB_VERSION "22.0.0" CACHE STRING "simdjson library version")
|
||||
set(SIMDJSON_LIB_SOVERSION "22" CACHE STRING "simdjson library soversion")
|
||||
|
||||
option(SIMDJSON_BUILD_STATIC_LIB "Build simdjson_static library along with simdjson" OFF)
|
||||
|
||||
@@ -106,6 +106,24 @@ if(
|
||||
)
|
||||
endif()
|
||||
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(loongarch64)$")
|
||||
option(SIMDJSON_PREFER_LSX "Prefer LoongArch SX" ON)
|
||||
include(CheckCXXCompilerFlag)
|
||||
check_cxx_compiler_flag(-mlasx COMPILER_SUPPORTS_LASX)
|
||||
check_cxx_compiler_flag(-mlsx COMPILER_SUPPORTS_LSX)
|
||||
if(COMPILER_SUPPORTS_LASX AND NOT SIMDJSON_PREFER_LSX)
|
||||
simdjson_add_props(
|
||||
target_compile_options PRIVATE
|
||||
-mlasx
|
||||
)
|
||||
elseif(COMPILER_SUPPORTS_LSX)
|
||||
simdjson_add_props(
|
||||
target_compile_options PRIVATE
|
||||
-mlsx
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# GCC and Clang have horrendous Debug builds when using SIMD.
|
||||
# A common fix is to use '-Og' instead.
|
||||
# bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54412
|
||||
|
||||
@@ -38,7 +38,7 @@ PROJECT_NAME = simdjson
|
||||
# could be handy for archiving the generated documentation or if some version
|
||||
# control system is used.
|
||||
|
||||
PROJECT_NUMBER = "3.8.0"
|
||||
PROJECT_NUMBER = "3.9.4"
|
||||
|
||||
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
||||
# for a project that appears at the top of each page and should give viewer a
|
||||
|
||||
@@ -59,6 +59,7 @@ Real-world usage
|
||||
- [vast](https://github.com/tenzir/vast)
|
||||
- [ada-url](https://github.com/ada-url/ada)
|
||||
- [fastgron](https://github.com/adamritter/fastgron)
|
||||
- [WasmEdge](https://wasmedge.org)
|
||||
|
||||
If you are planning to use simdjson in a product, please work from one of our releases.
|
||||
|
||||
@@ -180,7 +181,7 @@ CPU's multiple execution cores.
|
||||
|
||||
Our default front-end is called On Demand, and we wrote a paper about it:
|
||||
|
||||
- John Keiser, Daniel Lemire, [On-Demand JSON: A Better Way to Parse Documents?](http://arxiv.org/abs/2312.17149), Software: Practice and Experience (to appear)
|
||||
- John Keiser, Daniel Lemire, [On-Demand JSON: A Better Way to Parse Documents?](http://arxiv.org/abs/2312.17149), Software: Practice and Experience 54 (6), 2024.
|
||||
|
||||
Some people [enjoy reading the first (2019) simdjson paper](https://arxiv.org/abs/1902.08318): A description of the design
|
||||
and implementation of simdjson is in our research article:
|
||||
|
||||
+164
-81
@@ -9,36 +9,37 @@ An overview of what you need to know to use simdjson, with examples.
|
||||
- [Using simdjson with package managers](#using-simdjson-with-package-managers)
|
||||
- [Using simdjson as a CMake dependency](#using-simdjson-as-a-cmake-dependency)
|
||||
- [Versions](#versions)
|
||||
- [The Basics: Loading and Parsing JSON Documents](#the-basics-loading-and-parsing-json-documents)
|
||||
- [Documents are Iterators](#documents-are-iterators)
|
||||
- [Parser, Document and JSON Scope](#parser-document-and-json-scope)
|
||||
- [The basics: loading and parsing JSON documents](#the-basics-loading-and-parsing-json-documents)
|
||||
- [Documents are iterators](#documents-are-iterators)
|
||||
- [Parser, document and JSON scope](#parser-document-and-json-scope)
|
||||
- [string_view](#string_view)
|
||||
- [Using the Parsed JSON](#using-the-parsed-json)
|
||||
- [Using the Parsed JSON: Additional examples](#using-the-parsed-json-additional-examples)
|
||||
- [Avoiding pitfalls: enable development checks](#avoiding-pitfalls-enable-development-checks)
|
||||
- [Using the parsed JSON](#using-the-parsed-json)
|
||||
- [Using the parsed JSON: additional examples](#using-the-parsed-json-additional-examples)
|
||||
- [Adding support for custom types](#adding-support-for-custom-types)
|
||||
- [Minifying JSON strings without parsing](#minifying-json-strings-without-parsing)
|
||||
- [UTF-8 validation (alone)](#utf-8-validation-alone)
|
||||
- [JSON Pointer](#json-pointer)
|
||||
- [JSONPath](#json-path)
|
||||
- [Error Handling](#error-handling)
|
||||
- [Error Handling Examples without Exceptions](#error-handling-examples-without-exceptions)
|
||||
- [Disabling Exceptions](#disabling-exceptions)
|
||||
- [JSONPath](#jsonpath)
|
||||
- [Error handling](#error-handling)
|
||||
- [Error handling examples without exceptions](#error-handling-examples-without-exceptions)
|
||||
- [Disabling exceptions](#disabling-exceptions)
|
||||
- [Exceptions](#exceptions)
|
||||
- [Current location in document](#current-location-in-document)
|
||||
- [Checking for trailing content](#checking-for-trailing-content)
|
||||
- [Rewinding](#rewinding)
|
||||
- [Newline-Delimited JSON (ndjson) and JSON lines](#newline-delimited-json-ndjson-and-json-lines)
|
||||
- [Parsing Numbers Inside Strings](#parsing-numbers-inside-strings)
|
||||
- [Parsing numbers inside strings](#parsing-numbers-inside-strings)
|
||||
- [Dynamic Number Types](#dynamic-number-types)
|
||||
- [Raw Strings From Keys](#raw-strings-from-keys)
|
||||
- [General Direct Access to the Raw JSON String](#general-direct-access-to-the-raw-json-string)
|
||||
- [Storing Directly into an Existing String Instance](#storing-directly-into-an-existing-string-instance)
|
||||
- [Thread Safety](#thread-safety)
|
||||
- [Standard Compliance](#standard-compliance)
|
||||
- [Backwards Compatibility](#backwards-compatibility)
|
||||
- [Raw strings from keys](#raw-strings-from-keys)
|
||||
- [General direct access to the raw JSON string](#general-direct-access-to-the-raw-json-string)
|
||||
- [Storing directly into an existing string instance](#storing-directly-into-an-existing-string-instance)
|
||||
- [Thread safety](#thread-safety)
|
||||
- [Standard compliance](#standard-compliance)
|
||||
- [Backwards compatibility](#backwards-compatibility)
|
||||
- [Examples](#examples)
|
||||
- [Performance Tips](#performance-tips)
|
||||
- [Further Reading](#further-reading)
|
||||
- [Performance tips](#performance-tips)
|
||||
- [Further reading](#further-reading)
|
||||
|
||||
|
||||
Requirements
|
||||
@@ -50,7 +51,7 @@ Requirements
|
||||
|
||||
Support for AVX-512 require a processor with AVX512-VBMI2 support (Ice Lake or better, AMD Zen 4 or better) under a 64-bit system and a recent compiler (LLVM clang 6 or better, GCC 8 or better, Visual Studio 2019 or better). You need a correspondingly recent assembler such as gas (2.30+) or nasm (2.14+): recent compilers usually come with recent assemblers. If you mix a recent compiler with an incompatible/old assembler (e.g., when using a recent compiler with an old Linux distribution), you may get errors at build time because the compiler produces instructions that the assembler does not recognize: you should update your assembler to match your compiler (e.g., upgrade binutils to version 2.30 or better under Linux) or use an older compiler matching the capabilities of your assembler.
|
||||
|
||||
We test the library on a big-endian system (IBM s390x with Linux) .
|
||||
We test the library on a big-endian system (IBM s390x with Linux).
|
||||
|
||||
Including simdjson
|
||||
------------------
|
||||
@@ -63,20 +64,25 @@ into your project. Then include it in your project with:
|
||||
using namespace simdjson; // optional
|
||||
```
|
||||
|
||||
You can compile with:
|
||||
Under most systems, you can compile with:
|
||||
|
||||
```
|
||||
c++ myproject.cpp simdjson.cpp
|
||||
```
|
||||
|
||||
Note:
|
||||
- We recommend that you use simdjson by copying the single-header `simdjson.h` file along with the source file `simdjson.cpp` directly in your project, as they are part of [every release](https://github.com/simdjson/simdjson/releases) as assets. In this manner, you only have to compile `simdjson.cpp` as any other source file: it works well in every development environment. However, you may also use simdjson as a git submodule ([example](https://github.com/simdjson/cmakedemo)), using FetchContent ([example](https://github.com/simdjson/cmake_demo_single_file)), with ExternalProject_Add ([example](https://github.com/simdjson/cmakedemo_externalproject)) or with CPM ([example](https://github.com/cpm-cmake/CPM.cmake/tree/master/examples/simdjson)).
|
||||
- Users on macOS and other platforms where default compilers do not provide C++11 compliant by default should request it with the appropriate flag (e.g., `c++ -std=c++11 myproject.cpp simdjson.cpp`).
|
||||
- The library relies on [runtime CPU detection](implementation-selection.md): avoid specifying an architecture at compile time (e.g., `-march-native`) if you want your binaries to run everywhere.
|
||||
|
||||
Using simdjson with package managers
|
||||
------------------
|
||||
|
||||
You can install the simdjson library on your system or in your project using multiple package managers such as MSYS2, the conan package manager, vcpkg, brew, the apt package manager (debian-based Linux systems), the FreeBSD package manager (FreeBSD), and so on. [Visit our wiki for more details](https://github.com/simdjson/simdjson/wiki/Installing-simdjson-with-a-package-manager).
|
||||
You can install the simdjson library on your system or in your project using multiple package managers such as MSYS2, the conan package manager, vcpkg, brew, the apt package manager (debian-based Linux systems), the FreeBSD package manager (FreeBSD), and so on. E.g., [we provide an complete example with vcpkg](https://github.com/simdjson/simdjson-vcpkg) that works under Windows. [Visit our wiki for more details](https://github.com/simdjson/simdjson/wiki/Installing-simdjson-with-a-package-manager).
|
||||
|
||||
|
||||
|
||||
The following Linux distributions provide simdjson packages: Alpine, RedHat, Rocky Linux, Debian, Fedora, and Ubuntu.
|
||||
|
||||
Using simdjson as a CMake dependency
|
||||
------------------
|
||||
@@ -141,7 +147,7 @@ https://github.com/simdjson/simdjson/blob/vx.y.z/doc/basics.md
|
||||
where `x.y.z` should correspond to the version number you have
|
||||
chosen.
|
||||
|
||||
The Basics: Loading and Parsing JSON Documents
|
||||
The basics: loading and parsing JSON documents
|
||||
----------------------------------------------
|
||||
|
||||
The simdjson library allows you to navigate and validate JSON documents ([RFC 8259](https://www.tbray.org/ongoing/When/201x/2017/12/14/rfc8259.html)).
|
||||
@@ -153,7 +159,8 @@ For efficiency reasons, simdjson requires a string with a few bytes (`simdjson::
|
||||
at the end, these bytes may be read but their content does not affect the parsing. In practice,
|
||||
it means that the JSON inputs should be stored in a memory region with `simdjson::SIMDJSON_PADDING`
|
||||
extra bytes at the end. You do not have to set these bytes to specific values though you may
|
||||
want to if you want to avoid runtime warnings with some sanitizers.
|
||||
want to if you want to avoid runtime warnings with some sanitizers. Advanced users may want to
|
||||
read the section Free Padding in [our performance notes](performance.md).
|
||||
|
||||
The simdjson library offers a tree-like [API](https://en.wikipedia.org/wiki/API), which you can
|
||||
access by creating a `ondemand::parser` and calling the `iterate()` method. The iterate method
|
||||
@@ -204,7 +211,7 @@ simdjson::padded_string my_padded_data(data); // copies to a padded buffer
|
||||
We recommend against creating many `std::string` or many `std::padding_string` instances in your application to store your JSON data.
|
||||
Consider reusing the same buffers and limiting memory allocations.
|
||||
|
||||
By default, the simdjson library throws exceptions (`simdjson_error`) on errors. We omit `try`-`catch` clauses from our illustrating examples: if you omit `try`-`catch` in your code, an uncaught exception will halt your program. It is also possible to use simdjson without generating exceptions, and you may even build the library without exception support at all. See [Error Handling](#error-handling) for details.
|
||||
By default, the simdjson library throws exceptions (`simdjson_error`) on errors. We omit `try`-`catch` clauses from our illustrating examples: if you omit `try`-`catch` in your code, an uncaught exception will halt your program. It is also possible to use simdjson without generating exceptions, and you may even build the library without exception support at all. See [Error handling](#error-handling) for details.
|
||||
|
||||
|
||||
Some users may want to browse code along with the compiled assembly. You want to check out the following lists of examples:
|
||||
@@ -212,8 +219,14 @@ Some users may want to browse code along with the compiled assembly. You want to
|
||||
* [simdjson examples with errors handled through exceptions](https://godbolt.org/z/98Kx9Kqjn)
|
||||
* [simdjson examples with errors without exceptions](https://godbolt.org/z/PKG7GdbPo)
|
||||
|
||||
*Windows-specific*: Windows users who need to read files with
|
||||
non-ANSI characters in the name should set their code page to
|
||||
UTF-8 (65001). This should be the default with Windows 11 and better.
|
||||
Further, they may use the AreFileApisANSI function to determine whether
|
||||
the filename is interpreted using the ANSI or the system default OEM
|
||||
codepage, and they may call SetFileApisToOEM accordingly.
|
||||
|
||||
Documents are Iterators
|
||||
Documents are iterators
|
||||
-----------------------
|
||||
|
||||
The simdjson library relies on an approach to parsing JSON that we call "On Demand".
|
||||
@@ -223,13 +236,13 @@ walking through the original JSON text, merrily reading commas and colons and br
|
||||
you get where you are going. This is the key to On Demand's performance: since it's just an iterator,
|
||||
it lets you parse values as you use them. And particularly, it lets you *skip* values you do not want
|
||||
to use. On Demand is also ideally suited when you want to capture part of the document without parsing it
|
||||
immediately (e.g., see [General Direct Access to the Raw JSON String](#general-direct-access-to-the-raw-json-string)).
|
||||
immediately (e.g., see [General direct access to the raw JSON string](#general-direct-access-to-the-raw-json-string)).
|
||||
|
||||
We refer to "On Demand" as a front-end component since it is an interface between the
|
||||
low-level parsing functions and the user. It hides much of the complexity of parsing JSON
|
||||
documents.
|
||||
|
||||
### Parser, Document and JSON Scope
|
||||
### Parser, document and JSON scope
|
||||
|
||||
For code safety, you should keep (1) the `parser` instance, (2) the input string and (3) the document instance alive throughout your parsing. Additionally, you should follow the following rules:
|
||||
|
||||
@@ -258,7 +271,7 @@ copy the data into their own favorite class instances (e.g., alternatives to `st
|
||||
|
||||
A `std::string_view` instance is effectively just a pointer to a region in memory representing
|
||||
a string. In simdjson, we return `std::string_view` instances that either point within the
|
||||
input string you parsed (see [General Direct Access to the Raw JSON String](#general-direct-access-to-the-raw-json-string)), or to a temporary string buffer inside
|
||||
input string you parsed (see [General direct access to the raw JSON string](#general-direct-access-to-the-raw-json-string)), or to a temporary string buffer inside
|
||||
our parser class instances that is valid until the parser object is destroyed or you use it to parse another document.
|
||||
When using `std::string_view` instances, it is your responsibility to ensure that
|
||||
`std::string_view` instance does not outlive the pointed-to memory (e.g., either the input
|
||||
@@ -283,23 +296,31 @@ Some users prefer to use non-JSON native encoding formats such as UTF-16 or UTF-
|
||||
transcode the UTF-8 strings produced by the simdjson library to other formats. See the
|
||||
[simdutf library](https://github.com/simdutf/simdutf), for example.
|
||||
|
||||
Using the Parsed JSON
|
||||
---------------------
|
||||
Avoiding pitfalls: enable development checks
|
||||
--------------------
|
||||
|
||||
We recommend that you first compile and run your code in Debug mode: under Visual Studio,
|
||||
it means having the `_DEBUG` macro defined, and, for other compilers, it means leaving
|
||||
the `__OPTIMIZE__` macro undefined. The simdjson code will set `SIMDJSON_DEVELOPMENT_CHECKS=1`.
|
||||
We recommend that you first compile and run your code in Debug mode:
|
||||
|
||||
- under Visual Studio, it means having the `_DEBUG` macro defined,
|
||||
- for other compilers, it means leaving the `__OPTIMIZE__` macro undefined.
|
||||
|
||||
The simdjson code will set `SIMDJSON_DEVELOPMENT_CHECKS=1` in debug mode.
|
||||
Alternatively, you can set the macro `SIMDJSON_DEVELOPMENT_CHECKS` to 1 prior to including
|
||||
the `simdjson.h` header to enable these additional checks: just make sure you remove the
|
||||
definition once your code has been tested. When `SIMDJSON_DEVELOPMENT_CHECKS` is set to 1, the
|
||||
simdjson library runs additional (expensive) tests on your code to help ensure that you are
|
||||
using the library in a safe manner. Once your code has been tested, you can then run it in
|
||||
using the library in a safe manner.
|
||||
|
||||
Once your code has been tested, you can then run it in
|
||||
Release mode: under Visual Studio, it means having the `_DEBUG` macro undefined, and, for other
|
||||
compilers, it means setting `__OPTIMIZE__` to a positive integer. You can also forcefully
|
||||
disable these checks by setting `SIMDJSON_DEVELOPMENT_CHECKS` to 0. Once your code is tested, we
|
||||
further encourage you to define `NDEBUG` in your Release builds to disable additional runtime
|
||||
testing and get the best performance.
|
||||
|
||||
Using the parsed JSON
|
||||
---------------------
|
||||
|
||||
Once you have a document (`simdjson::ondemand::document`), you can navigate it with
|
||||
idiomatic C++ iterators, operators and casts. Besides the document instances and
|
||||
native types (`double`, `uint64_t`, `int64_t`, `bool`), we also access
|
||||
@@ -329,7 +350,7 @@ We invite you to keep the following rules in mind:
|
||||
The simdjson library makes generous use of `std::string_view` instances. If you are unfamiliar
|
||||
with `std::string_view` in C++, make sure to [read the section on std::string_view](#string_view).
|
||||
They behave much like an immutable `std::string` but they require no memory allocation. You can
|
||||
create a `std::string` instance from an `std::string_view` when you need it.
|
||||
create a `std::string` instance from a `std::string_view` when you need it.
|
||||
|
||||
The following specific instructions indicate how to use the JSON when exceptions are enabled, but simdjson has full, idiomatic
|
||||
support for users who avoid exceptions. See [the simdjson error handling documentation](basics.md#error-handling) for more.
|
||||
@@ -343,7 +364,7 @@ support for users who avoid exceptions. See [the simdjson error handling documen
|
||||
ondemand::object and ondemand::array. We also have explicit methods such as `get_string()`, `get_double()`,
|
||||
`get_uint64()`, `get_int64()`, `get_bool()`, `get_object()` and `get_array()`. After a cast or an explicit method,
|
||||
the number, string or boolean will be parsed, or the initial `{` or `[` will be verified for `ondemand::object` and `ondemand::array`. An exception may be thrown if
|
||||
the cast is not possible: there error code is `simdjson::INCORRECT_TYPE` (see [Error Handling](#error-handling)). Importantly, when getting an ondemand::object or ondemand::array instance, its content is
|
||||
the cast is not possible: there error code is `simdjson::INCORRECT_TYPE` (see [Error handling](#error-handling)). Importantly, when getting an ondemand::object or ondemand::array instance, its content is
|
||||
not validated: you are only guaranteed that the corresponding initial character (`{` or `[`) is present. Thus,
|
||||
for example, you could have an ondemand::object instance pointing at the invalid JSON `{ "this is not a valid object" }`: the validation occurs as you access the content.
|
||||
The `get_string()` returns a valid UTF-8 string, after
|
||||
@@ -359,20 +380,43 @@ support for users who avoid exceptions. See [the simdjson error handling documen
|
||||
> IMPORTANT NOTE: values can only be parsed once. Since documents are *iterators*, once you have
|
||||
> parsed a value (such as by casting to double), you cannot get at it again. It is an error to call
|
||||
> `get_string()` twice on an object (or to cast an object twice to `std::string_view`).
|
||||
* **Array Iteration:** To iterate through an array, use `for (auto value : array) { ... }`. This will
|
||||
step through each value in the JSON array.
|
||||
|
||||
To iterate through an array, you should be at the beginning
|
||||
of the array: to warn you, an OUT_OF_ORDER_ITERATION error is generated [when development checks](#avoiding-pitfalls-enable-development-checks) are active. If you need to access an array more
|
||||
than once, you may call `reset()` on it although we discourage this practice. Keep in mind that
|
||||
you should consume each value at most once.
|
||||
|
||||
If you know the type of the value, you can cast it right there, too! `for (double value : array) { ... }`.
|
||||
|
||||
You may also use explicit iterators: `for(auto i = array.begin(); i != array.end(); i++) {}`. You can check that an array is empty with the condition `auto i = array.begin(); if (i == array.end()) {...}`.
|
||||
* **Object Iteration:** You can iterate through an object's fields, as well: `for (auto field : object) { ... }`. You may also use explicit iterators : `for(auto i = object.begin(); i != object.end(); i++) { auto field = *i; .... }`. You can check that an object is empty with the condition `auto i = object.begin(); if (i == object.end()) {...}`.
|
||||
- `field.unescaped_key()` will get you the unescaped key string as a `std::string_view` instance. E.g., the JSON string `"\u00e1"` becomes the Unicode string `á`. Optionally, you pass `true` as a parameter to the `unescaped_key` method if you want invalid escape sequences to be replaced by a default replacement character (e.g., `\ud800\ud801\ud811`): otherwise bad escape sequences lead to an immediate error.
|
||||
- `field.escaped_key()` will get you the key string as as a `std::string_view` instance, but unlike `unescaped_key()`, the key is not processed, so no unescaping is done. E.g., the JSON string `"\u00e1"` becomes the Unicode string `\u00e1`. We expect that `escaped_key()` is faster than `field.unescaped_key()`.
|
||||
- `field.value()` will get you the value, which you can then use all these other methods on.
|
||||
|
||||
|
||||
To iterate through an object, you should be at the beginning
|
||||
of the object: to warn you, an OUT_OF_ORDER_ITERATION error is generated [when development checks](#avoiding-pitfalls-enable-development-checks) are active. If you need to access an object more
|
||||
than once, you may call `reset()` on it although we discourage this practice. Keep in mind that
|
||||
you should consume each value at most once.
|
||||
* **Array Index:** Because it is forward-only, you cannot look up an array element by index by index. Instead,
|
||||
you should iterate through the array and keep an index yourself. Exceptionally, if need a single value
|
||||
out of the array, you may use an array access (e.g., `array[1]`).
|
||||
* **Field Access:** To get the value of the "foo" field in an object, use `object["foo"]`. This will
|
||||
scan through the object looking for the field with the matching string, doing a character-by-character
|
||||
comparison. It may generate the error `simdjson::NO_SUCH_FIELD` if there is no such key in the object, it may throw an exception (see [Error Handling](#error-handling)). For efficiency reason, you should avoid looking up the same field repeatedly: e.g., do
|
||||
not do `object["foo"]` followed by `object["foo"]` with the same `object` instance. For best performance, you should try to query the keys in the same order they appear in the document. If you need several keys and you cannot predict the order they will appear in, it is recommended to iterate through all keys `for(auto field : object) {...}`. Keep in mind that On Demand does not buffer or save the result of the parsing: if you repeatedly access `object["foo"]`, then it must repeatedly seek the key and parse the content. The library does not provide a distinct function to check if a key is present, instead we recommend you attempt to access the key: e.g., by doing `ondemand::value val{}; if (!object["foo"].get(val)) {...}`, you have that `val` contains the requested value inside the if clause. It is your responsibility as a user to temporarily keep a reference to the value (`auto v = object["foo"]`), or to consume the content and store it in your own data structures. If you consume an
|
||||
comparison. It may generate the error `simdjson::NO_SUCH_FIELD` if there is no such key in the object, it may throw an exception (see [Error handling](#error-handling)). For efficiency reason, you should avoid looking up the same field repeatedly: e.g., do
|
||||
not do `object["foo"]` followed by `object["foo"]` with the same `object` instance. For best performance, you should try to query the keys in the same order they appear in the document. If you need several keys and you cannot predict the order they will appear in, it is recommended to iterate through all keys `for(auto field : object) {...}`. Generally, you should not mix and match iterating through an object (`for(auto field : object) {...}`) and key accesses (`object["foo"]`): if you need to iterate through an object after a key access, you need to call `reset()` on the object. Keep in mind that On Demand does not buffer or save the result of the parsing: if you repeatedly access `object["foo"]`, then it must repeatedly seek the key and parse the content. The library does not provide a distinct function to check if a key is present, instead we recommend you attempt to access the key: e.g., by doing `ondemand::value val{}; if (!object["foo"].get(val)) {...}`, you have that `val` contains the requested value inside the if clause. It is your responsibility as a user to temporarily keep a reference to the value (`auto v = object["foo"]`), or to consume the content and store it in your own data structures. If you consume an
|
||||
object twice: `std::string_view(object["foo"]` followed by `std::string_view(object["foo"]` then your code
|
||||
is in error. Furthermore, you can only consume one field at a time, on the same object. The
|
||||
value instance you get from `content["bids"]` becomes invalid when you call `content["asks"]`.
|
||||
If you have retrieved `content["bids"].get_array()` and you later call
|
||||
`content["asks"].get_array()`, then the first array should no longer be accessed: it would be
|
||||
unsafe to do so. You can detect such mistakes by first compiling and running the code in
|
||||
Debug mode: an OUT_OF_ORDER_ITERATION error is generated.
|
||||
unsafe to do so. You can detect such mistakes by first compiling and running the code [with development checks](#avoiding-pitfalls-enable-development-checks): an OUT_OF_ORDER_ITERATION error is generated.
|
||||
|
||||
> NOTE: JSON allows you to escape characters in keys. E.g., the key `"date"` may be written as
|
||||
> `"\u0064\u0061\u0074\u0065"`. By default, simdjson does *not* unescape keys when matching by default.
|
||||
> `"\u0064\u0061\u0074\u0065"`. By default, simdjson does *not* unescape keys when matching.
|
||||
> Thus if you search for the key `"date"` and the JSON document uses `"\u0064\u0061\u0074\u0065"`
|
||||
> as a key, it will not be recognized. This is not generally a problem. Nevertheless, if you do need
|
||||
> to support escaped keys, the method `unescaped_key()` provides the desired unescaped keys by
|
||||
@@ -381,7 +425,9 @@ support for users who avoid exceptions. See [the simdjson error handling documen
|
||||
> Unicode sequences with replacement, meaning that the decoding always succeeds but bogus Unicode
|
||||
> replacement characters are inserted. In general, you should expect a performance penalty
|
||||
> when using `unescaped_key()` compared to `key()` because of the string processing: the `key()`
|
||||
> function just points inside the source JSON document.
|
||||
> function just points inside the source JSON document. As a compromise, you may use `escaped_key()``
|
||||
> which returns a `std::string_view` instance pointing directly in the document, like `key()`, although,
|
||||
> unlike `key()`, it has to determine the location of the final quote character.
|
||||
>
|
||||
> ```c++
|
||||
> auto json = R"({"k\u0065y": 1})"_padded;
|
||||
@@ -391,6 +437,9 @@ support for users who avoid exceptions. See [the simdjson error handling documen
|
||||
> for(auto field : object) {
|
||||
> // parses and writes out the key, after unescaping it,
|
||||
> // to a string buffer. It causes a performance penalty.
|
||||
> // If you do not expect that unescaping is useful, you
|
||||
> // may replace field.unescaped_key() with
|
||||
> // field.escaped_key().
|
||||
> std::string_view keyv = field.unescaped_key();
|
||||
> if (keyv == "key") { std::cout << uint64_t(field.value()); }
|
||||
> }
|
||||
@@ -421,17 +470,6 @@ support for users who avoid exceptions. See [the simdjson error handling documen
|
||||
> double y = doc["y"]; // The cursor is now after the 2 (at })
|
||||
> double x = doc["x"]; // Success: [] loops back around to find "x"
|
||||
> ```
|
||||
* **Array Iteration:** To iterate through an array, use `for (auto value : array) { ... }`. This will
|
||||
step through each value in the JSON array.
|
||||
|
||||
If you know the type of the value, you can cast it right there, too! `for (double value : array) { ... }`.
|
||||
|
||||
You may also use explicit iterators: `for(auto i = array.begin(); i != array.end(); i++) {}`. You can check that an array is empty with the condition `auto i = array.begin(); if (i == array.end()) {...}`.
|
||||
* **Object Iteration:** You can iterate through an object's fields, as well: `for (auto field : object) { ... }`. You may also use explicit iterators : `for(auto i = object.begin(); i != object.end(); i++) { auto field = *i; .... }`. You can check that an object is empty with the condition `auto i = object.begin(); if (i == object.end()) {...}`.
|
||||
- `field.unescaped_key()` will get you the unescaped key string. E.g., the JSON string `"\u00e1"` becomes the Unicode string `á`. Optionally, you pass `true` as a parameter to the `unescaped_key` method if you want invalid escape sequences to be replaced by a default replacement character (e.g., `\ud800\ud801\ud811`): otherwise bad escape sequences lead to an immediate error.
|
||||
- `field.value()` will get you the value, which you can then use all these other methods on.
|
||||
* **Array Index:** Because it is forward-only, you cannot look up an array element by index by index. Instead,
|
||||
you should iterate through the array and keep an index yourself.
|
||||
* **Output to strings:** Given a document, a value, an array or an object in a JSON document, you can output a JSON string version suitable to be parsed again as JSON content: `simdjson::to_json_string(element)`. A call to `to_json_string` consumes fully the element: if you apply it on a document, the JSON pointer is advanced to the end of the document. The `simdjson::to_json_string` does not allocate memory. The `to_json_string` function should not be confused with retrieving the value of a string instance which are escaped and represented using a lightweight `std::string_view` instance pointing at an internal string buffer inside the parser instance. To illustrate, the first of the following two code segments will print the unescaped string `"test"` complete with the quote whereas the second one will print the escaped content of the string (without the quotes).
|
||||
> ```C++
|
||||
> // serialize a JSON to an escaped std::string instance so that it can be parsed again as JSON
|
||||
@@ -533,7 +571,7 @@ support for users who avoid exceptions. See [the simdjson error handling documen
|
||||
auto json = R"( { "test":{ "val1":1, "val2":2 } } )"_padded;
|
||||
auto doc = parser.iterate(json);
|
||||
size_t count = doc.count_fields(); // requires simdjson 1.0 or better
|
||||
std::cout << "Number of fields: " << new_count << std::endl; // Prints "Number of fields: 1"
|
||||
std::cout << "Number of fields: " << count << std::endl; // Prints "Number of fields: 1"
|
||||
```
|
||||
Similarly to `count_elements`, you should not let an object instance go out of scope before consuming it after calling
|
||||
the `count_fields` method. If you access an object inside a document, you can use the `count_fields` method as follow.
|
||||
@@ -548,7 +586,7 @@ support for users who avoid exceptions. See [the simdjson error handling documen
|
||||
* **Tree Walking and JSON Element Types:** Sometimes you don't necessarily have a document
|
||||
with a known type, and are trying to generically inspect or walk over JSON elements.
|
||||
You can also represent arbitrary JSON values with
|
||||
`ondemand::value` instances: it can represent anything except a scalar document (lone number, string, null or Boolean). You can check for scalar documents with the method `scalar()`. You can cast a document that is either an array or an object to an `ondemand::value` instance immediately after you create the document instance: you cannot create a `ondemand::value` instance from a document that has already been accessed as it would mean that you would have two instances of the object or array simultaneously (see [rewinding](#rewinding)). You can query the type of a document or a value with the `type()` method. The `type()` method does not consume or validate documents and values, but it tells you whether they are
|
||||
`ondemand::value` instances: it can represent anything except a scalar document (lone number, string, null or Boolean). You can check for scalar documents with the method `scalar()`. You can cast a document that is either an array or an object to an `ondemand::value` instance immediately after you create the document instance: you cannot create an `ondemand::value` instance from a document that has already been accessed as it would mean that you would have two instances of the object or array simultaneously (see [rewinding](#rewinding)). You can query the type of a document or a value with the `type()` method. The `type()` method does not consume or validate documents and values, but it tells you whether they are
|
||||
- arrays (`json_type::array`),
|
||||
- objects (`json_type::object`)
|
||||
- numbers (`json_type::number`),
|
||||
@@ -587,6 +625,9 @@ support for users who avoid exceptions. See [the simdjson error handling documen
|
||||
// key() returns the key as it appears in the raw
|
||||
// JSON document, if we want the unescaped key,
|
||||
// we should do field.unescaped_key().
|
||||
// We could also use field.escaped_key() if we want
|
||||
// a std::string_view instance, but we do not need
|
||||
// escaping.
|
||||
cout << "\"" << field.key() << "\": ";
|
||||
recursive_print_json(field.value());
|
||||
add_comma = true;
|
||||
@@ -628,7 +669,7 @@ support for users who avoid exceptions. See [the simdjson error handling documen
|
||||
}
|
||||
```
|
||||
|
||||
### Using the Parsed JSON: Additional examples
|
||||
### Using the parsed JSON: additional examples
|
||||
|
||||
|
||||
Let us review these concepts with some additional examples. For simplicity, we omit the include clauses (`#include "simdjson.h"`) as well as namespace-using clauses (`using namespace simdjson;`).
|
||||
@@ -691,6 +732,8 @@ ondemand::document doc = parser.iterate(cars_json);
|
||||
for (ondemand::field key_car : doc.get_object()) {
|
||||
// If I need a string_view and/or, I can use key_car.unescaped_key() instead, but
|
||||
// key_car.key() will be more performant otherwise.
|
||||
// If we want a std::string_view instance but we do not care about escaping, we
|
||||
// can also use key_car.escaped_key().
|
||||
cout << "identifier : " << key_car.key() << std::endl;
|
||||
// I can now access the subobject:
|
||||
ondemand::object car = key_car.value();
|
||||
@@ -798,7 +841,7 @@ simdjson::ondemand::value::get() noexcept {
|
||||
}
|
||||
```
|
||||
|
||||
We may then provide support for our `Car`` struct:
|
||||
We may then provide support for our `Car` struct:
|
||||
|
||||
```C++
|
||||
template <>
|
||||
@@ -1224,7 +1267,7 @@ doc.at_path(".\\u00E9") == 123; // true
|
||||
doc.at_path((const char*)u8".\u00E9") // returns an error (NO_SUCH_FIELD)
|
||||
```
|
||||
|
||||
Error Handling
|
||||
Error handling
|
||||
--------------
|
||||
|
||||
Error handing with exception and a single try/catch clause makes the code simple, but it gives you little control over errors. For easier debugging or more robust error handling, you may want to consider our exception-free approach.
|
||||
@@ -1374,9 +1417,9 @@ int main(void) {
|
||||
The `at` method can only be called once on an array. It cannot be used
|
||||
to iterate through the values of an array.
|
||||
|
||||
### Error Handling Examples without Exceptions
|
||||
### Error handling examples without exceptions
|
||||
|
||||
This is how the example in "Using the Parsed JSON" could be written using only error code checking (without exceptions):
|
||||
This is how the example in "Using the parsed JSON" could be written using only error code checking (without exceptions):
|
||||
|
||||
```c++
|
||||
bool parse() {
|
||||
@@ -1458,7 +1501,7 @@ having to handle exceptions.
|
||||
error = doc.get_object().get(object);
|
||||
if (error) { return false; }
|
||||
for(auto field : object) {
|
||||
// We could replace 'field.key() with field.unescaped_key(),
|
||||
// We could replace 'field.key() with field.unescaped_key() or field.escaped_key(),
|
||||
// and ondemand::raw_json_string by std::string_view.
|
||||
ondemand::raw_json_string keyv;
|
||||
error = field.key().get(keyv);
|
||||
@@ -1472,7 +1515,7 @@ having to handle exceptions.
|
||||
}
|
||||
```
|
||||
|
||||
### Disabling Exceptions
|
||||
### Disabling exceptions
|
||||
|
||||
The simdjson can be build with exceptions entirely disabled. It checks the `__cpp_exceptions` macro at compile time. Even if exceptions are enabled in your compiler, you may still disable exceptions specifically for simdjson, by setting `SIMDJSON_EXCEPTIONS` to `0` (false) at compile-time when building the simdjson library. If you are building with CMake, to ensure you don't write any code that uses exceptions, you compile with `SIMDJSON_EXCEPTIONS=OFF`. For example, if including the project via cmake:
|
||||
|
||||
@@ -1783,7 +1826,7 @@ If your documents are large (e.g., larger than a megabyte), then the `iterate_ma
|
||||
We also provide some support for comma-separated documents and other advanced features.
|
||||
See [iterate_many.md](iterate_many.md) for detailed information and design.
|
||||
|
||||
Parsing Numbers Inside Strings
|
||||
Parsing numbers inside strings
|
||||
------------------------------
|
||||
|
||||
Though the JSON specification allows for numbers and string values, many engineers choose to integrate the numbers inside strings, e.g., they prefer `{"a":"1.9"}` to`{"a":1.9}`.
|
||||
@@ -1877,7 +1920,7 @@ if (error) {
|
||||
}
|
||||
```
|
||||
It is also important to note that when dealing an invalid number inside a string, simdjson will report a `NUMBER_ERROR` error if the string begins with a number whereas simdjson
|
||||
will report a `INCORRECT_TYPE` error otherwise.
|
||||
will report an `INCORRECT_TYPE` error otherwise.
|
||||
|
||||
The `*_in_string` methods can also be called on a single document instance:
|
||||
e.g., when your document consist solely of a quoted number.
|
||||
@@ -1913,7 +1956,7 @@ Thus it is a dynamically typed number. Before accessing the value, you must dete
|
||||
* `number.get_number_type()` has value `number_type::unsigned_integer` if we have a integer in `[9223372036854775808,18446744073709551616)`. You can recover the value by the `get_uint64()` method applied on the `ondemand::number` instance. When `number.get_number_type()` has value `number_type::unsigned_integer`, you also have that `number.is_uint64()` is true. Calling `get_uint64()` on the `ondemand::number` instance when `number.get_number_type()` is not `number_type::unsigned_integer` is unsafe. You may replace `get_uint64()` by a cast to a `uint64_t` value.
|
||||
* `number.get_number_type()` has value `number_type::floating_point_number` if we have and we have a floating-point (binary64) number. You can recover the value by the `get_double()` method applied on the `ondemand::number` instance. When `number.get_number_type()` has value `number_type::floating_point_number`, you also have that `number.is_double()` is true. Calling `get_double()` on the `ondemand::number` instance when `number.get_number_type()` is not `number_type::floating_point_number` is unsafe. You may replace `get_double()` by a cast to a `double` value.
|
||||
* When the value is an integer outside of the valid ranges for a 64-bit integers, e.g., when it is smaller than -9223372036854775808 or larger than 18446744073709551615, then `number.get_number_type()` has value `number_type::big_integer`. If you try to parse
|
||||
such a number of `get_number()`, you get the error `BIGINT_ERROR`. You can access the underlying string of digits with the function `raw_json_token()` which returns an `std::string_view` instance starting at the beginning of the digit. You can also call `get_double()` to get a floating-point approximation.
|
||||
such a number of `get_number()`, you get the error `BIGINT_ERROR`. You can access the underlying string of digits with the function `raw_json_token()` which returns a `std::string_view` instance starting at the beginning of the digit. You can also call `get_double()` to get a floating-point approximation.
|
||||
|
||||
|
||||
You must check the type before accessing the value: it is an error to call `get_int64()` when `number.get_number_type()` is not `number_type::signed_integer` and when `number.is_int64()` is false. You are responsible for this check as the user of the library.
|
||||
@@ -2013,7 +2056,7 @@ This code prints the following:
|
||||
'99999999999999999999999 '
|
||||
```
|
||||
|
||||
Raw Strings From Keys
|
||||
Raw strings from keys
|
||||
-----------
|
||||
|
||||
It is sometimes useful to have access to a raw (unescaped) string: we make available a
|
||||
@@ -2021,7 +2064,7 @@ minimalist `raw_json_string` data type which contains a pointer inside the strin
|
||||
original document, right after the quote. It is accessible via `get_raw_json_string()` on a
|
||||
string instance and returned by the `key()` method on an object's field instance. It is always
|
||||
optional: replacing `get_raw_json_string()` with `get_string()` and `key()` by
|
||||
`unescaped_key()` returns an `string_view` instance of the unescaped string.
|
||||
`unescaped_key()` or `escaped_key()` returns an `string_view` instance of the unescaped/unprocessed string.
|
||||
|
||||
You can quickly compare a `raw_json_string` instance with a target string. You may also
|
||||
unescape the `raw_json_string` on your own string buffer: `parser.unescape(mystr, ptr)`
|
||||
@@ -2049,11 +2092,11 @@ JSON string to a user-provided buffer:
|
||||
}
|
||||
```
|
||||
|
||||
Some users might prefer to have a direct access to an `std::string_view` instance
|
||||
Some users might prefer to have a direct access to a `std::string_view` instance
|
||||
pointing inside the source document. The `key_raw_json_token()` method serves this
|
||||
purpose. It provides a view on the key, including the starting quote character,
|
||||
and everything up to the next `:` character after the final quote character. E.g.,
|
||||
if the key is `"name"` then `key_raw_json_token()` returns an `std::string_view` which
|
||||
if the key is `"name"` then `key_raw_json_token()` returns a `std::string_view` which
|
||||
begins with `"name"` and may containing trailing white-space characters.
|
||||
```C++
|
||||
auto json = R"( {"name" : "Jack The Ripper \u0033"} )"_padded;
|
||||
@@ -2065,7 +2108,7 @@ begins with `"name"` and may containing trailing white-space characters.
|
||||
```
|
||||
|
||||
|
||||
General Direct Access to the Raw JSON String
|
||||
General direct access to the raw JSON string
|
||||
--------------------------------
|
||||
If your value is a string, the `raw_json_string` you get with `get_raw_json_string()` gives you direct access to the unprocessed
|
||||
string. But the simdjson library allows you to have access to the raw underlying JSON
|
||||
@@ -2172,14 +2215,41 @@ string representation.
|
||||
```
|
||||
|
||||
|
||||
Storing Directly into an Existing String Instance
|
||||
You can use `raw_json()` to capture the content of some JSON values as `std::string_view`
|
||||
instances which can be safely used later. The `std::string_view` instances point inside
|
||||
the original document and do not depend in any way on simdjson. In the following example,
|
||||
we store the `std::string_view` instances inside a `std::vector<std::string_view>` instance
|
||||
and print the out after the parsing is concluded:
|
||||
|
||||
```cpp
|
||||
padded_string json_padded = "{\"a\":[1,2,3], \"b\": 2, \"c\": \"hello\"}"_padded;
|
||||
std::vector<std::string_view> fields;
|
||||
|
||||
ondemand::parser parser;
|
||||
auto doc = parser.iterate(json_padded);
|
||||
auto object = doc.get_object();
|
||||
for (auto field : object) {
|
||||
fields.push_back(field.value().raw_json());
|
||||
}
|
||||
// Output the fields
|
||||
// Expected output:
|
||||
// [1,2,3]
|
||||
// 2
|
||||
// "hello"
|
||||
for (std::string_view field_ref : fields) {
|
||||
std::cout << field_ref << std::endl;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Storing directly into an existing string instance
|
||||
-----------------------------------------------------
|
||||
|
||||
The simdjson library favours the use of `std::string_view` instances because
|
||||
it tends to lead to better performance due to causing fewer memory allocations.
|
||||
However, they are cases where you need to store a string result in an `std::string``
|
||||
However, they are cases where you need to store a string result in a `std::string`
|
||||
instance. You can do so with a templated version of the `to_string()` method which takes as
|
||||
a parameter a reference to an `std::string`.
|
||||
a parameter a reference to a `std::string`.
|
||||
|
||||
```C++
|
||||
auto json = R"({
|
||||
@@ -2204,7 +2274,7 @@ The `std::string` instance, once created, is independent. Unlike our `std::strin
|
||||
it does not point at data that is within our `parser` instance. The same caveat applies: you should
|
||||
only consume a JSON string once.
|
||||
|
||||
Because `get_string()` is a template that requires a type that can be assigned an `std::string`, you
|
||||
Because `get_string()` is a template that requires a type that can be assigned a `std::string`, you
|
||||
can use it with features such as `std::optional`:
|
||||
|
||||
```C++
|
||||
@@ -2219,7 +2289,7 @@ can use it with features such as `std::optional`:
|
||||
You should be mindful of the trade-off: allocating multiple
|
||||
`std::string` instances can become expensive.
|
||||
|
||||
Thread Safety
|
||||
Thread safety
|
||||
-------------
|
||||
|
||||
We built simdjson with thread safety in mind.
|
||||
@@ -2232,10 +2302,15 @@ acts as an iterator (and is therefore not thread safe).
|
||||
|
||||
The CPU detection, which runs the first time parsing is attempted and switches to the fastest
|
||||
parser for your CPU, is transparent and thread-safe.
|
||||
Our runtime dispatching is based on global objects that are instantiated at the beginning of the
|
||||
main thread and may be discarded at the end of the main thread. If you have multiple threads running
|
||||
and some threads use the library while the main thread is cleaning up ressources, you may encounter
|
||||
issues. If you expect such problems, you may consider using [std::quick_exit](https://en.cppreference.com/w/cpp/utility/program/quick_exit).
|
||||
|
||||
In a threaded environment, stack space is often limited. Running code like simdjson in debug mode may require hundreds of kilobytes of stack memory. Thus stack overflows are a possibility. We recommend you turn on optimization when working in an environment where stack space is limited. If you must run your code in debug mode, we recommend you configure your system to have more stack space. We discourage you from running production code based on a debug build.
|
||||
|
||||
Standard Compliance
|
||||
|
||||
Standard compliance
|
||||
--------------------
|
||||
|
||||
The simdjson library is fully compliant with the [RFC 8259](https://www.tbray.org/ongoing/When/201x/2017/12/14/rfc8259.html) JSON specification.
|
||||
@@ -2253,7 +2328,7 @@ The simdjson library is fully compliant with the [RFC 8259](https://www.tbray.o
|
||||
- The specification states that an implementation may set limits on the maximum depth of nesting. By default, the simdjson will refuse to parse documents with a depth exceeding 1024.
|
||||
|
||||
|
||||
Backwards Compatibility
|
||||
Backwards compatibility
|
||||
-----------------------
|
||||
|
||||
The only header file supported by simdjson is `simdjson.h`. Older versions of simdjson published a
|
||||
@@ -2420,7 +2495,7 @@ bool example() {
|
||||
ondemand::document doc = parser.iterate(json);
|
||||
ondemand::object root_object = doc.get_object();
|
||||
for(auto key_value : root_object) {
|
||||
// could get std::string_view with 'unescaped_key()':
|
||||
// could get std::string_view with 'unescaped_key()' or 'escaped_key()':
|
||||
std::cout << "key: " << key_value.key() << std::endl;
|
||||
ondemand::object obj = key_value.value();
|
||||
|
||||
@@ -2493,6 +2568,8 @@ bool example() {
|
||||
std::cout << "\n\ntop level:" << field.key() << std::endl;
|
||||
// You can get a proper std::string_view for the key with:
|
||||
// std::string_view key = field.unescaped_key();
|
||||
// or
|
||||
// std::string_view key = field.escaped_key();
|
||||
// and second for-range loop to get child-elements here
|
||||
for (ondemand::object inner_object : field.value()) {
|
||||
auto i = inner_object.begin();
|
||||
@@ -2506,6 +2583,9 @@ bool example() {
|
||||
<< "\" : " << inner_field.value() << ", ";
|
||||
// You can get proper std::string_view for the key and value with:
|
||||
// std::string_view inner_key = field.unescaped_key();
|
||||
// or
|
||||
// std::string_view inner_key = field.escaped_key();
|
||||
// and
|
||||
// std::string_view value_str = field.value();
|
||||
}
|
||||
}
|
||||
@@ -2543,13 +2623,14 @@ int main(void) {
|
||||
}
|
||||
```
|
||||
|
||||
Performance Tips
|
||||
Performance tips
|
||||
--------
|
||||
|
||||
|
||||
- Read [our performance notes](performance.md) for advanced topics.
|
||||
- The On Demand front-end works best when doing a single pass over the input: avoid calling `count_elements`, `rewind` and similar methods.
|
||||
- If you are familiar with assembly language, you may use the online tool godbolt to explore the compiled code. The following example may work: [https://godbolt.org/z/xE4GWs573](https://godbolt.org/z/xE4GWs573).
|
||||
- Given a field `field` in an object, calling `field.key()` is often faster than `field.unescaped_key()` so if you do not need an unescaped `std::string_view` instance, prefer `field.key()`.
|
||||
- Given a field `field` in an object, calling `field.key()` is often faster than `field.unescaped_key()` so if you do not need an unescaped `std::string_view` instance, prefer `field.key()`. Similarly, we expect `field.escaped_key()` to be faster than `field.unescaped_key()` even though both return a `std::string_view` instance.
|
||||
- For release builds, we recommend setting `NDEBUG` pre-processor directive when compiling the `simdjson` library. Importantly, using the optimization flags `-O2` or `-O3` under GCC and LLVM clang does not set the `NDEBUG` directive, you must set it manually (e.g., `-DNDEBUG`).
|
||||
- For long streams of JSON documents, consider [`iterate_many`](iterate_many.md) and [`parse_many`](parse_many.md) for better performance.
|
||||
- Never seek to access a field twice (e.g., o["data"] and later again o["data"]). Instead capture once an ondemand::value and reuse it.
|
||||
@@ -2569,8 +2650,10 @@ Performance Tips
|
||||
```
|
||||
- To better understand the operation of your On Demand parser, and whether it is performing as well as you think it should be, there is a logger feature built in to simdjson! To use it, define the pre-processor directive `SIMDJSON_VERBOSE_LOGGING` prior to including the `simdjson.h` header, which enables logging in simdjson. Run your code. It may generate a lot of logging output; adding printouts from your application that show each section may be helpful. The log's output will show step-by-step information on state, buffer pointer position, depth, and key retrieval status. Importantly, unless `SIMDJSON_VERBOSE_LOGGING` is defined, logging is entirely disabled and thus carries no overhead.
|
||||
|
||||
|
||||
|
||||
Further reading
|
||||
--------
|
||||
|
||||
|
||||
- John Keiser, Daniel Lemire, [On-Demand JSON: A Better Way to Parse Documents?](http://arxiv.org/abs/2312.17149), Software: Practice and Experience (to appear)
|
||||
- John Keiser, Daniel Lemire, [On-Demand JSON: A Better Way to Parse Documents?](http://arxiv.org/abs/2312.17149), Software: Practice and Experience 54 (6), 2024
|
||||
|
||||
@@ -71,6 +71,12 @@ For best performance, a `parser` instance should be reused over several files: o
|
||||
If you need a lower-level interface, you may call the function `parser.parse(const char * p, size_t l)` on a pointer `p` while specifying the
|
||||
length of your input `l` in bytes. To see how to get the very best performance from a low-level approach, you way want to read our [performance notes](https://github.com/simdjson/simdjson/blob/master/doc/performance.md#padding-and-temporary-copies) on this topic (see the Padding and Temporary Copies section).
|
||||
|
||||
*Windows-specific*: Windows users who need to read files with
|
||||
non-ANSI characters in the name should set their code page to
|
||||
UTF-8 (65001). This should be the default with Windows 11 and better.
|
||||
Further, they may use the AreFileApisANSI function to determine whether
|
||||
the filename is interpreted using the ANSI or the system default OEM
|
||||
codepage, and they may call SetFileApisToOEM accordingly.
|
||||
|
||||
Using the Parsed JSON
|
||||
---------------------
|
||||
|
||||
@@ -24,6 +24,8 @@ The current implementations are:
|
||||
* westmere: SSE4.2 (2010 Westmere or later).
|
||||
* arm64: 64-bit ARMv8-A NEON
|
||||
* ppc64: 64-bit POWER8 and POWER9 with VSX and ALTIVEC extensions. Both big endian and little endian are implemented, depending on the compiler you are using. The library is tested on recent, little-endian, POWER systems.
|
||||
* lasx: Loongson Advanced SIMD EXtension (LASX), a 256-bit vector expansion for the LoongArch architecture.
|
||||
* lsx: Loongson SIMD EXtension (LSX), a 128-bit vector expansion for the LoongArch architecture.
|
||||
* fallback: A generic implementation that runs on any 64-bit processor.
|
||||
|
||||
In many cases, you don't know where your compiled binary is going to run, so simdjson automatically
|
||||
@@ -33,7 +35,7 @@ it will include 2 (ppc64 and fallback).
|
||||
|
||||
If you know more about where you're going to run and want to save the space, you can disable any of
|
||||
these implementations at compile time with `-DSIMDJSON_IMPLEMENTATION_X=0` (where X is ICELAKE, HASWELL,
|
||||
WESTMERE, ARM64, PPC64 and FALLBACK).
|
||||
WESTMERE, ARM64, PPC64, LSX, LASX and FALLBACK).
|
||||
|
||||
The simdjson library automatically sets header flags for each implementation as it compiles; there
|
||||
is no need to set architecture-specific flags yourself (e.g., `-mavx2`, `/AVX2` or
|
||||
|
||||
@@ -752,7 +752,7 @@ On relevant systems, the On Demand API provides some support for runtime dispatc
|
||||
|
||||
Some users wish to run at the best possible speed. Under recent Intel and AMD processors, these users should take additional steps to verify that their code is well optimized.
|
||||
|
||||
Given that the On Demand API offer limited runtime dispatching, it matters that your code is compiled against a specific CPU target. You should verify that the code is compiled against the target you expect. Thankfully, the simdjson library will tell you exactly what it detects as an implementation: `icelake` (AVX512 x64 processors), `haswell` (AVX2 x64 processors), `westmere` (SSE4 x64 processors), `arm64` (64-bit ARM), `ppc64` (64-bit POWER), `fallback` (others). Under x64 processors, many programmers will want to target `haswell` whereas under ARM, most programmers will want to target `arm64` (and it should do so automatically). The `fallback` is probably only good for testing purposes, not for deployment.
|
||||
Given that the On Demand API offer limited runtime dispatching, it matters that your code is compiled against a specific CPU target. You should verify that the code is compiled against the target you expect. Thankfully, the simdjson library will tell you exactly what it detects as an implementation: `icelake` (AVX512 x64 processors), `haswell` (AVX2 x64 processors), `westmere` (SSE4 x64 processors), `arm64` (64-bit ARM), `ppc64` (64-bit POWER), `lasx` (LoongArch), `lsx` (LoongArch), `fallback` (others). Under x64 processors, many programmers will want to target `haswell` whereas under ARM, most programmers will want to target `arm64` (and it should do so automatically). The `fallback` is probably only good for testing purposes, not for deployment.
|
||||
|
||||
```C++
|
||||
std::cout << simdjson::builtin_implementation()->name() << std::endl;
|
||||
|
||||
@@ -14,6 +14,7 @@ testing and get the best performance.
|
||||
* [Number parsing](#number-parsing)
|
||||
* [Visual Studio](#visual-studio)
|
||||
* [Power Usage and Downclocking](#power-usage-and-downclocking)
|
||||
* [Free Padding](#free-padding)
|
||||
|
||||
|
||||
NDEBUG directive
|
||||
@@ -179,3 +180,110 @@ The simdjson library does not generally make use of heavy 256-bit instructions.
|
||||
the macro `SIMDJSON_AVX512_ALLOWED` to `0` in C++ prior to importing the headers.
|
||||
|
||||
You may still be worried about which SIMD instruction set is used by simdjson. Thankfully, [you can always determine and change which architecture-specific implementation is used](implementation-selection.md) by simdjson. Thus even if your CPU supports AVX2, you do not need to use AVX2. You are in control.
|
||||
|
||||
|
||||
Free Padding
|
||||
-------
|
||||
|
||||
For performance reasons, the simdjson library requires that the JSON input contain at least
|
||||
`simdjson::SIMDJSON_PADDING` bytes at the end of the stream. The value `simdjson::SIMDJSON_PADDING` is
|
||||
small (e.g., 64 bytes). On modern systems, you can safely read beyond an allocated buffers,
|
||||
as long as you remain within an allocated page. Pages on modern systems span at least 4 kilobytes,
|
||||
but can be significantly larger. E.g., Apple systems favour pages spanning 16 kilobytes.
|
||||
|
||||
In effect, it means that you can almost always read a few bytes beyond your current buffer---without
|
||||
allocating extra memory. However, tools such as valgrind or memory sanitizers will flag such behavior as unsafe.
|
||||
Nevertheless, you can still make sure of this capability in your code if you are an expert
|
||||
programmer and you are willing to silence sanitizer warnings. The following code provides
|
||||
a portable example.
|
||||
|
||||
|
||||
The conditional compilation checks for the `_MSC_VER` macro (indicating Microsoft Visual Studio)
|
||||
and includes platform-specific headers accordingly.
|
||||
The `page_size()` function determines the default size of a memory page in bytes on the system.
|
||||
On Windows (when `_WIN32` is defined), it uses `GetSystemInfo()` to retrieve system information and obtain the page size.
|
||||
On other platforms (non-Windows), it uses `sysconf(_SC_PAGESIZE)` to get the page size.
|
||||
The function returns the page size.
|
||||
The `need_allocation()` function checks whether the buffer (given by `buf`) plus the specified length (`len`) is near a page boundary.
|
||||
If the buffer extends beyond the current page when padded by `simdjson::SIMDJSON_PADDING`, it returns true, indicating that reallocation is needed.
|
||||
Otherwise, it returns false.
|
||||
The `get_padded_string_view()` creates a `padded_string_view` from the input buffer.
|
||||
If reallocation is needed (unlikely case), it allocates a new padded_string and assigns it to `jsonbuffer`.
|
||||
Otherwise (very likely), it creates a `padded_string_view` directly from the buffer.
|
||||
The `simdjson::SIMDJSON_PADDING` ensures that there is additional padding for parsing efficiency.
|
||||
The calling code just needs to provide `jsonbuffer` (an instance of `simdjson::padded_string`)
|
||||
and pass `get_padded_string_view(buf, len, jsonbuffer)` to `parser.iterate`. Most of the time,
|
||||
this code will not allocate new memory.
|
||||
|
||||
|
||||
```cpp
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <sysinfoapi.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include "simdjson.h"
|
||||
#include <cstdio>
|
||||
|
||||
// Returns the default size of the page in bytes on this system.
|
||||
long page_size() {
|
||||
#ifdef _WIN32
|
||||
SYSTEM_INFO sysInfo;
|
||||
GetSystemInfo(&sysInfo);
|
||||
long pagesize = sysInfo.dwPageSize;
|
||||
#else
|
||||
long pagesize = sysconf(_SC_PAGESIZE);
|
||||
#endif
|
||||
return pagesize;
|
||||
}
|
||||
|
||||
// Returns true if the buffer + len + simdjson::SIMDJSON_PADDING crosses the
|
||||
// page boundary.
|
||||
bool need_allocation(const char *buf, size_t len) {
|
||||
return ((reinterpret_cast<uintptr_t>(buf + len - 1) % page_size()) <
|
||||
simdjson::SIMDJSON_PADDING);
|
||||
}
|
||||
|
||||
simdjson::padded_string_view
|
||||
get_padded_string_view(const char *buf, size_t len,
|
||||
simdjson::padded_string &jsonbuffer) {
|
||||
if (need_allocation(buf, len)) { // unlikely case
|
||||
jsonbuffer = simdjson::padded_string(buf, len);
|
||||
return jsonbuffer;
|
||||
} else { // no reallcation needed (very likely)
|
||||
return simdjson::padded_string_view(buf, len,
|
||||
len + simdjson::SIMDJSON_PADDING);
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
printf("page_size: %ld\n", page_size());
|
||||
const char *jsonpoiner = R"(
|
||||
{
|
||||
"key": "value"
|
||||
}
|
||||
)";
|
||||
size_t len = strlen(jsonpoiner);
|
||||
simdjson::padded_string jsonbuffer; // only allocate if needed
|
||||
simdjson::ondemand::parser parser;
|
||||
simdjson::ondemand::document doc;
|
||||
simdjson::error_code error =
|
||||
parser.iterate(get_padded_string_view(jsonpoiner, len, jsonbuffer))
|
||||
.get(doc);
|
||||
if (error) {
|
||||
printf("error: %s\n", simdjson::error_message(error));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
std::string_view value;
|
||||
error = doc["key"].get_string().get(value);
|
||||
if (error) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
printf("Value: \"%.*s\"\n", (int)value.size(), value.data());
|
||||
if (value != "value") {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
```
|
||||
@@ -9,10 +9,10 @@
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#if _M_ARM64
|
||||
#if SIMDJSON_REGULAR_VISUAL_STUDIO && SIMDJSON_IS_ARM64
|
||||
// __umulh requires intrin.h
|
||||
#include <intrin.h>
|
||||
#endif // _M_ARM64
|
||||
#endif // SIMDJSON_REGULAR_VISUAL_STUDIO && SIMDJSON_IS_ARM64
|
||||
|
||||
namespace simdjson {
|
||||
namespace arm64 {
|
||||
@@ -32,13 +32,13 @@ static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars
|
||||
simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t value2) {
|
||||
internal::value128 answer;
|
||||
#if SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
|
||||
#ifdef _M_ARM64
|
||||
#if SIMDJSON_IS_ARM64
|
||||
// ARM64 has native support for 64-bit multiplications, no need to emultate
|
||||
answer.high = __umulh(value1, value2);
|
||||
answer.low = value1 * value2;
|
||||
#else
|
||||
answer.low = _umul128(value1, value2, &answer.high); // _umul128 not available on ARM64
|
||||
#endif // _M_ARM64
|
||||
#endif // SIMDJSON_IS_ARM64
|
||||
#else // SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
|
||||
__uint128_t r = (static_cast<__uint128_t>(value1)) * value2;
|
||||
answer.low = uint64_t(r);
|
||||
|
||||
@@ -134,7 +134,7 @@ namespace {
|
||||
tmp = vpaddq_u8(tmp, tmp);
|
||||
return vgetq_lane_u16(vreinterpretq_u16_u8(tmp), 0);
|
||||
}
|
||||
simdjson_inline bool any() const { return vmaxvq_u8(*this) != 0; }
|
||||
simdjson_inline bool any() const { return vmaxvq_u32(vreinterpretq_u32_u8(*this)) != 0; }
|
||||
};
|
||||
|
||||
// Unsigned bytes
|
||||
|
||||
@@ -20,6 +20,10 @@
|
||||
#include "simdjson/ppc64.h"
|
||||
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(westmere)
|
||||
#include "simdjson/westmere.h"
|
||||
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(lsx)
|
||||
#include "simdjson/lsx.h"
|
||||
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(lasx)
|
||||
#include "simdjson/lasx.h"
|
||||
#else
|
||||
#error Unknown SIMDJSON_BUILTIN_IMPLEMENTATION
|
||||
#endif
|
||||
|
||||
@@ -17,6 +17,10 @@ namespace simdjson {
|
||||
namespace ppc64 {}
|
||||
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(westmere)
|
||||
namespace westmere {}
|
||||
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(lsx)
|
||||
namespace lsx {}
|
||||
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(lasx)
|
||||
namespace lasx {}
|
||||
#else
|
||||
#error Unknown SIMDJSON_BUILTIN_IMPLEMENTATION
|
||||
#endif
|
||||
|
||||
@@ -19,6 +19,10 @@
|
||||
#include "simdjson/ppc64/implementation.h"
|
||||
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(westmere)
|
||||
#include "simdjson/westmere/implementation.h"
|
||||
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(lsx)
|
||||
#include "simdjson/lsx/implementation.h"
|
||||
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(lasx)
|
||||
#include "simdjson/lasx/implementation.h"
|
||||
#else
|
||||
#error Unknown SIMDJSON_BUILTIN_IMPLEMENTATION
|
||||
#endif
|
||||
|
||||
@@ -20,6 +20,10 @@
|
||||
#include "simdjson/ppc64/ondemand.h"
|
||||
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(westmere)
|
||||
#include "simdjson/westmere/ondemand.h"
|
||||
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(lsx)
|
||||
#include "simdjson/lsx/ondemand.h"
|
||||
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(lasx)
|
||||
#include "simdjson/lasx/ondemand.h"
|
||||
#else
|
||||
#error Unknown SIMDJSON_BUILTIN_IMPLEMENTATION
|
||||
#endif
|
||||
|
||||
@@ -223,7 +223,11 @@ simdjson_inline std::string_view document_stream::iterator::source() const noexc
|
||||
return std::string_view(start, next_doc_index - current_index() + 1);
|
||||
} else {
|
||||
size_t next_doc_index = stream->batch_start + stream->parser->implementation->structural_indexes[stream->parser->implementation->next_structural_index];
|
||||
return std::string_view(reinterpret_cast<const char*>(stream->buf) + current_index(), next_doc_index - current_index() - 1);
|
||||
size_t svlen = next_doc_index - current_index();
|
||||
while(svlen > 1 && (std::isspace(start[svlen-1]) || start[svlen-1] == '\0')) {
|
||||
svlen--;
|
||||
}
|
||||
return std::string_view(start, svlen);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -375,6 +375,23 @@ inline simdjson_result<element> element::operator[](const char *key) const noexc
|
||||
return at_key(key);
|
||||
}
|
||||
|
||||
inline bool is_pointer_well_formed(std::string_view json_pointer) noexcept {
|
||||
if (simdjson_unlikely(json_pointer[0] != '/')) {
|
||||
return false;
|
||||
}
|
||||
size_t escape = json_pointer.find('~');
|
||||
if (escape == std::string_view::npos) {
|
||||
return true;
|
||||
}
|
||||
if (escape == json_pointer.size() - 1) {
|
||||
return false;
|
||||
}
|
||||
if (json_pointer[escape + 1] != '0' && json_pointer[escape + 1] != '1') {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline simdjson_result<element> element::at_pointer(std::string_view json_pointer) const noexcept {
|
||||
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
|
||||
switch (tape.tape_ref_type()) {
|
||||
@@ -383,7 +400,10 @@ inline simdjson_result<element> element::at_pointer(std::string_view json_pointe
|
||||
case internal::tape_type::START_ARRAY:
|
||||
return array(tape).at_pointer(json_pointer);
|
||||
default: {
|
||||
if(!json_pointer.empty()) { // a non-empty string is invalid on an atom
|
||||
if (!json_pointer.empty()) { // a non-empty string can be invalid, or accessing a primitive (issue 2154)
|
||||
if (is_pointer_well_formed(json_pointer)) {
|
||||
return NO_SUCH_FIELD;
|
||||
}
|
||||
return INVALID_JSON_POINTER;
|
||||
}
|
||||
// an empty string means that we return the current node
|
||||
|
||||
@@ -191,7 +191,7 @@ simdjson_inline size_t parser::capacity() const noexcept {
|
||||
simdjson_inline size_t parser::max_capacity() const noexcept {
|
||||
return _max_capacity;
|
||||
}
|
||||
simdjson_inline size_t parser::max_depth() const noexcept {
|
||||
simdjson_pure simdjson_inline size_t parser::max_depth() const noexcept {
|
||||
return implementation ? implementation->max_depth() : DEFAULT_MAX_DEPTH;
|
||||
}
|
||||
|
||||
|
||||
@@ -83,6 +83,15 @@ public:
|
||||
* If the parser's current capacity is less than the file length, it will allocate enough capacity
|
||||
* to handle it (up to max_capacity).
|
||||
*
|
||||
* ## Windows and Unicode
|
||||
*
|
||||
* Windows users who need to read files with non-ANSI characters in the
|
||||
* name should set their code page to UTF-8 (65001) before calling this
|
||||
* function. This should be the default with Windows 11 and better.
|
||||
* Further, they may use the AreFileApisANSI function to determine whether
|
||||
* the filename is interpreted using the ANSI or the system default OEM
|
||||
* codepage, and they may call SetFileApisToOEM accordingly.
|
||||
*
|
||||
* @param path The path to load.
|
||||
* @return The document, or an error:
|
||||
* - IO_ERROR if there was an error opening or reading the file.
|
||||
@@ -518,7 +527,7 @@ public:
|
||||
*
|
||||
* @return Maximum depth, in bytes.
|
||||
*/
|
||||
simdjson_inline size_t max_depth() const noexcept;
|
||||
simdjson_pure simdjson_inline size_t max_depth() const noexcept;
|
||||
|
||||
/**
|
||||
* Set max_capacity. This is the largest document this parser can automatically support.
|
||||
|
||||
@@ -57,15 +57,15 @@ public:
|
||||
simdjson_inline void one_char(char c);
|
||||
|
||||
simdjson_inline void call_print_newline() {
|
||||
this->print_newline();
|
||||
static_cast<formatter*>(this)->print_newline();
|
||||
}
|
||||
|
||||
simdjson_inline void call_print_indents(size_t depth) {
|
||||
this->print_indents(depth);
|
||||
static_cast<formatter*>(this)->print_indents(depth);
|
||||
}
|
||||
|
||||
simdjson_inline void call_print_space() {
|
||||
this->print_space();
|
||||
static_cast<formatter*>(this)->print_space();
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
@@ -39,7 +39,7 @@ enum error_code {
|
||||
INDEX_OUT_OF_BOUNDS, ///< JSON array index too large
|
||||
NO_SUCH_FIELD, ///< JSON field not found in object
|
||||
IO_ERROR, ///< Error reading a file
|
||||
INVALID_JSON_POINTER, ///< Invalid JSON pointer reference
|
||||
INVALID_JSON_POINTER, ///< Invalid JSON pointer syntax
|
||||
INVALID_URI_FRAGMENT, ///< Invalid URI fragment
|
||||
UNEXPECTED_ERROR, ///< indicative of a bug in simdjson
|
||||
PARSER_IN_USE, ///< parser is already in use.
|
||||
|
||||
@@ -56,13 +56,13 @@ static simdjson_inline uint64_t _umul128(uint64_t ab, uint64_t cd, uint64_t *hi)
|
||||
simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t value2) {
|
||||
internal::value128 answer;
|
||||
#if SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
|
||||
#ifdef _M_ARM64
|
||||
#if SIMDJSON_IS_ARM64
|
||||
// ARM64 has native support for 64-bit multiplications, no need to emultate
|
||||
answer.high = __umulh(value1, value2);
|
||||
answer.low = value1 * value2;
|
||||
#else
|
||||
answer.low = _umul128(value1, value2, &answer.high); // _umul128 not available on ARM64
|
||||
#endif // _M_ARM64
|
||||
#endif // SIMDJSON_IS_ARM64
|
||||
#else // SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
|
||||
__uint128_t r = (static_cast<__uint128_t>(value1)) * value2;
|
||||
answer.low = uint64_t(r);
|
||||
|
||||
@@ -17,6 +17,10 @@
|
||||
#include "simdjson/arm64/begin.h"
|
||||
#elif SIMDJSON_IMPLEMENTATION_PPC64
|
||||
#include "simdjson/ppc64/begin.h"
|
||||
#elif SIMDJSON_IMPLEMENTATION_LSX
|
||||
#include "simdjson/lsx/begin.h"
|
||||
#elif SIMDJSON_IMPLEMENTATION_LASX
|
||||
#include "simdjson/lasx/begin.h"
|
||||
#elif SIMDJSON_IMPLEMENTATION_FALLBACK
|
||||
#include "simdjson/fallback/begin.h"
|
||||
#else
|
||||
|
||||
@@ -342,9 +342,18 @@ simdjson_inline std::string_view document_stream::iterator::source() const noexc
|
||||
depth--;
|
||||
break;
|
||||
default: // Scalar value document
|
||||
// TODO: Remove any trailing whitespaces
|
||||
// TODO: We could remove trailing whitespaces
|
||||
// This returns a string spanning from start of value to the beginning of the next document (excluded)
|
||||
return std::string_view(reinterpret_cast<const char*>(stream->buf) + current_index(), stream->parser->implementation->structural_indexes[++cur_struct_index] - current_index() - 1);
|
||||
{
|
||||
auto next_index = stream->parser->implementation->structural_indexes[++cur_struct_index];
|
||||
// normally the length would be next_index - current_index() - 1, except for the last document
|
||||
size_t svlen = next_index - current_index();
|
||||
const char *start = reinterpret_cast<const char*>(stream->buf) + current_index();
|
||||
while(svlen > 1 && (std::isspace(start[svlen-1]) || start[svlen-1] == '\0')) {
|
||||
svlen--;
|
||||
}
|
||||
return std::string_view(start, svlen);
|
||||
}
|
||||
}
|
||||
cur_struct_index++;
|
||||
}
|
||||
|
||||
@@ -49,6 +49,13 @@ simdjson_inline std::string_view field::key_raw_json_token() const noexcept {
|
||||
return std::string_view(reinterpret_cast<const char*>(first.buf-1), second.iter._json_iter->token.peek(-1) - first.buf + 1);
|
||||
}
|
||||
|
||||
simdjson_inline std::string_view field::escaped_key() const noexcept {
|
||||
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us.
|
||||
auto end_quote = second.iter._json_iter->token.peek(-1);
|
||||
while(*end_quote != '"') end_quote--;
|
||||
return std::string_view(reinterpret_cast<const char*>(first.buf), end_quote - first.buf);
|
||||
}
|
||||
|
||||
simdjson_inline value &field::value() & noexcept {
|
||||
return second;
|
||||
}
|
||||
@@ -88,6 +95,11 @@ simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLE
|
||||
return first.key_raw_json_token();
|
||||
}
|
||||
|
||||
simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::field>::escaped_key() noexcept {
|
||||
if (error()) { return error(); }
|
||||
return first.escaped_key();
|
||||
}
|
||||
|
||||
simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::field>::unescaped_key(bool allow_replacement) noexcept {
|
||||
if (error()) { return error(); }
|
||||
return first.unescaped_key(allow_replacement);
|
||||
|
||||
@@ -47,6 +47,12 @@ public:
|
||||
* some spaces after the last quote.
|
||||
*/
|
||||
simdjson_inline std::string_view key_raw_json_token() const noexcept;
|
||||
/**
|
||||
* Get the key as a string_view. This does not include the quotes and
|
||||
* the string is unprocessed key so it may contain escape characters
|
||||
* (e.g., \uXXXX or \n). Use unescaped_key() to get the unescaped key.
|
||||
*/
|
||||
simdjson_inline std::string_view escaped_key() const noexcept;
|
||||
/**
|
||||
* Get the field value.
|
||||
*/
|
||||
@@ -80,6 +86,7 @@ public:
|
||||
simdjson_inline simdjson_result<std::string_view> unescaped_key(bool allow_replacement = false) noexcept;
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::raw_json_string> key() noexcept;
|
||||
simdjson_inline simdjson_result<std::string_view> key_raw_json_token() noexcept;
|
||||
simdjson_inline simdjson_result<std::string_view> escaped_key() noexcept;
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> value() noexcept;
|
||||
};
|
||||
|
||||
|
||||
@@ -54,6 +54,23 @@ simdjson_inline json_iterator::json_iterator(const uint8_t *buf, ondemand::parse
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
|
||||
simdjson_inline json_iterator::json_iterator(const uint8_t *buf, ondemand::parser *_parser, bool streaming) noexcept
|
||||
: token(buf, &_parser->implementation->structural_indexes[0]),
|
||||
parser{_parser},
|
||||
_string_buf_loc{parser->string_buf.get()},
|
||||
_depth{1},
|
||||
_root{parser->implementation->structural_indexes.get()},
|
||||
_streaming{streaming}
|
||||
|
||||
{
|
||||
logger::log_headers();
|
||||
#if SIMDJSON_CHECK_EOF
|
||||
assert_more_tokens();
|
||||
#endif
|
||||
}
|
||||
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
|
||||
|
||||
inline void json_iterator::rewind() noexcept {
|
||||
token.set_position( root_position() );
|
||||
logger::log_headers(); // We start again
|
||||
|
||||
@@ -293,6 +293,9 @@ public:
|
||||
inline bool balanced() const noexcept;
|
||||
protected:
|
||||
simdjson_inline json_iterator(const uint8_t *buf, ondemand::parser *parser) noexcept;
|
||||
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
|
||||
simdjson_inline json_iterator(const uint8_t *buf, ondemand::parser *parser, bool streaming) noexcept;
|
||||
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
|
||||
/// The last token before the end
|
||||
simdjson_inline token_position last_position() const noexcept;
|
||||
/// The token *at* the end. This points at gibberish and should only be used for comparison.
|
||||
|
||||
@@ -56,6 +56,8 @@ public:
|
||||
* key a single time. Doing object["mykey"].to_string() and then again object["mykey"].to_string()
|
||||
* is an error.
|
||||
*
|
||||
* If you expect to have keys with escape characters, please review our documentation.
|
||||
*
|
||||
* @param key The key to look up.
|
||||
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
|
||||
*/
|
||||
@@ -93,6 +95,8 @@ public:
|
||||
* You are expected to access keys only once. You should access the value corresponding to a key
|
||||
* a single time. Doing object["mykey"].to_string() and then again object["mykey"].to_string() is an error.
|
||||
*
|
||||
* If you expect to have keys with escape characters, please review our documentation.
|
||||
*
|
||||
* @param key The key to look up.
|
||||
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
|
||||
*/
|
||||
|
||||
@@ -58,6 +58,27 @@ simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(p
|
||||
return document::start({ reinterpret_cast<const uint8_t *>(json.data()), this });
|
||||
}
|
||||
|
||||
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
|
||||
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate_allow_incomplete_json(padded_string_view json) & noexcept {
|
||||
if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; }
|
||||
|
||||
json.remove_utf8_bom();
|
||||
|
||||
// Allocate if needed
|
||||
if (capacity() < json.length() || !string_buf) {
|
||||
SIMDJSON_TRY( allocate(json.length(), max_depth()) );
|
||||
}
|
||||
|
||||
// Run stage 1.
|
||||
const simdjson::error_code err = implementation->stage1(reinterpret_cast<const uint8_t *>(json.data()), json.length(), stage1_mode::regular);
|
||||
if (err) {
|
||||
if (err != UNCLOSED_STRING)
|
||||
return err;
|
||||
}
|
||||
return document::start({ reinterpret_cast<const uint8_t *>(json.data()), this, true });
|
||||
}
|
||||
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
|
||||
|
||||
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const char *json, size_t len, size_t allocated) & noexcept {
|
||||
return iterate(padded_string_view(json, len, allocated));
|
||||
}
|
||||
@@ -129,13 +150,13 @@ inline simdjson_result<document_stream> parser::iterate_many(const padded_string
|
||||
return iterate_many(s.data(), s.length(), batch_size, allow_comma_separated);
|
||||
}
|
||||
|
||||
simdjson_inline size_t parser::capacity() const noexcept {
|
||||
simdjson_pure simdjson_inline size_t parser::capacity() const noexcept {
|
||||
return _capacity;
|
||||
}
|
||||
simdjson_inline size_t parser::max_capacity() const noexcept {
|
||||
simdjson_pure simdjson_inline size_t parser::max_capacity() const noexcept {
|
||||
return _max_capacity;
|
||||
}
|
||||
simdjson_inline size_t parser::max_depth() const noexcept {
|
||||
simdjson_pure simdjson_inline size_t parser::max_depth() const noexcept {
|
||||
return _max_depth;
|
||||
}
|
||||
|
||||
|
||||
@@ -98,6 +98,9 @@ public:
|
||||
* - UNCLOSED_STRING if there is an unclosed string in the document.
|
||||
*/
|
||||
simdjson_warn_unused simdjson_result<document> iterate(padded_string_view json) & noexcept;
|
||||
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
|
||||
simdjson_warn_unused simdjson_result<document> iterate_allow_incomplete_json(padded_string_view json) & noexcept;
|
||||
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
|
||||
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
|
||||
simdjson_warn_unused simdjson_result<document> iterate(const char *json, size_t len, size_t capacity) & noexcept;
|
||||
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
|
||||
@@ -242,9 +245,9 @@ public:
|
||||
simdjson_result<document_stream> iterate_many(const char *buf, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept = delete;
|
||||
|
||||
/** The capacity of this parser (the largest document it can process). */
|
||||
simdjson_inline size_t capacity() const noexcept;
|
||||
simdjson_pure simdjson_inline size_t capacity() const noexcept;
|
||||
/** The maximum capacity of this parser (the largest document it is allowed to process). */
|
||||
simdjson_inline size_t max_capacity() const noexcept;
|
||||
simdjson_pure simdjson_inline size_t max_capacity() const noexcept;
|
||||
simdjson_inline void set_max_capacity(size_t max_capacity) noexcept;
|
||||
/**
|
||||
* The maximum depth of this parser (the most deeply nested objects and arrays it can process).
|
||||
@@ -252,7 +255,7 @@ public:
|
||||
* The document's instance current_depth() method should be used to monitor the parsing
|
||||
* depth and limit it if desired.
|
||||
*/
|
||||
simdjson_inline size_t max_depth() const noexcept;
|
||||
simdjson_pure simdjson_inline size_t max_depth() const noexcept;
|
||||
|
||||
/**
|
||||
* Ensure this parser has enough memory to process JSON documents up to `capacity` bytes in length
|
||||
|
||||
@@ -239,6 +239,26 @@ simdjson_inline int32_t value::current_depth() const noexcept{
|
||||
return iter.json_iter().depth();
|
||||
}
|
||||
|
||||
inline bool is_pointer_well_formed(std::string_view json_pointer) noexcept {
|
||||
if (simdjson_unlikely(json_pointer.empty())) { // can't be
|
||||
return false;
|
||||
}
|
||||
if (simdjson_unlikely(json_pointer[0] != '/')) {
|
||||
return false;
|
||||
}
|
||||
size_t escape = json_pointer.find('~');
|
||||
if (escape == std::string_view::npos) {
|
||||
return true;
|
||||
}
|
||||
if (escape == json_pointer.size() - 1) {
|
||||
return false;
|
||||
}
|
||||
if (json_pointer[escape + 1] != '0' && json_pointer[escape + 1] != '1') {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
simdjson_inline simdjson_result<value> value::at_pointer(std::string_view json_pointer) noexcept {
|
||||
json_type t;
|
||||
SIMDJSON_TRY(type().get(t));
|
||||
@@ -249,6 +269,10 @@ simdjson_inline simdjson_result<value> value::at_pointer(std::string_view json_p
|
||||
case json_type::object:
|
||||
return (*this).get_object().at_pointer(json_pointer);
|
||||
default:
|
||||
// a non-empty string can be invalid, or accessing a primitive (issue 2154)
|
||||
if (is_pointer_well_formed(json_pointer)) {
|
||||
return NO_SUCH_FIELD;
|
||||
}
|
||||
return INVALID_JSON_POINTER;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,13 +37,13 @@ static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars
|
||||
simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t value2) {
|
||||
internal::value128 answer;
|
||||
#if SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
|
||||
#ifdef _M_ARM64
|
||||
#if SIMDJSON_IS_ARM64
|
||||
// ARM64 has native support for 64-bit multiplications, no need to emultate
|
||||
answer.high = __umulh(value1, value2);
|
||||
answer.low = value1 * value2;
|
||||
#else
|
||||
answer.low = _umul128(value1, value2, &answer.high); // _umul128 not available on ARM64
|
||||
#endif // _M_ARM64
|
||||
#endif // SIMDJSON_IS_ARM64
|
||||
#else // SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
|
||||
__uint128_t r = (static_cast<__uint128_t>(value1)) * value2;
|
||||
answer.low = uint64_t(r);
|
||||
|
||||
@@ -65,10 +65,10 @@ namespace simd {
|
||||
struct simd8<bool>: base8<bool> {
|
||||
static simdjson_inline simd8<bool> splat(bool _value) { return _mm256_set1_epi8(uint8_t(-(!!_value))); }
|
||||
|
||||
simdjson_inline simd8<bool>() : base8() {}
|
||||
simdjson_inline simd8<bool>(const __m256i _value) : base8<bool>(_value) {}
|
||||
simdjson_inline simd8() : base8() {}
|
||||
simdjson_inline simd8(const __m256i _value) : base8<bool>(_value) {}
|
||||
// Splat constructor
|
||||
simdjson_inline simd8<bool>(bool _value) : base8<bool>(splat(_value)) {}
|
||||
simdjson_inline simd8(bool _value) : base8<bool>(splat(_value)) {}
|
||||
|
||||
simdjson_inline int to_bitmask() const { return _mm256_movemask_epi8(*this); }
|
||||
simdjson_inline bool any() const { return !_mm256_testz_si256(*this, *this); }
|
||||
|
||||
@@ -33,13 +33,13 @@ static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars
|
||||
simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t value2) {
|
||||
internal::value128 answer;
|
||||
#if SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
|
||||
#ifdef _M_ARM64
|
||||
#if SIMDJSON_IS_ARM64
|
||||
// ARM64 has native support for 64-bit multiplications, no need to emultate
|
||||
answer.high = __umulh(value1, value2);
|
||||
answer.low = value1 * value2;
|
||||
#else
|
||||
answer.low = _umul128(value1, value2, &answer.high); // _umul128 not available on ARM64
|
||||
#endif // _M_ARM64
|
||||
#endif // SIMDJSON_IS_ARM64
|
||||
#else // SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
|
||||
__uint128_t r = (static_cast<__uint128_t>(value1)) * value2;
|
||||
answer.low = uint64_t(r);
|
||||
|
||||
@@ -93,10 +93,10 @@ namespace simd {
|
||||
struct simd8<bool>: base8<bool> {
|
||||
static simdjson_inline simd8<bool> splat(bool _value) { return _mm512_set1_epi8(uint8_t(-(!!_value))); }
|
||||
|
||||
simdjson_inline simd8<bool>() : base8() {}
|
||||
simdjson_inline simd8<bool>(const __m512i _value) : base8<bool>(_value) {}
|
||||
simdjson_inline simd8() : base8() {}
|
||||
simdjson_inline simd8(const __m512i _value) : base8<bool>(_value) {}
|
||||
// Splat constructor
|
||||
simdjson_inline simd8<bool>(bool _value) : base8<bool>(splat(_value)) {}
|
||||
simdjson_inline simd8(bool _value) : base8<bool>(splat(_value)) {}
|
||||
simdjson_inline bool any() const { return !!_mm512_test_epi8_mask (*this, *this); }
|
||||
simdjson_inline simd8<bool> operator~() const { return *this ^ true; }
|
||||
};
|
||||
|
||||
@@ -53,7 +53,7 @@ public:
|
||||
*
|
||||
* @return the name of the implementation, e.g. "haswell", "westmere", "arm64".
|
||||
*/
|
||||
virtual const std::string &name() const { return _name; }
|
||||
virtual std::string name() const { return std::string(_name); }
|
||||
|
||||
/**
|
||||
* The description of this implementation.
|
||||
@@ -63,7 +63,7 @@ public:
|
||||
*
|
||||
* @return the description of the implementation, e.g. "Intel/AMD AVX2", "Intel/AMD SSE4.2", "ARM NEON".
|
||||
*/
|
||||
virtual const std::string &description() const { return _description; }
|
||||
virtual std::string description() const { return std::string(_description); }
|
||||
|
||||
/**
|
||||
* The instruction sets this implementation is compiled against
|
||||
@@ -139,18 +139,19 @@ protected:
|
||||
_required_instruction_sets(required_instruction_sets)
|
||||
{
|
||||
}
|
||||
virtual ~implementation()=default;
|
||||
protected:
|
||||
~implementation() = default;
|
||||
|
||||
private:
|
||||
/**
|
||||
* The name of this implementation.
|
||||
*/
|
||||
const std::string _name;
|
||||
std::string_view _name;
|
||||
|
||||
/**
|
||||
* The description of this implementation.
|
||||
*/
|
||||
const std::string _description;
|
||||
std::string_view _description;
|
||||
|
||||
/**
|
||||
* Instruction sets required for this implementation.
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#define SIMDJSON_IMPLEMENTATION_ID_icelake 4
|
||||
#define SIMDJSON_IMPLEMENTATION_ID_ppc64 5
|
||||
#define SIMDJSON_IMPLEMENTATION_ID_westmere 6
|
||||
#define SIMDJSON_IMPLEMENTATION_ID_lsx 7
|
||||
#define SIMDJSON_IMPLEMENTATION_ID_lasx 8
|
||||
|
||||
#define SIMDJSON_IMPLEMENTATION_ID_FOR(IMPL) SIMDJSON_CAT(SIMDJSON_IMPLEMENTATION_ID_, IMPL)
|
||||
#define SIMDJSON_IMPLEMENTATION_ID SIMDJSON_IMPLEMENTATION_ID_FOR(SIMDJSON_IMPLEMENTATION)
|
||||
@@ -24,7 +26,11 @@
|
||||
#ifndef SIMDJSON_IMPLEMENTATION_ARM64
|
||||
#define SIMDJSON_IMPLEMENTATION_ARM64 (SIMDJSON_IS_ARM64)
|
||||
#endif
|
||||
#define SIMDJSON_CAN_ALWAYS_RUN_ARM64 SIMDJSON_IMPLEMENTATION_ARM64 && SIMDJSON_IS_ARM64
|
||||
#if SIMDJSON_IMPLEMENTATION_ARM64 && SIMDJSON_IS_ARM64
|
||||
#define SIMDJSON_CAN_ALWAYS_RUN_ARM64 1
|
||||
#else
|
||||
#define SIMDJSON_CAN_ALWAYS_RUN_ARM64 0
|
||||
#endif
|
||||
|
||||
// Default Icelake to on if this is x86-64. Even if we're not compiled for it, it could be selected
|
||||
// at runtime.
|
||||
@@ -35,9 +41,20 @@
|
||||
#ifdef _MSC_VER
|
||||
// To see why (__BMI__) && (__PCLMUL__) && (__LZCNT__) are not part of this next line, see
|
||||
// https://github.com/simdjson/simdjson/issues/1247
|
||||
#define SIMDJSON_CAN_ALWAYS_RUN_ICELAKE ((SIMDJSON_IMPLEMENTATION_ICELAKE) && (__AVX2__) && (__AVX512F__) && (__AVX512DQ__) && (__AVX512CD__) && (__AVX512BW__) && (__AVX512VL__) && (__AVX512VBMI2__))
|
||||
#if ((SIMDJSON_IMPLEMENTATION_ICELAKE) && (__AVX2__) && (__AVX512F__) && (__AVX512DQ__) && (__AVX512CD__) && (__AVX512BW__) && (__AVX512VL__) && (__AVX512VBMI2__))
|
||||
#define SIMDJSON_CAN_ALWAYS_RUN_ICELAKE 1
|
||||
#else
|
||||
#define SIMDJSON_CAN_ALWAYS_RUN_ICELAKE ((SIMDJSON_IMPLEMENTATION_ICELAKE) && (__AVX2__) && (__BMI__) && (__PCLMUL__) && (__LZCNT__) && (__AVX512F__) && (__AVX512DQ__) && (__AVX512CD__) && (__AVX512BW__) && (__AVX512VL__) && (__AVX512VBMI2__))
|
||||
#define SIMDJSON_CAN_ALWAYS_RUN_ICELAKE 0
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#if ((SIMDJSON_IMPLEMENTATION_ICELAKE) && (__AVX2__) && (__BMI__) && (__PCLMUL__) && (__LZCNT__) && (__AVX512F__) && (__AVX512DQ__) && (__AVX512CD__) && (__AVX512BW__) && (__AVX512VL__) && (__AVX512VBMI2__))
|
||||
#define SIMDJSON_CAN_ALWAYS_RUN_ICELAKE 1
|
||||
#else
|
||||
#define SIMDJSON_CAN_ALWAYS_RUN_ICELAKE 0
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
// Default Haswell to on if this is x86-64. Even if we're not compiled for it, it could be selected
|
||||
@@ -53,9 +70,20 @@
|
||||
#ifdef _MSC_VER
|
||||
// To see why (__BMI__) && (__PCLMUL__) && (__LZCNT__) are not part of this next line, see
|
||||
// https://github.com/simdjson/simdjson/issues/1247
|
||||
#define SIMDJSON_CAN_ALWAYS_RUN_HASWELL ((SIMDJSON_IMPLEMENTATION_HASWELL) && (SIMDJSON_IS_X86_64) && (__AVX2__))
|
||||
#if ((SIMDJSON_IMPLEMENTATION_HASWELL) && (SIMDJSON_IS_X86_64) && (__AVX2__))
|
||||
#define SIMDJSON_CAN_ALWAYS_RUN_HASWELL 1
|
||||
#else
|
||||
#define SIMDJSON_CAN_ALWAYS_RUN_HASWELL ((SIMDJSON_IMPLEMENTATION_HASWELL) && (SIMDJSON_IS_X86_64) && (__AVX2__) && (__BMI__) && (__PCLMUL__) && (__LZCNT__))
|
||||
#define SIMDJSON_CAN_ALWAYS_RUN_HASWELL 0
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#if ((SIMDJSON_IMPLEMENTATION_HASWELL) && (SIMDJSON_IS_X86_64) && (__AVX2__) && (__BMI__) && (__PCLMUL__) && (__LZCNT__))
|
||||
#define SIMDJSON_CAN_ALWAYS_RUN_HASWELL 1
|
||||
#else
|
||||
#define SIMDJSON_CAN_ALWAYS_RUN_HASWELL 0
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
// Default Westmere to on if this is x86-64.
|
||||
@@ -67,16 +95,40 @@
|
||||
#define SIMDJSON_IMPLEMENTATION_WESTMERE SIMDJSON_IS_X86_64
|
||||
#endif
|
||||
#endif
|
||||
#define SIMDJSON_CAN_ALWAYS_RUN_WESTMERE (SIMDJSON_IMPLEMENTATION_WESTMERE && SIMDJSON_IS_X86_64 && __SSE4_2__ && __PCLMUL__)
|
||||
|
||||
#if (SIMDJSON_IMPLEMENTATION_WESTMERE && SIMDJSON_IS_X86_64 && __SSE4_2__ && __PCLMUL__)
|
||||
#define SIMDJSON_CAN_ALWAYS_RUN_WESTMERE 1
|
||||
#else
|
||||
#define SIMDJSON_CAN_ALWAYS_RUN_WESTMERE 0
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef SIMDJSON_IMPLEMENTATION_PPC64
|
||||
#define SIMDJSON_IMPLEMENTATION_PPC64 (SIMDJSON_IS_PPC64 && SIMDJSON_IS_PPC64_VMX)
|
||||
#endif
|
||||
#define SIMDJSON_CAN_ALWAYS_RUN_PPC64 SIMDJSON_IMPLEMENTATION_PPC64 && SIMDJSON_IS_PPC64 && SIMDJSON_IS_PPC64_VMX
|
||||
#if SIMDJSON_IMPLEMENTATION_PPC64 && SIMDJSON_IS_PPC64 && SIMDJSON_IS_PPC64_VMX
|
||||
#define SIMDJSON_CAN_ALWAYS_RUN_PPC64 1
|
||||
#else
|
||||
#define SIMDJSON_CAN_ALWAYS_RUN_PPC64 0
|
||||
#endif
|
||||
|
||||
#ifndef SIMDJSON_IMPLEMENTATION_LASX
|
||||
#define SIMDJSON_IMPLEMENTATION_LASX (SIMDJSON_IS_LOONGARCH64 && __loongarch_asx)
|
||||
#endif
|
||||
#define SIMDJSON_CAN_ALWAYS_RUN_LASX (SIMDJSON_IMPLEMENTATION_LASX)
|
||||
|
||||
#ifndef SIMDJSON_IMPLEMENTATION_LSX
|
||||
#if SIMDJSON_CAN_ALWAYS_RUN_LASX
|
||||
#define SIMDJSON_IMPLEMENTATION_LSX 0
|
||||
#else
|
||||
#define SIMDJSON_IMPLEMENTATION_LSX (SIMDJSON_IS_LOONGARCH64 && __loongarch_sx)
|
||||
#endif
|
||||
#endif
|
||||
#define SIMDJSON_CAN_ALWAYS_RUN_LSX (SIMDJSON_IMPLEMENTATION_LSX)
|
||||
|
||||
// Default Fallback to on unless a builtin implementation has already been selected.
|
||||
#ifndef SIMDJSON_IMPLEMENTATION_FALLBACK
|
||||
#if SIMDJSON_CAN_ALWAYS_RUN_ARM64 || SIMDJSON_CAN_ALWAYS_RUN_ICELAKE || SIMDJSON_CAN_ALWAYS_RUN_HASWELL || SIMDJSON_CAN_ALWAYS_RUN_WESTMERE || SIMDJSON_CAN_ALWAYS_RUN_PPC64
|
||||
#if SIMDJSON_CAN_ALWAYS_RUN_ARM64 || SIMDJSON_CAN_ALWAYS_RUN_ICELAKE || SIMDJSON_CAN_ALWAYS_RUN_HASWELL || SIMDJSON_CAN_ALWAYS_RUN_WESTMERE || SIMDJSON_CAN_ALWAYS_RUN_PPC64 || SIMDJSON_CAN_ALWAYS_RUN_LSX || SIMDJSON_CAN_ALWAYS_RUN_LASX
|
||||
// if anything at all except fallback can always run, then disable fallback.
|
||||
#define SIMDJSON_IMPLEMENTATION_FALLBACK 0
|
||||
#else
|
||||
@@ -98,6 +150,10 @@
|
||||
#define SIMDJSON_BUILTIN_IMPLEMENTATION arm64
|
||||
#elif SIMDJSON_CAN_ALWAYS_RUN_PPC64
|
||||
#define SIMDJSON_BUILTIN_IMPLEMENTATION ppc64
|
||||
#elif SIMDJSON_CAN_ALWAYS_RUN_LSX
|
||||
#define SIMDJSON_BUILTIN_IMPLEMENTATION lsx
|
||||
#elif SIMDJSON_CAN_ALWAYS_RUN_LASX
|
||||
#define SIMDJSON_BUILTIN_IMPLEMENTATION lasx
|
||||
#elif SIMDJSON_CAN_ALWAYS_RUN_FALLBACK
|
||||
#define SIMDJSON_BUILTIN_IMPLEMENTATION fallback
|
||||
#else
|
||||
|
||||
@@ -177,14 +177,14 @@ public:
|
||||
*
|
||||
* @return Current capacity, in bytes.
|
||||
*/
|
||||
simdjson_inline size_t capacity() const noexcept;
|
||||
simdjson_pure simdjson_inline size_t capacity() const noexcept;
|
||||
|
||||
/**
|
||||
* The maximum level of nested object and arrays supported by this parser.
|
||||
*
|
||||
* @return Maximum depth, in bytes.
|
||||
*/
|
||||
simdjson_inline size_t max_depth() const noexcept;
|
||||
simdjson_pure simdjson_inline size_t max_depth() const noexcept;
|
||||
|
||||
/**
|
||||
* Ensure this parser has enough memory to process JSON documents up to `capacity` bytes in length
|
||||
@@ -225,11 +225,11 @@ simdjson_inline dom_parser_implementation::dom_parser_implementation() noexcept
|
||||
simdjson_inline dom_parser_implementation::dom_parser_implementation(dom_parser_implementation &&other) noexcept = default;
|
||||
simdjson_inline dom_parser_implementation &dom_parser_implementation::operator=(dom_parser_implementation &&other) noexcept = default;
|
||||
|
||||
simdjson_inline size_t dom_parser_implementation::capacity() const noexcept {
|
||||
simdjson_pure simdjson_inline size_t dom_parser_implementation::capacity() const noexcept {
|
||||
return _capacity;
|
||||
}
|
||||
|
||||
simdjson_inline size_t dom_parser_implementation::max_depth() const noexcept {
|
||||
simdjson_pure simdjson_inline size_t dom_parser_implementation::max_depth() const noexcept {
|
||||
return _max_depth;
|
||||
}
|
||||
|
||||
|
||||
@@ -66,7 +66,9 @@ enum instruction_set {
|
||||
AVX512CD = 0x2000,
|
||||
AVX512BW = 0x4000,
|
||||
AVX512VL = 0x8000,
|
||||
AVX512VBMI2 = 0x10000
|
||||
AVX512VBMI2 = 0x10000,
|
||||
LSX = 0x20000,
|
||||
LASX = 0x40000,
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
#ifndef SIMDJSON_LASX_H
|
||||
#define SIMDJSON_LASX_H
|
||||
|
||||
#include "simdjson/lasx/begin.h"
|
||||
#include "simdjson/generic/amalgamated.h"
|
||||
#include "simdjson/lasx/end.h"
|
||||
|
||||
#endif // SIMDJSON_LASX_H
|
||||
@@ -0,0 +1,26 @@
|
||||
#ifndef SIMDJSON_LASX_BASE_H
|
||||
#define SIMDJSON_LASX_BASE_H
|
||||
|
||||
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
|
||||
#include "simdjson/base.h"
|
||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||
|
||||
namespace simdjson {
|
||||
/**
|
||||
* Implementation for LASX.
|
||||
*/
|
||||
namespace lasx {
|
||||
|
||||
class implementation;
|
||||
|
||||
namespace {
|
||||
namespace simd {
|
||||
template <typename T> struct simd8;
|
||||
template <typename T> struct simd8x64;
|
||||
} // namespace simd
|
||||
} // unnamed namespace
|
||||
|
||||
} // namespace lasx
|
||||
} // namespace simdjson
|
||||
|
||||
#endif // SIMDJSON_LASX_BASE_H
|
||||
@@ -0,0 +1,10 @@
|
||||
#define SIMDJSON_IMPLEMENTATION lasx
|
||||
#include "simdjson/lasx/base.h"
|
||||
#include "simdjson/lasx/intrinsics.h"
|
||||
#include "simdjson/lasx/bitmanipulation.h"
|
||||
#include "simdjson/lasx/bitmask.h"
|
||||
#include "simdjson/lasx/numberparsing_defs.h"
|
||||
#include "simdjson/lasx/simd.h"
|
||||
#include "simdjson/lasx/stringparsing_defs.h"
|
||||
|
||||
#define SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT 1
|
||||
@@ -0,0 +1,50 @@
|
||||
#ifndef SIMDJSON_LASX_BITMANIPULATION_H
|
||||
#define SIMDJSON_LASX_BITMANIPULATION_H
|
||||
|
||||
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
|
||||
#include "simdjson/lasx/base.h"
|
||||
#include "simdjson/lasx/intrinsics.h"
|
||||
#include "simdjson/lasx/bitmask.h"
|
||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||
|
||||
namespace simdjson {
|
||||
namespace lasx {
|
||||
namespace {
|
||||
|
||||
// We sometimes call trailing_zero on inputs that are zero,
|
||||
// but the algorithms do not end up using the returned value.
|
||||
// Sadly, sanitizers are not smart enough to figure it out.
|
||||
SIMDJSON_NO_SANITIZE_UNDEFINED
|
||||
// This function can be used safely even if not all bytes have been
|
||||
// initialized.
|
||||
// See issue https://github.com/simdjson/simdjson/issues/1965
|
||||
SIMDJSON_NO_SANITIZE_MEMORY
|
||||
simdjson_inline int trailing_zeroes(uint64_t input_num) {
|
||||
return __builtin_ctzll(input_num);
|
||||
}
|
||||
|
||||
/* result might be undefined when input_num is zero */
|
||||
simdjson_inline uint64_t clear_lowest_bit(uint64_t input_num) {
|
||||
return input_num & (input_num-1);
|
||||
}
|
||||
|
||||
/* result might be undefined when input_num is zero */
|
||||
simdjson_inline int leading_zeroes(uint64_t input_num) {
|
||||
return __builtin_clzll(input_num);
|
||||
}
|
||||
|
||||
/* result might be undefined when input_num is zero */
|
||||
simdjson_inline int count_ones(uint64_t input_num) {
|
||||
return __lasx_xvpickve2gr_w(__lasx_xvpcnt_d(__m256i(v4u64{input_num, 0, 0, 0})), 0);
|
||||
}
|
||||
|
||||
simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, uint64_t *result) {
|
||||
return __builtin_uaddll_overflow(value1, value2,
|
||||
reinterpret_cast<unsigned long long *>(result));
|
||||
}
|
||||
|
||||
} // unnamed namespace
|
||||
} // namespace lasx
|
||||
} // namespace simdjson
|
||||
|
||||
#endif // SIMDJSON_LASX_BITMANIPULATION_H
|
||||
@@ -0,0 +1,31 @@
|
||||
#ifndef SIMDJSON_LASX_BITMASK_H
|
||||
#define SIMDJSON_LASX_BITMASK_H
|
||||
|
||||
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
|
||||
#include "simdjson/lasx/base.h"
|
||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||
|
||||
namespace simdjson {
|
||||
namespace lasx {
|
||||
namespace {
|
||||
|
||||
//
|
||||
// Perform a "cumulative bitwise xor," flipping bits each time a 1 is encountered.
|
||||
//
|
||||
// For example, prefix_xor(00100100) == 00011100
|
||||
//
|
||||
simdjson_inline uint64_t prefix_xor(uint64_t bitmask) {
|
||||
bitmask ^= bitmask << 1;
|
||||
bitmask ^= bitmask << 2;
|
||||
bitmask ^= bitmask << 4;
|
||||
bitmask ^= bitmask << 8;
|
||||
bitmask ^= bitmask << 16;
|
||||
bitmask ^= bitmask << 32;
|
||||
return bitmask;
|
||||
}
|
||||
|
||||
} // unnamed namespace
|
||||
} // namespace lasx
|
||||
} // namespace simdjson
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,6 @@
|
||||
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
|
||||
#include "simdjson/lasx/base.h"
|
||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||
|
||||
#undef SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT
|
||||
#undef SIMDJSON_IMPLEMENTATION
|
||||
@@ -0,0 +1,31 @@
|
||||
#ifndef SIMDJSON_LASX_IMPLEMENTATION_H
|
||||
#define SIMDJSON_LASX_IMPLEMENTATION_H
|
||||
|
||||
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
|
||||
#include "simdjson/base.h"
|
||||
#include "simdjson/implementation.h"
|
||||
#include "simdjson/internal/instruction_set.h"
|
||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||
|
||||
namespace simdjson {
|
||||
namespace lasx {
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
class implementation final : public simdjson::implementation {
|
||||
public:
|
||||
simdjson_inline implementation() : simdjson::implementation("lasx", "LoongArch ASX", internal::instruction_set::LASX) {}
|
||||
simdjson_warn_unused error_code create_dom_parser_implementation(
|
||||
size_t capacity,
|
||||
size_t max_length,
|
||||
std::unique_ptr<internal::dom_parser_implementation>& dst
|
||||
) const noexcept final;
|
||||
simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final;
|
||||
simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final;
|
||||
};
|
||||
|
||||
} // namespace lasx
|
||||
} // namespace simdjson
|
||||
|
||||
#endif // SIMDJSON_LASX_IMPLEMENTATION_H
|
||||
@@ -0,0 +1,14 @@
|
||||
#ifndef SIMDJSON_LASX_INTRINSICS_H
|
||||
#define SIMDJSON_LASX_INTRINSICS_H
|
||||
|
||||
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
|
||||
#include "simdjson/lasx/base.h"
|
||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||
|
||||
// This should be the correct header whether
|
||||
// you use visual studio or other compilers.
|
||||
#include <lasxintrin.h>
|
||||
|
||||
static_assert(sizeof(__m256i) <= simdjson::SIMDJSON_PADDING, "insufficient padding for LoongArch ASX");
|
||||
|
||||
#endif // SIMDJSON_LASX_INTRINSICS_H
|
||||
@@ -0,0 +1,41 @@
|
||||
#ifndef SIMDJSON_LASX_NUMBERPARSING_DEFS_H
|
||||
#define SIMDJSON_LASX_NUMBERPARSING_DEFS_H
|
||||
|
||||
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
|
||||
#include "simdjson/lasx/base.h"
|
||||
#include "simdjson/lasx/intrinsics.h"
|
||||
#include "simdjson/internal/numberparsing_tables.h"
|
||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||
|
||||
#include <cstring>
|
||||
|
||||
namespace simdjson {
|
||||
namespace lasx {
|
||||
namespace numberparsing {
|
||||
|
||||
// we don't have appropriate instructions, so let us use a scalar function
|
||||
// credit: https://johnnylee-sde.github.io/Fast-numeric-string-to-int/
|
||||
/** @private */
|
||||
static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) {
|
||||
uint64_t val;
|
||||
std::memcpy(&val, chars, sizeof(uint64_t));
|
||||
val = (val & 0x0F0F0F0F0F0F0F0F) * 2561 >> 8;
|
||||
val = (val & 0x00FF00FF00FF00FF) * 6553601 >> 16;
|
||||
return uint32_t((val & 0x0000FFFF0000FFFF) * 42949672960001 >> 32);
|
||||
}
|
||||
|
||||
simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t value2) {
|
||||
internal::value128 answer;
|
||||
__uint128_t r = (static_cast<__uint128_t>(value1)) * value2;
|
||||
answer.low = uint64_t(r);
|
||||
answer.high = uint64_t(r >> 64);
|
||||
return answer;
|
||||
}
|
||||
|
||||
} // namespace numberparsing
|
||||
} // namespace lasx
|
||||
} // namespace simdjson
|
||||
|
||||
#define SIMDJSON_SWAR_NUMBER_PARSING 1
|
||||
|
||||
#endif // SIMDJSON_LASX_NUMBERPARSING_DEFS_H
|
||||
@@ -0,0 +1,8 @@
|
||||
#ifndef SIMDJSON_LASX_ONDEMAND_H
|
||||
#define SIMDJSON_LASX_ONDEMAND_H
|
||||
|
||||
#include "simdjson/lasx/begin.h"
|
||||
#include "simdjson/generic/ondemand/amalgamated.h"
|
||||
#include "simdjson/lasx/end.h"
|
||||
|
||||
#endif // SIMDJSON_LASX_ONDEMAND_H
|
||||
@@ -0,0 +1,376 @@
|
||||
#ifndef SIMDJSON_LASX_SIMD_H
|
||||
#define SIMDJSON_LASX_SIMD_H
|
||||
|
||||
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
|
||||
#include "simdjson/lasx/base.h"
|
||||
#include "simdjson/lasx/bitmanipulation.h"
|
||||
#include "simdjson/internal/simdprune_tables.h"
|
||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||
|
||||
namespace simdjson {
|
||||
namespace lasx {
|
||||
namespace {
|
||||
namespace simd {
|
||||
|
||||
// Forward-declared so they can be used by splat and friends.
|
||||
template<typename Child>
|
||||
struct base {
|
||||
__m256i value;
|
||||
|
||||
// Zero constructor
|
||||
simdjson_inline base() : value{__m256i()} {}
|
||||
|
||||
// Conversion from SIMD register
|
||||
simdjson_inline base(const __m256i _value) : value(_value) {}
|
||||
|
||||
// Conversion to SIMD register
|
||||
simdjson_inline operator const __m256i&() const { return this->value; }
|
||||
simdjson_inline operator __m256i&() { return this->value; }
|
||||
simdjson_inline operator const v32i8&() const { return (v32i8&)this->value; }
|
||||
simdjson_inline operator v32i8&() { return (v32i8&)this->value; }
|
||||
|
||||
// Bit operations
|
||||
simdjson_inline Child operator|(const Child other) const { return __lasx_xvor_v(*this, other); }
|
||||
simdjson_inline Child operator&(const Child other) const { return __lasx_xvand_v(*this, other); }
|
||||
simdjson_inline Child operator^(const Child other) const { return __lasx_xvxor_v(*this, other); }
|
||||
simdjson_inline Child bit_andnot(const Child other) const { return __lasx_xvandn_v(other, *this); }
|
||||
simdjson_inline Child& operator|=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast | other; return *this_cast; }
|
||||
simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast & other; return *this_cast; }
|
||||
simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast ^ other; return *this_cast; }
|
||||
};
|
||||
|
||||
// Forward-declared so they can be used by splat and friends.
|
||||
template<typename T>
|
||||
struct simd8;
|
||||
|
||||
template<typename T, typename Mask=simd8<bool>>
|
||||
struct base8: base<simd8<T>> {
|
||||
simdjson_inline base8() : base<simd8<T>>() {}
|
||||
simdjson_inline base8(const __m256i _value) : base<simd8<T>>(_value) {}
|
||||
|
||||
friend simdjson_really_inline Mask operator==(const simd8<T> lhs, const simd8<T> rhs) { return __lasx_xvseq_b(lhs, rhs); }
|
||||
|
||||
static const int SIZE = sizeof(base<simd8<T>>::value);
|
||||
|
||||
template<int N=1>
|
||||
simdjson_inline simd8<T> prev(const simd8<T> prev_chunk) const {
|
||||
__m256i hi = __lasx_xvbsll_v(*this, N);
|
||||
__m256i lo = __lasx_xvbsrl_v(*this, 16 - N);
|
||||
__m256i tmp = __lasx_xvbsrl_v(prev_chunk, 16 - N);
|
||||
lo = __lasx_xvpermi_q(lo, tmp, 0x21);
|
||||
return __lasx_xvor_v(hi, lo);
|
||||
}
|
||||
};
|
||||
|
||||
// SIMD byte mask type (returned by things like eq and gt)
|
||||
template<>
|
||||
struct simd8<bool>: base8<bool> {
|
||||
static simdjson_inline simd8<bool> splat(bool _value) { return __lasx_xvreplgr2vr_b(uint8_t(-(!!_value))); }
|
||||
|
||||
simdjson_inline simd8() : base8() {}
|
||||
simdjson_inline simd8(const __m256i _value) : base8<bool>(_value) {}
|
||||
// Splat constructor
|
||||
simdjson_inline simd8(bool _value) : base8<bool>(splat(_value)) {}
|
||||
|
||||
simdjson_inline int to_bitmask() const {
|
||||
__m256i mask = __lasx_xvmskltz_b(*this);
|
||||
return (__lasx_xvpickve2gr_w(mask, 4) << 16) | (__lasx_xvpickve2gr_w(mask, 0));
|
||||
}
|
||||
simdjson_inline bool any() const {
|
||||
__m256i v = __lasx_xvmsknz_b(*this);
|
||||
return (0 == __lasx_xvpickve2gr_w(v, 0)) && (0 == __lasx_xvpickve2gr_w(v, 4));
|
||||
}
|
||||
simdjson_inline simd8<bool> operator~() const { return *this ^ true; }
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct base8_numeric: base8<T> {
|
||||
static simdjson_inline simd8<T> splat(T _value) {
|
||||
return __lasx_xvreplgr2vr_b(_value);
|
||||
}
|
||||
static simdjson_inline simd8<T> zero() { return __lasx_xvldi(0); }
|
||||
static simdjson_inline simd8<T> load(const T values[32]) {
|
||||
return __lasx_xvld(reinterpret_cast<const __m256i *>(values), 0);
|
||||
}
|
||||
// Repeat 16 values as many times as necessary (usually for lookup tables)
|
||||
static simdjson_inline simd8<T> repeat_16(
|
||||
T v0, T v1, T v2, T v3, T v4, T v5, T v6, T v7,
|
||||
T v8, T v9, T v10, T v11, T v12, T v13, T v14, T v15
|
||||
) {
|
||||
return simd8<T>(
|
||||
v0, v1, v2, v3, v4, v5, v6, v7,
|
||||
v8, v9, v10,v11,v12,v13,v14,v15,
|
||||
v0, v1, v2, v3, v4, v5, v6, v7,
|
||||
v8, v9, v10,v11,v12,v13,v14,v15
|
||||
);
|
||||
}
|
||||
|
||||
simdjson_inline base8_numeric() : base8<T>() {}
|
||||
simdjson_inline base8_numeric(const __m256i _value) : base8<T>(_value) {}
|
||||
|
||||
// Store to array
|
||||
simdjson_inline void store(T dst[32]) const {
|
||||
return __lasx_xvst(*this, reinterpret_cast<__m256i *>(dst), 0);
|
||||
}
|
||||
|
||||
// Addition/subtraction are the same for signed and unsigned
|
||||
simdjson_inline simd8<T> operator+(const simd8<T> other) const { return __lasx_xvadd_b(*this, other); }
|
||||
simdjson_inline simd8<T> operator-(const simd8<T> other) const { return __lasx_xvsub_b(*this, other); }
|
||||
simdjson_inline simd8<T>& operator+=(const simd8<T> other) { *this = *this + other; return *static_cast<simd8<T>*>(this); }
|
||||
simdjson_inline simd8<T>& operator-=(const simd8<T> other) { *this = *this - other; return *static_cast<simd8<T>*>(this); }
|
||||
|
||||
// Override to distinguish from bool version
|
||||
simdjson_inline simd8<T> operator~() const { return *this ^ 0xFFu; }
|
||||
|
||||
// Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values)
|
||||
template<typename L>
|
||||
simdjson_inline simd8<L> lookup_16(simd8<L> lookup_table) const {
|
||||
return __lasx_xvshuf_b(lookup_table, lookup_table, *this);
|
||||
}
|
||||
|
||||
// Copies to 'output" all bytes corresponding to a 0 in the mask (interpreted as a bitset).
|
||||
// Passing a 0 value for mask would be equivalent to writing out every byte to output.
|
||||
// Only the first 16 - count_ones(mask) bytes of the result are significant but 16 bytes
|
||||
// get written.
|
||||
template<typename L>
|
||||
simdjson_inline void compress(uint32_t mask, L * output) const {
|
||||
using internal::thintable_epi8;
|
||||
using internal::BitsSetTable256mul2;
|
||||
using internal::pshufb_combine_table;
|
||||
// this particular implementation was inspired by haswell
|
||||
// lasx do it in 4 steps, first 8 bytes and then second 8 bytes...
|
||||
uint8_t mask1 = uint8_t(mask); // least significant 8 bits
|
||||
uint8_t mask2 = uint8_t(mask >> 8); // second significant 8 bits
|
||||
uint8_t mask3 = uint8_t(mask >> 16); // ...
|
||||
uint8_t mask4 = uint8_t(mask >> 24); // ...
|
||||
// next line just loads the 64-bit values thintable_epi8[mask{1,2,3,4}]
|
||||
// into a 256-bit register.
|
||||
__m256i shufmask = {int64_t(thintable_epi8[mask1]), int64_t(thintable_epi8[mask2]) + 0x0808080808080808, int64_t(thintable_epi8[mask3]), int64_t(thintable_epi8[mask4]) + 0x0808080808080808};
|
||||
// this is the version "nearly pruned"
|
||||
__m256i pruned = __lasx_xvshuf_b(*this, *this, shufmask);
|
||||
// we still need to put the pieces back together.
|
||||
// we compute the popcount of the first words:
|
||||
int pop1 = BitsSetTable256mul2[mask1];
|
||||
int pop2 = BitsSetTable256mul2[mask2];
|
||||
int pop3 = BitsSetTable256mul2[mask3];
|
||||
|
||||
// then load the corresponding mask
|
||||
__m256i masklo = __lasx_xvldx(reinterpret_cast<void*>(reinterpret_cast<unsigned long>(pshufb_combine_table)), pop1 * 8);
|
||||
__m256i maskhi = __lasx_xvldx(reinterpret_cast<void*>(reinterpret_cast<unsigned long>(pshufb_combine_table)), pop3 * 8);
|
||||
__m256i compactmask = __lasx_xvpermi_q(maskhi, masklo, 0x20);
|
||||
__m256i answer = __lasx_xvshuf_b(pruned, pruned, compactmask);
|
||||
__lasx_xvst(answer, reinterpret_cast<uint8_t*>(output), 0);
|
||||
uint64_t value3 = __lasx_xvpickve2gr_du(answer, 2);
|
||||
uint64_t value4 = __lasx_xvpickve2gr_du(answer, 3);
|
||||
uint64_t *pos = reinterpret_cast<uint64_t*>(reinterpret_cast<uint8_t*>(output) + 16 - (pop1 + pop2) / 2);
|
||||
pos[0] = value3;
|
||||
pos[1] = value4;
|
||||
}
|
||||
|
||||
template<typename L>
|
||||
simdjson_inline simd8<L> lookup_16(
|
||||
L replace0, L replace1, L replace2, L replace3,
|
||||
L replace4, L replace5, L replace6, L replace7,
|
||||
L replace8, L replace9, L replace10, L replace11,
|
||||
L replace12, L replace13, L replace14, L replace15) const {
|
||||
return lookup_16(simd8<L>::repeat_16(
|
||||
replace0, replace1, replace2, replace3,
|
||||
replace4, replace5, replace6, replace7,
|
||||
replace8, replace9, replace10, replace11,
|
||||
replace12, replace13, replace14, replace15
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
// Signed bytes
|
||||
template<>
|
||||
struct simd8<int8_t> : base8_numeric<int8_t> {
|
||||
simdjson_inline simd8() : base8_numeric<int8_t>() {}
|
||||
simdjson_inline simd8(const __m256i _value) : base8_numeric<int8_t>(_value) {}
|
||||
// Splat constructor
|
||||
simdjson_inline simd8(int8_t _value) : simd8(splat(_value)) {}
|
||||
// Array constructor
|
||||
simdjson_inline simd8(const int8_t values[32]) : simd8(load(values)) {}
|
||||
// Member-by-member initialization
|
||||
simdjson_inline simd8(
|
||||
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
|
||||
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15,
|
||||
int8_t v16, int8_t v17, int8_t v18, int8_t v19, int8_t v20, int8_t v21, int8_t v22, int8_t v23,
|
||||
int8_t v24, int8_t v25, int8_t v26, int8_t v27, int8_t v28, int8_t v29, int8_t v30, int8_t v31
|
||||
) : simd8({
|
||||
v0, v1, v2, v3, v4, v5, v6, v7,
|
||||
v8, v9, v10,v11,v12,v13,v14,v15,
|
||||
v16,v17,v18,v19,v20,v21,v22,v23,
|
||||
v24,v25,v26,v27,v28,v29,v30,v31
|
||||
}) {}
|
||||
// Repeat 16 values as many times as necessary (usually for lookup tables)
|
||||
simdjson_inline static simd8<int8_t> repeat_16(
|
||||
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
|
||||
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15
|
||||
) {
|
||||
return simd8<int8_t>(
|
||||
v0, v1, v2, v3, v4, v5, v6, v7,
|
||||
v8, v9, v10,v11,v12,v13,v14,v15,
|
||||
v0, v1, v2, v3, v4, v5, v6, v7,
|
||||
v8, v9, v10,v11,v12,v13,v14,v15
|
||||
);
|
||||
}
|
||||
|
||||
// Order-sensitive comparisons
|
||||
simdjson_inline simd8<int8_t> max_val(const simd8<int8_t> other) const { return __lasx_xvmax_b(*this, other); }
|
||||
simdjson_inline simd8<int8_t> min_val(const simd8<int8_t> other) const { return __lasx_xvmin_b(*this, other); }
|
||||
simdjson_inline simd8<bool> operator>(const simd8<int8_t> other) const { return __lasx_xvslt_b(other, *this); }
|
||||
simdjson_inline simd8<bool> operator<(const simd8<int8_t> other) const { return __lasx_xvslt_b(*this, other); }
|
||||
};
|
||||
|
||||
// Unsigned bytes
|
||||
template<>
|
||||
struct simd8<uint8_t>: base8_numeric<uint8_t> {
|
||||
simdjson_inline simd8() : base8_numeric<uint8_t>() {}
|
||||
simdjson_inline simd8(const __m256i _value) : base8_numeric<uint8_t>(_value) {}
|
||||
// Splat constructor
|
||||
simdjson_inline simd8(uint8_t _value) : simd8(splat(_value)) {}
|
||||
// Array constructor
|
||||
simdjson_inline simd8(const uint8_t values[32]) : simd8(load(values)) {}
|
||||
// Member-by-member initialization
|
||||
simdjson_inline simd8(
|
||||
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
|
||||
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15,
|
||||
uint8_t v16, uint8_t v17, uint8_t v18, uint8_t v19, uint8_t v20, uint8_t v21, uint8_t v22, uint8_t v23,
|
||||
uint8_t v24, uint8_t v25, uint8_t v26, uint8_t v27, uint8_t v28, uint8_t v29, uint8_t v30, uint8_t v31
|
||||
) : simd8(__m256i(v32u8{
|
||||
v0, v1, v2, v3, v4, v5, v6, v7,
|
||||
v8, v9, v10,v11,v12,v13,v14,v15,
|
||||
v16,v17,v18,v19,v20,v21,v22,v23,
|
||||
v24,v25,v26,v27,v28,v29,v30,v31
|
||||
})) {}
|
||||
// Repeat 16 values as many times as necessary (usually for lookup tables)
|
||||
simdjson_inline static simd8<uint8_t> repeat_16(
|
||||
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
|
||||
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15
|
||||
) {
|
||||
return simd8<uint8_t>(
|
||||
v0, v1, v2, v3, v4, v5, v6, v7,
|
||||
v8, v9, v10,v11,v12,v13,v14,v15,
|
||||
v0, v1, v2, v3, v4, v5, v6, v7,
|
||||
v8, v9, v10,v11,v12,v13,v14,v15
|
||||
);
|
||||
}
|
||||
|
||||
// Saturated math
|
||||
simdjson_inline simd8<uint8_t> saturating_add(const simd8<uint8_t> other) const { return __lasx_xvsadd_bu(*this, other); }
|
||||
simdjson_inline simd8<uint8_t> saturating_sub(const simd8<uint8_t> other) const { return __lasx_xvssub_bu(*this, other); }
|
||||
|
||||
// Order-specific operations
|
||||
simdjson_inline simd8<uint8_t> max_val(const simd8<uint8_t> other) const { return __lasx_xvmax_bu(*this, other); }
|
||||
simdjson_inline simd8<uint8_t> min_val(const simd8<uint8_t> other) const { return __lasx_xvmin_bu(other, *this); }
|
||||
// Same as >, but only guarantees true is nonzero (< guarantees true = -1)
|
||||
simdjson_inline simd8<uint8_t> gt_bits(const simd8<uint8_t> other) const { return this->saturating_sub(other); }
|
||||
// Same as <, but only guarantees true is nonzero (< guarantees true = -1)
|
||||
simdjson_inline simd8<uint8_t> lt_bits(const simd8<uint8_t> other) const { return other.saturating_sub(*this); }
|
||||
simdjson_inline simd8<bool> operator<=(const simd8<uint8_t> other) const { return other.max_val(*this) == other; }
|
||||
simdjson_inline simd8<bool> operator>=(const simd8<uint8_t> other) const { return other.min_val(*this) == other; }
|
||||
simdjson_inline simd8<bool> operator>(const simd8<uint8_t> other) const { return this->gt_bits(other).any_bits_set(); }
|
||||
simdjson_inline simd8<bool> operator<(const simd8<uint8_t> other) const { return this->lt_bits(other).any_bits_set(); }
|
||||
|
||||
// Bit-specific operations
|
||||
simdjson_inline simd8<bool> bits_not_set() const { return *this == uint8_t(0); }
|
||||
simdjson_inline simd8<bool> bits_not_set(simd8<uint8_t> bits) const { return (*this & bits).bits_not_set(); }
|
||||
simdjson_inline simd8<bool> any_bits_set() const { return ~this->bits_not_set(); }
|
||||
simdjson_inline simd8<bool> any_bits_set(simd8<uint8_t> bits) const { return ~this->bits_not_set(bits); }
|
||||
simdjson_inline bool is_ascii() const {
|
||||
__m256i mask = __lasx_xvmskltz_b(*this);
|
||||
return (0 == __lasx_xvpickve2gr_w(mask, 0)) && (0 == __lasx_xvpickve2gr_w(mask, 4));
|
||||
}
|
||||
simdjson_inline bool bits_not_set_anywhere() const {
|
||||
__m256i v = __lasx_xvmsknz_b(*this);
|
||||
return (0 == __lasx_xvpickve2gr_w(v, 0)) && (0 == __lasx_xvpickve2gr_w(v, 4));
|
||||
}
|
||||
simdjson_inline bool any_bits_set_anywhere() const { return !bits_not_set_anywhere(); }
|
||||
simdjson_inline bool bits_not_set_anywhere(simd8<uint8_t> bits) const {
|
||||
__m256i v = __lasx_xvmsknz_b(__lasx_xvand_v(*this, bits));
|
||||
return (0 == __lasx_xvpickve2gr_w(v, 0)) && (0 == __lasx_xvpickve2gr_w(v, 4));
|
||||
}
|
||||
simdjson_inline bool any_bits_set_anywhere(simd8<uint8_t> bits) const { return !bits_not_set_anywhere(bits); }
|
||||
template<int N>
|
||||
simdjson_inline simd8<uint8_t> shr() const { return simd8<uint8_t>(__lasx_xvsrli_b(*this, N)); }
|
||||
template<int N>
|
||||
simdjson_inline simd8<uint8_t> shl() const { return simd8<uint8_t>(__lasx_xvslli_b(*this, N)); }
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct simd8x64 {
|
||||
static constexpr int NUM_CHUNKS = 64 / sizeof(simd8<T>);
|
||||
static_assert(NUM_CHUNKS == 2, "LASX kernel should use two registers per 64-byte block.");
|
||||
const simd8<T> chunks[NUM_CHUNKS];
|
||||
|
||||
simd8x64(const simd8x64<T>& o) = delete; // no copy allowed
|
||||
simd8x64<T>& operator=(const simd8<T>& other) = delete; // no assignment allowed
|
||||
simd8x64() = delete; // no default constructor allowed
|
||||
|
||||
simdjson_inline simd8x64(const simd8<T> chunk0, const simd8<T> chunk1) : chunks{chunk0, chunk1} {}
|
||||
simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8<T>::load(ptr), simd8<T>::load(ptr+32)} {}
|
||||
|
||||
simdjson_inline uint64_t compress(uint64_t mask, T * output) const {
|
||||
uint32_t mask1 = uint32_t(mask);
|
||||
uint32_t mask2 = uint32_t(mask >> 32);
|
||||
__m256i zcnt = __lasx_xvpcnt_w(__m256i(v4u64{~mask, 0, 0, 0}));
|
||||
uint64_t zcnt1 = __lasx_xvpickve2gr_wu(zcnt, 0);
|
||||
uint64_t zcnt2 = __lasx_xvpickve2gr_wu(zcnt, 1);
|
||||
// There should be a critical value which processes in scaler is faster.
|
||||
if (zcnt1)
|
||||
this->chunks[0].compress(mask1, output);
|
||||
if (zcnt2)
|
||||
this->chunks[1].compress(mask2, output + zcnt1);
|
||||
return zcnt1 + zcnt2;
|
||||
}
|
||||
|
||||
simdjson_inline void store(T ptr[64]) const {
|
||||
this->chunks[0].store(ptr+sizeof(simd8<T>)*0);
|
||||
this->chunks[1].store(ptr+sizeof(simd8<T>)*1);
|
||||
}
|
||||
|
||||
simdjson_inline uint64_t to_bitmask() const {
|
||||
__m256i mask0 = __lasx_xvmskltz_b(this->chunks[0]);
|
||||
__m256i mask1 = __lasx_xvmskltz_b(this->chunks[1]);
|
||||
__m256i mask_tmp = __lasx_xvpickve_w(mask0, 4);
|
||||
__m256i tmp = __lasx_xvpickve_w(mask1, 4);
|
||||
mask0 = __lasx_xvinsve0_w(mask0, mask1, 1);
|
||||
mask_tmp = __lasx_xvinsve0_w(mask_tmp, tmp, 1);
|
||||
return __lasx_xvpickve2gr_du(__lasx_xvpackev_h(mask_tmp, mask0), 0);
|
||||
}
|
||||
|
||||
simdjson_inline simd8<T> reduce_or() const {
|
||||
return this->chunks[0] | this->chunks[1];
|
||||
}
|
||||
|
||||
simdjson_inline uint64_t eq(const T m) const {
|
||||
const simd8<T> mask = simd8<T>::splat(m);
|
||||
return simd8x64<bool>(
|
||||
this->chunks[0] == mask,
|
||||
this->chunks[1] == mask
|
||||
).to_bitmask();
|
||||
}
|
||||
|
||||
simdjson_inline uint64_t eq(const simd8x64<uint8_t> &other) const {
|
||||
return simd8x64<bool>(
|
||||
this->chunks[0] == other.chunks[0],
|
||||
this->chunks[1] == other.chunks[1]
|
||||
).to_bitmask();
|
||||
}
|
||||
|
||||
simdjson_inline uint64_t lteq(const T m) const {
|
||||
const simd8<T> mask = simd8<T>::splat(m);
|
||||
return simd8x64<bool>(
|
||||
this->chunks[0] <= mask,
|
||||
this->chunks[1] <= mask
|
||||
).to_bitmask();
|
||||
}
|
||||
}; // struct simd8x64<T>
|
||||
|
||||
} // namespace simd
|
||||
} // unnamed namespace
|
||||
} // namespace lasx
|
||||
} // namespace simdjson
|
||||
|
||||
#endif // SIMDJSON_LASX_SIMD_H
|
||||
@@ -0,0 +1,47 @@
|
||||
#ifndef SIMDJSON_LASX_STRINGPARSING_DEFS_H
|
||||
#define SIMDJSON_LASX_STRINGPARSING_DEFS_H
|
||||
|
||||
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
|
||||
#include "simdjson/lasx/base.h"
|
||||
#include "simdjson/lasx/simd.h"
|
||||
#include "simdjson/lasx/bitmanipulation.h"
|
||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||
|
||||
namespace simdjson {
|
||||
namespace lasx {
|
||||
namespace {
|
||||
|
||||
using namespace simd;
|
||||
|
||||
// Holds backslashes and quotes locations.
|
||||
struct backslash_and_quote {
|
||||
public:
|
||||
static constexpr uint32_t BYTES_PROCESSED = 32;
|
||||
simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
|
||||
|
||||
simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; }
|
||||
simdjson_inline bool has_backslash() { return bs_bits != 0; }
|
||||
simdjson_inline int quote_index() { return trailing_zeroes(quote_bits); }
|
||||
simdjson_inline int backslash_index() { return trailing_zeroes(bs_bits); }
|
||||
|
||||
uint32_t bs_bits;
|
||||
uint32_t quote_bits;
|
||||
}; // struct backslash_and_quote
|
||||
|
||||
simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(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(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "backslash and quote finder must process fewer than SIMDJSON_PADDING bytes");
|
||||
simd8<uint8_t> v(src);
|
||||
v.store(dst);
|
||||
return {
|
||||
static_cast<uint32_t>((v == '\\').to_bitmask()), // bs_bits
|
||||
static_cast<uint32_t>((v == '"').to_bitmask()), // quote_bits
|
||||
};
|
||||
}
|
||||
|
||||
} // unnamed namespace
|
||||
} // namespace lasx
|
||||
} // namespace simdjson
|
||||
|
||||
#endif // SIMDJSON_LASX_STRINGPARSING_DEFS_H
|
||||
@@ -0,0 +1,8 @@
|
||||
#ifndef SIMDJSON_LSX_H
|
||||
#define SIMDJSON_LSX_H
|
||||
|
||||
#include "simdjson/lsx/begin.h"
|
||||
#include "simdjson/generic/amalgamated.h"
|
||||
#include "simdjson/lsx/end.h"
|
||||
|
||||
#endif // SIMDJSON_LSX_H
|
||||
@@ -0,0 +1,26 @@
|
||||
#ifndef SIMDJSON_LSX_BASE_H
|
||||
#define SIMDJSON_LSX_BASE_H
|
||||
|
||||
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
|
||||
#include "simdjson/base.h"
|
||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||
|
||||
namespace simdjson {
|
||||
/**
|
||||
* Implementation for LSX.
|
||||
*/
|
||||
namespace lsx {
|
||||
|
||||
class implementation;
|
||||
|
||||
namespace {
|
||||
namespace simd {
|
||||
template <typename T> struct simd8;
|
||||
template <typename T> struct simd8x64;
|
||||
} // namespace simd
|
||||
} // unnamed namespace
|
||||
|
||||
} // namespace lsx
|
||||
} // namespace simdjson
|
||||
|
||||
#endif // SIMDJSON_LSX_BASE_H
|
||||
@@ -0,0 +1,10 @@
|
||||
#define SIMDJSON_IMPLEMENTATION lsx
|
||||
#include "simdjson/lsx/base.h"
|
||||
#include "simdjson/lsx/intrinsics.h"
|
||||
#include "simdjson/lsx/bitmanipulation.h"
|
||||
#include "simdjson/lsx/bitmask.h"
|
||||
#include "simdjson/lsx/numberparsing_defs.h"
|
||||
#include "simdjson/lsx/simd.h"
|
||||
#include "simdjson/lsx/stringparsing_defs.h"
|
||||
|
||||
#define SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT 1
|
||||
@@ -0,0 +1,50 @@
|
||||
#ifndef SIMDJSON_LSX_BITMANIPULATION_H
|
||||
#define SIMDJSON_LSX_BITMANIPULATION_H
|
||||
|
||||
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
|
||||
#include "simdjson/lsx/base.h"
|
||||
#include "simdjson/lsx/intrinsics.h"
|
||||
#include "simdjson/lsx/bitmask.h"
|
||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||
|
||||
namespace simdjson {
|
||||
namespace lsx {
|
||||
namespace {
|
||||
|
||||
// We sometimes call trailing_zero on inputs that are zero,
|
||||
// but the algorithms do not end up using the returned value.
|
||||
// Sadly, sanitizers are not smart enough to figure it out.
|
||||
SIMDJSON_NO_SANITIZE_UNDEFINED
|
||||
// This function can be used safely even if not all bytes have been
|
||||
// initialized.
|
||||
// See issue https://github.com/simdjson/simdjson/issues/1965
|
||||
SIMDJSON_NO_SANITIZE_MEMORY
|
||||
simdjson_inline int trailing_zeroes(uint64_t input_num) {
|
||||
return __builtin_ctzll(input_num);
|
||||
}
|
||||
|
||||
/* result might be undefined when input_num is zero */
|
||||
simdjson_inline uint64_t clear_lowest_bit(uint64_t input_num) {
|
||||
return input_num & (input_num-1);
|
||||
}
|
||||
|
||||
/* result might be undefined when input_num is zero */
|
||||
simdjson_inline int leading_zeroes(uint64_t input_num) {
|
||||
return __builtin_clzll(input_num);
|
||||
}
|
||||
|
||||
/* result might be undefined when input_num is zero */
|
||||
simdjson_inline int count_ones(uint64_t input_num) {
|
||||
return __lsx_vpickve2gr_w(__lsx_vpcnt_d(__m128i(v2u64{input_num, 0})), 0);
|
||||
}
|
||||
|
||||
simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, uint64_t *result) {
|
||||
return __builtin_uaddll_overflow(value1, value2,
|
||||
reinterpret_cast<unsigned long long *>(result));
|
||||
}
|
||||
|
||||
} // unnamed namespace
|
||||
} // namespace lsx
|
||||
} // namespace simdjson
|
||||
|
||||
#endif // SIMDJSON_LSX_BITMANIPULATION_H
|
||||
@@ -0,0 +1,31 @@
|
||||
#ifndef SIMDJSON_LSX_BITMASK_H
|
||||
#define SIMDJSON_LSX_BITMASK_H
|
||||
|
||||
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
|
||||
#include "simdjson/lsx/base.h"
|
||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||
|
||||
namespace simdjson {
|
||||
namespace lsx {
|
||||
namespace {
|
||||
|
||||
//
|
||||
// Perform a "cumulative bitwise xor," flipping bits each time a 1 is encountered.
|
||||
//
|
||||
// For example, prefix_xor(00100100) == 00011100
|
||||
//
|
||||
simdjson_inline uint64_t prefix_xor(uint64_t bitmask) {
|
||||
bitmask ^= bitmask << 1;
|
||||
bitmask ^= bitmask << 2;
|
||||
bitmask ^= bitmask << 4;
|
||||
bitmask ^= bitmask << 8;
|
||||
bitmask ^= bitmask << 16;
|
||||
bitmask ^= bitmask << 32;
|
||||
return bitmask;
|
||||
}
|
||||
|
||||
} // unnamed namespace
|
||||
} // namespace lsx
|
||||
} // namespace simdjson
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,6 @@
|
||||
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
|
||||
#include "simdjson/lsx/base.h"
|
||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||
|
||||
#undef SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT
|
||||
#undef SIMDJSON_IMPLEMENTATION
|
||||
@@ -0,0 +1,31 @@
|
||||
#ifndef SIMDJSON_LSX_IMPLEMENTATION_H
|
||||
#define SIMDJSON_LSX_IMPLEMENTATION_H
|
||||
|
||||
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
|
||||
#include "simdjson/base.h"
|
||||
#include "simdjson/implementation.h"
|
||||
#include "simdjson/internal/instruction_set.h"
|
||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||
|
||||
namespace simdjson {
|
||||
namespace lsx {
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
class implementation final : public simdjson::implementation {
|
||||
public:
|
||||
simdjson_inline implementation() : simdjson::implementation("lsx", "LoongArch SX", internal::instruction_set::LSX) {}
|
||||
simdjson_warn_unused error_code create_dom_parser_implementation(
|
||||
size_t capacity,
|
||||
size_t max_length,
|
||||
std::unique_ptr<internal::dom_parser_implementation>& dst
|
||||
) const noexcept final;
|
||||
simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final;
|
||||
simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final;
|
||||
};
|
||||
|
||||
} // namespace lsx
|
||||
} // namespace simdjson
|
||||
|
||||
#endif // SIMDJSON_LSX_IMPLEMENTATION_H
|
||||
@@ -0,0 +1,14 @@
|
||||
#ifndef SIMDJSON_LSX_INTRINSICS_H
|
||||
#define SIMDJSON_LSX_INTRINSICS_H
|
||||
|
||||
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
|
||||
#include "simdjson/lsx/base.h"
|
||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||
|
||||
// This should be the correct header whether
|
||||
// you use visual studio or other compilers.
|
||||
#include <lsxintrin.h>
|
||||
|
||||
static_assert(sizeof(__m128i) <= simdjson::SIMDJSON_PADDING, "insufficient padding for LoongArch SX");
|
||||
|
||||
#endif // SIMDJSON_LSX_INTRINSICS_H
|
||||
@@ -0,0 +1,41 @@
|
||||
#ifndef SIMDJSON_LSX_NUMBERPARSING_DEFS_H
|
||||
#define SIMDJSON_LSX_NUMBERPARSING_DEFS_H
|
||||
|
||||
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
|
||||
#include "simdjson/lsx/base.h"
|
||||
#include "simdjson/lsx/intrinsics.h"
|
||||
#include "simdjson/internal/numberparsing_tables.h"
|
||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||
|
||||
#include <cstring>
|
||||
|
||||
namespace simdjson {
|
||||
namespace lsx {
|
||||
namespace numberparsing {
|
||||
|
||||
// we don't have appropriate instructions, so let us use a scalar function
|
||||
// credit: https://johnnylee-sde.github.io/Fast-numeric-string-to-int/
|
||||
/** @private */
|
||||
static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) {
|
||||
uint64_t val;
|
||||
std::memcpy(&val, chars, sizeof(uint64_t));
|
||||
val = (val & 0x0F0F0F0F0F0F0F0F) * 2561 >> 8;
|
||||
val = (val & 0x00FF00FF00FF00FF) * 6553601 >> 16;
|
||||
return uint32_t((val & 0x0000FFFF0000FFFF) * 42949672960001 >> 32);
|
||||
}
|
||||
|
||||
simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t value2) {
|
||||
internal::value128 answer;
|
||||
__uint128_t r = (static_cast<__uint128_t>(value1)) * value2;
|
||||
answer.low = uint64_t(r);
|
||||
answer.high = uint64_t(r >> 64);
|
||||
return answer;
|
||||
}
|
||||
|
||||
} // namespace numberparsing
|
||||
} // namespace lsx
|
||||
} // namespace simdjson
|
||||
|
||||
#define SIMDJSON_SWAR_NUMBER_PARSING 1
|
||||
|
||||
#endif // SIMDJSON_LSX_NUMBERPARSING_DEFS_H
|
||||
@@ -0,0 +1,8 @@
|
||||
#ifndef SIMDJSON_LSX_ONDEMAND_H
|
||||
#define SIMDJSON_LSX_ONDEMAND_H
|
||||
|
||||
#include "simdjson/lsx/begin.h"
|
||||
#include "simdjson/generic/ondemand/amalgamated.h"
|
||||
#include "simdjson/lsx/end.h"
|
||||
|
||||
#endif // SIMDJSON_LSX_ONDEMAND_H
|
||||
@@ -0,0 +1,354 @@
|
||||
#ifndef SIMDJSON_LSX_SIMD_H
|
||||
#define SIMDJSON_LSX_SIMD_H
|
||||
|
||||
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
|
||||
#include "simdjson/lsx/base.h"
|
||||
#include "simdjson/lsx/bitmanipulation.h"
|
||||
#include "simdjson/internal/simdprune_tables.h"
|
||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||
|
||||
namespace simdjson {
|
||||
namespace lsx {
|
||||
namespace {
|
||||
namespace simd {
|
||||
|
||||
// Forward-declared so they can be used by splat and friends.
|
||||
template<typename Child>
|
||||
struct base {
|
||||
__m128i value;
|
||||
|
||||
// Zero constructor
|
||||
simdjson_inline base() : value{__m128i()} {}
|
||||
|
||||
// Conversion from SIMD register
|
||||
simdjson_inline base(const __m128i _value) : value(_value) {}
|
||||
|
||||
// Conversion to SIMD register
|
||||
simdjson_inline operator const __m128i&() const { return this->value; }
|
||||
simdjson_inline operator __m128i&() { return this->value; }
|
||||
simdjson_inline operator const v16i8&() const { return (v16i8&)this->value; }
|
||||
simdjson_inline operator v16i8&() { return (v16i8&)this->value; }
|
||||
|
||||
// Bit operations
|
||||
simdjson_inline Child operator|(const Child other) const { return __lsx_vor_v(*this, other); }
|
||||
simdjson_inline Child operator&(const Child other) const { return __lsx_vand_v(*this, other); }
|
||||
simdjson_inline Child operator^(const Child other) const { return __lsx_vxor_v(*this, other); }
|
||||
simdjson_inline Child bit_andnot(const Child other) const { return __lsx_vandn_v(other, *this); }
|
||||
simdjson_inline Child& operator|=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast | other; return *this_cast; }
|
||||
simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast & other; return *this_cast; }
|
||||
simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast ^ other; return *this_cast; }
|
||||
};
|
||||
|
||||
// Forward-declared so they can be used by splat and friends.
|
||||
template<typename T>
|
||||
struct simd8;
|
||||
|
||||
template<typename T, typename Mask=simd8<bool>>
|
||||
struct base8: base<simd8<T>> {
|
||||
simdjson_inline base8() : base<simd8<T>>() {}
|
||||
simdjson_inline base8(const __m128i _value) : base<simd8<T>>(_value) {}
|
||||
|
||||
friend simdjson_really_inline Mask operator==(const simd8<T> lhs, const simd8<T> rhs) { return __lsx_vseq_b(lhs, rhs); }
|
||||
|
||||
static const int SIZE = sizeof(base<simd8<T>>::value);
|
||||
|
||||
template<int N=1>
|
||||
simdjson_inline simd8<T> prev(const simd8<T> prev_chunk) const {
|
||||
return __lsx_vor_v(__lsx_vbsll_v(*this, N), __lsx_vbsrl_v(prev_chunk, 16 - N));
|
||||
}
|
||||
};
|
||||
|
||||
// SIMD byte mask type (returned by things like eq and gt)
|
||||
template<>
|
||||
struct simd8<bool>: base8<bool> {
|
||||
static simdjson_inline simd8<bool> splat(bool _value) {
|
||||
return __lsx_vreplgr2vr_b(uint8_t(-(!!_value)));
|
||||
}
|
||||
|
||||
simdjson_inline simd8() : base8() {}
|
||||
simdjson_inline simd8(const __m128i _value) : base8<bool>(_value) {}
|
||||
// Splat constructor
|
||||
simdjson_inline simd8(bool _value) : base8<bool>(splat(_value)) {}
|
||||
|
||||
simdjson_inline int to_bitmask() const { return __lsx_vpickve2gr_w(__lsx_vmskltz_b(*this), 0); }
|
||||
simdjson_inline bool any() const { return 0 == __lsx_vpickve2gr_hu(__lsx_vmsknz_b(*this), 0); }
|
||||
simdjson_inline simd8<bool> operator~() const { return *this ^ true; }
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct base8_numeric: base8<T> {
|
||||
static simdjson_inline simd8<T> splat(T _value) { return __lsx_vreplgr2vr_b(_value); }
|
||||
static simdjson_inline simd8<T> zero() { return __lsx_vldi(0); }
|
||||
static simdjson_inline simd8<T> load(const T values[16]) {
|
||||
return __lsx_vld(reinterpret_cast<const __m128i *>(values), 0);
|
||||
}
|
||||
// Repeat 16 values as many times as necessary (usually for lookup tables)
|
||||
static simdjson_inline simd8<T> repeat_16(
|
||||
T v0, T v1, T v2, T v3, T v4, T v5, T v6, T v7,
|
||||
T v8, T v9, T v10, T v11, T v12, T v13, T v14, T v15
|
||||
) {
|
||||
return simd8<T>(
|
||||
v0, v1, v2, v3, v4, v5, v6, v7,
|
||||
v8, v9, v10,v11,v12,v13,v14,v15
|
||||
);
|
||||
}
|
||||
|
||||
simdjson_inline base8_numeric() : base8<T>() {}
|
||||
simdjson_inline base8_numeric(const __m128i _value) : base8<T>(_value) {}
|
||||
|
||||
// Store to array
|
||||
simdjson_inline void store(T dst[16]) const {
|
||||
return __lsx_vst(*this, reinterpret_cast<__m128i *>(dst), 0);
|
||||
}
|
||||
|
||||
// Addition/subtraction are the same for signed and unsigned
|
||||
simdjson_inline simd8<T> operator+(const simd8<T> other) const { return __lsx_vadd_b(*this, other); }
|
||||
simdjson_inline simd8<T> operator-(const simd8<T> other) const { return __lsx_vsub_b(*this, other); }
|
||||
simdjson_inline simd8<T>& operator+=(const simd8<T> other) { *this = *this + other; return *static_cast<simd8<T>*>(this); }
|
||||
simdjson_inline simd8<T>& operator-=(const simd8<T> other) { *this = *this - other; return *static_cast<simd8<T>*>(this); }
|
||||
|
||||
// Override to distinguish from bool version
|
||||
simdjson_inline simd8<T> operator~() const { return *this ^ 0xFFu; }
|
||||
|
||||
// Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values)
|
||||
template<typename L>
|
||||
simdjson_inline simd8<L> lookup_16(simd8<L> lookup_table) const {
|
||||
return __lsx_vshuf_b(lookup_table, lookup_table, *this);
|
||||
}
|
||||
|
||||
// Copies to 'output" all bytes corresponding to a 0 in the mask (interpreted as a bitset).
|
||||
// Passing a 0 value for mask would be equivalent to writing out every byte to output.
|
||||
// Only the first 16 - count_ones(mask) bytes of the result are significant but 16 bytes
|
||||
// get written.
|
||||
template<typename L>
|
||||
simdjson_inline void compress(uint16_t mask, L * output) const {
|
||||
using internal::thintable_epi8;
|
||||
using internal::BitsSetTable256mul2;
|
||||
using internal::pshufb_combine_table;
|
||||
// this particular implementation was inspired by haswell
|
||||
// lsx do it in 2 steps, first 8 bytes and then second 8 bytes...
|
||||
uint8_t mask1 = uint8_t(mask); // least significant 8 bits
|
||||
uint8_t mask2 = uint8_t(mask >> 8); // second least significant 8 bits
|
||||
// next line just loads the 64-bit values thintable_epi8[mask1] and
|
||||
// thintable_epi8[mask2] into a 128-bit register.
|
||||
__m128i shufmask = {int64_t(thintable_epi8[mask1]), int64_t(thintable_epi8[mask2]) + 0x0808080808080808};
|
||||
// this is the version "nearly pruned"
|
||||
__m128i pruned = __lsx_vshuf_b(*this, *this, shufmask);
|
||||
// we still need to put the pieces back together.
|
||||
// we compute the popcount of the first words:
|
||||
int pop1 = BitsSetTable256mul2[mask1];
|
||||
// then load the corresponding mask
|
||||
__m128i compactmask = __lsx_vldx(reinterpret_cast<void*>(reinterpret_cast<unsigned long>(pshufb_combine_table)), pop1 * 8);
|
||||
__m128i answer = __lsx_vshuf_b(pruned, pruned, compactmask);
|
||||
__lsx_vst(answer, reinterpret_cast<uint8_t*>(output), 0);
|
||||
}
|
||||
|
||||
template<typename L>
|
||||
simdjson_inline simd8<L> lookup_16(
|
||||
L replace0, L replace1, L replace2, L replace3,
|
||||
L replace4, L replace5, L replace6, L replace7,
|
||||
L replace8, L replace9, L replace10, L replace11,
|
||||
L replace12, L replace13, L replace14, L replace15) const {
|
||||
return lookup_16(simd8<L>::repeat_16(
|
||||
replace0, replace1, replace2, replace3,
|
||||
replace4, replace5, replace6, replace7,
|
||||
replace8, replace9, replace10, replace11,
|
||||
replace12, replace13, replace14, replace15
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
// Signed bytes
|
||||
template<>
|
||||
struct simd8<int8_t> : base8_numeric<int8_t> {
|
||||
simdjson_inline simd8() : base8_numeric<int8_t>() {}
|
||||
simdjson_inline simd8(const __m128i _value) : base8_numeric<int8_t>(_value) {}
|
||||
// Splat constructor
|
||||
simdjson_inline simd8(int8_t _value) : simd8(splat(_value)) {}
|
||||
// Array constructor
|
||||
simdjson_inline simd8(const int8_t values[16]) : simd8(load(values)) {}
|
||||
// Member-by-member initialization
|
||||
simdjson_inline simd8(
|
||||
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
|
||||
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15
|
||||
) : simd8({
|
||||
v0, v1, v2, v3, v4, v5, v6, v7,
|
||||
v8, v9, v10,v11,v12,v13,v14,v15
|
||||
}) {}
|
||||
// Repeat 16 values as many times as necessary (usually for lookup tables)
|
||||
simdjson_inline static simd8<int8_t> repeat_16(
|
||||
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
|
||||
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15
|
||||
) {
|
||||
return simd8<int8_t>(
|
||||
v0, v1, v2, v3, v4, v5, v6, v7,
|
||||
v8, v9, v10,v11,v12,v13,v14,v15
|
||||
);
|
||||
}
|
||||
|
||||
// Order-sensitive comparisons
|
||||
simdjson_inline simd8<int8_t> max_val(const simd8<int8_t> other) const { return __lsx_vmax_b(*this, other); }
|
||||
simdjson_inline simd8<int8_t> min_val(const simd8<int8_t> other) const { return __lsx_vmin_b(*this, other); }
|
||||
simdjson_inline simd8<bool> operator>(const simd8<int8_t> other) const { return __lsx_vslt_b(other, *this); }
|
||||
simdjson_inline simd8<bool> operator<(const simd8<int8_t> other) const { return __lsx_vslt_b(*this, other); }
|
||||
};
|
||||
|
||||
// Unsigned bytes
|
||||
template<>
|
||||
struct simd8<uint8_t>: base8_numeric<uint8_t> {
|
||||
simdjson_inline simd8() : base8_numeric<uint8_t>() {}
|
||||
simdjson_inline simd8(const __m128i _value) : base8_numeric<uint8_t>(_value) {}
|
||||
// Splat constructor
|
||||
simdjson_inline simd8(uint8_t _value) : simd8(splat(_value)) {}
|
||||
// Array constructor
|
||||
simdjson_inline simd8(const uint8_t values[16]) : simd8(load(values)) {}
|
||||
// Member-by-member initialization
|
||||
simdjson_inline simd8(
|
||||
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
|
||||
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15
|
||||
) : simd8(__m128i(v16u8{
|
||||
v0, v1, v2, v3, v4, v5, v6, v7,
|
||||
v8, v9, v10,v11,v12,v13,v14,v15
|
||||
})) {}
|
||||
// Repeat 16 values as many times as necessary (usually for lookup tables)
|
||||
simdjson_inline static simd8<uint8_t> repeat_16(
|
||||
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
|
||||
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15
|
||||
) {
|
||||
return simd8<uint8_t>(
|
||||
v0, v1, v2, v3, v4, v5, v6, v7,
|
||||
v8, v9, v10,v11,v12,v13,v14,v15
|
||||
);
|
||||
}
|
||||
|
||||
// Saturated math
|
||||
simdjson_inline simd8<uint8_t> saturating_add(const simd8<uint8_t> other) const { return __lsx_vsadd_bu(*this, other); }
|
||||
simdjson_inline simd8<uint8_t> saturating_sub(const simd8<uint8_t> other) const { return __lsx_vssub_bu(*this, other); }
|
||||
|
||||
// Order-specific operations
|
||||
simdjson_inline simd8<uint8_t> max_val(const simd8<uint8_t> other) const { return __lsx_vmax_bu(*this, other); }
|
||||
simdjson_inline simd8<uint8_t> min_val(const simd8<uint8_t> other) const { return __lsx_vmin_bu(other, *this); }
|
||||
// Same as >, but only guarantees true is nonzero (< guarantees true = -1)
|
||||
simdjson_inline simd8<uint8_t> gt_bits(const simd8<uint8_t> other) const { return this->saturating_sub(other); }
|
||||
// Same as <, but only guarantees true is nonzero (< guarantees true = -1)
|
||||
simdjson_inline simd8<uint8_t> lt_bits(const simd8<uint8_t> other) const { return other.saturating_sub(*this); }
|
||||
simdjson_inline simd8<bool> operator<=(const simd8<uint8_t> other) const { return other.max_val(*this) == other; }
|
||||
simdjson_inline simd8<bool> operator>=(const simd8<uint8_t> other) const { return other.min_val(*this) == other; }
|
||||
simdjson_inline simd8<bool> operator>(const simd8<uint8_t> other) const { return this->gt_bits(other).any_bits_set(); }
|
||||
simdjson_inline simd8<bool> operator<(const simd8<uint8_t> other) const { return this->lt_bits(other).any_bits_set(); }
|
||||
|
||||
// Bit-specific operations
|
||||
simdjson_inline simd8<bool> bits_not_set() const { return *this == uint8_t(0); }
|
||||
simdjson_inline simd8<bool> bits_not_set(simd8<uint8_t> bits) const { return (*this & bits).bits_not_set(); }
|
||||
simdjson_inline simd8<bool> any_bits_set() const { return ~this->bits_not_set(); }
|
||||
simdjson_inline simd8<bool> any_bits_set(simd8<uint8_t> bits) const { return ~this->bits_not_set(bits); }
|
||||
simdjson_inline bool is_ascii() const { return 0 == __lsx_vpickve2gr_w(__lsx_vmskltz_b(*this), 0); }
|
||||
simdjson_inline bool bits_not_set_anywhere() const { return 0 == __lsx_vpickve2gr_hu(__lsx_vmsknz_b(*this), 0); }
|
||||
simdjson_inline bool any_bits_set_anywhere() const { return !bits_not_set_anywhere(); }
|
||||
simdjson_inline bool bits_not_set_anywhere(simd8<uint8_t> bits) const {
|
||||
return 0 == __lsx_vpickve2gr_hu(__lsx_vmsknz_b(__lsx_vand_v(*this, bits)), 0);
|
||||
}
|
||||
simdjson_inline bool any_bits_set_anywhere(simd8<uint8_t> bits) const { return !bits_not_set_anywhere(bits); }
|
||||
template<int N>
|
||||
simdjson_inline simd8<uint8_t> shr() const { return simd8<uint8_t>(__lsx_vsrli_b(*this, N)); }
|
||||
template<int N>
|
||||
simdjson_inline simd8<uint8_t> shl() const { return simd8<uint8_t>(__lsx_vslli_b(*this, N)); }
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct simd8x64 {
|
||||
static constexpr int NUM_CHUNKS = 64 / sizeof(simd8<T>);
|
||||
static_assert(NUM_CHUNKS == 4, "LSX kernel should use four registers per 64-byte block.");
|
||||
const simd8<T> chunks[NUM_CHUNKS];
|
||||
|
||||
simd8x64(const simd8x64<T>& o) = delete; // no copy allowed
|
||||
simd8x64<T>& operator=(const simd8<T>& other) = delete; // no assignment allowed
|
||||
simd8x64() = delete; // no default constructor allowed
|
||||
|
||||
simdjson_inline simd8x64(const simd8<T> chunk0, const simd8<T> chunk1, const simd8<T> chunk2, const simd8<T> chunk3) : chunks{chunk0, chunk1, chunk2, chunk3} {}
|
||||
simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8<T>::load(ptr), simd8<T>::load(ptr+16), simd8<T>::load(ptr+32), simd8<T>::load(ptr+48)} {}
|
||||
|
||||
simdjson_inline uint64_t compress(uint64_t mask, T * output) const {
|
||||
uint16_t mask1 = uint16_t(mask);
|
||||
uint16_t mask2 = uint16_t(mask >> 16);
|
||||
uint16_t mask3 = uint16_t(mask >> 32);
|
||||
uint16_t mask4 = uint16_t(mask >> 48);
|
||||
__m128i zcnt = __lsx_vpcnt_h(__m128i(v2u64{~mask, 0}));
|
||||
uint64_t zcnt1 = __lsx_vpickve2gr_hu(zcnt, 0);
|
||||
uint64_t zcnt2 = __lsx_vpickve2gr_hu(zcnt, 1);
|
||||
uint64_t zcnt3 = __lsx_vpickve2gr_hu(zcnt, 2);
|
||||
uint64_t zcnt4 = __lsx_vpickve2gr_hu(zcnt, 3);
|
||||
uint8_t *voutput = reinterpret_cast<uint8_t*>(output);
|
||||
// There should be a critical value which processes in scaler is faster.
|
||||
if (zcnt1)
|
||||
this->chunks[0].compress(mask1, reinterpret_cast<T*>(voutput));
|
||||
voutput += zcnt1;
|
||||
if (zcnt2)
|
||||
this->chunks[1].compress(mask2, reinterpret_cast<T*>(voutput));
|
||||
voutput += zcnt2;
|
||||
if (zcnt3)
|
||||
this->chunks[2].compress(mask3, reinterpret_cast<T*>(voutput));
|
||||
voutput += zcnt3;
|
||||
if (zcnt4)
|
||||
this->chunks[3].compress(mask4, reinterpret_cast<T*>(voutput));
|
||||
voutput += zcnt4;
|
||||
return reinterpret_cast<uint64_t>(voutput) - reinterpret_cast<uint64_t>(output);
|
||||
}
|
||||
|
||||
simdjson_inline void store(T ptr[64]) const {
|
||||
this->chunks[0].store(ptr+sizeof(simd8<T>)*0);
|
||||
this->chunks[1].store(ptr+sizeof(simd8<T>)*1);
|
||||
this->chunks[2].store(ptr+sizeof(simd8<T>)*2);
|
||||
this->chunks[3].store(ptr+sizeof(simd8<T>)*3);
|
||||
}
|
||||
|
||||
simdjson_inline uint64_t to_bitmask() const {
|
||||
__m128i mask1 = __lsx_vmskltz_b(this->chunks[0]);
|
||||
__m128i mask2 = __lsx_vmskltz_b(this->chunks[1]);
|
||||
__m128i mask3 = __lsx_vmskltz_b(this->chunks[2]);
|
||||
__m128i mask4 = __lsx_vmskltz_b(this->chunks[3]);
|
||||
mask1 = __lsx_vilvl_h(mask2, mask1);
|
||||
mask2 = __lsx_vilvl_h(mask4, mask3);
|
||||
return __lsx_vpickve2gr_du(__lsx_vilvl_w(mask2, mask1), 0);
|
||||
}
|
||||
|
||||
simdjson_inline simd8<T> reduce_or() const {
|
||||
return (this->chunks[0] | this->chunks[1]) | (this->chunks[2] | this->chunks[3]);
|
||||
}
|
||||
|
||||
simdjson_inline uint64_t eq(const T m) const {
|
||||
const simd8<T> mask = simd8<T>::splat(m);
|
||||
return simd8x64<bool>(
|
||||
this->chunks[0] == mask,
|
||||
this->chunks[1] == mask,
|
||||
this->chunks[2] == mask,
|
||||
this->chunks[3] == mask
|
||||
).to_bitmask();
|
||||
}
|
||||
|
||||
simdjson_inline uint64_t eq(const simd8x64<uint8_t> &other) const {
|
||||
return simd8x64<bool>(
|
||||
this->chunks[0] == other.chunks[0],
|
||||
this->chunks[1] == other.chunks[1],
|
||||
this->chunks[2] == other.chunks[2],
|
||||
this->chunks[3] == other.chunks[3]
|
||||
).to_bitmask();
|
||||
}
|
||||
|
||||
simdjson_inline uint64_t lteq(const T m) const {
|
||||
const simd8<T> mask = simd8<T>::splat(m);
|
||||
return simd8x64<bool>(
|
||||
this->chunks[0] <= mask,
|
||||
this->chunks[1] <= mask,
|
||||
this->chunks[2] <= mask,
|
||||
this->chunks[3] <= mask
|
||||
).to_bitmask();
|
||||
}
|
||||
}; // struct simd8x64<T>
|
||||
|
||||
} // namespace simd
|
||||
} // unnamed namespace
|
||||
} // namespace lsx
|
||||
} // namespace simdjson
|
||||
|
||||
#endif // SIMDJSON_LSX_SIMD_H
|
||||
@@ -0,0 +1,53 @@
|
||||
#ifndef SIMDJSON_LSX_STRINGPARSING_DEFS_H
|
||||
#define SIMDJSON_LSX_STRINGPARSING_DEFS_H
|
||||
|
||||
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
|
||||
#include "simdjson/lsx/base.h"
|
||||
#include "simdjson/lsx/simd.h"
|
||||
#include "simdjson/lsx/bitmanipulation.h"
|
||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||
|
||||
namespace simdjson {
|
||||
namespace lsx {
|
||||
namespace {
|
||||
|
||||
using namespace simd;
|
||||
|
||||
// Holds backslashes and quotes locations.
|
||||
struct backslash_and_quote {
|
||||
public:
|
||||
static constexpr uint32_t BYTES_PROCESSED = 32;
|
||||
simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
|
||||
|
||||
simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; }
|
||||
simdjson_inline bool has_backslash() { return bs_bits != 0; }
|
||||
simdjson_inline int quote_index() { return trailing_zeroes(quote_bits); }
|
||||
simdjson_inline int backslash_index() { return trailing_zeroes(bs_bits); }
|
||||
|
||||
uint32_t bs_bits;
|
||||
uint32_t quote_bits;
|
||||
}; // struct backslash_and_quote
|
||||
|
||||
simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(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(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "backslash and quote finder must process fewer than SIMDJSON_PADDING bytes");
|
||||
simd8<uint8_t> v0(src);
|
||||
simd8<uint8_t> v1(src + sizeof(v0));
|
||||
v0.store(dst);
|
||||
v1.store(dst + sizeof(v0));
|
||||
|
||||
// Getting a 64-bit bitmask is much cheaper than multiple 16-bit bitmasks on LSX; therefore, we
|
||||
// smash them together into a 64-byte mask and get the bitmask from there.
|
||||
uint64_t bs_and_quote = simd8x64<bool>(v0 == '\\', v1 == '\\', v0 == '"', v1 == '"').to_bitmask();
|
||||
return {
|
||||
uint32_t(bs_and_quote), // bs_bits
|
||||
uint32_t(bs_and_quote >> 32) // quote_bits
|
||||
};
|
||||
}
|
||||
|
||||
} // unnamed namespace
|
||||
} // namespace lsx
|
||||
} // namespace simdjson
|
||||
|
||||
#endif // SIMDJSON_LSX_STRINGPARSING_DEFS_H
|
||||
@@ -6,13 +6,13 @@
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
// #pragma once // We remove #pragma once here as it generates a warning in some cases. We rely on the include guard.
|
||||
#pragma once
|
||||
|
||||
#ifndef NONSTD_SV_LITE_H_INCLUDED
|
||||
#define NONSTD_SV_LITE_H_INCLUDED
|
||||
|
||||
#define string_view_lite_MAJOR 1
|
||||
#define string_view_lite_MINOR 7
|
||||
#define string_view_lite_MINOR 8
|
||||
#define string_view_lite_PATCH 0
|
||||
|
||||
#define string_view_lite_VERSION nssv_STRINGIFY(string_view_lite_MAJOR) "." nssv_STRINGIFY(string_view_lite_MINOR) "." nssv_STRINGIFY(string_view_lite_PATCH)
|
||||
@@ -134,6 +134,8 @@
|
||||
|
||||
#if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace nonstd {
|
||||
|
||||
template< class CharT, class Traits, class Allocator = std::allocator<CharT> >
|
||||
|
||||
@@ -84,6 +84,7 @@ inline padded_string::padded_string(std::string_view sv_) noexcept
|
||||
inline padded_string::padded_string(padded_string &&o) noexcept
|
||||
: viable_size(o.viable_size), data_ptr(o.data_ptr) {
|
||||
o.data_ptr = nullptr; // we take ownership
|
||||
o.viable_size = 0;
|
||||
}
|
||||
|
||||
inline padded_string &padded_string::operator=(padded_string &&o) noexcept {
|
||||
|
||||
@@ -111,6 +111,15 @@ struct padded_string final {
|
||||
/**
|
||||
* Load this padded string from a file.
|
||||
*
|
||||
* ## Windows and Unicode
|
||||
*
|
||||
* Windows users who need to read files with non-ANSI characters in the
|
||||
* name should set their code page to UTF-8 (65001) before calling this
|
||||
* function. This should be the default with Windows 11 and better.
|
||||
* Further, they may use the AreFileApisANSI function to determine whether
|
||||
* the filename is interpreted using the ANSI or the system default OEM
|
||||
* codepage, and they may call SetFileApisToOEM accordingly.
|
||||
*
|
||||
* @return IO_ERROR on error. Be mindful that on some 32-bit systems,
|
||||
* the file size might be limited to 2 GB.
|
||||
*
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
#include <strings.h>
|
||||
#endif
|
||||
|
||||
// We are using size_t without namespace std:: throughout the project
|
||||
using std::size_t;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define SIMDJSON_VISUAL_STUDIO 1
|
||||
/**
|
||||
@@ -32,9 +35,9 @@
|
||||
#endif // __clang__
|
||||
#endif // _MSC_VER
|
||||
|
||||
#if defined(__x86_64__) || defined(_M_AMD64)
|
||||
#if (defined(__x86_64__) || defined(_M_AMD64)) && !defined(_M_ARM64EC)
|
||||
#define SIMDJSON_IS_X86_64 1
|
||||
#elif defined(__aarch64__) || defined(_M_ARM64)
|
||||
#elif defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC)
|
||||
#define SIMDJSON_IS_ARM64 1
|
||||
#elif defined(__riscv) && __riscv_xlen == 64
|
||||
#define SIMDJSON_IS_RISCV64 1
|
||||
@@ -148,6 +151,11 @@
|
||||
#define SIMDJSON_NO_SANITIZE_UNDEFINED
|
||||
#endif
|
||||
|
||||
#if defined(__clang__) || defined(__GNUC__)
|
||||
#define simdjson_pure [[gnu::pure]]
|
||||
#else
|
||||
#define simdjson_pure
|
||||
#endif
|
||||
|
||||
#if defined(__clang__) || defined(__GNUC__)
|
||||
#if defined(__has_feature)
|
||||
|
||||
@@ -41,13 +41,13 @@ static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars
|
||||
simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t value2) {
|
||||
internal::value128 answer;
|
||||
#if SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
|
||||
#ifdef _M_ARM64
|
||||
#if SIMDJSON_IS_ARM64
|
||||
// ARM64 has native support for 64-bit multiplications, no need to emultate
|
||||
answer.high = __umulh(value1, value2);
|
||||
answer.low = value1 * value2;
|
||||
#else
|
||||
answer.low = _umul128(value1, value2, &answer.high); // _umul128 not available on ARM64
|
||||
#endif // _M_ARM64
|
||||
#endif // SIMDJSON_IS_ARM64
|
||||
#else // SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
|
||||
__uint128_t r = (static_cast<__uint128_t>(value1)) * value2;
|
||||
answer.low = uint64_t(r);
|
||||
|
||||
@@ -96,11 +96,11 @@ template <> struct simd8<bool> : base8<bool> {
|
||||
return (__m128i)vec_splats((unsigned char)(-(!!_value)));
|
||||
}
|
||||
|
||||
simdjson_inline simd8<bool>() : base8<bool>() {}
|
||||
simdjson_inline simd8<bool>(const __m128i _value)
|
||||
simdjson_inline simd8() : base8<bool>() {}
|
||||
simdjson_inline simd8(const __m128i _value)
|
||||
: base8<bool>(_value) {}
|
||||
// Splat constructor
|
||||
simdjson_inline simd8<bool>(bool _value)
|
||||
simdjson_inline simd8(bool _value)
|
||||
: base8<bool>(splat(_value)) {}
|
||||
|
||||
simdjson_inline int to_bitmask() const {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#define SIMDJSON_SIMDJSON_VERSION_H
|
||||
|
||||
/** The version of simdjson being used (major.minor.revision) */
|
||||
#define SIMDJSON_VERSION "3.8.0"
|
||||
#define SIMDJSON_VERSION "3.9.4"
|
||||
|
||||
namespace simdjson {
|
||||
enum {
|
||||
@@ -15,11 +15,11 @@ enum {
|
||||
/**
|
||||
* The minor version (major.MINOR.revision) of simdjson being used.
|
||||
*/
|
||||
SIMDJSON_VERSION_MINOR = 8,
|
||||
SIMDJSON_VERSION_MINOR = 9,
|
||||
/**
|
||||
* The revision (major.minor.REVISION) of simdjson being used.
|
||||
*/
|
||||
SIMDJSON_VERSION_REVISION = 0
|
||||
SIMDJSON_VERSION_REVISION = 4
|
||||
};
|
||||
} // namespace simdjson
|
||||
|
||||
|
||||
@@ -35,13 +35,13 @@ static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars
|
||||
simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t value2) {
|
||||
internal::value128 answer;
|
||||
#if SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
|
||||
#ifdef _M_ARM64
|
||||
#if SIMDJSON_IS_ARM64
|
||||
// ARM64 has native support for 64-bit multiplications, no need to emultate
|
||||
answer.high = __umulh(value1, value2);
|
||||
answer.low = value1 * value2;
|
||||
#else
|
||||
answer.low = _umul128(value1, value2, &answer.high); // _umul128 not available on ARM64
|
||||
#endif // _M_ARM64
|
||||
#endif // SIMDJSON_IS_ARM64
|
||||
#else // SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
|
||||
__uint128_t r = (static_cast<__uint128_t>(value1)) * value2;
|
||||
answer.low = uint64_t(r);
|
||||
|
||||
@@ -59,10 +59,10 @@ namespace simd {
|
||||
struct simd8<bool>: base8<bool> {
|
||||
static simdjson_inline simd8<bool> splat(bool _value) { return _mm_set1_epi8(uint8_t(-(!!_value))); }
|
||||
|
||||
simdjson_inline simd8<bool>() : base8() {}
|
||||
simdjson_inline simd8<bool>(const __m128i _value) : base8<bool>(_value) {}
|
||||
simdjson_inline simd8() : base8() {}
|
||||
simdjson_inline simd8(const __m128i _value) : base8<bool>(_value) {}
|
||||
// Splat constructor
|
||||
simdjson_inline simd8<bool>(bool _value) : base8<bool>(splat(_value)) {}
|
||||
simdjson_inline simd8(bool _value) : base8<bool>(splat(_value)) {}
|
||||
|
||||
simdjson_inline int to_bitmask() const { return _mm_movemask_epi8(*this); }
|
||||
simdjson_inline bool any() const { return !_mm_testz_si128(*this, *this); }
|
||||
|
||||
@@ -38,8 +38,8 @@ else:
|
||||
|
||||
RelativeRoot = str # Literal['src','include'] # Literal not supported in Python 3.7 (CI)
|
||||
RELATIVE_ROOTS: List[RelativeRoot] = ['src', 'include' ]
|
||||
Implementation = str # Literal['arm64', 'fallback', 'haswell', 'icelake', 'ppc64', 'westmere'] # Literal not supported in Python 3.7 (CI)
|
||||
IMPLEMENTATIONS: List[Implementation] = [ 'arm64', 'haswell', 'icelake', 'ppc64', 'westmere', 'fallback' ]
|
||||
Implementation = str # Literal['arm64', 'fallback', 'haswell', 'icelake', 'ppc64', 'westmere', 'lsx', 'lasx'] # Literal not supported in Python 3.7 (CI)
|
||||
IMPLEMENTATIONS: List[Implementation] = [ 'arm64', 'haswell', 'icelake', 'lasx', 'lsx', 'ppc64', 'westmere', 'fallback' ]
|
||||
GENERIC_INCLUDE = "simdjson/generic"
|
||||
GENERIC_SRC = "generic"
|
||||
BUILTIN = "simdjson/builtin"
|
||||
|
||||
+12288
-85
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user