Compare commits

...

23 Commits

Author SHA1 Message Date
Daniel Lemire e510ed2774 Release candidate (3.7.0) (#2136)
* Release candidate (3.7.0)

* some extra comments.
2024-02-23 16:50:55 -05:00
Dirk Stolle dabac13dde Add missing word in documentation (#2133) 2024-02-18 11:50:12 -05:00
Daniel Lemire aef7111c05 Documenting Unicode support in JSONPath and JSON Pointer (#2132)
* Documenting our support of JSON Path and JSON Pointer vs. Unicode characters

* using more standard terminology (nitpicking)

* specifying UTF-8 encoding

* allow char8_t when compiling as C++20

* casting

* minor doc corrections
2024-02-15 20:28:38 -05:00
Daniel Lemire 0c08894a54 adopting string literals (#2128)
* adopting string literals

* String suffixes are C++14
2024-02-15 10:40:11 -05:00
Daniel Lemire 2fe3a1467d This PR adds a convenience method (is_string) as well as a new method (#2131)
key_raw_json_token() to get the raw key token.
2024-02-15 10:39:30 -05:00
Daniel Lemire 167a46a0e7 adding remark 2024-02-15 09:56:05 -05:00
Francisco Geiman Thiesen 2b532489cc Adding on-demand support for a subset of json path. (#2127)
* First implementation of the conversion logic.

- Not yet tested
- Not yet compiled
- Not yet used in ondemand array/object/etc...

* Adding at_path to ondemand arrays

* Adding at_path to array, document and value.

* Adding at_path to ondemand object as well.

Pending:
- Building
- Adding tests for each of the on-demand classes usage of at_path
- Properly documenting the subset of json path that is currently supported.

* Adding a simple json_path test to on_demand

* Still trying to compile the ondemand_readme_examples test with the at_path() call

* Fixing linking issues with array::at_path

* Fixing issues with the path -> pointer conversion and removing comments

* Adding a clone of json_pointer to test json_path extensively

* Adding ondemand_json_path_tests (all tests passing)

* Adding documentation for at_path()

* Removing some newlines

* Removing simdjson_result from the return type of the string conversion function. Now json_path_to_pointer_conversion returns a std::string.

* Fixing typo

* Making string concatenation explicit to avoid ubuntu gcc12 issue with -O3 flag.

* Addressing latest reviews

* Addressing latest reviews
2024-02-15 09:47:25 -05:00
Daniel Lemire 85163df88c Update ondemand_design.md 2024-02-14 23:36:25 -05:00
Daniel Lemire 6a5be0f0db Create SECURITY.md 2024-02-09 17:16:54 -05:00
Daniel Lemire 710a3c7241 Update implementation-selection.md 2024-02-09 11:46:22 -05:00
Daniel Lemire c93e02e172 Update implementation-selection.md 2024-02-09 11:45:15 -05:00
Daniel Lemire 517143aec4 Update implementation-selection.md 2024-02-09 11:36:38 -05:00
Francisco Geiman Thiesen 9feda5e703 Update HACKING.md (#2122)
Updating windows command
2024-02-09 10:29:07 -05:00
Daniel Lemire cda198b7b9 Update ondemand_design.md 2024-02-09 10:22:44 -05:00
Daniel Lemire aad17a6a8d Update basics.md 2024-02-09 10:20:42 -05:00
Daniel Lemire 32d072a208 fixing issue2120 (#2121) 2024-02-08 11:34:36 -05:00
Daniel Lemire 5eab555053 documenting the support for custom types (#2098)
* documenting the support for custom types

* fixes

* getting around -Weffc++

* calling get_raw_json_string()

* documenting the support for custom types

* fixes

* getting around -Weffc++

* calling get_raw_json_string()
2024-02-06 13:20:35 -05:00
Daniel Lemire 6430fbd39a Better documentation and support for the case where you have a single implementation. (#2118) 2024-02-06 13:20:23 -05:00
Daniel Lemire 4d2fc31b27 removes simdjson.cpp include from the amalgated demo (#2119)
Co-authored-by: Daniel Lemire <dlemire@lemire.me>
2024-02-05 19:34:50 -05:00
Bo Anderson 33610b59eb Allow some iterators to be used in standard C++ iterator functions (#2106) 2024-02-05 17:52:01 -05:00
Daniel Lemire 23290ae48d retiring deprecated DOM APIs (#2109) 2024-02-04 21:58:48 -05:00
Daniel Lemire eb3a085e0f Bash check is unneeded. (#2112) 2024-02-04 21:58:36 -05:00
Daniel Lemire 4c8ec35a48 fixing the order of the apple counters 2024-01-31 14:30:44 -05:00
77 changed files with 8452 additions and 6807 deletions
+5 -1
View File
@@ -45,12 +45,16 @@ It is fine to report bugs against our main branch, but if that is what you are d
**Configuration (please complete the following information if relevant)**
- OS: [e.g. Ubuntu 16.04.6 LTS]
- Compiler [e.g. Apple clang version 11.0.3 (clang-1103.0.32.59) x86_64-apple-darwin19.4.0]
- Compiler* [e.g. Apple clang version 11.0.3 (clang-1103.0.32.59) x86_64-apple-darwin19.4.0]
- Version [e.g. 22]
- Optimization setting (e.g., -O3)
We support up-to-date 64-bit ARM and x64 FreeBSD, macOS, Windows and Linux systems. Please ensure that your configuration is supported before labelling the issue as a bug.
* We do not support unreleased or experimental compilers. If you encounter an issue with a
pre-release version of a compiler, do not report it as a bug to simdjson. However, we always
invite contributions either in the form an analysis or of a code contribution.
**Indicate whether you are willing or able to provide a bug fix as a pull request**
If you plan to contribute to simdjson, please read our guide:
+6 -1
View File
@@ -98,6 +98,11 @@
"queue": "cpp",
"shared_mutex": "cpp",
"ranges": "cpp",
"span": "cpp"
"span": "cpp",
"__verbose_abort": "cpp",
"charconv": "cpp",
"source_location": "cpp",
"strstream": "cpp",
"typeindex": "cpp"
}
}
+3 -3
View File
@@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.14)
project(
simdjson
# The version number is modified by tools/release.py
VERSION 3.6.4
VERSION 3.7.0
DESCRIPTION "Parsing gigabytes of JSON per second"
HOMEPAGE_URL "https://simdjson.org/"
LANGUAGES CXX C
@@ -20,8 +20,8 @@ string(
# ---- Options, variables ----
# These version numbers are modified by tools/release.py
set(SIMDJSON_LIB_VERSION "19.0.0" CACHE STRING "simdjson library version")
set(SIMDJSON_LIB_SOVERSION "19" CACHE STRING "simdjson library soversion")
set(SIMDJSON_LIB_VERSION "20.0.0" CACHE STRING "simdjson library version")
set(SIMDJSON_LIB_SOVERSION "20" CACHE STRING "simdjson library soversion")
option(SIMDJSON_BUILD_STATIC_LIB "Build simdjson_static library along with simdjson" OFF)
+2 -1
View File
@@ -52,10 +52,11 @@ General Guidelines
Contributors are encouraged to :
- Document their changes. Though we do not enforce a rule regarding code comments, we prefer that non-trivial algorithms and techniques be somewhat documented in the code.
- Follow as much as possible the existing code style. We do not enforce a specific code style, but we prefer consistency.
- Follow as much as possible the existing code style. We do not enforce a specific code style, but we prefer consistency. We avoid contractions (isn't, aren't) in the comments.
- Modify as few lines of code as possible when working on an issue. The more lines you modify, the harder it is for your fellow human beings to understand what is going on.
- Tools may report "problems" with the code, but we never delegate programming to tools: if there is a problem with the code, we need to understand it. Thus we will not "fix" code merely to please a static analyzer.
- Provide tests for any new feature. We will not merge a new feature without tests.
- Run before/after benchmarks so that we can appreciate the effect of the changes on the performance.
Pull Requests
--------------
+1 -1
View File
@@ -38,7 +38,7 @@ PROJECT_NAME = simdjson
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = "3.6.4"
PROJECT_NUMBER = "3.7.0"
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
+3 -3
View File
@@ -80,7 +80,7 @@ simdjson's source structure, from the top level, looks like this:
* simdjson/internal/*.h: the `simdjson::internal` namespace. Private classes and functions used by the rest of simdjson.
* simdjson/dom.h: the `simdjson::dom` namespace. Includes all public DOM classes.
* simdjson/dom/*.h: Declarations/definitions for individual DOM classes.
* simdjson/arm64|fallback|haswell|icelake|ppc64|westmere.h: `simdjson::<implementation>` namesapce. Common implementation-specific tools like number and string parsing, as well as minification.
* simdjson/arm64|fallback|haswell|icelake|ppc64|westmere.h: `simdjson::<implementation>` namespace. Common implementation-specific tools like number and string parsing, as well as minification.
* simdjson/arm64|fallback|haswell|icelake|ppc64|westmere/*.h: implementation-specific functions such as , etc.
* simdjson/generic/*.h: the bulk of the actual code, written generically and compiled for each implementation, using functions defined in the implementation's .h files.
* simdjson/generic/dependencies.h: dependencies on common, non-implementation-specific simdjson classes. This will be included before including amalgamated.h.
@@ -304,7 +304,7 @@ Though having Visual Studio installed is necessary, one can build simdjson using
- `mkdir build`
- `cd build`
- `cmake ..`
- `cmake --build . -config Release`
- `cmake --build . --config Release`
Furthermore, if you have installed LLVM clang on Windows, for example as a component of Visual Studio 2019, you can configure and build simdjson using LLVM clang on Windows using cmake:
@@ -313,7 +313,7 @@ Furthermore, if you have installed LLVM clang on Windows, for example as a compo
- `mkdir build`
- `cd build`
- `cmake -T ClangCL ..`
- `cmake --build . -config Release`
- `cmake --build . --config Release`
## Various References
+1 -1
View File
@@ -168,7 +168,7 @@ We distinguish between "bindings" (which just wrap the C++ code) and a port to a
- [simdjzon](https://github.com/travisstaloch/simdjzon): zig port.
- [JSON-Simd](https://github.com/rawleyfowler/JSON-simd): Raku bindings.
- [JSON::SIMD](https://metacpan.org/pod/JSON::SIMD): Perl bindings; fully-featured JSON module that uses simdjson for decoding.
- [gemmaJSON](https://github.com/sainttttt/gemmaJSON): Nim json parser based on simdjson bindings.
- [gemmaJSON](https://github.com/sainttttt/gemmaJSON): Nim JSON parser based on simdjson bindings.
About simdjson
--------------
+7
View File
@@ -0,0 +1,7 @@
# Security Policy
## Reporting a Vulnerability
Please use the following contact information for reporting a vulnerability:
- [Daniel Lemire](https://github.com/lemire) - daniel@lemire.me
+2 -2
View File
@@ -1100,8 +1100,8 @@ struct AppleEvents {
return 1;
}
return performance_counters {
counters_0[counter_map[0]], counters_0[counter_map[3]],
counters_0[counter_map[2]], counters_0[counter_map[1]]};
counters_0[counter_map[0]], counters_0[counter_map[2]],
counters_0[counter_map[3]], counters_0[counter_map[1]]};
}
};
-158
View File
@@ -473,28 +473,6 @@ static void twitter_count(State& state) {
}
BENCHMARK(twitter_count);
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_DEPRECATED_WARNING
static void iterator_twitter_count(State& state) {
// Prints the number of results in twitter.json
padded_string json = padded_string::load(TWITTER_JSON);
ParsedJson pj = build_parsed_json(json);
for (simdjson_unused auto _ : state) {
ParsedJson::Iterator iter(pj);
// uint64_t result_count = doc["search_metadata"]["count"];
if (!iter.move_to_key("search_metadata")) { return; }
if (!iter.move_to_key("count")) { return; }
if (!iter.is_integer()) { return; }
int64_t result_count = iter.get_integer();
if (result_count != 100) { return; }
}
}
BENCHMARK(iterator_twitter_count);
SIMDJSON_POP_DISABLE_WARNINGS
#endif // SIMDJSON_DISABLE_DEPRECATED_API
static void twitter_default_profile(State& state) {
// Count unique users with a default profile.
dom::parser parser;
@@ -578,54 +556,6 @@ static void error_code_twitter_default_profile(State& state) noexcept {
}
BENCHMARK(error_code_twitter_default_profile);
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_DEPRECATED_WARNING
static void iterator_twitter_default_profile(State& state) {
// Count unique users with a default profile.
padded_string json;
auto error = padded_string::load(TWITTER_JSON).get(json);
if (error) { std::cerr << error << std::endl; return; }
ParsedJson pj = build_parsed_json(json);
for (simdjson_unused auto _ : state) {
set<string_view> default_users;
ParsedJson::Iterator iter(pj);
// for (dom::object tweet : doc["statuses"]) {
if (!(iter.move_to_key("statuses") && iter.is_array())) { return; }
if (iter.down()) { // first status
do {
// dom::object user = tweet["user"];
if (!(iter.move_to_key("user") && iter.is_object())) { return; }
// if (user["default_profile"]) {
if (iter.move_to_key("default_profile")) {
if (iter.is_true()) {
if (!iter.up()) { return; } // back to user
// default_users.insert(user["screen_name"]);
if (!(iter.move_to_key("screen_name") && iter.is_string())) { return; }
default_users.emplace(iter.get_string(), iter.get_string_length());
}
if (!iter.up()) { return; } // back to user
}
if (!iter.up()) { return; } // back to status
} while (iter.next()); // next status
}
if (default_users.size() != 86) { return; }
}
}
SIMDJSON_POP_DISABLE_WARNINGS
BENCHMARK(iterator_twitter_default_profile);
#endif // SIMDJSON_DISABLE_DEPRECATED_API
static void error_code_twitter_image_sizes(State& state) noexcept {
// Count unique image sizes
dom::parser parser;
@@ -680,92 +610,4 @@ static void parse_surrogate_pairs(State& state) {
}
BENCHMARK(parse_surrogate_pairs);
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_DEPRECATED_WARNING
static void iterator_twitter_image_sizes(State& state) {
// Count unique image sizes
padded_string json;
auto error = padded_string::load(TWITTER_JSON).get(json);
if (error) { std::cerr << error << std::endl; return; }
ParsedJson pj = build_parsed_json(json);
for (simdjson_unused auto _ : state) {
set<tuple<uint64_t, uint64_t>> image_sizes;
ParsedJson::Iterator iter(pj);
// for (dom::object tweet : doc["statuses"]) {
if (!(iter.move_to_key("statuses") && iter.is_array())) { return; }
if (iter.down()) { // first status
do {
// dom::object media;
// not_found = tweet["entities"]["media"].get(media);
// if (!not_found) {
if (iter.move_to_key("entities")) {
if (!iter.is_object()) { return; }
if (iter.move_to_key("media")) {
if (!iter.is_array()) { return; }
// for (dom::object image : media) {
if (iter.down()) { // first media
do {
// for (auto [key, size] : dom::object(image["sizes"])) {
if (!(iter.move_to_key("sizes") && iter.is_object())) { return; }
if (iter.down()) { // first size
do {
iter.move_to_value();
// image_sizes.insert({ size["w"], size["h"] });
if (!(iter.move_to_key("w")) && !iter.is_integer()) { return; }
uint64_t width = iter.get_integer();
if (!iter.up()) { return; } // back to size
if (!(iter.move_to_key("h")) && !iter.is_integer()) { return; }
uint64_t height = iter.get_integer();
if (!iter.up()) { return; } // back to size
image_sizes.emplace(width, height);
} while (iter.next()); // next size
if (!iter.up()) { return; } // back to sizes
}
if (!iter.up()) { return; } // back to image
} while (iter.next()); // next image
if (!iter.up()) { return; } // back to media
}
if (!iter.up()) { return; } // back to entities
}
if (!iter.up()) { return; } // back to status
}
} while (iter.next()); // next status
}
if (image_sizes.size() != 15) { return; };
}
}
BENCHMARK(iterator_twitter_image_sizes);
#endif // SIMDJSON_DISABLE_DEPRECATED_API
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
static void print_json(State& state) noexcept {
// Prints the number of results in twitter.json
dom::parser parser;
padded_string json;
auto error = padded_string::load(TWITTER_JSON).get(json);
if (error) { std::cerr << error << std::endl; return; }
int code = json_parse(json, parser);
if (code) { cerr << error_message(code) << endl; return; }
for (simdjson_unused auto _ : state) {
std::stringstream s;
if (!parser.print_json(s)) { cerr << "print_json failed" << endl; return; }
}
}
BENCHMARK(print_json);
#endif // SIMDJSON_DISABLE_DEPRECATED_API
SIMDJSON_POP_DISABLE_WARNINGS
BENCHMARK_MAIN();
-30
View File
@@ -169,22 +169,6 @@ BENCHMARK(parse_gsoc)->Repetitions(10)->ComputeStatistics("max", [](const std::v
})->DisplayAggregatesOnly(true);
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_DEPRECATED_WARNING
static void json_parse(State& state) {
ParsedJson pj;
if (!pj.allocate_capacity(EMPTY_ARRAY.length())) { return; }
for (simdjson_unused auto _ : state) {
auto error = json_parse(EMPTY_ARRAY, pj);
if (error) { return; }
}
}
SIMDJSON_POP_DISABLE_WARNINGS
BENCHMARK(json_parse);
#endif // SIMDJSON_DISABLE_DEPRECATED_API
static void parser_parse_error_code(State& state) {
dom::parser parser;
if (parser.allocate(EMPTY_ARRAY.length())) { return; }
@@ -213,20 +197,6 @@ BENCHMARK(parser_parse_exception);
#endif // SIMDJSON_EXCEPTIONS
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_DEPRECATED_WARNING
static void build_parsed_json(State& state) {
for (simdjson_unused auto _ : state) {
dom::parser parser = simdjson::build_parsed_json(EMPTY_ARRAY);
if (!parser.valid) { return; }
}
}
SIMDJSON_POP_DISABLE_WARNINGS
BENCHMARK(build_parsed_json);
#endif
static void document_parse_error_code(State& state) {
for (simdjson_unused auto _ : state) {
dom::parser parser;
+1 -1
View File
@@ -24,7 +24,7 @@ struct file_runner : public runner_base<I> {
simdjson_warn_unused bool before_run(benchmark::State &state) {
if (!runner_base<I>::after_run(state)) { return false; };
// Copy the original json in case we did *in situ* last time
// Copy the original JSON in case we did *in situ* last time
std::memcpy(json.data(), original_json.data(), original_json.size());
return true;
}
+1 -1
View File
@@ -13,7 +13,7 @@ struct string_runner : public runner_base<I> {
simdjson_warn_unused bool before_run(benchmark::State &state) {
if (!runner_base<I>::after_run(state)) { return false; };
// Copy the original json in case we did *in situ*
// Copy the original JSON in case we did *in situ*
std::memcpy(json.data(), original_json.data(), original_json.size());
return true;
}
+5 -1
View File
@@ -104,7 +104,11 @@ int main() {}
target_compile_definitions(rapidjson INTERFACE RAPIDJSON_HAS_STDSTRING)
target_include_directories(rapidjson SYSTEM INTERFACE
"${rapidjson_SOURCE_DIR}/include")
target_compile_definitions(rapidjson INTERFACE SIMDJSON_COMPETITION_RAPIDJSON)
if (CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 14)
message(STATUS "Disabling rapidjson")
else()
target_compile_definitions(rapidjson INTERFACE SIMDJSON_COMPETITION_RAPIDJSON)
endif()
import_dependency(sajson chadaustin/sajson 2dcfd35)
add_library(sajson INTERFACE)
+400 -14
View File
@@ -15,9 +15,11 @@ An overview of what you need to know to use simdjson, with examples.
- [string_view](#string_view)
- [Using the Parsed JSON](#using-the-parsed-json)
- [Using the Parsed JSON: Additional examples](#using-the-parsed-json-additional-examples)
- [Adding support for custom types](#adding-support-for-custom-types)
- [Minifying JSON strings without parsing](#minifying-json-strings-without-parsing)
- [UTF-8 validation (alone)](#utf-8-validation-alone)
- [JSON Pointer](#json-pointer)
- [JSONPath](#json-path)
- [Error Handling](#error-handling)
- [Error Handling Examples without Exceptions](#error-handling-examples-without-exceptions)
- [Disabling Exceptions](#disabling-exceptions)
@@ -28,7 +30,7 @@ An overview of what you need to know to use simdjson, with examples.
- [Newline-Delimited JSON (ndjson) and JSON lines](#newline-delimited-json-ndjson-and-json-lines)
- [Parsing Numbers Inside Strings](#parsing-numbers-inside-strings)
- [Dynamic Number Types](#dynamic-number-types)
- [Raw Strings](#raw-strings)
- [Raw Strings From Keys](#raw-strings-from-keys)
- [General Direct Access to the Raw JSON String](#general-direct-access-to-the-raw-json-string)
- [Storing Directly into an Existing String Instance](#storing-directly-into-an-existing-string-instance)
- [Thread Safety](#thread-safety)
@@ -69,7 +71,7 @@ c++ myproject.cpp simdjson.cpp
Note:
- Users on macOS and other platforms where default compilers do not provide C++11 compliant by default should request it with the appropriate flag (e.g., `c++ -std=c++11 myproject.cpp simdjson.cpp`).
- The library relies on [runtime CPU detection](implementation-selection.md): avoid specifying an architecture at compile time (e.g., `-march-native`).
- The library relies on [runtime CPU detection](implementation-selection.md): avoid specifying an architecture at compile time (e.g., `-march-native`) if you want your binaries to run everywhere.
Using simdjson with package managers
------------------
@@ -221,7 +223,7 @@ walking through the original JSON text, merrily reading commas and colons and br
you get where you are going. This is the key to On Demand's performance: since it's just an iterator,
it lets you parse values as you use them. And particularly, it lets you *skip* values you do not want
to use. On Demand is also ideally suited when you want to capture part of the document without parsing it
immediately (e.g., see [Raw Strings](#raw-strings)).
immediately (e.g., see [General Direct Access to the Raw JSON String](#general-direct-access-to-the-raw-json-string)).
We refer to "On Demand" as a front-end component since it is an interface between the
low-level parsing functions and the user. It hides much of the complexity of parsing JSON
@@ -256,7 +258,7 @@ copy the data into their own favorite class instances (e.g., alternatives to `st
A `std::string_view` instance is effectively just a pointer to a region in memory representing
a string. In simdjson, we return `std::string_view` instances that either point within the
input string you parsed (when using [raw Strings](#raw-strings)), or to a temporary string buffer inside
input string you parsed (see [General Direct Access to the Raw JSON String](#general-direct-access-to-the-raw-json-string)), or to a temporary string buffer inside
our parser class instances that is valid until the parser object is destroyed or you use it to parse another document.
When using `std::string_view` instances, it is your responsibility to ensure that
`std::string_view` instance does not outlive the pointed-to memory (e.g., either the input
@@ -424,8 +426,8 @@ support for users who avoid exceptions. See [the simdjson error handling documen
If you know the type of the value, you can cast it right there, too! `for (double value : array) { ... }`.
You may also use explicit iterators: `for(auto i = array.begin(); i != array.end(); i++) {}`. You can check that an array is empty with the condition `auto i = array.begin(); if(i == array.end()) {...}`.
* **Object Iteration:** You can iterate through an object's fields, as well: `for (auto field : object) { ... }`. You may also use explicit iterators : `for(auto i = object.begin(); i != object.end(); i++) { auto field = *i; .... }`. You can check that an object is empty with the condition `auto i = object.begin(); if(i == object.end()) {...}`.
You may also use explicit iterators: `for(auto i = array.begin(); i != array.end(); i++) {}`. You can check that an array is empty with the condition `auto i = array.begin(); if (i == array.end()) {...}`.
* **Object Iteration:** You can iterate through an object's fields, as well: `for (auto field : object) { ... }`. You may also use explicit iterators : `for(auto i = object.begin(); i != object.end(); i++) { auto field = *i; .... }`. You can check that an object is empty with the condition `auto i = object.begin(); if (i == object.end()) {...}`.
- `field.unescaped_key()` will get you the unescaped key string. E.g., the JSON string `"\u00e1"` becomes the Unicode string `á`. Optionally, you pass `true` as a parameter to the `unescaped_key` method if you want invalid escape sequences to be replaced by a default replacement character (e.g., `\ud800\ud801\ud811`): otherwise bad escape sequences lead to an immediate error.
- `field.value()` will get you the value, which you can then use all these other methods on.
* **Array Index:** Because it is forward-only, you cannot look up an array element by index by index. Instead,
@@ -555,7 +557,7 @@ support for users who avoid exceptions. See [the simdjson error handling documen
- null (`json_type::null`).
You must still validate and consume the values (e.g., call `is_null()`) after calling `type()`.
You may also access [raw strings](#raw-strings).
You may also access [the raw JSON string](#general-direct-access-to-the-raw-json-string).
For example, the following is a quick and dirty recursive function that verbosely prints the JSON document as JSON. This example also illustrates lifecycle requirements: the `document` instance holds the iterator. The document must remain in scope while you are accessing instances of `value`, `object` and `array`.
```c++
void recursive_print_json(ondemand::value element) {
@@ -739,6 +741,289 @@ for (ondemand::object points : parser.iterate(points_json)) {
}
```
Adding support for custom types
----------------------
Suppose you have your own types, such as a `Car` struct:
```C++
struct Car {
std::string make;
std::string model;
int64_t year;
std::vector<double> tire_pressure;
};
```
You might want to write code that automatically parses the JSON content to your custom
type:
```C++
padded_string json = R"( [ { "make": "Toyota", "model": "Camry", "year": 2018,
"tire_pressure": [ 40.1, 39.9 ] },
{ "make": "Kia", "model": "Soul", "year": 2012,
"tire_pressure": [ 30.1, 31.0 ] },
{ "make": "Toyota", "model": "Tercel", "year": 1999,
"tire_pressure": [ 29.8, 30.0 ] }
])"_padded;
ondemand::parser parser;
ondemand::document doc = parser.iterate(json);
for (auto val : doc) {
Car c(val);
std::cout << c.make << std::endl;
}
```
We may do so by providing additional template definitions to the `ondemand::value` type.
We may start by providing a definition for `std::vector<double>` as follows:
```c++
template <>
simdjson_inline simdjson_result<std::vector<double>>
simdjson::ondemand::value::get() noexcept {
ondemand::array array;
auto error = get_array().get(array);
if (error) { return error; }
std::vector<double> vec;
for (auto v : array) {
double val;
error = v.get_double().get(val);
if (error) { return error; }
vec.push_back(val);
}
return vec;
}
```
We may then provide support for our `Car`` struct:
```C++
template <>
simdjson_inline simdjson_result<Car> simdjson::ondemand::value::get() noexcept {
ondemand::object obj;
auto error = get_object().get(obj);
if (error) { return error; }
Car car;
// Instead of repeatedly obj["something"], we iterate through the object which
// we expect to be faster.
for (auto field : obj) {
raw_json_string key;
error = field.key().get(key);
if (error) { return error; }
if (key == "make") {
error = field.value().get_string(car.make);
if (error) { return error; }
} else if (key == "model") {
error = field.value().get_string(car.model);
if (error) { return error; }
} else if (key == "year") {
error = field.value().get_int64().get(car.year);
if (error) { return error; }
} else if (key == "tire_pressure") {
error = field.value().get<std::vector<double>>().get(car.tire_pressure);
if (auto error) { return error; }
}
}
return car;
}
```
And that is all that is needed! The following code is a complete example:
```c++
#include "simdjson.h"
#include <iostream>
#include <vector>
using namespace simdjson;
/**
* A custom type that we want to parse.
*/
struct Car {
std::string make;
std::string model;
int64_t year;
std::vector<double> tire_pressure;
};
template <>
simdjson_inline simdjson_result<std::vector<double>>
simdjson::ondemand::value::get() noexcept {
ondemand::array array;
auto error = get_array().get(array);
if (error) { return error; }
std::vector<double> vec;
for (auto v : array) {
double val;
error = v.get_double().get(val);
if (error) { return error; }
vec.push_back(val);
}
return vec;
}
template <>
simdjson_inline simdjson_result<Car> simdjson::ondemand::value::get() noexcept {
ondemand::object obj;
auto error = get_object().get(obj);
if (error) { return error; }
Car car;
// Instead of repeatedly obj["something"], we iterate through the object which
// we expect to be faster.
for (auto field : obj) {
raw_json_string key;
error = field.key().get(key);
if (error) { return error; }
if (key == "make") {
error = field.value().get_string(car.make);
if (error) { return error; }
} else if (key == "model") {
error = field.value().get_string(car.model);
if (error) { return error; }
} else if (key == "year") {
error = field.value().get_int64().get(car.year);
if (error) { return error; }
} else if (key == "tire_pressure") {
error = field.value().get<std::vector<double>>().get(car.tire_pressure);
if (error) { return error; }
}
}
return car;
}
int main(void) {
padded_string json = R"( [ { "make": "Toyota", "model": "Camry", "year": 2018,
"tire_pressure": [ 40.1, 39.9 ] },
{ "make": "Kia", "model": "Soul", "year": 2012,
"tire_pressure": [ 30.1, 31.0 ] },
{ "make": "Toyota", "model": "Tercel", "year": 1999,
"tire_pressure": [ 29.8, 30.0 ] }
])"_padded;
ondemand::parser parser;
ondemand::document doc = parser.iterate(json);
for (auto val : doc) {
Car c(val);
std::cout << c.make << std::endl;
}
direct();
return EXIT_SUCCESS;
}
```
Observe that we require an explicit cast (`Car c(val)` instead of `for (Car c : doc) {`): it is by design.
If you prefer to avoid exceptions, you may modify the `main` function as follows:
```c++
int main(void) {
padded_string json = R"( [ { "make": "Toyota", "model": "Camry", "year": 2018,
"tire_pressure": [ 40.1, 39.9 ] },
{ "make": "Kia", "model": "Soul", "year": 2012,
"tire_pressure": [ 30.1, 31.0 ] },
{ "make": "Toyota", "model": "Tercel", "year": 1999,
"tire_pressure": [ 29.8, 30.0 ] }
])"_padded;
ondemand::parser parser;
ondemand::document doc;
auto error = parser.iterate(json).get(doc);
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
for (auto val : doc) {
Car c;
error = val.get<Car>().get(c);
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
std::cout << c.make << std::endl;
}
return EXIT_SUCCESS;
}
```
Our example is limited to `ondemand::value` instances. If you wish to also be able to map
directly the document instance itself to a custom type, you need to provide the definitions to
the `ondemand::document` type. In this instance, we must replace the function with signature
`simdjson_result<Car> simdjson::ondemand::value::get()` with a function having signature
`simdjson_result<Car> simdjson::ondemand::document::get() &`. The following is a complete
example:
```C++
#include "simdjson.h"
#include <iostream>
#include <vector>
using namespace simdjson;
/**
* A custom type that we want to parse.
*/
struct Car {
std::string make;
std::string model;
int64_t year;
std::vector<double> tire_pressure;
};
template <>
simdjson_inline simdjson_result<std::vector<double>>
simdjson::ondemand::value::get() noexcept {
ondemand::array array;
if (auto error = get_array().get(array); error) { return error; }
std::vector<double> vec;
for (auto v : array) {
double val;
if (auto error = v.get_double().get(val); error) { return error; }
vec.push_back(val);
}
return vec;
}
template <>
simdjson_inline simdjson_result<Car> simdjson::ondemand::document::get() & noexcept {
ondemand::object obj;
auto error = get_object().get(obj);
if (error) {
return error;
}
Car car;
// Instead of repeatedly obj["something"], we iterate through the object which
// we expect to be faster.
for (auto field : obj) {
raw_json_string key;
error = field.key().get(key);
if (error) { return error; }
if (key == "make") {
error = field.value().get_string(car.make);
if (error) { return error; }
} else if (key == "model") {
error = field.value().get_string(car.model);
if (error) { return error; }
} else if (key == "year") {
error = field.value().get_int64().get(car.year);
if (error) { return error; }
} else if (key == "tire_pressure") {
error = field.value().get<std::vector<double>>().get(car.tire_pressure);
if (error) { return error; }
}
}
return car;
}
int main(void) {
padded_string json = R"( { "make": "Toyota", "model": "Camry", "year": 2018,
"tire_pressure": [ 40.1, 39.9 ] } )"_padded;
ondemand::parser parser;
ondemand::document doc = parser.iterate(json);
Car c(doc);
std::cout << c.make << std::endl;
return EXIT_SUCCESS;
}
```
Minifying JSON strings without parsing
----------------------
@@ -796,10 +1081,10 @@ auto cars = parser.iterate(cars_json);
cout << cars.at_pointer("/0/tire_pressure/1") << endl; // Prints 39.9
```
A JSON Path is a sequence of segments each starting with the '/' character. Within arrays, an integer
A JSON Pointer path is a sequence of segments each starting with the '/' character. Within arrays, a zero-based integer
index allows you to select the indexed node. Within objects, the string value of the key allows you to
select the value. If your keys contain the characters '/' or '~', they must be escaped as '~1' and
'~0' respectively. An empty JSON Path refers to the whole document.
'~0' respectively. An empty JSON Pointer Path refers to the whole document.
For multiple JSON pointer queries on a document, one can call `at_pointer` multiple times.
@@ -820,6 +1105,17 @@ for (size_t i = 0; i < size; i++) {
}
```
In most instances, a JSON Pointer is an ASCII string and the keys in a JSON document
are ASCII strings. We support UTF-8 in JSON Pointer, but key values are matched exactly, without unescaping or Unicode normalization. We do a byte-by-byte comparison. The e acute character is
considered distinct from its escaped version `\u00E9`. E.g.,
```c++
const padded_string json = "{\"\\u00E9\":123}"_padded;
auto doc = parser.iterate(json);
doc.at_pointer("/\\u00E9") == 123; // true
doc.at_pointer((const char*)u8"/\u00E9") // returns an error (NO_SUCH_FIELD)
```
Note that `at_pointer` calls [`rewind`](#rewind) to reset the parser at the beginning of the document. Hence, it invalidates all previously parsed values, objects and arrays: make sure to consume the values between each call to `at_pointer`. Consider the following example where one wants to store each object from the JSON into a vector of `struct car_type`:
```c++
@@ -879,9 +1175,55 @@ doc.rewind(); // Need to manually rewind to be able to use find_field properly f
std::cout << doc.find_field("k0") << std::endl; // Prints 27
```
When the JSON path is the empty string (`""`) applied to a scalar document (lone string, number, Boolean or null), a SCALAR_DOCUMENT_AS_VALUE error is returned because scalar document cannot
When the JSON Pointer Path is the empty string (`""`) applied to a scalar document (lone string, number, Boolean or null), a SCALAR_DOCUMENT_AS_VALUE error is returned because scalar document cannot
be represented as `value` instances. You can check that a document is a scalar with the method `scalar()`.
JSONPath
------------
The simdjson library now supports a subset of [JSONPath](https://datatracker.ietf.org/doc/html/draft-normington-jsonpath-00) through the `at_path()` method, allowing you to reach further into the document in a single call. The subset of JSONPath that is implemented is the subset that is trivially convertible into the JSON Pointer format, using `.` to access a field and `[]` to access a specific index.
This implementation relies on `at_path()` converting its argument to JSON Pointer and then calling `at_pointer`, which makes use of [`rewind`](#rewind) to reset the parser at the beginning of the document. Hence, it invalidates all previously parsed values, objects and arrays: make sure to consume the values between each call to `at_path`.
Consider the following example:
```c++
auto cars_json = R"( [
{ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] },
{ "make": "Kia", "model": "Soul", "year": 2012, "tire_pressure": [ 30.1, 31.0, 28.6, 28.7 ] },
{ "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
] )"_padded;
ondemand::parser parser;
auto cars = parser.iterate(cars_json);
cout << cars.at_path("[0].tire_pressure[1]") << endl; // Prints 39.9
```
A call to `at_path(json_path)` can result in any of the errors that are returned by the `at_pointer` method and if the conversion of `json_path` to JSON Pointer fails, it will lead to an `simdjson::INVALID_JSON_POINTER`error.
```c++
auto cars_json = R"( [
{ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] },
{ "make": "Kia", "model": "Soul", "year": 2012, "tire_pressure": [ 30.1, 31.0, 28.6, 28.7 ] },
{ "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
] )"_padded;
ondemand::parser parser;
auto cars = parser.iterate(cars_json);
ASSERT_ERROR(cars.at_path("[0].tire_presure[1").get(x), INVALID_JSON_POINTER); // Fails on conversion to JSON Pointer, since last square bracket was not properly closed.
ASSERT_ERROR(cars.at_path("[0].incorrect_field[1]").get(x), NO_SUCH_FIELD); // Conversion to JSON Pointer succeeds, but fails on at_pointer() since the path is invalid.
```
In most instances, a JSONPath is an ASCII string and the keys in a JSON document
are ASCII strings. We support UTF-8 within a JSONPath expression, but key values are
matched exactly, without unescaping or Unicode normalization. We do a byte-by-byte comparison.
The e acute character is considered distinct from its escaped version `\u00E9`. E.g.,
```c++
const padded_string json = "{\"\\u00E9\":123}"_padded;
auto doc = parser.iterate(json);
doc.at_path(".\\u00E9") == 123; // true
doc.at_path((const char*)u8".\u00E9") // returns an error (NO_SUCH_FIELD)
```
Error Handling
--------------
@@ -1617,7 +1959,7 @@ It will output:
9999999999999999999 negative: 0 is_integer: 1 large 64-bit integer: 9999999999999999999 large 64-bit integer: 9999999999999999999
```
Raw Strings
Raw Strings From Keys
-----------
It is sometimes useful to have access to a raw (unescaped) string: we make available a
@@ -1653,10 +1995,25 @@ JSON string to a user-provided buffer:
}
```
Some users might prefer to have a direct access to an `std::string_view` instance
pointing inside the source document. The `key_raw_json_token()` method serves this
purpose. It provides a view on the key, including the starting quote character,
and everything up to the next `:` character after the final quote character. E.g.,
if the key is `"name"` then `key_raw_json_token()` returns an `std::string_view` which
begins with `"name"` and may containing trailing white-space characters.
```C++
auto json = R"( {"name" : "Jack The Ripper \u0033"} )"_padded;
ondemand::parser parser;
ondemand::document doc = parser.iterate(json);
for (auto key_value : doc.get_object()) {
std::string_view keysv = key_value.key_raw_json_token(); // keysv is "\"name\" "
}
```
General Direct Access to the Raw JSON String
--------------------------------
If your value is a string, the `raw_json_string` you with `get_raw_json_string()` gives you direct access to the unprocessed
If your value is a string, the `raw_json_string` you get with `get_raw_json_string()` gives you direct access to the unprocessed
string. But the simdjson library allows you to have access to the raw underlying JSON
more generally, not just for strings.
@@ -1691,6 +2048,13 @@ string_view token = obj["value"].raw_json_token();
The `raw_json_token()` should be fast and free of allocation.
Given a quote-deliminated string, you find the string sequence inside the quote with a
single line of code:
```C++
std::string_view noquote(std::string_view v) { return {v.data()+1, v.find_last_of('"')-1}; }
```
If your value is an array or an object, `raw_json_token()` returns effectively a single
character (`[`) or (`}`) which is not very useful. For arrays and objects, we have another
method called `raw_json()` which consumes (traverses) the array or the object.
@@ -1779,7 +2143,7 @@ The same routine can be written without exceptions handling:
```C++
std::string name;
auto err = doc["name"].get_string(name);
if(err) { /* handle error */ }
if (err) { /* handle error */ }
```
The `std::string` instance, once created, is independent. Unlike our `std::string_view` instances,
@@ -1794,7 +2158,7 @@ can use it with features such as `std::optional`:
ondemand::parser parser;
ondemand::document doc = parser.iterate(json);
std::optional<std::string> value;
if(doc["foo1"].get_string(value)) { /* error */ }
if (doc["foo1"].get_string(value)) { /* error */ }
// value was populated with "3.1416"
```
@@ -2101,7 +2465,29 @@ bool example() {
}
```
* Example 4: Passing an array to a function
```C++
#include "simdjson.h"
#include <iostream>
// prints the content of the array as hexadecimal 64-bit integers
void f(simdjson::ondemand::array v) {
for(uint64_t val : v) {
std::cout << "0x" << std::hex << val << std::endl;
}
}
int main(void) {
simdjson::padded_string json = R"( [ 897314173811950000, 3122321 ])"_padded;
simdjson::ondemand::parser parser;
simdjson::ondemand::document doc = parser.iterate(json);
f(doc.get_array());
return EXIT_SUCCESS;
}
```
Performance Tips
--------
+3 -3
View File
@@ -223,13 +223,13 @@ dom::element cars = parser.parse(cars_json);
cout << cars.at_pointer("/0/tire_pressure/1") << endl; // Prints 39.9
```
A JSON Path is a sequence of segments each starting with the '/' character. Within arrays, an integer
A JSON Pointer expression is a sequence of segments each starting with the '/' character. Within arrays, an integer
index allows you to select the indexed node. Within objects, the string value of the key allows you to
select the value. If your keys contain the characters '/' or '~', they must be escaped as '~1' and
'~0' respectively. An empty JSON Path refers to the whole document.
'~0' respectively. An empty JSON Pointer expression refers to the whole document.
We also extend the JSON Pointer support to include *relative* paths.
You can apply a JSON path to any node and the path gets interpreted relatively, as if the current node were a whole JSON document.
You can apply a JSON Pointer expression to any node and the path gets interpreted relatively, as if the current node were a whole JSON document.
Consider the following example:
+7 -6
View File
@@ -11,22 +11,24 @@ CPU Architecture-Specific Implementations
Overview
--------
The simdjson library takes advantage of SIMD instruction sets such as NEON, SSE and AVX to achieve
much of its speed. Because these instruction sets work differently, simdjson has to compile a
different version of the JSON parser for different CPU architectures, often with different
algorithms to take better advantage of a given CPU!
The current implementations are:
* icelake: AVX-512F, AVX-512VBMI, etc.
* haswell: AVX2 (2013 Intel Haswell or later)
* icelake: AVX-512F, AVX-512_VBMI, AVX-512_VBMI2, AVX-512_DQ, AVX-512512_CD, AVX-512_BW, AVX-512_VL (2019 Intel Ice Lake, Intel Rocket Lake, Intel Sapphire Rapids, AMD Zen 4)
* haswell: AVX2 (2013 Intel Haswell or later, all AMD Zen processors)
* westmere: SSE4.2 (2010 Westmere or later).
* arm64: 64-bit ARMv8-A NEON
* ppc64: 64-bit POWER8 and POWER9 with VSX and ALTIVEC extensions. Both big endian and little endian are implemented, depends on the compiler you are using. The library is tested on recent, little-endian, POWER systems.
* ppc64: 64-bit POWER8 and POWER9 with VSX and ALTIVEC extensions. Both big endian and little endian are implemented, depending on the compiler you are using. The library is tested on recent, little-endian, POWER systems.
* fallback: A generic implementation that runs on any 64-bit processor.
In many cases, you don't know where your compiled binary is going to run, so simdjson automatically
compiles *all* the implementations into the executable. On Intel, it will include 4 implementations
(icelake, haswell, westmere and fallback), on ARM it will include 2 (arm64 and fallback), and on PPC
(icelake, haswell, westmere and fallback), on 64-bit ARM it will include just one since running dispatching is unnecessary, and on PPC
it will include 2 (ppc64 and fallback).
If you know more about where you're going to run and want to save the space, you can disable any of
@@ -75,8 +77,7 @@ And look them up by name:
```c++
cout << simdjson::get_available_implementations()["fallback"]->description() << endl;
```
Though the fallback implementation should always be available, others might be missing. When
an implementation is not available, the bracket call `simdjson::get_available_implementations()[name]`
When an implementation is not available, the bracket call `simdjson::get_available_implementations()[name]`
will return the null pointer.
The available implementations have been compiled but may not necessarily be run safely on your system
+2 -2
View File
@@ -686,7 +686,7 @@ in production systems:
// 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:
```C++
ondemand::parser parser;
@@ -779,4 +779,4 @@ Instead of specifying a specific microarchitecture, you can let your compiler do
Passing `-march=native` to the compiler may make On Demand faster by allowing it to use optimizations specific to your machine. You cannot do this, however, if you are compiling code that might be run on less advanced machines. That is, be mindful that when compiling with the `-march=native` flag, the resulting binary will run on the current system but may not run on other systems (e.g., on an old processor).
If you are compiling on an ARM or POWER system, you do not need to be concerned with CPU selection during compilation. The `-march=native` flag useful for best performance on x64 (e.g., Intel) systems but it is generally unsupported on some platforms such as ARM (aarch64) or POWER.
If you are compiling on an ARM or POWER system, you do not need to be concerned with CPU selection during compilation. The `-march=native` flag is useful for best performance on x64 (e.g., Intel) systems but it is generally unsupported on some platforms such as ARM (aarch64) or POWER.
+1 -1
View File
@@ -159,7 +159,7 @@ When compiling with Visual Studio, we recommend the flags `/Ob2 /O2` or better.
Recent versions of Microsoft Visual Studio on Windows provides support for the LLVM Clang compiler. You only need to install the "Clang compiler" optional component (ClangCL). You may also get a copy of the 64-bit LLVM CLang compiler for [Windows directly from LLVM](https://releases.llvm.org/download.html). The simdjson library fully supports the LLVM Clang compiler under Windows. In fact, you may get better performance out of simdjson with the LLVM Clang compiler than with the regular Visual Studio compiler. Meanwhile the [LLVM CLang compiler is binary compatible with Visual Studio](https://clang.llvm.org/docs/MSVCCompatibility.html) which means that you can combine their binaries (executables and libraries).
Under Windows, we also support the GNU GCC compiler via MSYS2. The performance of 64-bit MSYS2 under Windows excellent (on par with Linux).
Under Windows, we also support the GNU GCC compiler via MSYS2. The performance of 64-bit MSYS2 under Windows is excellent (on par with Linux).
Power Usage and Downclocking
+1 -1
View File
@@ -29,7 +29,7 @@ It is sometimes useful to start with an example. Consider the following JSON doc
The following is a dump of the content of the tape, with the first number of each line representing the index of a tape element.
### The Tape
| index | element (64 bit word) |
| index | element (64-bit word) |
| ----- | ------------------------------------------------------------------- |
| 0 | r // pointing to 39 (right after last node) |
| 1 | { // pointing to next tape location 38 (first node after the scope) |
+1 -1
View File
@@ -78,7 +78,7 @@ fi
# Different levels may uncover different types of bugs, see this interesting
# thread: https://github.com/google/oss-fuzz/issues/2295#issuecomment-481493392
# Oss-fuzz uses -O1 so it may be relevant to use something else than that,
# to do something oss-fuzz doesn't.
# to do something oss-fuzz does not do.
variant=sanitizers-O3
if [ ! -d build-$variant ] ; then
+2 -2
View File
@@ -7,11 +7,11 @@
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
// Split data into two strings, json pointer and the document string.
// Split data into two strings, JSON Pointer and the document string.
// Might end up with none, either or both being empty, important for
// covering edge cases such as
// https://github.com/simdjson/simdjson/issues/1142 Inputs missing the
// separator line will get an empty json pointer but the all the input put in
// separator line will get an empty JSON Pointer but the all the input put in
// the document string. This means test data from other fuzzers that take json
// input works for this fuzzer as well.
FuzzData fd(Data, Size);
-6
View File
@@ -10,18 +10,12 @@
#include "simdjson/dom/parser.h"
#include "simdjson/dom/serialization.h"
// Deprecated API
#include "simdjson/dom/jsonparser.h"
#include "simdjson/dom/parsedjson.h"
#include "simdjson/dom/parsedjson_iterator.h"
// Inline functions
#include "simdjson/dom/array-inl.h"
#include "simdjson/dom/document_stream-inl.h"
#include "simdjson/dom/document-inl.h"
#include "simdjson/dom/element-inl.h"
#include "simdjson/dom/object-inl.h"
#include "simdjson/dom/parsedjson_iterator-inl.h"
#include "simdjson/dom/parser-inl.h"
#include "simdjson/internal/tape_ref-inl.h"
#include "simdjson/dom/serialization-inl.h"
+4 -1
View File
@@ -19,11 +19,14 @@ public:
public:
using value_type = element;
using difference_type = std::ptrdiff_t;
using pointer = void;
using reference = value_type;
using iterator_category = std::forward_iterator_tag;
/**
* Get the actual value
*/
inline value_type operator*() const noexcept;
inline reference operator*() const noexcept;
/**
* Get the next value.
*
+2 -3
View File
@@ -293,7 +293,7 @@ public:
*
* @return The integer value.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON element is not an integer
* @exception simdjson_error(NUMBER_OUT_OF_RANGE) if the integer doesn't fit in 64 bits or is negative
* @exception simdjson_error(NUMBER_OUT_OF_RANGE) if the integer does not fit in 64 bits or is negative
*/
inline operator uint64_t() const noexcept(false);
/**
@@ -301,7 +301,7 @@ public:
*
* @return The integer value.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON element is not an integer
* @exception simdjson_error(NUMBER_OUT_OF_RANGE) if the integer doesn't fit in 64 bits
* @exception simdjson_error(NUMBER_OUT_OF_RANGE) if the integer does not fit in 64 bits
*/
inline operator int64_t() const noexcept(false);
/**
@@ -309,7 +309,6 @@ public:
*
* @return The double value.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON element is not a number
* @exception simdjson_error(NUMBER_OUT_OF_RANGE) if the integer doesn't fit in 64 bits or is negative
*/
inline operator double() const noexcept(false);
/**
-121
View File
@@ -1,121 +0,0 @@
// TODO Remove this -- deprecated API and files
#ifndef SIMDJSON_DOM_JSONPARSER_H
#define SIMDJSON_DOM_JSONPARSER_H
#include "simdjson/dom/base.h"
#include "simdjson/dom/parser.h"
#include "simdjson/dom/element.h"
#include "simdjson/dom/parser-inl.h"
namespace simdjson {
//
// C API (json_parse and build_parsed_json) declarations
//
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
[[deprecated("Use parser.parse() instead")]]
inline int json_parse(const uint8_t *buf, size_t len, dom::parser &parser, bool realloc_if_needed = true) noexcept {
error_code code = parser.parse(buf, len, realloc_if_needed).error();
// The deprecated json_parse API is a signal that the user plans to *use* the error code / valid
// bits in the parser instead of heeding the result code. The normal parser unsets those in
// anticipation of making the error code ephemeral.
// Here we put the code back into the parser, until we've removed this method.
parser.valid = code == SUCCESS;
parser.error = code;
return code;
}
[[deprecated("Use parser.parse() instead")]]
inline int json_parse(const char *buf, size_t len, dom::parser &parser, bool realloc_if_needed = true) noexcept {
error_code code = parser.parse(buf, len, realloc_if_needed).error();
// The deprecated json_parse API is a signal that the user plans to *use* the error code / valid
// bits in the parser instead of heeding the result code. The normal parser unsets those in
// anticipation of making the error code ephemeral.
// Here we put the code back into the parser, until we've removed this method.
parser.valid = code == SUCCESS;
parser.error = code;
return code;
}
[[deprecated("Use parser.parse() instead")]]
inline int json_parse(const std::string &s, dom::parser &parser, bool realloc_if_needed = true) noexcept {
error_code code = parser.parse(s.data(), s.length(), realloc_if_needed).error();
// The deprecated json_parse API is a signal that the user plans to *use* the error code / valid
// bits in the parser instead of heeding the result code. The normal parser unsets those in
// anticipation of making the error code ephemeral.
// Here we put the code back into the parser, until we've removed this method.
parser.valid = code == SUCCESS;
parser.error = code;
return code;
}
[[deprecated("Use parser.parse() instead")]]
inline int json_parse(const padded_string &s, dom::parser &parser) noexcept {
error_code code = parser.parse(s).error();
// The deprecated json_parse API is a signal that the user plans to *use* the error code / valid
// bits in the parser instead of heeding the result code. The normal parser unsets those in
// anticipation of making the error code ephemeral.
// Here we put the code back into the parser, until we've removed this method.
parser.valid = code == SUCCESS;
parser.error = code;
return code;
}
[[deprecated("Use parser.parse() instead")]]
simdjson_warn_unused inline dom::parser build_parsed_json(const uint8_t *buf, size_t len, bool realloc_if_needed = true) noexcept {
dom::parser parser;
error_code code = parser.parse(buf, len, realloc_if_needed).error();
// The deprecated json_parse API is a signal that the user plans to *use* the error code / valid
// bits in the parser instead of heeding the result code. The normal parser unsets those in
// anticipation of making the error code ephemeral.
// Here we put the code back into the parser, until we've removed this method.
parser.valid = code == SUCCESS;
parser.error = code;
return parser;
}
[[deprecated("Use parser.parse() instead")]]
simdjson_warn_unused inline dom::parser build_parsed_json(const char *buf, size_t len, bool realloc_if_needed = true) noexcept {
dom::parser parser;
error_code code = parser.parse(buf, len, realloc_if_needed).error();
// The deprecated json_parse API is a signal that the user plans to *use* the error code / valid
// bits in the parser instead of heeding the result code. The normal parser unsets those in
// anticipation of making the error code ephemeral.
// Here we put the code back into the parser, until we've removed this method.
parser.valid = code == SUCCESS;
parser.error = code;
return parser;
}
[[deprecated("Use parser.parse() instead")]]
simdjson_warn_unused inline dom::parser build_parsed_json(const std::string &s, bool realloc_if_needed = true) noexcept {
dom::parser parser;
error_code code = parser.parse(s.data(), s.length(), realloc_if_needed).error();
// The deprecated json_parse API is a signal that the user plans to *use* the error code / valid
// bits in the parser instead of heeding the result code. The normal parser unsets those in
// anticipation of making the error code ephemeral.
// Here we put the code back into the parser, until we've removed this method.
parser.valid = code == SUCCESS;
parser.error = code;
return parser;
}
[[deprecated("Use parser.parse() instead")]]
simdjson_warn_unused inline dom::parser build_parsed_json(const padded_string &s) noexcept {
dom::parser parser;
error_code code = parser.parse(s).error();
// The deprecated json_parse API is a signal that the user plans to *use* the error code / valid
// bits in the parser instead of heeding the result code. The normal parser unsets those in
// anticipation of making the error code ephemeral.
// Here we put the code back into the parser, until we've removed this method.
parser.valid = code == SUCCESS;
parser.error = code;
return parser;
}
#endif // SIMDJSON_DISABLE_DEPRECATED_API
/** @private We do not want to allow implicit conversion from C string to std::string. */
int json_parse(const char *buf, dom::parser &parser) noexcept = delete;
/** @private We do not want to allow implicit conversion from C string to std::string. */
dom::parser build_parsed_json(const char *buf) noexcept = delete;
} // namespace simdjson
#endif // SIMDJSON_DOM_JSONPARSER_H
+5 -2
View File
@@ -18,13 +18,16 @@ public:
class iterator {
public:
using value_type = key_value_pair;
using value_type = const key_value_pair;
using difference_type = std::ptrdiff_t;
using pointer = void;
using reference = value_type;
using iterator_category = std::forward_iterator_tag;
/**
* Get the actual key/value pair
*/
inline const value_type operator*() const noexcept;
inline reference operator*() const noexcept;
/**
* Get the next key/value pair.
*
-17
View File
@@ -1,17 +0,0 @@
// TODO Remove this -- deprecated API and files
#ifndef SIMDJSON_DOM_PARSEDJSON_H
#define SIMDJSON_DOM_PARSEDJSON_H
#include "simdjson/dom/base.h"
namespace simdjson {
/**
* @deprecated Use `dom::parser` instead.
*/
using ParsedJson [[deprecated("Use dom::parser instead")]] = dom::parser;
} // namespace simdjson
#endif // SIMDJSON_DOM_PARSEDJSON_H
@@ -1,545 +0,0 @@
#ifndef SIMDJSON_PARSEDJSON_ITERATOR_INL_H
#define SIMDJSON_PARSEDJSON_ITERATOR_INL_H
#include "simdjson/dom/base.h"
#include "simdjson/dom/parsedjson_iterator.h"
#include "simdjson/internal/jsonformatutils.h"
#include "simdjson/dom/parser-inl.h"
#include "simdjson/internal/tape_ref-inl.h"
#include <cstring>
#include <iterator>
#include <limits>
#include <ostream>
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
namespace simdjson {
// VS2017 reports deprecated warnings when you define a deprecated class's methods.
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_DEPRECATED_WARNING
// Because of template weirdness, the actual class definition is inline in the document class
simdjson_warn_unused bool dom::parser::Iterator::is_ok() const {
return location < tape_length;
}
// useful for debugging purposes
size_t dom::parser::Iterator::get_tape_location() const {
return location;
}
// useful for debugging purposes
size_t dom::parser::Iterator::get_tape_length() const {
return tape_length;
}
// returns the current depth (start at 1 with 0 reserved for the fictitious root
// node)
size_t dom::parser::Iterator::get_depth() const {
return depth;
}
// A scope is a series of nodes at the same depth, typically it is either an
// object ({) or an array ([). The root node has type 'r'.
uint8_t dom::parser::Iterator::get_scope_type() const {
return depth_index[depth].scope_type;
}
bool dom::parser::Iterator::move_forward() {
if (location + 1 >= tape_length) {
return false; // we are at the end!
}
if ((current_type == '[') || (current_type == '{')) {
// We are entering a new scope
depth++;
assert(depth < max_depth);
depth_index[depth].start_of_scope = location;
depth_index[depth].scope_type = current_type;
} else if ((current_type == ']') || (current_type == '}')) {
// Leaving a scope.
depth--;
} else if (is_number()) {
// these types use 2 locations on the tape, not just one.
location += 1;
}
location += 1;
current_val = doc.tape[location];
current_type = uint8_t(current_val >> 56);
return true;
}
void dom::parser::Iterator::move_to_value() {
// assume that we are on a key, so move by 1.
location += 1;
current_val = doc.tape[location];
current_type = uint8_t(current_val >> 56);
}
bool dom::parser::Iterator::move_to_key(const char *key) {
if (down()) {
do {
const bool right_key = (strcmp(get_string(), key) == 0);
move_to_value();
if (right_key) {
return true;
}
} while (next());
up();
}
return false;
}
bool dom::parser::Iterator::move_to_key_insensitive(
const char *key) {
if (down()) {
do {
const bool right_key = (simdjson_strcasecmp(get_string(), key) == 0);
move_to_value();
if (right_key) {
return true;
}
} while (next());
up();
}
return false;
}
bool dom::parser::Iterator::move_to_key(const char *key,
uint32_t length) {
if (down()) {
do {
bool right_key = ((get_string_length() == length) &&
(memcmp(get_string(), key, length) == 0));
move_to_value();
if (right_key) {
return true;
}
} while (next());
up();
}
return false;
}
bool dom::parser::Iterator::move_to_index(uint32_t index) {
if (down()) {
uint32_t i = 0;
for (; i < index; i++) {
if (!next()) {
break;
}
}
if (i == index) {
return true;
}
up();
}
return false;
}
bool dom::parser::Iterator::prev() {
size_t target_location = location;
to_start_scope();
size_t npos = location;
if (target_location == npos) {
return false; // we were already at the start
}
size_t oldnpos;
// we have that npos < target_location here
do {
oldnpos = npos;
if ((current_type == '[') || (current_type == '{')) {
// we need to jump
npos = uint32_t(current_val);
} else {
npos = npos + ((current_type == 'd' || current_type == 'l') ? 2 : 1);
}
} while (npos < target_location);
location = oldnpos;
current_val = doc.tape[location];
current_type = uint8_t(current_val >> 56);
return true;
}
bool dom::parser::Iterator::up() {
if (depth == 1) {
return false; // don't allow moving back to root
}
to_start_scope();
// next we just move to the previous value
depth--;
location -= 1;
current_val = doc.tape[location];
current_type = uint8_t(current_val >> 56);
return true;
}
bool dom::parser::Iterator::down() {
if (location + 1 >= tape_length) {
return false;
}
if ((current_type == '[') || (current_type == '{')) {
size_t npos = uint32_t(current_val);
if (npos == location + 2) {
return false; // we have an empty scope
}
depth++;
assert(depth < max_depth);
location = location + 1;
depth_index[depth].start_of_scope = location;
depth_index[depth].scope_type = current_type;
current_val = doc.tape[location];
current_type = uint8_t(current_val >> 56);
return true;
}
return false;
}
void dom::parser::Iterator::to_start_scope() {
location = depth_index[depth].start_of_scope;
current_val = doc.tape[location];
current_type = uint8_t(current_val >> 56);
}
inline void dom::parser::Iterator::rewind() {
while (up())
;
}
bool dom::parser::Iterator::next() {
size_t npos;
if ((current_type == '[') || (current_type == '{')) {
// we need to jump
npos = uint32_t(current_val);
} else {
npos = location + (is_number() ? 2 : 1);
}
uint64_t next_val = doc.tape[npos];
uint8_t next_type = uint8_t(next_val >> 56);
if ((next_type == ']') || (next_type == '}')) {
return false; // we reached the end of the scope
}
location = npos;
current_val = next_val;
current_type = next_type;
return true;
}
dom::parser::Iterator::Iterator(const dom::parser &pj) noexcept(false)
: doc(pj.doc)
{
#if SIMDJSON_EXCEPTIONS
if (!pj.valid) { throw simdjson_error(pj.error); }
#else
if (!pj.valid) { return; } // abort() usage is forbidden in the library
#endif
max_depth = pj.max_depth();
depth_index = new scopeindex_t[max_depth + 1];
depth_index[0].start_of_scope = location;
current_val = doc.tape[location++];
current_type = uint8_t(current_val >> 56);
depth_index[0].scope_type = current_type;
tape_length = size_t(current_val & internal::JSON_VALUE_MASK);
if (location < tape_length) {
// If we make it here, then depth_capacity must >=2, but the compiler
// may not know this.
current_val = doc.tape[location];
current_type = uint8_t(current_val >> 56);
depth++;
assert(depth < max_depth);
depth_index[depth].start_of_scope = location;
depth_index[depth].scope_type = current_type;
}
}
dom::parser::Iterator::Iterator(
const dom::parser::Iterator &o) noexcept
: doc(o.doc),
max_depth(o.depth),
depth(o.depth),
location(o.location),
tape_length(o.tape_length),
current_type(o.current_type),
current_val(o.current_val)
{
depth_index = new scopeindex_t[max_depth+1];
std::memcpy(depth_index, o.depth_index, (depth + 1) * sizeof(depth_index[0]));
}
dom::parser::Iterator::~Iterator() noexcept {
if (depth_index) { delete[] depth_index; }
}
bool dom::parser::Iterator::print(std::ostream &os, bool escape_strings) const {
if (!is_ok()) {
return false;
}
switch (current_type) {
case '"': // we have a string
os << '"';
if (escape_strings) {
os << internal::escape_json_string(std::string_view(get_string(), get_string_length()));
} else {
// was: os << get_string();, but given that we can include null chars, we
// have to do something crazier:
std::copy(get_string(), get_string() + get_string_length(), std::ostream_iterator<char>(os));
}
os << '"';
break;
case 'l': // we have a long int
os << get_integer();
break;
case 'u':
os << get_unsigned_integer();
break;
case 'd':
os << get_double();
break;
case 'n': // we have a null
os << "null";
break;
case 't': // we have a true
os << "true";
break;
case 'f': // we have a false
os << "false";
break;
case '{': // we have an object
case '}': // we end an object
case '[': // we start an array
case ']': // we end an array
os << char(current_type);
break;
default:
return false;
}
return true;
}
bool dom::parser::Iterator::move_to(const char *pointer,
uint32_t length) {
char *new_pointer = nullptr;
if (pointer[0] == '#') {
// Converting fragment representation to string representation
new_pointer = new char[length];
uint32_t new_length = 0;
for (uint32_t i = 1; i < length; i++) {
if (pointer[i] == '%' && pointer[i + 1] == 'x') {
#if __cpp_exceptions
try {
#endif
int fragment =
std::stoi(std::string(&pointer[i + 2], 2), nullptr, 16);
if (fragment == '\\' || fragment == '"' || (fragment <= 0x1F)) {
// escaping the character
new_pointer[new_length] = '\\';
new_length++;
}
new_pointer[new_length] = char(fragment);
i += 3;
#if __cpp_exceptions
} catch (std::invalid_argument &) {
delete[] new_pointer;
return false; // the fragment is invalid
}
#endif
} else {
new_pointer[new_length] = pointer[i];
}
new_length++;
}
length = new_length;
pointer = new_pointer;
}
// saving the current state
size_t depth_s = depth;
size_t location_s = location;
uint8_t current_type_s = current_type;
uint64_t current_val_s = current_val;
rewind(); // The json pointer is used from the root of the document.
bool found = relative_move_to(pointer, length);
delete[] new_pointer;
if (!found) {
// since the pointer has found nothing, we get back to the original
// position.
depth = depth_s;
location = location_s;
current_type = current_type_s;
current_val = current_val_s;
}
return found;
}
inline bool dom::parser::Iterator::move_to(const std::string &pointer) {
return move_to(pointer.c_str(), uint32_t(pointer.length()));
}
inline int64_t dom::parser::Iterator::get_integer() const {
if (location + 1 >= tape_length) {
return 0; // default value in case of error
}
return static_cast<int64_t>(doc.tape[location + 1]);
}
inline uint64_t dom::parser::Iterator::get_unsigned_integer() const {
if (location + 1 >= tape_length) {
return 0; // default value in case of error
}
return doc.tape[location + 1];
}
inline const char * dom::parser::Iterator::get_string() const {
return reinterpret_cast<const char *>(
doc.string_buf.get() + (current_val & internal::JSON_VALUE_MASK) + sizeof(uint32_t));
}
inline uint32_t dom::parser::Iterator::get_string_length() const {
uint32_t answer;
std::memcpy(&answer,
reinterpret_cast<const char *>(doc.string_buf.get() +
(current_val & internal::JSON_VALUE_MASK)),
sizeof(uint32_t));
return answer;
}
inline double dom::parser::Iterator::get_double() const {
if (location + 1 >= tape_length) {
return std::numeric_limits<double>::quiet_NaN(); // default value in
// case of error
}
double answer;
std::memcpy(&answer, &doc.tape[location + 1], sizeof(answer));
return answer;
}
bool dom::parser::Iterator::relative_move_to(const char *pointer,
uint32_t length) {
if (length == 0) {
// returns the whole document
return true;
}
if (pointer[0] != '/') {
// '/' must be the first character
return false;
}
// finding the key in an object or the index in an array
std::string key_or_index;
uint32_t offset = 1;
// checking for the "-" case
if (is_array() && pointer[1] == '-') {
if (length != 2) {
// the pointer must be exactly "/-"
// there can't be anything more after '-' as an index
return false;
}
key_or_index = '-';
offset = length; // will skip the loop coming right after
}
// We either transform the first reference token to a valid json key
// or we make sure it is a valid index in an array.
for (; offset < length; offset++) {
if (pointer[offset] == '/') {
// beginning of the next key or index
break;
}
if (is_array() && (pointer[offset] < '0' || pointer[offset] > '9')) {
// the index of an array must be an integer
// we also make sure std::stoi won't discard whitespaces later
return false;
}
if (pointer[offset] == '~') {
// "~1" represents "/"
if (pointer[offset + 1] == '1') {
key_or_index += '/';
offset++;
continue;
}
// "~0" represents "~"
if (pointer[offset + 1] == '0') {
key_or_index += '~';
offset++;
continue;
}
}
if (pointer[offset] == '\\') {
if (pointer[offset + 1] == '\\' || pointer[offset + 1] == '"' ||
(pointer[offset + 1] <= 0x1F)) {
key_or_index += pointer[offset + 1];
offset++;
continue;
}
return false; // invalid escaped character
}
if (pointer[offset] == '\"') {
// unescaped quote character. this is an invalid case.
// lets do nothing and assume most pointers will be valid.
// it won't find any corresponding json key anyway.
// return false;
}
key_or_index += pointer[offset];
}
bool found = false;
if (is_object()) {
if (move_to_key(key_or_index.c_str(), uint32_t(key_or_index.length()))) {
found = relative_move_to(pointer + offset, length - offset);
}
} else if (is_array()) {
if (key_or_index == "-") { // handling "-" case first
if (down()) {
while (next())
; // moving to the end of the array
// moving to the nonexistent value right after...
size_t npos;
if ((current_type == '[') || (current_type == '{')) {
// we need to jump
npos = uint32_t(current_val);
} else {
npos =
location + ((current_type == 'd' || current_type == 'l') ? 2 : 1);
}
location = npos;
current_val = doc.tape[npos];
current_type = uint8_t(current_val >> 56);
return true; // how could it fail ?
}
} else { // regular numeric index
// The index can't have a leading '0'
if (key_or_index[0] == '0' && key_or_index.length() > 1) {
return false;
}
// it cannot be empty
if (key_or_index.length() == 0) {
return false;
}
// we already checked the index contains only valid digits
uint32_t index = std::stoi(key_or_index);
if (move_to_index(index)) {
found = relative_move_to(pointer + offset, length - offset);
}
}
}
return found;
}
SIMDJSON_POP_DISABLE_WARNINGS
} // namespace simdjson
#endif // SIMDJSON_DISABLE_DEPRECATED_API
#endif // SIMDJSON_PARSEDJSON_ITERATOR_INL_H
-231
View File
@@ -1,231 +0,0 @@
// TODO Remove this -- deprecated API and files
#ifndef SIMDJSON_DOM_PARSEDJSON_ITERATOR_H
#define SIMDJSON_DOM_PARSEDJSON_ITERATOR_H
#include "simdjson/dom/base.h"
#include "simdjson/dom/parser.h"
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
namespace simdjson {
/** @private **/
class [[deprecated("Use the new DOM navigation API instead (see doc/basics.md)")]] dom::parser::Iterator {
public:
inline Iterator(const dom::parser &parser) noexcept(false);
inline Iterator(const Iterator &o) noexcept;
inline ~Iterator() noexcept;
inline Iterator& operator=(const Iterator&) = delete;
inline bool is_ok() const;
// useful for debugging purposes
inline size_t get_tape_location() const;
// useful for debugging purposes
inline size_t get_tape_length() const;
// returns the current depth (start at 1 with 0 reserved for the fictitious
// root node)
inline size_t get_depth() const;
// A scope is a series of nodes at the same depth, typically it is either an
// object ({) or an array ([). The root node has type 'r'.
inline uint8_t get_scope_type() const;
// move forward in document order
inline bool move_forward();
// retrieve the character code of what we're looking at:
// [{"slutfn are the possibilities
inline uint8_t get_type() const {
return current_type; // short functions should be inlined!
}
// get the int64_t value at this node; valid only if get_type is "l"
inline int64_t get_integer() const;
// get the value as uint64; valid only if if get_type is "u"
inline uint64_t get_unsigned_integer() const;
// get the string value at this node (NULL ended); valid only if get_type is "
// note that tabs, and line endings are escaped in the returned value (see
// print_with_escapes) return value is valid UTF-8, it may contain NULL chars
// within the string: get_string_length determines the true string length.
inline const char *get_string() const;
// return the length of the string in bytes
inline uint32_t get_string_length() const;
// get the double value at this node; valid only if
// get_type() is "d"
inline double get_double() const;
inline bool is_object_or_array() const { return is_object() || is_array(); }
inline bool is_object() const { return get_type() == '{'; }
inline bool is_array() const { return get_type() == '['; }
inline bool is_string() const { return get_type() == '"'; }
// Returns true if the current type of the node is an signed integer.
// You can get its value with `get_integer()`.
inline bool is_integer() const { return get_type() == 'l'; }
// Returns true if the current type of the node is an unsigned integer.
// You can get its value with `get_unsigned_integer()`.
//
// NOTE:
// Only a large value, which is out of range of a 64-bit signed integer, is
// represented internally as an unsigned node. On the other hand, a typical
// positive integer, such as 1, 42, or 1000000, is as a signed node.
// Be aware this function returns false for a signed node.
inline bool is_unsigned_integer() const { return get_type() == 'u'; }
// Returns true if the current type of the node is a double floating-point number.
inline bool is_double() const { return get_type() == 'd'; }
// Returns true if the current type of the node is a number (integer or floating-point).
inline bool is_number() const {
return is_integer() || is_unsigned_integer() || is_double();
}
// Returns true if the current type of the node is a bool with true value.
inline bool is_true() const { return get_type() == 't'; }
// Returns true if the current type of the node is a bool with false value.
inline bool is_false() const { return get_type() == 'f'; }
// Returns true if the current type of the node is null.
inline bool is_null() const { return get_type() == 'n'; }
// Returns true if the type byte represents an object of an array
static bool is_object_or_array(uint8_t type) {
return ((type == '[') || (type == '{'));
}
// when at {, go one level deep, looking for a given key
// if successful, we are left pointing at the value,
// if not, we are still pointing at the object ({)
// (in case of repeated keys, this only finds the first one).
// We seek the key using C's strcmp so if your JSON strings contain
// NULL chars, this would trigger a false positive: if you expect that
// to be the case, take extra precautions.
// Furthermore, we do the comparison character-by-character
// without taking into account Unicode equivalence.
inline bool move_to_key(const char *key);
// as above, but case insensitive lookup (strcmpi instead of strcmp)
inline bool move_to_key_insensitive(const char *key);
// when at {, go one level deep, looking for a given key
// if successful, we are left pointing at the value,
// if not, we are still pointing at the object ({)
// (in case of repeated keys, this only finds the first one).
// The string we search for can contain NULL values.
// Furthermore, we do the comparison character-by-character
// without taking into account Unicode equivalence.
inline bool move_to_key(const char *key, uint32_t length);
// when at a key location within an object, this moves to the accompanying
// value (located next to it). This is equivalent but much faster than
// calling "next()".
inline void move_to_value();
// when at [, go one level deep, and advance to the given index.
// if successful, we are left pointing at the value,
// if not, we are still pointing at the array ([)
inline bool move_to_index(uint32_t index);
// Moves the iterator to the value corresponding to the json pointer.
// Always search from the root of the document.
// if successful, we are left pointing at the value,
// if not, we are still pointing the same value we were pointing before the
// call. The json pointer follows the rfc6901 standard's syntax:
// https://tools.ietf.org/html/rfc6901 However, the standard says "If a
// referenced member name is not unique in an object, the member that is
// referenced is undefined, and evaluation fails". Here we just return the
// first corresponding value. The length parameter is the length of the
// jsonpointer string ('pointer').
inline bool move_to(const char *pointer, uint32_t length);
// Moves the iterator to the value corresponding to the json pointer.
// Always search from the root of the document.
// if successful, we are left pointing at the value,
// if not, we are still pointing the same value we were pointing before the
// call. The json pointer implementation follows the rfc6901 standard's
// syntax: https://tools.ietf.org/html/rfc6901 However, the standard says
// "If a referenced member name is not unique in an object, the member that
// is referenced is undefined, and evaluation fails". Here we just return
// the first corresponding value.
inline bool move_to(const std::string &pointer);
private:
// Almost the same as move_to(), except it searches from the current
// position. The pointer's syntax is identical, though that case is not
// handled by the rfc6901 standard. The '/' is still required at the
// beginning. However, contrary to move_to(), the URI Fragment Identifier
// Representation is not supported here. Also, in case of failure, we are
// left pointing at the closest value it could reach. For these reasons it
// is private. It exists because it is used by move_to().
inline bool relative_move_to(const char *pointer, uint32_t length);
public:
// throughout return true if we can do the navigation, false
// otherwise
// Within a given scope (series of nodes at the same depth within either an
// array or an object), we move forward.
// Thus, given [true, null, {"a":1}, [1,2]], we would visit true, null, {
// and [. At the object ({) or at the array ([), you can issue a "down" to
// visit their content. valid if we're not at the end of a scope (returns
// true).
inline bool next();
// Within a given scope (series of nodes at the same depth within either an
// array or an object), we move backward.
// Thus, given [true, null, {"a":1}, [1,2]], we would visit ], }, null, true
// when starting at the end of the scope. At the object ({) or at the array
// ([), you can issue a "down" to visit their content.
// Performance warning: This function is implemented by starting again
// from the beginning of the scope and scanning forward. You should expect
// it to be relatively slow.
inline bool prev();
// Moves back to either the containing array or object (type { or [) from
// within a contained scope.
// Valid unless we are at the first level of the document
inline bool up();
// Valid if we're at a [ or { and it starts a non-empty scope; moves us to
// start of that deeper scope if it not empty. Thus, given [true, null,
// {"a":1}, [1,2]], if we are at the { node, we would move to the "a" node.
inline bool down();
// move us to the start of our current scope,
// a scope is a series of nodes at the same level
inline void to_start_scope();
inline void rewind();
// print the node we are currently pointing at
inline bool print(std::ostream &os, bool escape_strings = true) const;
private:
const document &doc;
size_t max_depth{};
size_t depth{};
size_t location{}; // our current location on a tape
size_t tape_length{};
uint8_t current_type{};
uint64_t current_val{};
typedef struct {
size_t start_of_scope;
uint8_t scope_type;
} scopeindex_t;
scopeindex_t *depth_index{};
};
} // namespace simdjson
#endif // SIMDJSON_DISABLE_DEPRECATED_API
#endif // SIMDJSON_DOM_PARSEDJSON_ITERATOR_H
+2 -2
View File
@@ -412,7 +412,7 @@ simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const s
// If there were more than 18 digits, we may have overflowed the integer. We have to do
// something!!!!
if (simdjson_unlikely(p > start_exp+18)) {
// Skip leading zeroes: 1e000000000000000000001 is technically valid and doesn't overflow
// Skip leading zeroes: 1e000000000000000000001 is technically valid and does not overflow
while (*start_exp == '0') { start_exp++; }
// 19 digits could overflow int64_t and is kind of absurd anyway. We don't
// support exponents smaller than -999,999,999,999,999,999 and bigger
@@ -609,7 +609,7 @@ simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) {
} else if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INVALID_NUMBER(src); }
}
// Write unsigned if it doesn't fit in a signed integer.
// Write unsigned if it does not fit in a signed integer.
if (i > uint64_t(INT64_MAX)) {
WRITE_UNSIGNED(i, src, writer);
} else {
+58 -1
View File
@@ -163,6 +163,59 @@ inline simdjson_result<value> array::at_pointer(std::string_view json_pointer) n
return child;
}
inline std::string json_path_to_pointer_conversion(std::string_view json_path) {
if (json_path.empty() || (json_path.front() != '.' &&
json_path.front() != '[')) {
return "-1"; // This is just a sentinel value, the caller should check for this and return an error.
}
std::string result;
// Reserve space to reduce allocations, adjusting for potential increases due
// to escaping.
result.reserve(json_path.size() * 2);
size_t i = 0;
while (i < json_path.length()) {
if (json_path[i] == '.') {
result += '/';
} else if (json_path[i] == '[') {
result += '/';
++i; // Move past the '['
while (i < json_path.length() && json_path[i] != ']') {
if (json_path[i] == '~') {
result += "~0";
} else if (json_path[i] == '/') {
result += "~1";
} else {
result += json_path[i];
}
++i;
}
if (i == json_path.length() || json_path[i] != ']') {
return "-1"; // Using sentinel value that will be handled as an error by the caller.
}
} else {
if (json_path[i] == '~') {
result += "~0";
} else if (json_path[i] == '/') {
result += "~1";
} else {
result += json_path[i];
}
}
++i;
}
return result;
}
inline simdjson_result<value> array::at_path(std::string_view json_path) noexcept {
auto json_pointer = json_path_to_pointer_conversion(json_path);
if (json_pointer == "-1") { return INVALID_JSON_POINTER; }
return at_pointer(json_pointer);
}
simdjson_inline simdjson_result<value> array::at(size_t index) noexcept {
size_t i = 0;
for (auto value : *this) {
@@ -217,10 +270,14 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdj
if (error()) { return error(); }
return first.at_pointer(json_pointer);
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array>::at_path(std::string_view json_path) noexcept {
if (error()) { return error(); }
return first.at_path(json_path);
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array>::raw_json() noexcept {
if (error()) { return error(); }
return first.raw_json();
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_INL_H
#endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_INL_H
+18 -1
View File
@@ -100,6 +100,22 @@ public:
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
*/
inline simdjson_result<value> at_pointer(std::string_view json_pointer) noexcept;
/**
* Get the value associated with the given JSONPath expression. We only support
* JSONPath queries that trivially convertible to JSON Pointer queries: key
* names and array indices.
*
* https://datatracker.ietf.org/doc/html/draft-normington-jsonpath-00
*
* @return The value associated with the given JSONPath expression, or:
* - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails
* - NO_SUCH_FIELD if a field does not exist in an object
* - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length
* - INCORRECT_TYPE if a non-integer is used to access an array
*/
inline simdjson_result<value> at_path(std::string_view json_path) noexcept;
/**
* Consumes the array and returns a string_view instance corresponding to the
* array as represented in JSON. It points inside the original document.
@@ -190,10 +206,11 @@ public:
inline simdjson_result<bool> reset() & noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at(size_t index) noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_path(std::string_view json_path) noexcept;
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_H
#endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_H
@@ -7,6 +7,8 @@
#include "simdjson/generic/ondemand/array_iterator.h"
#include "simdjson/generic/ondemand/document.h"
#include "simdjson/generic/ondemand/json_iterator-inl.h"
#include "simdjson/generic/ondemand/json_path_to_pointer_conversion.h"
#include "simdjson/generic/ondemand/json_path_to_pointer_conversion-inl.h"
#include "simdjson/generic/ondemand/json_type.h"
#include "simdjson/generic/ondemand/object-inl.h"
#include "simdjson/generic/ondemand/raw_json_string.h"
@@ -181,6 +183,8 @@ template<typename T> simdjson_inline error_code document::get(T &out) && noexcep
}
#if SIMDJSON_EXCEPTIONS
template <class T>
simdjson_inline document::operator T() noexcept(false) { return get<T>(); }
simdjson_inline document::operator array() & noexcept(false) { return get_array(); }
simdjson_inline document::operator object() & noexcept(false) { return get_object(); }
simdjson_inline document::operator uint64_t() noexcept(false) { return get_uint64(); }
@@ -265,6 +269,13 @@ simdjson_inline simdjson_result<bool> document::is_scalar() noexcept {
return ! ((this_type == json_type::array) || (this_type == json_type::object));
}
simdjson_inline simdjson_result<bool> document::is_string() noexcept {
json_type this_type;
auto error = type().get(this_type);
if(error) { return error; }
return (this_type == json_type::string);
}
simdjson_inline bool document::is_negative() noexcept {
return get_root_value_iterator().is_root_negative();
}
@@ -305,6 +316,23 @@ simdjson_inline simdjson_result<value> document::at_pointer(std::string_view jso
}
}
simdjson_inline simdjson_result<value> document::at_path(std::string_view json_path) noexcept {
rewind(); // Rewind the document each time at_pointer is called
if (json_path.empty()) {
return this->get_value();
}
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t) {
case json_type::array:
return (*this).get_array().at_path(json_path);
case json_type::object:
return (*this).get_object().at_path(json_path);
default:
return INVALID_JSON_POINTER;
}
}
} // namespace ondemand
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
@@ -480,6 +508,10 @@ simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::o
return first.is_scalar();
}
simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::is_string() noexcept {
if (error()) { return error(); }
return first.is_string();
}
simdjson_inline bool simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::is_negative() noexcept {
if (error()) { return error(); }
@@ -568,6 +600,10 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjs
return first.at_pointer(json_pointer);
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::at_path(std::string_view json_path) noexcept {
if (error()) { return error(); }
return first.at_path(json_path);
}
} // namespace simdjson
@@ -609,15 +645,25 @@ simdjson_inline simdjson_result<raw_json_string> document_reference::get_raw_jso
simdjson_inline simdjson_result<bool> document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); }
simdjson_inline simdjson_result<value> document_reference::get_value() noexcept { return doc->get_value(); }
simdjson_inline simdjson_result<bool> document_reference::is_null() noexcept { return doc->get_root_value_iterator().is_root_null(false); }
template<> simdjson_inline simdjson_result<array> document_reference::get() & noexcept { return get_array(); }
template<> simdjson_inline simdjson_result<object> document_reference::get() & noexcept { return get_object(); }
template<> simdjson_inline simdjson_result<raw_json_string> document_reference::get() & noexcept { return get_raw_json_string(); }
template<> simdjson_inline simdjson_result<std::string_view> document_reference::get() & noexcept { return get_string(false); }
template<> simdjson_inline simdjson_result<double> document_reference::get() & noexcept { return get_double(); }
template<> simdjson_inline simdjson_result<uint64_t> document_reference::get() & noexcept { return get_uint64(); }
template<> simdjson_inline simdjson_result<int64_t> document_reference::get() & noexcept { return get_int64(); }
template<> simdjson_inline simdjson_result<bool> document_reference::get() & noexcept { return get_bool(); }
template<> simdjson_inline simdjson_result<value> document_reference::get() & noexcept { return get_value(); }
#if SIMDJSON_EXCEPTIONS
template <class T>
simdjson_inline document_reference::operator T() noexcept(false) { return get<T>(); }
simdjson_inline document_reference::operator array() & noexcept(false) { return array(*doc); }
simdjson_inline document_reference::operator object() & noexcept(false) { return object(*doc); }
simdjson_inline document_reference::operator uint64_t() noexcept(false) { return get_uint64(); }
simdjson_inline document_reference::operator int64_t() noexcept(false) { return get_int64(); }
simdjson_inline document_reference::operator double() noexcept(false) { return get_double(); }
simdjson_inline document_reference::operator std::string_view() noexcept(false) { return std::string_view(*doc); }
simdjson_inline document_reference::operator raw_json_string() noexcept(false) { return raw_json_string(*doc); }
simdjson_inline document_reference::operator raw_json_string() noexcept(false) { return get_raw_json_string(); }
simdjson_inline document_reference::operator bool() noexcept(false) { return get_bool(); }
simdjson_inline document_reference::operator value() noexcept(false) { return value(*doc); }
#endif
@@ -634,6 +680,7 @@ simdjson_inline simdjson_result<value> document_reference::find_field_unordered(
simdjson_inline simdjson_result<value> document_reference::find_field_unordered(const char *key) & noexcept { return doc->find_field_unordered(key); }
simdjson_inline simdjson_result<json_type> document_reference::type() noexcept { return doc->type(); }
simdjson_inline simdjson_result<bool> document_reference::is_scalar() noexcept { return doc->is_scalar(); }
simdjson_inline simdjson_result<bool> document_reference::is_string() noexcept { return doc->is_string(); }
simdjson_inline simdjson_result<const char *> document_reference::current_location() noexcept { return doc->current_location(); }
simdjson_inline int32_t document_reference::current_depth() const noexcept { return doc->current_depth(); }
simdjson_inline bool document_reference::is_negative() noexcept { return doc->is_negative(); }
@@ -642,6 +689,7 @@ simdjson_inline simdjson_result<number_type> document_reference::get_number_type
simdjson_inline simdjson_result<number> document_reference::get_number() noexcept { return doc->get_root_value_iterator().get_root_number(false); }
simdjson_inline simdjson_result<std::string_view> document_reference::raw_json_token() noexcept { return doc->raw_json_token(); }
simdjson_inline simdjson_result<value> document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); }
simdjson_inline simdjson_result<value> document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); }
simdjson_inline simdjson_result<std::string_view> document_reference::raw_json() noexcept { return doc->raw_json();}
simdjson_inline document_reference::operator document&() const noexcept { return *doc; }
@@ -773,6 +821,10 @@ simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::o
if (error()) { return error(); }
return first.is_scalar();
}
simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::is_string() noexcept {
if (error()) { return error(); }
return first.is_string();
}
simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::is_negative() noexcept {
if (error()) { return error(); }
return first.is_negative();
@@ -843,7 +895,13 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjs
return first.at_pointer(json_pointer);
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::at_path(std::string_view json_path) noexcept {
if (error()) {
return error();
}
return first.at_path(json_path);
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H
#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H
+57 -4
View File
@@ -212,6 +212,8 @@ public:
template<typename T> simdjson_inline error_code get(T &out) && noexcept;
#if SIMDJSON_EXCEPTIONS
template <class T>
explicit simdjson_inline operator T() noexcept(false);
/**
* Cast this JSON value to an array.
*
@@ -430,6 +432,14 @@ public:
*/
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
/**
* Checks whether the document is a string.
*
* @returns true if the type is string
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
*/
simdjson_inline simdjson_result<bool> is_string() noexcept;
/**
* Checks whether the document is a negative number.
*
@@ -577,6 +587,14 @@ public:
* auto doc = parser.iterate(json);
* doc.at_pointer("//a/1") == 20
*
* Key values are matched exactly, without unescaping or Unicode normalization.
* We do a byte-by-byte comparison. E.g.
*
* const padded_string json = "{\"\\u00E9\":123}"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("/\\u00E9") == 123
* doc.at_pointer((const char*)u8"/\u00E9") returns an error (NO_SUCH_FIELD)
*
* Note that at_pointer() automatically calls rewind between each call. Thus
* all values, objects and arrays that you have created so far (including unescaped strings)
* are invalidated. After calling at_pointer, you need to consume the result: string values
@@ -593,6 +611,30 @@ public:
* - SCALAR_DOCUMENT_AS_VALUE if the json_pointer is empty and the document is not a scalar (see is_scalar() function).
*/
simdjson_inline simdjson_result<value> at_pointer(std::string_view json_pointer) noexcept;
/**
* Get the value associated with the given JSONPath expression. We only support
* JSONPath queries that trivially convertible to JSON Pointer queries: key
* names and array indices.
*
* https://datatracker.ietf.org/doc/html/draft-normington-jsonpath-00
*
* Key values are matched exactly, without unescaping or Unicode normalization.
* We do a byte-by-byte comparison. E.g.
*
* const padded_string json = "{\"\\u00E9\":123}"_padded;
* auto doc = parser.iterate(json);
* doc.at_path(".\\u00E9") == 123
* doc.at_path((const char*)u8".\u00E9") returns an error (NO_SUCH_FIELD)
*
* @return The value associated with the given JSONPath expression, or:
* - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails
* - NO_SUCH_FIELD if a field does not exist in an object
* - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length
* - INCORRECT_TYPE if a non-integer is used to access an array
*/
simdjson_inline simdjson_result<value> at_path(std::string_view json_path) noexcept;
/**
* Consumes the document and returns a string_view instance corresponding to the
* document as represented in JSON. It points inside the original byte array containing
@@ -658,10 +700,12 @@ public:
simdjson_inline simdjson_result<value> get_value() noexcept;
simdjson_inline simdjson_result<bool> is_null() noexcept;
template<typename T> simdjson_inline simdjson_result<T> get() & noexcept;
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
simdjson_inline operator document&() const noexcept;
#if SIMDJSON_EXCEPTIONS
template <class T>
explicit simdjson_inline operator T() noexcept(false);
simdjson_inline operator array() & noexcept(false);
simdjson_inline operator object() & noexcept(false);
simdjson_inline operator uint64_t() noexcept(false);
@@ -686,6 +730,7 @@ public:
simdjson_inline simdjson_result<json_type> type() noexcept;
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
simdjson_inline simdjson_result<bool> is_string() noexcept;
simdjson_inline simdjson_result<const char *> current_location() noexcept;
simdjson_inline int32_t current_depth() const noexcept;
@@ -695,6 +740,8 @@ public:
simdjson_inline simdjson_result<number> get_number() noexcept;
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
simdjson_inline simdjson_result<value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<value> at_path(std::string_view json_path) noexcept;
private:
document *doc{nullptr};
};
@@ -734,8 +781,9 @@ public:
template<typename T> simdjson_inline error_code get(T &out) & noexcept;
template<typename T> simdjson_inline error_code get(T &out) && noexcept;
#if SIMDJSON_EXCEPTIONS
template <class T>
explicit simdjson_inline operator T() noexcept(false);
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::array() & noexcept(false);
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::object() & noexcept(false);
simdjson_inline operator uint64_t() noexcept(false);
@@ -759,6 +807,7 @@ public:
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field_unordered(const char *key) & noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::json_type> type() noexcept;
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
simdjson_inline simdjson_result<bool> is_string() noexcept;
simdjson_inline simdjson_result<const char *> current_location() noexcept;
simdjson_inline int32_t current_depth() const noexcept;
simdjson_inline bool at_end() const noexcept;
@@ -770,6 +819,7 @@ public:
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_path(std::string_view json_path) noexcept;
};
@@ -802,8 +852,9 @@ public:
simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> get_value() noexcept;
simdjson_inline simdjson_result<bool> is_null() noexcept;
#if SIMDJSON_EXCEPTIONS
template <class T>
explicit simdjson_inline operator T() noexcept(false);
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::array() & noexcept(false);
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::object() & noexcept(false);
simdjson_inline operator uint64_t() noexcept(false);
@@ -827,6 +878,7 @@ public:
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field_unordered(const char *key) & noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::json_type> type() noexcept;
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
simdjson_inline simdjson_result<bool> is_string() noexcept;
simdjson_inline simdjson_result<const char *> current_location() noexcept;
simdjson_inline simdjson_result<int32_t> current_depth() const noexcept;
simdjson_inline simdjson_result<bool> is_negative() noexcept;
@@ -837,9 +889,10 @@ public:
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_path(std::string_view json_path) noexcept;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_H
#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_H
@@ -122,10 +122,9 @@ public:
class iterator {
public:
using value_type = simdjson_result<document>;
using reference = value_type;
using reference = simdjson_result<ondemand::document_reference>;
using pointer = void;
using difference_type = std::ptrdiff_t;
using iterator_category = std::input_iterator_tag;
/**
@@ -135,7 +134,7 @@ public:
/**
* Get the current document (or error).
*/
simdjson_inline simdjson_result<ondemand::document_reference> operator*() noexcept;
simdjson_inline reference operator*() noexcept;
/**
* Advance to the next document (prefix).
*/
@@ -335,4 +334,4 @@ public:
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_H
#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_H
+14 -1
View File
@@ -12,7 +12,7 @@ namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
namespace ondemand {
// clang 6 doesn't think the default constructor can be noexcept, so we make it explicit
// clang 6 does not think the default constructor can be noexcept, so we make it explicit
simdjson_inline field::field() noexcept : std::pair<raw_json_string, ondemand::value>() {}
simdjson_inline field::field(raw_json_string key, ondemand::value &&value) noexcept
@@ -43,6 +43,12 @@ simdjson_inline raw_json_string field::key() const noexcept {
return first;
}
simdjson_inline std::string_view field::key_raw_json_token() const noexcept {
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us.
return std::string_view(reinterpret_cast<const char*>(first.buf-1), second.iter._json_iter->token.peek(-1) - first.buf + 1);
}
simdjson_inline value &field::value() & noexcept {
return second;
}
@@ -76,10 +82,17 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::raw_json_stri
if (error()) { return error(); }
return first.key();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::field>::key_raw_json_token() noexcept {
if (error()) { return error(); }
return first.key_raw_json_token();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::field>::unescaped_key(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.unescaped_key(allow_replacement);
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::field>::value() noexcept {
if (error()) { return error(); }
return std::move(first.value());
@@ -42,6 +42,11 @@ public:
* an unescaped C string: e.g., key() == "test".
*/
simdjson_inline raw_json_string key() const noexcept;
/**
* Get the unprocessed key as a string_view. This includes the quotes and may include
* some spaces after the last quote.
*/
simdjson_inline std::string_view key_raw_json_token() const noexcept;
/**
* Get the field value.
*/
@@ -74,6 +79,7 @@ public:
simdjson_inline simdjson_result<std::string_view> unescaped_key(bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::raw_json_string> key() noexcept;
simdjson_inline simdjson_result<std::string_view> key_raw_json_token() noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> value() noexcept;
};
@@ -146,7 +146,7 @@ public:
* @param delta The relative position of the token to retrieve. e.g. 0 = next token, -1 = prev token.
*
* TODO consider a string_view, assuming the length will get stripped out by the optimizer when
* it isn't used ...
* it is not used ...
*/
simdjson_inline const uint8_t *peek(int32_t delta=0) const noexcept;
/**
@@ -174,7 +174,7 @@ public:
* @param position The position of the token to retrieve.
*
* TODO consider a string_view, assuming the length will get stripped out by the optimizer when
* it isn't used ...
* it is not used ...
*/
simdjson_inline const uint8_t *peek(token_position position) const noexcept;
/**
@@ -191,7 +191,7 @@ public:
* This is not null-terminated; it is a view into the JSON.
*
* TODO consider a string_view, assuming the length will get stripped out by the optimizer when
* it isn't used ...
* it is not used ...
*/
simdjson_inline const uint8_t *peek_last() const noexcept;
@@ -300,6 +300,7 @@ protected:
friend class raw_json_string;
friend class parser;
friend class value_iterator;
friend class field;
template <typename... Args>
friend simdjson_inline void logger::log_line(const json_iterator &iter, const char *title_prefix, const char *title, std::string_view detail, int delta, int depth_delta, logger::log_level level, Args&&... args) noexcept;
template <typename... Args>
@@ -0,0 +1,67 @@
#pragma once
#ifndef SIMDJSON_ONDEMAND_GENERIC_JSON_PATH_TO_POINTER_CONVERSION_INL_H
#define SIMDJSON_ONDEMAND_GENERIC_JSON_PATH_TO_POINTER_CONVERSION_INL_H
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
#define SIMDJSON_GENERIC_ONDEMAND_JSON_PATH_TO_POINTER_CONVERSION_INL_H
#include "simdjson/generic/ondemand/json_path_to_pointer_conversion.h"
#endif // SIMDJSON_CONDITIONAL_INCLUDE
namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {}
namespace ondemand {
simdjson_inline std::string json_path_to_pointer_conversion(std::string_view json_path) {
if (json_path.empty() || (json_path.front() != '.' && json_path.front() != '[') {
return "-1"; // Sentinel value to be handled as an error by the caller.
}
std::string result;
// Reserve space to reduce allocations, adjusting for potential increases due
// to escaping.
result.reserve(json_path.size() * 2);
// Skip the initial '.' as it's assumed every path starts with it.
size_t i = 0;
while (i < json_path.length()) {
if (json_path[i] == '.') {
result += '/';
} else if (json_path[i] == '[') {
result += '/';
++i; // Move past the '['
while (i < json_path.length() && json_path[i] != ']') {
if (json_path[i] == '~') {
result += "~0";
} else if (json_path[i] == '/') {
result += "~1";
} else {
result += json_path[i];
}
++i;
}
if (i == json_path.length() || json_path[i] != ']') {
return "-1"; // Returning sentinel value that will be handled as an error by the caller
}
} else {
if (json_path[i] == '~') {
result += "~0";
} else if (json_path[i] == '/') {
result += "~1";
} else {
result += json_path[i];
}
}
++i;
}
return simdjson_result<std::string>(result);
}
} // namespace ondemand
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
#endif // SIMDJSON_ONDEMAND_GENERIC_JSON_PATH_TO_POINTER_CONVERSION_INL_H
@@ -0,0 +1,22 @@
#pragma once
#ifndef SIMDJSON_ONDEMAND_GENERIC_JSON_PATH_TO_POINTER_CONVERSION_H
#define SIMDJSON_ONDEMAND_GENERIC_JSON_PATH_TO_POINTER_CONVERSION_H
namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
namespace internal {
/**
* Converts JSONPath to JSON Pointer.
* @param json_path The JSONPath string to be converted.
* @return A string containing the equivalent JSON Pointer.
* @throws simdjson_error If the conversion fails.
*/
simdjson_inline std::string json_path_to_pointer_conversion(std::string_view json_path);
} // namespace internal
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
#endif // SIMDJSON_JSON_PATH_TO_POINTER_CONVERSION_H
+17 -1
View File
@@ -164,6 +164,14 @@ inline simdjson_result<value> object::at_pointer(std::string_view json_pointer)
return child;
}
inline simdjson_result<value> object::at_path(std::string_view json_path) noexcept {
auto json_pointer = json_path_to_pointer_conversion(json_path);
if (json_pointer == "-1") {
return INVALID_JSON_POINTER;
}
return at_pointer(json_pointer);
}
simdjson_inline simdjson_result<size_t> object::count_fields() & noexcept {
size_t count{0};
// Important: we do not consume any of the values.
@@ -236,6 +244,14 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjs
return first.at_pointer(json_pointer);
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object>::at_path(
std::string_view json_path) noexcept {
if (error()) {
return error();
}
return first.at_path(json_path);
}
inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object>::reset() noexcept {
if (error()) { return error(); }
return first.reset();
@@ -257,4 +273,4 @@ simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPL
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_OBJECT_INL_H
#endif // SIMDJSON_GENERIC_ONDEMAND_OBJECT_INL_H
+16 -1
View File
@@ -140,6 +140,19 @@ public:
*/
inline simdjson_result<value> at_pointer(std::string_view json_pointer) noexcept;
/**
* Get the value associated with the given JSONPath expression. We only support
* JSONPath queries that trivially convertible to JSON Pointer queries: key
* names and array indices.
*
* @return The value associated with the given JSONPath expression, or:
* - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails
* - NO_SUCH_FIELD if a field does not exist in an object
* - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length
* - INCORRECT_TYPE if a non-integer is used to access an array
*/
inline simdjson_result<value> at_path(std::string_view json_path) noexcept;
/**
* Reset the iterator so that we are pointing back at the
* beginning of the object. You should still consume values only once even if you
@@ -227,6 +240,8 @@ public:
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> operator[](std::string_view key) & noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> operator[](std::string_view key) && noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_path(std::string_view json_path) noexcept;
inline simdjson_result<bool> reset() noexcept;
inline simdjson_result<bool> is_empty() noexcept;
inline simdjson_result<size_t> count_fields() & noexcept;
@@ -236,4 +251,4 @@ public:
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_OBJECT_H
#endif // SIMDJSON_GENERIC_ONDEMAND_OBJECT_H
@@ -47,7 +47,7 @@ public:
* 1 = next token, -1 = prev token.
*
* TODO consider a string_view, assuming the length will get stripped out by the optimizer when
* it isn't used ...
* it is not used...
*/
simdjson_inline const uint8_t *peek(int32_t delta=0) const noexcept;
/**
+50 -3
View File
@@ -6,6 +6,8 @@
#include "simdjson/generic/ondemand/array.h"
#include "simdjson/generic/ondemand/array_iterator.h"
#include "simdjson/generic/ondemand/json_iterator.h"
#include "simdjson/generic/ondemand/json_path_to_pointer_conversion.h"
#include "simdjson/generic/ondemand/json_path_to_pointer_conversion-inl.h"
#include "simdjson/generic/ondemand/json_type.h"
#include "simdjson/generic/ondemand/object.h"
#include "simdjson/generic/ondemand/raw_json_string.h"
@@ -93,6 +95,10 @@ template<typename T> simdjson_inline error_code value::get(T &out) noexcept {
}
#if SIMDJSON_EXCEPTIONS
template <class T>
simdjson_inline value::operator T() noexcept(false) {
return get<T>();
}
simdjson_inline value::operator array() noexcept(false) {
return get_array();
}
@@ -179,6 +185,14 @@ simdjson_inline simdjson_result<bool> value::is_scalar() noexcept {
return ! ((this_type == json_type::array) || (this_type == json_type::object));
}
simdjson_inline simdjson_result<bool> value::is_string() noexcept {
json_type this_type;
auto error = type().get(this_type);
if(error) { return error; }
return (this_type == json_type::string);
}
simdjson_inline bool value::is_negative() noexcept {
return iter.is_negative();
}
@@ -239,6 +253,19 @@ simdjson_inline simdjson_result<value> value::at_pointer(std::string_view json_p
}
}
simdjson_inline simdjson_result<value> value::at_path(std::string_view json_path) noexcept {
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t) {
case json_type::array:
return (*this).get_array().at_path(json_path);
case json_type::object:
return (*this).get_object().at_path(json_path);
default:
return INVALID_JSON_POINTER;
}
}
} // namespace ondemand
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
@@ -392,6 +419,10 @@ simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::o
if (error()) { return error(); }
return first.is_scalar();
}
simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::is_string() noexcept {
if (error()) { return error(); }
return first.is_string();
}
simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::is_negative() noexcept {
if (error()) { return error(); }
return first.is_negative();
@@ -409,6 +440,11 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::number> simdj
return first.get_number();
}
#if SIMDJSON_EXCEPTIONS
template <class T>
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::operator T() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return static_cast<T>(first);
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::operator SIMDJSON_IMPLEMENTATION::ondemand::array() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
@@ -463,11 +499,22 @@ simdjson_inline simdjson_result<int32_t> simdjson_result<SIMDJSON_IMPLEMENTATION
return first.current_depth();
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::at_pointer(
std::string_view json_pointer) noexcept {
if (error()) {
return error();
}
return first.at_pointer(json_pointer);
}
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::at_path(
std::string_view json_path) noexcept {
if (error()) {
return error();
}
return first.at_path(json_path);
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H
#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H
+35 -1
View File
@@ -194,6 +194,14 @@ public:
simdjson_inline simdjson_result<bool> is_null() noexcept;
#if SIMDJSON_EXCEPTIONS
/**
* Cast this JSON value to an instance of type T. The programmer is responsible for
* providing an implementation of get<T> for the type T.
*
* @returns An instance of type T
*/
template <class T>
explicit simdjson_inline operator T() noexcept(false);
/**
* Cast this JSON value to an array.
*
@@ -393,6 +401,13 @@ public:
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
*/
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
/**
* Checks whether the value is a string.
*
* @returns true if the type is string
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
*/
simdjson_inline simdjson_result<bool> is_string() noexcept;
/**
* Checks whether the value is a negative number.
@@ -568,6 +583,20 @@ public:
*/
simdjson_inline simdjson_result<value> at_pointer(std::string_view json_pointer) noexcept;
/**
* Get the value associated with the given JSONPath expression. We only support
* JSONPath queries that trivially convertible to JSON Pointer queries: key
* names and array indices.
*
* @return The value associated with the given JSONPath expression, or:
* - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails
* - NO_SUCH_FIELD if a field does not exist in an object
* - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length
* - INCORRECT_TYPE if a non-integer is used to access an array
*/
simdjson_inline simdjson_result<value> at_path(std::string_view at_path) noexcept;
protected:
/**
* Create a value.
@@ -607,6 +636,7 @@ protected:
friend class object;
friend struct simdjson_result<value>;
friend struct simdjson_result<field>;
friend class field;
};
} // namespace ondemand
@@ -644,6 +674,8 @@ public:
template<typename T> simdjson_inline error_code get(T &out) noexcept;
#if SIMDJSON_EXCEPTIONS
template <class T>
explicit simdjson_inline operator T() noexcept(false);
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::array() noexcept(false);
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::object() noexcept(false);
simdjson_inline operator uint64_t() noexcept(false);
@@ -719,6 +751,7 @@ public:
*/
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::json_type> type() noexcept;
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
simdjson_inline simdjson_result<bool> is_string() noexcept;
simdjson_inline simdjson_result<bool> is_negative() noexcept;
simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::number_type> get_number_type() noexcept;
@@ -733,8 +766,9 @@ public:
/** @copydoc simdjson_inline int32_t current_depth() const noexcept */
simdjson_inline simdjson_result<int32_t> current_depth() const noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_path(std::string_view json_path) noexcept;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_H
#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_H
@@ -469,6 +469,7 @@ protected:
friend class object;
friend class array;
friend class value;
friend class field;
}; // value_iterator
} // namespace ondemand
+13 -1
View File
@@ -54,6 +54,14 @@ inline padded_string::padded_string(const char *data, size_t length) noexcept
std::memcpy(data_ptr, data, length);
}
}
#ifdef __cpp_char8_t
inline padded_string::padded_string(const char8_t *data, size_t length) noexcept
: viable_size(length), data_ptr(internal::allocate_padded_buffer(length)) {
if ((data != nullptr) && (data_ptr != nullptr)) {
std::memcpy(data_ptr, reinterpret_cast<const char *>(data), length);
}
}
#endif
// note: do not pass std::string arguments by value
inline padded_string::padded_string(const std::string & str_ ) noexcept
: viable_size(str_.size()), data_ptr(internal::allocate_padded_buffer(str_.size())) {
@@ -173,5 +181,9 @@ inline simdjson_result<padded_string> padded_string::load(std::string_view filen
inline simdjson::padded_string operator "" _padded(const char *str, size_t len) {
return simdjson::padded_string(str, len);
}
#ifdef __cpp_char8_t
inline simdjson::padded_string operator "" _padded(const char8_t *str, size_t len) {
return simdjson::padded_string(reinterpret_cast<const char8_t *>(str), len);
}
#endif
#endif // SIMDJSON_PADDED_STRING_INL_H
+6
View File
@@ -39,6 +39,9 @@ struct padded_string final {
* @param length the number of bytes to copy
*/
explicit inline padded_string(const char *data, size_t length) noexcept;
#ifdef __cpp_char8_t
explicit inline padded_string(const char8_t *data, size_t length) noexcept;
#endif
/**
* Create a new padded string by copying the given input.
*
@@ -150,6 +153,9 @@ inline std::ostream& operator<<(std::ostream& out, simdjson_result<padded_string
// This is deliberately outside of simdjson so that people get it without having to use the namespace
inline simdjson::padded_string operator "" _padded(const char *str, size_t len);
#ifdef __cpp_char8_t
inline simdjson::padded_string operator "" _padded(const char8_t *str, size_t len);
#endif
namespace simdjson {
namespace internal {
+8 -1
View File
@@ -11,13 +11,19 @@ namespace simdjson {
inline padded_string_view::padded_string_view(const char* s, size_t len, size_t capacity) noexcept
: std::string_view(s, len), _capacity(capacity)
{
if(_capacity < len) { _capacity = len; }
}
inline padded_string_view::padded_string_view(const uint8_t* s, size_t len, size_t capacity) noexcept
: padded_string_view(reinterpret_cast<const char*>(s), len, capacity)
{
}
#ifdef __cpp_char8_t
inline padded_string_view::padded_string_view(const char8_t* s, size_t len, size_t capacity) noexcept
: padded_string_view(reinterpret_cast<const char*>(s), len, capacity)
{
}
#endif
inline padded_string_view::padded_string_view(const std::string &s) noexcept
: std::string_view(s), _capacity(s.capacity())
{
@@ -26,6 +32,7 @@ inline padded_string_view::padded_string_view(const std::string &s) noexcept
inline padded_string_view::padded_string_view(std::string_view s, size_t capacity) noexcept
: std::string_view(s), _capacity(capacity)
{
if(_capacity < s.length()) { _capacity = s.length(); }
}
inline size_t padded_string_view::capacity() const noexcept { return _capacity; }
+7 -3
View File
@@ -28,12 +28,15 @@ public:
*
* @param s The string.
* @param len The length of the string (not including padding).
* @param capacity The allocated length of the string, including padding.
* @param capacity The allocated length of the string, including padding. If the capacity is less
* than the length, the capacity will be set to the length.
*/
explicit inline padded_string_view(const char* s, size_t len, size_t capacity) noexcept;
/** overload explicit inline padded_string_view(const char* s, size_t len) noexcept */
explicit inline padded_string_view(const uint8_t* s, size_t len, size_t capacity) noexcept;
#ifdef __cpp_char8_t
explicit inline padded_string_view(const char8_t* s, size_t len, size_t capacity) noexcept;
#endif
/**
* Promise the given string has at least SIMDJSON_PADDING extra bytes allocated to it.
*
@@ -47,7 +50,8 @@ public:
* Promise the given string_view has at least SIMDJSON_PADDING extra bytes allocated to it.
*
* @param s The string.
* @param capacity The allocated length of the string, including padding.
* @param capacity The allocated length of the string, including padding. If the capacity is less
* than the length, the capacity will be set to the length.
*/
explicit inline padded_string_view(std::string_view s, size_t capacity) noexcept;
+1 -1
View File
@@ -1,6 +1,6 @@
/**
* @file
* @deprecated We'll be removing this file so it isn't confused with the top level simdjson.h
* @deprecated We will be removing this file so it is not confused with the top level simdjson.h
*/
#ifndef SIMDJSON_SIMDJSON_H
#define SIMDJSON_SIMDJSON_H
+3 -3
View File
@@ -4,7 +4,7 @@
#define SIMDJSON_SIMDJSON_VERSION_H
/** The version of simdjson being used (major.minor.revision) */
#define SIMDJSON_VERSION "3.6.4"
#define SIMDJSON_VERSION "3.7.0"
namespace simdjson {
enum {
@@ -15,11 +15,11 @@ enum {
/**
* The minor version (major.MINOR.revision) of simdjson being used.
*/
SIMDJSON_VERSION_MINOR = 6,
SIMDJSON_VERSION_MINOR = 7,
/**
* The revision (major.minor.REVISION) of simdjson being used.
*/
SIMDJSON_VERSION_REVISION = 4
SIMDJSON_VERSION_REVISION = 0
};
} // namespace simdjson
+6 -6
View File
@@ -16,10 +16,6 @@ set(SINGLEHEADER_REPO_FILES
)
set_source_files_properties(${SINGLEHEADER_FILES} PROPERTIES GENERATED TRUE)
find_program(BASH bash)
# Under Windows, exectuting a bash script works, except that you cannot generally
# do bash C:/path to my script. You need a "mounted" path: /mnt/c/path
find_package(Python3 COMPONENTS Interpreter)
if (Python3_Interpreter_FOUND AND (NOT WIN32))
@@ -127,7 +123,10 @@ include(${PROJECT_SOURCE_DIR}/cmake/add_compile_only_test.cmake)
# Under Windows you should not mix static and dynamic. Pick one. The following test is static.
if(NOT SIMDJSON_LEGACY_VISUAL_STUDIO AND NOT SIMDJSON_WINDOWS_DLL)
add_executable(amalgamate_demo $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/amalgamate_demo.cpp>)
target_link_libraries(amalgamate_demo simdjson-singleheader-include-source simdjson-internal-flags)
add_library(amalgamate_demo_simdjson $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/simdjson.cpp>)
target_link_libraries(amalgamate_demo amalgamate_demo_simdjson simdjson-internal-flags)
target_include_directories(amalgamate_demo_simdjson PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>)
target_include_directories(amalgamate_demo PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>)
add_test(amalgamate_demo amalgamate_demo ${EXAMPLE_JSON} ${EXAMPLE_NDJSON})
set_property(TEST amalgamate_demo APPEND PROPERTY LABELS per_implementation singleheader)
add_dependencies(singleheader_tests amalgamate_demo)
@@ -168,7 +167,8 @@ if (NOT (${CMAKE_CURRENT_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_BINARY_DIR}))
# breaks. Simply put: under Windows you should not mix static and dynamic. Pick one.
if(NOT SIMDJSON_LEGACY_VISUAL_STUDIO AND NOT SIMDJSON_WINDOWS_DLL)
add_executable(amalgamate_demo_direct_from_repository ${CMAKE_CURRENT_SOURCE_DIR}/amalgamate_demo.cpp)
target_link_libraries(amalgamate_demo_direct_from_repository simdjson-singleheader-include-source-direct-from-repository simdjson-internal-flags)
add_library(amalgamate_demo_direct_from_repository_simdjson ${CMAKE_CURRENT_SOURCE_DIR}/simdjson.cpp)
target_link_libraries(amalgamate_demo_direct_from_repository amalgamate_demo_direct_from_repository_simdjson simdjson-singleheader-include-source-direct-from-repository simdjson-internal-flags)
add_test(amalgamate_demo_direct_from_repository amalgamate_demo_direct_from_repository ${EXAMPLE_JSON} ${EXAMPLE_NDJSON})
set_property(TEST amalgamate_demo_direct_from_repository APPEND PROPERTY LABELS per_implementation singleheader)
add_dependencies(singleheader_tests amalgamate_demo_direct_from_repository)
+1 -1
View File
@@ -1,4 +1,4 @@
Try :
```
c++ -O3 -std=c++17 -pthread -o amalgamate_demo amalgamate_demo.cpp && ./amalgamate_demo ../jsonexamples/twitter.json ../jsonexamples/amazon_cellphones.ndjson
c++ -O3 -std=c++17 -pthread -o amalgamate_demo amalgamate_demo.cpp simdjson.cpp && ./amalgamate_demo ../jsonexamples/twitter.json ../jsonexamples/amazon_cellphones.ndjson
+1 -1
View File
@@ -39,7 +39,7 @@ else:
RelativeRoot = str # Literal['src','include'] # Literal not supported in Python 3.7 (CI)
RELATIVE_ROOTS: List[RelativeRoot] = ['src', 'include' ]
Implementation = str # Literal['arm64', 'fallback', 'haswell', 'icelake', 'ppc64', 'westmere'] # Literal not supported in Python 3.7 (CI)
IMPLEMENTATIONS: List[Implementation] = [ 'arm64', 'fallback', 'haswell', 'icelake', 'ppc64', 'westmere' ]
IMPLEMENTATIONS: List[Implementation] = [ 'arm64', 'haswell', 'icelake', 'ppc64', 'westmere', 'fallback' ]
GENERIC_INCLUDE = "simdjson/generic"
GENERIC_SRC = "generic"
BUILTIN = "simdjson/builtin"
-1
View File
@@ -1,4 +1,3 @@
#include "simdjson.cpp"
#include "simdjson.h"
#include <iostream>
+3964 -3926
View File
File diff suppressed because it is too large Load Diff
+2630 -1622
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -183,7 +183,7 @@ simdjson_inline size_t trim_partial_utf8(const uint8_t *buf, size_t len) {
// up enough CPU: the second half of the functions is highly serial, only using 1 execution core
// at a time. The second input's scans has some dependency on the first ones finishing it, but
// they can make a lot of progress before they need that information.
// 3. Step 1 doesn't use enough capacity, so we run some extra stuff while we're waiting for that
// 3. Step 1 does not use enough capacity, so we run some extra stuff while we're waiting for that
// to finish: utf-8 checks and generating the output from the last iteration.
//
// The reason we run 2 inputs at a time, is steps 2 and 3 are *still* not enough to soak up all
+42 -3
View File
@@ -98,6 +98,38 @@ static const simdjson::westmere::implementation* get_westmere_singleton() {
namespace simdjson {
namespace internal {
// When there is a single implementation, we should not pay a price
// for dispatching to the best implementation. We should just use the
// one we have. This is a compile-time check.
#define SIMDJSON_SINGLE_IMPLEMENTATION (SIMDJSON_IMPLEMENTATION_ICELAKE \
+ SIMDJSON_IMPLEMENTATION_HASWELL + SIMDJSON_IMPLEMENTATION_WESTMERE \
+ SIMDJSON_IMPLEMENTATION_ARM64 + SIMDJSON_IMPLEMENTATION_PPC64 \
+ SIMDJSON_IMPLEMENTATION_FALLBACK == 1)
#if SIMDJSON_SINGLE_IMPLEMENTATION
static const implementation* get_single_implementation() {
return
#if SIMDJSON_IMPLEMENTATION_ICELAKE
get_icelake_singleton();
#endif
#if SIMDJSON_IMPLEMENTATION_HASWELL
get_haswell_singleton();
#endif
#if SIMDJSON_IMPLEMENTATION_WESTMERE
get_westmere_singleton();
#endif
#if SIMDJSON_IMPLEMENTATION_ARM64
get_arm64_singleton();
#endif
#if SIMDJSON_IMPLEMENTATION_PPC64
get_ppc64_singleton();
#endif
#if SIMDJSON_IMPLEMENTATION_FALLBACK
get_fallback_singleton();
#endif
}
#endif
// Static array of known implementations. We're hoping these get baked into the executable
// without requiring a static initializer.
@@ -227,9 +259,16 @@ SIMDJSON_DLLIMPORTEXPORT const internal::available_implementation_list& get_avai
}
SIMDJSON_DLLIMPORTEXPORT internal::atomic_ptr<const implementation>& get_active_implementation() {
static const internal::detect_best_supported_implementation_on_first_use detect_best_supported_implementation_on_first_use_singleton;
static internal::atomic_ptr<const implementation> active_implementation{&detect_best_supported_implementation_on_first_use_singleton};
return active_implementation;
#if SIMDJSON_SINGLE_IMPLEMENTATION
// We immediately select the only implementation we have, skipping the
// detect_best_supported_implementation_on_first_use_singleton.
static internal::atomic_ptr<const implementation> active_implementation{internal::get_single_implementation()};
return active_implementation;
#else
static const internal::detect_best_supported_implementation_on_first_use detect_best_supported_implementation_on_first_use_singleton;
static internal::atomic_ptr<const implementation> active_implementation{&detect_best_supported_implementation_on_first_use_singleton};
return active_implementation;
#endif
}
simdjson_warn_unused error_code minify(const char *buf, size_t len, char *dst, size_t &dst_len) noexcept {
+3 -4
View File
@@ -23,9 +23,6 @@ SIMDJSON_PUSH_DISABLE_UNUSED_WARNINGS
#if SIMDJSON_IMPLEMENTATION_ARM64
#include <arm64.cpp>
#endif
#if SIMDJSON_IMPLEMENTATION_FALLBACK
#include <fallback.cpp>
#endif
#if SIMDJSON_IMPLEMENTATION_HASWELL
#include <haswell.cpp>
#endif
@@ -38,7 +35,9 @@ SIMDJSON_PUSH_DISABLE_UNUSED_WARNINGS
#if SIMDJSON_IMPLEMENTATION_WESTMERE
#include <westmere.cpp>
#endif
#if SIMDJSON_IMPLEMENTATION_FALLBACK
#include <fallback.cpp>
#endif
#undef SIMDJSON_CONDITIONAL_INCLUDE
SIMDJSON_POP_DISABLE_UNUSED_WARNINGS
+10 -8
View File
@@ -78,14 +78,16 @@ if (BASH AND (NOT WIN32) AND SIMDJSON_BASH AND (TARGET json2json)) # The scripts
$<TARGET_FILE:checkimplementation>
)
endif()
add_test(
NAME simdjson_force_implementation_error
COMMAND
${CMAKE_COMMAND} -E env
SIMDJSON_FORCE_IMPLEMENTATION=doesnotexist
$<TARGET_FILE:json2json> ${EXAMPLE_JSON}
)
set_tests_properties(simdjson_force_implementation_error PROPERTIES WILL_FAIL TRUE)
if(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL x86_64 OR CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL amd64)
add_test(
NAME simdjson_force_implementation_error
COMMAND
${CMAKE_COMMAND} -E env
SIMDJSON_FORCE_IMPLEMENTATION=doesnotexist
$<TARGET_FILE:json2json> ${EXAMPLE_JSON}
)
set_tests_properties(simdjson_force_implementation_error PROPERTIES WILL_FAIL TRUE)
endif()
endif()
#
+48 -8
View File
@@ -10,6 +10,7 @@
#include <sstream>
#include <utility>
#include <unistd.h>
using namespace std::string_literals;
#include "simdjson.h"
#include "cast_tester.h"
@@ -912,6 +913,43 @@ namespace dom_api_tests {
return true;
}
bool object_iterator_advance() {
std::cout << "Running " << __func__ << std::endl;
string json(R"({ "a": 1, "b": 2, "c": 3 })");
const char* expected_key[] = { "a", "b", "c" };
uint64_t expected_value[] = { 1, 2, 3 };
dom::parser parser;
dom::object object;
ASSERT_SUCCESS( parser.parse(json).get(object) );
auto iter = object.begin();
for (auto i = 0; i * sizeof(expected_value[0]) < sizeof(expected_value); i++) {
auto [key, value] = *iter;
ASSERT_EQUAL( key, expected_key[i] );
ASSERT_EQUAL( value.get_uint64().value_unsafe(), expected_value[i] );
std::advance( iter, 1 );
}
return true;
}
bool array_iterator_advance() {
std::cout << "Running " << __func__ << std::endl;
string json(R"([ 1, 10, 100 ])");
uint64_t expected_value[] = { 1, 10, 100 };
dom::parser parser;
dom::array array;
ASSERT_SUCCESS( parser.parse(json).get(array) );
auto iter = array.begin();
for (auto i = 0; i * sizeof(expected_value[0]) < sizeof(expected_value); i++) {
uint64_t v;
ASSERT_SUCCESS( (*iter).get(v) );
ASSERT_EQUAL( v, expected_value[i] );
std::advance( iter, 1 );
}
return true;
}
bool string_value() {
std::cout << "Running " << __func__ << std::endl;
string json(R"([ "hi", "has backslash\\" ])");
@@ -1291,6 +1329,8 @@ namespace dom_api_tests {
array_iterator() &&
object_iterator_empty() &&
array_iterator_empty() &&
object_iterator_advance() &&
array_iterator_advance() &&
string_value() &&
numeric_values() &&
boolean_values() &&
@@ -2240,14 +2280,14 @@ bool simple_overflows() {
std::cout << "Running " << __func__ << std::endl;
simdjson::dom::parser parser;
simdjson::dom::element doc;
ASSERT_ERROR( parser.parse(std::string("[f]")).get(doc), simdjson::F_ATOM_ERROR);
ASSERT_ERROR( parser.parse(std::string("[t]")).get(doc), simdjson::T_ATOM_ERROR);
ASSERT_ERROR( parser.parse(std::string("[n]")).get(doc), simdjson::N_ATOM_ERROR);
ASSERT_ERROR( parser.parse(std::string("[-]")).get(doc), simdjson::NUMBER_ERROR);
ASSERT_ERROR( parser.parse(std::string("{\"a\":f}")).get(doc), simdjson::F_ATOM_ERROR);
ASSERT_ERROR( parser.parse(std::string("{\"a\":t}")).get(doc), simdjson::T_ATOM_ERROR);
ASSERT_ERROR( parser.parse(std::string("{\"a\":n}")).get(doc), simdjson::N_ATOM_ERROR);
ASSERT_ERROR( parser.parse(std::string("{\"a\":-}")).get(doc), simdjson::NUMBER_ERROR);
ASSERT_ERROR( parser.parse("[f]"s).get(doc), simdjson::F_ATOM_ERROR);
ASSERT_ERROR( parser.parse("[t]"s).get(doc), simdjson::T_ATOM_ERROR);
ASSERT_ERROR( parser.parse("[n]"s).get(doc), simdjson::N_ATOM_ERROR);
ASSERT_ERROR( parser.parse("[-]"s).get(doc), simdjson::NUMBER_ERROR);
ASSERT_ERROR( parser.parse("{\"a\":f}"s).get(doc), simdjson::F_ATOM_ERROR);
ASSERT_ERROR( parser.parse("{\"a\":t}"s).get(doc), simdjson::T_ATOM_ERROR);
ASSERT_ERROR( parser.parse("{\"a\":n}"s).get(doc), simdjson::N_ATOM_ERROR);
ASSERT_ERROR( parser.parse("{\"a\":-}"s).get(doc), simdjson::NUMBER_ERROR);
return true;
}
+5 -4
View File
@@ -1,8 +1,9 @@
#include <string>
#include <vector>
#include <cctype>
#include <unistd.h>
#include <random>
#include <string>
#include <unistd.h>
#include <vector>
using namespace std::string_literals;
#include "simdjson.h"
#include "test_macros.h"
@@ -879,7 +880,7 @@ namespace document_stream_tests {
bool issue1649() {
std::cout << "Running " << __func__ << std::endl;
std::size_t batch_size = 637;
const auto json=simdjson::padded_string(std::string("\xd7"));
const auto json=simdjson::padded_string("\xd7"s);
simdjson::dom::parser parser;
simdjson::dom::document_stream docs;
if(parser.parse_many(json,batch_size).get(docs)) {
+5 -4
View File
@@ -1,6 +1,7 @@
#include <string>
#include <vector>
#include <unistd.h>
using namespace std::string_literals;
#include "simdjson.h"
#include "test_macros.h"
@@ -138,10 +139,10 @@ namespace document_tests {
if (n >= buf.size()) { abort(); }
data.emplace_back(std::string(buf.data(), n));
}
data.emplace_back(std::string("true"));
data.emplace_back(std::string("false"));
data.emplace_back(std::string("null"));
data.emplace_back(std::string("0.1"));
data.emplace_back("true"s);
data.emplace_back("false"s);
data.emplace_back("null"s);
data.emplace_back("0.1"s);
size_t maxsize = 0;
for (auto &s : data) {
if (maxsize < s.size())
+3 -1
View File
@@ -5,6 +5,8 @@
*/
#include <iostream>
#include <string>
using namespace std::string_literals;
#include "simdjson.h"
#include "test_macros.h"
@@ -162,7 +164,7 @@ bool issue1142() {
dom::parser parser;
dom::element example = parser.parse(example_json);
auto e0 = dom::array(example).at(0).at_pointer("");
ASSERT_EQUAL(std::string("1"), simdjson::minify(e0))
ASSERT_EQUAL("1"s, simdjson::minify(e0))
auto o = dom::array(example).at(2).at_pointer("");
ASSERT_EQUAL(std::string(R"({"1":"bla"})"), simdjson::minify(o))
std::string_view s0 = dom::array(example).at(2).at_pointer("/1").at_pointer("");
+1
View File
@@ -13,6 +13,7 @@ add_cpp_test(ondemand_document_stream_tests LABELS ondemand acceptance per_impl
add_cpp_test(ondemand_error_tests LABELS ondemand acceptance per_implementation)
add_cpp_test(ondemand_error_location_tests LABELS ondemand acceptance per_implementation)
add_cpp_test(ondemand_json_pointer_tests LABELS ondemand acceptance per_implementation)
add_cpp_test(ondemand_json_path_tests LABELS ondemand acceptance per_implementation)
add_cpp_test(ondemand_key_string_tests LABELS ondemand acceptance per_implementation)
add_cpp_test(ondemand_misc_tests LABELS ondemand acceptance per_implementation)
add_cpp_test(ondemand_number_tests LABELS ondemand acceptance per_implementation)
@@ -7,9 +7,9 @@ using namespace simdjson;
#if SIMDJSON_EXCEPTIONS
// bogus functions for compilation tests
void process1(int ) {}
void process2(int ) {}
void process3(int ) {}
void process1(int64_t ) {}
void process2(int64_t ) {}
void process3(int64_t ) {}
// Do not run this, it is only meant to compile
void compilation_test_1() {
@@ -1,3 +1,6 @@
#include <string>
using namespace std::string_literals;
#include "simdjson.h"
#include "test_ondemand.h"
@@ -234,7 +237,7 @@ namespace document_stream_tests {
ASSERT_EQUAL(odstream.truncated_bytes(), 305);
break;
} else if (err) {
TEST_FAIL(std::string("ondemand: error accessing jsonpointer: ") + simdjson::error_message(err));
TEST_FAIL("ondemand: error accessing jsonpointer: "s + simdjson::error_message(err));
}
}
ASSERT_EQUAL(odstream.truncated_bytes(), 305);
@@ -322,6 +325,24 @@ namespace document_stream_tests {
TEST_SUCCEED();
}
bool simple_document_iteration_advance() {
TEST_START();
auto json = R"([1,[1,2]] {"a":1,"b":2} {"o":{"1":1,"2":2}} [1,2,3])"_padded;
ondemand::parser parser;
ondemand::document_stream stream;
ASSERT_SUCCESS(parser.iterate_many(json).get(stream));
std::string_view expected[4] = {"[1,[1,2]]", "{\"a\":1,\"b\":2}", "{\"o\":{\"1\":1,\"2\":2}}", "[1,2,3]"};
auto iter = stream.begin();
for (auto i = 0; i < 4; i++) {
auto doc = *iter;
std::string_view view;
ASSERT_SUCCESS(to_json_string(doc).get(view));
ASSERT_EQUAL(view, expected[i]);
std::advance(iter, 1);
}
TEST_SUCCEED();
}
bool skipbom() {
TEST_START();
auto json = "\xEF\xBB\xBF[1,[1,2]] {\"a\":1,\"b\":2} {\"o\":{\"1\":1,\"2\":2}} [1,2,3]"_padded;
@@ -856,6 +877,7 @@ namespace document_stream_tests {
simple_document_iteration() &&
simple_document_iteration_multiple_batches() &&
simple_document_iteration_with_parsing() &&
simple_document_iteration_advance() &&
atoms_json() &&
doc_index() &&
doc_index_multiple_batches() &&
@@ -879,4 +901,4 @@ namespace document_stream_tests {
int main (int argc, char *argv[]) {
return test_main(argc, argv, document_stream_tests::run);
}
}
+10
View File
@@ -6,6 +6,15 @@ using namespace simdjson;
namespace error_tests {
using namespace std;
bool issue2120() {
TEST_START();
ondemand::parser parser;
size_t bad_capacity = 0;
ondemand::document doc;
ASSERT_ERROR( parser.iterate("{\"a\": 0 }", bad_capacity).get(doc), INSUFFICIENT_PADDING);
TEST_SUCCEED();
}
bool badbadjson() {
TEST_START();
ondemand::parser parser;
@@ -366,6 +375,7 @@ namespace error_tests {
bool run() {
return
issue2120() &&
badbadjson() &&
badbadjson2() &&
issue1834() &&
+491
View File
@@ -0,0 +1,491 @@
#include "simdjson.h"
#include "test_ondemand.h"
#include <string>
using namespace simdjson;
namespace json_path_tests {
const padded_string TEST_JSON = R"(
{
"/~01abc": [
0,
{
"\\\" 0": [
"value0",
"value1"
]
}
],
"0": "0 ok",
"01": "01 ok",
"": "empty ok",
"arr": []
}
)"_padded;
const padded_string TEST_RFC_JSON = R"(
{
"foo": ["bar", "baz"],
"": 0,
"a/b": 1,
"c%d": 2,
"e^f": 3,
"g|h": 4,
"i\\j": 5,
"k\"l": 6,
" ": 7,
"m~n": 8
}
)"_padded;
bool run_success_test(const padded_string & json,std::string_view json_path,std::string expected) {
TEST_START();
std::cout <<":"<< json_path<<std::endl;
ondemand::parser parser;
ondemand::document doc;
ondemand::value val;
std::string_view actual;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
ASSERT_SUCCESS(doc.at_path(json_path).get(val));
ASSERT_SUCCESS(simdjson::to_json_string(val).get(actual));
ASSERT_EQUAL(actual,expected);
// We want to see if the value is usable besides to_json_string.
ASSERT_SUCCESS(parser.iterate(json).get(doc));
ASSERT_SUCCESS(doc.at_path(json_path).get(val));
ondemand::json_type type;
ASSERT_SUCCESS(val.type().get(type));
switch (type) {
case ondemand::json_type::array:
ASSERT_SUCCESS(val.get_array().error());
break;
case ondemand::json_type::object:
ASSERT_SUCCESS(val.get_object().error());
break;
case ondemand::json_type::number:
ASSERT_SUCCESS(val.get_double().error());
break;
case ondemand::json_type::string:
ASSERT_SUCCESS(val.get_string().error());
break;
case ondemand::json_type::boolean:
ASSERT_SUCCESS(val.get_bool().error());
break;
case ondemand::json_type::null:
ASSERT_SUCCESS(val.is_null().error());
break;
default:
TEST_FAIL("unexpected type");
}
TEST_SUCCEED();
}
bool run_failure_test(const padded_string & json,std::string_view json_path,error_code expected) {
TEST_START();
std::cout << "json_path: " << json_path << std::endl;
ondemand::parser parser;
ondemand::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
ASSERT_EQUAL(doc.at_path(json_path).error(),expected);
TEST_SUCCEED();
}
bool demo_test() {
TEST_START();
auto cars_json = R"( [
{ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] },
{ "make": "Kia", "model": "Soul", "year": 2012, "tire_pressure": [ 30.1, 31.0, 28.6, 28.7 ] },
{ "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
] )"_padded;
ondemand::parser parser;
ondemand::document cars;
double x;
ASSERT_SUCCESS(parser.iterate(cars_json).get(cars));
ASSERT_SUCCESS(cars.at_path("[0].tire_pressure[1]").get(x));
ASSERT_EQUAL(x,39.9);
TEST_SUCCEED();
}
bool demo_relative_path() {
TEST_START();
auto cars_json = R"( [
{ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] },
{ "make": "Kia", "model": "Soul", "year": 2012, "tire_pressure": [ 30.1, 31.0, 28.6, 28.7 ] },
{ "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
] )"_padded;
ondemand::parser parser;
ondemand::document cars;
std::vector<double> measured;
ASSERT_SUCCESS(parser.iterate(cars_json).get(cars));
for (auto car_element : cars) {
double x;
ASSERT_SUCCESS(car_element.at_path(".tire_pressure[1]").get(x));
measured.push_back(x);
}
std::vector<double> expected = {39.9, 31, 30};
if (measured != expected) { return false; }
TEST_SUCCEED();
}
bool document_as_scalar() {
TEST_START();
auto number_json = R"( 1 )"_padded;
auto null_json = R"( null )"_padded;
auto string_json = R"( "a" )"_padded;
auto true_json = R"( true )"_padded;
auto false_json = R"( false )"_padded;
auto object_json = R"( {} )"_padded;
auto array_json = R"( {} )"_padded;
ondemand::parser parser;
ondemand::document doc;
ondemand::value val;
bool is_scalar;
std::cout << " checking number"<< std::endl;
ASSERT_SUCCESS(parser.iterate(number_json).get(doc));
ASSERT_SUCCESS(doc.is_scalar().get(is_scalar));
ASSERT_TRUE(is_scalar);
ASSERT_ERROR(doc.at_path("").get(val), simdjson::SCALAR_DOCUMENT_AS_VALUE);
std::cout << " checking null"<< std::endl;
ASSERT_SUCCESS(parser.iterate(null_json).get(doc));
ASSERT_SUCCESS(doc.is_scalar().get(is_scalar));
ASSERT_TRUE(is_scalar);
ASSERT_ERROR(doc.at_path("").get(val), simdjson::SCALAR_DOCUMENT_AS_VALUE);
std::cout << " checking string"<< std::endl;
ASSERT_SUCCESS(parser.iterate(string_json).get(doc));
ASSERT_SUCCESS(doc.is_scalar().get(is_scalar));
ASSERT_TRUE(is_scalar);
ASSERT_ERROR(doc.at_path("").get(val), simdjson::SCALAR_DOCUMENT_AS_VALUE);
std::cout << " checking false"<< std::endl;
ASSERT_SUCCESS(parser.iterate(false_json).get(doc));
ASSERT_SUCCESS(doc.is_scalar().get(is_scalar));
ASSERT_TRUE(is_scalar);
ASSERT_ERROR(doc.at_path("").get(val), simdjson::SCALAR_DOCUMENT_AS_VALUE);
std::cout << " checking true"<< std::endl;
ASSERT_SUCCESS(parser.iterate(true_json).get(doc));
ASSERT_SUCCESS(doc.is_scalar().get(is_scalar));
ASSERT_TRUE(is_scalar);
ASSERT_ERROR(doc.at_path("").get(val), simdjson::SCALAR_DOCUMENT_AS_VALUE);
std::cout << " checking object"<< std::endl;
ASSERT_SUCCESS(parser.iterate(object_json).get(doc));
ASSERT_SUCCESS(doc.is_scalar().get(is_scalar));
ASSERT_FALSE(is_scalar);
ASSERT_SUCCESS(doc.at_path("").get(val));
std::cout << " checking array"<< std::endl;
ASSERT_SUCCESS(parser.iterate(array_json).get(doc));
ASSERT_SUCCESS(doc.is_scalar().get(is_scalar));
ASSERT_FALSE(is_scalar);
ASSERT_SUCCESS(doc.at_path("").get(val));
TEST_SUCCEED();
}
bool many_json_paths() {
TEST_START();
auto cars_json = R"( [
{ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] },
{ "make": "Kia", "model": "Soul", "year": 2012, "tire_pressure": [ 30.1, 31.0, 28.6, 28.7 ] },
{ "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
] )"_padded;
ondemand::parser parser;
ondemand::document cars;
std::vector<double> measured;
ASSERT_SUCCESS(parser.iterate(cars_json).get(cars));
for (int i = 0; i < 3; i++) {
double x;
std::string json_path = std::string("[") + std::to_string(i) + std::string("].tire_pressure[1]");
ASSERT_SUCCESS(cars.at_path(json_path).get(x));
measured.push_back(x);
}
std::vector<double> expected = {39.9, 31, 30};
if (measured != expected) { return false; }
TEST_SUCCEED();
}
bool run_broken_tests() {
TEST_START();
ondemand::parser parser;
ondemand::document doc;
ondemand::value v;
std::string_view val;
auto invalid_escape_key = R"( {"hello": [0,1,2,3], "te\est": "foo", "bool": true, "num":1234, "success":"yes"} )"_padded;
auto invalid_escape_value = R"( {"hello": [0,1,2,3], "test": "fo\eo", "bool": true, "num":1234, "success":"yes"} )"_padded;
auto invalid_escape_value_at_jp = R"( {"hello": [0,1,2,3], "test": "foo", "bool": true, "num":1234, "success":"y\es"} )"_padded;
auto unclosed_object = R"( {"test": "foo", "bool": true, "num":1234, "success":"yes" )"_padded;
auto missing_bracket_before = R"( {"hello": [0,1,2,3, "test": "foo", "bool": true, "num":1234, "success":"yes"} )"_padded;
auto missing_bracket_after = R"( {"test": "foo", "bool": true, "num":1234, "success":"yes", "hello":[0,1,2,3} )"_padded;
std::string json_path = ".success";
std::cout << "\t- invalid_escape_key" << std::endl;
ASSERT_SUCCESS(parser.iterate(invalid_escape_key).get(doc));
ASSERT_SUCCESS(doc.at_path(json_path).get(val));
std::cout << "\t- invalid_escape_value" << std::endl;
ASSERT_SUCCESS(parser.iterate(invalid_escape_value).get(doc));
ASSERT_SUCCESS(doc.at_path(json_path).get(val));
std::cout << "\t- invalid_escape_value_at_jp_nomat" << std::endl;
ASSERT_SUCCESS(parser.iterate(invalid_escape_value_at_jp).get(doc));
ASSERT_SUCCESS(doc.at_path(json_path).get(v));
std::cout << "\t- invalid_escape_value_at_jp" << std::endl;
ASSERT_SUCCESS(parser.iterate(invalid_escape_value_at_jp).get(doc));
ASSERT_ERROR(doc.at_path(json_path).get(val), simdjson::STRING_ERROR);
std::cout << "\t- unclosed_object" << std::endl;
ASSERT_SUCCESS(parser.iterate(unclosed_object).get(doc));
ASSERT_ERROR(doc.at_path(json_path).get(val), simdjson::INCOMPLETE_ARRAY_OR_OBJECT);
std::cout << "\t- missing_bracket_before" << std::endl;
ASSERT_SUCCESS(parser.iterate(missing_bracket_before).get(doc));
ASSERT_ERROR(doc.at_path(json_path).get(val), simdjson::TAPE_ERROR);
std::cout << "\t- missing_bracket_after" << std::endl;
ASSERT_SUCCESS(parser.iterate(missing_bracket_after).get(doc));
ASSERT_SUCCESS(doc.at_path(json_path).get(val));
TEST_SUCCEED();
}
bool many_json_paths_object_array() {
TEST_START();
auto dogcatpotato = R"( { "dog" : [1,2,3], "cat" : [5, 6, 7], "potato" : [1234]})"_padded;
ondemand::parser parser;
ondemand::document doc;
ASSERT_SUCCESS(parser.iterate(dogcatpotato).get(doc));
ondemand::object obj;
ASSERT_SUCCESS(doc.get_object().get(obj));
int64_t x;
ASSERT_SUCCESS(obj.at_path(".dog[1]").get(x));
ASSERT_EQUAL(x, 2);
ASSERT_SUCCESS(obj.at_path(".potato[0]").get(x));
ASSERT_EQUAL(x, 1234);
TEST_SUCCEED();
}
bool many_json_paths_object() {
TEST_START();
auto cfoofoo2 = R"( { "c" :{ "foo": { "a": [ 10, 20, 30 ] }}, "d": { "foo2": { "a": [ 10, 20, 30 ] }} , "e": 120 })"_padded;
ondemand::parser parser;
ondemand::document doc;
ASSERT_SUCCESS(parser.iterate(cfoofoo2).get(doc));
ondemand::object obj;
ASSERT_SUCCESS(doc.get_object().get(obj));
int64_t x;
ASSERT_SUCCESS(obj.at_path(".c.foo.a[1]").get(x));
ASSERT_EQUAL(x, 20);
ASSERT_SUCCESS(obj.at_path(".d.foo2.a.2").get(x));
ASSERT_EQUAL(x, 30);
ASSERT_SUCCESS(obj.at_path(".e").get(x));
ASSERT_EQUAL(x, 120);
TEST_SUCCEED();
}
bool many_json_paths_array() {
TEST_START();
auto cfoofoo2 = R"( [ 111, 2, 3, { "foo": { "a": [ 10, 20, 33 ] }}, { "foo2": { "a": [ 10, 20, 30 ] }}, 1001 ])"_padded;
ondemand::parser parser;
ondemand::document doc;
ASSERT_SUCCESS(parser.iterate(cfoofoo2).get(doc));
ondemand::array arr;
ASSERT_SUCCESS(doc.get_array().get(arr));
int64_t x;
ASSERT_SUCCESS(arr.at_path("[3].foo.a[1]").get(x));
ASSERT_EQUAL(x, 20);
TEST_SUCCEED();
}
struct car_type {
std::string make;
std::string model;
uint64_t year;
std::vector<double> tire_pressure;
car_type(std::string_view _make, std::string_view _model, uint64_t _year,
std::vector<double>&& _tire_pressure) :
make{_make}, model{_model}, year(_year), tire_pressure(_tire_pressure) {}
};
bool json_path_invalidation() {
TEST_START();
auto cars_json = R"( [
{ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] },
{ "make": "Kia", "model": "Soul", "year": 2012, "tire_pressure": [ 30.1, 31.0, 28.6, 28.7 ] },
{ "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
] )"_padded;
ondemand::parser parser;
ondemand::document cars;
std::vector<double> measured;
ASSERT_SUCCESS(parser.iterate(cars_json).get(cars));
std::vector<car_type> content;
for (int i = 0; i < 3; i++) {
ondemand::object obj;
std::string json_path = std::string("[") + std::to_string(i) + std::string("]");
// Each successive at_path call invalidates
// previously parsed values, strings, objects and array.
ASSERT_SUCCESS(cars.at_path(json_path).get(obj));
// We materialize the object.
std::string_view make;
ASSERT_SUCCESS(obj["make"].get(make));
std::string_view model;
ASSERT_SUCCESS(obj["model"].get(model));
uint64_t year;
ASSERT_SUCCESS(obj["year"].get(year));
// We materialize the array.
ondemand::array arr;
ASSERT_SUCCESS(obj["tire_pressure"].get(arr));
std::vector<double> values;
for(auto x : arr) {
double value_double;
ASSERT_SUCCESS(x.get(value_double));
values.push_back(value_double);
}
content.emplace_back(make, model, year, std::move(values));
}
std::string expected[] = {"Toyota", "Kia", "Toyota"};
int i = 0;
for (car_type c : content) {
std::cout << c.make << " " << c.model << " " << c.year << "\n";
ASSERT_EQUAL(expected[i++], c.make);
}
TEST_SUCCEED();
}
#if SIMDJSON_EXCEPTIONS
bool json_path_invalidation_exceptions() {
TEST_START();
auto cars_json = R"( [
{ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] },
{ "make": "Kia", "model": "Soul", "year": 2012, "tire_pressure": [ 30.1, 31.0, 28.6, 28.7 ] },
{ "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
] )"_padded;
ondemand::parser parser;
std::vector<double> measured;
ondemand::document cars = parser.iterate(cars_json);
std::vector<car_type> content;
for (int i = 0; i < 3; i++) {
std::string json_path = std::string("[") + std::to_string(i) + std::string("]");
// Each successive at_path call invalidates
// previously parsed values, strings, objects and array.
ondemand::object obj(cars.at_path(json_path).get_object());
// We materialize the object.
std::string_view make = obj["make"];
std::string_view model = obj["model"];
uint64_t year(obj["year"]);
// We materialize the array.
ondemand::array arr(obj["tire_pressure"].get_array());
std::vector<double> values;
for(auto x : arr) {
double value_double(x.get_double());
values.push_back(value_double);
}
content.emplace_back(make, model, year, std::move(values));
}
std::string expected[] = {"Toyota", "Kia", "Toyota"};
for (car_type c : content) {
std::cout << c.make << " " << c.model << " " << c.year << "\n";
}
TEST_SUCCEED();
}
#endif
bool run() {
return
#if SIMDJSON_EXCEPTIONS
json_path_invalidation_exceptions() &&
#endif
many_json_paths_array() &&
many_json_paths_object() &&
many_json_paths_object_array() &&
run_broken_tests() &&
json_path_invalidation() &&
demo_test() &&
demo_relative_path() &&
run_success_test(TEST_RFC_JSON,".foo",R"(["bar", "baz"])") &&
run_success_test(TEST_RFC_JSON,".foo[0]",R"("bar")") &&
run_success_test(TEST_RFC_JSON,".",R"(0)") &&
run_success_test(TEST_RFC_JSON,".a/b",R"(1)") &&
run_success_test(TEST_RFC_JSON,".c%d",R"(2)") &&
run_success_test(TEST_RFC_JSON,".e^f",R"(3)") &&
run_success_test(TEST_RFC_JSON,".g|h",R"(4)") &&
run_success_test(TEST_RFC_JSON,R"(.i\\j)",R"(5)") &&
run_success_test(TEST_RFC_JSON,R"(.k\"l)",R"(6)") &&
run_success_test(TEST_RFC_JSON,". ",R"(7)") &&
run_success_test(TEST_RFC_JSON,".m~n",R"(8)") &&
run_success_test(TEST_RFC_JSON,"",R"({
"foo": ["bar", "baz"],
"": 0,
"a/b": 1,
"c%d": 2,
"e^f": 3,
"g|h": 4,
"i\\j": 5,
"k\"l": 6,
" ": 7,
"m~n": 8
})") &&
run_success_test(TEST_JSON, "", R"({
"/~01abc": [
0,
{
"\\\" 0": [
"value0",
"value1"
]
}
],
"0": "0 ok",
"01": "01 ok",
"": "empty ok",
"arr": []
})") &&
run_success_test(TEST_JSON, R"(./~01abc)", R"([
0,
{
"\\\" 0": [
"value0",
"value1"
]
}
])") &&
run_success_test(TEST_JSON, R"(./~01abc[1])", R"({
"\\\" 0": [
"value0",
"value1"
]
})") &&
run_success_test(TEST_JSON, R"(./~01abc[1].\\\" 0)", R"([
"value0",
"value1"
])") &&
run_success_test(TEST_JSON, R"(./~01abc[1].\\\" 0[0])", "\"value0\"") &&
run_success_test(TEST_JSON, R"(./~01abc[1].\\\" 0[1])", "\"value1\"") &&
run_success_test(TEST_JSON, ".arr", R"([])") &&
run_success_test(TEST_JSON, ".0", "\"0 ok\"") &&
run_success_test(TEST_JSON, ".01", "\"01 ok\"") &&
run_success_test(TEST_JSON, "", R"({
"/~01abc": [
0,
{
"\\\" 0": [
"value0",
"value1"
]
}
],
"0": "0 ok",
"01": "01 ok",
"": "empty ok",
"arr": []
})") &&
run_failure_test(TEST_JSON, R"(./~01abc[1].\\\" 0[2])", INDEX_OUT_OF_BOUNDS) &&
run_failure_test(TEST_JSON, ".arr[0]", INDEX_OUT_OF_BOUNDS) &&
run_failure_test(TEST_JSON, "/~01abc", INVALID_JSON_POINTER) &&
run_failure_test(TEST_JSON, ".~1abc", NO_SUCH_FIELD) &&
run_failure_test(TEST_JSON, "./~01abc.01", INVALID_JSON_POINTER) &&
run_failure_test(TEST_JSON, "./~01abc.", INVALID_JSON_POINTER) &&
run_failure_test(TEST_JSON, "./~01abc.-", INDEX_OUT_OF_BOUNDS) &&
many_json_paths() &&
document_as_scalar() &&
true;
}
} // json_path_tests
int main(int argc, char *argv[]) {
return test_main(argc, argv, json_path_tests::run);
}
@@ -1,6 +1,7 @@
#include "simdjson.h"
#include "test_ondemand.h"
#include <string>
using namespace std::string_literals;
using namespace simdjson;
@@ -195,7 +196,7 @@ namespace json_pointer_tests {
ASSERT_SUCCESS(parser.iterate(cars_json).get(cars));
for (int i = 0; i < 3; i++) {
double x;
std::string json_pointer = std::string("/") + std::to_string(i) + std::string("/tire_pressure/1");
std::string json_pointer = "/"s + std::to_string(i) + "/tire_pressure/1"s;
ASSERT_SUCCESS(cars.at_pointer(json_pointer).get(x));
measured.push_back(x);
}
@@ -315,7 +316,7 @@ namespace json_pointer_tests {
std::vector<car_type> content;
for (int i = 0; i < 3; i++) {
ondemand::object obj;
std::string json_pointer = std::string("/") + std::to_string(i);
std::string json_pointer = "/"s + std::to_string(i);
// Each successive at_pointer call invalidates
// previously parsed values, strings, objects and array.
ASSERT_SUCCESS(cars.at_pointer(json_pointer).get(obj));
@@ -360,7 +361,7 @@ namespace json_pointer_tests {
ondemand::document cars = parser.iterate(cars_json);
std::vector<car_type> content;
for (int i = 0; i < 3; i++) {
std::string json_pointer = std::string("/") + std::to_string(i);
std::string json_pointer = "/"s + std::to_string(i);
// Each successive at_pointer call invalidates
// previously parsed values, strings, objects and array.
ondemand::object obj(cars.at_pointer(json_pointer).get_object());
+4 -4
View File
@@ -550,7 +550,7 @@ namespace misc_tests {
simdjson_warn_unused bool test_raw_json_token(string_view json, string_view expected_token, int expected_start_index = 0) {
string title("'");
title.append(json.data(), json.length());
title += std::string("'");
title += "'"s;
padded_string json_padded = json;
SUBTEST(title, test_ondemand_doc(json_padded, [&](auto doc) {
string_view token;
@@ -564,11 +564,11 @@ namespace misc_tests {
// Test values
auto json_in_hash = string(R"({"a":)");
json_in_hash.append(json.data(), json.length());
json_in_hash += std::string("}");
json_in_hash += "}"s;
json_padded = json_in_hash;
title = std::string("'");
title = "'"s;
title.append(json_in_hash.data(), json_in_hash.length());
title += std::string("'");
title += "'"s;
SUBTEST(title, test_ondemand_doc(json_padded, [&](auto doc) {
string_view token;
ASSERT_SUCCESS( doc["a"].raw_json_token().get(token) );
+2 -2
View File
@@ -356,7 +356,7 @@ namespace object_tests {
// used in issue_1521
// difficult to use as a lambda because it is recursive.
void broken_descend(ondemand::object node) {
if(auto type = node.find_field_unordered("type"); type.error() == SUCCESS && type == "child") {
if(auto type = node.find_field_unordered("type"); type.error() == SUCCESS && type.get_string().value() == "child") {
auto n = node.find_field_unordered("name");
if(n.error() == simdjson::SUCCESS) {
std::cout << std::string_view(n) << std::endl;
@@ -399,7 +399,7 @@ namespace object_tests {
// difficult to use as a lambda because it is recursive.
void descend(ondemand::object node) {
auto n = node.find_field_unordered("name");
if(auto type = node.find_field_unordered("type"); type.error() == SUCCESS && type == "child") {
if(auto type = node.find_field_unordered("type"); type.error() == SUCCESS && type.get_string().value() == "child") {
if(n.error() == simdjson::SUCCESS) {
std::cout << std::string_view(n) << std::endl;
}
+255 -4
View File
@@ -21,7 +21,6 @@ bool string2() {
return true;
}
bool to_string_example_no_except() {
TEST_START();
auto json = R"({
@@ -38,11 +37,149 @@ bool to_string_example_no_except() {
TEST_SUCCEED();
}
struct Car {
Car() : make(), model(), year(), tire_pressure() {}
std::string make;
std::string model;
int64_t year;
std::vector<double> tire_pressure;
};
template <>
simdjson_inline simdjson_result<std::vector<double>>
simdjson::ondemand::value::get() noexcept {
// This works on a value, if the std::vector<double> is the document, we need to also implement
// simdjson::ondemand::document::get<std::vector<double>>.
ondemand::array array;
auto error = get_array().get(array);
if(error) { return error; }
std::vector<double> vec;
for (auto v : array) {
double val;
error = v.get_double().get(val);
if(error) { return error; }
vec.push_back(val);
}
return vec;
}
template <>
simdjson_inline simdjson_result<Car> simdjson::ondemand::value::get() noexcept {
ondemand::object obj;
auto error = get_object().get(obj);
if (error) { return error; }
Car car;
// Instead of repeatedly obj["something"], we iterate through the object which
// we expect to be faster.
for (auto field : obj) {
raw_json_string key;
if (error = field.key().get(key); error) { return error; }
if (key == "make") {
error = field.value().get_string(car.make);
if(error) { return error; }
} else if (key == "model") {
error = field.value().get_string(car.model);
if(error) { return error; }
} else if (key == "year") {
error = field.value().get_int64().get(car.year);
if(error) { return error; }
} else if (key == "tire_pressure") {
error = field.value().get<std::vector<double>>().get(car.tire_pressure);
if (error) { return error; }
}
}
return car;
}
int custom_type_without_exceptions() {
padded_string json = R"( [ { "make": "Toyota", "model": "Camry", "year": 2018,
"tire_pressure": [ 40.1, 39.9 ] },
{ "make": "Kia", "model": "Soul", "year": 2012,
"tire_pressure": [ 30.1, 31.0 ] },
{ "make": "Toyota", "model": "Tercel", "year": 1999,
"tire_pressure": [ 29.8, 30.0 ] }
])"_padded;
ondemand::parser parser;
ondemand::document doc;
auto error = parser.iterate(json).get(doc);
if(error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
for (auto val : doc) {
Car c;
error = val.get<Car>().get(c);
if(error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
std::cout << c.make << std::endl;
}
return 0;
}
template <>
simdjson_inline simdjson_result<Car> simdjson::ondemand::document::get() & noexcept {
ondemand::object obj;
auto error = get_object().get(obj);
if (error) {
return error;
}
Car car;
// Instead of repeatedly obj["something"], we iterate through the object which
// we expect to be faster.
for (auto field : obj) {
raw_json_string key;
error = field.key().get(key);
if (error) { return error; }
if (key == "make") {
error = field.value().get_string(car.make);
if (error) { return error; }
} else if (key == "model") {
error = field.value().get_string(car.model);
if (error) { return error; }
} else if (key == "year") {
error = field.value().get_int64().get(car.year);
if (error) { return error; }
} else if (key == "tire_pressure") {
error = field.value().get<std::vector<double>>().get(car.tire_pressure);
if (error) { return error; }
}
}
return car;
}
#if SIMDJSON_EXCEPTIONS
int custom_type_on_document() {
padded_string json = R"( { "make": "Toyota", "model": "Camry", "year": 2018,
"tire_pressure": [ 40.1, 39.9 ] } )"_padded;
ondemand::parser parser;
ondemand::document doc = parser.iterate(json);
Car c(doc);
std::cout << c.make << std::endl;
return 0;
}
int custom_type_with_exceptions() {
padded_string json = R"( [ { "make": "Toyota", "model": "Camry", "year": 2018,
"tire_pressure": [ 40.1, 39.9 ] },
{ "make": "Kia", "model": "Soul", "year": 2012,
"tire_pressure": [ 30.1, 31.0 ] },
{ "make": "Toyota", "model": "Tercel", "year": 1999,
"tire_pressure": [ 29.8, 30.0 ] }
])"_padded;
ondemand::parser parser;
ondemand::document doc = parser.iterate(json);
for (auto val : doc) {
Car c(val);
std::cout << c.make << std::endl;
}
return 0;
}
bool to_string_example() {
TEST_START();
TEST_START();
auto json = R"({
"name": "Daniel",
"age": 42
@@ -931,7 +1068,83 @@ bool json_pointer_simple() {
TEST_SUCCEED();
}
bool json_pointer_unicode() {
TEST_START();
const padded_string json = u8"{\"\u00E9\":123}"_padded;
ondemand::parser parser;
ondemand::document doc;
int64_t x;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
ASSERT_SUCCESS(doc.at_pointer((const char*)u8"/\u00E9").get(x));
ASSERT_EQUAL(x,123);
const padded_string json2 = "{\"\\u00E9\":123}"_padded;
ASSERT_SUCCESS(parser.iterate(json2).get(doc));
ASSERT_SUCCESS(doc.at_pointer("/\\u00E9").get(x));
ASSERT_ERROR(doc.at_pointer((const char*)u8"/\u00E9"), NO_SUCH_FIELD);
ASSERT_EQUAL(x,123);
TEST_SUCCEED();
}
bool json_path_simple() {
TEST_START();
ondemand::parser parser;
ondemand::document cars;
double x;
ASSERT_SUCCESS(parser.iterate(cars_json).get(cars));
ASSERT_SUCCESS(cars.at_path("[0].tire_pressure[1]").get(x));
ASSERT_EQUAL(x,39.9);
TEST_SUCCEED();
}
bool json_path_unicode() {
TEST_START();
ondemand::parser parser;
ondemand::document doc;
const padded_string json = u8"{\"\u00E9\":123}"_padded;
int64_t x;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
ASSERT_SUCCESS(doc.at_path((const char*)u8".\u00E9").get(x));
ASSERT_EQUAL(x,123);
const padded_string json2 = "{\"\\u00E9\":123}"_padded;
ASSERT_SUCCESS(parser.iterate(json2).get(doc));
ASSERT_SUCCESS(doc.at_path(".\\u00E9").get(x));
ASSERT_ERROR(doc.at_path((const char*)u8".\u00E9"), NO_SUCH_FIELD);
ASSERT_EQUAL(x,123);
TEST_SUCCEED();
}
bool invalid_json_path() {
TEST_START();
ondemand::parser parser;
ondemand::document cars;
double x;
ASSERT_SUCCESS(parser.iterate(cars_json).get(cars));
ASSERT_ERROR(cars.at_path("[0].tire_presure[1").get(x), INVALID_JSON_POINTER); // Fails on conversion to JSON Pointer
ASSERT_ERROR(cars.at_path("[0].incorrect_field[1]").get(x), NO_SUCH_FIELD); // Fails on at_pointer()
TEST_SUCCEED();
}
bool json_pointer_multiple() {
TEST_START();
using namespace std::string_literals;
ondemand::parser parser;
ondemand::document cars;
size_t size;
ASSERT_SUCCESS(parser.iterate(cars_json).get(cars));
ASSERT_SUCCESS(cars.count_elements().get(size));
double expected[] = {39.9, 31, 30};
for (size_t i = 0; i < size; i++) {
std::string json_pointer = "/"s + std::to_string(i) + "/tire_pressure/1"s;
double x;
ASSERT_SUCCESS(cars.at_pointer(json_pointer).get(x));
ASSERT_EQUAL(x,expected[i]);
}
TEST_SUCCEED();
}
bool json_path_multiple() {
TEST_START();
ondemand::parser parser;
ondemand::document cars;
@@ -940,9 +1153,9 @@ bool json_pointer_multiple() {
ASSERT_SUCCESS(cars.count_elements().get(size));
double expected[] = {39.9, 31, 30};
for (size_t i = 0; i < size; i++) {
std::string json_pointer = std::string("/") + std::to_string(i) + std::string("/tire_pressure/1");
std::string json_path = "["s + std::to_string(i) + "].tire_pressure[1]"s;
double x;
ASSERT_SUCCESS(cars.at_pointer(json_pointer).get(x));
ASSERT_SUCCESS(cars.at_path(json_path).get(x));
ASSERT_EQUAL(x,expected[i]);
}
TEST_SUCCEED();
@@ -1078,6 +1291,38 @@ bool simple_error_example() {
#if SIMDJSON_EXCEPTIONS
#include "simdjson.h"
#include <iostream>
// prints the content of the array as hexadecimal 64-bit integers
void f(simdjson::ondemand::array v) {
for(uint64_t val : v) {
std::cout << "0x" << std::hex << val << std::endl;
}
}
int callf(void) {
simdjson::padded_string json = R"( [ 897314173811950000, 3122321 ])"_padded;
simdjson::ondemand::parser parser;
simdjson::ondemand::document doc = parser.iterate(json);
f(doc.get_array());
return EXIT_SUCCESS;
}
bool key_raw_json_token() {
TEST_START();
auto json = R"( {"name" : "Jack The Ripper \u0033"} )"_padded;
ondemand::parser parser;
ondemand::document doc = parser.iterate(json);
for (auto key_value : doc.get_object()) {
std::string_view keysv = key_value.key_raw_json_token();
ASSERT_EQUAL(keysv,"\"name\" ");
}
TEST_SUCCEED();
}
bool raw_string() {
TEST_START();
auto json = R"( {"name": "Jack The Ripper \u0033"} )"_padded;
@@ -1533,6 +1778,7 @@ bool value_raw_json_object() {
bool run() {
return true
#if SIMDJSON_EXCEPTIONS
&& key_raw_json_token()
&& to_optional()
&& value_raw_json_array() && value_raw_json_object()
&& gen_raw1() && gen_raw2() && gen_raw3()
@@ -1561,7 +1807,12 @@ bool run() {
#endif
&& using_the_parsed_json_6()
&& json_pointer_simple()
&& json_pointer_unicode()
&& json_path_simple()
&& json_path_unicode()
&& invalid_json_path()
&& json_pointer_multiple()
&& json_path_multiple()
&& json_pointer_rewind()
&& iterate_many_example()
&& iterate_many_truncated_example()
+3 -3
View File
@@ -80,7 +80,7 @@ namespace scalar_tests {
}
{
padded_string array_json = std::string("[") + std::string(json) + "]";
padded_string array_json = "["s + std::string(json) + "]";
std::cout << "- JSON: " << array_json << endl;
SUBTEST( "simdjson_result<value>", test_ondemand_doc(array_json, [&](auto doc_result) {
int count = 0;
@@ -123,7 +123,7 @@ namespace scalar_tests {
}
{
padded_string whitespace_array_json = std::string("[") + std::string(json) + " ]";
padded_string whitespace_array_json = "["s + std::string(json) + " ]";
std::cout << "- JSON: " << whitespace_array_json << endl;
SUBTEST( "simdjson_result<value>", test_ondemand_doc(whitespace_array_json, [&](auto doc_result) {
@@ -256,7 +256,7 @@ namespace scalar_tests {
ASSERT_EQUAL( expected, T(doc) );
return true;
}));
padded_string array_json = std::string("[") + std::string(json) + "]";
padded_string array_json = "["s + std::string(json) + "]";
std::cout << "- JSON: " << array_json << endl;
SUBTEST( "value", test_ondemand_doc(array_json, [&](auto doc_result) {
int count = 0;