Compare commits

...

13 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
Daniel Lemire 1f369ef210 minor patch which allows us to pass mutable strings to simdjson::from… (#2448)
* minor patch which allows us to pass mutable strings to simdjson::from and fix
an issue with ambiguous integrals

* compatibility patch.
2025-09-15 22:22:25 -06:00
Daniel Lemire 22dcdc9f1e Update README.md 2025-09-15 18:52:15 -06:00
Dirk Stolle dda2dafa30 fix some typos (#2446) 2025-09-15 18:23:13 -06:00
Daniel Lemire 72e9d44e10 fixing indent 2025-09-15 17:08:12 -06:00
Daniel Lemire 8aae14931d release candidate 4.0.2 (#2441)
* release candidate 4.0.2

* more fixes

* fixing typos

* adding macro check
2025-09-15 09:17:43 -06:00
Daniel Lemire ef3d1ac25f fixing 2440 2025-09-14 08:09:27 -06:00
Daniel Lemire 9292480a9b removing leftover <experimental/...> (#2438) 2025-09-14 08:00:26 -06:00
Daniel Lemire 611ea97dbd Add C++26 reflection example link to README 2025-09-12 21:22:45 -04:00
Daniel Lemire f249e7e128 patch release 2025-09-12 19:26:58 -04:00
Marian Klymov 44fddaa807 Replace POSITION_INDEPENDENT_CODE with INTERFACE_POSITION_INDEPENDENT_CODE for older CMake (#2437) 2025-09-12 19:26:22 -04:00
Daniel Lemire be16e1ae04 removing space 2025-09-11 20:58:31 -04:00
22 changed files with 889 additions and 253 deletions
@@ -0,0 +1,22 @@
name: Ubuntu 24.04 CI (CXX 20, noexcept)
on: [push, pull_request]
jobs:
ubuntu-build:
if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]')
runs-on: ubuntu-24.04
strategy:
matrix:
cxx: [g++-13, clang++-16]
steps:
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
- name: Prepare
run: cmake -DSIMDJSON_CXX_STANDARD=20 -DSIMDJSON_EXCEPTIONS=OFF -DSIMDJSON_DEVELOPER_MODE=ON -B build
env:
CXX: ${{matrix.cxx}}
- name: Build
run: cmake --build build -j=2
- name: Test
run: ctest --output-on-failure --test-dir 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.0
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.0"
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
+2
View File
@@ -117,8 +117,10 @@ Godbolt
-------------
Some users may want to browse code along with the compiled assembly. You want to check out the following lists of examples:
* [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
-------------------
+34 -30
View File
@@ -6,38 +6,42 @@ CPMAddPackage(
EXCLUDE_FROM_ALL YES
)
option(SIMDJSON_USE_RUST "Build the static_reflect benchmark" OFF)
if(NOT WIN32)
# We want the check whether Rust is available before trying to build a crate.
CPMAddPackage(
NAME corrosion
GITHUB_REPOSITORY corrosion-rs/corrosion
VERSION 0.4.4
DOWNLOAD_ONLY ON
OPTIONS "Rust_FIND_QUIETLY OFF"
)
include("${corrosion_SOURCE_DIR}/cmake/FindRust.cmake")
endif()
if(RUST_FOUND)
message(STATUS "Rust found: " ${Rust_VERSION} )
add_subdirectory("${corrosion_SOURCE_DIR}" "${PROJECT_BINARY_DIR}/_deps/corrosion" EXCLUDE_FROM_ALL)
# Important: we want to build in release mode!
corrosion_import_crate(MANIFEST_PATH "serde-benchmark/Cargo.toml" NO_LINKER_OVERRIDE PROFILE release)
else()
message(STATUS "Rust/Cargo is unavailable." )
message(STATUS "We will not benchmark serde-benchmark." )
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
message(STATUS "Under macOS, you may be able to install rust with")
message(STATUS "curl https://sh.rustup.rs -sSf | sh")
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
message(STATUS "Under Linux, you may be able to install rust with a command such as")
message(STATUS "apt-get install cargo" )
message(STATUS "or" )
message(STATUS "curl https://sh.rustup.rs -sSf | sh")
if(SIMDJSON_USER_RUST)
if(NOT WIN32)
# We want the check whether Rust is available before trying to build a crate.
CPMAddPackage(
NAME corrosion
GITHUB_REPOSITORY corrosion-rs/corrosion
VERSION 0.4.4
DOWNLOAD_ONLY ON
OPTIONS "Rust_FIND_QUIETLY OFF"
)
include("${corrosion_SOURCE_DIR}/cmake/FindRust.cmake")
endif()
endif()
if(RUST_FOUND)
message(STATUS "Rust found: " ${Rust_VERSION} )
add_subdirectory("${corrosion_SOURCE_DIR}" "${PROJECT_BINARY_DIR}/_deps/corrosion" EXCLUDE_FROM_ALL)
# Important: we want to build in release mode!
corrosion_import_crate(MANIFEST_PATH "serde-benchmark/Cargo.toml" NO_LINKER_OVERRIDE PROFILE release)
else()
message(STATUS "Rust/Cargo is unavailable." )
message(STATUS "We will not benchmark serde-benchmark." )
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
message(STATUS "Under macOS, you may be able to install rust with")
message(STATUS "curl https://sh.rustup.rs -sSf | sh")
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
message(STATUS "Under Linux, you may be able to install rust with a command such as")
message(STATUS "apt-get install cargo" )
message(STATUS "or" )
message(STATUS "curl https://sh.rustup.rs -sSf | sh")
endif()
endif()
else(SIMDJSON_USER_RUST)
message(STATUS "We will not benchmark serde-benchmark." )
endif(SIMDJSON_USER_RUST)
# Add the benchmark executable targets
add_subdirectory(twitter_benchmark)
+1 -1
View File
@@ -4,7 +4,7 @@
add_library(simdjson-internal-flags INTERFACE)
if(NOT DEFINED CMAKE_POSITION_INDEPENDENT_CODE)
# We default to ON for all targets, so that we can use the library in shared libraries.
set_target_properties(simdjson-internal-flags PROPERTIES POSITION_INDEPENDENT_CODE ON)
set_target_properties(simdjson-internal-flags PROPERTIES INTERFACE_POSITION_INDEPENDENT_CODE ON)
endif(NOT DEFINED CMAKE_POSITION_INDEPENDENT_CODE)
option(SIMDJSON_CHECK_EOF "Check for the end of the input buffer. The setting is unnecessary since we require padding of the inputs. You should expect tests to fail with this option turned on." OFF)
+3 -3
View File
@@ -1395,12 +1395,12 @@ Car c = doc.get<Car>();
We try to automate the parsing of any given structure or class
by looking at its non-static public members. At compile-time,
the library looks at a simple structre like `Car` and
the library looks at a simple structure like `Car` and
maps it to parsing code. We call the default constructor,
and then assign values to the public members.
#### Special cases
#### Special cases
However, there are instances where the construction cannot
be easily automated. Let us consider a class without any
@@ -1450,7 +1450,7 @@ struct complicated_weather_data {
};
```
The code might as simple as the following.
The code might be as simple as the following.
```cpp
auto padded = R"({"time":["2023-03-15T12:00:00Z"],"temperature":[42]})"_padded;
+1 -1
View File
@@ -41,7 +41,7 @@ Parameters:
- `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
After writting the content, if you have reasons to believe that the content might violate UTF-8 conventions, you can check it as follows:
After writing the content, if you have reasons to believe that the content might violate UTF-8 conventions, you can check it as follows:
- `validate_unicode()`: Checks if the content in the JSON buffer is valid UTF-8. Returns: true if the content is valid UTF-8, false otherwise.
+1 -1
View File
@@ -129,7 +129,7 @@ Once you have an element, you can navigate it with idiomatic C++ iterators, oper
The strings contain unescaped valid UTF-8 strings: no unmatched surrogate is allowed.
Internally, numbers are stored as either 64-bit integers or 64-bit floating-point numbers.
Thus it is possible to get the full 64-bit integer range (either signed or unsigned).
By default, the string `-0` is parsed as the integer 0 as in Pytho or C++. If you set the macro
By default, the string `-0` is parsed as the integer 0 as in Python or C++. If you set the macro
`SIMDJSON_MINUS_ZERO_AS_FLOAT` to `1` when building simdjson, you can get that `-0` is mapped to `-0.0`
as in JavaScript. You can get the desired effect by building simdjson with cmake setting the
`SIMDJSON_MINUS_ZERO_AS_FLOAT` to on: `cmake -B build -D SIMDJSON_MINUS_ZERO_AS_FLOAT=ON`.
+10
View File
@@ -116,6 +116,16 @@ template <typename T>
inline auto to_adaptor<T>::operator()(ondemand::parser &parser, padded_string_view const str) const noexcept {
return auto_parser<ondemand::parser *>{parser, str};
}
template <typename T>
inline auto to_adaptor<T>::operator()(std::string str) const noexcept {
return auto_parser{pad_with_reserve(str)};
}
template <typename T>
inline auto to_adaptor<T>::operator()(ondemand::parser &parser, std::string str) const noexcept {
return auto_parser<ondemand::parser *>{parser, pad_with_reserve(str)};
}
} // namespace internal
} // namespace convert
} // namespace simdjson
+5
View File
@@ -72,6 +72,11 @@ struct to_adaptor {
T operator()(simdjson_result<ondemand::value> &val) const noexcept;
auto operator()(padded_string_view const str) const noexcept;
auto operator()(ondemand::parser &parser, padded_string_view const str) const noexcept;
// The std::string is padded with reserve to ensure there is enough space for padding.
// Some sanitizers may not like this, so you can use simdjson::pad instead.
// simdjson::from(simdjson::pad(str))
auto operator()(std::string str) const noexcept;
auto operator()(ondemand::parser &parser, std::string str) const noexcept;
};
} // namespace internal
} // namespace convert
@@ -12,8 +12,9 @@
#if SIMDJSON_STATIC_REFLECTION
#include <charconv>
#include <chrono>
#include <cstring>
#include <experimental/meta>
#include <meta>
#include <memory>
#include <optional>
#include <string_view>
@@ -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> &&
@@ -284,10 +284,12 @@ simdjson_inline void string_builder::clear() noexcept {
namespace internal {
// We could specialize further for 32-bit integers.
simdjson_really_inline int int_log2(uint32_t x) { return (63 - leading_zeroes(x | 1)); }
simdjson_really_inline int fast_digit_count(uint32_t x) {
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 fast_digit_count_32(uint32_t x) {
static uint64_t table[] = {
4294967296, 8589934582, 8589934582, 8589934582, 12884901788,
12884901788, 12884901788, 17179868184, 17179868184, 17179868184,
@@ -299,9 +301,8 @@ simdjson_really_inline int fast_digit_count(uint32_t x) {
return uint32_t((x + table[int_log2(x)]) >> 32);
}
simdjson_really_inline int int_log2(uint64_t x) { return 63 - leading_zeroes(x | 1); }
simdjson_really_inline int fast_digit_count(uint64_t x) {
simdjson_really_inline int fast_digit_count_64(uint64_t x) {
static uint64_t table[] = {9,
99,
999,
@@ -332,7 +333,11 @@ simdjson_really_inline size_t digit_count(number_type v) noexcept {
static_assert(sizeof(number_type) == 8 || sizeof(number_type) == 4 ||
sizeof(number_type) == 2 || sizeof(number_type) == 1,
"We only support 8-bit, 16-bit, 32-bit and 64-bit numbers");
return fast_digit_count(v);
SIMDJSON_IF_CONSTEXPR(sizeof(number_type) <= 4) {
return fast_digit_count_32(static_cast<uint32_t>(v));
} 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,
@@ -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 {
@@ -253,7 +273,9 @@ error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept(nothrow_deser
using value_type = typename std::remove_cvref_t<T>::value_type;
// Check if the value is null
if (val.is_null()) {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset(); // Set to nullopt
return SUCCESS;
}
@@ -286,7 +308,7 @@ error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept {
}
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)) {
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 succesful or the field is not found, we continue
@@ -361,7 +383,9 @@ error_code tag_invoke(deserialize_tag, simdjson_value &val, std::shared_ptr<T> &
// Unique pointers
////////////////////////////////////////
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<bool> &out) noexcept {
if (val.is_null()) {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
@@ -374,7 +398,9 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<bool> &out) no
}
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<int64_t> &out) noexcept {
if (val.is_null()) {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
@@ -387,7 +413,9 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<int64_t> &out)
}
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<uint64_t> &out) noexcept {
if (val.is_null()) {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
@@ -400,7 +428,9 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<uint64_t> &out
}
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<double> &out) noexcept {
if (val.is_null()) {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
@@ -413,7 +443,9 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<double> &out)
}
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<std::string_view> &out) noexcept {
if (val.is_null()) {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
@@ -430,7 +462,9 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<std::string_vi
// Shared pointers
////////////////////////////////////////
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<bool> &out) noexcept {
if (val.is_null()) {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
@@ -443,7 +477,9 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<bool> &out) no
}
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<int64_t> &out) noexcept {
if (val.is_null()) {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
@@ -456,7 +492,9 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<int64_t> &out)
}
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<uint64_t> &out) noexcept {
if (val.is_null()) {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
@@ -469,7 +507,9 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<uint64_t> &out
}
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<double> &out) noexcept {
if (val.is_null()) {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
@@ -482,7 +522,9 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<double> &out)
}
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<std::string_view> &out) noexcept {
if (val.is_null()) {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
@@ -504,7 +546,9 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<std::string_vi
////////////////////////////////////////
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<std::string> &out) noexcept {
// Check if the value is null
if (val.is_null()) {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset(); // Set to nullptr
return SUCCESS;
}
@@ -520,7 +564,9 @@ error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<std::string> &
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<std::string> &out) noexcept {
// Check if the value is null
if (val.is_null()) {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset(); // Set to nullptr
return SUCCESS;
}
@@ -536,7 +582,9 @@ error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<std::string> &
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<int> &out) noexcept {
// Check if the value is null
if (val.is_null()) {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset(); // Set to nullptr
return SUCCESS;
}
+3 -1
View File
@@ -79,7 +79,9 @@ public:
using value_type = typename std::remove_cvref_t<T>::value_type;
// Check if the value is null
if (is_null()) {
bool is_null_value;
SIMDJSON_TRY( is_null().get(is_null_value) );
if (is_null_value) {
out.reset(); // Set to nullopt
return SUCCESS;
}
+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.0"
#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 = 0
SIMDJSON_VERSION_REVISION = 3
};
} // namespace simdjson
+1 -1
View File
@@ -1,4 +1,4 @@
/* auto-generated on 2025-09-09 18:44:21 -0400. version 4.0.0 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
+482 -187
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.
@@ -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;
}
+2 -1
View File
@@ -8,7 +8,7 @@
using namespace simdjson;
namespace ranges_test {
#if SIMDJSON_EXCEPTIONS
bool printout() {
TEST_START();
auto cars_json = R"( [
@@ -43,6 +43,7 @@ namespace ranges_test {
ASSERT_TRUE(std::ranges::equal(cars, expected));
TEST_SUCCEED();
}
#endif // SIMDJSON_EXCEPTIONS
bool run() {
return
+52 -1
View File
@@ -2,6 +2,7 @@
#include "simdjson/convert.h"
#include "test_ondemand.h"
#include <chrono>
#include <map>
#include <ranges>
#include <string>
@@ -21,6 +22,24 @@ private:
std::string date_str;
};
struct Meeting {
std::string title;
std::vector<std::string> attendees;
std::optional<std::string> location;
bool is_recurring;
};
struct MeetingTime {
std::string title;
std::chrono::system_clock::time_point start_time;
std::vector<std::string> attendees;
std::optional<std::string> location;
bool is_recurring;
};
namespace simdjson {
template <typename simdjson_value>
auto tag_invoke(deserialize_tag, simdjson_value &val, MyDate& date) {
@@ -160,6 +179,38 @@ bool simple() {
ASSERT_SUCCESS(doc.get(p));
TEST_SUCCEED();
}
bool meeting_test() {
TEST_START();
std::string json = simdjson::to_json(Meeting{
.title = "CppCon Planning",
.attendees = {"Alice", "Bob", "Charlie"},
.location = "Denver",
.is_recurring = true
});
Meeting m2 = simdjson::from(json);
std::cout << m2.title << std::endl;
TEST_SUCCEED();
}
bool meeting_time_test() {
TEST_START();
auto start_time = std::chrono::system_clock::now();
std::string json = simdjson::to_json(MeetingTime{
.title = "CppCon Planning",
.start_time = start_time,
.attendees = {"Alice", "Bob", "Charlie"},
.location = "Denver",
.is_recurring = true
});
std::cout << json << std::endl;
MeetingTime m2 = simdjson::from(json);
//ASSERT_EQUAL(m2.start_time, start_time);
std::cout << m2.title << std::endl;
TEST_SUCCEED();
}
#endif
bool broken() {
TEST_START();
@@ -415,7 +466,7 @@ bool test_to_vs_from_equivalence() {
bool run() {
return
#if SIMDJSON_STATIC_REFLECTION
bad_player() && good_player() && complicated_weather_test() &&
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() &&