Compare commits

...

28 Commits

Author SHA1 Message Date
Daniel Lemire 9dd6e9c959 version bump 2023-10-25 19:35:51 -04:00
Daniel Lemire 6db330c1a2 Automating string reallocation when it is possible (#2076)
* Automating string reallocation

* Typo
2023-10-25 19:34:52 -04:00
Daniel Lemire 993ac4b87c Adding the ability to write directly to an std::string. It is mostly syntaxic sugar (#2075) 2023-10-25 19:32:12 -04:00
Daniel Lemire 35e87896f0 minor tweak 2023-10-25 10:50:27 -04:00
Daniel Lemire f7e281cadc removing noisy warning 2023-10-20 15:45:40 -04:00
Daniel Lemire 13405afd4b This provides verbose static_assert messages (#2074)
* This provides verbose static_assert messages

* minor fix
2023-10-20 15:42:18 -04:00
Daniel Lemire 24b44309fb Removing commented code 2023-10-15 14:56:32 -04:00
Momtchil Momtchev e11ad58aad add a total order for elements (#2072) 2023-10-09 15:41:57 -04:00
Daniel Lemire 1875ed6550 Version bump. 2023-09-20 10:13:47 -04:00
Daniel Lemire 80d26298a0 Redesigning visit_primitive so that it is optimized for strings and (#2060)
numbers.

Co-authored-by: Daniel Lemire <dlemire@lemire.me>
2023-09-20 10:09:56 -04:00
Antoine Prouvost 25b5015c09 Add simdjson_static lib (#2068)
* Add simdjson_static lib

* Make simdjson_static a separate optional export file
2023-09-20 10:09:27 -04:00
Antoine Prouvost 26f8c566c7 Fix issue template bold text (#2067) 2023-09-19 11:15:54 -04:00
Daniel Lemire ff77ac801e Additional documentation based on issue 2063 (#2065)
* Additional documentation based on issue 2063

* Update basics.md
2023-09-11 10:01:35 -04:00
Daniel Lemire e0699994ef Testing macOS shared library support (#2062) 2023-09-09 12:48:42 -04:00
Daniel Lemire 68ba9a1b2a Adding a few spaces (minor formatting) (#2059) 2023-08-30 20:23:58 -04:00
Daniel Lemire 6fed6bd29b Update README.md 2023-08-30 19:26:34 -04:00
Piotr Rżysko cc24bb4114 Removed number parsing fallback (#2056) 2023-08-29 18:28:21 -04:00
John Keiser 14ec0ca8f4 Merge pull request #2052 from simdjson/jkeiser/structural_indexer_step
Write out 4 structural indexes at a time instead of 8
2023-08-29 15:24:54 -07:00
John Keiser 8dabd02c3a Default to step = 4 2023-08-29 15:48:52 -04:00
Daniel Lemire c9692005fd This allows us to pass SIMDJSON_STRUCTURAL_INDEXER_STEP as a command-line option. (#2055) 2023-08-25 13:47:20 -04:00
Daniel Lemire 2e0a9397c9 Merge branch 'master' into jkeiser/structural_indexer_step 2023-08-25 11:34:13 -04:00
Daniel Lemire f7922075c3 Adding support for Apple event counters (#2054) 2023-08-25 11:33:24 -04:00
Daniel Lemire b2e20e04c9 Standard compatibility fixes (#2053)
* Standard compatibility fixes

* missing commit

* Should work.

* Fix.

* Fix.

* Should work now.

---------

Co-authored-by: Daniel Lemire <dlemire@lemire.me>
2023-08-25 10:18:02 -04:00
John Keiser adc9d18efd Change step for structural_indexes to 2 2023-08-23 19:02:05 -07:00
Daniel Lemire bf849e3619 Preparing release 2023-08-22 14:05:14 -04:00
Daniel Lemire c09e8dc225 Icelake processes 64 bytes at a time when processing strings. (#2050)
Co-authored-by: Daniel Lemire <dlemire@lemire.me>
2023-08-18 14:37:10 -04:00
Daniel Lemire 5bf585ef02 Fixing failing thread sanitizer (#2051) 2023-08-18 14:35:12 -04:00
Daniel Lemire caff09cafc Update iterate_many.md 2023-08-14 12:18:09 -04:00
47 changed files with 3004 additions and 789 deletions
+1 -1
View File
@@ -31,7 +31,7 @@ A clear and concise description of any alternative solutions or features you've
**Additional context** **Additional context**
Add any other context or screenshots about the feature request here. 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 If you plan to contribute to simdjson, please read our
* CONTRIBUTING guide: https://github.com/simdjson/simdjson/blob/master/CONTRIBUTING.md and 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 * HACKING guide: https://github.com/simdjson/simdjson/blob/master/HACKING.md
+11
View File
@@ -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 && 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 && cd ../tests/installation_tests/find &&
mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../../../build/destination .. && cmake --build . 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 .
@@ -1,4 +1,4 @@
name: Ubuntu 20.04 CI (GCC 9) with Thread Sanitizer name: Ubuntu 22.04 CI (GCC 11) with Thread Sanitizer
on: [push, pull_request] on: [push, pull_request]
@@ -7,7 +7,7 @@ jobs:
if: >- if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') && ! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]') ! contains(toJSON(github.event.commits.*.message), '[skip github]')
runs-on: ubuntu-20.04 runs-on: ubuntu-22.04
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: actions/cache@v3 - uses: actions/cache@v3
+32 -5
View File
@@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.14)
project( project(
simdjson simdjson
# The version number is modified by tools/release.py # The version number is modified by tools/release.py
VERSION 3.2.2 VERSION 3.4.0
DESCRIPTION "Parsing gigabytes of JSON per second" DESCRIPTION "Parsing gigabytes of JSON per second"
HOMEPAGE_URL "https://simdjson.org/" HOMEPAGE_URL "https://simdjson.org/"
LANGUAGES CXX C LANGUAGES CXX C
@@ -20,8 +20,10 @@ string(
# ---- Options, variables ---- # ---- Options, variables ----
# These version numbers are modified by tools/release.py # These version numbers are modified by tools/release.py
set(SIMDJSON_LIB_VERSION "16.0.0" CACHE STRING "simdjson library version") set(SIMDJSON_LIB_VERSION "17.0.0" CACHE STRING "simdjson library version")
set(SIMDJSON_LIB_SOVERSION "16" CACHE STRING "simdjson library soversion") set(SIMDJSON_LIB_SOVERSION "17" 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) option(SIMDJSON_ENABLE_THREADS "Link with thread support" ON)
@@ -56,8 +58,17 @@ include(cmake/developer-options.cmake)
# ---- simdjson library ---- # ---- 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) 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( set_target_properties(
simdjson PROPERTIES simdjson PROPERTIES
@@ -117,6 +128,9 @@ if(SIMDJSON_ENABLE_THREADS)
endif() endif()
simdjson_apply_props(simdjson) simdjson_apply_props(simdjson)
if(SIMDJSON_BUILD_STATIC_LIB)
simdjson_apply_props(simdjson_static)
endif()
# ---- Install rules ---- # ---- Install rules ----
@@ -138,7 +152,6 @@ install(
ARCHIVE COMPONENT simdjson_Development ARCHIVE COMPONENT simdjson_Development
INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
) )
configure_file(cmake/simdjson-config.cmake.in simdjson-config.cmake @ONLY) configure_file(cmake/simdjson-config.cmake.in simdjson-config.cmake @ONLY)
write_basic_package_version_file( write_basic_package_version_file(
@@ -167,6 +180,20 @@ install(
COMPONENT simdjson_Development 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 # pkg-config
include(cmake/JoinPaths.cmake) include(cmake/JoinPaths.cmake)
join_paths(PKGCONFIG_INCLUDEDIR "\${prefix}" "${CMAKE_INSTALL_INCLUDEDIR}") join_paths(PKGCONFIG_INCLUDEDIR "\${prefix}" "${CMAKE_INSTALL_INCLUDEDIR}")
+1 -1
View File
@@ -38,7 +38,7 @@ PROJECT_NAME = simdjson
# could be handy for archiving the generated documentation or if some version # could be handy for archiving the generated documentation or if some version
# control system is used. # control system is used.
PROJECT_NUMBER = "3.2.2" PROJECT_NUMBER = "3.4.0"
# Using the PROJECT_BRIEF tag one can provide an optional one line description # 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 # for a project that appears at the top of each page and should give viewer a
-2
View File
@@ -1,8 +1,6 @@
[![Ubuntu 20.04 CI](https://github.com/simdjson/simdjson/workflows/Ubuntu%2020.04%20CI%20(GCC%209)/badge.svg)](https://simdjson.org/plots.html) [![Ubuntu 20.04 CI](https://github.com/simdjson/simdjson/workflows/Ubuntu%2020.04%20CI%20(GCC%209)/badge.svg)](https://simdjson.org/plots.html)
![VS16-CI](https://github.com/simdjson/simdjson/workflows/VS16-CI/badge.svg)
[![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/simdjson.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:simdjson) [![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/simdjson.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:simdjson)
![MinGW64-CI](https://github.com/simdjson/simdjson/workflows/MinGW64-CI/badge.svg)
[![][license img]][license] [![][license img]][license]
[![Doxygen Documentation](https://img.shields.io/badge/docs-doxygen-green.svg)](https://simdjson.github.io/simdjson/) [![Doxygen Documentation](https://img.shields.io/badge/docs-doxygen-green.svg)](https://simdjson.github.io/simdjson/)
File diff suppressed because it is too large Load Diff
+13 -9
View File
@@ -445,7 +445,7 @@ struct benchmarker {
return 100.0 * a / b; return 100.0 * a / b;
} }
void print(bool tabbed_output) const { void print(bool tabbed_output, bool stage1_only) const {
if (tabbed_output) { if (tabbed_output) {
char* filename_copy = reinterpret_cast<char*>(malloc(strlen(filename)+1)); char* filename_copy = reinterpret_cast<char*>(malloc(strlen(filename)+1));
SIMDJSON_PUSH_DISABLE_WARNINGS 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)); stats->blocks_with_16_structurals_flipped, percent(stats->blocks_with_16_structurals_flipped, stats->blocks));
} }
printf("\n"); printf("\n");
printf("All Stages (excluding allocation)\n"); if(!stage1_only) {
print_aggregate("| " , all_stages_without_allocation.best); printf("All Stages (excluding allocation)\n");
// frequently, allocation is a tiny fraction of the running time so we omit it print_aggregate("| " , all_stages_without_allocation.best);
if(allocate_stage.best.elapsed_sec() > 0.01 * all_stages_without_allocation.best.elapsed_sec()) { // frequently, allocation is a tiny fraction of the running time so we omit it
printf("|- Allocation\n"); if(allocate_stage.best.elapsed_sec() > 0.01 * all_stages_without_allocation.best.elapsed_sec()) {
print_aggregate("| ", allocate_stage.best); printf("|- Allocation\n");
print_aggregate("| ", allocate_stage.best);
}
} }
printf("|- Stage 1\n"); printf("|- Stage 1\n");
print_aggregate("| ", stage1.best); print_aggregate("| ", stage1.best);
printf("|- Stage 2\n"); if(!stage1_only) {
print_aggregate("| ", stage2.best); printf("|- Stage 2\n");
print_aggregate("| ", stage2.best);
}
if (collector.has_events()) { if (collector.has_events()) {
double freq1 = (stage1.best.cycles() / stage1.best.elapsed_sec()) / 1000000000.0; double freq1 = (stage1.best.cycles() / stage1.best.elapsed_sec()) / 1000000000.0;
double freq2 = (stage2.best.cycles() / stage2.best.elapsed_sec()) / 1000000000.0; double freq2 = (stage2.best.cycles() / stage2.best.elapsed_sec()) / 1000000000.0;
+1 -1
View File
@@ -218,7 +218,7 @@ int main(int argc, char *argv[]) {
if (!options.verbose) { progress.erase(); } if (!options.verbose) { progress.erase(); }
for (size_t i=0; i<options.files.size(); i++) { 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]; delete benchmarkers[i];
} }
+28 -3
View File
@@ -34,11 +34,15 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "linux-perf-events.h"
#ifdef __linux__ #ifdef __linux__
#include "linux-perf-events.h"
#include <libgen.h> #include <libgen.h>
#endif #endif
#if __APPLE__ && __aarch64__
#include "apple/apple_arm_events.h"
#endif
#include "simdjson.h" #include "simdjson.h"
using std::string; using std::string;
@@ -134,7 +138,7 @@ struct event_collector {
#if defined(__linux__) #if defined(__linux__)
LinuxEvents<PERF_TYPE_HARDWARE> linux_events; 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 #if SIMDJSON_SIMPLE_PERFORMANCE_COUNTERS
PERF_COUNT_HW_CPU_CYCLES, PERF_COUNT_HW_CPU_CYCLES,
PERF_COUNT_HW_INSTRUCTIONS, PERF_COUNT_HW_INSTRUCTIONS,
@@ -149,8 +153,17 @@ struct event_collector {
bool has_events() { bool has_events() {
return linux_events.is_working(); 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 #else
event_collector(simdjson_unused bool _quiet = false) {} event_collector() {}
bool has_events() { bool has_events() {
return false; return false;
} }
@@ -159,6 +172,8 @@ struct event_collector {
simdjson_inline void start() { simdjson_inline void start() {
#if defined(__linux) #if defined(__linux)
linux_events.start(); linux_events.start();
#elif __APPLE__ && __aarch64__
if(has_events()) { diff = apple_events.get_counters(); }
#endif #endif
start_clock = steady_clock::now(); start_clock = steady_clock::now();
} }
@@ -166,6 +181,16 @@ struct event_collector {
time_point<steady_clock> end_clock = steady_clock::now(); time_point<steady_clock> end_clock = steady_clock::now();
#if defined(__linux) #if defined(__linux)
linux_events.end(count.event_counts); 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 #endif
count.elapsed = end_clock - start_clock; count.elapsed = end_clock - start_clock;
return count; return count;
@@ -19,7 +19,7 @@ void maybe_display_implementation() {
template<typename B, typename R> static void run_json_benchmark(benchmark::State &state) { template<typename B, typename R> static void run_json_benchmark(benchmark::State &state) {
maybe_display_implementation(); maybe_display_implementation();
event_collector collector(true); event_collector collector;
event_aggregate events; event_aggregate events;
// Warmup and equality check (make sure the data is right!) // Warmup and equality check (make sure the data is right!)
+5 -1
View File
@@ -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 ${SIMDJSON_CXX_STANDARD})
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_MACOSX_RPATH OFF)
set(CMAKE_THREAD_PREFER_PTHREAD ON) set(CMAKE_THREAD_PREFER_PTHREAD ON)
set(THREADS_PREFER_PTHREAD_FLAG 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 # LTO seems to create all sorts of fun problems. Let us
# disable temporarily. # disable temporarily.
#include(CheckIPOSupported) #include(CheckIPOSupported)
+1
View File
@@ -4,3 +4,4 @@ if("@SIMDJSON_ENABLE_THREADS@")
endif() endif()
include("${CMAKE_CURRENT_LIST_DIR}/simdjsonTargets.cmake") include("${CMAKE_CURRENT_LIST_DIR}/simdjsonTargets.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/simdjson_staticTargets.cmake" OPTIONAL)
+152 -38
View File
@@ -179,8 +179,8 @@ strcpy(json, "[1]");
ondemand::document doc = parser.iterate(json, strlen(json), sizeof(json)); 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 simdjson library will also accept `std::string` instances. If the provided
the string exceeds the `size()` by at least `SIMDJSON_PADDING`. You can increase the `capacity()` with the `reserve()` function of your strings. reference is non-const, it will allocate padding as needed.
You can copy your data directly on a `simdjson::padded_string` as follows: You can copy your data directly on a `simdjson::padded_string` as follows:
@@ -355,7 +355,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 * **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 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 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. 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 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 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"]`. value instance you get from `content["bids"]` becomes invalid when you call `content["asks"]`.
@@ -370,7 +370,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 > 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 > 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` > 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++ > ```c++
> auto json = R"({"k\u0065y": 1})"_padded; > auto json = R"({"k\u0065y": 1})"_padded;
@@ -381,7 +385,7 @@ support for users who avoid exceptions. See [the simdjson error handling documen
> // parses and writes out the key, after unescaping it, > // parses and writes out the key, after unescaping it,
> // to a string buffer. It causes a performance penalty. > // to a string buffer. It causes a performance penalty.
> std::string_view keyv = field.unescaped_key(); > 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 +418,10 @@ support for users who avoid exceptions. See [the simdjson error handling documen
step through each value in the JSON array. 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) { ... }`. 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. - `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, * **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. you should iterate through the array and keep an index yourself.
@@ -445,7 +451,7 @@ support for users who avoid exceptions. See [the simdjson error handling documen
> { > {
> ondemand::parser parser; > ondemand::parser parser;
> for (ondemand::object car : parser.iterate(cars_json)) { > 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"])); > arrays.push_back(simdjson::to_json_string(car["tire_pressure"]));
> } > }
> } > }
@@ -454,7 +460,7 @@ support for users who avoid exceptions. See [the simdjson error handling documen
> std::ostringstream oss; > std::ostringstream oss;
> oss << "["; > oss << "[";
> for(size_t i = 0; i < arrays.size(); i++) { > for(size_t i = 0; i < arrays.size(); i++) {
> if(i>0) { oss << ","; } > if (i>0) { oss << ","; }
> oss << arrays[i]; > oss << arrays[i];
> } > }
> oss << "]"; > oss << "]";
@@ -597,7 +603,7 @@ support for users who avoid exceptions. See [the simdjson error handling documen
case ondemand::json_type::null: case ondemand::json_type::null:
// We check that the value is indeed null // We check that the value is indeed null
// otherwise: an error is thrown. // otherwise: an error is thrown.
if(element.is_null()) { if (element.is_null()) {
cout << "null"; cout << "null";
} }
break; break;
@@ -910,11 +916,11 @@ bool simple_error_example() {
ondemand::parser parser; ondemand::parser parser;
auto json = R"({"bad number":3.14.1 })"_padded; auto json = R"({"bad number":3.14.1 })"_padded;
ondemand::document doc; ondemand::document doc;
if( parser.iterate(json).get(doc) != SUCCESS ) { return false; } if (parser.iterate(json).get(doc) != SUCCESS) { return false; }
double x; double x;
auto error = doc["bad number"].get_double().get(x); auto error = doc["bad number"].get_double().get(x);
// returns "simdjson::NUMBER_ERROR" // returns "simdjson::NUMBER_ERROR"
if(error != SUCCESS) { if (error != SUCCESS) {
std::cout << error << std::endl; std::cout << error << std::endl;
return false; return false;
} }
@@ -976,10 +982,10 @@ it selects the key `"count"` within that object.
int main(void) { int main(void) {
simdjson::ondemand::parser parser; simdjson::ondemand::parser parser;
auto error = padded_string::load("twitter.json").get(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; }
simdjson::ondemand::document tweets; simdjson::ondemand::document tweets;
error = parser.iterate(json).get(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; simdjson::ondemand::value res;
error = tweets["search_metadata"]["count"].get(res); error = tweets["search_metadata"]["count"].get(res);
if (error != SUCCESS) { if (error != SUCCESS) {
@@ -1010,12 +1016,12 @@ int main(void) {
simdjson::ondemand::document tweets; simdjson::ondemand::document tweets;
padded_string json; padded_string json;
auto error = padded_string::load("twitter.json").get(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); 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; uint64_t identifier;
error = tweets["statuses"].at(0)["id"].get(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; std::cout << identifier << std::endl;
} }
``` ```
@@ -1039,40 +1045,40 @@ bool parse() {
// Iterating through an array of objects // Iterating through an array of objects
auto error = parser.iterate(cars_json).get(doc); 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 ondemand::array cars; // invalid until the get() succeeds
error = doc.get_array().get(cars); error = doc.get_array().get(cars);
for (auto car_value : cars) { for (auto car_value : cars) {
ondemand::object car; // invalid until the get() succeeds ondemand::object car; // invalid until the get() succeeds
error = car_value.get_object().get(car); 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 // Accessing a field by name
std::string_view make; std::string_view make;
std::string_view model; std::string_view model;
error = car["make"].get(make); 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); 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; cout << "Make/Model: " << make << "/" << model << endl;
// Casting a JSON element to an integer // Casting a JSON element to an integer
uint64_t year{}; uint64_t year{};
error = car["year"].get(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; cout << "- This car is " << 2020 - year << " years old." << endl;
// Iterating through an array of floats // Iterating through an array of floats
double total_tire_pressure = 0; double total_tire_pressure = 0;
ondemand::array pressures; ondemand::array pressures;
error = car["tire_pressure"].get_array().get(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) { for (auto tire_pressure_value : pressures) {
double tire_pressure; double tire_pressure;
error = tire_pressure_value.get_double().get(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; total_tire_pressure += tire_pressure;
} }
cout << "- Average tire pressure: " << (total_tire_pressure / 4) << endl; cout << "- Average tire pressure: " << (total_tire_pressure / 4) << endl;
@@ -1088,7 +1094,7 @@ after you have initialized them and checked that there is no error:
ondemand::object car; // invalid until the get() succeeds ondemand::object car; // invalid until the get() succeeds
// the `car` instance should not use used before it is initialized // the `car` instance should not use used before it is initialized
error = car_value.get_object().get(car); error = car_value.get_object().get(car);
if(error) { if (error) {
// the `car` instance should not use used // the `car` instance should not use used
} else { } else {
// the `car` instance can be safely used // the `car` instance can be safely used
@@ -1102,20 +1108,20 @@ having to handle exceptions.
ondemand::parser parser; ondemand::parser parser;
ondemand::document doc; ondemand::document doc;
auto error = parser.iterate(json).get(doc); auto error = parser.iterate(json).get(doc);
if(error) { return false; } if (error) { return false; }
ondemand::object object; // invalid until the get() succeeds ondemand::object object; // invalid until the get() succeeds
error = doc.get_object().get(object); error = doc.get_object().get(object);
if(error) { return false; } if (error) { return false; }
for(auto field : object) { for(auto field : object) {
// We could replace 'field.key() with field.unescaped_key(), // We could replace 'field.key() with field.unescaped_key(),
// and ondemand::raw_json_string by std::string_view. // and ondemand::raw_json_string by std::string_view.
ondemand::raw_json_string keyv; ondemand::raw_json_string keyv;
error = field.key().get(keyv); error = field.key().get(keyv);
if(error) { return false; } if (error) { return false; }
if(keyv == "key") { if (keyv == "key") {
uint64_t intvalue; uint64_t intvalue;
error = field.value().get(intvalue); error = field.value().get(intvalue);
if(error) { return false; } if (error) { return false; }
std::cout << intvalue; std::cout << intvalue;
} }
} }
@@ -1294,7 +1300,7 @@ content.
for (uint64_t values : array) { for (uint64_t values : array) {
std::cout << values << std::endl; 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]. // 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; std::cerr << "trailing content at byte index " << doc.current_location() - json.data() << std::endl;
} }
@@ -1324,7 +1330,7 @@ before printout the data.
auto doc = parser.iterate(cars_json); auto doc = parser.iterate(cars_json);
for (simdjson_unused ondemand::object car : doc) { 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"; std::cout << "We have " << count << " Toyota cars.\n";
doc.rewind(); // requires simdjson 1.0 or better doc.rewind(); // requires simdjson 1.0 or better
@@ -1392,18 +1398,18 @@ ondemand::parser parser;
ondemand::document_stream stream; ondemand::document_stream stream;
size_t counter{0}; size_t counter{0};
auto error = parser.iterate_many(json, 50).get(stream); auto error = parser.iterate_many(json, 50).get(stream);
if( error ) { /* handle the error */ } if (error) { /* handle the error */ }
for (auto doc: stream) { for (auto doc: stream) {
if(counter < 6) { if (counter < 6) {
int64_t val; int64_t val;
error = doc.at_pointer("/4").get(val); error = doc.at_pointer("/4").get(val);
if( error ) { /* handle the error */ } if (error) { /* handle the error */ }
std::cout << "5 = " << val << std::endl; std::cout << "5 = " << val << std::endl;
} else { } else {
ondemand::value val; ondemand::value val;
error = doc.at_pointer("/4").get(val); error = doc.at_pointer("/4").get(val);
// error == simdjson::CAPACITY // error == simdjson::CAPACITY
if(error) { if (error) {
std::cerr << error << std::endl; std::cerr << error << std::endl;
// We left 293 bytes unprocessed at the tail end of the input. // We left 293 bytes unprocessed at the tail end of the input.
std::cout << " unprocessed bytes at the end: " << stream.truncated_bytes() << std::endl; std::cout << " unprocessed bytes at the end: " << stream.truncated_bytes() << std::endl;
@@ -1647,7 +1653,7 @@ JSON string to a user-provided buffer:
General Direct Access to the Raw JSON String General Direct Access to the Raw JSON String
-------------------------------- --------------------------------
If your value is a string, the `raw_json_string` 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 string. The simdjson library allows you to have access to the raw underlying JSON
more generally. more generally.
@@ -1716,6 +1722,41 @@ obj.reset(); // revise the object
uint64_t x = obj["value"]; // gives me 123 uint64_t x = obj["value"]; // gives me 123
``` ```
Storing Directly into an Existing std::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 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.
You should be mindful of the trade-off: allocating multiple
`std::string` instances can become expensive.
Thread Safety Thread Safety
------------- -------------
@@ -1944,6 +1985,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 Performance Tips
-------- --------
@@ -1969,4 +2083,4 @@ Performance Tips
std::string_view year = data["year"]; std::string_view year = data["year"];
std::string_view rating = data["rating"]; 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
View File
@@ -329,10 +329,10 @@ int main(void) {
simdjson::dom::parser parser; simdjson::dom::parser parser;
simdjson::dom::element tweets; // invalid until the get() succeeds simdjson::dom::element tweets; // invalid until the get() succeeds
auto error = parser.load("twitter.json").get(tweets); 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; uint64_t identifier;
error = tweets["statuses"].at(0)["id"].get(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; std::cout << identifier << std::endl;
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
+3 -3
View File
@@ -101,8 +101,8 @@ by comparing it with the null pointer.
```c++ ```c++
auto my_implementation = simdjson::get_available_implementations()["haswell"]; auto my_implementation = simdjson::get_available_implementations()["haswell"];
if(! my_implementation) { exit(1); } if (! my_implementation) { exit(1); }
if(! my_implementation->supported_by_runtime_system()) { exit(1); } if (! my_implementation->supported_by_runtime_system()) { exit(1); }
simdjson::get_active_implementation() = my_implementation; simdjson::get_active_implementation() = my_implementation;
``` ```
@@ -113,7 +113,7 @@ You should call `supported_by_runtime_system()` to compare the processor's featu
```c++ ```c++
for (auto implementation : simdjson::get_available_implementations()) { 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; cout << implementation->name() << ": " << implementation->description() << endl;
} }
} }
+7 -4
View File
@@ -17,6 +17,9 @@ Contents
- [Motivations](#motivations) - [Motivations](#motivations)
- [How it works](#how-it-works) - [How it works](#how-it-works)
- [Context](#context)
- [Design](#design)
- [Threads](#threads)
- [Support](#support) - [Support](#support)
- [API](#api) - [API](#api)
- [Use cases](#use-cases) - [Use cases](#use-cases)
@@ -193,12 +196,12 @@ Let us illustrate the idea with code:
simdjson::ondemand::parser parser; simdjson::ondemand::parser parser;
simdjson::ondemand::document_stream stream; simdjson::ondemand::document_stream stream;
auto error = parser.iterate_many(json).get(stream); auto error = parser.iterate_many(json).get(stream);
if( error ) { /* do something */ } if (error) { /* do something */ }
auto i = stream.begin(); auto i = stream.begin();
size_t count{0}; size_t count{0};
for(; i != stream.end(); ++i) { for(; i != stream.end(); ++i) {
auto doc = *i; auto doc = *i;
if(!i.error()) { if (!i.error()) {
std::cout << "got full document at " << i.current_index() << std::endl; std::cout << "got full document at " << i.current_index() << std::endl;
std::cout << i.source() << std::endl; std::cout << i.source() << std::endl;
count++; count++;
@@ -234,7 +237,7 @@ Consider the following example where a truncated document (`{"key":"intentionall
simdjson::ondemand::parser parser; simdjson::ondemand::parser parser;
simdjson::ondemand::document_stream stream; simdjson::ondemand::document_stream stream;
auto error = parser.iterate_many(json,json.size()).get(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) { for(auto i = stream.begin(); i != stream.end(); ++i) {
std::cout << i.source() << std::endl; std::cout << i.source() << std::endl;
} }
@@ -266,7 +269,7 @@ Example:
// we pass 'true' to the allow_comma parameter, the batch size will be set to at least // we pass 'true' to the allow_comma parameter, the batch size will be set to at least
// the document size. // the document size.
auto error = parser.iterate_many(json, 32, true).get(doc_stream); 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) { for (auto doc : doc_stream) {
std::cout << doc.type() << std::endl; std::cout << doc.type() << std::endl;
} }
+4 -4
View File
@@ -679,11 +679,11 @@ in production systems:
ondemand::object c1 = parent["child1"]; ondemand::object c1 = parent["child1"];
// c1 owns the focus // 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 // c2 attempts to grab the focus from parent but fails
ondemand::object c2 = parent["child2"]; ondemand::object c2 = parent["child2"];
// c2 is now in an unsafe state and the following line would be unsafe // 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: A correct usage is given by the following example:
@@ -697,7 +697,7 @@ in production systems:
{ {
ondemand::object c1 = parent["child1"]; ondemand::object c1 = parent["child1"];
// c1 grabbed the focus from parent // 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 // c1 went out of scope, so its destructor was called and the focus
// was handed back to parent. // was handed back to parent.
@@ -705,7 +705,7 @@ in production systems:
ondemand::object c2 = parent["child2"]; ondemand::object c2 = parent["child2"];
// c2 grabbed the focus from parent // c2 grabbed the focus from parent
// the following is safe: // the following is safe:
if(std::string_view(c2["name"]) != "Daniel") { return false; } if (std::string_view(c2["name"]) != "Daniel") { return false; }
} }
``` ```
+3 -3
View File
@@ -184,12 +184,12 @@ Let us illustrate the idea with code:
simdjson::dom::parser parser; simdjson::dom::parser parser;
simdjson::dom::document_stream stream; simdjson::dom::document_stream stream;
auto error = parser.parse_many(json).get(stream); auto error = parser.parse_many(json).get(stream);
if( error ) { /* do something */ } if (error) { /* do something */ }
auto i = stream.begin(); auto i = stream.begin();
size_t count{0}; size_t count{0};
for(; i != stream.end(); ++i) { for(; i != stream.end(); ++i) {
auto doc = *i; auto doc = *i;
if(!doc.error()) { if (!doc.error()) {
std::cout << "got full document at " << i.current_index() << std::endl; std::cout << "got full document at " << i.current_index() << std::endl;
std::cout << i.source() << std::endl; std::cout << i.source() << std::endl;
count++; count++;
@@ -225,7 +225,7 @@ Consider the following example where a truncated document (`{"key":"intentionall
simdjson::dom::parser parser; simdjson::dom::parser parser;
simdjson::dom::document_stream stream; simdjson::dom::document_stream stream;
auto error = parser.parse_many(json,json.size()).get(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) { for(auto doc : stream) {
std::cout << doc << std::endl; std::cout << doc << std::endl;
} }
+8
View File
@@ -32,4 +32,12 @@
#error simdjson requires a compiler compliant with the C++11 standard #error simdjson requires a compiler compliant with the C++11 standard
#endif #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 #endif // SIMDJSON_COMPILER_CHECK_H
+6
View File
@@ -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 { inline simdjson_result<element> element::at_key_case_insensitive(std::string_view key) const noexcept {
return get<object>().at_key_case_insensitive(key); 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 { inline bool element::dump_raw_tape(std::ostream &out) const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914 SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
+21 -1
View File
@@ -211,7 +211,11 @@ public:
inline simdjson_result<T> get() const noexcept { inline simdjson_result<T> get() const noexcept {
// Unless the simdjson library provides an inline implementation, calling this method should // Unless the simdjson library provides an inline implementation, calling this method should
// immediately fail. // 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; 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. */ /** @private for debugging. Prints out the root element. */
inline bool dump_raw_tape(std::ostream &out) const noexcept; inline bool dump_raw_tape(std::ostream &out) const noexcept;
+4 -8
View File
@@ -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 // 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. // 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 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 // then we get a better approximation to i * 5^q.
// will not suffice, though it is seemingly very hard to find such a scenario.
// //
// That's for when q>=0. The logic for q<0 is somewhat similar but it is somewhat // That's for when q>=0. The logic for q<0 is somewhat similar but it is somewhat
// more complicated. // 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]); simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]);
firstproduct.low += secondproduct.high; firstproduct.low += secondproduct.high;
if(secondproduct.high > firstproduct.low) { firstproduct.high++; } if(secondproduct.high > firstproduct.low) { firstproduct.high++; }
// At this point, we might need to add at most one to firstproduct, but this // As it has been proven by Noble Mushtak and Daniel Lemire in "Fast Number Parsing Without
// can only change the value of firstproduct.high if firstproduct.low is maximal. // Fallback" (https://arxiv.org/abs/2212.06644), at this point we are sure that the product
if(simdjson_unlikely(firstproduct.low == 0xFFFFFFFFFFFFFFFF)) { // is sufficiently accurate, and more computation is not needed.
// This is very unlikely, but if so, we need to do much more work!
return false;
}
} }
uint64_t lower = firstproduct.low; uint64_t lower = firstproduct.low;
uint64_t upper = firstproduct.high; uint64_t upper = firstproduct.high;
@@ -128,6 +128,9 @@ 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 { 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); return get_root_value_iterator().get_root_string(true, allow_replacement);
} }
simdjson_inline error_code document::get_string(std::string& 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 { simdjson_inline simdjson_result<std::string_view> document::get_wobbly_string() noexcept {
return get_root_value_iterator().get_root_wobbly_string(true); return get_root_value_iterator().get_root_wobbly_string(true);
} }
@@ -397,6 +400,10 @@ simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLE
if (error()) { return error(); } if (error()) { return error(); }
return first.get_string(allow_replacement); return first.get_string(allow_replacement);
} }
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::get_string(std::string& 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 { simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::get_wobbly_string() noexcept {
if (error()) { return error(); } if (error()) { return error(); }
return first.get_wobbly_string(); return first.get_wobbly_string();
@@ -583,6 +590,7 @@ 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() 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<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); } 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); }
simdjson_inline error_code document_reference::get_string(std::string& 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<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<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); } simdjson_inline simdjson_result<bool> document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); }
@@ -719,6 +727,10 @@ simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLE
if (error()) { return error(); } if (error()) { return error(); }
return first.get_string(allow_replacement); return first.get_string(allow_replacement);
} }
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get_string(std::string& 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 { simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get_wobbly_string() noexcept {
if (error()) { return error(); } if (error()) { return error(); }
return first.get_wobbly_string(); return first.get_wobbly_string();
+25 -2
View File
@@ -100,6 +100,20 @@ public:
* @returns INCORRECT_TYPE if the JSON value is not a string. * @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; 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.
*/
simdjson_inline error_code get_string(std::string& receiver, bool allow_replacement = false) noexcept;
/** /**
* Cast this JSON value to a string. * Cast this JSON value to a string.
* *
@@ -160,13 +174,19 @@ public:
template<typename T> simdjson_inline simdjson_result<T> get() & noexcept { template<typename T> simdjson_inline simdjson_result<T> get() & noexcept {
// Unless the simdjson library provides an inline implementation, calling this method should // Unless the simdjson library provides an inline implementation, calling this method should
// immediately fail. // 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 */ /** @overload template<typename T> simdjson_result<T> get() & noexcept */
template<typename T> simdjson_inline simdjson_result<T> get() && noexcept { template<typename T> simdjson_inline simdjson_result<T> get() && noexcept {
// Unless the simdjson library provides an inline implementation, calling this method should // Unless the simdjson library provides an inline implementation, calling this method should
// immediately fail. // 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.");
} }
/** /**
@@ -618,6 +638,7 @@ public:
simdjson_inline simdjson_result<double> get_double() noexcept; simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() 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; simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
simdjson_inline error_code get_string(std::string& receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() 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<raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept; simdjson_inline simdjson_result<bool> get_bool() noexcept;
@@ -687,6 +708,7 @@ public:
simdjson_inline simdjson_result<double> get_double() noexcept; simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() 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; simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
simdjson_inline error_code get_string(std::string& receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() 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<SIMDJSON_IMPLEMENTATION::ondemand::raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept; simdjson_inline simdjson_result<bool> get_bool() noexcept;
@@ -759,6 +781,7 @@ public:
simdjson_inline simdjson_result<double> get_double() noexcept; simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() 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; simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
simdjson_inline error_code get_string(std::string& receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() 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<SIMDJSON_IMPLEMENTATION::ondemand::raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept; simdjson_inline simdjson_result<bool> get_bool() noexcept;
@@ -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(raw_json_string in, bool allow_replacement) noexcept;
simdjson_inline simdjson_result<std::string_view> unescape_wobbly(raw_json_string in) 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 void reenter_child(token_position position, depth_t child_depth) noexcept;
simdjson_inline error_code consume_character(char c) noexcept; simdjson_inline error_code consume_character(char c) noexcept;
@@ -68,6 +68,13 @@ simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(s
return iterate(padded_string_view(json, allocated)); 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 { simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const std::string &json) & noexcept {
return iterate(padded_string_view(json)); return iterate(padded_string_view(json));
} }
@@ -107,6 +107,8 @@ public:
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */ /** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const std::string &json) & noexcept; simdjson_warn_unused simdjson_result<document> iterate(const std::string &json) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view 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; simdjson_warn_unused simdjson_result<document> iterate(const simdjson_result<padded_string> &json) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view 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; simdjson_warn_unused simdjson_result<document> iterate(const simdjson_result<padded_string_view> &json) & noexcept;
@@ -47,6 +47,9 @@ 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 { simdjson_inline simdjson_result<std::string_view> value::get_string(bool allow_replacement) noexcept {
return iter.get_string(allow_replacement); return iter.get_string(allow_replacement);
} }
simdjson_inline error_code value::get_string(std::string& receiver, bool allow_replacement) noexcept {
return iter.get_string(receiver, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> value::get_wobbly_string() noexcept { simdjson_inline simdjson_result<std::string_view> value::get_wobbly_string() noexcept {
return iter.get_wobbly_string(); return iter.get_wobbly_string();
} }
@@ -319,6 +322,10 @@ simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLE
if (error()) { return error(); } if (error()) { return error(); }
return first.get_string(allow_replacement); return first.get_string(allow_replacement);
} }
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::get_string(std::string& 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 { simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::get_wobbly_string() noexcept {
if (error()) { return error(); } if (error()) { return error(); }
return first.get_wobbly_string(); return first.get_wobbly_string();
+19 -1
View File
@@ -38,7 +38,10 @@ public:
template<typename T> simdjson_inline simdjson_result<T> get() noexcept { template<typename T> simdjson_inline simdjson_result<T> get() noexcept {
// Unless the simdjson library provides an inline implementation, calling this method should // Unless the simdjson library provides an inline implementation, calling this method should
// immediately fail. // 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,20 @@ public:
*/ */
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept; 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.
*/
simdjson_inline error_code get_string(std::string& receiver, bool allow_replacement = false) noexcept;
/** /**
* Cast this JSON value to a "wobbly" string. * Cast this JSON value to a "wobbly" string.
@@ -602,6 +619,7 @@ public:
simdjson_inline simdjson_result<double> get_double() noexcept; simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() 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; simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
simdjson_inline error_code get_string(std::string& receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() 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<SIMDJSON_IMPLEMENTATION::ondemand::raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept; simdjson_inline simdjson_result<bool> get_bool() noexcept;
@@ -513,6 +513,13 @@ 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 { 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); return get_raw_json_string().unescape(json_iter(), allow_replacement);
} }
simdjson_warn_unused simdjson_inline error_code value_iterator::get_string(std::string& receiver, bool allow_replacement) noexcept {
std::string_view content;
auto err = get_string(allow_replacement).get(content);
if (err) { return err; }
receiver = std::string(content);
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_wobbly_string() noexcept { 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()); return get_raw_json_string().unescape_wobbly(json_iter());
} }
@@ -636,6 +643,13 @@ 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 { 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); return get_root_raw_json_string(check_trailing).unescape(json_iter(), allow_replacement);
} }
simdjson_warn_unused simdjson_inline error_code value_iterator::get_root_string(std::string& 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 = std::string(content);
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_root_wobbly_string(bool check_trailing) noexcept { 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()); return get_root_raw_json_string(check_trailing).unescape_wobbly(json_iter());
} }
@@ -296,6 +296,7 @@ public:
*/ */
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement) noexcept; simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement) noexcept;
simdjson_warn_unused simdjson_inline error_code get_string(std::string& 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<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<raw_json_string> get_raw_json_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept; simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
@@ -312,7 +313,8 @@ public:
simdjson_warn_unused simdjson_inline simdjson_result<number> get_number() noexcept; 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_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; simdjson_warn_unused simdjson_inline error_code get_root_string(std::string& 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<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(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_root_uint64_in_string(bool check_trailing) noexcept; simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_root_uint64_in_string(bool check_trailing) noexcept;
@@ -16,7 +16,7 @@ using namespace simd;
// Holds backslashes and quotes locations. // Holds backslashes and quotes locations.
struct backslash_and_quote { struct backslash_and_quote {
public: public:
static constexpr uint32_t BYTES_PROCESSED = 32; static constexpr uint32_t BYTES_PROCESSED = 64;
simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; } simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; }
+2 -5
View File
@@ -58,11 +58,8 @@
#if SIMDJSON_IS_32BITS #if SIMDJSON_IS_32BITS
#ifndef SIMDJSON_NO_PORTABILITY_WARNING #ifndef SIMDJSON_NO_PORTABILITY_WARNING
#pragma message("The simdjson library is designed \ // In the future, we should allow programmers
for 64-bit processors and it seems that you are not \ // to get warning.
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.")
#endif // SIMDJSON_NO_PORTABILITY_WARNING #endif // SIMDJSON_NO_PORTABILITY_WARNING
#endif // SIMDJSON_IS_32BITS #endif // SIMDJSON_IS_32BITS
+3 -3
View File
@@ -4,7 +4,7 @@
#define SIMDJSON_SIMDJSON_VERSION_H #define SIMDJSON_SIMDJSON_VERSION_H
/** The version of simdjson being used (major.minor.revision) */ /** The version of simdjson being used (major.minor.revision) */
#define SIMDJSON_VERSION "3.2.2" #define SIMDJSON_VERSION "3.4.0"
namespace simdjson { namespace simdjson {
enum { enum {
@@ -15,11 +15,11 @@ enum {
/** /**
* The minor version (major.MINOR.revision) of simdjson being used. * The minor version (major.MINOR.revision) of simdjson being used.
*/ */
SIMDJSON_VERSION_MINOR = 2, SIMDJSON_VERSION_MINOR = 4,
/** /**
* The revision (major.minor.REVISION) of simdjson being used. * The revision (major.minor.REVISION) of simdjson being used.
*/ */
SIMDJSON_VERSION_REVISION = 2 SIMDJSON_VERSION_REVISION = 0
}; };
} // namespace simdjson } // namespace simdjson
+409 -506
View File
File diff suppressed because it is too large Load Diff
+607 -84
View File
File diff suppressed because it is too large Load Diff
+67 -77
View File
@@ -27,6 +27,58 @@ public:
simdjson_inline bit_indexer(uint32_t *index_buf) : tail(index_buf) {} 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 // 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 // plus their position in the bitvector, and store these indexes at
// base_ptr[base] incrementing base as we go // base_ptr[base] incrementing base as we go
@@ -44,91 +96,29 @@ public:
// it helps tremendously. // it helps tremendously.
if (bits == 0) if (bits == 0)
return; 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 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 write_indexes_stepped<0, STEP_UNTIL, STEP>(idx, bits, cnt);
// branch mispredictions here. 16+ structurals per block means either punctuation ({} [] , :) SIMDJSON_IF_CONSTEXPR (STEP_UNTIL < 64) {
// or the start of a value ("abc" true 123) every four characters. if (simdjson_unlikely(STEP_UNTIL < cnt)) {
if (simdjson_unlikely(cnt > 16)) { for (int i=STEP_UNTIL; i<cnt; i++) {
i = 16; write_index(idx, bits, i);
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);
} }
} }
} }
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; this->tail += cnt;
#endif
} }
#endif // SIMDJSON_GENERIC_JSON_STRUCTURAL_INDEXER_CUSTOM_BIT_INDEXER #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. // Clear CUSTOM_BIT_INDEXER so other implementations can set it if they need to.
#undef SIMDJSON_GENERIC_JSON_STRUCTURAL_INDEXER_CUSTOM_BIT_INDEXER #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; 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) { simdjson_inline void check_next_input(const simd8x64<uint8_t>& input) {
if(simdjson_likely(is_ascii(input))) { if(simdjson_likely(is_ascii(input))) {
this->error |= this->prev_incomplete; this->error |= this->prev_incomplete;
+7 -5
View File
@@ -303,15 +303,17 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::visit_root_primit
} }
template<typename V> template<typename V>
simdjson_warn_unused simdjson_inline error_code json_iterator::visit_primitive(V &visitor, const uint8_t *value) noexcept { 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) { switch (*value) {
case '"': return visitor.visit_string(*this, value);
case 't': return visitor.visit_true_atom(*this, value); case 't': return visitor.visit_true_atom(*this, value);
case 'f': return visitor.visit_false_atom(*this, value); case 'f': return visitor.visit_false_atom(*this, value);
case 'n': return visitor.visit_null_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: default:
log_error("Non-value found when value was expected!"); log_error("Non-value found when value was expected!");
return TAPE_ERROR; return TAPE_ERROR;
+9 -1
View File
@@ -51,6 +51,10 @@ namespace document_tests {
simdjson::dom::array array; simdjson::dom::array array;
ASSERT_SUCCESS( parser.parse(smalljson).get(array) ); ASSERT_SUCCESS( parser.parse(smalljson).get(array) );
ASSERT_EQUAL( array.size(), 3 ); 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; return true;
} }
bool count_object_example() { bool count_object_example() {
@@ -60,6 +64,10 @@ namespace document_tests {
simdjson::dom::object object; simdjson::dom::object object;
ASSERT_SUCCESS( parser.parse(smalljson).get(object) ); ASSERT_SUCCESS( parser.parse(smalljson).get(object) );
ASSERT_EQUAL( object.size(), 3 ); 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; return true;
} }
bool padded_with_open_bracket() { bool padded_with_open_bracket() {
@@ -232,4 +240,4 @@ int main(int argc, char *argv[]) {
} else { } else {
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
+1 -1
View File
@@ -32,7 +32,7 @@ static bool parse_and_validate(const std::string src, T expected) {
const padded_string pstr{src}; const padded_string pstr{src};
simdjson::dom::parser parser; 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{}; int64_t actual{};
ASSERT_SUCCESS( parser.parse(pstr)["key"].get(actual) ); ASSERT_SUCCESS( parser.parse(pstr)["key"].get(actual) );
std::cout << std::boolalpha << "test: " << (expected == actual) << std::endl; std::cout << std::boolalpha << "test: " << (expected == actual) << std::endl;
+1
View File
@@ -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_parse_api_tests LABELS ondemand acceptance per_implementation)
add_cpp_test(ondemand_readme_examples 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_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_twitter_tests LABELS ondemand acceptance per_implementation)
add_cpp_test(ondemand_wrong_type_error_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) add_cpp_test(ondemand_iterate_many_csv LABELS ondemand acceptance per_implementation)
+9 -3
View File
@@ -144,14 +144,20 @@ namespace parse_api_tests {
} }
{ {
std::string json = "12"; std::string json = "12345642314123421321321321321321312321321321321312";
json.shrink_to_fit(); json.shrink_to_fit();
cout << "- string, 0 padding" << endl; 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 // It's actually kind of hard to allocate "just enough" capacity, since the string tends
// to grow more than you tell it to. // to grow more than you tell it to.
} }
TEST_SUCCEED(); TEST_SUCCEED();
} }
+174
View File
@@ -20,7 +20,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 #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() { bool gen_raw1() {
TEST_START(); TEST_START();
simdjson::ondemand::parser parser; simdjson::ondemand::parser parser;
@@ -73,6 +105,144 @@ bool at_end() {
TEST_SUCCEED(); 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() { bool number_tests() {
TEST_START(); TEST_START();
ondemand::parser parser; ondemand::parser parser;
@@ -1352,10 +1522,14 @@ bool run() {
&& current_location_user_error() && current_location_user_error()
&& current_location_out_of_bounds() && current_location_out_of_bounds()
&& current_location_no_error() && current_location_no_error()
&& to_string_example_no_except()
#if SIMDJSON_EXCEPTIONS #if SIMDJSON_EXCEPTIONS
&& to_string_example()
&& raw_string() && raw_string()
&& number_tests() && number_tests()
&& current_location_tape_error_with_except() && current_location_tape_error_with_except()
&& examplecrt()
&& examplecrt_realloc()
#endif #endif
; ;
} }
+210
View File
@@ -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);
}
-2
View File
@@ -167,8 +167,6 @@ print("the commandline is {}".format(cp.args))
if(cp.returncode != 0): if(cp.returncode != 0):
print("Failed to run doxygen") 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") pattern = re.compile("https://simdjson.org/api/(\d+\.\d+\.\d+)/index.html")
readmefile = maindir + os.sep + "README.md" readmefile = maindir + os.sep + "README.md"