Compare commits

...

3 Commits

Author SHA1 Message Date
Daniel Lemire b2932d1b8f release candidate 4.0.6 (#2464) 2025-09-21 08:13:48 -06:00
Daniel Lemire a7811090ef fixing issue 2458 (#2461) 2025-09-20 22:23:09 -06:00
Daniel Lemire 3320885fac Fixing issue 2462 (#2463)
* fun

* progress

* completing the documentation
2025-09-20 22:22:57 -06:00
20 changed files with 1135 additions and 685 deletions
+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.6
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.6"
# 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
+4
View File
@@ -174,6 +174,10 @@ 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)
+3
View File
@@ -1400,6 +1400,9 @@ 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`).
#### Special cases
However, there are instances where the construction cannot
+67
View File
@@ -190,6 +190,38 @@ 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
------------------------
@@ -275,4 +307,39 @@ pattern:
} 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
```
+21
View File
@@ -122,11 +122,32 @@ 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>;
};
} // 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
+4 -2
View File
@@ -79,6 +79,7 @@ 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 T() noexcept(false) {
@@ -87,6 +88,7 @@ inline auto_parser<parser_type>::operator T() noexcept(false) {
}
return m_doc.get<T>();
}
#endif // SIMDJSON_EXCEPTIONS
template <typename parser_type>
template <typename T>
@@ -109,7 +111,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{str};
return auto_parser<ondemand::parser *>{str};
}
template <typename T>
@@ -119,7 +121,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{pad_with_reserve(str)};
return auto_parser<ondemand::parser *>{pad_with_reserve(str)};
}
template <typename T>
+5 -2
View File
@@ -54,10 +54,11 @@ public:
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>);
@@ -80,6 +81,8 @@ 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
@@ -7,55 +7,8 @@
#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
@@ -38,7 +38,7 @@ concept container_but_not_string =
!std::is_same_v<T, std::string_view> && !std::is_same_v<T, const char *>;
template <class T>
requires(container_but_not_string<T>)
requires(container_but_not_string<T> && !require_custom_serialization<T>)
constexpr void atom(string_builder &b, const T &t) {
if (t.size() == 0) {
b.append_raw("[]");
@@ -63,6 +63,7 @@ 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("{}");
@@ -99,7 +100,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>)
!std::is_same_v<T, char> && !require_custom_serialization<T>)
constexpr void atom(string_builder &b, const T &t) {
int i = 0;
b.append('{');
@@ -117,6 +118,7 @@ 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());
@@ -127,6 +129,7 @@ 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);
@@ -137,7 +140,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>)
requires(std::is_enum_v<T> && !require_custom_serialization<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));
@@ -161,7 +164,7 @@ template <concepts::appendable_containers 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*>)
!std::is_same_v<T, std::string_view> && !std::is_same_v<T, const char*> && !require_custom_serialization<T>)
constexpr void atom(string_builder &b, const T &container) {
if (container.empty()) {
b.append_raw("[]");
@@ -196,11 +199,13 @@ 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);
}
@@ -209,12 +214,13 @@ template <concepts::appendable_containers 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*>)
!std::is_same_v<T, std::string_view> && !std::is_same_v<T, const char*> && !require_custom_serialization<T>)
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);
}
@@ -229,7 +235,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>)
!std::is_same_v<Z, char> && !require_custom_serialization<Z>)
void append(string_builder &b, const Z &z) {
int i = 0;
b.append('{');
@@ -247,7 +253,7 @@ void append(string_builder &b, const Z &z) {
// works for container
template <class Z>
requires(container_but_not_string<Z>)
requires(container_but_not_string<Z> && !require_custom_serialization<Z>)
void append(string_builder &b, const Z &z) {
if (z.size() == 0) {
b.append_raw("[]");
@@ -262,6 +268,13 @@ void append(string_builder &b, const Z &z) {
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) {
string_builder b(initial_capacity);
@@ -360,6 +360,7 @@ static const char decimal_table[200] = {
};
} // namespace internal
template <typename number_type, typename>
simdjson_inline void string_builder::append(number_type v) noexcept {
static_assert(std::is_same<number_type, bool>::value ||
@@ -505,6 +506,7 @@ 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);
@@ -512,9 +514,17 @@ 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 )
std::is_same<T, const char*>::value)
simdjson_inline void string_builder::append(const T &value) {
escape_and_append_with_quotes(value);
}
@@ -10,6 +10,38 @@
#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 {
#if SIMDJSON_SUPPORTS_CONCEPTS
@@ -17,7 +49,7 @@ namespace builder {
namespace internal {
template <std::size_t N>
struct fixed_string {
constexpr fixed_string(const char (&str)[N]) {
constexpr fixed_string(const char (&str)[N]) {
for (std::size_t i = 0; i < N; ++i) {
data[i] = str[i];
}
@@ -25,6 +57,8 @@ struct fixed_string {
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 {
@@ -54,7 +88,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;
/**
@@ -148,8 +182,13 @@ public:
// 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 ||
@@ -279,6 +318,8 @@ simdjson_warn_unused simdjson_error to_json(const Z &z, std::string &s, size_t i
}
#endif
#if SIMDJSON_SUPPORTS_CONCEPTS
#endif // SIMDJSON_SUPPORTS_CONCEPTS
} // namespace simdjson
@@ -16,15 +16,12 @@
#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>;
@@ -38,7 +35,6 @@ 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));
@@ -47,7 +43,6 @@ error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
}
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>;
@@ -77,7 +72,6 @@ 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));
@@ -94,7 +88,6 @@ 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(
@@ -140,7 +133,6 @@ 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(
@@ -222,7 +214,6 @@ 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;
@@ -248,7 +239,6 @@ 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;
@@ -274,7 +264,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> && !require_custom_serialization<T>);
!concepts::appendable_containers<T>);
template <typename T, typename ValT>
@@ -286,18 +276,26 @@ 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));
// Note: removed static assert as optional types are now handled generically
// as long we are successful or the field is not found, we continue
if(e == simdjson::SUCCESS || e == simdjson::NO_SUCH_FIELD) {
e = obj[key].get(out.[: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:]));
}
}
};
return e;
return simdjson::SUCCESS;
}
// Support for enum deserialization - deserialize from string representation using expand approach from P2996R12
+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.6"
namespace simdjson {
enum {
@@ -19,7 +19,7 @@ enum {
/**
* The revision (major.minor.REVISION) of simdjson being used.
*/
SIMDJSON_VERSION_REVISION = 5
SIMDJSON_VERSION_REVISION = 6
};
} // namespace simdjson
+22 -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-20 22:23:09 -0600. version 4.0.6 Do not edit! */
/* including simdjson.cpp: */
/* begin file simdjson.cpp */
#define SIMDJSON_SRC_SIMDJSON_CPP
@@ -3009,11 +3009,32 @@ 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>;
};
} // 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
+745 -599
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.
+2 -1
View File
@@ -1,7 +1,8 @@
# 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)
@@ -0,0 +1,138 @@
#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);
}
+31 -2
View File
@@ -151,13 +151,42 @@ simdjson::padded_string json_cars =
}
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
@@ -478,7 +507,7 @@ bool test_to_vs_from_equivalence() {
bool run() {
return
#if SIMDJSON_STATIC_REFLECTION
meeting_time_test() && meeting_test() && bad_player() && good_player() && complicated_weather_test() &&
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() &&