Compare commits

..

27 Commits

Author SHA1 Message Date
Daniel Lemire 82563dcf70 correcting version bump 2024-04-04 14:08:25 -04:00
Daniel Lemire 646bd3f2c3 version bump 2024-04-04 12:24:07 -04:00
Daniel Lemire 4b0a908bb2 [skip ci] adding documentation regarding new hardware support 2024-04-04 12:22:03 -04:00
Jinyang He 4c98e51c53 Add LoongArch LSX and LASX support (#2159)
* Add LoongArch SX support

* Add LoongArch ASX support
2024-04-04 12:09:36 -04:00
Daniel Lemire 58e3d5d9cc fix: fix path to cross-compiler 2024-04-03 22:37:46 -04:00
Daniel Lemire a22a92dc72 trying to add loong to our ci tests (#2160)
* trying to add loong to our ci tests

* tuning loongarch

* updating the loong toolset
2024-04-03 15:18:45 -04:00
Daniel Lemire c4654553dd [skip ci] windows and unicode 2024-03-26 09:31:50 -04:00
Daniel Lemire 5997f82c37 Update basics.md 2024-03-20 14:26:11 -04:00
Daniel Lemire bee410b315 comment 2024-03-20 01:52:42 -04:00
Daniel Lemire b115e3ef61 some updates to our CI (#2151) 2024-03-17 11:59:50 -04:00
Daniel Lemire 669229df5d more documentation. 2024-03-17 10:29:41 -04:00
Daniel Lemire b6a2286e62 adding some documentation for escaped_key(). 2024-03-17 10:27:13 -04:00
Twice 3a97c57773 Add escaped_key method to ondemand::field (#2150) 2024-03-17 10:10:31 -04:00
Daniel Lemire e1c6a778f8 3.8.0 2024-03-10 13:25:53 -04:00
Daniel Lemire d413dd5b0e v3.7.1 2024-03-10 13:24:07 -04:00
Daniel Lemire a33fdc2770 Unconfusing clang (#2147)
* In some cases, clang might try to cast an ondemand::document to an
ondemand::document, instead of calling the move constructor. So we
can disable the template cast.

* In some cases, clang might get confused when constructing an ondemand
document in a constructor: instead of calling the move constructor, it
somehow ends up trying to cast a document to a document. We can easily
disallow this behavior with std::enable_if.

* making compatible with C++11

---------

Co-authored-by: Daniel Lemire <dlemire@lemire.me>
2024-03-10 13:20:39 -04:00
Daniel Lemire 87f5f7a250 added some documentation in the code related to custom types (#2146) 2024-03-09 18:45:05 -05:00
Daniel Lemire 9923d27994 upper case 2024-03-06 16:01:21 -05:00
Piotr Rżysko 876be23545 Add link to simdjson-java (#2145) 2024-03-06 15:55:16 -05:00
Daniel Lemire 2028bdaeac This documents the big-int feature, and adds a few tests. (#2142)
* This documents the big-int feature, and adds a few tests.

* moving check_if_integer

* trimming the example.

* More trimming.

---------

Co-authored-by: Daniel Lemire <dlemire@lemire.me>
2024-02-26 20:39:20 -05:00
Daniel Lemire 1ac3a00ca9 verify issue 2137 (#2138)
* fix issue 2137

* updating the single header

---------

Co-authored-by: Daniel Lemire <dlemire@lemire.me>
2024-02-26 20:39:08 -05:00
Daniel Lemire 6d7cc2c7c1 fixing build 2024-02-26 20:00:17 -05:00
Daniel Lemire 61a52287fc Fixing apple builds 2024-02-26 19:50:22 -05:00
Daniel Lemire 37a9aaad48 [no ci] tweaking the wording 2024-02-26 14:05:12 -05:00
Serge Aleynikov 78325888ea Add functionality to detect big integers (#2139) 2024-02-26 14:00:33 -05:00
Shi Pujin a90cb5d900 add loongarch64 (#2141) 2024-02-26 13:54:49 -05:00
Daniel Lemire 34a01ec3af removing unnecessary spaces 2024-02-23 19:21:31 -05:00
101 changed files with 42816 additions and 707 deletions
+1 -1
View File
@@ -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') }}
+3 -3
View File
@@ -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/
+50
View File
@@ -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
+1 -1
View File
@@ -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') }}
+1 -1
View File
@@ -25,7 +25,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v3
- uses: actions/cache@v4
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
+1 -1
View File
@@ -27,7 +27,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v3
- uses: actions/cache@v4
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
+1 -1
View File
@@ -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') }}
+1 -1
View File
@@ -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') }}
+1 -1
View File
@@ -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') }}
+1 -1
View File
@@ -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') }}
+2 -2
View File
@@ -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') }}
+1 -1
View File
@@ -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') }}
+1 -1
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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') }}
@@ -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') }}
+3 -3
View File
@@ -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
+1 -1
View File
@@ -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 -1
View File
@@ -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') }}
+7 -1
View File
@@ -103,6 +103,12 @@
"charconv": "cpp",
"source_location": "cpp",
"strstream": "cpp",
"typeindex": "cpp"
"typeindex": "cpp",
"*.tcc": "cpp",
"memory_resource": "cpp",
"numbers": "cpp",
"semaphore": "cpp",
"stop_token": "cpp",
"cfenv": "cpp"
}
}
+21 -3
View File
@@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.14)
project(
simdjson
# The version number is modified by tools/release.py
VERSION 3.7.0
VERSION 3.9.0
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 "20.0.0" CACHE STRING "simdjson library version")
set(SIMDJSON_LIB_SOVERSION "20" 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
+1 -1
View File
@@ -38,7 +38,7 @@ PROJECT_NAME = simdjson
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = "3.7.0"
PROJECT_NUMBER = "3.9.0"
# 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
-4
View File
@@ -283,8 +283,6 @@ If your compiler does not default on C++11 support or better you may get failing
Note that the name of directory (`build`) is arbitrary, you can name it as you want (e.g., `buildgcc`) and you can have as many different such directories as you would like (one per configuration).
## Usage (CMake on 64-bit Windows using Visual Studio 2019 or better)
Recent versions of Visual Studio support CMake natively, [please refer to the Visual Studio documentation](https://learn.microsoft.com/en-us/cpp/build/cmake-projects-in-visual-studio?view=msvc-170).
@@ -309,13 +307,11 @@ Though having Visual Studio installed is necessary, one can build simdjson using
Furthermore, if you have installed LLVM clang on Windows, for example as a component of Visual Studio 2019, you can configure and build simdjson using LLVM clang on Windows using cmake:
- `mkdir build`
- `cd build`
- `cmake -T ClangCL ..`
- `cmake --build . --config Release`
## Various References
- [How to implement atoi using SIMD?](https://stackoverflow.com/questions/35127060/how-to-implement-atoi-using-simd)
+1
View File
@@ -169,6 +169,7 @@ We distinguish between "bindings" (which just wrap the C++ code) and a port to a
- [JSON-Simd](https://github.com/rawleyfowler/JSON-simd): Raku bindings.
- [JSON::SIMD](https://metacpan.org/pod/JSON::SIMD): Perl bindings; fully-featured JSON module that uses simdjson for decoding.
- [gemmaJSON](https://github.com/sainttttt/gemmaJSON): Nim JSON parser based on simdjson bindings.
- [simdjson-java](https://github.com/simdjson/simdjson-java): Java port.
About simdjson
--------------
-1
View File
@@ -993,7 +993,6 @@ struct AppleEvents {
// check permission
int force_ctrs = 0;
if (kpc_force_all_ctrs_get(&force_ctrs)) {
printf("Permission denied, xnu/kpc requires root privileges.\n");
return (worked = false);
}
int ret;
+4 -4
View File
@@ -22,7 +22,7 @@ endif()
# The bulk of our benchmarking and testing data has been moved simdjson/simdjson-data
import_dependency(simdjson-data simdjson/simdjson-data a5b13babe65c1bba7186b41b43d4cbdc20a5c470)
add_dependency(simdjson-data)
option(SIMDJSON_USE_BOOST_JSON "Try to include BOOST_JSON, this may break your binaries under some systems." OFF)
# This prevents variables declared with set() from unnecessarily escaping and
# should not be called more than once
function(competition_scope_)
@@ -36,8 +36,8 @@ function(competition_scope_)
#endif
int main() {}
]] USE_BOOST_JSON)
if(USE_BOOST_JSON)
]] SIMDJSON_FOUND_STRING_VIEW)
if(SIMDJSON_FOUND_STRING_VIEW AND SIMDJSON_USE_BOOST_JSON)
import_dependency(boostjson boostorg/json ee8d72d)
add_library(boostjson STATIC "${boostjson_SOURCE_DIR}/src/src.cpp")
target_compile_definitions(boostjson PUBLIC BOOST_JSON_STANDALONE)
@@ -134,7 +134,7 @@ int main() {}
add_library(competition-core INTERFACE)
target_link_libraries(competition-core INTERFACE nlohmann_json rapidjson sajson cjson jsmn yyjson)
if(USE_BOOST_JSON)
if(TARGET boostjson)
target_compile_definitions(boostjson INTERFACE HAS_BOOST_JSON)
target_link_libraries(competition-core INTERFACE boostjson)
endif()
+92 -16
View File
@@ -212,6 +212,12 @@ 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
-----------------------
@@ -329,7 +335,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.
@@ -372,7 +378,7 @@ support for users who avoid exceptions. See [the simdjson error handling documen
Debug mode: 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 +387,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 +399,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()); }
> }
@@ -428,7 +439,8 @@ support for users who avoid exceptions. See [the simdjson error handling documen
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.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.
* **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.
@@ -548,7 +560,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 +599,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;
@@ -691,6 +706,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();
@@ -1458,7 +1475,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);
@@ -1877,7 +1894,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.
@@ -1910,8 +1927,10 @@ An `ondemand::number` instance may contain an integer value or a floating-point
Thus it is a dynamically typed number. Before accessing the value, you must determine the detected type:
* `number.get_number_type()` has value `number_type::signed_integer` if we have a integer in [-9223372036854775808,9223372036854775808). You can recover the value by the `get_int64()` method applied on the `ondemand::number` instance. When `number.get_number_type()` has value `number_type::signed_integer`, you also have that `number.is_int64()` is true. Calling `get_int64()` on the `ondemand::number` instance when `number.get_number_type()` is not `number_type::signed_integer` is unsafe. You may replace `get_int64()` by a cast to a `int64_t` value.
* `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::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 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.
@@ -1944,6 +1963,9 @@ Consider the following example:
std::cout << "float: " << double(num) << " ";
std::cout << "float: " << num.get_double() << std::endl;
break;
case ondemand::number_type::big_integer:
std::cout << "big-integer: " << val.raw_json_token() << std::endl;
break;
}
}
```
@@ -1959,6 +1981,55 @@ It will output:
9999999999999999999 negative: 0 is_integer: 1 large 64-bit integer: 9999999999999999999 large 64-bit integer: 9999999999999999999
```
In the following example, we have an array of integers that are outside the valid range of 64-bit signed or
unsigned integers. Calling `get_number_type()` on the values returns `ondemand::number_type::big_integer`.
You can try to represent these big integers as 64-bit floating-point numbers, though you typically lose
precision in the process (as illustrated in the example).
```C++
ondemand::parser parser;
padded_string docdata = R"([-9223372036854775809, 18446744073709551617, 99999999999999999999999 ])"_padded;
double dexpected[] = {-9223372036854775808.0, 18446744073709551616.0, 1e23};
ondemand::document doc = parser.iterate(docdata);
ondemand::array arr = doc.get_array();
for(ondemand::value val : arr) {
if(val.get_number_type() == ondemand::number_type::big_integer) {
std::cout << val.get_double() << std::endl;
// might print -9.22337e+18, 1.84467e+19, 1e+23
}
}
```
This program might print:
```
-9.22337e+18
1.84467e+19
1e+23
```
You may get access to the underlying string representing the big integer with
`raw_json_token()` and you may parse the resulting number strings using your own parser.
```c++
ondemand::parser parser;
padded_string docdata = R"([-9223372036854775809, 18446744073709551617, 99999999999999999999999 ])"_padded;
ondemand::document doc = parser.iterate(docdata);
ondemand::array arr = doc.get_array();
for(ondemand::value val : arr) {
// val.get_number_type() == ondemand::number_type::big_integer
if(val.get_number_type() == ondemand::number_type::big_integer) {
std::string_view token = val.raw_json_token();
// token = "-9223372036854775809", "18446744073709551617", "99999999999999999999999 "
std::cout << "'" << token << "'" << std::endl;
}
}
```
This code prints the following:
```
'-9223372036854775809'
'18446744073709551617'
'99999999999999999999999 '
```
Raw Strings From Keys
-----------
@@ -1967,7 +2038,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)`
@@ -1995,11 +2066,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;
@@ -2123,9 +2194,9 @@ 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"({
@@ -2150,7 +2221,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++
@@ -2366,7 +2437,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();
@@ -2439,6 +2510,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();
@@ -2452,6 +2525,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();
}
}
@@ -2495,7 +2571,7 @@ Performance Tips
- 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.
+6
View File
@@ -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
---------------------
+3 -1
View File
@@ -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
+1 -1
View File
@@ -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;
+4
View File
@@ -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
+4
View File
@@ -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
+4
View File
@@ -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
+9
View File
@@ -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.
+1
View File
@@ -27,6 +27,7 @@ enum error_code {
F_ATOM_ERROR, ///< Problem while parsing an atom starting with the letter 'f'
N_ATOM_ERROR, ///< Problem while parsing an atom starting with the letter 'n'
NUMBER_ERROR, ///< Problem while parsing a number
BIGINT_ERROR, ///< The integer value exceeds 64 bits
UTF8_ERROR, ///< the input is not valid UTF-8
UNINITIALIZED, ///< unknown error, or uninitialized document
EMPTY, ///< no structural element found
+6 -1
View File
@@ -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
@@ -37,7 +41,8 @@ class dom_parser_implementation;
enum class number_type {
floating_point_number=1, /// a binary64 number
signed_integer, /// a signed integer that fits in a 64-bit word using two's complement
unsigned_integer /// a positive integer larger or equal to 1<<63
unsigned_integer, /// a positive integer larger or equal to 1<<63
big_integer /// a big integer that does not fit in a 64-bit word
};
} // namespace SIMDJSON_IMPLEMENTATION
+59 -10
View File
@@ -20,11 +20,13 @@ namespace numberparsing {
#define WRITE_INTEGER(VALUE, SRC, WRITER) (found_integer((VALUE), (SRC)), (WRITER).append_s64((VALUE)))
#define WRITE_UNSIGNED(VALUE, SRC, WRITER) (found_unsigned_integer((VALUE), (SRC)), (WRITER).append_u64((VALUE)))
#define WRITE_DOUBLE(VALUE, SRC, WRITER) (found_float((VALUE), (SRC)), (WRITER).append_double((VALUE)))
#define BIGINT_NUMBER(SRC) (found_invalid_number((SRC)), BIGINT_ERROR)
#else
#define INVALID_NUMBER(SRC) (NUMBER_ERROR)
#define WRITE_INTEGER(VALUE, SRC, WRITER) (WRITER).append_s64((VALUE))
#define WRITE_UNSIGNED(VALUE, SRC, WRITER) (WRITER).append_u64((VALUE))
#define WRITE_DOUBLE(VALUE, SRC, WRITER) (WRITER).append_double((VALUE))
#define BIGINT_NUMBER(SRC) (BIGINT_ERROR)
#endif
namespace {
@@ -355,6 +357,10 @@ simdjson_inline bool parse_digit(const uint8_t c, I &i) {
return true;
}
simdjson_inline bool is_digit(const uint8_t c) {
return static_cast<uint8_t>(c - '0') <= 9;
}
simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) {
// we continue with the fiction that we have an integer. If the
// floating point number is representable as x * 10^z for some integer
@@ -434,6 +440,23 @@ simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const s
return SUCCESS;
}
simdjson_inline bool check_if_integer(const uint8_t *const src, size_t max_length) {
const uint8_t *const srcend = src + max_length;
bool negative = (*src == '-'); // we can always read at least one character after the '-'
const uint8_t *p = src + uint8_t(negative);
if(p == srcend) { return false; }
if(*p == '0') {
++p;
if(p == srcend) { return true; }
if(jsoncharutils::is_not_structural_or_whitespace(*p)) { return false; }
return true;
}
while(p != srcend && is_digit(*p)) { ++p; }
if(p == srcend) { return true; }
if(jsoncharutils::is_not_structural_or_whitespace(*p)) { return false; }
return true;
}
simdjson_inline size_t significant_digits(const uint8_t * start_digits, size_t digit_count) {
// It is possible that the integer had an overflow.
// We have to handle the case where we have 0.0000somenumber.
@@ -508,6 +531,18 @@ simdjson_inline error_code write_float(const uint8_t *const src, bool negative,
return SUCCESS;
}
// parse the number at src
// define JSON_TEST_NUMBERS for unit testing
//
// It is assumed that the number is followed by a structural ({,},],[) character
// or a white space character. If that is not the case (e.g., when the JSON
// document is made of a single number), then it is necessary to copy the
// content and append a space before calling this function.
//
// Our objective is accurate parsing (ULP of 0) at high speed.
template<typename W>
simdjson_inline error_code parse_number(const uint8_t *const src, W &writer);
// for performance analysis, it is sometimes useful to skip parsing
#ifdef SIMDJSON_SKIPNUMBERPARSING
@@ -586,11 +621,11 @@ simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) {
// The longest positive 64-bit number is 20 digits.
// We do it this way so we don't trigger this branch unless we must.
size_t longest_digit_count = negative ? 19 : 20;
if (digit_count > longest_digit_count) { return INVALID_NUMBER(src); }
if (digit_count > longest_digit_count) { return BIGINT_NUMBER(src); }
if (digit_count == longest_digit_count) {
if (negative) {
// Anything negative above INT64_MAX+1 is invalid
if (i > uint64_t(INT64_MAX)+1) { return INVALID_NUMBER(src); }
if (i > uint64_t(INT64_MAX)+1) { return BIGINT_NUMBER(src); }
WRITE_INTEGER(~i+1, src, writer);
if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return INVALID_NUMBER(src); }
return SUCCESS;
@@ -1060,19 +1095,32 @@ simdjson_unused simdjson_inline simdjson_result<number_type> get_number_type(con
src += uint8_t(negative);
const uint8_t *p = src;
while(static_cast<uint8_t>(*p - '0') <= 9) { p++; }
size_t digit_count = size_t(p - src);
if ( p == src ) { return NUMBER_ERROR; }
if (jsoncharutils::is_structural_or_whitespace(*p)) {
static const uint8_t * smaller_big_integer = reinterpret_cast<const uint8_t *>("9223372036854775808");
// We have an integer.
if(simdjson_unlikely(digit_count > 20)) {
return number_type::big_integer;
}
// If the number is negative and valid, it must be a signed integer.
if(negative) { return number_type::signed_integer; }
if(negative) {
if (simdjson_unlikely(digit_count > 19)) return number_type::big_integer;
if (simdjson_unlikely(digit_count == 19 && memcmp(src, smaller_big_integer, 19) > 0)) {
return number_type::big_integer;
}
return number_type::signed_integer;
}
// Let us check if we have a big integer (>=2**64).
static const uint8_t * two_to_sixtyfour = reinterpret_cast<const uint8_t *>("18446744073709551616");
if((digit_count > 20) || (digit_count == 20 && memcmp(src, two_to_sixtyfour, 20) >= 0)) {
return number_type::big_integer;
}
// The number is positive and smaller than 18446744073709551616 (or 2**64).
// We want values larger or equal to 9223372036854775808 to be unsigned
// integers, and the other values to be signed integers.
int digit_count = int(p - src);
if(digit_count >= 19) {
const uint8_t * smaller_big_integer = reinterpret_cast<const uint8_t *>("9223372036854775808");
if((digit_count >= 20) || (memcmp(src, smaller_big_integer, 19) >= 0)) {
return number_type::unsigned_integer;
}
if((digit_count == 20) || (digit_count >= 19 && memcmp(src, smaller_big_integer, 19) >= 0)) {
return number_type::unsigned_integer;
}
return number_type::signed_integer;
}
@@ -1250,6 +1298,7 @@ inline std::ostream& operator<<(std::ostream& out, number_type type) noexcept {
case number_type::signed_integer: out << "integer in [-9223372036854775808,9223372036854775808)"; break;
case number_type::unsigned_integer: out << "unsigned integer in [9223372036854775808,18446744073709551616)"; break;
case number_type::floating_point_number: out << "floating-point number (binary64)"; break;
case number_type::big_integer: out << "big integer"; break;
default: SIMDJSON_UNREACHABLE();
}
return out;
@@ -1258,4 +1307,4 @@ inline std::ostream& operator<<(std::ostream& out, number_type type) noexcept {
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
#endif // SIMDJSON_GENERIC_NUMBERPARSING_H
#endif // SIMDJSON_GENERIC_NUMBERPARSING_H
@@ -295,7 +295,7 @@ simdjson_inline simdjson_result<number> document::get_number() noexcept {
simdjson_inline simdjson_result<std::string_view> document::raw_json_token() noexcept {
auto _iter = get_root_value_iterator();
return std::string_view(reinterpret_cast<const char*>(_iter.peek_start()), _iter.peek_start_length());
return std::string_view(reinterpret_cast<const char*>(_iter.peek_start()), _iter.peek_root_length());
}
simdjson_inline simdjson_result<value> document::at_pointer(std::string_view json_pointer) noexcept {
@@ -535,6 +535,11 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::number> simdj
#if SIMDJSON_EXCEPTIONS
template <class T, typename std::enable_if<std::is_same<T, SIMDJSON_IMPLEMENTATION::ondemand::document>::value == false>::type>
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::operator T() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::operator SIMDJSON_IMPLEMENTATION::ondemand::array() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
@@ -842,6 +847,11 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::number> simdj
return first.get_number();
}
#if SIMDJSON_EXCEPTIONS
template <class T, typename std::enable_if<std::is_same<T, SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::value == false>::type>
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::operator T() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::operator SIMDJSON_IMPLEMENTATION::ondemand::array() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
+22 -9
View File
@@ -179,21 +179,23 @@ public:
* @returns INCORRECT_TYPE If the JSON value is not the given type.
*/
template<typename T> simdjson_inline simdjson_result<T> get() & noexcept {
// Unless the simdjson library provides an inline implementation, calling this method should
// Unless the simdjson library or the user provides an inline implementation, calling this method should
// immediately fail.
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
"The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), "
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template.");
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template."
" You may also add support for custom types, see our documentation.");
}
/** @overload template<typename T> simdjson_result<T> get() & noexcept */
template<typename T> simdjson_inline simdjson_result<T> get() && noexcept {
// Unless the simdjson library provides an inline implementation, calling this method should
// Unless the simdjson library or the user provides an inline implementation, calling this method should
// immediately fail.
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
"The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), "
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template.");
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template."
" You may also add support for custom types, see our documentation.");
}
/**
@@ -212,6 +214,15 @@ public:
template<typename T> simdjson_inline error_code get(T &out) && noexcept;
#if SIMDJSON_EXCEPTIONS
/**
* Cast this JSON value to an instance of type T. The programmer is responsible for
* providing an implementation of get<T> for the type T, if T is not one of the types
* supported by the library (object, array, raw_json_string, string_view, uint64_t, etc.)
*
* See https://github.com/simdjson/simdjson/blob/master/doc/basics.md#adding-support-for-custom-types
*
* @returns An instance of type T
*/
template <class T>
explicit simdjson_inline operator T() noexcept(false);
/**
@@ -386,7 +397,7 @@ public:
* APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order.
*
* Use find_field() if you are sure fields will be in order (or are willing to treat it as if the
* field wasn't there when they aren't).
* field was not there when they are not in order).
*
* You must consume the fields on an object one at a time. A request for a new key
* invalidates previous field values: it makes them unsafe. E.g., the array
@@ -467,9 +478,11 @@ public:
* get_number().get_number_type().
*
* get_number_type() is number_type::unsigned_integer if we have
* an integer greater or equal to 9223372036854775808
* an integer greater or equal to 9223372036854775808 and no larger than 18446744073709551615.
* get_number_type() is number_type::signed_integer if we have an
* integer that is less than 9223372036854775808
* integer that is less than 9223372036854775808 and greater or equal to -9223372036854775808.
* get_number_type() is number_type::big_integer if we have an integer outside
* of those ranges (either larger than 18446744073709551615 or smaller than -9223372036854775808).
* Otherwise, get_number_type() has value number_type::floating_point_number
*
* This function requires processing the number string, but it is expected
@@ -782,7 +795,7 @@ public:
template<typename T> simdjson_inline error_code get(T &out) & noexcept;
template<typename T> simdjson_inline error_code get(T &out) && noexcept;
#if SIMDJSON_EXCEPTIONS
template <class T>
template <class T, typename std::enable_if<std::is_same<T, SIMDJSON_IMPLEMENTATION::ondemand::document>::value == false>::type>
explicit simdjson_inline operator T() noexcept(false);
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::array() & noexcept(false);
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::object() & noexcept(false);
@@ -853,7 +866,7 @@ public:
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> get_value() noexcept;
simdjson_inline simdjson_result<bool> is_null() noexcept;
#if SIMDJSON_EXCEPTIONS
template <class T>
template <class T, typename std::enable_if<std::is_same<T, SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::value == false>::type>
explicit simdjson_inline operator T() noexcept(false);
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::array() & noexcept(false);
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::object() & noexcept(false);
@@ -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;
};
@@ -286,6 +286,12 @@ simdjson_inline uint32_t json_iterator::peek_length(token_position position) con
#endif // SIMDJSON_CHECK_EOF
return token.peek_length(position);
}
simdjson_inline uint32_t json_iterator::peek_root_length(token_position position) const noexcept {
#if SIMDJSON_CHECK_EOF
assert_valid_position(position);
#endif // SIMDJSON_CHECK_EOF
return token.peek_root_length(position);
}
simdjson_inline token_position json_iterator::last_position() const noexcept {
// The following line fails under some compilers...
@@ -185,6 +185,14 @@ public:
* @param position The position of the token to retrieve.
*/
simdjson_inline uint32_t peek_length(token_position position) const noexcept;
/**
* Get the maximum length of the JSON text for the current root token.
*
* The length will include any whitespace at the end of the token.
*
* @param position The position of the token to retrieve.
*/
simdjson_inline uint32_t peek_root_length(token_position position) const noexcept;
/**
* Get the JSON text for the last token in the document.
*
@@ -48,7 +48,6 @@ simdjson_inline number::operator uint64_t() const noexcept {
return get_uint64();
}
simdjson_inline bool number::is_int64() const noexcept {
return get_number_type() == number_type::signed_integer;
}
+5 -1
View File
@@ -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.
*/
@@ -77,7 +79,7 @@ public:
* APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order.
*
* Use find_field() if you are sure fields will be in order (or are willing to treat it as if the
* field wasn't there when they aren't).
* field was not there when they are not in order).
*
* If you have multiple fields with a matching key ({"x": 1, "x": 1}) be mindful
* that only one field is returned.
@@ -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.
*/
@@ -37,6 +37,11 @@ simdjson_inline uint32_t token_iterator::peek_length(token_position position) co
return *(position+1) - *position;
}
simdjson_inline uint32_t token_iterator::peek_root_length(token_position position) const noexcept {
return *(position+2) - *(position) > *(position+1) - *(position) ?
*(position+1) - *(position)
: *(position+2) - *(position);
}
simdjson_inline const uint8_t *token_iterator::peek(int32_t delta) const noexcept {
return &buf[*(_position+delta)];
}
@@ -77,7 +77,14 @@ public:
* @param position The position of the token.
*/
simdjson_inline uint32_t peek_length(token_position position) const noexcept;
/**
* Get the maximum length of the JSON text for a root token.
*
* The length will include any whitespace at the end of the token.
*
* @param position The position of the token (start of the document).
*/
simdjson_inline uint32_t peek_root_length(token_position position) const noexcept;
/**
* Return the current index.
*/
+16 -9
View File
@@ -36,12 +36,13 @@ public:
* @returns INCORRECT_TYPE If the JSON value is not the given type.
*/
template<typename T> simdjson_inline simdjson_result<T> get() noexcept {
// Unless the simdjson library provides an inline implementation, calling this method should
// Unless the simdjson library or the user provides an inline implementation, calling this method should
// immediately fail.
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
"The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), "
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template.");
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template."
" You may also add support for custom types, see our documentation.");
}
/**
@@ -196,7 +197,10 @@ public:
#if SIMDJSON_EXCEPTIONS
/**
* Cast this JSON value to an instance of type T. The programmer is responsible for
* providing an implementation of get<T> for the type T.
* providing an implementation of get<T> for the type T, if T is not one of the types
* supported by the library (object, array, raw_json_string, string_view, uint64_t, etc.).
*
* See https://github.com/simdjson/simdjson/blob/master/doc/basics.md#adding-support-for-custom-types
*
* @returns An instance of type T
*/
@@ -365,7 +369,7 @@ public:
* that only one field is returned.
*
* Use find_field() if you are sure fields will be in order (or are willing to treat it as if the
* field wasn't there when they aren't).
* field as not there when they are not in order).
*
* @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.
@@ -440,10 +444,12 @@ public:
* get_number().get_number_type().
*
* get_number_type() is number_type::unsigned_integer if we have
* an integer greater or equal to 9223372036854775808
* an integer greater or equal to 9223372036854775808.
* get_number_type() is number_type::signed_integer if we have an
* integer that is less than 9223372036854775808
* Otherwise, get_number_type() has value number_type::floating_point_number
* integer that is less than 9223372036854775808.
* get_number_type() is number_type::big_integer for integers that do not fit in 64 bits,
* in which case the digit_count is set to the length of the big integer string.
* Otherwise, get_number_type() has value number_type::floating_point_number.
*
* This function requires processing the number string, but it is expected
* to be faster than get_number().get_number_type() because it is does not
@@ -470,6 +476,8 @@ public:
* You can recover the value by calling number.get_uint64() and you
* have that number.is_uint64() is true.
*
* For integers that do not fit in 64 bits, the function returns BIGINT_ERROR error code.
*
* Otherwise, number.get_number_type() has value number_type::floating_point_number
* and we have a binary64 number.
* You can recover the value by calling number.get_double() and you
@@ -485,7 +493,6 @@ public:
*/
simdjson_warn_unused simdjson_inline simdjson_result<number> get_number() noexcept;
/**
* Get the raw JSON for this token.
*
@@ -729,7 +736,7 @@ public:
* APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order.
*
* Use find_field() if you are sure fields will be in order (or are willing to treat it as if the
* field wasn't there when they aren't).
* field as not there when they are not in order).
*
* @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.
@@ -137,7 +137,7 @@ simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::find_
} else if (!is_open()) {
#if SIMDJSON_DEVELOPMENT_CHECKS
// If we're past the end of the object, we're being iterated out of order.
// Note: this isn't perfect detection. It's possible the user is inside some other object; if so,
// Note: this is not perfect detection. It's possible the user is inside some other object; if so,
// this object iterator will blithely scan that object for fields.
if (_json_iter->depth() < depth() - 1) { return OUT_OF_ORDER_ITERATION; }
#endif
@@ -249,7 +249,7 @@ simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::find_
#if SIMDJSON_DEVELOPMENT_CHECKS
// If we're past the end of the object, we're being iterated out of order.
// Note: this isn't perfect detection. It's possible the user is inside some other object; if so,
// Note: this is not perfect detection. It's possible the user is inside some other object; if so,
// this object iterator will blithely scan that object for fields.
if (_json_iter->depth() < depth() - 1) { return OUT_OF_ORDER_ITERATION; }
#endif
@@ -591,7 +591,7 @@ simdjson_inline simdjson_result<number> value_iterator::get_number() noexcept {
}
simdjson_inline simdjson_result<bool> value_iterator::is_root_integer(bool check_trailing) noexcept {
auto max_len = peek_start_length();
auto max_len = peek_root_length();
auto json = peek_root_scalar("is_root_integer");
uint8_t tmpbuf[20+1+1]{}; // <20 digits> is the longest possible unsigned integer
tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
@@ -607,7 +607,7 @@ simdjson_inline simdjson_result<bool> value_iterator::is_root_integer(bool check
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::number_type> value_iterator::get_root_number_type(bool check_trailing) noexcept {
auto max_len = peek_start_length();
auto max_len = peek_root_length();
auto json = peek_root_scalar("number");
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
@@ -615,7 +615,12 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::number_type> value_iter
uint8_t tmpbuf[1074+8+1+1];
tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
if(numberparsing::check_if_integer(json, max_len)) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
logger::log_error(*_json_iter, start_position(), depth(), "Found big integer");
return number_type::big_integer;
}
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters and not a big integer");
return NUMBER_ERROR;
}
auto answer = numberparsing::get_number_type(tmpbuf);
@@ -623,15 +628,21 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::number_type> value_iter
return answer;
}
simdjson_inline simdjson_result<number> value_iterator::get_root_number(bool check_trailing) noexcept {
auto max_len = peek_start_length();
auto max_len = peek_root_length();
auto json = peek_root_scalar("number");
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
// NOTE: the current approach doesn't work for very big integer numbers containing more than 1074 digits.
uint8_t tmpbuf[1074+8+1+1];
tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
if(numberparsing::check_if_integer(json, max_len)) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
logger::log_error(*_json_iter, start_position(), depth(), "Found big integer");
return BIGINT_ERROR;
}
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters and not a big integer");
return NUMBER_ERROR;
}
number num;
@@ -663,7 +674,7 @@ simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> value_iter
return raw_json_string(json+1);
}
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::get_root_uint64(bool check_trailing) noexcept {
auto max_len = peek_start_length();
auto max_len = peek_root_length();
auto json = peek_root_scalar("uint64");
uint8_t tmpbuf[20+1+1]{}; // <20 digits> is the longest possible unsigned integer
tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
@@ -679,7 +690,7 @@ simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::g
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::get_root_uint64_in_string(bool check_trailing) noexcept {
auto max_len = peek_start_length();
auto max_len = peek_root_length();
auto json = peek_root_scalar("uint64");
uint8_t tmpbuf[20+1+1]{}; // <20 digits> is the longest possible unsigned integer
tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
@@ -695,7 +706,7 @@ simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::g
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::get_root_int64(bool check_trailing) noexcept {
auto max_len = peek_start_length();
auto max_len = peek_root_length();
auto json = peek_root_scalar("int64");
uint8_t tmpbuf[20+1+1]; // -<19 digits> is the longest possible integer
tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
@@ -712,7 +723,7 @@ simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::ge
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::get_root_int64_in_string(bool check_trailing) noexcept {
auto max_len = peek_start_length();
auto max_len = peek_root_length();
auto json = peek_root_scalar("int64");
uint8_t tmpbuf[20+1+1]; // -<19 digits> is the longest possible integer
tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
@@ -729,7 +740,7 @@ simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::ge
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get_root_double(bool check_trailing) noexcept {
auto max_len = peek_start_length();
auto max_len = peek_root_length();
auto json = peek_root_scalar("double");
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
@@ -749,7 +760,7 @@ simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get
}
simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get_root_double_in_string(bool check_trailing) noexcept {
auto max_len = peek_start_length();
auto max_len = peek_root_length();
auto json = peek_root_scalar("double");
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
@@ -768,7 +779,7 @@ simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::get_root_bool(bool check_trailing) noexcept {
auto max_len = peek_start_length();
auto max_len = peek_root_length();
auto json = peek_root_scalar("bool");
uint8_t tmpbuf[5+1+1]; // +1 for null termination
tmpbuf[5+1] = '\0'; // make sure that buffer is always null terminated.
@@ -781,7 +792,7 @@ simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::get_r
return result;
}
simdjson_inline simdjson_result<bool> value_iterator::is_root_null(bool check_trailing) noexcept {
auto max_len = peek_start_length();
auto max_len = peek_root_length();
auto json = peek_root_scalar("null");
bool result = (max_len >= 4 && !atomparsing::str4ncmp(json, "null") &&
(max_len == 4 || jsoncharutils::is_structural_or_whitespace(json[4])));
@@ -853,6 +864,9 @@ simdjson_inline const uint8_t *value_iterator::peek_start() const noexcept {
simdjson_inline uint32_t value_iterator::peek_start_length() const noexcept {
return _json_iter->peek_length(start_position());
}
simdjson_inline uint32_t value_iterator::peek_root_length() const noexcept {
return _json_iter->peek_root_length(start_position());
}
simdjson_inline const uint8_t *value_iterator::peek_scalar(const char *type) noexcept {
logger::log_value(*_json_iter, start_position(), depth(), type);
@@ -375,6 +375,7 @@ protected:
simdjson_inline simdjson_result<bool> parse_bool(const uint8_t *json) const noexcept;
simdjson_inline const uint8_t *peek_start() const noexcept;
simdjson_inline uint32_t peek_start_length() const noexcept;
simdjson_inline uint32_t peek_root_length() const noexcept;
/**
* The general idea of the advance_... methods and the peek_* methods
+21 -1
View File
@@ -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)
@@ -74,9 +76,23 @@
#endif
#define SIMDJSON_CAN_ALWAYS_RUN_PPC64 SIMDJSON_IMPLEMENTATION_PPC64 && SIMDJSON_IS_PPC64 && SIMDJSON_IS_PPC64_VMX
#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 +114,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
+3 -1
View File
@@ -66,7 +66,9 @@ enum instruction_set {
AVX512CD = 0x2000,
AVX512BW = 0x4000,
AVX512VL = 0x8000,
AVX512VBMI2 = 0x10000
AVX512VBMI2 = 0x10000,
LSX = 0x20000,
LASX = 0x40000,
};
} // namespace internal
+8
View File
@@ -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
+26
View File
@@ -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
+10
View File
@@ -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
+50
View File
@@ -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
+31
View File
@@ -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
+6
View File
@@ -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
+31
View File
@@ -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
+14
View File
@@ -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
+8
View File
@@ -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
+376
View File
@@ -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<bool>() : base8() {}
simdjson_inline simd8<bool>(const __m256i _value) : base8<bool>(_value) {}
// Splat constructor
simdjson_inline simd8<bool>(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
+8
View File
@@ -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
+26
View File
@@ -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
+10
View File
@@ -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
+50
View File
@@ -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
+31
View File
@@ -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
+6
View File
@@ -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
+31
View File
@@ -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
+14
View File
@@ -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
+41
View File
@@ -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
+8
View File
@@ -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
+354
View File
@@ -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<bool>() : base8() {}
simdjson_inline simd8<bool>(const __m128i _value) : base8<bool>(_value) {}
// Splat constructor
simdjson_inline simd8<bool>(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
+53
View File
@@ -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
+9
View File
@@ -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.
*
+2
View File
@@ -38,6 +38,8 @@
#define SIMDJSON_IS_ARM64 1
#elif defined(__riscv) && __riscv_xlen == 64
#define SIMDJSON_IS_RISCV64 1
#elif defined(__loongarch_lp64)
#define SIMDJSON_IS_LOONGARCH64 1
#elif defined(__PPC64__) || defined(_M_PPC64)
#if defined(__ALTIVEC__)
#define SIMDJSON_IS_PPC64_VMX 1
+2 -2
View File
@@ -4,7 +4,7 @@
#define SIMDJSON_SIMDJSON_VERSION_H
/** The version of simdjson being used (major.minor.revision) */
#define SIMDJSON_VERSION "3.7.0"
#define SIMDJSON_VERSION "3.9.0"
namespace simdjson {
enum {
@@ -15,7 +15,7 @@ enum {
/**
* The minor version (major.MINOR.revision) of simdjson being used.
*/
SIMDJSON_VERSION_MINOR = 7,
SIMDJSON_VERSION_MINOR = 9,
/**
* The revision (major.minor.REVISION) of simdjson being used.
*/
+2 -2
View File
@@ -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"
+12716 -256
View File
File diff suppressed because it is too large Load Diff
+27770 -287
View File
File diff suppressed because it is too large Load Diff
+4 -4
View File
@@ -53,8 +53,8 @@ simdjson_inline bool handle_unicode_codepoint(const uint8_t **src_ptr,
// Use the default Unicode Character 'REPLACEMENT CHARACTER' (U+FFFD)
constexpr uint32_t substitution_code_point = 0xfffd;
// jsoncharutils::hex_to_u32_nocheck fills high 16 bits of the return value with 1s if the
// conversion isn't valid; we defer the check for this to inside the
// multilingual plane check
// conversion is not valid; we defer the check for this to inside the
// multilingual plane check.
uint32_t code_point = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2);
*src_ptr += 6;
@@ -111,8 +111,8 @@ simdjson_inline bool handle_unicode_codepoint_wobbly(const uint8_t **src_ptr,
// It is not ideal that this function is nearly identical to handle_unicode_codepoint.
//
// jsoncharutils::hex_to_u32_nocheck fills high 16 bits of the return value with 1s if the
// conversion isn't valid; we defer the check for this to inside the
// multilingual plane check
// conversion is not valid; we defer the check for this to inside the
// multilingual plane check.
uint32_t code_point = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2);
*src_ptr += 6;
// If we found a high surrogate, we must
+37
View File
@@ -93,6 +93,30 @@ static const simdjson::westmere::implementation* get_westmere_singleton() {
} // namespace simdjson
#endif // SIMDJSON_IMPLEMENTATION_WESTMERE
#if SIMDJSON_IMPLEMENTATION_LSX
#include <simdjson/lsx/implementation.h>
namespace simdjson {
namespace internal {
static const simdjson::lsx::implementation* get_lsx_singleton() {
static const simdjson::lsx::implementation lsx_singleton{};
return &lsx_singleton;
}
} // namespace internal
} // namespace simdjson
#endif // SIMDJSON_IMPLEMENTATION_LSX
#if SIMDJSON_IMPLEMENTATION_LASX
#include <simdjson/lasx/implementation.h>
namespace simdjson {
namespace internal {
static const simdjson::lasx::implementation* get_lasx_singleton() {
static const simdjson::lasx::implementation lasx_singleton{};
return &lasx_singleton;
}
} // namespace internal
} // namespace simdjson
#endif // SIMDJSON_IMPLEMENTATION_LASX
#undef SIMDJSON_CONDITIONAL_INCLUDE
namespace simdjson {
@@ -104,6 +128,7 @@ namespace internal {
#define SIMDJSON_SINGLE_IMPLEMENTATION (SIMDJSON_IMPLEMENTATION_ICELAKE \
+ SIMDJSON_IMPLEMENTATION_HASWELL + SIMDJSON_IMPLEMENTATION_WESTMERE \
+ SIMDJSON_IMPLEMENTATION_ARM64 + SIMDJSON_IMPLEMENTATION_PPC64 \
+ SIMDJSON_IMPLEMENTATION_LSX + SIMDJSON_IMPLEMENTATION_LASX \
+ SIMDJSON_IMPLEMENTATION_FALLBACK == 1)
#if SIMDJSON_SINGLE_IMPLEMENTATION
@@ -124,6 +149,12 @@ namespace internal {
#if SIMDJSON_IMPLEMENTATION_PPC64
get_ppc64_singleton();
#endif
#if SIMDJSON_IMPLEMENTATION_LSX
get_lsx_singleton();
#endif
#if SIMDJSON_IMPLEMENTATION_LASX
get_lasx_singleton();
#endif
#if SIMDJSON_IMPLEMENTATION_FALLBACK
get_fallback_singleton();
#endif
@@ -176,6 +207,12 @@ static const std::initializer_list<const implementation *>& get_available_implem
#if SIMDJSON_IMPLEMENTATION_PPC64
get_ppc64_singleton(),
#endif
#if SIMDJSON_IMPLEMENTATION_LSX
get_lsx_singleton(),
#endif
#if SIMDJSON_IMPLEMENTATION_LASX
get_lasx_singleton(),
#endif
#if SIMDJSON_IMPLEMENTATION_FALLBACK
get_fallback_singleton(),
#endif
+1
View File
@@ -18,6 +18,7 @@ namespace internal {
{ F_ATOM_ERROR, "F_ATOM_ERROR: Problem while parsing an atom starting with the letter 'f'" },
{ N_ATOM_ERROR, "N_ATOM_ERROR: Problem while parsing an atom starting with the letter 'n'" },
{ NUMBER_ERROR, "NUMBER_ERROR: Problem while parsing a number" },
{ BIGINT_ERROR, "BIGINT_ERROR: Big integer value that cannot be represented using 64 bits" },
{ UTF8_ERROR, "UTF8_ERROR: The input is not valid UTF-8" },
{ UNINITIALIZED, "UNINITIALIZED: Uninitialized" },
{ EMPTY, "EMPTY: no JSON found" },
+13
View File
@@ -218,6 +218,19 @@ static inline uint32_t detect_supported_architectures() {
return host_isa;
}
#elif defined(__loongarch_sx) && !defined(__loongarch_asx)
static inline uint32_t detect_supported_architectures() {
return instruction_set::LSX;
}
#elif defined(__loongarch_asx)
static inline uint32_t detect_supported_architectures() {
return instruction_set::LASX;
}
#else // fallback
+2 -2
View File
@@ -3,7 +3,7 @@
#include <simdjson/implementation_detection.h>
#if SIMDJSON_IMPLEMENTATION_ARM64 || SIMDJSON_IMPLEMENTATION_ICELAKE || SIMDJSON_IMPLEMENTATION_HASWELL || SIMDJSON_IMPLEMENTATION_WESTMERE || SIMDJSON_IMPLEMENTATION_PPC64
#if SIMDJSON_IMPLEMENTATION_ARM64 || SIMDJSON_IMPLEMENTATION_ICELAKE || SIMDJSON_IMPLEMENTATION_HASWELL || SIMDJSON_IMPLEMENTATION_WESTMERE || SIMDJSON_IMPLEMENTATION_PPC64 || SIMDJSON_IMPLEMENTATION_LSX || SIMDJSON_IMPLEMENTATION_LASX
#include <cstdint>
@@ -133,6 +133,6 @@ SIMDJSON_DLLIMPORTEXPORT const uint64_t thintable_epi8[256] = {
} // namespace internal
} // namespace simdjson
#endif // SIMDJSON_IMPLEMENTATION_ARM64 || SIMDJSON_IMPLEMENTATION_ICELAKE || SIMDJSON_IMPLEMENTATION_HASWELL || SIMDJSON_IMPLEMENTATION_WESTMERE || SIMDJSON_IMPLEMENTATION_PPC64
#endif // SIMDJSON_IMPLEMENTATION_ARM64 || SIMDJSON_IMPLEMENTATION_ICELAKE || SIMDJSON_IMPLEMENTATION_HASWELL || SIMDJSON_IMPLEMENTATION_WESTMERE || SIMDJSON_IMPLEMENTATION_PPC64 || SIMDJSON_IMPLEMENTATION_LSX || SIMDJSON_IMPLEMENTATION_LASX
#endif // SIMDJSON_SRC_SIMDPRUNE_TABLES_CPP
+132
View File
@@ -0,0 +1,132 @@
#ifndef SIMDJSON_SRC_LASX_CPP
#define SIMDJSON_SRC_LASX_CPP
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
#include <base.h>
#endif // SIMDJSON_CONDITIONAL_INCLUDE
#include <simdjson/lasx.h>
#include <simdjson/lasx/implementation.h>
#include <simdjson/lasx/begin.h>
#include <generic/amalgamated.h>
#include <generic/stage1/amalgamated.h>
#include <generic/stage2/amalgamated.h>
//
// Stage 1
//
namespace simdjson {
namespace lasx {
simdjson_warn_unused error_code implementation::create_dom_parser_implementation(
size_t capacity,
size_t max_depth,
std::unique_ptr<internal::dom_parser_implementation>& dst
) const noexcept {
dst.reset( new (std::nothrow) dom_parser_implementation() );
if (!dst) { return MEMALLOC; }
if (auto err = dst->set_capacity(capacity))
return err;
if (auto err = dst->set_max_depth(max_depth))
return err;
return SUCCESS;
}
namespace {
using namespace simd;
simdjson_inline json_character_block json_character_block::classify(const simd::simd8x64<uint8_t>& in) {
// Inspired by haswell.
// LASX use low 5 bits as index. For the 6 operators (:,[]{}), the unique-5bits is [6:2].
// The ASCII white-space and operators have these values: (char, hex, unique-5bits)
// (' ', 20, 00000) ('\t', 09, 01001) ('\n', 0A, 01010) ('\r', 0D, 01101)
// (',', 2C, 01011) (':', 3A, 01110) ('[', 5B, 10110) ('{', 7B, 11110) (']', 5D, 10111) ('}', 7D, 11111)
const simd8<uint8_t> ws_table = simd8<uint8_t>::repeat_16(
' ', 0, 0, 0, 0, 0, 0, 0, 0, '\t', '\n', 0, 0, '\r', 0, 0
);
const simd8<uint8_t> op_table_lo = simd8<uint8_t>::repeat_16(
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ',', 0, 0, ':', 0
);
const simd8<uint8_t> op_table_hi = simd8<uint8_t>::repeat_16(
0, 0, 0, 0, 0, 0, '[', ']', 0, 0, 0, 0, 0, 0, '{', '}'
);
uint64_t ws = in.eq({
in.chunks[0].lookup_16(ws_table),
in.chunks[1].lookup_16(ws_table),
});
uint64_t op = in.eq({
__lasx_xvshuf_b(op_table_hi, op_table_lo, in.chunks[0].shr<2>()),
__lasx_xvshuf_b(op_table_hi, op_table_lo, in.chunks[1].shr<2>()),
});
return { ws, op };
}
simdjson_inline bool is_ascii(const simd8x64<uint8_t>& input) {
return input.reduce_or().is_ascii();
}
simdjson_inline simd8<uint8_t> must_be_2_3_continuation(const simd8<uint8_t> prev2, const simd8<uint8_t> prev3) {
simd8<uint8_t> is_third_byte = prev2.saturating_sub(0xe0u-0x80); // Only 111_____ will be >= 0x80
simd8<uint8_t> is_fourth_byte = prev3.saturating_sub(0xf0u-0x80); // Only 1111____ will be >= 0x80
return is_third_byte | is_fourth_byte;
}
} // unnamed namespace
} // namespace lasx
} // namespace simdjson
//
// Stage 2
//
//
// Implementation-specific overrides
//
namespace simdjson {
namespace lasx {
simdjson_warn_unused error_code implementation::minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept {
return lasx::stage1::json_minifier::minify<64>(buf, len, dst, dst_len);
}
simdjson_warn_unused error_code dom_parser_implementation::stage1(const uint8_t *_buf, size_t _len, stage1_mode streaming) noexcept {
this->buf = _buf;
this->len = _len;
return lasx::stage1::json_structural_indexer::index<64>(buf, len, *this, streaming);
}
simdjson_warn_unused bool implementation::validate_utf8(const char *buf, size_t len) const noexcept {
return lasx::stage1::generic_validate_utf8(buf,len);
}
simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document &_doc) noexcept {
return stage2::tape_builder::parse_document<false>(*this, _doc);
}
simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::document &_doc) noexcept {
return stage2::tape_builder::parse_document<true>(*this, _doc);
}
simdjson_warn_unused uint8_t *dom_parser_implementation::parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) const noexcept {
return lasx::stringparsing::parse_string(src, dst, allow_replacement);
}
simdjson_warn_unused uint8_t *dom_parser_implementation::parse_wobbly_string(const uint8_t *src, uint8_t *dst) const noexcept {
return lasx::stringparsing::parse_wobbly_string(src, dst);
}
simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept {
auto error = stage1(_buf, _len, stage1_mode::regular);
if (error) { return error; }
return stage2(_doc);
}
} // namespace lasx
} // namespace simdjson
#include <simdjson/lasx/end.h>
#endif // SIMDJSON_SRC_LASX_CPP
+136
View File
@@ -0,0 +1,136 @@
#ifndef SIMDJSON_SRC_LSX_CPP
#define SIMDJSON_SRC_LSX_CPP
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
#include <base.h>
#endif // SIMDJSON_CONDITIONAL_INCLUDE
#include <simdjson/lsx.h>
#include <simdjson/lsx/implementation.h>
#include <simdjson/lsx/begin.h>
#include <generic/amalgamated.h>
#include <generic/stage1/amalgamated.h>
#include <generic/stage2/amalgamated.h>
//
// Stage 1
//
namespace simdjson {
namespace lsx {
simdjson_warn_unused error_code implementation::create_dom_parser_implementation(
size_t capacity,
size_t max_depth,
std::unique_ptr<internal::dom_parser_implementation>& dst
) const noexcept {
dst.reset( new (std::nothrow) dom_parser_implementation() );
if (!dst) { return MEMALLOC; }
if (auto err = dst->set_capacity(capacity))
return err;
if (auto err = dst->set_max_depth(max_depth))
return err;
return SUCCESS;
}
namespace {
using namespace simd;
simdjson_inline json_character_block json_character_block::classify(const simd::simd8x64<uint8_t>& in) {
// Inspired by haswell.
// LSX use low 5 bits as index. For the 6 operators (:,[]{}), the unique-5bits is [6:2].
// The ASCII white-space and operators have these values: (char, hex, unique-5bits)
// (' ', 20, 00000) ('\t', 09, 01001) ('\n', 0A, 01010) ('\r', 0D, 01101)
// (',', 2C, 01011) (':', 3A, 01110) ('[', 5B, 10110) ('{', 7B, 11110) (']', 5D, 10111) ('}', 7D, 11111)
const simd8<uint8_t> ws_table = simd8<uint8_t>::repeat_16(
' ', 0, 0, 0, 0, 0, 0, 0, 0, '\t', '\n', 0, 0, '\r', 0, 0
);
const simd8<uint8_t> op_table_lo = simd8<uint8_t>::repeat_16(
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ',', 0, 0, ':', 0
);
const simd8<uint8_t> op_table_hi = simd8<uint8_t>::repeat_16(
0, 0, 0, 0, 0, 0, '[', ']', 0, 0, 0, 0, 0, 0, '{', '}'
);
uint64_t ws = in.eq({
in.chunks[0].lookup_16(ws_table),
in.chunks[1].lookup_16(ws_table),
in.chunks[2].lookup_16(ws_table),
in.chunks[3].lookup_16(ws_table)
});
uint64_t op = in.eq({
__lsx_vshuf_b(op_table_hi, op_table_lo, in.chunks[0].shr<2>()),
__lsx_vshuf_b(op_table_hi, op_table_lo, in.chunks[1].shr<2>()),
__lsx_vshuf_b(op_table_hi, op_table_lo, in.chunks[2].shr<2>()),
__lsx_vshuf_b(op_table_hi, op_table_lo, in.chunks[3].shr<2>())
});
return { ws, op };
}
simdjson_inline bool is_ascii(const simd8x64<uint8_t>& input) {
return input.reduce_or().is_ascii();
}
simdjson_inline simd8<uint8_t> must_be_2_3_continuation(const simd8<uint8_t> prev2, const simd8<uint8_t> prev3) {
simd8<uint8_t> is_third_byte = prev2.saturating_sub(0xe0u-0x80); // Only 111_____ will be >= 0x80
simd8<uint8_t> is_fourth_byte = prev3.saturating_sub(0xf0u-0x80); // Only 1111____ will be >= 0x80
return is_third_byte | is_fourth_byte;
}
} // unnamed namespace
} // namespace lsx
} // namespace simdjson
//
// Stage 2
//
//
// Implementation-specific overrides
//
namespace simdjson {
namespace lsx {
simdjson_warn_unused error_code implementation::minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept {
return lsx::stage1::json_minifier::minify<64>(buf, len, dst, dst_len);
}
simdjson_warn_unused error_code dom_parser_implementation::stage1(const uint8_t *_buf, size_t _len, stage1_mode streaming) noexcept {
this->buf = _buf;
this->len = _len;
return lsx::stage1::json_structural_indexer::index<64>(buf, len, *this, streaming);
}
simdjson_warn_unused bool implementation::validate_utf8(const char *buf, size_t len) const noexcept {
return lsx::stage1::generic_validate_utf8(buf,len);
}
simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document &_doc) noexcept {
return stage2::tape_builder::parse_document<false>(*this, _doc);
}
simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::document &_doc) noexcept {
return stage2::tape_builder::parse_document<true>(*this, _doc);
}
simdjson_warn_unused uint8_t *dom_parser_implementation::parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) const noexcept {
return lsx::stringparsing::parse_string(src, dst, allow_replacement);
}
simdjson_warn_unused uint8_t *dom_parser_implementation::parse_wobbly_string(const uint8_t *src, uint8_t *dst) const noexcept {
return lsx::stringparsing::parse_wobbly_string(src, dst);
}
simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept {
auto error = stage1(_buf, _len, stage1_mode::regular);
if (error) { return error; }
return stage2(_doc);
}
} // namespace lsx
} // namespace simdjson
#include <simdjson/lsx/end.h>
#endif // SIMDJSON_SRC_LSX_CPP
+6
View File
@@ -35,6 +35,12 @@ SIMDJSON_PUSH_DISABLE_UNUSED_WARNINGS
#if SIMDJSON_IMPLEMENTATION_WESTMERE
#include <westmere.cpp>
#endif
#if SIMDJSON_IMPLEMENTATION_LSX
#include <lsx.cpp>
#endif
#if SIMDJSON_IMPLEMENTATION_LASX
#include <lasx.cpp>
#endif
#if SIMDJSON_IMPLEMENTATION_FALLBACK
#include <fallback.cpp>
#endif
@@ -102,7 +102,7 @@ namespace array_error_tests {
ONDEMAND_SUBTEST("unclosed extra comma", R"({ "a": [,)", assert_iterate(doc["a"], { INCOMPLETE_ARRAY_OR_OBJECT }));
ONDEMAND_SUBTEST("unclosed extra comma", R"({ "a": [,,)", assert_iterate(doc["a"], { INCOMPLETE_ARRAY_OR_OBJECT }));
ONDEMAND_SUBTEST("unclosed ", R"({ "a": [1 )", assert_iterate(doc["a"], { INCOMPLETE_ARRAY_OR_OBJECT }));
// TODO These pass the user values that may run past the end of the buffer if they aren't careful
// TODO These pass the user values that may run past the end of the buffer if they are not careful
// In particular, if the padding is decorated with the wrong values, we could cause overrun!
ONDEMAND_SUBTEST("unclosed ", R"({ "a": [1,)", assert_iterate(doc["a"], { INCOMPLETE_ARRAY_OR_OBJECT }));
ONDEMAND_SUBTEST("unclosed ", R"({ "a": [1)", assert_iterate(doc["a"], { INCOMPLETE_ARRAY_OR_OBJECT }));
@@ -651,6 +651,26 @@ namespace document_stream_tests {
}
bool issue2137() {
TEST_START();
auto json = "true { "_padded;
ondemand::parser parser;
ondemand::document_stream stream;
ASSERT_SUCCESS(parser.iterate_many(json).get(stream));
for (auto doc: stream) {
bool val{};
ASSERT_SUCCESS(doc.get_bool().get(val));
std::string_view raw_json;
ASSERT_SUCCESS(doc.raw_json_token().get(raw_json));
ASSERT_EQUAL(val, 1);
std::string s(raw_json);
ASSERT_EQUAL(s, "true ");
}
size_t t = stream.truncated_bytes();
ASSERT_EQUAL(3, t);
TEST_SUCCEED();
}
bool issue1668() {
TEST_START();
auto json = R"([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100])"_padded;
@@ -861,6 +881,7 @@ namespace document_stream_tests {
bool run() {
return
issue2137() &&
skipbom() &&
issue1977() &&
string_with_trailing() &&
+25
View File
@@ -106,6 +106,29 @@ namespace error_tests {
TEST_SUCCEED();
}
#if SIMDJSON_EXCEPTIONS
// This is a compile-only test
struct Class {
Class(std::string text)
: parser()
, doc(parser.iterate(text))
{
}
simdjson::ondemand::parser parser;
simdjson::ondemand::document doc;
};
bool document_in_class() {
TEST_START();
std::string text = "{}";
Class c(text);
TEST_SUCCEED();
}
bool direct_document() {
TEST_START();
std::string text = "{}";
simdjson::ondemand::parser parser;
simdjson::ondemand::document doc(parser.iterate(text));
TEST_SUCCEED();
}
bool raw_json_string_except() {
TEST_START();
ondemand::parser parser;
@@ -381,6 +404,8 @@ namespace error_tests {
issue1834() &&
issue1834_2() &&
#if SIMDJSON_EXCEPTIONS
document_in_class() &&
direct_document() &&
raw_json_string_except() &&
raw_json_string_except_with_io() &&
#endif
@@ -17,11 +17,26 @@ namespace key_string_tests {
}
return true;
}
bool parser_escaped_key() {
TEST_START();
ondemand::parser parser;
const padded_string json = "{ \"1\": \"1\", \"2\" : \"2\", \"3\" \t : \"3\", \"abc\"\n\t\n: \"abc\", \"\\u0075\": \"\\\\u0075\" }"_padded;
auto doc = parser.iterate(json);
for(auto field : doc.get_object()) {
std::string_view keyv = field.escaped_key();
std::string_view valuev = field.value();
if(keyv != valuev) { return false; }
}
return true;
}
#endif // SIMDJSON_EXCEPTIONS
bool run() {
return
#if SIMDJSON_EXCEPTIONS
parser_key_value() &&
parser_escaped_key() &&
#endif // SIMDJSON_EXCEPTIONS
true;
}
+96 -31
View File
@@ -211,7 +211,7 @@ namespace number_tests {
bool get_number_tests() {
TEST_START();
ondemand::parser parser;
padded_string docdata = R"([1.0, 3, 1, 3.1415,-13231232,9999999999999999999,-9223372036854775807,-9223372036854775808])"_padded;
padded_string docdata = R"([1.0, 3, 1, 3.1415,-13231232,9999999999999999999,-9223372036854775807,-9223372036854775808,12345678901234567890123])"_padded;
ondemand::number_type expectedtypes[] = {ondemand::number_type::floating_point_number,
ondemand::number_type::signed_integer,
ondemand::number_type::signed_integer,
@@ -219,10 +219,11 @@ namespace number_tests {
ondemand::number_type::signed_integer,
ondemand::number_type::unsigned_integer,
ondemand::number_type::signed_integer,
ondemand::number_type::signed_integer
ondemand::number_type::signed_integer,
ondemand::number_type::big_integer
};
bool is_negative[] = {false, false, false, false, true, false, true, true};
bool is_integer[] = {false, true, true, false, true, true, true, true};
bool is_negative[] = {false, false, false, false, true, false, true, true, false};
bool is_integer[] = {false, true, true, false, true, true, true, true, true};
ondemand::document doc;
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
@@ -233,17 +234,25 @@ namespace number_tests {
ondemand::value val;
ASSERT_SUCCESS(valr.get(val));
ondemand::number_type nt{};
ASSERT_SUCCESS(val.get_number_type().get(nt));
auto r = val.get_number_type().get(nt);
ASSERT_SUCCESS(r);
ASSERT_EQUAL(expectedtypes[counter], nt);
ondemand::number num;
ASSERT_SUCCESS(val.get_number().get(num));
ASSERT_EQUAL(is_negative[counter], val.is_negative());
bool intvalue{};
ASSERT_SUCCESS(val.is_integer().get(intvalue));
auto res = val.is_integer().get(intvalue);
ASSERT_SUCCESS(res);
ondemand::number num;
auto rr = val.get_number().get(num);
if(counter == 8) {
ASSERT_EQUAL(rr, BIGINT_ERROR);
ASSERT_EQUAL(nt, ondemand::number_type::big_integer);
} else {
ASSERT_SUCCESS(rr);
ondemand::number_type t = num.get_number_type();
ASSERT_EQUAL(expectedtypes[counter], t);
}
ASSERT_EQUAL(is_integer[counter], intvalue);
ondemand::number_type t = num.get_number_type();
ASSERT_EQUAL(expectedtypes[counter], t);
switch(t) {
ASSERT_EQUAL(is_negative[counter], val.is_negative());
switch(nt) {
case ondemand::number_type::signed_integer:
ASSERT_TRUE(num.is_int64());
break;
@@ -253,25 +262,39 @@ namespace number_tests {
case ondemand::number_type::floating_point_number:
ASSERT_TRUE(num.is_double());
break;
case ondemand::number_type::big_integer:
break;
}
if(counter == 0) {
ASSERT_EQUAL(num.get_double(), 1.0);
ASSERT_EQUAL((double)num, 1.0);
} else if(counter == 1) {
ASSERT_EQUAL(num.get_int64(), 3);
ASSERT_EQUAL((int64_t)num, 3);
} else if(counter == 2) {
ASSERT_EQUAL(num.get_int64(), 1);
ASSERT_EQUAL((int64_t)num, 1);
} else if(counter == 3) {
ASSERT_EQUAL(num.get_double(), 3.1415);
ASSERT_EQUAL((double)num, 3.1415);
} else if(counter == 4) {
ASSERT_EQUAL(num.get_int64(), -13231232);
ASSERT_EQUAL((int64_t)num, -13231232);
} else if(counter == 5) {
ASSERT_EQUAL(num.get_uint64(), UINT64_C(9999999999999999999));
ASSERT_EQUAL((uint64_t)num, UINT64_C(9999999999999999999));
switch(counter) {
case 0:
ASSERT_EQUAL(num.get_double(), 1.0);
ASSERT_EQUAL((double)num, 1.0);
break;
case 1:
ASSERT_EQUAL(num.get_int64(), 3);
ASSERT_EQUAL((int64_t)num, 3);
break;
case 2:
ASSERT_EQUAL(num.get_int64(), 1);
ASSERT_EQUAL((int64_t)num, 1);
break;
case 3:
ASSERT_EQUAL(num.get_double(), 3.1415);
ASSERT_EQUAL((double)num, 3.1415);
break;
case 4:
ASSERT_EQUAL(num.get_int64(), -13231232);
ASSERT_EQUAL((int64_t)num, -13231232);
break;
case 5:
ASSERT_EQUAL(num.get_uint64(), UINT64_C(9999999999999999999));
ASSERT_EQUAL((uint64_t)num, UINT64_C(9999999999999999999));
break;
case 8:
ASSERT_EQUAL(val.raw_json_token(), "12345678901234567890123");
break;
default:
break;
}
counter++;
}
@@ -441,8 +464,50 @@ namespace number_tests {
TEST_SUCCEED();
}
bool big_int_not_zero() {
TEST_START();
ondemand::parser parser;
ondemand::document doc;
// This is not a big integer, it is a mistake
padded_string docdata = R"(09500000000000000000000000000000000000)"_padded;
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
ASSERT_ERROR(doc.get_number(), NUMBER_ERROR);
TEST_SUCCEED();
}
bool negative_big_int() {
TEST_START();
ondemand::parser parser;
ondemand::document doc;
// This is not a big integer, it is a mistake
padded_string docdata = R"(-18446744073709551616)"_padded;
ASSERT_SUCCESS(parser.iterate(docdata).get(doc));
ASSERT_ERROR(doc.get_number(), BIGINT_ERROR);
std::string_view my_big;
ASSERT_SUCCESS(doc.raw_json_token().get(my_big));
ASSERT_EQUAL(my_big, "-18446744073709551616");
TEST_SUCCEED();
}
bool gigantic_big_int() {
TEST_START();
ondemand::parser parser;
ondemand::document doc;
std::string number(2000, '1');
// This is not a big integer, it is a mistake
ASSERT_SUCCESS(parser.iterate(number).get(doc));
ASSERT_ERROR(doc.get_number(), BIGINT_ERROR);
std::string_view my_big;
ASSERT_SUCCESS(doc.raw_json_token().get(my_big));
ASSERT_EQUAL(my_big, number);
TEST_SUCCEED();
}
bool run() {
return issue2099() &&
return gigantic_big_int() &&
big_int_not_zero() &&
negative_big_int() &&
issue2099() &&
issue2093() &&
issue2045() &&
issue2017() &&
@@ -119,7 +119,7 @@ namespace object_error_tests {
bool object_lookup_miss_unclosed_error() {
TEST_START();
ONDEMAND_SUBTEST("unclosed", R"({ "a": 1, )", assert_error(doc["b"], INCOMPLETE_ARRAY_OR_OBJECT));
// TODO These next two pass the user a value that may run past the end of the buffer if they aren't careful.
// TODO These next two pass the user a value that may run past the end of the buffer if they are not careful.
// In particular, if the padding is decorated with the wrong values, we could cause overrun!
ONDEMAND_SUBTEST("unclosed", R"({ "a": 1 )", assert_error(doc["b"], INCOMPLETE_ARRAY_OR_OBJECT));
ONDEMAND_SUBTEST("unclosed", R"({ "a": )", assert_error(doc["b"], INCOMPLETE_ARRAY_OR_OBJECT));
+78 -6
View File
@@ -3,6 +3,9 @@
#if __cpp_lib_optional >= 201606L
#include <optional>
#endif
#if SIMDJSON_CPLUSPLUS17
#include <charconv>
#endif
using namespace std;
using namespace simdjson;
using error_code=simdjson::error_code;
@@ -382,22 +385,84 @@ bool examplecrt_realloc() {
TEST_SUCCEED();
}
#if SIMDJSON_CPLUSPLUS17
bool big_int_array() {
TEST_START();
ondemand::parser parser;
padded_string docdata = R"([-9223372036854775809, 18446744073709551617, 99999999999999999999999 ])"_padded;
std::string expected[] = {"-9223372036854775809", "18446744073709551617", "99999999999999999999999 "};
ondemand::document doc = parser.iterate(docdata);
ondemand::array arr = doc.get_array();
size_t i = 0;
for(ondemand::value val : arr) {
if(i > 3) {
std::cerr << "unexpected number of elements" << std::endl;
return false;
}
if(val.get_number_type() != ondemand::number_type::big_integer) {
std::cerr << "unexpected number type" << std::endl;
std::cout << val.get_number_type() << std::endl;
std::cout << val.raw_json_token() << std::endl;
return false;
}
std::string_view token = val.raw_json_token();
std::string_view expected_token = expected[i];
if(token != expected_token) {
std::cerr << "unexpected token: " << token << " expected: " << expected_token << std::endl;
return false;
}
i++;
}
if(i != 3) {
std::cerr << "unexpected number of elements" << std::endl;
return false;
}
TEST_SUCCEED();
}
#endif
bool big_int_array_as_double() {
TEST_START();
ondemand::parser parser;
padded_string docdata = R"([-9223372036854775809, 18446744073709551617, 99999999999999999999999 ])"_padded;
double dexpected[] = {-9223372036854775808.0, 18446744073709551616.0, 1e23};
ondemand::document doc = parser.iterate(docdata);
ondemand::array arr = doc.get_array();
size_t i = 0;
for(ondemand::value val : arr) {
if(i > 3) {
std::cerr << "unexpected number of elements" << std::endl;
return false;
}
if((val.get_number_type() != ondemand::number_type::big_integer) || (dexpected[i] != val.get_double())) {
return false;
}
i++;
}
if(i != 3) {
std::cerr << "unexpected number of elements" << std::endl;
return false;
}
TEST_SUCCEED();
}
bool number_tests() {
TEST_START();
ondemand::parser parser;
padded_string docdata = R"([1.0, 3, 1, 3.1415,-13231232,9999999999999999999])"_padded;
padded_string docdata = R"([1.0, 3, 1, 3.1415,-13231232,9999999999999999999,12345678901234567890123])"_padded;
ondemand::document doc = parser.iterate(docdata);
ondemand::array arr = doc.get_array();
for(ondemand::value val : arr) {
std::cout << val << " ";
std::cout << "negative: " << val.is_negative() << " ";
std::cout << "is_integer: " << val.is_integer() << " ";
ondemand::number num = val.get_number();
ondemand::number_type t = num.get_number_type();
// direct computation without materializing the number:
ondemand::number_type dt = val.get_number_type();
if(t != dt) { throw std::runtime_error("bug"); }
switch(t) {
ondemand::number num;
auto res = val.get_number().get(num);
ondemand::number_type t = num.get_number_type();
if(t != dt && res != BIGINT_ERROR) { throw std::runtime_error("bug"); }
switch(dt) {
case ondemand::number_type::signed_integer:
std::cout << "integer: " << int64_t(num) << " ";
std::cout << "integer: " << num.get_int64() << std::endl;
@@ -410,6 +475,9 @@ bool number_tests() {
std::cout << "float: " << double(num) << " ";
std::cout << "float: " << num.get_double() << std::endl;
break;
case ondemand::number_type::big_integer:
std::cout << "big-integer: " << val.raw_json_token() << std::endl;
break;
}
}
TEST_SUCCEED();
@@ -1778,6 +1846,10 @@ bool value_raw_json_object() {
bool run() {
return true
#if SIMDJSON_EXCEPTIONS
#if SIMDJSON_CPLUSPLUS17
&& big_int_array()
#endif // SIMDJSON_CPLUSPLUS17
&& big_int_array_as_double()
&& key_raw_json_token()
&& to_optional()
&& value_raw_json_array() && value_raw_json_object()
@@ -1785,7 +1857,7 @@ bool run() {
&& at_end()
&& example1956() && example1958()
&& allow_comma_separated_example()
// && basics_1() // Fails because twitter.json isn't in current directory. Compile test only.
// && basics_1() // Fails because twitter.json is not in current directory. Compile test only.
&& basics_treewalk()
&& basics_treewalk_breakline()
&& json_value_with_array_count()

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