Compare commits

...

9 Commits

Author SHA1 Message Date
Daniel Lemire 1875ed6550 Version bump. 2023-09-20 10:13:47 -04:00
Daniel Lemire 80d26298a0 Redesigning visit_primitive so that it is optimized for strings and (#2060)
numbers.

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

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

* Update basics.md
2023-09-11 10:01:35 -04:00
Daniel Lemire e0699994ef Testing macOS shared library support (#2062) 2023-09-09 12:48:42 -04:00
Daniel Lemire 68ba9a1b2a Adding a few spaces (minor formatting) (#2059) 2023-08-30 20:23:58 -04:00
Daniel Lemire 6fed6bd29b Update README.md 2023-08-30 19:26:34 -04:00
Piotr Rżysko cc24bb4114 Removed number parsing fallback (#2056) 2023-08-29 18:28:21 -04:00
19 changed files with 698 additions and 626 deletions
+1 -1
View File
@@ -31,7 +31,7 @@ A clear and concise description of any alternative solutions or features you've
**Additional context** **Additional context**
Add any other context or screenshots about the feature request here. Add any other context or screenshots about the feature request here.
** Are you willing to contribute code or documentation toward this new feature? ** **Are you willing to contribute code or documentation toward this new feature?**
If you plan to contribute to simdjson, please read our If you plan to contribute to simdjson, please read our
* CONTRIBUTING guide: https://github.com/simdjson/simdjson/blob/master/CONTRIBUTING.md and our * CONTRIBUTING guide: https://github.com/simdjson/simdjson/blob/master/CONTRIBUTING.md and our
* HACKING guide: https://github.com/simdjson/simdjson/blob/master/HACKING.md * HACKING guide: https://github.com/simdjson/simdjson/blob/master/HACKING.md
+11
View File
@@ -31,3 +31,14 @@ jobs:
echo -e '#include <simdjson.h>\nint main(int argc,char**argv) {simdjson::dom::parser parser;simdjson::dom::element tweets = parser.load(argv[1]); }' > tmp.cpp && c++ -Idestination/include -Ldestination/lib -std=c++17 -Wl,-rpath,destination/lib -o linkandrun tmp.cpp -lsimdjson && ./linkandrun jsonexamples/twitter.json && echo -e '#include <simdjson.h>\nint main(int argc,char**argv) {simdjson::dom::parser parser;simdjson::dom::element tweets = parser.load(argv[1]); }' > tmp.cpp && c++ -Idestination/include -Ldestination/lib -std=c++17 -Wl,-rpath,destination/lib -o linkandrun tmp.cpp -lsimdjson && ./linkandrun jsonexamples/twitter.json &&
cd ../tests/installation_tests/find && cd ../tests/installation_tests/find &&
mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../../../build/destination .. && cmake --build . mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../../../build/destination .. && cmake --build .
- name: Use cmake (shared)
run: |
mkdir buildshared &&
cd buildshared &&
cmake -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
cmake --build . &&
ctest --output-on-failure -LE explicitonly -j &&
cmake --install . &&
echo -e '#include <simdjson.h>\nint main(int argc,char**argv) {simdjson::dom::parser parser;simdjson::dom::element tweets = parser.load(argv[1]); }' > tmp.cpp && c++ -Idestination/include -Ldestination/lib -std=c++17 -Wl,-rpath,destination/lib -o linkandrun tmp.cpp -lsimdjson && ./linkandrun jsonexamples/twitter.json &&
cd ../tests/installation_tests/find &&
mkdir buildshared && cd buildshared && cmake -DCMAKE_INSTALL_PREFIX:PATH=../../../buildshared/destination .. && cmake --build .
+30 -3
View File
@@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.14)
project( project(
simdjson simdjson
# The version number is modified by tools/release.py # The version number is modified by tools/release.py
VERSION 3.2.3 VERSION 3.3.0
DESCRIPTION "Parsing gigabytes of JSON per second" DESCRIPTION "Parsing gigabytes of JSON per second"
HOMEPAGE_URL "https://simdjson.org/" HOMEPAGE_URL "https://simdjson.org/"
LANGUAGES CXX C LANGUAGES CXX C
@@ -23,6 +23,8 @@ string(
set(SIMDJSON_LIB_VERSION "16.0.0" CACHE STRING "simdjson library version") set(SIMDJSON_LIB_VERSION "16.0.0" CACHE STRING "simdjson library version")
set(SIMDJSON_LIB_SOVERSION "16" CACHE STRING "simdjson library soversion") set(SIMDJSON_LIB_SOVERSION "16" CACHE STRING "simdjson library soversion")
option(SIMDJSON_BUILD_STATIC_LIB "Build simdjson_static library along with simdjson" OFF)
option(SIMDJSON_ENABLE_THREADS "Link with thread support" ON) option(SIMDJSON_ENABLE_THREADS "Link with thread support" ON)
include(cmake/simdjson-props.cmake) include(cmake/simdjson-props.cmake)
@@ -56,8 +58,17 @@ include(cmake/developer-options.cmake)
# ---- simdjson library ---- # ---- simdjson library ----
add_library(simdjson src/simdjson.cpp) set(SIMDJSON_SOURCES src/simdjson.cpp)
add_library(simdjson ${SIMDJSON_SOURCES})
add_library(simdjson::simdjson ALIAS simdjson) add_library(simdjson::simdjson ALIAS simdjson)
set(SIMDJSON_LIBRARIES simdjson)
if(SIMDJSON_BUILD_STATIC_LIB)
add_library(simdjson_static STATIC ${SIMDJSON_SOURCES})
add_library(simdjson::simdjson_static ALIAS simdjson_static)
list(APPEND SIMDJSON_LIBRARIES simdjson_static)
endif()
set_target_properties( set_target_properties(
simdjson PROPERTIES simdjson PROPERTIES
@@ -117,6 +128,9 @@ if(SIMDJSON_ENABLE_THREADS)
endif() endif()
simdjson_apply_props(simdjson) simdjson_apply_props(simdjson)
if(SIMDJSON_BUILD_STATIC_LIB)
simdjson_apply_props(simdjson_static)
endif()
# ---- Install rules ---- # ---- Install rules ----
@@ -138,7 +152,6 @@ install(
ARCHIVE COMPONENT simdjson_Development ARCHIVE COMPONENT simdjson_Development
INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
) )
configure_file(cmake/simdjson-config.cmake.in simdjson-config.cmake @ONLY) configure_file(cmake/simdjson-config.cmake.in simdjson-config.cmake @ONLY)
write_basic_package_version_file( write_basic_package_version_file(
@@ -167,6 +180,20 @@ install(
COMPONENT simdjson_Development COMPONENT simdjson_Development
) )
if(SIMDJSON_BUILD_STATIC_LIB)
install(
TARGETS simdjson_static
EXPORT simdjson_staticTargets
ARCHIVE COMPONENT simdjson_Development
)
install(
EXPORT simdjson_staticTargets
NAMESPACE simdjson::
DESTINATION "${SIMDJSON_INSTALL_CMAKEDIR}"
COMPONENT simdjson_Development
)
endif()
# pkg-config # pkg-config
include(cmake/JoinPaths.cmake) include(cmake/JoinPaths.cmake)
join_paths(PKGCONFIG_INCLUDEDIR "\${prefix}" "${CMAKE_INSTALL_INCLUDEDIR}") join_paths(PKGCONFIG_INCLUDEDIR "\${prefix}" "${CMAKE_INSTALL_INCLUDEDIR}")
+1 -1
View File
@@ -38,7 +38,7 @@ PROJECT_NAME = simdjson
# could be handy for archiving the generated documentation or if some version # could be handy for archiving the generated documentation or if some version
# control system is used. # control system is used.
PROJECT_NUMBER = "3.2.3" PROJECT_NUMBER = "3.3.0"
# Using the PROJECT_BRIEF tag one can provide an optional one line description # Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a # for a project that appears at the top of each page and should give viewer a
-2
View File
@@ -1,8 +1,6 @@
[![Ubuntu 20.04 CI](https://github.com/simdjson/simdjson/workflows/Ubuntu%2020.04%20CI%20(GCC%209)/badge.svg)](https://simdjson.org/plots.html) [![Ubuntu 20.04 CI](https://github.com/simdjson/simdjson/workflows/Ubuntu%2020.04%20CI%20(GCC%209)/badge.svg)](https://simdjson.org/plots.html)
![VS16-CI](https://github.com/simdjson/simdjson/workflows/VS16-CI/badge.svg)
[![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/simdjson.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:simdjson) [![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/simdjson.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:simdjson)
![MinGW64-CI](https://github.com/simdjson/simdjson/workflows/MinGW64-CI/badge.svg)
[![][license img]][license] [![][license img]][license]
[![Doxygen Documentation](https://img.shields.io/badge/docs-doxygen-green.svg)](https://simdjson.github.io/simdjson/) [![Doxygen Documentation](https://img.shields.io/badge/docs-doxygen-green.svg)](https://simdjson.github.io/simdjson/)
-1
View File
@@ -114,7 +114,6 @@ set(SIMDJSON_CXX_STANDARD 17 CACHE STRING "the C++ standard to use for simdjson"
set(CMAKE_CXX_STANDARD ${SIMDJSON_CXX_STANDARD}) set(CMAKE_CXX_STANDARD ${SIMDJSON_CXX_STANDARD})
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_MACOSX_RPATH OFF)
set(CMAKE_THREAD_PREFER_PTHREAD ON) set(CMAKE_THREAD_PREFER_PTHREAD ON)
set(THREADS_PREFER_PTHREAD_FLAG ON) set(THREADS_PREFER_PTHREAD_FLAG ON)
set(SIMDJSON_STRUCTURAL_INDEXER_STEP CACHE STRING "the SIMDJSON_STRUCTURAL_INDEXER_STEP variable") set(SIMDJSON_STRUCTURAL_INDEXER_STEP CACHE STRING "the SIMDJSON_STRUCTURAL_INDEXER_STEP variable")
+1
View File
@@ -4,3 +4,4 @@ if("@SIMDJSON_ENABLE_THREADS@")
endif() endif()
include("${CMAKE_CURRENT_LIST_DIR}/simdjsonTargets.cmake") include("${CMAKE_CURRENT_LIST_DIR}/simdjsonTargets.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/simdjson_staticTargets.cmake" OPTIONAL)
+114 -35
View File
@@ -355,7 +355,7 @@ support for users who avoid exceptions. See [the simdjson error handling documen
* **Field Access:** To get the value of the "foo" field in an object, use `object["foo"]`. This will * **Field Access:** To get the value of the "foo" field in an object, use `object["foo"]`. This will
scan through the object looking for the field with the matching string, doing a character-by-character scan through the object looking for the field with the matching string, doing a character-by-character
comparison. It may generate the error `simdjson::NO_SUCH_FIELD` if there is no such key in the object, it may throw an exception (see [Error Handling](#error-handling)). For efficiency reason, you should avoid looking up the same field repeatedly: e.g., do comparison. It may generate the error `simdjson::NO_SUCH_FIELD` if there is no such key in the object, it may throw an exception (see [Error Handling](#error-handling)). For efficiency reason, you should avoid looking up the same field repeatedly: e.g., do
not do `object["foo"]` followed by `object["foo"]` with the same `object` instance. Keep in mind that On Demand does not buffer or save the result of the parsing: if you repeatedly access `object["foo"]`, then it must repeatedly seek the key and parse the content. The library does not provide a distinct function to check if a key is present, instead we recommend you attempt to access the key: e.g., by doing `ondemand::value val{}; if(!object["foo"].get(val)) {...}`, you have that `val` contains the requested value inside the if clause. It is your responsibility as a user to temporarily keep a reference to the value (`auto v = object["foo"]`), or to consume the content and store it in your own data structures. If you consume an not do `object["foo"]` followed by `object["foo"]` with the same `object` instance. Keep in mind that On Demand does not buffer or save the result of the parsing: if you repeatedly access `object["foo"]`, then it must repeatedly seek the key and parse the content. The library does not provide a distinct function to check if a key is present, instead we recommend you attempt to access the key: e.g., by doing `ondemand::value val{}; if (!object["foo"].get(val)) {...}`, you have that `val` contains the requested value inside the if clause. It is your responsibility as a user to temporarily keep a reference to the value (`auto v = object["foo"]`), or to consume the content and store it in your own data structures. If you consume an
object twice: `std::string_view(object["foo"]` followed by `std::string_view(object["foo"]` then your code object twice: `std::string_view(object["foo"]` followed by `std::string_view(object["foo"]` then your code
is in error. Furthermore, you can only consume one field at a time, on the same object. The is in error. Furthermore, you can only consume one field at a time, on the same object. The
value instance you get from `content["bids"]` becomes invalid when you call `content["asks"]`. value instance you get from `content["bids"]` becomes invalid when you call `content["asks"]`.
@@ -370,7 +370,11 @@ support for users who avoid exceptions. See [the simdjson error handling documen
> as a key, it will not be recognized. This is not generally a problem. Nevertheless, if you do need > as a key, it will not be recognized. This is not generally a problem. Nevertheless, if you do need
> to support escaped keys, the method `unescaped_key()` provides the desired unescaped keys by > to support escaped keys, the method `unescaped_key()` provides the desired unescaped keys by
> parsing and writing out the unescaped keys to a string buffer and returning a `std::string_view` > parsing and writing out the unescaped keys to a string buffer and returning a `std::string_view`
> instance. You should expect a performance penalty when using `unescaped_key()`. > instance. The `unescaped_key` takes an optional Boolean value: passing it true will decode invalid
> Unicode sequences with replacement, meaning that the decoding always succeeds but bogus Unicode
> replacement characters are inserted. In general, you should expect a performance penalty
> when using `unescaped_key()` compared to `key()` because of the string processing: the `key()`
> function just points inside the source JSON document.
> >
> ```c++ > ```c++
> auto json = R"({"k\u0065y": 1})"_padded; > auto json = R"({"k\u0065y": 1})"_padded;
@@ -381,7 +385,7 @@ support for users who avoid exceptions. See [the simdjson error handling documen
> // parses and writes out the key, after unescaping it, > // parses and writes out the key, after unescaping it,
> // to a string buffer. It causes a performance penalty. > // to a string buffer. It causes a performance penalty.
> std::string_view keyv = field.unescaped_key(); > std::string_view keyv = field.unescaped_key();
> if(keyv == "key") { std::cout << uint64_t(field.value()); } > if (keyv == "key") { std::cout << uint64_t(field.value()); }
> } > }
> ``` > ```
> >
@@ -414,8 +418,10 @@ support for users who avoid exceptions. See [the simdjson error handling documen
step through each value in the JSON array. step through each value in the JSON array.
If you know the type of the value, you can cast it right there, too! `for (double value : array) { ... }`. If you know the type of the value, you can cast it right there, too! `for (double value : array) { ... }`.
* **Object Iteration:** You can iterate through an object's fields, as well: `for (auto field : object) { ... }`
- `field.unescaped_key()` will get you the unescaped key string. You may also use explicit iterators: `for(auto i = array.begin(); i != array.end(); i++) {}`. You can check that an array is empty with the condition `auto i = array.begin(); if(i == array.end()) {...}`.
* **Object Iteration:** You can iterate through an object's fields, as well: `for (auto field : object) { ... }`. You may also use explicit iterators : `for(auto i = object.begin(); i != object.end(); i++) { auto field = *i; .... }`. You can check that an object is empty with the condition `auto i = object.begin(); if(i == object.end()) {...}`.
- `field.unescaped_key()` will get you the unescaped key string. E.g., the JSON string `"\u00e1"` becomes the Unicode string `á`. Optionally, you pass `true` as a parameter to the `unescaped_key` method if you want invalid escape sequences to be replaced by a default replacement character (e.g., `\ud800\ud801\ud811`): otherwise bad escape sequences lead to an immediate error.
- `field.value()` will get you the value, which you can then use all these other methods on. - `field.value()` will get you the value, which you can then use all these other methods on.
* **Array Index:** Because it is forward-only, you cannot look up an array element by index by index. Instead, * **Array Index:** Because it is forward-only, you cannot look up an array element by index by index. Instead,
you should iterate through the array and keep an index yourself. you should iterate through the array and keep an index yourself.
@@ -445,7 +451,7 @@ support for users who avoid exceptions. See [the simdjson error handling documen
> { > {
> ondemand::parser parser; > ondemand::parser parser;
> for (ondemand::object car : parser.iterate(cars_json)) { > for (ondemand::object car : parser.iterate(cars_json)) {
> if(uint64_t(car["year"]) > 2000) { > if (uint64_t(car["year"]) > 2000) {
> arrays.push_back(simdjson::to_json_string(car["tire_pressure"])); > arrays.push_back(simdjson::to_json_string(car["tire_pressure"]));
> } > }
> } > }
@@ -454,7 +460,7 @@ support for users who avoid exceptions. See [the simdjson error handling documen
> std::ostringstream oss; > std::ostringstream oss;
> oss << "["; > oss << "[";
> for(size_t i = 0; i < arrays.size(); i++) { > for(size_t i = 0; i < arrays.size(); i++) {
> if(i>0) { oss << ","; } > if (i>0) { oss << ","; }
> oss << arrays[i]; > oss << arrays[i];
> } > }
> oss << "]"; > oss << "]";
@@ -597,7 +603,7 @@ support for users who avoid exceptions. See [the simdjson error handling documen
case ondemand::json_type::null: case ondemand::json_type::null:
// We check that the value is indeed null // We check that the value is indeed null
// otherwise: an error is thrown. // otherwise: an error is thrown.
if(element.is_null()) { if (element.is_null()) {
cout << "null"; cout << "null";
} }
break; break;
@@ -910,11 +916,11 @@ bool simple_error_example() {
ondemand::parser parser; ondemand::parser parser;
auto json = R"({"bad number":3.14.1 })"_padded; auto json = R"({"bad number":3.14.1 })"_padded;
ondemand::document doc; ondemand::document doc;
if( parser.iterate(json).get(doc) != SUCCESS ) { return false; } if (parser.iterate(json).get(doc) != SUCCESS) { return false; }
double x; double x;
auto error = doc["bad number"].get_double().get(x); auto error = doc["bad number"].get_double().get(x);
// returns "simdjson::NUMBER_ERROR" // returns "simdjson::NUMBER_ERROR"
if(error != SUCCESS) { if (error != SUCCESS) {
std::cout << error << std::endl; std::cout << error << std::endl;
return false; return false;
} }
@@ -976,10 +982,10 @@ it selects the key `"count"` within that object.
int main(void) { int main(void) {
simdjson::ondemand::parser parser; simdjson::ondemand::parser parser;
auto error = padded_string::load("twitter.json").get(json); auto error = padded_string::load("twitter.json").get(json);
if(error) { std::cerr << error << std::endl; return EXIT_FAILURE; } if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
simdjson::ondemand::document tweets; simdjson::ondemand::document tweets;
error = parser.iterate(json).get(tweets); error = parser.iterate(json).get(tweets);
if( error ) { std::cerr << error << std::endl; return EXIT_FAILURE; } if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
simdjson::ondemand::value res; simdjson::ondemand::value res;
error = tweets["search_metadata"]["count"].get(res); error = tweets["search_metadata"]["count"].get(res);
if (error != SUCCESS) { if (error != SUCCESS) {
@@ -1010,12 +1016,12 @@ int main(void) {
simdjson::ondemand::document tweets; simdjson::ondemand::document tweets;
padded_string json; padded_string json;
auto error = padded_string::load("twitter.json").get(json); auto error = padded_string::load("twitter.json").get(json);
if(error) { std::cerr << error << std::endl; return EXIT_FAILURE; } if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
error = parser.iterate(json).get(tweets); error = parser.iterate(json).get(tweets);
if(error) { std::cerr << error << std::endl; return EXIT_FAILURE; } if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
uint64_t identifier; uint64_t identifier;
error = tweets["statuses"].at(0)["id"].get(identifier); error = tweets["statuses"].at(0)["id"].get(identifier);
if(error) { std::cerr << error << std::endl; return EXIT_FAILURE; } if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
std::cout << identifier << std::endl; std::cout << identifier << std::endl;
} }
``` ```
@@ -1039,40 +1045,40 @@ bool parse() {
// Iterating through an array of objects // Iterating through an array of objects
auto error = parser.iterate(cars_json).get(doc); auto error = parser.iterate(cars_json).get(doc);
if(error) { std::cerr << error << std::endl; return false; } if (error) { std::cerr << error << std::endl; return false; }
ondemand::array cars; // invalid until the get() succeeds ondemand::array cars; // invalid until the get() succeeds
error = doc.get_array().get(cars); error = doc.get_array().get(cars);
for (auto car_value : cars) { for (auto car_value : cars) {
ondemand::object car; // invalid until the get() succeeds ondemand::object car; // invalid until the get() succeeds
error = car_value.get_object().get(car); error = car_value.get_object().get(car);
if(error) { std::cerr << error << std::endl; return false; } if (error) { std::cerr << error << std::endl; return false; }
// Accessing a field by name // Accessing a field by name
std::string_view make; std::string_view make;
std::string_view model; std::string_view model;
error = car["make"].get(make); error = car["make"].get(make);
if(error) { std::cerr << error << std::endl; return false; } if (error) { std::cerr << error << std::endl; return false; }
error = car["model"].get(model); error = car["model"].get(model);
if(error) { std::cerr << error << std::endl; return false; } if (error) { std::cerr << error << std::endl; return false; }
cout << "Make/Model: " << make << "/" << model << endl; cout << "Make/Model: " << make << "/" << model << endl;
// Casting a JSON element to an integer // Casting a JSON element to an integer
uint64_t year{}; uint64_t year{};
error = car["year"].get(year); error = car["year"].get(year);
if(error) { std::cerr << error << std::endl; return false; } if (error) { std::cerr << error << std::endl; return false; }
cout << "- This car is " << 2020 - year << " years old." << endl; cout << "- This car is " << 2020 - year << " years old." << endl;
// Iterating through an array of floats // Iterating through an array of floats
double total_tire_pressure = 0; double total_tire_pressure = 0;
ondemand::array pressures; ondemand::array pressures;
error = car["tire_pressure"].get_array().get(pressures); error = car["tire_pressure"].get_array().get(pressures);
if(error) { std::cerr << error << std::endl; return false; } if (error) { std::cerr << error << std::endl; return false; }
for (auto tire_pressure_value : pressures) { for (auto tire_pressure_value : pressures) {
double tire_pressure; double tire_pressure;
error = tire_pressure_value.get_double().get(tire_pressure); error = tire_pressure_value.get_double().get(tire_pressure);
if(error) { std::cerr << error << std::endl; return false; } if (error) { std::cerr << error << std::endl; return false; }
total_tire_pressure += tire_pressure; total_tire_pressure += tire_pressure;
} }
cout << "- Average tire pressure: " << (total_tire_pressure / 4) << endl; cout << "- Average tire pressure: " << (total_tire_pressure / 4) << endl;
@@ -1088,7 +1094,7 @@ after you have initialized them and checked that there is no error:
ondemand::object car; // invalid until the get() succeeds ondemand::object car; // invalid until the get() succeeds
// the `car` instance should not use used before it is initialized // the `car` instance should not use used before it is initialized
error = car_value.get_object().get(car); error = car_value.get_object().get(car);
if(error) { if (error) {
// the `car` instance should not use used // the `car` instance should not use used
} else { } else {
// the `car` instance can be safely used // the `car` instance can be safely used
@@ -1102,20 +1108,20 @@ having to handle exceptions.
ondemand::parser parser; ondemand::parser parser;
ondemand::document doc; ondemand::document doc;
auto error = parser.iterate(json).get(doc); auto error = parser.iterate(json).get(doc);
if(error) { return false; } if (error) { return false; }
ondemand::object object; // invalid until the get() succeeds ondemand::object object; // invalid until the get() succeeds
error = doc.get_object().get(object); error = doc.get_object().get(object);
if(error) { return false; } if (error) { return false; }
for(auto field : object) { for(auto field : object) {
// We could replace 'field.key() with field.unescaped_key(), // We could replace 'field.key() with field.unescaped_key(),
// and ondemand::raw_json_string by std::string_view. // and ondemand::raw_json_string by std::string_view.
ondemand::raw_json_string keyv; ondemand::raw_json_string keyv;
error = field.key().get(keyv); error = field.key().get(keyv);
if(error) { return false; } if (error) { return false; }
if(keyv == "key") { if (keyv == "key") {
uint64_t intvalue; uint64_t intvalue;
error = field.value().get(intvalue); error = field.value().get(intvalue);
if(error) { return false; } if (error) { return false; }
std::cout << intvalue; std::cout << intvalue;
} }
} }
@@ -1294,7 +1300,7 @@ content.
for (uint64_t values : array) { for (uint64_t values : array) {
std::cout << values << std::endl; std::cout << values << std::endl;
} }
if(!doc.at_end()) { if (!doc.at_end()) {
// In this instance, we will be left pointing at 'foo' since we have consumed the array [1,2]. // In this instance, we will be left pointing at 'foo' since we have consumed the array [1,2].
std::cerr << "trailing content at byte index " << doc.current_location() - json.data() << std::endl; std::cerr << "trailing content at byte index " << doc.current_location() - json.data() << std::endl;
} }
@@ -1324,7 +1330,7 @@ before printout the data.
auto doc = parser.iterate(cars_json); auto doc = parser.iterate(cars_json);
for (simdjson_unused ondemand::object car : doc) { for (simdjson_unused ondemand::object car : doc) {
if(car["make"] == "Toyota") { count++; } if (car["make"] == "Toyota") { count++; }
} }
std::cout << "We have " << count << " Toyota cars.\n"; std::cout << "We have " << count << " Toyota cars.\n";
doc.rewind(); // requires simdjson 1.0 or better doc.rewind(); // requires simdjson 1.0 or better
@@ -1392,18 +1398,18 @@ ondemand::parser parser;
ondemand::document_stream stream; ondemand::document_stream stream;
size_t counter{0}; size_t counter{0};
auto error = parser.iterate_many(json, 50).get(stream); auto error = parser.iterate_many(json, 50).get(stream);
if( error ) { /* handle the error */ } if (error) { /* handle the error */ }
for (auto doc: stream) { for (auto doc: stream) {
if(counter < 6) { if (counter < 6) {
int64_t val; int64_t val;
error = doc.at_pointer("/4").get(val); error = doc.at_pointer("/4").get(val);
if( error ) { /* handle the error */ } if (error) { /* handle the error */ }
std::cout << "5 = " << val << std::endl; std::cout << "5 = " << val << std::endl;
} else { } else {
ondemand::value val; ondemand::value val;
error = doc.at_pointer("/4").get(val); error = doc.at_pointer("/4").get(val);
// error == simdjson::CAPACITY // error == simdjson::CAPACITY
if(error) { if (error) {
std::cerr << error << std::endl; std::cerr << error << std::endl;
// We left 293 bytes unprocessed at the tail end of the input. // We left 293 bytes unprocessed at the tail end of the input.
std::cout << " unprocessed bytes at the end: " << stream.truncated_bytes() << std::endl; std::cout << " unprocessed bytes at the end: " << stream.truncated_bytes() << std::endl;
@@ -1944,6 +1950,79 @@ bool example() {
} }
``` ```
* Example 3: CRT
```C++
bool example() {
padded_string padded_input_json = R"([
{ "monitor": [
{ "id": "monitor", "type": "toggle", "label": "monitor" },
{ "id": "profile", "type": "selector", "label": "collection" },
{ "id": "overlay", "type": "selector", "label": "overlay" },
{ "id": "zoom", "type": "toggleSlider", "label": "zoom" }
] },
{ "crt": [
{ "id": "system", "type": "multi", "label": "system", "choices": "PAL, NTSC" },
{ "type": "spacer" },
{ "id": "brightness", "type": "slider", "icon": "brightness" },
{ "id": "contrast", "type": "slider", "icon": "contrast" },
{ "id": "saturation", "type": "slider", "icon": "saturation" },
{ "type": "spacer" },
{ "id": "overscan", "type": "toggleSlider", "label": "overscan" },
{ "type": "spacer" },
{ "id": "emulation", "type": "toggle", "label": "CRT emulation" },
{ "type": "spacer" },
{ "id": "curve", "type": "toggleSlider", "label": "curve" },
{ "id": "bleed", "type": "toggleSlider", "label": "bleed" },
{ "id": "vignette", "type": "toggleSlider", "label": "vignette" },
{ "id": "scanlines", "type": "toggleSlider", "label": "scanlines" },
{ "id": "gridlines", "type": "toggleSlider", "label": "gridlines" },
{ "id": "glow", "type": "toggleSlider", "label": "glow" },
{ "id": "flicker", "type": "toggleSlider", "label": "flicker" },
{ "id": "noise", "type": "toggleSlider", "label": "noise" },
{}
] }
])"_padded;
auto parser = ondemand::parser{};
auto doc = parser.iterate(padded_input_json);
auto root_array = doc.get_array();
// the root should be an object, not an array, but that's the JSON we are
// given.
for (ondemand::object node : root_array) {
// We know that we are going to have just one element in the object.
for (auto field : node) {
std::cout << "\n\ntop level:" << field.key() << std::endl;
// You can get a proper std::string_view for the key with:
// std::string_view key = field.unescaped_key();
// and second for-range loop to get child-elements here
for (ondemand::object inner_object : field.value()) {
auto i = inner_object.begin();
if (i == inner_object.end()) {
std::cout << "empty object" << std::endl;
continue;
} else {
for (; i != inner_object.end(); ++i) {
auto inner_field = *i;
std::cout << '"' << inner_field.key()
<< "\" : " << inner_field.value() << ", ";
// You can get proper std::string_view for the key and value with:
// std::string_view inner_key = field.unescaped_key();
// std::string_view value_str = field.value();
}
}
std::cout << std::endl;
}
// You can break here if you only want just the first element.
// break;
}
}
return true;
}
```
Performance Tips Performance Tips
-------- --------
@@ -1969,4 +2048,4 @@ Performance Tips
std::string_view year = data["year"]; std::string_view year = data["year"];
std::string_view rating = data["rating"]; std::string_view rating = data["rating"];
``` ```
- To better understand the operation of your On Demand parser, and whether it is performing as well as you think it should be, there is a logger feature built in to simdjson! To use it, define the pre-processor directive `SIMDJSON_VERBOSE_LOGGING` prior to including the `simdjson.h` header, which enables logging in simdjson. Run your code. It may generate a lot of logging output; adding printouts from your application that show each section may be helpful. The log's output will show step-by-step information on state, buffer pointer position, depth, and key retrieval status. Importantly, unless `SIMDJSON_VERBOSE_LOGGING` is defined, logging is entirely disabled and thus carries no overhead. - To better understand the operation of your On Demand parser, and whether it is performing as well as you think it should be, there is a logger feature built in to simdjson! To use it, define the pre-processor directive `SIMDJSON_VERBOSE_LOGGING` prior to including the `simdjson.h` header, which enables logging in simdjson. Run your code. It may generate a lot of logging output; adding printouts from your application that show each section may be helpful. The log's output will show step-by-step information on state, buffer pointer position, depth, and key retrieval status. Importantly, unless `SIMDJSON_VERBOSE_LOGGING` is defined, logging is entirely disabled and thus carries no overhead.
+2 -2
View File
@@ -329,10 +329,10 @@ int main(void) {
simdjson::dom::parser parser; simdjson::dom::parser parser;
simdjson::dom::element tweets; // invalid until the get() succeeds simdjson::dom::element tweets; // invalid until the get() succeeds
auto error = parser.load("twitter.json").get(tweets); auto error = parser.load("twitter.json").get(tweets);
if(error) { std::cerr << error << std::endl; return EXIT_FAILURE; } if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
uint64_t identifier; uint64_t identifier;
error = tweets["statuses"].at(0)["id"].get(identifier); error = tweets["statuses"].at(0)["id"].get(identifier);
if(error) { std::cerr << error << std::endl; return EXIT_FAILURE; } if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
std::cout << identifier << std::endl; std::cout << identifier << std::endl;
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
+3 -3
View File
@@ -101,8 +101,8 @@ by comparing it with the null pointer.
```c++ ```c++
auto my_implementation = simdjson::get_available_implementations()["haswell"]; auto my_implementation = simdjson::get_available_implementations()["haswell"];
if(! my_implementation) { exit(1); } if (! my_implementation) { exit(1); }
if(! my_implementation->supported_by_runtime_system()) { exit(1); } if (! my_implementation->supported_by_runtime_system()) { exit(1); }
simdjson::get_active_implementation() = my_implementation; simdjson::get_active_implementation() = my_implementation;
``` ```
@@ -113,7 +113,7 @@ You should call `supported_by_runtime_system()` to compare the processor's featu
```c++ ```c++
for (auto implementation : simdjson::get_available_implementations()) { for (auto implementation : simdjson::get_available_implementations()) {
if(implementation->supported_by_runtime_system()) { if (implementation->supported_by_runtime_system()) {
cout << implementation->name() << ": " << implementation->description() << endl; cout << implementation->name() << ": " << implementation->description() << endl;
} }
} }
+4 -4
View File
@@ -196,12 +196,12 @@ Let us illustrate the idea with code:
simdjson::ondemand::parser parser; simdjson::ondemand::parser parser;
simdjson::ondemand::document_stream stream; simdjson::ondemand::document_stream stream;
auto error = parser.iterate_many(json).get(stream); auto error = parser.iterate_many(json).get(stream);
if( error ) { /* do something */ } if (error) { /* do something */ }
auto i = stream.begin(); auto i = stream.begin();
size_t count{0}; size_t count{0};
for(; i != stream.end(); ++i) { for(; i != stream.end(); ++i) {
auto doc = *i; auto doc = *i;
if(!i.error()) { if (!i.error()) {
std::cout << "got full document at " << i.current_index() << std::endl; std::cout << "got full document at " << i.current_index() << std::endl;
std::cout << i.source() << std::endl; std::cout << i.source() << std::endl;
count++; count++;
@@ -237,7 +237,7 @@ Consider the following example where a truncated document (`{"key":"intentionall
simdjson::ondemand::parser parser; simdjson::ondemand::parser parser;
simdjson::ondemand::document_stream stream; simdjson::ondemand::document_stream stream;
auto error = parser.iterate_many(json,json.size()).get(stream); auto error = parser.iterate_many(json,json.size()).get(stream);
if(error) { std::cerr << error << std::endl; return; } if (error) { std::cerr << error << std::endl; return; }
for(auto i = stream.begin(); i != stream.end(); ++i) { for(auto i = stream.begin(); i != stream.end(); ++i) {
std::cout << i.source() << std::endl; std::cout << i.source() << std::endl;
} }
@@ -269,7 +269,7 @@ Example:
// we pass 'true' to the allow_comma parameter, the batch size will be set to at least // we pass 'true' to the allow_comma parameter, the batch size will be set to at least
// the document size. // the document size.
auto error = parser.iterate_many(json, 32, true).get(doc_stream); auto error = parser.iterate_many(json, 32, true).get(doc_stream);
if(error) { std::cerr << error << std::endl; return; } if (error) { std::cerr << error << std::endl; return; }
for (auto doc : doc_stream) { for (auto doc : doc_stream) {
std::cout << doc.type() << std::endl; std::cout << doc.type() << std::endl;
} }
+4 -4
View File
@@ -679,11 +679,11 @@ in production systems:
ondemand::object c1 = parent["child1"]; ondemand::object c1 = parent["child1"];
// c1 owns the focus // c1 owns the focus
// //
if(std::string_view(c1["name"]) != "John") { ... } if (std::string_view(c1["name"]) != "John") { ... }
// c2 attempts to grab the focus from parent but fails // c2 attempts to grab the focus from parent but fails
ondemand::object c2 = parent["child2"]; ondemand::object c2 = parent["child2"];
// c2 is now in an unsafe state and the following line would be unsafe // c2 is now in an unsafe state and the following line would be unsafe
// if(std::string_view(c2["name"]) != "Daniel") { return false; } // if (std::string_view(c2["name"]) != "Daniel") { return false; }
``` ```
A correct usage is given by the following example: A correct usage is given by the following example:
@@ -697,7 +697,7 @@ in production systems:
{ {
ondemand::object c1 = parent["child1"]; ondemand::object c1 = parent["child1"];
// c1 grabbed the focus from parent // c1 grabbed the focus from parent
if(std::string_view(c1["name"]) != "John") { return false; } if (std::string_view(c1["name"]) != "John") { return false; }
} }
// c1 went out of scope, so its destructor was called and the focus // c1 went out of scope, so its destructor was called and the focus
// was handed back to parent. // was handed back to parent.
@@ -705,7 +705,7 @@ in production systems:
ondemand::object c2 = parent["child2"]; ondemand::object c2 = parent["child2"];
// c2 grabbed the focus from parent // c2 grabbed the focus from parent
// the following is safe: // the following is safe:
if(std::string_view(c2["name"]) != "Daniel") { return false; } if (std::string_view(c2["name"]) != "Daniel") { return false; }
} }
``` ```
+3 -3
View File
@@ -184,12 +184,12 @@ Let us illustrate the idea with code:
simdjson::dom::parser parser; simdjson::dom::parser parser;
simdjson::dom::document_stream stream; simdjson::dom::document_stream stream;
auto error = parser.parse_many(json).get(stream); auto error = parser.parse_many(json).get(stream);
if( error ) { /* do something */ } if (error) { /* do something */ }
auto i = stream.begin(); auto i = stream.begin();
size_t count{0}; size_t count{0};
for(; i != stream.end(); ++i) { for(; i != stream.end(); ++i) {
auto doc = *i; auto doc = *i;
if(!doc.error()) { if (!doc.error()) {
std::cout << "got full document at " << i.current_index() << std::endl; std::cout << "got full document at " << i.current_index() << std::endl;
std::cout << i.source() << std::endl; std::cout << i.source() << std::endl;
count++; count++;
@@ -225,7 +225,7 @@ Consider the following example where a truncated document (`{"key":"intentionall
simdjson::dom::parser parser; simdjson::dom::parser parser;
simdjson::dom::document_stream stream; simdjson::dom::document_stream stream;
auto error = parser.parse_many(json,json.size()).get(stream); auto error = parser.parse_many(json,json.size()).get(stream);
if(error) { std::cerr << error << std::endl; return; } if (error) { std::cerr << error << std::endl; return; }
for(auto doc : stream) { for(auto doc : stream) {
std::cout << doc << std::endl; std::cout << doc << std::endl;
} }
+4 -8
View File
@@ -186,8 +186,7 @@ simdjson_inline bool compute_float_64(int64_t power, uint64_t i, bool negative,
// power_of_five_128[index]. Usually, that's good enough to approximate i * 5^q // power_of_five_128[index]. Usually, that's good enough to approximate i * 5^q
// to the desired approximation using one multiplication. Sometimes it does not suffice. // to the desired approximation using one multiplication. Sometimes it does not suffice.
// Then we store the next most significant 64 bits in power_of_five_128[index + 1], and // Then we store the next most significant 64 bits in power_of_five_128[index + 1], and
// then we get a better approximation to i * 5^q. In very rare cases, even that // then we get a better approximation to i * 5^q.
// will not suffice, though it is seemingly very hard to find such a scenario.
// //
// That's for when q>=0. The logic for q<0 is somewhat similar but it is somewhat // That's for when q>=0. The logic for q<0 is somewhat similar but it is somewhat
// more complicated. // more complicated.
@@ -202,12 +201,9 @@ simdjson_inline bool compute_float_64(int64_t power, uint64_t i, bool negative,
simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]); simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]);
firstproduct.low += secondproduct.high; firstproduct.low += secondproduct.high;
if(secondproduct.high > firstproduct.low) { firstproduct.high++; } if(secondproduct.high > firstproduct.low) { firstproduct.high++; }
// At this point, we might need to add at most one to firstproduct, but this // As it has been proven by Noble Mushtak and Daniel Lemire in "Fast Number Parsing Without
// can only change the value of firstproduct.high if firstproduct.low is maximal. // Fallback" (https://arxiv.org/abs/2212.06644), at this point we are sure that the product
if(simdjson_unlikely(firstproduct.low == 0xFFFFFFFFFFFFFFFF)) { // is sufficiently accurate, and more computation is not needed.
// This is very unlikely, but if so, we need to do much more work!
return false;
}
} }
uint64_t lower = firstproduct.low; uint64_t lower = firstproduct.low;
uint64_t upper = firstproduct.high; uint64_t upper = firstproduct.high;
+3 -3
View File
@@ -4,7 +4,7 @@
#define SIMDJSON_SIMDJSON_VERSION_H #define SIMDJSON_SIMDJSON_VERSION_H
/** The version of simdjson being used (major.minor.revision) */ /** The version of simdjson being used (major.minor.revision) */
#define SIMDJSON_VERSION "3.2.3" #define SIMDJSON_VERSION "3.3.0"
namespace simdjson { namespace simdjson {
enum { enum {
@@ -15,11 +15,11 @@ enum {
/** /**
* The minor version (major.MINOR.revision) of simdjson being used. * The minor version (major.MINOR.revision) of simdjson being used.
*/ */
SIMDJSON_VERSION_MINOR = 2, SIMDJSON_VERSION_MINOR = 3,
/** /**
* The revision (major.minor.REVISION) of simdjson being used. * The revision (major.minor.REVISION) of simdjson being used.
*/ */
SIMDJSON_VERSION_REVISION = 3 SIMDJSON_VERSION_REVISION = 0
}; };
} // namespace simdjson } // namespace simdjson
+405 -499
View File
File diff suppressed because it is too large Load Diff
+36 -52
View File
@@ -1,4 +1,4 @@
/* auto-generated on 2023-08-18 14:37:10 -0400. Do not edit! */ /* auto-generated on . Do not edit! */
/* including simdjson.h: */ /* including simdjson.h: */
/* begin file simdjson.h */ /* begin file simdjson.h */
#ifndef SIMDJSON_H #ifndef SIMDJSON_H
@@ -79,6 +79,14 @@
#error simdjson requires a compiler compliant with the C++11 standard #error simdjson requires a compiler compliant with the C++11 standard
#endif #endif
#ifndef SIMDJSON_IF_CONSTEXPR
#if SIMDJSON_CPLUSPLUS17
#define SIMDJSON_IF_CONSTEXPR if constexpr
#else
#define SIMDJSON_IF_CONSTEXPR if
#endif
#endif
#endif // SIMDJSON_COMPILER_CHECK_H #endif // SIMDJSON_COMPILER_CHECK_H
/* end file simdjson/compiler_check.h */ /* end file simdjson/compiler_check.h */
/* including simdjson/portability.h: #include "simdjson/portability.h" */ /* including simdjson/portability.h: #include "simdjson/portability.h" */
@@ -2314,7 +2322,7 @@ namespace std {
#define SIMDJSON_SIMDJSON_VERSION_H #define SIMDJSON_SIMDJSON_VERSION_H
/** The version of simdjson being used (major.minor.revision) */ /** The version of simdjson being used (major.minor.revision) */
#define SIMDJSON_VERSION "3.2.3" #define SIMDJSON_VERSION "3.3.0"
namespace simdjson { namespace simdjson {
enum { enum {
@@ -2325,11 +2333,11 @@ enum {
/** /**
* The minor version (major.MINOR.revision) of simdjson being used. * The minor version (major.MINOR.revision) of simdjson being used.
*/ */
SIMDJSON_VERSION_MINOR = 2, SIMDJSON_VERSION_MINOR = 3,
/** /**
* The revision (major.minor.REVISION) of simdjson being used. * The revision (major.minor.REVISION) of simdjson being used.
*/ */
SIMDJSON_VERSION_REVISION = 3 SIMDJSON_VERSION_REVISION = 0
}; };
} // namespace simdjson } // namespace simdjson
@@ -11813,8 +11821,7 @@ simdjson_inline bool compute_float_64(int64_t power, uint64_t i, bool negative,
// power_of_five_128[index]. Usually, that's good enough to approximate i * 5^q // power_of_five_128[index]. Usually, that's good enough to approximate i * 5^q
// to the desired approximation using one multiplication. Sometimes it does not suffice. // to the desired approximation using one multiplication. Sometimes it does not suffice.
// Then we store the next most significant 64 bits in power_of_five_128[index + 1], and // Then we store the next most significant 64 bits in power_of_five_128[index + 1], and
// then we get a better approximation to i * 5^q. In very rare cases, even that // then we get a better approximation to i * 5^q.
// will not suffice, though it is seemingly very hard to find such a scenario.
// //
// That's for when q>=0. The logic for q<0 is somewhat similar but it is somewhat // That's for when q>=0. The logic for q<0 is somewhat similar but it is somewhat
// more complicated. // more complicated.
@@ -11829,12 +11836,9 @@ simdjson_inline bool compute_float_64(int64_t power, uint64_t i, bool negative,
simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]); simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]);
firstproduct.low += secondproduct.high; firstproduct.low += secondproduct.high;
if(secondproduct.high > firstproduct.low) { firstproduct.high++; } if(secondproduct.high > firstproduct.low) { firstproduct.high++; }
// At this point, we might need to add at most one to firstproduct, but this // As it has been proven by Noble Mushtak and Daniel Lemire in "Fast Number Parsing Without
// can only change the value of firstproduct.high if firstproduct.low is maximal. // Fallback" (https://arxiv.org/abs/2212.06644), at this point we are sure that the product
if(simdjson_unlikely(firstproduct.low == 0xFFFFFFFFFFFFFFFF)) { // is sufficiently accurate, and more computation is not needed.
// This is very unlikely, but if so, we need to do much more work!
return false;
}
} }
uint64_t lower = firstproduct.low; uint64_t lower = firstproduct.low;
uint64_t upper = firstproduct.high; uint64_t upper = firstproduct.high;
@@ -13869,8 +13873,7 @@ simdjson_inline bool compute_float_64(int64_t power, uint64_t i, bool negative,
// power_of_five_128[index]. Usually, that's good enough to approximate i * 5^q // power_of_five_128[index]. Usually, that's good enough to approximate i * 5^q
// to the desired approximation using one multiplication. Sometimes it does not suffice. // to the desired approximation using one multiplication. Sometimes it does not suffice.
// Then we store the next most significant 64 bits in power_of_five_128[index + 1], and // Then we store the next most significant 64 bits in power_of_five_128[index + 1], and
// then we get a better approximation to i * 5^q. In very rare cases, even that // then we get a better approximation to i * 5^q.
// will not suffice, though it is seemingly very hard to find such a scenario.
// //
// That's for when q>=0. The logic for q<0 is somewhat similar but it is somewhat // That's for when q>=0. The logic for q<0 is somewhat similar but it is somewhat
// more complicated. // more complicated.
@@ -13885,12 +13888,9 @@ simdjson_inline bool compute_float_64(int64_t power, uint64_t i, bool negative,
simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]); simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]);
firstproduct.low += secondproduct.high; firstproduct.low += secondproduct.high;
if(secondproduct.high > firstproduct.low) { firstproduct.high++; } if(secondproduct.high > firstproduct.low) { firstproduct.high++; }
// At this point, we might need to add at most one to firstproduct, but this // As it has been proven by Noble Mushtak and Daniel Lemire in "Fast Number Parsing Without
// can only change the value of firstproduct.high if firstproduct.low is maximal. // Fallback" (https://arxiv.org/abs/2212.06644), at this point we are sure that the product
if(simdjson_unlikely(firstproduct.low == 0xFFFFFFFFFFFFFFFF)) { // is sufficiently accurate, and more computation is not needed.
// This is very unlikely, but if so, we need to do much more work!
return false;
}
} }
uint64_t lower = firstproduct.low; uint64_t lower = firstproduct.low;
uint64_t upper = firstproduct.high; uint64_t upper = firstproduct.high;
@@ -16417,8 +16417,7 @@ simdjson_inline bool compute_float_64(int64_t power, uint64_t i, bool negative,
// power_of_five_128[index]. Usually, that's good enough to approximate i * 5^q // power_of_five_128[index]. Usually, that's good enough to approximate i * 5^q
// to the desired approximation using one multiplication. Sometimes it does not suffice. // to the desired approximation using one multiplication. Sometimes it does not suffice.
// Then we store the next most significant 64 bits in power_of_five_128[index + 1], and // Then we store the next most significant 64 bits in power_of_five_128[index + 1], and
// then we get a better approximation to i * 5^q. In very rare cases, even that // then we get a better approximation to i * 5^q.
// will not suffice, though it is seemingly very hard to find such a scenario.
// //
// That's for when q>=0. The logic for q<0 is somewhat similar but it is somewhat // That's for when q>=0. The logic for q<0 is somewhat similar but it is somewhat
// more complicated. // more complicated.
@@ -16433,12 +16432,9 @@ simdjson_inline bool compute_float_64(int64_t power, uint64_t i, bool negative,
simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]); simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]);
firstproduct.low += secondproduct.high; firstproduct.low += secondproduct.high;
if(secondproduct.high > firstproduct.low) { firstproduct.high++; } if(secondproduct.high > firstproduct.low) { firstproduct.high++; }
// At this point, we might need to add at most one to firstproduct, but this // As it has been proven by Noble Mushtak and Daniel Lemire in "Fast Number Parsing Without
// can only change the value of firstproduct.high if firstproduct.low is maximal. // Fallback" (https://arxiv.org/abs/2212.06644), at this point we are sure that the product
if(simdjson_unlikely(firstproduct.low == 0xFFFFFFFFFFFFFFFF)) { // is sufficiently accurate, and more computation is not needed.
// This is very unlikely, but if so, we need to do much more work!
return false;
}
} }
uint64_t lower = firstproduct.low; uint64_t lower = firstproduct.low;
uint64_t upper = firstproduct.high; uint64_t upper = firstproduct.high;
@@ -18964,8 +18960,7 @@ simdjson_inline bool compute_float_64(int64_t power, uint64_t i, bool negative,
// power_of_five_128[index]. Usually, that's good enough to approximate i * 5^q // power_of_five_128[index]. Usually, that's good enough to approximate i * 5^q
// to the desired approximation using one multiplication. Sometimes it does not suffice. // to the desired approximation using one multiplication. Sometimes it does not suffice.
// Then we store the next most significant 64 bits in power_of_five_128[index + 1], and // Then we store the next most significant 64 bits in power_of_five_128[index + 1], and
// then we get a better approximation to i * 5^q. In very rare cases, even that // then we get a better approximation to i * 5^q.
// will not suffice, though it is seemingly very hard to find such a scenario.
// //
// That's for when q>=0. The logic for q<0 is somewhat similar but it is somewhat // That's for when q>=0. The logic for q<0 is somewhat similar but it is somewhat
// more complicated. // more complicated.
@@ -18980,12 +18975,9 @@ simdjson_inline bool compute_float_64(int64_t power, uint64_t i, bool negative,
simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]); simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]);
firstproduct.low += secondproduct.high; firstproduct.low += secondproduct.high;
if(secondproduct.high > firstproduct.low) { firstproduct.high++; } if(secondproduct.high > firstproduct.low) { firstproduct.high++; }
// At this point, we might need to add at most one to firstproduct, but this // As it has been proven by Noble Mushtak and Daniel Lemire in "Fast Number Parsing Without
// can only change the value of firstproduct.high if firstproduct.low is maximal. // Fallback" (https://arxiv.org/abs/2212.06644), at this point we are sure that the product
if(simdjson_unlikely(firstproduct.low == 0xFFFFFFFFFFFFFFFF)) { // is sufficiently accurate, and more computation is not needed.
// This is very unlikely, but if so, we need to do much more work!
return false;
}
} }
uint64_t lower = firstproduct.low; uint64_t lower = firstproduct.low;
uint64_t upper = firstproduct.high; uint64_t upper = firstproduct.high;
@@ -21626,8 +21618,7 @@ simdjson_inline bool compute_float_64(int64_t power, uint64_t i, bool negative,
// power_of_five_128[index]. Usually, that's good enough to approximate i * 5^q // power_of_five_128[index]. Usually, that's good enough to approximate i * 5^q
// to the desired approximation using one multiplication. Sometimes it does not suffice. // to the desired approximation using one multiplication. Sometimes it does not suffice.
// Then we store the next most significant 64 bits in power_of_five_128[index + 1], and // Then we store the next most significant 64 bits in power_of_five_128[index + 1], and
// then we get a better approximation to i * 5^q. In very rare cases, even that // then we get a better approximation to i * 5^q.
// will not suffice, though it is seemingly very hard to find such a scenario.
// //
// That's for when q>=0. The logic for q<0 is somewhat similar but it is somewhat // That's for when q>=0. The logic for q<0 is somewhat similar but it is somewhat
// more complicated. // more complicated.
@@ -21642,12 +21633,9 @@ simdjson_inline bool compute_float_64(int64_t power, uint64_t i, bool negative,
simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]); simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]);
firstproduct.low += secondproduct.high; firstproduct.low += secondproduct.high;
if(secondproduct.high > firstproduct.low) { firstproduct.high++; } if(secondproduct.high > firstproduct.low) { firstproduct.high++; }
// At this point, we might need to add at most one to firstproduct, but this // As it has been proven by Noble Mushtak and Daniel Lemire in "Fast Number Parsing Without
// can only change the value of firstproduct.high if firstproduct.low is maximal. // Fallback" (https://arxiv.org/abs/2212.06644), at this point we are sure that the product
if(simdjson_unlikely(firstproduct.low == 0xFFFFFFFFFFFFFFFF)) { // is sufficiently accurate, and more computation is not needed.
// This is very unlikely, but if so, we need to do much more work!
return false;
}
} }
uint64_t lower = firstproduct.low; uint64_t lower = firstproduct.low;
uint64_t upper = firstproduct.high; uint64_t upper = firstproduct.high;
@@ -24611,8 +24599,7 @@ simdjson_inline bool compute_float_64(int64_t power, uint64_t i, bool negative,
// power_of_five_128[index]. Usually, that's good enough to approximate i * 5^q // power_of_five_128[index]. Usually, that's good enough to approximate i * 5^q
// to the desired approximation using one multiplication. Sometimes it does not suffice. // to the desired approximation using one multiplication. Sometimes it does not suffice.
// Then we store the next most significant 64 bits in power_of_five_128[index + 1], and // Then we store the next most significant 64 bits in power_of_five_128[index + 1], and
// then we get a better approximation to i * 5^q. In very rare cases, even that // then we get a better approximation to i * 5^q.
// will not suffice, though it is seemingly very hard to find such a scenario.
// //
// That's for when q>=0. The logic for q<0 is somewhat similar but it is somewhat // That's for when q>=0. The logic for q<0 is somewhat similar but it is somewhat
// more complicated. // more complicated.
@@ -24627,12 +24614,9 @@ simdjson_inline bool compute_float_64(int64_t power, uint64_t i, bool negative,
simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]); simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]);
firstproduct.low += secondproduct.high; firstproduct.low += secondproduct.high;
if(secondproduct.high > firstproduct.low) { firstproduct.high++; } if(secondproduct.high > firstproduct.low) { firstproduct.high++; }
// At this point, we might need to add at most one to firstproduct, but this // As it has been proven by Noble Mushtak and Daniel Lemire in "Fast Number Parsing Without
// can only change the value of firstproduct.high if firstproduct.low is maximal. // Fallback" (https://arxiv.org/abs/2212.06644), at this point we are sure that the product
if(simdjson_unlikely(firstproduct.low == 0xFFFFFFFFFFFFFFFF)) { // is sufficiently accurate, and more computation is not needed.
// This is very unlikely, but if so, we need to do much more work!
return false;
}
} }
uint64_t lower = firstproduct.low; uint64_t lower = firstproduct.low;
uint64_t upper = firstproduct.high; uint64_t upper = firstproduct.high;
+7 -5
View File
@@ -303,15 +303,17 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::visit_root_primit
} }
template<typename V> template<typename V>
simdjson_warn_unused simdjson_inline error_code json_iterator::visit_primitive(V &visitor, const uint8_t *value) noexcept { simdjson_warn_unused simdjson_inline error_code json_iterator::visit_primitive(V &visitor, const uint8_t *value) noexcept {
// Use the fact that most scalars are going to be either strings or numbers.
if(*value == '"') {
return visitor.visit_string(*this, value);
} else if (((*value - '0') < 10) || (*value == '-')) {
return visitor.visit_number(*this, value);
}
// true, false, null are uncommon.
switch (*value) { switch (*value) {
case '"': return visitor.visit_string(*this, value);
case 't': return visitor.visit_true_atom(*this, value); case 't': return visitor.visit_true_atom(*this, value);
case 'f': return visitor.visit_false_atom(*this, value); case 'f': return visitor.visit_false_atom(*this, value);
case 'n': return visitor.visit_null_atom(*this, value); case 'n': return visitor.visit_null_atom(*this, value);
case '-':
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
return visitor.visit_number(*this, value);
default: default:
log_error("Non-value found when value was expected!"); log_error("Non-value found when value was expected!");
return TAPE_ERROR; return TAPE_ERROR;
@@ -73,6 +73,74 @@ bool at_end() {
TEST_SUCCEED(); TEST_SUCCEED();
} }
bool examplecrt() {
TEST_START();
padded_string padded_input_json = R"([
{ "monitor": [
{ "id": "monitor", "type": "toggle", "label": "monitor" },
{ "id": "profile", "type": "selector", "label": "collection" },
{ "id": "overlay", "type": "selector", "label": "overlay" },
{ "id": "zoom", "type": "toggleSlider", "label": "zoom" }
] },
{ "crt": [
{ "id": "system", "type": "multi", "label": "system", "choices": "PAL, NTSC" },
{ "type": "spacer" },
{ "id": "brightness", "type": "slider", "icon": "brightness" },
{ "id": "contrast", "type": "slider", "icon": "contrast" },
{ "id": "saturation", "type": "slider", "icon": "saturation" },
{ "type": "spacer" },
{ "id": "overscan", "type": "toggleSlider", "label": "overscan" },
{ "type": "spacer" },
{ "id": "emulation", "type": "toggle", "label": "CRT emulation" },
{ "type": "spacer" },
{ "id": "curve", "type": "toggleSlider", "label": "curve" },
{ "id": "bleed", "type": "toggleSlider", "label": "bleed" },
{ "id": "vignette", "type": "toggleSlider", "label": "vignette" },
{ "id": "scanlines", "type": "toggleSlider", "label": "scanlines" },
{ "id": "gridlines", "type": "toggleSlider", "label": "gridlines" },
{ "id": "glow", "type": "toggleSlider", "label": "glow" },
{ "id": "flicker", "type": "toggleSlider", "label": "flicker" },
{ "id": "noise", "type": "toggleSlider", "label": "noise" },
{}
] }
])"_padded;
auto parser = ondemand::parser{};
auto doc = parser.iterate(padded_input_json);
auto root_array = doc.get_array();
// the root should be an object, not an array, but that's the JSON we are
// given.
for (ondemand::object node : root_array) {
// We know that we are going to have just one element in the object.
for (auto field : node) {
std::cout << "\n\ntop level:" << field.key() << std::endl;
// You can get a proper std::string_view for the key with:
// std::string_view key = field.unescaped_key();
// and second for-range loop to get child-elements here
for (ondemand::object inner_object : field.value()) {
auto i = inner_object.begin();
if (i == inner_object.end()) {
std::cout << "empty object" << std::endl;
continue;
} else {
for (; i != inner_object.end(); ++i) {
auto inner_field = *i;
std::cout << '"' << inner_field.key()
<< "\" : " << inner_field.value() << ", ";
// You can get proper std::string_view for the key and value with:
// std::string_view inner_key = field.unescaped_key();
// std::string_view value_str = field.value();
}
}
std::cout << std::endl;
}
// You can break here if you only want just the first element.
// break;
}
}
TEST_SUCCEED();
}
bool number_tests() { bool number_tests() {
TEST_START(); TEST_START();
ondemand::parser parser; ondemand::parser parser;
@@ -1356,6 +1424,7 @@ bool run() {
&& raw_string() && raw_string()
&& number_tests() && number_tests()
&& current_location_tape_error_with_except() && current_location_tape_error_with_except()
&& examplecrt()
#endif #endif
; ;
} }