mirror of
https://github.com/simdjson/simdjson
synced 2026-06-08 17:27:07 +00:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d293aa3ff2 | |||
| d5a7918e0d |
@@ -1,4 +1,4 @@
|
||||
name: Ubuntu aarch64 (GCC 13)
|
||||
name: Ubuntu ppc64le (GCC 11)
|
||||
|
||||
on:
|
||||
push:
|
||||
|
||||
@@ -31,4 +31,4 @@ jobs:
|
||||
echo -e '#include <simdjson.h>\nint main(int argc,char**argv) {simdjson::dom::parser parser;simdjson::dom::element tweets = parser.load(argv[1]); }' > tmp.cpp && c++ -Idestination/include -Ldestination/lib -std=c++17 -Wl,-rpath,destination/lib -o linkandrun tmp.cpp -lsimdjson && ./linkandrun jsonexamples/twitter.json &&
|
||||
mkdir testfindpackage &&
|
||||
cd testfindpackage &&
|
||||
echo -e 'cmake_minimum_required(VERSION 3.14)\nproject(simdjsontester)\nset(CMAKE_CXX_STANDARD 17)\nfind_package(simdjson REQUIRED)'> CMakeLists.txt && mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../destination .. && cmake --build .
|
||||
echo -e 'cmake_minimum_required(VERSION 3.1)\nproject(simdjsontester)\nset(CMAKE_CXX_STANDARD 17)\nfind_package(simdjson REQUIRED)'> CMakeLists.txt && mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../destination .. && cmake --build .
|
||||
|
||||
@@ -31,4 +31,4 @@ jobs:
|
||||
echo -e '#include <simdjson.h>\nint main(int argc,char**argv) {simdjson::dom::parser parser;simdjson::dom::element tweets = parser.load(argv[1]); }' > tmp.cpp && c++ -Idestination/include -Ldestination/lib -std=c++17 -Wl,-rpath,destination/lib -o linkandrun tmp.cpp -lsimdjson && ./linkandrun jsonexamples/twitter.json &&
|
||||
mkdir testfindpackage &&
|
||||
cd testfindpackage &&
|
||||
echo -e 'cmake_minimum_required(VERSION 3.14)\nproject(simdjsontester)\nset(CMAKE_CXX_STANDARD 17)\nfind_package(simdjson REQUIRED)'> CMakeLists.txt && mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../destination .. && cmake --build .
|
||||
echo -e 'cmake_minimum_required(VERSION 3.1)\nproject(simdjsontester)\nset(CMAKE_CXX_STANDARD 17)\nfind_package(simdjson REQUIRED)'> CMakeLists.txt && mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../destination .. && cmake --build .
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.14)
|
||||
project(
|
||||
simdjson
|
||||
# The version number is modified by tools/release.py
|
||||
VERSION 4.0.7
|
||||
VERSION 4.0.3
|
||||
DESCRIPTION "Parsing gigabytes of JSON per second"
|
||||
HOMEPAGE_URL "https://simdjson.org/"
|
||||
LANGUAGES CXX C
|
||||
|
||||
@@ -38,7 +38,7 @@ PROJECT_NAME = simdjson
|
||||
# could be handy for archiving the generated documentation or if some version
|
||||
# control system is used.
|
||||
|
||||
PROJECT_NUMBER = "4.0.7"
|
||||
PROJECT_NUMBER = "4.0.3"
|
||||
|
||||
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
||||
# for a project that appears at the top of each page and should give viewer a
|
||||
|
||||
@@ -62,7 +62,6 @@ Real-world usage
|
||||
- [WasmEdge](https://wasmedge.org)
|
||||
- [RonDB](https://github.com/logicalclocks/rondb)
|
||||
- [GreptimeDB](https://github.com/GreptimeTeam/greptimedb)
|
||||
- [mamba](https://github.com/mamba-org/mamba)
|
||||
|
||||
|
||||
If you are planning to use simdjson in a product, please work from one of our releases.
|
||||
@@ -121,6 +120,7 @@ Some users may want to browse code along with the compiled assembly. You want to
|
||||
* [C++26 reflection example](https://godbolt.org/z/K3Px64TqK)
|
||||
* [simdjson examples with errors handled through exceptions](https://godbolt.org/z/7G5qE4sr9)
|
||||
* [simdjson examples with errors without exceptions](https://godbolt.org/z/e9dWb9E4v)
|
||||
|
||||
|
||||
Performance results
|
||||
-------------------
|
||||
|
||||
@@ -44,10 +44,7 @@ struct yyjson_base {
|
||||
|
||||
struct yyjson : yyjson_base {
|
||||
bool run(simdjson::padded_string &json, std::vector<uint64_t> &result) {
|
||||
yyjson_doc *doc = yyjson_read(json.data(), json.size(), 0);
|
||||
bool b = yyjson_base::run(doc, result);
|
||||
yyjson_doc_free(doc);
|
||||
return b;
|
||||
return yyjson_base::run(yyjson_read(json.data(), json.size(), 0), result);
|
||||
}
|
||||
};
|
||||
BENCHMARK_TEMPLATE(distinct_user_id, yyjson)->UseManualTime();
|
||||
@@ -55,15 +52,11 @@ BENCHMARK_TEMPLATE(distinct_user_id, yyjson)->UseManualTime();
|
||||
#if SIMDJSON_COMPETITION_ONDEMAND_INSITU
|
||||
struct yyjson_insitu : yyjson_base {
|
||||
bool run(simdjson::padded_string &json, std::vector<uint64_t> &result) {
|
||||
yyjson_doc *doc = yyjson_read_opts(json.data(), json.size(), YYJSON_READ_INSITU, 0, 0);
|
||||
bool b = yyjson_base::run(doc, result);
|
||||
yyjson_doc_free(doc);
|
||||
return b;
|
||||
return yyjson_base::run(yyjson_read_opts(json.data(), json.size(), YYJSON_READ_INSITU, 0, 0), result);
|
||||
}
|
||||
};
|
||||
BENCHMARK_TEMPLATE(distinct_user_id, yyjson_insitu)->UseManualTime();
|
||||
#endif // SIMDJSON_COMPETITION_ONDEMAND_INSITU
|
||||
|
||||
} // namespace distinct_user_id
|
||||
|
||||
#endif // SIMDJSON_COMPETITION_YYJSON
|
||||
|
||||
@@ -36,26 +36,18 @@ struct yyjson_base {
|
||||
|
||||
struct yyjson : yyjson_base {
|
||||
bool run(simdjson::padded_string &json, uint64_t find_id, std::string_view &result) {
|
||||
yyjson_doc *doc = yyjson_read(json.data(), json.size(), 0);
|
||||
bool b = yyjson_base::run(doc, find_id, result);
|
||||
yyjson_doc_free(doc);
|
||||
return b;
|
||||
return yyjson_base::run(yyjson_read(json.data(), json.size(), 0), find_id, result);
|
||||
}
|
||||
};
|
||||
BENCHMARK_TEMPLATE(find_tweet, yyjson)->UseManualTime();
|
||||
|
||||
#if SIMDJSON_COMPETITION_ONDEMAND_INSITU
|
||||
struct yyjson_insitu : yyjson_base {
|
||||
bool run(simdjson::padded_string &json, uint64_t find_id, std::string_view &result) {
|
||||
yyjson_doc *doc = yyjson_read_opts(json.data(), json.size(), YYJSON_READ_INSITU, 0, 0);
|
||||
bool b = yyjson_base::run(doc, find_id, result);
|
||||
yyjson_doc_free(doc);
|
||||
return b;
|
||||
return yyjson_base::run(yyjson_read_opts(json.data(), json.size(), YYJSON_READ_INSITU, 0, 0), find_id, result);
|
||||
}
|
||||
};
|
||||
BENCHMARK_TEMPLATE(find_tweet, yyjson_insitu)->UseManualTime();
|
||||
#endif // SIMDJSON_COMPETITION_ONDEMAND_INSITU
|
||||
|
||||
} // namespace find_tweet
|
||||
|
||||
#endif // SIMDJSON_COMPETITION_YYJSON
|
||||
|
||||
@@ -100,7 +100,6 @@ struct yyjson : yyjson2msgpack {
|
||||
std::string_view &result) {
|
||||
yyjson_doc *doc = yyjson_read(json.data(), json.size(), 0);
|
||||
result = to_msgpack(doc, reinterpret_cast<uint8_t*>(buffer));
|
||||
yyjson_doc_free(doc);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@@ -114,7 +113,6 @@ struct yyjson_insitu : yyjson2msgpack {
|
||||
yyjson_doc *doc =
|
||||
yyjson_read_opts(json.data(), json.size(), YYJSON_READ_INSITU, 0, 0);
|
||||
result = to_msgpack(doc, reinterpret_cast<uint8_t*>(buffer));
|
||||
yyjson_doc_free(doc);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@@ -122,4 +120,4 @@ BENCHMARK_TEMPLATE(json2msgpack, yyjson_insitu)->UseManualTime();
|
||||
#endif // SIMDJSON_COMPETITION_ONDEMAND_INSITU
|
||||
} // namespace json2msgpack
|
||||
|
||||
#endif // SIMDJSON_COMPETITION_YYJSON
|
||||
#endif // SIMDJSON_COMPETITION_YYJSON
|
||||
@@ -49,26 +49,18 @@ struct yyjson_base {
|
||||
|
||||
struct yyjson : yyjson_base {
|
||||
bool run(simdjson::padded_string &json, std::vector<point> &result) {
|
||||
yyjson_doc *doc = yyjson_read(json.data(), json.size(), 0);
|
||||
bool b = yyjson_base::run(doc, result);
|
||||
yyjson_doc_free(doc);
|
||||
return b;
|
||||
return yyjson_base::run(yyjson_read(json.data(), json.size(), 0), result);
|
||||
}
|
||||
};
|
||||
BENCHMARK_TEMPLATE(kostya, yyjson)->UseManualTime();
|
||||
|
||||
#if SIMDJSON_COMPETITION_ONDEMAND_INSITU
|
||||
struct yyjson_insitu : yyjson_base {
|
||||
bool run(simdjson::padded_string &json, std::vector<point> &result) {
|
||||
yyjson_doc *doc = yyjson_read_opts(json.data(), json.size(), YYJSON_READ_INSITU, 0, 0);
|
||||
bool b = yyjson_base::run(doc, result);
|
||||
yyjson_doc_free(doc);
|
||||
return b;
|
||||
return yyjson_base::run(yyjson_read_opts(json.data(), json.size(), YYJSON_READ_INSITU, 0, 0), result);
|
||||
}
|
||||
};
|
||||
BENCHMARK_TEMPLATE(kostya, yyjson_insitu)->UseManualTime();
|
||||
#endif // SIMDJSON_COMPETITION_ONDEMAND_INSITU
|
||||
|
||||
} // namespace kostya
|
||||
|
||||
#endif // SIMDJSON_COMPETITION_YYJSON
|
||||
|
||||
@@ -47,26 +47,18 @@ struct yyjson_base {
|
||||
|
||||
struct yyjson : yyjson_base {
|
||||
bool run(simdjson::padded_string &json, std::vector<point> &result) {
|
||||
yyjson_doc *doc = yyjson_read(json.data(), json.size(), 0);
|
||||
bool b = yyjson_base::run(doc, result);
|
||||
yyjson_doc_free(doc);
|
||||
return b;
|
||||
return yyjson_base::run(yyjson_read(json.data(), json.size(), 0), result);
|
||||
}
|
||||
};
|
||||
BENCHMARK_TEMPLATE(large_random, yyjson)->UseManualTime();
|
||||
|
||||
#if SIMDJSON_COMPETITION_ONDEMAND_INSITU
|
||||
struct yyjson_insitu : yyjson_base {
|
||||
bool run(simdjson::padded_string &json, std::vector<point> &result) {
|
||||
yyjson_doc *doc = yyjson_read_opts(json.data(), json.size(), YYJSON_READ_INSITU, 0, 0);
|
||||
bool b = yyjson_base::run(doc, result);
|
||||
yyjson_doc_free(doc);
|
||||
return b;
|
||||
return yyjson_base::run(yyjson_read_opts(json.data(), json.size(), YYJSON_READ_INSITU, 0, 0), result);
|
||||
}
|
||||
};
|
||||
BENCHMARK_TEMPLATE(large_random, yyjson_insitu)->UseManualTime();
|
||||
#endif // SIMDJSON_COMPETITION_ONDEMAND_INSITU
|
||||
|
||||
} // namespace large_random
|
||||
|
||||
#endif // SIMDJSON_COMPETITION_YYJSON
|
||||
|
||||
@@ -62,26 +62,19 @@ struct yyjson_base {
|
||||
|
||||
struct yyjson : yyjson_base {
|
||||
bool run(simdjson::padded_string &json, std::vector<tweet<std::string_view>> &result) {
|
||||
yyjson_doc *doc = yyjson_read(json.data(), json.size(), 0);
|
||||
bool b = yyjson_base::run(doc, result);
|
||||
yyjson_doc_free(doc);
|
||||
return b;
|
||||
return yyjson_base::run(yyjson_read(json.data(), json.size(), 0), result);
|
||||
}
|
||||
};
|
||||
BENCHMARK_TEMPLATE(partial_tweets, yyjson)->UseManualTime();
|
||||
|
||||
#if SIMDJSON_COMPETITION_ONDEMAND_INSITU
|
||||
struct yyjson_insitu : yyjson_base {
|
||||
bool run(simdjson::padded_string &json, std::vector<tweet<std::string_view>> &result) {
|
||||
yyjson_doc *doc = yyjson_read_opts(json.data(), json.size(), YYJSON_READ_INSITU, 0, 0);
|
||||
bool b = yyjson_base::run(doc, result);
|
||||
yyjson_doc_free(doc);
|
||||
return b;
|
||||
return yyjson_base::run(yyjson_read_opts(json.data(), json.size(), YYJSON_READ_INSITU, 0, 0), result);
|
||||
}
|
||||
};
|
||||
BENCHMARK_TEMPLATE(partial_tweets, yyjson_insitu)->UseManualTime();
|
||||
#endif // SIMDJSON_COMPETITION_ONDEMAND_INSITU
|
||||
|
||||
} // namespace partial_tweets
|
||||
|
||||
#endif // SIMDJSON_COMPETITION_YYJSON
|
||||
|
||||
|
||||
@@ -51,26 +51,18 @@ struct yyjson_base {
|
||||
|
||||
struct yyjson : yyjson_base {
|
||||
bool run(simdjson::padded_string &json, int64_t max_retweet_count, top_tweet_result<StringType> &result) {
|
||||
yyjson_doc *doc = yyjson_read(json.data(), json.size(), 0);
|
||||
bool b = yyjson_base::run(doc, max_retweet_count, result);
|
||||
yyjson_doc_free(doc);
|
||||
return b;
|
||||
return yyjson_base::run(yyjson_read(json.data(), json.size(), 0), max_retweet_count, result);
|
||||
}
|
||||
};
|
||||
BENCHMARK_TEMPLATE(top_tweet, yyjson)->UseManualTime();
|
||||
|
||||
#if SIMDJSON_COMPETITION_ONDEMAND_INSITU
|
||||
struct yyjson_insitu : yyjson_base {
|
||||
bool run(simdjson::padded_string &json, int64_t max_retweet_count, top_tweet_result<StringType> &result) {
|
||||
yyjson_doc *doc = yyjson_read_opts(json.data(), json.size(), YYJSON_READ_INSITU, 0, 0);
|
||||
bool b = yyjson_base::run(doc, max_retweet_count, result);
|
||||
yyjson_doc_free(doc);
|
||||
return b;
|
||||
return yyjson_base::run(yyjson_read_opts(json.data(), json.size(), YYJSON_READ_INSITU, 0, 0), max_retweet_count, result);
|
||||
}
|
||||
};
|
||||
BENCHMARK_TEMPLATE(top_tweet, yyjson_insitu)->UseManualTime();
|
||||
#endif // SIMDJSON_COMPETITION_ONDEMAND_INSITU
|
||||
|
||||
} // namespace top_tweet
|
||||
|
||||
#endif // SIMDJSON_COMPETITION_YYJSON
|
||||
|
||||
@@ -174,10 +174,6 @@ else()
|
||||
-Werror -Wall -Wextra -Weffc++ -Wsign-compare -Wshadow -Wwrite-strings
|
||||
-Wpointer-arith -Winit-self -Wconversion -Wno-sign-conversion
|
||||
)
|
||||
if(CMAKE_CXX_STANDARD VERSION_GREATER_EQUAL 20)
|
||||
target_compile_options(simdjson-internal-flags INTERFACE -Wctad-maybe-unsupported)
|
||||
endif()
|
||||
|
||||
endif()
|
||||
|
||||
option(SIMDJSON_GLIBCXX_ASSERTIONS "Set _GLIBCXX_ASSERTIONS" OFF)
|
||||
|
||||
+5
-48
@@ -452,7 +452,7 @@ support for users who avoid exceptions. See [the simdjson error handling documen
|
||||
* **Field Access:** To get the value of the "foo" field in an object, use `object["foo"]`. This will
|
||||
scan through the object looking for the field with the matching string, doing a character-by-character
|
||||
comparison. It may generate the error `simdjson::NO_SUCH_FIELD` if there is no such key in the object, it may throw an exception (see [Error handling](#error-handling)). For efficiency reason, you should avoid looking up the same field repeatedly: e.g., do
|
||||
not do `object["foo"]` followed by `object["foo"]` with the same `object` instance. Generally, you should not mix and match iterating through an object (`for(auto field : object) {...}`) and key accesses (`object["foo"]`): if you need to iterate through an object after a key access, you need to call `reset()` on the object. Whenever you call `reset()`, you need to keep in mind that though you can iterate over the array repeatedly, values should be consumed only once (e.g., repeatedly calling `unescaped_key()` on the same key is forbidden). Keep in mind that On-Demand does not buffer or save the result of the parsing: if you repeatedly access `object["foo"]`, then it must repeatedly seek the key and parse the content. The library does not provide a distinct function to check if a key is present, instead we recommend you attempt to access the key: e.g., by doing `ondemand::value val{}; if (!object["foo"].get(val)) {...}`, you have that `val` contains the requested value inside the if clause. It is your responsibility as a user to temporarily keep a reference to the value (`auto v = object["foo"]`), or to consume the content and store it in your own data structures. If you consume an
|
||||
not do `object["foo"]` followed by `object["foo"]` with the same `object` instance. Generally, you should not mix and match iterating through an object (`for(auto field : object) {...}`) and key accesses (`object["foo"]`): if you need to iterate through an object after a key access, you need to call `reset()` on the object. Whenever you call `reset()`, you need to keep in mind that though you can iterate over the array repeatedly, values should be consumedonly once (e.g., repeatedly calling `unescaped_key()` on the same key is forbidden). Keep in mind that On-Demand does not buffer or save the result of the parsing: if you repeatedly access `object["foo"]`, then it must repeatedly seek the key and parse the content. The library does not provide a distinct function to check if a key is present, instead we recommend you attempt to access the key: e.g., by doing `ondemand::value val{}; if (!object["foo"].get(val)) {...}`, you have that `val` contains the requested value inside the if clause. It is your responsibility as a user to temporarily keep a reference to the value (`auto v = object["foo"]`), or to consume the content and store it in your own data structures. If you consume an
|
||||
object twice: `std::string_view(object["foo"]` followed by `std::string_view(object["foo"]` then your code
|
||||
is in error. Furthermore, you can only consume one field at a time, on the same object. The
|
||||
value instance you get from `content["bids"]` becomes invalid when you call `content["asks"]`.
|
||||
@@ -1400,42 +1400,6 @@ maps it to parsing code. We call the default constructor,
|
||||
and then assign values to the public members.
|
||||
|
||||
|
||||
If a key is missing in the JSON document, an error is generated (`NO_SUCH_FIELD`),
|
||||
except if the attribute is of a type like `std::optional` (`simdjson::concepts::optional_type`).
|
||||
|
||||
|
||||
Sometimes you might want to only extract some attributes from the JSON. You can
|
||||
achieve this result with the `extract_into` method supported by both `object` and
|
||||
`document` instances. It returns an error code that evaluates to false when there
|
||||
is no error.
|
||||
|
||||
Consider the following example where you only want to parse the make and the model
|
||||
from the JSON:
|
||||
|
||||
```cpp
|
||||
struct car_type {
|
||||
std::string make;
|
||||
std::string model;
|
||||
uint64_t year;
|
||||
std::vector<double> tire_pressure;
|
||||
};
|
||||
|
||||
void f() {
|
||||
auto json = R"( {
|
||||
"make": "Toyota",
|
||||
"model": "Camry",
|
||||
"year": 2024,
|
||||
"tire_pressure": [ 40.1, 39.9 ]
|
||||
} )"_padded;
|
||||
ondemand::parser parser;
|
||||
ondemand::document doc = parser.iterate(json);
|
||||
Car car{};
|
||||
auto error = doc.extract_into<"make","model">(car);
|
||||
if(error) { /** error handling */ }
|
||||
// only car.make and car.
|
||||
}
|
||||
```
|
||||
|
||||
#### Special cases
|
||||
|
||||
However, there are instances where the construction cannot
|
||||
@@ -1492,7 +1456,7 @@ The code might be as simple as the following.
|
||||
auto padded = R"({"time":["2023-03-15T12:00:00Z"],"temperature":[42]})"_padded;
|
||||
simdjson::ondemand::parser parser;
|
||||
simdjson::ondemand::document doc = parser.iterate(padded);
|
||||
complicated_weather_data p = doc.get<complicated_weather_data>();
|
||||
complicated_weather_data p = doc.get<>(complicated_weather_data);
|
||||
```
|
||||
|
||||
Thus you can combine C++26 static reflection with custom deserialization
|
||||
@@ -1513,12 +1477,6 @@ type without a document instance like so:
|
||||
Car car = simdjson::from(json);
|
||||
```
|
||||
|
||||
You can also use the `simdjson::from` syntax without exceptions, like so:
|
||||
```cpp
|
||||
Car car;
|
||||
simdjson::error_code err = simdjson::from(json_car).get(car);
|
||||
```
|
||||
|
||||
You can also use the `simdjson::from` syntax to iterate over an array.
|
||||
|
||||
```cpp
|
||||
@@ -2807,16 +2765,15 @@ a parameter a reference to a `std::string`.
|
||||
ondemand::parser parser;
|
||||
ondemand::document doc = parser.iterate(json);
|
||||
std::string name;
|
||||
auto error = doc["name"].get_string(name);
|
||||
if(error) { /* handle error */ }
|
||||
doc["name"].get_string(name);
|
||||
```
|
||||
|
||||
The same routine can be written without exceptions handling:
|
||||
|
||||
```C++
|
||||
std::string name;
|
||||
auto error = doc["name"].get_string(name);
|
||||
if (error) { /* handle error */ }
|
||||
auto err = doc["name"].get_string(name);
|
||||
if (err) { /* handle error */ }
|
||||
```
|
||||
|
||||
The `std::string` instance, once created, is independent. Unlike our `std::string_view` instances,
|
||||
|
||||
+10
-120
@@ -34,13 +34,12 @@ It has the following methods to add content to the string:
|
||||
- `append_null()`: Appends the string "null" to the JSON buffer.
|
||||
- `clear()`: Clears the contents of the JSON buffer, resetting the position to 0 while retaining the allocated capacity.
|
||||
- `escape_and_append(std::string_view input)`: Appends a string view to the JSON buffer after escaping special characters (e.g., quotes, backslashes) as required by JSON.
|
||||
- `escape_and_append_with_quotes(std::string_view input)` Appends a string view surrounded by double quotes (e.g., "input") to the JSON buffer after escaping special characters. For constant strings, you may also do `escape_and_append_with_quotes<"mystring">()`.
|
||||
- `escape_and_append_with_quotes(std::string_view input)` Appends a string view surrounded by double quotes (e.g., "input") to the JSON buffer after escaping special characters.
|
||||
Parameters:
|
||||
- `escape_and_append_with_quotes(char input)`: Appends a single character surrounded by double quotes (e.g., "c") to the JSON buffer after escaping it if necessary.
|
||||
- `append_raw(const char *c)`: Appends a null-terminated C string directly to the JSON buffer without escaping.
|
||||
- `append_raw(std::string_view input)`: Appends a string view directly to the JSON buffer without escaping.
|
||||
- `append_raw(const char *str, size_t len)`: Appends a specified number of characters from a C string directly to the JSON
|
||||
- `append_key_value(key,value)`: Appends a key and a value (`"json":somevalue`)
|
||||
- `append_key_value<"mykey">(value)`: Appends a key and a value (`"json":somevalue`), useful when the key is a compile-time constant (C++20).
|
||||
|
||||
After writing the content, if you have reasons to believe that the content might violate UTF-8 conventions, you can check it as follows:
|
||||
|
||||
@@ -53,7 +52,7 @@ Once you are satisfied, you can recover the string as follows:
|
||||
|
||||
- `operator std::string()`: Converts the JSON buffer to an std::string. (Might throw if an error occurred.)
|
||||
- `operator std::string_view()`: Converts the JSON buffer to an std::string_view. (Might throw if an error occurred.)
|
||||
- `view()`: Returns a view of the written JSON buffer as a `simdjson_result<std::string_view>` (C++20).
|
||||
- `view()`: Returns a view of the written JSON buffer as a `simdjson_result<std::string_view>`.
|
||||
|
||||
The later method (`view()`) is recommended. For performance reasons, we expect you to explicitly call `validate_unicode()` as needed (e.g., prior to calling `view()`).
|
||||
|
||||
@@ -132,20 +131,20 @@ In all cases, the `std::string_view` instance depends the corresponding `string_
|
||||
|
||||
|
||||
|
||||
If you have C++20, you can simplify the code, as the `std::vector<double>` is automatically supported. Further, we can pass the keys (which are compile-time
|
||||
constant) as template parameter (for improved performance).
|
||||
If you have C++20, you can simplify the code, as the `std::vector<double>` is automatically
|
||||
supported.
|
||||
|
||||
```cpp
|
||||
Car c = {"Toyota", "Corolla", 2017, {30.0,30.2,30.513,30.79}};
|
||||
simdjson::builder::string_builder sb;
|
||||
sb.start_object();
|
||||
sb.append_key_value<"make">(c.make);
|
||||
sb.append_key_value("make", c.make);
|
||||
sb.append_comma();
|
||||
sb.append_key_value<"model">(c.model);
|
||||
sb.append_key_value("model", c.model);
|
||||
sb.append_comma();
|
||||
sb.append_key_value<"year">(c.year);
|
||||
sb.append_key_value("year", c.year);
|
||||
sb.append_comma();
|
||||
sb.append_key_value<"tire_pressure">(c.tire_pressure);
|
||||
sb.append_key_value("tire_pressure", c.tire_pressure);
|
||||
sb.end_object();
|
||||
std::string_view p = sb.view();
|
||||
```
|
||||
@@ -176,52 +175,9 @@ std::vector<std::vector<double>> c = {{1.0, 2.0}, {3.0, 4.0}};
|
||||
std::string json = simdjson::to_json(c);
|
||||
```
|
||||
|
||||
We also have an overload for when you want to reuse the same `std::string` instance:
|
||||
|
||||
|
||||
```cpp
|
||||
std::vector<std::vector<double>> c = {{1.0, 2.0}, {3.0, 4.0}};
|
||||
std::string json;
|
||||
auto error = simdjson::to_json(c, json);
|
||||
if(error) { /* there was an error */ }
|
||||
```
|
||||
|
||||
|
||||
We do recommend that you create and reuse the `string_builder` instance for performance
|
||||
reasons.
|
||||
|
||||
You can also add custom serialization functions using a `tag_invoke` function.
|
||||
For example, the following
|
||||
function will allow you to serialize instances of the type `Car`.
|
||||
|
||||
```cpp
|
||||
#include <simdjson>
|
||||
|
||||
struct Car {
|
||||
std::string make;
|
||||
std::string model;
|
||||
int64_t year;
|
||||
std::vector<float> tire_pressure;
|
||||
};
|
||||
|
||||
namespace simdjson {
|
||||
|
||||
template <typename builder_type>
|
||||
void tag_invoke(serialize_tag, builder_type &builder, const Car& car) {
|
||||
builder.start_object();
|
||||
builder.append_key_value("make", car.make);
|
||||
builder.append_comma();
|
||||
builder.append_key_value("model", car.model);
|
||||
builder.append_comma();
|
||||
builder.append_key_value("year", car.year);
|
||||
builder.append_comma();
|
||||
builder.append_key_value("tire_pressure", car.tire_pressure);
|
||||
builder.end_object();
|
||||
}
|
||||
|
||||
} // namespace simdjson
|
||||
```
|
||||
|
||||
C++26 static reflection
|
||||
------------------------
|
||||
|
||||
@@ -283,38 +239,7 @@ with the `simdjson::to_json` template function.
|
||||
If you know the output size, in bytes, of your JSON string, you may
|
||||
pass it as a second parameter (e.g., `simdjson::to_json(c, 31123)`).
|
||||
|
||||
Sometimes you may want to reuse the same `std::string` instance. We
|
||||
have an overload for this purpose:
|
||||
|
||||
```cpp
|
||||
Car c = {"Toyota", "Corolla", 2017, {30.0,30.2,30.513,30.79}};
|
||||
std::string s;
|
||||
auto error = simdjson::to_json(c, s);
|
||||
if(error) { /* there was an error */ }
|
||||
```
|
||||
|
||||
You can then also add a third parameter for the expected output size in bytes.
|
||||
|
||||
### Extracting just some fields
|
||||
In some instances, your class might have many fields that you do not want to serialize.
|
||||
You can achieve this result with the `simdjson::extract_from` template. In the following
|
||||
example, we serialize only the `year` and `price` fields on the `Car` instance.
|
||||
```cpp
|
||||
struct Car {
|
||||
std::string make;
|
||||
std::string model;
|
||||
int year;
|
||||
double price;
|
||||
bool electric;
|
||||
};
|
||||
Car car{"Ford", "F-150", 2024, 55000.0, false};
|
||||
// Extract year and price
|
||||
std::string json_result = simdjson::extract_from<"year", "price">(car);
|
||||
// Alternatively:
|
||||
// std::string json_result;
|
||||
// auto error = extract_from<"year", "price">(car).get(json_result);
|
||||
// if(error) { /* error handling */ }
|
||||
```
|
||||
|
||||
### Without `string_buffer` instance but with explicit error handling
|
||||
|
||||
@@ -323,44 +248,9 @@ pattern:
|
||||
|
||||
```cpp
|
||||
std::string json;
|
||||
if(simdjson::to_json(c).get(json)) {
|
||||
if(simdjson::to(c).get(json)) {
|
||||
// there was an error
|
||||
} else {
|
||||
// json contain the serialized JSON
|
||||
}
|
||||
```
|
||||
|
||||
### Customization
|
||||
|
||||
If you want to serialize a value in a custome way, you can do it with a
|
||||
`tag_invoke` specialization like the following example which will map
|
||||
the year attribute to a string.
|
||||
|
||||
|
||||
```cpp
|
||||
#include <simdjson>
|
||||
|
||||
struct Car {
|
||||
std::string make;
|
||||
std::string model;
|
||||
int64_t year;
|
||||
std::vector<float> tire_pressure;
|
||||
};
|
||||
|
||||
namespace simdjson {
|
||||
|
||||
template <typename builder_type>
|
||||
void tag_invoke(serialize_tag, builder_type &builder, const Car& car) {
|
||||
builder.start_object();
|
||||
builder.append_key_value("make", car.make);
|
||||
builder.append_comma();
|
||||
builder.append_key_value("model", car.model);
|
||||
builder.append_comma();
|
||||
builder.append_key_value("year", std::to_string(car.year));
|
||||
builder.append_comma();
|
||||
builder.append_key_value("tire_pressure", car.tire_pressure);
|
||||
builder.end_object();
|
||||
}
|
||||
|
||||
} // namespace simdjson
|
||||
```
|
||||
+1
-1
@@ -253,7 +253,7 @@ long page_size() {
|
||||
// page boundary.
|
||||
bool need_allocation(const char *buf, size_t len) {
|
||||
return ((reinterpret_cast<uintptr_t>(buf + len - 1) % page_size())
|
||||
+ simdjson::SIMDJSON_PADDING >= static_cast<uintptr_t>(page_size()));
|
||||
+ simdjson::SIMDJSON_PADDING > static_cast<uintptr_t>(page_size()));
|
||||
}
|
||||
|
||||
simdjson::padded_string_view
|
||||
|
||||
@@ -17,7 +17,7 @@ using namespace simd;
|
||||
struct backslash_and_quote {
|
||||
public:
|
||||
static constexpr uint32_t BYTES_PROCESSED = 32;
|
||||
simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
|
||||
simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
|
||||
|
||||
simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; }
|
||||
simdjson_inline bool has_backslash() { return bs_bits != 0; }
|
||||
|
||||
@@ -122,46 +122,11 @@ concept optional_type = requires(std::remove_cvref_t<T> obj) {
|
||||
} -> std::convertible_to<typename std::remove_cvref_t<T>::value_type>;
|
||||
};
|
||||
{ static_cast<bool>(obj) } -> std::same_as<bool>; // convertible to bool
|
||||
{ obj.reset() } noexcept -> std::same_as<void>;
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Types we serialize as JSON strings (not as containers)
|
||||
template <typename T>
|
||||
concept string_like =
|
||||
std::is_same_v<std::remove_cvref_t<T>, std::string> ||
|
||||
std::is_same_v<std::remove_cvref_t<T>, std::string_view> ||
|
||||
std::is_same_v<std::remove_cvref_t<T>, const char*> ||
|
||||
std::is_same_v<std::remove_cvref_t<T>, char*>;
|
||||
|
||||
// Concept that checks if a type is a container but not a string (because
|
||||
// strings handling must be handled differently)
|
||||
// Now uses iterator-based approach for broader container support
|
||||
template <typename T>
|
||||
concept container_but_not_string =
|
||||
std::ranges::input_range<T> && !string_like<T> && !concepts::string_view_keyed_map<T>;
|
||||
|
||||
|
||||
} // namespace concepts
|
||||
|
||||
|
||||
/**
|
||||
* We use tag_invoke as our customization point mechanism.
|
||||
*/
|
||||
template <typename Tag, typename... Args>
|
||||
concept tag_invocable = requires(Tag tag, Args... args) {
|
||||
tag_invoke(std::forward<Tag>(tag), std::forward<Args>(args)...);
|
||||
};
|
||||
|
||||
template <typename Tag, typename... Args>
|
||||
concept nothrow_tag_invocable =
|
||||
tag_invocable<Tag, Args...> && requires(Tag tag, Args... args) {
|
||||
{
|
||||
tag_invoke(std::forward<Tag>(tag), std::forward<Args>(args)...)
|
||||
} noexcept;
|
||||
};
|
||||
|
||||
} // namespace simdjson
|
||||
#endif // SIMDJSON_SUPPORTS_CONCEPTS
|
||||
#endif // SIMDJSON_CONCEPTS_H
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
#include <string_view>
|
||||
#include <array>
|
||||
|
||||
#if SIMDJSON_CONSTEVAL
|
||||
namespace simdjson {
|
||||
namespace constevalutil {
|
||||
#if SIMDJSON_CONSTEVAL
|
||||
|
||||
constexpr static std::array<uint8_t, 256> json_quotable_character = {
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
@@ -47,30 +47,7 @@ consteval std::string consteval_to_quoted_escaped(std::string_view input) {
|
||||
out.push_back('"');
|
||||
return out;
|
||||
}
|
||||
#endif // SIMDJSON_CONSTEVAL
|
||||
|
||||
|
||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
||||
template <std::size_t N>
|
||||
struct fixed_string {
|
||||
constexpr fixed_string() : data{} {} // Default constructor for buffers
|
||||
constexpr fixed_string(const char (&str)[N]) {
|
||||
for (std::size_t i = 0; i < N; ++i) {
|
||||
data[i] = str[i];
|
||||
}
|
||||
}
|
||||
char data[N];
|
||||
constexpr std::string_view view() const { return {data, N - 1}; }
|
||||
};
|
||||
template <std::size_t N>
|
||||
fixed_string(const char (&)[N]) -> fixed_string<N>;
|
||||
|
||||
template <fixed_string str>
|
||||
struct string_constant {
|
||||
static constexpr std::string_view value = str.view();
|
||||
};
|
||||
#endif // SIMDJSON_SUPPORTS_CONCEPTS
|
||||
|
||||
} // namespace constevalutil
|
||||
} // namespace simdjson
|
||||
#endif // SIMDJSON_CONSTEVAL
|
||||
#endif // SIMDJSON_CONSTEVALUTIL_H
|
||||
@@ -58,12 +58,6 @@ inline simdjson_result<T> auto_parser<parser_type>::result() noexcept(is_nothrow
|
||||
return m_doc.get<T>();
|
||||
}
|
||||
|
||||
template <typename parser_type>
|
||||
template <typename T>
|
||||
simdjson_warn_unused simdjson_inline error_code auto_parser<parser_type>::get(T &value) && noexcept(is_nothrow_gettable<T>) {
|
||||
return result<T>().get(value);
|
||||
}
|
||||
|
||||
template <typename parser_type>
|
||||
inline simdjson_result<ondemand::array> auto_parser<parser_type>::array() noexcept {
|
||||
return result<ondemand::array>();
|
||||
@@ -79,7 +73,12 @@ inline simdjson_result<ondemand::number> auto_parser<parser_type>::number() noex
|
||||
return result<ondemand::number>();
|
||||
}
|
||||
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
template <typename parser_type>
|
||||
template <typename T>
|
||||
inline auto_parser<parser_type>::operator simdjson_result<T>() noexcept(is_nothrow_gettable<T>) {
|
||||
return result<T>();
|
||||
}
|
||||
|
||||
template <typename parser_type>
|
||||
template <typename T>
|
||||
inline auto_parser<parser_type>::operator T() noexcept(false) {
|
||||
@@ -88,7 +87,6 @@ inline auto_parser<parser_type>::operator T() noexcept(false) {
|
||||
}
|
||||
return m_doc.get<T>();
|
||||
}
|
||||
#endif // SIMDJSON_EXCEPTIONS
|
||||
|
||||
template <typename parser_type>
|
||||
template <typename T>
|
||||
@@ -111,7 +109,7 @@ inline T to_adaptor<T>::operator()(simdjson_result<ondemand::value> &val) const
|
||||
|
||||
template <typename T>
|
||||
inline auto to_adaptor<T>::operator()(padded_string_view const str) const noexcept {
|
||||
return auto_parser<ondemand::parser *>{str};
|
||||
return auto_parser{str};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@@ -121,7 +119,7 @@ inline auto to_adaptor<T>::operator()(ondemand::parser &parser, padded_string_vi
|
||||
|
||||
template <typename T>
|
||||
inline auto to_adaptor<T>::operator()(std::string str) const noexcept {
|
||||
return auto_parser<ondemand::parser *>{pad_with_reserve(str)};
|
||||
return auto_parser{pad_with_reserve(str)};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
|
||||
@@ -46,19 +46,16 @@ public:
|
||||
|
||||
template <typename T>
|
||||
simdjson_warn_unused simdjson_inline simdjson_result<T> result() noexcept(is_nothrow_gettable<T>);
|
||||
template <typename T>
|
||||
simdjson_warn_unused simdjson_inline error_code get(T &value) && noexcept(is_nothrow_gettable<T>);
|
||||
|
||||
|
||||
simdjson_warn_unused simdjson_inline simdjson_result<ondemand::array> array() noexcept;
|
||||
simdjson_warn_unused simdjson_inline simdjson_result<ondemand::object> object() noexcept;
|
||||
simdjson_warn_unused simdjson_inline simdjson_result<ondemand::number> number() noexcept;
|
||||
|
||||
template <typename T>
|
||||
simdjson_warn_unused simdjson_inline explicit(false) operator simdjson_result<T>() noexcept(is_nothrow_gettable<T>);
|
||||
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
template <typename T>
|
||||
simdjson_warn_unused simdjson_inline explicit(false) operator T() noexcept(false);
|
||||
#endif // SIMDJSON_EXCEPTIONS
|
||||
|
||||
template <typename T>
|
||||
simdjson_warn_unused simdjson_inline std::optional<T> optional() noexcept(is_nothrow_gettable<T>);
|
||||
@@ -81,8 +78,6 @@ struct to_adaptor {
|
||||
auto operator()(std::string str) const noexcept;
|
||||
auto operator()(ondemand::parser &parser, std::string str) const noexcept;
|
||||
};
|
||||
// deduction guide
|
||||
auto_parser(padded_string_view const str) -> auto_parser<ondemand::parser*>;
|
||||
} // namespace internal
|
||||
} // namespace convert
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace {
|
||||
struct backslash_and_quote {
|
||||
public:
|
||||
static constexpr uint32_t BYTES_PROCESSED = 1;
|
||||
simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
|
||||
simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
|
||||
|
||||
simdjson_inline bool has_quote_first() { return c == '"'; }
|
||||
simdjson_inline bool has_backslash() { return c == '\\'; }
|
||||
|
||||
@@ -29,13 +29,13 @@ simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_b
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base<T>::error() const noexcept {
|
||||
simdjson_inline error_code implementation_simdjson_result_base<T>::error() const noexcept {
|
||||
return this->second;
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
simdjson_warn_unused simdjson_inline bool implementation_simdjson_result_base<T>::has_value() const noexcept {
|
||||
simdjson_inline bool implementation_simdjson_result_base<T>::has_value() const noexcept {
|
||||
return this->error() == SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -67,17 +67,17 @@ struct implementation_simdjson_result_base {
|
||||
*
|
||||
* @param value The variable to assign the value to. May not be set if there is an error.
|
||||
*/
|
||||
simdjson_warn_unused simdjson_inline error_code get(T &value) && noexcept;
|
||||
simdjson_inline error_code get(T &value) && noexcept;
|
||||
|
||||
/**
|
||||
* The error.
|
||||
*/
|
||||
simdjson_warn_unused simdjson_inline error_code error() const noexcept;
|
||||
simdjson_inline error_code error() const noexcept;
|
||||
|
||||
/**
|
||||
* Whether there is a value.
|
||||
*/
|
||||
simdjson_warn_unused simdjson_inline bool has_value() const noexcept;
|
||||
simdjson_inline bool has_value() const noexcept;
|
||||
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
|
||||
|
||||
@@ -361,7 +361,7 @@ simdjson_inline bool is_digit(const uint8_t c) {
|
||||
return static_cast<uint8_t>(c - '0') <= 9;
|
||||
}
|
||||
|
||||
simdjson_warn_unused simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) {
|
||||
simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) {
|
||||
// we continue with the fiction that we have an integer. If the
|
||||
// floating point number is representable as x * 10^z for some integer
|
||||
// z that fits in 53 bits, then we will be able to convert back the
|
||||
@@ -389,7 +389,7 @@ simdjson_warn_unused simdjson_inline error_code parse_decimal_after_separator(si
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
simdjson_warn_unused simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const src, const uint8_t *&p, int64_t &exponent) {
|
||||
simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const src, const uint8_t *&p, int64_t &exponent) {
|
||||
// Exp Sign: -123.456e[-]78
|
||||
bool neg_exp = ('-' == *p);
|
||||
if (neg_exp || '+' == *p) { p++; } // Skip + as well
|
||||
@@ -478,7 +478,7 @@ static error_code slow_float_parsing(simdjson_unused const uint8_t * src, double
|
||||
|
||||
/** @private */
|
||||
template<typename W>
|
||||
simdjson_warn_unused simdjson_inline error_code write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer) {
|
||||
simdjson_inline error_code write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer) {
|
||||
// If we frequently had to deal with long strings of digits,
|
||||
// we could extend our code by using a 128-bit integer instead
|
||||
// of a 64-bit integer. However, this is uncommon in practice.
|
||||
@@ -541,13 +541,13 @@ simdjson_warn_unused simdjson_inline error_code write_float(const uint8_t *const
|
||||
//
|
||||
// Our objective is accurate parsing (ULP of 0) at high speed.
|
||||
template<typename W>
|
||||
simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer);
|
||||
simdjson_inline error_code parse_number(const uint8_t *const src, W &writer);
|
||||
|
||||
// for performance analysis, it is sometimes useful to skip parsing
|
||||
#ifdef SIMDJSON_SKIPNUMBERPARSING
|
||||
|
||||
template<typename W>
|
||||
simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const, W &writer) {
|
||||
simdjson_inline error_code parse_number(const uint8_t *const, W &writer) {
|
||||
writer.append_s64(0); // always write zero
|
||||
return SUCCESS; // always succeeds
|
||||
}
|
||||
@@ -573,7 +573,7 @@ simdjson_unused simdjson_inline simdjson_result<number_type> get_number_type(con
|
||||
//
|
||||
// Our objective is accurate parsing (ULP of 0) at high speed.
|
||||
template<typename W>
|
||||
simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) {
|
||||
simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) {
|
||||
//
|
||||
// Check for minus sign
|
||||
//
|
||||
|
||||
@@ -14,9 +14,6 @@
|
||||
#include "simdjson/generic/ondemand/raw_json_string.h"
|
||||
#include "simdjson/generic/ondemand/parser.h"
|
||||
|
||||
// JSON builder - needed for extract_into functionality
|
||||
#include "simdjson/generic/ondemand/json_string_builder.h"
|
||||
|
||||
// All other declarations
|
||||
#include "simdjson/generic/ondemand/array.h"
|
||||
#include "simdjson/generic/ondemand/array_iterator.h"
|
||||
@@ -48,11 +45,9 @@
|
||||
#include "simdjson/generic/ondemand/token_iterator-inl.h"
|
||||
#include "simdjson/generic/ondemand/value_iterator-inl.h"
|
||||
|
||||
// JSON builder inline definitions
|
||||
// JSON builder, ideally they should not be part of the ondemand directory
|
||||
// but it is convenient for now to have them here.
|
||||
#include "simdjson/generic/ondemand/json_string_builder.h"
|
||||
#include "simdjson/generic/ondemand/json_string_builder-inl.h"
|
||||
#include "simdjson/generic/ondemand/json_builder.h"
|
||||
|
||||
// Compile-time JSON parsing (C++26 P2996 reflection)
|
||||
#include "simdjson/generic/ondemand/compile_time_json.h"
|
||||
#include "simdjson/generic/ondemand/compile_time_json-inl.h"
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ simdjson_inline simdjson_result<array_iterator> array::begin() noexcept {
|
||||
simdjson_inline simdjson_result<array_iterator> array::end() noexcept {
|
||||
return array_iterator(iter);
|
||||
}
|
||||
simdjson_warn_unused simdjson_warn_unused simdjson_inline error_code array::consume() noexcept {
|
||||
simdjson_inline error_code array::consume() noexcept {
|
||||
auto error = iter.json_iter().skip_child(iter.depth()-1);
|
||||
if(error) { iter.abandon(); }
|
||||
return error;
|
||||
|
||||
@@ -141,7 +141,7 @@ public:
|
||||
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
|
||||
*/
|
||||
template <typename T>
|
||||
simdjson_warn_unused simdjson_inline error_code get(T &out)
|
||||
simdjson_inline error_code get(T &out)
|
||||
noexcept(custom_deserializable<T, array> ? nothrow_custom_deserializable<T, array> : true) {
|
||||
static_assert(custom_deserializable<T, array>);
|
||||
return deserialize(*this, out);
|
||||
@@ -166,7 +166,7 @@ protected:
|
||||
/**
|
||||
* Go to the end of the array, no matter where you are right now.
|
||||
*/
|
||||
simdjson_warn_unused simdjson_inline error_code consume() noexcept;
|
||||
simdjson_inline error_code consume() noexcept;
|
||||
|
||||
/**
|
||||
* Begin array iteration.
|
||||
@@ -252,7 +252,7 @@ public:
|
||||
return first.get<T>();
|
||||
}
|
||||
template<typename T>
|
||||
simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept {
|
||||
simdjson_inline error_code get(T& out) noexcept {
|
||||
if (error()) { return error(); }
|
||||
if constexpr (std::is_same_v<T, SIMDJSON_IMPLEMENTATION::ondemand::array>) {
|
||||
out = first;
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
/**
|
||||
* @file compile_time_json-inl.h
|
||||
* @brief Implementation details for compile-time JSON parsing
|
||||
*
|
||||
* This file contains inline implementations and helper utilities for compile-time
|
||||
* JSON parsing. Currently, the main implementation is self-contained in the header.
|
||||
*/
|
||||
|
||||
#ifndef SIMDJSON_GENERIC_COMPILE_TIME_JSON_INL_H
|
||||
|
||||
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
|
||||
#define SIMDJSON_GENERIC_COMPILE_TIME_JSON_INL_H
|
||||
#include "simdjson/generic/ondemand/compile_time_json.h"
|
||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
|
||||
#include <meta>
|
||||
#include <array>
|
||||
#include <string_view>
|
||||
#include <cstdint>
|
||||
|
||||
namespace simdjson {
|
||||
namespace SIMDJSON_IMPLEMENTATION {
|
||||
namespace compile_time {
|
||||
|
||||
/**
|
||||
* @brief Optimized constexpr string to integer conversion
|
||||
*
|
||||
* This can be used for more efficient integer parsing in the future.
|
||||
* Currently, we parse all numbers as doubles for simplicity.
|
||||
*/
|
||||
constexpr int64_t parse_int_fast(std::string_view str) {
|
||||
int64_t result = 0;
|
||||
bool negative = false;
|
||||
std::size_t i = 0;
|
||||
|
||||
if (i < str.size() && str[i] == '-') {
|
||||
negative = true;
|
||||
++i;
|
||||
}
|
||||
|
||||
while (i < str.size() && str[i] >= '0' && str[i] <= '9') {
|
||||
result = result * 10 + (str[i] - '0');
|
||||
++i;
|
||||
}
|
||||
|
||||
return negative ? -result : result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unescape JSON string at compile-time
|
||||
*
|
||||
* Currently, strings are returned as views into the original JSON.
|
||||
* This function can be used in the future for proper escape handling.
|
||||
*/
|
||||
template<std::size_t MaxLen = 1024>
|
||||
constexpr auto unescape_json_string(std::string_view escaped) {
|
||||
std::array<char, MaxLen> result{};
|
||||
std::size_t out_pos = 0;
|
||||
std::size_t i = 0;
|
||||
|
||||
while (i < escaped.size() && out_pos < MaxLen) {
|
||||
if (escaped[i] == '\\' && i + 1 < escaped.size()) {
|
||||
++i;
|
||||
switch (escaped[i]) {
|
||||
case '"': result[out_pos++] = '"'; break;
|
||||
case '\\': result[out_pos++] = '\\'; break;
|
||||
case '/': result[out_pos++] = '/'; break;
|
||||
case 'b': result[out_pos++] = '\b'; break;
|
||||
case 'f': result[out_pos++] = '\f'; break;
|
||||
case 'n': result[out_pos++] = '\n'; break;
|
||||
case 'r': result[out_pos++] = '\r'; break;
|
||||
case 't': result[out_pos++] = '\t'; break;
|
||||
case 'u':
|
||||
// Unicode escape - would need proper implementation
|
||||
// For now, skip the escape sequence
|
||||
i += 4; // Skip 4 hex digits
|
||||
break;
|
||||
default:
|
||||
result[out_pos++] = escaped[i];
|
||||
}
|
||||
++i;
|
||||
} else {
|
||||
result[out_pos++] = escaped[i];
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
return std::pair{result, out_pos};
|
||||
}
|
||||
|
||||
} // namespace compile_time
|
||||
} // namespace SIMDJSON_IMPLEMENTATION
|
||||
} // namespace simdjson
|
||||
|
||||
#endif // SIMDJSON_STATIC_REFLECTION
|
||||
#endif // SIMDJSON_GENERIC_COMPILE_TIME_JSON_INL_H
|
||||
@@ -1,464 +0,0 @@
|
||||
/**
|
||||
* @file compile_time_json.h
|
||||
* @brief Compile-time JSON parsing using C++26 reflection with std::meta::substitute()
|
||||
*
|
||||
* Based on the godbolt example: https://godbolt.org/z/Kn5b46T8j
|
||||
* Uses the Outer<Ms...>::Inner + substitute() pattern for recursive type generation.
|
||||
*/
|
||||
|
||||
#ifndef SIMDJSON_GENERIC_COMPILE_TIME_JSON_H
|
||||
|
||||
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
|
||||
#define SIMDJSON_GENERIC_COMPILE_TIME_JSON_H
|
||||
#include "simdjson/generic/ondemand/base.h"
|
||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
|
||||
#include <meta>
|
||||
#include <string_view>
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
|
||||
namespace simdjson {
|
||||
namespace SIMDJSON_IMPLEMENTATION {
|
||||
namespace compile_time {
|
||||
|
||||
/**
|
||||
* @brief Helper struct for substitute() pattern
|
||||
* The consteval block can use define_aggregate because it's in a template context
|
||||
*/
|
||||
template <std::meta::info ...Ms>
|
||||
struct Outer {
|
||||
struct Inner;
|
||||
consteval {
|
||||
std::meta::define_aggregate(^^Inner, {Ms...});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Type alias for the generated struct
|
||||
*/
|
||||
template <std::meta::info ...Ms>
|
||||
using Cls = Outer<Ms...>::Inner;
|
||||
|
||||
/**
|
||||
* @brief Variable template for constructing instances with values
|
||||
*/
|
||||
template <typename T, auto ... Vs>
|
||||
constexpr auto construct_from = T{Vs...};
|
||||
|
||||
// Forward declaration
|
||||
consteval std::meta::info parse_json_impl(std::string_view json);
|
||||
|
||||
/**
|
||||
* @brief Parse JSON array and return std::meta::info for the generated array
|
||||
*/
|
||||
consteval std::meta::info parse_json_array_impl(std::string_view json) {
|
||||
auto cursor = json.begin();
|
||||
auto end = json.end();
|
||||
|
||||
auto is_whitespace = [](char c) {
|
||||
return c == ' ' || c == '\n' || c == '\t' || c == '\r';
|
||||
};
|
||||
|
||||
auto skip_whitespace = [&]() -> void {
|
||||
while (cursor != end && is_whitespace(*cursor)) cursor++;
|
||||
};
|
||||
|
||||
auto expect_consume = [&](char c) -> void {
|
||||
skip_whitespace();
|
||||
if (cursor == end || *(cursor++) != c) throw "unexpected character";
|
||||
};
|
||||
|
||||
auto parse_value = [&](std::string &out) -> void {
|
||||
skip_whitespace();
|
||||
|
||||
bool quoted = false;
|
||||
unsigned depth = 0;
|
||||
while (true) {
|
||||
if (cursor == end) throw "unexpected end of stream";
|
||||
if (is_whitespace(*cursor) && !quoted && depth == 0)
|
||||
break;
|
||||
|
||||
if (depth == 0 && (*cursor == ',' || *cursor == ']'))
|
||||
break;
|
||||
out += *(cursor++);
|
||||
|
||||
if (out.back() == '{')
|
||||
++depth;
|
||||
else if (out.back() == '}')
|
||||
--depth;
|
||||
else if (out.back() == '[')
|
||||
++depth;
|
||||
else if (out.back() == ']')
|
||||
--depth;
|
||||
else if (out.back() == '"') {
|
||||
if (quoted && depth == 0)
|
||||
break;
|
||||
quoted = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
skip_whitespace();
|
||||
expect_consume('[');
|
||||
|
||||
std::vector<std::meta::info> values = {^^void};
|
||||
std::meta::info element_type = ^^void;
|
||||
bool first = true;
|
||||
|
||||
using std::meta::reflect_constant, std::meta::reflect_constant_string;
|
||||
|
||||
skip_whitespace();
|
||||
if (cursor != end && *cursor == ']') {
|
||||
expect_consume(']');
|
||||
// Empty array - use int as placeholder type since void doesn't work
|
||||
auto array_type = std::meta::substitute(^^std::array, {^^int, reflect_constant(0uz)});
|
||||
values[0] = array_type;
|
||||
return std::meta::substitute(^^construct_from, values);
|
||||
}
|
||||
|
||||
while (cursor != end && *cursor != ']') {
|
||||
std::string value;
|
||||
parse_value(value);
|
||||
|
||||
if (value.empty()) throw "expected value";
|
||||
|
||||
if (value[0] == '"') {
|
||||
if (value.back() != '"') throw "expected end of string";
|
||||
std::string_view contents(&value[1], value.size() - 2);
|
||||
|
||||
if (first) element_type = ^^char const*;
|
||||
values.push_back(reflect_constant_string(contents));
|
||||
} else if (value == "true") {
|
||||
if (first) element_type = ^^bool;
|
||||
values.push_back(reflect_constant(true));
|
||||
} else if (value == "false") {
|
||||
if (first) element_type = ^^bool;
|
||||
values.push_back(reflect_constant(false));
|
||||
} else if (value == "null") {
|
||||
if (first) element_type = ^^std::nullptr_t;
|
||||
values.push_back(reflect_constant(nullptr));
|
||||
} else if ((value[0] >= '0' && value[0] <= '9') || value[0] == '-') {
|
||||
// Try to parse as integer first
|
||||
bool is_int = true;
|
||||
for (char c : value) {
|
||||
if (c == '.' || c == 'e' || c == 'E') {
|
||||
is_int = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_int) {
|
||||
int contents = [](std::string_view in) {
|
||||
int out = 0;
|
||||
bool negative = false;
|
||||
std::size_t i = 0;
|
||||
if (in[0] == '-') {
|
||||
negative = true;
|
||||
i = 1;
|
||||
}
|
||||
for (; i < in.size(); ++i) {
|
||||
out = out * 10 + (in[i] - '0');
|
||||
}
|
||||
return negative ? -out : out;
|
||||
}(value);
|
||||
|
||||
if (first) element_type = ^^int;
|
||||
values.push_back(reflect_constant(contents));
|
||||
} else {
|
||||
// Parse as double
|
||||
double contents = [](std::string_view in) {
|
||||
double result = 0.0;
|
||||
double sign = 1.0;
|
||||
std::size_t i = 0;
|
||||
|
||||
if (in[0] == '-') {
|
||||
sign = -1.0;
|
||||
i = 1;
|
||||
}
|
||||
|
||||
while (i < in.size() && in[i] >= '0' && in[i] <= '9') {
|
||||
result = result * 10.0 + (in[i] - '0');
|
||||
++i;
|
||||
}
|
||||
|
||||
if (i < in.size() && in[i] == '.') {
|
||||
++i;
|
||||
double fraction = 0.0;
|
||||
double divisor = 1.0;
|
||||
while (i < in.size() && in[i] >= '0' && in[i] <= '9') {
|
||||
fraction = fraction * 10.0 + (in[i] - '0');
|
||||
divisor *= 10.0;
|
||||
++i;
|
||||
}
|
||||
result += fraction / divisor;
|
||||
}
|
||||
|
||||
return result * sign;
|
||||
}(value);
|
||||
|
||||
if (first) element_type = ^^double;
|
||||
values.push_back(reflect_constant(contents));
|
||||
}
|
||||
} else if (value[0] == '{') {
|
||||
// Nested object in array
|
||||
std::meta::info parsed = parse_json_impl(value);
|
||||
if (first) element_type = std::meta::type_of(parsed);
|
||||
values.push_back(parsed);
|
||||
} else if (value[0] == '[') {
|
||||
// Nested array
|
||||
std::meta::info parsed = parse_json_array_impl(value);
|
||||
if (first) element_type = std::meta::type_of(parsed);
|
||||
values.push_back(parsed);
|
||||
}
|
||||
|
||||
first = false;
|
||||
|
||||
skip_whitespace();
|
||||
if (cursor != end && *cursor == ',')
|
||||
++cursor;
|
||||
}
|
||||
|
||||
if (cursor == end) throw "unexpected end";
|
||||
expect_consume(']');
|
||||
|
||||
// Create std::array<ElementType, Count> type
|
||||
std::size_t count = values.size() - 1; // -1 because first element is ^^void placeholder
|
||||
auto array_type = std::meta::substitute(^^std::array, {element_type, reflect_constant(count)});
|
||||
|
||||
// Create array instance with values
|
||||
values[0] = array_type;
|
||||
return std::meta::substitute(^^construct_from, values);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Parse JSON and return std::meta::info for the generated type + instance
|
||||
*/
|
||||
consteval std::meta::info parse_json_impl(std::string_view json) {
|
||||
auto cursor = json.begin();
|
||||
auto end = json.end();
|
||||
|
||||
auto is_whitespace = [](char c) {
|
||||
return c == ' ' || c == '\n' || c == '\t' || c == '\r';
|
||||
};
|
||||
|
||||
auto skip_whitespace = [&]() -> void {
|
||||
while (cursor != end && is_whitespace(*cursor)) cursor++;
|
||||
};
|
||||
|
||||
auto expect_consume = [&](char c) -> void {
|
||||
skip_whitespace();
|
||||
if (cursor == end || *(cursor++) != c) throw "unexpected character";
|
||||
};
|
||||
|
||||
auto parse_until = [&](std::vector<char> delims, std::string &out) -> void {
|
||||
skip_whitespace();
|
||||
while (cursor != end &&
|
||||
!std::ranges::any_of(delims, [&](char c) { return c == *cursor; }))
|
||||
out += *(cursor++);
|
||||
};
|
||||
|
||||
auto parse_delimited = [&](char lhs, std::string &out, char rhs) -> void {
|
||||
skip_whitespace();
|
||||
expect_consume(lhs);
|
||||
parse_until({rhs}, out);
|
||||
expect_consume(rhs);
|
||||
};
|
||||
|
||||
auto parse_value = [&](std::string &out) -> void {
|
||||
skip_whitespace();
|
||||
|
||||
bool quoted = false;
|
||||
unsigned depth = 0;
|
||||
bool in_array = false;
|
||||
while (true) {
|
||||
if (cursor == end) throw "unexpected end of stream";
|
||||
if (is_whitespace(*cursor) && !quoted && depth == 0)
|
||||
break;
|
||||
|
||||
if (depth == 0 && (*cursor == ',' || *cursor == '}' || *cursor == ']'))
|
||||
break;
|
||||
out += *(cursor++);
|
||||
|
||||
if (out.back() == '{')
|
||||
++depth;
|
||||
else if (out.back() == '}')
|
||||
--depth;
|
||||
else if (out.back() == '[') {
|
||||
in_array = true;
|
||||
++depth;
|
||||
} else if (out.back() == ']')
|
||||
--depth;
|
||||
else if (out.back() == '"') {
|
||||
if (quoted && depth == 0)
|
||||
break;
|
||||
quoted = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
skip_whitespace();
|
||||
expect_consume('{');
|
||||
|
||||
std::vector<std::meta::info> members;
|
||||
std::vector<std::meta::info> values = {^^void};
|
||||
|
||||
using std::meta::reflect_constant, std::meta::reflect_constant_string;
|
||||
while (cursor != end && *cursor != '}') {
|
||||
std::string field_name;
|
||||
std::string value;
|
||||
|
||||
parse_delimited('"', field_name, '"');
|
||||
expect_consume(':');
|
||||
parse_value(value);
|
||||
|
||||
if (value.empty()) throw "expected value";
|
||||
if (cursor == end) throw "unexpected end of stream";
|
||||
|
||||
if (value[0] == '"') {
|
||||
if (value.back() != '"') throw "expected end of string";
|
||||
std::string_view contents(&value[1], value.size() - 2);
|
||||
|
||||
auto dms = std::meta::data_member_spec(^^char const*, {.name=field_name});
|
||||
members.push_back(reflect_constant(dms));
|
||||
values.push_back(reflect_constant_string(contents));
|
||||
} else if (value == "true") {
|
||||
auto dms = std::meta::data_member_spec(^^bool, {.name=field_name});
|
||||
members.push_back(reflect_constant(dms));
|
||||
values.push_back(reflect_constant(true));
|
||||
} else if (value == "false") {
|
||||
auto dms = std::meta::data_member_spec(^^bool, {.name=field_name});
|
||||
members.push_back(reflect_constant(dms));
|
||||
values.push_back(reflect_constant(false));
|
||||
} else if (value == "null") {
|
||||
auto dms = std::meta::data_member_spec(^^std::nullptr_t, {.name=field_name});
|
||||
members.push_back(reflect_constant(dms));
|
||||
values.push_back(reflect_constant(nullptr));
|
||||
} else if ((value[0] >= '0' && value[0] <= '9') || value[0] == '-') {
|
||||
// Try to parse as integer first
|
||||
bool is_int = true;
|
||||
for (char c : value) {
|
||||
if (c == '.' || c == 'e' || c == 'E') {
|
||||
is_int = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_int) {
|
||||
int contents = [](std::string_view in) {
|
||||
int out = 0;
|
||||
bool negative = false;
|
||||
std::size_t i = 0;
|
||||
if (in[0] == '-') {
|
||||
negative = true;
|
||||
i = 1;
|
||||
}
|
||||
for (; i < in.size(); ++i) {
|
||||
out = out * 10 + (in[i] - '0');
|
||||
}
|
||||
return negative ? -out : out;
|
||||
}(value);
|
||||
|
||||
auto dms = std::meta::data_member_spec(^^int, {.name=field_name});
|
||||
members.push_back(reflect_constant(dms));
|
||||
values.push_back(reflect_constant(contents));
|
||||
} else {
|
||||
// Parse as double
|
||||
double contents = [](std::string_view in) {
|
||||
double result = 0.0;
|
||||
double sign = 1.0;
|
||||
std::size_t i = 0;
|
||||
|
||||
if (in[0] == '-') {
|
||||
sign = -1.0;
|
||||
i = 1;
|
||||
}
|
||||
|
||||
while (i < in.size() && in[i] >= '0' && in[i] <= '9') {
|
||||
result = result * 10.0 + (in[i] - '0');
|
||||
++i;
|
||||
}
|
||||
|
||||
if (i < in.size() && in[i] == '.') {
|
||||
++i;
|
||||
double fraction = 0.0;
|
||||
double divisor = 1.0;
|
||||
while (i < in.size() && in[i] >= '0' && in[i] <= '9') {
|
||||
fraction = fraction * 10.0 + (in[i] - '0');
|
||||
divisor *= 10.0;
|
||||
++i;
|
||||
}
|
||||
result += fraction / divisor;
|
||||
}
|
||||
|
||||
return result * sign;
|
||||
}(value);
|
||||
|
||||
auto dms = std::meta::data_member_spec(^^double, {.name=field_name});
|
||||
members.push_back(reflect_constant(dms));
|
||||
values.push_back(reflect_constant(contents));
|
||||
}
|
||||
} else if (value[0] == '{') {
|
||||
// Nested object
|
||||
std::meta::info parsed = parse_json_impl(value);
|
||||
|
||||
auto dms = std::meta::data_member_spec(std::meta::type_of(parsed), {.name=field_name});
|
||||
members.push_back(reflect_constant(dms));
|
||||
values.push_back(parsed);
|
||||
} else if (value[0] == '[') {
|
||||
// Array
|
||||
std::meta::info parsed = parse_json_array_impl(value);
|
||||
|
||||
auto dms = std::meta::data_member_spec(std::meta::type_of(parsed), {.name=field_name});
|
||||
members.push_back(reflect_constant(dms));
|
||||
values.push_back(parsed);
|
||||
}
|
||||
|
||||
skip_whitespace();
|
||||
if (cursor != end && *cursor == ',')
|
||||
++cursor;
|
||||
}
|
||||
|
||||
if (cursor == end) throw "unexpected end";
|
||||
expect_consume('}');
|
||||
|
||||
// The substitute() trick:
|
||||
// 1. Create the type: Cls<member_specs...>
|
||||
values[0] = std::meta::substitute(^^Cls, members);
|
||||
// 2. Create instance: construct_from<Type, values...>
|
||||
return std::meta::substitute(^^construct_from, values);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Main parse_json function - template wrapper
|
||||
*/
|
||||
template<constevalutil::fixed_string json_str>
|
||||
consteval auto parse_json() {
|
||||
constexpr std::meta::info result = parse_json_impl(json_str.view());
|
||||
return [:result:];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief JSON validation
|
||||
*/
|
||||
template<constevalutil::fixed_string json_str>
|
||||
consteval bool validate_json() {
|
||||
try {
|
||||
parse_json_impl(json_str.view());
|
||||
return true;
|
||||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace compile_time
|
||||
} // namespace SIMDJSON_IMPLEMENTATION
|
||||
} // namespace simdjson
|
||||
|
||||
#endif // SIMDJSON_STATIC_REFLECTION
|
||||
#endif // SIMDJSON_GENERIC_COMPILE_TIME_JSON_H
|
||||
@@ -7,8 +7,55 @@
|
||||
#include "simdjson/generic/ondemand/array.h"
|
||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||
|
||||
#include <concepts>
|
||||
namespace simdjson {
|
||||
|
||||
namespace tag_invoke_fn_ns {
|
||||
void tag_invoke();
|
||||
|
||||
struct tag_invoke_fn {
|
||||
template <typename Tag, typename... Args>
|
||||
requires requires(Tag tag, Args &&...args) {
|
||||
tag_invoke(std::forward<Tag>(tag), std::forward<Args>(args)...);
|
||||
}
|
||||
constexpr auto operator()(Tag tag, Args &&...args) const
|
||||
noexcept(noexcept(tag_invoke(std::forward<Tag>(tag),
|
||||
std::forward<Args>(args)...)))
|
||||
-> decltype(tag_invoke(std::forward<Tag>(tag),
|
||||
std::forward<Args>(args)...)) {
|
||||
return tag_invoke(std::forward<Tag>(tag), std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
} // namespace tag_invoke_fn_ns
|
||||
|
||||
inline namespace tag_invoke_ns {
|
||||
inline constexpr tag_invoke_fn_ns::tag_invoke_fn tag_invoke = {};
|
||||
} // namespace tag_invoke_ns
|
||||
|
||||
template <typename Tag, typename... Args>
|
||||
concept tag_invocable = requires(Tag tag, Args... args) {
|
||||
tag_invoke(std::forward<Tag>(tag), std::forward<Args>(args)...);
|
||||
};
|
||||
|
||||
template <typename Tag, typename... Args>
|
||||
concept nothrow_tag_invocable =
|
||||
tag_invocable<Tag, Args...> && requires(Tag tag, Args... args) {
|
||||
{
|
||||
tag_invoke(std::forward<Tag>(tag), std::forward<Args>(args)...)
|
||||
} noexcept;
|
||||
};
|
||||
|
||||
template <typename Tag, typename... Args>
|
||||
using tag_invoke_result =
|
||||
std::invoke_result<decltype(tag_invoke), Tag, Args...>;
|
||||
|
||||
template <typename Tag, typename... Args>
|
||||
using tag_invoke_result_t =
|
||||
std::invoke_result_t<decltype(tag_invoke), Tag, Args...>;
|
||||
|
||||
template <auto &Tag> using tag_t = std::decay_t<decltype(Tag)>;
|
||||
|
||||
|
||||
struct deserialize_tag;
|
||||
|
||||
/// These types are deserializable in a built-in way
|
||||
|
||||
@@ -141,7 +141,7 @@ simdjson_inline simdjson_result<std::string_view> document::get_string(bool allo
|
||||
return get_root_value_iterator().get_root_string(true, allow_replacement);
|
||||
}
|
||||
template <typename string_type>
|
||||
simdjson_warn_unused simdjson_inline error_code document::get_string(string_type& receiver, bool allow_replacement) noexcept {
|
||||
simdjson_inline error_code document::get_string(string_type& receiver, bool allow_replacement) noexcept {
|
||||
return get_root_value_iterator().get_root_string(receiver, true, allow_replacement);
|
||||
}
|
||||
simdjson_inline simdjson_result<std::string_view> document::get_wobbly_string() noexcept {
|
||||
@@ -167,15 +167,15 @@ template<> simdjson_inline simdjson_result<int64_t> document::get() & noexcept {
|
||||
template<> simdjson_inline simdjson_result<bool> document::get() & noexcept { return get_bool(); }
|
||||
template<> simdjson_inline simdjson_result<value> document::get() & noexcept { return get_value(); }
|
||||
|
||||
template<> simdjson_warn_unused simdjson_inline error_code document::get(array& out) & noexcept { return get_array().get(out); }
|
||||
template<> simdjson_warn_unused simdjson_inline error_code document::get(object& out) & noexcept { return get_object().get(out); }
|
||||
template<> simdjson_warn_unused simdjson_inline error_code document::get(raw_json_string& out) & noexcept { return get_raw_json_string().get(out); }
|
||||
template<> simdjson_warn_unused simdjson_inline error_code document::get(std::string_view& out) & noexcept { return get_string(false).get(out); }
|
||||
template<> simdjson_warn_unused simdjson_inline error_code document::get(double& out) & noexcept { return get_double().get(out); }
|
||||
template<> simdjson_warn_unused simdjson_inline error_code document::get(uint64_t& out) & noexcept { return get_uint64().get(out); }
|
||||
template<> simdjson_warn_unused simdjson_inline error_code document::get(int64_t& out) & noexcept { return get_int64().get(out); }
|
||||
template<> simdjson_warn_unused simdjson_inline error_code document::get(bool& out) & noexcept { return get_bool().get(out); }
|
||||
template<> simdjson_warn_unused simdjson_inline error_code document::get(value& out) & noexcept { return get_value().get(out); }
|
||||
template<> simdjson_inline error_code document::get(array& out) & noexcept { return get_array().get(out); }
|
||||
template<> simdjson_inline error_code document::get(object& out) & noexcept { return get_object().get(out); }
|
||||
template<> simdjson_inline error_code document::get(raw_json_string& out) & noexcept { return get_raw_json_string().get(out); }
|
||||
template<> simdjson_inline error_code document::get(std::string_view& out) & noexcept { return get_string(false).get(out); }
|
||||
template<> simdjson_inline error_code document::get(double& out) & noexcept { return get_double().get(out); }
|
||||
template<> simdjson_inline error_code document::get(uint64_t& out) & noexcept { return get_uint64().get(out); }
|
||||
template<> simdjson_inline error_code document::get(int64_t& out) & noexcept { return get_int64().get(out); }
|
||||
template<> simdjson_inline error_code document::get(bool& out) & noexcept { return get_bool().get(out); }
|
||||
template<> simdjson_inline error_code document::get(value& out) & noexcept { return get_value().get(out); }
|
||||
|
||||
template<> simdjson_deprecated simdjson_inline simdjson_result<raw_json_string> document::get() && noexcept { return get_raw_json_string(); }
|
||||
template<> simdjson_deprecated simdjson_inline simdjson_result<std::string_view> document::get() && noexcept { return get_string(false); }
|
||||
@@ -245,7 +245,7 @@ simdjson_inline simdjson_result<value> document::operator[](const char *key) & n
|
||||
return start_or_resume_object()[key];
|
||||
}
|
||||
|
||||
simdjson_warn_unused simdjson_inline error_code document::consume() noexcept {
|
||||
simdjson_inline error_code document::consume() noexcept {
|
||||
bool scalar = false;
|
||||
auto error = is_scalar().get(scalar);
|
||||
if(error) { return error; }
|
||||
@@ -347,54 +347,6 @@ simdjson_inline simdjson_result<value> document::at_path(std::string_view json_p
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
|
||||
|
||||
template<constevalutil::fixed_string... FieldNames, typename T>
|
||||
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
|
||||
simdjson_warn_unused simdjson_inline error_code document::extract_into(T& out) & noexcept {
|
||||
// Helper to check if a field name matches any of the requested fields
|
||||
auto should_extract = [](std::string_view field_name) constexpr -> bool {
|
||||
return ((FieldNames.view() == field_name) || ...);
|
||||
};
|
||||
|
||||
// Iterate through all members of T using reflection
|
||||
template for (constexpr auto mem : std::define_static_array(
|
||||
std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
|
||||
|
||||
if constexpr (!std::meta::is_const(mem) && std::meta::is_public(mem)) {
|
||||
constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem));
|
||||
|
||||
// Only extract this field if it's in our list of requested fields
|
||||
if constexpr (should_extract(key)) {
|
||||
// Try to find and extract the field
|
||||
if constexpr (concepts::optional_type<decltype(out.[:mem:])>) {
|
||||
// For optional fields, it's ok if they're missing
|
||||
auto field_result = find_field_unordered(key);
|
||||
if (!field_result.error()) {
|
||||
auto error = field_result.get(out.[:mem:]);
|
||||
if (error && error != NO_SUCH_FIELD) {
|
||||
return error;
|
||||
}
|
||||
} else if (field_result.error() != NO_SUCH_FIELD) {
|
||||
return field_result.error();
|
||||
} else {
|
||||
out.[:mem:].reset();
|
||||
}
|
||||
} else {
|
||||
// For required fields (in the requested list), fail if missing
|
||||
SIMDJSON_TRY((*this)[key].get(out.[:mem:]));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
|
||||
|
||||
} // namespace ondemand
|
||||
} // namespace SIMDJSON_IMPLEMENTATION
|
||||
} // namespace simdjson
|
||||
@@ -502,7 +454,7 @@ simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLE
|
||||
return first.get_string(allow_replacement);
|
||||
}
|
||||
template <typename string_type>
|
||||
simdjson_warn_unused simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::get_string(string_type& receiver, bool allow_replacement) noexcept {
|
||||
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::get_string(string_type& receiver, bool allow_replacement) noexcept {
|
||||
if (error()) { return error(); }
|
||||
return first.get_string(receiver, allow_replacement);
|
||||
}
|
||||
@@ -538,12 +490,12 @@ simdjson_deprecated simdjson_inline simdjson_result<T> simdjson_result<SIMDJSON_
|
||||
return std::forward<SIMDJSON_IMPLEMENTATION::ondemand::document>(first).get<T>();
|
||||
}
|
||||
template<typename T>
|
||||
simdjson_warn_unused simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::get(T &out) & noexcept {
|
||||
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::get(T &out) & noexcept {
|
||||
if (error()) { return error(); }
|
||||
return first.get<T>(out);
|
||||
}
|
||||
template<typename T>
|
||||
simdjson_warn_unused simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::get(T &out) && noexcept {
|
||||
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::get(T &out) && noexcept {
|
||||
if (error()) { return error(); }
|
||||
return std::forward<SIMDJSON_IMPLEMENTATION::ondemand::document>(first).get<T>(out);
|
||||
}
|
||||
@@ -553,8 +505,8 @@ template<> simdjson_deprecated simdjson_inline simdjson_result<SIMDJSON_IMPLEMEN
|
||||
if (error()) { return error(); }
|
||||
return std::forward<SIMDJSON_IMPLEMENTATION::ondemand::document>(first);
|
||||
}
|
||||
template<> simdjson_warn_unused simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::get<SIMDJSON_IMPLEMENTATION::ondemand::document>(SIMDJSON_IMPLEMENTATION::ondemand::document &out) & noexcept = delete;
|
||||
template<> simdjson_warn_unused simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::get<SIMDJSON_IMPLEMENTATION::ondemand::document>(SIMDJSON_IMPLEMENTATION::ondemand::document &out) && noexcept {
|
||||
template<> simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::get<SIMDJSON_IMPLEMENTATION::ondemand::document>(SIMDJSON_IMPLEMENTATION::ondemand::document &out) & noexcept = delete;
|
||||
template<> simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::get<SIMDJSON_IMPLEMENTATION::ondemand::document>(SIMDJSON_IMPLEMENTATION::ondemand::document &out) && noexcept {
|
||||
if (error()) { return error(); }
|
||||
out = std::forward<SIMDJSON_IMPLEMENTATION::ondemand::document>(first);
|
||||
return SUCCESS;
|
||||
@@ -672,15 +624,6 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjs
|
||||
return first.at_path(json_path);
|
||||
}
|
||||
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
template<constevalutil::fixed_string... FieldNames, typename T>
|
||||
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
|
||||
simdjson_warn_unused simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::extract_into(T& out) & noexcept {
|
||||
if (error()) { return error(); }
|
||||
return first.extract_into<FieldNames...>(out);
|
||||
}
|
||||
#endif // SIMDJSON_STATIC_REFLECTION
|
||||
|
||||
} // namespace simdjson
|
||||
|
||||
|
||||
@@ -715,7 +658,7 @@ simdjson_inline simdjson_result<double> document_reference::get_double() noexcep
|
||||
simdjson_inline simdjson_result<double> document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double(false); }
|
||||
simdjson_inline simdjson_result<std::string_view> document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); }
|
||||
template <typename string_type>
|
||||
simdjson_warn_unused simdjson_inline error_code document_reference::get_string(string_type& receiver, bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(receiver, false, allow_replacement); }
|
||||
simdjson_inline error_code document_reference::get_string(string_type& receiver, bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(receiver, false, allow_replacement); }
|
||||
simdjson_inline simdjson_result<std::string_view> document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); }
|
||||
simdjson_inline simdjson_result<raw_json_string> document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); }
|
||||
simdjson_inline simdjson_result<bool> document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); }
|
||||
@@ -768,13 +711,7 @@ simdjson_inline simdjson_result<value> document_reference::at_pointer(std::strin
|
||||
simdjson_inline simdjson_result<value> document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); }
|
||||
simdjson_inline simdjson_result<std::string_view> document_reference::raw_json() noexcept { return doc->raw_json();}
|
||||
simdjson_inline document_reference::operator document&() const noexcept { return *doc; }
|
||||
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
|
||||
template<constevalutil::fixed_string... FieldNames, typename T>
|
||||
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
|
||||
simdjson_warn_unused simdjson_inline error_code document_reference::extract_into(T& out) & noexcept {
|
||||
return doc->extract_into<FieldNames...>(out);
|
||||
}
|
||||
#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
|
||||
|
||||
} // namespace ondemand
|
||||
} // namespace SIMDJSON_IMPLEMENTATION
|
||||
} // namespace simdjson
|
||||
@@ -871,7 +808,7 @@ simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLE
|
||||
return first.get_string(allow_replacement);
|
||||
}
|
||||
template <typename string_type>
|
||||
simdjson_warn_unused simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get_string(string_type& receiver, bool allow_replacement) noexcept {
|
||||
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get_string(string_type& receiver, bool allow_replacement) noexcept {
|
||||
if (error()) { return error(); }
|
||||
return first.get_string(receiver, allow_replacement);
|
||||
}
|
||||
@@ -906,12 +843,12 @@ simdjson_inline simdjson_result<T> simdjson_result<SIMDJSON_IMPLEMENTATION::onde
|
||||
return std::forward<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>(first).get<T>();
|
||||
}
|
||||
template <class T>
|
||||
simdjson_warn_unused simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get(T &out) & noexcept {
|
||||
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_warn_unused simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get(T &out) && noexcept {
|
||||
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);
|
||||
}
|
||||
@@ -928,13 +865,13 @@ simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::o
|
||||
return first.is_string();
|
||||
}
|
||||
template <>
|
||||
simdjson_warn_unused simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get(SIMDJSON_IMPLEMENTATION::ondemand::document_reference &out) & noexcept {
|
||||
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_warn_unused simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::get(SIMDJSON_IMPLEMENTATION::ondemand::document_reference &out) && noexcept {
|
||||
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;
|
||||
@@ -1022,14 +959,7 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjs
|
||||
}
|
||||
return first.at_path(json_path);
|
||||
}
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
template<constevalutil::fixed_string... FieldNames, typename T>
|
||||
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
|
||||
simdjson_warn_unused simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document_reference>::extract_into(T& out) & noexcept {
|
||||
if (error()) { return error(); }
|
||||
return first.extract_into<FieldNames...>(out);
|
||||
}
|
||||
#endif // SIMDJSON_STATIC_REFLECTION
|
||||
|
||||
} // namespace simdjson
|
||||
|
||||
#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H
|
||||
|
||||
@@ -117,7 +117,7 @@ public:
|
||||
* @returns INCORRECT_TYPE if the JSON value is not a string. Otherwise, we return SUCCESS.
|
||||
*/
|
||||
template <typename string_type>
|
||||
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
|
||||
simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
|
||||
/**
|
||||
* Cast this JSON value to a string.
|
||||
*
|
||||
@@ -228,7 +228,7 @@ public:
|
||||
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
|
||||
*/
|
||||
template<typename T>
|
||||
simdjson_warn_unused simdjson_inline error_code get(T &out) &
|
||||
simdjson_inline error_code get(T &out) &
|
||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
||||
noexcept(custom_deserializable<T, document> ? nothrow_custom_deserializable<T, document> : true)
|
||||
#else
|
||||
@@ -725,41 +725,11 @@ public:
|
||||
* the JSON document.
|
||||
*/
|
||||
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
|
||||
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
/**
|
||||
* Extract only specific fields from the JSON object into a struct.
|
||||
*
|
||||
* This allows selective deserialization of only the fields you need,
|
||||
* potentially improving performance by skipping unwanted fields.
|
||||
*
|
||||
* Example:
|
||||
* ```c++
|
||||
* struct Car {
|
||||
* std::string make;
|
||||
* std::string model;
|
||||
* int year;
|
||||
* double price;
|
||||
* };
|
||||
*
|
||||
* Car car;
|
||||
* doc.extract_into<"make", "model">(car);
|
||||
* // Only 'make' and 'model' fields are extracted from JSON
|
||||
* ```
|
||||
*
|
||||
* @tparam FieldNames Compile-time string literals specifying which fields to extract
|
||||
* @param out The output struct to populate with selected fields
|
||||
* @returns SUCCESS on success, or an error code if a required field is missing or has wrong type
|
||||
*/
|
||||
template<constevalutil::fixed_string... FieldNames, typename T>
|
||||
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
|
||||
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
|
||||
#endif // SIMDJSON_STATIC_REFLECTION
|
||||
protected:
|
||||
/**
|
||||
* Consumes the document.
|
||||
*/
|
||||
simdjson_warn_unused simdjson_inline error_code consume() noexcept;
|
||||
simdjson_inline error_code consume() noexcept;
|
||||
|
||||
simdjson_inline document(ondemand::json_iterator &&iter) noexcept;
|
||||
simdjson_inline const uint8_t *text(uint32_t idx) const noexcept;
|
||||
@@ -812,7 +782,7 @@ public:
|
||||
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
|
||||
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
|
||||
template <typename string_type>
|
||||
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
|
||||
simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
|
||||
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
|
||||
simdjson_inline simdjson_result<raw_json_string> get_raw_json_string() noexcept;
|
||||
simdjson_inline simdjson_result<bool> get_bool() noexcept;
|
||||
@@ -856,7 +826,7 @@ public:
|
||||
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
|
||||
*/
|
||||
template<typename T>
|
||||
simdjson_warn_unused simdjson_inline error_code get(T &out) &
|
||||
simdjson_inline error_code get(T &out) &
|
||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
||||
noexcept(custom_deserializable<T, document> ? nothrow_custom_deserializable<T, document_reference> : true)
|
||||
#else
|
||||
@@ -891,11 +861,6 @@ public:
|
||||
/** @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;
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
template<constevalutil::fixed_string... FieldNames, typename T>
|
||||
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
|
||||
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
|
||||
#endif // SIMDJSON_STATIC_REFLECTION
|
||||
simdjson_inline operator document&() const noexcept;
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
template <class T>
|
||||
@@ -964,7 +929,7 @@ public:
|
||||
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
|
||||
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
|
||||
template <typename string_type>
|
||||
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
|
||||
simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
|
||||
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::raw_json_string> get_raw_json_string() noexcept;
|
||||
simdjson_inline simdjson_result<bool> get_bool() noexcept;
|
||||
@@ -1019,11 +984,6 @@ public:
|
||||
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_path(std::string_view json_path) noexcept;
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
template<constevalutil::fixed_string... FieldNames, typename T>
|
||||
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
|
||||
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
|
||||
#endif // SIMDJSON_STATIC_REFLECTION
|
||||
};
|
||||
|
||||
|
||||
@@ -1050,7 +1010,7 @@ public:
|
||||
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
|
||||
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
|
||||
template <typename string_type>
|
||||
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
|
||||
simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
|
||||
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::raw_json_string> get_raw_json_string() noexcept;
|
||||
simdjson_inline simdjson_result<bool> get_bool() noexcept;
|
||||
@@ -1101,11 +1061,6 @@ public:
|
||||
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_path(std::string_view json_path) noexcept;
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
template<constevalutil::fixed_string... FieldNames, typename T>
|
||||
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
|
||||
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
|
||||
#endif // SIMDJSON_STATIC_REFLECTION
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -312,10 +312,7 @@ inline void document_stream::next_document() noexcept {
|
||||
// Always set depth=1 at the start of document
|
||||
doc.iter._depth = 1;
|
||||
// consume comma if comma separated is allowed
|
||||
if (allow_comma_separated) {
|
||||
error_code ignored = doc.iter.consume_character(',');
|
||||
static_cast<void>(ignored); // ignored on purpose
|
||||
}
|
||||
if (allow_comma_separated) { doc.iter.consume_character(','); }
|
||||
// Resets the string buffer at the beginning, thus invalidating the strings.
|
||||
doc.iter._string_buf_loc = parser->string_buf.get();
|
||||
doc.iter._root = doc.iter.position();
|
||||
|
||||
@@ -114,7 +114,7 @@ simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLE
|
||||
}
|
||||
|
||||
template<typename string_type>
|
||||
simdjson_warn_unused simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::field>::unescaped_key(string_type &receiver, bool allow_replacement) noexcept {
|
||||
simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::field>::unescaped_key(string_type &receiver, bool allow_replacement) noexcept {
|
||||
if (error()) { return error(); }
|
||||
return first.unescaped_key(receiver, allow_replacement);
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
|
||||
#include <charconv>
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
#include <meta>
|
||||
#include <memory>
|
||||
@@ -25,21 +26,30 @@ namespace simdjson {
|
||||
namespace SIMDJSON_IMPLEMENTATION {
|
||||
namespace builder {
|
||||
|
||||
// Concept that checks if a type is a container but not a string (because
|
||||
// strings handling must be handled differently)
|
||||
template <typename T>
|
||||
concept container_but_not_string =
|
||||
requires(T a) {
|
||||
{ a.size() } -> std::convertible_to<std::size_t>;
|
||||
{
|
||||
a[std::declval<std::size_t>()]
|
||||
}; // check if elements are accessible for the subscript operator
|
||||
} && !std::is_same_v<T, std::string> &&
|
||||
!std::is_same_v<T, std::string_view> && !std::is_same_v<T, const char *>;
|
||||
|
||||
template <class T>
|
||||
requires(concepts::container_but_not_string<T> && !require_custom_serialization<T>)
|
||||
requires(container_but_not_string<T>)
|
||||
constexpr void atom(string_builder &b, const T &t) {
|
||||
auto it = t.begin();
|
||||
auto end = t.end();
|
||||
if (it == end) {
|
||||
if (t.size() == 0) {
|
||||
b.append_raw("[]");
|
||||
return;
|
||||
}
|
||||
b.append('[');
|
||||
atom(b, *it);
|
||||
++it;
|
||||
for (; it != end; ++it) {
|
||||
atom(b, t[0]);
|
||||
for (size_t i = 1; i < t.size(); ++i) {
|
||||
b.append(',');
|
||||
atom(b, *it);
|
||||
atom(b, t[i]);
|
||||
}
|
||||
b.append(']');
|
||||
}
|
||||
@@ -54,7 +64,6 @@ constexpr void atom(string_builder &b, const T &t) {
|
||||
}
|
||||
|
||||
template <concepts::string_view_keyed_map T>
|
||||
requires(!require_custom_serialization<T>)
|
||||
constexpr void atom(string_builder &b, const T &m) {
|
||||
if (m.empty()) {
|
||||
b.append_raw("{}");
|
||||
@@ -82,8 +91,22 @@ constexpr void atom(string_builder &b, const number_type t) {
|
||||
b.append(t);
|
||||
}
|
||||
|
||||
// Support for std::chrono::duration - serialize as milliseconds
|
||||
template <typename Rep, typename Period>
|
||||
void atom(string_builder &b, const std::chrono::duration<Rep, Period> &duration) {
|
||||
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
|
||||
b.append(ms);
|
||||
}
|
||||
|
||||
// Support for std::chrono::time_point - serialize as milliseconds since epoch
|
||||
template <typename Clock, typename Duration>
|
||||
void atom(string_builder &b, const std::chrono::time_point<Clock, Duration> &time_point) {
|
||||
auto ms_since_epoch = std::chrono::duration_cast<std::chrono::milliseconds>(time_point.time_since_epoch()).count();
|
||||
b.append(ms_since_epoch);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
requires(std::is_class_v<T> && !concepts::container_but_not_string<T> &&
|
||||
requires(std::is_class_v<T> && !container_but_not_string<T> &&
|
||||
!concepts::string_view_keyed_map<T> &&
|
||||
!concepts::optional_type<T> &&
|
||||
!concepts::smart_pointer<T> &&
|
||||
@@ -91,7 +114,7 @@ template <class T>
|
||||
!std::is_same_v<T, std::string> &&
|
||||
!std::is_same_v<T, std::string_view> &&
|
||||
!std::is_same_v<T, const char*> &&
|
||||
!std::is_same_v<T, char> && !require_custom_serialization<T>)
|
||||
!std::is_same_v<T, char>)
|
||||
constexpr void atom(string_builder &b, const T &t) {
|
||||
int i = 0;
|
||||
b.append('{');
|
||||
@@ -109,7 +132,6 @@ constexpr void atom(string_builder &b, const T &t) {
|
||||
|
||||
// Support for optional types (std::optional, etc.)
|
||||
template <concepts::optional_type T>
|
||||
requires(!require_custom_serialization<T>)
|
||||
constexpr void atom(string_builder &b, const T &opt) {
|
||||
if (opt) {
|
||||
atom(b, opt.value());
|
||||
@@ -120,7 +142,6 @@ constexpr void atom(string_builder &b, const T &opt) {
|
||||
|
||||
// Support for smart pointers (std::unique_ptr, std::shared_ptr, etc.)
|
||||
template <concepts::smart_pointer T>
|
||||
requires(!require_custom_serialization<T>)
|
||||
constexpr void atom(string_builder &b, const T &ptr) {
|
||||
if (ptr) {
|
||||
atom(b, *ptr);
|
||||
@@ -131,7 +152,7 @@ constexpr void atom(string_builder &b, const T &ptr) {
|
||||
|
||||
// Support for enums - serialize as string representation using expand approach from P2996R12
|
||||
template <typename T>
|
||||
requires(std::is_enum_v<T> && !require_custom_serialization<T>)
|
||||
requires(std::is_enum_v<T>)
|
||||
void atom(string_builder &b, const T &e) {
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T));
|
||||
@@ -152,10 +173,10 @@ void atom(string_builder &b, const T &e) {
|
||||
|
||||
// Support for appendable containers that don't have operator[] (sets, etc.)
|
||||
template <concepts::appendable_containers T>
|
||||
requires(!concepts::container_but_not_string<T> && !concepts::string_view_keyed_map<T> &&
|
||||
requires(!container_but_not_string<T> && !concepts::string_view_keyed_map<T> &&
|
||||
!concepts::optional_type<T> && !concepts::smart_pointer<T> &&
|
||||
!std::is_same_v<T, std::string> &&
|
||||
!std::is_same_v<T, std::string_view> && !std::is_same_v<T, const char*> && !require_custom_serialization<T>)
|
||||
!std::is_same_v<T, std::string_view> && !std::is_same_v<T, const char*>)
|
||||
constexpr void atom(string_builder &b, const T &container) {
|
||||
if (container.empty()) {
|
||||
b.append_raw("[]");
|
||||
@@ -190,35 +211,32 @@ void append(string_builder &b, const T &t) {
|
||||
}
|
||||
|
||||
template <concepts::optional_type T>
|
||||
requires(!require_custom_serialization<T>)
|
||||
void append(string_builder &b, const T &t) {
|
||||
atom(b, t);
|
||||
}
|
||||
|
||||
template <concepts::smart_pointer T>
|
||||
requires(!require_custom_serialization<T>)
|
||||
void append(string_builder &b, const T &t) {
|
||||
atom(b, t);
|
||||
}
|
||||
|
||||
template <concepts::appendable_containers T>
|
||||
requires(!concepts::container_but_not_string<T> && !concepts::string_view_keyed_map<T> &&
|
||||
requires(!container_but_not_string<T> && !concepts::string_view_keyed_map<T> &&
|
||||
!concepts::optional_type<T> && !concepts::smart_pointer<T> &&
|
||||
!std::is_same_v<T, std::string> &&
|
||||
!std::is_same_v<T, std::string_view> && !std::is_same_v<T, const char*> && !require_custom_serialization<T>)
|
||||
!std::is_same_v<T, std::string_view> && !std::is_same_v<T, const char*>)
|
||||
void append(string_builder &b, const T &t) {
|
||||
atom(b, t);
|
||||
}
|
||||
|
||||
template <concepts::string_view_keyed_map T>
|
||||
requires(!require_custom_serialization<T>)
|
||||
void append(string_builder &b, const T &t) {
|
||||
atom(b, t);
|
||||
}
|
||||
|
||||
// works for struct
|
||||
template <class Z>
|
||||
requires(std::is_class_v<Z> && !concepts::container_but_not_string<Z> &&
|
||||
requires(std::is_class_v<Z> && !container_but_not_string<Z> &&
|
||||
!concepts::string_view_keyed_map<Z> &&
|
||||
!concepts::optional_type<Z> &&
|
||||
!concepts::smart_pointer<Z> &&
|
||||
@@ -226,7 +244,7 @@ template <class Z>
|
||||
!std::is_same_v<Z, std::string> &&
|
||||
!std::is_same_v<Z, std::string_view> &&
|
||||
!std::is_same_v<Z, const char*> &&
|
||||
!std::is_same_v<Z, char> && !require_custom_serialization<Z>)
|
||||
!std::is_same_v<Z, char>)
|
||||
void append(string_builder &b, const Z &z) {
|
||||
int i = 0;
|
||||
b.append('{');
|
||||
@@ -242,35 +260,25 @@ void append(string_builder &b, const Z &z) {
|
||||
b.append('}');
|
||||
}
|
||||
|
||||
// works for container that have begin() and end() iterators
|
||||
// works for container
|
||||
template <class Z>
|
||||
requires(concepts::container_but_not_string<Z> && !require_custom_serialization<Z>)
|
||||
requires(container_but_not_string<Z>)
|
||||
void append(string_builder &b, const Z &z) {
|
||||
auto it = z.begin();
|
||||
auto end = z.end();
|
||||
if (it == end) {
|
||||
if (z.size() == 0) {
|
||||
b.append_raw("[]");
|
||||
return;
|
||||
}
|
||||
b.append('[');
|
||||
atom(b, *it);
|
||||
++it;
|
||||
for (; it != end; ++it) {
|
||||
atom(b, z[0]);
|
||||
for (size_t i = 1; i < z.size(); ++i) {
|
||||
b.append(',');
|
||||
atom(b, *it);
|
||||
atom(b, z[i]);
|
||||
}
|
||||
b.append(']');
|
||||
}
|
||||
|
||||
template <class Z>
|
||||
requires (require_custom_serialization<Z>)
|
||||
void append(string_builder &b, const Z &z) {
|
||||
b.append(z);
|
||||
}
|
||||
|
||||
|
||||
template <class Z>
|
||||
simdjson_warn_unused simdjson_result<std::string> to_json_string(const Z &z, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
|
||||
simdjson_result<std::string> to_json_string(const Z &z, size_t initial_capacity = 1024) {
|
||||
string_builder b(initial_capacity);
|
||||
append(b, z);
|
||||
std::string_view s;
|
||||
@@ -279,8 +287,8 @@ simdjson_warn_unused simdjson_result<std::string> to_json_string(const Z &z, siz
|
||||
}
|
||||
|
||||
template <class Z>
|
||||
simdjson_warn_unused simdjson_error to_json(const Z &z, std::string &s, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
|
||||
string_builder b(initial_capacity);
|
||||
simdjson_error to_json(const Z &z, std::string &s) {
|
||||
string_builder b;
|
||||
append(b, z);
|
||||
std::string_view view;
|
||||
if(auto e = b.view().get(view); e) { return e; }
|
||||
@@ -293,88 +301,13 @@ string_builder& operator<<(string_builder& b, const Z& z) {
|
||||
append(b, z);
|
||||
return b;
|
||||
}
|
||||
|
||||
// extract_from: Serialize only specific fields from a struct to JSON
|
||||
template<constevalutil::fixed_string... FieldNames, typename T>
|
||||
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
|
||||
void extract_from(string_builder &b, const T &obj) {
|
||||
// Helper to check if a field name matches any of the requested fields
|
||||
auto should_extract = [](std::string_view field_name) constexpr -> bool {
|
||||
return ((FieldNames.view() == field_name) || ...);
|
||||
};
|
||||
|
||||
b.append('{');
|
||||
bool first = true;
|
||||
|
||||
// Iterate through all members of T using reflection
|
||||
template for (constexpr auto mem : std::define_static_array(
|
||||
std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
|
||||
|
||||
if constexpr (std::meta::is_public(mem)) {
|
||||
constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem));
|
||||
|
||||
// Only serialize this field if it's in our list of requested fields
|
||||
if constexpr (should_extract(key)) {
|
||||
if (!first) {
|
||||
b.append(',');
|
||||
}
|
||||
first = false;
|
||||
|
||||
// Serialize the key
|
||||
constexpr auto quoted_key = std::define_static_string(constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(mem)));
|
||||
b.append_raw(quoted_key);
|
||||
b.append(':');
|
||||
|
||||
// Serialize the value
|
||||
atom(b, obj.[:mem:]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
b.append('}');
|
||||
}
|
||||
|
||||
template<constevalutil::fixed_string... FieldNames, typename T>
|
||||
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
|
||||
simdjson_warn_unused simdjson_result<std::string> extract_from(const T &obj, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
|
||||
string_builder b(initial_capacity);
|
||||
extract_from<FieldNames...>(b, obj);
|
||||
std::string_view s;
|
||||
if(auto e = b.view().get(s); e) { return e; }
|
||||
return std::string(s);
|
||||
}
|
||||
|
||||
} // namespace builder
|
||||
} // namespace SIMDJSON_IMPLEMENTATION
|
||||
// Alias the function template to 'to' in the global namespace
|
||||
template <class Z>
|
||||
simdjson_warn_unused simdjson_result<std::string> to_json(const Z &z, size_t initial_capacity = SIMDJSON_IMPLEMENTATION::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
|
||||
SIMDJSON_IMPLEMENTATION::builder::string_builder b(initial_capacity);
|
||||
SIMDJSON_IMPLEMENTATION::builder::append(b, z);
|
||||
std::string_view s;
|
||||
if(auto e = b.view().get(s); e) { return e; }
|
||||
return std::string(s);
|
||||
simdjson_result<std::string> to_json(const Z &z, size_t initial_capacity = 1024) {
|
||||
return SIMDJSON_IMPLEMENTATION::builder::to_json_string(z, initial_capacity);
|
||||
}
|
||||
template <class Z>
|
||||
simdjson_warn_unused simdjson_error to_json(const Z &z, std::string &s, size_t initial_capacity = SIMDJSON_IMPLEMENTATION::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
|
||||
SIMDJSON_IMPLEMENTATION::builder::string_builder b(initial_capacity);
|
||||
SIMDJSON_IMPLEMENTATION::builder::append(b, z);
|
||||
std::string_view view;
|
||||
if(auto e = b.view().get(view); e) { return e; }
|
||||
s.assign(view);
|
||||
return SUCCESS;
|
||||
}
|
||||
// Global namespace function for extract_from
|
||||
template<constevalutil::fixed_string... FieldNames, typename T>
|
||||
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
|
||||
simdjson_warn_unused simdjson_result<std::string> extract_from(const T &obj, size_t initial_capacity = SIMDJSON_IMPLEMENTATION::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
|
||||
SIMDJSON_IMPLEMENTATION::builder::string_builder b(initial_capacity);
|
||||
SIMDJSON_IMPLEMENTATION::builder::extract_from<FieldNames...>(b, obj);
|
||||
std::string_view s;
|
||||
if(auto e = b.view().get(s); e) { return e; }
|
||||
return std::string(s);
|
||||
}
|
||||
|
||||
} // namespace simdjson
|
||||
|
||||
#endif // SIMDJSON_STATIC_REFLECTION
|
||||
|
||||
@@ -344,7 +344,7 @@ simdjson_inline uint8_t *&json_iterator::string_buf_loc() noexcept {
|
||||
return _string_buf_loc;
|
||||
}
|
||||
|
||||
simdjson_warn_unused simdjson_inline error_code json_iterator::report_error(error_code _error, const char *message) noexcept {
|
||||
simdjson_inline error_code json_iterator::report_error(error_code _error, const char *message) noexcept {
|
||||
SIMDJSON_ASSUME(_error != SUCCESS && _error != UNINITIALIZED && _error != INCORRECT_TYPE && _error != NO_SUCH_FIELD);
|
||||
logger::log_error(*this, message);
|
||||
error = _error;
|
||||
@@ -388,7 +388,7 @@ simdjson_inline void json_iterator::reenter_child(token_position position, depth
|
||||
_depth = child_depth;
|
||||
}
|
||||
|
||||
simdjson_warn_unused simdjson_inline error_code json_iterator::consume_character(char c) noexcept {
|
||||
simdjson_inline error_code json_iterator::consume_character(char c) noexcept {
|
||||
if (*peek() == c) {
|
||||
return_current_and_advance();
|
||||
return SUCCESS;
|
||||
@@ -411,7 +411,7 @@ simdjson_inline void json_iterator::set_start_position(depth_t depth, token_posi
|
||||
#endif
|
||||
|
||||
|
||||
simdjson_warn_unused simdjson_inline error_code json_iterator::optional_error(error_code _error, const char *message) noexcept {
|
||||
simdjson_inline error_code json_iterator::optional_error(error_code _error, const char *message) noexcept {
|
||||
SIMDJSON_ASSUME(_error == INCORRECT_TYPE || _error == NO_SUCH_FIELD);
|
||||
logger::log_error(*this, message);
|
||||
return _error;
|
||||
|
||||
@@ -238,14 +238,14 @@ public:
|
||||
* @param error The error to report. Must not be SUCCESS, UNINITIALIZED, INCORRECT_TYPE, or NO_SUCH_FIELD.
|
||||
* @param message An error message to report with the error.
|
||||
*/
|
||||
simdjson_warn_unused simdjson_inline error_code report_error(error_code error, const char *message) noexcept;
|
||||
simdjson_inline error_code report_error(error_code error, const char *message) noexcept;
|
||||
|
||||
/**
|
||||
* Log error, but don't stop iteration.
|
||||
* @param error The error to report. Must be INCORRECT_TYPE, or NO_SUCH_FIELD.
|
||||
* @param message An error message to report with the error.
|
||||
*/
|
||||
simdjson_warn_unused simdjson_inline error_code optional_error(error_code error, const char *message) noexcept;
|
||||
simdjson_inline error_code optional_error(error_code error, const char *message) noexcept;
|
||||
|
||||
/**
|
||||
* Take an input in json containing max_len characters and attempt to copy it over to tmpbuf, a buffer with
|
||||
@@ -265,7 +265,7 @@ public:
|
||||
|
||||
simdjson_inline void reenter_child(token_position position, depth_t child_depth) noexcept;
|
||||
|
||||
simdjson_warn_unused simdjson_inline error_code consume_character(char c) noexcept;
|
||||
simdjson_inline error_code consume_character(char c) noexcept;
|
||||
#if SIMDJSON_DEVELOPMENT_CHECKS
|
||||
simdjson_inline token_position start_position(depth_t depth) const noexcept;
|
||||
simdjson_inline void set_start_position(depth_t depth, token_position position) noexcept;
|
||||
|
||||
@@ -42,25 +42,24 @@ namespace simdjson {
|
||||
namespace SIMDJSON_IMPLEMENTATION {
|
||||
namespace builder {
|
||||
|
||||
static SIMDJSON_CONSTEXPR_LAMBDA std::array<uint8_t, 256>
|
||||
json_quotable_character = {
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
static SIMDJSON_CONSTEXPR_LAMBDA std::array<uint8_t, 256> json_quotable_character = {
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
|
||||
/**
|
||||
|
||||
A possible SWAR implementation of has_json_escapable_byte. It is not used
|
||||
because it is slower than the current implementation. It is kept here for
|
||||
reference (to show that we tried it).
|
||||
A possible SWAR implementation of has_json_escapable_byte. It is not used because
|
||||
it is slower than the current implementation. It is kept here for reference (to show
|
||||
that we tried it).
|
||||
|
||||
inline bool has_json_escapable_byte(uint64_t x) {
|
||||
uint64_t is_ascii = 0x8080808080808080ULL & ~x;
|
||||
@@ -77,7 +76,7 @@ SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline bool
|
||||
simple_needs_escaping(std::string_view v) {
|
||||
for (char c : v) {
|
||||
// a table lookup is faster than a series of comparisons
|
||||
if (json_quotable_character[static_cast<uint8_t>(c)]) {
|
||||
if(json_quotable_character[static_cast<uint8_t>(c)]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -118,8 +117,7 @@ simdjson_inline bool fast_needs_escaping(std::string_view view) {
|
||||
__m128i running = _mm_setzero_si128();
|
||||
for (; i + 15 < view.size(); i += 16) {
|
||||
|
||||
__m128i word =
|
||||
_mm_loadu_si128(reinterpret_cast<const __m128i *>(view.data() + i));
|
||||
__m128i word = _mm_loadu_si128(reinterpret_cast<const __m128i *>(view.data() + i));
|
||||
running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(34)));
|
||||
running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(92)));
|
||||
running = _mm_or_si128(
|
||||
@@ -127,8 +125,8 @@ simdjson_inline bool fast_needs_escaping(std::string_view view) {
|
||||
_mm_setzero_si128()));
|
||||
}
|
||||
if (i < view.size()) {
|
||||
__m128i word = _mm_loadu_si128(
|
||||
reinterpret_cast<const __m128i *>(view.data() + view.length() - 16));
|
||||
__m128i word =
|
||||
_mm_loadu_si128(reinterpret_cast<const __m128i *>(view.data() + view.length() - 16));
|
||||
running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(34)));
|
||||
running = _mm_or_si128(running, _mm_cmpeq_epi8(word, _mm_set1_epi8(92)));
|
||||
running = _mm_or_si128(
|
||||
@@ -143,6 +141,7 @@ simdjson_inline bool fast_needs_escaping(std::string_view view) {
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
SIMDJSON_CONSTEXPR_LAMBDA inline size_t
|
||||
find_next_json_quotable_character(const std::string_view view,
|
||||
size_t location) noexcept {
|
||||
@@ -157,15 +156,15 @@ find_next_json_quotable_character(const std::string_view view,
|
||||
|
||||
SIMDJSON_CONSTEXPR_LAMBDA static std::string_view control_chars[] = {
|
||||
"\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006",
|
||||
"\\u0007", "\\b", "\\t", "\\n", "\\u000b", "\\f", "\\r",
|
||||
"\\u0007", "\\b", "\\t", "\\n", "\\u000b", "\\f", "\\r",
|
||||
"\\u000e", "\\u000f", "\\u0010", "\\u0011", "\\u0012", "\\u0013", "\\u0014",
|
||||
"\\u0015", "\\u0016", "\\u0017", "\\u0018", "\\u0019", "\\u001a", "\\u001b",
|
||||
"\\u001c", "\\u001d", "\\u001e", "\\u001f"};
|
||||
|
||||
// All Unicode characters may be placed within the quotation marks, except for
|
||||
// the characters that MUST be escaped: quotation mark, reverse solidus, and the
|
||||
// control characters (U+0000 through U+001F). There are two-character sequence
|
||||
// escape representations of some popular characters:
|
||||
// All Unicode characters may be placed within the quotation marks, except for the
|
||||
// characters that MUST be escaped: quotation mark, reverse solidus, and the control
|
||||
// characters (U+0000 through U+001F).
|
||||
// There are two-character sequence escape representations of some popular characters:
|
||||
// \", \\, \b, \f, \n, \r, \t.
|
||||
SIMDJSON_CONSTEXPR_LAMBDA void escape_json_char(char c, char *&out) {
|
||||
if (c == '"') {
|
||||
@@ -285,11 +284,10 @@ simdjson_inline void string_builder::clear() noexcept {
|
||||
|
||||
namespace internal {
|
||||
|
||||
|
||||
template <typename number_type, typename = typename std::enable_if<
|
||||
std::is_unsigned<number_type>::value>::type>
|
||||
simdjson_really_inline int int_log2(number_type x) {
|
||||
return 63 - leading_zeroes(uint64_t(x) | 1);
|
||||
}
|
||||
simdjson_really_inline int int_log2(number_type x) { return 63 - leading_zeroes(uint64_t(x) | 1); }
|
||||
|
||||
simdjson_really_inline int fast_digit_count_32(uint32_t x) {
|
||||
static uint64_t table[] = {
|
||||
@@ -303,6 +301,7 @@ simdjson_really_inline int fast_digit_count_32(uint32_t x) {
|
||||
return uint32_t((x + table[int_log2(x)]) >> 32);
|
||||
}
|
||||
|
||||
|
||||
simdjson_really_inline int fast_digit_count_64(uint64_t x) {
|
||||
static uint64_t table[] = {9,
|
||||
99,
|
||||
@@ -336,29 +335,28 @@ simdjson_really_inline size_t digit_count(number_type v) noexcept {
|
||||
"We only support 8-bit, 16-bit, 32-bit and 64-bit numbers");
|
||||
SIMDJSON_IF_CONSTEXPR(sizeof(number_type) <= 4) {
|
||||
return fast_digit_count_32(static_cast<uint32_t>(v));
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return fast_digit_count_64(static_cast<uint64_t>(v));
|
||||
}
|
||||
}
|
||||
static const char decimal_table[200] = {
|
||||
0x30, 0x30, 0x30, 0x31, 0x30, 0x32, 0x30, 0x33, 0x30, 0x34, 0x30, 0x35,
|
||||
0x30, 0x36, 0x30, 0x37, 0x30, 0x38, 0x30, 0x39, 0x31, 0x30, 0x31, 0x31,
|
||||
0x31, 0x32, 0x31, 0x33, 0x31, 0x34, 0x31, 0x35, 0x31, 0x36, 0x31, 0x37,
|
||||
0x31, 0x38, 0x31, 0x39, 0x32, 0x30, 0x32, 0x31, 0x32, 0x32, 0x32, 0x33,
|
||||
0x32, 0x34, 0x32, 0x35, 0x32, 0x36, 0x32, 0x37, 0x32, 0x38, 0x32, 0x39,
|
||||
0x33, 0x30, 0x33, 0x31, 0x33, 0x32, 0x33, 0x33, 0x33, 0x34, 0x33, 0x35,
|
||||
0x33, 0x36, 0x33, 0x37, 0x33, 0x38, 0x33, 0x39, 0x34, 0x30, 0x34, 0x31,
|
||||
0x34, 0x32, 0x34, 0x33, 0x34, 0x34, 0x34, 0x35, 0x34, 0x36, 0x34, 0x37,
|
||||
0x34, 0x38, 0x34, 0x39, 0x35, 0x30, 0x35, 0x31, 0x35, 0x32, 0x35, 0x33,
|
||||
0x35, 0x34, 0x35, 0x35, 0x35, 0x36, 0x35, 0x37, 0x35, 0x38, 0x35, 0x39,
|
||||
0x36, 0x30, 0x36, 0x31, 0x36, 0x32, 0x36, 0x33, 0x36, 0x34, 0x36, 0x35,
|
||||
0x36, 0x36, 0x36, 0x37, 0x36, 0x38, 0x36, 0x39, 0x37, 0x30, 0x37, 0x31,
|
||||
0x37, 0x32, 0x37, 0x33, 0x37, 0x34, 0x37, 0x35, 0x37, 0x36, 0x37, 0x37,
|
||||
0x37, 0x38, 0x37, 0x39, 0x38, 0x30, 0x38, 0x31, 0x38, 0x32, 0x38, 0x33,
|
||||
0x38, 0x34, 0x38, 0x35, 0x38, 0x36, 0x38, 0x37, 0x38, 0x38, 0x38, 0x39,
|
||||
0x39, 0x30, 0x39, 0x31, 0x39, 0x32, 0x39, 0x33, 0x39, 0x34, 0x39, 0x35,
|
||||
0x39, 0x36, 0x39, 0x37, 0x39, 0x38, 0x39, 0x39,
|
||||
0x30, 0x30, 0x30, 0x31, 0x30, 0x32, 0x30, 0x33, 0x30, 0x34, 0x30, 0x35,
|
||||
0x30, 0x36, 0x30, 0x37, 0x30, 0x38, 0x30, 0x39, 0x31, 0x30, 0x31, 0x31,
|
||||
0x31, 0x32, 0x31, 0x33, 0x31, 0x34, 0x31, 0x35, 0x31, 0x36, 0x31, 0x37,
|
||||
0x31, 0x38, 0x31, 0x39, 0x32, 0x30, 0x32, 0x31, 0x32, 0x32, 0x32, 0x33,
|
||||
0x32, 0x34, 0x32, 0x35, 0x32, 0x36, 0x32, 0x37, 0x32, 0x38, 0x32, 0x39,
|
||||
0x33, 0x30, 0x33, 0x31, 0x33, 0x32, 0x33, 0x33, 0x33, 0x34, 0x33, 0x35,
|
||||
0x33, 0x36, 0x33, 0x37, 0x33, 0x38, 0x33, 0x39, 0x34, 0x30, 0x34, 0x31,
|
||||
0x34, 0x32, 0x34, 0x33, 0x34, 0x34, 0x34, 0x35, 0x34, 0x36, 0x34, 0x37,
|
||||
0x34, 0x38, 0x34, 0x39, 0x35, 0x30, 0x35, 0x31, 0x35, 0x32, 0x35, 0x33,
|
||||
0x35, 0x34, 0x35, 0x35, 0x35, 0x36, 0x35, 0x37, 0x35, 0x38, 0x35, 0x39,
|
||||
0x36, 0x30, 0x36, 0x31, 0x36, 0x32, 0x36, 0x33, 0x36, 0x34, 0x36, 0x35,
|
||||
0x36, 0x36, 0x36, 0x37, 0x36, 0x38, 0x36, 0x39, 0x37, 0x30, 0x37, 0x31,
|
||||
0x37, 0x32, 0x37, 0x33, 0x37, 0x34, 0x37, 0x35, 0x37, 0x36, 0x37, 0x37,
|
||||
0x37, 0x38, 0x37, 0x39, 0x38, 0x30, 0x38, 0x31, 0x38, 0x32, 0x38, 0x33,
|
||||
0x38, 0x34, 0x38, 0x35, 0x38, 0x36, 0x38, 0x37, 0x38, 0x38, 0x38, 0x39,
|
||||
0x39, 0x30, 0x39, 0x31, 0x39, 0x32, 0x39, 0x33, 0x39, 0x34, 0x39, 0x35,
|
||||
0x39, 0x36, 0x39, 0x37, 0x39, 0x38, 0x39, 0x39,
|
||||
};
|
||||
} // namespace internal
|
||||
|
||||
@@ -394,7 +392,7 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
|
||||
size_t dc = internal::digit_count(pv);
|
||||
char *write_pointer = buffer.get() + position + dc - 1;
|
||||
while (pv >= 100) {
|
||||
memcpy(write_pointer - 1, &internal::decimal_table[(pv % 100) * 2], 2);
|
||||
memcpy(write_pointer - 1, &internal::decimal_table[(pv % 100)*2], 2);
|
||||
write_pointer -= 2;
|
||||
pv /= 100;
|
||||
}
|
||||
@@ -421,7 +419,7 @@ simdjson_inline void string_builder::append(number_type v) noexcept {
|
||||
}
|
||||
char *write_pointer = buffer.get() + position + dc - 1;
|
||||
while (pv >= 100) {
|
||||
memcpy(write_pointer - 1, &internal::decimal_table[(pv % 100) * 2], 2);
|
||||
memcpy(write_pointer - 1, &internal::decimal_table[(pv % 100)*2], 2);
|
||||
write_pointer -= 2;
|
||||
pv /= 100;
|
||||
}
|
||||
@@ -473,17 +471,10 @@ string_builder::escape_and_append_with_quotes(char input) noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
simdjson_inline void
|
||||
string_builder::escape_and_append_with_quotes(const char *input) noexcept {
|
||||
simdjson_inline void string_builder::escape_and_append_with_quotes(const char* input) noexcept {
|
||||
std::string_view cinput(input);
|
||||
escape_and_append_with_quotes(cinput);
|
||||
}
|
||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
||||
template <constevalutil::fixed_string key>
|
||||
simdjson_inline void string_builder::escape_and_append_with_quotes() noexcept {
|
||||
escape_and_append_with_quotes(constevalutil::string_constant<key>::value);
|
||||
}
|
||||
#endif
|
||||
|
||||
simdjson_inline void string_builder::append_raw(const char *c) noexcept {
|
||||
size_t len = std::strlen(c);
|
||||
@@ -508,7 +499,6 @@ simdjson_inline void string_builder::append_raw(const char *str,
|
||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
||||
// Support for optional types (std::optional, etc.)
|
||||
template <concepts::optional_type T>
|
||||
requires(!require_custom_serialization<T>)
|
||||
simdjson_inline void string_builder::append(const T &opt) {
|
||||
if (opt) {
|
||||
append(*opt);
|
||||
@@ -516,25 +506,18 @@ simdjson_inline void string_builder::append(const T &opt) {
|
||||
append_null();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
requires(require_custom_serialization<T>)
|
||||
simdjson_inline void string_builder::append(const T &val) {
|
||||
serialize(*this, val);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
requires(std::is_convertible<T, std::string_view>::value ||
|
||||
std::is_same<T, const char *>::value)
|
||||
requires(std::is_convertible<T, std::string_view>::value ||
|
||||
std::is_same<T, const char*>::value )
|
||||
simdjson_inline void string_builder::append(const T &value) {
|
||||
escape_and_append_with_quotes(value);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SIMDJSON_SUPPORTS_RANGES && SIMDJSON_SUPPORTS_CONCEPTS
|
||||
// Support for range-based appending (std::ranges::view, etc.)
|
||||
// Support for range-based appending (std::ranges::view, etc.)
|
||||
template <std::ranges::range R>
|
||||
requires(!std::is_convertible<R, std::string_view>::value)
|
||||
requires (!std::is_convertible<R, std::string_view>::value)
|
||||
simdjson_inline void string_builder::append(const R &range) noexcept {
|
||||
auto it = std::ranges::begin(range);
|
||||
auto end = std::ranges::end(range);
|
||||
@@ -551,8 +534,8 @@ simdjson_inline void string_builder::append(const R &range) noexcept {
|
||||
|
||||
// Append remaining items with preceding commas
|
||||
for (; it != end; ++it) {
|
||||
append_comma();
|
||||
append_key_value(it->first, it->second);
|
||||
append_comma();
|
||||
append_key_value(it->first, it->second);
|
||||
}
|
||||
end_object();
|
||||
} else {
|
||||
@@ -568,10 +551,11 @@ simdjson_inline void string_builder::append(const R &range) noexcept {
|
||||
|
||||
// Append remaining items with preceding commas
|
||||
for (; it != end; ++it) {
|
||||
append_comma();
|
||||
append(*it);
|
||||
append_comma();
|
||||
append(*it);
|
||||
}
|
||||
end_array();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -579,7 +563,7 @@ simdjson_inline void string_builder::append(const R &range) noexcept {
|
||||
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
simdjson_inline string_builder::operator std::string() const noexcept(false) {
|
||||
return std::string(operator std::string_view());
|
||||
return std::string(std::string_view());
|
||||
}
|
||||
|
||||
simdjson_inline string_builder::operator std::string_view() const
|
||||
@@ -608,95 +592,70 @@ simdjson_inline bool string_builder::validate_unicode() const noexcept {
|
||||
return simdjson::validate_utf8(buffer.get(), position);
|
||||
}
|
||||
|
||||
simdjson_inline void string_builder::start_object() noexcept {
|
||||
simdjson_inline void string_builder::start_object() noexcept {
|
||||
if (capacity_check(1)) {
|
||||
buffer.get()[position++] = '{';
|
||||
}
|
||||
}
|
||||
|
||||
simdjson_inline void string_builder::end_object() noexcept {
|
||||
|
||||
simdjson_inline void string_builder::end_object() noexcept {
|
||||
if (capacity_check(1)) {
|
||||
buffer.get()[position++] = '}';
|
||||
}
|
||||
}
|
||||
|
||||
simdjson_inline void string_builder::start_array() noexcept {
|
||||
|
||||
simdjson_inline void string_builder::start_array() noexcept {
|
||||
if (capacity_check(1)) {
|
||||
buffer.get()[position++] = '[';
|
||||
}
|
||||
}
|
||||
|
||||
simdjson_inline void string_builder::end_array() noexcept {
|
||||
|
||||
simdjson_inline void string_builder::end_array() noexcept {
|
||||
if (capacity_check(1)) {
|
||||
buffer.get()[position++] = ']';
|
||||
}
|
||||
}
|
||||
|
||||
simdjson_inline void string_builder::append_comma() noexcept {
|
||||
|
||||
simdjson_inline void string_builder::append_comma() noexcept {
|
||||
if (capacity_check(1)) {
|
||||
buffer.get()[position++] = ',';
|
||||
}
|
||||
}
|
||||
|
||||
simdjson_inline void string_builder::append_colon() noexcept {
|
||||
|
||||
simdjson_inline void string_builder::append_colon() noexcept {
|
||||
if (capacity_check(1)) {
|
||||
buffer.get()[position++] = ':';
|
||||
}
|
||||
}
|
||||
|
||||
template <typename key_type, typename value_type>
|
||||
simdjson_inline void
|
||||
string_builder::append_key_value(key_type key, value_type value) noexcept {
|
||||
static_assert(std::is_same<key_type, const char *>::value ||
|
||||
std::is_convertible<key_type, std::string_view>::value,
|
||||
"Unsupported key type");
|
||||
template<typename key_type, typename value_type>
|
||||
simdjson_inline void string_builder::append_key_value(key_type key, value_type value) noexcept {
|
||||
static_assert(
|
||||
std::is_same<key_type, const char*>::value ||
|
||||
std::is_convertible<key_type, std::string_view>::value,
|
||||
"Unsupported key type");
|
||||
escape_and_append_with_quotes(key);
|
||||
append_colon();
|
||||
SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, std::nullptr_t>::value) {
|
||||
append_null();
|
||||
}
|
||||
else SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, char>::value) {
|
||||
} else SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, char>::value) {
|
||||
escape_and_append_with_quotes(value);
|
||||
}
|
||||
else SIMDJSON_IF_CONSTEXPR(
|
||||
std::is_convertible<value_type, std::string_view>::value) {
|
||||
} else SIMDJSON_IF_CONSTEXPR(std::is_convertible<value_type, std::string_view>::value) {
|
||||
escape_and_append_with_quotes(value);
|
||||
}
|
||||
else SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, const char *>::value) {
|
||||
} else SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, const char*>::value) {
|
||||
escape_and_append_with_quotes(value);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
append(value);
|
||||
}
|
||||
}
|
||||
|
||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
||||
template <constevalutil::fixed_string key, typename value_type>
|
||||
simdjson_inline void
|
||||
string_builder::append_key_value(value_type value) noexcept {
|
||||
escape_and_append_with_quotes<key>();
|
||||
append_colon();
|
||||
SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, std::nullptr_t>::value) {
|
||||
append_null();
|
||||
}
|
||||
else SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, char>::value) {
|
||||
escape_and_append_with_quotes(value);
|
||||
}
|
||||
else SIMDJSON_IF_CONSTEXPR(
|
||||
std::is_convertible<value_type, std::string_view>::value) {
|
||||
escape_and_append_with_quotes(value);
|
||||
}
|
||||
else SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, const char *>::value) {
|
||||
escape_and_append_with_quotes(value);
|
||||
}
|
||||
else {
|
||||
append(value);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace builder
|
||||
} // namespace SIMDJSON_IMPLEMENTATION
|
||||
} // namespace simdjson
|
||||
|
||||
#endif // SIMDJSON_GENERIC_STRING_BUILDER_INL_H
|
||||
#endif // SIMDJSON_GENERIC_STRING_BUILDER_INL_H
|
||||
@@ -10,40 +10,9 @@
|
||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||
|
||||
namespace simdjson {
|
||||
|
||||
|
||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
||||
|
||||
namespace SIMDJSON_IMPLEMENTATION {
|
||||
namespace builder {
|
||||
class string_builder;
|
||||
}}
|
||||
|
||||
template <typename T, typename = void>
|
||||
struct has_custom_serialization : std::false_type {};
|
||||
|
||||
inline constexpr struct serialize_tag {
|
||||
template <typename T>
|
||||
requires custom_deserializable<T>
|
||||
constexpr void operator()(SIMDJSON_IMPLEMENTATION::builder::string_builder& b, T& obj) const{
|
||||
return tag_invoke(*this, b, obj);
|
||||
}
|
||||
|
||||
|
||||
} serialize{};
|
||||
template <typename T>
|
||||
struct has_custom_serialization<T, std::void_t<
|
||||
decltype(tag_invoke(serialize, std::declval<SIMDJSON_IMPLEMENTATION::builder::string_builder&>(), std::declval<T&>()))
|
||||
>> : std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
constexpr bool require_custom_serialization = has_custom_serialization<T>::value;
|
||||
#else
|
||||
struct has_custom_serialization : std::false_type {};
|
||||
#endif // SIMDJSON_SUPPORTS_CONCEPTS
|
||||
|
||||
namespace SIMDJSON_IMPLEMENTATION {
|
||||
namespace builder {
|
||||
/**
|
||||
* A builder for JSON strings representing documents. This is a low-level
|
||||
* builder that is not meant to be used directly by end-users. Though it
|
||||
@@ -55,9 +24,7 @@ namespace builder {
|
||||
*/
|
||||
class string_builder {
|
||||
public:
|
||||
simdjson_inline string_builder(size_t initial_capacity = DEFAULT_INITIAL_CAPACITY);
|
||||
|
||||
static constexpr size_t DEFAULT_INITIAL_CAPACITY = 1024;
|
||||
simdjson_inline string_builder(size_t initial_capacity = 1024);
|
||||
|
||||
/**
|
||||
* Append number (includes Booleans). Booleans are mapped to the strings
|
||||
@@ -66,7 +33,7 @@ public:
|
||||
* represents the number.
|
||||
*/
|
||||
template<typename number_type,
|
||||
typename = typename std::enable_if<std::is_arithmetic<number_type>::value>::type>
|
||||
typename = typename std::enable_if<std::is_arithmetic<number_type>::value>::type>
|
||||
simdjson_inline void append(number_type v) noexcept;
|
||||
|
||||
/**
|
||||
@@ -95,10 +62,7 @@ public:
|
||||
* There is no UTF-8 validation.
|
||||
*/
|
||||
simdjson_inline void escape_and_append_with_quotes(std::string_view input) noexcept;
|
||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
||||
template<constevalutil::fixed_string key>
|
||||
simdjson_inline void escape_and_append_with_quotes() noexcept;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Append the character surrounded by double quotes, after escaping it.
|
||||
* There is no UTF-8 validation.
|
||||
@@ -152,21 +116,13 @@ public:
|
||||
* The key is escaped and surrounded by double quotes.
|
||||
* The value is escaped if it is a string.
|
||||
*/
|
||||
template<typename key_type, typename value_type>
|
||||
template<typename key_type, typename value_type>
|
||||
simdjson_inline void append_key_value(key_type key, value_type value) noexcept;
|
||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
||||
template<constevalutil::fixed_string key, typename value_type>
|
||||
simdjson_inline void append_key_value(value_type value) noexcept;
|
||||
|
||||
// Support for optional types (std::optional, etc.)
|
||||
template <concepts::optional_type T>
|
||||
requires(!require_custom_serialization<T>)
|
||||
simdjson_inline void append(const T &opt);
|
||||
|
||||
template <typename T>
|
||||
requires(require_custom_serialization<T>)
|
||||
simdjson_inline void append(const T &val);
|
||||
|
||||
// Support for string-like types
|
||||
template <typename T>
|
||||
requires(std::is_convertible<T, std::string_view>::value ||
|
||||
@@ -276,7 +232,7 @@ private:
|
||||
#if !SIMDJSON_STATIC_REFLECTION
|
||||
// fallback implementation until we have static reflection
|
||||
template <class Z>
|
||||
simdjson_warn_unused simdjson_result<std::string> to_json(const Z &z, size_t initial_capacity = simdjson::SIMDJSON_IMPLEMENTATION::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
|
||||
simdjson_result<std::string> to_json(const Z &z, size_t initial_capacity = 1024) {
|
||||
simdjson::SIMDJSON_IMPLEMENTATION::builder::string_builder b(initial_capacity);
|
||||
b.append(z);
|
||||
std::string_view s;
|
||||
@@ -284,20 +240,8 @@ simdjson_warn_unused simdjson_result<std::string> to_json(const Z &z, size_t ini
|
||||
if(e) { return e; }
|
||||
return std::string(s);
|
||||
}
|
||||
template <class Z>
|
||||
simdjson_warn_unused simdjson_error to_json(const Z &z, std::string &s, size_t initial_capacity = simdjson::SIMDJSON_IMPLEMENTATION::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
|
||||
simdjson::SIMDJSON_IMPLEMENTATION::builder::string_builder b(initial_capacity);
|
||||
b.append(z);
|
||||
std::string_view sv;
|
||||
auto e = b.view().get(sv);
|
||||
if(e) { return e; }
|
||||
s.assign(sv.data(), sv.size());
|
||||
return simdjson::SUCCESS;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
||||
#endif // SIMDJSON_SUPPORTS_CONCEPTS
|
||||
|
||||
} // namespace simdjson
|
||||
|
||||
|
||||
@@ -9,10 +9,6 @@
|
||||
#include "simdjson/generic/ondemand/raw_json_string.h"
|
||||
#include "simdjson/generic/ondemand/json_iterator.h"
|
||||
#include "simdjson/generic/ondemand/value-inl.h"
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
#include "simdjson/generic/ondemand/json_string_builder.h" // for constevalutil::fixed_string
|
||||
#include <meta>
|
||||
#endif
|
||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||
|
||||
namespace simdjson {
|
||||
@@ -70,7 +66,7 @@ simdjson_inline simdjson_result<object> object::start_root(value_iterator &iter)
|
||||
SIMDJSON_TRY( iter.start_root_object().error() );
|
||||
return object(iter);
|
||||
}
|
||||
simdjson_warn_unused simdjson_inline error_code object::consume() noexcept {
|
||||
simdjson_inline error_code object::consume() noexcept {
|
||||
if(iter.is_at_key()) {
|
||||
/**
|
||||
* whenever you are pointing at a key, calling skip_child() is
|
||||
@@ -199,52 +195,6 @@ simdjson_inline simdjson_result<bool> object::reset() & noexcept {
|
||||
return iter.reset_object();
|
||||
}
|
||||
|
||||
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
|
||||
|
||||
template<constevalutil::fixed_string... FieldNames, typename T>
|
||||
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
|
||||
simdjson_warn_unused simdjson_inline error_code object::extract_into(T& out) & noexcept {
|
||||
// Helper to check if a field name matches any of the requested fields
|
||||
auto should_extract = [](std::string_view field_name) constexpr -> bool {
|
||||
return ((FieldNames.view() == field_name) || ...);
|
||||
};
|
||||
|
||||
// Iterate through all members of T using reflection
|
||||
template for (constexpr auto mem : std::define_static_array(
|
||||
std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
|
||||
|
||||
if constexpr (!std::meta::is_const(mem) && std::meta::is_public(mem)) {
|
||||
constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem));
|
||||
|
||||
// Only extract this field if it's in our list of requested fields
|
||||
if constexpr (should_extract(key)) {
|
||||
// Try to find and extract the field
|
||||
if constexpr (concepts::optional_type<decltype(out.[:mem:])>) {
|
||||
// For optional fields, it's ok if they're missing
|
||||
auto field_result = find_field_unordered(key);
|
||||
if (!field_result.error()) {
|
||||
auto error = field_result.get(out.[:mem:]);
|
||||
if (error && error != NO_SUCH_FIELD) {
|
||||
return error;
|
||||
}
|
||||
} else if (field_result.error() != NO_SUCH_FIELD) {
|
||||
return field_result.error();
|
||||
} else {
|
||||
out.[:mem:].reset();
|
||||
}
|
||||
} else {
|
||||
// For required fields (in the requested list), fail if missing
|
||||
SIMDJSON_TRY((*this)[key].get(out.[:mem:]));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
|
||||
|
||||
} // namespace ondemand
|
||||
} // namespace SIMDJSON_IMPLEMENTATION
|
||||
} // namespace simdjson
|
||||
|
||||
@@ -5,9 +5,6 @@
|
||||
#include "simdjson/generic/ondemand/base.h"
|
||||
#include "simdjson/generic/implementation_simdjson_result_base.h"
|
||||
#include "simdjson/generic/ondemand/value_iterator.h"
|
||||
#if SIMDJSON_STATIC_REFLECTION && SIMDJSON_SUPPORTS_CONCEPTS
|
||||
#include "simdjson/generic/ondemand/json_string_builder.h" // for constevalutil::fixed_string
|
||||
#endif
|
||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||
|
||||
namespace simdjson {
|
||||
@@ -214,7 +211,7 @@ public:
|
||||
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
|
||||
*/
|
||||
template <typename T>
|
||||
simdjson_warn_unused simdjson_inline error_code get(T &out)
|
||||
simdjson_inline error_code get(T &out)
|
||||
noexcept(custom_deserializable<T, object> ? nothrow_custom_deserializable<T, object> : true) {
|
||||
static_assert(custom_deserializable<T, object>);
|
||||
return deserialize(*this, out);
|
||||
@@ -234,42 +231,12 @@ public:
|
||||
SIMDJSON_TRY(get<T>(out));
|
||||
return out;
|
||||
}
|
||||
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
/**
|
||||
* Extract only specific fields from the JSON object into a struct.
|
||||
*
|
||||
* This allows selective deserialization of only the fields you need,
|
||||
* potentially improving performance by skipping unwanted fields.
|
||||
*
|
||||
* Example:
|
||||
* ```c++
|
||||
* struct Car {
|
||||
* std::string make;
|
||||
* std::string model;
|
||||
* int year;
|
||||
* double price;
|
||||
* };
|
||||
*
|
||||
* Car car;
|
||||
* object.extract_into<"make", "model">(car);
|
||||
* // Only 'make' and 'model' fields are extracted from JSON
|
||||
* ```
|
||||
*
|
||||
* @tparam FieldNames Compile-time string literals specifying which fields to extract
|
||||
* @param out The output struct to populate with selected fields
|
||||
* @returns SUCCESS on success, or an error code if a required field is missing or has wrong type
|
||||
*/
|
||||
template<constevalutil::fixed_string... FieldNames, typename T>
|
||||
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
|
||||
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
|
||||
#endif // SIMDJSON_STATIC_REFLECTION
|
||||
#endif // SIMDJSON_SUPPORTS_CONCEPTS
|
||||
protected:
|
||||
/**
|
||||
* Go to the end of the object, no matter where you are right now.
|
||||
*/
|
||||
simdjson_warn_unused simdjson_inline error_code consume() noexcept;
|
||||
simdjson_inline error_code consume() noexcept;
|
||||
static simdjson_inline simdjson_result<object> start(value_iterator &iter) noexcept;
|
||||
static simdjson_inline simdjson_result<object> start_root(value_iterator &iter) noexcept;
|
||||
static simdjson_inline simdjson_result<object> started(value_iterator &iter) noexcept;
|
||||
@@ -324,7 +291,7 @@ public:
|
||||
return first.get<T>();
|
||||
}
|
||||
template<typename T>
|
||||
simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept {
|
||||
simdjson_inline error_code get(T& out) noexcept {
|
||||
if (error()) { return error(); }
|
||||
if constexpr (std::is_same_v<T, SIMDJSON_IMPLEMENTATION::ondemand::object>) {
|
||||
out = first;
|
||||
@@ -333,16 +300,6 @@ public:
|
||||
}
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
// TODO: move this code into object-inl.h
|
||||
template<constevalutil::fixed_string... FieldNames, typename T>
|
||||
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
|
||||
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) noexcept {
|
||||
if (error()) { return error(); }
|
||||
return first.extract_into<FieldNames...>(out);
|
||||
}
|
||||
#endif // SIMDJSON_STATIC_REFLECTION
|
||||
#endif // SIMDJSON_SUPPORTS_CONCEPTS
|
||||
};
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "simdjson/generic/ondemand/base.h"
|
||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||
|
||||
#include <chrono>
|
||||
#include <concepts>
|
||||
#include <limits>
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
@@ -16,12 +17,15 @@
|
||||
#endif
|
||||
|
||||
namespace simdjson {
|
||||
template <typename T>
|
||||
constexpr bool require_custom_serialization = false;
|
||||
|
||||
//////////////////////////////
|
||||
// Number deserialization
|
||||
//////////////////////////////
|
||||
|
||||
template <std::unsigned_integral T>
|
||||
requires(!require_custom_serialization<T>)
|
||||
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
|
||||
using limits = std::numeric_limits<T>;
|
||||
|
||||
@@ -35,6 +39,7 @@ error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
|
||||
}
|
||||
|
||||
template <std::floating_point T>
|
||||
requires(!require_custom_serialization<T>)
|
||||
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
|
||||
double x;
|
||||
SIMDJSON_TRY(val.get_double().get(x));
|
||||
@@ -42,7 +47,27 @@ error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
// Support for std::chrono::duration - deserialize from milliseconds
|
||||
template <typename Rep, typename Period, typename ValT>
|
||||
error_code tag_invoke(deserialize_tag, ValT &val, std::chrono::duration<Rep, Period> &out) noexcept {
|
||||
int64_t ms;
|
||||
SIMDJSON_TRY(val.get_int64().get(ms));
|
||||
out = std::chrono::duration_cast<std::chrono::duration<Rep, Period>>(std::chrono::milliseconds(ms));
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
// Support for std::chrono::time_point - deserialize from milliseconds since epoch
|
||||
template <typename Clock, typename Duration, typename ValT>
|
||||
error_code tag_invoke(deserialize_tag, ValT &val, std::chrono::time_point<Clock, Duration> &out) noexcept {
|
||||
int64_t ms_since_epoch;
|
||||
SIMDJSON_TRY(val.get_int64().get(ms_since_epoch));
|
||||
auto duration = std::chrono::milliseconds(ms_since_epoch);
|
||||
out = std::chrono::time_point<Clock, Duration>(std::chrono::duration_cast<Duration>(duration));
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
template <std::signed_integral T>
|
||||
requires(!require_custom_serialization<T>)
|
||||
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
|
||||
using limits = std::numeric_limits<T>;
|
||||
|
||||
@@ -72,6 +97,7 @@ error_code tag_invoke(deserialize_tag, auto &val, char &out) noexcept {
|
||||
|
||||
// any string-like type (can be constructed from std::string_view)
|
||||
template <concepts::constructible_from_string_view T, typename ValT>
|
||||
requires(!require_custom_serialization<T>)
|
||||
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(std::is_nothrow_constructible_v<T, std::string_view>) {
|
||||
std::string_view str;
|
||||
SIMDJSON_TRY(val.get_string().get(str));
|
||||
@@ -88,6 +114,7 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(std::is_nothr
|
||||
* doc.get<std::vector<int>>().
|
||||
*/
|
||||
template <concepts::appendable_containers T, typename ValT>
|
||||
requires(!require_custom_serialization<T>)
|
||||
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
|
||||
using value_type = typename std::remove_cvref_t<T>::value_type;
|
||||
static_assert(
|
||||
@@ -133,6 +160,7 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
|
||||
* string-keyed types.
|
||||
*/
|
||||
template <concepts::string_view_keyed_map T, typename ValT>
|
||||
requires(!require_custom_serialization<T>)
|
||||
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
|
||||
using value_type = typename std::remove_cvref_t<T>::mapped_type;
|
||||
static_assert(
|
||||
@@ -214,6 +242,7 @@ error_code tag_invoke(deserialize_tag, SIMDJSON_IMPLEMENTATION::ondemand::docume
|
||||
* @return status of the conversion
|
||||
*/
|
||||
template <concepts::smart_pointer 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>::element_type, ValT>) {
|
||||
using element_type = typename std::remove_cvref_t<T>::element_type;
|
||||
|
||||
@@ -239,6 +268,7 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deser
|
||||
* This CPO (Customization Point Object) will help deserialize into optional types.
|
||||
*/
|
||||
template <concepts::optional_type T>
|
||||
requires(!require_custom_serialization<T>)
|
||||
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept(nothrow_deserializable<typename std::remove_cvref_t<T>::value_type, decltype(val)>) {
|
||||
using value_type = typename std::remove_cvref_t<T>::value_type;
|
||||
|
||||
@@ -264,7 +294,7 @@ error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept(nothrow_deser
|
||||
template <typename T>
|
||||
constexpr bool user_defined_type = (std::is_class_v<T>
|
||||
&& !std::is_same_v<T, std::string> && !std::is_same_v<T, std::string_view> && !concepts::optional_type<T> &&
|
||||
!concepts::appendable_containers<T>);
|
||||
!concepts::appendable_containers<T> && !require_custom_serialization<T>);
|
||||
|
||||
|
||||
template <typename T, typename ValT>
|
||||
@@ -276,26 +306,18 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept {
|
||||
} else {
|
||||
SIMDJSON_TRY(val.get_object().get(obj));
|
||||
}
|
||||
error_code e = simdjson::SUCCESS;
|
||||
template for (constexpr auto mem : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
|
||||
if constexpr (!std::meta::is_const(mem) && std::meta::is_public(mem)) {
|
||||
constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem));
|
||||
if constexpr (concepts::optional_type<decltype(out.[:mem:])>) {
|
||||
// for optional members, it's ok if the key is missing
|
||||
auto error = obj[key].get(out.[:mem:]);
|
||||
if (error && error != NO_SUCH_FIELD) {
|
||||
if(error == NO_SUCH_FIELD) {
|
||||
out.[:mem:].reset();
|
||||
continue;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
} else {
|
||||
// for non-optional members, the key must be present
|
||||
SIMDJSON_TRY(obj[key].get(out.[:mem:]));
|
||||
// Note: removed static assert as optional types are now handled generically
|
||||
// as long we are succesful or the field is not found, we continue
|
||||
if(e == simdjson::SUCCESS || e == simdjson::NO_SUCH_FIELD) {
|
||||
e = obj[key].get(out.[:mem:]);
|
||||
}
|
||||
}
|
||||
};
|
||||
return simdjson::SUCCESS;
|
||||
return e;
|
||||
}
|
||||
|
||||
// Support for enum deserialization - deserialize from string representation using expand approach from P2996R12
|
||||
|
||||
@@ -48,7 +48,7 @@ simdjson_inline simdjson_result<std::string_view> value::get_string(bool allow_r
|
||||
return iter.get_string(allow_replacement);
|
||||
}
|
||||
template <typename string_type>
|
||||
simdjson_warn_unused simdjson_inline error_code value::get_string(string_type& receiver, bool allow_replacement) noexcept {
|
||||
simdjson_inline error_code value::get_string(string_type& receiver, bool allow_replacement) noexcept {
|
||||
return iter.get_string(receiver, allow_replacement);
|
||||
}
|
||||
simdjson_inline simdjson_result<std::string_view> value::get_wobbly_string() noexcept {
|
||||
@@ -90,15 +90,15 @@ template<> simdjson_inline simdjson_result<int64_t> value::get() noexcept { retu
|
||||
template<> simdjson_inline simdjson_result<bool> value::get() noexcept { return get_bool(); }
|
||||
|
||||
|
||||
template<> simdjson_warn_unused simdjson_inline error_code value::get(array& out) noexcept { return get_array().get(out); }
|
||||
template<> simdjson_warn_unused simdjson_inline error_code value::get(object& out) noexcept { return get_object().get(out); }
|
||||
template<> simdjson_warn_unused simdjson_inline error_code value::get(raw_json_string& out) noexcept { return get_raw_json_string().get(out); }
|
||||
template<> simdjson_warn_unused simdjson_inline error_code value::get(std::string_view& out) noexcept { return get_string(false).get(out); }
|
||||
template<> simdjson_warn_unused simdjson_inline error_code value::get(number& out) noexcept { return get_number().get(out); }
|
||||
template<> simdjson_warn_unused simdjson_inline error_code value::get(double& out) noexcept { return get_double().get(out); }
|
||||
template<> simdjson_warn_unused simdjson_inline error_code value::get(uint64_t& out) noexcept { return get_uint64().get(out); }
|
||||
template<> simdjson_warn_unused simdjson_inline error_code value::get(int64_t& out) noexcept { return get_int64().get(out); }
|
||||
template<> simdjson_warn_unused simdjson_inline error_code value::get(bool& out) noexcept { return get_bool().get(out); }
|
||||
template<> simdjson_inline error_code value::get(array& out) noexcept { return get_array().get(out); }
|
||||
template<> simdjson_inline error_code value::get(object& out) noexcept { return get_object().get(out); }
|
||||
template<> simdjson_inline error_code value::get(raw_json_string& out) noexcept { return get_raw_json_string().get(out); }
|
||||
template<> simdjson_inline error_code value::get(std::string_view& out) noexcept { return get_string(false).get(out); }
|
||||
template<> simdjson_inline error_code value::get(number& out) noexcept { return get_number().get(out); }
|
||||
template<> simdjson_inline error_code value::get(double& out) noexcept { return get_double().get(out); }
|
||||
template<> simdjson_inline error_code value::get(uint64_t& out) noexcept { return get_uint64().get(out); }
|
||||
template<> simdjson_inline error_code value::get(int64_t& out) noexcept { return get_int64().get(out); }
|
||||
template<> simdjson_inline error_code value::get(bool& out) noexcept { return get_bool().get(out); }
|
||||
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
template <class T>
|
||||
|
||||
@@ -65,7 +65,7 @@ public:
|
||||
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
|
||||
*/
|
||||
template <typename T>
|
||||
simdjson_warn_unused simdjson_inline error_code get(T &out)
|
||||
simdjson_inline error_code get(T &out)
|
||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
||||
noexcept(custom_deserializable<T, value> ? nothrow_custom_deserializable<T, value> : true)
|
||||
#else
|
||||
@@ -217,7 +217,7 @@ public:
|
||||
* @returns INCORRECT_TYPE if the JSON value is not a string. Otherwise, we return SUCCESS.
|
||||
*/
|
||||
template <typename string_type>
|
||||
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
|
||||
simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
|
||||
|
||||
/**
|
||||
* Cast this JSON value to a "wobbly" string.
|
||||
@@ -738,7 +738,7 @@ public:
|
||||
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
|
||||
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
|
||||
template <typename string_type>
|
||||
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
|
||||
simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
|
||||
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::raw_json_string> get_raw_json_string() noexcept;
|
||||
simdjson_inline simdjson_result<bool> get_bool() noexcept;
|
||||
|
||||
@@ -41,7 +41,7 @@ simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::start
|
||||
if (*_json_iter->peek() == '}') {
|
||||
logger::log_value(*_json_iter, "empty object");
|
||||
_json_iter->return_current_and_advance();
|
||||
SIMDJSON_TRY(end_container());
|
||||
end_container();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -895,7 +895,7 @@ simdjson_inline void value_iterator::advance_scalar(const char *type) noexcept {
|
||||
_json_iter->ascend_to(depth()-1);
|
||||
}
|
||||
|
||||
simdjson_warn_unused simdjson_inline error_code value_iterator::start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept {
|
||||
simdjson_inline error_code value_iterator::start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept {
|
||||
logger::log_start_value(*_json_iter, start_position(), depth(), type);
|
||||
// If we're not at the position anymore, we don't want to advance the cursor.
|
||||
const uint8_t *json;
|
||||
|
||||
@@ -335,7 +335,7 @@ public:
|
||||
simdjson_warn_unused simdjson_inline simdjson_result<number> get_root_number(bool check_trailing) noexcept;
|
||||
simdjson_warn_unused simdjson_inline simdjson_result<bool> is_root_null(bool check_trailing) noexcept;
|
||||
|
||||
simdjson_warn_unused simdjson_inline error_code error() const noexcept;
|
||||
simdjson_inline error_code error() const noexcept;
|
||||
simdjson_inline uint8_t *&string_buf_loc() noexcept;
|
||||
simdjson_inline const json_iterator &json_iter() const noexcept;
|
||||
simdjson_inline json_iterator &json_iter() noexcept;
|
||||
@@ -419,8 +419,8 @@ protected:
|
||||
simdjson_inline const uint8_t *peek_non_root_scalar(const char *type) noexcept;
|
||||
|
||||
|
||||
simdjson_warn_unused simdjson_inline error_code start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept;
|
||||
simdjson_warn_unused simdjson_inline error_code end_container() noexcept;
|
||||
simdjson_inline error_code start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept;
|
||||
simdjson_inline error_code end_container() noexcept;
|
||||
|
||||
/**
|
||||
* Advance to a place expecting a value (increasing depth).
|
||||
@@ -430,8 +430,8 @@ protected:
|
||||
*/
|
||||
simdjson_inline simdjson_result<const uint8_t *> advance_to_value() noexcept;
|
||||
|
||||
simdjson_warn_unused simdjson_inline error_code incorrect_type_error(const char *message) const noexcept;
|
||||
simdjson_warn_unused simdjson_inline error_code error_unless_more_tokens(uint32_t tokens=1) const noexcept;
|
||||
simdjson_inline error_code incorrect_type_error(const char *message) const noexcept;
|
||||
simdjson_inline error_code error_unless_more_tokens(uint32_t tokens=1) const noexcept;
|
||||
|
||||
simdjson_inline bool is_at_start() const noexcept;
|
||||
/**
|
||||
@@ -468,7 +468,7 @@ protected:
|
||||
/** @copydoc error_code json_iterator::end_position() const noexcept; */
|
||||
simdjson_inline token_position end_position() const noexcept;
|
||||
/** @copydoc error_code json_iterator::report_error(error_code error, const char *message) noexcept; */
|
||||
simdjson_warn_unused simdjson_inline error_code report_error(error_code error, const char *message) noexcept;
|
||||
simdjson_inline error_code report_error(error_code error, const char *message) noexcept;
|
||||
|
||||
friend class document;
|
||||
friend class object;
|
||||
|
||||
@@ -17,7 +17,7 @@ using namespace simd;
|
||||
struct backslash_and_quote {
|
||||
public:
|
||||
static constexpr uint32_t BYTES_PROCESSED = 32;
|
||||
simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
|
||||
simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
|
||||
|
||||
simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; }
|
||||
simdjson_inline bool has_backslash() { return ((quote_bits - 1) & bs_bits) != 0; }
|
||||
|
||||
@@ -17,7 +17,7 @@ using namespace simd;
|
||||
struct backslash_and_quote {
|
||||
public:
|
||||
static constexpr uint32_t BYTES_PROCESSED = 64;
|
||||
simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
|
||||
simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
|
||||
|
||||
simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; }
|
||||
simdjson_inline bool has_backslash() { return ((quote_bits - 1) & bs_bits) != 0; }
|
||||
|
||||
@@ -17,7 +17,7 @@ using namespace simd;
|
||||
struct backslash_and_quote {
|
||||
public:
|
||||
static constexpr uint32_t BYTES_PROCESSED = 32;
|
||||
simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
|
||||
simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
|
||||
|
||||
simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; }
|
||||
simdjson_inline bool has_backslash() { return bs_bits != 0; }
|
||||
|
||||
@@ -17,7 +17,7 @@ using namespace simd;
|
||||
struct backslash_and_quote {
|
||||
public:
|
||||
static constexpr uint32_t BYTES_PROCESSED = 32;
|
||||
simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
|
||||
simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
|
||||
|
||||
simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; }
|
||||
simdjson_inline bool has_backslash() { return bs_bits != 0; }
|
||||
@@ -67,7 +67,7 @@ simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *ds
|
||||
simd8<bool> is_backslash = (v == '\\');
|
||||
simd8<bool> is_control = (v < 32);
|
||||
return {
|
||||
static_cast<uint64_t>((is_backslash | is_quote | is_control).to_bitmask())
|
||||
(is_backslash | is_quote | is_control).to_bitmask()
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ using namespace simd;
|
||||
struct backslash_and_quote {
|
||||
public:
|
||||
static constexpr uint32_t BYTES_PROCESSED = 32;
|
||||
simdjson_inline backslash_and_quote
|
||||
simdjson_inline static backslash_and_quote
|
||||
copy_and_find(const uint8_t *src, uint8_t *dst);
|
||||
|
||||
simdjson_inline bool has_quote_first() {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#define SIMDJSON_SIMDJSON_VERSION_H
|
||||
|
||||
/** The version of simdjson being used (major.minor.revision) */
|
||||
#define SIMDJSON_VERSION "4.0.7"
|
||||
#define SIMDJSON_VERSION "4.0.3"
|
||||
|
||||
namespace simdjson {
|
||||
enum {
|
||||
@@ -19,7 +19,7 @@ enum {
|
||||
/**
|
||||
* The revision (major.minor.REVISION) of simdjson being used.
|
||||
*/
|
||||
SIMDJSON_VERSION_REVISION = 7
|
||||
SIMDJSON_VERSION_REVISION = 3
|
||||
};
|
||||
} // namespace simdjson
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ using namespace simd;
|
||||
struct backslash_and_quote {
|
||||
public:
|
||||
static constexpr uint32_t BYTES_PROCESSED = 32;
|
||||
simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
|
||||
simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
|
||||
|
||||
simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; }
|
||||
simdjson_inline bool has_backslash() { return bs_bits != 0; }
|
||||
|
||||
+168
-241
File diff suppressed because it is too large
Load Diff
+4137
-6937
File diff suppressed because it is too large
Load Diff
Binary file not shown.
+1
-1
@@ -209,7 +209,7 @@ simdjson_inline bool validate_string() {
|
||||
//
|
||||
// Parse the entire input in STEP_SIZE-byte chunks.
|
||||
//
|
||||
simdjson_warn_unused simdjson_inline error_code scan() {
|
||||
simdjson_inline error_code scan() {
|
||||
bool unclosed_string = false;
|
||||
for (;idx<len;idx++) {
|
||||
do {
|
||||
|
||||
@@ -29,7 +29,7 @@ private:
|
||||
template<size_t STEP_SIZE>
|
||||
simdjson_inline void step(const uint8_t *block_buf, buf_block_reader<STEP_SIZE> &reader) noexcept;
|
||||
simdjson_inline void next(const simd::simd8x64<uint8_t>& in, const json_block& block);
|
||||
simdjson_warn_unused simdjson_inline error_code finish(uint8_t *dst_start, size_t &dst_len);
|
||||
simdjson_inline error_code finish(uint8_t *dst_start, size_t &dst_len);
|
||||
json_scanner scanner{};
|
||||
uint8_t *dst;
|
||||
};
|
||||
@@ -39,7 +39,7 @@ simdjson_inline void json_minifier::next(const simd::simd8x64<uint8_t>& in, cons
|
||||
dst += in.compress(mask, dst);
|
||||
}
|
||||
|
||||
simdjson_warn_unused simdjson_inline error_code json_minifier::finish(uint8_t *dst_start, size_t &dst_len) {
|
||||
simdjson_inline error_code json_minifier::finish(uint8_t *dst_start, size_t &dst_len) {
|
||||
error_code error = scanner.finish();
|
||||
if (error) { dst_len = 0; return error; }
|
||||
dst_len = dst - dst_start;
|
||||
|
||||
@@ -108,7 +108,7 @@ public:
|
||||
json_scanner() = default;
|
||||
simdjson_inline json_block next(const simd::simd8x64<uint8_t>& in);
|
||||
// Returns either UNCLOSED_STRING or SUCCESS
|
||||
simdjson_warn_unused simdjson_inline error_code finish();
|
||||
simdjson_inline error_code finish();
|
||||
|
||||
private:
|
||||
// Whether the last character of the previous iteration is part of a scalar token
|
||||
@@ -156,7 +156,7 @@ simdjson_inline json_block json_scanner::next(const simd::simd8x64<uint8_t>& in)
|
||||
);
|
||||
}
|
||||
|
||||
simdjson_warn_unused simdjson_inline error_code json_scanner::finish() {
|
||||
simdjson_inline error_code json_scanner::finish() {
|
||||
return string_scanner.finish();
|
||||
}
|
||||
|
||||
|
||||
@@ -141,7 +141,7 @@ private:
|
||||
template<size_t STEP_SIZE>
|
||||
simdjson_inline void step(const uint8_t *block, buf_block_reader<STEP_SIZE> &reader) noexcept;
|
||||
simdjson_inline void next(const simd::simd8x64<uint8_t>& in, const json_block& block, size_t idx);
|
||||
simdjson_warn_unused simdjson_inline error_code finish(dom_parser_implementation &parser, size_t idx, size_t len, stage1_mode partial);
|
||||
simdjson_inline error_code finish(dom_parser_implementation &parser, size_t idx, size_t len, stage1_mode partial);
|
||||
|
||||
json_scanner scanner{};
|
||||
utf8_checker checker{};
|
||||
|
||||
@@ -195,7 +195,7 @@ using namespace simd;
|
||||
}
|
||||
}
|
||||
// do not forget to call check_eof!
|
||||
simdjson_warn_unused simdjson_inline error_code errors() {
|
||||
simdjson_inline error_code errors() {
|
||||
return this->error.any_bits_set_anywhere() ? error_code::UTF8_ERROR : error_code::SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -151,8 +151,7 @@ simdjson_inline bool handle_unicode_codepoint_wobbly(const uint8_t **src_ptr,
|
||||
simdjson_warn_unused simdjson_inline uint8_t *parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) {
|
||||
while (1) {
|
||||
// Copy the next n bytes, and find the backslash and quote in them.
|
||||
auto b = backslash_and_quote{};
|
||||
auto bs_quote = b.copy_and_find(src, dst);
|
||||
auto bs_quote = backslash_and_quote::copy_and_find(src, dst);
|
||||
// If the next thing is the end quote, copy and return
|
||||
if (bs_quote.has_quote_first()) {
|
||||
// we encountered quotes first. Move dst to point to quotes and exit
|
||||
@@ -197,8 +196,7 @@ simdjson_warn_unused simdjson_inline uint8_t *parse_wobbly_string(const uint8_t
|
||||
// It is not ideal that this function is nearly identical to parse_string.
|
||||
while (1) {
|
||||
// Copy the next n bytes, and find the backslash and quote in them.
|
||||
auto b = backslash_and_quote{};
|
||||
auto bs_quote = b.copy_and_find(src, dst);
|
||||
auto bs_quote = backslash_and_quote::copy_and_find(src, dst);
|
||||
// If the next thing is the end quote, copy and return
|
||||
if (bs_quote.has_quote_first()) {
|
||||
// we encountered quotes first. Move dst to point to quotes and exit
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
# All remaining tests link with simdjson proper
|
||||
include_directories(..)
|
||||
add_cpp_test(builder_string_builder_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(builder_string_builder_tests LABELS ondemand acceptance per_implementation)
|
||||
if(SIMDJSON_STATIC_REFLECTION)
|
||||
add_cpp_test(static_reflection_custom_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(static_reflection_builder_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(static_reflection_comprehensive_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(static_reflection_edge_cases_tests LABELS ondemand acceptance per_implementation)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -50,8 +50,9 @@ namespace builder_tests {
|
||||
TEST_START();
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
Car c = {"Toyota", "Corolla", 2017, {30.0,30.2,30.513,30.79}};
|
||||
std::string pstr;
|
||||
ASSERT_SUCCESS(builder::to_json_string(c).get(pstr));
|
||||
auto result = builder::to_json_string(c);
|
||||
ASSERT_SUCCESS(result);
|
||||
std::string pstr = result.value();
|
||||
ASSERT_EQUAL(pstr, "{\"make\":\"Toyota\",\"model\":\"Corolla\",\"year\":2017,\"tire_pressure\":[30.0,30.2,30.513,30.79]}");
|
||||
simdjson::ondemand::parser parser;
|
||||
simdjson::ondemand::document doc;
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
#include "simdjson.h"
|
||||
#include "test_builder.h"
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
#include <iostream>
|
||||
|
||||
using namespace simdjson;
|
||||
|
||||
namespace builder_tests {
|
||||
|
||||
struct Meeting {
|
||||
std::string title;
|
||||
std::chrono::system_clock::time_point start_time;
|
||||
std::vector<std::string> attendees;
|
||||
std::optional<std::string> location;
|
||||
bool is_recurring;
|
||||
};
|
||||
|
||||
bool test_meeting_roundtrip() {
|
||||
TEST_START();
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
// Create a Meeting instance with current time
|
||||
auto now = std::chrono::system_clock::now();
|
||||
Meeting original{
|
||||
.title = "CppCon Planning",
|
||||
.start_time = now,
|
||||
.attendees = {"Alice", "Bob", "Charlie"},
|
||||
.location = "Denver",
|
||||
.is_recurring = true
|
||||
};
|
||||
|
||||
// Serialize to JSON
|
||||
auto json_result = builder::to_json_string(original);
|
||||
ASSERT_SUCCESS(json_result);
|
||||
std::string json = json_result.value();
|
||||
|
||||
std::cout << "Serialized JSON: " << json << std::endl;
|
||||
|
||||
// Parse back from JSON
|
||||
ondemand::parser parser;
|
||||
auto doc_result = parser.iterate(pad(json));
|
||||
ASSERT_SUCCESS(doc_result);
|
||||
|
||||
auto get_result = doc_result.value().get<Meeting>();
|
||||
ASSERT_SUCCESS(get_result);
|
||||
|
||||
Meeting deserialized = std::move(get_result.value());
|
||||
|
||||
// Verify round-trip
|
||||
ASSERT_EQUAL(deserialized.title, original.title);
|
||||
ASSERT_EQUAL(deserialized.is_recurring, original.is_recurring);
|
||||
ASSERT_TRUE(deserialized.location.has_value());
|
||||
ASSERT_EQUAL(deserialized.location.value(), original.location.value());
|
||||
ASSERT_EQUAL(deserialized.attendees.size(), original.attendees.size());
|
||||
for(size_t i = 0; i < original.attendees.size(); i++) {
|
||||
ASSERT_EQUAL(deserialized.attendees[i], original.attendees[i]);
|
||||
}
|
||||
|
||||
// Check that the time point is correctly deserialized with millisecond precision
|
||||
auto original_ms = std::chrono::duration_cast<std::chrono::milliseconds>(original.start_time.time_since_epoch()).count();
|
||||
auto deserialized_ms = std::chrono::duration_cast<std::chrono::milliseconds>(deserialized.start_time.time_since_epoch()).count();
|
||||
|
||||
std::cout << "Original time: " << original_ms << "ms since epoch" << std::endl;
|
||||
std::cout << "Deserialized time: " << deserialized_ms << "ms since epoch" << std::endl;
|
||||
|
||||
// Verify exact equality at millisecond precision
|
||||
ASSERT_EQUAL(deserialized_ms, original_ms);
|
||||
|
||||
#endif
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool test_duration_roundtrip() {
|
||||
TEST_START();
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
struct Task {
|
||||
std::string name;
|
||||
std::chrono::milliseconds duration_ms;
|
||||
std::chrono::seconds duration_s;
|
||||
std::chrono::minutes duration_min;
|
||||
};
|
||||
|
||||
Task original{
|
||||
.name = "Build Project",
|
||||
.duration_ms = std::chrono::milliseconds(5432),
|
||||
.duration_s = std::chrono::seconds(300),
|
||||
.duration_min = std::chrono::minutes(45)
|
||||
};
|
||||
|
||||
// Serialize to JSON
|
||||
auto json_result = builder::to_json_string(original);
|
||||
ASSERT_SUCCESS(json_result);
|
||||
std::string json = json_result.value();
|
||||
|
||||
std::cout << "Duration test JSON: " << json << std::endl;
|
||||
|
||||
// Parse back from JSON
|
||||
ondemand::parser parser;
|
||||
auto doc_result = parser.iterate(pad(json));
|
||||
ASSERT_SUCCESS(doc_result);
|
||||
|
||||
auto get_result = doc_result.value().get<Task>();
|
||||
ASSERT_SUCCESS(get_result);
|
||||
|
||||
Task deserialized = std::move(get_result.value());
|
||||
|
||||
// Verify round-trip
|
||||
ASSERT_EQUAL(deserialized.name, original.name);
|
||||
ASSERT_EQUAL(deserialized.duration_ms.count(), original.duration_ms.count());
|
||||
ASSERT_EQUAL(deserialized.duration_s.count(), original.duration_s.count());
|
||||
ASSERT_EQUAL(deserialized.duration_min.count(), original.duration_min.count());
|
||||
|
||||
#endif
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool test_different_clock_types() {
|
||||
TEST_START();
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
struct Schedule {
|
||||
std::string event;
|
||||
std::chrono::system_clock::time_point system_time;
|
||||
std::chrono::steady_clock::time_point steady_time;
|
||||
};
|
||||
|
||||
Schedule original{
|
||||
.event = "Conference",
|
||||
.system_time = std::chrono::system_clock::now(),
|
||||
.steady_time = std::chrono::steady_clock::now()
|
||||
};
|
||||
|
||||
// Serialize to JSON
|
||||
auto json_result = builder::to_json_string(original);
|
||||
ASSERT_SUCCESS(json_result);
|
||||
std::string json = json_result.value();
|
||||
|
||||
std::cout << "Clock types test JSON: " << json << std::endl;
|
||||
|
||||
// Parse back from JSON
|
||||
ondemand::parser parser;
|
||||
auto doc_result = parser.iterate(pad(json));
|
||||
ASSERT_SUCCESS(doc_result);
|
||||
|
||||
auto get_result = doc_result.value().get<Schedule>();
|
||||
ASSERT_SUCCESS(get_result);
|
||||
|
||||
Schedule deserialized = std::move(get_result.value());
|
||||
|
||||
// Verify round-trip - check millisecond precision
|
||||
ASSERT_EQUAL(deserialized.event, original.event);
|
||||
|
||||
auto orig_sys_ms = std::chrono::duration_cast<std::chrono::milliseconds>(original.system_time.time_since_epoch()).count();
|
||||
auto deser_sys_ms = std::chrono::duration_cast<std::chrono::milliseconds>(deserialized.system_time.time_since_epoch()).count();
|
||||
ASSERT_EQUAL(deser_sys_ms, orig_sys_ms);
|
||||
|
||||
auto orig_steady_ms = std::chrono::duration_cast<std::chrono::milliseconds>(original.steady_time.time_since_epoch()).count();
|
||||
auto deser_steady_ms = std::chrono::duration_cast<std::chrono::milliseconds>(deserialized.steady_time.time_since_epoch()).count();
|
||||
ASSERT_EQUAL(deser_steady_ms, orig_steady_ms);
|
||||
|
||||
#endif
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool run() {
|
||||
return test_meeting_roundtrip() &&
|
||||
test_duration_roundtrip() &&
|
||||
test_different_clock_types();
|
||||
}
|
||||
|
||||
} // namespace builder_tests
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
return builder_tests::run() ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
@@ -3,7 +3,6 @@
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
@@ -13,84 +12,6 @@ using namespace simdjson;
|
||||
|
||||
namespace builder_tests {
|
||||
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
// Custom type for testing tag_invoke with extract_into
|
||||
struct Price {
|
||||
double amount;
|
||||
std::string currency;
|
||||
|
||||
// Custom deserializer that applies currency conversion
|
||||
friend error_code tag_invoke(deserialize_tag,
|
||||
ondemand::value& val,
|
||||
Price& price) noexcept {
|
||||
ondemand::object obj;
|
||||
auto error = val.get_object().get(obj);
|
||||
if (error) return error;
|
||||
|
||||
// Get the raw amount
|
||||
error = obj["amount"].get(price.amount);
|
||||
if (error) return error;
|
||||
|
||||
// Get the currency
|
||||
std::string_view currency_sv;
|
||||
error = obj["currency"].get(currency_sv);
|
||||
if (error) return error;
|
||||
price.currency = std::string(currency_sv);
|
||||
|
||||
// Custom logic: Convert EUR to USD for consistency
|
||||
if (price.currency == "EUR") {
|
||||
price.amount = price.amount * 1.1; // Simplified conversion
|
||||
price.currency = "USD";
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
};
|
||||
|
||||
struct Product {
|
||||
std::string name;
|
||||
Price price; // Custom deserializable type
|
||||
int stock;
|
||||
};
|
||||
|
||||
// Another custom type for testing
|
||||
struct Dimensions {
|
||||
double value;
|
||||
std::string unit;
|
||||
|
||||
// Custom deserializer that converts to metric
|
||||
friend error_code tag_invoke(deserialize_tag,
|
||||
ondemand::value& val,
|
||||
Dimensions& dim) noexcept {
|
||||
ondemand::object obj;
|
||||
auto error = val.get_object().get(obj);
|
||||
if (error) return error;
|
||||
|
||||
error = obj["value"].get(dim.value);
|
||||
if (error) return error;
|
||||
|
||||
std::string_view unit_sv;
|
||||
error = obj["unit"].get(unit_sv);
|
||||
if (error) return error;
|
||||
dim.unit = std::string(unit_sv);
|
||||
|
||||
// Convert inches to cm
|
||||
if (dim.unit == "inches") {
|
||||
dim.value = dim.value * 2.54;
|
||||
dim.unit = "cm";
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
};
|
||||
|
||||
struct Package {
|
||||
std::string id;
|
||||
Dimensions weight;
|
||||
Dimensions length;
|
||||
};
|
||||
#endif
|
||||
|
||||
bool test_primitive_types() {
|
||||
TEST_START();
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
@@ -104,9 +25,10 @@ namespace builder_tests {
|
||||
|
||||
PrimitiveTypes test{true, 'X', 42, 3.14159, 2.71f};
|
||||
|
||||
std::string json;
|
||||
ASSERT_SUCCESS(builder::to_json_string(test).get(json));
|
||||
auto result = builder::to_json_string(test);
|
||||
ASSERT_SUCCESS(result);
|
||||
|
||||
std::string json = result.value();
|
||||
ASSERT_TRUE(json.find("\"bool_val\":true") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"char_val\":\"X\"") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"int_val\":42") != std::string::npos);
|
||||
@@ -117,9 +39,10 @@ namespace builder_tests {
|
||||
auto doc_result = parser.iterate(pad(json));
|
||||
ASSERT_SUCCESS(doc_result);
|
||||
|
||||
PrimitiveTypes deserialized;
|
||||
ASSERT_SUCCESS(doc_result.get<PrimitiveTypes>().get(deserialized));
|
||||
auto get_result = doc_result.value().get<PrimitiveTypes>();
|
||||
ASSERT_SUCCESS(get_result);
|
||||
|
||||
PrimitiveTypes deserialized = std::move(get_result.value());
|
||||
ASSERT_EQUAL(deserialized.bool_val, test.bool_val);
|
||||
ASSERT_EQUAL(deserialized.char_val, test.char_val);
|
||||
ASSERT_EQUAL(deserialized.int_val, test.int_val);
|
||||
@@ -137,9 +60,11 @@ namespace builder_tests {
|
||||
};
|
||||
|
||||
StringTypes test{"hello world", "test_view"};
|
||||
std::string json;
|
||||
ASSERT_SUCCESS(builder::to_json_string(test).get(json));
|
||||
|
||||
auto result = builder::to_json_string(test);
|
||||
ASSERT_SUCCESS(result);
|
||||
|
||||
std::string json = result.value();
|
||||
ASSERT_TRUE(json.find("\"string_val\":\"hello world\"") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"string_view_val\":\"test_view\"") != std::string::npos);
|
||||
|
||||
@@ -147,9 +72,11 @@ namespace builder_tests {
|
||||
ondemand::parser parser;
|
||||
auto doc_result = parser.iterate(pad(json));
|
||||
ASSERT_SUCCESS(doc_result);
|
||||
StringTypes deserialized;
|
||||
ASSERT_SUCCESS(doc_result.get<StringTypes>().get(deserialized));
|
||||
|
||||
auto get_result = doc_result.value().get<StringTypes>();
|
||||
ASSERT_SUCCESS(get_result);
|
||||
|
||||
StringTypes deserialized = std::move(get_result.value());
|
||||
ASSERT_EQUAL(deserialized.string_val, test.string_val);
|
||||
#endif
|
||||
TEST_SUCCEED();
|
||||
@@ -171,8 +98,10 @@ namespace builder_tests {
|
||||
test.opt_int_null = std::nullopt;
|
||||
test.opt_string_null = std::nullopt;
|
||||
|
||||
std::string json;
|
||||
ASSERT_SUCCESS(builder::to_json_string(test).get(json));
|
||||
auto result = builder::to_json_string(test);
|
||||
ASSERT_SUCCESS(result);
|
||||
|
||||
std::string json = result.value();
|
||||
ASSERT_TRUE(json.find("\"opt_int_with_value\":42") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"opt_string_with_value\":\"optional_test\"") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"opt_int_null\":null") != std::string::npos);
|
||||
@@ -183,9 +112,10 @@ namespace builder_tests {
|
||||
auto doc_result = parser.iterate(pad(json));
|
||||
ASSERT_SUCCESS(doc_result);
|
||||
|
||||
OptionalTypes deserialized;
|
||||
ASSERT_SUCCESS(doc_result.get<OptionalTypes>().get(deserialized));
|
||||
auto get_result = doc_result.value().get<OptionalTypes>();
|
||||
ASSERT_SUCCESS(get_result);
|
||||
|
||||
OptionalTypes deserialized = std::move(get_result.value());
|
||||
ASSERT_TRUE(deserialized.opt_int_with_value.has_value());
|
||||
ASSERT_EQUAL(*deserialized.opt_int_with_value, 42);
|
||||
ASSERT_TRUE(deserialized.opt_string_with_value.has_value());
|
||||
@@ -214,8 +144,10 @@ namespace builder_tests {
|
||||
test.unique_int_null = nullptr;
|
||||
test.shared_string_null = nullptr;
|
||||
|
||||
std::string json;
|
||||
ASSERT_SUCCESS(builder::to_json_string(test).get(json));
|
||||
auto result = builder::to_json_string(test);
|
||||
ASSERT_SUCCESS(result);
|
||||
|
||||
std::string json = result.value();
|
||||
ASSERT_TRUE(json.find("\"unique_int_with_value\":123") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"shared_string_with_value\":\"shared_test\"") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"unique_bool_with_value\":true") != std::string::npos);
|
||||
@@ -225,9 +157,12 @@ namespace builder_tests {
|
||||
// Test round-trip
|
||||
ondemand::parser parser;
|
||||
auto doc_result = parser.iterate(pad(json));
|
||||
ASSERT_SUCCESS(doc_result);
|
||||
|
||||
SmartPointerTypes deserialized;
|
||||
ASSERT_SUCCESS(doc_result.get<SmartPointerTypes>().get(deserialized));
|
||||
auto get_result = doc_result.value().get<SmartPointerTypes>();
|
||||
ASSERT_SUCCESS(get_result);
|
||||
|
||||
SmartPointerTypes deserialized = std::move(get_result.value());
|
||||
ASSERT_TRUE(deserialized.unique_int_with_value != nullptr);
|
||||
ASSERT_EQUAL(*deserialized.unique_int_with_value, 123);
|
||||
ASSERT_TRUE(deserialized.shared_string_with_value != nullptr);
|
||||
@@ -243,7 +178,6 @@ namespace builder_tests {
|
||||
bool test_container_types() {
|
||||
TEST_START();
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
// Test basic container types
|
||||
struct ContainerTypes {
|
||||
std::vector<int> int_vector;
|
||||
std::set<std::string> string_set;
|
||||
@@ -258,8 +192,7 @@ namespace builder_tests {
|
||||
auto result = builder::to_json_string(test);
|
||||
ASSERT_SUCCESS(result);
|
||||
|
||||
std::string json;
|
||||
ASSERT_SUCCESS(builder::to_json_string(test).get(json));
|
||||
std::string json = result.value();
|
||||
ASSERT_TRUE(json.find("\"int_vector\":[1,2,3,4,5]") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"string_set\":[") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"string_map\":{") != std::string::npos);
|
||||
@@ -268,447 +201,27 @@ namespace builder_tests {
|
||||
ondemand::parser parser;
|
||||
auto doc_result = parser.iterate(pad(json));
|
||||
ASSERT_SUCCESS(doc_result);
|
||||
ContainerTypes deserialized;
|
||||
ASSERT_SUCCESS(doc_result.get<ContainerTypes>().get(deserialized));
|
||||
|
||||
auto get_result = doc_result.value().get<ContainerTypes>();
|
||||
ASSERT_SUCCESS(get_result);
|
||||
|
||||
ContainerTypes deserialized = std::move(get_result.value());
|
||||
ASSERT_EQUAL(deserialized.int_vector.size(), 5);
|
||||
ASSERT_EQUAL(deserialized.string_set.size(), 3);
|
||||
ASSERT_EQUAL(deserialized.string_map.size(), 3);
|
||||
ASSERT_EQUAL(deserialized.int_vector[0], 1);
|
||||
ASSERT_EQUAL(deserialized.int_vector[4], 5);
|
||||
|
||||
// Test std::list with iterator-based serialization
|
||||
struct ListContainer {
|
||||
std::list<int> int_list;
|
||||
std::list<std::string> string_list;
|
||||
};
|
||||
|
||||
ListContainer list_test;
|
||||
list_test.int_list = {10, 20, 30, 40, 50};
|
||||
list_test.string_list = {"first", "second", "third"};
|
||||
|
||||
std::string list_result;
|
||||
ASSERT_SUCCESS( builder::to_json_string(list_test).get(list_result));
|
||||
|
||||
// Check that list serialization produces correct JSON array format
|
||||
ASSERT_TRUE(list_result.find("\"int_list\":[10,20,30,40,50]") != std::string::npos);
|
||||
ASSERT_TRUE(list_result.find("\"string_list\":[\"first\",\"second\",\"third\"]") != std::string::npos);
|
||||
|
||||
// Test list round-trip
|
||||
auto list_doc_result = parser.iterate(pad(list_result));
|
||||
ASSERT_SUCCESS(list_doc_result);
|
||||
|
||||
ListContainer list_deserialized;
|
||||
ASSERT_SUCCESS(list_doc_result.get<ListContainer>().get(list_deserialized));
|
||||
ASSERT_EQUAL(list_deserialized.int_list.size(), 5);
|
||||
ASSERT_EQUAL(list_deserialized.string_list.size(), 3);
|
||||
|
||||
// Check list values
|
||||
auto it = list_deserialized.int_list.begin();
|
||||
ASSERT_EQUAL(*it, 10);
|
||||
std::advance(it, 4);
|
||||
ASSERT_EQUAL(*it, 50);
|
||||
|
||||
auto str_it = list_deserialized.string_list.begin();
|
||||
ASSERT_EQUAL(*str_it, "first");
|
||||
std::advance(str_it, 2);
|
||||
ASSERT_EQUAL(*str_it, "third");
|
||||
#endif
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool test_extract_into() {
|
||||
TEST_START();
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
struct Car {
|
||||
std::string make;
|
||||
std::string model;
|
||||
int year;
|
||||
double price;
|
||||
std::optional<std::string> color;
|
||||
};
|
||||
|
||||
ondemand::parser parser;
|
||||
|
||||
// Test 1: Extract only specific fields
|
||||
{
|
||||
auto padded = R"({
|
||||
"make": "Toyota",
|
||||
"model": "Camry",
|
||||
"year": 2024,
|
||||
"price": 28999.99,
|
||||
"color": "Blue",
|
||||
"engine": "V6",
|
||||
"transmission": "Automatic"
|
||||
})"_padded;
|
||||
|
||||
Car car{};
|
||||
ondemand::document doc;
|
||||
ASSERT_SUCCESS( parser.iterate(padded).get(doc) );
|
||||
|
||||
ondemand::object obj;
|
||||
ASSERT_SUCCESS(doc.get_object().get(obj));
|
||||
|
||||
// Extract only 'make' and 'model'
|
||||
ASSERT_SUCCESS(obj.extract_into<"make","model">(car));
|
||||
|
||||
ASSERT_EQUAL(car.make, "Toyota");
|
||||
ASSERT_EQUAL(car.model, "Camry");
|
||||
ASSERT_EQUAL(car.year, 0); // Not extracted
|
||||
ASSERT_EQUAL(car.price, 0); // Not extracted
|
||||
|
||||
ASSERT_SUCCESS(parser.iterate(padded).get(doc));
|
||||
|
||||
// Extract only 'make' and 'model'
|
||||
ASSERT_SUCCESS(doc.extract_into<"make", "model">(car));
|
||||
|
||||
ASSERT_EQUAL(car.make, "Toyota");
|
||||
ASSERT_EQUAL(car.model, "Camry");
|
||||
ASSERT_EQUAL(car.year, 0); // Not extracted
|
||||
ASSERT_EQUAL(car.price, 0); // Not extracted
|
||||
}
|
||||
|
||||
// Test 2: Extract with optional field
|
||||
{
|
||||
auto padded = R"({
|
||||
"make": "Honda",
|
||||
"model": "Accord",
|
||||
"year": 2023,
|
||||
"price": 26999.99,
|
||||
"color": "Red"
|
||||
})"_padded;
|
||||
|
||||
Car car{};
|
||||
ondemand::document doc;
|
||||
ASSERT_SUCCESS(parser.iterate(padded).get(doc));
|
||||
|
||||
ondemand::object obj;
|
||||
ASSERT_SUCCESS(doc.get_object().get(obj));
|
||||
|
||||
// Extract including optional 'color'
|
||||
ASSERT_SUCCESS(obj.extract_into<"make", "model", "color">(car));
|
||||
|
||||
ASSERT_EQUAL(car.make, "Honda");
|
||||
ASSERT_EQUAL(car.model, "Accord");
|
||||
ASSERT_TRUE(car.color.has_value());
|
||||
ASSERT_EQUAL(*car.color, "Red");
|
||||
}
|
||||
|
||||
// Test 3: Extract with missing optional field
|
||||
{
|
||||
auto padded = R"({
|
||||
"make": "Ford",
|
||||
"model": "F-150",
|
||||
"year": 2024,
|
||||
"price": 35999.99
|
||||
})"_padded;
|
||||
|
||||
Car car{};
|
||||
ondemand::document doc;
|
||||
ASSERT_SUCCESS(parser.iterate(padded).get(doc));
|
||||
|
||||
ondemand::object obj;
|
||||
ASSERT_SUCCESS(doc.get_object().get(obj));
|
||||
|
||||
// Try to extract including optional 'color' which doesn't exist
|
||||
ASSERT_SUCCESS(obj.extract_into<"make", "model", "color">(car));
|
||||
|
||||
ASSERT_EQUAL(car.make, "Ford");
|
||||
ASSERT_EQUAL(car.model, "F-150");
|
||||
ASSERT_FALSE(car.color.has_value()); // Should be empty
|
||||
}
|
||||
|
||||
// Test 4: Extract with custom deserializable type using tag_invoke
|
||||
{
|
||||
// Test using the Price struct defined at namespace level
|
||||
|
||||
auto padded = R"({
|
||||
"name": "Laptop",
|
||||
"price": {
|
||||
"amount": 1000,
|
||||
"currency": "EUR"
|
||||
},
|
||||
"stock": 15,
|
||||
"description": "High-end gaming laptop"
|
||||
})"_padded;
|
||||
|
||||
Product product{};
|
||||
auto doc = parser.iterate(padded);
|
||||
ASSERT_SUCCESS(doc);
|
||||
|
||||
ondemand::object obj;
|
||||
auto obj_result = doc.get_object().get(obj);
|
||||
ASSERT_SUCCESS(obj_result);
|
||||
|
||||
// Extract including price field with custom deserializer
|
||||
ASSERT_SUCCESS(obj.extract_into<"name", "price">(product));
|
||||
|
||||
ASSERT_EQUAL(product.name, "Laptop");
|
||||
// Verify custom deserializer was invoked: EUR should be converted to USD
|
||||
ASSERT_EQUAL(product.price.currency, "USD"); // Should be converted from EUR
|
||||
// Verify custom deserializer was invoked: amount should be 1100 (1000 * 1.1)
|
||||
ASSERT_EQUAL(product.price.amount, 1100); // Should be exactly 1100 after conversion
|
||||
ASSERT_EQUAL(product.stock, 0); // Not extracted
|
||||
}
|
||||
|
||||
// Test 5: Extract with nested custom deserializable types
|
||||
{
|
||||
// Test using the Dimensions struct defined at namespace level
|
||||
|
||||
auto padded = R"({
|
||||
"id": "PKG123",
|
||||
"weight": {
|
||||
"value": 10,
|
||||
"unit": "pounds"
|
||||
},
|
||||
"length": {
|
||||
"value": 12,
|
||||
"unit": "inches"
|
||||
},
|
||||
"fragile": true
|
||||
})"_padded;
|
||||
|
||||
Package pkg{};
|
||||
ondemand::document doc;
|
||||
ASSERT_SUCCESS(parser.iterate(padded).get(doc));
|
||||
|
||||
ondemand::object obj;
|
||||
ASSERT_SUCCESS(doc.get_object().get(obj));
|
||||
|
||||
// Extract only length (with custom deserializer), skip weight
|
||||
ASSERT_SUCCESS(obj.extract_into<"id", "length">(pkg));
|
||||
|
||||
ASSERT_EQUAL(pkg.id, "PKG123");
|
||||
// Verify custom deserializer was invoked: inches should be converted to cm
|
||||
ASSERT_EQUAL(pkg.length.unit, "cm"); // Should be converted from inches
|
||||
// Verify exact conversion: 12 inches * 2.54 = 30.48 cm
|
||||
ASSERT_EQUAL(pkg.length.value, 30.48); // Should be exactly 30.48 after conversion
|
||||
ASSERT_EQUAL(pkg.weight.unit, ""); // Weight not extracted
|
||||
ASSERT_EQUAL(pkg.weight.value, 0); // Weight value should remain at default
|
||||
}
|
||||
#endif
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
|
||||
bool test_extract_from() {
|
||||
TEST_START();
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
// Test 1: Extract specific fields from Car struct
|
||||
{
|
||||
struct Car {
|
||||
std::string make;
|
||||
std::string model;
|
||||
int year;
|
||||
double price;
|
||||
bool electric;
|
||||
};
|
||||
|
||||
Car car{"Tesla", "Model 3", 2023, 42000.0, true};
|
||||
|
||||
// Extract only make and model
|
||||
std::string json;
|
||||
ASSERT_SUCCESS((extract_from<"make", "model">(car).get(json)));
|
||||
|
||||
// Parse back to verify correctness
|
||||
auto padded = pad(json);
|
||||
ondemand::parser parser;
|
||||
auto doc = parser.iterate(padded);
|
||||
ASSERT_SUCCESS(doc);
|
||||
|
||||
std::string_view make;
|
||||
std::string_view model;
|
||||
ASSERT_SUCCESS(doc["make"].get(make));
|
||||
ASSERT_SUCCESS(doc["model"].get(model));
|
||||
ASSERT_EQUAL(make, "Tesla");
|
||||
ASSERT_EQUAL(model, "Model 3");
|
||||
|
||||
// Verify excluded fields are not present
|
||||
auto year_result = doc["year"];
|
||||
ASSERT_ERROR(year_result.error(), NO_SUCH_FIELD);
|
||||
auto price_result = doc["price"];
|
||||
ASSERT_ERROR(price_result.error(), NO_SUCH_FIELD);
|
||||
auto electric_result = doc["electric"];
|
||||
ASSERT_ERROR(electric_result.error(), NO_SUCH_FIELD);
|
||||
}
|
||||
|
||||
// Test 2: Extract different field combination
|
||||
{
|
||||
struct Car {
|
||||
std::string make;
|
||||
std::string model;
|
||||
int year;
|
||||
double price;
|
||||
bool electric;
|
||||
};
|
||||
|
||||
Car car{"Ford", "F-150", 2024, 55000.0, false};
|
||||
|
||||
// Extract year and price
|
||||
std::string json;
|
||||
ASSERT_SUCCESS((extract_from<"year", "price">(car).get(json)));
|
||||
|
||||
auto padded = pad(json);
|
||||
ondemand::parser parser;
|
||||
auto doc = parser.iterate(padded);
|
||||
ASSERT_SUCCESS(doc);
|
||||
|
||||
int64_t year;
|
||||
double price;
|
||||
ASSERT_SUCCESS(doc["year"].get(year));
|
||||
ASSERT_SUCCESS(doc["price"].get(price));
|
||||
ASSERT_EQUAL(year, 2024);
|
||||
ASSERT_EQUAL(price, 55000.0);
|
||||
|
||||
// Verify excluded fields
|
||||
auto make_result = doc["make"];
|
||||
ASSERT_ERROR(make_result.error(), NO_SUCH_FIELD);
|
||||
}
|
||||
|
||||
// Test 3: Extract from struct with optional fields
|
||||
{
|
||||
struct Person {
|
||||
std::string name;
|
||||
int age;
|
||||
std::optional<std::string> email;
|
||||
std::optional<std::string> phone;
|
||||
};
|
||||
|
||||
Person person{"John Doe", 30, "john@example.com", std::nullopt};
|
||||
|
||||
// Extract name and email
|
||||
std::string json;
|
||||
ASSERT_SUCCESS((extract_from<"name", "email">(person).get(json)));
|
||||
|
||||
auto padded = pad(json);
|
||||
ondemand::parser parser;
|
||||
auto doc = parser.iterate(padded);
|
||||
ASSERT_SUCCESS(doc);
|
||||
|
||||
std::string_view name;
|
||||
std::string_view email;
|
||||
ASSERT_SUCCESS(doc["name"].get(name));
|
||||
ASSERT_SUCCESS(doc["email"].get(email));
|
||||
ASSERT_EQUAL(name, "John Doe");
|
||||
ASSERT_EQUAL(email, "john@example.com");
|
||||
}
|
||||
|
||||
// Test 4: Extract with optional that has value
|
||||
{
|
||||
struct Person {
|
||||
std::string name;
|
||||
int age;
|
||||
std::optional<std::string> email;
|
||||
std::optional<std::string> phone;
|
||||
};
|
||||
|
||||
Person person{"Jane Smith", 25, "jane@example.com", "555-1234"};
|
||||
|
||||
// Extract name, age, and phone
|
||||
std::string json;
|
||||
ASSERT_SUCCESS((extract_from<"name", "age", "phone">(person).get(json)));
|
||||
|
||||
auto padded = pad(json);
|
||||
ondemand::parser parser;
|
||||
auto doc = parser.iterate(padded);
|
||||
ASSERT_SUCCESS(doc);
|
||||
|
||||
std::string_view name;
|
||||
int64_t age;
|
||||
std::string_view phone;
|
||||
ASSERT_SUCCESS(doc["name"].get(name));
|
||||
ASSERT_SUCCESS(doc["age"].get(age));
|
||||
ASSERT_SUCCESS(doc["phone"].get(phone));
|
||||
ASSERT_EQUAL(name, "Jane Smith");
|
||||
ASSERT_EQUAL(age, 25);
|
||||
ASSERT_EQUAL(phone, "555-1234");
|
||||
|
||||
// Email should not be present
|
||||
auto email_result = doc["email"];
|
||||
ASSERT_ERROR(email_result.error(), NO_SUCH_FIELD);
|
||||
}
|
||||
|
||||
// Test 5: Round-trip test - serialize with extract_from, deserialize with extract_into
|
||||
{
|
||||
struct Product {
|
||||
std::string id;
|
||||
std::string name;
|
||||
double price;
|
||||
int stock;
|
||||
};
|
||||
|
||||
Product original{"P123", "Widget", 19.99, 100};
|
||||
|
||||
// Extract specific fields to JSON
|
||||
std::string json;
|
||||
ASSERT_SUCCESS((extract_from<"id", "name", "price">(original).get(json)));
|
||||
|
||||
// Parse and extract back
|
||||
Product restored{"", "", 0.0, 0};
|
||||
auto padded = pad(json);
|
||||
ondemand::parser parser;
|
||||
auto doc = parser.iterate(padded);
|
||||
ASSERT_SUCCESS(doc);
|
||||
|
||||
ondemand::object obj;
|
||||
ASSERT_SUCCESS(doc.get_object().get(obj));
|
||||
auto extract_result = obj.extract_into<"id", "name", "price">(restored);
|
||||
ASSERT_SUCCESS(extract_result);
|
||||
|
||||
// Verify fields match
|
||||
ASSERT_EQUAL(restored.id, original.id);
|
||||
ASSERT_EQUAL(restored.name, original.name);
|
||||
ASSERT_EQUAL(restored.price, original.price);
|
||||
ASSERT_EQUAL(restored.stock, 0); // Stock should remain at default
|
||||
}
|
||||
|
||||
// Test 6: Extract from nested structs
|
||||
{
|
||||
struct Address {
|
||||
std::string street;
|
||||
std::string city;
|
||||
std::string zip;
|
||||
};
|
||||
|
||||
struct Company {
|
||||
std::string name;
|
||||
Address headquarters;
|
||||
int employees;
|
||||
};
|
||||
|
||||
Company company{"TechCorp", {"123 Main St", "San Francisco", "94105"}, 500};
|
||||
|
||||
// Extract name and employees only
|
||||
std::string json;
|
||||
ASSERT_SUCCESS((extract_from<"name", "employees">(company).get(json)));
|
||||
|
||||
auto padded = pad(json);
|
||||
ondemand::parser parser;
|
||||
auto doc = parser.iterate(padded);
|
||||
ASSERT_SUCCESS(doc);
|
||||
|
||||
std::string_view name;
|
||||
int64_t employees;
|
||||
ASSERT_SUCCESS(doc["name"].get(name));
|
||||
ASSERT_SUCCESS(doc["employees"].get(employees));
|
||||
ASSERT_EQUAL(name, "TechCorp");
|
||||
ASSERT_EQUAL(employees, 500);
|
||||
|
||||
// headquarters should not be present
|
||||
auto hq_result = doc["headquarters"];
|
||||
ASSERT_ERROR(hq_result.error(), NO_SUCH_FIELD);
|
||||
}
|
||||
#endif
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool run() {
|
||||
return test_primitive_types() &&
|
||||
test_string_types() &&
|
||||
test_optional_types() &&
|
||||
test_smart_pointer_types() &&
|
||||
test_container_types() &&
|
||||
test_extract_into() &&
|
||||
test_extract_from();
|
||||
test_container_types();
|
||||
}
|
||||
|
||||
} // namespace builder_tests
|
||||
|
||||
@@ -1,138 +0,0 @@
|
||||
|
||||
|
||||
|
||||
#include "simdjson.h"
|
||||
#include "test_builder.h"
|
||||
#include <charconv>
|
||||
#include <string>
|
||||
|
||||
using namespace simdjson;
|
||||
|
||||
// Suppose that we want to serialize/deserialize Car using
|
||||
// strings for the year
|
||||
struct Car {
|
||||
std::string make;
|
||||
std::string model;
|
||||
int64_t year;
|
||||
std::vector<float> tire_pressure;
|
||||
};
|
||||
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
std::string_view year_str;
|
||||
if ((error = obj["year"].get(year_str))) {
|
||||
return error;
|
||||
}
|
||||
int64_t year_value;
|
||||
auto [ptr, ec] = std::from_chars(year_str.data(), year_str.data() + year_str.size(), year_value);
|
||||
if (ec != std::errc{}) {
|
||||
return INCORRECT_TYPE;
|
||||
}
|
||||
car.year = year_value;
|
||||
if ((error = obj["tire_pressure"].get<std::vector<float>>().get(
|
||||
car.tire_pressure))) {
|
||||
return error;
|
||||
}
|
||||
return simdjson::SUCCESS;
|
||||
}
|
||||
|
||||
template <typename builder_type>
|
||||
void tag_invoke(serialize_tag, builder_type &builder, const Car& car) {
|
||||
builder.start_object();
|
||||
builder.append_key_value("make", car.make);
|
||||
builder.append_comma();
|
||||
builder.append_key_value("model", car.model);
|
||||
builder.append_comma();
|
||||
builder.append_key_value("year", std::to_string(car.year));
|
||||
builder.append_comma();
|
||||
builder.append_key_value("tire_pressure", car.tire_pressure);
|
||||
builder.end_object();
|
||||
}
|
||||
|
||||
} // namespace simdjson
|
||||
namespace custom_tests {
|
||||
|
||||
bool test_car_deserialization() {
|
||||
TEST_START();
|
||||
auto json = R"({"make":"Toyota","model":"Camry","year":"2018","tire_pressure":[30.5,30.6,30.7,30.8]})"_padded;
|
||||
ondemand::parser parser;
|
||||
auto doc_result = parser.iterate(json);
|
||||
ASSERT_SUCCESS(doc_result);
|
||||
Car car;
|
||||
ASSERT_SUCCESS(doc_result.get(car));
|
||||
if (car.make != "Toyota" || car.model != "Camry" || std::to_string(car.year) != "2018" || car.tire_pressure.size() != 4 || car.tire_pressure[0] != 30.5f) {
|
||||
return false;
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
bool test_car_serialization() {
|
||||
TEST_START();
|
||||
Car car{"Toyota", "Camry", 2018, {30.5f,30.6f,30.7f,30.8f}};
|
||||
simdjson::builder::string_builder builder;
|
||||
builder.append(car);
|
||||
std::string_view json;
|
||||
ASSERT_SUCCESS(builder.view().get(json));
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
bool test_car_roundtrip() {
|
||||
TEST_START();
|
||||
Car car{"Toyota", "Camry", 2018, {30.5f,30.6f,30.7f,30.8f}};
|
||||
simdjson::builder::string_builder builder;
|
||||
builder.append(car);
|
||||
std::string json;
|
||||
ASSERT_SUCCESS(builder.view().get(json));
|
||||
ondemand::parser parser;
|
||||
auto doc_result = parser.iterate(json);
|
||||
ASSERT_SUCCESS(doc_result);
|
||||
Car carback;
|
||||
ASSERT_SUCCESS(doc_result.get(carback));
|
||||
|
||||
if (carback.make != car.make || carback.model != car.model || carback.year != car.year || carback.tire_pressure != car.tire_pressure) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool test_car_roundtrip_to_json() {
|
||||
TEST_START();
|
||||
Car car{"Toyota", "Camry", 2018, {30.5f,30.6f,30.7f,30.8f}};
|
||||
std::string json;
|
||||
ASSERT_SUCCESS(simdjson::to_json(car).get(json));
|
||||
simdjson::builder::string_builder builder;
|
||||
ondemand::parser parser;
|
||||
auto doc_result = parser.iterate(json);
|
||||
ASSERT_SUCCESS(doc_result);
|
||||
Car carback;
|
||||
ASSERT_SUCCESS(doc_result.get(carback));
|
||||
|
||||
if (carback.make != car.make || carback.model != car.model || carback.year != car.year || carback.tire_pressure != car.tire_pressure) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
bool run() {
|
||||
return test_car_deserialization() && test_car_serialization() && test_car_roundtrip() && test_car_roundtrip_to_json();
|
||||
}
|
||||
|
||||
} // namespace builder_tests
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
return test_main(argc, argv, custom_tests::run);
|
||||
}
|
||||
@@ -28,8 +28,10 @@ namespace builder_tests {
|
||||
test.null_unique_ptr = nullptr;
|
||||
test.null_shared_ptr = nullptr;
|
||||
|
||||
std::string json;
|
||||
ASSERT_SUCCESS(builder::to_json_string(test).get(json));
|
||||
auto result = builder::to_json_string(test);
|
||||
ASSERT_SUCCESS(result);
|
||||
|
||||
std::string json = result.value();
|
||||
ASSERT_TRUE(json.find("\"empty_string\":\"\"") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"empty_vector\":[]") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"null_optional\":null") != std::string::npos);
|
||||
@@ -41,8 +43,10 @@ namespace builder_tests {
|
||||
auto doc_result = parser.iterate(pad(json));
|
||||
ASSERT_SUCCESS(doc_result);
|
||||
|
||||
EmptyValues deserialized;
|
||||
ASSERT_SUCCESS(doc_result.get<EmptyValues>().get(deserialized));
|
||||
auto get_result = doc_result.value().get<EmptyValues>();
|
||||
ASSERT_SUCCESS(get_result);
|
||||
|
||||
EmptyValues deserialized = std::move(get_result.value());
|
||||
ASSERT_EQUAL(deserialized.empty_string, "");
|
||||
ASSERT_EQUAL(deserialized.empty_vector.size(), 0);
|
||||
ASSERT_FALSE(deserialized.null_optional.has_value());
|
||||
@@ -70,8 +74,10 @@ namespace builder_tests {
|
||||
test.unicode = "Café résumé";
|
||||
test.null_char = '\0';
|
||||
|
||||
std::string json;
|
||||
ASSERT_SUCCESS(builder::to_json_string(test).get(json));
|
||||
auto result = builder::to_json_string(test);
|
||||
ASSERT_SUCCESS(result);
|
||||
|
||||
std::string json = result.value();
|
||||
// Test that quotes are properly escaped
|
||||
ASSERT_TRUE(json.find("\\\"Hello\\\"") != std::string::npos);
|
||||
// Test that backslashes are properly escaped
|
||||
@@ -94,17 +100,17 @@ namespace builder_tests {
|
||||
test_no_null.newlines = test.newlines;
|
||||
test_no_null.unicode = test.unicode;
|
||||
|
||||
std::string result_no_null;
|
||||
ASSERT_SUCCESS(builder::to_json_string(test_no_null).get(result_no_null));
|
||||
auto result_no_null = builder::to_json_string(test_no_null);
|
||||
ASSERT_SUCCESS(result_no_null);
|
||||
|
||||
ondemand::parser parser;
|
||||
ondemand::document doc_result;
|
||||
auto doc_result = parser.iterate(pad(result_no_null.value()));
|
||||
ASSERT_SUCCESS(doc_result);
|
||||
|
||||
ASSERT_SUCCESS(parser.iterate(pad(result_no_null)).get(doc_result));
|
||||
|
||||
SpecialCharsNoNull deserialized;
|
||||
ASSERT_SUCCESS(doc_result.get<SpecialCharsNoNull>().get(deserialized));
|
||||
auto get_result = doc_result.value().get<SpecialCharsNoNull>();
|
||||
ASSERT_SUCCESS(get_result);
|
||||
|
||||
SpecialCharsNoNull deserialized = std::move(get_result.value());
|
||||
ASSERT_EQUAL(deserialized.quotes, test.quotes);
|
||||
ASSERT_EQUAL(deserialized.backslashes, test.backslashes);
|
||||
ASSERT_EQUAL(deserialized.newlines, test.newlines);
|
||||
@@ -133,18 +139,22 @@ namespace builder_tests {
|
||||
test.true_val = true;
|
||||
test.false_val = false;
|
||||
|
||||
std::string json;
|
||||
ASSERT_SUCCESS(builder::to_json_string(test).get(json));
|
||||
auto result = builder::to_json_string(test);
|
||||
ASSERT_SUCCESS(result);
|
||||
|
||||
std::string json = result.value();
|
||||
ASSERT_TRUE(json.find("\"true_val\":true") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"false_val\":false") != std::string::npos);
|
||||
|
||||
// Test round-trip
|
||||
ondemand::parser parser;
|
||||
ondemand::document doc_result;
|
||||
auto doc_result = parser.iterate(pad(json));
|
||||
ASSERT_SUCCESS(doc_result);
|
||||
|
||||
ASSERT_SUCCESS(parser.iterate(pad(json)).get(doc_result));
|
||||
NumericLimits deserialized;
|
||||
ASSERT_SUCCESS(doc_result.get<NumericLimits>().get(deserialized));
|
||||
auto get_result = doc_result.value().get<NumericLimits>();
|
||||
ASSERT_SUCCESS(get_result);
|
||||
|
||||
NumericLimits deserialized = std::move(get_result.value());
|
||||
ASSERT_EQUAL(deserialized.max_int, test.max_int);
|
||||
ASSERT_EQUAL(deserialized.min_int, test.min_int);
|
||||
ASSERT_EQUAL(deserialized.true_val, true);
|
||||
@@ -174,8 +184,10 @@ namespace builder_tests {
|
||||
test.optional_inner = Inner{99, "optional"};
|
||||
test.unique_inner = std::make_unique<Inner>(Inner{123, "unique"});
|
||||
|
||||
std::string json;
|
||||
ASSERT_SUCCESS(builder::to_json_string(test).get(json));
|
||||
auto result = builder::to_json_string(test);
|
||||
ASSERT_SUCCESS(result);
|
||||
|
||||
std::string json = result.value();
|
||||
ASSERT_TRUE(json.find("\"inner_obj\":{") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"inner_vector\":[") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"optional_inner\":{") != std::string::npos);
|
||||
@@ -186,8 +198,10 @@ namespace builder_tests {
|
||||
auto doc_result = parser.iterate(pad(json));
|
||||
ASSERT_SUCCESS(doc_result);
|
||||
|
||||
Outer deserialized;
|
||||
ASSERT_SUCCESS(doc_result.get<Outer>().get(deserialized));
|
||||
auto get_result = doc_result.value().get<Outer>();
|
||||
ASSERT_SUCCESS(get_result);
|
||||
|
||||
Outer deserialized = std::move(get_result.value());
|
||||
ASSERT_EQUAL(deserialized.inner_obj.value, 42);
|
||||
ASSERT_EQUAL(deserialized.inner_obj.name, "inner");
|
||||
ASSERT_EQUAL(deserialized.inner_vector.size(), 2);
|
||||
|
||||
@@ -21,21 +21,26 @@ namespace builder_tests {
|
||||
};
|
||||
|
||||
EnumStruct test{Color::Red, 42};
|
||||
std::string json;
|
||||
ASSERT_SUCCESS(builder::to_json_string(test).get(json));
|
||||
|
||||
auto result = builder::to_json_string(test);
|
||||
ASSERT_SUCCESS(result);
|
||||
|
||||
std::string json = result.value();
|
||||
// Enum should be serialized as string (Red)
|
||||
ASSERT_TRUE(json.find("\"color\":\"Red\"") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"value\":42") != std::string::npos);
|
||||
|
||||
// Test different enum values
|
||||
test.color = Color::Green;
|
||||
std::string json2;
|
||||
ASSERT_SUCCESS(builder::to_json_string(test).get(json2));
|
||||
auto result2 = builder::to_json_string(test);
|
||||
ASSERT_SUCCESS(result2);
|
||||
std::string json2 = result2.value();
|
||||
ASSERT_TRUE(json2.find("\"color\":\"Green\"") != std::string::npos);
|
||||
|
||||
test.color = Color::Blue;
|
||||
std::string json3;
|
||||
ASSERT_SUCCESS(builder::to_json_string(test).get(json3));
|
||||
auto result3 = builder::to_json_string(test);
|
||||
ASSERT_SUCCESS(result3);
|
||||
std::string json3 = result3.value();
|
||||
ASSERT_TRUE(json3.find("\"color\":\"Blue\"") != std::string::npos);
|
||||
#endif
|
||||
TEST_SUCCEED();
|
||||
@@ -56,37 +61,41 @@ namespace builder_tests {
|
||||
};
|
||||
|
||||
// Test deserialization of different enum values with string representation
|
||||
auto json1 = R"({"status":"Active","name":"test1"})"_padded;
|
||||
std::string json1 = "{\"status\":\"Active\",\"name\":\"test1\"}";
|
||||
ondemand::parser parser1;
|
||||
ondemand::document doc_result1;
|
||||
ASSERT_SUCCESS(parser1.iterate(json1).get(doc_result1));
|
||||
StatusStruct deserialized1;
|
||||
ASSERT_SUCCESS(doc_result1.get<StatusStruct>().get(deserialized1));
|
||||
auto doc_result1 = parser1.iterate(pad(json1));
|
||||
ASSERT_SUCCESS(doc_result1);
|
||||
|
||||
auto get_result1 = doc_result1.value().get<StatusStruct>();
|
||||
ASSERT_SUCCESS(get_result1);
|
||||
|
||||
StatusStruct deserialized1 = std::move(get_result1.value());
|
||||
ASSERT_TRUE(deserialized1.status == Status::Active);
|
||||
ASSERT_EQUAL(deserialized1.name, "test1");
|
||||
|
||||
// Test Status::Inactive
|
||||
auto json2 = R"({"status":"Inactive","name":"test2"})"_padded;
|
||||
std::string json2 = "{\"status\":\"Inactive\",\"name\":\"test2\"}";
|
||||
ondemand::parser parser2;
|
||||
ondemand::document doc_result2;
|
||||
ASSERT_SUCCESS(parser2.iterate(json2).get(doc_result2));
|
||||
auto doc_result2 = parser2.iterate(pad(json2));
|
||||
ASSERT_SUCCESS(doc_result2);
|
||||
|
||||
StatusStruct deserialized2;
|
||||
ASSERT_SUCCESS(doc_result2.get<StatusStruct>().get(deserialized2));
|
||||
auto get_result2 = doc_result2.value().get<StatusStruct>();
|
||||
ASSERT_SUCCESS(get_result2);
|
||||
|
||||
StatusStruct deserialized2 = std::move(get_result2.value());
|
||||
ASSERT_TRUE(deserialized2.status == Status::Inactive);
|
||||
ASSERT_EQUAL(deserialized2.name, "test2");
|
||||
|
||||
// Test Status::Pending
|
||||
std::string json3 = "{\"status\":\"Pending\",\"name\":\"test3\"}";
|
||||
ondemand::parser parser3;
|
||||
ondemand::document doc_result3;
|
||||
ASSERT_SUCCESS(parser3.iterate(pad(json3)).get(doc_result3));
|
||||
auto doc_result3 = parser3.iterate(pad(json3));
|
||||
ASSERT_SUCCESS(doc_result3);
|
||||
|
||||
StatusStruct deserialized3;
|
||||
ASSERT_SUCCESS(doc_result3.get<StatusStruct>().get(deserialized3));
|
||||
auto get_result3 = doc_result3.value().get<StatusStruct>();
|
||||
ASSERT_SUCCESS(get_result3);
|
||||
|
||||
StatusStruct deserialized3 = std::move(get_result3.value());
|
||||
ASSERT_TRUE(deserialized3.status == Status::Pending);
|
||||
ASSERT_EQUAL(deserialized3.name, "test3");
|
||||
#endif
|
||||
@@ -112,8 +121,10 @@ namespace builder_tests {
|
||||
Task original{Priority::High, "Important task", 123};
|
||||
|
||||
// Serialize
|
||||
std::string json;
|
||||
ASSERT_SUCCESS(builder::to_json_string(original).get(json));
|
||||
auto serialize_result = builder::to_json_string(original);
|
||||
ASSERT_SUCCESS(serialize_result);
|
||||
|
||||
std::string json = serialize_result.value();
|
||||
ASSERT_TRUE(json.find("\"priority\":\"High\"") != std::string::npos); // High as string
|
||||
ASSERT_TRUE(json.find("\"description\":\"Important task\"") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"id\":123") != std::string::npos);
|
||||
@@ -121,12 +132,13 @@ namespace builder_tests {
|
||||
// Deserialize
|
||||
ondemand::parser parser;
|
||||
std::cout << json << std::endl;
|
||||
ondemand::document doc_result;
|
||||
ASSERT_SUCCESS(parser.iterate(pad(json)).get(doc_result));
|
||||
auto doc_result = parser.iterate(pad(json));
|
||||
ASSERT_SUCCESS(doc_result);
|
||||
|
||||
Task deserialized;
|
||||
ASSERT_SUCCESS(doc_result.get<Task>().get(deserialized));
|
||||
auto get_result = doc_result.value().get<Task>();
|
||||
ASSERT_SUCCESS(get_result);
|
||||
|
||||
Task deserialized = std::move(get_result.value());
|
||||
ASSERT_TRUE(deserialized.priority == Priority::High);
|
||||
ASSERT_EQUAL(deserialized.description, "Important task");
|
||||
ASSERT_EQUAL(deserialized.id, 123);
|
||||
@@ -150,18 +162,22 @@ namespace builder_tests {
|
||||
|
||||
Response test{ErrorCode::NotFound, "Resource not found"};
|
||||
|
||||
std::string json;
|
||||
ASSERT_SUCCESS(builder::to_json_string(test).get(json));
|
||||
auto result = builder::to_json_string(test);
|
||||
ASSERT_SUCCESS(result);
|
||||
|
||||
std::string json = result.value();
|
||||
ASSERT_TRUE(json.find("\"error\":\"NotFound\"") != std::string::npos);
|
||||
ASSERT_TRUE(json.find("\"message\":\"Resource not found\"") != std::string::npos);
|
||||
|
||||
// Test round-trip
|
||||
ondemand::parser parser;
|
||||
ondemand::document doc_result;
|
||||
ASSERT_SUCCESS(parser.iterate(pad(json)).get(doc_result));
|
||||
Response deserialized;
|
||||
ASSERT_SUCCESS(doc_result.get<Response>().get(deserialized));
|
||||
auto doc_result = parser.iterate(pad(json));
|
||||
ASSERT_SUCCESS(doc_result);
|
||||
|
||||
auto get_result = doc_result.value().get<Response>();
|
||||
ASSERT_SUCCESS(get_result);
|
||||
|
||||
Response deserialized = std::move(get_result.value());
|
||||
ASSERT_TRUE(deserialized.error == ErrorCode::NotFound);
|
||||
ASSERT_EQUAL(deserialized.message, "Resource not found");
|
||||
#endif
|
||||
@@ -204,8 +220,10 @@ namespace builder_tests {
|
||||
|
||||
Date test{Day::Friday, Month::July, 2024};
|
||||
|
||||
std::string json;
|
||||
ASSERT_SUCCESS(builder::to_json_string(test).get(json));
|
||||
auto result = builder::to_json_string(test);
|
||||
ASSERT_SUCCESS(result);
|
||||
|
||||
std::string json = result.value();
|
||||
ASSERT_TRUE(json.find("\"day\":\"Friday\"") != std::string::npos); // Friday as string
|
||||
ASSERT_TRUE(json.find("\"month\":\"July\"") != std::string::npos); // July as string
|
||||
ASSERT_TRUE(json.find("\"year\":2024") != std::string::npos);
|
||||
@@ -214,8 +232,11 @@ namespace builder_tests {
|
||||
ondemand::parser parser;
|
||||
auto doc_result = parser.iterate(pad(json));
|
||||
ASSERT_SUCCESS(doc_result);
|
||||
Date deserialized;
|
||||
ASSERT_SUCCESS(doc_result.get<Date>().get(deserialized));
|
||||
|
||||
auto get_result = doc_result.value().get<Date>();
|
||||
ASSERT_SUCCESS(get_result);
|
||||
|
||||
Date deserialized = std::move(get_result.value());
|
||||
ASSERT_TRUE(deserialized.day == Day::Friday);
|
||||
ASSERT_TRUE(deserialized.month == Month::July);
|
||||
ASSERT_EQUAL(deserialized.year, 2024);
|
||||
|
||||
@@ -123,7 +123,7 @@ endif()
|
||||
|
||||
# Add the tests if we're on:
|
||||
# 1. Visual Studio 2022 v17.6 or later
|
||||
# 2. GCC v14.0.0 or later (GCC v13.0.0 cannot handle pipe operator of lambda)
|
||||
# 2. GCC v14.0.0 or later (GCC v13.0.0 cannot handle pipe oprator of lambda)
|
||||
# 3. Clang v15.0.0 or later (certain version C++ headers occur error when compiling)
|
||||
if(
|
||||
(MSVC AND MSVC_VERSION LESS 1930) OR
|
||||
|
||||
@@ -1,418 +0,0 @@
|
||||
/**
|
||||
* @file compile_time_json_tests.cpp
|
||||
* @brief Comprehensive tests for compile-time JSON parsing using C++26 P2996 reflection
|
||||
*/
|
||||
|
||||
#include "simdjson.h"
|
||||
#include "test_ondemand.h"
|
||||
|
||||
using namespace simdjson;
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
namespace compile_time_json_tests {
|
||||
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
using namespace arm64::compile_time;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Test 1: Basic object with primitives
|
||||
*/
|
||||
bool test_basic_object() {
|
||||
TEST_START();
|
||||
|
||||
constexpr auto config = parse_json<R"({
|
||||
"port": 8080,
|
||||
"host": "localhost",
|
||||
"debug": true,
|
||||
"timeout": 30.5
|
||||
})">();
|
||||
|
||||
static_assert(config.port == 8080);
|
||||
static_assert(std::string_view(config.host) == "localhost");
|
||||
static_assert(config.debug == true);
|
||||
static_assert(config.timeout == 30.5);
|
||||
|
||||
ASSERT_EQUAL(config.port, 8080);
|
||||
ASSERT_EQUAL(std::string_view(config.host), "localhost"sv);
|
||||
ASSERT_TRUE(config.debug);
|
||||
ASSERT_EQUAL(config.timeout, 30.5);
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 2: Nested objects
|
||||
*/
|
||||
bool test_nested_objects() {
|
||||
TEST_START();
|
||||
|
||||
constexpr auto config = parse_json<R"({
|
||||
"server_port": 3000,
|
||||
"enable_ssl": true,
|
||||
"database": {
|
||||
"host": "db.example.com",
|
||||
"port": 5432,
|
||||
"timeout_sec": 30.0
|
||||
}
|
||||
})">();
|
||||
|
||||
static_assert(config.server_port == 3000);
|
||||
static_assert(config.enable_ssl == true);
|
||||
static_assert(std::string_view(config.database.host) == "db.example.com");
|
||||
static_assert(config.database.port == 5432);
|
||||
static_assert(config.database.timeout_sec == 30.0);
|
||||
|
||||
ASSERT_EQUAL(config.server_port, 3000);
|
||||
ASSERT_TRUE(config.enable_ssl);
|
||||
ASSERT_EQUAL(std::string_view(config.database.host), "db.example.com"sv);
|
||||
ASSERT_EQUAL(config.database.port, 5432);
|
||||
ASSERT_EQUAL(config.database.timeout_sec, 30.0);
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 3: Deeply nested objects (3+ levels)
|
||||
*/
|
||||
bool test_deeply_nested_objects() {
|
||||
TEST_START();
|
||||
|
||||
constexpr auto config = parse_json<R"({
|
||||
"app_name": "MyApp",
|
||||
"version": 1.5,
|
||||
"server": {
|
||||
"host": "api.example.com",
|
||||
"port": 443,
|
||||
"tls": {
|
||||
"enabled": true,
|
||||
"cert_path": "/etc/ssl/cert.pem",
|
||||
"min_version": 1.3
|
||||
}
|
||||
}
|
||||
})">();
|
||||
|
||||
static_assert(std::string_view(config.app_name) == "MyApp");
|
||||
static_assert(config.version == 1.5);
|
||||
static_assert(std::string_view(config.server.host) == "api.example.com");
|
||||
static_assert(config.server.port == 443);
|
||||
static_assert(config.server.tls.enabled == true);
|
||||
static_assert(std::string_view(config.server.tls.cert_path) == "/etc/ssl/cert.pem");
|
||||
static_assert(config.server.tls.min_version == 1.3);
|
||||
|
||||
ASSERT_EQUAL(std::string_view(config.app_name), "MyApp"sv);
|
||||
ASSERT_EQUAL(config.server.tls.enabled, true);
|
||||
ASSERT_EQUAL(config.server.tls.min_version, 1.3);
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 4: Empty object
|
||||
*/
|
||||
bool test_empty_object() {
|
||||
TEST_START();
|
||||
|
||||
constexpr auto config = parse_json<"{}">();
|
||||
(void)config; // Suppress unused warning
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 5: Negative numbers
|
||||
*/
|
||||
bool test_negative_numbers() {
|
||||
TEST_START();
|
||||
|
||||
constexpr auto data = parse_json<R"({
|
||||
"temperature": -273.15,
|
||||
"count": -42
|
||||
})">();
|
||||
|
||||
static_assert(data.temperature == -273.15);
|
||||
static_assert(data.count == -42);
|
||||
|
||||
ASSERT_EQUAL(data.temperature, -273.15);
|
||||
ASSERT_EQUAL(data.count, -42);
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 6: Whitespace handling
|
||||
*/
|
||||
bool test_whitespace() {
|
||||
TEST_START();
|
||||
|
||||
constexpr auto data = parse_json<R"(
|
||||
{
|
||||
"key1" : "value1" ,
|
||||
"key2" : 42
|
||||
}
|
||||
)">();
|
||||
|
||||
static_assert(std::string_view(data.key1) == "value1");
|
||||
static_assert(data.key2 == 42);
|
||||
|
||||
ASSERT_EQUAL(std::string_view(data.key1), "value1"sv);
|
||||
ASSERT_EQUAL(data.key2, 42);
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 7: Real-time system configuration
|
||||
*/
|
||||
bool test_realtime_config() {
|
||||
TEST_START();
|
||||
|
||||
constexpr auto config = parse_json<R"({
|
||||
"control_loop_hz": 1000,
|
||||
"max_acceleration": 9.8,
|
||||
"min_velocity": -50.0,
|
||||
"max_velocity": 50.0,
|
||||
"enable_safety_checks": true,
|
||||
"log_level": "INFO"
|
||||
})">();
|
||||
|
||||
static_assert(config.control_loop_hz == 1000);
|
||||
static_assert(config.max_acceleration == 9.8);
|
||||
static_assert(config.enable_safety_checks == true);
|
||||
|
||||
ASSERT_EQUAL(config.control_loop_hz, 1000);
|
||||
ASSERT_EQUAL(config.max_acceleration, 9.8);
|
||||
ASSERT_EQUAL(config.min_velocity, -50.0);
|
||||
ASSERT_EQUAL(config.max_velocity, 50.0);
|
||||
ASSERT_TRUE(config.enable_safety_checks);
|
||||
ASSERT_EQUAL(std::string_view(config.log_level), "INFO"sv);
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 8: External JSON file (future #embed support)
|
||||
*/
|
||||
bool test_external_json_embed() {
|
||||
TEST_START();
|
||||
|
||||
// Future C++26 with #embed:
|
||||
// constexpr auto config = parse_json<#embed "test_config.json">();
|
||||
|
||||
// Current workaround - inline the JSON from test_config.json
|
||||
constexpr auto config = parse_json<R"({
|
||||
"system_name": "RealTimeController",
|
||||
"version": "2.1.0",
|
||||
"control_loop_hz": 1000,
|
||||
"max_latency_us": 500,
|
||||
"enable_diagnostics": true,
|
||||
"log_level": "INFO"
|
||||
})">();
|
||||
|
||||
static_assert(std::string_view(config.system_name) == "RealTimeController");
|
||||
static_assert(std::string_view(config.version) == "2.1.0");
|
||||
static_assert(config.control_loop_hz == 1000);
|
||||
static_assert(config.max_latency_us == 500);
|
||||
static_assert(config.enable_diagnostics == true);
|
||||
static_assert(std::string_view(config.log_level) == "INFO");
|
||||
|
||||
ASSERT_EQUAL(std::string_view(config.system_name), "RealTimeController"sv);
|
||||
ASSERT_EQUAL(std::string_view(config.version), "2.1.0"sv);
|
||||
ASSERT_EQUAL(config.control_loop_hz, 1000);
|
||||
ASSERT_EQUAL(config.max_latency_us, 500);
|
||||
ASSERT_TRUE(config.enable_diagnostics);
|
||||
ASSERT_EQUAL(std::string_view(config.log_level), "INFO"sv);
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 9: JSON validation
|
||||
*/
|
||||
bool test_json_validation() {
|
||||
TEST_START();
|
||||
|
||||
static_assert(validate_json<R"({"valid": true})">());
|
||||
static_assert(validate_json<R"({"nested": {"deep": 42}})">());
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 10: Null values
|
||||
*/
|
||||
bool test_null_values() {
|
||||
TEST_START();
|
||||
|
||||
constexpr auto data = parse_json<R"({
|
||||
"nullable_field": null,
|
||||
"number": 42
|
||||
})">();
|
||||
|
||||
static_assert(data.nullable_field == nullptr);
|
||||
static_assert(data.number == 42);
|
||||
|
||||
ASSERT_EQUAL(data.nullable_field, nullptr);
|
||||
ASSERT_EQUAL(data.number, 42);
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 11: Arrays of primitives
|
||||
*/
|
||||
bool test_arrays_primitives() {
|
||||
TEST_START();
|
||||
|
||||
constexpr auto data = parse_json<R"({
|
||||
"values": [1, 2, 3, 4, 5],
|
||||
"flags": [true, false, true]
|
||||
})">();
|
||||
|
||||
static_assert(data.values.size() == 5);
|
||||
static_assert(data.values[0] == 1);
|
||||
static_assert(data.values[4] == 5);
|
||||
static_assert(data.flags.size() == 3);
|
||||
static_assert(data.flags[0] == true);
|
||||
static_assert(data.flags[1] == false);
|
||||
|
||||
ASSERT_EQUAL(data.values.size(), 5);
|
||||
ASSERT_EQUAL(data.values[0], 1);
|
||||
ASSERT_EQUAL(data.values[4], 5);
|
||||
ASSERT_EQUAL(data.flags.size(), 3);
|
||||
ASSERT_TRUE(data.flags[0]);
|
||||
ASSERT_FALSE(data.flags[1]);
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 12: Arrays of objects
|
||||
*/
|
||||
bool test_arrays_of_objects() {
|
||||
TEST_START();
|
||||
|
||||
constexpr auto data = parse_json<R"({
|
||||
"users": [
|
||||
{"name": "Alice", "age": 30},
|
||||
{"name": "Bob", "age": 25}
|
||||
]
|
||||
})">();
|
||||
|
||||
static_assert(data.users.size() == 2);
|
||||
static_assert(std::string_view(data.users[0].name) == "Alice");
|
||||
static_assert(data.users[0].age == 30);
|
||||
static_assert(std::string_view(data.users[1].name) == "Bob");
|
||||
static_assert(data.users[1].age == 25);
|
||||
|
||||
ASSERT_EQUAL(data.users.size(), 2);
|
||||
ASSERT_EQUAL(std::string_view(data.users[0].name), "Alice"sv);
|
||||
ASSERT_EQUAL(data.users[0].age, 30);
|
||||
ASSERT_EQUAL(std::string_view(data.users[1].name), "Bob"sv);
|
||||
ASSERT_EQUAL(data.users[1].age, 25);
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 13: Nested arrays in objects
|
||||
*/
|
||||
bool test_nested_arrays() {
|
||||
TEST_START();
|
||||
|
||||
constexpr auto data = parse_json<R"({
|
||||
"config": {
|
||||
"ports": [8080, 8081, 8082]
|
||||
}
|
||||
})">();
|
||||
|
||||
static_assert(data.config.ports.size() == 3);
|
||||
static_assert(data.config.ports[0] == 8080);
|
||||
static_assert(data.config.ports[2] == 8082);
|
||||
|
||||
ASSERT_EQUAL(data.config.ports.size(), 3);
|
||||
ASSERT_EQUAL(data.config.ports[0], 8080);
|
||||
ASSERT_EQUAL(data.config.ports[2], 8082);
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 14: Complex mixed structure with arrays and nested objects
|
||||
*/
|
||||
bool test_complex_mixed() {
|
||||
TEST_START();
|
||||
|
||||
constexpr auto config = parse_json<R"({
|
||||
"app": "myapp",
|
||||
"version": 1.0,
|
||||
"config": {
|
||||
"ports": [8080, 8081, 8082],
|
||||
"enabled": true
|
||||
},
|
||||
"servers": [
|
||||
{"host": "server1", "port": 3000},
|
||||
{"host": "server2", "port": 3001}
|
||||
]
|
||||
})">();
|
||||
|
||||
static_assert(std::string_view(config.app) == "myapp");
|
||||
static_assert(config.version == 1.0);
|
||||
static_assert(config.config.ports[0] == 8080);
|
||||
static_assert(config.config.enabled == true);
|
||||
static_assert(std::string_view(config.servers[0].host) == "server1");
|
||||
static_assert(config.servers[1].port == 3001);
|
||||
|
||||
ASSERT_EQUAL(std::string_view(config.app), "myapp"sv);
|
||||
ASSERT_EQUAL(config.config.ports[0], 8080);
|
||||
ASSERT_EQUAL(std::string_view(config.servers[0].host), "server1"sv);
|
||||
ASSERT_EQUAL(config.servers[1].port, 3001);
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 15: Empty arrays
|
||||
*/
|
||||
bool test_empty_arrays() {
|
||||
TEST_START();
|
||||
|
||||
constexpr auto data = parse_json<R"({
|
||||
"empty": []
|
||||
})">();
|
||||
|
||||
static_assert(data.empty.size() == 0);
|
||||
|
||||
ASSERT_EQUAL(data.empty.size(), 0);
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool run() {
|
||||
return test_basic_object() &&
|
||||
test_nested_objects() &&
|
||||
test_deeply_nested_objects() &&
|
||||
test_empty_object() &&
|
||||
test_negative_numbers() &&
|
||||
test_whitespace() &&
|
||||
test_realtime_config() &&
|
||||
test_external_json_embed() &&
|
||||
test_json_validation() &&
|
||||
test_null_values() &&
|
||||
test_arrays_primitives() &&
|
||||
test_arrays_of_objects() &&
|
||||
test_nested_arrays() &&
|
||||
test_complex_mixed() &&
|
||||
test_empty_arrays();
|
||||
}
|
||||
|
||||
} // namespace compile_time_json_tests
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
return test_main(argc, argv, compile_time_json_tests::run);
|
||||
#else
|
||||
std::cout << "Compile-time JSON tests require SIMDJSON_STATIC_REFLECTION=ON" << std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
#endif
|
||||
}
|
||||
@@ -375,7 +375,7 @@ namespace array_tests {
|
||||
ASSERT_EQUAL(i*sizeof(uint64_t), sizeof(expected_value));
|
||||
std::vector<int64_t> container(i); // container of size 'i'.
|
||||
|
||||
ASSERT_SUCCESS( doc_result.rewind() );
|
||||
doc_result.rewind();
|
||||
ASSERT_RESULT( doc_result.type(), json_type::array );
|
||||
ASSERT_SUCCESS( doc_result.get(array) );
|
||||
i = 0;
|
||||
@@ -783,7 +783,7 @@ namespace array_tests {
|
||||
for (auto value : array) { (void) value; i++; }
|
||||
ASSERT_EQUAL(i, 0);
|
||||
|
||||
ASSERT_SUCCESS( doc_result.rewind() );
|
||||
doc_result.rewind();
|
||||
ASSERT_RESULT( doc_result.type(), json_type::array );
|
||||
ASSERT_SUCCESS( doc_result.get(array) );
|
||||
i = 0;
|
||||
|
||||
@@ -23,7 +23,7 @@ long page_size() {
|
||||
// page boundary.
|
||||
bool need_allocation(const char *buf, size_t len) {
|
||||
return ((reinterpret_cast<uintptr_t>(buf + len - 1) % page_size())
|
||||
+ simdjson::SIMDJSON_PADDING >= static_cast<uintptr_t>(page_size()));
|
||||
+ simdjson::SIMDJSON_PADDING > static_cast<uintptr_t>(page_size()));
|
||||
}
|
||||
|
||||
bool check_need_allocation() {
|
||||
|
||||
@@ -62,6 +62,57 @@ struct complicated_weather_data {
|
||||
#ifdef __cpp_lib_ranges
|
||||
|
||||
namespace convert_tests {
|
||||
#if SIMDJSON_EXCEPTIONS && SIMDJSON_SUPPORTS_CONCEPTS
|
||||
struct Car {
|
||||
std::string make{};
|
||||
std::string model{};
|
||||
int year{};
|
||||
std::vector<double> tire_pressure{};
|
||||
|
||||
friend simdjson::error_code tag_invoke(simdjson::deserialize_tag, auto &val,
|
||||
Car &car) {
|
||||
simdjson::ondemand::object obj;
|
||||
auto error = val.get_object().get(obj);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
// 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 simdjson::SUCCESS;
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(simdjson::custom_deserializable<std::unique_ptr<Car>>,
|
||||
"It should be deserializable");
|
||||
|
||||
|
||||
simdjson::padded_string json_car =
|
||||
R"( {
|
||||
@@ -79,114 +130,22 @@ simdjson::padded_string json_cars =
|
||||
"tire_pressure": [ 29.8, 30.0 ] }
|
||||
])"_padded;
|
||||
|
||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
||||
struct Car {
|
||||
std::string make{};
|
||||
std::string model{};
|
||||
int year{};
|
||||
std::vector<double> tire_pressure{};
|
||||
|
||||
friend simdjson::error_code tag_invoke(simdjson::deserialize_tag, auto &val,
|
||||
Car &car) {
|
||||
simdjson::ondemand::object obj;
|
||||
auto error = val.get_object().get(obj);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
// 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 simdjson::SUCCESS;
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(simdjson::custom_deserializable<std::unique_ptr<Car>>,
|
||||
"It should be deserializable");
|
||||
|
||||
|
||||
bool simple_no_except() {
|
||||
TEST_START();
|
||||
Car car;
|
||||
ASSERT_SUCCESS(simdjson::from(json_car).get(car));
|
||||
if (car.make != "Toyota" || car.model != "Camry" || car.year != 2018) {
|
||||
return false;
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
bool simple() {
|
||||
TEST_START();
|
||||
Car car = simdjson::from(json_car);
|
||||
if (car.make != "Toyota" || car.model != "Camry" || car.year != 2018) {
|
||||
return false;
|
||||
}
|
||||
#endif // SIMDJSON_SUPPORTS_CONCEPTS
|
||||
#if SIMDJSON_EXCEPTIONS && SIMDJSON_SUPPORTS_CONCEPTS
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
|
||||
bool simple() {
|
||||
TEST_START();
|
||||
Car car = simdjson::from(json_car);
|
||||
if (car.make != "Toyota" || car.model != "Camry" || car.year != 2018) {
|
||||
return false;
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
struct Player {
|
||||
std::string username;
|
||||
int level;
|
||||
double health;
|
||||
};
|
||||
struct BadPlayer {
|
||||
int username; // Oops, should be string!
|
||||
int level;
|
||||
double health;
|
||||
};
|
||||
struct OptionalPlayer {
|
||||
std::string username;
|
||||
std::optional<int> level;
|
||||
double health;
|
||||
};
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
bool missing_key_player() {
|
||||
TEST_START();
|
||||
std::string json = R"({"username":"Alice","health":100.0})";
|
||||
simdjson::padded_string padded(json);
|
||||
Player p;
|
||||
simdjson::ondemand::parser parser;
|
||||
auto doc = parser.iterate(padded);
|
||||
ASSERT_ERROR(doc.get(p), simdjson::NO_SUCH_FIELD);
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
bool missing_key_optional_player() {
|
||||
TEST_START();
|
||||
std::string json = R"({"username":"Alice","health":100.0})";
|
||||
simdjson::padded_string padded(json);
|
||||
OptionalPlayer p;
|
||||
simdjson::ondemand::parser parser;
|
||||
auto doc = parser.iterate(padded);
|
||||
ASSERT_SUCCESS(doc.get(p));
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
bool bad_player() {
|
||||
TEST_START();
|
||||
// username is a string but we declared it as an int
|
||||
@@ -252,7 +211,7 @@ simdjson::padded_string json_cars =
|
||||
}
|
||||
|
||||
|
||||
#endif // SIMDJSON_STATIC_REFLECTION
|
||||
#endif
|
||||
bool broken() {
|
||||
TEST_START();
|
||||
simdjson::padded_string short_json_cars = R"( { "make )"_padded;
|
||||
@@ -469,6 +428,15 @@ bool test_to_adaptor_with_single_value() {
|
||||
if (obj_result.error()) {
|
||||
return false;
|
||||
}
|
||||
/* simdjson::ondemand::object obj = std::move(obj_result.value());
|
||||
|
||||
// We deliberately omit this part because simdjson::to has been removed.
|
||||
auto year_val = obj["year"];
|
||||
int64_t year = simdjson::to<int64_t>(year_val);
|
||||
if (year != 2018) {
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
@@ -497,11 +465,8 @@ bool test_to_vs_from_equivalence() {
|
||||
#endif // SIMDJSON_EXCEPTIONS
|
||||
bool run() {
|
||||
return
|
||||
#if SIMDJSON_STATIC_REFLECTION && SIMDJSON_EXCEPTIONS && SIMDJSON_SUPPORTS_CONCEPTS
|
||||
missing_key_optional_player() && missing_key_player() && meeting_time_test() && meeting_test() && bad_player() && good_player() && complicated_weather_test() &&
|
||||
#endif
|
||||
#if SIMDJSON_SUPPORTS_CONCEPTS
|
||||
simple_no_except() &&
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
meeting_time_test() && meeting_test() && bad_player() && good_player() && complicated_weather_test() &&
|
||||
#endif
|
||||
#if SIMDJSON_EXCEPTIONS && SIMDJSON_SUPPORTS_CONCEPTS
|
||||
broken() && simple() && simple_optional() && with_parser() && to_array() &&
|
||||
|
||||
@@ -485,7 +485,7 @@ namespace object_tests {
|
||||
i++;
|
||||
}
|
||||
ASSERT_EQUAL( i*sizeof(uint64_t), sizeof(expected_value) );
|
||||
ASSERT_SUCCESS( doc_result.rewind() );
|
||||
doc_result.rewind();
|
||||
ASSERT_RESULT( doc_result.type(), json_type::object );
|
||||
ASSERT_SUCCESS( doc_result.get(object) );
|
||||
i = 0;
|
||||
|
||||
@@ -233,47 +233,10 @@ bool to_string_example() {
|
||||
ondemand::parser parser;
|
||||
ondemand::document doc = parser.iterate(json);
|
||||
std::string name;
|
||||
ASSERT_SUCCESS(doc["name"].get_string(name));
|
||||
doc["name"].get_string(name);
|
||||
ASSERT_EQUAL(name, "Daniel");
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
bool partial_car_extract() {
|
||||
TEST_START();
|
||||
auto json = R"( {
|
||||
"make": "Toyota",
|
||||
"model": "Camry",
|
||||
"year": 2024,
|
||||
"tire_pressure": [ 40.1, 39.9 ]
|
||||
} )"_padded;
|
||||
ondemand::parser parser;
|
||||
ondemand::document doc = parser.iterate(json);
|
||||
Car car{};
|
||||
ASSERT_SUCCESS( (doc.extract_into<"make","model">(car)) );
|
||||
ASSERT_EQUAL(car.make, "Toyota");
|
||||
ASSERT_EQUAL(car.model, "Camry");
|
||||
ASSERT_EQUAL(car.year, 0); // Not extracted
|
||||
ASSERT_EQUAL(car.tire_pressure.size(), 0); // Not extracted
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool temperature_example() {
|
||||
TEST_START();
|
||||
struct complicated_weather_data {
|
||||
std::vector<std::string> time;
|
||||
std::vector<float> temperature;
|
||||
};
|
||||
auto padded = R"({"time":["2023-03-15T12:00:00Z"],"temperature":[42]})"_padded;
|
||||
simdjson::ondemand::parser parser;
|
||||
simdjson::ondemand::document doc = parser.iterate(padded);
|
||||
complicated_weather_data p = doc.get<complicated_weather_data>();
|
||||
ASSERT_EQUAL(p.time.size(), 1);
|
||||
ASSERT_EQUAL(p.time[0], "2023-03-15T12:00:00Z");
|
||||
ASSERT_EQUAL(p.temperature.size(), 1);
|
||||
ASSERT_EQUAL(p.temperature[0], 42);
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
#endif // SIMDJSON_STATIC_REFLECTION
|
||||
|
||||
bool gen_raw1() {
|
||||
TEST_START();
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"system_name": "RealTimeController",
|
||||
"version": "2.1.0",
|
||||
"control_loop_hz": 1000,
|
||||
"max_latency_us": 500,
|
||||
"enable_diagnostics": true,
|
||||
"log_level": "INFO"
|
||||
}
|
||||
+1
-1
@@ -130,7 +130,7 @@ simdjson_inline bool assert_iterate_error(T &arr, simdjson::error_code expected,
|
||||
#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_RESULT(ACTUAL, EXPECTED) do { if (!::assert_result ((ACTUAL), (EXPECTED), #ACTUAL)) { return false; } } while (0);
|
||||
#define ASSERT_SUCCESS(...) do { if (!::assert_success((__VA_ARGS__), #__VA_ARGS__)) { return false; } } while (0);
|
||||
#define ASSERT_SUCCESS(ACTUAL) do { if (!::assert_success((ACTUAL), #ACTUAL)) { return false; } } while (0);
|
||||
#define ASSERT_FAILURE(ACTUAL) do { if (::assert_success((ACTUAL), #ACTUAL)) { return false; } } while (0);
|
||||
#define ASSERT_ERROR(ACTUAL, EXPECTED) do { if (!::assert_error ((ACTUAL), (EXPECTED), #ACTUAL)) { return false; } } while (0);
|
||||
#define ASSERT_TRUE(ACTUAL) do { if (!::assert_true ((ACTUAL), #ACTUAL)) { return false; } } while (0);
|
||||
|
||||
Reference in New Issue
Block a user