mirror of
https://github.com/simdjson/simdjson
synced 2026-06-08 17:27:07 +00:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b7b01fd788 | |||
| 2c07a242ea |
+183
-183
@@ -17,7 +17,7 @@ An overview of what you need to know to use simdjson, with examples.
|
|||||||
- [Using the parsed JSON](#using-the-parsed-json)
|
- [Using the parsed JSON](#using-the-parsed-json)
|
||||||
- [Using the parsed JSON: additional examples](#using-the-parsed-json-additional-examples)
|
- [Using the parsed JSON: additional examples](#using-the-parsed-json-additional-examples)
|
||||||
- [Adding support for custom types](#adding-support-for-custom-types)
|
- [Adding support for custom types](#adding-support-for-custom-types)
|
||||||
- [1. Specialize `simdjson::ondemand::value::get` to get custom types](#1-specialize-simdjsonondemandvalueget-to-get-custom-types)
|
- [1. Specialize `simdjson::ondemand::value::get` to get custom types (pre-C++20)](#1-specialize-simdjsonondemandvalueget-to-get-custom-types-pre-c20)
|
||||||
- [2. Use `tag_invoke` for custom types (C++20)](#2-use-tag_invoke-for-custom-types-c20)
|
- [2. Use `tag_invoke` for custom types (C++20)](#2-use-tag_invoke-for-custom-types-c20)
|
||||||
- [Minifying JSON strings without parsing](#minifying-json-strings-without-parsing)
|
- [Minifying JSON strings without parsing](#minifying-json-strings-without-parsing)
|
||||||
- [UTF-8 validation (alone)](#utf-8-validation-alone)
|
- [UTF-8 validation (alone)](#utf-8-validation-alone)
|
||||||
@@ -528,7 +528,7 @@ support for users who avoid exceptions. See [the simdjson error handling documen
|
|||||||
double value;
|
double value;
|
||||||
auto doc = parser.iterate(abstract_json);
|
auto doc = parser.iterate(abstract_json);
|
||||||
auto error = doc["str"]["123"]["abc"].get(value);
|
auto error = doc["str"]["123"]["abc"].get(value);
|
||||||
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
|
if (error) { std::cerr << simdjson::error_message(error) << std::endl; return EXIT_FAILURE; }
|
||||||
cout << value << endl; // Prints 3.14
|
cout << value << endl; // Prints 3.14
|
||||||
```
|
```
|
||||||
This examples also show how we can string several operations and only check for the error once, a strategy we call *error chaining*.
|
This examples also show how we can string several operations and only check for the error once, a strategy we call *error chaining*.
|
||||||
@@ -797,17 +797,10 @@ There are 2 main ways provided by simdjson to deserialize a value into a custom
|
|||||||
2. Specialize `simdjson::ondemand::value::get` for each value
|
2. Specialize `simdjson::ondemand::value::get` for each value
|
||||||
2. Using `tag_invoke` *(the recommended way if your system supports C++20 or better)*
|
2. Using `tag_invoke` *(the recommended way if your system supports C++20 or better)*
|
||||||
|
|
||||||
The differences between the two approaches are as follows:
|
We describe both of them in the following sections. Most users who have systems compatible with
|
||||||
|
C++20 or better should skip ahead to [using `tag_invoke` for custom types (C++20)](#2-use-tag_invoke-for-custom-types-c20) as it is more powerful and simpler.
|
||||||
|
|
||||||
1. Your compiler needs to support C++20 for `tag_invoke`.
|
### 1. Specialize `simdjson::ondemand::value::get` to get custom types (pre-C++20)
|
||||||
2. The first way limits you to know your type fully, but in the `tag_invoke` way, you can use C++20 concepts and template meta programming as well.
|
|
||||||
3. Another difference between the two ways is that in `tag_invoke` way you may or may not mark your deserialization function (`tag_invoke` itself) as `noexcept`, but in the
|
|
||||||
*template specialization* way (the first way) you HAVE TO mark your specialization as `noexcept` which means that you may need to catch exceptions so
|
|
||||||
that your function does not throw any exception all the while it potentially can (for example any type like `std::string`, `std::vector`, etc. that has an allocator can potentially throw exceptions).
|
|
||||||
4. Another difference is that the `tag_invoke` way can work on both `document` and `value` but in the first way you have to provide 2 distinct specializations.
|
|
||||||
5. If you by mistake use both way with each other, the *specialization way* will be used silently due to backward-compatibility.
|
|
||||||
|
|
||||||
### 1. Specialize `simdjson::ondemand::value::get` to get custom types
|
|
||||||
|
|
||||||
Suppose you have your own types, such as a `Car` struct:
|
Suppose you have your own types, such as a `Car` struct:
|
||||||
|
|
||||||
@@ -843,13 +836,14 @@ type:
|
|||||||
|
|
||||||
We may do so by providing additional template definitions to the `ondemand::value` type.
|
We may do so by providing additional template definitions to the `ondemand::value` type.
|
||||||
We may start by providing a definition for `std::vector<double>` as follows. Observe
|
We may start by providing a definition for `std::vector<double>` as follows. Observe
|
||||||
how we guard the code with `#ifndef __cpp_concepts`: that is because the necessary code
|
how we guard the code with `#if !SIMDJSON_SUPPORTS_DESERIALIZATION`: that is because the necessary code
|
||||||
is automatically provided by simdjson if C++20 (and concepts) are available.
|
is automatically provided by simdjson if C++20 (and concepts) are available.
|
||||||
See [Use `tag_invoke` for custom types](#2-use-tag_invoke-for-custom-types-c20) if you have
|
See [Use `tag_invoke` for custom types](#2-use-tag_invoke-for-custom-types-c20) if you have
|
||||||
C++20 support.
|
C++20 support.
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
#ifndef __cpp_concepts // because the code is unnecessary with C++20
|
#if !SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
|
// The code is unnecessary with C++20:
|
||||||
template <>
|
template <>
|
||||||
simdjson_inline simdjson_result<std::vector<double>>
|
simdjson_inline simdjson_result<std::vector<double>>
|
||||||
simdjson::ondemand::value::get() noexcept {
|
simdjson::ondemand::value::get() noexcept {
|
||||||
@@ -861,7 +855,7 @@ simdjson::ondemand::value::get() noexcept {
|
|||||||
double val;
|
double val;
|
||||||
error = v.get_double().get(val);
|
error = v.get_double().get(val);
|
||||||
if (error) { return error; }
|
if (error) { return error; }
|
||||||
try { vec.push_back(val); } catch (...) { return simdjson::UNEXPECTED_ERROR; }
|
vec.push_back(val);
|
||||||
}
|
}
|
||||||
return vec;
|
return vec;
|
||||||
}
|
}
|
||||||
@@ -877,27 +871,10 @@ simdjson_inline simdjson_result<Car> simdjson::ondemand::value::get() noexcept {
|
|||||||
auto error = get_object().get(obj);
|
auto error = get_object().get(obj);
|
||||||
if (error) { return error; }
|
if (error) { return error; }
|
||||||
Car car;
|
Car car;
|
||||||
// Instead of repeatedly obj["something"], we iterate through the object which
|
if((error = obj["make"].get_string(car.make))) { return error; }
|
||||||
// we expect to be faster.
|
if((error = obj["model"].get_string(car.model))) { return error; }
|
||||||
for (auto field : obj) {
|
if((error = obj["year"].get_int64().get(car.year))) { return error; }
|
||||||
raw_json_string key;
|
if((error = obj["tire_pressure"].get<std::vector<double>>().get(car.tire_pressure))) { return error; }
|
||||||
error = field.key().get(key);
|
|
||||||
if (error) { return error; }
|
|
||||||
|
|
||||||
if (key == "make") {
|
|
||||||
error = field.value().get_string(car.make);
|
|
||||||
if (error) { return error; }
|
|
||||||
} else if (key == "model") {
|
|
||||||
error = field.value().get_string(car.model);
|
|
||||||
if (error) { return error; }
|
|
||||||
} else if (key == "year") {
|
|
||||||
error = field.value().get_int64().get(car.year);
|
|
||||||
if (error) { return error; }
|
|
||||||
} else if (key == "tire_pressure") {
|
|
||||||
error = field.value().get<std::vector<double>>().get(car.tire_pressure);
|
|
||||||
if (error) { return error; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return car;
|
return car;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -921,7 +898,8 @@ struct Car {
|
|||||||
std::vector<double> tire_pressure;
|
std::vector<double> tire_pressure;
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifndef __cpp_concepts // because the code is unnecessary with C++20
|
#if !SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
|
// This code is not necessary if you have a C++20 compliant system:
|
||||||
template <>
|
template <>
|
||||||
simdjson_inline simdjson_result<std::vector<double>>
|
simdjson_inline simdjson_result<std::vector<double>>
|
||||||
simdjson::ondemand::value::get() noexcept {
|
simdjson::ondemand::value::get() noexcept {
|
||||||
@@ -939,33 +917,16 @@ simdjson::ondemand::value::get() noexcept {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
simdjson_inline simdjson_result<Car> simdjson::ondemand::value::get() noexcept {
|
simdjson_inline simdjson_result<Car> simdjson::ondemand::value::get() noexcept {
|
||||||
ondemand::object obj;
|
ondemand::object obj;
|
||||||
auto error = get_object().get(obj);
|
auto error = get_object().get(obj);
|
||||||
if (error) { return error; }
|
if (error) { return error; }
|
||||||
Car car;
|
Car car;
|
||||||
// Instead of repeatedly obj["something"], we iterate through the object which
|
if((error = obj["make"].get_string(car.make))) { return error; }
|
||||||
// we expect to be faster.
|
if((error = obj["model"].get_string(car.model))) { return error; }
|
||||||
for (auto field : obj) {
|
if((error = obj["year"].get_int64().get(car.year))) { return error; }
|
||||||
raw_json_string key;
|
if((error = obj["tire_pressure"].get<std::vector<double>>().get(car.tire_pressure))) { return error; }
|
||||||
error = field.key().get(key);
|
|
||||||
if (error) { return error; }
|
|
||||||
if (key == "make") {
|
|
||||||
error = field.value().get_string(car.make);
|
|
||||||
if (error) { return error; }
|
|
||||||
} else if (key == "model") {
|
|
||||||
error = field.value().get_string(car.model);
|
|
||||||
if (error) { return error; }
|
|
||||||
} else if (key == "year") {
|
|
||||||
error = field.value().get_int64().get(car.year);
|
|
||||||
if (error) { return error; }
|
|
||||||
} else if (key == "tire_pressure") {
|
|
||||||
error = field.value().get<std::vector<double>>().get(car.tire_pressure);
|
|
||||||
if (error) { return error; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return car;
|
return car;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -983,7 +944,6 @@ int main(void) {
|
|||||||
Car c(val); // an exception may be thrown
|
Car c(val); // an exception may be thrown
|
||||||
std::cout << c.make << std::endl;
|
std::cout << c.make << std::endl;
|
||||||
}
|
}
|
||||||
direct();
|
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -1004,11 +964,11 @@ int main(void) {
|
|||||||
ondemand::parser parser;
|
ondemand::parser parser;
|
||||||
ondemand::document doc;
|
ondemand::document doc;
|
||||||
auto error = parser.iterate(json).get(doc);
|
auto error = parser.iterate(json).get(doc);
|
||||||
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
|
if (error) { std::cerr << simdjson::error_message(error) << std::endl; return EXIT_FAILURE; }
|
||||||
for (auto val : doc) {
|
for (auto val : doc) {
|
||||||
Car c;
|
Car c;
|
||||||
error = val.get<Car>().get(c);
|
error = val.get<Car>().get(c);
|
||||||
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
|
if (error) { std::cerr << simdjson::error_message(error) << std::endl; return EXIT_FAILURE; }
|
||||||
std::cout << c.make << std::endl;
|
std::cout << c.make << std::endl;
|
||||||
}
|
}
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
@@ -1039,16 +999,19 @@ struct Car {
|
|||||||
std::vector<double> tire_pressure;
|
std::vector<double> tire_pressure;
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifndef __cpp_concepts // because the code is unnecessary with C++20
|
#if !SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
|
// This code is not necessary if you have a C++20 compliant system:
|
||||||
template <>
|
template <>
|
||||||
simdjson_inline simdjson_result<std::vector<double>>
|
simdjson_inline simdjson_result<std::vector<double>>
|
||||||
simdjson::ondemand::value::get() noexcept {
|
simdjson::ondemand::value::get() noexcept {
|
||||||
ondemand::array array;
|
ondemand::array array;
|
||||||
if (auto error = get_array().get(array); error) { return error; }
|
auto error = get_array().get(array);
|
||||||
|
if (error) { return error; }
|
||||||
std::vector<double> vec;
|
std::vector<double> vec;
|
||||||
for (auto v : array) {
|
for (auto v : array) {
|
||||||
double val;
|
double val;
|
||||||
if (auto error = v.get_double().get(val); error) { return error; }
|
error = v.get_double().get(val);
|
||||||
|
if (error) { return error; }
|
||||||
vec.push_back(val);
|
vec.push_back(val);
|
||||||
}
|
}
|
||||||
return vec;
|
return vec;
|
||||||
@@ -1059,30 +1022,26 @@ template <>
|
|||||||
simdjson_inline simdjson_result<Car> simdjson::ondemand::document::get() & noexcept {
|
simdjson_inline simdjson_result<Car> simdjson::ondemand::document::get() & noexcept {
|
||||||
ondemand::object obj;
|
ondemand::object obj;
|
||||||
auto error = get_object().get(obj);
|
auto error = get_object().get(obj);
|
||||||
if (error) {
|
if (error) { return error; }
|
||||||
return error;
|
|
||||||
}
|
|
||||||
Car car;
|
Car car;
|
||||||
// Instead of repeatedly obj["something"], we iterate through the object which
|
if((error = obj["make"].get_string(car.make))) { return error; }
|
||||||
// we expect to be faster.
|
if((error = obj["model"].get_string(car.model))) { return error; }
|
||||||
for (auto field : obj) {
|
if((error = obj["year"].get_int64().get(car.year))) { return error; }
|
||||||
raw_json_string key;
|
if((error = obj["tire_pressure"].get<std::vector<double>>().get(car.tire_pressure))) { return error; }
|
||||||
error = field.key().get(key);
|
return car;
|
||||||
if (error) { return error; }
|
}
|
||||||
if (key == "make") {
|
|
||||||
error = field.value().get_string(car.make);
|
|
||||||
if (error) { return error; }
|
template <>
|
||||||
} else if (key == "model") {
|
simdjson_inline simdjson_result<Car> simdjson::ondemand::document::get() noexcept {
|
||||||
error = field.value().get_string(car.model);
|
ondemand::object obj;
|
||||||
if (error) { return error; }
|
auto error = get_object().get(obj);
|
||||||
} else if (key == "year") {
|
if (error) { return error; }
|
||||||
error = field.value().get_int64().get(car.year);
|
Car car;
|
||||||
if (error) { return error; }
|
if((error = obj["make"].get_string(car.make))) { return error; }
|
||||||
} else if (key == "tire_pressure") {
|
if((error = obj["model"].get_string(car.model))) { return error; }
|
||||||
error = field.value().get<std::vector<double>>().get(car.tire_pressure);
|
if((error = obj["year"].get_int64().get(car.year))) { return error; }
|
||||||
if (error) { return error; }
|
if((error = obj["tire_pressure"].get<std::vector<double>>().get(car.tire_pressure))) { return error; }
|
||||||
}
|
|
||||||
}
|
|
||||||
return car;
|
return car;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1099,68 +1058,75 @@ int main(void) {
|
|||||||
|
|
||||||
### 2. Use `tag_invoke` for custom types (C++20)
|
### 2. Use `tag_invoke` for custom types (C++20)
|
||||||
|
|
||||||
A standard approach to provide support for deserialization is to add a `tag_invoke` function
|
In C++20, the standard introduced the notion of *customization point*.
|
||||||
in your own class:
|
A customization point is a function or function object that can be customized for different types. It allows library authors to provide default behavior while giving users the ability to override this behavior for specific types.
|
||||||
|
|
||||||
|
A tag_invoke function serves as a mechanism for customization points. It is not directly part of the C++ standard library but is often used in libraries that implement customization points.
|
||||||
|
The tag_invoke function is typically a generic function that takes a tag type and additional arguments.
|
||||||
|
The first argument is usually a tag type (often an empty struct) that uniquely identifies the customization point (e.g., deserialization of custom types in simdjson). Users or library providers can specialize tag_invoke for their types by defining it in the appropriate namespace, often inline namespace.
|
||||||
|
|
||||||
|
|
||||||
|
If your system supports C++20, we recommend that you adopt the `tag_invoke` approach
|
||||||
|
instead to deserialize custom types. It may prove to be considerably simpler. When
|
||||||
|
simdjson detects the necessary support, it sets the `SIMDJSON_SUPPORTS_DESERIALIZATION` macro
|
||||||
|
to 1, otherwise it is set to 0.
|
||||||
|
|
||||||
|
Consider a custom class `Car`:
|
||||||
|
|
||||||
```C++
|
```C++
|
||||||
/**
|
|
||||||
* A custom type that we want to parse.
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct Car {
|
struct Car {
|
||||||
std::string make;
|
std::string make;
|
||||||
std::string model;
|
std::string model;
|
||||||
int year = 0;
|
int year;
|
||||||
std::vector<double> tire_pressure;
|
std::vector<float> tire_pressure;
|
||||||
|
|
||||||
friend simdjson_result<Car>
|
|
||||||
tag_invoke(simdjson::deserialize_tag, std::type_identity<Car>, auto &val) {
|
|
||||||
simdjson::ondemand::object obj;
|
|
||||||
auto error = val.get_object().get(obj);
|
|
||||||
if (error) {
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
Car car{};
|
|
||||||
// Instead of repeatedly obj["something"], we iterate through the object
|
|
||||||
// which we expect to be faster.
|
|
||||||
for (auto field : obj) {
|
|
||||||
simdjson::ondemand::raw_json_string key;
|
|
||||||
error = field.key().get(key);
|
|
||||||
if (error) {
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
if (key == "make") {
|
|
||||||
error = field.value().get_string(car.make);
|
|
||||||
if (error) {
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
} else if (key == "model") {
|
|
||||||
error = field.value().get_string(car.model);
|
|
||||||
if (error) {
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
} else if (key == "year") {
|
|
||||||
error = field.value().get(car.year);
|
|
||||||
if (error) {
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
} else if (key == "tire_pressure") {
|
|
||||||
error = field.value().get(car.tire_pressure);
|
|
||||||
if (error) {
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return car;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
Let us explain each argument of `tag_invoke`:
|
Observe how we defined the class to use types that simdjson does not directly support (`float`, `int`).
|
||||||
|
With C++20 support, the library grabs from the JSON the generic type (`double`, `int`) and then it
|
||||||
|
casts it automatically.
|
||||||
|
|
||||||
- `simdjson::deserialize_tag`: it is the tag for Customization Point Object (CPO)
|
You may support deserializing directly from a JSON value or document to your own `Car` instance
|
||||||
- `std::type_identity<T>`: We specify the custom type we want here
|
by defining a single `tag_invoke` function:
|
||||||
- `simdjson::ondemand::value&` or `simdjson::ondemand::document&`: the value or document that we want to deserialize from; you may want to just specify `auto&` to capture both of them.
|
|
||||||
|
|
||||||
|
```C++
|
||||||
|
namespace simdjson {
|
||||||
|
// This tag_invoke MUST be inside simdjson namespace
|
||||||
|
template <typename simdjson_value>
|
||||||
|
auto tag_invoke(deserialize_tag, simdjson_value &val, Car& car) {
|
||||||
|
ondemand::object obj;
|
||||||
|
auto error = val.get_object().get(obj);
|
||||||
|
if (error) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
if ((error = obj["make"].get_string(car.make))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
if ((error = obj["model"].get_string(car.model))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
if ((error = obj["year"].get(car.year))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
if ((error = obj["tire_pressure"].get<std::vector<float>>().get(
|
||||||
|
car.tire_pressure))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
return simdjson::SUCCESS;
|
||||||
|
}
|
||||||
|
} // namespace simdjson
|
||||||
|
```
|
||||||
|
|
||||||
|
Observe how we call `get<std::vector<float>>()` even though we never defined support
|
||||||
|
for `std::vector<float>` in the simdjson library: it is all automated thanks to C++20 concepts.
|
||||||
|
|
||||||
|
Importantly, the `tag_invoke` function must be inside the `simdjson` namespace.
|
||||||
|
Let us explain each argument of `tag_invoke` function.
|
||||||
|
|
||||||
|
- `simdjson::deserialize_tag`: it is the tag for Customization Point Object (CPO). You may often ignore this parameter. It is used to indicate that you mean to provide a deserialization function for simdjson.
|
||||||
|
- `var`: It receives automatically a `simdjson` value type (document, value, document_reference).
|
||||||
|
- The third parameter is an instance of the type that you want to support.
|
||||||
|
|
||||||
You can use it like so:
|
You can use it like so:
|
||||||
|
|
||||||
@@ -1179,11 +1145,6 @@ Observe how we first get an instance of `document` and then we cast.
|
|||||||
You can also handle errors explicitly:
|
You can also handle errors explicitly:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
simdjson::padded_string json =
|
|
||||||
R"( { "make": "Toyota", "model": "Camry", "year": 2018,
|
|
||||||
"tire_pressure": [ 40.1, 39.9 ] })"_padded;
|
|
||||||
simdjson::ondemand::parser parser;
|
|
||||||
simdjson::ondemand::document doc = parser.iterate(json);
|
|
||||||
Car c;
|
Car c;
|
||||||
auto error = doc.get(c);
|
auto error = doc.get(c);
|
||||||
if(error) { std::cerr << simdjson::error_message(error); return false; }
|
if(error) { std::cerr << simdjson::error_message(error); return false; }
|
||||||
@@ -1215,8 +1176,6 @@ explicit casting. The cast may throw an exception.
|
|||||||
Once more, you can handle errors explicitly:
|
Once more, you can handle errors explicitly:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
simdjson::ondemand::parser parser;
|
|
||||||
simdjson::ondemand::document doc = parser.iterate(json);
|
|
||||||
for (auto val : doc) {
|
for (auto val : doc) {
|
||||||
Car c;
|
Car c;
|
||||||
auto error = val.get(c);
|
auto error = val.get(c);
|
||||||
@@ -1239,25 +1198,13 @@ You can also use the custom `Car` type as part of a template such as `std::vecto
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
You can also extend support to arbitrary templates.
|
By default, we support a wide range of standard templates such as
|
||||||
Suppose your custom types are `std::unique_ptr<any type>`,
|
`std::vector`, `std::list`, `std::set`, `std::stack`, `std:queue`,
|
||||||
you could add the following `tag_invoke`:
|
`std:deque`, `std::priority_queue`, `std::unordered_set`, `std::multiset`,
|
||||||
|
`std::unordered_multiset`, `std::unique_ptr`, `std::shared_ptr`, `std::optional`,
|
||||||
|
etc. They are handled automatically.
|
||||||
|
|
||||||
```C++
|
E.g., you can recover an `std::unique_ptr<Car>` like so:
|
||||||
namespace simdjson {
|
|
||||||
|
|
||||||
// This tag_invoke MUST be inside simdjson namespace since we can't make it a friend of unique_ptr
|
|
||||||
template <typename T>
|
|
||||||
auto tag_invoke(deserialize_tag, std::type_identity<std::unique_ptr<T>>, auto &val) {
|
|
||||||
return simdjson_result{std::make_unique<T>(val.template get<T>())};
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace simdjson
|
|
||||||
```
|
|
||||||
|
|
||||||
You can also mark your `tag_invoke` function as `noexcept` and we will obey that, unlike the *template specialization* way.
|
|
||||||
|
|
||||||
You would use it like this:
|
|
||||||
```C++
|
```C++
|
||||||
int main() {
|
int main() {
|
||||||
auto const json = R"( { "make": "Toyota", "model": "Camry", "year": 2018,
|
auto const json = R"( { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||||
@@ -1270,6 +1217,59 @@ int main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You may also conditionally fill in `std::optional` values.
|
||||||
|
|
||||||
|
```C++
|
||||||
|
|
||||||
|
padded_string json =
|
||||||
|
R"( { "car1": { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||||
|
"tire_pressure": [ 40.1, 39.9 ] }
|
||||||
|
})"_padded;
|
||||||
|
ondemand::parser parser;
|
||||||
|
ondemand::document doc = parser.iterate(json);
|
||||||
|
std::optional<Car> car;
|
||||||
|
error = doc["key not found"].get<std::optional<Car>>().get(car);
|
||||||
|
// car has no value, error != simdjson::SUCCESS
|
||||||
|
error = doc["car1"].get<std::optional<Car>>().get(car);
|
||||||
|
// car has value Car{"Toyota", "Camry", 2018, {40.1f, 39.9f}}
|
||||||
|
// error is simdjson::SUCCESS
|
||||||
|
```
|
||||||
|
|
||||||
|
And so forth.
|
||||||
|
|
||||||
|
Advanced users may want to overwrite the defaults provided by the simdjson library.
|
||||||
|
Suppose for example that you want to construct an instance of `std::list<Car>`, but
|
||||||
|
you also want to filter out any car made by Toyota. You may provide your own
|
||||||
|
`tag_invoke` function:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
namespace simdjson {
|
||||||
|
// suppose we want to filter out all Toyotas
|
||||||
|
template <typename simdjson_value>
|
||||||
|
auto tag_invoke(deserialize_tag, simdjson_value &val, std::list<Car>& car) {
|
||||||
|
ondemand::array arr;
|
||||||
|
auto error = val.get_array().get(arr);
|
||||||
|
if (error) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
for (auto v : arr) {
|
||||||
|
Car c;
|
||||||
|
if ((error = v.get<Car>().get(c))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
if(c.make != "Toyota") {
|
||||||
|
car.push_back(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return simdjson::SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
With this code, deserializing an `std::list<Car>` instance would capture only the cars
|
||||||
|
that are not made by Toyota.
|
||||||
|
|
||||||
|
|
||||||
Minifying JSON strings without parsing
|
Minifying JSON strings without parsing
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
@@ -1284,6 +1284,7 @@ In some cases, you may have valid JSON strings that you do not wish to parse but
|
|||||||
std::unique_ptr<char[]> buffer{new char[length]};
|
std::unique_ptr<char[]> buffer{new char[length]};
|
||||||
size_t new_length{}; // It will receive the minified length.
|
size_t new_length{}; // It will receive the minified length.
|
||||||
auto error = simdjson::minify(some_string, length, buffer.get(), new_length);
|
auto error = simdjson::minify(some_string, length, buffer.get(), new_length);
|
||||||
|
if(error) { std::cerr << simdjson::error_message(error); }
|
||||||
// The buffer variable now has "[1,2,3,4]" and new_length has value 9.
|
// The buffer variable now has "[1,2,3,4]" and new_length has value 9.
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -1481,7 +1482,7 @@ pair. You can retrieve the value with .get() without generating an exception, li
|
|||||||
```c++
|
```c++
|
||||||
ondemand::document doc;
|
ondemand::document doc;
|
||||||
auto error = parser.iterate(json).get(doc);
|
auto error = parser.iterate(json).get(doc);
|
||||||
if (error) { cerr << error << endl; exit(1); }
|
if(error) { std::cerr << simdjson::error_message(error); exit(1); }
|
||||||
```
|
```
|
||||||
|
|
||||||
When there is no error, the error code `simdjson::SUCCESS`is returned: it evaluates as false as a Boolean.
|
When there is no error, the error code `simdjson::SUCCESS`is returned: it evaluates as false as a Boolean.
|
||||||
@@ -1512,7 +1513,7 @@ bool simple_error_example() {
|
|||||||
auto error = doc["bad number"].get_double().get(x);
|
auto error = doc["bad number"].get_double().get(x);
|
||||||
// returns "simdjson::NUMBER_ERROR"
|
// returns "simdjson::NUMBER_ERROR"
|
||||||
if (error != SUCCESS) {
|
if (error != SUCCESS) {
|
||||||
std::cout << error << std::endl;
|
std::cerr << simdjson::error_message(error) << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
std::cout << "Got " << x << std::endl;
|
std::cout << "Got " << x << std::endl;
|
||||||
@@ -1565,7 +1566,6 @@ We can write a "quick start" example where we attempt to parse the following JSO
|
|||||||
Our program loads the file, selects value corresponding to key `"search_metadata"` which expected to be an object, and then
|
Our program loads the file, selects value corresponding to key `"search_metadata"` which expected to be an object, and then
|
||||||
it selects the key `"count"` within that object.
|
it selects the key `"count"` within that object.
|
||||||
|
|
||||||
|
|
||||||
```C++
|
```C++
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include "simdjson.h"
|
#include "simdjson.h"
|
||||||
@@ -1573,10 +1573,10 @@ it selects the key `"count"` within that object.
|
|||||||
int main(void) {
|
int main(void) {
|
||||||
simdjson::ondemand::parser parser;
|
simdjson::ondemand::parser parser;
|
||||||
auto error = padded_string::load("twitter.json").get(json);
|
auto error = padded_string::load("twitter.json").get(json);
|
||||||
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
|
if (error) { std::cerr << simdjson::error_message(error) << std::endl; return EXIT_FAILURE; }
|
||||||
simdjson::ondemand::document tweets;
|
simdjson::ondemand::document tweets;
|
||||||
error = parser.iterate(json).get(tweets);
|
error = parser.iterate(json).get(tweets);
|
||||||
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
|
if (error) { std::cerr << simdjson::error_message(error) << std::endl; return EXIT_FAILURE; }
|
||||||
simdjson::ondemand::value res;
|
simdjson::ondemand::value res;
|
||||||
error = tweets["search_metadata"]["count"].get(res);
|
error = tweets["search_metadata"]["count"].get(res);
|
||||||
if (error != SUCCESS) {
|
if (error != SUCCESS) {
|
||||||
@@ -1607,12 +1607,12 @@ int main(void) {
|
|||||||
simdjson::ondemand::document tweets;
|
simdjson::ondemand::document tweets;
|
||||||
padded_string json;
|
padded_string json;
|
||||||
auto error = padded_string::load("twitter.json").get(json);
|
auto error = padded_string::load("twitter.json").get(json);
|
||||||
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
|
if (error) { std::cerr << simdjson::error_message(error) << std::endl; return EXIT_FAILURE; }
|
||||||
error = parser.iterate(json).get(tweets);
|
error = parser.iterate(json).get(tweets);
|
||||||
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
|
if (error) { std::cerr << simdjson::error_message(error) << std::endl; return EXIT_FAILURE; }
|
||||||
uint64_t identifier;
|
uint64_t identifier;
|
||||||
error = tweets["statuses"].at(0)["id"].get(identifier);
|
error = tweets["statuses"].at(0)["id"].get(identifier);
|
||||||
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
|
if (error) { std::cerr << simdjson::error_message(error) << std::endl; return EXIT_FAILURE; }
|
||||||
std::cout << identifier << std::endl;
|
std::cout << identifier << std::endl;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -1636,40 +1636,40 @@ bool parse() {
|
|||||||
|
|
||||||
// Iterating through an array of objects
|
// Iterating through an array of objects
|
||||||
auto error = parser.iterate(cars_json).get(doc);
|
auto error = parser.iterate(cars_json).get(doc);
|
||||||
if (error) { std::cerr << error << std::endl; return false; }
|
if (error) { std::cerr << simdjson::error_message(error) << std::endl; return false; }
|
||||||
ondemand::array cars; // invalid until the get() succeeds
|
ondemand::array cars; // invalid until the get() succeeds
|
||||||
error = doc.get_array().get(cars);
|
error = doc.get_array().get(cars);
|
||||||
|
|
||||||
for (auto car_value : cars) {
|
for (auto car_value : cars) {
|
||||||
ondemand::object car; // invalid until the get() succeeds
|
ondemand::object car; // invalid until the get() succeeds
|
||||||
error = car_value.get_object().get(car);
|
error = car_value.get_object().get(car);
|
||||||
if (error) { std::cerr << error << std::endl; return false; }
|
if (error) { std::cerr << simdjson::error_message(error) << std::endl; return false; }
|
||||||
|
|
||||||
// Accessing a field by name
|
// Accessing a field by name
|
||||||
std::string_view make;
|
std::string_view make;
|
||||||
std::string_view model;
|
std::string_view model;
|
||||||
error = car["make"].get(make);
|
error = car["make"].get(make);
|
||||||
if (error) { std::cerr << error << std::endl; return false; }
|
if (error) { std::cerr << simdjson::error_message(error) << std::endl; return false; }
|
||||||
error = car["model"].get(model);
|
error = car["model"].get(model);
|
||||||
if (error) { std::cerr << error << std::endl; return false; }
|
if (error) { std::cerr << simdjson::error_message(error) << std::endl; return false; }
|
||||||
|
|
||||||
cout << "Make/Model: " << make << "/" << model << endl;
|
cout << "Make/Model: " << make << "/" << model << endl;
|
||||||
|
|
||||||
// Casting a JSON element to an integer
|
// Casting a JSON element to an integer
|
||||||
uint64_t year{};
|
uint64_t year{};
|
||||||
error = car["year"].get(year);
|
error = car["year"].get(year);
|
||||||
if (error) { std::cerr << error << std::endl; return false; }
|
if (error) { std::cerr << simdjson::error_message(error) << std::endl; return false; }
|
||||||
cout << "- This car is " << 2020 - year << " years old." << endl;
|
cout << "- This car is " << 2020 - year << " years old." << endl;
|
||||||
|
|
||||||
// Iterating through an array of floats
|
// Iterating through an array of floats
|
||||||
double total_tire_pressure = 0;
|
double total_tire_pressure = 0;
|
||||||
ondemand::array pressures;
|
ondemand::array pressures;
|
||||||
error = car["tire_pressure"].get_array().get(pressures);
|
error = car["tire_pressure"].get_array().get(pressures);
|
||||||
if (error) { std::cerr << error << std::endl; return false; }
|
if (error) { std::cerr << simdjson::error_message(error) << std::endl; return false; }
|
||||||
for (auto tire_pressure_value : pressures) {
|
for (auto tire_pressure_value : pressures) {
|
||||||
double tire_pressure;
|
double tire_pressure;
|
||||||
error = tire_pressure_value.get_double().get(tire_pressure);
|
error = tire_pressure_value.get_double().get(tire_pressure);
|
||||||
if (error) { std::cerr << error << std::endl; return false; }
|
if (error) { std::cerr << simdjson::error_message(error) << std::endl; return false; }
|
||||||
total_tire_pressure += tire_pressure;
|
total_tire_pressure += tire_pressure;
|
||||||
}
|
}
|
||||||
cout << "- Average tire pressure: " << (total_tire_pressure / 4) << endl;
|
cout << "- Average tire pressure: " << (total_tire_pressure / 4) << endl;
|
||||||
@@ -1791,7 +1791,7 @@ auto doc = parser.iterate(broken_json);
|
|||||||
int64_t i;
|
int64_t i;
|
||||||
auto error = doc["integer"].get_int64().get(i); // Expect to get integer from "integer" key, but get TAPE_ERROR
|
auto error = doc["integer"].get_int64().get(i); // Expect to get integer from "integer" key, but get TAPE_ERROR
|
||||||
if (error) {
|
if (error) {
|
||||||
std::cout << error << std::endl; // Prints TAPE_ERROR error message
|
std::cerr << simdjson::error_message(error) << std::endl; // Prints TAPE_ERROR error message
|
||||||
// Recover a pointer to the location of the first error:
|
// Recover a pointer to the location of the first error:
|
||||||
const char * ptr;
|
const char * ptr;
|
||||||
doc.current_location().get(ptr);
|
doc.current_location().get(ptr);
|
||||||
@@ -1828,7 +1828,7 @@ auto doc = parser.iterate(json);
|
|||||||
int64_t i;
|
int64_t i;
|
||||||
auto error = doc["integer"].get_int64().get(i); // Incorrect call on array, INCORRECT_TYPE error
|
auto error = doc["integer"].get_int64().get(i); // Incorrect call on array, INCORRECT_TYPE error
|
||||||
if (error) {
|
if (error) {
|
||||||
std::cout << error << std::endl; // Prints INCORRECT_TYPE error message
|
std::cerr << simdjson::error_message(error) << std::endl; // Prints INCORRECT_TYPE error message
|
||||||
std::cout<< doc.current_location() << std::endl; // Prints "[1,2,3] " (location of INCORRECT_TYPE error)
|
std::cout<< doc.current_location() << std::endl; // Prints "[1,2,3] " (location of INCORRECT_TYPE error)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -2001,7 +2001,7 @@ for (auto doc: stream) {
|
|||||||
error = doc.at_pointer("/4").get(val);
|
error = doc.at_pointer("/4").get(val);
|
||||||
// error == simdjson::CAPACITY
|
// error == simdjson::CAPACITY
|
||||||
if (error) {
|
if (error) {
|
||||||
std::cerr << error << std::endl;
|
std::cerr << simdjson::error_message(error) << std::endl;
|
||||||
// We left 293 bytes unprocessed at the tail end of the input.
|
// We left 293 bytes unprocessed at the tail end of the input.
|
||||||
std::cout << " unprocessed bytes at the end: " << stream.truncated_bytes() << std::endl;
|
std::cout << " unprocessed bytes at the end: " << stream.truncated_bytes() << std::endl;
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ Contents
|
|||||||
- [Use cases](#use-cases)
|
- [Use cases](#use-cases)
|
||||||
- [Tracking your position](#tracking-your-position)
|
- [Tracking your position](#tracking-your-position)
|
||||||
- [Incomplete streams](#incomplete-streams)
|
- [Incomplete streams](#incomplete-streams)
|
||||||
|
- [C++20 features](#c20-features)
|
||||||
|
|
||||||
Motivation
|
Motivation
|
||||||
-----------
|
-----------
|
||||||
@@ -288,3 +289,111 @@ string
|
|||||||
object
|
object
|
||||||
array
|
array
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
C++20 features
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
In C++20, the standard introduced the notion of *customization point*.
|
||||||
|
A customization point is a function or function object that can be customized for different types. It allows library authors to provide default behavior while giving users the ability to override this behavior for specific types.
|
||||||
|
|
||||||
|
A tag_invoke function serves as a mechanism for customization points. It is not directly part of the C++ standard library but is often used in libraries that implement customization points.
|
||||||
|
The tag_invoke function is typically a generic function that takes a tag type and additional arguments.
|
||||||
|
The first argument is usually a tag type (often an empty struct) that uniquely identifies the customization point (e.g., deserialization of custom types in simdjson). Users or library providers can specialize tag_invoke for their types by defining it in the appropriate namespace, often inline namespace.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
You can deserialize you own data structures conveniently if your system supports C++20.
|
||||||
|
When it is the case, the macro `SIMDJSON_SUPPORTS_DESERIALIZATION` will be set to 1 by
|
||||||
|
the simdjson library.
|
||||||
|
|
||||||
|
Consider a custom class `Car`:
|
||||||
|
|
||||||
|
```C++
|
||||||
|
struct Car {
|
||||||
|
std::string make;
|
||||||
|
std::string model;
|
||||||
|
int year;
|
||||||
|
std::vector<float> tire_pressure;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
You may support deserializing directly from a JSON value or document to your own `Car` instance
|
||||||
|
by defining a single `tag_invoke` function:
|
||||||
|
|
||||||
|
|
||||||
|
```C++
|
||||||
|
namespace simdjson {
|
||||||
|
// This tag_invoke MUST be inside simdjson namespace
|
||||||
|
template <typename simdjson_value>
|
||||||
|
auto tag_invoke(deserialize_tag, simdjson_value &val, Car& car) {
|
||||||
|
ondemand::object obj;
|
||||||
|
auto error = val.get_object().get(obj);
|
||||||
|
if (error) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
if ((error = obj["make"].get_string(car.make))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
if ((error = obj["model"].get_string(car.model))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
if ((error = obj["year"].get(car.year))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
if ((error = obj["tire_pressure"].get<std::vector<float>>().get(
|
||||||
|
car.tire_pressure))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
return simdjson::SUCCESS;
|
||||||
|
}
|
||||||
|
} // namespace simdjson
|
||||||
|
```
|
||||||
|
|
||||||
|
Importantly, the `tag_invoke` function must be inside the `simdjson` namespace.
|
||||||
|
Let us explain each argument of `tag_invoke` function.
|
||||||
|
|
||||||
|
- `simdjson::deserialize_tag`: it is the tag for Customization Point Object (CPO). You may often ignore this parameter. It is used to indicate that you mean to provide a deserialization function for simdjson.
|
||||||
|
- `var`: It receives automatically a `simdjson` value type (document, value, document_reference).
|
||||||
|
- The third parameter is an instance of the type that you want to support.
|
||||||
|
|
||||||
|
Please see our main documentation (`basics.md`) under
|
||||||
|
"Use `tag_invoke` for custom types (C++20)" for details about
|
||||||
|
tag_invoke functions.
|
||||||
|
|
||||||
|
Given a stream of JSON documents, you can add them to a data struture
|
||||||
|
such as a `std::vector<Car>` like so if you support exceptions:
|
||||||
|
|
||||||
|
```C++
|
||||||
|
padded_string json =
|
||||||
|
R"( { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||||
|
"tire_pressure": [ 40.1, 39.9 ] }
|
||||||
|
{ "make": "Kia", "model": "Soul", "year": 2012,
|
||||||
|
"tire_pressure": [ 30.1, 31.0 ] }
|
||||||
|
{ "make": "Toyota", "model": "Tercel", "year": 1999,
|
||||||
|
"tire_pressure": [ 29.8, 30.0 ] }
|
||||||
|
)"_padded;
|
||||||
|
ondemand::parser parser;
|
||||||
|
ondemand::document_stream stream;
|
||||||
|
[[maybe_unused]] auto error = parser.iterate_many(json).get(stream);
|
||||||
|
std::vector<Car> cars;
|
||||||
|
for(auto doc : stream) {
|
||||||
|
cars.push_back((Car)doc); // an exception may be thrown
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Otherwise you may use this longer version for explicit handling of errors:
|
||||||
|
|
||||||
|
|
||||||
|
```C++
|
||||||
|
std::vector<Car> cars;
|
||||||
|
for(auto doc : stream) {
|
||||||
|
Car c;
|
||||||
|
if ((error = doc.get<Car>().get(c))) {
|
||||||
|
std::cerr << simdjson::error_message(error); << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
cars.push_back(c);
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -53,5 +53,4 @@
|
|||||||
|
|
||||||
#include "simdjson/dom.h"
|
#include "simdjson/dom.h"
|
||||||
#include "simdjson/ondemand.h"
|
#include "simdjson/ondemand.h"
|
||||||
|
|
||||||
#endif // SIMDJSON_H
|
#endif // SIMDJSON_H
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include "simdjson/compiler_check.h"
|
#include "simdjson/compiler_check.h"
|
||||||
#include "simdjson/error.h"
|
#include "simdjson/error.h"
|
||||||
#include "simdjson/portability.h"
|
#include "simdjson/portability.h"
|
||||||
|
#include "simdjson/concepts.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The top level simdjson namespace, containing everything the library provides.
|
* @brief The top level simdjson namespace, containing everything the library provides.
|
||||||
|
|||||||
@@ -0,0 +1,113 @@
|
|||||||
|
#ifndef SIMDJSON_CONCEPTS_H
|
||||||
|
#define SIMDJSON_CONCEPTS_H
|
||||||
|
#if SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
|
|
||||||
|
#include <concepts>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace simdjson {
|
||||||
|
namespace concepts {
|
||||||
|
|
||||||
|
namespace details {
|
||||||
|
#define SIMDJSON_IMPL_CONCEPT(name, method) \
|
||||||
|
template <typename T> \
|
||||||
|
concept supports_##name = !std::is_const_v<T> && requires { \
|
||||||
|
typename std::remove_cvref_t<T>::value_type; \
|
||||||
|
requires requires(typename std::remove_cvref_t<T>::value_type &&val, \
|
||||||
|
T obj) { \
|
||||||
|
obj.method(std::move(val)); \
|
||||||
|
requires !requires { obj = std::move(val); }; \
|
||||||
|
}; \
|
||||||
|
};
|
||||||
|
|
||||||
|
SIMDJSON_IMPL_CONCEPT(emplace_back, emplace_back);
|
||||||
|
SIMDJSON_IMPL_CONCEPT(emplace, emplace);
|
||||||
|
SIMDJSON_IMPL_CONCEPT(push_back, push_back);
|
||||||
|
SIMDJSON_IMPL_CONCEPT(add, add);
|
||||||
|
SIMDJSON_IMPL_CONCEPT(push, push);
|
||||||
|
SIMDJSON_IMPL_CONCEPT(append, append);
|
||||||
|
SIMDJSON_IMPL_CONCEPT(insert, insert);
|
||||||
|
SIMDJSON_IMPL_CONCEPT(op_append, operator+=);
|
||||||
|
|
||||||
|
#undef SIMDJSON_IMPL_CONCEPT
|
||||||
|
} // namespace details
|
||||||
|
|
||||||
|
/// Check if T is a container that we can append to, including:
|
||||||
|
/// std::vector, std::deque, std::list, std::string, ...
|
||||||
|
template <typename T>
|
||||||
|
concept appendable_containers =
|
||||||
|
details::supports_emplace_back<T> || details::supports_emplace<T> ||
|
||||||
|
details::supports_push_back<T> || details::supports_push<T> ||
|
||||||
|
details::supports_add<T> || details::supports_append<T> ||
|
||||||
|
details::supports_insert<T>;
|
||||||
|
|
||||||
|
/// Insert into the container however possible
|
||||||
|
template <appendable_containers T, typename... Args>
|
||||||
|
constexpr decltype(auto) emplace_one(T &vec, Args &&...args) {
|
||||||
|
if constexpr (details::supports_emplace_back<T>) {
|
||||||
|
return vec.emplace_back(std::forward<Args>(args)...);
|
||||||
|
} else if constexpr (details::supports_emplace<T>) {
|
||||||
|
return vec.emplace(std::forward<Args>(args)...);
|
||||||
|
} else if constexpr (details::supports_push_back<T>) {
|
||||||
|
return vec.push_back(std::forward<Args>(args)...);
|
||||||
|
} else if constexpr (details::supports_push<T>) {
|
||||||
|
return vec.push(std::forward<Args>(args)...);
|
||||||
|
} else if constexpr (details::supports_add<T>) {
|
||||||
|
return vec.add(std::forward<Args>(args)...);
|
||||||
|
} else if constexpr (details::supports_append<T>) {
|
||||||
|
return vec.append(std::forward<Args>(args)...);
|
||||||
|
} else if constexpr (details::supports_insert<T>) {
|
||||||
|
return vec.insert(std::forward<Args>(args)...);
|
||||||
|
} else if constexpr (details::supports_op_append<T> && sizeof...(Args) == 1) {
|
||||||
|
return vec.operator+=(std::forward<Args>(args)...);
|
||||||
|
} else {
|
||||||
|
static_assert(!sizeof(T *),
|
||||||
|
"We don't know how to add things to this container");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This checks if the container will return a reference to the newly added
|
||||||
|
/// element after an insert which for example `std::vector::emplace_back` does
|
||||||
|
/// since C++17; this will allow some optimizations.
|
||||||
|
template <typename T>
|
||||||
|
concept returns_reference = appendable_containers<T> && requires {
|
||||||
|
typename std::remove_cvref_t<T>::reference;
|
||||||
|
requires requires(typename std::remove_cvref_t<T>::value_type &&val, T obj) {
|
||||||
|
{
|
||||||
|
emplace_one(obj, std::move(val))
|
||||||
|
} -> std::same_as<typename std::remove_cvref_t<T>::reference>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
concept smart_pointer = requires(std::remove_cvref_t<T> ptr) {
|
||||||
|
// Check if T has a member type named element_type
|
||||||
|
typename std::remove_cvref_t<T>::element_type;
|
||||||
|
|
||||||
|
// Check if T has a get() member function
|
||||||
|
{
|
||||||
|
ptr.get()
|
||||||
|
} -> std::same_as<typename std::remove_cvref_t<T>::element_type *>;
|
||||||
|
|
||||||
|
// Check if T can be dereferenced
|
||||||
|
{ *ptr } -> std::same_as<typename std::remove_cvref_t<T>::element_type &>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
concept optional_type = requires(std::remove_cvref_t<T> obj) {
|
||||||
|
typename std::remove_cvref_t<T>::value_type;
|
||||||
|
{ obj.value() } -> std::same_as<typename std::remove_cvref_t<T>::value_type&>;
|
||||||
|
requires requires(typename std::remove_cvref_t<T>::value_type &&val) {
|
||||||
|
obj.emplace(std::move(val));
|
||||||
|
obj = std::move(val);
|
||||||
|
{
|
||||||
|
obj.value_or(val)
|
||||||
|
} -> std::convertible_to<typename std::remove_cvref_t<T>::value_type>;
|
||||||
|
};
|
||||||
|
{ static_cast<bool>(obj) } -> std::same_as<bool>; // convertible to bool
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace concepts
|
||||||
|
} // namespace simdjson
|
||||||
|
#endif // SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
|
#endif // SIMDJSON_CONCEPTS_H
|
||||||
@@ -16,5 +16,4 @@
|
|||||||
#include "simdjson/internal/jsoncharutils_tables.h"
|
#include "simdjson/internal/jsoncharutils_tables.h"
|
||||||
#include "simdjson/internal/numberparsing_tables.h"
|
#include "simdjson/internal/numberparsing_tables.h"
|
||||||
#include "simdjson/internal/simdprune_tables.h"
|
#include "simdjson/internal/simdprune_tables.h"
|
||||||
|
|
||||||
#endif // SIMDJSON_GENERIC_DEPENDENCIES_H
|
#endif // SIMDJSON_GENERIC_DEPENDENCIES_H
|
||||||
@@ -90,6 +90,7 @@ concept nothrow_deserializable = nothrow_custom_deserializable<T, ValT> || is_bu
|
|||||||
inline constexpr struct deserialize_tag {
|
inline constexpr struct deserialize_tag {
|
||||||
using value_type = SIMDJSON_IMPLEMENTATION::ondemand::value;
|
using value_type = SIMDJSON_IMPLEMENTATION::ondemand::value;
|
||||||
using document_type = SIMDJSON_IMPLEMENTATION::ondemand::document;
|
using document_type = SIMDJSON_IMPLEMENTATION::ondemand::document;
|
||||||
|
using document_reference_type = SIMDJSON_IMPLEMENTATION::ondemand::document_reference;
|
||||||
|
|
||||||
// Customization Point for value
|
// Customization Point for value
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@@ -105,6 +106,14 @@ inline constexpr struct deserialize_tag {
|
|||||||
return tag_invoke(*this, object, output);
|
return tag_invoke(*this, object, output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Customization Point for document reference
|
||||||
|
template <typename T>
|
||||||
|
requires custom_deserializable<T, document_reference_type>
|
||||||
|
[[nodiscard]] constexpr /* error_code */ auto operator()(document_reference_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, document_reference_type>) {
|
||||||
|
return tag_invoke(*this, object, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} deserialize{};
|
} deserialize{};
|
||||||
|
|
||||||
} // namespace simdjson
|
} // namespace simdjson
|
||||||
|
|||||||
@@ -816,6 +816,26 @@ simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::o
|
|||||||
if (error()) { return error(); }
|
if (error()) { return error(); }
|
||||||
return first.is_null();
|
return first.is_null();
|
||||||
}
|
}
|
||||||
|
template<typename T>
|
||||||
|
simdjson_inline simdjson_result<T> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get() & noexcept {
|
||||||
|
if (error()) { return error(); }
|
||||||
|
return first.get<T>();
|
||||||
|
}
|
||||||
|
template<typename T>
|
||||||
|
simdjson_inline simdjson_result<T> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get() && noexcept {
|
||||||
|
if (error()) { return error(); }
|
||||||
|
return std::forward<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>(first).get<T>();
|
||||||
|
}
|
||||||
|
template <class T>
|
||||||
|
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get(T &out) & noexcept {
|
||||||
|
if (error()) { return error(); }
|
||||||
|
return first.get<T>(out);
|
||||||
|
}
|
||||||
|
template <class T>
|
||||||
|
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get(T &out) && noexcept {
|
||||||
|
if (error()) { return error(); }
|
||||||
|
return std::forward<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>(first).get<T>(out);
|
||||||
|
}
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::json_type> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::type() noexcept {
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::json_type> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::type() noexcept {
|
||||||
if (error()) { return error(); }
|
if (error()) { return error(); }
|
||||||
return first.type();
|
return first.type();
|
||||||
@@ -828,6 +848,18 @@ simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::o
|
|||||||
if (error()) { return error(); }
|
if (error()) { return error(); }
|
||||||
return first.is_string();
|
return first.is_string();
|
||||||
}
|
}
|
||||||
|
template <>
|
||||||
|
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get(SIMDJSON_IMPLEMENTATION::ondemand::document_reference &out) & noexcept {
|
||||||
|
if (error()) { return error(); }
|
||||||
|
out = first;
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
template <>
|
||||||
|
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get(SIMDJSON_IMPLEMENTATION::ondemand::document_reference &out) && noexcept {
|
||||||
|
if (error()) { return error(); }
|
||||||
|
out = first;
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::is_negative() noexcept {
|
simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::is_negative() noexcept {
|
||||||
if (error()) { return error(); }
|
if (error()) { return error(); }
|
||||||
return first.is_negative();
|
return first.is_negative();
|
||||||
@@ -845,10 +877,12 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::number> simdj
|
|||||||
return first.get_number();
|
return first.get_number();
|
||||||
}
|
}
|
||||||
#if SIMDJSON_EXCEPTIONS
|
#if SIMDJSON_EXCEPTIONS
|
||||||
template <class T, typename std::enable_if<std::is_same<T, SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::value == false>::type>
|
template <class T>
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::operator T() noexcept(false) {
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::operator T() noexcept(false) {
|
||||||
|
static_assert(std::is_same<T, SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::value == false, "You should not call get<T> when T is a document");
|
||||||
|
static_assert(std::is_same<T, SIMDJSON_IMPLEMENTATION::ondemand::document>::value == false, "You should not call get<T> when T is a document");
|
||||||
if (error()) { throw simdjson_error(error()); }
|
if (error()) { throw simdjson_error(error()); }
|
||||||
return first;
|
return first.get<T>();
|
||||||
}
|
}
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::operator SIMDJSON_IMPLEMENTATION::ondemand::array() & noexcept(false) {
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::operator SIMDJSON_IMPLEMENTATION::ondemand::array() & noexcept(false) {
|
||||||
if (error()) { throw simdjson_error(error()); }
|
if (error()) { throw simdjson_error(error()); }
|
||||||
|
|||||||
@@ -756,7 +756,70 @@ public:
|
|||||||
simdjson_inline simdjson_result<value> get_value() noexcept;
|
simdjson_inline simdjson_result<value> get_value() noexcept;
|
||||||
|
|
||||||
simdjson_inline simdjson_result<bool> is_null() noexcept;
|
simdjson_inline simdjson_result<bool> is_null() noexcept;
|
||||||
template<typename T> simdjson_inline simdjson_result<T> get() & noexcept;
|
template <typename T>
|
||||||
|
simdjson_inline simdjson_result<T> get() &
|
||||||
|
#if SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
|
noexcept(custom_deserializable<T, document> ? nothrow_custom_deserializable<T, document> : true)
|
||||||
|
#else
|
||||||
|
noexcept
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
static_assert(std::is_default_constructible<T>::value, "Cannot initialize the specified type.");
|
||||||
|
T out{};
|
||||||
|
SIMDJSON_TRY(get<T>(out));
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
template<typename T>
|
||||||
|
simdjson_inline simdjson_result<T> get() &&
|
||||||
|
#if SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
|
noexcept(custom_deserializable<T, document> ? nothrow_custom_deserializable<T, document> : true)
|
||||||
|
#else
|
||||||
|
noexcept
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
static_assert(!std::is_same<T, array>::value && !std::is_same<T, object>::value, "You should never hold either an ondemand::array or ondemand::object without a corresponding ondemand::document_reference being alive; that would be Undefined Behaviour.");
|
||||||
|
return static_cast<document&>(*this).get<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get this value as the given type.
|
||||||
|
*
|
||||||
|
* Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool, value
|
||||||
|
*
|
||||||
|
* Be mindful that the document instance must remain in scope while you are accessing object, array and value instances.
|
||||||
|
*
|
||||||
|
* @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized.
|
||||||
|
* @returns INCORRECT_TYPE If the JSON value is not an object.
|
||||||
|
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
simdjson_inline error_code get(T &out) &
|
||||||
|
#if SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
|
noexcept(custom_deserializable<T, document> ? nothrow_custom_deserializable<T, document_reference> : true)
|
||||||
|
#else
|
||||||
|
noexcept
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
#if SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
|
if constexpr (custom_deserializable<T, document_reference>) {
|
||||||
|
return deserialize(*this, out);
|
||||||
|
} else {
|
||||||
|
#endif // SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
|
// Unless the simdjson library or the user provides an inline implementation, calling this method should
|
||||||
|
// immediately fail.
|
||||||
|
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
|
||||||
|
"The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
|
||||||
|
"int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), "
|
||||||
|
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template."
|
||||||
|
" You may also add support for custom types, see our documentation.");
|
||||||
|
static_cast<void>(out); // to get rid of unused errors
|
||||||
|
return UNINITIALIZED;
|
||||||
|
#if SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
/** @overload template<typename T> error_code get(T &out) & noexcept */
|
||||||
|
template<typename T> simdjson_inline error_code get(T &out) && noexcept;
|
||||||
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
|
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
|
||||||
simdjson_inline operator document&() const noexcept;
|
simdjson_inline operator document&() const noexcept;
|
||||||
#if SIMDJSON_EXCEPTIONS
|
#if SIMDJSON_EXCEPTIONS
|
||||||
@@ -908,8 +971,14 @@ public:
|
|||||||
simdjson_inline simdjson_result<bool> get_bool() noexcept;
|
simdjson_inline simdjson_result<bool> get_bool() noexcept;
|
||||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> get_value() noexcept;
|
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> get_value() noexcept;
|
||||||
simdjson_inline simdjson_result<bool> is_null() noexcept;
|
simdjson_inline simdjson_result<bool> is_null() noexcept;
|
||||||
|
|
||||||
|
template<typename T> simdjson_inline simdjson_result<T> get() & noexcept;
|
||||||
|
template<typename T> simdjson_inline simdjson_result<T> get() && noexcept;
|
||||||
|
|
||||||
|
template<typename T> simdjson_inline error_code get(T &out) & noexcept;
|
||||||
|
template<typename T> simdjson_inline error_code get(T &out) && noexcept;
|
||||||
#if SIMDJSON_EXCEPTIONS
|
#if SIMDJSON_EXCEPTIONS
|
||||||
template <class T, typename std::enable_if<std::is_same<T, SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::value == false>::type>
|
template <class T>
|
||||||
explicit simdjson_inline operator T() noexcept(false);
|
explicit simdjson_inline operator T() noexcept(false);
|
||||||
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::array() & noexcept(false);
|
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::array() & noexcept(false);
|
||||||
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::object() & noexcept(false);
|
simdjson_inline operator SIMDJSON_IMPLEMENTATION::ondemand::object() & noexcept(false);
|
||||||
|
|||||||
@@ -152,7 +152,6 @@ simdjson_inline document_stream::iterator::iterator(document_stream* _stream, bo
|
|||||||
}
|
}
|
||||||
|
|
||||||
simdjson_inline simdjson_result<ondemand::document_reference> document_stream::iterator::operator*() noexcept {
|
simdjson_inline simdjson_result<ondemand::document_reference> document_stream::iterator::operator*() noexcept {
|
||||||
//if(stream->error) { return stream->error; }
|
|
||||||
return simdjson_result<ondemand::document_reference>(stream->doc, stream->error);
|
return simdjson_result<ondemand::document_reference>(stream->doc, stream->error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,24 +3,23 @@
|
|||||||
#ifndef SIMDJSON_ONDEMAND_DESERIALIZE_H
|
#ifndef SIMDJSON_ONDEMAND_DESERIALIZE_H
|
||||||
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
|
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
|
||||||
#define SIMDJSON_ONDEMAND_DESERIALIZE_H
|
#define SIMDJSON_ONDEMAND_DESERIALIZE_H
|
||||||
#include "simdjson/generic/ondemand/base.h"
|
|
||||||
#include "simdjson/generic/ondemand/array.h"
|
#include "simdjson/generic/ondemand/array.h"
|
||||||
|
#include "simdjson/generic/ondemand/base.h"
|
||||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||||
|
|
||||||
#include <concepts>
|
#include <concepts>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <list>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace simdjson {
|
namespace simdjson {
|
||||||
|
template <typename T>
|
||||||
|
constexpr bool require_custom_serialization = false;
|
||||||
|
|
||||||
//////////////////////////////
|
//////////////////////////////
|
||||||
// Number deserialization
|
// Number deserialization
|
||||||
//////////////////////////////
|
//////////////////////////////
|
||||||
|
|
||||||
template <std::unsigned_integral T>
|
template <std::unsigned_integral T>
|
||||||
|
requires(!require_custom_serialization<T>)
|
||||||
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
|
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
|
||||||
using limits = std::numeric_limits<T>;
|
using limits = std::numeric_limits<T>;
|
||||||
|
|
||||||
@@ -34,6 +33,7 @@ error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <std::floating_point T>
|
template <std::floating_point T>
|
||||||
|
requires(!require_custom_serialization<T>)
|
||||||
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
|
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
|
||||||
double x;
|
double x;
|
||||||
SIMDJSON_TRY(val.get_double().get(x));
|
SIMDJSON_TRY(val.get_double().get(x));
|
||||||
@@ -42,6 +42,7 @@ error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <std::signed_integral T>
|
template <std::signed_integral T>
|
||||||
|
requires(!require_custom_serialization<T>)
|
||||||
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
|
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
|
||||||
using limits = std::numeric_limits<T>;
|
using limits = std::numeric_limits<T>;
|
||||||
|
|
||||||
@@ -54,66 +55,6 @@ error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
|
|||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////
|
|
||||||
// STL list deserialization
|
|
||||||
//////////////////////////////
|
|
||||||
|
|
||||||
template <typename T, typename AllocT, typename ValT>
|
|
||||||
error_code tag_invoke(deserialize_tag, ValT &val,
|
|
||||||
std::list<T, AllocT> &out) noexcept(false) {
|
|
||||||
|
|
||||||
// For better error messages, don't use these as constraints on
|
|
||||||
// the tag_invoke CPO.
|
|
||||||
static_assert(
|
|
||||||
deserializable<T, ValT>,
|
|
||||||
"The specified type inside the list must itself be deserializable");
|
|
||||||
static_assert(
|
|
||||||
std::is_default_constructible_v<T>,
|
|
||||||
"The specified type inside the list must default constructible.");
|
|
||||||
|
|
||||||
SIMDJSON_IMPLEMENTATION::ondemand::array arr;
|
|
||||||
SIMDJSON_TRY(val.get_array().get(arr));
|
|
||||||
for (auto v : arr) {
|
|
||||||
if (auto const err = v.get<T>().get(out.emplace_back()); err) {
|
|
||||||
// If an error occurs, the empty element that we just inserted gets
|
|
||||||
// removed. We're not using a temp variable because if T is a heavy type,
|
|
||||||
// we want the valid path to be the fast path and the slow path be the
|
|
||||||
// path that has errors in it.
|
|
||||||
static_cast<void>(out.pop_back());
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////
|
|
||||||
// std::string deserialization
|
|
||||||
//////////////////////////////
|
|
||||||
|
|
||||||
template <typename CharT,
|
|
||||||
typename TraitsT,
|
|
||||||
typename AllocT,
|
|
||||||
typename ValT>
|
|
||||||
error_code tag_invoke(deserialize_tag, ValT &val, std::basic_string<CharT, TraitsT, AllocT> &out) noexcept(false) {
|
|
||||||
using string_type = std::basic_string<CharT, TraitsT, AllocT>;
|
|
||||||
|
|
||||||
if constexpr (std::same_as<string_type, string_type>) {
|
|
||||||
SIMDJSON_TRY(val.get_string(out));
|
|
||||||
} else {
|
|
||||||
// todo: optimize performance
|
|
||||||
std::string tmp;
|
|
||||||
SIMDJSON_TRY(val.get_string(tmp));
|
|
||||||
for (auto const ch : tmp) {
|
|
||||||
out.push_back(ch);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////
|
|
||||||
// STL Vector deserialization
|
|
||||||
//////////////////////////////
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* STL containers have several constructors including one that takes a single
|
* STL containers have several constructors including one that takes a single
|
||||||
* size argument. Thus, some compilers (Visual Studio) will not be able to
|
* size argument. Thus, some compilers (Visual Studio) will not be able to
|
||||||
@@ -121,75 +62,104 @@ error_code tag_invoke(deserialize_tag, ValT &val, std::basic_string<CharT, Trait
|
|||||||
* explicitly specify the type of the container as needed: e.g.,
|
* explicitly specify the type of the container as needed: e.g.,
|
||||||
* doc.get<std::vector<int>>().
|
* doc.get<std::vector<int>>().
|
||||||
*/
|
*/
|
||||||
template <typename T, typename AllocT, typename ValT>
|
template <concepts::appendable_containers T, typename ValT>
|
||||||
error_code tag_invoke(deserialize_tag, ValT &val,
|
requires(!require_custom_serialization<T>)
|
||||||
std::vector<T, AllocT> &out) noexcept(false) {
|
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
|
||||||
|
using value_type = typename std::remove_cvref_t<T>::value_type;
|
||||||
// For better error messages, don't use these as constraints on
|
|
||||||
// the tag_invoke CPO.
|
|
||||||
static_assert(
|
static_assert(
|
||||||
deserializable<T, ValT>,
|
deserializable<value_type, ValT>,
|
||||||
"The specified type inside the vector must itself be deserializable");
|
"The specified type inside the container must itself be deserializable");
|
||||||
static_assert(
|
static_assert(
|
||||||
std::is_default_constructible_v<T>,
|
std::is_default_constructible_v<value_type>,
|
||||||
"The specified type inside the vector must default constructible.");
|
"The specified type inside the container must default constructible.");
|
||||||
|
|
||||||
SIMDJSON_IMPLEMENTATION::ondemand::array arr;
|
SIMDJSON_IMPLEMENTATION::ondemand::array arr;
|
||||||
SIMDJSON_TRY(val.get_array().get(arr));
|
SIMDJSON_TRY(val.get_array().get(arr));
|
||||||
for (auto v : arr) {
|
for (auto v : arr) {
|
||||||
if (auto const err = v.get<T>().get(out.emplace_back()); err) {
|
if constexpr (concepts::returns_reference<T>) {
|
||||||
// If an error occurs, the empty element that we just inserted gets
|
if (auto const err = v.get<value_type>().get(concepts::emplace_one(out));
|
||||||
// removed. We're not using a temp variable because if T is a heavy type,
|
err) {
|
||||||
// we want the valid path to be the fast path and the slow path be the
|
// If an error occurs, the empty element that we just inserted gets
|
||||||
// path that has errors in it.
|
// removed. We're not using a temp variable because if T is a heavy
|
||||||
static_cast<void>(out.pop_back());
|
// type, we want the valid path to be the fast path and the slow path be
|
||||||
return err;
|
// the path that has errors in it.
|
||||||
|
if constexpr (requires { out.pop_back(); }) {
|
||||||
|
static_cast<void>(out.pop_back());
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
value_type temp;
|
||||||
|
if (auto const err = v.get<value_type>().get(temp); err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
concepts::emplace_one(out, std::move(temp));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////
|
|
||||||
// std::unique_ptr deserialization
|
|
||||||
//////////////////////////////
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This CPO (Customization Point Object) will help deserialize into
|
* This CPO (Customization Point Object) will help deserialize into
|
||||||
* `unique_ptr`s.
|
* smart pointers.
|
||||||
*
|
*
|
||||||
* If constructing T is nothrow, this conversion should be nothrow as well since
|
* If constructing T is nothrow, this conversion should be nothrow as well since
|
||||||
* we return MEMALLOC if we're not able to allocate memory instead of throwing
|
* we return MEMALLOC if we're not able to allocate memory instead of throwing
|
||||||
* the the error message.
|
* the error message.
|
||||||
*
|
*
|
||||||
* @tparam T The type inside the unique_ptr
|
* @tparam T The type inside the smart pointer
|
||||||
* @tparam Deleter The Deleter of the unique_ptr
|
|
||||||
* @tparam ValT document/value type
|
* @tparam ValT document/value type
|
||||||
* @param val document/value
|
* @param val document/value
|
||||||
* @param out output unique_ptr
|
* @param out a reference to the smart pointer
|
||||||
* @return status of the conversion
|
* @return status of the conversion
|
||||||
*/
|
*/
|
||||||
template <typename T, typename Deleter, typename ValT>
|
template <concepts::smart_pointer T, typename ValT>
|
||||||
error_code tag_invoke(deserialize_tag, ValT &val,
|
requires(!require_custom_serialization<T>)
|
||||||
std::unique_ptr<T, Deleter>
|
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable<typename std::remove_cvref_t<T>::element_type, ValT>) {
|
||||||
&out) noexcept(nothrow_deserializable<T, ValT>) {
|
using element_type = typename std::remove_cvref_t<T>::element_type;
|
||||||
|
|
||||||
// For better error messages, don't use these as constraints on
|
// For better error messages, don't use these as constraints on
|
||||||
// the tag_invoke CPO.
|
// the tag_invoke CPO.
|
||||||
static_assert(
|
static_assert(
|
||||||
deserializable<T, ValT>,
|
deserializable<element_type, ValT>,
|
||||||
"The specified type inside the unique_ptr must itself be deserializable");
|
"The specified type inside the unique_ptr must itself be deserializable");
|
||||||
static_assert(
|
static_assert(
|
||||||
std::is_default_constructible_v<T>,
|
std::is_default_constructible_v<element_type>,
|
||||||
"The specified type inside the unique_ptr must default constructible.");
|
"The specified type inside the unique_ptr must default constructible.");
|
||||||
|
|
||||||
auto ptr = new (std::nothrow) T();
|
auto ptr = new (std::nothrow) element_type();
|
||||||
if (ptr == nullptr) {
|
if (ptr == nullptr) {
|
||||||
return MEMALLOC;
|
return MEMALLOC;
|
||||||
}
|
}
|
||||||
SIMDJSON_TRY(val.template get<T>(*ptr));
|
SIMDJSON_TRY(val.template get<element_type>(*ptr));
|
||||||
out.reset(ptr);
|
out.reset(ptr);
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This CPO (Customization Point Object) will help deserialize into optional types.
|
||||||
|
*/
|
||||||
|
template <concepts::optional_type T, typename ValT>
|
||||||
|
requires(!require_custom_serialization<T>)
|
||||||
|
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable<typename std::remove_cvref_t<T>::value_type, ValT>) {
|
||||||
|
using value_type = typename std::remove_cvref_t<T>::value_type;
|
||||||
|
|
||||||
|
static_assert(
|
||||||
|
deserializable<value_type, ValT>,
|
||||||
|
"The specified type inside the unique_ptr must itself be deserializable");
|
||||||
|
static_assert(
|
||||||
|
std::is_default_constructible_v<value_type>,
|
||||||
|
"The specified type inside the unique_ptr must default constructible.");
|
||||||
|
|
||||||
|
if (!out) {
|
||||||
|
out.emplace();
|
||||||
|
}
|
||||||
|
SIMDJSON_TRY(val.template get<value_type>(out.value()));
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace simdjson
|
} // namespace simdjson
|
||||||
|
|
||||||
#endif // SIMDJSON_ONDEMAND_DESERIALIZE_H
|
#endif // SIMDJSON_ONDEMAND_DESERIALIZE_H
|
||||||
|
|||||||
+117
-2
@@ -1,4 +1,4 @@
|
|||||||
/* auto-generated on 2024-07-15 08:51:57 -0400. Do not edit! */
|
/* auto-generated on 2024-09-30 10:57:33 -0400. Do not edit! */
|
||||||
/* including simdjson.cpp: */
|
/* including simdjson.cpp: */
|
||||||
/* begin file simdjson.cpp */
|
/* begin file simdjson.cpp */
|
||||||
#define SIMDJSON_SRC_SIMDJSON_CPP
|
#define SIMDJSON_SRC_SIMDJSON_CPP
|
||||||
@@ -2670,6 +2670,122 @@ inline const std::string error_message(int error) noexcept;
|
|||||||
#endif // SIMDJSON_ERROR_H
|
#endif // SIMDJSON_ERROR_H
|
||||||
/* end file simdjson/error.h */
|
/* end file simdjson/error.h */
|
||||||
/* skipped duplicate #include "simdjson/portability.h" */
|
/* skipped duplicate #include "simdjson/portability.h" */
|
||||||
|
/* including simdjson/concepts.h: #include "simdjson/concepts.h" */
|
||||||
|
/* begin file simdjson/concepts.h */
|
||||||
|
#ifndef SIMDJSON_CONCEPTS_H
|
||||||
|
#define SIMDJSON_CONCEPTS_H
|
||||||
|
#if SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
|
|
||||||
|
#include <concepts>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace simdjson {
|
||||||
|
namespace concepts {
|
||||||
|
|
||||||
|
namespace details {
|
||||||
|
#define SIMDJSON_IMPL_CONCEPT(name, method) \
|
||||||
|
template <typename T> \
|
||||||
|
concept supports_##name = !std::is_const_v<T> && requires { \
|
||||||
|
typename std::remove_cvref_t<T>::value_type; \
|
||||||
|
requires requires(typename std::remove_cvref_t<T>::value_type &&val, \
|
||||||
|
T obj) { \
|
||||||
|
obj.method(std::move(val)); \
|
||||||
|
requires !requires { obj = std::move(val); }; \
|
||||||
|
}; \
|
||||||
|
};
|
||||||
|
|
||||||
|
SIMDJSON_IMPL_CONCEPT(emplace_back, emplace_back);
|
||||||
|
SIMDJSON_IMPL_CONCEPT(emplace, emplace);
|
||||||
|
SIMDJSON_IMPL_CONCEPT(push_back, push_back);
|
||||||
|
SIMDJSON_IMPL_CONCEPT(add, add);
|
||||||
|
SIMDJSON_IMPL_CONCEPT(push, push);
|
||||||
|
SIMDJSON_IMPL_CONCEPT(append, append);
|
||||||
|
SIMDJSON_IMPL_CONCEPT(insert, insert);
|
||||||
|
SIMDJSON_IMPL_CONCEPT(op_append, operator+=);
|
||||||
|
|
||||||
|
#undef SIMDJSON_IMPL_CONCEPT
|
||||||
|
} // namespace details
|
||||||
|
|
||||||
|
/// Check if T is a container that we can append to, including:
|
||||||
|
/// std::vector, std::deque, std::list, std::string, ...
|
||||||
|
template <typename T>
|
||||||
|
concept appendable_containers =
|
||||||
|
details::supports_emplace_back<T> || details::supports_emplace<T> ||
|
||||||
|
details::supports_push_back<T> || details::supports_push<T> ||
|
||||||
|
details::supports_add<T> || details::supports_append<T> ||
|
||||||
|
details::supports_insert<T>;
|
||||||
|
|
||||||
|
/// Insert into the container however possible
|
||||||
|
template <appendable_containers T, typename... Args>
|
||||||
|
constexpr decltype(auto) emplace_one(T &vec, Args &&...args) {
|
||||||
|
if constexpr (details::supports_emplace_back<T>) {
|
||||||
|
return vec.emplace_back(std::forward<Args>(args)...);
|
||||||
|
} else if constexpr (details::supports_emplace<T>) {
|
||||||
|
return vec.emplace(std::forward<Args>(args)...);
|
||||||
|
} else if constexpr (details::supports_push_back<T>) {
|
||||||
|
return vec.push_back(std::forward<Args>(args)...);
|
||||||
|
} else if constexpr (details::supports_push<T>) {
|
||||||
|
return vec.push(std::forward<Args>(args)...);
|
||||||
|
} else if constexpr (details::supports_add<T>) {
|
||||||
|
return vec.add(std::forward<Args>(args)...);
|
||||||
|
} else if constexpr (details::supports_append<T>) {
|
||||||
|
return vec.append(std::forward<Args>(args)...);
|
||||||
|
} else if constexpr (details::supports_insert<T>) {
|
||||||
|
return vec.insert(std::forward<Args>(args)...);
|
||||||
|
} else if constexpr (details::supports_op_append<T> && sizeof...(Args) == 1) {
|
||||||
|
return vec.operator+=(std::forward<Args>(args)...);
|
||||||
|
} else {
|
||||||
|
static_assert(!sizeof(T *),
|
||||||
|
"We don't know how to add things to this container");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This checks if the container will return a reference to the newly added
|
||||||
|
/// element after an insert which for example `std::vector::emplace_back` does
|
||||||
|
/// since C++17; this will allow some optimizations.
|
||||||
|
template <typename T>
|
||||||
|
concept returns_reference = appendable_containers<T> && requires {
|
||||||
|
typename std::remove_cvref_t<T>::reference;
|
||||||
|
requires requires(typename std::remove_cvref_t<T>::value_type &&val, T obj) {
|
||||||
|
{
|
||||||
|
emplace_one(obj, std::move(val))
|
||||||
|
} -> std::same_as<typename std::remove_cvref_t<T>::reference>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
concept smart_pointer = requires(std::remove_cvref_t<T> ptr) {
|
||||||
|
// Check if T has a member type named element_type
|
||||||
|
typename std::remove_cvref_t<T>::element_type;
|
||||||
|
|
||||||
|
// Check if T has a get() member function
|
||||||
|
{
|
||||||
|
ptr.get()
|
||||||
|
} -> std::same_as<typename std::remove_cvref_t<T>::element_type *>;
|
||||||
|
|
||||||
|
// Check if T can be dereferenced
|
||||||
|
{ *ptr } -> std::same_as<typename std::remove_cvref_t<T>::element_type &>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
concept optional_type = requires(std::remove_cvref_t<T> obj) {
|
||||||
|
typename std::remove_cvref_t<T>::value_type;
|
||||||
|
{ obj.value() } -> std::same_as<typename std::remove_cvref_t<T>::value_type&>;
|
||||||
|
requires requires(typename std::remove_cvref_t<T>::value_type &&val) {
|
||||||
|
obj.emplace(std::move(val));
|
||||||
|
obj = std::move(val);
|
||||||
|
{
|
||||||
|
obj.value_or(val)
|
||||||
|
} -> std::convertible_to<typename std::remove_cvref_t<T>::value_type>;
|
||||||
|
};
|
||||||
|
{ static_cast<bool>(obj) } -> std::same_as<bool>; // convertible to bool
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace concepts
|
||||||
|
} // namespace simdjson
|
||||||
|
#endif // SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
|
#endif // SIMDJSON_CONCEPTS_H
|
||||||
|
/* end file simdjson/concepts.h */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The top level simdjson namespace, containing everything the library provides.
|
* @brief The top level simdjson namespace, containing everything the library provides.
|
||||||
@@ -6455,7 +6571,6 @@ extern SIMDJSON_DLLIMPORTEXPORT const uint64_t thintable_epi8[256];
|
|||||||
|
|
||||||
#endif // SIMDJSON_INTERNAL_SIMDPRUNE_TABLES_H
|
#endif // SIMDJSON_INTERNAL_SIMDPRUNE_TABLES_H
|
||||||
/* end file simdjson/internal/simdprune_tables.h */
|
/* end file simdjson/internal/simdprune_tables.h */
|
||||||
|
|
||||||
#endif // SIMDJSON_GENERIC_DEPENDENCIES_H
|
#endif // SIMDJSON_GENERIC_DEPENDENCIES_H
|
||||||
/* end file simdjson/generic/dependencies.h */
|
/* end file simdjson/generic/dependencies.h */
|
||||||
/* including generic/dependencies.h: #include <generic/dependencies.h> */
|
/* including generic/dependencies.h: #include <generic/dependencies.h> */
|
||||||
|
|||||||
+1453
-795
File diff suppressed because it is too large
Load Diff
@@ -2,6 +2,8 @@
|
|||||||
link_libraries(simdjson)
|
link_libraries(simdjson)
|
||||||
include_directories(..)
|
include_directories(..)
|
||||||
add_subdirectory(compilation_failure_tests)
|
add_subdirectory(compilation_failure_tests)
|
||||||
|
add_cpp_test(ondemand_car_tag_invoke_deserialization LABELS ondemand acceptance per_implementation)
|
||||||
|
add_cpp_test(ondemand_car_deserialization LABELS ondemand acceptance per_implementation)
|
||||||
add_cpp_test(ondemand_log_tests LABELS ondemand acceptance per_implementation)
|
add_cpp_test(ondemand_log_tests LABELS ondemand acceptance per_implementation)
|
||||||
add_cpp_test(ondemand_log_error_tests LABELS ondemand acceptance per_implementation)
|
add_cpp_test(ondemand_log_error_tests LABELS ondemand acceptance per_implementation)
|
||||||
add_cpp_test(ondemand_tostring_tests LABELS ondemand acceptance per_implementation)
|
add_cpp_test(ondemand_tostring_tests LABELS ondemand acceptance per_implementation)
|
||||||
|
|||||||
@@ -0,0 +1,263 @@
|
|||||||
|
#include "simdjson.h"
|
||||||
|
#include "test_ondemand.h"
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using namespace simdjson;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A custom type that we want to parse.
|
||||||
|
*/
|
||||||
|
struct Car {
|
||||||
|
std::string make{};
|
||||||
|
std::string model{};
|
||||||
|
int64_t year{};
|
||||||
|
std::vector<double> tire_pressure{};
|
||||||
|
bool operator==(const Car &other) const {
|
||||||
|
return make == other.make && model == other.model && year == other.year &&
|
||||||
|
tire_pressure == other.tire_pressure;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::ostream &operator<<(std::ostream &os, const Car &c) {
|
||||||
|
os << c.make << " " << c.model << " " << c.year << " ";
|
||||||
|
for (auto p : c.tire_pressure) {
|
||||||
|
os << p << " ";
|
||||||
|
}
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
|
// This code is not necessary if you have a C++20 compliant system:
|
||||||
|
template <>
|
||||||
|
simdjson_inline simdjson_result<std::vector<double>>
|
||||||
|
simdjson::ondemand::value::get() noexcept {
|
||||||
|
ondemand::array array;
|
||||||
|
auto error = get_array().get(array);
|
||||||
|
if (error) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
std::vector<double> vec;
|
||||||
|
for (auto v : array) {
|
||||||
|
double val;
|
||||||
|
error = v.get_double().get(val);
|
||||||
|
if (error) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
vec.push_back(val);
|
||||||
|
}
|
||||||
|
return vec;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <>
|
||||||
|
simdjson_inline simdjson_result<Car> simdjson::ondemand::value::get() noexcept {
|
||||||
|
ondemand::object obj;
|
||||||
|
auto error = get_object().get(obj);
|
||||||
|
if (error) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
Car car;
|
||||||
|
if ((error = obj["make"].get_string(car.make))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
if ((error = obj["model"].get_string(car.model))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
if ((error = obj["year"].get_int64().get(car.year))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
if ((error = obj["tire_pressure"].get<std::vector<double>>().get(
|
||||||
|
car.tire_pressure))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
return car;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
simdjson_inline simdjson_result<Car>
|
||||||
|
simdjson::ondemand::document::get() &noexcept {
|
||||||
|
ondemand::object obj;
|
||||||
|
auto error = get_object().get(obj);
|
||||||
|
if (error) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
Car car;
|
||||||
|
if ((error = obj["make"].get_string(car.make))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
if ((error = obj["model"].get_string(car.model))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
if ((error = obj["year"].get_int64().get(car.year))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
if ((error = obj["tire_pressure"].get<std::vector<double>>().get(
|
||||||
|
car.tire_pressure))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
return car;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <>
|
||||||
|
simdjson_inline simdjson_result<Car>
|
||||||
|
simdjson::ondemand::document_reference::get() &noexcept {
|
||||||
|
ondemand::object obj;
|
||||||
|
auto error = get_object().get(obj);
|
||||||
|
if (error) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
Car car;
|
||||||
|
if ((error = obj["make"].get_string(car.make))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
if ((error = obj["model"].get_string(car.model))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
if ((error = obj["year"].get_int64().get(car.year))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
if ((error = obj["tire_pressure"].get<std::vector<double>>().get(
|
||||||
|
car.tire_pressure))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
return car;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main_should_compile(void) {
|
||||||
|
padded_string json =
|
||||||
|
R"( [ { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||||
|
"tire_pressure": [ 40.1, 39.9 ] },
|
||||||
|
{ "make": "Kia", "model": "Soul", "year": 2012,
|
||||||
|
"tire_pressure": [ 30.1, 31.0 ] },
|
||||||
|
{ "make": "Toyota", "model": "Tercel", "year": 1999,
|
||||||
|
"tire_pressure": [ 29.8, 30.0 ] }
|
||||||
|
])"_padded;
|
||||||
|
ondemand::parser parser;
|
||||||
|
ondemand::document doc;
|
||||||
|
[[maybe_unused]] auto doc_error = parser.iterate(json).get(doc);
|
||||||
|
for (auto val : doc) {
|
||||||
|
#if SIMDJSON_EXCEPTIONS
|
||||||
|
Car c(val); // an exception may be thrown
|
||||||
|
#else
|
||||||
|
Car c;
|
||||||
|
if (auto error = val.get<Car>().get(c)) {
|
||||||
|
std::cerr << error << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace car_error_tests {
|
||||||
|
bool car_deserialize() {
|
||||||
|
TEST_START();
|
||||||
|
padded_string json =
|
||||||
|
R"( [ { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||||
|
"tire_pressure": [ 40.1, 39.9 ] },
|
||||||
|
{ "make": "Kia", "model": "Soul", "year": 2012,
|
||||||
|
"tire_pressure": [ 30.1, 31.0 ] },
|
||||||
|
{ "make": "Toyota", "model": "Tercel", "year": 1999,
|
||||||
|
"tire_pressure": [ 29.8, 30.0 ] }
|
||||||
|
])"_padded;
|
||||||
|
ondemand::parser parser;
|
||||||
|
ondemand::document doc;
|
||||||
|
[[maybe_unused]] auto doc_error = parser.iterate(json).get(doc);
|
||||||
|
std::vector<Car> cars;
|
||||||
|
for (auto val : doc) {
|
||||||
|
#if SIMDJSON_EXCEPTIONS
|
||||||
|
Car c(val); // an exception may be thrown
|
||||||
|
#else
|
||||||
|
Car c;
|
||||||
|
if (auto error = val.get<Car>().get(c)) {
|
||||||
|
std::cerr << error << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
cars.push_back(c);
|
||||||
|
std::cout << c.make << std::endl;
|
||||||
|
}
|
||||||
|
std::vector<Car> expected = {{"Toyota", "Camry", 2018, {40.1, 39.9}},
|
||||||
|
{"Kia", "Soul", 2012, {30.1, 31.0}},
|
||||||
|
{"Toyota", "Tercel", 1999, {29.8, 30.0}}};
|
||||||
|
ASSERT_EQUAL(cars.size(), expected.size());
|
||||||
|
for (size_t i = 0; i < cars.size(); i++) {
|
||||||
|
ASSERT_EQUAL(cars[i], expected[i]);
|
||||||
|
}
|
||||||
|
TEST_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool car_doc_deserialize() {
|
||||||
|
TEST_START();
|
||||||
|
padded_string json = R"( { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||||
|
"tire_pressure": [ 40.1, 39.9 ] }
|
||||||
|
)"_padded;
|
||||||
|
ondemand::parser parser;
|
||||||
|
ondemand::document doc;
|
||||||
|
[[maybe_unused]] auto doc_error = parser.iterate(json).get(doc);
|
||||||
|
#if SIMDJSON_EXCEPTIONS
|
||||||
|
Car c(doc); // an exception may be thrown
|
||||||
|
#else
|
||||||
|
Car c;
|
||||||
|
if (auto error = doc.get<Car>().get(c)) {
|
||||||
|
std::cerr << error << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
Car expected = {"Toyota", "Camry", 2018, {40.1, 39.9}};
|
||||||
|
ASSERT_EQUAL(c, expected);
|
||||||
|
TEST_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool car_stream_deserialize() {
|
||||||
|
TEST_START();
|
||||||
|
padded_string json =
|
||||||
|
R"( { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||||
|
"tire_pressure": [ 40.1, 39.9 ] }
|
||||||
|
{ "make": "Kia", "model": "Soul", "year": 2012,
|
||||||
|
"tire_pressure": [ 30.1, 31.0 ] }
|
||||||
|
{ "make": "Toyota", "model": "Tercel", "year": 1999,
|
||||||
|
"tire_pressure": [ 29.8, 30.0 ] }
|
||||||
|
)"_padded;
|
||||||
|
ondemand::parser parser;
|
||||||
|
ondemand::document_stream stream;
|
||||||
|
auto error = parser.iterate_many(json).get(stream);
|
||||||
|
if(error) {
|
||||||
|
std::cerr << error << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
std::vector<Car> cars;
|
||||||
|
|
||||||
|
#if SIMDJSON_EXCEPTIONS
|
||||||
|
for(ondemand::document_reference doc : stream) {
|
||||||
|
//Car c(doc); // an exception may be thrown
|
||||||
|
cars.push_back(Car(doc)); // an exception may be thrown
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
for(auto doc : stream) {
|
||||||
|
Car c;
|
||||||
|
if ((error = doc.get<Car>().get(c))) {
|
||||||
|
std::cerr << error << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
cars.push_back(c);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
std::vector<Car> expected = {{"Toyota", "Camry", 2018, {40.1, 39.9}},
|
||||||
|
{"Kia", "Soul", 2012, {30.1, 31.0}},
|
||||||
|
{"Toyota", "Tercel", 1999, {29.8, 30.0}}};
|
||||||
|
ASSERT_EQUAL(cars.size(), expected.size());
|
||||||
|
for (size_t i = 0; i < cars.size(); i++) {
|
||||||
|
ASSERT_EQUAL(cars[i], expected[i]);
|
||||||
|
}
|
||||||
|
TEST_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool run() { return car_stream_deserialize() && car_doc_deserialize() && car_deserialize(); }
|
||||||
|
} // namespace car_error_tests
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
return test_main(argc, argv, car_error_tests::run);
|
||||||
|
}
|
||||||
@@ -0,0 +1,335 @@
|
|||||||
|
#include "simdjson.h"
|
||||||
|
#include "test_ondemand.h"
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
#include <list>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
using namespace simdjson;
|
||||||
|
|
||||||
|
#if !SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
printf("This test is only relevant when SIMDJSON_SUPPORTS_DESERIALIZATION is true (C++20)\n");
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A custom type that we want to parse.
|
||||||
|
*/
|
||||||
|
struct Car {
|
||||||
|
std::string make{};
|
||||||
|
std::string model{};
|
||||||
|
int year{};
|
||||||
|
std::vector<float> tire_pressure{};
|
||||||
|
bool operator==(const Car &other) const {
|
||||||
|
return make == other.make && model == other.model && year == other.year &&
|
||||||
|
tire_pressure == other.tire_pressure;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::ostream &operator<<(std::ostream &os, const Car &c) {
|
||||||
|
os << c.make << " " << c.model << " " << c.year << " ";
|
||||||
|
for (auto p : c.tire_pressure) {
|
||||||
|
os << p << " ";
|
||||||
|
}
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace simdjson {
|
||||||
|
// This tag_invoke MUST be inside simdjson namespace
|
||||||
|
template <typename simdjson_value>
|
||||||
|
auto tag_invoke(deserialize_tag, simdjson_value &val, Car& car) {
|
||||||
|
ondemand::object obj;
|
||||||
|
auto error = val.get_object().get(obj);
|
||||||
|
if (error) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
if ((error = obj["make"].get_string(car.make))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
if ((error = obj["model"].get_string(car.model))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
if ((error = obj["year"].get(car.year))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
if ((error = obj["tire_pressure"].get<std::vector<float>>().get(
|
||||||
|
car.tire_pressure))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
return simdjson::SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// suppose we want to filter out all Toyotas
|
||||||
|
template <typename simdjson_value>
|
||||||
|
auto tag_invoke(deserialize_tag, simdjson_value &val, std::list<Car>& car) {
|
||||||
|
ondemand::array arr;
|
||||||
|
auto error = val.get_array().get(arr);
|
||||||
|
if (error) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
for (auto v : arr) {
|
||||||
|
Car c;
|
||||||
|
if ((error = v.get<Car>().get(c))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
if(c.make != "Toyota") {
|
||||||
|
car.push_back(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return simdjson::SUCCESS;
|
||||||
|
}
|
||||||
|
} // namespace simdjson
|
||||||
|
|
||||||
|
|
||||||
|
int main_should_compile(void) {
|
||||||
|
padded_string json =
|
||||||
|
R"( [ { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||||
|
"tire_pressure": [ 40.1, 39.9 ] },
|
||||||
|
{ "make": "Kia", "model": "Soul", "year": 2012,
|
||||||
|
"tire_pressure": [ 30.1, 31.0 ] },
|
||||||
|
{ "make": "Toyota", "model": "Tercel", "year": 1999,
|
||||||
|
"tire_pressure": [ 29.8, 30.0 ] }
|
||||||
|
])"_padded;
|
||||||
|
ondemand::parser parser;
|
||||||
|
ondemand::document doc;
|
||||||
|
[[maybe_unused]] auto doc_error = parser.iterate(json).get(doc);
|
||||||
|
for (auto val : doc) {
|
||||||
|
#if SIMDJSON_EXCEPTIONS
|
||||||
|
Car c(val); // an exception may be thrown
|
||||||
|
#else
|
||||||
|
Car c;
|
||||||
|
if (auto error = val.get<Car>().get(c)) {
|
||||||
|
std::cerr << error << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace car_error_tests {
|
||||||
|
bool car_deserialize() {
|
||||||
|
TEST_START();
|
||||||
|
padded_string json =
|
||||||
|
R"( [ { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||||
|
"tire_pressure": [ 40.1, 39.9 ] },
|
||||||
|
{ "make": "Kia", "model": "Soul", "year": 2012,
|
||||||
|
"tire_pressure": [ 30.1, 31.0 ] },
|
||||||
|
{ "make": "Toyota", "model": "Tercel", "year": 1999,
|
||||||
|
"tire_pressure": [ 29.8, 30.0 ] }
|
||||||
|
])"_padded;
|
||||||
|
ondemand::parser parser;
|
||||||
|
ondemand::document doc;
|
||||||
|
[[maybe_unused]] auto doc_error = parser.iterate(json).get(doc);
|
||||||
|
std::vector<Car> cars;
|
||||||
|
for (auto val : doc) {
|
||||||
|
#if SIMDJSON_EXCEPTIONS
|
||||||
|
Car c(val); // an exception may be thrown
|
||||||
|
#else
|
||||||
|
Car c;
|
||||||
|
if (auto error = val.get<Car>().get(c)) {
|
||||||
|
std::cerr << error << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
cars.push_back(c);
|
||||||
|
std::cout << c.make << std::endl;
|
||||||
|
}
|
||||||
|
std::vector<Car> expected = {{"Toyota", "Camry", 2018, {40.1f, 39.9f}},
|
||||||
|
{"Kia", "Soul", 2012, {30.1f, 31.0f}},
|
||||||
|
{"Toyota", "Tercel", 1999, {29.8f, 30.0f}}};
|
||||||
|
ASSERT_EQUAL(cars.size(), expected.size());
|
||||||
|
for (size_t i = 0; i < cars.size(); i++) {
|
||||||
|
ASSERT_EQUAL(cars[i], expected[i]);
|
||||||
|
}
|
||||||
|
TEST_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool vector_car_deserialize() {
|
||||||
|
TEST_START();
|
||||||
|
padded_string json =
|
||||||
|
R"( [ { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||||
|
"tire_pressure": [ 40.1, 39.9 ] },
|
||||||
|
{ "make": "Kia", "model": "Soul", "year": 2012,
|
||||||
|
"tire_pressure": [ 30.1, 31.0 ] },
|
||||||
|
{ "make": "Toyota", "model": "Tercel", "year": 1999,
|
||||||
|
"tire_pressure": [ 29.8, 30.0 ] }
|
||||||
|
])"_padded;
|
||||||
|
ondemand::parser parser;
|
||||||
|
ondemand::document doc;
|
||||||
|
[[maybe_unused]] auto doc_error = parser.iterate(json).get(doc);
|
||||||
|
#if SIMDJSON_EXCEPTIONS
|
||||||
|
#if SIMDJSON_REGULAR_VISUAL_STUDIO
|
||||||
|
std::vector<Car> cars = doc.get<std::vector<Car>>(); // an exception may be thrown
|
||||||
|
#else
|
||||||
|
std::vector<Car> cars(doc); // an exception may be thrown
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
std::vector<Car> cars;
|
||||||
|
if (auto error = doc.get<std::vector<Car>>().get(cars)) {
|
||||||
|
std::cerr << error << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
std::vector<Car> expected = {{"Toyota", "Camry", 2018, {40.1f, 39.9f}},
|
||||||
|
{"Kia", "Soul", 2012, {30.1f, 31.0f}},
|
||||||
|
{"Toyota", "Tercel", 1999, {29.8f, 30.0f}}};
|
||||||
|
ASSERT_EQUAL(cars.size(), expected.size());
|
||||||
|
for (size_t i = 0; i < cars.size(); i++) {
|
||||||
|
ASSERT_EQUAL(cars[i], expected[i]);
|
||||||
|
}
|
||||||
|
TEST_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool list_car_deserialize() {
|
||||||
|
TEST_START();
|
||||||
|
padded_string json =
|
||||||
|
R"( [ { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||||
|
"tire_pressure": [ 40.1, 39.9 ] },
|
||||||
|
{ "make": "Kia", "model": "Soul", "year": 2012,
|
||||||
|
"tire_pressure": [ 30.1, 31.0 ] },
|
||||||
|
{ "make": "Toyota", "model": "Tercel", "year": 1999,
|
||||||
|
"tire_pressure": [ 29.8, 30.0 ] }
|
||||||
|
])"_padded;
|
||||||
|
ondemand::parser parser;
|
||||||
|
ondemand::document doc;
|
||||||
|
[[maybe_unused]] auto doc_error = parser.iterate(json).get(doc);
|
||||||
|
#if SIMDJSON_EXCEPTIONS
|
||||||
|
#if SIMDJSON_REGULAR_VISUAL_STUDIO
|
||||||
|
std::list<Car> cars = doc.get<std::list<Car>>(); // an exception may be thrown
|
||||||
|
#else
|
||||||
|
std::list<Car> cars(doc); // an exception may be thrown
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
std::list<Car> cars;
|
||||||
|
if (auto error = doc.get<std::list<Car>>().get(cars)) {
|
||||||
|
std::cerr << error << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
std::list<Car> expected = {{"Kia", "Soul", 2012, {30.1f, 31.0f}}};
|
||||||
|
ASSERT_EQUAL(cars.size(), expected.size());
|
||||||
|
ASSERT_EQUAL(cars.front(), expected.front());
|
||||||
|
TEST_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool optional_car_deserialize() {
|
||||||
|
TEST_START();
|
||||||
|
padded_string json =
|
||||||
|
R"( { "car1": { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||||
|
"tire_pressure": [ 40.1, 39.9 ] }
|
||||||
|
})"_padded;
|
||||||
|
ondemand::parser parser;
|
||||||
|
ondemand::document doc;
|
||||||
|
[[maybe_unused]] auto error = parser.iterate(json).get(doc);
|
||||||
|
std::optional<Car> car;
|
||||||
|
error = doc["key not found"].get<std::optional<Car>>().get(car);
|
||||||
|
ASSERT_TRUE(!car);
|
||||||
|
error = doc["car1"].get<std::optional<Car>>().get(car);
|
||||||
|
std::optional<Car> expected = Car{"Toyota", "Camry", 2018, {40.1f, 39.9f}};
|
||||||
|
ASSERT_TRUE(car == expected);
|
||||||
|
TEST_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool car_doc_deserialize() {
|
||||||
|
TEST_START();
|
||||||
|
padded_string json = R"( { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||||
|
"tire_pressure": [ 40.1, 39.9 ] }
|
||||||
|
)"_padded;
|
||||||
|
ondemand::parser parser;
|
||||||
|
ondemand::document doc;
|
||||||
|
[[maybe_unused]] auto doc_error = parser.iterate(json).get(doc);
|
||||||
|
#if SIMDJSON_EXCEPTIONS
|
||||||
|
Car c(doc); // an exception may be thrown
|
||||||
|
#else
|
||||||
|
Car c;
|
||||||
|
if (auto error = doc.get<Car>().get(c)) {
|
||||||
|
std::cerr << error << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
Car expected = {"Toyota", "Camry", 2018, {40.1f, 39.9f}};
|
||||||
|
ASSERT_EQUAL(c, expected);
|
||||||
|
TEST_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool car_stream_deserialize() {
|
||||||
|
TEST_START();
|
||||||
|
padded_string json =
|
||||||
|
R"( { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||||
|
"tire_pressure": [ 40.1, 39.9 ] }
|
||||||
|
{ "make": "Kia", "model": "Soul", "year": 2012,
|
||||||
|
"tire_pressure": [ 30.1, 31.0 ] }
|
||||||
|
{ "make": "Toyota", "model": "Tercel", "year": 1999,
|
||||||
|
"tire_pressure": [ 29.8, 30.0 ] }
|
||||||
|
)"_padded;
|
||||||
|
ondemand::parser parser;
|
||||||
|
ondemand::document_stream stream;
|
||||||
|
[[maybe_unused]] auto error = parser.iterate_many(json).get(stream);
|
||||||
|
std::vector<Car> cars;
|
||||||
|
|
||||||
|
#if SIMDJSON_EXCEPTIONS
|
||||||
|
for(auto doc : stream) {
|
||||||
|
cars.push_back((Car)doc); // an exception may be thrown
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
for(auto doc : stream) {
|
||||||
|
Car c;
|
||||||
|
if ((error = doc.get<Car>().get(c))) {
|
||||||
|
std::cerr << error << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
cars.push_back(c);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
std::vector<Car> expected = {{"Toyota", "Camry", 2018, {40.1f, 39.9f}},
|
||||||
|
{"Kia", "Soul", 2012, {30.1f, 31.0f}},
|
||||||
|
{"Toyota", "Tercel", 1999, {29.8f, 30.0f}}};
|
||||||
|
ASSERT_EQUAL(cars.size(), expected.size());
|
||||||
|
for (size_t i = 0; i < cars.size(); i++) {
|
||||||
|
ASSERT_EQUAL(cars[i], expected[i]);
|
||||||
|
}
|
||||||
|
TEST_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool car_unique_ptr_deserialize() {
|
||||||
|
TEST_START();
|
||||||
|
auto const json = R"( { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||||
|
"tire_pressure": [ 40.1, 39.9 ] })"_padded;
|
||||||
|
simdjson::ondemand::parser parser;
|
||||||
|
simdjson::ondemand::document doc;
|
||||||
|
[[maybe_unused]] auto error = parser.iterate(json).get(doc);
|
||||||
|
#if SIMDJSON_EXCEPTIONS
|
||||||
|
std::unique_ptr<Car> c(doc);
|
||||||
|
#else
|
||||||
|
std::unique_ptr<Car> c;
|
||||||
|
if ((error = doc.get<std::unique_ptr<Car>>().get(c))) {
|
||||||
|
std::cerr << error << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
ASSERT_EQUAL(c->make, "Toyota");
|
||||||
|
TEST_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool run() { return list_car_deserialize()
|
||||||
|
&& optional_car_deserialize()
|
||||||
|
&& car_unique_ptr_deserialize()
|
||||||
|
&& vector_car_deserialize()
|
||||||
|
&& car_stream_deserialize()
|
||||||
|
&& car_doc_deserialize()
|
||||||
|
&& car_deserialize(); }
|
||||||
|
} // namespace car_error_tests
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
return test_main(argc, argv, car_error_tests::run);
|
||||||
|
}
|
||||||
|
#endif // SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
@@ -4,20 +4,6 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#if SIMDJSON_SUPPORTS_DESERIALIZATION
|
|
||||||
|
|
||||||
namespace simdjson {
|
|
||||||
|
|
||||||
// unique_ptr<T>
|
|
||||||
template <typename T>
|
|
||||||
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<T>& out) {
|
|
||||||
out = std::make_unique<T>(val.template get<T>());
|
|
||||||
return SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace simdjson
|
|
||||||
|
|
||||||
#endif // SIMDJSON_SUPPORTS_DESERIALIZATION
|
|
||||||
|
|
||||||
namespace doc_custom_types_tests {
|
namespace doc_custom_types_tests {
|
||||||
#if SIMDJSON_EXCEPTIONS && defined(__cpp_concepts)
|
#if SIMDJSON_EXCEPTIONS && defined(__cpp_concepts)
|
||||||
|
|||||||
@@ -4,40 +4,6 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#if SIMDJSON_SUPPORTS_DESERIALIZATION
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct is_unique_ptr : std::false_type {
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct is_unique_ptr<std::unique_ptr<T>> : std::true_type {
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
concept is_unique_ptr_v = is_unique_ptr<T>::value;
|
|
||||||
|
|
||||||
|
|
||||||
namespace simdjson {
|
|
||||||
|
|
||||||
// this is to demonstrate that we can use concept; otherwise a simple
|
|
||||||
// type_identity<unique_ptr<T>> as the second argument would have done the job.
|
|
||||||
//
|
|
||||||
// This tag_invoke MUST be inside simdjson namespace
|
|
||||||
template <typename T>
|
|
||||||
requires is_unique_ptr_v<T>
|
|
||||||
auto tag_invoke(deserialize_tag, auto &val, T& out) {
|
|
||||||
using type = typename T::element_type;
|
|
||||||
out = std::make_unique<type>(val.template get<type>());
|
|
||||||
return SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace simdjson
|
|
||||||
|
|
||||||
|
|
||||||
#endif // SIMDJSON_SUPPORTS_DESERIALIZATION
|
|
||||||
|
|
||||||
|
|
||||||
namespace custom_types_tests {
|
namespace custom_types_tests {
|
||||||
#if SIMDJSON_EXCEPTIONS && SIMDJSON_SUPPORTS_DESERIALIZATION
|
#if SIMDJSON_EXCEPTIONS && SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
|
|||||||
@@ -76,29 +76,13 @@ simdjson_inline simdjson_result<Car> simdjson::ondemand::value::get() noexcept {
|
|||||||
auto error = get_object().get(obj);
|
auto error = get_object().get(obj);
|
||||||
if (error) { return error; }
|
if (error) { return error; }
|
||||||
Car car;
|
Car car;
|
||||||
// Instead of repeatedly obj["something"], we iterate through the object which
|
if((error = obj["make"].get_string(car.make))) { return error; }
|
||||||
// we expect to be faster.
|
if((error = obj["model"].get_string(car.model))) { return error; }
|
||||||
for (auto field : obj) {
|
if((error = obj["year"].get_int64().get(car.year))) { return error; }
|
||||||
raw_json_string key;
|
if((error = obj["tire_pressure"].get<std::vector<double>>().get(car.tire_pressure))) { return error; }
|
||||||
if (error = field.key().get(key); error) { return error; }
|
|
||||||
if (key == "make") {
|
|
||||||
error = field.value().get_string(car.make);
|
|
||||||
if(error) { return error; }
|
|
||||||
} else if (key == "model") {
|
|
||||||
error = field.value().get_string(car.model);
|
|
||||||
if(error) { return error; }
|
|
||||||
} else if (key == "year") {
|
|
||||||
error = field.value().get_int64().get(car.year);
|
|
||||||
if(error) { return error; }
|
|
||||||
} else if (key == "tire_pressure") {
|
|
||||||
error = field.value().get<std::vector<double>>().get(car.tire_pressure);
|
|
||||||
if (error) { return error; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return car;
|
return car;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int custom_type_without_exceptions() {
|
int custom_type_without_exceptions() {
|
||||||
padded_string json = R"( [ { "make": "Toyota", "model": "Camry", "year": 2018,
|
padded_string json = R"( [ { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||||
"tire_pressure": [ 40.1, 39.9 ] },
|
"tire_pressure": [ 40.1, 39.9 ] },
|
||||||
|
|||||||
@@ -3,11 +3,68 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <list>
|
||||||
|
#include <set>
|
||||||
|
#include <stack>
|
||||||
|
#include <queue>
|
||||||
|
#include <deque>
|
||||||
|
#include <unordered_set>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
#if SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
|
class Array : public std::vector<float> {};
|
||||||
|
static_assert(simdjson::concepts::appendable_containers<Array>, "Array must be appendable_containers");
|
||||||
|
static_assert(!simdjson::require_custom_serialization<Array>);
|
||||||
|
namespace simdjson {
|
||||||
|
// This tag_invoke MUST be inside simdjson namespace
|
||||||
|
template <typename simdjson_value>
|
||||||
|
auto tag_invoke(deserialize_tag, simdjson_value &val, Array& out) {
|
||||||
|
simdjson::ondemand::array arr;
|
||||||
|
SIMDJSON_TRY(val.get_array().get(arr));
|
||||||
|
for (auto v : arr) {
|
||||||
|
float temp;
|
||||||
|
if (auto const err = v.get<float>().get(temp); err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
out.push_back(temp);
|
||||||
|
out.push_back(temp); // adding it twice
|
||||||
|
}
|
||||||
|
return error_code::SUCCESS;
|
||||||
|
}
|
||||||
|
} // namespace simdjson
|
||||||
|
|
||||||
|
// require_custom_serialization
|
||||||
|
#endif // SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
|
|
||||||
namespace stl_types {
|
namespace stl_types {
|
||||||
#if SIMDJSON_EXCEPTIONS && SIMDJSON_SUPPORTS_DESERIALIZATION
|
#if SIMDJSON_EXCEPTIONS && SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
|
|
||||||
bool basic_general_madness() {
|
bool basic_general_madness() {
|
||||||
|
TEST_START();
|
||||||
|
simdjson::padded_string json =
|
||||||
|
R"( [ { "codes": [1,2,3,4,18,19] },
|
||||||
|
{ "codes": [1,3,4,111,1111] } ])"_padded;
|
||||||
|
|
||||||
|
simdjson::ondemand::parser parser;
|
||||||
|
simdjson::ondemand::document doc = parser.iterate(json);
|
||||||
|
std::vector<uint16_t> codes;
|
||||||
|
for (auto val : doc) {
|
||||||
|
simdjson::ondemand::object obj;
|
||||||
|
SIMDJSON_TRY(val.get_object().get(obj));
|
||||||
|
obj["codes"].get(codes); // append to it
|
||||||
|
}
|
||||||
|
|
||||||
|
if (codes.size() != 11) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (codes[5] != 19 || codes[7] != 3 || codes[9] != 111) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool basic_general_madness_vector() {
|
||||||
TEST_START();
|
TEST_START();
|
||||||
simdjson::padded_string json =
|
simdjson::padded_string json =
|
||||||
R"( [ { "codes": [1.2, 3.4, 5.6, 7.8, 9.0] },
|
R"( [ { "codes": [1.2, 3.4, 5.6, 7.8, 9.0] },
|
||||||
@@ -15,7 +72,7 @@ bool basic_general_madness() {
|
|||||||
|
|
||||||
simdjson::ondemand::parser parser;
|
simdjson::ondemand::parser parser;
|
||||||
simdjson::ondemand::document doc = parser.iterate(json);
|
simdjson::ondemand::document doc = parser.iterate(json);
|
||||||
std::vector<std::unique_ptr<float>> codes;
|
std::vector<float> codes;
|
||||||
for (auto val : doc) {
|
for (auto val : doc) {
|
||||||
simdjson::ondemand::object obj;
|
simdjson::ondemand::object obj;
|
||||||
SIMDJSON_TRY(val.get_object().get(obj));
|
SIMDJSON_TRY(val.get_object().get(obj));
|
||||||
@@ -25,18 +82,170 @@ bool basic_general_madness() {
|
|||||||
if (codes.size() != 10) {
|
if (codes.size() != 10) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (*codes[5] != 2.2f || *codes[7] != 6.6f || *codes[9] != 10.0f) {
|
if (codes[5] != 2.2f || codes[7] != 6.6f || codes[9] != 10.0f) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_SUCCEED();
|
TEST_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool basic_general_madness_list() {
|
||||||
|
TEST_START();
|
||||||
|
simdjson::padded_string json =
|
||||||
|
R"( [ { "codes": [1.2, 3.4, 5.6, 7.8, 9.0] },
|
||||||
|
{ "codes": [2.2, 4.4, 6.6, 8.8, 10.0] } ])"_padded;
|
||||||
|
|
||||||
|
simdjson::ondemand::parser parser;
|
||||||
|
simdjson::ondemand::document doc = parser.iterate(json);
|
||||||
|
std::list<float> codes;
|
||||||
|
for (auto val : doc) {
|
||||||
|
simdjson::ondemand::object obj;
|
||||||
|
SIMDJSON_TRY(val.get_object().get(obj));
|
||||||
|
obj["codes"].get(codes); // append to it
|
||||||
|
}
|
||||||
|
|
||||||
|
if (codes.size() != 10) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (codes.back() != 10.0f || codes.front() != 1.2f) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool basic_general_madness_Array() {
|
||||||
|
TEST_START();
|
||||||
|
simdjson::padded_string json =
|
||||||
|
R"( [ { "codes": [1.2, 3.4, 5.6, 7.8, 9.0] },
|
||||||
|
{ "codes": [2.2, 4.4, 6.6, 8.8, 10.0] } ])"_padded;
|
||||||
|
|
||||||
|
simdjson::ondemand::parser parser;
|
||||||
|
simdjson::ondemand::document doc = parser.iterate(json);
|
||||||
|
Array codes;
|
||||||
|
for (auto val : doc) {
|
||||||
|
simdjson::ondemand::object obj;
|
||||||
|
SIMDJSON_TRY(val.get_object().get(obj));
|
||||||
|
obj["codes"].get(codes); // append to it
|
||||||
|
}
|
||||||
|
ASSERT_EQUAL(codes.size(), 20);
|
||||||
|
TEST_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <template <typename...> typename Container, typename ValT = float>
|
||||||
|
bool basic_general_madness_container() {
|
||||||
|
TYPED_TEST_START(Container<ValT>);
|
||||||
|
|
||||||
|
simdjson::padded_string json =
|
||||||
|
R"( [ { "codes": [1.0, 3.4, 5.6, 7.8, 9.0] },
|
||||||
|
{ "codes": [2.2, 4.4, 6.6, 8.8, 10.0] } ])"_padded;
|
||||||
|
|
||||||
|
simdjson::ondemand::parser parser;
|
||||||
|
simdjson::ondemand::document doc = parser.iterate(json);
|
||||||
|
Container<ValT> codes;
|
||||||
|
for (auto val : doc) {
|
||||||
|
simdjson::ondemand::object obj;
|
||||||
|
SIMDJSON_TRY(val.get_object().get(obj));
|
||||||
|
obj["codes"].get(codes); // append to it
|
||||||
|
}
|
||||||
|
|
||||||
|
if (codes.size() != 10) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if constexpr (requires {codes.front();}) {
|
||||||
|
if constexpr (requires {*codes.front();}) {
|
||||||
|
if (*codes.front() != 1.0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (codes.front() != 1.0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if constexpr (requires {codes.back();} ) {
|
||||||
|
if constexpr (requires {*codes.back();}) {
|
||||||
|
if (*codes.back() != 10.0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (codes.back() != 10.0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#endif // SIMDJSON_EXCEPTIONS
|
#endif // SIMDJSON_EXCEPTIONS
|
||||||
bool run() {
|
bool run() {
|
||||||
return
|
return
|
||||||
#if SIMDJSON_EXCEPTIONS && SIMDJSON_SUPPORTS_DESERIALIZATION
|
#if SIMDJSON_EXCEPTIONS && SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||||
basic_general_madness() &&
|
basic_general_madness() &&
|
||||||
|
basic_general_madness_vector() &&
|
||||||
|
basic_general_madness_list() &&
|
||||||
|
basic_general_madness_Array() &&
|
||||||
|
basic_general_madness_container<std::vector>() &&
|
||||||
|
basic_general_madness_container<std::list>() &&
|
||||||
|
basic_general_madness_container<std::set>() &&
|
||||||
|
basic_general_madness_container<std::stack>() &&
|
||||||
|
basic_general_madness_container<std::queue>() &&
|
||||||
|
basic_general_madness_container<std::deque>() &&
|
||||||
|
basic_general_madness_container<std::priority_queue>() &&
|
||||||
|
basic_general_madness_container<std::unordered_set>() &&
|
||||||
|
basic_general_madness_container<std::multiset>() &&
|
||||||
|
basic_general_madness_container<std::unordered_multiset>() &&
|
||||||
|
// double:
|
||||||
|
basic_general_madness_container<std::vector, double>() &&
|
||||||
|
basic_general_madness_container<std::list, double>() &&
|
||||||
|
basic_general_madness_container<std::set, double>() &&
|
||||||
|
basic_general_madness_container<std::stack, double>() &&
|
||||||
|
basic_general_madness_container<std::queue, double>() &&
|
||||||
|
basic_general_madness_container<std::deque, double>() &&
|
||||||
|
basic_general_madness_container<std::priority_queue, double>() &&
|
||||||
|
basic_general_madness_container<std::unordered_set, double>() &&
|
||||||
|
basic_general_madness_container<std::multiset, double>() &&
|
||||||
|
basic_general_madness_container<std::unordered_multiset, double>() &&
|
||||||
|
// unique_ptr<double>:
|
||||||
|
basic_general_madness_container<std::vector, std::unique_ptr<double>>() &&
|
||||||
|
basic_general_madness_container<std::list, std::unique_ptr<double>>() &&
|
||||||
|
basic_general_madness_container<std::set, std::unique_ptr<double>>() &&
|
||||||
|
basic_general_madness_container<std::stack, std::unique_ptr<double>>() &&
|
||||||
|
basic_general_madness_container<std::deque, std::unique_ptr<double>>() &&
|
||||||
|
basic_general_madness_container<std::queue, std::unique_ptr<double>>() &&
|
||||||
|
basic_general_madness_container<std::priority_queue, std::unique_ptr<double>>() &&
|
||||||
|
basic_general_madness_container<std::unordered_set, std::unique_ptr<double>>() &&
|
||||||
|
basic_general_madness_container<std::multiset, std::unique_ptr<double>>() &&
|
||||||
|
basic_general_madness_container<std::unordered_multiset, std::unique_ptr<double>>() &&
|
||||||
|
// shared_ptr<double>:
|
||||||
|
basic_general_madness_container<std::vector, std::shared_ptr<double>>() &&
|
||||||
|
basic_general_madness_container<std::list, std::shared_ptr<double>>() &&
|
||||||
|
basic_general_madness_container<std::set, std::shared_ptr<double>>() &&
|
||||||
|
basic_general_madness_container<std::stack, std::shared_ptr<double>>() &&
|
||||||
|
basic_general_madness_container<std::deque, std::shared_ptr<double>>() &&
|
||||||
|
basic_general_madness_container<std::queue, std::shared_ptr<double>>() &&
|
||||||
|
basic_general_madness_container<std::priority_queue, std::shared_ptr<double>>() &&
|
||||||
|
basic_general_madness_container<std::unordered_set, std::shared_ptr<double>>() &&
|
||||||
|
basic_general_madness_container<std::multiset, std::shared_ptr<double>>() &&
|
||||||
|
basic_general_madness_container<std::unordered_multiset, std::shared_ptr<double>>() &&
|
||||||
|
// optional<double>:
|
||||||
|
basic_general_madness_container<std::vector, std::optional<double>>() &&
|
||||||
|
basic_general_madness_container<std::list, std::optional<double>>() &&
|
||||||
|
basic_general_madness_container<std::set, std::optional<double>>() &&
|
||||||
|
basic_general_madness_container<std::stack, std::optional<double>>() &&
|
||||||
|
basic_general_madness_container<std::deque, std::optional<double>>() &&
|
||||||
|
basic_general_madness_container<std::queue, std::optional<double>>() &&
|
||||||
|
basic_general_madness_container<std::priority_queue, std::optional<double>>() &&
|
||||||
|
basic_general_madness_container<std::unordered_set, std::optional<double>>() &&
|
||||||
|
basic_general_madness_container<std::multiset, std::optional<double>>() &&
|
||||||
|
basic_general_madness_container<std::unordered_multiset, std::optional<double>>() &&
|
||||||
#endif // SIMDJSON_EXCEPTIONS
|
#endif // SIMDJSON_EXCEPTIONS
|
||||||
true;
|
true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -110,6 +110,7 @@ simdjson_inline bool assert_iterate_error(T &arr, simdjson::error_code expected,
|
|||||||
return assert_equal( count, 1, operation );
|
return assert_equal( count, 1, operation );
|
||||||
}
|
}
|
||||||
#define TEST_START() do { std::cout << "> Running " << __func__ << " ..." << std::endl; } while(0);
|
#define TEST_START() do { std::cout << "> Running " << __func__ << " ..." << std::endl; } while(0);
|
||||||
|
#define TYPED_TEST_START(T) do { std::cout << "> Running " << __func__ << "<" << typeid(T).name() << "> ..." << std::endl; } while(0);
|
||||||
#define SUBTEST(NAME, TEST) do { std::cout << " - Subtest " << (NAME) << " ..." << std::endl; if (!(TEST)) { return false; } } while (0);
|
#define SUBTEST(NAME, TEST) do { std::cout << " - Subtest " << (NAME) << " ..." << std::endl; if (!(TEST)) { return false; } } while (0);
|
||||||
#define ASSERT_EQUAL(ACTUAL, EXPECTED) do { if (!::assert_equal ((ACTUAL), (EXPECTED), #ACTUAL)) { return false; } } while (0);
|
#define ASSERT_EQUAL(ACTUAL, EXPECTED) do { if (!::assert_equal ((ACTUAL), (EXPECTED), #ACTUAL)) { return false; } } while (0);
|
||||||
#define ASSERT_RESULT(ACTUAL, EXPECTED) do { if (!::assert_result ((ACTUAL), (EXPECTED), #ACTUAL)) { return false; } } while (0);
|
#define ASSERT_RESULT(ACTUAL, EXPECTED) do { if (!::assert_result ((ACTUAL), (EXPECTED), #ACTUAL)) { return false; } } while (0);
|
||||||
|
|||||||
Reference in New Issue
Block a user