Compare commits

..

2 Commits

Author SHA1 Message Date
Daniel Lemire d293aa3ff2 Merge branch 'master' into francisco/fix_datetime 2025-09-16 13:42:22 -06:00
Francisco Geiman Thiesen d5a7918e0d Adding fix for chrono 2025-09-16 11:26:19 -06:00
21 changed files with 395 additions and 886 deletions
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.14)
project(
simdjson
# The version number is modified by tools/release.py
VERSION 4.0.5
VERSION 4.0.3
DESCRIPTION "Parsing gigabytes of JSON per second"
HOMEPAGE_URL "https://simdjson.org/"
LANGUAGES CXX C
+1 -1
View File
@@ -38,7 +38,7 @@ PROJECT_NAME = simdjson
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = "4.0.5"
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
+1 -1
View File
@@ -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
-------------------
-6
View File
@@ -1477,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
+10 -32
View File
@@ -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,17 +175,6 @@ 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.
@@ -251,17 +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.
### Without `string_buffer` instance but with explicit error handling
@@ -270,7 +248,7 @@ 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
+6 -6
View File
@@ -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,6 +73,12 @@ inline simdjson_result<ondemand::number> auto_parser<parser_type>::number() noex
return result<ondemand::number>();
}
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) {
+3 -5
View File
@@ -46,16 +46,14 @@ 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>);
template <typename T>
simdjson_warn_unused simdjson_inline explicit(false) operator simdjson_result<T>() noexcept(is_nothrow_gettable<T>);
template <typename T>
simdjson_warn_unused simdjson_inline explicit(false) operator T() noexcept(false);
@@ -12,6 +12,7 @@
#if SIMDJSON_STATIC_REFLECTION
#include <charconv>
#include <chrono>
#include <cstring>
#include <meta>
#include <memory>
@@ -90,6 +91,20 @@ 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> && !container_but_not_string<T> &&
!concepts::string_view_keyed_map<T> &&
@@ -263,7 +278,7 @@ void append(string_builder &b, const Z &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;
@@ -272,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; }
@@ -290,13 +305,9 @@ string_builder& operator<<(string_builder& b, const Z& z) {
} // 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_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) {
return SIMDJSON_IMPLEMENTATION::builder::to_json(z, s, initial_capacity);
}
} // namespace simdjson
#endif // SIMDJSON_STATIC_REFLECTION
@@ -475,12 +475,6 @@ simdjson_inline void string_builder::escape_and_append_with_quotes(const char* i
std::string_view cinput(input);
escape_and_append_with_quotes(cinput);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template<internal::fixed_string key>
simdjson_inline void string_builder::escape_and_append_with_quotes() noexcept {
escape_and_append_with_quotes(internal::string_constant<key>::value);
}
#endif
simdjson_inline void string_builder::append_raw(const char *c) noexcept {
size_t len = std::strlen(c);
@@ -660,25 +654,6 @@ simdjson_inline void string_builder::append_key_value(key_type key, value_type v
}
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template<internal::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
@@ -12,26 +12,7 @@
namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
namespace builder {
#if SIMDJSON_SUPPORTS_CONCEPTS
// Helper to create string constants
namespace internal {
template <std::size_t N>
struct fixed_string {
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 <fixed_string str>
struct string_constant {
static constexpr std::string_view value = str.view();
};
} // namespace internal
#endif // SIMDJSON_SUPPORTS_CONCEPTS
/**
* 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
@@ -43,9 +24,7 @@ struct string_constant {
*/
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
@@ -83,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<internal::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.
@@ -140,12 +116,9 @@ 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<internal::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>
simdjson_inline void append(const T &opt);
@@ -259,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;
@@ -267,16 +240,6 @@ 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
@@ -8,6 +8,7 @@
#include "simdjson/generic/ondemand/base.h"
#endif // SIMDJSON_CONDITIONAL_INCLUDE
#include <chrono>
#include <concepts>
#include <limits>
#if SIMDJSON_STATIC_REFLECTION
@@ -46,6 +47,25 @@ 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 {
@@ -291,7 +311,7 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept {
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));
// Note: removed static assert as optional types are now handled generically
// as long we are successful or the field is not found, we continue
// 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:]);
}
+2 -2
View File
@@ -4,7 +4,7 @@
#define SIMDJSON_SIMDJSON_VERSION_H
/** The version of simdjson being used (major.minor.revision) */
#define SIMDJSON_VERSION "4.0.5"
#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 = 5
SIMDJSON_VERSION_REVISION = 3
};
} // namespace simdjson
+1 -1
View File
@@ -1,4 +1,4 @@
/* auto-generated on 2025-09-18 15:16:38 -0600. version 4.0.5 Do not edit! */
/* auto-generated on 2025-09-15 19:47:29 -0600. version 4.0.3 Do not edit! */
/* including simdjson.cpp: */
/* begin file simdjson.cpp */
#define SIMDJSON_SRC_SIMDJSON_CPP
+84 -614
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.
+5 -66
View File
@@ -331,6 +331,8 @@ namespace builder_tests {
TEST_SUCCEED();
}
void serialize_car(const Car& car, simdjson::builder::string_builder& builder) {
// start of JSON
builder.start_object();
@@ -367,43 +369,7 @@ namespace builder_tests {
builder.end_object();
}
#if SIMDJSON_SUPPORTS_CONCEPTS
void serialize_car_template(const Car& car, simdjson::builder::string_builder& builder) {
// start of JSON
builder.start_object();
// "make"
builder.append_key_value<"make">(car.make);
builder.append_comma();
// "model"
builder.append_key_value<"model">(car.model);
builder.append_comma();
// "year"
builder.append_key_value<"year">(car.year);
builder.append_comma();
// "tire_pressure"
builder.escape_and_append_with_quotes<"tire_pressure">();
builder.append_colon();
builder.start_array();
// vector tire_pressure
for (size_t i = 0; i < car.tire_pressure.size(); ++i) {
builder.append(car.tire_pressure[i]);
if (i < car.tire_pressure.size() - 1) {
builder.append_comma();
}
}
// end of array
builder.end_array();
// end of object
builder.end_object();
}
#endif
bool car_test() {
TEST_START();
simdjson::builder::string_builder sb;
@@ -415,18 +381,7 @@ namespace builder_tests {
ASSERT_EQUAL(p, "{\"make\":\"Toyota\",\"model\":\"Corolla\",\"year\":2017,\"tire_pressure\":[30.0,30.2,30.513,30.79]}");
TEST_SUCCEED();
}
#if SIMDJSON_SUPPORTS_CONCEPTS
bool car_test_template() {
TEST_START();
simdjson::builder::string_builder sb;
Car c = {"Toyota", "Corolla", 2017, {30.0,30.2,30.513,30.79}};
serialize_car_template(c, sb);
std::string_view p;
auto result = sb.view().get(p);
ASSERT_SUCCESS(result);
ASSERT_EQUAL(p, "{\"make\":\"Toyota\",\"model\":\"Corolla\",\"year\":2017,\"tire_pressure\":[30.0,30.2,30.513,30.79]}");
TEST_SUCCEED();
}
#if SIMDJSON_SUPPORTS_CONCEPTS
bool serialize_optional() {
TEST_START();
simdjson::builder::string_builder sb;
@@ -444,7 +399,7 @@ namespace builder_tests {
ASSERT_EQUAL(p, "null");
TEST_SUCCEED();
}
#endif
#endif
#if SIMDJSON_SUPPORTS_RANGES && SIMDJSON_SUPPORTS_CONCEPTS
void serialize_car_simple(const Car& car, simdjson::builder::string_builder& builder) {
@@ -519,20 +474,6 @@ namespace builder_tests {
ASSERT_EQUAL(s, "[[1.0,2.0],[3.0,4.0]]");
TEST_SUCCEED();
}
bool double_double_test_to_string() {
TEST_START();
std::vector<std::vector<double>> c = {{1.0, 2.0}, {3.0, 4.0}};
simdjson::builder::string_builder sb;
sb.append(c);
std::string_view p;
auto result = sb.view().get(p);
ASSERT_SUCCESS(result);
ASSERT_EQUAL(p, "[[1.0,2.0],[3.0,4.0]]");
std::string s;
simdjson::simdjson_error ec = simdjson::to_json(c,s);
ASSERT_EQUAL(s, "[[1.0,2.0],[3.0,4.0]]");
TEST_SUCCEED();
}
#if SIMDJSON_EXCEPTIONS
bool car_test_simple_complete_exceptions() {
TEST_START();
@@ -581,7 +522,6 @@ namespace builder_tests {
#if SIMDJSON_SUPPORTS_RANGES && SIMDJSON_SUPPORTS_CONCEPTS
map_test() &&
double_double_test() &&
double_double_test_to_string() &&
car_test_simple() &&
car_test_simple_complete() &&
#if SIMDJSON_EXCEPTIONS
@@ -589,8 +529,7 @@ namespace builder_tests {
#endif
#endif
#if SIMDJSON_SUPPORTS_CONCEPTS
car_test_template() &&
serialize_optional() &&
serialize_optional() &&
#endif
append_char() &&
append_integer() &&
@@ -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;
}
+1 -1
View File
@@ -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
+58 -73
View File
@@ -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,78 +130,15 @@ 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 BadPlayer {
int username; // Oops, should be string!
@@ -480,9 +468,6 @@ bool run() {
#if SIMDJSON_STATIC_REFLECTION
meeting_time_test() && meeting_test() && bad_player() && good_player() && complicated_weather_test() &&
#endif
#if SIMDJSON_SUPPORTS_CONCEPTS
simple_no_except() &&
#endif
#if SIMDJSON_EXCEPTIONS && SIMDJSON_SUPPORTS_CONCEPTS
broken() && simple() && simple_optional() && with_parser() && to_array() &&
to_bad_array() &&