mirror of
https://github.com/simdjson/simdjson
synced 2026-06-08 17:27:07 +00:00
Compare commits
48 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bca8d3e277 | |||
| 97196fa969 | |||
| 23fab307ca | |||
| 0a14e05ec7 | |||
| b88f831dbd | |||
| 71a73c2c47 | |||
| 07603aa7f7 | |||
| b8a6b889c7 | |||
| 7898234bb3 | |||
| f52533538a | |||
| 3bb2f0a186 | |||
| d6551bf278 | |||
| 0173eaaad7 | |||
| 5459dbd6c6 | |||
| 9c45f1f292 | |||
| 665cbba159 | |||
| a26f664907 | |||
| 17cb457ffd | |||
| 8b2af0473a | |||
| f1bfa43385 | |||
| 29fbbee852 | |||
| baa7d961ce | |||
| 6412b27c7e | |||
| 503da5ec83 | |||
| 9dd6e9c959 | |||
| 6db330c1a2 | |||
| 993ac4b87c | |||
| 35e87896f0 | |||
| f7e281cadc | |||
| 13405afd4b | |||
| 24b44309fb | |||
| e11ad58aad | |||
| 1875ed6550 | |||
| 80d26298a0 | |||
| 25b5015c09 | |||
| 26f8c566c7 | |||
| ff77ac801e | |||
| e0699994ef | |||
| 68ba9a1b2a | |||
| 6fed6bd29b | |||
| cc24bb4114 | |||
| 14ec0ca8f4 | |||
| 8dabd02c3a | |||
| c9692005fd | |||
| 2e0a9397c9 | |||
| f7922075c3 | |||
| b2e20e04c9 | |||
| adc9d18efd |
@@ -31,7 +31,7 @@ A clear and concise description of any alternative solutions or features you've
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
|
||||
** Are you willing to contribute code or documentation toward this new feature? **
|
||||
**Are you willing to contribute code or documentation toward this new feature?**
|
||||
If you plan to contribute to simdjson, please read our
|
||||
* CONTRIBUTING guide: https://github.com/simdjson/simdjson/blob/master/CONTRIBUTING.md and our
|
||||
* HACKING guide: https://github.com/simdjson/simdjson/blob/master/HACKING.md
|
||||
|
||||
@@ -31,3 +31,14 @@ jobs:
|
||||
echo -e '#include <simdjson.h>\nint main(int argc,char**argv) {simdjson::dom::parser parser;simdjson::dom::element tweets = parser.load(argv[1]); }' > tmp.cpp && c++ -Idestination/include -Ldestination/lib -std=c++17 -Wl,-rpath,destination/lib -o linkandrun tmp.cpp -lsimdjson && ./linkandrun jsonexamples/twitter.json &&
|
||||
cd ../tests/installation_tests/find &&
|
||||
mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../../../build/destination .. && cmake --build .
|
||||
- name: Use cmake (shared)
|
||||
run: |
|
||||
mkdir buildshared &&
|
||||
cd buildshared &&
|
||||
cmake -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
|
||||
cmake --build . &&
|
||||
ctest --output-on-failure -LE explicitonly -j &&
|
||||
cmake --install . &&
|
||||
echo -e '#include <simdjson.h>\nint main(int argc,char**argv) {simdjson::dom::parser parser;simdjson::dom::element tweets = parser.load(argv[1]); }' > tmp.cpp && c++ -Idestination/include -Ldestination/lib -std=c++17 -Wl,-rpath,destination/lib -o linkandrun tmp.cpp -lsimdjson && ./linkandrun jsonexamples/twitter.json &&
|
||||
cd ../tests/installation_tests/find &&
|
||||
mkdir buildshared && cd buildshared && cmake -DCMAKE_INSTALL_PREFIX:PATH=../../../buildshared/destination .. && cmake --build .
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
name: Ubuntu riscv64 (GCC 11)
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: uraimo/run-on-arch-action@v2
|
||||
name: Test
|
||||
id: runcmd
|
||||
with:
|
||||
arch: riscv64
|
||||
distro: ubuntu_latest
|
||||
githubToken: ${{ github.token }}
|
||||
install: |
|
||||
apt-get update -q -y
|
||||
apt-get install -y cmake make g++
|
||||
run: |
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -B build
|
||||
cmake --build build -j=2
|
||||
ctest --output-on-failure --test-dir build
|
||||
@@ -32,7 +32,6 @@ jobs:
|
||||
with:
|
||||
path: dependencies/.cache
|
||||
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||
ctest --output-on-failure -LE explicitonly -j
|
||||
- name: Use cmake with undefined sanitizer
|
||||
run: |
|
||||
mkdir builddebugundefsani &&
|
||||
|
||||
+32
-5
@@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.14)
|
||||
project(
|
||||
simdjson
|
||||
# The version number is modified by tools/release.py
|
||||
VERSION 3.2.3
|
||||
VERSION 3.6.1
|
||||
DESCRIPTION "Parsing gigabytes of JSON per second"
|
||||
HOMEPAGE_URL "https://simdjson.org/"
|
||||
LANGUAGES CXX C
|
||||
@@ -20,8 +20,10 @@ string(
|
||||
# ---- Options, variables ----
|
||||
|
||||
# These version numbers are modified by tools/release.py
|
||||
set(SIMDJSON_LIB_VERSION "16.0.0" CACHE STRING "simdjson library version")
|
||||
set(SIMDJSON_LIB_SOVERSION "16" CACHE STRING "simdjson library soversion")
|
||||
set(SIMDJSON_LIB_VERSION "19.0.0" CACHE STRING "simdjson library version")
|
||||
set(SIMDJSON_LIB_SOVERSION "19" CACHE STRING "simdjson library soversion")
|
||||
|
||||
option(SIMDJSON_BUILD_STATIC_LIB "Build simdjson_static library along with simdjson" OFF)
|
||||
|
||||
option(SIMDJSON_ENABLE_THREADS "Link with thread support" ON)
|
||||
|
||||
@@ -56,8 +58,17 @@ include(cmake/developer-options.cmake)
|
||||
|
||||
# ---- simdjson library ----
|
||||
|
||||
add_library(simdjson src/simdjson.cpp)
|
||||
set(SIMDJSON_SOURCES src/simdjson.cpp)
|
||||
|
||||
add_library(simdjson ${SIMDJSON_SOURCES})
|
||||
add_library(simdjson::simdjson ALIAS simdjson)
|
||||
set(SIMDJSON_LIBRARIES simdjson)
|
||||
|
||||
if(SIMDJSON_BUILD_STATIC_LIB)
|
||||
add_library(simdjson_static STATIC ${SIMDJSON_SOURCES})
|
||||
add_library(simdjson::simdjson_static ALIAS simdjson_static)
|
||||
list(APPEND SIMDJSON_LIBRARIES simdjson_static)
|
||||
endif()
|
||||
|
||||
set_target_properties(
|
||||
simdjson PROPERTIES
|
||||
@@ -117,6 +128,9 @@ if(SIMDJSON_ENABLE_THREADS)
|
||||
endif()
|
||||
|
||||
simdjson_apply_props(simdjson)
|
||||
if(SIMDJSON_BUILD_STATIC_LIB)
|
||||
simdjson_apply_props(simdjson_static)
|
||||
endif()
|
||||
|
||||
# ---- Install rules ----
|
||||
|
||||
@@ -138,7 +152,6 @@ install(
|
||||
ARCHIVE COMPONENT simdjson_Development
|
||||
INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
|
||||
)
|
||||
|
||||
configure_file(cmake/simdjson-config.cmake.in simdjson-config.cmake @ONLY)
|
||||
|
||||
write_basic_package_version_file(
|
||||
@@ -167,6 +180,20 @@ install(
|
||||
COMPONENT simdjson_Development
|
||||
)
|
||||
|
||||
if(SIMDJSON_BUILD_STATIC_LIB)
|
||||
install(
|
||||
TARGETS simdjson_static
|
||||
EXPORT simdjson_staticTargets
|
||||
ARCHIVE COMPONENT simdjson_Development
|
||||
)
|
||||
install(
|
||||
EXPORT simdjson_staticTargets
|
||||
NAMESPACE simdjson::
|
||||
DESTINATION "${SIMDJSON_INSTALL_CMAKEDIR}"
|
||||
COMPONENT simdjson_Development
|
||||
)
|
||||
endif()
|
||||
|
||||
# pkg-config
|
||||
include(cmake/JoinPaths.cmake)
|
||||
join_paths(PKGCONFIG_INCLUDEDIR "\${prefix}" "${CMAKE_INSTALL_INCLUDEDIR}")
|
||||
|
||||
@@ -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.2.3"
|
||||
PROJECT_NUMBER = "3.6.1"
|
||||
|
||||
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
||||
# for a project that appears at the top of each page and should give viewer a
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
|
||||
[/badge.svg)](https://simdjson.org/plots.html)
|
||||

|
||||
[](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:simdjson)
|
||||

|
||||
[![][license img]][license]
|
||||
|
||||
[](https://simdjson.github.io/simdjson/)
|
||||
@@ -42,8 +40,10 @@ Table of Contents
|
||||
Real-world usage
|
||||
----------------
|
||||
|
||||
- [Node.js](https://nodejs.org/)
|
||||
- [ClickHouse](https://github.com/ClickHouse/ClickHouse)
|
||||
- [Facebook/Meta Velox](https://velox-lib.io)
|
||||
- [Meta Velox](https://velox-lib.io)
|
||||
- [Google Pax](https://github.com/google/paxml)
|
||||
- [milvus](https://github.com/milvus-io/milvus)
|
||||
- [Clang Build Analyzer](https://github.com/aras-p/ClangBuildAnalyzer)
|
||||
- [Shopify HeapProfiler](https://github.com/Shopify/heap-profiler)
|
||||
@@ -165,6 +165,7 @@ We distinguish between "bindings" (which just wrap the C++ code) and a port to a
|
||||
- [simdjzon](https://github.com/travisstaloch/simdjzon): zig port.
|
||||
- [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.
|
||||
|
||||
About simdjson
|
||||
--------------
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+13
-9
@@ -445,7 +445,7 @@ struct benchmarker {
|
||||
return 100.0 * a / b;
|
||||
}
|
||||
|
||||
void print(bool tabbed_output) const {
|
||||
void print(bool tabbed_output, bool stage1_only) const {
|
||||
if (tabbed_output) {
|
||||
char* filename_copy = reinterpret_cast<char*>(malloc(strlen(filename)+1));
|
||||
SIMDJSON_PUSH_DISABLE_WARNINGS
|
||||
@@ -503,17 +503,21 @@ struct benchmarker {
|
||||
stats->blocks_with_16_structurals_flipped, percent(stats->blocks_with_16_structurals_flipped, stats->blocks));
|
||||
}
|
||||
printf("\n");
|
||||
printf("All Stages (excluding allocation)\n");
|
||||
print_aggregate("| " , all_stages_without_allocation.best);
|
||||
// frequently, allocation is a tiny fraction of the running time so we omit it
|
||||
if(allocate_stage.best.elapsed_sec() > 0.01 * all_stages_without_allocation.best.elapsed_sec()) {
|
||||
printf("|- Allocation\n");
|
||||
print_aggregate("| ", allocate_stage.best);
|
||||
if(!stage1_only) {
|
||||
printf("All Stages (excluding allocation)\n");
|
||||
print_aggregate("| " , all_stages_without_allocation.best);
|
||||
// frequently, allocation is a tiny fraction of the running time so we omit it
|
||||
if(allocate_stage.best.elapsed_sec() > 0.01 * all_stages_without_allocation.best.elapsed_sec()) {
|
||||
printf("|- Allocation\n");
|
||||
print_aggregate("| ", allocate_stage.best);
|
||||
}
|
||||
}
|
||||
printf("|- Stage 1\n");
|
||||
print_aggregate("| ", stage1.best);
|
||||
printf("|- Stage 2\n");
|
||||
print_aggregate("| ", stage2.best);
|
||||
if(!stage1_only) {
|
||||
printf("|- Stage 2\n");
|
||||
print_aggregate("| ", stage2.best);
|
||||
}
|
||||
if (collector.has_events()) {
|
||||
double freq1 = (stage1.best.cycles() / stage1.best.elapsed_sec()) / 1000000000.0;
|
||||
double freq2 = (stage2.best.cycles() / stage2.best.elapsed_sec()) / 1000000000.0;
|
||||
|
||||
@@ -218,7 +218,7 @@ int main(int argc, char *argv[]) {
|
||||
if (!options.verbose) { progress.erase(); }
|
||||
|
||||
for (size_t i=0; i<options.files.size(); i++) {
|
||||
benchmarkers[i]->print(options.tabbed_output);
|
||||
benchmarkers[i]->print(options.tabbed_output, options.stage1_only);
|
||||
delete benchmarkers[i];
|
||||
}
|
||||
|
||||
|
||||
@@ -34,11 +34,15 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "linux-perf-events.h"
|
||||
#ifdef __linux__
|
||||
#include "linux-perf-events.h"
|
||||
#include <libgen.h>
|
||||
#endif
|
||||
|
||||
#if __APPLE__ && __aarch64__
|
||||
#include "apple/apple_arm_events.h"
|
||||
#endif
|
||||
|
||||
#include "simdjson.h"
|
||||
|
||||
using std::string;
|
||||
@@ -134,7 +138,7 @@ struct event_collector {
|
||||
|
||||
#if defined(__linux__)
|
||||
LinuxEvents<PERF_TYPE_HARDWARE> linux_events;
|
||||
event_collector(simdjson_unused bool quiet = false) : linux_events(vector<int>{
|
||||
event_collector() : linux_events(vector<int>{
|
||||
#if SIMDJSON_SIMPLE_PERFORMANCE_COUNTERS
|
||||
PERF_COUNT_HW_CPU_CYCLES,
|
||||
PERF_COUNT_HW_INSTRUCTIONS,
|
||||
@@ -149,8 +153,17 @@ struct event_collector {
|
||||
bool has_events() {
|
||||
return linux_events.is_working();
|
||||
}
|
||||
#elif __APPLE__ && __aarch64__
|
||||
AppleEvents apple_events;
|
||||
performance_counters diff;
|
||||
event_collector() : diff(0) {
|
||||
apple_events.setup_performance_counters();
|
||||
}
|
||||
bool has_events() {
|
||||
return apple_events.setup_performance_counters();
|
||||
}
|
||||
#else
|
||||
event_collector(simdjson_unused bool _quiet = false) {}
|
||||
event_collector() {}
|
||||
bool has_events() {
|
||||
return false;
|
||||
}
|
||||
@@ -159,6 +172,8 @@ struct event_collector {
|
||||
simdjson_inline void start() {
|
||||
#if defined(__linux)
|
||||
linux_events.start();
|
||||
#elif __APPLE__ && __aarch64__
|
||||
if(has_events()) { diff = apple_events.get_counters(); }
|
||||
#endif
|
||||
start_clock = steady_clock::now();
|
||||
}
|
||||
@@ -166,6 +181,16 @@ struct event_collector {
|
||||
time_point<steady_clock> end_clock = steady_clock::now();
|
||||
#if defined(__linux)
|
||||
linux_events.end(count.event_counts);
|
||||
#elif __APPLE__ && __aarch64__
|
||||
if(has_events()) {
|
||||
performance_counters end = apple_events.get_counters();
|
||||
diff = end - diff;
|
||||
}
|
||||
count.event_counts[0] = diff.cycles;
|
||||
count.event_counts[1] = diff.instructions;
|
||||
count.event_counts[2] = diff.missed_branches;
|
||||
count.event_counts[3] = 0;
|
||||
count.event_counts[4] = 0;
|
||||
#endif
|
||||
count.elapsed = end_clock - start_clock;
|
||||
return count;
|
||||
|
||||
@@ -19,7 +19,7 @@ void maybe_display_implementation() {
|
||||
template<typename B, typename R> static void run_json_benchmark(benchmark::State &state) {
|
||||
maybe_display_implementation();
|
||||
|
||||
event_collector collector(true);
|
||||
event_collector collector;
|
||||
event_aggregate events;
|
||||
|
||||
// Warmup and equality check (make sure the data is right!)
|
||||
|
||||
@@ -114,10 +114,14 @@ set(SIMDJSON_CXX_STANDARD 17 CACHE STRING "the C++ standard to use for simdjson"
|
||||
set(CMAKE_CXX_STANDARD ${SIMDJSON_CXX_STANDARD})
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
set(CMAKE_MACOSX_RPATH OFF)
|
||||
set(CMAKE_THREAD_PREFER_PTHREAD ON)
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
set(SIMDJSON_STRUCTURAL_INDEXER_STEP CACHE STRING "the SIMDJSON_STRUCTURAL_INDEXER_STEP variable")
|
||||
|
||||
if(SIMDJSON_STRUCTURAL_INDEXER_STEP)
|
||||
message(STATUS "Setting SIMDJSON_STRUCTURAL_INDEXER_STEP to ${SIMDJSON_STRUCTURAL_INDEXER_STEP}.")
|
||||
add_compile_definitions(SIMDJSON_STRUCTURAL_INDEXER_STEP=${SIMDJSON_STRUCTURAL_INDEXER_STEP})
|
||||
endif()
|
||||
# LTO seems to create all sorts of fun problems. Let us
|
||||
# disable temporarily.
|
||||
#include(CheckIPOSupported)
|
||||
|
||||
@@ -4,3 +4,4 @@ if("@SIMDJSON_ENABLE_THREADS@")
|
||||
endif()
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/simdjsonTargets.cmake")
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/simdjson_staticTargets.cmake" OPTIONAL)
|
||||
|
||||
+203
-46
@@ -30,6 +30,7 @@ An overview of what you need to know to use simdjson, with examples.
|
||||
- [Dynamic Number Types](#dynamic-number-types)
|
||||
- [Raw Strings](#raw-strings)
|
||||
- [General Direct Access to the Raw JSON String](#general-direct-access-to-the-raw-json-string)
|
||||
- [Storing Directly into an Existing String Instance](#storing-directly-into-an-existing-string-instance)
|
||||
- [Thread Safety](#thread-safety)
|
||||
- [Standard Compliance](#standard-compliance)
|
||||
- [Backwards Compatibility](#backwards-compatibility)
|
||||
@@ -85,7 +86,7 @@ include(FetchContent)
|
||||
FetchContent_Declare(
|
||||
simdjson
|
||||
GIT_REPOSITORY https://github.com/simdjson/simdjson.git
|
||||
GIT_TAG tags/v0.9.6
|
||||
GIT_TAG tags/v3.6.0
|
||||
GIT_SHALLOW TRUE)
|
||||
|
||||
FetchContent_MakeAvailable(simdjson)
|
||||
@@ -179,8 +180,8 @@ strcpy(json, "[1]");
|
||||
ondemand::document doc = parser.iterate(json, strlen(json), sizeof(json));
|
||||
```
|
||||
|
||||
The simdjson library will also accept `std::string` instances, as long as the `capacity()` of
|
||||
the string exceeds the `size()` by at least `SIMDJSON_PADDING`. You can increase the `capacity()` with the `reserve()` function of your strings.
|
||||
The simdjson library will also accept `std::string` instances. If the provided
|
||||
reference is non-const, it will allocate padding as needed.
|
||||
|
||||
You can copy your data directly on a `simdjson::padded_string` as follows:
|
||||
|
||||
@@ -218,7 +219,8 @@ This means that while you iterate an array, or search for a field in an object,
|
||||
walking through the original JSON text, merrily reading commas and colons and brackets to make sure
|
||||
you get where you are going. This is the key to On Demand's performance: since it's just an iterator,
|
||||
it lets you parse values as you use them. And particularly, it lets you *skip* values you do not want
|
||||
to use.
|
||||
to use. On Demand is also ideally suited when you want to capture part of the document without parsing it
|
||||
immediately (e.g., see [Raw Strings](#raw-strings)).
|
||||
|
||||
We refer to "On Demand" as a front-end component since it is an interface between the
|
||||
low-level parsing functions and the user. It hides much of the complexity of parsing JSON
|
||||
@@ -253,7 +255,8 @@ copy the data into their own favorite class instances (e.g., alternatives to `st
|
||||
|
||||
A `std::string_view` instance is effectively just a pointer to a region in memory representing
|
||||
a string. In simdjson, we return `std::string_view` instances that either point within the
|
||||
input string you parsed, or to a temporary string buffer inside our parser class instances.
|
||||
input string you parsed (when using [raw Strings](#raw-strings)), or to a temporary string buffer inside
|
||||
our parser class instances that is valid until the parser object is destroyed or you use it to parse another document.
|
||||
When using `std::string_view` instances, it is your responsibility to ensure that
|
||||
`std::string_view` instance does not outlive the pointed-to memory (e.g., either the input
|
||||
buffer or the parser instance). Furthermore, some operations reset the string buffer
|
||||
@@ -261,6 +264,7 @@ inside our parser instances: e.g., when we parse a new document. Thus a `std::st
|
||||
is often best viewed as a temporary string value that is tied to the document you are parsing.
|
||||
At the cost of some memory allocation, you may convert your `std::string_view` instances for long-term storage into `std::string` instances:
|
||||
`std::string mycopy(view)` (C++17) or `std::string mycopy(view.begin(), view.end())` (prior to C++17).
|
||||
For convenience, we also allow [storing an escaped string directly into an existing string instance](#storing-directly-into-an-existing-string-instance).
|
||||
|
||||
The `std::string_view` class has become standard as part of C++17 but it is not always available
|
||||
on compilers which only supports C++11. When we detect that `string_view` is natively
|
||||
@@ -355,7 +359,7 @@ support for users who avoid exceptions. See [the simdjson error handling documen
|
||||
* **Field Access:** To get the value of the "foo" field in an object, use `object["foo"]`. This will
|
||||
scan through the object looking for the field with the matching string, doing a character-by-character
|
||||
comparison. It may generate the error `simdjson::NO_SUCH_FIELD` if there is no such key in the object, it may throw an exception (see [Error Handling](#error-handling)). For efficiency reason, you should avoid looking up the same field repeatedly: e.g., do
|
||||
not do `object["foo"]` followed by `object["foo"]` with the same `object` instance. Keep in mind that On Demand does not buffer or save the result of the parsing: if you repeatedly access `object["foo"]`, then it must repeatedly seek the key and parse the content. The library does not provide a distinct function to check if a key is present, instead we recommend you attempt to access the key: e.g., by doing `ondemand::value val{}; if(!object["foo"].get(val)) {...}`, you have that `val` contains the requested value inside the if clause. It is your responsibility as a user to temporarily keep a reference to the value (`auto v = object["foo"]`), or to consume the content and store it in your own data structures. If you consume an
|
||||
not do `object["foo"]` followed by `object["foo"]` with the same `object` instance. For best performance, you should try to query the keys in the same order they appear in the document. If you need several keys and you cannot predict the order they will appear in, it is recommended to iterate through all keys `for(auto field : object) {...}`. Keep in mind that On Demand does not buffer or save the result of the parsing: if you repeatedly access `object["foo"]`, then it must repeatedly seek the key and parse the content. The library does not provide a distinct function to check if a key is present, instead we recommend you attempt to access the key: e.g., by doing `ondemand::value val{}; if (!object["foo"].get(val)) {...}`, you have that `val` contains the requested value inside the if clause. It is your responsibility as a user to temporarily keep a reference to the value (`auto v = object["foo"]`), or to consume the content and store it in your own data structures. If you consume an
|
||||
object twice: `std::string_view(object["foo"]` followed by `std::string_view(object["foo"]` then your code
|
||||
is in error. Furthermore, you can only consume one field at a time, on the same object. The
|
||||
value instance you get from `content["bids"]` becomes invalid when you call `content["asks"]`.
|
||||
@@ -370,7 +374,11 @@ support for users who avoid exceptions. See [the simdjson error handling documen
|
||||
> 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
|
||||
> parsing and writing out the unescaped keys to a string buffer and returning a `std::string_view`
|
||||
> instance. You should expect a performance penalty when using `unescaped_key()`.
|
||||
> instance. The `unescaped_key` takes an optional Boolean value: passing it true will decode invalid
|
||||
> 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.
|
||||
>
|
||||
> ```c++
|
||||
> auto json = R"({"k\u0065y": 1})"_padded;
|
||||
@@ -381,7 +389,7 @@ support for users who avoid exceptions. See [the simdjson error handling documen
|
||||
> // parses and writes out the key, after unescaping it,
|
||||
> // to a string buffer. It causes a performance penalty.
|
||||
> std::string_view keyv = field.unescaped_key();
|
||||
> if(keyv == "key") { std::cout << uint64_t(field.value()); }
|
||||
> if (keyv == "key") { std::cout << uint64_t(field.value()); }
|
||||
> }
|
||||
> ```
|
||||
>
|
||||
@@ -414,8 +422,10 @@ support for users who avoid exceptions. See [the simdjson error handling documen
|
||||
step through each value in the JSON array.
|
||||
|
||||
If you know the type of the value, you can cast it right there, too! `for (double value : array) { ... }`.
|
||||
* **Object Iteration:** You can iterate through an object's fields, as well: `for (auto field : object) { ... }`
|
||||
- `field.unescaped_key()` will get you the unescaped key string.
|
||||
|
||||
You may also use explicit iterators: `for(auto i = array.begin(); i != array.end(); i++) {}`. You can check that an array is empty with the condition `auto i = array.begin(); if(i == array.end()) {...}`.
|
||||
* **Object Iteration:** You can iterate through an object's fields, as well: `for (auto field : object) { ... }`. You may also use explicit iterators : `for(auto i = object.begin(); i != object.end(); i++) { auto field = *i; .... }`. You can check that an object is empty with the condition `auto i = object.begin(); if(i == object.end()) {...}`.
|
||||
- `field.unescaped_key()` will get you the unescaped key string. E.g., the JSON string `"\u00e1"` becomes the Unicode string `á`. Optionally, you pass `true` as a parameter to the `unescaped_key` method if you want invalid escape sequences to be replaced by a default replacement character (e.g., `\ud800\ud801\ud811`): otherwise bad escape sequences lead to an immediate error.
|
||||
- `field.value()` will get you the value, which you can then use all these other methods on.
|
||||
* **Array Index:** Because it is forward-only, you cannot look up an array element by index by index. Instead,
|
||||
you should iterate through the array and keep an index yourself.
|
||||
@@ -445,7 +455,7 @@ support for users who avoid exceptions. See [the simdjson error handling documen
|
||||
> {
|
||||
> ondemand::parser parser;
|
||||
> for (ondemand::object car : parser.iterate(cars_json)) {
|
||||
> if(uint64_t(car["year"]) > 2000) {
|
||||
> if (uint64_t(car["year"]) > 2000) {
|
||||
> arrays.push_back(simdjson::to_json_string(car["tire_pressure"]));
|
||||
> }
|
||||
> }
|
||||
@@ -454,7 +464,7 @@ support for users who avoid exceptions. See [the simdjson error handling documen
|
||||
> std::ostringstream oss;
|
||||
> oss << "[";
|
||||
> for(size_t i = 0; i < arrays.size(); i++) {
|
||||
> if(i>0) { oss << ","; }
|
||||
> if (i>0) { oss << ","; }
|
||||
> oss << arrays[i];
|
||||
> }
|
||||
> oss << "]";
|
||||
@@ -535,9 +545,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 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 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
|
||||
- arrays (`json_type::array`),
|
||||
- objects (`json_type::object`)
|
||||
- numbers (`json_type::number`),
|
||||
@@ -597,7 +605,7 @@ support for users who avoid exceptions. See [the simdjson error handling documen
|
||||
case ondemand::json_type::null:
|
||||
// We check that the value is indeed null
|
||||
// otherwise: an error is thrown.
|
||||
if(element.is_null()) {
|
||||
if (element.is_null()) {
|
||||
cout << "null";
|
||||
}
|
||||
break;
|
||||
@@ -910,11 +918,11 @@ bool simple_error_example() {
|
||||
ondemand::parser parser;
|
||||
auto json = R"({"bad number":3.14.1 })"_padded;
|
||||
ondemand::document doc;
|
||||
if( parser.iterate(json).get(doc) != SUCCESS ) { return false; }
|
||||
if (parser.iterate(json).get(doc) != SUCCESS) { return false; }
|
||||
double x;
|
||||
auto error = doc["bad number"].get_double().get(x);
|
||||
// returns "simdjson::NUMBER_ERROR"
|
||||
if(error != SUCCESS) {
|
||||
if (error != SUCCESS) {
|
||||
std::cout << error << std::endl;
|
||||
return false;
|
||||
}
|
||||
@@ -976,10 +984,10 @@ it selects the key `"count"` within that object.
|
||||
int main(void) {
|
||||
simdjson::ondemand::parser parser;
|
||||
auto error = padded_string::load("twitter.json").get(json);
|
||||
if(error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
|
||||
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
|
||||
simdjson::ondemand::document tweets;
|
||||
error = parser.iterate(json).get(tweets);
|
||||
if( error ) { std::cerr << error << std::endl; return EXIT_FAILURE; }
|
||||
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
|
||||
simdjson::ondemand::value res;
|
||||
error = tweets["search_metadata"]["count"].get(res);
|
||||
if (error != SUCCESS) {
|
||||
@@ -1010,12 +1018,12 @@ int main(void) {
|
||||
simdjson::ondemand::document tweets;
|
||||
padded_string json;
|
||||
auto error = padded_string::load("twitter.json").get(json);
|
||||
if(error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
|
||||
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
|
||||
error = parser.iterate(json).get(tweets);
|
||||
if(error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
|
||||
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
|
||||
uint64_t identifier;
|
||||
error = tweets["statuses"].at(0)["id"].get(identifier);
|
||||
if(error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
|
||||
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
|
||||
std::cout << identifier << std::endl;
|
||||
}
|
||||
```
|
||||
@@ -1039,40 +1047,40 @@ bool parse() {
|
||||
|
||||
// Iterating through an array of objects
|
||||
auto error = parser.iterate(cars_json).get(doc);
|
||||
if(error) { std::cerr << error << std::endl; return false; }
|
||||
if (error) { std::cerr << error << std::endl; return false; }
|
||||
ondemand::array cars; // invalid until the get() succeeds
|
||||
error = doc.get_array().get(cars);
|
||||
|
||||
for (auto car_value : cars) {
|
||||
ondemand::object car; // invalid until the get() succeeds
|
||||
error = car_value.get_object().get(car);
|
||||
if(error) { std::cerr << error << std::endl; return false; }
|
||||
if (error) { std::cerr << error << std::endl; return false; }
|
||||
|
||||
// Accessing a field by name
|
||||
std::string_view make;
|
||||
std::string_view model;
|
||||
error = car["make"].get(make);
|
||||
if(error) { std::cerr << error << std::endl; return false; }
|
||||
if (error) { std::cerr << error << std::endl; return false; }
|
||||
error = car["model"].get(model);
|
||||
if(error) { std::cerr << error << std::endl; return false; }
|
||||
if (error) { std::cerr << error << std::endl; return false; }
|
||||
|
||||
cout << "Make/Model: " << make << "/" << model << endl;
|
||||
|
||||
// Casting a JSON element to an integer
|
||||
uint64_t year{};
|
||||
error = car["year"].get(year);
|
||||
if(error) { std::cerr << error << std::endl; return false; }
|
||||
if (error) { std::cerr << error << std::endl; return false; }
|
||||
cout << "- This car is " << 2020 - year << " years old." << endl;
|
||||
|
||||
// Iterating through an array of floats
|
||||
double total_tire_pressure = 0;
|
||||
ondemand::array pressures;
|
||||
error = car["tire_pressure"].get_array().get(pressures);
|
||||
if(error) { std::cerr << error << std::endl; return false; }
|
||||
if (error) { std::cerr << error << std::endl; return false; }
|
||||
for (auto tire_pressure_value : pressures) {
|
||||
double tire_pressure;
|
||||
error = tire_pressure_value.get_double().get(tire_pressure);
|
||||
if(error) { std::cerr << error << std::endl; return false; }
|
||||
if (error) { std::cerr << error << std::endl; return false; }
|
||||
total_tire_pressure += tire_pressure;
|
||||
}
|
||||
cout << "- Average tire pressure: " << (total_tire_pressure / 4) << endl;
|
||||
@@ -1088,7 +1096,7 @@ after you have initialized them and checked that there is no error:
|
||||
ondemand::object car; // invalid until the get() succeeds
|
||||
// the `car` instance should not use used before it is initialized
|
||||
error = car_value.get_object().get(car);
|
||||
if(error) {
|
||||
if (error) {
|
||||
// the `car` instance should not use used
|
||||
} else {
|
||||
// the `car` instance can be safely used
|
||||
@@ -1102,20 +1110,20 @@ having to handle exceptions.
|
||||
ondemand::parser parser;
|
||||
ondemand::document doc;
|
||||
auto error = parser.iterate(json).get(doc);
|
||||
if(error) { return false; }
|
||||
if (error) { return false; }
|
||||
ondemand::object object; // invalid until the get() succeeds
|
||||
error = doc.get_object().get(object);
|
||||
if(error) { return false; }
|
||||
if (error) { return false; }
|
||||
for(auto field : object) {
|
||||
// We could replace 'field.key() with field.unescaped_key(),
|
||||
// and ondemand::raw_json_string by std::string_view.
|
||||
ondemand::raw_json_string keyv;
|
||||
error = field.key().get(keyv);
|
||||
if(error) { return false; }
|
||||
if(keyv == "key") {
|
||||
if (error) { return false; }
|
||||
if (keyv == "key") {
|
||||
uint64_t intvalue;
|
||||
error = field.value().get(intvalue);
|
||||
if(error) { return false; }
|
||||
if (error) { return false; }
|
||||
std::cout << intvalue;
|
||||
}
|
||||
}
|
||||
@@ -1294,7 +1302,7 @@ content.
|
||||
for (uint64_t values : array) {
|
||||
std::cout << values << std::endl;
|
||||
}
|
||||
if(!doc.at_end()) {
|
||||
if (!doc.at_end()) {
|
||||
// In this instance, we will be left pointing at 'foo' since we have consumed the array [1,2].
|
||||
std::cerr << "trailing content at byte index " << doc.current_location() - json.data() << std::endl;
|
||||
}
|
||||
@@ -1324,7 +1332,7 @@ before printout the data.
|
||||
|
||||
auto doc = parser.iterate(cars_json);
|
||||
for (simdjson_unused ondemand::object car : doc) {
|
||||
if(car["make"] == "Toyota") { count++; }
|
||||
if (car["make"] == "Toyota") { count++; }
|
||||
}
|
||||
std::cout << "We have " << count << " Toyota cars.\n";
|
||||
doc.rewind(); // requires simdjson 1.0 or better
|
||||
@@ -1392,18 +1400,18 @@ ondemand::parser parser;
|
||||
ondemand::document_stream stream;
|
||||
size_t counter{0};
|
||||
auto error = parser.iterate_many(json, 50).get(stream);
|
||||
if( error ) { /* handle the error */ }
|
||||
if (error) { /* handle the error */ }
|
||||
for (auto doc: stream) {
|
||||
if(counter < 6) {
|
||||
if (counter < 6) {
|
||||
int64_t val;
|
||||
error = doc.at_pointer("/4").get(val);
|
||||
if( error ) { /* handle the error */ }
|
||||
if (error) { /* handle the error */ }
|
||||
std::cout << "5 = " << val << std::endl;
|
||||
} else {
|
||||
ondemand::value val;
|
||||
error = doc.at_pointer("/4").get(val);
|
||||
// error == simdjson::CAPACITY
|
||||
if(error) {
|
||||
if (error) {
|
||||
std::cerr << error << std::endl;
|
||||
// We left 293 bytes unprocessed at the tail end of the input.
|
||||
std::cout << " unprocessed bytes at the end: " << stream.truncated_bytes() << std::endl;
|
||||
@@ -1647,7 +1655,7 @@ JSON string to a user-provided buffer:
|
||||
|
||||
General Direct Access to the Raw JSON String
|
||||
--------------------------------
|
||||
If your value is a string, the `raw_json_string` gives you direct access to the unprocess
|
||||
If your value is a string, the `raw_json_string` you with `get_raw_json_string()` gives you direct access to the unprocessed
|
||||
string. The simdjson library allows you to have access to the raw underlying JSON
|
||||
more generally.
|
||||
|
||||
@@ -1682,9 +1690,9 @@ string_view token = obj["value"].raw_json_token();
|
||||
|
||||
The `raw_json_token()` should be fast and free of allocation.
|
||||
|
||||
If you value is an array or an object, `raw_json_token()` returns effectively a single
|
||||
If your value is an array or an object, `raw_json_token()` returns effectively a single
|
||||
character (`[`) or (`}`) which is not very useful. For arrays and objects, we have another
|
||||
method called `raw_json()` which consumes (traverse) the array or the object.
|
||||
method called `raw_json()` which consumes (traverses) the array or the object.
|
||||
|
||||
```C++
|
||||
simdjson::ondemand::parser parser;
|
||||
@@ -1716,6 +1724,82 @@ obj.reset(); // revise the object
|
||||
uint64_t x = obj["value"]; // gives me 123
|
||||
```
|
||||
|
||||
You can use `raw_json()` with the values inside an array and object. When
|
||||
calling `raw_json()` on an untyped value, it acts as `raw_json()` when the
|
||||
value is an array or an object. Otherwise, it acts as `raw_json_token()`.
|
||||
It is useful if you do not care for the type of the value and just wants a
|
||||
string representation.
|
||||
|
||||
```C++
|
||||
auto json = R"( [1,2,"fds", {"a":1}, [1,344]] )"_padded;
|
||||
ondemand::parser parser;
|
||||
ondemand::document doc = parser.iterate(json);
|
||||
size_t counter = 0;
|
||||
for(auto array: doc) {
|
||||
std::string_view raw = array.raw_json();
|
||||
// will capture "1", "2", "\"fds\"", "{\"a\":1}", "[1,344]"
|
||||
}
|
||||
```
|
||||
|
||||
```C++
|
||||
auto json = R"( {"key1":1,"key2":2,"key3":"fds", "key4":{"a":1}, "key5":[1,344]} )"_padded;
|
||||
ondemand::parser parser;
|
||||
ondemand::document doc = parser.iterate(json);
|
||||
size_t counter = 0;
|
||||
for(auto key_value: doc.get_object()) {
|
||||
std::string_view raw = key_value.value().raw_json();
|
||||
// will capture "1", "2", "\"fds\"", "{\"a\":1}", "[1,344]"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
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``
|
||||
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`.
|
||||
|
||||
```C++
|
||||
auto json = R"({
|
||||
"name": "Daniel",
|
||||
"age": 42
|
||||
})"_padded;
|
||||
ondemand::parser parser;
|
||||
ondemand::document doc = parser.iterate(json);
|
||||
std::string name;
|
||||
doc["name"].get_string(name);
|
||||
```
|
||||
|
||||
The same routine can be written without exceptions handling:
|
||||
|
||||
```C++
|
||||
std::string name;
|
||||
auto err = doc["name"].get_string(name);
|
||||
if(err) { /* handle error */ }
|
||||
```
|
||||
|
||||
The `std::string` instance, once created, is independent. Unlike our `std::string_view` instances,
|
||||
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
|
||||
can use it with features such as `std::optional`:
|
||||
|
||||
```C++
|
||||
auto json = R"({ "foo1": "3.1416" } )"_padded;
|
||||
ondemand::parser parser;
|
||||
ondemand::document doc = parser.iterate(json);
|
||||
std::optional<std::string> value;
|
||||
if(doc["foo1"].get_string(value)) { /* error */ }
|
||||
// value was populated with "3.1416"
|
||||
```
|
||||
|
||||
You should be mindful of the trade-off: allocating multiple
|
||||
`std::string` instances can become expensive.
|
||||
|
||||
Thread Safety
|
||||
-------------
|
||||
|
||||
@@ -1944,6 +2028,79 @@ bool example() {
|
||||
}
|
||||
```
|
||||
|
||||
* Example 3: CRT
|
||||
|
||||
```C++
|
||||
|
||||
bool example() {
|
||||
padded_string padded_input_json = R"([
|
||||
{ "monitor": [
|
||||
{ "id": "monitor", "type": "toggle", "label": "monitor" },
|
||||
{ "id": "profile", "type": "selector", "label": "collection" },
|
||||
{ "id": "overlay", "type": "selector", "label": "overlay" },
|
||||
{ "id": "zoom", "type": "toggleSlider", "label": "zoom" }
|
||||
] },
|
||||
|
||||
{ "crt": [
|
||||
{ "id": "system", "type": "multi", "label": "system", "choices": "PAL, NTSC" },
|
||||
{ "type": "spacer" },
|
||||
{ "id": "brightness", "type": "slider", "icon": "brightness" },
|
||||
{ "id": "contrast", "type": "slider", "icon": "contrast" },
|
||||
{ "id": "saturation", "type": "slider", "icon": "saturation" },
|
||||
{ "type": "spacer" },
|
||||
{ "id": "overscan", "type": "toggleSlider", "label": "overscan" },
|
||||
{ "type": "spacer" },
|
||||
{ "id": "emulation", "type": "toggle", "label": "CRT emulation" },
|
||||
{ "type": "spacer" },
|
||||
{ "id": "curve", "type": "toggleSlider", "label": "curve" },
|
||||
{ "id": "bleed", "type": "toggleSlider", "label": "bleed" },
|
||||
{ "id": "vignette", "type": "toggleSlider", "label": "vignette" },
|
||||
{ "id": "scanlines", "type": "toggleSlider", "label": "scanlines" },
|
||||
{ "id": "gridlines", "type": "toggleSlider", "label": "gridlines" },
|
||||
{ "id": "glow", "type": "toggleSlider", "label": "glow" },
|
||||
{ "id": "flicker", "type": "toggleSlider", "label": "flicker" },
|
||||
{ "id": "noise", "type": "toggleSlider", "label": "noise" },
|
||||
{}
|
||||
] }
|
||||
])"_padded;
|
||||
auto parser = ondemand::parser{};
|
||||
auto doc = parser.iterate(padded_input_json);
|
||||
auto root_array = doc.get_array();
|
||||
// the root should be an object, not an array, but that's the JSON we are
|
||||
// given.
|
||||
for (ondemand::object node : root_array) {
|
||||
// We know that we are going to have just one element in the object.
|
||||
for (auto field : node) {
|
||||
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();
|
||||
// and second for-range loop to get child-elements here
|
||||
for (ondemand::object inner_object : field.value()) {
|
||||
auto i = inner_object.begin();
|
||||
if (i == inner_object.end()) {
|
||||
std::cout << "empty object" << std::endl;
|
||||
continue;
|
||||
} else {
|
||||
for (; i != inner_object.end(); ++i) {
|
||||
auto inner_field = *i;
|
||||
std::cout << '"' << inner_field.key()
|
||||
<< "\" : " << inner_field.value() << ", ";
|
||||
// You can get proper std::string_view for the key and value with:
|
||||
// std::string_view inner_key = field.unescaped_key();
|
||||
// std::string_view value_str = field.value();
|
||||
}
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
// You can break here if you only want just the first element.
|
||||
// break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
Performance Tips
|
||||
--------
|
||||
@@ -1969,4 +2126,4 @@ Performance Tips
|
||||
std::string_view year = data["year"];
|
||||
std::string_view rating = data["rating"];
|
||||
```
|
||||
- To better understand the operation of your On Demand parser, and whether it is performing as well as you think it should be, there is a logger feature built in to simdjson! To use it, define the pre-processor directive `SIMDJSON_VERBOSE_LOGGING` prior to including the `simdjson.h` header, which enables logging in simdjson. Run your code. It may generate a lot of logging output; adding printouts from your application that show each section may be helpful. The log's output will show step-by-step information on state, buffer pointer position, depth, and key retrieval status. Importantly, unless `SIMDJSON_VERBOSE_LOGGING` is defined, logging is entirely disabled and thus carries no overhead.
|
||||
- To better understand the operation of your On Demand parser, and whether it is performing as well as you think it should be, there is a logger feature built in to simdjson! To use it, define the pre-processor directive `SIMDJSON_VERBOSE_LOGGING` prior to including the `simdjson.h` header, which enables logging in simdjson. Run your code. It may generate a lot of logging output; adding printouts from your application that show each section may be helpful. The log's output will show step-by-step information on state, buffer pointer position, depth, and key retrieval status. Importantly, unless `SIMDJSON_VERBOSE_LOGGING` is defined, logging is entirely disabled and thus carries no overhead.
|
||||
|
||||
+2
-2
@@ -329,10 +329,10 @@ int main(void) {
|
||||
simdjson::dom::parser parser;
|
||||
simdjson::dom::element tweets; // invalid until the get() succeeds
|
||||
auto error = parser.load("twitter.json").get(tweets);
|
||||
if(error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
|
||||
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
|
||||
uint64_t identifier;
|
||||
error = tweets["statuses"].at(0)["id"].get(identifier);
|
||||
if(error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
|
||||
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
|
||||
std::cout << identifier << std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -101,8 +101,8 @@ by comparing it with the null pointer.
|
||||
|
||||
```c++
|
||||
auto my_implementation = simdjson::get_available_implementations()["haswell"];
|
||||
if(! my_implementation) { exit(1); }
|
||||
if(! my_implementation->supported_by_runtime_system()) { exit(1); }
|
||||
if (! my_implementation) { exit(1); }
|
||||
if (! my_implementation->supported_by_runtime_system()) { exit(1); }
|
||||
simdjson::get_active_implementation() = my_implementation;
|
||||
```
|
||||
|
||||
@@ -113,7 +113,7 @@ You should call `supported_by_runtime_system()` to compare the processor's featu
|
||||
|
||||
```c++
|
||||
for (auto implementation : simdjson::get_available_implementations()) {
|
||||
if(implementation->supported_by_runtime_system()) {
|
||||
if (implementation->supported_by_runtime_system()) {
|
||||
cout << implementation->name() << ": " << implementation->description() << endl;
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -196,12 +196,12 @@ Let us illustrate the idea with code:
|
||||
simdjson::ondemand::parser parser;
|
||||
simdjson::ondemand::document_stream stream;
|
||||
auto error = parser.iterate_many(json).get(stream);
|
||||
if( error ) { /* do something */ }
|
||||
if (error) { /* do something */ }
|
||||
auto i = stream.begin();
|
||||
size_t count{0};
|
||||
for(; i != stream.end(); ++i) {
|
||||
auto doc = *i;
|
||||
if(!i.error()) {
|
||||
if (!i.error()) {
|
||||
std::cout << "got full document at " << i.current_index() << std::endl;
|
||||
std::cout << i.source() << std::endl;
|
||||
count++;
|
||||
@@ -237,7 +237,7 @@ Consider the following example where a truncated document (`{"key":"intentionall
|
||||
simdjson::ondemand::parser parser;
|
||||
simdjson::ondemand::document_stream stream;
|
||||
auto error = parser.iterate_many(json,json.size()).get(stream);
|
||||
if(error) { std::cerr << error << std::endl; return; }
|
||||
if (error) { std::cerr << error << std::endl; return; }
|
||||
for(auto i = stream.begin(); i != stream.end(); ++i) {
|
||||
std::cout << i.source() << std::endl;
|
||||
}
|
||||
@@ -269,7 +269,7 @@ Example:
|
||||
// we pass 'true' to the allow_comma parameter, the batch size will be set to at least
|
||||
// the document size.
|
||||
auto error = parser.iterate_many(json, 32, true).get(doc_stream);
|
||||
if(error) { std::cerr << error << std::endl; return; }
|
||||
if (error) { std::cerr << error << std::endl; return; }
|
||||
for (auto doc : doc_stream) {
|
||||
std::cout << doc.type() << std::endl;
|
||||
}
|
||||
|
||||
@@ -679,11 +679,11 @@ in production systems:
|
||||
ondemand::object c1 = parent["child1"];
|
||||
// c1 owns the focus
|
||||
//
|
||||
if(std::string_view(c1["name"]) != "John") { ... }
|
||||
if (std::string_view(c1["name"]) != "John") { ... }
|
||||
// c2 attempts to grab the focus from parent but fails
|
||||
ondemand::object c2 = parent["child2"];
|
||||
// c2 is now in an unsafe state and the following line would be unsafe
|
||||
// if(std::string_view(c2["name"]) != "Daniel") { return false; }
|
||||
// if (std::string_view(c2["name"]) != "Daniel") { return false; }
|
||||
```
|
||||
|
||||
A correct usage is given by the following example:
|
||||
@@ -697,7 +697,7 @@ in production systems:
|
||||
{
|
||||
ondemand::object c1 = parent["child1"];
|
||||
// c1 grabbed the focus from parent
|
||||
if(std::string_view(c1["name"]) != "John") { return false; }
|
||||
if (std::string_view(c1["name"]) != "John") { return false; }
|
||||
}
|
||||
// c1 went out of scope, so its destructor was called and the focus
|
||||
// was handed back to parent.
|
||||
@@ -705,7 +705,7 @@ in production systems:
|
||||
ondemand::object c2 = parent["child2"];
|
||||
// c2 grabbed the focus from parent
|
||||
// the following is safe:
|
||||
if(std::string_view(c2["name"]) != "Daniel") { return false; }
|
||||
if (std::string_view(c2["name"]) != "Daniel") { return false; }
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
+3
-3
@@ -184,12 +184,12 @@ Let us illustrate the idea with code:
|
||||
simdjson::dom::parser parser;
|
||||
simdjson::dom::document_stream stream;
|
||||
auto error = parser.parse_many(json).get(stream);
|
||||
if( error ) { /* do something */ }
|
||||
if (error) { /* do something */ }
|
||||
auto i = stream.begin();
|
||||
size_t count{0};
|
||||
for(; i != stream.end(); ++i) {
|
||||
auto doc = *i;
|
||||
if(!doc.error()) {
|
||||
if (!doc.error()) {
|
||||
std::cout << "got full document at " << i.current_index() << std::endl;
|
||||
std::cout << i.source() << std::endl;
|
||||
count++;
|
||||
@@ -225,7 +225,7 @@ Consider the following example where a truncated document (`{"key":"intentionall
|
||||
simdjson::dom::parser parser;
|
||||
simdjson::dom::document_stream stream;
|
||||
auto error = parser.parse_many(json,json.size()).get(stream);
|
||||
if(error) { std::cerr << error << std::endl; return; }
|
||||
if (error) { std::cerr << error << std::endl; return; }
|
||||
for(auto doc : stream) {
|
||||
std::cout << doc << std::endl;
|
||||
}
|
||||
|
||||
@@ -17,89 +17,53 @@ namespace {
|
||||
// Start of private section with Visual Studio workaround
|
||||
|
||||
|
||||
/**
|
||||
* make_uint8x16_t initializes a SIMD register (uint8x16_t).
|
||||
* This is needed because, incredibly, the syntax uint8x16_t x = {1,2,3...}
|
||||
* is not recognized under Visual Studio! This is a workaround.
|
||||
* Using a std::initializer_list<uint8_t> as a parameter resulted in
|
||||
* inefficient code. With the current approach, if the parameters are
|
||||
* compile-time constants,
|
||||
* GNU GCC compiles it to ldr, the same as uint8x16_t x = {1,2,3...}.
|
||||
* You should not use this function except for compile-time constants:
|
||||
* it is not efficient.
|
||||
*/
|
||||
simdjson_inline uint8x16_t make_uint8x16_t(uint8_t x1, uint8_t x2, uint8_t x3, uint8_t x4,
|
||||
uint8_t x5, uint8_t x6, uint8_t x7, uint8_t x8,
|
||||
uint8_t x9, uint8_t x10, uint8_t x11, uint8_t x12,
|
||||
uint8_t x13, uint8_t x14, uint8_t x15, uint8_t x16) {
|
||||
// Doing a load like so end ups generating worse code.
|
||||
// uint8_t array[16] = {x1, x2, x3, x4, x5, x6, x7, x8,
|
||||
// x9, x10,x11,x12,x13,x14,x15,x16};
|
||||
// return vld1q_u8(array);
|
||||
uint8x16_t x{};
|
||||
// incredibly, Visual Studio does not allow x[0] = x1
|
||||
x = vsetq_lane_u8(x1, x, 0);
|
||||
x = vsetq_lane_u8(x2, x, 1);
|
||||
x = vsetq_lane_u8(x3, x, 2);
|
||||
x = vsetq_lane_u8(x4, x, 3);
|
||||
x = vsetq_lane_u8(x5, x, 4);
|
||||
x = vsetq_lane_u8(x6, x, 5);
|
||||
x = vsetq_lane_u8(x7, x, 6);
|
||||
x = vsetq_lane_u8(x8, x, 7);
|
||||
x = vsetq_lane_u8(x9, x, 8);
|
||||
x = vsetq_lane_u8(x10, x, 9);
|
||||
x = vsetq_lane_u8(x11, x, 10);
|
||||
x = vsetq_lane_u8(x12, x, 11);
|
||||
x = vsetq_lane_u8(x13, x, 12);
|
||||
x = vsetq_lane_u8(x14, x, 13);
|
||||
x = vsetq_lane_u8(x15, x, 14);
|
||||
x = vsetq_lane_u8(x16, x, 15);
|
||||
return x;
|
||||
}
|
||||
#ifndef simdjson_make_uint8x16_t
|
||||
#define simdjson_make_uint8x16_t(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, \
|
||||
x13, x14, x15, x16) \
|
||||
([=]() { \
|
||||
static uint8_t array[16] = {x1, x2, x3, x4, x5, x6, x7, x8, \
|
||||
x9, x10, x11, x12, x13, x14, x15, x16}; \
|
||||
return vld1q_u8(array); \
|
||||
}())
|
||||
#endif
|
||||
#ifndef simdjson_make_int8x16_t
|
||||
#define simdjson_make_int8x16_t(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, \
|
||||
x13, x14, x15, x16) \
|
||||
([=]() { \
|
||||
static int8_t array[16] = {x1, x2, x3, x4, x5, x6, x7, x8, \
|
||||
x9, x10, x11, x12, x13, x14, x15, x16}; \
|
||||
return vld1q_s8(array); \
|
||||
}())
|
||||
#endif
|
||||
|
||||
simdjson_inline uint8x8_t make_uint8x8_t(uint8_t x1, uint8_t x2, uint8_t x3, uint8_t x4,
|
||||
uint8_t x5, uint8_t x6, uint8_t x7, uint8_t x8) {
|
||||
uint8x8_t x{};
|
||||
x = vset_lane_u8(x1, x, 0);
|
||||
x = vset_lane_u8(x2, x, 1);
|
||||
x = vset_lane_u8(x3, x, 2);
|
||||
x = vset_lane_u8(x4, x, 3);
|
||||
x = vset_lane_u8(x5, x, 4);
|
||||
x = vset_lane_u8(x6, x, 5);
|
||||
x = vset_lane_u8(x7, x, 6);
|
||||
x = vset_lane_u8(x8, x, 7);
|
||||
return x;
|
||||
}
|
||||
|
||||
// We have to do the same work for make_int8x16_t
|
||||
simdjson_inline int8x16_t make_int8x16_t(int8_t x1, int8_t x2, int8_t x3, int8_t x4,
|
||||
int8_t x5, int8_t x6, int8_t x7, int8_t x8,
|
||||
int8_t x9, int8_t x10, int8_t x11, int8_t x12,
|
||||
int8_t x13, int8_t x14, int8_t x15, int8_t x16) {
|
||||
// Doing a load like so end ups generating worse code.
|
||||
// int8_t array[16] = {x1, x2, x3, x4, x5, x6, x7, x8,
|
||||
// x9, x10,x11,x12,x13,x14,x15,x16};
|
||||
// return vld1q_s8(array);
|
||||
int8x16_t x{};
|
||||
// incredibly, Visual Studio does not allow x[0] = x1
|
||||
x = vsetq_lane_s8(x1, x, 0);
|
||||
x = vsetq_lane_s8(x2, x, 1);
|
||||
x = vsetq_lane_s8(x3, x, 2);
|
||||
x = vsetq_lane_s8(x4, x, 3);
|
||||
x = vsetq_lane_s8(x5, x, 4);
|
||||
x = vsetq_lane_s8(x6, x, 5);
|
||||
x = vsetq_lane_s8(x7, x, 6);
|
||||
x = vsetq_lane_s8(x8, x, 7);
|
||||
x = vsetq_lane_s8(x9, x, 8);
|
||||
x = vsetq_lane_s8(x10, x, 9);
|
||||
x = vsetq_lane_s8(x11, x, 10);
|
||||
x = vsetq_lane_s8(x12, x, 11);
|
||||
x = vsetq_lane_s8(x13, x, 12);
|
||||
x = vsetq_lane_s8(x14, x, 13);
|
||||
x = vsetq_lane_s8(x15, x, 14);
|
||||
x = vsetq_lane_s8(x16, x, 15);
|
||||
return x;
|
||||
}
|
||||
#ifndef simdjson_make_uint8x8_t
|
||||
#define simdjson_make_uint8x8_t(x1, x2, x3, x4, x5, x6, x7, x8) \
|
||||
([=]() { \
|
||||
static uint8_t array[8] = {x1, x2, x3, x4, x5, x6, x7, x8}; \
|
||||
return vld1_u8(array); \
|
||||
}())
|
||||
#endif
|
||||
#ifndef simdjson_make_int8x8_t
|
||||
#define simdjson_make_int8x8_t(x1, x2, x3, x4, x5, x6, x7, x8) \
|
||||
([=]() { \
|
||||
static int8_t array[8] = {x1, x2, x3, x4, x5, x6, x7, x8}; \
|
||||
return vld1_s8(array); \
|
||||
}())
|
||||
#endif
|
||||
#ifndef simdjson_make_uint16x8_t
|
||||
#define simdjson_make_uint16x8_t(x1, x2, x3, x4, x5, x6, x7, x8) \
|
||||
([=]() { \
|
||||
static uint16_t array[8] = {x1, x2, x3, x4, x5, x6, x7, x8}; \
|
||||
return vld1q_u16(array); \
|
||||
}())
|
||||
#endif
|
||||
#ifndef simdjson_make_int16x8_t
|
||||
#define simdjson_make_int16x8_t(x1, x2, x3, x4, x5, x6, x7, x8) \
|
||||
([=]() { \
|
||||
static int16_t array[8] = {x1, x2, x3, x4, x5, x6, x7, x8}; \
|
||||
return vld1q_s16(array); \
|
||||
}())
|
||||
#endif
|
||||
|
||||
// End of private section with Visual Studio workaround
|
||||
} // namespace
|
||||
@@ -158,7 +122,7 @@ simdjson_inline int8x16_t make_int8x16_t(int8_t x1, int8_t x2, int8_t x3, int
|
||||
// purposes (cutting it down to uint16_t costs performance in some compilers).
|
||||
simdjson_inline uint32_t to_bitmask() const {
|
||||
#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO
|
||||
const uint8x16_t bit_mask = make_uint8x16_t(0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80,
|
||||
const uint8x16_t bit_mask = simdjson_make_uint8x16_t(0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80,
|
||||
0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80);
|
||||
#else
|
||||
const uint8x16_t bit_mask = {0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80,
|
||||
@@ -192,7 +156,7 @@ simdjson_inline int8x16_t make_int8x16_t(int8_t x1, int8_t x2, int8_t x3, int
|
||||
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(make_uint8x16_t(
|
||||
) : simd8(simdjson_make_uint8x16_t(
|
||||
v0, v1, v2, v3, v4, v5, v6, v7,
|
||||
v8, v9, v10,v11,v12,v13,v14,v15
|
||||
)) {}
|
||||
@@ -283,7 +247,7 @@ simdjson_inline int8x16_t make_int8x16_t(int8_t x1, int8_t x2, int8_t x3, int
|
||||
uint8x16_t shufmask = vreinterpretq_u8_u64(shufmask64);
|
||||
// we increment by 0x08 the second half of the mask
|
||||
#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO
|
||||
uint8x16_t inc = make_uint8x16_t(0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08);
|
||||
uint8x16_t inc = simdjson_make_uint8x16_t(0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08);
|
||||
#else
|
||||
uint8x16_t inc = {0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08};
|
||||
#endif
|
||||
@@ -313,7 +277,7 @@ simdjson_inline int8x16_t make_int8x16_t(int8_t x1, int8_t x2, int8_t x3, int
|
||||
uint8x8_t compactmask2 = vcreate_u8(thintable_epi8[mask2]);
|
||||
// we increment by 0x08 the second half of the mask
|
||||
#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO
|
||||
uint8x8_t inc = make_uint8x8_t(0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08);
|
||||
uint8x8_t inc = simdjson_make_uint8x8_t(0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08);
|
||||
#else
|
||||
uint8x8_t inc = {0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08};
|
||||
#endif
|
||||
@@ -368,7 +332,7 @@ simdjson_inline int8x16_t make_int8x16_t(int8_t x1, int8_t x2, int8_t x3, int
|
||||
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(make_int8x16_t(
|
||||
) : simd8(simdjson_make_int8x16_t(
|
||||
v0, v1, v2, v3, v4, v5, v6, v7,
|
||||
v8, v9, v10,v11,v12,v13,v14,v15
|
||||
)) {}
|
||||
@@ -486,7 +450,7 @@ simdjson_inline int8x16_t make_int8x16_t(int8_t x1, int8_t x2, int8_t x3, int
|
||||
|
||||
simdjson_inline uint64_t to_bitmask() const {
|
||||
#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO
|
||||
const uint8x16_t bit_mask = make_uint8x16_t(
|
||||
const uint8x16_t bit_mask = simdjson_make_uint8x16_t(
|
||||
0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80,
|
||||
0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80
|
||||
);
|
||||
|
||||
@@ -32,4 +32,12 @@
|
||||
#error simdjson requires a compiler compliant with the C++11 standard
|
||||
#endif
|
||||
|
||||
#ifndef SIMDJSON_IF_CONSTEXPR
|
||||
#if SIMDJSON_CPLUSPLUS17
|
||||
#define SIMDJSON_IF_CONSTEXPR if constexpr
|
||||
#else
|
||||
#define SIMDJSON_IF_CONSTEXPR if
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif // SIMDJSON_COMPILER_CHECK_H
|
||||
|
||||
@@ -224,8 +224,7 @@ private:
|
||||
* Parse the next document found in the buffer previously given to document_stream.
|
||||
*
|
||||
* The content should be a valid JSON document encoded as UTF-8. If there is a
|
||||
* UTF-8 BOM, the caller is responsible for omitting it, UTF-8 BOM are
|
||||
* discouraged.
|
||||
* UTF-8 BOM, the parser skips it.
|
||||
*
|
||||
* You do NOT need to pre-allocate a parser. This function takes care of
|
||||
* pre-allocating a capacity defined by the batch_size defined when creating the
|
||||
|
||||
@@ -410,6 +410,12 @@ inline simdjson_result<element> element::at_key(std::string_view key) const noex
|
||||
inline simdjson_result<element> element::at_key_case_insensitive(std::string_view key) const noexcept {
|
||||
return get<object>().at_key_case_insensitive(key);
|
||||
}
|
||||
inline bool element::operator<(const element &other) const noexcept {
|
||||
return tape.json_index < other.tape.json_index;
|
||||
}
|
||||
inline bool element::operator==(const element &other) const noexcept {
|
||||
return tape.json_index == other.tape.json_index;
|
||||
}
|
||||
|
||||
inline bool element::dump_raw_tape(std::ostream &out) const noexcept {
|
||||
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
|
||||
|
||||
@@ -211,7 +211,11 @@ public:
|
||||
inline simdjson_result<T> get() const noexcept {
|
||||
// Unless the simdjson library 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.");
|
||||
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
|
||||
"The supported types are Boolean (bool), numbers (double, uint64_t, int64_t), "
|
||||
"strings (std::string_view, const char *), arrays (dom::array) and objects (dom::object). "
|
||||
"We recommand you use get_double(), get_bool(), get_uint64(), get_int64(), "
|
||||
"get_object(), get_array() or get_string() instead of the get template.");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -451,6 +455,22 @@ public:
|
||||
*/
|
||||
inline simdjson_result<element> at_key_case_insensitive(std::string_view key) const noexcept;
|
||||
|
||||
/**
|
||||
* operator< defines a total order for element allowing to use them in
|
||||
* ordered C++ STL containers
|
||||
*
|
||||
* @return TRUE if the key appears before the other one in the tape
|
||||
*/
|
||||
inline bool operator<(const element &other) const noexcept;
|
||||
|
||||
/**
|
||||
* operator== allows to verify if two element values reference the
|
||||
* same JSON item
|
||||
*
|
||||
* @return TRUE if the two values references the same JSON element
|
||||
*/
|
||||
inline bool operator==(const element &other) const noexcept;
|
||||
|
||||
/** @private for debugging. Prints out the root element. */
|
||||
inline bool dump_raw_tape(std::ostream &out) const noexcept;
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "simdjson/dom/element-inl.h"
|
||||
|
||||
#include <climits>
|
||||
#include <cstring> /* memcmp */
|
||||
|
||||
namespace simdjson {
|
||||
namespace dom {
|
||||
@@ -120,8 +121,14 @@ inline simdjson_result<element> parser::parse_into_document(document& provided_d
|
||||
_loaded_bytes_capacity = len;
|
||||
}
|
||||
std::memcpy(static_cast<void *>(loaded_bytes.get()), buf, len);
|
||||
buf = reinterpret_cast<const uint8_t*>(loaded_bytes.get());
|
||||
}
|
||||
_error = implementation->parse(realloc_if_needed ? reinterpret_cast<const uint8_t*>(loaded_bytes.get()): buf, len, provided_doc);
|
||||
|
||||
if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) {
|
||||
buf += 3;
|
||||
len -= 3;
|
||||
}
|
||||
_error = implementation->parse(buf, len, provided_doc);
|
||||
|
||||
if (_error) { return _error; }
|
||||
|
||||
@@ -158,6 +165,10 @@ simdjson_inline simdjson_result<element> parser::parse(const padded_string_view
|
||||
|
||||
inline simdjson_result<document_stream> parser::parse_many(const uint8_t *buf, size_t len, size_t batch_size) noexcept {
|
||||
if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; }
|
||||
if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) {
|
||||
buf += 3;
|
||||
len -= 3;
|
||||
}
|
||||
return document_stream(*this, buf, len, batch_size);
|
||||
}
|
||||
inline simdjson_result<document_stream> parser::parse_many(const char *buf, size_t len, size_t batch_size) noexcept {
|
||||
|
||||
@@ -254,6 +254,8 @@ public:
|
||||
* And, possibly, no document many have been parsed when the `parser.load_many(path)` function
|
||||
* returned.
|
||||
*
|
||||
* If there is a UTF-8 BOM, the parser skips it.
|
||||
*
|
||||
* ### Format
|
||||
*
|
||||
* The file must contain a series of one or more JSON documents, concatenated into a single
|
||||
@@ -346,6 +348,8 @@ public:
|
||||
* cout << std::string(doc["title"]) << endl;
|
||||
* }
|
||||
*
|
||||
* If there is a UTF-8 BOM, the parser skips it.
|
||||
*
|
||||
* ### Format
|
||||
*
|
||||
* The buffer must contain a series of one or more JSON documents, concatenated into a single
|
||||
|
||||
@@ -42,7 +42,7 @@ enum error_code {
|
||||
INVALID_URI_FRAGMENT, ///< Invalid URI fragment
|
||||
UNEXPECTED_ERROR, ///< indicative of a bug in simdjson
|
||||
PARSER_IN_USE, ///< parser is already in use.
|
||||
OUT_OF_ORDER_ITERATION, ///< tried to iterate an array or object out of order
|
||||
OUT_OF_ORDER_ITERATION, ///< tried to iterate an array or object out of order (checked when SIMDJSON_DEVELOPMENT_CHECKS=1)
|
||||
INSUFFICIENT_PADDING, ///< The JSON doesn't have enough padding for simdjson to safely parse it.
|
||||
INCOMPLETE_ARRAY_OR_OBJECT, ///< The document ends early.
|
||||
SCALAR_DOCUMENT_AS_VALUE, ///< A scalar document is treated as a value.
|
||||
@@ -51,6 +51,13 @@ enum error_code {
|
||||
NUM_ERROR_CODES
|
||||
};
|
||||
|
||||
/**
|
||||
* It is the convention throughout the code that the macro SIMDJSON_DEVELOPMENT_CHECKS determines whether
|
||||
* we check for OUT_OF_ORDER_ITERATION. The logic behind it is that these errors only occurs when the code
|
||||
* that was written while breaking some simdjson::ondemand requirement. They should not occur in released
|
||||
* code after these issues were fixed.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get the error message for the given error code.
|
||||
*
|
||||
@@ -210,11 +217,11 @@ struct simdjson_result : public internal::simdjson_result_base<T> {
|
||||
*/
|
||||
simdjson_inline simdjson_result() noexcept;
|
||||
/**
|
||||
* @private Create a new error result.
|
||||
* @private Create a new successful result.
|
||||
*/
|
||||
simdjson_inline simdjson_result(T &&value) noexcept;
|
||||
/**
|
||||
* @private Create a new successful result.
|
||||
* @private Create a new error result.
|
||||
*/
|
||||
simdjson_inline simdjson_result(error_code error_code) noexcept;
|
||||
/**
|
||||
|
||||
@@ -186,8 +186,7 @@ simdjson_inline bool compute_float_64(int64_t power, uint64_t i, bool negative,
|
||||
// power_of_five_128[index]. Usually, that's good enough to approximate i * 5^q
|
||||
// to the desired approximation using one multiplication. Sometimes it does not suffice.
|
||||
// Then we store the next most significant 64 bits in power_of_five_128[index + 1], and
|
||||
// then we get a better approximation to i * 5^q. In very rare cases, even that
|
||||
// will not suffice, though it is seemingly very hard to find such a scenario.
|
||||
// then we get a better approximation to i * 5^q.
|
||||
//
|
||||
// That's for when q>=0. The logic for q<0 is somewhat similar but it is somewhat
|
||||
// more complicated.
|
||||
@@ -202,12 +201,9 @@ simdjson_inline bool compute_float_64(int64_t power, uint64_t i, bool negative,
|
||||
simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]);
|
||||
firstproduct.low += secondproduct.high;
|
||||
if(secondproduct.high > firstproduct.low) { firstproduct.high++; }
|
||||
// At this point, we might need to add at most one to firstproduct, but this
|
||||
// can only change the value of firstproduct.high if firstproduct.low is maximal.
|
||||
if(simdjson_unlikely(firstproduct.low == 0xFFFFFFFFFFFFFFFF)) {
|
||||
// This is very unlikely, but if so, we need to do much more work!
|
||||
return false;
|
||||
}
|
||||
// As it has been proven by Noble Mushtak and Daniel Lemire in "Fast Number Parsing Without
|
||||
// Fallback" (https://arxiv.org/abs/2212.06644), at this point we are sure that the product
|
||||
// is sufficiently accurate, and more computation is not needed.
|
||||
}
|
||||
uint64_t lower = firstproduct.low;
|
||||
uint64_t upper = firstproduct.high;
|
||||
|
||||
@@ -68,7 +68,17 @@ simdjson_inline simdjson_result<object> document::start_or_resume_object() noexc
|
||||
simdjson_inline simdjson_result<value> document::get_value() noexcept {
|
||||
// Make sure we start any arrays or objects before returning, so that start_root_<object/array>()
|
||||
// gets called.
|
||||
iter.assert_at_document_depth();
|
||||
|
||||
// It is the convention throughout the code that the macro `SIMDJSON_DEVELOPMENT_CHECKS` determines whether
|
||||
// we check for OUT_OF_ORDER_ITERATION. Proper on::demand code should never trigger this error.
|
||||
#if SIMDJSON_DEVELOPMENT_CHECKS
|
||||
if (!iter.at_root()) { return OUT_OF_ORDER_ITERATION; }
|
||||
#endif
|
||||
// assert_at_root() serves two purposes: in Debug mode, whether or not
|
||||
// SIMDJSON_DEVELOPMENT_CHECKS is set or not, it checks that we are at the root of
|
||||
// the document (this will typically be redundant). In release mode, it generates
|
||||
// SIMDJSON_ASSUME statements to allow the compiler to make assumptions.
|
||||
iter.assert_at_root();
|
||||
switch (*iter.peek()) {
|
||||
case '[': {
|
||||
// The following lines check that the document ends with ].
|
||||
@@ -128,6 +138,10 @@ simdjson_inline simdjson_result<double> document::get_double_in_string() noexcep
|
||||
simdjson_inline simdjson_result<std::string_view> document::get_string(bool allow_replacement) noexcept {
|
||||
return get_root_value_iterator().get_root_string(true, allow_replacement);
|
||||
}
|
||||
template <typename string_type>
|
||||
simdjson_inline error_code document::get_string(string_type& receiver, bool allow_replacement) noexcept {
|
||||
return get_root_value_iterator().get_root_string(receiver, true, allow_replacement);
|
||||
}
|
||||
simdjson_inline simdjson_result<std::string_view> document::get_wobbly_string() noexcept {
|
||||
return get_root_value_iterator().get_root_wobbly_string(true);
|
||||
}
|
||||
@@ -397,6 +411,11 @@ simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLE
|
||||
if (error()) { return error(); }
|
||||
return first.get_string(allow_replacement);
|
||||
}
|
||||
template <typename string_type>
|
||||
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::get_string(string_type& receiver, bool allow_replacement) noexcept {
|
||||
if (error()) { return error(); }
|
||||
return first.get_string(receiver, allow_replacement);
|
||||
}
|
||||
simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::get_wobbly_string() noexcept {
|
||||
if (error()) { return error(); }
|
||||
return first.get_wobbly_string();
|
||||
@@ -583,6 +602,8 @@ simdjson_inline simdjson_result<int64_t> document_reference::get_int64_in_string
|
||||
simdjson_inline simdjson_result<double> document_reference::get_double() noexcept { return doc->get_root_value_iterator().get_root_double(false); }
|
||||
simdjson_inline simdjson_result<double> document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double(false); }
|
||||
simdjson_inline simdjson_result<std::string_view> document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); }
|
||||
template <typename string_type>
|
||||
simdjson_inline error_code document_reference::get_string(string_type& receiver, bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(receiver, false, allow_replacement); }
|
||||
simdjson_inline simdjson_result<std::string_view> document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); }
|
||||
simdjson_inline simdjson_result<raw_json_string> document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); }
|
||||
simdjson_inline simdjson_result<bool> document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); }
|
||||
@@ -719,6 +740,11 @@ simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLE
|
||||
if (error()) { return error(); }
|
||||
return first.get_string(allow_replacement);
|
||||
}
|
||||
template <typename string_type>
|
||||
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get_string(string_type& receiver, bool allow_replacement) noexcept {
|
||||
if (error()) { return error(); }
|
||||
return first.get_string(receiver, allow_replacement);
|
||||
}
|
||||
simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get_wobbly_string() noexcept {
|
||||
if (error()) { return error(); }
|
||||
return first.get_wobbly_string();
|
||||
|
||||
@@ -100,6 +100,21 @@ public:
|
||||
* @returns INCORRECT_TYPE if the JSON value is not a string.
|
||||
*/
|
||||
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
|
||||
/**
|
||||
* Attempts to fill the provided std::string reference with the parsed value of the current string.
|
||||
*
|
||||
* The string is guaranteed to be valid UTF-8.
|
||||
*
|
||||
* Important: a value should be consumed once. Calling get_string() twice on the same value
|
||||
* is an error.
|
||||
*
|
||||
* Performance: This method may be slower than get_string() or get_string(bool) because it may need to allocate memory.
|
||||
* We recommend you avoid allocating an std::string unless you need to.
|
||||
*
|
||||
* @returns INCORRECT_TYPE if the JSON value is not a string. Otherwise, we return SUCCESS.
|
||||
*/
|
||||
template <typename string_type>
|
||||
simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
|
||||
/**
|
||||
* Cast this JSON value to a string.
|
||||
*
|
||||
@@ -131,6 +146,12 @@ public:
|
||||
/**
|
||||
* Cast this JSON value to a value when the document is an object or an array.
|
||||
*
|
||||
* You must not have begun iterating through the object or array. When
|
||||
* SIMDJSON_DEVELOPMENT_CHECKS is set to 1 (which is the case when building in Debug mode
|
||||
* by default), and you have already begun iterating,
|
||||
* you will get an OUT_OF_ORDER_ITERATION error. If you have begun iterating, you can use
|
||||
* rewind() to reset the document to its initial state before calling this method.
|
||||
*
|
||||
* @returns A value if a JSON array or object cannot be found.
|
||||
* @returns SCALAR_DOCUMENT_AS_VALUE error is the document is a scalar (see is_scalar() function).
|
||||
*/
|
||||
@@ -160,13 +181,19 @@ public:
|
||||
template<typename T> simdjson_inline simdjson_result<T> get() & noexcept {
|
||||
// Unless the simdjson library 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.");
|
||||
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.");
|
||||
}
|
||||
/** @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
|
||||
// immediately fail.
|
||||
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library.");
|
||||
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.");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -247,10 +274,15 @@ public:
|
||||
*/
|
||||
simdjson_inline operator bool() noexcept(false);
|
||||
/**
|
||||
* Cast this JSON value to a value.
|
||||
* Cast this JSON value to a value when the document is an object or an array.
|
||||
*
|
||||
* @returns A value value.
|
||||
* @exception if a JSON value cannot be found
|
||||
* You must not have begun iterating through the object or array. When
|
||||
* SIMDJSON_DEVELOPMENT_CHECKS is defined, and you have already begun iterating,
|
||||
* you will get an OUT_OF_ORDER_ITERATION error. If you have begun iterating, you can use
|
||||
* rewind() to reset the document to its initial state before calling this method.
|
||||
*
|
||||
* @returns A value value if a JSON array or object cannot be found.
|
||||
* @exception SCALAR_DOCUMENT_AS_VALUE error is the document is a scalar (see is_scalar() function).
|
||||
*/
|
||||
simdjson_inline operator value() noexcept(false);
|
||||
#endif
|
||||
@@ -618,6 +650,8 @@ public:
|
||||
simdjson_inline simdjson_result<double> get_double() noexcept;
|
||||
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
|
||||
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
|
||||
template <typename string_type>
|
||||
simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
|
||||
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
|
||||
simdjson_inline simdjson_result<raw_json_string> get_raw_json_string() noexcept;
|
||||
simdjson_inline simdjson_result<bool> get_bool() noexcept;
|
||||
@@ -687,6 +721,8 @@ public:
|
||||
simdjson_inline simdjson_result<double> get_double() noexcept;
|
||||
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
|
||||
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
|
||||
template <typename string_type>
|
||||
simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
|
||||
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::raw_json_string> get_raw_json_string() noexcept;
|
||||
simdjson_inline simdjson_result<bool> get_bool() noexcept;
|
||||
@@ -759,6 +795,8 @@ public:
|
||||
simdjson_inline simdjson_result<double> get_double() noexcept;
|
||||
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
|
||||
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
|
||||
template <typename string_type>
|
||||
simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
|
||||
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::raw_json_string> get_raw_json_string() noexcept;
|
||||
simdjson_inline simdjson_result<bool> get_bool() noexcept;
|
||||
|
||||
@@ -241,8 +241,7 @@ private:
|
||||
* Parse the next document found in the buffer previously given to document_stream.
|
||||
*
|
||||
* The content should be a valid JSON document encoded as UTF-8. If there is a
|
||||
* UTF-8 BOM, the caller is responsible for omitting it, UTF-8 BOM are
|
||||
* discouraged.
|
||||
* UTF-8 BOM, the parser skips it.
|
||||
*
|
||||
* You do NOT need to pre-allocate a parser. This function takes care of
|
||||
* pre-allocating a capacity defined by the batch_size defined when creating the
|
||||
|
||||
@@ -254,6 +254,7 @@ public:
|
||||
*/
|
||||
simdjson_inline simdjson_result<std::string_view> unescape(raw_json_string in, bool allow_replacement) noexcept;
|
||||
simdjson_inline simdjson_result<std::string_view> unescape_wobbly(raw_json_string in) noexcept;
|
||||
|
||||
simdjson_inline void reenter_child(token_position position, depth_t child_depth) noexcept;
|
||||
|
||||
simdjson_inline error_code consume_character(char c) noexcept;
|
||||
|
||||
@@ -46,6 +46,8 @@ simdjson_warn_unused simdjson_inline error_code parser::allocate(size_t new_capa
|
||||
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(padded_string_view json) & noexcept {
|
||||
if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; }
|
||||
|
||||
json.remove_utf8_bom();
|
||||
|
||||
// Allocate if needed
|
||||
if (capacity() < json.length() || !string_buf) {
|
||||
SIMDJSON_TRY( allocate(json.length(), max_depth()) );
|
||||
@@ -68,6 +70,13 @@ simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(s
|
||||
return iterate(padded_string_view(json, allocated));
|
||||
}
|
||||
|
||||
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(std::string &json) & noexcept {
|
||||
if(json.capacity() - json.size() < SIMDJSON_PADDING) {
|
||||
json.reserve(json.size() + SIMDJSON_PADDING);
|
||||
}
|
||||
return iterate(padded_string_view(json));
|
||||
}
|
||||
|
||||
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const std::string &json) & noexcept {
|
||||
return iterate(padded_string_view(json));
|
||||
}
|
||||
@@ -89,6 +98,8 @@ simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(c
|
||||
simdjson_warn_unused simdjson_inline simdjson_result<json_iterator> parser::iterate_raw(padded_string_view json) & noexcept {
|
||||
if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; }
|
||||
|
||||
json.remove_utf8_bom();
|
||||
|
||||
// Allocate if needed
|
||||
if (capacity() < json.length()) {
|
||||
SIMDJSON_TRY( allocate(json.length(), max_depth()) );
|
||||
@@ -101,6 +112,10 @@ simdjson_warn_unused simdjson_inline simdjson_result<json_iterator> parser::iter
|
||||
|
||||
inline simdjson_result<document_stream> parser::iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept {
|
||||
if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; }
|
||||
if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) {
|
||||
buf += 3;
|
||||
len -= 3;
|
||||
}
|
||||
if(allow_comma_separated && batch_size < len) { batch_size = len; }
|
||||
return document_stream(*this, buf, len, batch_size, allow_comma_separated);
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ public:
|
||||
* It is expected that the content is a valid UTF-8 file, containing a valid JSON document.
|
||||
* Otherwise the iterate method may return an error. In particular, the whole input should be
|
||||
* valid: we do not attempt to tolerate incorrect content either before or after a JSON
|
||||
* document.
|
||||
* document. If there is a UTF-8 BOM, the parser skips it.
|
||||
*
|
||||
* ### IMPORTANT: Validate what you use
|
||||
*
|
||||
@@ -107,6 +107,8 @@ public:
|
||||
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
|
||||
simdjson_warn_unused simdjson_result<document> iterate(const std::string &json) & noexcept;
|
||||
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
|
||||
simdjson_warn_unused simdjson_result<document> iterate(std::string &json) & noexcept;
|
||||
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
|
||||
simdjson_warn_unused simdjson_result<document> iterate(const simdjson_result<padded_string> &json) & noexcept;
|
||||
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
|
||||
simdjson_warn_unused simdjson_result<document> iterate(const simdjson_result<padded_string_view> &json) & noexcept;
|
||||
@@ -186,6 +188,7 @@ public:
|
||||
* arrays or objects) MUST be separated with ASCII whitespace.
|
||||
*
|
||||
* The characters inside a JSON document, and between JSON documents, must be valid Unicode (UTF-8).
|
||||
* If there is a UTF-8 BOM, the parser skips it.
|
||||
*
|
||||
* The documents must not exceed batch_size bytes (by default 1MB) or they will fail to parse.
|
||||
* Setting batch_size to excessively large or excessively small values may impact negatively the
|
||||
|
||||
@@ -47,6 +47,10 @@ simdjson_inline simdjson_result<raw_json_string> value::get_raw_json_string() no
|
||||
simdjson_inline simdjson_result<std::string_view> value::get_string(bool allow_replacement) noexcept {
|
||||
return iter.get_string(allow_replacement);
|
||||
}
|
||||
template <typename string_type>
|
||||
simdjson_inline error_code value::get_string(string_type& receiver, bool allow_replacement) noexcept {
|
||||
return iter.get_string(receiver, allow_replacement);
|
||||
}
|
||||
simdjson_inline simdjson_result<std::string_view> value::get_wobbly_string() noexcept {
|
||||
return iter.get_wobbly_string();
|
||||
}
|
||||
@@ -193,6 +197,26 @@ simdjson_inline std::string_view value::raw_json_token() noexcept {
|
||||
return std::string_view(reinterpret_cast<const char*>(iter.peek_start()), iter.peek_start_length());
|
||||
}
|
||||
|
||||
simdjson_inline simdjson_result<std::string_view> value::raw_json() noexcept {
|
||||
json_type t;
|
||||
SIMDJSON_TRY(type().get(t));
|
||||
switch (t)
|
||||
{
|
||||
case json_type::array: {
|
||||
ondemand::array array;
|
||||
SIMDJSON_TRY(get_array().get(array));
|
||||
return array.raw_json();
|
||||
}
|
||||
case json_type::object: {
|
||||
ondemand::object object;
|
||||
SIMDJSON_TRY(get_object().get(object));
|
||||
return object.raw_json();
|
||||
}
|
||||
default:
|
||||
return raw_json_token();
|
||||
}
|
||||
}
|
||||
|
||||
simdjson_inline simdjson_result<const char *> value::current_location() noexcept {
|
||||
return iter.json_iter().current_location();
|
||||
}
|
||||
@@ -319,6 +343,11 @@ simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLE
|
||||
if (error()) { return error(); }
|
||||
return first.get_string(allow_replacement);
|
||||
}
|
||||
template <typename string_type>
|
||||
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::get_string(string_type& receiver, bool allow_replacement) noexcept {
|
||||
if (error()) { return error(); }
|
||||
return first.get_string(receiver, allow_replacement);
|
||||
}
|
||||
simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::get_wobbly_string() noexcept {
|
||||
if (error()) { return error(); }
|
||||
return first.get_wobbly_string();
|
||||
@@ -419,6 +448,11 @@ simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLE
|
||||
return first.raw_json_token();
|
||||
}
|
||||
|
||||
simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::raw_json() noexcept {
|
||||
if (error()) { return error(); }
|
||||
return first.raw_json();
|
||||
}
|
||||
|
||||
simdjson_inline simdjson_result<const char *> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::current_location() noexcept {
|
||||
if (error()) { return error(); }
|
||||
return first.current_location();
|
||||
|
||||
@@ -38,7 +38,10 @@ public:
|
||||
template<typename T> simdjson_inline simdjson_result<T> get() noexcept {
|
||||
// Unless the simdjson library 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.");
|
||||
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.");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -132,6 +135,21 @@ public:
|
||||
*/
|
||||
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
|
||||
|
||||
/**
|
||||
* Attempts to fill the provided std::string reference with the parsed value of the current string.
|
||||
*
|
||||
* The string is guaranteed to be valid UTF-8.
|
||||
*
|
||||
* Important: a value should be consumed once. Calling get_string() twice on the same value
|
||||
* is an error.
|
||||
*
|
||||
* Performance: This method may be slower than get_string() or get_string(bool) because it may need to allocate memory.
|
||||
* We recommend you avoid allocating an std::string unless you need to.
|
||||
*
|
||||
* @returns INCORRECT_TYPE if the JSON value is not a string. Otherwise, we return SUCCESS.
|
||||
*/
|
||||
template <typename string_type>
|
||||
simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
|
||||
|
||||
/**
|
||||
* Cast this JSON value to a "wobbly" string.
|
||||
@@ -475,9 +493,21 @@ public:
|
||||
* - true
|
||||
* - false
|
||||
* - null
|
||||
*
|
||||
* See also value::raw_json().
|
||||
*/
|
||||
simdjson_inline std::string_view raw_json_token() noexcept;
|
||||
|
||||
/**
|
||||
* Get a string_view pointing at this value in the JSON document.
|
||||
* If this element is an array or an object, it consumes the array or the object
|
||||
* and returns a string_view instance corresponding to the
|
||||
* array as represented in JSON. It points inside the original document.
|
||||
* If this element is a scalar (string, number, Boolean, null), it returns what
|
||||
* raw_json_token() would return.
|
||||
*/
|
||||
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
|
||||
|
||||
/**
|
||||
* Returns the current location in the document if in bounds.
|
||||
*/
|
||||
@@ -602,6 +632,8 @@ public:
|
||||
simdjson_inline simdjson_result<double> get_double() noexcept;
|
||||
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
|
||||
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
|
||||
template <typename string_type>
|
||||
simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
|
||||
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::raw_json_string> get_raw_json_string() noexcept;
|
||||
simdjson_inline simdjson_result<bool> get_bool() noexcept;
|
||||
@@ -694,6 +726,7 @@ public:
|
||||
|
||||
/** @copydoc simdjson_inline std::string_view value::raw_json_token() const noexcept */
|
||||
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
|
||||
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
|
||||
|
||||
/** @copydoc simdjson_inline simdjson_result<const char *> current_location() noexcept */
|
||||
simdjson_inline simdjson_result<const char *> current_location() noexcept;
|
||||
|
||||
@@ -513,6 +513,14 @@ simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::parse
|
||||
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_string(bool allow_replacement) noexcept {
|
||||
return get_raw_json_string().unescape(json_iter(), allow_replacement);
|
||||
}
|
||||
template <typename string_type>
|
||||
simdjson_warn_unused simdjson_inline error_code value_iterator::get_string(string_type& receiver, bool allow_replacement) noexcept {
|
||||
std::string_view content;
|
||||
auto err = get_string(allow_replacement).get(content);
|
||||
if (err) { return err; }
|
||||
receiver = content;
|
||||
return SUCCESS;
|
||||
}
|
||||
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_wobbly_string() noexcept {
|
||||
return get_raw_json_string().unescape_wobbly(json_iter());
|
||||
}
|
||||
@@ -636,6 +644,14 @@ simdjson_inline simdjson_result<number> value_iterator::get_root_number(bool che
|
||||
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_root_string(bool check_trailing, bool allow_replacement) noexcept {
|
||||
return get_root_raw_json_string(check_trailing).unescape(json_iter(), allow_replacement);
|
||||
}
|
||||
template <typename string_type>
|
||||
simdjson_warn_unused simdjson_inline error_code value_iterator::get_root_string(string_type& receiver, bool check_trailing, bool allow_replacement) noexcept {
|
||||
std::string_view content;
|
||||
auto err = get_root_string(check_trailing, allow_replacement).get(content);
|
||||
if (err) { return err; }
|
||||
receiver = content;
|
||||
return SUCCESS;
|
||||
}
|
||||
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_root_wobbly_string(bool check_trailing) noexcept {
|
||||
return get_root_raw_json_string(check_trailing).unescape_wobbly(json_iter());
|
||||
}
|
||||
|
||||
@@ -296,6 +296,8 @@ public:
|
||||
*/
|
||||
|
||||
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement) noexcept;
|
||||
template <typename string_type>
|
||||
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement) noexcept;
|
||||
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
|
||||
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> get_raw_json_string() noexcept;
|
||||
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
|
||||
@@ -312,7 +314,9 @@ public:
|
||||
simdjson_warn_unused simdjson_inline simdjson_result<number> get_number() noexcept;
|
||||
|
||||
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_root_string(bool check_trailing, bool allow_replacement) noexcept;
|
||||
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_root_wobbly_string(bool check_trailing) noexcept;
|
||||
template <typename string_type>
|
||||
simdjson_warn_unused simdjson_inline error_code get_root_string(string_type& receiver, bool check_trailing, bool allow_replacement) noexcept;
|
||||
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_root_wobbly_string(bool check_trailing) noexcept;
|
||||
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> get_root_raw_json_string(bool check_trailing) noexcept;
|
||||
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_root_uint64(bool check_trailing) noexcept;
|
||||
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_root_uint64_in_string(bool check_trailing) noexcept;
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
#define SIMDJSON_PADDED_STRING_VIEW_INL_H
|
||||
|
||||
#include "simdjson/padded_string_view.h"
|
||||
|
||||
#include "simdjson/error-inl.h"
|
||||
|
||||
#include <cstring> /* memcmp */
|
||||
|
||||
namespace simdjson {
|
||||
|
||||
inline padded_string_view::padded_string_view(const char* s, size_t len, size_t capacity) noexcept
|
||||
@@ -31,6 +32,16 @@ inline size_t padded_string_view::capacity() const noexcept { return _capacity;
|
||||
|
||||
inline size_t padded_string_view::padding() const noexcept { return capacity() - length(); }
|
||||
|
||||
inline bool padded_string_view::remove_utf8_bom() noexcept {
|
||||
if(length() < 3) { return false; }
|
||||
if (std::memcmp(data(), "\xEF\xBB\xBF", 3) == 0) {
|
||||
remove_prefix(3);
|
||||
_capacity -= 3;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
inline std::ostream& operator<<(std::ostream& out, simdjson_result<padded_string_view> &s) noexcept(false) { return out << s.value(); }
|
||||
#endif
|
||||
|
||||
@@ -54,6 +54,13 @@ public:
|
||||
/** The number of allocated bytes. */
|
||||
inline size_t capacity() const noexcept;
|
||||
|
||||
/**
|
||||
* Remove the UTF-8 Byte Order Mark (BOM) if it exists.
|
||||
*
|
||||
* @return whether a BOM was found and removed
|
||||
*/
|
||||
inline bool remove_utf8_bom() noexcept;
|
||||
|
||||
/** The amount of padding on the string (capacity() - length()) */
|
||||
inline size_t padding() const noexcept;
|
||||
|
||||
|
||||
@@ -36,6 +36,8 @@
|
||||
#define SIMDJSON_IS_X86_64 1
|
||||
#elif defined(__aarch64__) || defined(_M_ARM64)
|
||||
#define SIMDJSON_IS_ARM64 1
|
||||
#elif defined(__riscv) && __riscv_xlen == 64
|
||||
#define SIMDJSON_IS_RISCV64 1
|
||||
#elif defined(__PPC64__) || defined(_M_PPC64)
|
||||
#if defined(__ALTIVEC__)
|
||||
#define SIMDJSON_IS_PPC64_VMX 1
|
||||
@@ -58,11 +60,8 @@
|
||||
|
||||
#if SIMDJSON_IS_32BITS
|
||||
#ifndef SIMDJSON_NO_PORTABILITY_WARNING
|
||||
#pragma message("The simdjson library is designed \
|
||||
for 64-bit processors and it seems that you are not \
|
||||
compiling for a known 64-bit platform. All fast kernels \
|
||||
will be disabled and performance may be poor. Please \
|
||||
use a 64-bit target such as x64, 64-bit ARM or 64-bit PPC.")
|
||||
// In the future, we should allow programmers
|
||||
// to get warning.
|
||||
#endif // SIMDJSON_NO_PORTABILITY_WARNING
|
||||
#endif // SIMDJSON_IS_32BITS
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#define SIMDJSON_SIMDJSON_VERSION_H
|
||||
|
||||
/** The version of simdjson being used (major.minor.revision) */
|
||||
#define SIMDJSON_VERSION "3.2.3"
|
||||
#define SIMDJSON_VERSION "3.6.1"
|
||||
|
||||
namespace simdjson {
|
||||
enum {
|
||||
@@ -15,11 +15,11 @@ enum {
|
||||
/**
|
||||
* The minor version (major.MINOR.revision) of simdjson being used.
|
||||
*/
|
||||
SIMDJSON_VERSION_MINOR = 2,
|
||||
SIMDJSON_VERSION_MINOR = 6,
|
||||
/**
|
||||
* The revision (major.minor.REVISION) of simdjson being used.
|
||||
*/
|
||||
SIMDJSON_VERSION_REVISION = 3
|
||||
SIMDJSON_VERSION_REVISION = 1
|
||||
};
|
||||
} // namespace simdjson
|
||||
|
||||
|
||||
+523
-707
File diff suppressed because it is too large
Load Diff
+1297
-307
File diff suppressed because it is too large
Load Diff
@@ -27,6 +27,58 @@ public:
|
||||
|
||||
simdjson_inline bit_indexer(uint32_t *index_buf) : tail(index_buf) {}
|
||||
|
||||
#if SIMDJSON_PREFER_REVERSE_BITS
|
||||
/**
|
||||
* ARM lacks a fast trailing zero instruction, but it has a fast
|
||||
* bit reversal instruction and a fast leading zero instruction.
|
||||
* Thus it may be profitable to reverse the bits (once) and then
|
||||
* to rely on a sequence of instructions that call the leading
|
||||
* zero instruction.
|
||||
*
|
||||
* Performance notes:
|
||||
* The chosen routine is not optimal in terms of data dependency
|
||||
* since zero_leading_bit might require two instructions. However,
|
||||
* it tends to minimize the total number of instructions which is
|
||||
* beneficial.
|
||||
*/
|
||||
simdjson_inline void write_index(uint32_t idx, uint64_t& rev_bits, int i) {
|
||||
int lz = leading_zeroes(rev_bits);
|
||||
this->tail[i] = static_cast<uint32_t>(idx) + lz;
|
||||
rev_bits = zero_leading_bit(rev_bits, lz);
|
||||
}
|
||||
#else
|
||||
/**
|
||||
* Under recent x64 systems, we often have both a fast trailing zero
|
||||
* instruction and a fast 'clear-lower-bit' instruction so the following
|
||||
* algorithm can be competitive.
|
||||
*/
|
||||
|
||||
simdjson_inline void write_index(uint32_t idx, uint64_t& bits, int i) {
|
||||
this->tail[i] = idx + trailing_zeroes(bits);
|
||||
bits = clear_lowest_bit(bits);
|
||||
}
|
||||
#endif // SIMDJSON_PREFER_REVERSE_BITS
|
||||
|
||||
template <int START, int N>
|
||||
simdjson_inline int write_indexes(uint32_t idx, uint64_t& bits) {
|
||||
write_index(idx, bits, START);
|
||||
SIMDJSON_IF_CONSTEXPR (N > 1) {
|
||||
write_indexes<(N-1>0?START+1:START), (N-1>=0?N-1:1)>(idx, bits);
|
||||
}
|
||||
return START+N;
|
||||
}
|
||||
|
||||
template <int START, int END, int STEP>
|
||||
simdjson_inline int write_indexes_stepped(uint32_t idx, uint64_t& bits, int cnt) {
|
||||
write_indexes<START, STEP>(idx, bits);
|
||||
SIMDJSON_IF_CONSTEXPR ((START+STEP) < END) {
|
||||
if (simdjson_unlikely((START+STEP) < cnt)) {
|
||||
write_indexes_stepped<(START+STEP<END?START+STEP:END), END, STEP>(idx, bits, cnt);
|
||||
}
|
||||
}
|
||||
return ((END-START) % STEP) == 0 ? END : (END-START) - ((END-START) % STEP) + STEP;
|
||||
}
|
||||
|
||||
// flatten out values in 'bits' assuming that they are are to have values of idx
|
||||
// plus their position in the bitvector, and store these indexes at
|
||||
// base_ptr[base] incrementing base as we go
|
||||
@@ -44,91 +96,29 @@ public:
|
||||
// it helps tremendously.
|
||||
if (bits == 0)
|
||||
return;
|
||||
#if SIMDJSON_PREFER_REVERSE_BITS
|
||||
/**
|
||||
* ARM lacks a fast trailing zero instruction, but it has a fast
|
||||
* bit reversal instruction and a fast leading zero instruction.
|
||||
* Thus it may be profitable to reverse the bits (once) and then
|
||||
* to rely on a sequence of instructions that call the leading
|
||||
* zero instruction.
|
||||
*
|
||||
* Performance notes:
|
||||
* The chosen routine is not optimal in terms of data dependency
|
||||
* since zero_leading_bit might require two instructions. However,
|
||||
* it tends to minimize the total number of instructions which is
|
||||
* beneficial.
|
||||
*/
|
||||
|
||||
uint64_t rev_bits = reverse_bits(bits);
|
||||
int cnt = static_cast<int>(count_ones(bits));
|
||||
int i = 0;
|
||||
// Do the first 8 all together
|
||||
for (; i<8; i++) {
|
||||
int lz = leading_zeroes(rev_bits);
|
||||
this->tail[i] = static_cast<uint32_t>(idx) + lz;
|
||||
rev_bits = zero_leading_bit(rev_bits, lz);
|
||||
}
|
||||
// Do the next 8 all together (we hope in most cases it won't happen at all
|
||||
// and the branch is easily predicted).
|
||||
if (simdjson_unlikely(cnt > 8)) {
|
||||
i = 8;
|
||||
for (; i<16; i++) {
|
||||
int lz = leading_zeroes(rev_bits);
|
||||
this->tail[i] = static_cast<uint32_t>(idx) + lz;
|
||||
rev_bits = zero_leading_bit(rev_bits, lz);
|
||||
}
|
||||
|
||||
#if SIMDJSON_PREFER_REVERSE_BITS
|
||||
bits = reverse_bits(bits);
|
||||
#endif
|
||||
#ifdef SIMDJSON_STRUCTURAL_INDEXER_STEP
|
||||
static constexpr const int STEP = SIMDJSON_STRUCTURAL_INDEXER_STEP;
|
||||
#else
|
||||
static constexpr const int STEP = 4;
|
||||
#endif
|
||||
static constexpr const int STEP_UNTIL = 24;
|
||||
|
||||
// Most files don't have 16+ structurals per block, so we take several basically guaranteed
|
||||
// branch mispredictions here. 16+ structurals per block means either punctuation ({} [] , :)
|
||||
// or the start of a value ("abc" true 123) every four characters.
|
||||
if (simdjson_unlikely(cnt > 16)) {
|
||||
i = 16;
|
||||
while (rev_bits != 0) {
|
||||
int lz = leading_zeroes(rev_bits);
|
||||
this->tail[i++] = static_cast<uint32_t>(idx) + lz;
|
||||
rev_bits = zero_leading_bit(rev_bits, lz);
|
||||
write_indexes_stepped<0, STEP_UNTIL, STEP>(idx, bits, cnt);
|
||||
SIMDJSON_IF_CONSTEXPR (STEP_UNTIL < 64) {
|
||||
if (simdjson_unlikely(STEP_UNTIL < cnt)) {
|
||||
for (int i=STEP_UNTIL; i<cnt; i++) {
|
||||
write_index(idx, bits, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
this->tail += cnt;
|
||||
#else // SIMDJSON_PREFER_REVERSE_BITS
|
||||
/**
|
||||
* Under recent x64 systems, we often have both a fast trailing zero
|
||||
* instruction and a fast 'clear-lower-bit' instruction so the following
|
||||
* algorithm can be competitive.
|
||||
*/
|
||||
|
||||
int cnt = static_cast<int>(count_ones(bits));
|
||||
// Do the first 8 all together
|
||||
for (int i=0; i<8; i++) {
|
||||
this->tail[i] = idx + trailing_zeroes(bits);
|
||||
bits = clear_lowest_bit(bits);
|
||||
}
|
||||
|
||||
// Do the next 8 all together (we hope in most cases it won't happen at all
|
||||
// and the branch is easily predicted).
|
||||
if (simdjson_unlikely(cnt > 8)) {
|
||||
for (int i=8; i<16; i++) {
|
||||
this->tail[i] = idx + trailing_zeroes(bits);
|
||||
bits = clear_lowest_bit(bits);
|
||||
}
|
||||
|
||||
// Most files don't have 16+ structurals per block, so we take several basically guaranteed
|
||||
// branch mispredictions here. 16+ structurals per block means either punctuation ({} [] , :)
|
||||
// or the start of a value ("abc" true 123) every four characters.
|
||||
if (simdjson_unlikely(cnt > 16)) {
|
||||
int i = 16;
|
||||
do {
|
||||
this->tail[i] = idx + trailing_zeroes(bits);
|
||||
bits = clear_lowest_bit(bits);
|
||||
i++;
|
||||
} while (i < cnt);
|
||||
}
|
||||
}
|
||||
|
||||
this->tail += cnt;
|
||||
#endif
|
||||
}
|
||||
#endif // SIMDJSON_GENERIC_JSON_STRUCTURAL_INDEXER_CUSTOM_BIT_INDEXER
|
||||
|
||||
@@ -365,4 +355,4 @@ simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementa
|
||||
// Clear CUSTOM_BIT_INDEXER so other implementations can set it if they need to.
|
||||
#undef SIMDJSON_GENERIC_JSON_STRUCTURAL_INDEXER_CUSTOM_BIT_INDEXER
|
||||
|
||||
#endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_STRUCTURAL_INDEXER_H
|
||||
#endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_STRUCTURAL_INDEXER_H
|
||||
|
||||
@@ -170,14 +170,6 @@ using namespace simd;
|
||||
this->error |= this->prev_incomplete;
|
||||
}
|
||||
|
||||
#ifndef SIMDJSON_IF_CONSTEXPR
|
||||
#if SIMDJSON_CPLUSPLUS17
|
||||
#define SIMDJSON_IF_CONSTEXPR if constexpr
|
||||
#else
|
||||
#define SIMDJSON_IF_CONSTEXPR if
|
||||
#endif
|
||||
#endif
|
||||
|
||||
simdjson_inline void check_next_input(const simd8x64<uint8_t>& input) {
|
||||
if(simdjson_likely(is_ascii(input))) {
|
||||
this->error |= this->prev_incomplete;
|
||||
|
||||
@@ -303,15 +303,17 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::visit_root_primit
|
||||
}
|
||||
template<typename V>
|
||||
simdjson_warn_unused simdjson_inline error_code json_iterator::visit_primitive(V &visitor, const uint8_t *value) noexcept {
|
||||
// Use the fact that most scalars are going to be either strings or numbers.
|
||||
if(*value == '"') {
|
||||
return visitor.visit_string(*this, value);
|
||||
} else if (((*value - '0') < 10) || (*value == '-')) {
|
||||
return visitor.visit_number(*this, value);
|
||||
}
|
||||
// true, false, null are uncommon.
|
||||
switch (*value) {
|
||||
case '"': return visitor.visit_string(*this, value);
|
||||
case 't': return visitor.visit_true_atom(*this, value);
|
||||
case 'f': return visitor.visit_false_atom(*this, value);
|
||||
case 'n': return visitor.visit_null_atom(*this, value);
|
||||
case '-':
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
return visitor.visit_number(*this, value);
|
||||
default:
|
||||
log_error("Non-value found when value was expected!");
|
||||
return TAPE_ERROR;
|
||||
|
||||
@@ -189,8 +189,6 @@ simdjson_warn_unused simdjson_inline uint8_t *parse_string(const uint8_t *src, u
|
||||
dst += backslash_and_quote::BYTES_PROCESSED;
|
||||
}
|
||||
}
|
||||
/* can't be reached */
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
simdjson_warn_unused simdjson_inline uint8_t *parse_wobbly_string(const uint8_t *src, uint8_t *dst) {
|
||||
@@ -236,8 +234,6 @@ simdjson_warn_unused simdjson_inline uint8_t *parse_wobbly_string(const uint8_t
|
||||
dst += backslash_and_quote::BYTES_PROCESSED;
|
||||
}
|
||||
}
|
||||
/* can't be reached */
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace stringparsing
|
||||
|
||||
@@ -66,6 +66,16 @@ namespace number_tests {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bomskip() {
|
||||
TEST_START();
|
||||
simdjson::dom::parser parser;
|
||||
simdjson::padded_string docdata = "\xEF\xBB\xBF{\"score\":0.8825149536132812}"_padded;
|
||||
double score;
|
||||
ASSERT_SUCCESS(parser.parse(docdata)["score"].get_double().get(score));
|
||||
ASSERT_EQUAL(score, 0.8825149536132812);
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool issue2017() {
|
||||
TEST_START();
|
||||
simdjson::dom::parser parser;
|
||||
@@ -386,7 +396,8 @@ namespace number_tests {
|
||||
}
|
||||
|
||||
bool run() {
|
||||
return issue2017() &&
|
||||
return bomskip() &&
|
||||
issue2017() &&
|
||||
truncated_borderline() &&
|
||||
specific_tests() &&
|
||||
ground_truth() &&
|
||||
@@ -1658,6 +1669,7 @@ namespace validate_tests {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool test_validate() {
|
||||
std::cout << "Running " << __func__ << std::endl;
|
||||
const std::string test = R"({ "foo" : 1, "bar" : [ 1, 2, 3 ], "baz": { "a": 1, "b": 2, "c": 3 } })";
|
||||
@@ -1666,6 +1678,7 @@ namespace validate_tests {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool test_range() {
|
||||
std::cout << "Running " << __func__ << std::endl;
|
||||
for(size_t len = 0; len <= 128; len++) {
|
||||
@@ -1683,6 +1696,7 @@ namespace validate_tests {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool test_issue1169() {
|
||||
std::cout << "Running " << __func__ << std::endl;
|
||||
std::vector<uint8_t> source(64,' ');
|
||||
@@ -1693,6 +1707,7 @@ namespace validate_tests {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool test_issue1169_long() {
|
||||
std::cout << "Running " << __func__ << std::endl;
|
||||
for(size_t len = 1; len <= 128; len++) {
|
||||
@@ -1702,6 +1717,7 @@ namespace validate_tests {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool test_random() {
|
||||
std::cout << "Running " << __func__ << std::endl;
|
||||
std::vector<uint8_t> source(64,' ');
|
||||
@@ -1763,6 +1779,7 @@ namespace minify_tests {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// this is meant to test buffer overflows.
|
||||
bool test_various_lengths2() {
|
||||
std::cout << "Running " << __func__ << std::endl;
|
||||
@@ -1781,6 +1798,7 @@ namespace minify_tests {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool test_single_quote() {
|
||||
std::cout << "Running " << __func__ << std::endl;
|
||||
const std::string test = "\"";
|
||||
@@ -1801,12 +1819,14 @@ namespace minify_tests {
|
||||
const std::string minified(R"({"foo":1,"bar":[1,2,0.11111111111111113],"baz":{"a":3.1415926535897936,"b":2,"c":3.141592653589794}})");
|
||||
return check_minification(test.c_str(), test.size(), minified.c_str(), minified.size());
|
||||
}
|
||||
|
||||
bool test_minify_array() {
|
||||
std::cout << "Running " << __func__ << std::endl;
|
||||
std::string test("[ 1, 2, 3]");
|
||||
std::string minified("[1,2,3]");
|
||||
return check_minification(test.c_str(), test.size(), minified.c_str(), minified.size());
|
||||
}
|
||||
|
||||
bool test_minify_object() {
|
||||
std::cout << "Running " << __func__ << std::endl;
|
||||
std::string test(R"({ "foo " : 1, "b ar" : [ 1, 2, 3 ], "baz": { "a": 1, "b": 2, "c": 3 } })");
|
||||
@@ -1849,6 +1869,7 @@ namespace format_tests {
|
||||
s << doc;
|
||||
return assert_minified(s);
|
||||
}
|
||||
|
||||
bool print_minify_parser_parse() {
|
||||
std::cout << "Running " << __func__ << std::endl;
|
||||
dom::parser parser;
|
||||
@@ -1868,6 +1889,7 @@ namespace format_tests {
|
||||
s << value;
|
||||
return assert_minified(s, "1");
|
||||
}
|
||||
|
||||
bool print_minify_element() {
|
||||
std::cout << "Running " << __func__ << std::endl;
|
||||
dom::parser parser;
|
||||
@@ -1887,6 +1909,7 @@ namespace format_tests {
|
||||
s << array;
|
||||
return assert_minified(s, "[1,2,0.11111111111111113]");
|
||||
}
|
||||
|
||||
bool print_minify_array() {
|
||||
std::cout << "Running " << __func__ << std::endl;
|
||||
dom::parser parser;
|
||||
@@ -1906,6 +1929,7 @@ namespace format_tests {
|
||||
s << object;
|
||||
return assert_minified(s, R"({"a":3.1415926535897936,"b":2,"c":3.141592653589794})");
|
||||
}
|
||||
|
||||
bool print_minify_object() {
|
||||
std::cout << "Running " << __func__ << std::endl;
|
||||
dom::parser parser;
|
||||
@@ -1925,6 +1949,7 @@ namespace format_tests {
|
||||
s << parser.parse(DOCUMENT);
|
||||
return assert_minified(s);
|
||||
}
|
||||
|
||||
bool print_minify_parser_parse_exception() {
|
||||
std::cout << "Running " << __func__ << std::endl;
|
||||
dom::parser parser;
|
||||
@@ -1940,6 +1965,7 @@ namespace format_tests {
|
||||
s << parser.parse(DOCUMENT)["foo"];
|
||||
return assert_minified(s, "1");
|
||||
}
|
||||
|
||||
bool print_minify_element_result_exception() {
|
||||
std::cout << "Running " << __func__ << std::endl;
|
||||
dom::parser parser;
|
||||
@@ -1956,6 +1982,7 @@ namespace format_tests {
|
||||
s << value;
|
||||
return assert_minified(s, "1");
|
||||
}
|
||||
|
||||
bool print_minify_element_exception() {
|
||||
std::cout << "Running " << __func__ << std::endl;
|
||||
dom::parser parser;
|
||||
@@ -1972,6 +1999,7 @@ namespace format_tests {
|
||||
s << parser.parse(DOCUMENT)["bar"].get_array();
|
||||
return assert_minified(s, "[1,2,0.11111111111111113]");
|
||||
}
|
||||
|
||||
bool print_minify_array_result_exception() {
|
||||
std::cout << "Running " << __func__ << std::endl;
|
||||
dom::parser parser;
|
||||
|
||||
@@ -397,6 +397,33 @@ namespace document_stream_tests {
|
||||
std::cout << "number of documents " << count << std::endl;
|
||||
return count == 1;
|
||||
}
|
||||
|
||||
bool skipbom() {
|
||||
std::cout << "Running " << __func__ << std::endl;
|
||||
simdjson::dom::parser parser;
|
||||
auto json = "\xEF\xBB\xBF{\"hello\": \"world\"}"_padded;
|
||||
simdjson::dom::document_stream stream;
|
||||
ASSERT_SUCCESS(parser.parse_many(json).get(stream));
|
||||
size_t count = 0;
|
||||
for (auto doc : stream) {
|
||||
if(doc.error()) {
|
||||
std::cerr << "Unexpected error: " << doc.error() << std::endl;
|
||||
return false;
|
||||
}
|
||||
std::string expected = R"({"hello":"world"})";
|
||||
simdjson::dom::element this_document;
|
||||
ASSERT_SUCCESS(doc.get(this_document));
|
||||
|
||||
std::string answer = simdjson::minify(this_document);
|
||||
if(answer != expected) {
|
||||
std::cout << this_document << std::endl;
|
||||
return false;
|
||||
}
|
||||
count += 1;
|
||||
}
|
||||
std::cout << "number of documents " << count << std::endl;
|
||||
return count == 1;
|
||||
}
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
bool single_document_exceptions() {
|
||||
std::cout << "Running " << __func__ << std::endl;
|
||||
@@ -913,7 +940,8 @@ namespace document_stream_tests {
|
||||
}
|
||||
|
||||
bool run() {
|
||||
return fuzzaccess() &&
|
||||
return skipbom() &&
|
||||
fuzzaccess() &&
|
||||
baby_fuzzer() &&
|
||||
issue1649() &&
|
||||
adversarial_single_document_array() &&
|
||||
|
||||
@@ -51,6 +51,10 @@ namespace document_tests {
|
||||
simdjson::dom::array array;
|
||||
ASSERT_SUCCESS( parser.parse(smalljson).get(array) );
|
||||
ASSERT_EQUAL( array.size(), 3 );
|
||||
ASSERT_EQUAL( *array.begin() < *array.end(), true );
|
||||
ASSERT_EQUAL( *array.end() < *array.begin(), false );
|
||||
ASSERT_EQUAL( *array.begin() == *array.begin(), true );
|
||||
ASSERT_EQUAL( *array.begin() == *array.end(), false );
|
||||
return true;
|
||||
}
|
||||
bool count_object_example() {
|
||||
@@ -60,6 +64,10 @@ namespace document_tests {
|
||||
simdjson::dom::object object;
|
||||
ASSERT_SUCCESS( parser.parse(smalljson).get(object) );
|
||||
ASSERT_EQUAL( object.size(), 3 );
|
||||
ASSERT_EQUAL( (*object.begin()).value < (*object.end()).value, true );
|
||||
ASSERT_EQUAL( (*object.end()).value < (*object.begin()).value, false );
|
||||
ASSERT_EQUAL( (*object.begin()).value == (*object.begin()).value, true );
|
||||
ASSERT_EQUAL( (*object.begin()).value == (*object.end()).value, false );
|
||||
return true;
|
||||
}
|
||||
bool padded_with_open_bracket() {
|
||||
@@ -232,4 +240,4 @@ int main(int argc, char *argv[]) {
|
||||
} else {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ static bool parse_and_validate(const std::string src, T expected) {
|
||||
const padded_string pstr{src};
|
||||
simdjson::dom::parser parser;
|
||||
|
||||
if constexpr (std::is_same<int64_t, T>::value) {
|
||||
SIMDJSON_IF_CONSTEXPR (std::is_same<int64_t, T>::value) {
|
||||
int64_t actual{};
|
||||
ASSERT_SUCCESS( parser.parse(pstr)["key"].get(actual) );
|
||||
std::cout << std::boolalpha << "test: " << (expected == actual) << std::endl;
|
||||
|
||||
@@ -23,6 +23,7 @@ add_cpp_test(ondemand_ordering_tests LABELS ondemand acceptance per_impl
|
||||
add_cpp_test(ondemand_parse_api_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_readme_examples LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_scalar_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_to_string LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_twitter_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_wrong_type_error_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_iterate_many_csv LABELS ondemand acceptance per_implementation)
|
||||
|
||||
@@ -6,6 +6,17 @@ using namespace simdjson;
|
||||
namespace array_tests {
|
||||
using namespace std;
|
||||
using simdjson::ondemand::json_type;
|
||||
|
||||
bool document_is_array_treated_as_object() {
|
||||
TEST_START();
|
||||
auto json = R"( [ {"key" : "value"} ] )"_padded;
|
||||
ondemand::parser parser;
|
||||
ondemand::document doc;
|
||||
ASSERT_SUCCESS(parser.iterate(json).get(doc));
|
||||
std::string_view v;
|
||||
ASSERT_ERROR(doc["key"].get_string().get(v), INCORRECT_TYPE);
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
bool issue1977() {
|
||||
TEST_START();
|
||||
auto json = R"([1, 2] foo ])"_padded;
|
||||
@@ -844,6 +855,7 @@ namespace array_tests {
|
||||
|
||||
bool run() {
|
||||
return
|
||||
document_is_array_treated_as_object() &&
|
||||
issue1977() &&
|
||||
issue1876() &&
|
||||
issue1742() &&
|
||||
|
||||
@@ -322,6 +322,43 @@ namespace document_stream_tests {
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool skipbom() {
|
||||
TEST_START();
|
||||
auto json = "\xEF\xBB\xBF[1,[1,2]] {\"a\":1,\"b\":2} {\"o\":{\"1\":1,\"2\":2}} [1,2,3]"_padded;
|
||||
ondemand::parser parser;
|
||||
ondemand::document_stream stream;
|
||||
ASSERT_SUCCESS(parser.iterate_many(json).get(stream));
|
||||
std::string_view expected[4] = {"[1,[1,2]]", "{\"a\":1,\"b\":2}", "{\"o\":{\"1\":1,\"2\":2}}", "[1,2,3]"};
|
||||
size_t counter{0};
|
||||
auto i = stream.begin();
|
||||
int64_t x;
|
||||
|
||||
ASSERT_EQUAL(i.source(),expected[counter++]);
|
||||
ASSERT_SUCCESS( (*i).at_pointer("/1/1").get(x) );
|
||||
ASSERT_EQUAL(x,2);
|
||||
++i;
|
||||
|
||||
ASSERT_EQUAL(i.source(),expected[counter++]);
|
||||
simdjson_result<ondemand::document_reference> xxx = *i;
|
||||
ASSERT_SUCCESS( xxx.find_field("a").get(x) );
|
||||
ASSERT_EQUAL(x,1);
|
||||
++i;
|
||||
|
||||
ASSERT_EQUAL(i.source(),expected[counter++]);
|
||||
ASSERT_SUCCESS( (*i).at_pointer("/o/2").get(x) );
|
||||
ASSERT_EQUAL(x,2);
|
||||
++i;
|
||||
|
||||
ASSERT_EQUAL(i.source(),expected[counter++]);
|
||||
ASSERT_SUCCESS( (*i).at_pointer("/2").get(x) );
|
||||
ASSERT_EQUAL(x,3);
|
||||
++i;
|
||||
|
||||
if (i != stream.end()) { return false; }
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool atoms_json() {
|
||||
TEST_START();
|
||||
auto json = R"(5 true 20.3 "string" )"_padded;
|
||||
@@ -803,6 +840,7 @@ namespace document_stream_tests {
|
||||
|
||||
bool run() {
|
||||
return
|
||||
skipbom() &&
|
||||
issue1977() &&
|
||||
string_with_trailing() &&
|
||||
uint64_with_trailing() &&
|
||||
|
||||
@@ -12,10 +12,22 @@ namespace misc_tests {
|
||||
ondemand::document doc;
|
||||
ASSERT_SUCCESS(parser.iterate(error_phrase).get(doc));
|
||||
bool b;
|
||||
ASSERT_SUCCESS( doc.get_bool().get(b));
|
||||
ASSERT_SUCCESS(doc.get_bool().get(b));
|
||||
ASSERT_FALSE(b);
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
bool skipbom() {
|
||||
auto error_phrase = "\xEF\xBB\xBF false"_padded;
|
||||
TEST_START();
|
||||
ondemand::parser parser;
|
||||
ondemand::document doc;
|
||||
ASSERT_SUCCESS(parser.iterate(error_phrase).get(doc));
|
||||
bool b;
|
||||
ASSERT_SUCCESS(doc.get_bool().get(b));
|
||||
ASSERT_FALSE(b);
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
|
||||
bool issue1981_failure() {
|
||||
auto error_phrase = R"(falseA)"_padded;
|
||||
@@ -593,6 +605,7 @@ namespace misc_tests {
|
||||
|
||||
bool run() {
|
||||
return
|
||||
skipbom() &&
|
||||
issue1981_success() &&
|
||||
issue1981_failure() &&
|
||||
replacement_char() &&
|
||||
|
||||
@@ -137,6 +137,27 @@ namespace object_error_tests {
|
||||
}
|
||||
|
||||
#ifdef SIMDJSON_DEVELOPMENT_CHECKS
|
||||
bool issue2084() {
|
||||
TEST_START();
|
||||
auto json = R"( {"foo": "bar"} )"_padded;
|
||||
SUBTEST("document->value after access", test_ondemand_doc(json, [&](auto doc) {
|
||||
std::string_view foo;
|
||||
ASSERT_SUCCESS(doc["foo"].get(foo));
|
||||
ondemand::value value;
|
||||
ASSERT_ERROR(doc.get_value().get(value), OUT_OF_ORDER_ITERATION);
|
||||
return true;
|
||||
}));
|
||||
SUBTEST("document->value after access and rewind", test_ondemand_doc(json, [&](auto doc) {
|
||||
std::string_view foo;
|
||||
ASSERT_SUCCESS(doc["foo"].get(foo));
|
||||
doc.rewind();
|
||||
ondemand::value value;
|
||||
ASSERT_SUCCESS(doc.get_value().get(value));
|
||||
return true;
|
||||
}));
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool out_of_order_object_iteration_error() {
|
||||
TEST_START();
|
||||
auto json = R"([ { "x": 1, "y": 2 } ])"_padded;
|
||||
@@ -555,6 +576,7 @@ namespace object_error_tests {
|
||||
object_lookup_miss_wrong_key_type_error() &&
|
||||
object_lookup_miss_next_error() &&
|
||||
#ifdef SIMDJSON_DEVELOPMENT_CHECKS
|
||||
issue2084() &&
|
||||
out_of_order_object_iteration_error() &&
|
||||
out_of_order_top_level_object_iteration_error() &&
|
||||
out_of_order_object_index_child_error() &&
|
||||
|
||||
@@ -144,14 +144,20 @@ namespace parse_api_tests {
|
||||
}
|
||||
|
||||
{
|
||||
std::string json = "12";
|
||||
std::string json = "12345642314123421321321321321321312321321321321312";
|
||||
json.shrink_to_fit();
|
||||
cout << "- string, 0 padding" << endl;
|
||||
ASSERT_ERROR( parser.iterate(json), INSUFFICIENT_PADDING );
|
||||
ASSERT_SUCCESS( parser.iterate(json) );
|
||||
}
|
||||
|
||||
{
|
||||
std::string json = "12345642314123421321321321321321312321321321321312";
|
||||
json.shrink_to_fit();
|
||||
cout << "- string, 0 padding" << endl;
|
||||
ASSERT_ERROR( parser.iterate((const std::string&)json), INSUFFICIENT_PADDING );
|
||||
// It's actually kind of hard to allocate "just enough" capacity, since the string tends
|
||||
// to grow more than you tell it to.
|
||||
}
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#include "simdjson.h"
|
||||
#include "test_ondemand.h"
|
||||
|
||||
#if __cpp_lib_optional >= 201606L
|
||||
#include <optional>
|
||||
#endif
|
||||
using namespace std;
|
||||
using namespace simdjson;
|
||||
using error_code=simdjson::error_code;
|
||||
@@ -20,7 +22,39 @@ bool string2() {
|
||||
}
|
||||
|
||||
|
||||
bool to_string_example_no_except() {
|
||||
TEST_START();
|
||||
auto json = R"({
|
||||
"name": "Daniel",
|
||||
"age": 42
|
||||
})"_padded;
|
||||
ondemand::parser parser;
|
||||
ondemand::document doc;
|
||||
auto err = parser.iterate(json).get(doc);
|
||||
if(err) { return false; }
|
||||
std::string name;
|
||||
err = doc["name"].get_string(name);
|
||||
if(err) { return false; }
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
|
||||
|
||||
bool to_string_example() {
|
||||
TEST_START();
|
||||
auto json = R"({
|
||||
"name": "Daniel",
|
||||
"age": 42
|
||||
})"_padded;
|
||||
ondemand::parser parser;
|
||||
ondemand::document doc = parser.iterate(json);
|
||||
std::string name;
|
||||
doc["name"].get_string(name);
|
||||
ASSERT_EQUAL(name, "Daniel");
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool gen_raw1() {
|
||||
TEST_START();
|
||||
simdjson::ondemand::parser parser;
|
||||
@@ -73,6 +107,144 @@ bool at_end() {
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool examplecrt() {
|
||||
TEST_START();
|
||||
padded_string padded_input_json = R"([
|
||||
{ "monitor": [
|
||||
{ "id": "monitor", "type": "toggle", "label": "monitor" },
|
||||
{ "id": "profile", "type": "selector", "label": "collection" },
|
||||
{ "id": "overlay", "type": "selector", "label": "overlay" },
|
||||
{ "id": "zoom", "type": "toggleSlider", "label": "zoom" }
|
||||
] },
|
||||
|
||||
{ "crt": [
|
||||
{ "id": "system", "type": "multi", "label": "system", "choices": "PAL, NTSC" },
|
||||
{ "type": "spacer" },
|
||||
{ "id": "brightness", "type": "slider", "icon": "brightness" },
|
||||
{ "id": "contrast", "type": "slider", "icon": "contrast" },
|
||||
{ "id": "saturation", "type": "slider", "icon": "saturation" },
|
||||
{ "type": "spacer" },
|
||||
{ "id": "overscan", "type": "toggleSlider", "label": "overscan" },
|
||||
{ "type": "spacer" },
|
||||
{ "id": "emulation", "type": "toggle", "label": "CRT emulation" },
|
||||
{ "type": "spacer" },
|
||||
{ "id": "curve", "type": "toggleSlider", "label": "curve" },
|
||||
{ "id": "bleed", "type": "toggleSlider", "label": "bleed" },
|
||||
{ "id": "vignette", "type": "toggleSlider", "label": "vignette" },
|
||||
{ "id": "scanlines", "type": "toggleSlider", "label": "scanlines" },
|
||||
{ "id": "gridlines", "type": "toggleSlider", "label": "gridlines" },
|
||||
{ "id": "glow", "type": "toggleSlider", "label": "glow" },
|
||||
{ "id": "flicker", "type": "toggleSlider", "label": "flicker" },
|
||||
{ "id": "noise", "type": "toggleSlider", "label": "noise" },
|
||||
{}
|
||||
] }
|
||||
])"_padded;
|
||||
auto parser = ondemand::parser{};
|
||||
auto doc = parser.iterate(padded_input_json);
|
||||
auto root_array = doc.get_array();
|
||||
// the root should be an object, not an array, but that's the JSON we are
|
||||
// given.
|
||||
for (ondemand::object node : root_array) {
|
||||
// We know that we are going to have just one element in the object.
|
||||
for (auto field : node) {
|
||||
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();
|
||||
// and second for-range loop to get child-elements here
|
||||
for (ondemand::object inner_object : field.value()) {
|
||||
auto i = inner_object.begin();
|
||||
if (i == inner_object.end()) {
|
||||
std::cout << "empty object" << std::endl;
|
||||
continue;
|
||||
} else {
|
||||
for (; i != inner_object.end(); ++i) {
|
||||
auto inner_field = *i;
|
||||
std::cout << '"' << inner_field.key()
|
||||
<< "\" : " << inner_field.value() << ", ";
|
||||
// You can get proper std::string_view for the key and value with:
|
||||
// std::string_view inner_key = field.unescaped_key();
|
||||
// std::string_view value_str = field.value();
|
||||
}
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
// You can break here if you only want just the first element.
|
||||
// break;
|
||||
}
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
|
||||
bool examplecrt_realloc() {
|
||||
TEST_START();
|
||||
std::string unpadded_input_json = R"([
|
||||
{ "monitor": [
|
||||
{ "id": "monitor", "type": "toggle", "label": "monitor" },
|
||||
{ "id": "profile", "type": "selector", "label": "collection" },
|
||||
{ "id": "overlay", "type": "selector", "label": "overlay" },
|
||||
{ "id": "zoom", "type": "toggleSlider", "label": "zoom" }
|
||||
] },
|
||||
|
||||
{ "crt": [
|
||||
{ "id": "system", "type": "multi", "label": "system", "choices": "PAL, NTSC" },
|
||||
{ "type": "spacer" },
|
||||
{ "id": "brightness", "type": "slider", "icon": "brightness" },
|
||||
{ "id": "contrast", "type": "slider", "icon": "contrast" },
|
||||
{ "id": "saturation", "type": "slider", "icon": "saturation" },
|
||||
{ "type": "spacer" },
|
||||
{ "id": "overscan", "type": "toggleSlider", "label": "overscan" },
|
||||
{ "type": "spacer" },
|
||||
{ "id": "emulation", "type": "toggle", "label": "CRT emulation" },
|
||||
{ "type": "spacer" },
|
||||
{ "id": "curve", "type": "toggleSlider", "label": "curve" },
|
||||
{ "id": "bleed", "type": "toggleSlider", "label": "bleed" },
|
||||
{ "id": "vignette", "type": "toggleSlider", "label": "vignette" },
|
||||
{ "id": "scanlines", "type": "toggleSlider", "label": "scanlines" },
|
||||
{ "id": "gridlines", "type": "toggleSlider", "label": "gridlines" },
|
||||
{ "id": "glow", "type": "toggleSlider", "label": "glow" },
|
||||
{ "id": "flicker", "type": "toggleSlider", "label": "flicker" },
|
||||
{ "id": "noise", "type": "toggleSlider", "label": "noise" },
|
||||
{}
|
||||
] }
|
||||
])";
|
||||
unpadded_input_json.shrink_to_fit();
|
||||
auto parser = ondemand::parser{};
|
||||
auto doc = parser.iterate(unpadded_input_json);
|
||||
auto root_array = doc.get_array();
|
||||
// the root should be an object, not an array, but that's the JSON we are
|
||||
// given.
|
||||
for (ondemand::object node : root_array) {
|
||||
// We know that we are going to have just one element in the object.
|
||||
for (auto field : node) {
|
||||
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();
|
||||
// and second for-range loop to get child-elements here
|
||||
for (ondemand::object inner_object : field.value()) {
|
||||
auto i = inner_object.begin();
|
||||
if (i == inner_object.end()) {
|
||||
std::cout << "empty object" << std::endl;
|
||||
continue;
|
||||
} else {
|
||||
for (; i != inner_object.end(); ++i) {
|
||||
auto inner_field = *i;
|
||||
std::cout << '"' << inner_field.key()
|
||||
<< "\" : " << inner_field.value() << ", ";
|
||||
// You can get proper std::string_view for the key and value with:
|
||||
// std::string_view inner_key = field.unescaped_key();
|
||||
// std::string_view value_str = field.value();
|
||||
}
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
// You can break here if you only want just the first element.
|
||||
// break;
|
||||
}
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool number_tests() {
|
||||
TEST_START();
|
||||
ondemand::parser parser;
|
||||
@@ -1309,10 +1481,60 @@ bool example1958() {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool to_optional() {
|
||||
TEST_START();
|
||||
auto json = R"({ "foo1": "3.1416" } )"_padded;
|
||||
ondemand::parser parser;
|
||||
ondemand::document doc = parser.iterate(json);
|
||||
#if __cpp_lib_optional >= 201606L
|
||||
std::optional<std::string> value;
|
||||
ASSERT_SUCCESS(doc["foo1"].get_string(value));
|
||||
std::cout << value.value() << std::endl;
|
||||
#else
|
||||
std::string value;
|
||||
ASSERT_SUCCESS(doc["foo1"].get_string(value));
|
||||
std::cout << value << std::endl;
|
||||
#endif
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool value_raw_json_array() {
|
||||
TEST_START();
|
||||
auto json = R"( [1,2,"fds", {"a":1}, [1,344]] )"_padded;
|
||||
ondemand::parser parser;
|
||||
ondemand::document doc = parser.iterate(json);
|
||||
std::string_view expected[] = {"1", "2", "\"fds\"", "{\"a\":1}", "[1,344]"};
|
||||
size_t counter = 0;
|
||||
for(auto array: doc) {
|
||||
std::string_view raw = array.raw_json();
|
||||
ASSERT_EQUAL(raw, expected[counter++]);
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
|
||||
bool value_raw_json_object() {
|
||||
TEST_START();
|
||||
auto json = R"( {"key1":1,"key2":2,"key3":"fds", "key4":{"a":1}, "key5":[1,344]} )"_padded;
|
||||
ondemand::parser parser;
|
||||
ondemand::document doc = parser.iterate(json);
|
||||
std::string_view expected[] = {"1", "2", "\"fds\"", "{\"a\":1}", "[1,344]"};
|
||||
size_t counter = 0;
|
||||
for(auto key_value: doc.get_object()) {
|
||||
std::string_view raw = key_value.value().raw_json();
|
||||
ASSERT_EQUAL(raw, expected[counter++]);
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
#endif
|
||||
bool run() {
|
||||
return true
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
&& to_optional()
|
||||
&& value_raw_json_array() && value_raw_json_object()
|
||||
&& gen_raw1() && gen_raw2() && gen_raw3()
|
||||
&& at_end()
|
||||
&& example1956() && example1958()
|
||||
@@ -1352,10 +1574,14 @@ bool run() {
|
||||
&& current_location_user_error()
|
||||
&& current_location_out_of_bounds()
|
||||
&& current_location_no_error()
|
||||
&& to_string_example_no_except()
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
&& to_string_example()
|
||||
&& raw_string()
|
||||
&& number_tests()
|
||||
&& current_location_tape_error_with_except()
|
||||
&& examplecrt()
|
||||
&& examplecrt_realloc()
|
||||
#endif
|
||||
;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,210 @@
|
||||
#include "simdjson.h"
|
||||
#include "test_ondemand.h"
|
||||
|
||||
using namespace simdjson;
|
||||
|
||||
namespace json_package_tests {
|
||||
using namespace std;
|
||||
bool baby() {
|
||||
TEST_START();
|
||||
auto json = R"({
|
||||
"name": "Daniel",
|
||||
"age": 42
|
||||
})"_padded;
|
||||
ondemand::parser parser;
|
||||
ondemand::document doc;
|
||||
ASSERT_SUCCESS(parser.iterate(json).get(doc));
|
||||
|
||||
simdjson::ondemand::object main_object;
|
||||
ASSERT_SUCCESS(doc.get_object().get(main_object));
|
||||
std::string name;
|
||||
ASSERT_SUCCESS(main_object["name"].get_string(name));
|
||||
ASSERT_EQUAL(name, "Daniel");
|
||||
uint64_t age;
|
||||
ASSERT_SUCCESS(main_object["age"].get_uint64().get(age));
|
||||
ASSERT_EQUAL(age, 42);
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool thirtysecondsofcode() {
|
||||
TEST_START();
|
||||
auto json = R"({
|
||||
"name": "30-seconds-of-code",
|
||||
"private": true,
|
||||
"version": "10.0.0",
|
||||
"description": "30 seconds of code website.",
|
||||
"exports": "./index.js",
|
||||
"author": "chalarangelo",
|
||||
"type": "module",
|
||||
"devDependencies": {
|
||||
"@jsiqle/core": "^3.0.0",
|
||||
"astro": "^3.2.0",
|
||||
"chalk": "^5.3.0",
|
||||
"eslint": "^8.50.0",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"front-matter": "^4.0.2",
|
||||
"fs-extra": "^11.1.1",
|
||||
"glob": "^10.3.10",
|
||||
"hast-util-to-html": "^9.0.0",
|
||||
"js-yaml": "^4.1.0",
|
||||
"mdast-util-to-hast": "^13.0.2",
|
||||
"prettier": "^3.0.3",
|
||||
"prettier-plugin-astro": "^0.12.0",
|
||||
"prismjs": "^1.29.0",
|
||||
"remark": "^15.0.1",
|
||||
"remark-gfm": "^4.0.0",
|
||||
"sass": "^1.68.0",
|
||||
"sharp": "^0.32.6",
|
||||
"unist-util-select": "^5.0.0",
|
||||
"unist-util-visit": "^5.0.0",
|
||||
"unist-util-visit-parents": "^6.0.1",
|
||||
"webfonts-generator": "^0.4.0"
|
||||
},
|
||||
"imports": {
|
||||
"#blocks/*": "./src/blocks/*.js",
|
||||
"#components/*": "./src/components/*.astro",
|
||||
"#layouts/*": "./src/layouts/*.astro",
|
||||
"#settings/*": "./src/settings/*.js",
|
||||
"#prefabs": "./src/prefabs/index.js",
|
||||
"#utils": "./src/utils/index.js",
|
||||
"#utils/search": "./src/utils/search.js"
|
||||
},
|
||||
"scripts": {
|
||||
"predev": "NODE_ENV=development node ./src/scripts/develop.js",
|
||||
"dev": "astro dev --port 8000",
|
||||
"start": "astro dev --port 8000",
|
||||
"prebuild": "NODE_ENV=production node ./src/scripts/build.js",
|
||||
"build": "astro build",
|
||||
"preview": "astro preview --port 9000",
|
||||
"watch": "NODE_ENV=development node ./src/scripts/watch.js",
|
||||
"console": "NODE_ENV=production node ./src/scripts/console.js",
|
||||
"create": "NODE_ENV=production node ./src/scripts/create.js",
|
||||
"icons": "NODE_ENV=production node ./src/scripts/icons.js",
|
||||
"manifest": "NODE_ENV=production node ./src/scripts/manifest.js"
|
||||
},
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/30-seconds/30-seconds-of-code"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/30-seconds/30-seconds-of-code/issues"
|
||||
},
|
||||
"browserslist": [
|
||||
"> 0.5% and last 4 versions and not dead and not ie>0 and not op_mini all and not and_uc>0 and not edge<79"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18.14.2"
|
||||
}
|
||||
})"_padded;
|
||||
ondemand::parser parser;
|
||||
ondemand::document doc;
|
||||
ASSERT_SUCCESS(parser.iterate(json).get(doc));
|
||||
|
||||
simdjson::ondemand::object main_object;
|
||||
ASSERT_SUCCESS(doc.get_object().get(main_object));
|
||||
|
||||
simdjson::ondemand::raw_json_string key;
|
||||
simdjson::ondemand::value value;
|
||||
|
||||
for (auto field : main_object) {
|
||||
// Throw error if getting key or value fails.
|
||||
ASSERT_SUCCESS(field.key().get(key));
|
||||
ASSERT_SUCCESS(field.value().get(value));
|
||||
|
||||
if (key == "name") {
|
||||
std::string name;
|
||||
ASSERT_SUCCESS(value.get_string(name));
|
||||
ASSERT_EQUAL(name, "30-seconds-of-code");
|
||||
} else if (key == "main") {
|
||||
std::string main;
|
||||
ASSERT_SUCCESS(value.get_string(main));
|
||||
// unused
|
||||
} else if (key == "exports") {
|
||||
simdjson::ondemand::json_type exports_type;
|
||||
if (!value.type().get(exports_type)) {
|
||||
std::string_view exports;
|
||||
switch (exports_type) {
|
||||
case simdjson::ondemand::json_type::object: {
|
||||
simdjson::ondemand::object exports_object;
|
||||
if (!value.get_object().get(exports_object) &&
|
||||
!exports_object.raw_json().get(exports)) {
|
||||
// unused
|
||||
}
|
||||
break;
|
||||
}
|
||||
case simdjson::ondemand::json_type::array: {
|
||||
simdjson::ondemand::array exports_array;
|
||||
if (!value.get_array().get(exports_array) &&
|
||||
!exports_array.raw_json().get(exports)) {
|
||||
// unused
|
||||
}
|
||||
break;
|
||||
}
|
||||
case simdjson::ondemand::json_type::string: {
|
||||
if (!value.get_string().get(exports)) {
|
||||
ASSERT_EQUAL(exports, "./index.js");
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (key == "imports") {
|
||||
simdjson::ondemand::json_type imports_type;
|
||||
if (!value.type().get(imports_type)) {
|
||||
std::string_view imports;
|
||||
switch (imports_type) {
|
||||
case simdjson::ondemand::json_type::object: {
|
||||
simdjson::ondemand::object imports_object;
|
||||
if (!value.get_object().get(imports_object) &&
|
||||
!imports_object.raw_json().get(imports)) {
|
||||
ASSERT_EQUAL(imports, R"({
|
||||
"#blocks/*": "./src/blocks/*.js",
|
||||
"#components/*": "./src/components/*.astro",
|
||||
"#layouts/*": "./src/layouts/*.astro",
|
||||
"#settings/*": "./src/settings/*.js",
|
||||
"#prefabs": "./src/prefabs/index.js",
|
||||
"#utils": "./src/utils/index.js",
|
||||
"#utils/search": "./src/utils/search.js"
|
||||
})");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case simdjson::ondemand::json_type::array: {
|
||||
simdjson::ondemand::array imports_array;
|
||||
if (!value.get_array().get(imports_array) &&
|
||||
!imports_array.raw_json().get(imports)) {
|
||||
// unused
|
||||
}
|
||||
break;
|
||||
}
|
||||
case simdjson::ondemand::json_type::string: {
|
||||
if (!value.get_string().get(imports)) {
|
||||
// unused
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (key == "type") {
|
||||
std::string_view type;
|
||||
if (!value.get_string().get(type) &&
|
||||
(type == "commonjs" || type == "module")) {
|
||||
ASSERT_EQUAL(type, "module");
|
||||
}
|
||||
}
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool run() { return thirtysecondsofcode() && baby(); }
|
||||
|
||||
} // namespace json_package_tests
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
return test_main(argc, argv, json_package_tests::run);
|
||||
}
|
||||
@@ -167,8 +167,6 @@ print("the commandline is {}".format(cp.args))
|
||||
if(cp.returncode != 0):
|
||||
print("Failed to run doxygen")
|
||||
|
||||
#ipe = subprocess.Popen(["doxygen"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=maindir)
|
||||
#doxygenresult = pipe.communicate()[0].decode().strip()
|
||||
|
||||
pattern = re.compile("https://simdjson.org/api/(\d+\.\d+\.\d+)/index.html")
|
||||
readmefile = maindir + os.sep + "README.md"
|
||||
|
||||
Reference in New Issue
Block a user