Compare commits

..

17 Commits

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

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

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

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

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

* Update basics.md
2023-09-11 10:01:35 -04:00
Daniel Lemire e0699994ef Testing macOS shared library support (#2062) 2023-09-09 12:48:42 -04:00
Daniel Lemire 68ba9a1b2a Adding a few spaces (minor formatting) (#2059) 2023-08-30 20:23:58 -04:00
Daniel Lemire 6fed6bd29b Update README.md 2023-08-30 19:26:34 -04:00
Piotr Rżysko cc24bb4114 Removed number parsing fallback (#2056) 2023-08-29 18:28:21 -04:00
94 changed files with 3012 additions and 2868 deletions
+1 -1
View File
@@ -31,7 +31,7 @@ A clear and concise description of any alternative solutions or features you've
**Additional context**
Add any other context or screenshots about the feature request here.
** Are you willing to contribute code or documentation toward this new feature? **
**Are you willing to contribute code or documentation toward this new feature?**
If you plan to contribute to simdjson, please read our
* CONTRIBUTING guide: https://github.com/simdjson/simdjson/blob/master/CONTRIBUTING.md and our
* HACKING guide: https://github.com/simdjson/simdjson/blob/master/HACKING.md
+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 &&
cd ../tests/installation_tests/find &&
mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../../../build/destination .. && cmake --build .
- name: Use cmake (shared)
run: |
mkdir buildshared &&
cd buildshared &&
cmake -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
cmake --build . &&
ctest --output-on-failure -LE explicitonly -j &&
cmake --install . &&
echo -e '#include <simdjson.h>\nint main(int argc,char**argv) {simdjson::dom::parser parser;simdjson::dom::element tweets = parser.load(argv[1]); }' > tmp.cpp && c++ -Idestination/include -Ldestination/lib -std=c++17 -Wl,-rpath,destination/lib -o linkandrun tmp.cpp -lsimdjson && ./linkandrun jsonexamples/twitter.json &&
cd ../tests/installation_tests/find &&
mkdir buildshared && cd buildshared && cmake -DCMAKE_INSTALL_PREFIX:PATH=../../../buildshared/destination .. && cmake --build .
+32 -5
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.2.3
VERSION 3.4.0
DESCRIPTION "Parsing gigabytes of JSON per second"
HOMEPAGE_URL "https://simdjson.org/"
LANGUAGES CXX C
@@ -20,8 +20,10 @@ string(
# ---- Options, variables ----
# These version numbers are modified by tools/release.py
set(SIMDJSON_LIB_VERSION "16.0.0" CACHE STRING "simdjson library version")
set(SIMDJSON_LIB_SOVERSION "16" CACHE STRING "simdjson library soversion")
set(SIMDJSON_LIB_VERSION "17.0.0" CACHE STRING "simdjson library version")
set(SIMDJSON_LIB_SOVERSION "17" CACHE STRING "simdjson library soversion")
option(SIMDJSON_BUILD_STATIC_LIB "Build simdjson_static library along with simdjson" OFF)
option(SIMDJSON_ENABLE_THREADS "Link with thread support" ON)
@@ -56,8 +58,17 @@ include(cmake/developer-options.cmake)
# ---- simdjson library ----
add_library(simdjson src/simdjson.cpp)
set(SIMDJSON_SOURCES src/simdjson.cpp)
add_library(simdjson ${SIMDJSON_SOURCES})
add_library(simdjson::simdjson ALIAS simdjson)
set(SIMDJSON_LIBRARIES simdjson)
if(SIMDJSON_BUILD_STATIC_LIB)
add_library(simdjson_static STATIC ${SIMDJSON_SOURCES})
add_library(simdjson::simdjson_static ALIAS simdjson_static)
list(APPEND SIMDJSON_LIBRARIES simdjson_static)
endif()
set_target_properties(
simdjson PROPERTIES
@@ -117,6 +128,9 @@ if(SIMDJSON_ENABLE_THREADS)
endif()
simdjson_apply_props(simdjson)
if(SIMDJSON_BUILD_STATIC_LIB)
simdjson_apply_props(simdjson_static)
endif()
# ---- Install rules ----
@@ -138,7 +152,6 @@ install(
ARCHIVE COMPONENT simdjson_Development
INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
)
configure_file(cmake/simdjson-config.cmake.in simdjson-config.cmake @ONLY)
write_basic_package_version_file(
@@ -167,6 +180,20 @@ install(
COMPONENT simdjson_Development
)
if(SIMDJSON_BUILD_STATIC_LIB)
install(
TARGETS simdjson_static
EXPORT simdjson_staticTargets
ARCHIVE COMPONENT simdjson_Development
)
install(
EXPORT simdjson_staticTargets
NAMESPACE simdjson::
DESTINATION "${SIMDJSON_INSTALL_CMAKEDIR}"
COMPONENT simdjson_Development
)
endif()
# pkg-config
include(cmake/JoinPaths.cmake)
join_paths(PKGCONFIG_INCLUDEDIR "\${prefix}" "${CMAKE_INSTALL_INCLUDEDIR}")
+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.2.3"
PROJECT_NUMBER = "3.4.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
-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)
![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)
![MinGW64-CI](https://github.com/simdjson/simdjson/workflows/MinGW64-CI/badge.svg)
[![][license img]][license]
[![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_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_MACOSX_RPATH OFF)
set(CMAKE_THREAD_PREFER_PTHREAD ON)
set(THREADS_PREFER_PTHREAD_FLAG ON)
set(SIMDJSON_STRUCTURAL_INDEXER_STEP CACHE STRING "the SIMDJSON_STRUCTURAL_INDEXER_STEP variable")
+1
View File
@@ -4,3 +4,4 @@ if("@SIMDJSON_ENABLE_THREADS@")
endif()
include("${CMAKE_CURRENT_LIST_DIR}/simdjsonTargets.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/simdjson_staticTargets.cmake" OPTIONAL)
+152 -38
View File
@@ -179,8 +179,8 @@ strcpy(json, "[1]");
ondemand::document doc = parser.iterate(json, strlen(json), sizeof(json));
```
The simdjson library will also accept `std::string` instances, as long as the `capacity()` of
the string exceeds the `size()` by at least `SIMDJSON_PADDING`. You can increase the `capacity()` with the `reserve()` function of your strings.
The simdjson library will also accept `std::string` instances. If the provided
reference is non-const, it will allocate padding as needed.
You can copy your data directly on a `simdjson::padded_string` as follows:
@@ -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
scan through the object looking for the field with the matching string, doing a character-by-character
comparison. It may generate the error `simdjson::NO_SUCH_FIELD` if there is no such key in the object, it may throw an exception (see [Error Handling](#error-handling)). For efficiency reason, you should avoid looking up the same field repeatedly: e.g., do
not do `object["foo"]` followed by `object["foo"]` with the same `object` instance. Keep in mind that On Demand does not buffer or save the result of the parsing: if you repeatedly access `object["foo"]`, then it must repeatedly seek the key and parse the content. The library does not provide a distinct function to check if a key is present, instead we recommend you attempt to access the key: e.g., by doing `ondemand::value val{}; if(!object["foo"].get(val)) {...}`, you have that `val` contains the requested value inside the if clause. It is your responsibility as a user to temporarily keep a reference to the value (`auto v = object["foo"]`), or to consume the content and store it in your own data structures. If you consume an
not do `object["foo"]` followed by `object["foo"]` with the same `object` instance. Keep in mind that On Demand does not buffer or save the result of the parsing: if you repeatedly access `object["foo"]`, then it must repeatedly seek the key and parse the content. The library does not provide a distinct function to check if a key is present, instead we recommend you attempt to access the key: e.g., by doing `ondemand::value val{}; if (!object["foo"].get(val)) {...}`, you have that `val` contains the requested value inside the if clause. It is your responsibility as a user to temporarily keep a reference to the value (`auto v = object["foo"]`), or to consume the content and store it in your own data structures. If you consume an
object twice: `std::string_view(object["foo"]` followed by `std::string_view(object["foo"]` then your code
is in error. Furthermore, you can only consume one field at a time, on the same object. The
value instance you get from `content["bids"]` becomes invalid when you call `content["asks"]`.
@@ -370,7 +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
> to support escaped keys, the method `unescaped_key()` provides the desired unescaped keys by
> parsing and writing out the unescaped keys to a string buffer and returning a `std::string_view`
> instance. You should expect a performance penalty when using `unescaped_key()`.
> instance. The `unescaped_key` takes an optional Boolean value: passing it true will decode invalid
> Unicode sequences with replacement, meaning that the decoding always succeeds but bogus Unicode
> replacement characters are inserted. In general, you should expect a performance penalty
> when using `unescaped_key()` compared to `key()` because of the string processing: the `key()`
> function just points inside the source JSON document.
>
> ```c++
> auto json = R"({"k\u0065y": 1})"_padded;
@@ -381,7 +385,7 @@ support for users who avoid exceptions. See [the simdjson error handling documen
> // parses and writes out the key, after unescaping it,
> // to a string buffer. It causes a performance penalty.
> std::string_view keyv = field.unescaped_key();
> if(keyv == "key") { std::cout << uint64_t(field.value()); }
> if (keyv == "key") { std::cout << uint64_t(field.value()); }
> }
> ```
>
@@ -414,8 +418,10 @@ support for users who avoid exceptions. See [the simdjson error handling documen
step through each value in the JSON array.
If you know the type of the value, you can cast it right there, too! `for (double value : array) { ... }`.
* **Object Iteration:** You can iterate through an object's fields, as well: `for (auto field : object) { ... }`
- `field.unescaped_key()` will get you the unescaped key string.
You may also use explicit iterators: `for(auto i = array.begin(); i != array.end(); i++) {}`. You can check that an array is empty with the condition `auto i = array.begin(); if(i == array.end()) {...}`.
* **Object Iteration:** You can iterate through an object's fields, as well: `for (auto field : object) { ... }`. You may also use explicit iterators : `for(auto i = object.begin(); i != object.end(); i++) { auto field = *i; .... }`. You can check that an object is empty with the condition `auto i = object.begin(); if(i == object.end()) {...}`.
- `field.unescaped_key()` will get you the unescaped key string. E.g., the JSON string `"\u00e1"` becomes the Unicode string `á`. Optionally, you pass `true` as a parameter to the `unescaped_key` method if you want invalid escape sequences to be replaced by a default replacement character (e.g., `\ud800\ud801\ud811`): otherwise bad escape sequences lead to an immediate error.
- `field.value()` will get you the value, which you can then use all these other methods on.
* **Array Index:** Because it is forward-only, you cannot look up an array element by index by index. Instead,
you should iterate through the array and keep an index yourself.
@@ -445,7 +451,7 @@ support for users who avoid exceptions. See [the simdjson error handling documen
> {
> ondemand::parser parser;
> for (ondemand::object car : parser.iterate(cars_json)) {
> if(uint64_t(car["year"]) > 2000) {
> if (uint64_t(car["year"]) > 2000) {
> arrays.push_back(simdjson::to_json_string(car["tire_pressure"]));
> }
> }
@@ -454,7 +460,7 @@ support for users who avoid exceptions. See [the simdjson error handling documen
> std::ostringstream oss;
> oss << "[";
> for(size_t i = 0; i < arrays.size(); i++) {
> if(i>0) { oss << ","; }
> if (i>0) { oss << ","; }
> oss << arrays[i];
> }
> oss << "]";
@@ -597,7 +603,7 @@ support for users who avoid exceptions. See [the simdjson error handling documen
case ondemand::json_type::null:
// We check that the value is indeed null
// otherwise: an error is thrown.
if(element.is_null()) {
if (element.is_null()) {
cout << "null";
}
break;
@@ -910,11 +916,11 @@ bool simple_error_example() {
ondemand::parser parser;
auto json = R"({"bad number":3.14.1 })"_padded;
ondemand::document doc;
if( parser.iterate(json).get(doc) != SUCCESS ) { return false; }
if (parser.iterate(json).get(doc) != SUCCESS) { return false; }
double x;
auto error = doc["bad number"].get_double().get(x);
// returns "simdjson::NUMBER_ERROR"
if(error != SUCCESS) {
if (error != SUCCESS) {
std::cout << error << std::endl;
return false;
}
@@ -976,10 +982,10 @@ it selects the key `"count"` within that object.
int main(void) {
simdjson::ondemand::parser parser;
auto error = padded_string::load("twitter.json").get(json);
if(error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
simdjson::ondemand::document tweets;
error = parser.iterate(json).get(tweets);
if( error ) { std::cerr << error << std::endl; return EXIT_FAILURE; }
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
simdjson::ondemand::value res;
error = tweets["search_metadata"]["count"].get(res);
if (error != SUCCESS) {
@@ -1010,12 +1016,12 @@ int main(void) {
simdjson::ondemand::document tweets;
padded_string json;
auto error = padded_string::load("twitter.json").get(json);
if(error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
error = parser.iterate(json).get(tweets);
if(error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
uint64_t identifier;
error = tweets["statuses"].at(0)["id"].get(identifier);
if(error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
std::cout << identifier << std::endl;
}
```
@@ -1039,40 +1045,40 @@ bool parse() {
// Iterating through an array of objects
auto error = parser.iterate(cars_json).get(doc);
if(error) { std::cerr << error << std::endl; return false; }
if (error) { std::cerr << error << std::endl; return false; }
ondemand::array cars; // invalid until the get() succeeds
error = doc.get_array().get(cars);
for (auto car_value : cars) {
ondemand::object car; // invalid until the get() succeeds
error = car_value.get_object().get(car);
if(error) { std::cerr << error << std::endl; return false; }
if (error) { std::cerr << error << std::endl; return false; }
// Accessing a field by name
std::string_view make;
std::string_view model;
error = car["make"].get(make);
if(error) { std::cerr << error << std::endl; return false; }
if (error) { std::cerr << error << std::endl; return false; }
error = car["model"].get(model);
if(error) { std::cerr << error << std::endl; return false; }
if (error) { std::cerr << error << std::endl; return false; }
cout << "Make/Model: " << make << "/" << model << endl;
// Casting a JSON element to an integer
uint64_t year{};
error = car["year"].get(year);
if(error) { std::cerr << error << std::endl; return false; }
if (error) { std::cerr << error << std::endl; return false; }
cout << "- This car is " << 2020 - year << " years old." << endl;
// Iterating through an array of floats
double total_tire_pressure = 0;
ondemand::array pressures;
error = car["tire_pressure"].get_array().get(pressures);
if(error) { std::cerr << error << std::endl; return false; }
if (error) { std::cerr << error << std::endl; return false; }
for (auto tire_pressure_value : pressures) {
double tire_pressure;
error = tire_pressure_value.get_double().get(tire_pressure);
if(error) { std::cerr << error << std::endl; return false; }
if (error) { std::cerr << error << std::endl; return false; }
total_tire_pressure += tire_pressure;
}
cout << "- Average tire pressure: " << (total_tire_pressure / 4) << endl;
@@ -1088,7 +1094,7 @@ after you have initialized them and checked that there is no error:
ondemand::object car; // invalid until the get() succeeds
// the `car` instance should not use used before it is initialized
error = car_value.get_object().get(car);
if(error) {
if (error) {
// the `car` instance should not use used
} else {
// the `car` instance can be safely used
@@ -1102,20 +1108,20 @@ having to handle exceptions.
ondemand::parser parser;
ondemand::document doc;
auto error = parser.iterate(json).get(doc);
if(error) { return false; }
if (error) { return false; }
ondemand::object object; // invalid until the get() succeeds
error = doc.get_object().get(object);
if(error) { return false; }
if (error) { return false; }
for(auto field : object) {
// We could replace 'field.key() with field.unescaped_key(),
// and ondemand::raw_json_string by std::string_view.
ondemand::raw_json_string keyv;
error = field.key().get(keyv);
if(error) { return false; }
if(keyv == "key") {
if (error) { return false; }
if (keyv == "key") {
uint64_t intvalue;
error = field.value().get(intvalue);
if(error) { return false; }
if (error) { return false; }
std::cout << intvalue;
}
}
@@ -1294,7 +1300,7 @@ content.
for (uint64_t values : array) {
std::cout << values << std::endl;
}
if(!doc.at_end()) {
if (!doc.at_end()) {
// In this instance, we will be left pointing at 'foo' since we have consumed the array [1,2].
std::cerr << "trailing content at byte index " << doc.current_location() - json.data() << std::endl;
}
@@ -1324,7 +1330,7 @@ before printout the data.
auto doc = parser.iterate(cars_json);
for (simdjson_unused ondemand::object car : doc) {
if(car["make"] == "Toyota") { count++; }
if (car["make"] == "Toyota") { count++; }
}
std::cout << "We have " << count << " Toyota cars.\n";
doc.rewind(); // requires simdjson 1.0 or better
@@ -1392,18 +1398,18 @@ ondemand::parser parser;
ondemand::document_stream stream;
size_t counter{0};
auto error = parser.iterate_many(json, 50).get(stream);
if( error ) { /* handle the error */ }
if (error) { /* handle the error */ }
for (auto doc: stream) {
if(counter < 6) {
if (counter < 6) {
int64_t val;
error = doc.at_pointer("/4").get(val);
if( error ) { /* handle the error */ }
if (error) { /* handle the error */ }
std::cout << "5 = " << val << std::endl;
} else {
ondemand::value val;
error = doc.at_pointer("/4").get(val);
// error == simdjson::CAPACITY
if(error) {
if (error) {
std::cerr << error << std::endl;
// We left 293 bytes unprocessed at the tail end of the input.
std::cout << " unprocessed bytes at the end: " << stream.truncated_bytes() << std::endl;
@@ -1647,7 +1653,7 @@ JSON string to a user-provided buffer:
General Direct Access to the Raw JSON String
--------------------------------
If your value is a string, the `raw_json_string` gives you direct access to the unprocess
If your value is a string, the `raw_json_string` you with `get_raw_json_string()` gives you direct access to the unprocessed
string. The simdjson library allows you to have access to the raw underlying JSON
more generally.
@@ -1716,6 +1722,41 @@ obj.reset(); // revise the object
uint64_t x = obj["value"]; // gives me 123
```
Storing Directly into an Existing std::string Instance
-----------------------------------------------------
The simdjson library favours the use of `std::string_view` instances because
it tends to lead to better performance due to causing fewer memory allocations.
However, they are cases where you need to store a string result in an `std::string``
instance. You can do so with a version of the `to_string()` method which takes as
a parameter a reference to an `std::string`.
```C++
auto json = R"({
"name": "Daniel",
"age": 42
})"_padded;
ondemand::parser parser;
ondemand::document doc = parser.iterate(json);
std::string name;
doc["name"].get_string(name);
```
The same routine can be written without exceptions handling:
```C++
std::string name;
auto err = doc["name"].get_string(name);
if(err) { /* handle error */ }
```
The `std::string` instance, once created, is independent. Unlike our `std::string_view` instances, it does not point at data that is
within our `parser` instance. The same caveat applies: you should
only consume a JSON string once.
You should be mindful of the trade-off: allocating multiple
`std::string` instances can become expensive.
Thread Safety
-------------
@@ -1944,6 +1985,79 @@ bool example() {
}
```
* Example 3: CRT
```C++
bool example() {
padded_string padded_input_json = R"([
{ "monitor": [
{ "id": "monitor", "type": "toggle", "label": "monitor" },
{ "id": "profile", "type": "selector", "label": "collection" },
{ "id": "overlay", "type": "selector", "label": "overlay" },
{ "id": "zoom", "type": "toggleSlider", "label": "zoom" }
] },
{ "crt": [
{ "id": "system", "type": "multi", "label": "system", "choices": "PAL, NTSC" },
{ "type": "spacer" },
{ "id": "brightness", "type": "slider", "icon": "brightness" },
{ "id": "contrast", "type": "slider", "icon": "contrast" },
{ "id": "saturation", "type": "slider", "icon": "saturation" },
{ "type": "spacer" },
{ "id": "overscan", "type": "toggleSlider", "label": "overscan" },
{ "type": "spacer" },
{ "id": "emulation", "type": "toggle", "label": "CRT emulation" },
{ "type": "spacer" },
{ "id": "curve", "type": "toggleSlider", "label": "curve" },
{ "id": "bleed", "type": "toggleSlider", "label": "bleed" },
{ "id": "vignette", "type": "toggleSlider", "label": "vignette" },
{ "id": "scanlines", "type": "toggleSlider", "label": "scanlines" },
{ "id": "gridlines", "type": "toggleSlider", "label": "gridlines" },
{ "id": "glow", "type": "toggleSlider", "label": "glow" },
{ "id": "flicker", "type": "toggleSlider", "label": "flicker" },
{ "id": "noise", "type": "toggleSlider", "label": "noise" },
{}
] }
])"_padded;
auto parser = ondemand::parser{};
auto doc = parser.iterate(padded_input_json);
auto root_array = doc.get_array();
// the root should be an object, not an array, but that's the JSON we are
// given.
for (ondemand::object node : root_array) {
// We know that we are going to have just one element in the object.
for (auto field : node) {
std::cout << "\n\ntop level:" << field.key() << std::endl;
// You can get a proper std::string_view for the key with:
// std::string_view key = field.unescaped_key();
// and second for-range loop to get child-elements here
for (ondemand::object inner_object : field.value()) {
auto i = inner_object.begin();
if (i == inner_object.end()) {
std::cout << "empty object" << std::endl;
continue;
} else {
for (; i != inner_object.end(); ++i) {
auto inner_field = *i;
std::cout << '"' << inner_field.key()
<< "\" : " << inner_field.value() << ", ";
// You can get proper std::string_view for the key and value with:
// std::string_view inner_key = field.unescaped_key();
// std::string_view value_str = field.value();
}
}
std::cout << std::endl;
}
// You can break here if you only want just the first element.
// break;
}
}
return true;
}
```
Performance Tips
--------
@@ -1969,4 +2083,4 @@ Performance Tips
std::string_view year = data["year"];
std::string_view rating = data["rating"];
```
- To better understand the operation of your On Demand parser, and whether it is performing as well as you think it should be, there is a logger feature built in to simdjson! To use it, define the pre-processor directive `SIMDJSON_VERBOSE_LOGGING` prior to including the `simdjson.h` header, which enables logging in simdjson. Run your code. It may generate a lot of logging output; adding printouts from your application that show each section may be helpful. The log's output will show step-by-step information on state, buffer pointer position, depth, and key retrieval status. Importantly, unless `SIMDJSON_VERBOSE_LOGGING` is defined, logging is entirely disabled and thus carries no overhead.
- To better understand the operation of your On Demand parser, and whether it is performing as well as you think it should be, there is a logger feature built in to simdjson! To use it, define the pre-processor directive `SIMDJSON_VERBOSE_LOGGING` prior to including the `simdjson.h` header, which enables logging in simdjson. Run your code. It may generate a lot of logging output; adding printouts from your application that show each section may be helpful. The log's output will show step-by-step information on state, buffer pointer position, depth, and key retrieval status. Importantly, unless `SIMDJSON_VERBOSE_LOGGING` is defined, logging is entirely disabled and thus carries no overhead.
+2 -2
View File
@@ -329,10 +329,10 @@ int main(void) {
simdjson::dom::parser parser;
simdjson::dom::element tweets; // invalid until the get() succeeds
auto error = parser.load("twitter.json").get(tweets);
if(error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
uint64_t identifier;
error = tweets["statuses"].at(0)["id"].get(identifier);
if(error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
std::cout << identifier << std::endl;
return EXIT_SUCCESS;
}
+3 -3
View File
@@ -101,8 +101,8 @@ by comparing it with the null pointer.
```c++
auto my_implementation = simdjson::get_available_implementations()["haswell"];
if(! my_implementation) { exit(1); }
if(! my_implementation->supported_by_runtime_system()) { exit(1); }
if (! my_implementation) { exit(1); }
if (! my_implementation->supported_by_runtime_system()) { exit(1); }
simdjson::get_active_implementation() = my_implementation;
```
@@ -113,7 +113,7 @@ You should call `supported_by_runtime_system()` to compare the processor's featu
```c++
for (auto implementation : simdjson::get_available_implementations()) {
if(implementation->supported_by_runtime_system()) {
if (implementation->supported_by_runtime_system()) {
cout << implementation->name() << ": " << implementation->description() << endl;
}
}
+4 -4
View File
@@ -196,12 +196,12 @@ Let us illustrate the idea with code:
simdjson::ondemand::parser parser;
simdjson::ondemand::document_stream stream;
auto error = parser.iterate_many(json).get(stream);
if( error ) { /* do something */ }
if (error) { /* do something */ }
auto i = stream.begin();
size_t count{0};
for(; i != stream.end(); ++i) {
auto doc = *i;
if(!i.error()) {
if (!i.error()) {
std::cout << "got full document at " << i.current_index() << std::endl;
std::cout << i.source() << std::endl;
count++;
@@ -237,7 +237,7 @@ Consider the following example where a truncated document (`{"key":"intentionall
simdjson::ondemand::parser parser;
simdjson::ondemand::document_stream stream;
auto error = parser.iterate_many(json,json.size()).get(stream);
if(error) { std::cerr << error << std::endl; return; }
if (error) { std::cerr << error << std::endl; return; }
for(auto i = stream.begin(); i != stream.end(); ++i) {
std::cout << i.source() << std::endl;
}
@@ -269,7 +269,7 @@ Example:
// we pass 'true' to the allow_comma parameter, the batch size will be set to at least
// the document size.
auto error = parser.iterate_many(json, 32, true).get(doc_stream);
if(error) { std::cerr << error << std::endl; return; }
if (error) { std::cerr << error << std::endl; return; }
for (auto doc : doc_stream) {
std::cout << doc.type() << std::endl;
}
+4 -4
View File
@@ -679,11 +679,11 @@ in production systems:
ondemand::object c1 = parent["child1"];
// c1 owns the focus
//
if(std::string_view(c1["name"]) != "John") { ... }
if (std::string_view(c1["name"]) != "John") { ... }
// c2 attempts to grab the focus from parent but fails
ondemand::object c2 = parent["child2"];
// c2 is now in an unsafe state and the following line would be unsafe
// if(std::string_view(c2["name"]) != "Daniel") { return false; }
// if (std::string_view(c2["name"]) != "Daniel") { return false; }
```
A correct usage is given by the following example:
@@ -697,7 +697,7 @@ in production systems:
{
ondemand::object c1 = parent["child1"];
// c1 grabbed the focus from parent
if(std::string_view(c1["name"]) != "John") { return false; }
if (std::string_view(c1["name"]) != "John") { return false; }
}
// c1 went out of scope, so its destructor was called and the focus
// was handed back to parent.
@@ -705,7 +705,7 @@ in production systems:
ondemand::object c2 = parent["child2"];
// c2 grabbed the focus from parent
// the following is safe:
if(std::string_view(c2["name"]) != "Daniel") { return false; }
if (std::string_view(c2["name"]) != "Daniel") { return false; }
}
```
+3 -3
View File
@@ -184,12 +184,12 @@ Let us illustrate the idea with code:
simdjson::dom::parser parser;
simdjson::dom::document_stream stream;
auto error = parser.parse_many(json).get(stream);
if( error ) { /* do something */ }
if (error) { /* do something */ }
auto i = stream.begin();
size_t count{0};
for(; i != stream.end(); ++i) {
auto doc = *i;
if(!doc.error()) {
if (!doc.error()) {
std::cout << "got full document at " << i.current_index() << std::endl;
std::cout << i.source() << std::endl;
count++;
@@ -225,7 +225,7 @@ Consider the following example where a truncated document (`{"key":"intentionall
simdjson::dom::parser parser;
simdjson::dom::document_stream stream;
auto error = parser.parse_many(json,json.size()).get(stream);
if(error) { std::cerr << error << std::endl; return; }
if (error) { std::cerr << error << std::endl; return; }
for(auto doc : stream) {
std::cout << doc << std::endl;
}
-1
View File
@@ -3,7 +3,6 @@
#include "simdjson/arm64/begin.h"
#include "simdjson/generic/amalgamated.h"
#include "simdjson/generic/lookup_table.h"
#include "simdjson/arm64/end.h"
#endif // SIMDJSON_ARM64_H
+2 -4
View File
@@ -13,14 +13,12 @@ namespace arm64 {
class implementation;
namespace {
namespace simd {
template <typename T> struct simd8;
template <> struct simd8<bool>;
template <> struct simd8<uint8_t>;
template <typename T> struct simd8x64;
} // namespace simd
} // unnamed namespace
} // namespace arm64
} // namespace simdjson
+1 -1
View File
@@ -1,7 +1,7 @@
#define SIMDJSON_IMPLEMENTATION arm64
#include "simdjson/arm64/base.h"
#include "simdjson/arm64/intrinsics.h"
#include "simdjson/arm64/bitmask.h"
#include "simdjson/arm64/bitmanipulation.h"
#include "simdjson/arm64/bitmask.h"
#include "simdjson/arm64/numberparsing_defs.h"
#include "simdjson/arm64/simd.h"
+106
View File
@@ -0,0 +1,106 @@
#ifndef SIMDJSON_ARM64_BITMANIPULATION_H
#define SIMDJSON_ARM64_BITMANIPULATION_H
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
#include "simdjson/arm64/base.h"
#include "simdjson/arm64/intrinsics.h"
#endif // SIMDJSON_CONDITIONAL_INCLUDE
namespace simdjson {
namespace arm64 {
namespace {
// We sometimes call trailing_zero on inputs that are zero,
// but the algorithms do not end up using the returned value.
// Sadly, sanitizers are not smart enough to figure it out.
SIMDJSON_NO_SANITIZE_UNDEFINED
// This function can be used safely even if not all bytes have been
// initialized.
// See issue https://github.com/simdjson/simdjson/issues/1965
SIMDJSON_NO_SANITIZE_MEMORY
simdjson_inline int trailing_zeroes(uint64_t input_num) {
#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO
unsigned long ret;
// Search the mask data from least significant bit (LSB)
// to the most significant bit (MSB) for a set bit (1).
_BitScanForward64(&ret, input_num);
return (int)ret;
#else // SIMDJSON_REGULAR_VISUAL_STUDIO
return __builtin_ctzll(input_num);
#endif // SIMDJSON_REGULAR_VISUAL_STUDIO
}
/* result might be undefined when input_num is zero */
simdjson_inline uint64_t clear_lowest_bit(uint64_t input_num) {
return input_num & (input_num-1);
}
/* result might be undefined when input_num is zero */
simdjson_inline int leading_zeroes(uint64_t input_num) {
#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO
unsigned long leading_zero = 0;
// Search the mask data from most significant bit (MSB)
// to least significant bit (LSB) for a set bit (1).
if (_BitScanReverse64(&leading_zero, input_num))
return (int)(63 - leading_zero);
else
return 64;
#else
return __builtin_clzll(input_num);
#endif// SIMDJSON_REGULAR_VISUAL_STUDIO
}
/* result might be undefined when input_num is zero */
simdjson_inline int count_ones(uint64_t input_num) {
return vaddv_u8(vcnt_u8(vcreate_u8(input_num)));
}
#if defined(__GNUC__) // catches clang and gcc
/**
* ARM has a fast 64-bit "bit reversal function" that is handy. However,
* it is not generally available as an intrinsic function under Visual
* Studio (though this might be changing). Even under clang/gcc, we
* apparently need to invoke inline assembly.
*/
/*
* We use SIMDJSON_PREFER_REVERSE_BITS as a hint that algorithms that
* work well with bit reversal may use it.
*/
#define SIMDJSON_PREFER_REVERSE_BITS 1
/* reverse the bits */
simdjson_inline uint64_t reverse_bits(uint64_t input_num) {
uint64_t rev_bits;
__asm("rbit %0, %1" : "=r"(rev_bits) : "r"(input_num));
return rev_bits;
}
/**
* Flips bit at index 63 - lz. Thus if you have 'leading_zeroes' leading zeroes,
* then this will set to zero the leading bit. It is possible for leading_zeroes to be
* greating or equal to 63 in which case we trigger undefined behavior, but the output
* of such undefined behavior is never used.
**/
SIMDJSON_NO_SANITIZE_UNDEFINED
simdjson_inline uint64_t zero_leading_bit(uint64_t rev_bits, int leading_zeroes) {
return rev_bits ^ (uint64_t(0x8000000000000000) >> leading_zeroes);
}
#endif
simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, uint64_t *result) {
#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO
*result = value1 + value2;
return *result < value1;
#else
return __builtin_uaddll_overflow(value1, value2,
reinterpret_cast<unsigned long long *>(result));
#endif
}
} // unnamed namespace
} // namespace arm64
} // namespace simdjson
#endif // SIMDJSON_ARM64_BITMANIPULATION_H
+1 -119
View File
@@ -3,129 +3,11 @@
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
#include "simdjson/arm64/base.h"
#include "simdjson/arm64/intrinsics.h"
#endif // SIMDJSON_CONDITIONAL_INCLUDE
namespace simdjson {
namespace arm64 {
namespace bitmask {
// We sometimes call trailing_zero on inputs that are zero,
// but the algorithms do not end up using the returned value.
// Sadly, sanitizers are not smart enough to figure it out.
SIMDJSON_NO_SANITIZE_UNDEFINED
// This function can be used safely even if not all bytes have been
// initialized.
// See issue https://github.com/simdjson/simdjson/issues/1965
SIMDJSON_NO_SANITIZE_MEMORY
simdjson_inline int trailing_zeroes(uint64_t input_num) {
#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO
unsigned long ret;
// Search the mask data from least significant bit (LSB)
// to the most significant bit (MSB) for a set bit (1).
_BitScanForward64(&ret, input_num);
return (int)ret;
#else // SIMDJSON_REGULAR_VISUAL_STUDIO
return __builtin_ctzll(input_num);
#endif // SIMDJSON_REGULAR_VISUAL_STUDIO
}
/* result might be undefined when input_num is zero */
simdjson_inline uint64_t clear_lowest_bit(uint64_t input_num) {
return input_num & (input_num-1);
}
/* result might be undefined when input_num is zero */
simdjson_inline int leading_zeroes(uint64_t input_num) {
#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO
unsigned long leading_zero = 0;
// Search the mask data from most significant bit (MSB)
// to least significant bit (LSB) for a set bit (1).
if (_BitScanReverse64(&leading_zero, input_num))
return (int)(63 - leading_zero);
else
return 64;
#else
return __builtin_clzll(input_num);
#endif// SIMDJSON_REGULAR_VISUAL_STUDIO
}
/* result might be undefined when input_num is zero */
simdjson_inline int count_ones(uint64_t input_num) {
return vaddv_u8(vcnt_u8(vcreate_u8(input_num)));
}
#if defined(__GNUC__) // catches clang and gcc
/**
* ARM has a fast 64-bit "bit reversal function" that is handy. However,
* it is not generally available as an intrinsic function under Visual
* Studio (though this might be changing). Even under clang/gcc, we
* apparently need to invoke inline assembly.
*/
/*
* We use SIMDJSON_PREFER_REVERSE_BITS as a hint that algorithms that
* work well with bit reversal may use it.
*/
#define SIMDJSON_PREFER_REVERSE_BITS 1
/* reverse the bits */
simdjson_inline uint64_t reverse_bits(uint64_t input_num) {
uint64_t rev_bits;
__asm("rbit %0, %1" : "=r"(rev_bits) : "r"(input_num));
return rev_bits;
}
/**
* Flips bit at index 63 - lz. Thus if you have 'leading_zeroes' leading zeroes,
* then this will set to zero the leading bit. It is possible for leading_zeroes to be
* greating or equal to 63 in which case we trigger undefined behavior, but the output
* of such undefined behavior is never used.
**/
SIMDJSON_NO_SANITIZE_UNDEFINED
simdjson_inline uint64_t zero_leading_bit(uint64_t rev_bits, int leading_zeroes) {
return rev_bits ^ (uint64_t(0x8000000000000000) >> leading_zeroes);
}
#endif
simdjson_inline uint64_t add_carry_out(uint64_t value1, uint64_t value2, bool &carry_out) {
#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO
uint64_t result = value1 + value2;
carry_out = result < value1;
return result;
#else
unsigned long long result;
carry_out = __builtin_uaddll_overflow(value1, value2, &result);
return result;
#endif
}
simdjson_inline uint64_t subtract_borrow(uint64_t value1, uint64_t value2, bool &borrow) {
#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO
value2 += borrow;
uint64_t result = value1 - value2;
borrow = value1 > value2;
return result;
#else
unsigned long long result;
bool borrow1 = __builtin_usubll_overflow(value1, value2, &result);
borrow = borrow1 | __builtin_usubll_overflow(result, borrow, &result);
return result;
#endif
}
simdjson_inline uint64_t subtract_borrow_out(uint64_t value1, uint64_t value2, bool &borrow_out) {
#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO
uint64_t result = value1 - value2;
borrow_out = result > value1;
return result;
#else
unsigned long long result;
borrow_out = __builtin_usubll_overflow(value1, value2, &result);
return result;
#endif
}
namespace {
//
// Perform a "cumulative bitwise xor," flipping bits each time a 1 is encountered.
+50 -204
View File
@@ -3,12 +3,13 @@
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
#include "simdjson/arm64/base.h"
#include "simdjson/arm64/bitmask.h"
#include "simdjson/arm64/bitmanipulation.h"
#include "simdjson/internal/simdprune_tables.h"
#endif // SIMDJSON_CONDITIONAL_INCLUDE
namespace simdjson {
namespace arm64 {
namespace {
namespace simd {
#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO
@@ -27,7 +28,7 @@ namespace {
* You should not use this function except for compile-time constants:
* it is not efficient.
*/
simdjson_inline simd_t make_uint8x16_t(uint8_t x1, uint8_t x2, uint8_t x3, uint8_t x4,
simdjson_inline uint8x16_t make_uint8x16_t(uint8_t x1, uint8_t x2, uint8_t x3, uint8_t x4,
uint8_t x5, uint8_t x6, uint8_t x7, uint8_t x8,
uint8_t x9, uint8_t x10, uint8_t x11, uint8_t x12,
uint8_t x13, uint8_t x14, uint8_t x15, uint8_t x16) {
@@ -35,7 +36,7 @@ simdjson_inline simd_t make_uint8x16_t(uint8_t x1, uint8_t x2, uint8_t x3, ui
// uint8_t array[16] = {x1, x2, x3, x4, x5, x6, x7, x8,
// x9, x10,x11,x12,x13,x14,x15,x16};
// return vld1q_u8(array);
simd_t x{};
uint8x16_t x{};
// incredibly, Visual Studio does not allow x[0] = x1
x = vsetq_lane_u8(x1, x, 0);
x = vsetq_lane_u8(x2, x, 1);
@@ -108,28 +109,18 @@ simdjson_inline int8x16_t make_int8x16_t(int8_t x1, int8_t x2, int8_t x3, int
template<typename T>
struct simd8;
#if !SIMDJSON_IS_ARM && !defined(SIMDJSON_CONDITIONAL_INCLUDE)
// Make errors a bit more manageable when editing on non-ARM
struct uint8x16_t { uint8_t x[16]; };
#endif
//
// Base class of simd8<uint8_t> and simd8<bool>, both of which use uint8x16_t internally.
//
template<typename T, typename Mask=simd8<bool>>
struct base_u8 {
/** The actual underlying system SIMD type. */
using simd_t = uint8x16_t;
static constexpr const int LANES = sizeof(simd_t);
using bitmask_t = uint16_t;
static_assert(sizeof(bitmask_t)*8 == LANES, "Bitmask type's bits must equal the simd type's bytes");
simd_t value;
uint8x16_t value;
static const int SIZE = sizeof(value);
// Conversion from/to SIMD register
simdjson_inline base_u8(const simd_t _value) : value(_value) {}
simdjson_inline operator const simd_t&() const { return this->value; }
simdjson_inline operator simd_t&() { return this->value; }
simdjson_inline base_u8(const uint8x16_t _value) : value(_value) {}
simdjson_inline operator const uint8x16_t&() const { return this->value; }
simdjson_inline operator uint8x16_t&() { return this->value; }
// Bit operations
simdjson_inline simd8<T> operator|(const simd8<T> other) const { return vorrq_u8(*this, other); }
@@ -141,8 +132,7 @@ simdjson_inline int8x16_t make_int8x16_t(int8_t x1, int8_t x2, int8_t x3, int
simdjson_inline simd8<T>& operator&=(const simd8<T> other) { auto this_cast = static_cast<simd8<T>*>(this); *this_cast = *this_cast & other; return *this_cast; }
simdjson_inline simd8<T>& operator^=(const simd8<T> other) { auto this_cast = static_cast<simd8<T>*>(this); *this_cast = *this_cast ^ other; return *this_cast; }
simdjson_inline Mask eq(const simd8<T> rhs) const { return vceqq_u8(*this, rhs); }
friend simdjson_inline Mask operator==(const simd8<T> lhs, const simd8<T> rhs) { return lhs.eq(rhs); }
friend simdjson_inline Mask operator==(const simd8<T> lhs, const simd8<T> rhs) { return vceqq_u8(lhs, rhs); }
template<int N=1>
simdjson_inline simd8<T> prev(const simd8<T> prev_chunk) const {
@@ -158,7 +148,7 @@ simdjson_inline int8x16_t make_int8x16_t(int8_t x1, int8_t x2, int8_t x3, int
static simdjson_inline simd8<bool> splat(bool _value) { return vmovq_n_u8(uint8_t(-(!!_value))); }
simdjson_inline simd8(const simd_t _value) : base_u8<bool>(_value) {}
simdjson_inline simd8(const uint8x16_t _value) : base_u8<bool>(_value) {}
// False constructor
simdjson_inline simd8() : simd8(vdupq_n_u8(0)) {}
// Splat constructor
@@ -168,14 +158,14 @@ simdjson_inline int8x16_t make_int8x16_t(int8_t x1, int8_t x2, int8_t x3, int
// purposes (cutting it down to uint16_t costs performance in some compilers).
simdjson_inline uint32_t to_bitmask() const {
#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO
const simd_t bit_mask = make_simd_t(0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80,
const uint8x16_t bit_mask = make_uint8x16_t(0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80,
0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80);
#else
const simd_t bit_mask = {0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80,
const uint8x16_t bit_mask = {0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80,
0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80};
#endif
auto minput = *this & bit_mask;
simd_t tmp = vpaddq_u8(minput, minput);
uint8x16_t tmp = vpaddq_u8(minput, minput);
tmp = vpaddq_u8(tmp, tmp);
tmp = vpaddq_u8(tmp, tmp);
return vgetq_lane_u16(vreinterpretq_u16_u8(tmp), 0);
@@ -186,14 +176,11 @@ simdjson_inline int8x16_t make_int8x16_t(int8_t x1, int8_t x2, int8_t x3, int
// Unsigned bytes
template<>
struct simd8<uint8_t>: base_u8<uint8_t> {
using typename base_u8<uint8_t>::simd_t;
using base_u8<uint8_t>::LANES;
static simdjson_inline uint8x16_t splat(uint8_t _value) { return vmovq_n_u8(_value); }
static simdjson_inline uint8x16_t zero() { return vdupq_n_u8(0); }
static simdjson_inline uint8x16_t load(const uint8_t* values) { return vld1q_u8(values); }
static simdjson_inline simd_t splat(uint8_t _value) { return vmovq_n_u8(_value); }
static simdjson_inline simd_t zero() { return vdupq_n_u8(0); }
static simdjson_inline simd_t load(const uint8_t* values) { return vld1q_u8(values); }
simdjson_inline simd8(const simd_t _value) : base_u8<uint8_t>(_value) {}
simdjson_inline simd8(const uint8x16_t _value) : base_u8<uint8_t>(_value) {}
// Zero constructor
simdjson_inline simd8() : simd8(zero()) {}
// Array constructor
@@ -213,7 +200,7 @@ simdjson_inline int8x16_t make_int8x16_t(int8_t x1, int8_t x2, int8_t x3, int
simdjson_inline simd8(
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15
) : simd8(simd_t{
) : simd8(uint8x16_t{
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
}) {}
@@ -267,20 +254,15 @@ simdjson_inline int8x16_t make_int8x16_t(int8_t x1, int8_t x2, int8_t x3, int
simdjson_inline simd8<uint8_t> shl() const { return vshlq_n_u8(*this, N); }
// Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values)
simdjson_inline simd8<uint8_t> lookup_16(simd8<uint8_t> lookup_table) const {
template<typename L>
simdjson_inline simd8<L> lookup_16(simd8<L> lookup_table) const {
return lookup_table.apply_lookup_16_to(*this);
}
// Perform a lookup based on the lower 4 bits of each lane. (Platform-dependent behavior for
// non-ASCII values--may look up the lower 4 bits on some platforms, and return 0 on others.)
simdjson_inline simd8<uint8_t> lookup_low_nibble_ascii(simd8<uint8_t> lookup_table) const {
return lookup_table.apply_lookup_16_to(*this & 0b10001111);
}
// Copies to 'output" all bytes corresponding to a 0 in the mask (interpreted as a bitset).
// Passing a 0 value for mask would be equivalent to writing out every byte to output.
// Only the first 16 - bitmask::count_ones(mask) bytes of the result are significant but 16 bytes
// Only the first 16 - count_ones(mask) bytes of the result are significant but 16 bytes
// get written.
// Design consideration: it seems like a function with the
// signature simd8<L> compress(uint16_t mask) would be
@@ -298,16 +280,16 @@ simdjson_inline int8x16_t make_int8x16_t(int8_t x1, int8_t x2, int8_t x3, int
// thintable_epi8[mask2] into a 128-bit register, using only
// two instructions on most compilers.
uint64x2_t shufmask64 = {thintable_epi8[mask1], thintable_epi8[mask2]};
simd_t shufmask = vreinterpretq_u8_u64(shufmask64);
uint8x16_t shufmask = vreinterpretq_u8_u64(shufmask64);
// we increment by 0x08 the second half of the mask
#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO
simd_t inc = make_uint8x16_t(0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08);
uint8x16_t inc = make_uint8x16_t(0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08);
#else
simd_t inc = {0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08};
uint8x16_t inc = {0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08};
#endif
shufmask = vaddq_u8(shufmask, inc);
// this is the version "nearly pruned"
simd_t pruned = vqtbl1q_u8(*this, shufmask);
uint8x16_t pruned = vqtbl1q_u8(*this, shufmask);
// we still need to put the two halves together.
// we compute the popcount of the first half:
int pop1 = BitsSetTable256mul2[mask1];
@@ -315,8 +297,8 @@ simdjson_inline int8x16_t make_int8x16_t(int8_t x1, int8_t x2, int8_t x3, int
// only the first pop1 bytes from the first 8 bytes, and then
// it fills in with the bytes from the second 8 bytes + some filling
// at the end.
simd_t compactmask = vld1q_u8(reinterpret_cast<const uint8_t *>(pshufb_combine_table + pop1 * 8));
simd_t answer = vqtbl1q_u8(pruned, compactmask);
uint8x16_t compactmask = vld1q_u8(reinterpret_cast<const uint8_t *>(pshufb_combine_table + pop1 * 8));
uint8x16_t answer = vqtbl1q_u8(pruned, compactmask);
vst1q_u8(reinterpret_cast<uint8_t*>(output), answer);
}
@@ -341,6 +323,20 @@ simdjson_inline int8x16_t make_int8x16_t(int8_t x1, int8_t x2, int8_t x3, int
vst1_u8((uint8_t*)output2, vqtbl1_u8(*this, compactmask2));
}
template<typename L>
simdjson_inline simd8<L> lookup_16(
L replace0, L replace1, L replace2, L replace3,
L replace4, L replace5, L replace6, L replace7,
L replace8, L replace9, L replace10, L replace11,
L replace12, L replace13, L replace14, L replace15) const {
return lookup_16(simd8<L>::repeat_16(
replace0, replace1, replace2, replace3,
replace4, replace5, replace6, replace7,
replace8, replace9, replace10, replace11,
replace12, replace13, replace14, replace15
));
}
template<typename T>
simdjson_inline simd8<uint8_t> apply_lookup_16_to(const simd8<T> original) {
return vqtbl1q_u8(*this, simd8<uint8_t>(original));
@@ -405,7 +401,7 @@ simdjson_inline int8x16_t make_int8x16_t(int8_t x1, int8_t x2, int8_t x3, int
// In theory, we could check this occurrence with std::same_as and std::enabled_if but it is C++14
// and relatively ugly and hard to read.
#ifndef SIMDJSON_REGULAR_VISUAL_STUDIO
simdjson_inline explicit simd8(const simd_t other): simd8(vreinterpretq_s8_u8(other)) {}
simdjson_inline explicit simd8(const uint8x16_t other): simd8(vreinterpretq_s8_u8(other)) {}
#endif
simdjson_inline explicit operator simd8<uint8_t>() const { return vreinterpretq_u8_s8(this->value); }
@@ -428,13 +424,10 @@ simdjson_inline int8x16_t make_int8x16_t(int8_t x1, int8_t x2, int8_t x3, int
}
// Perform a lookup assuming no value is larger than 16
simdjson_inline simd8<int8_t> lookup_16(simd8<int8_t> lookup_table) const {
template<typename L>
simdjson_inline simd8<L> lookup_16(simd8<L> lookup_table) const {
return lookup_table.apply_lookup_16_to(*this);
}
// Perform a lookup based on the lower 4 bits of each lane, returning 0 for values with a high bit of 1.
simdjson_inline simd8<int8_t> lookup_low_nibble_ascii(simd8<int8_t> lookup_table) const {
return lookup_table.apply_lookup_16_to(*this & 0b10001111);
}
template<typename L>
simdjson_inline simd8<L> lookup_16(
L replace0, L replace1, L replace2, L replace3,
@@ -467,8 +460,6 @@ simdjson_inline int8x16_t make_int8x16_t(int8_t x1, int8_t x2, int8_t x3, int
simdjson_inline simd8x64(const simd8<T> chunk0, const simd8<T> chunk1, const simd8<T> chunk2, const simd8<T> chunk3) : chunks{chunk0, chunk1, chunk2, chunk3} {}
simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8<T>::load(ptr), simd8<T>::load(ptr+16), simd8<T>::load(ptr+32), simd8<T>::load(ptr+48)} {}
simdjson_inline simd8x64(simd8x64<T>&& o) noexcept = default;
simdjson_inline simd8x64<T>& operator=(simd8x64<T>&& other) noexcept = default;
simdjson_inline void store(T ptr[64]) const {
this->chunks[0].store(ptr+sizeof(simd8<T>)*0);
@@ -495,19 +486,19 @@ simdjson_inline int8x16_t make_int8x16_t(int8_t x1, int8_t x2, int8_t x3, int
simdjson_inline uint64_t to_bitmask() const {
#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO
const simd_t bit_mask = make_uint8x16_t(
const uint8x16_t bit_mask = make_uint8x16_t(
0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80,
0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80
);
#else
const simd_t bit_mask = {
const uint8x16_t bit_mask = {
0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80,
0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80
};
#endif
// Add each of the elements next to each other, successively, to stuff each 8 byte mask into one.
simd_t sum0 = vpaddq_u8(this->chunks[0] & bit_mask, this->chunks[1] & bit_mask);
simd_t sum1 = vpaddq_u8(this->chunks[2] & bit_mask, this->chunks[3] & bit_mask);
uint8x16_t sum0 = vpaddq_u8(this->chunks[0] & bit_mask, this->chunks[1] & bit_mask);
uint8x16_t sum1 = vpaddq_u8(this->chunks[2] & bit_mask, this->chunks[3] & bit_mask);
sum0 = vpaddq_u8(sum0, sum1);
sum0 = vpaddq_u8(sum0, sum0);
return vgetq_lane_u64(vreinterpretq_u64_u8(sum0), 0);
@@ -523,24 +514,6 @@ simdjson_inline int8x16_t make_int8x16_t(int8_t x1, int8_t x2, int8_t x3, int
).to_bitmask();
}
simdjson_inline simd8x64<T> lookup_16(const simd8<T>& lookup_table) const {
return {
this->chunks[0].lookup_16(lookup_table),
this->chunks[1].lookup_16(lookup_table),
this->chunks[2].lookup_16(lookup_table),
this->chunks[3].lookup_16(lookup_table)
};
}
simdjson_inline simd8x64<T> lookup_low_nibble_ascii(const simd8<T>& lookup_table) const {
return {
this->chunks[0].lookup_low_nibble_ascii(lookup_table),
this->chunks[1].lookup_low_nibble_ascii(lookup_table),
this->chunks[2].lookup_low_nibble_ascii(lookup_table),
this->chunks[3].lookup_low_nibble_ascii(lookup_table)
};
}
simdjson_inline uint64_t lteq(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return simd8x64<bool>(
@@ -550,137 +523,10 @@ simdjson_inline int8x16_t make_int8x16_t(int8_t x1, int8_t x2, int8_t x3, int
this->chunks[3] <= mask
).to_bitmask();
}
simdjson_inline simd8x64<T> operator&(const simd8x64<T>& other) const {
return {
this->chunks[0] & other.chunks[0],
this->chunks[1] & other.chunks[1],
this->chunks[2] & other.chunks[2],
this->chunks[3] & other.chunks[3]
};
}
simdjson_inline simd8x64<T> operator&(const simd8<T>& other) const {
return {
this->chunks[0] & other,
this->chunks[1] & other,
this->chunks[2] & other,
this->chunks[3] & other
};
}
simdjson_inline simd8x64<T> operator|(const simd8x64<T>& other) const {
return {
this->chunks[0] | other.chunks[0],
this->chunks[1] | other.chunks[1],
this->chunks[2] | other.chunks[2],
this->chunks[3] | other.chunks[3]
};
}
simdjson_inline simd8x64<T> operator|(const simd8<T>& other) const {
return {
this->chunks[0] | other,
this->chunks[1] | other,
this->chunks[2] | other,
this->chunks[3] | other
};
}
simdjson_inline simd8x64<T> operator^(const simd8x64<T>& other) const {
return {
this->chunks[0] ^ other.chunks[0],
this->chunks[1] ^ other.chunks[1],
this->chunks[2] ^ other.chunks[2],
this->chunks[3] ^ other.chunks[3]
};
}
simdjson_inline simd8x64<T> operator^(const simd8<T>& other) const {
return {
this->chunks[0] ^ other,
this->chunks[1] ^ other,
this->chunks[2] ^ other,
this->chunks[3] ^ other
};
}
simdjson_inline simd8x64<T> bit_andnot(const simd8x64<T>& other) const {
return {
this->chunks[0].bit_andnot(other.chunks[0]),
this->chunks[1].bit_andnot(other.chunks[1]),
this->chunks[2].bit_andnot(other.chunks[2]),
this->chunks[3].bit_andnot(other.chunks[3])
};
}
simdjson_inline simd8x64<T> bit_andnot(const simd8<T>& other) const {
return {
this->chunks[0].bit_andnot(other),
this->chunks[1].bit_andnot(other),
this->chunks[2].bit_andnot(other),
this->chunks[3].bit_andnot(other)
};
}
template <int N>
simdjson_inline simd8x64<T> shr() const noexcept {
return {
this->chunks[0].template shr<N>(),
this->chunks[1].template shr<N>(),
this->chunks[2].template shr<N>(),
this->chunks[3].template shr<N>()
};
}
template <int N>
simdjson_inline simd8x64<T> shl() const noexcept {
return {
this->chunks[0].template shl<N>(),
this->chunks[1].template shl<N>(),
this->chunks[2].template shl<N>(),
this->chunks[3].template shl<N>()
};
}
simdjson_inline simd8x64<bool> any_bits_set(const simd8<T>& bits) const {
return {
this->chunks[0].any_bits_set(bits),
this->chunks[1].any_bits_set(bits),
this->chunks[2].any_bits_set(bits),
this->chunks[3].any_bits_set(bits)
};
}
simdjson_inline simd8x64<bool> any_bits_set(const simd8x64<T>& bits) const {
return {
this->chunks[0].any_bits_set(bits.chunks[0]),
this->chunks[1].any_bits_set(bits.chunks[1]),
this->chunks[2].any_bits_set(bits.chunks[2]),
this->chunks[3].any_bits_set(bits.chunks[3])
};
}
simdjson_inline simd8x64<bool> no_bits_set(const simd8<T>& bits) const {
return {
this->chunks[0].no_bits_set(bits),
this->chunks[1].no_bits_set(bits),
this->chunks[2].no_bits_set(bits),
this->chunks[3].no_bits_set(bits)
};
}
simdjson_inline simd8x64<bool> no_bits_set(const simd8x64<T>& bits) const {
return {
this->chunks[0].no_bits_set(bits.chunks[0]),
this->chunks[1].no_bits_set(bits.chunks[1]),
this->chunks[2].no_bits_set(bits.chunks[2]),
this->chunks[3].no_bits_set(bits.chunks[3])
};
}
}; // struct simd8x64<T>
} // namespace simd
} // unnamed namespace
} // namespace arm64
} // namespace simdjson
+3 -3
View File
@@ -4,7 +4,7 @@
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
#include "simdjson/arm64/base.h"
#include "simdjson/arm64/simd.h"
#include "simdjson/arm64/bitmask.h"
#include "simdjson/arm64/bitmanipulation.h"
#endif // SIMDJSON_CONDITIONAL_INCLUDE
namespace simdjson {
@@ -21,8 +21,8 @@ public:
simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; }
simdjson_inline bool has_backslash() { return bs_bits != 0; }
simdjson_inline int quote_index() { return bitmask::trailing_zeroes(quote_bits); }
simdjson_inline int backslash_index() { return bitmask::trailing_zeroes(bs_bits); }
simdjson_inline int quote_index() { return trailing_zeroes(quote_bits); }
simdjson_inline int backslash_index() { return trailing_zeroes(bs_bits); }
uint32_t bs_bits;
uint32_t quote_bits;
-35
View File
@@ -167,41 +167,6 @@ double from_chars(const char *first, const char* end) noexcept;
#define simdjson_inline simdjson_really_inline
#endif
#ifndef simdjson_constexpr
#if __cpp_constexpr
#define simdjson_constexpr constexpr simdjson_inline
#else
#define simdjson_constexpr simdjson_inline
#endif
#endif
// simdjson_constexpr
#ifndef simdjson_consteval
#if __cpp_consteval
#define simdjson_consteval consteval simdjson_inline
#else
#define simdjson_consteval simdjson_constexpr
#endif
#endif // simdjson_consteval
#ifndef simdjson_constinit
#if __cpp_constinit
#define simdjson_constinit constinit
#elif __cpp_consteval
#define simdjson_constinit consteval
#else
#define simdjson_constinit constexpr
#endif
#endif // simdjson_constinit
#ifndef simdjson_if_constexpr
#if SIMDJSON_CPLUSPLUS17
#define simdjson_if_constexpr constexpr
#else
#define simdjson_if_constexpr
#endif
#endif
#if SIMDJSON_VISUAL_STUDIO
/**
* Windows users need to do some extra work when building
+6
View File
@@ -410,6 +410,12 @@ inline simdjson_result<element> element::at_key(std::string_view key) const noex
inline simdjson_result<element> element::at_key_case_insensitive(std::string_view key) const noexcept {
return get<object>().at_key_case_insensitive(key);
}
inline bool element::operator<(const element &other) const noexcept {
return tape.json_index < other.tape.json_index;
}
inline bool element::operator==(const element &other) const noexcept {
return tape.json_index == other.tape.json_index;
}
inline bool element::dump_raw_tape(std::ostream &out) const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
+21 -1
View File
@@ -211,7 +211,11 @@ public:
inline simdjson_result<T> get() const noexcept {
// Unless the simdjson library provides an inline implementation, calling this method should
// immediately fail.
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library.");
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
"The supported types are Boolean (bool), numbers (double, uint64_t, int64_t), "
"strings (std::string_view, const char *), arrays (dom::array) and objects (dom::object). "
"We recommand you use get_double(), get_bool(), get_uint64(), get_int64(), "
"get_object(), get_array() or get_string() instead of the get template.");
}
/**
@@ -451,6 +455,22 @@ public:
*/
inline simdjson_result<element> at_key_case_insensitive(std::string_view key) const noexcept;
/**
* operator< defines a total order for element allowing to use them in
* ordered C++ STL containers
*
* @return TRUE if the key appears before the other one in the tape
*/
inline bool operator<(const element &other) const noexcept;
/**
* operator== allows to verify if two element values reference the
* same JSON item
*
* @return TRUE if the two values references the same JSON element
*/
inline bool operator==(const element &other) const noexcept;
/** @private for debugging. Prints out the root element. */
inline bool dump_raw_tape(std::ostream &out) const noexcept;
+1 -1
View File
@@ -1,5 +1,5 @@
#define SIMDJSON_IMPLEMENTATION fallback
#include "simdjson/fallback/base.h"
#include "simdjson/fallback/bitmask.h"
#include "simdjson/fallback/bitmanipulation.h"
#include "simdjson/fallback/stringparsing_defs.h"
#include "simdjson/fallback/numberparsing_defs.h"
@@ -1,5 +1,5 @@
#ifndef SIMDJSON_FALLBACK_BITMASK_H
#define SIMDJSON_FALLBACK_BITMASK_H
#ifndef SIMDJSON_FALLBACK_BITMANIPULATION_H
#define SIMDJSON_FALLBACK_BITMANIPULATION_H
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
#include "simdjson/fallback/base.h"
@@ -7,7 +7,7 @@
namespace simdjson {
namespace fallback {
namespace bitmask {
namespace {
#if defined(_MSC_VER) && !defined(_M_ARM64) && !defined(_M_X64)
static inline unsigned char _BitScanForward64(unsigned long* ret, uint64_t x) {
-303
View File
@@ -1,303 +0,0 @@
#ifndef SIMDJSON_GENERIC_SIMD_LOOKUP_TABLE_H
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
#define SIMDJSON_GENERIC_SIMD_LOOKUP_TABLE_H
#include "simdjson/generic/base.h"
#endif // SIMDJSON_CONDITIONAL_INCLUDE
namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
namespace simd {
template <typename T = uint8_t>
struct simd8_buffer {
T buf[simd8<T>::LANES] = {0};
simdjson_consteval T& operator[](size_t i) noexcept { return buf[i]; }
simdjson_consteval T operator[](size_t i) const noexcept { return buf[i]; }
simdjson_inline operator simd8<T>() const noexcept { return buf; }
};
struct nibble_range;
struct byte_range {
struct _exclusive{};
static simdjson_constinit _exclusive exclusive{};
simdjson_consteval byte_range(uint8_t start, uint16_t end, const _exclusive&) noexcept : _start{start}, _end{end} {
SIMDJSON_ASSUME(start < end && end <= 256);
}
simdjson_consteval byte_range(uint8_t first, uint8_t last) noexcept : byte_range(first, static_cast<uint16_t>(last+1), exclusive) {}
simdjson_consteval byte_range(uint8_t first) noexcept : byte_range(first, first) {}
simdjson_consteval size_t size() const noexcept { return static_cast<size_t>(_end - _start); }
simdjson_consteval byte_range operator|(const byte_range& other) const noexcept {
SIMDJSON_ASSUME((_end + 1 >= other._start) || (other._end + 1 >= _start));
return {std::min(_start, other._start), std::max(_end, other._end), exclusive};
}
simdjson_consteval bool includes(uint8_t byte) const noexcept { return _start <= byte && byte < _end; }
struct nibble_iter {
int nibble;
const int last_nibble;
simdjson_consteval nibble_iter& operator++() noexcept {
if (nibble == last_nibble) {
nibble = -1;
} else {
++nibble;
nibble %= 16;
}
return *this;
}
simdjson_consteval nibble_iter operator++(int) noexcept { auto copy = *this; ++*this; return copy; }
simdjson_consteval bool operator==(const nibble_iter& other) const noexcept { return nibble == other.nibble; }
simdjson_consteval bool operator!=(const nibble_iter& other) const noexcept { return nibble != other.nibble; }
simdjson_consteval uint8_t operator*() const noexcept { return static_cast<uint8_t>(nibble); }
simdjson_consteval nibble_iter begin() const noexcept { return *this; }
simdjson_consteval nibble_iter end() const noexcept { return {-1, last_nibble}; }
};
simdjson_consteval nibble_iter nibble(int shift) const noexcept {
SIMDJSON_ASSUME(_start < _end);
auto first_nibble = _start >> shift;
auto last_nibble = (_end-1) >> shift;
if ((last_nibble - first_nibble) >= 16) { return {0x00, 0x0F}; }
return {static_cast<uint8_t>(first_nibble & 0x0F), static_cast<uint8_t>(last_nibble & 0x0F)};
}
struct _iter {
uint16_t value;
simdjson_consteval _iter& operator++() noexcept { ++value; return *this; }
simdjson_consteval _iter operator++(int) noexcept { auto copy = *this; ++*this; return copy; }
simdjson_consteval bool operator==(const _iter& other) const noexcept { return value == other.value; }
simdjson_consteval bool operator!=(const _iter& other) const noexcept { return value != other.value; }
simdjson_consteval uint8_t operator*() const noexcept { return static_cast<uint8_t>(value); }
};
simdjson_consteval _iter begin() const noexcept { return _iter{_start}; }
simdjson_consteval _iter end() const noexcept { return _iter{_end}; }
uint8_t _start;
const uint16_t _end;
};
namespace {
struct _lookup_entry_range;
struct _lookup_entry : byte_range {
const uint8_t value;
simdjson_consteval _lookup_entry(const byte_range& bytes, uint8_t value) noexcept
: byte_range{bytes}, value{value} {}
};
simdjson_consteval simd8_buffer<uint8_t> _make_nibble_lookup_table(
std::initializer_list<_lookup_entry> entries, int shift) noexcept {
// Make the buffer
simd8_buffer<uint8_t> buf;
for (auto entry : entries) {
for (auto key : entry.nibble(shift)) {
// Repeat the value over and over for longer simd types.
for (uint8_t k = key; k < sizeof(buf); k += 16) { buf[k] |= entry.value; }
}
}
return buf;
}
} // unnamed namespace
/**
* Byte lookup table where the key is the high 4 bits of the input, and the value is an
* arbitrary byte.
*
* - Unmatched values yield 0.
* - Multiple keys may yield the same value.
* - Multiple bytes with the same high 4 bits may NOT yield different values.
*
* ```
* enum ops_t : uint8_t {
* COMMA = 1,
* COLON = 2,
* BRACKET = 3,
* CURLY = 4
* };
* static constinit const high_nibble_lookup OPS(
* {',', COMMA},
* {':', COLON},
* {'[', BRACKET},
* {']', BRACKET},
* {'{', CURLY},
* {'}', CURLY}
* );
* simd8<uint8_t> lookup_ops(simd8<uint8_t>& operators) { return OPS[operators]; }
* ```
*/
struct high_nibble_lookup {
const simd8_buffer<uint8_t> table;
/**
* Construct a nibble lookup table from the high bits of the input to the output.
*
* @param entries A list of {key, value} pairs (e.g. {'a', 10}).
* @error asserts if multiple keys have the same high 4 bits but different values.
*/
simdjson_consteval high_nibble_lookup(std::initializer_list<_lookup_entry> entries) noexcept
: table{_make_nibble_lookup_table(entries, 4)} {}
simdjson_consteval high_nibble_lookup(const simd8_buffer<uint8_t>& table) noexcept : table(table) {}
/** Look up the value corresponding the higher 4 bits of each input byte, and return it. */
simdjson_inline simd8<uint8_t> operator[](const simd8<uint8_t>& keys) const noexcept { return lookup(keys); }
/** Look up the value corresponding the higher 4 bits of each input byte, and return it. */
simdjson_inline simd8<uint8_t> lookup(const simd8<uint8_t>& keys) const noexcept { return lookup_low(keys.shr<4>()); }
/**
* Look up the value in the table assuming the high 4 key bits are stored in the lower 4 bits.
* @pre all indexes be less than 16.
*/
simdjson_inline simd8<uint8_t> lookup_low(const simd8<uint8_t>& shifted_keys) const noexcept {
return shifted_keys.lookup_16(table);
}
/**
* Look up the value in the table assuming the high 4 key bits are stored in the lower 4 bits.
* @pre all indexes be less than 16.
*/
simdjson_inline simd8x64<uint8_t> lookup_low(const simd8x64<uint8_t>& shifted_keys) const noexcept {
return shifted_keys.lookup_16(table);
}
/** Look up the value corresponding the higher 4 bits of each input byte, and return it. */
simdjson_inline simd8x64<uint8_t> lookup(const simd8x64<uint8_t>& keys) const noexcept { return lookup_low(keys.shr<4>()); }
/** Look up the value corresponding the higher 4 bits of each input byte, and return it. */
simdjson_inline simd8x64<uint8_t> operator[](const simd8x64<uint8_t>& keys) const noexcept { return lookup(keys); }
simdjson_consteval uint8_t operator[](uint8_t key) const noexcept { return lookup(key); }
simdjson_consteval uint8_t lookup(uint8_t key) const noexcept { return table[key >> 4]; }
};
/**
* Byte lookup table where the key is the low 4 bits of the input, and the value is an
* arbitrary byte.
*
* - Unmatched values yield 0.
* - Multiple keys may yield the same value.
* - Multiple bytes with the same low 4 bits may NOT yield different values.
*
* ```
* enum ops_t : uint8_t {
* COMMA = 1,
* COLON = 2,
* BRACKET = 3,
* CURLY = 4
* };
* static constinit const high_nibble_lookup OPS(
* {',', COMMA},
* {':', COLON},
* {'[', BRACKET},
* {']', BRACKET},
* {'{', CURLY},
* {'}', CURLY}
* );
* simd8<uint8_t> lookup_ops(simd8<uint8_t>& operators) { return OPS[operators]; }
* ```
*/
struct low_nibble_lookup {
const simd8_buffer<uint8_t> table;
/**
* Construct a nibble lookup table from the low bits of the input to the output.
*
* @param entries A list of {key, value} pairs (e.g. {'a', 0}).
* @error asserts if multiple keys have the same low 4 bits but different values.
*/
simdjson_consteval low_nibble_lookup(std::initializer_list<_lookup_entry> entries) noexcept
: table{_make_nibble_lookup_table(entries, 0)} {}
simdjson_consteval low_nibble_lookup(const simd8_buffer<uint8_t>& table) noexcept : table(table) {}
/** Look up the value corresponding the lower 4 bits of each input byte, and return it. */
simdjson_inline simd8<uint8_t> operator[](const simd8<uint8_t>& keys) const noexcept { return lookup(keys); }
/** Look up the value corresponding the lower 4 bits of each input byte, and return it. */
simdjson_inline simd8<uint8_t> lookup(const simd8<uint8_t>& keys) const noexcept {
return keys.lookup_low_nibble_ascii(table);
}
/**
* Look up the value in the table. Behavior is system-dependent for indexes greater than 16.
*
* - On some platforms like arm64, indexes greater than 16 will not match anything in the table.
* - On platforms like Intel, index bits 4-6 will be ignored, but if the high bit is set, it
* will not match anything in the table. greater than 16 will be ignored, *except* if the high bit is 1,
*/
simdjson_inline simd8<uint8_t> lookup_unsafe(const simd8<uint8_t>& keys) const noexcept {
return keys.lookup_16(table);
}
/** Look up the value corresponding the lower 4 bits of each input byte, and return it. */
simdjson_inline simd8x64<uint8_t> operator[](const simd8x64<uint8_t>& keys) const noexcept { return lookup(keys); }
/** Look up the value corresponding the lower 4 bits of each input byte, and return it. */
simdjson_inline simd8x64<uint8_t> lookup(const simd8x64<uint8_t>& keys) const noexcept {
return keys.lookup_low_nibble_ascii(table);
}
/**
* Look up the value in the table. Behavior is system-dependent for indexes greater than 16.
*
* - On some platforms like arm64, indexes greater than 16 will not match anything in the table.
* - On platforms like Intel, index bits 4-6 will be ignored, but if the high bit is set, it
* will not match anything in the table. greater than 16 will be ignored, *except* if the high bit is 1,
*/
simdjson_inline simd8x64<uint8_t> lookup_unsafe(const simd8x64<uint8_t>& low_keys) const noexcept {
return low_keys.lookup_16(table);
}
simdjson_consteval uint8_t operator[](uint8_t key) const noexcept { return lookup(key); }
simdjson_consteval uint8_t lookup(uint8_t key) const noexcept { return table[key & 0x0F]; }
};
/**
* Classifies bytes by looking up their lower 4 bits, then their high 4 bits, and &'ing the
* results together.
*
* Pass the bytes you want to match, and the classifications you want for them.
*/
struct byte_classifier {
const low_nibble_lookup low;
const high_nibble_lookup high;
simdjson_consteval byte_classifier(std::initializer_list<_lookup_entry> entries)
: low{entries}, high{entries} {}
simdjson_inline simd8<uint8_t> classify(const simd8<uint8_t>& bytes) const noexcept {
return low.lookup(bytes) & high.lookup(bytes);
}
simdjson_inline simd8x64<uint8_t> classify(const simd8x64<uint8_t>& bytes) const noexcept {
auto low_lookup = low.lookup(bytes); // 3 (+simd:N)
auto high_lookup = high.lookup(bytes); // 6 (+simd:2N)
return low_lookup & high_lookup; // 3 (+simd:N)
// critical path: 9 (+simd:4N)
}
simdjson_consteval uint8_t classify(uint8_t byte) const noexcept {
return low.lookup(byte) & high.lookup(byte);
}
simdjson_inline simd8<uint8_t> operator[](const simd8<uint8_t>& bytes) const noexcept { return classify(bytes); }
simdjson_inline simd8x64<uint8_t> operator[](const simd8x64<uint8_t>& bytes) const noexcept { return classify(bytes); }
simdjson_consteval uint8_t operator[](uint8_t byte) const noexcept { return classify(byte); }
simdjson_inline bool matches_correctly(std::initializer_list<_lookup_entry> entries) const noexcept {
uint8_t expected_output[256] = {};
for (auto entry : entries) {
for (uint8_t byte : entry) {
expected_output[byte] |= entry.value;
}
}
for (uint8_t byte = 0; byte <= 0xFF; byte++) {
if (expected_output[byte] != classify(byte)) { return false; }
}
return true;
}
};
} // namespace simd
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
#endif // SIMDJSON_GENERIC_SIMD_LOOKUP_TABLE_H
+5 -9
View File
@@ -143,7 +143,7 @@ simdjson_inline bool compute_float_64(int64_t power, uint64_t i, bool negative,
// We want the most significant bit of i to be 1. Shift if needed.
int lz = bitmask::leading_zeroes(i);
int lz = leading_zeroes(i);
i <<= lz;
@@ -186,8 +186,7 @@ simdjson_inline bool compute_float_64(int64_t power, uint64_t i, bool negative,
// power_of_five_128[index]. Usually, that's good enough to approximate i * 5^q
// to the desired approximation using one multiplication. Sometimes it does not suffice.
// Then we store the next most significant 64 bits in power_of_five_128[index + 1], and
// then we get a better approximation to i * 5^q. In very rare cases, even that
// will not suffice, though it is seemingly very hard to find such a scenario.
// then we get a better approximation to i * 5^q.
//
// That's for when q>=0. The logic for q<0 is somewhat similar but it is somewhat
// more complicated.
@@ -202,12 +201,9 @@ simdjson_inline bool compute_float_64(int64_t power, uint64_t i, bool negative,
simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]);
firstproduct.low += secondproduct.high;
if(secondproduct.high > firstproduct.low) { firstproduct.high++; }
// At this point, we might need to add at most one to firstproduct, but this
// can only change the value of firstproduct.high if firstproduct.low is maximal.
if(simdjson_unlikely(firstproduct.low == 0xFFFFFFFFFFFFFFFF)) {
// This is very unlikely, but if so, we need to do much more work!
return false;
}
// As it has been proven by Noble Mushtak and Daniel Lemire in "Fast Number Parsing Without
// Fallback" (https://arxiv.org/abs/2212.06644), at this point we are sure that the product
// is sufficiently accurate, and more computation is not needed.
}
uint64_t lower = firstproduct.low;
uint64_t upper = firstproduct.high;
@@ -128,6 +128,9 @@ simdjson_inline simdjson_result<double> document::get_double_in_string() noexcep
simdjson_inline simdjson_result<std::string_view> document::get_string(bool allow_replacement) noexcept {
return get_root_value_iterator().get_root_string(true, allow_replacement);
}
simdjson_inline error_code document::get_string(std::string& receiver, bool allow_replacement) noexcept {
return get_root_value_iterator().get_root_string(receiver, true, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> document::get_wobbly_string() noexcept {
return get_root_value_iterator().get_root_wobbly_string(true);
}
@@ -397,6 +400,10 @@ simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLE
if (error()) { return error(); }
return first.get_string(allow_replacement);
}
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::get_string(std::string& receiver, bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(receiver, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::get_wobbly_string() noexcept {
if (error()) { return error(); }
return first.get_wobbly_string();
@@ -583,6 +590,7 @@ simdjson_inline simdjson_result<int64_t> document_reference::get_int64_in_string
simdjson_inline simdjson_result<double> document_reference::get_double() noexcept { return doc->get_root_value_iterator().get_root_double(false); }
simdjson_inline simdjson_result<double> document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double(false); }
simdjson_inline simdjson_result<std::string_view> document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); }
simdjson_inline error_code document_reference::get_string(std::string& receiver, bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(receiver, false, allow_replacement); }
simdjson_inline simdjson_result<std::string_view> document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); }
simdjson_inline simdjson_result<raw_json_string> document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); }
simdjson_inline simdjson_result<bool> document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); }
@@ -719,6 +727,10 @@ simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLE
if (error()) { return error(); }
return first.get_string(allow_replacement);
}
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get_string(std::string& receiver, bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(receiver, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get_wobbly_string() noexcept {
if (error()) { return error(); }
return first.get_wobbly_string();
+25 -2
View File
@@ -100,6 +100,20 @@ public:
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
/**
* Attempts to fill the provided std::string reference with the parsed value of the current string.
*
* The string is guaranteed to be valid UTF-8.
*
* Important: a value should be consumed once. Calling get_string() twice on the same value
* is an error.
*
* Performance: This method may be slower than get_string() or get_string(bool) because it may need to allocate memory.
* We recommend you avoid allocating an std::string unless you need to.
*
* @returns INCORRECT_TYPE if the JSON value is not a string. Otherwise, we return SUCCESS.
*/
simdjson_inline error_code get_string(std::string& receiver, bool allow_replacement = false) noexcept;
/**
* Cast this JSON value to a string.
*
@@ -160,13 +174,19 @@ public:
template<typename T> simdjson_inline simdjson_result<T> get() & noexcept {
// Unless the simdjson library provides an inline implementation, calling this method should
// immediately fail.
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library.");
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
"The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), "
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template.");
}
/** @overload template<typename T> simdjson_result<T> get() & noexcept */
template<typename T> simdjson_inline simdjson_result<T> get() && noexcept {
// Unless the simdjson library provides an inline implementation, calling this method should
// immediately fail.
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library.");
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
"The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), "
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template.");
}
/**
@@ -618,6 +638,7 @@ public:
simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
simdjson_inline error_code get_string(std::string& receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_inline simdjson_result<raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept;
@@ -687,6 +708,7 @@ public:
simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
simdjson_inline error_code get_string(std::string& receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept;
@@ -759,6 +781,7 @@ public:
simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
simdjson_inline error_code get_string(std::string& receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept;
@@ -101,6 +101,10 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::skip_child(depth_
case '[': case '{': case ':':
logger::log_start_value(*this, "skip");
break;
// If there is a comma, we have just finished a value in an array/object, and need to get back in
case ',':
logger::log_value(*this, "skip");
break;
// ] or } means we just finished a value and need to jump out of the array/object
case ']': case '}':
logger::log_end_value(*this, "skip");
@@ -254,6 +254,7 @@ public:
*/
simdjson_inline simdjson_result<std::string_view> unescape(raw_json_string in, bool allow_replacement) noexcept;
simdjson_inline simdjson_result<std::string_view> unescape_wobbly(raw_json_string in) noexcept;
simdjson_inline void reenter_child(token_position position, depth_t child_depth) noexcept;
simdjson_inline error_code consume_character(char c) noexcept;
@@ -68,6 +68,13 @@ simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(s
return iterate(padded_string_view(json, allocated));
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(std::string &json) & noexcept {
if(json.capacity() - json.size() < SIMDJSON_PADDING) {
json.reserve(json.size() + SIMDJSON_PADDING);
}
return iterate(padded_string_view(json));
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const std::string &json) & noexcept {
return iterate(padded_string_view(json));
}
@@ -107,6 +107,8 @@ public:
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const std::string &json) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(std::string &json) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const simdjson_result<padded_string> &json) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const simdjson_result<padded_string_view> &json) & noexcept;
@@ -47,6 +47,9 @@ simdjson_inline simdjson_result<raw_json_string> value::get_raw_json_string() no
simdjson_inline simdjson_result<std::string_view> value::get_string(bool allow_replacement) noexcept {
return iter.get_string(allow_replacement);
}
simdjson_inline error_code value::get_string(std::string& receiver, bool allow_replacement) noexcept {
return iter.get_string(receiver, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> value::get_wobbly_string() noexcept {
return iter.get_wobbly_string();
}
@@ -319,6 +322,10 @@ simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLE
if (error()) { return error(); }
return first.get_string(allow_replacement);
}
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::get_string(std::string& receiver, bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(receiver, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::get_wobbly_string() noexcept {
if (error()) { return error(); }
return first.get_wobbly_string();
+19 -1
View File
@@ -38,7 +38,10 @@ public:
template<typename T> simdjson_inline simdjson_result<T> get() noexcept {
// Unless the simdjson library provides an inline implementation, calling this method should
// immediately fail.
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library.");
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
"The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), "
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template.");
}
/**
@@ -132,6 +135,20 @@ public:
*/
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
/**
* Attempts to fill the provided std::string reference with the parsed value of the current string.
*
* The string is guaranteed to be valid UTF-8.
*
* Important: a value should be consumed once. Calling get_string() twice on the same value
* is an error.
*
* Performance: This method may be slower than get_string() or get_string(bool) because it may need to allocate memory.
* We recommend you avoid allocating an std::string unless you need to.
*
* @returns INCORRECT_TYPE if the JSON value is not a string. Otherwise, we return SUCCESS.
*/
simdjson_inline error_code get_string(std::string& receiver, bool allow_replacement = false) noexcept;
/**
* Cast this JSON value to a "wobbly" string.
@@ -602,6 +619,7 @@ public:
simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
simdjson_inline error_code get_string(std::string& receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept;
@@ -97,12 +97,16 @@ simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::has_n
// It's illegal to call this unless there are more tokens: anything that ends in } or ] is
// obligated to verify there are more tokens if they are not the top level.
if (_json_iter->consume_character('}')) {
logger::log_end_value(*_json_iter, "object");
SIMDJSON_TRY( end_container() );
return false;
switch (*_json_iter->return_current_and_advance()) {
case '}':
logger::log_end_value(*_json_iter, "object");
SIMDJSON_TRY( end_container() );
return false;
case ',':
return true;
default:
return report_error(TAPE_ERROR, "Missing comma between object fields");
}
return true;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::find_field_raw(const std::string_view key) noexcept {
@@ -479,13 +483,16 @@ simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::has_n
assert_at_next();
logger::log_event(*this, "has_next_element");
if (_json_iter->consume_character(']')) {
switch (*_json_iter->return_current_and_advance()) {
case ']':
logger::log_end_value(*_json_iter, "array");
SIMDJSON_TRY( end_container() );
return false;
} else {
_json_iter->descend_to(depth()+1);
return true;
case ',':
_json_iter->descend_to(depth()+1);
return true;
default:
return report_error(TAPE_ERROR, "Missing comma between array elements");
}
}
@@ -506,6 +513,13 @@ simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::parse
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_string(bool allow_replacement) noexcept {
return get_raw_json_string().unescape(json_iter(), allow_replacement);
}
simdjson_warn_unused simdjson_inline error_code value_iterator::get_string(std::string& receiver, bool allow_replacement) noexcept {
std::string_view content;
auto err = get_string(allow_replacement).get(content);
if (err) { return err; }
receiver = std::string(content);
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_wobbly_string() noexcept {
return get_raw_json_string().unescape_wobbly(json_iter());
}
@@ -629,6 +643,13 @@ simdjson_inline simdjson_result<number> value_iterator::get_root_number(bool che
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_root_string(bool check_trailing, bool allow_replacement) noexcept {
return get_root_raw_json_string(check_trailing).unescape(json_iter(), allow_replacement);
}
simdjson_warn_unused simdjson_inline error_code value_iterator::get_root_string(std::string& receiver, bool check_trailing, bool allow_replacement) noexcept {
std::string_view content;
auto err = get_root_string(check_trailing, allow_replacement).get(content);
if (err) { return err; }
receiver = std::string(content);
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_root_wobbly_string(bool check_trailing) noexcept {
return get_root_raw_json_string(check_trailing).unescape_wobbly(json_iter());
}
@@ -296,6 +296,7 @@ public:
*/
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement) noexcept;
simdjson_warn_unused simdjson_inline error_code get_string(std::string& receiver, bool allow_replacement) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> get_raw_json_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
@@ -312,7 +313,8 @@ public:
simdjson_warn_unused simdjson_inline simdjson_result<number> get_number() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_root_string(bool check_trailing, bool allow_replacement) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_root_wobbly_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline error_code get_root_string(std::string& receiver, bool check_trailing, bool allow_replacement) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_root_wobbly_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> get_root_raw_json_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_root_uint64(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_root_uint64_in_string(bool check_trailing) noexcept;
-1
View File
@@ -3,7 +3,6 @@
#include "simdjson/haswell/begin.h"
#include "simdjson/generic/amalgamated.h"
#include "simdjson/generic/lookup_table.h"
#include "simdjson/haswell/end.h"
#endif // SIMDJSON_HASWELL_H
+2 -4
View File
@@ -14,14 +14,12 @@ namespace haswell {
class implementation;
namespace {
namespace simd {
template <typename T> struct simd8;
template <> struct simd8<bool>;
template <> struct simd8<uint8_t>;
template <typename T> struct simd8x64;
} // namespace simd
} // unnamed namespace
} // namespace haswell
} // namespace simdjson
+1 -1
View File
@@ -7,7 +7,7 @@
SIMDJSON_TARGET_REGION("avx2,bmi,pclmul,lzcnt,popcnt")
#endif
#include "simdjson/haswell/bitmask.h"
#include "simdjson/haswell/bitmanipulation.h"
#include "simdjson/haswell/bitmask.h"
#include "simdjson/haswell/numberparsing_defs.h"
#include "simdjson/haswell/simd.h"
@@ -0,0 +1,71 @@
#ifndef SIMDJSON_HASWELL_BITMANIPULATION_H
#define SIMDJSON_HASWELL_BITMANIPULATION_H
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
#include "simdjson/haswell/base.h"
#include "simdjson/haswell/intrinsics.h"
#include "simdjson/haswell/bitmask.h"
#endif // SIMDJSON_CONDITIONAL_INCLUDE
namespace simdjson {
namespace haswell {
namespace {
// We sometimes call trailing_zero on inputs that are zero,
// but the algorithms do not end up using the returned value.
// Sadly, sanitizers are not smart enough to figure it out.
SIMDJSON_NO_SANITIZE_UNDEFINED
// This function can be used safely even if not all bytes have been
// initialized.
// See issue https://github.com/simdjson/simdjson/issues/1965
SIMDJSON_NO_SANITIZE_MEMORY
simdjson_inline int trailing_zeroes(uint64_t input_num) {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
return (int)_tzcnt_u64(input_num);
#else // SIMDJSON_REGULAR_VISUAL_STUDIO
////////
// You might expect the next line to be equivalent to
// return (int)_tzcnt_u64(input_num);
// but the generated code differs and might be less efficient?
////////
return __builtin_ctzll(input_num);
#endif // SIMDJSON_REGULAR_VISUAL_STUDIO
}
/* result might be undefined when input_num is zero */
simdjson_inline uint64_t clear_lowest_bit(uint64_t input_num) {
return _blsr_u64(input_num);
}
/* result might be undefined when input_num is zero */
simdjson_inline int leading_zeroes(uint64_t input_num) {
return int(_lzcnt_u64(input_num));
}
#if SIMDJSON_REGULAR_VISUAL_STUDIO
simdjson_inline unsigned __int64 count_ones(uint64_t input_num) {
// note: we do not support legacy 32-bit Windows in this kernel
return __popcnt64(input_num);// Visual Studio wants two underscores
}
#else
simdjson_inline long long int count_ones(uint64_t input_num) {
return _popcnt64(input_num);
}
#endif
simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2,
uint64_t *result) {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
return _addcarry_u64(0, value1, value2,
reinterpret_cast<unsigned __int64 *>(result));
#else
return __builtin_uaddll_overflow(value1, value2,
reinterpret_cast<unsigned long long *>(result));
#endif
}
} // unnamed namespace
} // namespace haswell
} // namespace simdjson
#endif // SIMDJSON_HASWELL_BITMANIPULATION_H
+3 -88
View File
@@ -8,99 +8,14 @@
namespace simdjson {
namespace haswell {
namespace bitmask {
simdjson_constinit uint64_t ALL = 0xFFFFFFFFFFFFFFFF;
simdjson_constinit uint64_t NONE = 0xFFFFFFFFFFFFFFFF;
simdjson_constinit uint64_t EVEN = 0x5555555555555555;
simdjson_constinit uint64_t ODD = 0xAAAAAAAAAAAAAAAA;
// We sometimes call trailing_zero on inputs that are zero,
// but the algorithms do not end up using the returned value.
// Sadly, sanitizers are not smart enough to figure it out.
SIMDJSON_NO_SANITIZE_UNDEFINED
// This function can be used safely even if not all bytes have been
// initialized.
// See issue https://github.com/simdjson/simdjson/issues/1965
SIMDJSON_NO_SANITIZE_MEMORY
simdjson_inline int trailing_zeroes(uint64_t input_num) {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
return (int)_tzcnt_u64(input_num);
#else // SIMDJSON_REGULAR_VISUAL_STUDIO
////////
// You might expect the next line to be equivalent to
// return (int)_tzcnt_u64(input_num);
// but the generated code differs and might be less efficient?
////////
return __builtin_ctzll(input_num);
#endif // SIMDJSON_REGULAR_VISUAL_STUDIO
}
/* result might be undefined when input_num is zero */
simdjson_inline uint64_t clear_lowest_bit(uint64_t input_num) {
return _blsr_u64(input_num);
}
/* result might be undefined when input_num is zero */
simdjson_inline int leading_zeroes(uint64_t input_num) {
return int(_lzcnt_u64(input_num));
}
#if SIMDJSON_REGULAR_VISUAL_STUDIO
simdjson_inline unsigned __int64 count_ones(uint64_t input_num) {
// note: we do not support legacy 32-bit Windows in this kernel
return __popcnt64(input_num);// Visual Studio wants two underscores
}
#else
simdjson_inline long long int count_ones(uint64_t input_num) {
return _popcnt64(input_num);
}
#endif
simdjson_inline uint64_t add_carry_out(const uint64_t value1, const uint64_t value2, bool& carry_out) noexcept {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
unsigned __int64 result;
carry_out = _addcarry_u64(0, value1, value2, &result);
return result;
#else
unsigned long long result;
carry_out = __builtin_uaddll_overflow(value1, value2, &result);
return result;
#endif
}
simdjson_inline uint64_t subtract_borrow(const uint64_t value1, const uint64_t value2, bool& borrow) noexcept {
// TODO only do this on GCC, not clang
// #if SIMDJSON_REGULAR_VISUAL_STUDIO
// unsigned __int64 result;
// borrow = _subborrow_u64(borrow, value1, value2, &result);
// return result;
// #else
unsigned long long result;
bool borrow1 = __builtin_usubll_overflow(value1, value2, &result);
borrow = borrow1 | __builtin_usubll_overflow(result, borrow, &result);
return result;
// #endif
}
simdjson_inline uint64_t subtract_borrow_out(const uint64_t value1, const int64_t value2, bool& borrow_out) noexcept {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
unsigned __int64 result;
borrow_out = _subborrow_u64(0, value1, value2, &result);
return result;
#else
unsigned long long result;
borrow_out = __builtin_usubll_overflow(value1, value2, &result); // 2 (one to set )
return result;
#endif
}
namespace {
//
// Perform a "cumulative bitwise xor," flipping bits each time a 1 is encountered.
//
// For example, prefix_xor(00100100) == 00011100
//
simdjson_inline uint64_t prefix_xor(const uint64_t bitmask) noexcept {
simdjson_inline uint64_t prefix_xor(const uint64_t bitmask) {
// There should be no such thing with a processor supporting avx2
// but not clmul.
__m128i all_ones = _mm_set1_epi8('\xFF');
@@ -108,7 +23,7 @@ simdjson_inline uint64_t prefix_xor(const uint64_t bitmask) noexcept {
return _mm_cvtsi128_si64(result);
}
} // namespace bitmask
} // unnamed namespace
} // namespace haswell
} // namespace simdjson
+57 -157
View File
@@ -4,30 +4,29 @@
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
#include "simdjson/haswell/base.h"
#include "simdjson/haswell/intrinsics.h"
#include "simdjson/haswell/bitmask.h"
#include "simdjson/haswell/bitmanipulation.h"
#include "simdjson/internal/simdprune_tables.h"
#endif // SIMDJSON_CONDITIONAL_INCLUDE
namespace simdjson {
namespace haswell {
namespace {
namespace simd {
// Forward-declared so they can be used by splat and friends.
template<typename Child>
struct base {
/** The actual underlying system SIMD type. */
using simd_t = __m256i;
simd_t value;
__m256i value;
// Zero constructor
simdjson_inline base() : value{simd_t()} {}
simdjson_inline base() : value{__m256i()} {}
// Conversion from SIMD register
simdjson_inline base(const simd_t _value) : value(_value) {}
simdjson_inline base(const __m256i _value) : value(_value) {}
// Conversion to SIMD register
simdjson_inline operator const simd_t&() const { return this->value; }
simdjson_inline operator simd_t&() { return this->value; }
simdjson_inline operator const __m256i&() const { return this->value; }
simdjson_inline operator __m256i&() { return this->value; }
// Bit operations
simdjson_inline Child operator|(const Child other) const { return _mm256_or_si256(*this, other); }
@@ -45,16 +44,15 @@ namespace simd {
template<typename T, typename Mask=simd8<bool>>
struct base8: base<simd8<T>> {
using typename base<simd8<T>>::simd_t;
static constexpr const int LANES = sizeof(simd_t);
using bitmask_t = uint32_t;
static_assert(sizeof(bitmask_t)*8 == LANES, "Bitmask type's bits must equal the simd type's bytes");
typedef uint32_t bitmask_t;
typedef uint64_t bitmask2_t;
simdjson_inline base8() : base<simd8<T>>() {}
simdjson_inline base8(const simd_t _value) : base<simd8<T>>(_value) {}
simdjson_inline base8(const __m256i _value) : base<simd8<T>>(_value) {}
simdjson_inline Mask eq(const simd8<T> rhs) const { return _mm256_cmpeq_epi8(*this, rhs); }
friend simdjson_inline Mask operator==(const simd8<T> lhs, const simd8<T> rhs) { return lhs.eq(rhs); }
friend simdjson_really_inline Mask operator==(const simd8<T> lhs, const simd8<T> rhs) { return _mm256_cmpeq_epi8(lhs, rhs); }
static const int SIZE = sizeof(base<T>::value);
template<int N=1>
simdjson_inline simd8<T> prev(const simd8<T> prev_chunk) const {
@@ -68,7 +66,7 @@ namespace simd {
static simdjson_inline simd8<bool> splat(bool _value) { return _mm256_set1_epi8(uint8_t(-(!!_value))); }
simdjson_inline simd8<bool>() : base8() {}
simdjson_inline simd8<bool>(const simd_t _value) : base8<bool>(_value) {}
simdjson_inline simd8<bool>(const __m256i _value) : base8<bool>(_value) {}
// Splat constructor
simdjson_inline simd8<bool>(bool _value) : base8<bool>(splat(_value)) {}
@@ -79,12 +77,10 @@ namespace simd {
template<typename T>
struct base8_numeric: base8<T> {
using typename base8<T>::simd_t;
using base8<T>::LANES;
static simdjson_inline simd8<T> splat(T _value) { return _mm256_set1_epi8(_value); }
static simdjson_inline simd8<T> zero() { return _mm256_setzero_si256(); }
static simdjson_inline simd8<T> load(const T values[32]) {
return _mm256_loadu_si256(reinterpret_cast<const simd_t *>(values));
return _mm256_loadu_si256(reinterpret_cast<const __m256i *>(values));
}
// Repeat 16 values as many times as necessary (usually for lookup tables)
static simdjson_inline simd8<T> repeat_16(
@@ -100,10 +96,10 @@ namespace simd {
}
simdjson_inline base8_numeric() : base8<T>() {}
simdjson_inline base8_numeric(const simd_t _value) : base8<T>(_value) {}
simdjson_inline base8_numeric(const __m256i _value) : base8<T>(_value) {}
// Store to array
simdjson_inline void store(T dst[32]) const { return _mm256_storeu_si256(reinterpret_cast<simd_t *>(dst), *this); }
simdjson_inline void store(T dst[32]) const { return _mm256_storeu_si256(reinterpret_cast<__m256i *>(dst), *this); }
// Addition/subtraction are the same for signed and unsigned
simdjson_inline simd8<T> operator+(const simd8<T> other) const { return _mm256_add_epi8(*this, other); }
@@ -115,18 +111,14 @@ namespace simd {
simdjson_inline simd8<T> operator~() const { return *this ^ 0xFFu; }
// Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values)
simdjson_inline simd8<T> lookup_16(const simd8<T>& lookup_table) const {
template<typename L>
simdjson_inline simd8<L> lookup_16(simd8<L> lookup_table) const {
return _mm256_shuffle_epi8(lookup_table, *this);
}
// Perform a lookup based on the lower 4 bits of each lane. (Platform-dependent behavior for
// non-ASCII values--may look up the lower 4 bits on some platforms, and return 0 on others.)
simdjson_inline simd8<T> lookup_low_nibble_ascii(const simd8<T>& lookup_table) const {
return lookup_16(lookup_table);
}
// Copies to 'output" all bytes corresponding to a 0 in the mask (interpreted as a bitset).
// Passing a 0 value for mask would be equivalent to writing out every byte to output.
// Only the first 32 - bitmask::count_ones(mask) bytes of the result are significant but 32 bytes
// Only the first 32 - count_ones(mask) bytes of the result are significant but 32 bytes
// get written.
// Design consideration: it seems like a function with the
// signature simd8<L> compress(uint32_t mask) would be
@@ -145,14 +137,14 @@ namespace simd {
// next line just loads the 64-bit values thintable_epi8[mask1] and
// thintable_epi8[mask2] into a 128-bit register, using only
// two instructions on most compilers.
simd_t shufmask = _mm256_set_epi64x(thintable_epi8[mask4], thintable_epi8[mask3],
__m256i shufmask = _mm256_set_epi64x(thintable_epi8[mask4], thintable_epi8[mask3],
thintable_epi8[mask2], thintable_epi8[mask1]);
// we increment by 0x08 the second half of the mask and so forth
shufmask =
_mm256_add_epi8(shufmask, _mm256_set_epi32(0x18181818, 0x18181818,
0x10101010, 0x10101010, 0x08080808, 0x08080808, 0, 0));
// this is the version "nearly pruned"
simd_t pruned = _mm256_shuffle_epi8(*this, shufmask);
__m256i pruned = _mm256_shuffle_epi8(*this, shufmask);
// we still need to put the pieces back together.
// we compute the popcount of the first words:
int pop1 = BitsSetTable256mul2[mask1];
@@ -160,20 +152,35 @@ namespace simd {
// then load the corresponding mask
// could be done with _mm256_loadu2_m128i but many standard libraries omit this intrinsic.
simd_t v256 = _mm256_castsi128_si256(
__m256i v256 = _mm256_castsi128_si256(
_mm_loadu_si128(reinterpret_cast<const __m128i *>(pshufb_combine_table + pop1 * 8)));
simd_t compactmask = _mm256_insertf128_si256(v256,
__m256i compactmask = _mm256_insertf128_si256(v256,
_mm_loadu_si128(reinterpret_cast<const __m128i *>(pshufb_combine_table + pop3 * 8)), 1);
simd_t almostthere = _mm256_shuffle_epi8(pruned, compactmask);
__m256i almostthere = _mm256_shuffle_epi8(pruned, compactmask);
// We just need to write out the result.
// This is the tricky bit that is hard to do
// if we want to return a SIMD register, since there
// is no single-instruction approach to recombine
// the two 128-bit lanes with an offset.
__m128i v128 = _mm256_castsi256_si128(almostthere);
__m128i v128;
v128 = _mm256_castsi256_si128(almostthere);
_mm_storeu_si128( reinterpret_cast<__m128i *>(output), v128);
v128 = _mm256_extractf128_si256(almostthere, 1);
_mm_storeu_si128( reinterpret_cast<__m128i *>(output + 16 - bitmask::count_ones(mask & 0xFFFF)), v128);
_mm_storeu_si128( reinterpret_cast<__m128i *>(output + 16 - count_ones(mask & 0xFFFF)), v128);
}
template<typename L>
simdjson_inline simd8<L> lookup_16(
L replace0, L replace1, L replace2, L replace3,
L replace4, L replace5, L replace6, L replace7,
L replace8, L replace9, L replace10, L replace11,
L replace12, L replace13, L replace14, L replace15) const {
return lookup_16(simd8<L>::repeat_16(
replace0, replace1, replace2, replace3,
replace4, replace5, replace6, replace7,
replace8, replace9, replace10, replace11,
replace12, replace13, replace14, replace15
));
}
};
@@ -181,7 +188,7 @@ namespace simd {
template<>
struct simd8<int8_t> : base8_numeric<int8_t> {
simdjson_inline simd8() : base8_numeric<int8_t>() {}
simdjson_inline simd8(const simd_t _value) : base8_numeric<int8_t>(_value) {}
simdjson_inline simd8(const __m256i _value) : base8_numeric<int8_t>(_value) {}
// Splat constructor
simdjson_inline simd8(int8_t _value) : simd8(splat(_value)) {}
// Array constructor
@@ -222,7 +229,7 @@ namespace simd {
template<>
struct simd8<uint8_t>: base8_numeric<uint8_t> {
simdjson_inline simd8() : base8_numeric<uint8_t>() {}
simdjson_inline simd8(const simd_t _value) : base8_numeric<uint8_t>(_value) {}
simdjson_inline simd8(const __m256i _value) : base8_numeric<uint8_t>(_value) {}
// Splat constructor
simdjson_inline simd8(uint8_t _value) : simd8(splat(_value)) {}
// Array constructor
@@ -300,15 +307,13 @@ namespace simd {
simdjson_inline simd8x64(const simd8<T> chunk0, const simd8<T> chunk1) : chunks{chunk0, chunk1} {}
simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8<T>::load(ptr), simd8<T>::load(ptr+32)} {}
simdjson_inline simd8x64(simd8x64<T>&& o) noexcept = default;
simdjson_inline simd8x64<T>& operator=(simd8x64<T>&& other) noexcept = default;
simdjson_inline uint64_t compress(uint64_t mask, T * output) const {
uint32_t mask1 = uint32_t(mask);
uint32_t mask2 = uint32_t(mask >> 32);
this->chunks[0].compress(mask1, output);
this->chunks[1].compress(mask2, output + 32 - bitmask::count_ones(mask1));
return 64 - bitmask::count_ones(mask);
this->chunks[1].compress(mask2, output + 32 - count_ones(mask1));
return 64 - count_ones(mask);
}
simdjson_inline void store(T ptr[64]) const {
@@ -318,7 +323,7 @@ namespace simd {
simdjson_inline uint64_t to_bitmask() const {
uint64_t r_lo = uint32_t(this->chunks[0].to_bitmask());
uint64_t r_hi = this->chunks[1].to_bitmask();
uint64_t r_hi = this->chunks[1].to_bitmask();
return r_lo | (r_hi << 32);
}
@@ -326,6 +331,14 @@ namespace simd {
return this->chunks[0] | this->chunks[1];
}
simdjson_inline simd8x64<T> bit_or(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return simd8x64<T>(
this->chunks[0] | mask,
this->chunks[1] | mask
);
}
simdjson_inline uint64_t eq(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return simd8x64<bool>(
@@ -334,27 +347,13 @@ namespace simd {
).to_bitmask();
}
simdjson_inline uint64_t eq(const simd8x64<T> &other) const {
simdjson_inline uint64_t eq(const simd8x64<uint8_t> &other) const {
return simd8x64<bool>(
this->chunks[0] == other.chunks[0],
this->chunks[1] == other.chunks[1]
).to_bitmask();
}
simdjson_inline simd8x64<T> lookup_16(const simd8<T>& lookup_table) const {
return {
this->chunks[0].lookup_16(lookup_table),
this->chunks[1].lookup_16(lookup_table)
};
}
simdjson_inline simd8x64<T> lookup_low_nibble_ascii(const simd8<T>& lookup_table) const {
return {
this->chunks[0].lookup_low_nibble_ascii(lookup_table),
this->chunks[1].lookup_low_nibble_ascii(lookup_table)
};
}
simdjson_inline uint64_t lteq(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return simd8x64<bool>(
@@ -362,110 +361,11 @@ namespace simd {
this->chunks[1] <= mask
).to_bitmask();
}
simdjson_inline simd8x64<T> operator&(const simd8x64<T>& other) const {
return {
this->chunks[0] & other.chunks[0],
this->chunks[1] & other.chunks[1]
};
}
simdjson_inline simd8x64<T> operator&(const simd8<T>& other) const {
return {
this->chunks[0] & other,
this->chunks[1] & other
};
}
simdjson_inline simd8x64<T> operator|(const simd8x64<T>& other) const {
return {
this->chunks[0] | other.chunks[0],
this->chunks[1] | other.chunks[1]
};
}
simdjson_inline simd8x64<T> operator|(const simd8<T>& other) const {
return {
this->chunks[0] | other,
this->chunks[1] | other
};
}
simdjson_inline simd8x64<T> operator^(const simd8x64<T>& other) const {
return {
this->chunks[0] ^ other.chunks[0],
this->chunks[1] ^ other.chunks[1]
};
}
simdjson_inline simd8x64<T> operator^(const simd8<T>& other) const {
return {
this->chunks[0] ^ other,
this->chunks[1] ^ other
};
}
simdjson_inline simd8x64<T> bit_andnot(const simd8x64<T>& other) const {
return {
this->chunks[0].bit_andnot(other.chunks[0]),
this->chunks[1].bit_andnot(other.chunks[1])
};
}
simdjson_inline simd8x64<T> bit_andnot(const simd8<T>& other) const {
return {
this->chunks[0].bit_andnot(other),
this->chunks[1].bit_andnot(other),
};
}
template <int N>
simdjson_inline simd8x64<T> shr() const noexcept {
return {
this->chunks[0].template shr<N>(),
this->chunks[1].template shr<N>()
};
}
template <int N>
simdjson_inline simd8x64<T> shl() const noexcept {
return {
this->chunks[0].template shl<N>(),
this->chunks[1].template shl<N>()
};
}
simdjson_inline simd8x64<bool> any_bits_set(const simd8<T>& bits) const {
return {
this->chunks[0].any_bits_set(bits),
this->chunks[1].any_bits_set(bits)
};
}
simdjson_inline simd8x64<bool> any_bits_set(const simd8x64<T>& bits) const {
return {
this->chunks[0].any_bits_set(bits.chunks[0]),
this->chunks[1].any_bits_set(bits.chunks[1])
};
}
simdjson_inline simd8x64<bool> no_bits_set(const simd8<T>& bits) const {
return {
this->chunks[0].no_bits_set(bits),
this->chunks[1].no_bits_set(bits)
};
}
simdjson_inline simd8x64<bool> no_bits_set(const simd8x64<T>& bits) const {
return {
this->chunks[0].no_bits_set(bits.chunks[0]),
this->chunks[1].no_bits_set(bits.chunks[1])
};
}
}; // struct simd8x64<T>
} // namespace simd
} // unnamed namespace
} // namespace haswell
} // namespace simdjson
@@ -4,7 +4,7 @@
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
#include "simdjson/haswell/base.h"
#include "simdjson/haswell/simd.h"
#include "simdjson/haswell/bitmask.h"
#include "simdjson/haswell/bitmanipulation.h"
#endif // SIMDJSON_CONDITIONAL_INCLUDE
namespace simdjson {
@@ -21,8 +21,8 @@ public:
simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; }
simdjson_inline bool has_backslash() { return ((quote_bits - 1) & bs_bits) != 0; }
simdjson_inline int quote_index() { return bitmask::trailing_zeroes(quote_bits); }
simdjson_inline int backslash_index() { return bitmask::trailing_zeroes(bs_bits); }
simdjson_inline int quote_index() { return trailing_zeroes(quote_bits); }
simdjson_inline int backslash_index() { return trailing_zeroes(bs_bits); }
uint32_t bs_bits;
uint32_t quote_bits;
-1
View File
@@ -3,7 +3,6 @@
#include "simdjson/icelake/begin.h"
#include "simdjson/generic/amalgamated.h"
#include "simdjson/generic/lookup_table.h"
#include "simdjson/icelake/end.h"
#endif // SIMDJSON_ICELAKE_H
-9
View File
@@ -14,15 +14,6 @@ namespace icelake {
class implementation;
namespace simd {
template <typename T> struct simd8;
template <> struct simd8<bool>;
template <> struct simd8<uint8_t>;
template <typename T> struct simd8x64;
} // unnamed namespace
} // namespace icelake
} // namespace simdjson
+1
View File
@@ -6,6 +6,7 @@
SIMDJSON_TARGET_REGION("avx512f,avx512dq,avx512cd,avx512bw,avx512vbmi,avx512vbmi2,avx512vl,avx2,bmi,pclmul,lzcnt,popcnt")
#endif
#include "simdjson/icelake/bitmanipulation.h"
#include "simdjson/icelake/bitmask.h"
#include "simdjson/icelake/simd.h"
#include "simdjson/icelake/stringparsing_defs.h"
@@ -0,0 +1,70 @@
#ifndef SIMDJSON_ICELAKE_BITMANIPULATION_H
#define SIMDJSON_ICELAKE_BITMANIPULATION_H
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
#include "simdjson/icelake/base.h"
#include "simdjson/icelake/intrinsics.h"
#endif // SIMDJSON_CONDITIONAL_INCLUDE
namespace simdjson {
namespace icelake {
namespace {
// We sometimes call trailing_zero on inputs that are zero,
// but the algorithms do not end up using the returned value.
// Sadly, sanitizers are not smart enough to figure it out.
SIMDJSON_NO_SANITIZE_UNDEFINED
// This function can be used safely even if not all bytes have been
// initialized.
// See issue https://github.com/simdjson/simdjson/issues/1965
SIMDJSON_NO_SANITIZE_MEMORY
simdjson_inline int trailing_zeroes(uint64_t input_num) {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
return (int)_tzcnt_u64(input_num);
#else // SIMDJSON_REGULAR_VISUAL_STUDIO
////////
// You might expect the next line to be equivalent to
// return (int)_tzcnt_u64(input_num);
// but the generated code differs and might be less efficient?
////////
return __builtin_ctzll(input_num);
#endif // SIMDJSON_REGULAR_VISUAL_STUDIO
}
/* result might be undefined when input_num is zero */
simdjson_inline uint64_t clear_lowest_bit(uint64_t input_num) {
return _blsr_u64(input_num);
}
/* result might be undefined when input_num is zero */
simdjson_inline int leading_zeroes(uint64_t input_num) {
return int(_lzcnt_u64(input_num));
}
#if SIMDJSON_REGULAR_VISUAL_STUDIO
simdjson_inline unsigned __int64 count_ones(uint64_t input_num) {
// note: we do not support legacy 32-bit Windows
return __popcnt64(input_num);// Visual Studio wants two underscores
}
#else
simdjson_inline long long int count_ones(uint64_t input_num) {
return _popcnt64(input_num);
}
#endif
simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2,
uint64_t *result) {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
return _addcarry_u64(0, value1, value2,
reinterpret_cast<unsigned __int64 *>(result));
#else
return __builtin_uaddll_overflow(value1, value2,
reinterpret_cast<unsigned long long *>(result));
#endif
}
} // unnamed namespace
} // namespace icelake
} // namespace simdjson
#endif // SIMDJSON_ICELAKE_BITMANIPULATION_H
+3 -88
View File
@@ -8,99 +8,14 @@
namespace simdjson {
namespace icelake {
namespace bitmask {
simdjson_constinit uint64_t ALL = 0xFFFFFFFFFFFFFFFF;
simdjson_constinit uint64_t NONE = 0xFFFFFFFFFFFFFFFF;
simdjson_constinit uint64_t EVEN = 0x5555555555555555;
simdjson_constinit uint64_t ODD = 0xAAAAAAAAAAAAAAAA;
// We sometimes call trailing_zero on inputs that are zero,
// but the algorithms do not end up using the returned value.
// Sadly, sanitizers are not smart enough to figure it out.
SIMDJSON_NO_SANITIZE_UNDEFINED
// This function can be used safely even if not all bytes have been
// initialized.
// See issue https://github.com/simdjson/simdjson/issues/1965
SIMDJSON_NO_SANITIZE_MEMORY
simdjson_inline int trailing_zeroes(const uint64_t input_num) noexcept {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
return (int)_tzcnt_u64(input_num);
#else // SIMDJSON_REGULAR_VISUAL_STUDIO
////////
// You might expect the next line to be equivalent to
// return (int)_tzcnt_u64(input_num);
// but the generated code differs and might be less efficient?
////////
return __builtin_ctzll(input_num);
#endif // SIMDJSON_REGULAR_VISUAL_STUDIO
}
/* result might be undefined when input_num is zero */
simdjson_inline uint64_t clear_lowest_bit(const uint64_t input_num) noexcept {
return _blsr_u64(input_num);
}
/* result might be undefined when input_num is zero */
simdjson_inline int leading_zeroes(uint64_t input_num) noexcept {
return int(_lzcnt_u64(input_num));
}
#if SIMDJSON_REGULAR_VISUAL_STUDIO
simdjson_inline unsigned __int64 count_ones(uint64_t input_num) noexcept {
// note: we do not support legacy 32-bit Windows
return __popcnt64(input_num);// Visual Studio wants two underscores
}
#else
simdjson_inline long long int count_ones(const uint64_t input_num) noexcept {
return _popcnt64(input_num);
}
#endif
simdjson_inline uint64_t add_carry_out(const uint64_t value1, const uint64_t value2, bool& carry_out) noexcept {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
unsigned __int64 result;
carry_out = _addcarry_u64(0, value1, value2, &result);
return result;
#else
unsigned long long result;
carry_out = __builtin_uaddll_overflow(value1, value2, &result);
return result;
#endif
}
simdjson_inline uint64_t subtract_borrow(const uint64_t value1, const uint64_t value2, bool& borrow) noexcept {
// TODO only do this on GCC, not clang
// #if SIMDJSON_REGULAR_VISUAL_STUDIO
// unsigned __int64 result;
// borrow = _subborrow_u64(borrow, value1, value2, &result);
// return result;
// #else
unsigned long long result;
bool borrow1 = __builtin_usubll_overflow(value1, value2, &result);
borrow = borrow1 | __builtin_usubll_overflow(result, borrow, &result);
return result;
// #endif
}
simdjson_inline uint64_t subtract_borrow_out(const uint64_t value1, const int64_t value2, bool& borrow_out) noexcept {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
unsigned __int64 result;
borrow_out = _subborrow_u64(0, value1, value2, &result);
return result;
#else
unsigned long long result;
borrow_out = __builtin_usubll_overflow(value1, value2, &result); // 2 (one to set )
return result;
#endif
}
namespace {
//
// Perform a "cumulative bitwise xor," flipping bits each time a 1 is encountered.
//
// For example, prefix_xor(00100100) == 00011100
//
simdjson_inline uint64_t prefix_xor(const uint64_t bitmask) noexcept {
simdjson_inline uint64_t prefix_xor(const uint64_t bitmask) {
// There should be no such thing with a processor supporting avx2
// but not clmul.
__m128i all_ones = _mm_set1_epi8('\xFF');
@@ -108,7 +23,7 @@ simdjson_inline uint64_t prefix_xor(const uint64_t bitmask) noexcept {
return _mm_cvtsi128_si64(result);
}
} // namespace bitmask
} // unnamed namespace
} // namespace icelake
} // namespace simdjson
+49 -138
View File
@@ -4,7 +4,7 @@
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
#include "simdjson/icelake/base.h"
#include "simdjson/icelake/intrinsics.h"
#include "simdjson/icelake/bitmask.h"
#include "simdjson/icelake/bitmanipulation.h"
#include "simdjson/internal/simdprune_tables.h"
#endif // SIMDJSON_CONDITIONAL_INCLUDE
@@ -18,7 +18,7 @@
/**
* GCC 8 fails to provide _mm512_set_epi8. We roll our own.
*/
inline simd_t _mm512_set_epi8(uint8_t a0, uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5, uint8_t a6, uint8_t a7, uint8_t a8, uint8_t a9, uint8_t a10, uint8_t a11, uint8_t a12, uint8_t a13, uint8_t a14, uint8_t a15, uint8_t a16, uint8_t a17, uint8_t a18, uint8_t a19, uint8_t a20, uint8_t a21, uint8_t a22, uint8_t a23, uint8_t a24, uint8_t a25, uint8_t a26, uint8_t a27, uint8_t a28, uint8_t a29, uint8_t a30, uint8_t a31, uint8_t a32, uint8_t a33, uint8_t a34, uint8_t a35, uint8_t a36, uint8_t a37, uint8_t a38, uint8_t a39, uint8_t a40, uint8_t a41, uint8_t a42, uint8_t a43, uint8_t a44, uint8_t a45, uint8_t a46, uint8_t a47, uint8_t a48, uint8_t a49, uint8_t a50, uint8_t a51, uint8_t a52, uint8_t a53, uint8_t a54, uint8_t a55, uint8_t a56, uint8_t a57, uint8_t a58, uint8_t a59, uint8_t a60, uint8_t a61, uint8_t a62, uint8_t a63) {
inline __m512i _mm512_set_epi8(uint8_t a0, uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5, uint8_t a6, uint8_t a7, uint8_t a8, uint8_t a9, uint8_t a10, uint8_t a11, uint8_t a12, uint8_t a13, uint8_t a14, uint8_t a15, uint8_t a16, uint8_t a17, uint8_t a18, uint8_t a19, uint8_t a20, uint8_t a21, uint8_t a22, uint8_t a23, uint8_t a24, uint8_t a25, uint8_t a26, uint8_t a27, uint8_t a28, uint8_t a29, uint8_t a30, uint8_t a31, uint8_t a32, uint8_t a33, uint8_t a34, uint8_t a35, uint8_t a36, uint8_t a37, uint8_t a38, uint8_t a39, uint8_t a40, uint8_t a41, uint8_t a42, uint8_t a43, uint8_t a44, uint8_t a45, uint8_t a46, uint8_t a47, uint8_t a48, uint8_t a49, uint8_t a50, uint8_t a51, uint8_t a52, uint8_t a53, uint8_t a54, uint8_t a55, uint8_t a56, uint8_t a57, uint8_t a58, uint8_t a59, uint8_t a60, uint8_t a61, uint8_t a62, uint8_t a63) {
return _mm512_set_epi64(uint64_t(a7) + (uint64_t(a6) << 8) + (uint64_t(a5) << 16) + (uint64_t(a4) << 24) + (uint64_t(a3) << 32) + (uint64_t(a2) << 40) + (uint64_t(a1) << 48) + (uint64_t(a0) << 56),
uint64_t(a15) + (uint64_t(a14) << 8) + (uint64_t(a13) << 16) + (uint64_t(a12) << 24) + (uint64_t(a11) << 32) + (uint64_t(a10) << 40) + (uint64_t(a9) << 48) + (uint64_t(a8) << 56),
uint64_t(a23) + (uint64_t(a22) << 8) + (uint64_t(a21) << 16) + (uint64_t(a20) << 24) + (uint64_t(a19) << 32) + (uint64_t(a18) << 40) + (uint64_t(a17) << 48) + (uint64_t(a16) << 56),
@@ -34,23 +34,23 @@ inline simd_t _mm512_set_epi8(uint8_t a0, uint8_t a1, uint8_t a2, uint8_t a3, ui
namespace simdjson {
namespace icelake {
namespace {
namespace simd {
// Forward-declared so they can be used by splat and friends.
template<typename Child>
struct base {
using simd_t = __m512i;
simd_t value;
__m512i value;
// Zero constructor
simdjson_inline base() : value{simd_t()} {}
simdjson_inline base() : value{__m512i()} {}
// Conversion from SIMD register
simdjson_inline base(const simd_t _value) : value(_value) {}
simdjson_inline base(const __m512i _value) : value(_value) {}
// Conversion to SIMD register
simdjson_inline operator const simd_t&() const { return this->value; }
simdjson_inline operator simd_t&() { return this->value; }
simdjson_inline operator const __m512i&() const { return this->value; }
simdjson_inline operator __m512i&() { return this->value; }
// Bit operations
simdjson_inline Child operator|(const Child other) const { return _mm512_or_si512(*this, other); }
@@ -68,16 +68,17 @@ namespace simd {
template<typename T, typename Mask=simd8<bool>>
struct base8: base<simd8<T>> {
using typename base<simd8<T>>::simd_t;
static constexpr const int LANES = sizeof(simd_t);
using bitmask_t = uint64_t;
static_assert(sizeof(bitmask_t)*8 == LANES, "Bitmask type's bits must equal the simd type's bytes");
typedef uint32_t bitmask_t;
typedef uint64_t bitmask2_t;
simdjson_inline base8() : base<simd8<T>>() {}
simdjson_inline base8(const simd_t _value) : base<simd8<T>>(_value) {}
simdjson_inline base8(const __m512i _value) : base<simd8<T>>(_value) {}
simdjson_inline uint64_t eq(const simd8<T> rhs) const { return _mm512_cmpeq_epi8_mask(*this, rhs); }
friend simdjson_inline uint64_t operator==(const simd8<T> lhs, const simd8<T> rhs) { return lhs.eq(rhs); }
friend simdjson_really_inline uint64_t operator==(const simd8<T> lhs, const simd8<T> rhs) {
return _mm512_cmpeq_epi8_mask(lhs, rhs);
}
static const int SIZE = sizeof(base<T>::value);
template<int N=1>
simdjson_inline simd8<T> prev(const simd8<T> prev_chunk) const {
@@ -93,24 +94,19 @@ namespace simd {
static simdjson_inline simd8<bool> splat(bool _value) { return _mm512_set1_epi8(uint8_t(-(!!_value))); }
simdjson_inline simd8<bool>() : base8() {}
simdjson_inline simd8<bool>(const simd_t _value) : base8<bool>(_value) {}
simdjson_inline simd8<bool>(const __m512i _value) : base8<bool>(_value) {}
// Splat constructor
simdjson_inline simd8<bool>(bool _value) : base8<bool>(splat(_value)) {}
simdjson_inline bool any() const { return !!_mm512_test_epi8_mask (*this, *this); }
simdjson_inline simd8<bool> operator~() const { return *this ^ true; }
simdjson_inline uint64_t to_bitmask() const noexcept { return _mm512_movepi8_mask(*this); }
};
template<typename T>
struct base8_numeric: base8<T> {
using typename base8<T>::simd_t;
using base8<T>::LANES;
static simdjson_inline simd8<T> splat(T _value) { return _mm512_set1_epi8(_value); }
static simdjson_inline simd8<T> zero() { return _mm512_setzero_si512(); }
static simdjson_inline simd8<T> load(const T values[64]) {
return _mm512_loadu_si512(reinterpret_cast<const simd_t *>(values));
return _mm512_loadu_si512(reinterpret_cast<const __m512i *>(values));
}
// Repeat 16 values as many times as necessary (usually for lookup tables)
static simdjson_inline simd8<T> repeat_16(
@@ -130,10 +126,10 @@ namespace simd {
}
simdjson_inline base8_numeric() : base8<T>() {}
simdjson_inline base8_numeric(const simd_t _value) : base8<T>(_value) {}
simdjson_inline base8_numeric(const __m512i _value) : base8<T>(_value) {}
// Store to array
simdjson_inline void store(T dst[64]) const { return _mm512_storeu_si512(reinterpret_cast<simd_t *>(dst), *this); }
simdjson_inline void store(T dst[64]) const { return _mm512_storeu_si512(reinterpret_cast<__m512i *>(dst), *this); }
// Addition/subtraction are the same for signed and unsigned
simdjson_inline simd8<T> operator+(const simd8<T> other) const { return _mm512_add_epi8(*this, other); }
@@ -145,18 +141,14 @@ namespace simd {
simdjson_inline simd8<T> operator~() const { return *this ^ 0xFFu; }
// Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values)
simdjson_inline simd8<T> lookup_16(const simd8<T>& lookup_table) const {
template<typename L>
simdjson_inline simd8<L> lookup_16(simd8<L> lookup_table) const {
return _mm512_shuffle_epi8(lookup_table, *this);
}
// Perform a lookup based on the lower 4 bits of each lane. (Platform-dependent behavior for
// non-ASCII values--may look up the lower 4 bits on some platforms, and return 0 on others.)
simdjson_inline simd8<T> lookup_low_nibble_ascii(const simd8<T>& lookup_table) const {
return lookup_16(lookup_table);
}
// Copies to 'output" all bytes corresponding to a 0 in the mask (interpreted as a bitset).
// Passing a 0 value for mask would be equivalent to writing out every byte to output.
// Only the first 32 - bitmask::count_ones(mask) bytes of the result are significant but 32 bytes
// Only the first 32 - count_ones(mask) bytes of the result are significant but 32 bytes
// get written.
// Design consideration: it seems like a function with the
// signature simd8<L> compress(uint32_t mask) would be
@@ -165,13 +157,27 @@ namespace simd {
simdjson_inline void compress(uint64_t mask, L * output) const {
_mm512_mask_compressstoreu_epi8 (output,~mask,*this);
}
template<typename L>
simdjson_inline simd8<L> lookup_16(
L replace0, L replace1, L replace2, L replace3,
L replace4, L replace5, L replace6, L replace7,
L replace8, L replace9, L replace10, L replace11,
L replace12, L replace13, L replace14, L replace15) const {
return lookup_16(simd8<L>::repeat_16(
replace0, replace1, replace2, replace3,
replace4, replace5, replace6, replace7,
replace8, replace9, replace10, replace11,
replace12, replace13, replace14, replace15
));
}
};
// Signed bytes
template<>
struct simd8<int8_t> : base8_numeric<int8_t> {
simdjson_inline simd8() : base8_numeric<int8_t>() {}
simdjson_inline simd8(const simd_t _value) : base8_numeric<int8_t>(_value) {}
simdjson_inline simd8(const __m512i _value) : base8_numeric<int8_t>(_value) {}
// Splat constructor
simdjson_inline simd8(int8_t _value) : simd8(splat(_value)) {}
// Array constructor
@@ -226,7 +232,7 @@ namespace simd {
template<>
struct simd8<uint8_t>: base8_numeric<uint8_t> {
simdjson_inline simd8() : base8_numeric<uint8_t>() {}
simdjson_inline simd8(const simd_t _value) : base8_numeric<uint8_t>(_value) {}
simdjson_inline simd8(const __m512i _value) : base8_numeric<uint8_t>(_value) {}
// Splat constructor
simdjson_inline simd8(uint8_t _value) : simd8(splat(_value)) {}
// Array constructor
@@ -321,16 +327,10 @@ namespace simd {
simdjson_inline simd8x64(const simd8<T> chunk0, const simd8<T> chunk1) : chunks{chunk0, chunk1} {}
simdjson_inline simd8x64(const simd8<T> chunk0) : chunks{chunk0} {}
simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8<T>::load(ptr)} {}
simdjson_inline simd8x64(simd8x64<T>&& o) noexcept = default;
simdjson_inline simd8x64<T>& operator=(simd8x64<T>&& other) noexcept = default;
simdjson_inline uint64_t to_bitmask() const noexcept {
return this->chunks[0].to_bitmask();
}
simdjson_inline uint64_t compress(uint64_t mask, T * output) const {
this->chunks[0].compress(mask, output);
return 64 - bitmask::count_ones(mask);
return 64 - count_ones(mask);
}
simdjson_inline void store(T ptr[64]) const {
@@ -341,120 +341,31 @@ namespace simd {
return this->chunks[0];
}
simdjson_inline simd8x64<T> bit_or(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return simd8x64<T>(
this->chunks[0] | mask
);
}
simdjson_inline uint64_t eq(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return this->chunks[0] == mask;
}
simdjson_inline uint64_t eq(const simd8x64<T> &other) const {
simdjson_inline uint64_t eq(const simd8x64<uint8_t> &other) const {
return this->chunks[0] == other.chunks[0];
}
simdjson_inline simd8x64<T> lookup_16(const simd8<T>& lookup_table) const {
return { this->chunks[0].lookup_16(lookup_table) };
}
simdjson_inline simd8x64<T> lookup_low_nibble_ascii(const simd8<T>& lookup_table) const {
return {
this->chunks[0].lookup_low_nibble_ascii(lookup_table)
};
}
simdjson_inline uint64_t lteq(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return this->chunks[0] <= mask;
}
simdjson_inline simd8x64<T> operator&(const simd8x64<T>& other) const {
return {
this->chunks[0] & other.chunks[0]
};
}
simdjson_inline simd8x64<T> operator&(const simd8<T>& other) const {
return {
this->chunks[0] & other
};
}
simdjson_inline simd8x64<T> operator|(const simd8x64<T>& other) const {
return {
this->chunks[0] | other.chunks[0]
};
}
simdjson_inline simd8x64<T> operator|(const simd8<T>& other) const {
return {
this->chunks[0] | other
};
}
simdjson_inline simd8x64<T> operator^(const simd8x64<T>& other) const {
return {
this->chunks[0] ^ other.chunks[0]
};
}
simdjson_inline simd8x64<T> operator^(const simd8<T>& other) const {
return {
this->chunks[0] ^ other
};
}
simdjson_inline simd8x64<T> bit_andnot(const simd8x64<T>& other) const {
return {
this->chunks[0].bit_andnot(other.chunks[0])
};
}
simdjson_inline simd8x64<T> bit_andnot(const simd8<T>& other) const {
return {
this->chunks[0].bit_andnot(other)
};
}
template <int N>
simdjson_inline simd8x64<T> shr() const noexcept {
return {
this->chunks[0].template shr<N>()
};
}
template <int N>
simdjson_inline simd8x64<T> shl() const noexcept {
return {
this->chunks[0].template shl<N>()
};
}
simdjson_inline simd8x64<bool> any_bits_set(const simd8<T>& bits) const {
return {
this->chunks[0].any_bits_set(bits)
};
}
simdjson_inline simd8x64<bool> any_bits_set(const simd8x64<T>& bits) const {
return {
this->chunks[0].any_bits_set(bits.chunks[0])
};
}
simdjson_inline simd8x64<bool> no_bits_set(const simd8<T>& bits) const {
return {
this->chunks[0].no_bits_set(bits)
};
}
simdjson_inline simd8x64<bool> no_bits_set(const simd8x64<T>& bits) const {
return {
this->chunks[0].no_bits_set(bits.chunks[0])
};
}
}; // struct simd8x64<T>
} // namespace simd
} // unnamed namespace
} // namespace icelake
} // namespace simdjson
@@ -4,7 +4,7 @@
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
#include "simdjson/icelake/base.h"
#include "simdjson/icelake/simd.h"
#include "simdjson/icelake/bitmask.h"
#include "simdjson/icelake/bitmanipulation.h"
#endif // SIMDJSON_CONDITIONAL_INCLUDE
namespace simdjson {
@@ -21,8 +21,8 @@ public:
simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; }
simdjson_inline bool has_backslash() { return ((quote_bits - 1) & bs_bits) != 0; }
simdjson_inline int quote_index() { return bitmask::trailing_zeroes(quote_bits); }
simdjson_inline int backslash_index() { return bitmask::trailing_zeroes(bs_bits); }
simdjson_inline int quote_index() { return trailing_zeroes(quote_bits); }
simdjson_inline int backslash_index() { return trailing_zeroes(bs_bits); }
uint64_t bs_bits;
uint64_t quote_bits;
+2 -5
View File
@@ -58,11 +58,8 @@
#if SIMDJSON_IS_32BITS
#ifndef SIMDJSON_NO_PORTABILITY_WARNING
#pragma message("The simdjson library is designed \
for 64-bit processors and it seems that you are not \
compiling for a known 64-bit platform. All fast kernels \
will be disabled and performance may be poor. Please \
use a 64-bit target such as x64, 64-bit ARM or 64-bit PPC.")
// In the future, we should allow programmers
// to get warning.
#endif // SIMDJSON_NO_PORTABILITY_WARNING
#endif // SIMDJSON_IS_32BITS
-1
View File
@@ -3,7 +3,6 @@
#include "simdjson/ppc64/begin.h"
#include "simdjson/generic/amalgamated.h"
#include "simdjson/generic/lookup_table.h"
#include "simdjson/ppc64/end.h"
#endif // SIMDJSON_PPC64_H
+2 -4
View File
@@ -13,14 +13,12 @@ namespace ppc64 {
class implementation;
namespace {
namespace simd {
template <typename T> struct simd8;
template <> struct simd8<bool>;
template <> struct simd8<uint8_t>;
template <typename T> struct simd8x64;
} // namespace simd
} // unnamed namespace
} // namespace ppc64
} // namespace simdjson
+1 -1
View File
@@ -1,7 +1,7 @@
#define SIMDJSON_IMPLEMENTATION ppc64
#include "simdjson/ppc64/base.h"
#include "simdjson/ppc64/intrinsics.h"
#include "simdjson/ppc64/bitmask.h"
#include "simdjson/ppc64/bitmanipulation.h"
#include "simdjson/ppc64/bitmask.h"
#include "simdjson/ppc64/numberparsing_defs.h"
#include "simdjson/ppc64/simd.h"
+78
View File
@@ -0,0 +1,78 @@
#ifndef SIMDJSON_PPC64_BITMANIPULATION_H
#define SIMDJSON_PPC64_BITMANIPULATION_H
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
#include "simdjson/ppc64/base.h"
#endif // SIMDJSON_CONDITIONAL_INCLUDE
namespace simdjson {
namespace ppc64 {
namespace {
// We sometimes call trailing_zero on inputs that are zero,
// but the algorithms do not end up using the returned value.
// Sadly, sanitizers are not smart enough to figure it out.
SIMDJSON_NO_SANITIZE_UNDEFINED
// This function can be used safely even if not all bytes have been
// initialized.
// See issue https://github.com/simdjson/simdjson/issues/1965
SIMDJSON_NO_SANITIZE_MEMORY
simdjson_inline int trailing_zeroes(uint64_t input_num) {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
unsigned long ret;
// Search the mask data from least significant bit (LSB)
// to the most significant bit (MSB) for a set bit (1).
_BitScanForward64(&ret, input_num);
return (int)ret;
#else // SIMDJSON_REGULAR_VISUAL_STUDIO
return __builtin_ctzll(input_num);
#endif // SIMDJSON_REGULAR_VISUAL_STUDIO
}
/* result might be undefined when input_num is zero */
simdjson_inline uint64_t clear_lowest_bit(uint64_t input_num) {
return input_num & (input_num - 1);
}
/* result might be undefined when input_num is zero */
simdjson_inline int leading_zeroes(uint64_t input_num) {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
unsigned long leading_zero = 0;
// Search the mask data from most significant bit (MSB)
// to least significant bit (LSB) for a set bit (1).
if (_BitScanReverse64(&leading_zero, input_num))
return (int)(63 - leading_zero);
else
return 64;
#else
return __builtin_clzll(input_num);
#endif // SIMDJSON_REGULAR_VISUAL_STUDIO
}
#if SIMDJSON_REGULAR_VISUAL_STUDIO
simdjson_inline int count_ones(uint64_t input_num) {
// note: we do not support legacy 32-bit Windows in this kernel
return __popcnt64(input_num); // Visual Studio wants two underscores
}
#else
simdjson_inline int count_ones(uint64_t input_num) {
return __builtin_popcountll(input_num);
}
#endif
simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2,
uint64_t *result) {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
*result = value1 + value2;
return *result < value1;
#else
return __builtin_uaddll_overflow(value1, value2,
reinterpret_cast<unsigned long long *>(result));
#endif
}
} // unnamed namespace
} // namespace ppc64
} // namespace simdjson
#endif // SIMDJSON_PPC64_BITMANIPULATION_H
+1 -95
View File
@@ -3,105 +3,11 @@
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
#include "simdjson/ppc64/base.h"
#include "simdjson/ppc64/intrinsics.h"
#endif // SIMDJSON_CONDITIONAL_INCLUDE
namespace simdjson {
namespace ppc64 {
namespace bitmask {
simdjson_constinit uint64_t ALL = 0xFFFFFFFFFFFFFFFF;
simdjson_constinit uint64_t NONE = 0xFFFFFFFFFFFFFFFF;
simdjson_constinit uint64_t EVEN = 0x5555555555555555;
simdjson_constinit uint64_t ODD = 0xAAAAAAAAAAAAAAAA;
// We sometimes call trailing_zero on inputs that are zero,
// but the algorithms do not end up using the returned value.
// Sadly, sanitizers are not smart enough to figure it out.
SIMDJSON_NO_SANITIZE_UNDEFINED
// This function can be used safely even if not all bytes have been
// initialized.
// See issue https://github.com/simdjson/simdjson/issues/1965
SIMDJSON_NO_SANITIZE_MEMORY
simdjson_inline int trailing_zeroes(uint64_t input_num) {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
unsigned long ret;
// Search the mask data from least significant bit (LSB)
// to the most significant bit (MSB) for a set bit (1).
_BitScanForward64(&ret, input_num);
return (int)ret;
#else // SIMDJSON_REGULAR_VISUAL_STUDIO
return __builtin_ctzll(input_num);
#endif // SIMDJSON_REGULAR_VISUAL_STUDIO
}
/* result might be undefined when input_num is zero */
simdjson_inline uint64_t clear_lowest_bit(uint64_t input_num) {
return input_num & (input_num - 1);
}
/* result might be undefined when input_num is zero */
simdjson_inline int leading_zeroes(uint64_t input_num) {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
unsigned long leading_zero = 0;
// Search the mask data from most significant bit (MSB)
// to least significant bit (LSB) for a set bit (1).
if (_BitScanReverse64(&leading_zero, input_num))
return (int)(63 - leading_zero);
else
return 64;
#else
return __builtin_clzll(input_num);
#endif // SIMDJSON_REGULAR_VISUAL_STUDIO
}
#if SIMDJSON_REGULAR_VISUAL_STUDIO
simdjson_inline int count_ones(uint64_t input_num) {
// note: we do not support legacy 32-bit Windows in this kernel
return __popcnt64(input_num); // Visual Studio wants two underscores
}
#else
simdjson_inline int count_ones(uint64_t input_num) {
return __builtin_popcountll(input_num);
}
#endif
simdjson_inline uint64_t add_carry_out(uint64_t value1, uint64_t value2, bool& carry_out) {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
uint64_t result = value1 + value2;
carry_out = result < value1;
return result;
#else
unsigned long long result;
carry_out = __builtin_uaddll_overflow(value1, value2, &result);
return result;
#endif
}
simdjson_inline uint64_t subtract_borrow(const uint64_t value1, const uint64_t value2, bool& borrow) noexcept {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
uint64_t result = value1 - value2 - borrow;
borrow_out = result > value1;
return result;
#else
unsigned long long result;
bool borrow1 = __builtin_usubll_overflow(value1, value2, &result);
borrow = borrow1 | __builtin_usubll_overflow(result, borrow, &result);
return result;
#endif
}
simdjson_inline uint64_t subtract_borrow_out(uint64_t value1, uint64_t value2, bool& borrow_out) noexcept {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
uint64_t result = value1 - value2;
borrow_out = result > value1;
return result;
#else
unsigned long long result;
borrow_out = __builtin_usubll_overflow(value1, value2, &result);
return result;
#endif
}
namespace {
//
// Perform a "cumulative bitwise xor," flipping bits each time a 1 is
+92 -242
View File
@@ -3,7 +3,7 @@
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
#include "simdjson/ppc64/base.h"
#include "simdjson/ppc64/bitmask.h"
#include "simdjson/ppc64/bitmanipulation.h"
#include "simdjson/internal/simdprune_tables.h"
#endif // SIMDJSON_CONDITIONAL_INCLUDE
@@ -11,45 +11,38 @@
namespace simdjson {
namespace ppc64 {
namespace {
namespace simd {
#if !(SIMDJSON_IS_PPC64 && SIMDJSON_IS_PPC64_VMX) && !defined(SIMDJSON_CONDITIONAL_INCLUDE)
// Make errors a bit more manageable when editing on non-ARM
struct __m128u { uint8_t buf[16]; };
using __m128i = __m128u;
#else
using __m128u = __vector unsigned char;
using __m128i = __vector signed char;
#endif
using __m128i = __vector unsigned char;
template <typename Child> struct base {
using simd_t = __m128u;
simd_t value;
__m128i value;
// Zero constructor
simdjson_inline base() : value{simd_t()} {}
simdjson_inline base() : value{__m128i()} {}
// Conversion from SIMD register
simdjson_inline base(const simd_t _value) : value(_value) {}
simdjson_inline base(const __m128i _value) : value(_value) {}
// Conversion to SIMD register
simdjson_inline operator const simd_t &() const {
simdjson_inline operator const __m128i &() const {
return this->value;
}
simdjson_inline operator simd_t &() { return this->value; }
simdjson_inline operator __m128i &() { return this->value; }
// Bit operations
simdjson_inline Child operator|(const Child other) const {
return vec_or(this->value, (simd_t)other);
return vec_or(this->value, (__m128i)other);
}
simdjson_inline Child operator&(const Child other) const {
return vec_and(this->value, (simd_t)other);
return vec_and(this->value, (__m128i)other);
}
simdjson_inline Child operator^(const Child other) const {
return vec_xor(this->value, (simd_t)other);
return vec_xor(this->value, (__m128i)other);
}
simdjson_inline Child bit_andnot(const Child other) const {
return vec_andc(this->value, (simd_t)other);
return vec_andc(this->value, (__m128i)other);
}
simdjson_inline Child &operator|=(const Child other) {
auto this_cast = static_cast<Child*>(this);
@@ -70,27 +63,28 @@ template <typename Child> struct base {
template <typename T, typename Mask = simd8<bool>>
struct base8 : base<simd8<T>> {
using typename base<simd8<T>>::simd_t;
static constexpr const int LANES = sizeof(simd_t);
using bitmask_t = uint16_t;
static_assert(sizeof(bitmask_t)*8 == LANES, "Bitmask type's bits must equal the simd type's bytes");
typedef uint16_t bitmask_t;
typedef uint32_t bitmask2_t;
simdjson_inline base8() : base<simd8<T>>() {}
simdjson_inline base8(const simd_t _value) : base<simd8<T>>(_value) {}
simdjson_inline base8(const __m128i _value) : base<simd8<T>>(_value) {}
simdjson_inline Mask eq(const simd8<T> rhs) const { return (simd_t)vec_cmpeq(this->value, (simd_t)rhs); }
friend simdjson_inline Mask operator==(const simd8<T> lhs, const simd8<T> rhs) { return lhs.eq(rhs); }
friend simdjson_inline Mask operator==(const simd8<T> lhs, const simd8<T> rhs) {
return (__m128i)vec_cmpeq(lhs.value, (__m128i)rhs);
}
static const int SIZE = sizeof(base<simd8<T>>::value);
template <int N = 1>
simdjson_inline simd8<T> prev(const simd8<T>& prev_chunk) const {
simd_t chunk = this->value;
simdjson_inline simd8<T> prev(simd8<T> prev_chunk) const {
__m128i chunk = this->value;
#ifdef __LITTLE_ENDIAN__
chunk = (simd_t)vec_reve(this->value);
prev_chunk = (simd_t)vec_reve((simd_t)prev_chunk);
chunk = (__m128i)vec_reve(this->value);
prev_chunk = (__m128i)vec_reve((__m128i)prev_chunk);
#endif
chunk = (simd_t)vec_sld((simd_t)prev_chunk, (simd_t)chunk, 16 - N);
chunk = (__m128i)vec_sld((__m128i)prev_chunk, (__m128i)chunk, 16 - N);
#ifdef __LITTLE_ENDIAN__
chunk = (simd_t)vec_reve((simd_t)chunk);
chunk = (__m128i)vec_reve((__m128i)chunk);
#endif
return chunk;
}
@@ -98,14 +92,12 @@ struct base8 : base<simd8<T>> {
// SIMD byte mask type (returned by things like eq and gt)
template <> struct simd8<bool> : base8<bool> {
using typename base8<bool>::simd_t;
static simdjson_inline simd8<bool> splat(bool _value) {
return (simd_t)vec_splats((unsigned char)(-(!!_value)));
return (__m128i)vec_splats((unsigned char)(-(!!_value)));
}
simdjson_inline simd8<bool>() : base8<bool>() {}
simdjson_inline simd8<bool>(const simd_t _value)
simdjson_inline simd8<bool>(const __m128i _value)
: base8<bool>(_value) {}
// Splat constructor
simdjson_inline simd8<bool>(bool _value)
@@ -113,11 +105,11 @@ template <> struct simd8<bool> : base8<bool> {
simdjson_inline int to_bitmask() const {
__vector unsigned long long result;
const simd_t perm_mask = {0x78, 0x70, 0x68, 0x60, 0x58, 0x50, 0x48, 0x40,
const __m128i perm_mask = {0x78, 0x70, 0x68, 0x60, 0x58, 0x50, 0x48, 0x40,
0x38, 0x30, 0x28, 0x20, 0x18, 0x10, 0x08, 0x00};
result = ((__vector unsigned long long)vec_vbpermq((simd_t)this->value,
(simd_t)perm_mask));
result = ((__vector unsigned long long)vec_vbpermq((__m128i)this->value,
(__m128i)perm_mask));
#ifdef __LITTLE_ENDIAN__
return static_cast<int>(result[1]);
#else
@@ -125,24 +117,21 @@ template <> struct simd8<bool> : base8<bool> {
#endif
}
simdjson_inline bool any() const {
return !vec_all_eq(this->value, (simd_t)vec_splats(0));
return !vec_all_eq(this->value, (__m128i)vec_splats(0));
}
simdjson_inline simd8<bool> operator~() const {
return this->value ^ (simd_t)splat(true);
return this->value ^ (__m128i)splat(true);
}
};
template <typename T> struct base8_numeric : base8<T> {
using typename base8<T>::simd_t;
using base8<T>::LANES;
static simdjson_inline simd8<T> splat(T value) {
(void)value;
return (simd_t)vec_splats(value);
return (__m128i)vec_splats(value);
}
static simdjson_inline simd8<T> zero() { return splat(0); }
static simdjson_inline simd8<T> load(const T values[16]) {
return (simd_t)(vec_vsx_ld(0, reinterpret_cast<const uint8_t *>(values)));
return (__m128i)(vec_vsx_ld(0, reinterpret_cast<const uint8_t *>(values)));
}
// Repeat 16 values as many times as necessary (usually for lookup tables)
static simdjson_inline simd8<T> repeat_16(T v0, T v1, T v2, T v3, T v4,
@@ -154,12 +143,12 @@ template <typename T> struct base8_numeric : base8<T> {
}
simdjson_inline base8_numeric() : base8<T>() {}
simdjson_inline base8_numeric(const simd_t _value)
simdjson_inline base8_numeric(const __m128i _value)
: base8<T>(_value) {}
// Store to array
simdjson_inline void store(T dst[16]) const {
vec_vsx_st(this->value, 0, reinterpret_cast<simd_t *>(dst));
vec_vsx_st(this->value, 0, reinterpret_cast<__m128i *>(dst));
}
// Override to distinguish from bool version
@@ -167,10 +156,10 @@ template <typename T> struct base8_numeric : base8<T> {
// Addition/subtraction are the same for signed and unsigned
simdjson_inline simd8<T> operator+(const simd8<T> other) const {
return (simd_t)((simd_t)this->value + (simd_t)other);
return (__m128i)((__m128i)this->value + (__m128i)other);
}
simdjson_inline simd8<T> operator-(const simd8<T> other) const {
return (simd_t)((simd_t)this->value - (simd_t)other);
return (__m128i)((__m128i)this->value - (__m128i)other);
}
simdjson_inline simd8<T> &operator+=(const simd8<T> other) {
*this = *this + other;
@@ -183,18 +172,14 @@ template <typename T> struct base8_numeric : base8<T> {
// Perform a lookup assuming the value is between 0 and 16 (undefined behavior
// for out of range values)
simdjson_inline simd8<T> lookup_16(const simd8<T>& lookup_table) const {
return (simd_t)vec_perm((simd_t)lookup_table, (simd_t)lookup_table, this->value);
}
// Perform a lookup based on the lower 4 bits of each lane. (Platform-dependent behavior for
// non-ASCII values--may look up the lower 4 bits on some platforms, and return 0 on others.)
simdjson_inline simd8<T> lookup_low_nibble_ascii(const simd8<T>& lookup_table) const {
return lookup_16(lookup_table);
template <typename L>
simdjson_inline simd8<L> lookup_16(simd8<L> lookup_table) const {
return (__m128i)vec_perm((__m128i)lookup_table, (__m128i)lookup_table, this->value);
}
// Copies to 'output" all bytes corresponding to a 0 in the mask (interpreted
// as a bitset). Passing a 0 value for mask would be equivalent to writing out
// every byte to output. Only the first 16 - bitmask::count_ones(mask) bytes of the
// every byte to output. Only the first 16 - count_ones(mask) bytes of the
// result are significant but 16 bytes get written. Design consideration: it
// seems like a function with the signature simd8<L> compress(uint32_t mask)
// would be sensible, but the AVX ISA makes this kind of approach difficult.
@@ -211,19 +196,19 @@ template <typename T> struct base8_numeric : base8<T> {
// thintable_epi8[mask2] into a 128-bit register, using only
// two instructions on most compilers.
#ifdef __LITTLE_ENDIAN__
simd_t shufmask = (simd_t)(__vector unsigned long long){
__m128i shufmask = (__m128i)(__vector unsigned long long){
thintable_epi8[mask1], thintable_epi8[mask2]};
#else
simd_t shufmask = (simd_t)(__vector unsigned long long){
__m128i shufmask = (__m128i)(__vector unsigned long long){
thintable_epi8[mask2], thintable_epi8[mask1]};
shufmask = (simd_t)vec_reve((simd_t)shufmask);
shufmask = (__m128i)vec_reve((__m128i)shufmask);
#endif
// we increment by 0x08 the second half of the mask
shufmask = ((simd_t)shufmask) +
((simd_t)(__vector int){0, 0, 0x08080808, 0x08080808});
shufmask = ((__m128i)shufmask) +
((__m128i)(__vector int){0, 0, 0x08080808, 0x08080808});
// this is the version "nearly pruned"
simd_t pruned = vec_perm(this->value, this->value, shufmask);
__m128i pruned = vec_perm(this->value, this->value, shufmask);
// we still need to put the two halves together.
// we compute the popcount of the first half:
int pop1 = BitsSetTable256mul2[mask1];
@@ -231,17 +216,29 @@ template <typename T> struct base8_numeric : base8<T> {
// only the first pop1 bytes from the first 8 bytes, and then
// it fills in with the bytes from the second 8 bytes + some filling
// at the end.
simd_t compactmask =
__m128i compactmask =
vec_vsx_ld(0, reinterpret_cast<const uint8_t *>(pshufb_combine_table + pop1 * 8));
simd_t answer = vec_perm(pruned, (simd_t)vec_splats(0), compactmask);
vec_vsx_st(answer, 0, reinterpret_cast<simd_t *>(output));
__m128i answer = vec_perm(pruned, (__m128i)vec_splats(0), compactmask);
vec_vsx_st(answer, 0, reinterpret_cast<__m128i *>(output));
}
template <typename L>
simdjson_inline simd8<L>
lookup_16(L replace0, L replace1, L replace2, L replace3, L replace4,
L replace5, L replace6, L replace7, L replace8, L replace9,
L replace10, L replace11, L replace12, L replace13, L replace14,
L replace15) const {
return lookup_16(simd8<L>::repeat_16(
replace0, replace1, replace2, replace3, replace4, replace5, replace6,
replace7, replace8, replace9, replace10, replace11, replace12,
replace13, replace14, replace15));
}
};
// Signed bytes
template <> struct simd8<int8_t> : base8_numeric<int8_t> {
simdjson_inline simd8() : base8_numeric<int8_t>() {}
simdjson_inline simd8(const simd_t _value)
simdjson_inline simd8(const __m128i _value)
: base8_numeric<int8_t>(_value) {}
// Splat constructor
simdjson_inline simd8(int8_t _value) : simd8(splat(_value)) {}
@@ -252,7 +249,7 @@ template <> struct simd8<int8_t> : base8_numeric<int8_t> {
int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11,
int8_t v12, int8_t v13, int8_t v14, int8_t v15)
: simd8((simd_t)(__m128i){v0, v1, v2, v3, v4, v5, v6, v7,
: simd8((__m128i)(__vector signed char){v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10, v11, v12, v13, v14,
v15}) {}
// Repeat 16 values as many times as necessary (usually for lookup tables)
@@ -267,30 +264,30 @@ template <> struct simd8<int8_t> : base8_numeric<int8_t> {
// Order-sensitive comparisons
simdjson_inline simd8<int8_t>
max_val(const simd8<int8_t> other) const {
return (simd_t)vec_max((__m128i)this->value,
(__m128i)(simd_t)other);
return (__m128i)vec_max((__vector signed char)this->value,
(__vector signed char)(__m128i)other);
}
simdjson_inline simd8<int8_t>
min_val(const simd8<int8_t> other) const {
return (simd_t)vec_min((__m128i)this->value,
(__m128i)(simd_t)other);
return (__m128i)vec_min((__vector signed char)this->value,
(__vector signed char)(__m128i)other);
}
simdjson_inline simd8<bool>
operator>(const simd8<int8_t> other) const {
return (simd_t)vec_cmpgt((__m128i)this->value,
(__m128i)(simd_t)other);
return (__m128i)vec_cmpgt((__vector signed char)this->value,
(__vector signed char)(__m128i)other);
}
simdjson_inline simd8<bool>
operator<(const simd8<int8_t> other) const {
return (simd_t)vec_cmplt((__m128i)this->value,
(__m128i)(simd_t)other);
return (__m128i)vec_cmplt((__vector signed char)this->value,
(__vector signed char)(__m128i)other);
}
};
// Unsigned bytes
template <> struct simd8<uint8_t> : base8_numeric<uint8_t> {
simdjson_inline simd8() : base8_numeric<uint8_t>() {}
simdjson_inline simd8(const simd_t _value)
simdjson_inline simd8(const __m128i _value)
: base8_numeric<uint8_t>(_value) {}
// Splat constructor
simdjson_inline simd8(uint8_t _value) : simd8(splat(_value)) {}
@@ -301,7 +298,7 @@ template <> struct simd8<uint8_t> : base8_numeric<uint8_t> {
simd8(uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5,
uint8_t v6, uint8_t v7, uint8_t v8, uint8_t v9, uint8_t v10,
uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15)
: simd8((simd_t){v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12,
: simd8((__m128i){v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12,
v13, v14, v15}) {}
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<uint8_t>
@@ -316,21 +313,21 @@ template <> struct simd8<uint8_t> : base8_numeric<uint8_t> {
// Saturated math
simdjson_inline simd8<uint8_t>
saturating_add(const simd8<uint8_t> other) const {
return (simd_t)vec_adds(this->value, (simd_t)other);
return (__m128i)vec_adds(this->value, (__m128i)other);
}
simdjson_inline simd8<uint8_t>
saturating_sub(const simd8<uint8_t> other) const {
return (simd_t)vec_subs(this->value, (simd_t)other);
return (__m128i)vec_subs(this->value, (__m128i)other);
}
// Order-specific operations
simdjson_inline simd8<uint8_t>
max_val(const simd8<uint8_t> other) const {
return (simd_t)vec_max(this->value, (simd_t)other);
return (__m128i)vec_max(this->value, (__m128i)other);
}
simdjson_inline simd8<uint8_t>
min_val(const simd8<uint8_t> other) const {
return (simd_t)vec_min(this->value, (simd_t)other);
return (__m128i)vec_min(this->value, (__m128i)other);
}
// Same as >, but only guarantees true is nonzero (< guarantees true = -1)
simdjson_inline simd8<uint8_t>
@@ -361,7 +358,7 @@ template <> struct simd8<uint8_t> : base8_numeric<uint8_t> {
// Bit-specific operations
simdjson_inline simd8<bool> bits_not_set() const {
return (simd_t)vec_cmpeq(this->value, (simd_t)vec_splats(uint8_t(0)));
return (__m128i)vec_cmpeq(this->value, (__m128i)vec_splats(uint8_t(0)));
}
simdjson_inline simd8<bool> bits_not_set(simd8<uint8_t> bits) const {
return (*this & bits).bits_not_set();
@@ -373,25 +370,25 @@ template <> struct simd8<uint8_t> : base8_numeric<uint8_t> {
return ~this->bits_not_set(bits);
}
simdjson_inline bool bits_not_set_anywhere() const {
return vec_all_eq(this->value, (simd_t)vec_splats(0));
return vec_all_eq(this->value, (__m128i)vec_splats(0));
}
simdjson_inline bool any_bits_set_anywhere() const {
return !bits_not_set_anywhere();
}
simdjson_inline bool bits_not_set_anywhere(simd8<uint8_t> bits) const {
return vec_all_eq(vec_and(this->value, (simd_t)bits),
(simd_t)vec_splats(0));
return vec_all_eq(vec_and(this->value, (__m128i)bits),
(__m128i)vec_splats(0));
}
simdjson_inline bool any_bits_set_anywhere(simd8<uint8_t> bits) const {
return !bits_not_set_anywhere(bits);
}
template <int N> simdjson_inline simd8<uint8_t> shr() const {
return simd8<uint8_t>(
(simd_t)vec_sr(this->value, (simd_t)vec_splat_u8(N)));
(__m128i)vec_sr(this->value, (__m128i)vec_splat_u8(N)));
}
template <int N> simdjson_inline simd8<uint8_t> shl() const {
return simd8<uint8_t>(
(simd_t)vec_sl(this->value, (simd_t)vec_splat_u8(N)));
(__m128i)vec_sl(this->value, (__m128i)vec_splat_u8(N)));
}
};
@@ -412,8 +409,6 @@ template <typename T> struct simd8x64 {
simdjson_inline simd8x64(const T ptr[64])
: chunks{simd8<T>::load(ptr), simd8<T>::load(ptr + 16),
simd8<T>::load(ptr + 32), simd8<T>::load(ptr + 48)} {}
simdjson_inline simd8x64(simd8x64<T>&& o) noexcept = default;
simdjson_inline simd8x64<T>& operator=(simd8x64<T>&& other) noexcept = default;
simdjson_inline void store(T ptr[64]) const {
this->chunks[0].store(ptr + sizeof(simd8<T>) * 0);
@@ -430,12 +425,12 @@ template <typename T> struct simd8x64 {
simdjson_inline uint64_t compress(uint64_t mask, T *output) const {
this->chunks[0].compress(uint16_t(mask), output);
this->chunks[1].compress(uint16_t(mask >> 16),
output + 16 - bitmask::count_ones(mask & 0xFFFF));
output + 16 - count_ones(mask & 0xFFFF));
this->chunks[2].compress(uint16_t(mask >> 32),
output + 32 - bitmask::count_ones(mask & 0xFFFFFFFF));
output + 32 - count_ones(mask & 0xFFFFFFFF));
this->chunks[3].compress(uint16_t(mask >> 48),
output + 48 - bitmask::count_ones(mask & 0xFFFFFFFFFFFF));
return 64 - bitmask::count_ones(mask);
output + 48 - count_ones(mask & 0xFFFFFFFFFFFF));
return 64 - count_ones(mask);
}
simdjson_inline uint64_t to_bitmask() const {
@@ -453,7 +448,7 @@ template <typename T> struct simd8x64 {
.to_bitmask();
}
simdjson_inline uint64_t eq(const simd8x64<T> &other) const {
simdjson_inline uint64_t eq(const simd8x64<uint8_t> &other) const {
return simd8x64<bool>(this->chunks[0] == other.chunks[0],
this->chunks[1] == other.chunks[1],
this->chunks[2] == other.chunks[2],
@@ -461,161 +456,16 @@ template <typename T> struct simd8x64 {
.to_bitmask();
}
simdjson_inline simd8x64<T> lookup_16(const simd8<T>& lookup_table) const {
return {
this->chunks[0].lookup_16(lookup_table),
this->chunks[1].lookup_16(lookup_table),
this->chunks[2].lookup_16(lookup_table),
this->chunks[3].lookup_16(lookup_table),
};
}
simdjson_inline simd8x64<T> lookup_low_nibble_ascii(const simd8<T>& lookup_table) const {
return {
this->chunks[0].lookup_low_nibble_ascii(lookup_table),
this->chunks[1].lookup_low_nibble_ascii(lookup_table),
this->chunks[2].lookup_low_nibble_ascii(lookup_table),
this->chunks[3].lookup_low_nibble_ascii(lookup_table)
};
}
simdjson_inline uint64_t lteq(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return simd8x64<bool>(this->chunks[0] <= mask, this->chunks[1] <= mask,
this->chunks[2] <= mask, this->chunks[3] <= mask)
.to_bitmask();
}
simdjson_inline simd8x64<T> operator&(const simd8x64<T>& other) const {
return {
this->chunks[0] & other.chunks[0],
this->chunks[1] & other.chunks[1],
this->chunks[2] & other.chunks[2],
this->chunks[3] & other.chunks[3]
};
}
simdjson_inline simd8x64<T> operator&(const simd8<T>& other) const {
return {
this->chunks[0] & other,
this->chunks[1] & other,
this->chunks[2] & other,
this->chunks[3] & other
};
}
simdjson_inline simd8x64<T> operator|(const simd8x64<T>& other) const {
return {
this->chunks[0] | other.chunks[0],
this->chunks[1] | other.chunks[1],
this->chunks[2] | other.chunks[2],
this->chunks[3] | other.chunks[3]
};
}
simdjson_inline simd8x64<T> operator|(const simd8<T>& other) const {
return {
this->chunks[0] | other,
this->chunks[1] | other,
this->chunks[2] | other,
this->chunks[3] | other
};
}
simdjson_inline simd8x64<T> operator^(const simd8x64<T>& other) const {
return {
this->chunks[0] ^ other.chunks[0],
this->chunks[1] ^ other.chunks[1],
this->chunks[2] ^ other.chunks[2],
this->chunks[3] ^ other.chunks[3]
};
}
simdjson_inline simd8x64<T> operator^(const simd8<T>& other) const {
return {
this->chunks[0] ^ other,
this->chunks[1] ^ other,
this->chunks[2] ^ other,
this->chunks[3] ^ other
};
}
simdjson_inline simd8x64<T> bit_andnot(const simd8x64<T>& other) const {
return {
this->chunks[0].bit_andnot(other.chunks[0]),
this->chunks[1].bit_andnot(other.chunks[1]),
this->chunks[2].bit_andnot(other.chunks[2]),
this->chunks[3].bit_andnot(other.chunks[3])
};
}
simdjson_inline simd8x64<T> bit_andnot(const simd8<T>& other) const {
return {
this->chunks[0].bit_andnot(other),
this->chunks[1].bit_andnot(other),
this->chunks[2].bit_andnot(other),
this->chunks[3].bit_andnot(other)
};
}
template <int N>
simdjson_inline simd8x64<T> shr() const noexcept {
return {
this->chunks[0].template shr<N>(),
this->chunks[1].template shr<N>(),
this->chunks[2].template shr<N>(),
this->chunks[3].template shr<N>()
};
}
template <int N>
simdjson_inline simd8x64<T> shl() const noexcept {
return {
this->chunks[0].template shl<N>(),
this->chunks[1].template shl<N>(),
this->chunks[2].template shl<N>(),
this->chunks[3].template shl<N>()
};
}
simdjson_inline simd8x64<bool> any_bits_set(const simd8<T>& bits) const {
return {
this->chunks[0].any_bits_set(bits),
this->chunks[1].any_bits_set(bits),
this->chunks[2].any_bits_set(bits),
this->chunks[3].any_bits_set(bits)
};
}
simdjson_inline simd8x64<bool> any_bits_set(const simd8x64<T>& bits) const {
return {
this->chunks[0].any_bits_set(bits.chunks[0]),
this->chunks[1].any_bits_set(bits.chunks[1]),
this->chunks[2].any_bits_set(bits.chunks[2]),
this->chunks[3].any_bits_set(bits.chunks[3])
};
}
simdjson_inline simd8x64<bool> no_bits_set(const simd8<T>& bits) const {
return {
this->chunks[0].no_bits_set(bits),
this->chunks[1].no_bits_set(bits),
this->chunks[2].no_bits_set(bits),
this->chunks[3].no_bits_set(bits)
};
}
simdjson_inline simd8x64<bool> no_bits_set(const simd8x64<T>& bits) const {
return {
this->chunks[0].no_bits_set(bits.chunks[0]),
this->chunks[1].no_bits_set(bits.chunks[1]),
this->chunks[2].no_bits_set(bits.chunks[2]),
this->chunks[3].no_bits_set(bits.chunks[3])
};
}
}; // struct simd8x64<T>
} // namespace simd
} // unnamed namespace
} // namespace ppc64
} // namespace simdjson
+3 -3
View File
@@ -3,7 +3,7 @@
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
#include "simdjson/ppc64/base.h"
#include "simdjson/ppc64/bitmask.h"
#include "simdjson/ppc64/bitmanipulation.h"
#include "simdjson/ppc64/simd.h"
#endif // SIMDJSON_CONDITIONAL_INCLUDE
@@ -25,10 +25,10 @@ public:
}
simdjson_inline bool has_backslash() { return bs_bits != 0; }
simdjson_inline int quote_index() {
return bitmask::trailing_zeroes(quote_bits);
return trailing_zeroes(quote_bits);
}
simdjson_inline int backslash_index() {
return bitmask::trailing_zeroes(bs_bits);
return trailing_zeroes(bs_bits);
}
uint32_t bs_bits;
+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.2.3"
#define SIMDJSON_VERSION "3.4.0"
namespace simdjson {
enum {
@@ -15,11 +15,11 @@ enum {
/**
* The minor version (major.MINOR.revision) of simdjson being used.
*/
SIMDJSON_VERSION_MINOR = 2,
SIMDJSON_VERSION_MINOR = 4,
/**
* The revision (major.minor.REVISION) of simdjson being used.
*/
SIMDJSON_VERSION_REVISION = 3
SIMDJSON_VERSION_REVISION = 0
};
} // namespace simdjson
-1
View File
@@ -3,7 +3,6 @@
#include "simdjson/westmere/begin.h"
#include "simdjson/generic/amalgamated.h"
#include "simdjson/generic/lookup_table.h"
#include "simdjson/westmere/end.h"
#endif // SIMDJSON_WESTMERE_H
+2 -2
View File
@@ -14,14 +14,14 @@ namespace westmere {
class implementation;
namespace {
namespace simd {
template <typename T> struct simd8;
template <> struct simd8<bool>;
template <> struct simd8<uint8_t>;
template <typename T> struct simd8x64;
} // namespace simd
} // unnamed namespace
} // namespace westmere
} // namespace simdjson
+1
View File
@@ -6,6 +6,7 @@
SIMDJSON_TARGET_REGION("sse4.2,pclmul,popcnt")
#endif
#include "simdjson/westmere/bitmanipulation.h"
#include "simdjson/westmere/bitmask.h"
#include "simdjson/westmere/numberparsing_defs.h"
#include "simdjson/westmere/simd.h"
@@ -0,0 +1,79 @@
#ifndef SIMDJSON_WESTMERE_BITMANIPULATION_H
#define SIMDJSON_WESTMERE_BITMANIPULATION_H
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
#include "simdjson/westmere/base.h"
#include "simdjson/westmere/intrinsics.h"
#endif // SIMDJSON_CONDITIONAL_INCLUDE
namespace simdjson {
namespace westmere {
namespace {
// We sometimes call trailing_zero on inputs that are zero,
// but the algorithms do not end up using the returned value.
// Sadly, sanitizers are not smart enough to figure it out.
SIMDJSON_NO_SANITIZE_UNDEFINED
// This function can be used safely even if not all bytes have been
// initialized.
// See issue https://github.com/simdjson/simdjson/issues/1965
SIMDJSON_NO_SANITIZE_MEMORY
simdjson_inline int trailing_zeroes(uint64_t input_num) {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
unsigned long ret;
// Search the mask data from least significant bit (LSB)
// to the most significant bit (MSB) for a set bit (1).
_BitScanForward64(&ret, input_num);
return (int)ret;
#else // SIMDJSON_REGULAR_VISUAL_STUDIO
return __builtin_ctzll(input_num);
#endif // SIMDJSON_REGULAR_VISUAL_STUDIO
}
/* result might be undefined when input_num is zero */
simdjson_inline uint64_t clear_lowest_bit(uint64_t input_num) {
return input_num & (input_num-1);
}
/* result might be undefined when input_num is zero */
simdjson_inline int leading_zeroes(uint64_t input_num) {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
unsigned long leading_zero = 0;
// Search the mask data from most significant bit (MSB)
// to least significant bit (LSB) for a set bit (1).
if (_BitScanReverse64(&leading_zero, input_num))
return (int)(63 - leading_zero);
else
return 64;
#else
return __builtin_clzll(input_num);
#endif// SIMDJSON_REGULAR_VISUAL_STUDIO
}
#if SIMDJSON_REGULAR_VISUAL_STUDIO
simdjson_inline unsigned __int64 count_ones(uint64_t input_num) {
// note: we do not support legacy 32-bit Windows in this kernel
return __popcnt64(input_num);// Visual Studio wants two underscores
}
#else
simdjson_inline long long int count_ones(uint64_t input_num) {
return _popcnt64(input_num);
}
#endif
simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2,
uint64_t *result) {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
return _addcarry_u64(0, value1, value2,
reinterpret_cast<unsigned __int64 *>(result));
#else
return __builtin_uaddll_overflow(value1, value2,
reinterpret_cast<unsigned long long *>(result));
#endif
}
} // unnamed namespace
} // namespace westmere
} // namespace simdjson
#endif // SIMDJSON_WESTMERE_BITMANIPULATION_H
+1 -83
View File
@@ -8,89 +8,7 @@
namespace simdjson {
namespace westmere {
namespace bitmask {
simdjson_constinit uint64_t ALL = 0xFFFFFFFFFFFFFFFF;
simdjson_constinit uint64_t NONE = 0xFFFFFFFFFFFFFFFF;
simdjson_constinit uint64_t EVEN = 0x5555555555555555;
simdjson_constinit uint64_t ODD = 0xAAAAAAAAAAAAAAAA;
// We sometimes call trailing_zero on inputs that are zero,
// but the algorithms do not end up using the returned value.
// Sadly, sanitizers are not smart enough to figure it out.
SIMDJSON_NO_SANITIZE_UNDEFINED
// This function can be used safely even if not all bytes have been
// initialized.
// See issue https://github.com/simdjson/simdjson/issues/1965
SIMDJSON_NO_SANITIZE_MEMORY
simdjson_inline int trailing_zeroes(uint64_t input_num) {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
unsigned long ret;
// Search the mask data from least significant bit (LSB)
// to the most significant bit (MSB) for a set bit (1).
_BitScanForward64(&ret, input_num);
return (int)ret;
#else // SIMDJSON_REGULAR_VISUAL_STUDIO
return __builtin_ctzll(input_num);
#endif // SIMDJSON_REGULAR_VISUAL_STUDIO
}
/* result might be undefined when input_num is zero */
simdjson_inline uint64_t clear_lowest_bit(uint64_t input_num) {
return input_num & (input_num-1);
}
/* result might be undefined when input_num is zero */
simdjson_inline int leading_zeroes(uint64_t input_num) {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
unsigned long leading_zero = 0;
// Search the mask data from most significant bit (MSB)
// to least significant bit (LSB) for a set bit (1).
if (_BitScanReverse64(&leading_zero, input_num))
return (int)(63 - leading_zero);
else
return 64;
#else
return __builtin_clzll(input_num);
#endif// SIMDJSON_REGULAR_VISUAL_STUDIO
}
#if SIMDJSON_REGULAR_VISUAL_STUDIO
simdjson_inline unsigned __int64 count_ones(uint64_t input_num) {
// note: we do not support legacy 32-bit Windows in this kernel
return __popcnt64(input_num);// Visual Studio wants two underscores
}
#else
simdjson_inline long long int count_ones(uint64_t input_num) {
return _popcnt64(input_num);
}
#endif
simdjson_inline uint64_t add_carry_out(const uint64_t value1, const uint64_t value2, bool& carry_out) noexcept {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
unsigned __int64 result;
carry_out = _addcarry_u64(0, value1, value2, &result);
return result;
#else
unsigned long long result;
carry_out = __builtin_uaddll_overflow(value1, value2, &result);
return result;
#endif
}
simdjson_inline uint64_t subtract_borrow(const uint64_t value1, const uint64_t value2, bool& borrow) noexcept {
unsigned long long result;
bool borrow1 = __builtin_usubll_overflow(value1, value2, &result);
borrow = borrow1 | __builtin_usubll_overflow(result, borrow, &result);
return result;
}
simdjson_inline uint64_t subtract_borrow_out(const uint64_t value1, const int64_t value2, bool& borrow_out) noexcept {
unsigned long long result;
borrow_out = __builtin_usubll_overflow(value1, value2, &result); // 2 (one to set )
return result;
}
namespace {
//
// Perform a "cumulative bitwise xor," flipping bits each time a 1 is encountered.
+48 -190
View File
@@ -3,28 +3,28 @@
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
#include "simdjson/westmere/base.h"
#include "simdjson/westmere/bitmask.h"
#include "simdjson/westmere/bitmanipulation.h"
#include "simdjson/internal/simdprune_tables.h"
#endif // SIMDJSON_CONDITIONAL_INCLUDE
namespace simdjson {
namespace westmere {
namespace {
namespace simd {
template<typename Child>
struct base {
using simd_t = __m128i;
simd_t value;
__m128i value;
// Zero constructor
simdjson_inline base() : value{simd_t()} {}
simdjson_inline base() : value{__m128i()} {}
// Conversion from SIMD register
simdjson_inline base(const simd_t _value) : value(_value) {}
simdjson_inline base(const __m128i _value) : value(_value) {}
// Conversion to SIMD register
simdjson_inline operator const simd_t&() const { return this->value; }
simdjson_inline operator simd_t&() { return this->value; }
simdjson_inline operator const __m128i&() const { return this->value; }
simdjson_inline operator __m128i&() { return this->value; }
// Bit operations
simdjson_inline Child operator|(const Child other) const { return _mm_or_si128(*this, other); }
@@ -38,16 +38,16 @@ namespace simd {
template<typename T, typename Mask=simd8<bool>>
struct base8: base<simd8<T>> {
using typename base<simd8<T>>::simd_t;
static constexpr const int LANES = sizeof(simd_t);
using bitmask_t = uint16_t;
static_assert(sizeof(bitmask_t)*8 == LANES, "Bitmask type's bits must equal the simd type's bytes");
typedef uint16_t bitmask_t;
typedef uint32_t bitmask2_t;
simdjson_inline base8() : base<simd8<T>>() {}
simdjson_inline base8(const simd_t _value) : base<simd8<T>>(_value) {}
simdjson_inline base8(const __m128i _value) : base<simd8<T>>(_value) {}
friend simdjson_inline Mask operator==(const simd8<T> lhs, const simd8<T> rhs) { return _mm_cmpeq_epi8(lhs, rhs); }
static const int SIZE = sizeof(base<simd8<T>>::value);
template<int N=1>
simdjson_inline simd8<T> prev(const simd8<T> prev_chunk) const {
return _mm_alignr_epi8(*this, prev_chunk, 16 - N);
@@ -57,29 +57,24 @@ namespace simd {
// SIMD byte mask type (returned by things like eq and gt)
template<>
struct simd8<bool>: base8<bool> {
using typename base8<bool>::simd_t;
static simdjson_inline simd8<bool> splat(bool _value) { return _mm_set1_epi8(uint8_t(-(!!_value))); }
simdjson_inline simd8<bool>() : base8() {}
simdjson_inline simd8<bool>(const simd_t _value) : base8<bool>(_value) {}
simdjson_inline simd8<bool>(const __m128i _value) : base8<bool>(_value) {}
// Splat constructor
simdjson_inline simd8<bool>(bool _value) : base8<bool>(splat(_value)) {}
simdjson_inline auto to_bitmask() const { return _mm_movemask_epi8(*this); }
simdjson_inline int to_bitmask() const { return _mm_movemask_epi8(*this); }
simdjson_inline bool any() const { return !_mm_testz_si128(*this, *this); }
simdjson_inline simd8<bool> operator~() const { return *this ^ true; }
};
template<typename T>
struct base8_numeric: base8<T> {
using typename base8<T>::simd_t;
using base8<T>::LANES;
static simdjson_inline simd8<T> splat(T _value) { return _mm_set1_epi8(_value); }
static simdjson_inline simd8<T> zero() { return _mm_setzero_si128(); }
static simdjson_inline simd8<T> load(const T values[16]) {
return _mm_loadu_si128(reinterpret_cast<const simd_t *>(values));
return _mm_loadu_si128(reinterpret_cast<const __m128i *>(values));
}
// Repeat 16 values as many times as necessary (usually for lookup tables)
static simdjson_inline simd8<T> repeat_16(
@@ -93,10 +88,10 @@ namespace simd {
}
simdjson_inline base8_numeric() : base8<T>() {}
simdjson_inline base8_numeric(const simd_t _value) : base8<T>(_value) {}
simdjson_inline base8_numeric(const __m128i _value) : base8<T>(_value) {}
// Store to array
simdjson_inline void store(T dst[16]) const { return _mm_storeu_si128(reinterpret_cast<simd_t *>(dst), *this); }
simdjson_inline void store(T dst[16]) const { return _mm_storeu_si128(reinterpret_cast<__m128i *>(dst), *this); }
// Override to distinguish from bool version
simdjson_inline simd8<T> operator~() const { return *this ^ 0xFFu; }
@@ -108,18 +103,14 @@ namespace simd {
simdjson_inline simd8<T>& operator-=(const simd8<T> other) { *this = *this - other; return *static_cast<simd8<T>*>(this); }
// Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values)
simdjson_inline simd8<T> lookup_16(const simd8<T>& lookup_table) const {
template<typename L>
simdjson_inline simd8<L> lookup_16(simd8<L> lookup_table) const {
return _mm_shuffle_epi8(lookup_table, *this);
}
// Perform a lookup based on the lower 4 bits of each lane. (Platform-dependent behavior for
// non-ASCII values--may look up the lower 4 bits on some platforms, and return 0 on others.)
simdjson_inline simd8<T> lookup_low_nibble_ascii(const simd8<T>& lookup_table) const {
return lookup_16(lookup_table);
}
// Copies to 'output" all bytes corresponding to a 0 in the mask (interpreted as a bitset).
// Passing a 0 value for mask would be equivalent to writing out every byte to output.
// Only the first 16 - bitmask::count_ones(mask) bytes of the result are significant but 16 bytes
// Only the first 16 - count_ones(mask) bytes of the result are significant but 16 bytes
// get written.
// Design consideration: it seems like a function with the
// signature simd8<L> compress(uint32_t mask) would be
@@ -136,12 +127,12 @@ namespace simd {
// next line just loads the 64-bit values thintable_epi8[mask1] and
// thintable_epi8[mask2] into a 128-bit register, using only
// two instructions on most compilers.
simd_t shufmask = _mm_set_epi64x(thintable_epi8[mask2], thintable_epi8[mask1]);
__m128i shufmask = _mm_set_epi64x(thintable_epi8[mask2], thintable_epi8[mask1]);
// we increment by 0x08 the second half of the mask
shufmask =
_mm_add_epi8(shufmask, _mm_set_epi32(0x08080808, 0x08080808, 0, 0));
// this is the version "nearly pruned"
simd_t pruned = _mm_shuffle_epi8(*this, shufmask);
__m128i pruned = _mm_shuffle_epi8(*this, shufmask);
// we still need to put the two halves together.
// we compute the popcount of the first half:
int pop1 = BitsSetTable256mul2[mask1];
@@ -149,10 +140,24 @@ namespace simd {
// only the first pop1 bytes from the first 8 bytes, and then
// it fills in with the bytes from the second 8 bytes + some filling
// at the end.
simd_t compactmask =
_mm_loadu_si128(reinterpret_cast<const simd_t *>(pshufb_combine_table + pop1 * 8));
simd_t answer = _mm_shuffle_epi8(pruned, compactmask);
_mm_storeu_si128(reinterpret_cast<simd_t *>(output), answer);
__m128i compactmask =
_mm_loadu_si128(reinterpret_cast<const __m128i *>(pshufb_combine_table + pop1 * 8));
__m128i answer = _mm_shuffle_epi8(pruned, compactmask);
_mm_storeu_si128(reinterpret_cast<__m128i *>(output), answer);
}
template<typename L>
simdjson_inline simd8<L> lookup_16(
L replace0, L replace1, L replace2, L replace3,
L replace4, L replace5, L replace6, L replace7,
L replace8, L replace9, L replace10, L replace11,
L replace12, L replace13, L replace14, L replace15) const {
return lookup_16(simd8<L>::repeat_16(
replace0, replace1, replace2, replace3,
replace4, replace5, replace6, replace7,
replace8, replace9, replace10, replace11,
replace12, replace13, replace14, replace15
));
}
};
@@ -160,7 +165,7 @@ namespace simd {
template<>
struct simd8<int8_t> : base8_numeric<int8_t> {
simdjson_inline simd8() : base8_numeric<int8_t>() {}
simdjson_inline simd8(const simd_t _value) : base8_numeric<int8_t>(_value) {}
simdjson_inline simd8(const __m128i _value) : base8_numeric<int8_t>(_value) {}
// Splat constructor
simdjson_inline simd8(int8_t _value) : simd8(splat(_value)) {}
// Array constructor
@@ -195,7 +200,7 @@ namespace simd {
template<>
struct simd8<uint8_t>: base8_numeric<uint8_t> {
simdjson_inline simd8() : base8_numeric<uint8_t>() {}
simdjson_inline simd8(const simd_t _value) : base8_numeric<uint8_t>(_value) {}
simdjson_inline simd8(const __m128i _value) : base8_numeric<uint8_t>(_value) {}
// Splat constructor
simdjson_inline simd8(uint8_t _value) : simd8(splat(_value)) {}
// Array constructor
@@ -267,8 +272,6 @@ namespace simd {
simdjson_inline simd8x64(const simd8<T> chunk0, const simd8<T> chunk1, const simd8<T> chunk2, const simd8<T> chunk3) : chunks{chunk0, chunk1, chunk2, chunk3} {}
simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8<T>::load(ptr), simd8<T>::load(ptr+16), simd8<T>::load(ptr+32), simd8<T>::load(ptr+48)} {}
simdjson_inline simd8x64(simd8x64<T>&& o) noexcept = default;
simdjson_inline simd8x64<T>& operator=(simd8x64<T>&& other) noexcept = default;
simdjson_inline void store(T ptr[64]) const {
this->chunks[0].store(ptr+sizeof(simd8<T>)*0);
@@ -283,10 +286,10 @@ namespace simd {
simdjson_inline uint64_t compress(uint64_t mask, T * output) const {
this->chunks[0].compress(uint16_t(mask), output);
this->chunks[1].compress(uint16_t(mask >> 16), output + 16 - bitmask::count_ones(mask & 0xFFFF));
this->chunks[2].compress(uint16_t(mask >> 32), output + 32 - bitmask::count_ones(mask & 0xFFFFFFFF));
this->chunks[3].compress(uint16_t(mask >> 48), output + 48 - bitmask::count_ones(mask & 0xFFFFFFFFFFFF));
return 64 - bitmask::count_ones(mask);
this->chunks[1].compress(uint16_t(mask >> 16), output + 16 - count_ones(mask & 0xFFFF));
this->chunks[2].compress(uint16_t(mask >> 32), output + 32 - count_ones(mask & 0xFFFFFFFF));
this->chunks[3].compress(uint16_t(mask >> 48), output + 48 - count_ones(mask & 0xFFFFFFFFFFFF));
return 64 - count_ones(mask);
}
simdjson_inline uint64_t to_bitmask() const {
@@ -307,7 +310,7 @@ namespace simd {
).to_bitmask();
}
simdjson_inline uint64_t eq(const simd8x64<T> &other) const {
simdjson_inline uint64_t eq(const simd8x64<uint8_t> &other) const {
return simd8x64<bool>(
this->chunks[0] == other.chunks[0],
this->chunks[1] == other.chunks[1],
@@ -325,155 +328,10 @@ namespace simd {
this->chunks[3] <= mask
).to_bitmask();
}
simdjson_inline simd8x64<T> lookup_16(const simd8<T>& lookup_table) const {
return {
this->chunks[0].lookup_16(lookup_table),
this->chunks[1].lookup_16(lookup_table),
this->chunks[2].lookup_16(lookup_table),
this->chunks[3].lookup_16(lookup_table),
};
}
simdjson_inline simd8x64<T> lookup_low_nibble_ascii(const simd8<T>& lookup_table) const {
return {
this->chunks[0].lookup_low_nibble_ascii(lookup_table),
this->chunks[1].lookup_low_nibble_ascii(lookup_table),
this->chunks[2].lookup_low_nibble_ascii(lookup_table),
this->chunks[3].lookup_low_nibble_ascii(lookup_table)
};
}
simdjson_inline simd8x64<T> operator&(const simd8x64<T>& other) const {
return {
this->chunks[0] & other.chunks[0],
this->chunks[1] & other.chunks[1],
this->chunks[2] & other.chunks[2],
this->chunks[3] & other.chunks[3]
};
}
simdjson_inline simd8x64<T> operator&(const simd8<T>& other) const {
return {
this->chunks[0] & other,
this->chunks[1] & other,
this->chunks[2] & other,
this->chunks[3] & other
};
}
simdjson_inline simd8x64<T> operator|(const simd8x64<T>& other) const {
return {
this->chunks[0] | other.chunks[0],
this->chunks[1] | other.chunks[1],
this->chunks[2] | other.chunks[2],
this->chunks[3] | other.chunks[3]
};
}
simdjson_inline simd8x64<T> operator|(const simd8<T>& other) const {
return {
this->chunks[0] | other,
this->chunks[1] | other,
this->chunks[2] | other,
this->chunks[3] | other
};
}
simdjson_inline simd8x64<T> operator^(const simd8x64<T>& other) const {
return {
this->chunks[0] ^ other.chunks[0],
this->chunks[1] ^ other.chunks[1],
this->chunks[2] ^ other.chunks[2],
this->chunks[3] ^ other.chunks[3]
};
}
simdjson_inline simd8x64<T> operator^(const simd8<T>& other) const {
return {
this->chunks[0] ^ other,
this->chunks[1] ^ other,
this->chunks[2] ^ other,
this->chunks[3] ^ other
};
}
simdjson_inline simd8x64<T> bit_andnot(const simd8x64<T>& other) const {
return {
this->chunks[0].bit_andnot(other.chunks[0]),
this->chunks[1].bit_andnot(other.chunks[1]),
this->chunks[2].bit_andnot(other.chunks[2]),
this->chunks[3].bit_andnot(other.chunks[3])
};
}
simdjson_inline simd8x64<T> bit_andnot(const simd8<T>& other) const {
return {
this->chunks[0].bit_andnot(other),
this->chunks[1].bit_andnot(other),
this->chunks[2].bit_andnot(other),
this->chunks[3].bit_andnot(other)
};
}
template <int N>
simdjson_inline simd8x64<T> shr() const noexcept {
return {
this->chunks[0].template shr<N>(),
this->chunks[1].template shr<N>(),
this->chunks[2].template shr<N>(),
this->chunks[3].template shr<N>()
};
}
template <int N>
simdjson_inline simd8x64<T> shl() const noexcept {
return {
this->chunks[0].template shl<N>(),
this->chunks[1].template shl<N>(),
this->chunks[2].template shl<N>(),
this->chunks[3].template shl<N>()
};
}
simdjson_inline simd8x64<bool> any_bits_set(const simd8<T>& bits) const {
return {
this->chunks[0].any_bits_set(bits),
this->chunks[1].any_bits_set(bits),
this->chunks[2].any_bits_set(bits),
this->chunks[3].any_bits_set(bits)
};
}
simdjson_inline simd8x64<bool> any_bits_set(const simd8x64<T>& bits) const {
return {
this->chunks[0].any_bits_set(bits.chunks[0]),
this->chunks[1].any_bits_set(bits.chunks[1]),
this->chunks[2].any_bits_set(bits.chunks[2]),
this->chunks[3].any_bits_set(bits.chunks[3])
};
}
simdjson_inline simd8x64<bool> no_bits_set(const simd8<T>& bits) const {
return {
this->chunks[0].no_bits_set(bits),
this->chunks[1].no_bits_set(bits),
this->chunks[2].no_bits_set(bits),
this->chunks[3].no_bits_set(bits)
};
}
simdjson_inline simd8x64<bool> no_bits_set(const simd8x64<T>& bits) const {
return {
this->chunks[0].no_bits_set(bits.chunks[0]),
this->chunks[1].no_bits_set(bits.chunks[1]),
this->chunks[2].no_bits_set(bits.chunks[2]),
this->chunks[3].no_bits_set(bits.chunks[3])
};
}
}; // struct simd8x64<T>
} // namespace simd
} // unnamed namespace
} // namespace westmere
} // namespace simdjson
@@ -1,7 +1,7 @@
#ifndef SIMDJSON_WESTMERE_STRINGPARSING_DEFS_H
#define SIMDJSON_WESTMERE_STRINGPARSING_DEFS_H
#include "simdjson/westmere/bitmask.h"
#include "simdjson/westmere/bitmanipulation.h"
#include "simdjson/westmere/simd.h"
namespace simdjson {
@@ -18,8 +18,8 @@ public:
simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; }
simdjson_inline bool has_backslash() { return bs_bits != 0; }
simdjson_inline int quote_index() { return bitmask::trailing_zeroes(quote_bits); }
simdjson_inline int backslash_index() { return bitmask::trailing_zeroes(bs_bits); }
simdjson_inline int quote_index() { return trailing_zeroes(quote_bits); }
simdjson_inline int backslash_index() { return trailing_zeroes(bs_bits); }
uint32_t bs_bits;
uint32_t quote_bits;
+407 -504
View File
File diff suppressed because it is too large Load Diff
+605 -82
View File
File diff suppressed because it is too large Load Diff
+20 -26
View File
@@ -37,27 +37,6 @@ namespace {
using namespace simd;
enum op_whitespace_t : uint8_t {
OPEN_OR_CLOSE = 1u << 0,
COLON = 1u << 1,
COMMA = 1u << 2,
TAB_CR_LF = 1u << 3,
SPACE = 1u << 4,
};
simdjson_constinit byte_classifier OP_WHITESPACE_CLASSIFIER({
_lookup_entry{ ' ', op_whitespace_t::SPACE },
{ '\t', op_whitespace_t::TAB_CR_LF },
{ '\r', op_whitespace_t::TAB_CR_LF },
{ '\n', op_whitespace_t::TAB_CR_LF },
{ ':', op_whitespace_t::COLON },
{ ',', op_whitespace_t::COMMA },
{ '{', op_whitespace_t::OPEN_OR_CLOSE },
{ '[', op_whitespace_t::OPEN_OR_CLOSE },
{ '}', op_whitespace_t::OPEN_OR_CLOSE },
{ ']', op_whitespace_t::OPEN_OR_CLOSE },
});
simdjson_inline json_character_block json_character_block::classify(const simd::simd8x64<uint8_t>& in) {
// Functional programming causes trouble with Visual Studio.
// Keeping this version in comments since it is much nicer:
@@ -68,7 +47,16 @@ simdjson_inline json_character_block json_character_block::classify(const simd::
// auto shuf_hi = nib_hi.lookup_16<uint8_t>(8, 0, 18, 4, 0, 1, 0, 1, 0, 0, 0, 3, 2, 1, 0, 0);
// return shuf_lo & shuf_hi;
// });
simd8x64<uint8_t> op_whitespace = OP_WHITESPACE_CLASSIFIER[in];
const simd8<uint8_t> table1(16, 0, 0, 0, 0, 0, 0, 0, 0, 8, 12, 1, 2, 9, 0, 0);
const simd8<uint8_t> table2(8, 0, 18, 4, 0, 1, 0, 1, 0, 0, 0, 3, 2, 1, 0, 0);
simd8x64<uint8_t> v(
(in.chunks[0] & 0xf).lookup_16(table1) & (in.chunks[0].shr<4>()).lookup_16(table2),
(in.chunks[1] & 0xf).lookup_16(table1) & (in.chunks[1].shr<4>()).lookup_16(table2),
(in.chunks[2] & 0xf).lookup_16(table1) & (in.chunks[2].shr<4>()).lookup_16(table2),
(in.chunks[3] & 0xf).lookup_16(table1) & (in.chunks[3].shr<4>()).lookup_16(table2)
);
// We compute whitespace and op separately. If the code later only use one or the
// other, given the fact that all functions are aggressively inlined, we can
@@ -86,12 +74,18 @@ simdjson_inline json_character_block json_character_block::classify(const simd::
// there is a small untaken optimization opportunity here. We deliberately
// do not pick it up.
uint64_t op = op_whitespace.any_bits_set(
op_whitespace_t::SPACE | op_whitespace_t::TAB_CR_LF
uint64_t op = simd8x64<bool>(
v.chunks[0].any_bits_set(0x7),
v.chunks[1].any_bits_set(0x7),
v.chunks[2].any_bits_set(0x7),
v.chunks[3].any_bits_set(0x7)
).to_bitmask();
uint64_t whitespace = op_whitespace.any_bits_set(
op_whitespace_t::COLON | op_whitespace_t::COMMA | op_whitespace_t::OPEN_OR_CLOSE
uint64_t whitespace = simd8x64<bool>(
v.chunks[0].any_bits_set(0x18),
v.chunks[1].any_bits_set(0x18),
v.chunks[2].any_bits_set(0x18),
v.chunks[3].any_bits_set(0x18)
).to_bitmask();
return { whitespace, op };
+1
View File
@@ -4,3 +4,4 @@
#include <generic/base.h>
#include <generic/dom_parser_implementation.h>
#include <generic/json_character_block.h>
+2
View File
@@ -10,6 +10,8 @@ namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
namespace {
struct json_character_block;
} // unnamed namespace
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
+27
View File
@@ -0,0 +1,27 @@
#ifndef SIMDJSON_SRC_GENERIC_JSON_CHARACTER_BLOCK_H
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
#define SIMDJSON_SRC_GENERIC_JSON_CHARACTER_BLOCK_H
#include <generic/base.h>
#endif // SIMDJSON_CONDITIONAL_INCLUDE
namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
namespace {
struct json_character_block {
static simdjson_inline json_character_block classify(const simd::simd8x64<uint8_t>& in);
simdjson_inline uint64_t whitespace() const noexcept { return _whitespace; }
simdjson_inline uint64_t op() const noexcept { return _op; }
simdjson_inline uint64_t scalar() const noexcept { return ~(op() | whitespace()); }
uint64_t _whitespace;
uint64_t _op;
};
} // unnamed namespace
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
#endif // SIMDJSON_SRC_GENERIC_JSON_CHARACTER_BLOCK_H
+2
View File
@@ -13,8 +13,10 @@ namespace stage1 {
class bit_indexer;
template<size_t STEP_SIZE>
struct buf_block_reader;
struct json_block;
class json_minifier;
class json_scanner;
struct json_string_block;
class json_string_scanner;
class json_structural_indexer;
@@ -45,6 +45,7 @@ simdjson_inline uint32_t find_next_document_index(dom_parser_implementation &par
auto idxb = parser.structural_indexes[i];
switch (parser.buf[idxb]) {
case ':':
case ',':
continue;
case '}':
obj_cnt--;
@@ -64,6 +65,7 @@ simdjson_inline uint32_t find_next_document_index(dom_parser_implementation &par
case '{':
case '[':
case ':':
case ',':
continue;
}
// Last document is complete, so the next document will appear after!
+18 -22
View File
@@ -47,12 +47,10 @@ struct json_escape_scanner {
* @param potential_escape A mask of the character that can escape others (but could be
* escaped itself). e.g. block.eq('\\')
*/
simdjson_really_inline escaped_and_escape next(
uint64_t backslash // [2+N]
) noexcept {
simdjson_really_inline escaped_and_escape next(uint64_t backslash) noexcept {
#if !SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT
if (!backslash) { return {next_escaped_without_backslashes(), 0}; } // 0 (+2)
if (!backslash) { return {next_escaped_without_backslashes(), 0}; }
#endif
// | | Mask (shows characters instead of 1's) | Depth | Instructions |
@@ -61,22 +59,23 @@ struct json_escape_scanner {
// | | ` even odd even odd odd` | | |
// | potential_escape | ` \ \\\ \\\ \\\\ \\\\ \\\` | 1 | 1 (backslash & ~first_is_escaped)
// | escape_and_terminal_code | ` \n \ \n \ \n \ \ \ \ \ \` | 5 | 5 (next_escape_and_terminal_code())
// | escaped | `\ \ n \ n \ \ \ \ \ ` X | 6 | 6 (escape_and_terminal_code ^ (potential_escape | first_is_escaped))
// | escape | ` \ \ \ \ \ \ \ \ \ \` | 6 | 7 (escape_and_terminal_code & backslash)
// | first_is_escaped | `\ ` | 7 (*) | 8 (escape >> 63) ()
// | escaped | `\ \ n \ n \ \ \ \ \ ` X | 6 | 7 (escape_and_terminal_code ^ (potential_escape | first_is_escaped))
// | escape | ` \ \ \ \ \ \ \ \ \ \` | 6 | 8 (escape_and_terminal_code & backslash)
// | first_is_escaped | `\ ` | 7 (*) | 9 (escape >> 63) ()
// (*) this is not needed until the next iteration
uint64_t escape_and_terminal_code = next_escape_and_terminal_code(backslash & ~this->next_is_escaped); // 5+N (4 total)
uint64_t escaped = escape_and_terminal_code ^ (backslash | this->next_is_escaped); // [5+N] 1
uint64_t escape = escape_and_terminal_code & backslash; // [5+N] 1
this->next_is_escaped = escape >> 63; // 1
uint64_t escape_and_terminal_code = next_escape_and_terminal_code(backslash & ~this->next_is_escaped);
uint64_t escaped = escape_and_terminal_code ^ (backslash | this->next_is_escaped);
uint64_t escape = escape_and_terminal_code & backslash;
this->next_is_escaped = escape >> 63;
return {escaped, escape};
// shortest path to escaped: 2+N (2 total) or 6+N (8 total)
}
private:
static constexpr const uint64_t ODD_BITS = 0xAAAAAAAAAAAAAAAAULL;
simdjson_really_inline uint64_t next_escaped_without_backslashes() noexcept {
uint64_t escaped = this->next_is_escaped; // (register swap, probably 0 latency ultimately)
this->next_is_escaped = 0; // 1
uint64_t escaped = this->next_is_escaped;
this->next_is_escaped = 0;
return escaped;
}
@@ -94,9 +93,7 @@ private:
* & the result with potential_escape to get just the escape characters.
* ^ the result with (potential_escape | first_is_escaped) to get escaped characters.
*/
static simdjson_really_inline uint64_t next_escape_and_terminal_code(
uint64_t potential_escape // [2+N]
) noexcept {
static simdjson_really_inline uint64_t next_escape_and_terminal_code(uint64_t potential_escape) noexcept {
// If we were to just shift and mask out any odd bits, we'd actually get a *half* right answer:
// any even-aligned backslash runs would be correct! Odd-aligned backslash runs would be
// inverted (\\\ would be 010 instead of 101).
@@ -127,23 +124,22 @@ private:
//
// Escaped characters are characters following an escape.
uint64_t maybe_escaped = potential_escape << 1; // [2+N] 1
uint64_t maybe_escaped = potential_escape << 1;
// To distinguish odd from even escape sequences, therefore, we turn on any *starting*
// escapes that are on an odd byte. (We actually bring in all odd bits, for speed.)
// - Odd runs of backslashes are 0000, and the code at the end ("n" in \n or \\n) is 1.
// - Odd runs of backslashes are 1111, and the code at the end ("n" in \n or \\n) is 0.
// - All other odd bytes are 1, and even bytes are 0.
uint64_t maybe_escaped_and_odd_bits = maybe_escaped | bitmask::ODD; // [3+N] 1
uint64_t even_series_codes_and_odd_bits = maybe_escaped_and_odd_bits - potential_escape; // 1
uint64_t maybe_escaped_and_odd_bits = maybe_escaped | ODD_BITS;
uint64_t even_series_codes_and_odd_bits = maybe_escaped_and_odd_bits - potential_escape;
// Now we flip all odd bytes back with xor. This:
// - Makes odd runs of backslashes go from 0000 to 1010
// - Makes even runs of backslashes go from 1111 to 1010
// - Sets actually-escaped codes to 1 (the n in \n and \\n: \n = 11, \\n = 100)
// - Resets all other bytes to 0
return even_series_codes_and_odd_bits ^ bitmask::ODD; // 1
// shortest path: 5+N (+4)
return even_series_codes_and_odd_bits ^ ODD_BITS;
}
};
+10 -9
View File
@@ -28,14 +28,15 @@ private:
{}
template<size_t STEP_SIZE>
simdjson_inline void step(const uint8_t *block_buf, buf_block_reader<STEP_SIZE> &reader) noexcept;
simdjson_inline void next(const simd::simd8x64<uint8_t>& in, uint64_t whitespace);
simdjson_inline void next(const simd::simd8x64<uint8_t>& in, const json_block& block);
simdjson_inline error_code finish(uint8_t *dst_start, size_t &dst_len);
json_scanner scanner{};
uint8_t *dst;
};
simdjson_inline void json_minifier::next(const simd::simd8x64<uint8_t>& in, uint64_t ws) {
dst += in.compress(ws, dst);
simdjson_inline void json_minifier::next(const simd::simd8x64<uint8_t>& in, const json_block& block) {
uint64_t mask = block.whitespace();
dst += in.compress(mask, dst);
}
simdjson_inline error_code json_minifier::finish(uint8_t *dst_start, size_t &dst_len) {
@@ -49,18 +50,18 @@ template<>
simdjson_inline void json_minifier::step<128>(const uint8_t *block_buf, buf_block_reader<128> &reader) noexcept {
simd::simd8x64<uint8_t> in_1(block_buf);
simd::simd8x64<uint8_t> in_2(block_buf+64);
uint64_t ws_1 = scanner.next_whitespace(in_1);
uint64_t ws_2 = scanner.next_whitespace(in_2);
this->next(in_1, ws_1);
this->next(in_2, ws_2);
json_block block_1 = scanner.next(in_1);
json_block block_2 = scanner.next(in_2);
this->next(in_1, block_1);
this->next(in_2, block_2);
reader.advance();
}
template<>
simdjson_inline void json_minifier::step<64>(const uint8_t *block_buf, buf_block_reader<64> &reader) noexcept {
simd::simd8x64<uint8_t> in_1(block_buf);
uint64_t ws_1 = scanner.next(in_1);
this->next(block_buf, ws_1);
json_block block_1 = scanner.next(in_1);
this->next(block_buf, block_1);
reader.advance();
}
+112 -172
View File
@@ -1,12 +1,10 @@
#include "simdjson/icelake/bitmask.h"
#ifndef SIMDJSON_SRC_GENERIC_STAGE1_JSON_SCANNER_H
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
#define SIMDJSON_SRC_GENERIC_STAGE1_JSON_SCANNER_H
#include <generic/stage1/base.h>
#include <generic/json_character_block.h>
#include <generic/stage1/json_string_scanner.h>
#include <generic/stage1/buf_block_reader.h>
#include <simdjson/generic/lookup_table.h>
#endif // SIMDJSON_CONDITIONAL_INCLUDE
namespace simdjson {
@@ -14,71 +12,82 @@ namespace SIMDJSON_IMPLEMENTATION {
namespace {
namespace stage1 {
static simdjson_constinit low_nibble_lookup WHITESPACE_MATCH = {
{ ' ', ' ' },
{ '\t', '\t' },
{ '\r', '\r' },
{ '\n', '\n' },
};
/**
* A block of scanned json, with information on operators and scalars.
*
* We seek to identify pseudo-structural characters. Anything that is inside
* a string must be omitted (hence & ~_string.string_tail()).
* Otherwise, pseudo-structural characters come in two forms.
* 1. We have the structural characters ([,],{,},:, comma). The
* term 'structural character' is from the JSON RFC.
* 2. We have the 'scalar pseudo-structural characters'.
* Scalars are quotes, and any character except structural characters and white space.
*
* To identify the scalar pseudo-structural characters, we must look at what comes
* before them: it must be a space, a quote or a structural characters.
* Starting with simdjson v0.3, we identify them by
* negation: we identify everything that is followed by a non-quote scalar,
* and we negate that. Whatever remains must be a 'scalar pseudo-structural character'.
*/
struct json_block {
public:
// We spell out the constructors in the hope of resolving inlining issues with Visual Studio 2017
simdjson_inline json_block(json_string_block&& string, json_character_block characters, uint64_t follows_potential_nonquote_scalar) :
_string(std::move(string)), _characters(characters), _follows_potential_nonquote_scalar(follows_potential_nonquote_scalar) {}
simdjson_inline json_block(json_string_block string, json_character_block characters, uint64_t follows_potential_nonquote_scalar) :
_string(string), _characters(characters), _follows_potential_nonquote_scalar(follows_potential_nonquote_scalar) {}
struct basic_block_classification {
uint64_t open;
uint64_t close;
uint64_t comma;
uint64_t colon;
uint64_t backslash;
uint64_t raw_quote;
uint64_t ws;
uint64_t ctrl;
/**
* The start of structurals.
* In simdjson prior to v0.3, these were called the pseudo-structural characters.
**/
simdjson_inline uint64_t structural_start() const noexcept { return potential_structural_start() & ~_string.string_tail(); }
/** All JSON whitespace (i.e. not in a string) */
simdjson_inline uint64_t whitespace() const noexcept { return non_quote_outside_string(_characters.whitespace()); }
simdjson_inline basic_block_classification(const simd8x64<uint8_t>& in) : basic_block_classification(in, in | ('{' - '[')) {}
// Helpers
simdjson_inline uint64_t sep() const noexcept { return comma | colon; }
simdjson_inline uint64_t sep_open() const noexcept { return sep() | open; }
simdjson_inline uint64_t scalar_close() const noexcept { return ~sep_open() & ~ws; }
simdjson_inline uint64_t scalar() const noexcept { return scalar_close() & ~close; }
simdjson_inline uint64_t op_without_comma() const noexcept { return colon | open | close; }
/** Whether the given characters are inside a string (only works on non-quotes) */
simdjson_inline uint64_t non_quote_inside_string(uint64_t mask) const noexcept { return _string.non_quote_inside_string(mask); }
/** Whether the given characters are outside a string (only works on non-quotes) */
simdjson_inline uint64_t non_quote_outside_string(uint64_t mask) const noexcept { return _string.non_quote_outside_string(mask); }
// string and escape characters
json_string_block _string;
// whitespace, structural characters ('operators'), scalars
json_character_block _characters;
// whether the previous character was a scalar
uint64_t _follows_potential_nonquote_scalar;
private:
enum ws_op {
COMMA = 1 << 0,
COLON = 1 << 1,
OPEN = 1 << 2,
CLOSE = 1 << 3,
QUOTE = 1 << 4,
BACKSLASH = 1 << 5,
SPACE = 1 << 6,
TAB_CR_LF = 1 << 7,
OP = COMMA | COLON | OPEN | CLOSE,
SEP = COMMA | COLON,
WS = SPACE | TAB_CR_LF,
};
// Potential structurals (i.e. disregarding strings)
static simdjson_constinit byte_classifier CLASSIFIER = {
{ ',', COMMA },
{ ':', COLON },
{ '[', OPEN },
{ '{', OPEN },
{ ']', CLOSE },
{ '}', CLOSE },
{ '\"', QUOTE },
{ ' ', SPACE },
{ '\t', TAB_CR_LF },
{ '\r', TAB_CR_LF },
{ '\n', TAB_CR_LF },
{ '\\', BACKSLASH },
};
simdjson_inline basic_block_classification(const simd8x64<uint8_t>& in, const simd8x64<uint8_t>& curlified) :
open{curlified.eq('{')},
close{curlified.eq('}')},
comma{in.eq(',')},
colon{in.eq(':')},
backslash{in.eq('\\')},
raw_quote{in.eq('"')},
ws{in.eq(WHITESPACE_MATCH.lookup(in))},
ctrl{in.lteq(0x1F)}
{}
/**
* structural elements ([,],{,},:, comma) plus scalar starts like 123, true and "abc".
* They may reside inside a string.
**/
simdjson_inline uint64_t potential_structural_start() const noexcept { return _characters.op() | potential_scalar_start(); }
/**
* The start of non-operator runs, like 123, true and "abc".
* It main reside inside a string.
**/
simdjson_inline uint64_t potential_scalar_start() const noexcept {
// The term "scalar" refers to anything except structural characters and white space
// (so letters, numbers, quotes).
// Whenever it is preceded by something that is not a structural element ({,},[,],:, ") nor a white-space
// then we know that it is irrelevant structurally.
return _characters.scalar() & ~follows_potential_scalar();
}
/**
* Whether the given character is immediately after a non-operator like 123, true.
* The characters following a quote are not included.
*/
simdjson_inline uint64_t follows_potential_scalar() const noexcept {
// _follows_potential_nonquote_scalar: is defined as marking any character that follows a character
// that is not a structural element ({,},[,],:, comma) nor a quote (") and that is not a
// white space.
// It is understood that within quoted region, anything at all could be marked (irrelevant).
return _follows_potential_nonquote_scalar;
}
};
/**
@@ -96,128 +105,59 @@ private:
*/
class json_scanner {
public:
simdjson_inline json_scanner() = default;
simdjson_inline uint64_t next(const simd::simd8x64<uint8_t>& in) noexcept;
simdjson_inline uint64_t next_whitespace(const simd::simd8x64<uint8_t>& in) noexcept;
json_scanner() = default;
simdjson_inline json_block next(const simd::simd8x64<uint8_t>& in);
// Returns either UNCLOSED_STRING or SUCCESS
simdjson_inline error_code finish() const noexcept;
simdjson_inline uint64_t next(const simd::simd8x64<uint8_t>& in, const basic_block_classification& block) noexcept;
simdjson_inline uint64_t next_whitespace(const simd::simd8x64<uint8_t>& in, const basic_block_classification& block) noexcept;
simdjson_inline error_code finish();
private:
simdjson_inline uint64_t next_separated_values(uint64_t sep_open, uint64_t scalar_close) noexcept;
simdjson_inline void check_errors(const simd8x64<uint8_t>& in, uint64_t scalar, uint64_t ctrl, uint64_t sep, uint64_t open, uint64_t raw_quote, uint64_t separated_values, uint64_t in_string) noexcept;
// Whether the last character of the previous iteration is part of a scalar token
// (anything except whitespace or a structural character/'operator').
uint64_t prev_scalar = 0ULL;
json_string_scanner string_scanner{};
uint64_t still_in_scalar{};
bool still_in_value{};
uint64_t error{};
};
simdjson_inline uint64_t json_scanner::next(const simd::simd8x64<uint8_t>& in) noexcept {
return next(in, in);
//
// Check if the current character immediately follows a matching character.
//
// For example, this checks for quotes with backslashes in front of them:
//
// const uint64_t backslashed_quote = in.eq('"') & immediately_follows(in.eq('\'), prev_backslash);
//
simdjson_inline uint64_t follows(const uint64_t match, uint64_t &overflow) {
const uint64_t result = match << 1 | overflow;
overflow = match >> 63;
return result;
}
simdjson_inline uint64_t json_scanner::next(const simd::simd8x64<uint8_t>& in, const basic_block_classification& block) noexcept {
// printf("\n");
// printf("%30.30s: %s\n", "next", format_input_text(in));
// Figure out what's in a string
uint64_t quote = string_scanner.next_unescaped_quotes(block.backslash, block.raw_quote);
uint64_t in_string = string_scanner.next_in_string(quote);
// Get structurals
uint64_t scalar_close = block.scalar_close();
uint64_t separated_values = next_separated_values(block.sep_open(), scalar_close);
uint64_t scalar = scalar_close & ~block.close;
uint64_t lead_value = scalar & separated_values;
uint64_t all_structurals = block.op_without_comma() | lead_value;
// Join up structurals and strings
uint64_t structurals = all_structurals & ~in_string;
// Check for errors
// this->error |= block.ctrl & in_string;
check_errors(in, scalar, block.ctrl, block.sep(), block.open, quote, separated_values, in_string);
return structurals;
simdjson_inline json_block json_scanner::next(const simd::simd8x64<uint8_t>& in) {
json_string_block strings = string_scanner.next(in);
// identifies the white-space and the structural characters
json_character_block characters = json_character_block::classify(in);
// The term "scalar" refers to anything except structural characters and white space
// (so letters, numbers, quotes).
// We want follows_scalar to mark anything that follows a non-quote scalar (so letters and numbers).
//
// A terminal quote should either be followed by a structural character (comma, brace, bracket, colon)
// or nothing. However, we still want ' "a string"true ' to mark the 't' of 'true' as a potential
// pseudo-structural character just like we would if we had ' "a string" true '; otherwise we
// may need to add an extra check when parsing strings.
//
// Performance: there are many ways to skin this cat.
const uint64_t nonquote_scalar = characters.scalar() & ~strings.quote();
uint64_t follows_nonquote_scalar = follows(nonquote_scalar, prev_scalar);
// We are returning a function-local object so either we get a move constructor
// or we get copy elision.
return json_block(
strings,// strings is a function-local object so either it moves or the copy is elided.
characters,
follows_nonquote_scalar
);
}
simdjson_inline uint64_t json_scanner::next_separated_values(
uint64_t sep_open,
uint64_t scalar_close
) noexcept {
// Split the JSON by separators. After this, we know:
// - the lead character of every valid scalar.
// - there is least one scalar/close bracket between each separator
// - open bracket is always after separator or at beginning of the document
// OPEN|WS* CLOSE|SCALAR (CLOSE|SCALAR|WS)* SEP OPEN|WS*
// 1|0 * 1 0|1 * 1 1|0 *
// (We include open brackets with separators because we can easily detect some errors from that.)
return bitmask::subtract_borrow(sep_open, scalar_close, this->still_in_value);
}
simdjson_inline void json_scanner::check_errors(
const simd8x64<uint8_t>& in,
uint64_t scalar,
uint64_t ctrl,
uint64_t sep,
uint64_t open,
uint64_t raw_quote,
uint64_t separated_values,
uint64_t in_string
) noexcept {
// Detect separator errors
// ERROR: missing separator between scalars or close brackets (scalar preceded by anything other than separator, open, or beginning of document)
uint64_t next_in_scalar = scalar & ~raw_quote;
uint64_t in_scalar = next_in_scalar << 1 | this->still_in_scalar;
this->still_in_scalar = next_in_scalar >> 63;
uint64_t first_scalar = scalar & ~in_scalar;
// Take away lead scalar characters, which are allowed to be the first scalar character
uint64_t missing_separator_error = first_scalar & ~separated_values;
// ERROR: separator with another separator or open bracket ahead of it (or at beginning of document)
uint64_t extra_separator_error = sep & separated_values;
// ERROR: open bracket without separator ahead of it (except at beginning of document)
uint64_t missing_separator_before_open_error = open & ~separated_values;
// Put it all together
uint64_t raw_separator_error = missing_separator_error | extra_separator_error | missing_separator_before_open_error;
this->error |= (raw_separator_error & ~in_string) | (ctrl & in_string);
// NOT validated:
// - Object/array: Brace balance / type
// - Object: key type = string
// - Object: Colon only between key and value
// - Empty object/array: close bracket before separator preceded by open bracket
// - UTF-8 in strings
// - scalar format
}
simdjson_inline uint64_t json_scanner::next_whitespace(
const simd::simd8x64<uint8_t>& in
) noexcept {
return next_whitespace(in, in);
}
simdjson_inline uint64_t json_scanner::next_whitespace(
const simd::simd8x64<uint8_t>& in,
const basic_block_classification& block
) noexcept {
uint64_t in_string = string_scanner.next(block.backslash, block.raw_quote);
return block.ws & ~in_string;
}
simdjson_inline error_code json_scanner::finish() const noexcept {
if (this->error | this->string_scanner.finish()) {
return TAPE_ERROR;
}
return SUCCESS;
simdjson_inline error_code json_scanner::finish() {
return string_scanner.finish();
}
} // namespace stage1
+54 -38
View File
@@ -11,22 +11,44 @@ namespace SIMDJSON_IMPLEMENTATION {
namespace {
namespace stage1 {
struct json_string_block {
// We spell out the constructors in the hope of resolving inlining issues with Visual Studio 2017
simdjson_really_inline json_string_block(uint64_t escaped, uint64_t quote, uint64_t in_string) :
_escaped(escaped), _quote(quote), _in_string(in_string) {}
// Escaped characters (characters following an escape() character)
simdjson_really_inline uint64_t escaped() const { return _escaped; }
// Real (non-backslashed) quotes
simdjson_really_inline uint64_t quote() const { return _quote; }
// Only characters inside the string (not including the quotes)
simdjson_really_inline uint64_t string_content() const { return _in_string & ~_quote; }
// Return a mask of whether the given characters are inside a string (only works on non-quotes)
simdjson_really_inline uint64_t non_quote_inside_string(uint64_t mask) const { return mask & _in_string; }
// Return a mask of whether the given characters are inside a string (only works on non-quotes)
simdjson_really_inline uint64_t non_quote_outside_string(uint64_t mask) const { return mask & ~_in_string; }
// Tail of string (everything except the start quote)
simdjson_really_inline uint64_t string_tail() const { return _in_string ^ _quote; }
// escaped characters (backslashed--does not include the hex characters after \u)
uint64_t _escaped;
// real quotes (non-escaped ones)
uint64_t _quote;
// string characters (includes start quote but not end quote)
uint64_t _in_string;
};
// Scans blocks for string characters, storing the state necessary to do so
class json_string_scanner {
public:
simdjson_inline uint64_t next(uint64_t backslash, uint64_t raw_quote) noexcept;
simdjson_inline uint64_t next_unescaped_quotes(uint64_t backslash, uint64_t raw_quote) noexcept;
simdjson_inline uint64_t next_in_string(uint64_t in_string) noexcept;
simdjson_really_inline json_string_block next(const simd::simd8x64<uint8_t>& in);
// Returns either UNCLOSED_STRING or SUCCESS
simdjson_inline error_code finish() const noexcept;
simdjson_really_inline error_code finish();
private:
// Scans for escape characters
json_escape_scanner escape_scanner{};
// Whether the last iteration was still inside a string (all 1's = true, all 0's = false).
bool still_in_string{};
unsigned penalty_box = 0;
uint64_t prev_in_string = 0ULL;
};
//
@@ -37,39 +59,33 @@ private:
//
// Backslash sequences outside of quotes will be detected in stage 2.
//
simdjson_inline uint64_t json_string_scanner::next(
uint64_t backslash, // 3+LN
uint64_t raw_quote // 3+LN
) noexcept {
uint64_t quote = next_unescaped_quotes(backslash, raw_quote); // 4+LN (+3) ... 8+LN (+9)
return next_in_string(quote); // 14+LN ... 18+LN (+2+simd:3)
// critical path = 14+LN (+5+simd:3) ... 18+LN (+11+simd:3)
simdjson_really_inline json_string_block json_string_scanner::next(const simd::simd8x64<uint8_t>& in) {
const uint64_t backslash = in.eq('\\');
const uint64_t escaped = escape_scanner.next(backslash).escaped;
const uint64_t quote = in.eq('"') & ~escaped;
//
// prefix_xor flips on bits inside the string (and flips off the end quote).
//
// Then we xor with prev_in_string: if we were in a string already, its effect is flipped
// (characters inside strings are outside, and characters outside strings are inside).
//
const uint64_t in_string = prefix_xor(quote) ^ prev_in_string;
//
// Check if we're still in a string at the end of the box so the next block will know
//
prev_in_string = uint64_t(static_cast<int64_t>(in_string) >> 63);
// Use ^ to turn the beginning quote off, and the end quote on.
// We are returning a function-local object so either we get a move constructor
// or we get copy elision.
return json_string_block(escaped, quote, in_string);
}
simdjson_inline uint64_t json_string_scanner::next_unescaped_quotes(
uint64_t backslash, // 3+LN
uint64_t raw_quote // 3+LN
) noexcept {
uint64_t escaped = escape_scanner.next(backslash).escaped; // 3+LN (+2) or 7+LN (+8)
return raw_quote & ~escaped; // 4+LN or 8+LN (+1)
// critical path: 4+LN (+3) or 8+LN (+9)
}
simdjson_inline uint64_t json_string_scanner::next_in_string(
uint64_t quote // 4+LN ... 8+LN
) noexcept {
// This shouldn't happen often, so we take the heavy branch penalty for it and use the
// high-latency prefix_xor.
// this->still_in_string = was_still_in_string;
uint64_t in_string = bitmask::prefix_xor(quote ^ this->still_in_string); // 14+LN (+1+simd:3)
this->still_in_string = in_string >> 63; // 15+LN (+1)
return in_string ^ quote;
// critical path 14+LN ... 18+LN (+2+simd:3)
}
simdjson_inline error_code json_string_scanner::finish() const noexcept {
if (still_in_string) {
simdjson_really_inline error_code json_string_scanner::finish() {
if (prev_in_string) {
return UNCLOSED_STRING;
}
return SUCCESS;
+20 -15
View File
@@ -42,9 +42,9 @@ public:
* beneficial.
*/
simdjson_inline void write_index(uint32_t idx, uint64_t& rev_bits, int i) {
int lz = bitmask::leading_zeroes(rev_bits);
int lz = leading_zeroes(rev_bits);
this->tail[i] = static_cast<uint32_t>(idx) + lz;
rev_bits = bitmask::zero_leading_bit(rev_bits, lz);
rev_bits = zero_leading_bit(rev_bits, lz);
}
#else
/**
@@ -54,8 +54,8 @@ public:
*/
simdjson_inline void write_index(uint32_t idx, uint64_t& bits, int i) {
this->tail[i] = idx + bitmask::trailing_zeroes(bits);
bits = bitmask::clear_lowest_bit(bits);
this->tail[i] = idx + trailing_zeroes(bits);
bits = clear_lowest_bit(bits);
}
#endif // SIMDJSON_PREFER_REVERSE_BITS
@@ -97,10 +97,10 @@ public:
if (bits == 0)
return;
int cnt = static_cast<int>(bitmask::count_ones(bits));
int cnt = static_cast<int>(count_ones(bits));
#if SIMDJSON_PREFER_REVERSE_BITS
bits = bitmask::reverse_bits(bits);
bits = reverse_bits(bits);
#endif
#ifdef SIMDJSON_STRUCTURAL_INDEXER_STEP
static constexpr const int STEP = SIMDJSON_STRUCTURAL_INDEXER_STEP;
@@ -140,13 +140,14 @@ private:
simdjson_inline json_structural_indexer(uint32_t *structural_indexes);
template<size_t STEP_SIZE>
simdjson_inline void step(const uint8_t *block, buf_block_reader<STEP_SIZE> &reader) noexcept;
simdjson_inline void next(const simd::simd8x64<uint8_t>& in, uint64_t structurals, size_t idx);
simdjson_inline void next(const simd::simd8x64<uint8_t>& in, const json_block& block, size_t idx);
simdjson_inline error_code finish(dom_parser_implementation &parser, size_t idx, size_t len, stage1_mode partial);
json_scanner scanner{};
utf8_checker checker{};
bit_indexer indexer;
uint64_t prev_structurals = 0;
uint64_t unescaped_chars_error = 0;
};
simdjson_inline json_structural_indexer::json_structural_indexer(uint32_t *structural_indexes) : indexer{structural_indexes} {}
@@ -220,28 +221,29 @@ template<>
simdjson_inline void json_structural_indexer::step<128>(const uint8_t *block, buf_block_reader<128> &reader) noexcept {
simd::simd8x64<uint8_t> in_1(block);
simd::simd8x64<uint8_t> in_2(block+64);
uint64_t structurals_1 = scanner.next(in_1);
uint64_t structurals_2 = scanner.next(in_2);
this->next(in_1, structurals_1, reader.block_index());
this->next(in_2, structurals_2, reader.block_index()+64);
json_block block_1 = scanner.next(in_1);
json_block block_2 = scanner.next(in_2);
this->next(in_1, block_1, reader.block_index());
this->next(in_2, block_2, reader.block_index()+64);
reader.advance();
}
template<>
simdjson_inline void json_structural_indexer::step<64>(const uint8_t *block, buf_block_reader<64> &reader) noexcept {
simd::simd8x64<uint8_t> in_1(block);
uint64_t structurals_1 = scanner.next(in_1);
this->next(in_1, structurals_1, reader.block_index());
json_block block_1 = scanner.next(in_1);
this->next(in_1, block_1, reader.block_index());
reader.advance();
}
simdjson_inline void json_structural_indexer::next(const simd::simd8x64<uint8_t>& in,uint64_t structurals, size_t idx) {
simdjson_inline void json_structural_indexer::next(const simd::simd8x64<uint8_t>& in, const json_block& block, size_t idx) {
uint64_t unescaped = in.lteq(0x1F);
#if SIMDJSON_UTF8VALIDATION
checker.check_next_input(in);
#endif
indexer.write(uint32_t(idx-64), prev_structurals); // Output *last* iteration's structurals to the parser
prev_structurals = structurals;
prev_structurals = block.structural_start();
unescaped_chars_error |= block.non_quote_inside_string(unescaped);
}
simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementation &parser, size_t idx, size_t len, stage1_mode partial) {
@@ -256,6 +258,9 @@ simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementa
const bool have_unclosed_string = (error == UNCLOSED_STRING);
if (simdjson_unlikely(should_we_exit)) { return error; }
if (unescaped_chars_error) {
return UNESCAPED_CHARS;
}
parser.n_structural_indexes = uint32_t(indexer.tail - parser.structural_indexes.get());
/***
* The On Demand API requires special padding.
+6 -15
View File
@@ -41,7 +41,7 @@ using namespace simd;
// 11111___ 1000____
constexpr const uint8_t OVERLONG_4 = 1<<6; // 11110000 1000____
const simd8<uint8_t> byte_1_high = prev1.shr<4>().lookup_16(simd8<uint8_t>::repeat_16(
const simd8<uint8_t> byte_1_high = prev1.shr<4>().lookup_16<uint8_t>(
// 0_______ ________ <ASCII in byte 1>
TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG,
TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG,
@@ -55,10 +55,9 @@ using namespace simd;
TOO_SHORT | OVERLONG_3 | SURROGATE,
// 1111____ ________ <four+ byte lead in byte 1>
TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4
));
);
constexpr const uint8_t CARRY = TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 .
// TODO use lookup_low_nibble_ascii to avoid & for Intel
const simd8<uint8_t> byte_1_low = (prev1 & 0x0F).lookup_16(simd8<uint8_t>::repeat_16(
const simd8<uint8_t> byte_1_low = (prev1 & 0x0F).lookup_16<uint8_t>(
// ____0000 ________
CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4,
// ____0001 ________
@@ -85,8 +84,8 @@ using namespace simd;
CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE,
CARRY | TOO_LARGE | TOO_LARGE_1000,
CARRY | TOO_LARGE | TOO_LARGE_1000
));
const simd8<uint8_t> byte_2_high = input.shr<4>().lookup_16(simd8<uint8_t>::repeat_16(
);
const simd8<uint8_t> byte_2_high = input.shr<4>().lookup_16<uint8_t>(
// ________ 0_______ <ASCII in byte 2>
TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT,
TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT,
@@ -101,7 +100,7 @@ using namespace simd;
// ________ 11______
TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT
));
);
return (byte_1_high & byte_1_low & byte_2_high);
}
simdjson_inline simd8<uint8_t> check_multibyte_lengths(const simd8<uint8_t> input,
@@ -171,14 +170,6 @@ using namespace simd;
this->error |= this->prev_incomplete;
}
#ifndef SIMDJSON_IF_CONSTEXPR
#if SIMDJSON_CPLUSPLUS17
#define SIMDJSON_IF_CONSTEXPR if constexpr
#else
#define SIMDJSON_IF_CONSTEXPR if
#endif
#endif
simdjson_inline void check_next_input(const simd8x64<uint8_t>& input) {
if(simdjson_likely(is_ascii(input))) {
this->error |= this->prev_incomplete;
+21 -22
View File
@@ -177,18 +177,19 @@ object_field:
}
}
object_continue: {
auto key = advance();
switch (*key) {
case '}': log_end_value("object"); SIMDJSON_TRY( visitor.visit_object_end(*this) ); goto scope_end;
case '"':
object_continue:
switch (*advance()) {
case ',':
SIMDJSON_TRY( visitor.increment_count(*this) );
SIMDJSON_TRY( visitor.visit_key(*this, key) );
{
auto key = advance();
if (simdjson_unlikely( *key != '"' )) { log_error("Key string missing at beginning of field in object"); return TAPE_ERROR; }
SIMDJSON_TRY( visitor.visit_key(*this, key) );
}
goto object_field;
default:
log_error("Key string missing at beginning of field in object"); return TAPE_ERROR;
case '}': log_end_value("object"); SIMDJSON_TRY( visitor.visit_object_end(*this) ); goto scope_end;
default: log_error("No comma between object fields"); return TAPE_ERROR;
}
}
scope_end:
depth--;
@@ -218,16 +219,12 @@ array_value:
}
array_continue:
if (*peek() == ']') {
advance();
log_end_value("array");
SIMDJSON_TRY( visitor.visit_array_end(*this) );
goto scope_end;
switch (*advance()) {
case ',': SIMDJSON_TRY( visitor.increment_count(*this) ); goto array_value;
case ']': log_end_value("array"); SIMDJSON_TRY( visitor.visit_array_end(*this) ); goto scope_end;
default: log_error("Missing comma between array values"); return TAPE_ERROR;
}
SIMDJSON_TRY( visitor.increment_count(*this) );
goto array_value;
document_end:
log_end_value("document");
SIMDJSON_TRY( visitor.visit_document_end(*this) );
@@ -306,15 +303,17 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::visit_root_primit
}
template<typename V>
simdjson_warn_unused simdjson_inline error_code json_iterator::visit_primitive(V &visitor, const uint8_t *value) noexcept {
// Use the fact that most scalars are going to be either strings or numbers.
if(*value == '"') {
return visitor.visit_string(*this, value);
} else if (((*value - '0') < 10) || (*value == '-')) {
return visitor.visit_number(*this, value);
}
// true, false, null are uncommon.
switch (*value) {
case '"': return visitor.visit_string(*this, value);
case 't': return visitor.visit_true_atom(*this, value);
case 'f': return visitor.visit_false_atom(*this, value);
case 'n': return visitor.visit_null_atom(*this, value);
case '-':
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
return visitor.visit_number(*this, value);
default:
log_error("Non-value found when value was expected!");
return TAPE_ERROR;
+57 -2
View File
@@ -38,8 +38,63 @@ namespace {
using namespace simd;
simdjson_inline bool is_ascii(const simd8x64<uint8_t>& in) {
return in.reduce_or().is_ascii();
// This identifies structural characters (comma, colon, braces, brackets),
// and ASCII white-space ('\r','\n','\t',' ').
simdjson_inline json_character_block json_character_block::classify(const simd::simd8x64<uint8_t>& in) {
// These lookups rely on the fact that anything < 127 will match the lower 4 bits, which is why
// we can't use the generic lookup_16.
const auto whitespace_table = simd8<uint8_t>::repeat_16(' ', 100, 100, 100, 17, 100, 113, 2, 100, '\t', '\n', 112, 100, '\r', 100, 100);
// The 6 operators (:,[]{}) have these values:
//
// , 2C
// : 3A
// [ 5B
// { 7B
// ] 5D
// } 7D
//
// If you use | 0x20 to turn [ and ] into { and }, the lower 4 bits of each character is unique.
// We exploit this, using a simd 4-bit lookup to tell us which character match against, and then
// match it (against | 0x20).
//
// To prevent recognizing other characters, everything else gets compared with 0, which cannot
// match due to the | 0x20.
//
// NOTE: Due to the | 0x20, this ALSO treats <FF> and <SUB> (control characters 0C and 1A) like ,
// and :. This gets caught in stage 2, which checks the actual character to ensure the right
// operators are in the right places.
const auto op_table = simd8<uint8_t>::repeat_16(
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, ':', '{', // : = 3A, [ = 5B, { = 7B
',', '}', 0, 0 // , = 2C, ] = 5D, } = 7D
);
// We compute whitespace and op separately. If later code only uses one or the
// other, given the fact that all functions are aggressively inlined, we can
// hope that useless computations will be omitted. This is namely case when
// minifying (we only need whitespace).
const uint64_t whitespace = in.eq({
_mm256_shuffle_epi8(whitespace_table, in.chunks[0]),
_mm256_shuffle_epi8(whitespace_table, in.chunks[1])
});
// Turn [ and ] into { and }
const simd8x64<uint8_t> curlified{
in.chunks[0] | 0x20,
in.chunks[1] | 0x20
};
const uint64_t op = curlified.eq({
_mm256_shuffle_epi8(op_table, in.chunks[0]),
_mm256_shuffle_epi8(op_table, in.chunks[1])
});
return { whitespace, op };
}
simdjson_inline bool is_ascii(const simd8x64<uint8_t>& input) {
return input.reduce_or().is_ascii();
}
simdjson_unused simdjson_inline simd8<bool> must_be_continuation(const simd8<uint8_t> prev1, const simd8<uint8_t> prev2, const simd8<uint8_t> prev3) {
+53 -1
View File
@@ -43,6 +43,58 @@ namespace {
using namespace simd;
// This identifies structural characters (comma, colon, braces, brackets),
// and ASCII white-space ('\r','\n','\t',' ').
simdjson_inline json_character_block json_character_block::classify(const simd::simd8x64<uint8_t>& in) {
// These lookups rely on the fact that anything < 127 will match the lower 4 bits, which is why
// we can't use the generic lookup_16.
const auto whitespace_table = simd8<uint8_t>::repeat_16(' ', 100, 100, 100, 17, 100, 113, 2, 100, '\t', '\n', 112, 100, '\r', 100, 100);
// The 6 operators (:,[]{}) have these values:
//
// , 2C
// : 3A
// [ 5B
// { 7B
// ] 5D
// } 7D
//
// If you use | 0x20 to turn [ and ] into { and }, the lower 4 bits of each character is unique.
// We exploit this, using a simd 4-bit lookup to tell us which character match against, and then
// match it (against | 0x20).
//
// To prevent recognizing other characters, everything else gets compared with 0, which cannot
// match due to the | 0x20.
//
// NOTE: Due to the | 0x20, this ALSO treats <FF> and <SUB> (control characters 0C and 1A) like ,
// and :. This gets caught in stage 2, which checks the actual character to ensure the right
// operators are in the right places.
const auto op_table = simd8<uint8_t>::repeat_16(
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, ':', '{', // : = 3A, [ = 5B, { = 7B
',', '}', 0, 0 // , = 2C, ] = 5D, } = 7D
);
// We compute whitespace and op separately. If later code only uses one or the
// other, given the fact that all functions are aggressively inlined, we can
// hope that useless computations will be omitted. This is namely case when
// minifying (we only need whitespace).
const uint64_t whitespace = in.eq({
_mm512_shuffle_epi8(whitespace_table, in.chunks[0])
});
// Turn [ and ] into { and }
const simd8x64<uint8_t> curlified{
in.chunks[0] | 0x20
};
const uint64_t op = curlified.eq({
_mm512_shuffle_epi8(op_table, in.chunks[0])
});
return { whitespace, op };
}
simdjson_inline bool is_ascii(const simd8x64<uint8_t>& input) {
return input.reduce_or().is_ascii();
}
@@ -89,7 +141,7 @@ simdjson_inline void bit_indexer::write(uint32_t idx, uint64_t bits) {
));
const __m512i start_index = _mm512_set1_epi32(idx);
const auto count = bitmask::count_ones(bits);
const auto count = count_ones(bits);
__m512i t0 = _mm512_cvtepu8_epi32(_mm512_castsi512_si128(indexes));
_mm512_storeu_si512(this->tail, _mm512_add_epi32(t0, start_index));
+18 -50
View File
@@ -37,61 +37,29 @@ namespace {
using namespace simd;
enum op_whitespace_t : uint8_t {
OPEN_OR_CLOSE = 1u << 0,
COLON = 1u << 1,
COMMA = 1u << 2,
TAB_CR_LF = 1u << 3,
SPACE = 1u << 4,
};
simdjson_constinit byte_classifier OP_WHITESPACE_CLASSIFIER({
_lookup_entry{ ' ', op_whitespace_t::SPACE },
{ '\t', op_whitespace_t::TAB_CR_LF },
{ '\r', op_whitespace_t::TAB_CR_LF },
{ '\n', op_whitespace_t::TAB_CR_LF },
{ ':', op_whitespace_t::COLON },
{ ',', op_whitespace_t::COMMA },
{ '{', op_whitespace_t::OPEN_OR_CLOSE },
{ '[', op_whitespace_t::OPEN_OR_CLOSE },
{ '}', op_whitespace_t::OPEN_OR_CLOSE },
{ ']', op_whitespace_t::OPEN_OR_CLOSE },
});
simdjson_inline json_character_block json_character_block::classify(const simd::simd8x64<uint8_t>& in) {
// Functional programming causes trouble with Visual Studio.
// Keeping this version in comments since it is much nicer:
// auto v = in.map<uint8_t>([&](simd8<uint8_t> chunk) {
// auto nib_lo = chunk & 0xf;
// auto nib_hi = chunk.shr<4>();
// auto shuf_lo = nib_lo.lookup_16<uint8_t>(16, 0, 0, 0, 0, 0, 0, 0, 0, 8, 12, 1, 2, 9, 0, 0);
// auto shuf_hi = nib_hi.lookup_16<uint8_t>(8, 0, 18, 4, 0, 1, 0, 1, 0, 0, 0, 3, 2, 1, 0, 0);
// return shuf_lo & shuf_hi;
// });
simd8x64<uint8_t> op_whitespace = OP_WHITESPACE_CLASSIFIER[in];
const simd8<uint8_t> table1(16, 0, 0, 0, 0, 0, 0, 0, 0, 8, 12, 1, 2, 9, 0, 0);
const simd8<uint8_t> table2(8, 0, 18, 4, 0, 1, 0, 1, 0, 0, 0, 3, 2, 1, 0, 0);
// We compute whitespace and op separately. If the code later only use one or the
// other, given the fact that all functions are aggressively inlined, we can
// hope that useless computations will be omitted. This is namely case when
// minifying (we only need whitespace). *However* if we only need spaces,
// it is likely that we will still compute 'v' above with two lookup_16: one
// could do it a bit cheaper. This is in contrast with the x64 implementations
// where we can, efficiently, do the white space and structural matching
// separately. One reason for this difference is that on ARM NEON, the table
// lookups either zero or leave unchanged the characters exceeding 0xF whereas
// on x64, the equivalent instruction (pshufb) automatically applies a mask,
// ignoring the 4 most significant bits. Thus the x64 implementation is
// optimized differently. This being said, if you use this code strictly
// just for minification (or just to identify the structural characters),
// there is a small untaken optimization opportunity here. We deliberately
// do not pick it up.
simd8x64<uint8_t> v(
(in.chunks[0] & 0xf).lookup_16(table1) & (in.chunks[0].shr<4>()).lookup_16(table2),
(in.chunks[1] & 0xf).lookup_16(table1) & (in.chunks[1].shr<4>()).lookup_16(table2),
(in.chunks[2] & 0xf).lookup_16(table1) & (in.chunks[2].shr<4>()).lookup_16(table2),
(in.chunks[3] & 0xf).lookup_16(table1) & (in.chunks[3].shr<4>()).lookup_16(table2)
);
uint64_t op = op_whitespace.any_bits_set(
op_whitespace_t::SPACE | op_whitespace_t::TAB_CR_LF
uint64_t op = simd8x64<bool>(
v.chunks[0].any_bits_set(0x7),
v.chunks[1].any_bits_set(0x7),
v.chunks[2].any_bits_set(0x7),
v.chunks[3].any_bits_set(0x7)
).to_bitmask();
uint64_t whitespace = op_whitespace.any_bits_set(
op_whitespace_t::COLON | op_whitespace_t::COMMA | op_whitespace_t::OPEN_OR_CLOSE
uint64_t whitespace = simd8x64<bool>(
v.chunks[0].any_bits_set(0x18),
v.chunks[1].any_bits_set(0x18),
v.chunks[2].any_bits_set(0x18),
v.chunks[3].any_bits_set(0x18)
).to_bitmask();
return { whitespace, op };
+59
View File
@@ -38,6 +38,65 @@ namespace {
using namespace simd;
simdjson_inline json_character_block json_character_block::classify(const simd::simd8x64<uint8_t>& in) {
// These lookups rely on the fact that anything < 127 will match the lower 4 bits, which is why
// we can't use the generic lookup_16.
auto whitespace_table = simd8<uint8_t>::repeat_16(' ', 100, 100, 100, 17, 100, 113, 2, 100, '\t', '\n', 112, 100, '\r', 100, 100);
// The 6 operators (:,[]{}) have these values:
//
// , 2C
// : 3A
// [ 5B
// { 7B
// ] 5D
// } 7D
//
// If you use | 0x20 to turn [ and ] into { and }, the lower 4 bits of each character is unique.
// We exploit this, using a simd 4-bit lookup to tell us which character match against, and then
// match it (against | 0x20).
//
// To prevent recognizing other characters, everything else gets compared with 0, which cannot
// match due to the | 0x20.
//
// NOTE: Due to the | 0x20, this ALSO treats <FF> and <SUB> (control characters 0C and 1A) like ,
// and :. This gets caught in stage 2, which checks the actual character to ensure the right
// operators are in the right places.
const auto op_table = simd8<uint8_t>::repeat_16(
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, ':', '{', // : = 3A, [ = 5B, { = 7B
',', '}', 0, 0 // , = 2C, ] = 5D, } = 7D
);
// We compute whitespace and op separately. If the code later only use one or the
// other, given the fact that all functions are aggressively inlined, we can
// hope that useless computations will be omitted. This is namely case when
// minifying (we only need whitespace).
const uint64_t whitespace = in.eq({
_mm_shuffle_epi8(whitespace_table, in.chunks[0]),
_mm_shuffle_epi8(whitespace_table, in.chunks[1]),
_mm_shuffle_epi8(whitespace_table, in.chunks[2]),
_mm_shuffle_epi8(whitespace_table, in.chunks[3])
});
// Turn [ and ] into { and }
const simd8x64<uint8_t> curlified{
in.chunks[0] | 0x20,
in.chunks[1] | 0x20,
in.chunks[2] | 0x20,
in.chunks[3] | 0x20
};
const uint64_t op = curlified.eq({
_mm_shuffle_epi8(op_table, in.chunks[0]),
_mm_shuffle_epi8(op_table, in.chunks[1]),
_mm_shuffle_epi8(op_table, in.chunks[2]),
_mm_shuffle_epi8(op_table, in.chunks[3])
});
return { whitespace, op };
}
simdjson_inline bool is_ascii(const simd8x64<uint8_t>& input) {
return input.reduce_or().is_ascii();
}
+12 -12
View File
@@ -687,24 +687,24 @@ namespace parse_api_tests {
return parser_load_empty() &&
parser_moving_parser() &&
parser_parse() &&
// parser_parse_many() &&
// #ifdef SIMDJSON_ENABLE_DEPRECATED_API
// parser_parse_many_deprecated() &&
// #endif
// parser_parse_many_empty() &&
// parser_parse_many_empty_batches() &&
parser_parse_many() &&
#ifdef SIMDJSON_ENABLE_DEPRECATED_API
parser_parse_many_deprecated() &&
#endif
parser_parse_many_empty() &&
parser_parse_many_empty_batches() &&
parser_load() &&
// parser_load_many() &&
// #ifdef SIMDJSON_ENABLE_DEPRECATED_API
// parser_load_many_deprecated() &&
// #endif
parser_load_many() &&
#ifdef SIMDJSON_ENABLE_DEPRECATED_API
parser_load_many_deprecated() &&
#endif
#if SIMDJSON_EXCEPTIONS
parser_moving_parser_and_recovering_struct() &&
parser_moving_parser_and_recovering() &&
parser_parse_exception() &&
// parser_parse_many_exception() &&
parser_parse_many_exception() &&
parser_load_exception() &&
// parser_load_many_exception() &&
parser_load_many_exception() &&
issue679() &&
#endif
true;
+9 -1
View File
@@ -51,6 +51,10 @@ namespace document_tests {
simdjson::dom::array array;
ASSERT_SUCCESS( parser.parse(smalljson).get(array) );
ASSERT_EQUAL( array.size(), 3 );
ASSERT_EQUAL( *array.begin() < *array.end(), true );
ASSERT_EQUAL( *array.end() < *array.begin(), false );
ASSERT_EQUAL( *array.begin() == *array.begin(), true );
ASSERT_EQUAL( *array.begin() == *array.end(), false );
return true;
}
bool count_object_example() {
@@ -60,6 +64,10 @@ namespace document_tests {
simdjson::dom::object object;
ASSERT_SUCCESS( parser.parse(smalljson).get(object) );
ASSERT_EQUAL( object.size(), 3 );
ASSERT_EQUAL( (*object.begin()).value < (*object.end()).value, true );
ASSERT_EQUAL( (*object.end()).value < (*object.begin()).value, false );
ASSERT_EQUAL( (*object.begin()).value == (*object.begin()).value, true );
ASSERT_EQUAL( (*object.begin()).value == (*object.end()).value, false );
return true;
}
bool padded_with_open_bracket() {
@@ -232,4 +240,4 @@ int main(int argc, char *argv[]) {
} else {
return EXIT_FAILURE;
}
}
}
+1
View File
@@ -23,6 +23,7 @@ add_cpp_test(ondemand_ordering_tests LABELS ondemand acceptance per_impl
add_cpp_test(ondemand_parse_api_tests LABELS ondemand acceptance per_implementation)
add_cpp_test(ondemand_readme_examples LABELS ondemand acceptance per_implementation)
add_cpp_test(ondemand_scalar_tests LABELS ondemand acceptance per_implementation)
add_cpp_test(ondemand_to_string LABELS ondemand acceptance per_implementation)
add_cpp_test(ondemand_twitter_tests LABELS ondemand acceptance per_implementation)
add_cpp_test(ondemand_wrong_type_error_tests LABELS ondemand acceptance per_implementation)
add_cpp_test(ondemand_iterate_many_csv LABELS ondemand acceptance per_implementation)
+9 -3
View File
@@ -144,14 +144,20 @@ namespace parse_api_tests {
}
{
std::string json = "12";
std::string json = "12345642314123421321321321321321312321321321321312";
json.shrink_to_fit();
cout << "- string, 0 padding" << endl;
ASSERT_ERROR( parser.iterate(json), INSUFFICIENT_PADDING );
ASSERT_SUCCESS( parser.iterate(json) );
}
{
std::string json = "12345642314123421321321321321321312321321321321312";
json.shrink_to_fit();
cout << "- string, 0 padding" << endl;
ASSERT_ERROR( parser.iterate((const std::string&)json), INSUFFICIENT_PADDING );
// It's actually kind of hard to allocate "just enough" capacity, since the string tends
// to grow more than you tell it to.
}
TEST_SUCCEED();
}
+174
View File
@@ -20,7 +20,39 @@ bool string2() {
}
bool to_string_example_no_except() {
TEST_START();
auto json = R"({
"name": "Daniel",
"age": 42
})"_padded;
ondemand::parser parser;
ondemand::document doc;
auto err = parser.iterate(json).get(doc);
if(err) { return false; }
std::string name;
err = doc["name"].get_string(name);
if(err) { return false; }
TEST_SUCCEED();
}
#if SIMDJSON_EXCEPTIONS
bool to_string_example() {
TEST_START();
auto json = R"({
"name": "Daniel",
"age": 42
})"_padded;
ondemand::parser parser;
ondemand::document doc = parser.iterate(json);
std::string name;
doc["name"].get_string(name);
ASSERT_EQUAL(name, "Daniel");
TEST_SUCCEED();
}
bool gen_raw1() {
TEST_START();
simdjson::ondemand::parser parser;
@@ -73,6 +105,144 @@ bool at_end() {
TEST_SUCCEED();
}
bool examplecrt() {
TEST_START();
padded_string padded_input_json = R"([
{ "monitor": [
{ "id": "monitor", "type": "toggle", "label": "monitor" },
{ "id": "profile", "type": "selector", "label": "collection" },
{ "id": "overlay", "type": "selector", "label": "overlay" },
{ "id": "zoom", "type": "toggleSlider", "label": "zoom" }
] },
{ "crt": [
{ "id": "system", "type": "multi", "label": "system", "choices": "PAL, NTSC" },
{ "type": "spacer" },
{ "id": "brightness", "type": "slider", "icon": "brightness" },
{ "id": "contrast", "type": "slider", "icon": "contrast" },
{ "id": "saturation", "type": "slider", "icon": "saturation" },
{ "type": "spacer" },
{ "id": "overscan", "type": "toggleSlider", "label": "overscan" },
{ "type": "spacer" },
{ "id": "emulation", "type": "toggle", "label": "CRT emulation" },
{ "type": "spacer" },
{ "id": "curve", "type": "toggleSlider", "label": "curve" },
{ "id": "bleed", "type": "toggleSlider", "label": "bleed" },
{ "id": "vignette", "type": "toggleSlider", "label": "vignette" },
{ "id": "scanlines", "type": "toggleSlider", "label": "scanlines" },
{ "id": "gridlines", "type": "toggleSlider", "label": "gridlines" },
{ "id": "glow", "type": "toggleSlider", "label": "glow" },
{ "id": "flicker", "type": "toggleSlider", "label": "flicker" },
{ "id": "noise", "type": "toggleSlider", "label": "noise" },
{}
] }
])"_padded;
auto parser = ondemand::parser{};
auto doc = parser.iterate(padded_input_json);
auto root_array = doc.get_array();
// the root should be an object, not an array, but that's the JSON we are
// given.
for (ondemand::object node : root_array) {
// We know that we are going to have just one element in the object.
for (auto field : node) {
std::cout << "\n\ntop level:" << field.key() << std::endl;
// You can get a proper std::string_view for the key with:
// std::string_view key = field.unescaped_key();
// and second for-range loop to get child-elements here
for (ondemand::object inner_object : field.value()) {
auto i = inner_object.begin();
if (i == inner_object.end()) {
std::cout << "empty object" << std::endl;
continue;
} else {
for (; i != inner_object.end(); ++i) {
auto inner_field = *i;
std::cout << '"' << inner_field.key()
<< "\" : " << inner_field.value() << ", ";
// You can get proper std::string_view for the key and value with:
// std::string_view inner_key = field.unescaped_key();
// std::string_view value_str = field.value();
}
}
std::cout << std::endl;
}
// You can break here if you only want just the first element.
// break;
}
}
TEST_SUCCEED();
}
bool examplecrt_realloc() {
TEST_START();
std::string unpadded_input_json = R"([
{ "monitor": [
{ "id": "monitor", "type": "toggle", "label": "monitor" },
{ "id": "profile", "type": "selector", "label": "collection" },
{ "id": "overlay", "type": "selector", "label": "overlay" },
{ "id": "zoom", "type": "toggleSlider", "label": "zoom" }
] },
{ "crt": [
{ "id": "system", "type": "multi", "label": "system", "choices": "PAL, NTSC" },
{ "type": "spacer" },
{ "id": "brightness", "type": "slider", "icon": "brightness" },
{ "id": "contrast", "type": "slider", "icon": "contrast" },
{ "id": "saturation", "type": "slider", "icon": "saturation" },
{ "type": "spacer" },
{ "id": "overscan", "type": "toggleSlider", "label": "overscan" },
{ "type": "spacer" },
{ "id": "emulation", "type": "toggle", "label": "CRT emulation" },
{ "type": "spacer" },
{ "id": "curve", "type": "toggleSlider", "label": "curve" },
{ "id": "bleed", "type": "toggleSlider", "label": "bleed" },
{ "id": "vignette", "type": "toggleSlider", "label": "vignette" },
{ "id": "scanlines", "type": "toggleSlider", "label": "scanlines" },
{ "id": "gridlines", "type": "toggleSlider", "label": "gridlines" },
{ "id": "glow", "type": "toggleSlider", "label": "glow" },
{ "id": "flicker", "type": "toggleSlider", "label": "flicker" },
{ "id": "noise", "type": "toggleSlider", "label": "noise" },
{}
] }
])";
unpadded_input_json.shrink_to_fit();
auto parser = ondemand::parser{};
auto doc = parser.iterate(unpadded_input_json);
auto root_array = doc.get_array();
// the root should be an object, not an array, but that's the JSON we are
// given.
for (ondemand::object node : root_array) {
// We know that we are going to have just one element in the object.
for (auto field : node) {
std::cout << "\n\ntop level:" << field.key() << std::endl;
// You can get a proper std::string_view for the key with:
// std::string_view key = field.unescaped_key();
// and second for-range loop to get child-elements here
for (ondemand::object inner_object : field.value()) {
auto i = inner_object.begin();
if (i == inner_object.end()) {
std::cout << "empty object" << std::endl;
continue;
} else {
for (; i != inner_object.end(); ++i) {
auto inner_field = *i;
std::cout << '"' << inner_field.key()
<< "\" : " << inner_field.value() << ", ";
// You can get proper std::string_view for the key and value with:
// std::string_view inner_key = field.unescaped_key();
// std::string_view value_str = field.value();
}
}
std::cout << std::endl;
}
// You can break here if you only want just the first element.
// break;
}
}
TEST_SUCCEED();
}
bool number_tests() {
TEST_START();
ondemand::parser parser;
@@ -1352,10 +1522,14 @@ bool run() {
&& current_location_user_error()
&& current_location_out_of_bounds()
&& current_location_no_error()
&& to_string_example_no_except()
#if SIMDJSON_EXCEPTIONS
&& to_string_example()
&& raw_string()
&& number_tests()
&& current_location_tape_error_with_except()
&& examplecrt()
&& examplecrt_realloc()
#endif
;
}
+210
View File
@@ -0,0 +1,210 @@
#include "simdjson.h"
#include "test_ondemand.h"
using namespace simdjson;
namespace json_package_tests {
using namespace std;
bool baby() {
TEST_START();
auto json = R"({
"name": "Daniel",
"age": 42
})"_padded;
ondemand::parser parser;
ondemand::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
simdjson::ondemand::object main_object;
ASSERT_SUCCESS(doc.get_object().get(main_object));
std::string name;
ASSERT_SUCCESS(main_object["name"].get_string(name));
ASSERT_EQUAL(name, "Daniel");
uint64_t age;
ASSERT_SUCCESS(main_object["age"].get_uint64().get(age));
ASSERT_EQUAL(age, 42);
TEST_SUCCEED();
}
bool thirtysecondsofcode() {
TEST_START();
auto json = R"({
"name": "30-seconds-of-code",
"private": true,
"version": "10.0.0",
"description": "30 seconds of code website.",
"exports": "./index.js",
"author": "chalarangelo",
"type": "module",
"devDependencies": {
"@jsiqle/core": "^3.0.0",
"astro": "^3.2.0",
"chalk": "^5.3.0",
"eslint": "^8.50.0",
"eslint-config-prettier": "^9.0.0",
"front-matter": "^4.0.2",
"fs-extra": "^11.1.1",
"glob": "^10.3.10",
"hast-util-to-html": "^9.0.0",
"js-yaml": "^4.1.0",
"mdast-util-to-hast": "^13.0.2",
"prettier": "^3.0.3",
"prettier-plugin-astro": "^0.12.0",
"prismjs": "^1.29.0",
"remark": "^15.0.1",
"remark-gfm": "^4.0.0",
"sass": "^1.68.0",
"sharp": "^0.32.6",
"unist-util-select": "^5.0.0",
"unist-util-visit": "^5.0.0",
"unist-util-visit-parents": "^6.0.1",
"webfonts-generator": "^0.4.0"
},
"imports": {
"#blocks/*": "./src/blocks/*.js",
"#components/*": "./src/components/*.astro",
"#layouts/*": "./src/layouts/*.astro",
"#settings/*": "./src/settings/*.js",
"#prefabs": "./src/prefabs/index.js",
"#utils": "./src/utils/index.js",
"#utils/search": "./src/utils/search.js"
},
"scripts": {
"predev": "NODE_ENV=development node ./src/scripts/develop.js",
"dev": "astro dev --port 8000",
"start": "astro dev --port 8000",
"prebuild": "NODE_ENV=production node ./src/scripts/build.js",
"build": "astro build",
"preview": "astro preview --port 9000",
"watch": "NODE_ENV=development node ./src/scripts/watch.js",
"console": "NODE_ENV=production node ./src/scripts/console.js",
"create": "NODE_ENV=production node ./src/scripts/create.js",
"icons": "NODE_ENV=production node ./src/scripts/icons.js",
"manifest": "NODE_ENV=production node ./src/scripts/manifest.js"
},
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/30-seconds/30-seconds-of-code"
},
"bugs": {
"url": "https://github.com/30-seconds/30-seconds-of-code/issues"
},
"browserslist": [
"> 0.5% and last 4 versions and not dead and not ie>0 and not op_mini all and not and_uc>0 and not edge<79"
],
"engines": {
"node": ">=18.14.2"
}
})"_padded;
ondemand::parser parser;
ondemand::document doc;
ASSERT_SUCCESS(parser.iterate(json).get(doc));
simdjson::ondemand::object main_object;
ASSERT_SUCCESS(doc.get_object().get(main_object));
simdjson::ondemand::raw_json_string key;
simdjson::ondemand::value value;
for (auto field : main_object) {
// Throw error if getting key or value fails.
ASSERT_SUCCESS(field.key().get(key));
ASSERT_SUCCESS(field.value().get(value));
if (key == "name") {
std::string name;
ASSERT_SUCCESS(value.get_string(name));
ASSERT_EQUAL(name, "30-seconds-of-code");
} else if (key == "main") {
std::string main;
ASSERT_SUCCESS(value.get_string(main));
// unused
} else if (key == "exports") {
simdjson::ondemand::json_type exports_type;
if (!value.type().get(exports_type)) {
std::string_view exports;
switch (exports_type) {
case simdjson::ondemand::json_type::object: {
simdjson::ondemand::object exports_object;
if (!value.get_object().get(exports_object) &&
!exports_object.raw_json().get(exports)) {
// unused
}
break;
}
case simdjson::ondemand::json_type::array: {
simdjson::ondemand::array exports_array;
if (!value.get_array().get(exports_array) &&
!exports_array.raw_json().get(exports)) {
// unused
}
break;
}
case simdjson::ondemand::json_type::string: {
if (!value.get_string().get(exports)) {
ASSERT_EQUAL(exports, "./index.js");
}
break;
}
default:
break;
}
}
} else if (key == "imports") {
simdjson::ondemand::json_type imports_type;
if (!value.type().get(imports_type)) {
std::string_view imports;
switch (imports_type) {
case simdjson::ondemand::json_type::object: {
simdjson::ondemand::object imports_object;
if (!value.get_object().get(imports_object) &&
!imports_object.raw_json().get(imports)) {
ASSERT_EQUAL(imports, R"({
"#blocks/*": "./src/blocks/*.js",
"#components/*": "./src/components/*.astro",
"#layouts/*": "./src/layouts/*.astro",
"#settings/*": "./src/settings/*.js",
"#prefabs": "./src/prefabs/index.js",
"#utils": "./src/utils/index.js",
"#utils/search": "./src/utils/search.js"
})");
}
break;
}
case simdjson::ondemand::json_type::array: {
simdjson::ondemand::array imports_array;
if (!value.get_array().get(imports_array) &&
!imports_array.raw_json().get(imports)) {
// unused
}
break;
}
case simdjson::ondemand::json_type::string: {
if (!value.get_string().get(imports)) {
// unused
}
break;
}
default:
break;
}
}
} else if (key == "type") {
std::string_view type;
if (!value.get_string().get(type) &&
(type == "commonjs" || type == "module")) {
ASSERT_EQUAL(type, "module");
}
}
}
TEST_SUCCEED();
}
bool run() { return thirtysecondsofcode() && baby(); }
} // namespace json_package_tests
int main(int argc, char *argv[]) {
return test_main(argc, argv, json_package_tests::run);
}
+1 -5
View File
@@ -237,11 +237,7 @@ void test() {
for (size_t i = 0; i < sizeof(badsequences)/sizeof(badsequences[0]); i++) {
size_t len = std::strlen(badsequences[i]);
if (simdjson::validate_utf8(badsequences[i], len)) {
printf("bug lookup2 badsequences[%zu] ", i);
for (size_t j = 0; j < len; j++) {
printf("%02x ", (unsigned char)badsequences[i][j]);
}
printf("\n");
printf("bug lookup2 badsequences[%zu]\n", i);
abort();
}
}
-2
View File
@@ -167,8 +167,6 @@ print("the commandline is {}".format(cp.args))
if(cp.returncode != 0):
print("Failed to run doxygen")
#ipe = subprocess.Popen(["doxygen"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=maindir)
#doxygenresult = pipe.communicate()[0].decode().strip()
pattern = re.compile("https://simdjson.org/api/(\d+\.\d+\.\d+)/index.html")
readmefile = maindir + os.sep + "README.md"