mirror of
https://github.com/simdjson/simdjson
synced 2026-06-08 17:27:07 +00:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d1de135ebd | |||
| 0edf1008c9 | |||
| 6a0d7bcb55 | |||
| 6f196d0af0 | |||
| c3414a1234 | |||
| 5d2107b470 | |||
| 90aa198430 | |||
| 02b488b88d | |||
| 47e3e95867 | |||
| 588c0677f5 | |||
| 68ba9a1b2a | |||
| 6fed6bd29b | |||
| cc24bb4114 |
Vendored
+3
-2
@@ -3,7 +3,7 @@
|
||||
{"column": 95 },
|
||||
{"column": 120 }
|
||||
],
|
||||
"files.trimTrailingWhitespace": true,
|
||||
"files.trimTrailingWhitespace": false,
|
||||
"files.associations": {
|
||||
".clangd": "yaml",
|
||||
"array": "cpp",
|
||||
@@ -99,5 +99,6 @@
|
||||
"shared_mutex": "cpp",
|
||||
"ranges": "cpp",
|
||||
"span": "cpp"
|
||||
}
|
||||
},
|
||||
"editor.trimAutoWhitespace": true
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
|
||||
[/badge.svg)](https://simdjson.org/plots.html)
|
||||

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

|
||||
[![][license img]][license]
|
||||
|
||||
[](https://simdjson.github.io/simdjson/)
|
||||
|
||||
+31
-31
@@ -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"]`.
|
||||
@@ -381,7 +381,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()); }
|
||||
> }
|
||||
> ```
|
||||
>
|
||||
@@ -445,7 +445,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 +454,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 +597,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 +910,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 +976,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 +1010,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 +1039,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 +1088,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 +1102,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 +1294,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 +1324,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 +1392,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;
|
||||
|
||||
+2
-2
@@ -329,10 +329,10 @@ int main(void) {
|
||||
simdjson::dom::parser parser;
|
||||
simdjson::dom::element tweets; // invalid until the get() succeeds
|
||||
auto error = parser.load("twitter.json").get(tweets);
|
||||
if(error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
|
||||
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
|
||||
uint64_t identifier;
|
||||
error = tweets["statuses"].at(0)["id"].get(identifier);
|
||||
if(error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
|
||||
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
|
||||
std::cout << identifier << std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -101,8 +101,8 @@ by comparing it with the null pointer.
|
||||
|
||||
```c++
|
||||
auto my_implementation = simdjson::get_available_implementations()["haswell"];
|
||||
if(! my_implementation) { exit(1); }
|
||||
if(! my_implementation->supported_by_runtime_system()) { exit(1); }
|
||||
if (! my_implementation) { exit(1); }
|
||||
if (! my_implementation->supported_by_runtime_system()) { exit(1); }
|
||||
simdjson::get_active_implementation() = my_implementation;
|
||||
```
|
||||
|
||||
@@ -113,7 +113,7 @@ You should call `supported_by_runtime_system()` to compare the processor's featu
|
||||
|
||||
```c++
|
||||
for (auto implementation : simdjson::get_available_implementations()) {
|
||||
if(implementation->supported_by_runtime_system()) {
|
||||
if (implementation->supported_by_runtime_system()) {
|
||||
cout << implementation->name() << ": " << implementation->description() << endl;
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -196,12 +196,12 @@ Let us illustrate the idea with code:
|
||||
simdjson::ondemand::parser parser;
|
||||
simdjson::ondemand::document_stream stream;
|
||||
auto error = parser.iterate_many(json).get(stream);
|
||||
if( error ) { /* do something */ }
|
||||
if (error) { /* do something */ }
|
||||
auto i = stream.begin();
|
||||
size_t count{0};
|
||||
for(; i != stream.end(); ++i) {
|
||||
auto doc = *i;
|
||||
if(!i.error()) {
|
||||
if (!i.error()) {
|
||||
std::cout << "got full document at " << i.current_index() << std::endl;
|
||||
std::cout << i.source() << std::endl;
|
||||
count++;
|
||||
@@ -237,7 +237,7 @@ Consider the following example where a truncated document (`{"key":"intentionall
|
||||
simdjson::ondemand::parser parser;
|
||||
simdjson::ondemand::document_stream stream;
|
||||
auto error = parser.iterate_many(json,json.size()).get(stream);
|
||||
if(error) { std::cerr << error << std::endl; return; }
|
||||
if (error) { std::cerr << error << std::endl; return; }
|
||||
for(auto i = stream.begin(); i != stream.end(); ++i) {
|
||||
std::cout << i.source() << std::endl;
|
||||
}
|
||||
@@ -269,7 +269,7 @@ Example:
|
||||
// we pass 'true' to the allow_comma parameter, the batch size will be set to at least
|
||||
// the document size.
|
||||
auto error = parser.iterate_many(json, 32, true).get(doc_stream);
|
||||
if(error) { std::cerr << error << std::endl; return; }
|
||||
if (error) { std::cerr << error << std::endl; return; }
|
||||
for (auto doc : doc_stream) {
|
||||
std::cout << doc.type() << std::endl;
|
||||
}
|
||||
|
||||
@@ -679,11 +679,11 @@ in production systems:
|
||||
ondemand::object c1 = parent["child1"];
|
||||
// c1 owns the focus
|
||||
//
|
||||
if(std::string_view(c1["name"]) != "John") { ... }
|
||||
if (std::string_view(c1["name"]) != "John") { ... }
|
||||
// c2 attempts to grab the focus from parent but fails
|
||||
ondemand::object c2 = parent["child2"];
|
||||
// c2 is now in an unsafe state and the following line would be unsafe
|
||||
// if(std::string_view(c2["name"]) != "Daniel") { return false; }
|
||||
// if (std::string_view(c2["name"]) != "Daniel") { return false; }
|
||||
```
|
||||
|
||||
A correct usage is given by the following example:
|
||||
@@ -697,7 +697,7 @@ in production systems:
|
||||
{
|
||||
ondemand::object c1 = parent["child1"];
|
||||
// c1 grabbed the focus from parent
|
||||
if(std::string_view(c1["name"]) != "John") { return false; }
|
||||
if (std::string_view(c1["name"]) != "John") { return false; }
|
||||
}
|
||||
// c1 went out of scope, so its destructor was called and the focus
|
||||
// was handed back to parent.
|
||||
@@ -705,7 +705,7 @@ in production systems:
|
||||
ondemand::object c2 = parent["child2"];
|
||||
// c2 grabbed the focus from parent
|
||||
// the following is safe:
|
||||
if(std::string_view(c2["name"]) != "Daniel") { return false; }
|
||||
if (std::string_view(c2["name"]) != "Daniel") { return false; }
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
+3
-3
@@ -184,12 +184,12 @@ Let us illustrate the idea with code:
|
||||
simdjson::dom::parser parser;
|
||||
simdjson::dom::document_stream stream;
|
||||
auto error = parser.parse_many(json).get(stream);
|
||||
if( error ) { /* do something */ }
|
||||
if (error) { /* do something */ }
|
||||
auto i = stream.begin();
|
||||
size_t count{0};
|
||||
for(; i != stream.end(); ++i) {
|
||||
auto doc = *i;
|
||||
if(!doc.error()) {
|
||||
if (!doc.error()) {
|
||||
std::cout << "got full document at " << i.current_index() << std::endl;
|
||||
std::cout << i.source() << std::endl;
|
||||
count++;
|
||||
@@ -225,7 +225,7 @@ Consider the following example where a truncated document (`{"key":"intentionall
|
||||
simdjson::dom::parser parser;
|
||||
simdjson::dom::document_stream stream;
|
||||
auto error = parser.parse_many(json,json.size()).get(stream);
|
||||
if(error) { std::cerr << error << std::endl; return; }
|
||||
if (error) { std::cerr << error << std::endl; return; }
|
||||
for(auto doc : stream) {
|
||||
std::cout << doc << std::endl;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,277 @@
|
||||
number of iterations 3000
|
||||
|
||||
| Original (jsonexamples/twitter-padded-numbers.json) 773
|
||||
|- 18-digit numbers (jsonexamples/twitter18.json) 757
|
||||
|- 8-digit numbers (jsonexamples/twitter8.json) 766
|
||||
|- 1-digit numbers (jsonexamples/twitter1.json) 544
|
||||
| |- strings -> no backslashes (jsonexamples/twitter1-nobackslash.json) 499
|
||||
| |- strings -> no utf8 (jsonexamples/twitter1-nobackslash-noutf8.json) 489
|
||||
| |- strings -> empty strings (jsonexamples/twitter1-emptystring.json) 351
|
||||
| |- no number/bool/null -> all strings (jsonexamples/twitter-allstrings.json) 216
|
||||
| |- no objects -> all arrays (jsonexamples/twitter-allstrings-arrays.json) 427
|
||||
| |- arrays -> no nested arrays (jsonexamples/twitter-allstrings-flatarray.json) 3
|
||||
|
||||
jsonexamples/twitter-padded-numbers.json
|
||||
========================================
|
||||
10306 blocks - 659626 bytes - 55262 structurals ( 8.4 %)
|
||||
special blocks with: utf8 2289 ( 22.2 %) - escape 604 ( 5.9 %) - 0 structurals 1270 ( 12.3 %) - 1+ structurals 9037 ( 87.7 %) - 8+ structurals 2576 ( 25.0 %) - 16+ structurals 0 ( 0.0 %)
|
||||
special block flips: utf8 1096 ( 10.6 %) - escape 646 ( 6.3 %) - 0 structurals 932 ( 9.0 %) - 1+ structurals 932 ( 9.0 %) - 8+ structurals 2843 ( 27.6 %) - 16+ structurals 0 ( 0.0 %)
|
||||
|
||||
All Stages (excluding allocation)
|
||||
| Speed : 20.7212 ns per block ( 97.66%) - 0.3238 ns per byte - 3.8647 ns per structural - 3.0885 GB/s
|
||||
| Cycles : 64.1524 per block ( 97.86%) - 1.0024 per byte - 11.9652 per structural - 3.096 GHz est. frequency
|
||||
| Instructions : 200.2565 per block (100.00%) - 3.1291 per byte - 37.3502 per structural - 3.122 per cycle
|
||||
| Misses : 873 branch misses ( 93.08%) - 0 cache misses ( 0.00%) - 28032.00 cache references
|
||||
|- Stage 1
|
||||
| Speed : 6.0073 ns per block ( 28.31%) - 0.0939 ns per byte - 1.1204 ns per structural - 10.6534 GB/s
|
||||
| Cycles : 18.6130 per block ( 28.39%) - 0.2908 per byte - 3.4715 per structural - 3.098 GHz est. frequency
|
||||
| Instructions : 61.8107 per block ( 30.87%) - 0.9658 per byte - 11.5284 per structural - 3.321 per cycle
|
||||
| Misses : 93 branch misses ( 9.92%) - 0 cache misses ( 0.00%) - 12229.00 cache references
|
||||
|- Stage 2
|
||||
| Speed : 14.6555 ns per block ( 69.07%) - 0.2290 ns per byte - 2.7334 ns per structural - 4.3668 GB/s
|
||||
| Cycles : 45.3587 per block ( 69.19%) - 0.7088 per byte - 8.4599 per structural - 3.095 GHz est. frequency
|
||||
| Instructions : 138.4458 per block ( 69.13%) - 2.1633 per byte - 25.8217 per structural - 3.052 per cycle
|
||||
| Misses : 773 branch misses ( 82.42%) - 0 cache misses ( 0.00%) - 15925.00 cache references
|
||||
|
||||
4682.2 documents parsed per second (best)
|
||||
|
||||
jsonexamples/twitter18.json
|
||||
===========================
|
||||
10306 blocks - 659626 bytes - 55262 structurals ( 8.4 %)
|
||||
special blocks with: utf8 2289 ( 22.2 %) - escape 604 ( 5.9 %) - 0 structurals 1270 ( 12.3 %) - 1+ structurals 9037 ( 87.7 %) - 8+ structurals 2585 ( 25.1 %) - 16+ structurals 0 ( 0.0 %)
|
||||
special block flips: utf8 1096 ( 10.6 %) - escape 646 ( 6.3 %) - 0 structurals 932 ( 9.0 %) - 1+ structurals 932 ( 9.0 %) - 8+ structurals 2847 ( 27.6 %) - 16+ structurals 0 ( 0.0 %)
|
||||
|
||||
All Stages (excluding allocation)
|
||||
| Speed : 21.1739 ns per block ( 93.63%) - 0.3309 ns per byte - 3.9492 ns per structural - 3.0225 GB/s
|
||||
| Cycles : 65.5570 per block ( 97.40%) - 1.0244 per byte - 12.2271 per structural - 3.096 GHz est. frequency
|
||||
| Instructions : 205.6160 per block (100.00%) - 3.2129 per byte - 38.3498 per structural - 3.136 per cycle
|
||||
| Misses : 856 branch misses ( 97.20%) - 0 cache misses ( 0.00%) - 28297.00 cache references
|
||||
|- Stage 1
|
||||
| Speed : 5.9968 ns per block ( 26.52%) - 0.0937 ns per byte - 1.1185 ns per structural - 10.6720 GB/s
|
||||
| Cycles : 18.5808 per block ( 27.61%) - 0.2903 per byte - 3.4655 per structural - 3.098 GHz est. frequency
|
||||
| Instructions : 61.8107 per block ( 30.06%) - 0.9658 per byte - 11.5284 per structural - 3.327 per cycle
|
||||
| Misses : 103 branch misses ( 11.70%) - 0 cache misses ( 0.00%) - 12271.00 cache references
|
||||
|- Stage 2
|
||||
| Speed : 15.0964 ns per block ( 66.75%) - 0.2359 ns per byte - 2.8157 ns per structural - 4.2393 GB/s
|
||||
| Cycles : 46.7271 per block ( 69.43%) - 0.7301 per byte - 8.7151 per structural - 3.095 GHz est. frequency
|
||||
| Instructions : 143.8053 per block ( 69.94%) - 2.2470 per byte - 26.8213 per structural - 3.078 per cycle
|
||||
| Misses : 757 branch misses ( 85.96%) - 0 cache misses ( 0.00%) - 16107.00 cache references
|
||||
|
||||
4582.1 documents parsed per second (best)
|
||||
|
||||
jsonexamples/twitter8.json
|
||||
==========================
|
||||
10306 blocks - 659626 bytes - 55262 structurals ( 8.4 %)
|
||||
special blocks with: utf8 2289 ( 22.2 %) - escape 604 ( 5.9 %) - 0 structurals 1270 ( 12.3 %) - 1+ structurals 9037 ( 87.7 %) - 8+ structurals 2576 ( 25.0 %) - 16+ structurals 0 ( 0.0 %)
|
||||
special block flips: utf8 1096 ( 10.6 %) - escape 646 ( 6.3 %) - 0 structurals 932 ( 9.0 %) - 1+ structurals 932 ( 9.0 %) - 8+ structurals 2843 ( 27.6 %) - 16+ structurals 0 ( 0.0 %)
|
||||
|
||||
All Stages (excluding allocation)
|
||||
| Speed : 20.6976 ns per block ( 97.48%) - 0.3234 ns per byte - 3.8603 ns per structural - 3.0920 GB/s
|
||||
| Cycles : 64.0733 per block ( 97.72%) - 1.0012 per byte - 11.9504 per structural - 3.096 GHz est. frequency
|
||||
| Instructions : 200.2565 per block (100.00%) - 3.1291 per byte - 37.3502 per structural - 3.125 per cycle
|
||||
| Misses : 860 branch misses ( 91.48%) - 0 cache misses ( 0.00%) - 28700.00 cache references
|
||||
|- Stage 1
|
||||
| Speed : 6.0103 ns per block ( 28.31%) - 0.0939 ns per byte - 1.1210 ns per structural - 10.6481 GB/s
|
||||
| Cycles : 18.6231 per block ( 28.40%) - 0.2910 per byte - 3.4734 per structural - 3.099 GHz est. frequency
|
||||
| Instructions : 61.8107 per block ( 30.87%) - 0.9658 per byte - 11.5284 per structural - 3.319 per cycle
|
||||
| Misses : 102 branch misses ( 10.85%) - 0 cache misses ( 0.00%) - 11933.00 cache references
|
||||
|- Stage 2
|
||||
| Speed : 14.6407 ns per block ( 68.95%) - 0.2288 ns per byte - 2.7307 ns per structural - 4.3712 GB/s
|
||||
| Cycles : 45.3059 per block ( 69.10%) - 0.7079 per byte - 8.4501 per structural - 3.095 GHz est. frequency
|
||||
| Instructions : 138.4458 per block ( 69.13%) - 2.1633 per byte - 25.8217 per structural - 3.056 per cycle
|
||||
| Misses : 766 branch misses ( 81.48%) - 0 cache misses ( 0.00%) - 16566.00 cache references
|
||||
|
||||
4687.6 documents parsed per second (best)
|
||||
|
||||
jsonexamples/twitter1.json
|
||||
==========================
|
||||
10306 blocks - 659626 bytes - 55262 structurals ( 8.4 %)
|
||||
special blocks with: utf8 2289 ( 22.2 %) - escape 604 ( 5.9 %) - 0 structurals 1270 ( 12.3 %) - 1+ structurals 9037 ( 87.7 %) - 8+ structurals 2584 ( 25.1 %) - 16+ structurals 0 ( 0.0 %)
|
||||
special block flips: utf8 1096 ( 10.6 %) - escape 646 ( 6.3 %) - 0 structurals 932 ( 9.0 %) - 1+ structurals 932 ( 9.0 %) - 8+ structurals 2859 ( 27.7 %) - 16+ structurals 0 ( 0.0 %)
|
||||
|
||||
All Stages (excluding allocation)
|
||||
| Speed : 20.1200 ns per block ( 93.76%) - 0.3144 ns per byte - 3.7526 ns per structural - 3.1808 GB/s
|
||||
| Cycles : 62.2865 per block ( 97.52%) - 0.9733 per byte - 11.6172 per structural - 3.096 GHz est. frequency
|
||||
| Instructions : 194.5666 per block (100.00%) - 3.0402 per byte - 36.2889 per structural - 3.124 per cycle
|
||||
| Misses : 673 branch misses ( 97.88%) - 0 cache misses ( 0.00%) - 28320.00 cache references
|
||||
|- Stage 1
|
||||
| Speed : 6.0046 ns per block ( 27.98%) - 0.0938 ns per byte - 1.1199 ns per structural - 10.6582 GB/s
|
||||
| Cycles : 18.6140 per block ( 29.14%) - 0.2909 per byte - 3.4717 per structural - 3.100 GHz est. frequency
|
||||
| Instructions : 61.8107 per block ( 31.77%) - 0.9658 per byte - 11.5284 per structural - 3.321 per cycle
|
||||
| Misses : 113 branch misses ( 16.43%) - 0 cache misses ( 0.00%) - 12175.00 cache references
|
||||
|- Stage 2
|
||||
| Speed : 14.0716 ns per block ( 65.57%) - 0.2199 ns per byte - 2.6245 ns per structural - 4.5480 GB/s
|
||||
| Cycles : 43.5464 per block ( 68.18%) - 0.6804 per byte - 8.1219 per structural - 3.095 GHz est. frequency
|
||||
| Instructions : 132.7559 per block ( 68.23%) - 2.0744 per byte - 24.7605 per structural - 3.049 per cycle
|
||||
| Misses : 544 branch misses ( 79.11%) - 0 cache misses ( 0.00%) - 16212.00 cache references
|
||||
|
||||
4822.1 documents parsed per second (best)
|
||||
|
||||
jsonexamples/twitter1-nobackslash.json
|
||||
======================================
|
||||
10306 blocks - 659626 bytes - 55262 structurals ( 8.4 %)
|
||||
special blocks with: utf8 2289 ( 22.2 %) - escape 0 ( 0.0 %) - 0 structurals 1270 ( 12.3 %) - 1+ structurals 9037 ( 87.7 %) - 8+ structurals 2584 ( 25.1 %) - 16+ structurals 0 ( 0.0 %)
|
||||
special block flips: utf8 1096 ( 10.6 %) - escape 0 ( 0.0 %) - 0 structurals 932 ( 9.0 %) - 1+ structurals 932 ( 9.0 %) - 8+ structurals 2859 ( 27.7 %) - 16+ structurals 0 ( 0.0 %)
|
||||
|
||||
All Stages (excluding allocation)
|
||||
| Speed : 19.3473 ns per block ( 94.75%) - 0.3023 ns per byte - 3.6085 ns per structural - 3.3078 GB/s
|
||||
| Cycles : 59.8930 per block ( 97.21%) - 0.9359 per byte - 11.1707 per structural - 3.096 GHz est. frequency
|
||||
| Instructions : 191.2882 per block (100.00%) - 2.9890 per byte - 35.6774 per structural - 3.194 per cycle
|
||||
| Misses : 624 branch misses ( 99.02%) - 2 cache misses ( 30.96%) - 28632.00 cache references
|
||||
|- Stage 1
|
||||
| Speed : 5.9519 ns per block ( 29.15%) - 0.0930 ns per byte - 1.1101 ns per structural - 10.7526 GB/s
|
||||
| Cycles : 18.4460 per block ( 29.94%) - 0.2882 per byte - 3.4404 per structural - 3.099 GHz est. frequency
|
||||
| Instructions : 61.1661 per block ( 31.98%) - 0.9558 per byte - 11.4082 per structural - 3.316 per cycle
|
||||
| Misses : 92 branch misses ( 14.60%) - 1 cache misses ( 15.48%) - 12277.00 cache references
|
||||
|- Stage 2
|
||||
| Speed : 13.3283 ns per block ( 65.28%) - 0.2083 ns per byte - 2.4859 ns per structural - 4.8016 GB/s
|
||||
| Cycles : 41.2453 per block ( 66.95%) - 0.6445 per byte - 7.6927 per structural - 3.095 GHz est. frequency
|
||||
| Instructions : 130.1221 per block ( 68.02%) - 2.0332 per byte - 24.2693 per structural - 3.155 per cycle
|
||||
| Misses : 499 branch misses ( 79.19%) - 1 cache misses ( 15.48%) - 16358.00 cache references
|
||||
|
||||
5014.7 documents parsed per second (best)
|
||||
|
||||
jsonexamples/twitter1-nobackslash-noutf8.json
|
||||
=============================================
|
||||
10306 blocks - 659626 bytes - 55262 structurals ( 8.4 %)
|
||||
special blocks with: utf8 0 ( 0.0 %) - escape 0 ( 0.0 %) - 0 structurals 1270 ( 12.3 %) - 1+ structurals 9037 ( 87.7 %) - 8+ structurals 2584 ( 25.1 %) - 16+ structurals 0 ( 0.0 %)
|
||||
special block flips: utf8 0 ( 0.0 %) - escape 0 ( 0.0 %) - 0 structurals 932 ( 9.0 %) - 1+ structurals 932 ( 9.0 %) - 8+ structurals 2859 ( 27.7 %) - 16+ structurals 0 ( 0.0 %)
|
||||
|
||||
All Stages (excluding allocation)
|
||||
| Speed : 18.9506 ns per block ( 97.78%) - 0.2961 ns per byte - 3.5345 ns per structural - 3.3771 GB/s
|
||||
| Cycles : 58.6756 per block ( 97.97%) - 0.9168 per byte - 10.9437 per structural - 3.096 GHz est. frequency
|
||||
| Instructions : 186.6244 per block (100.00%) - 2.9161 per byte - 34.8076 per structural - 3.181 per cycle
|
||||
| Misses : 634 branch misses ( 99.08%) - 0 cache misses ( 0.00%) - 28596.00 cache references
|
||||
|- Stage 1
|
||||
| Speed : 5.4613 ns per block ( 28.18%) - 0.0853 ns per byte - 1.0186 ns per structural - 11.7184 GB/s
|
||||
| Cycles : 16.9236 per block ( 28.26%) - 0.2644 per byte - 3.1565 per structural - 3.099 GHz est. frequency
|
||||
| Instructions : 56.5024 per block ( 30.28%) - 0.8829 per byte - 10.5383 per structural - 3.339 per cycle
|
||||
| Misses : 130 branch misses ( 20.32%) - 0 cache misses ( 0.00%) - 12365.00 cache references
|
||||
|- Stage 2
|
||||
| Speed : 13.4108 ns per block ( 69.19%) - 0.2096 ns per byte - 2.5013 ns per structural - 4.7721 GB/s
|
||||
| Cycles : 41.5073 per block ( 69.31%) - 0.6486 per byte - 7.7416 per structural - 3.095 GHz est. frequency
|
||||
| Instructions : 130.1221 per block ( 69.72%) - 2.0332 per byte - 24.2693 per structural - 3.135 per cycle
|
||||
| Misses : 489 branch misses ( 76.42%) - 0 cache misses ( 0.00%) - 16279.00 cache references
|
||||
|
||||
5119.7 documents parsed per second (best)
|
||||
|
||||
jsonexamples/twitter1-emptystring.json
|
||||
======================================
|
||||
10306 blocks - 659626 bytes - 55262 structurals ( 8.4 %)
|
||||
special blocks with: utf8 0 ( 0.0 %) - escape 0 ( 0.0 %) - 0 structurals 1269 ( 12.3 %) - 1+ structurals 9038 ( 87.7 %) - 8+ structurals 2856 ( 27.7 %) - 16+ structurals 0 ( 0.0 %)
|
||||
special block flips: utf8 0 ( 0.0 %) - escape 0 ( 0.0 %) - 0 structurals 928 ( 9.0 %) - 1+ structurals 928 ( 9.0 %) - 8+ structurals 3123 ( 30.3 %) - 16+ structurals 0 ( 0.0 %)
|
||||
|
||||
All Stages (excluding allocation)
|
||||
| Speed : 18.0587 ns per block ( 93.58%) - 0.2822 ns per byte - 3.3682 ns per structural - 3.5439 GB/s
|
||||
| Cycles : 55.9116 per block ( 97.35%) - 0.8736 per byte - 10.4282 per structural - 3.096 GHz est. frequency
|
||||
| Instructions : 183.3181 per block (100.00%) - 2.8644 per byte - 34.1909 per structural - 3.279 per cycle
|
||||
| Misses : 473 branch misses (101.19%) - 1 cache misses ( 15.11%) - 18833.00 cache references
|
||||
|- Stage 1
|
||||
| Speed : 5.4026 ns per block ( 28.00%) - 0.0844 ns per byte - 1.0077 ns per structural - 11.8457 GB/s
|
||||
| Cycles : 16.7461 per block ( 29.16%) - 0.2617 per byte - 3.1233 per structural - 3.100 GHz est. frequency
|
||||
| Instructions : 56.5028 per block ( 30.82%) - 0.8829 per byte - 10.5384 per structural - 3.374 per cycle
|
||||
| Misses : 112 branch misses ( 23.96%) - 0 cache misses ( 0.00%) - 10717.00 cache references
|
||||
|- Stage 2
|
||||
| Speed : 12.6022 ns per block ( 65.30%) - 0.1969 ns per byte - 2.3505 ns per structural - 5.0783 GB/s
|
||||
| Cycles : 39.0031 per block ( 67.91%) - 0.6094 per byte - 7.2745 per structural - 3.095 GHz est. frequency
|
||||
| Instructions : 126.8154 per block ( 69.18%) - 1.9816 per byte - 23.6525 per structural - 3.251 per cycle
|
||||
| Misses : 351 branch misses ( 75.09%) - 1 cache misses ( 15.11%) - 8120.00 cache references
|
||||
|
||||
5372.6 documents parsed per second (best)
|
||||
|
||||
jsonexamples/twitter-allstrings.json
|
||||
====================================
|
||||
10306 blocks - 659626 bytes - 55262 structurals ( 8.4 %)
|
||||
special blocks with: utf8 0 ( 0.0 %) - escape 0 ( 0.0 %) - 0 structurals 1269 ( 12.3 %) - 1+ structurals 9038 ( 87.7 %) - 8+ structurals 2853 ( 27.7 %) - 16+ structurals 0 ( 0.0 %)
|
||||
special block flips: utf8 0 ( 0.0 %) - escape 0 ( 0.0 %) - 0 structurals 928 ( 9.0 %) - 1+ structurals 928 ( 9.0 %) - 8+ structurals 3107 ( 30.1 %) - 16+ structurals 0 ( 0.0 %)
|
||||
|
||||
All Stages (excluding allocation)
|
||||
| Speed : 19.6482 ns per block ( 97.20%) - 0.3070 ns per byte - 3.6646 ns per structural - 3.2572 GB/s
|
||||
| Cycles : 60.8255 per block ( 97.62%) - 0.9504 per byte - 11.3446 per structural - 3.096 GHz est. frequency
|
||||
| Instructions : 183.8463 per block (100.00%) - 2.8727 per byte - 34.2895 per structural - 3.023 per cycle
|
||||
| Misses : 300 branch misses ( 96.97%) - 0 cache misses ( 0.00%) - 18361.00 cache references
|
||||
|- Stage 1
|
||||
| Speed : 5.4053 ns per block ( 26.74%) - 0.0845 ns per byte - 1.0081 ns per structural - 11.8399 GB/s
|
||||
| Cycles : 16.7545 per block ( 26.89%) - 0.2618 per byte - 3.1249 per structural - 3.100 GHz est. frequency
|
||||
| Instructions : 56.5028 per block ( 30.73%) - 0.8829 per byte - 10.5384 per structural - 3.372 per cycle
|
||||
| Misses : 98 branch misses ( 31.68%) - 1 cache misses ( 18.41%) - 10786.00 cache references
|
||||
|- Stage 2
|
||||
| Speed : 14.0367 ns per block ( 69.44%) - 0.2193 ns per byte - 2.6180 ns per structural - 4.5593 GB/s
|
||||
| Cycles : 43.4371 per block ( 69.71%) - 0.6787 per byte - 8.1015 per structural - 3.095 GHz est. frequency
|
||||
| Instructions : 127.3447 per block ( 69.27%) - 1.9898 per byte - 23.7513 per structural - 2.932 per cycle
|
||||
| Misses : 216 branch misses ( 69.82%) - 0 cache misses ( 0.00%) - 7835.00 cache references
|
||||
|
||||
4937.9 documents parsed per second (best)
|
||||
|
||||
jsonexamples/twitter-allstrings-arrays.json
|
||||
===========================================
|
||||
10306 blocks - 659626 bytes - 55262 structurals ( 8.4 %)
|
||||
special blocks with: utf8 0 ( 0.0 %) - escape 0 ( 0.0 %) - 0 structurals 1269 ( 12.3 %) - 1+ structurals 9038 ( 87.7 %) - 8+ structurals 2853 ( 27.7 %) - 16+ structurals 0 ( 0.0 %)
|
||||
special block flips: utf8 0 ( 0.0 %) - escape 0 ( 0.0 %) - 0 structurals 928 ( 9.0 %) - 1+ structurals 928 ( 9.0 %) - 8+ structurals 3107 ( 30.1 %) - 16+ structurals 0 ( 0.0 %)
|
||||
|
||||
All Stages (excluding allocation)
|
||||
| Speed : 23.6768 ns per block ( 96.29%) - 0.3700 ns per byte - 4.4160 ns per structural - 2.7030 GB/s
|
||||
| Cycles : 72.7530 per block ( 95.79%) - 1.1368 per byte - 13.5693 per structural - 3.073 GHz est. frequency
|
||||
| Instructions : 225.0789 per block (100.00%) - 3.5170 per byte - 41.9798 per structural - 3.094 per cycle
|
||||
| Misses : 547 branch misses (104.38%) - 0 cache misses ( 0.00%) - 17259.00 cache references
|
||||
|- Stage 1
|
||||
| Speed : 5.4063 ns per block ( 21.99%) - 0.0845 ns per byte - 1.0083 ns per structural - 11.8376 GB/s
|
||||
| Cycles : 16.7598 per block ( 22.07%) - 0.2619 per byte - 3.1259 per structural - 3.100 GHz est. frequency
|
||||
| Instructions : 56.5028 per block ( 25.10%) - 0.8829 per byte - 10.5384 per structural - 3.371 per cycle
|
||||
| Misses : 101 branch misses ( 19.27%) - 0 cache misses ( 0.00%) - 10570.00 cache references
|
||||
|- Stage 2
|
||||
| Speed : 18.2062 ns per block ( 74.04%) - 0.2845 ns per byte - 3.3957 ns per structural - 3.5152 GB/s
|
||||
| Cycles : 55.7937 per block ( 73.46%) - 0.8718 per byte - 10.4062 per structural - 3.065 GHz est. frequency
|
||||
| Instructions : 168.5761 per block ( 74.90%) - 2.6341 per byte - 31.4414 per structural - 3.021 per cycle
|
||||
| Misses : 427 branch misses ( 81.48%) - 0 cache misses ( 0.00%) - 6797.00 cache references
|
||||
|
||||
4097.7 documents parsed per second (best)
|
||||
|
||||
jsonexamples/twitter-allstrings-flatarray.json
|
||||
==============================================
|
||||
10306 blocks - 659626 bytes - 49890 structurals ( 7.6 %)
|
||||
special blocks with: utf8 0 ( 0.0 %) - escape 0 ( 0.0 %) - 0 structurals 1279 ( 12.4 %) - 1+ structurals 9028 ( 87.6 %) - 8+ structurals 2100 ( 20.4 %) - 16+ structurals 0 ( 0.0 %)
|
||||
special block flips: utf8 0 ( 0.0 %) - escape 0 ( 0.0 %) - 0 structurals 946 ( 9.2 %) - 1+ structurals 946 ( 9.2 %) - 8+ structurals 2668 ( 25.9 %) - 16+ structurals 0 ( 0.0 %)
|
||||
|
||||
All Stages (excluding allocation)
|
||||
| Speed : 20.5314 ns per block ( 98.77%) - 0.3208 ns per byte - 4.2417 ns per structural - 3.1171 GB/s
|
||||
| Cycles : 63.5535 per block ( 98.95%) - 0.9931 per byte - 13.1298 per structural - 3.095 GHz est. frequency
|
||||
| Instructions : 208.9967 per block (100.00%) - 3.2657 per byte - 43.1776 per structural - 3.289 per cycle
|
||||
| Misses : 94 branch misses ( 99.52%) - 0 cache misses ( 0.00%) - 15420.00 cache references
|
||||
|- Stage 1
|
||||
| Speed : 5.3985 ns per block ( 25.97%) - 0.0844 ns per byte - 1.1153 ns per structural - 11.8548 GB/s
|
||||
| Cycles : 16.7318 per block ( 26.05%) - 0.2614 per byte - 3.4567 per structural - 3.099 GHz est. frequency
|
||||
| Instructions : 56.4917 per block ( 27.03%) - 0.8827 per byte - 11.6709 per structural - 3.376 per cycle
|
||||
| Misses : 87 branch misses ( 92.11%) - 0 cache misses ( 0.00%) - 9889.00 cache references
|
||||
|- Stage 2
|
||||
| Speed : 15.0691 ns per block ( 72.49%) - 0.2355 ns per byte - 3.1132 ns per structural - 4.2470 GB/s
|
||||
| Cycles : 46.6318 per block ( 72.61%) - 0.7286 per byte - 9.6339 per structural - 3.095 GHz est. frequency
|
||||
| Instructions : 152.5050 per block ( 72.97%) - 2.3830 per byte - 31.5067 per structural - 3.270 per cycle
|
||||
| Misses : 3 branch misses ( 3.18%) - 0 cache misses ( 0.00%) - 5610.00 cache references
|
||||
|
||||
4725.5 documents parsed per second (best)
|
||||
|
||||
jsonexamples/twitter-allstrings-flatobjects.json
|
||||
================================================
|
||||
10306 blocks - 659626 bytes - 56091 structurals ( 8.5 %)
|
||||
special blocks with: utf8 0 ( 0.0 %) - escape 0 ( 0.0 %) - 0 structurals 1269 ( 12.3 %) - 1+ structurals 9038 ( 87.7 %) - 8+ structurals 2973 ( 28.8 %) - 16+ structurals 0 ( 0.0 %)
|
||||
special block flips: utf8 0 ( 0.0 %) - escape 0 ( 0.0 %) - 0 structurals 928 ( 9.0 %) - 1+ structurals 928 ( 9.0 %) - 8+ structurals 3093 ( 30.0 %) - 16+ structurals 0 ( 0.0 %)
|
||||
|
||||
All Stages (excluding allocation)
|
||||
| Speed : 19.4292 ns per block ( 97.71%) - 0.3036 ns per byte - 3.5702 ns per structural - 3.2939 GB/s
|
||||
| Cycles : 60.1577 per block ( 97.90%) - 0.9400 per byte - 11.0543 per structural - 3.096 GHz est. frequency
|
||||
| Instructions : 181.6747 per block (100.00%) - 2.8388 per byte - 33.3836 per structural - 3.020 per cycle
|
||||
| Misses : 508 branch misses (100.57%) - 0 cache misses ( 0.00%) - 17421.00 cache references
|
||||
|- Stage 1
|
||||
| Speed : 5.4138 ns per block ( 27.22%) - 0.0846 ns per byte - 0.9948 ns per structural - 11.8213 GB/s
|
||||
| Cycles : 16.7833 per block ( 27.31%) - 0.2622 per byte - 3.0840 per structural - 3.100 GHz est. frequency
|
||||
| Instructions : 56.5028 per block ( 31.10%) - 0.8829 per byte - 10.3827 per structural - 3.367 per cycle
|
||||
| Misses : 99 branch misses ( 19.60%) - 0 cache misses ( 0.00%) - 10517.00 cache references
|
||||
|- Stage 2
|
||||
| Speed : 13.9445 ns per block ( 70.12%) - 0.2179 ns per byte - 2.5624 ns per structural - 4.5895 GB/s
|
||||
| Cycles : 43.1562 per block ( 70.23%) - 0.6743 per byte - 7.9302 per structural - 3.095 GHz est. frequency
|
||||
| Instructions : 125.1719 per block ( 68.90%) - 1.9559 per byte - 23.0010 per structural - 2.900 per cycle
|
||||
| Misses : 402 branch misses ( 79.59%) - 0 cache misses ( 0.00%) - 6973.00 cache references
|
||||
|
||||
4993.6 documents parsed per second (best)
|
||||
|
||||
@@ -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
|
||||
@@ -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,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"
|
||||
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
|
||||
@@ -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,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"
|
||||
|
||||
@@ -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
|
||||
@@ -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
@@ -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,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,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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
Executable
+263
@@ -0,0 +1,263 @@
|
||||
from itertools import groupby
|
||||
from pathlib import Path
|
||||
from typing import Any, Iterable, Literal, Optional, OrderedDict, cast
|
||||
from io import BufferedWriter, BufferedReader
|
||||
import re
|
||||
import sys
|
||||
|
||||
ContainerState = Literal['orig', 'array', 'flat']
|
||||
ScalarState = Literal['orig', '1digit', 'str']
|
||||
StringState = Literal['orig', 'unescaped', 'ascii', 'empty']
|
||||
CONTAINER_STATES: list[ContainerState] = ['orig', 'array', 'flat']
|
||||
SCALAR_STATES: list[ScalarState] = ['orig', '1digit', 'str']
|
||||
STRING_STATES: list[StringState] = ['orig', 'unescaped', 'ascii', 'empty']
|
||||
|
||||
class Result:
|
||||
class Metric:
|
||||
def __init__(self, name: str, value: float, units: str):
|
||||
self.name = name
|
||||
self.value = value
|
||||
self.units = units
|
||||
|
||||
def best(self, other: 'Result.Metric'):
|
||||
assert self.name == other.name
|
||||
assert self.units == other.units
|
||||
if self.name == 'Speed':
|
||||
return self if self.value > other.value else other
|
||||
else:
|
||||
return self if self.value < other.value else other
|
||||
|
||||
class Stage:
|
||||
def __init__(self, stage: str):
|
||||
self.stage = stage
|
||||
self.metrics: dict[str, Result.Metric] = {}
|
||||
|
||||
@property
|
||||
def speed(self):
|
||||
return self.metrics['Speed']
|
||||
|
||||
@property
|
||||
def cycles(self):
|
||||
return self.metrics['Cycles']
|
||||
|
||||
@property
|
||||
def instructions(self):
|
||||
return self.metrics['Instructions']
|
||||
|
||||
@property
|
||||
def misses(self):
|
||||
return self.metrics['Misses']
|
||||
|
||||
def _merge_in(self, other: 'Result.Stage'):
|
||||
assert self.stage == other.stage
|
||||
for metric in other.metrics.values():
|
||||
if metric.name not in self.metrics:
|
||||
self.metrics[metric.name] = metric
|
||||
self.metrics[metric.name] = self.metrics[metric.name].best(metric)
|
||||
|
||||
def __init__(self, json_file: Path):
|
||||
self.json_file = json_file
|
||||
self.stages = dict[str, Result.Stage]()
|
||||
match = re.match(r'(.*)-([^-]*)-([^-]*)-([^-]*)$', json_file.stem)
|
||||
if match:
|
||||
self.base_json_file = match.group(1)
|
||||
assert match.group(2) in CONTAINER_STATES
|
||||
self.container_state: ContainerState = cast(ContainerState, match.group(2))
|
||||
assert match.group(3) in SCALAR_STATES
|
||||
self.scalar_state: ScalarState = cast(ScalarState, match.group(3))
|
||||
assert match.group(4) in STRING_STATES
|
||||
self.string_state: StringState = cast(StringState, match.group(4))
|
||||
else:
|
||||
self.base_json_file = self.json_file.stem
|
||||
self.container_state = 'orig'
|
||||
self.scalar_state = 'orig'
|
||||
self.string_state = 'orig'
|
||||
self.docs_per_second: float = -1
|
||||
|
||||
def __lt__(self, other: 'Result'):
|
||||
if self.base_json_file != other.base_json_file:
|
||||
return self.base_json_file < other.base_json_file
|
||||
if self.container_state != other.container_state:
|
||||
return CONTAINER_STATES.index(self.container_state) < CONTAINER_STATES.index(other.container_state)
|
||||
if self.scalar_state != other.scalar_state:
|
||||
return SCALAR_STATES.index(self.scalar_state) < SCALAR_STATES.index(other.scalar_state)
|
||||
if self.string_state != other.string_state:
|
||||
return STRING_STATES.index(self.string_state) < STRING_STATES.index(other.string_state)
|
||||
return False
|
||||
|
||||
def merge(self, other: 'Result'):
|
||||
merged = Result(self.json_file)
|
||||
merged._merge_in(self)
|
||||
merged._merge_in(other)
|
||||
return merged
|
||||
|
||||
def _merge_in(self, other: 'Result'):
|
||||
assert self.json_file == other.json_file
|
||||
if other.docs_per_second > self.docs_per_second:
|
||||
self.docs_per_second = other.docs_per_second
|
||||
for stage in other.stages.values():
|
||||
if stage.stage not in self.stages:
|
||||
self.stages[stage.stage] = Result.Stage(stage.stage)
|
||||
self.stages[stage.stage]._merge_in(stage)
|
||||
|
||||
@property
|
||||
def stage1(self):
|
||||
return self.stages['Stage 1']
|
||||
|
||||
@property
|
||||
def stage2(self):
|
||||
return self.stages['Stage 2']
|
||||
|
||||
def read_results(results_file: Path):
|
||||
with open(result_file, 'rt') as input:
|
||||
result = None
|
||||
stage = None
|
||||
|
||||
prev_line = None
|
||||
for line in input:
|
||||
if re.match(r'=+$', line):
|
||||
assert result is None
|
||||
assert prev_line is not None
|
||||
result = Result(Path(prev_line.strip()))
|
||||
|
||||
# Stage
|
||||
match = re.match(r'\|-(.+)$', line)
|
||||
if match:
|
||||
assert result is not None
|
||||
stage = Result.Stage(match.group(1).strip())
|
||||
result.stages[stage.stage] = stage
|
||||
|
||||
# Metrics
|
||||
if stage is not None:
|
||||
match = re.match(r'\|([^:]+):\s*([-+0-9.]+)\s+([^(-]+)', line)
|
||||
if match and stage is not None:
|
||||
metric = Result.Metric(match.group(1).strip(), float(match.group(2)), match.group(3).strip())
|
||||
stage.metrics[metric.name] = metric
|
||||
|
||||
# Documents per second
|
||||
match = re.match(r'\s*([-+0-9.]+)\s*documents parsed per second', line)
|
||||
if match:
|
||||
assert result is not None
|
||||
result.docs_per_second = float(match.group(1))
|
||||
yield result
|
||||
result = None
|
||||
stage = None
|
||||
|
||||
prev_line = line
|
||||
|
||||
# Merge multiple results for the same file
|
||||
all_results = dict[Path, Result]()
|
||||
for result_file in sys.argv[1:]:
|
||||
for result in read_results(Path(result_file)):
|
||||
if result.json_file in all_results:
|
||||
all_results[result.json_file] = all_results[result.json_file].merge(result)
|
||||
else:
|
||||
all_results[result.json_file] = result
|
||||
|
||||
def print_row(row: Iterable, key_lengths: OrderedDict[str, int], rjust: set[str]):
|
||||
column_iter = iter(row)
|
||||
for (key, width) in key_lengths.items():
|
||||
value = str(next(column_iter))
|
||||
if key in rjust:
|
||||
print(f"| {str(value).rjust(width)} ", end='')
|
||||
else:
|
||||
print(f"| {str(value).ljust(width)} ", end='')
|
||||
print("|")
|
||||
|
||||
def print_table(rows: list[OrderedDict], key_lengths: Optional[OrderedDict[str, int]] = None, rjust = set[str]()):
|
||||
if key_lengths is None:
|
||||
key_lengths = OrderedDict[str, int]()
|
||||
for entry in rows:
|
||||
for (key, value) in entry.items():
|
||||
if key not in key_lengths:
|
||||
key_lengths[key] = len(key)
|
||||
key_lengths[key] = max(key_lengths[key], len(str(value)))
|
||||
|
||||
print_row(list(key_lengths.keys()), key_lengths, rjust)
|
||||
print("|", *[ f"{(':' if key in rjust else '').rjust(len+2, '-')}|" for (key, len) in key_lengths.items() ], sep='')
|
||||
|
||||
for row in rows:
|
||||
print_row(row.values(), key_lengths, rjust)
|
||||
|
||||
for (file, results) in groupby(sorted(all_results.values()), lambda r: r.base_json_file):
|
||||
results = [*results]
|
||||
print()
|
||||
print(f"# {file}.json Branch Miss Variants")
|
||||
print()
|
||||
print_table(
|
||||
[
|
||||
OrderedDict([
|
||||
('Contain', result.container_state),
|
||||
('Scalars', result.scalar_state),
|
||||
('Strings', result.string_state),
|
||||
('Cycles', '%.4f' % result.stage2.cycles.value),
|
||||
('Instrs', '%.4f' % result.stage2.instructions.value),
|
||||
('Misses', int(result.stage2.misses.value)),
|
||||
('Docs/sec', '%.1f' % result.docs_per_second),
|
||||
])
|
||||
for result in results
|
||||
],
|
||||
rjust = set(['Cycles', 'Instrs', 'Misses', 'Docs/sec'])
|
||||
)
|
||||
misses = {
|
||||
(r.container_state, r.scalar_state, r.string_state): int(r.stage2.misses.value)
|
||||
for r in results
|
||||
}
|
||||
|
||||
print()
|
||||
print('## Container State Transition Miss Reduction')
|
||||
print()
|
||||
rows = list[OrderedDict[str, object]]()
|
||||
PRINT_STRING_STATES: list[StringState] = [s for s in STRING_STATES if s != 'unescaped']
|
||||
for (i,from_state) in enumerate(CONTAINER_STATES[0:-1]):
|
||||
for to_state in CONTAINER_STATES[i+1:]:
|
||||
rows.append(OrderedDict([
|
||||
('Contain', f"{from_state} -> {to_state}"),
|
||||
*[
|
||||
(
|
||||
f"{scalar_state} {string_state}",
|
||||
misses[(from_state, scalar_state, string_state)] - misses[(to_state, scalar_state, string_state)]
|
||||
)
|
||||
for scalar_state in SCALAR_STATES
|
||||
for string_state in PRINT_STRING_STATES
|
||||
]
|
||||
]))
|
||||
print_table(rows, rjust = [*rows[0].keys()][1:])
|
||||
|
||||
print()
|
||||
print('## Scalar State Transition Miss Reduction')
|
||||
print()
|
||||
rows = list[OrderedDict[str, object]]()
|
||||
for (i,from_state) in enumerate(SCALAR_STATES[0:-1]):
|
||||
for to_state in SCALAR_STATES[i+1:]:
|
||||
rows.append(OrderedDict([
|
||||
('Scalars', f"{from_state} -> {to_state}"),
|
||||
*[
|
||||
(
|
||||
f"{container_state} {string_state}",
|
||||
misses[(container_state, from_state, string_state)] - misses[(container_state, to_state, string_state)]
|
||||
)
|
||||
for container_state in CONTAINER_STATES
|
||||
for string_state in PRINT_STRING_STATES
|
||||
]
|
||||
]))
|
||||
print_table(rows, rjust = [*rows[0].keys()][1:])
|
||||
|
||||
print()
|
||||
print('## String State Transition Miss Reduction')
|
||||
print()
|
||||
rows = list[OrderedDict[str, object]]()
|
||||
for (i,from_state) in enumerate(STRING_STATES[0:-1]):
|
||||
for to_state in STRING_STATES[i+1:]:
|
||||
rows.append(OrderedDict([
|
||||
('Strings', f"{from_state} -> {to_state}"),
|
||||
*[
|
||||
(
|
||||
f"{container_state} {scalar_state}",
|
||||
misses[(container_state, scalar_state, from_state)] - misses[(container_state, scalar_state, to_state)]
|
||||
)
|
||||
for container_state in CONTAINER_STATES
|
||||
for scalar_state in SCALAR_STATES
|
||||
]
|
||||
]))
|
||||
print_table(rows, rjust = [*rows[0].keys()][1:])
|
||||
Executable
+170
@@ -0,0 +1,170 @@
|
||||
from pathlib import Path
|
||||
from typing import Literal
|
||||
from io import BufferedWriter, BufferedReader
|
||||
import re
|
||||
import sys
|
||||
|
||||
ContainerState = Literal['orig', 'array', 'flat']
|
||||
ScalarState = Literal['orig', '1digit', 'str']
|
||||
StringState = Literal['orig', 'unescaped', 'ascii', 'empty']
|
||||
CONTAINER_STATES: list[ContainerState] = ['orig', 'array', 'flat']
|
||||
SCALAR_STATES: list[ScalarState] = ['orig', '1digit', 'str']
|
||||
STRING_STATES: list[StringState] = ['orig', 'unescaped', 'ascii', 'empty']
|
||||
|
||||
def right_pad(padded_length: int, b: bytes):
|
||||
assert len(b) <= padded_length
|
||||
return b + b' '*(padded_length-len(b))
|
||||
|
||||
def right_pad2(r: bytes, b: bytes):
|
||||
print(f"right_pad({r}, {b})")
|
||||
return right_pad(len(r), b)
|
||||
|
||||
class JsonFile:
|
||||
def __init__(self,
|
||||
original_json_file: Path,
|
||||
container_state: ContainerState = 'orig',
|
||||
scalar_state: ScalarState = 'orig',
|
||||
string_state: StringState = 'orig'):
|
||||
self.original_json_file = original_json_file
|
||||
self.container_state: ContainerState = container_state
|
||||
self.scalar_state: ScalarState = scalar_state
|
||||
self.string_state: StringState = string_state
|
||||
|
||||
@property
|
||||
def path(self):
|
||||
if self.container_state == 'orig' and self.scalar_state == 'orig' and self.string_state == 'orig':
|
||||
return self.original_json_file
|
||||
else:
|
||||
return self.original_json_file.with_stem(f"{self.original_json_file.stem}-{self.container_state}-{self.scalar_state}-{self.string_state}")
|
||||
|
||||
def write(self, force: bool = False):
|
||||
if force or not self.path.exists():
|
||||
with open(self.path, 'wb') as out:
|
||||
self.write_to(out)
|
||||
|
||||
def with_container_state(self, container_state: ContainerState):
|
||||
return JsonFile(self.original_json_file, container_state, self.scalar_state, self.string_state)
|
||||
def with_scalar_state(self, scalar_state: ScalarState):
|
||||
return JsonFile(self.original_json_file, self.container_state, scalar_state, self.string_state)
|
||||
def with_string_state(self, string_state: StringState):
|
||||
return JsonFile(self.original_json_file, self.container_state, self.scalar_state, string_state)
|
||||
|
||||
def open(self):
|
||||
return open(self.path, 'rb')
|
||||
|
||||
def write_to(self, out: BufferedWriter):
|
||||
if self.string_state == 'unescaped':
|
||||
return self.remove_escapes(self.with_string_state('orig').open(), out)
|
||||
elif self.string_state == 'ascii':
|
||||
return self.remove_utf8(self.with_string_state('unescaped').open(), out)
|
||||
elif self.string_state == 'empty':
|
||||
return self.replace_strings(self.with_string_state('ascii').open(), out, b'""')
|
||||
else:
|
||||
assert self.string_state == 'orig'
|
||||
|
||||
if self.scalar_state == '1digit':
|
||||
return self.replace_numbers(self.with_scalar_state('orig').open(), out, b'0')
|
||||
elif self.scalar_state == 'str':
|
||||
return self.replace_non_strings(self.with_scalar_state('1digit').open(), out, b'""')
|
||||
else:
|
||||
assert self.scalar_state == 'orig'
|
||||
|
||||
if self.container_state == 'array':
|
||||
return self.replace_objects_with_arrays(self.with_container_state('orig').open(), out)
|
||||
elif self.container_state == 'flat':
|
||||
return self.remove_nesting(self.with_container_state('array').open(), out)
|
||||
else:
|
||||
assert self.container_state == 'orig'
|
||||
|
||||
assert self.path.exists()
|
||||
|
||||
def remove_escapes(self, input: BufferedReader, out: BufferedWriter):
|
||||
for line in input:
|
||||
out.write(re.sub(rb'\\(.)', rb'__', line))
|
||||
|
||||
def remove_utf8(self, input: BufferedReader, out: BufferedWriter):
|
||||
for line in input:
|
||||
out.write(bytes([(b if b < 128 else ord('_')) for b in line]))
|
||||
|
||||
def replace_strings(self, input: BufferedReader, out: BufferedWriter, replacement: bytes):
|
||||
for line in input:
|
||||
assert line.find(b'\\') == -1
|
||||
out.write(re.sub(rb'"([^"]*)"', lambda s: right_pad(len(s.group(0)), replacement), line))
|
||||
|
||||
def replace_numbers(self, input: BufferedReader, out: BufferedWriter, replacement: bytes):
|
||||
for line in input:
|
||||
for (non_string, string) in self.split_by_strings(line):
|
||||
out.write(re.sub(rb'\s*[-0-9][-+0-9.eE]*\s*', lambda s: right_pad(len(s.group(0)), replacement), non_string))
|
||||
out.write(string)
|
||||
|
||||
def replace_non_strings(self, input: BufferedReader, out: BufferedWriter, replacement: bytes):
|
||||
for line in input:
|
||||
for (non_string, string) in self.split_by_strings(line):
|
||||
out.write(re.sub(rb'\s*[^,:{}[\] \r\t\n]+\s*', lambda s: right_pad(len(s.group(0)), replacement), non_string))
|
||||
out.write(string)
|
||||
|
||||
def replace_objects_with_arrays(self, input: BufferedReader, out: BufferedWriter):
|
||||
for line in input:
|
||||
for (non_string, string) in self.split_by_strings(line):
|
||||
out.write(non_string.replace(b'{', b'[').replace(b'}', b']').replace(b':', b','))
|
||||
out.write(string)
|
||||
|
||||
def remove_nesting(self, input: BufferedReader, out: BufferedWriter):
|
||||
prev_line = None
|
||||
is_first_line = True
|
||||
lines = iter(input)
|
||||
line = next(lines, None)
|
||||
next_line = None
|
||||
while line is not None:
|
||||
out_line = b''
|
||||
# Remove any { } or [ ], and replace : with ,
|
||||
for (non_string, string) in self.split_by_strings(line):
|
||||
# Replace empty objects or arrays with ""
|
||||
non_string = re.sub(rb'(\{(\s|\n)*\}|\[(\s|\n*)\])', lambda s: right_pad(len(s.group(0)), b'""'), non_string)
|
||||
# Remove other braces entirely
|
||||
non_string = re.sub(rb'([{}[\]])', lambda s: right_pad(len(s.group(0)), b' '), non_string)
|
||||
# Replace : with ,
|
||||
non_string = non_string.replace(b':', b',')
|
||||
out_line += non_string
|
||||
out_line += string
|
||||
|
||||
# Replace the first character with [
|
||||
if next_line is None:
|
||||
assert line[0] in [ord(x) for x in [ b'[', b'{', b' ', b'\t', b'\r', b'\n' ]]
|
||||
out_line = b'[' + out_line[1:]
|
||||
|
||||
# Replace the last character with ]
|
||||
next_line = next(lines, None)
|
||||
if next_line is None:
|
||||
assert out_line[-1] in [ord(x) for x in [ b']', b'}', b' ', b'\t', b'\r', b'\n' ]]
|
||||
out_line = bytes(out_line[:-1] + b']')
|
||||
line = next_line
|
||||
out.write(out_line)
|
||||
|
||||
def split_by_strings(self, line: bytes):
|
||||
result: list[tuple[bytes, bytes]] = []
|
||||
while len(line) > 0:
|
||||
quote = line.find(b'"')
|
||||
if quote == -1:
|
||||
result.append((line, b''))
|
||||
break
|
||||
end_quote = quote+1
|
||||
while line[end_quote] != ord(b'"'):
|
||||
assert end_quote < len(line)
|
||||
if line[end_quote] == ord(b'\\'):
|
||||
end_quote += 1
|
||||
end_quote += 1
|
||||
result.append((line[:quote],line[quote:end_quote+1]))
|
||||
line = line[end_quote+1:]
|
||||
return result
|
||||
|
||||
original_json_file = Path(sys.argv[1])
|
||||
for container_state in CONTAINER_STATES:
|
||||
for scalar_state in SCALAR_STATES:
|
||||
for string_state in STRING_STATES:
|
||||
output_file = JsonFile(original_json_file, container_state, scalar_state, string_state)
|
||||
if output_file.path.exists():
|
||||
print(f"Skipping {output_file.path}")
|
||||
continue
|
||||
print(f"Writing {output_file.path}")
|
||||
output_file.write()
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+20
-26
@@ -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 };
|
||||
|
||||
@@ -4,3 +4,4 @@
|
||||
|
||||
#include <generic/base.h>
|
||||
#include <generic/dom_parser_implementation.h>
|
||||
#include <generic/json_character_block.h>
|
||||
|
||||
@@ -10,6 +10,8 @@ namespace simdjson {
|
||||
namespace SIMDJSON_IMPLEMENTATION {
|
||||
namespace {
|
||||
|
||||
struct json_character_block;
|
||||
|
||||
} // unnamed namespace
|
||||
} // namespace SIMDJSON_IMPLEMENTATION
|
||||
} // namespace simdjson
|
||||
|
||||
@@ -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
|
||||
@@ -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!
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user