mirror of
https://github.com/simdjson/simdjson
synced 2026-06-08 17:27:07 +00:00
Merge pull request #2406 from simdjson/fix-convert-ci-failures
Introducing simplified api with from/to adapters (thanks to @the-moisrex for driving this) and fixing all the CI errors.
This commit is contained in:
@@ -1346,6 +1346,34 @@ auto tag_invoke(deserialize_tag, simdjson_value &val, std::list<Car>& car) {
|
||||
With this code, deserializing an `std::list<Car>` instance would capture only the cars
|
||||
that are not made by Toyota.
|
||||
|
||||
|
||||
|
||||
For even more convenience, you can do it directly without a parser instance like so:
|
||||
|
||||
```cpp
|
||||
Car car = simdjson::from(json);
|
||||
```
|
||||
|
||||
You can also use C++20 ranges to iterate over an array:
|
||||
|
||||
```cpp
|
||||
simdjson::padded_string json_cars =
|
||||
R"( [ { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||
"tire_pressure": [ 40.1, 39.9 ] },
|
||||
{ "make": "Kia", "model": "Soul", "year": 2012,
|
||||
"tire_pressure": [ 30.1, 31.0 ] },
|
||||
{ "make": "Toyota", "model": "Tercel", "year": 1999,
|
||||
"tire_pressure": [ 29.8, 30.0 ] }
|
||||
])"_padded;
|
||||
|
||||
for (Car car : simdjson::from(json_cars) | simdjson::as<Car>()) {
|
||||
if (car.year < 1998) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### 3. Using static reflection (C++26)
|
||||
|
||||
If you have a C++26 compatible compiler, you can compile
|
||||
@@ -1367,6 +1395,31 @@ simdjson::ondemand::document doc = parser.iterate(simdjson::pad(json));
|
||||
Car c = doc.get<Car>();
|
||||
```
|
||||
|
||||
Just like when using `tag_invoke` for custom types (but without the `tag_invoke` code), you can parse a class instance directly without a parser instance:
|
||||
|
||||
```cpp
|
||||
Car car = simdjson::from(json);
|
||||
```
|
||||
|
||||
Similarly, you can also use C++20 ranges to iterate over an array:
|
||||
|
||||
```cpp
|
||||
simdjson::padded_string json_cars =
|
||||
R"( [ { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||
"tire_pressure": [ 40.1, 39.9 ] },
|
||||
{ "make": "Kia", "model": "Soul", "year": 2012,
|
||||
"tire_pressure": [ 30.1, 31.0 ] },
|
||||
{ "make": "Toyota", "model": "Tercel", "year": 1999,
|
||||
"tire_pressure": [ 29.8, 30.0 ] }
|
||||
])"_padded;
|
||||
|
||||
for (Car car : simdjson::from(json_cars) | simdjson::as<Car>()) {
|
||||
if (car.year < 1998) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can also automatically serialize the `Car` instance to a JSON string, see
|
||||
our [Builder documentation](builder.md).
|
||||
|
||||
|
||||
+4
-4
@@ -165,7 +165,7 @@ automatically. In most cases, it should work automatically:
|
||||
|
||||
In some instances, you might want to create a string directly from your own data type.
|
||||
You can create a string directly, without an explicit `string_builder` instance
|
||||
with the `simdjson::builder::to_json_string` function.
|
||||
with the `simdjson::to_json` template function.
|
||||
(Under the hood a `string_builder` instance may still be created.)
|
||||
|
||||
```cpp
|
||||
@@ -178,12 +178,12 @@ with the `simdjson::builder::to_json_string` function.
|
||||
|
||||
void f() {
|
||||
Car c = {"Toyota", "Corolla", 2017, {30.0,30.2,30.513,30.79}};
|
||||
std::string json = simdjson::builder::to_json_string(c);
|
||||
std::string json = simdjson::to_json(c);
|
||||
}
|
||||
```
|
||||
|
||||
If you know the output size, in bytes, of your JSON string, you may
|
||||
pass it as a second parameter (e.g., `simdjson::builder::to_json_string(c, 31123)`).
|
||||
pass it as a second parameter (e.g., `simdjson::to_json(c, 31123)`).
|
||||
|
||||
|
||||
|
||||
@@ -194,7 +194,7 @@ pattern:
|
||||
|
||||
```cpp
|
||||
std::string json;
|
||||
if(simdjson::builder::to_json_string(c).get(json)) {
|
||||
if(simdjson::to(c).get(json)) {
|
||||
// there was an error
|
||||
} else {
|
||||
// json contain the serialized JSON
|
||||
|
||||
@@ -53,4 +53,5 @@
|
||||
|
||||
#include "simdjson/dom.h"
|
||||
#include "simdjson/ondemand.h"
|
||||
#include "simdjson/convert.h"
|
||||
#endif // SIMDJSON_H
|
||||
|
||||
@@ -0,0 +1,307 @@
|
||||
#ifndef SIMDJSON_CONVERT_H
|
||||
#define SIMDJSON_CONVERT_H
|
||||
#if __cpp_concepts
|
||||
|
||||
#include "simdjson/ondemand.h"
|
||||
#include <optional>
|
||||
#ifdef __cpp_lib_ranges
|
||||
#include <ranges>
|
||||
#endif
|
||||
|
||||
namespace simdjson {
|
||||
|
||||
struct [[nodiscard]] auto_iterator_end {};
|
||||
|
||||
/**
|
||||
* A Wrapper for simdjson_result<ondemand::array_iterator> in order to make it
|
||||
* compatible with ranges (to satisfy std::ranges::input_range).
|
||||
*/
|
||||
struct [[nodiscard]] auto_iterator {
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using type = simdjson_result<ondemand::array_iterator>;
|
||||
using value_type = simdjson_result<ondemand::value>; // type::value_type
|
||||
using reference = value_type &;
|
||||
using const_reference = const value_type &;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
|
||||
struct auto_iterator_storage {
|
||||
type m_iter{};
|
||||
mutable value_type m_value{};
|
||||
};
|
||||
|
||||
private:
|
||||
auto_iterator_storage *m_storage = nullptr;
|
||||
|
||||
public:
|
||||
constexpr auto_iterator() noexcept = default;
|
||||
explicit auto_iterator(auto_iterator_storage &storage) noexcept
|
||||
: m_storage{&storage} {};
|
||||
auto_iterator(auto_iterator const &) = default;
|
||||
auto_iterator(auto_iterator &&) = default;
|
||||
auto_iterator &operator=(auto_iterator const &) = default;
|
||||
auto_iterator &operator=(auto_iterator &&) noexcept = default;
|
||||
~auto_iterator() = default;
|
||||
|
||||
reference operator*() const noexcept { return m_storage->m_value; }
|
||||
reference operator*() noexcept { return m_storage->m_value; }
|
||||
|
||||
auto_iterator &operator++() noexcept {
|
||||
++m_storage->m_iter;
|
||||
m_storage->m_value =
|
||||
m_storage->m_iter.at_end() || m_storage->m_iter.error() != SUCCESS
|
||||
? value_type{}
|
||||
: *m_storage->m_iter;
|
||||
return *this;
|
||||
}
|
||||
auto_iterator operator++(int) noexcept {
|
||||
auto_iterator const tmp = *this;
|
||||
operator++();
|
||||
return tmp;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(auto_iterator const &other) const noexcept {
|
||||
return m_storage == other.m_storage &&
|
||||
m_storage->m_iter == other.m_storage->m_iter;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(auto_iterator_end) const noexcept {
|
||||
return m_storage != nullptr && m_storage->m_iter.at_end();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename ParserType = ondemand::parser>
|
||||
struct [[nodiscard]] auto_parser
|
||||
#if __cpp_lib_ranges
|
||||
: std::ranges::view_interface<auto_parser<ParserType>>
|
||||
#endif
|
||||
{
|
||||
using value_type = simdjson_result<ondemand::value>;
|
||||
using size_type = size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = value_type *;
|
||||
using const_pointer = const value_type *;
|
||||
using reference = value_type &;
|
||||
using const_reference = const value_type &;
|
||||
using iterator = auto_iterator;
|
||||
using const_iterator = auto_iterator; // auto_iterator is already const
|
||||
|
||||
private:
|
||||
ParserType m_parser;
|
||||
ondemand::document m_doc;
|
||||
error_code m_error{SUCCESS};
|
||||
|
||||
// Caching the iterator here:
|
||||
iterator::auto_iterator_storage iter_storage{};
|
||||
|
||||
template <typename T>
|
||||
static constexpr bool is_nothrow_gettable = requires(ondemand::document doc) {
|
||||
{ doc.get<T>() } noexcept;
|
||||
};
|
||||
|
||||
public:
|
||||
// non-pointer constructors:
|
||||
explicit auto_parser(ParserType &&parser, ondemand::document &&doc) noexcept
|
||||
requires(!std::is_pointer_v<ParserType>)
|
||||
: m_parser{std::move(parser)}, m_doc{std::move(doc)} {}
|
||||
|
||||
explicit auto_parser(ParserType &&parser,
|
||||
padded_string_view const str) noexcept
|
||||
requires(!std::is_pointer_v<ParserType>)
|
||||
: m_parser{std::move(parser)}, m_doc{}, m_error{SUCCESS} {
|
||||
m_error = m_parser.iterate(str).get(m_doc);
|
||||
}
|
||||
|
||||
explicit auto_parser(padded_string_view const str) noexcept
|
||||
requires(!std::is_pointer_v<ParserType>)
|
||||
: auto_parser{ParserType{}, str} {}
|
||||
|
||||
// pointer constructors:
|
||||
explicit auto_parser(std::remove_pointer_t<ParserType> &parser,
|
||||
ondemand::document &&doc) noexcept
|
||||
requires(std::is_pointer_v<ParserType>)
|
||||
: m_parser{&parser}, m_doc{std::move(doc)} {}
|
||||
|
||||
explicit auto_parser(std::remove_pointer_t<ParserType> &parser,
|
||||
padded_string_view const str) noexcept
|
||||
requires(std::is_pointer_v<ParserType>)
|
||||
: m_parser{&parser}, m_doc{}, m_error{SUCCESS} {
|
||||
m_error = m_parser->iterate(str).get(m_doc);
|
||||
}
|
||||
|
||||
explicit auto_parser(ParserType parser, ondemand::document &&doc) noexcept
|
||||
requires(std::is_pointer_v<ParserType>)
|
||||
: auto_parser{*parser, std::move(doc)} {}
|
||||
|
||||
auto_parser(auto_parser const &) = delete;
|
||||
auto_parser &operator=(auto_parser const &) = delete;
|
||||
auto_parser(auto_parser &&) noexcept = default;
|
||||
auto_parser &operator=(auto_parser &&) noexcept = default;
|
||||
~auto_parser() = default;
|
||||
|
||||
/// Get the parser
|
||||
[[nodiscard]] std::remove_pointer_t<ParserType> &parser() noexcept {
|
||||
if constexpr (std::is_pointer_v<ParserType>) {
|
||||
return *m_parser;
|
||||
} else {
|
||||
return m_parser;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] simdjson_inline simdjson_result<T>
|
||||
result() noexcept(is_nothrow_gettable<T>) {
|
||||
if (m_error != SUCCESS) {
|
||||
return m_error;
|
||||
}
|
||||
// For array and object types, we need to be at the start of the document
|
||||
return m_doc.get<T>();
|
||||
}
|
||||
|
||||
[[nodiscard]] simdjson_inline simdjson_result<ondemand::array>
|
||||
array() noexcept {
|
||||
return result<ondemand::array>();
|
||||
}
|
||||
|
||||
[[nodiscard]] simdjson_inline simdjson_result<ondemand::object>
|
||||
object() noexcept {
|
||||
return result<ondemand::object>();
|
||||
}
|
||||
|
||||
[[nodiscard]] simdjson_inline simdjson_result<ondemand::number>
|
||||
number() noexcept {
|
||||
return result<ondemand::number>();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] simdjson_inline explicit(false)
|
||||
operator simdjson_result<T>() noexcept(is_nothrow_gettable<T>) {
|
||||
return result<T>();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] simdjson_inline explicit(false) operator T() noexcept(false) {
|
||||
if (m_error != SUCCESS) {
|
||||
throw simdjson_error(m_error);
|
||||
}
|
||||
return m_doc.get<T>();
|
||||
}
|
||||
|
||||
// We can't have "operator std::optional<T>" because it would create an
|
||||
// ambiguity for the compiler.
|
||||
// We also cannot have "operator T*" without manual memory management.
|
||||
// We also cannot have "operator T&" without manual memory management either.
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] simdjson_inline std::optional<T>
|
||||
optional() noexcept(is_nothrow_gettable<T>) {
|
||||
if (m_error != SUCCESS) {
|
||||
return std::nullopt;
|
||||
}
|
||||
T value;
|
||||
// For std::optional<T>
|
||||
if (m_doc.get<T>().get(value)) [[unlikely]] {
|
||||
return std::nullopt;
|
||||
}
|
||||
return {std::move(value)};
|
||||
}
|
||||
|
||||
simdjson_inline auto_iterator begin() noexcept {
|
||||
if (m_error != SUCCESS) {
|
||||
// Create an iterator with the error
|
||||
iter_storage.m_iter = iterator::type(m_error);
|
||||
iter_storage.m_value = value_type{};
|
||||
return auto_iterator{iter_storage};
|
||||
}
|
||||
if (iter_storage.m_iter.error() != SUCCESS &&
|
||||
!iter_storage.m_iter.at_end()) {
|
||||
// Try to get the document as an array
|
||||
ondemand::array arr;
|
||||
if(auto error = m_doc.get_array().get(arr); error == SUCCESS) {
|
||||
iter_storage = {.m_iter = iterator::type{arr.begin()},
|
||||
.m_value = iterator::value_type{
|
||||
iter_storage.m_iter.at_end() ||
|
||||
iter_storage.m_iter.error() != SUCCESS
|
||||
? value_type{}
|
||||
: *iter_storage.m_iter}};
|
||||
} else {
|
||||
// If it's not an array, create an error iterator
|
||||
iter_storage.m_iter = iterator::type(error);
|
||||
iter_storage.m_value = value_type{};
|
||||
}
|
||||
}
|
||||
return auto_iterator{iter_storage};
|
||||
}
|
||||
simdjson_inline auto_iterator_end end() noexcept { return {}; }
|
||||
};
|
||||
|
||||
#ifdef __cpp_lib_ranges
|
||||
|
||||
// For C++20, we implement our own pipe operator since range_adaptor_closure is C++23
|
||||
static constexpr struct [[nodiscard]] no_errors_adaptor {
|
||||
|
||||
[[nodiscard]] bool
|
||||
operator()(simdjson_result<ondemand::value> const &val) const noexcept {
|
||||
return val.error() == SUCCESS;
|
||||
}
|
||||
|
||||
template <std::ranges::range Range>
|
||||
auto operator()(Range &&rng) const noexcept {
|
||||
return std::forward<Range>(rng) | std::views::filter(*this);
|
||||
}
|
||||
} no_errors;
|
||||
|
||||
template <typename T = void>
|
||||
struct [[nodiscard]] to_adaptor {
|
||||
|
||||
/// Convert to T
|
||||
[[nodiscard]] T
|
||||
operator()(simdjson_result<ondemand::value> &val) const noexcept {
|
||||
return val.get<T>();
|
||||
}
|
||||
|
||||
/// Make it an adaptor
|
||||
template <std::ranges::range Range>
|
||||
auto operator()(Range &&rng) const noexcept {
|
||||
return std::forward<Range>(rng) | no_errors | std::views::transform(*this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse input string into any object if possible.
|
||||
*/
|
||||
auto operator()(padded_string_view const str) const noexcept {
|
||||
return auto_parser{str};
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the input using the specified parser into any object if possible.
|
||||
*/
|
||||
auto operator()(ondemand::parser &parser,
|
||||
padded_string_view const str) const noexcept {
|
||||
return auto_parser<ondemand::parser *>{parser, str};
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T> static constexpr to_adaptor<T> to{};
|
||||
|
||||
static constexpr to_adaptor<> from{};
|
||||
|
||||
template <typename T = void>
|
||||
using as = to_adaptor<T>;
|
||||
|
||||
// For C++20 ranges without range_adaptor_closure, we need to define pipe operators
|
||||
template <std::ranges::range Range>
|
||||
inline auto operator|(Range&& range, const no_errors_adaptor& adaptor) {
|
||||
return adaptor(std::forward<Range>(range));
|
||||
}
|
||||
|
||||
template <std::ranges::range Range, typename T>
|
||||
inline auto operator|(Range&& range, const to_adaptor<T>& adaptor) {
|
||||
return adaptor(std::forward<Range>(range));
|
||||
}
|
||||
|
||||
#endif // __cpp_lib_ranges
|
||||
|
||||
} // namespace simdjson
|
||||
|
||||
#endif // __cpp_concepts
|
||||
#endif // SIMDJSON_CONVERT_H
|
||||
@@ -122,6 +122,7 @@ struct implementation_simdjson_result_base {
|
||||
* the error() method returns a value that evaluates to false.
|
||||
*/
|
||||
simdjson_inline T&& value_unsafe() && noexcept;
|
||||
|
||||
protected:
|
||||
/** users should never directly access first and second. **/
|
||||
T first{}; /** Users should never directly access 'first'. **/
|
||||
|
||||
@@ -36,6 +36,9 @@ simdjson_inline array_iterator &array_iterator::operator++() noexcept {
|
||||
return *this;
|
||||
}
|
||||
|
||||
simdjson_inline bool array_iterator::at_end() const noexcept {
|
||||
return iter.at_end();
|
||||
}
|
||||
} // namespace ondemand
|
||||
} // namespace SIMDJSON_IMPLEMENTATION
|
||||
} // namespace simdjson
|
||||
@@ -72,7 +75,9 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array_iterato
|
||||
++(first);
|
||||
return *this;
|
||||
}
|
||||
|
||||
simdjson_inline bool simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array_iterator>::at_end() const noexcept {
|
||||
return !first.iter.is_valid() || first.at_end();
|
||||
}
|
||||
} // namespace simdjson
|
||||
|
||||
#endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H
|
||||
@@ -34,7 +34,8 @@ public:
|
||||
*
|
||||
* Part of the std::iterator interface.
|
||||
*/
|
||||
simdjson_inline simdjson_result<value> operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION.
|
||||
simdjson_inline simdjson_result<value>
|
||||
operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION.
|
||||
/**
|
||||
* Check if we are at the end of the JSON.
|
||||
*
|
||||
@@ -58,6 +59,11 @@ public:
|
||||
*/
|
||||
simdjson_inline array_iterator &operator++() noexcept;
|
||||
|
||||
/**
|
||||
* Check if the array is at the end.
|
||||
*/
|
||||
[[nodiscard]] simdjson_inline bool at_end() const noexcept;
|
||||
|
||||
private:
|
||||
value_iterator iter{};
|
||||
|
||||
@@ -76,7 +82,6 @@ namespace simdjson {
|
||||
|
||||
template<>
|
||||
struct simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array_iterator> : public SIMDJSON_IMPLEMENTATION::implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::ondemand::array_iterator> {
|
||||
public:
|
||||
simdjson_inline simdjson_result(SIMDJSON_IMPLEMENTATION::ondemand::array_iterator &&value) noexcept; ///< @private
|
||||
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
|
||||
simdjson_inline simdjson_result() noexcept = default;
|
||||
@@ -89,6 +94,8 @@ public:
|
||||
simdjson_inline bool operator==(const simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array_iterator> &) const noexcept;
|
||||
simdjson_inline bool operator!=(const simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array_iterator> &) const noexcept;
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array_iterator> &operator++() noexcept;
|
||||
|
||||
[[nodiscard]] simdjson_inline bool at_end() const noexcept;
|
||||
};
|
||||
|
||||
} // namespace simdjson
|
||||
|
||||
@@ -296,6 +296,11 @@ string_builder& operator<<(string_builder& b, const Z& z) {
|
||||
}
|
||||
} // namespace builder
|
||||
} // namespace SIMDJSON_IMPLEMENTATION
|
||||
// Alias the function template to 'to' in the global namespace
|
||||
template <class Z>
|
||||
simdjson_result<std::string> to_json(const Z &z, size_t initial_capacity = 1024) {
|
||||
return SIMDJSON_IMPLEMENTATION::builder::to_json_string(z, initial_capacity);
|
||||
}
|
||||
} // namespace simdjson
|
||||
#endif // SIMDJSON_STATIC_REFLECTION
|
||||
|
||||
|
||||
@@ -218,6 +218,8 @@ simdjson_inline void json_iterator::assert_valid_position(token_position positio
|
||||
#ifndef SIMDJSON_CLANG_VISUAL_STUDIO
|
||||
SIMDJSON_ASSUME( position >= &parser->implementation->structural_indexes[0] );
|
||||
SIMDJSON_ASSUME( position < &parser->implementation->structural_indexes[parser->implementation->n_structural_indexes] );
|
||||
#else
|
||||
(void)position; // Suppress unused parameter warning
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#include <concepts>
|
||||
#include <limits>
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
#include <experimental/meta>
|
||||
#include <meta>
|
||||
// #include <static_reflection> // for std::define_static_string - header not available yet
|
||||
#endif
|
||||
|
||||
|
||||
+5
-1
@@ -8,7 +8,11 @@ RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends ca-certificates gnupg \
|
||||
build-essential cmake make python3 zlib1g wget subversion unzip ninja-build git linux-perf && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
RUN git clone --depth=1 --branch p2996 https://github.com/bloomberg/clang-p2996.git /tmp/clang-source
|
||||
ARG CLANG_COMMIT=d77eff1cbd78fd065668acf93b1f5f400d39134d
|
||||
RUN git clone --depth=1 https://github.com/bloomberg/clang-p2996.git /tmp/clang-source && \
|
||||
cd /tmp/clang-source && \
|
||||
git fetch origin $CLANG_COMMIT --depth=1 && \
|
||||
git checkout $CLANG_COMMIT
|
||||
RUN cmake -S /tmp/clang-source/llvm -B /tmp/clang-source/build-llvm -DCMAKE_BUILD_TYPE=Release \
|
||||
-DLLVM_ENABLE_ASSERTIONS=ON \
|
||||
-DLLVM_UNREACHABLE_OPTIMIZE=ON \
|
||||
|
||||
@@ -64,6 +64,7 @@ cmake --build buildreflect --target benchmark_serialization_citm_catalog benchma
|
||||
|
||||
6. Run the tests...
|
||||
```bash
|
||||
cmake --build buildreflect
|
||||
ctest --test-dir buildreflect --output-on-failure
|
||||
```
|
||||
|
||||
|
||||
+109
-45
@@ -1,4 +1,4 @@
|
||||
/* auto-generated on 2025-07-14 15:43:52 -0400. Do not edit! */
|
||||
/* auto-generated on 2025-08-05 16:29:59 +0000. version 4.0.0 Do not edit! */
|
||||
/* including simdjson.cpp: */
|
||||
/* begin file simdjson.cpp */
|
||||
#define SIMDJSON_SRC_SIMDJSON_CPP
|
||||
@@ -577,17 +577,6 @@ double from_chars(const char *first, const char* end) noexcept;
|
||||
// We assume by default static linkage
|
||||
#define SIMDJSON_DLLIMPORTEXPORT
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Workaround for the vcpkg package manager. Only vcpkg should
|
||||
* ever touch the next line. The SIMDJSON_USING_LIBRARY macro is otherwise unused.
|
||||
*/
|
||||
#if SIMDJSON_USING_LIBRARY
|
||||
#define SIMDJSON_DLLIMPORTEXPORT __declspec(dllimport)
|
||||
#endif
|
||||
/**
|
||||
* End of workaround for the vcpkg package manager.
|
||||
*/
|
||||
#else
|
||||
#define SIMDJSON_DLLIMPORTEXPORT
|
||||
#endif
|
||||
@@ -2444,6 +2433,18 @@ namespace std {
|
||||
#define SIMDJSON_AVX512_ALLOWED 1
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef __has_cpp_attribute
|
||||
#define simdjson_lifetime_bound
|
||||
#elif __has_cpp_attribute(msvc::lifetimebound)
|
||||
#define simdjson_lifetime_bound [[msvc::lifetimebound]]
|
||||
#elif __has_cpp_attribute(clang::lifetimebound)
|
||||
#define simdjson_lifetime_bound [[clang::lifetimebound]]
|
||||
#elif __has_cpp_attribute(lifetimebound)
|
||||
#define simdjson_lifetime_bound [[lifetimebound]]
|
||||
#else
|
||||
#define simdjson_lifetime_bound
|
||||
#endif
|
||||
#endif // SIMDJSON_COMMON_DEFS_H
|
||||
/* end file simdjson/common_defs.h */
|
||||
/* skipped duplicate #include "simdjson/compiler_check.h" */
|
||||
@@ -2908,7 +2909,6 @@ concept optional_type = requires(std::remove_cvref_t<T> obj) {
|
||||
{ obj.value() } -> std::same_as<typename std::remove_cvref_t<T>::value_type&>;
|
||||
requires requires(typename std::remove_cvref_t<T>::value_type &&val) {
|
||||
obj.emplace(std::move(val));
|
||||
obj = std::move(val);
|
||||
{
|
||||
obj.value_or(val)
|
||||
} -> std::convertible_to<typename std::remove_cvref_t<T>::value_type>;
|
||||
@@ -9170,6 +9170,7 @@ struct implementation_simdjson_result_base {
|
||||
* the error() method returns a value that evaluates to false.
|
||||
*/
|
||||
simdjson_inline T&& value_unsafe() && noexcept;
|
||||
|
||||
protected:
|
||||
/** users should never directly access first and second. **/
|
||||
T first{}; /** Users should never directly access 'first'. **/
|
||||
@@ -15609,6 +15610,7 @@ struct implementation_simdjson_result_base {
|
||||
* the error() method returns a value that evaluates to false.
|
||||
*/
|
||||
simdjson_inline T&& value_unsafe() && noexcept;
|
||||
|
||||
protected:
|
||||
/** users should never directly access first and second. **/
|
||||
T first{}; /** Users should never directly access 'first'. **/
|
||||
@@ -21903,6 +21905,7 @@ struct implementation_simdjson_result_base {
|
||||
* the error() method returns a value that evaluates to false.
|
||||
*/
|
||||
simdjson_inline T&& value_unsafe() && noexcept;
|
||||
|
||||
protected:
|
||||
/** users should never directly access first and second. **/
|
||||
T first{}; /** Users should never directly access 'first'. **/
|
||||
@@ -28354,6 +28357,7 @@ struct implementation_simdjson_result_base {
|
||||
* the error() method returns a value that evaluates to false.
|
||||
*/
|
||||
simdjson_inline T&& value_unsafe() && noexcept;
|
||||
|
||||
protected:
|
||||
/** users should never directly access first and second. **/
|
||||
T first{}; /** Users should never directly access 'first'. **/
|
||||
@@ -35164,6 +35168,7 @@ struct implementation_simdjson_result_base {
|
||||
* the error() method returns a value that evaluates to false.
|
||||
*/
|
||||
simdjson_inline T&& value_unsafe() && noexcept;
|
||||
|
||||
protected:
|
||||
/** users should never directly access first and second. **/
|
||||
T first{}; /** Users should never directly access 'first'. **/
|
||||
@@ -41796,6 +41801,7 @@ struct implementation_simdjson_result_base {
|
||||
* the error() method returns a value that evaluates to false.
|
||||
*/
|
||||
simdjson_inline T&& value_unsafe() && noexcept;
|
||||
|
||||
protected:
|
||||
/** users should never directly access first and second. **/
|
||||
T first{}; /** Users should never directly access 'first'. **/
|
||||
@@ -47874,6 +47880,7 @@ struct implementation_simdjson_result_base {
|
||||
* the error() method returns a value that evaluates to false.
|
||||
*/
|
||||
simdjson_inline T&& value_unsafe() && noexcept;
|
||||
|
||||
protected:
|
||||
/** users should never directly access first and second. **/
|
||||
T first{}; /** Users should never directly access 'first'. **/
|
||||
@@ -53544,6 +53551,7 @@ struct implementation_simdjson_result_base {
|
||||
* the error() method returns a value that evaluates to false.
|
||||
*/
|
||||
simdjson_inline T&& value_unsafe() && noexcept;
|
||||
|
||||
protected:
|
||||
/** users should never directly access first and second. **/
|
||||
T first{}; /** Users should never directly access 'first'. **/
|
||||
@@ -56568,10 +56576,78 @@ simdjson_inline void validate_utf8_character() {
|
||||
idx += 4;
|
||||
}
|
||||
|
||||
static const uint8_t CHAR_TYPE_SPACE = 1 << 0;
|
||||
static const uint8_t CHAR_TYPE_OPERATOR = 1 << 1;
|
||||
static const uint8_t CHAR_TYPE_ESC_ASCII = 1 << 2;
|
||||
static const uint8_t CHAR_TYPE_NON_ASCII = 1 << 3;
|
||||
|
||||
const uint8_t char_table[256] = {
|
||||
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
|
||||
0x04, 0x05, 0x05, 0x04, 0x04, 0x05, 0x04, 0x04,
|
||||
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
|
||||
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
|
||||
0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x02, 0x04, 0x02, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00,
|
||||
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
||||
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
||||
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
||||
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
||||
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
||||
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
||||
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
||||
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
||||
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
||||
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
||||
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
||||
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
||||
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
||||
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
||||
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
||||
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08
|
||||
};
|
||||
|
||||
simdjson_inline bool char_is_type(uint8_t c, uint8_t type) {
|
||||
return (char_table[c] & type);
|
||||
}
|
||||
|
||||
simdjson_inline bool char_is_space(uint8_t c) {
|
||||
return char_is_type(c, CHAR_TYPE_SPACE);
|
||||
}
|
||||
|
||||
simdjson_inline bool char_is_operator(uint8_t c) {
|
||||
return char_is_type(c, CHAR_TYPE_OPERATOR);
|
||||
}
|
||||
|
||||
simdjson_inline bool char_is_space_or_operator(uint8_t c) {
|
||||
return char_is_type(c, CHAR_TYPE_SPACE | CHAR_TYPE_OPERATOR);
|
||||
}
|
||||
|
||||
simdjson_inline bool char_is_ascii_stop(uint8_t c) {
|
||||
return char_is_type(c, CHAR_TYPE_ESC_ASCII | CHAR_TYPE_NON_ASCII);
|
||||
}
|
||||
|
||||
// Returns true if the string is unclosed.
|
||||
simdjson_inline bool validate_string() {
|
||||
idx++; // skip first quote
|
||||
while (idx < len && buf[idx] != '"') {
|
||||
while (idx < len) {
|
||||
do {
|
||||
if (char_is_ascii_stop(buf[idx])) { break; }
|
||||
idx++;
|
||||
} while (idx < len);
|
||||
if (idx >= len) { return true; }
|
||||
if (buf[idx] == '"') {
|
||||
return false;
|
||||
}
|
||||
if (buf[idx] == '\\') {
|
||||
idx += 2;
|
||||
} else if (simdjson_unlikely(buf[idx] & 0x80)) {
|
||||
@@ -56585,43 +56661,31 @@ simdjson_inline bool validate_string() {
|
||||
return false;
|
||||
}
|
||||
|
||||
simdjson_inline bool is_whitespace_or_operator(uint8_t c) {
|
||||
switch (c) {
|
||||
case '{': case '}': case '[': case ']': case ',': case ':':
|
||||
case ' ': case '\r': case '\n': case '\t':
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Parse the entire input in STEP_SIZE-byte chunks.
|
||||
//
|
||||
simdjson_inline error_code scan() {
|
||||
bool unclosed_string = false;
|
||||
for (;idx<len;idx++) {
|
||||
switch (buf[idx]) {
|
||||
// String
|
||||
case '"':
|
||||
add_structural();
|
||||
unclosed_string |= validate_string();
|
||||
break;
|
||||
// Operator
|
||||
case '{': case '}': case '[': case ']': case ',': case ':':
|
||||
add_structural();
|
||||
break;
|
||||
// Whitespace
|
||||
case ' ': case '\r': case '\n': case '\t':
|
||||
break;
|
||||
// Primitive or invalid character (invalid characters will be checked in stage 2)
|
||||
default:
|
||||
// Anything else, add the structural and go until we find the next one
|
||||
add_structural();
|
||||
while (idx+1<len && !is_whitespace_or_operator(buf[idx+1])) {
|
||||
idx++;
|
||||
};
|
||||
break;
|
||||
do {
|
||||
if (!char_is_space(buf[idx])) { break; }
|
||||
idx++;
|
||||
} while (idx < len);
|
||||
if (idx >= len) { break; }
|
||||
// String
|
||||
if (buf[idx] == '"') {
|
||||
add_structural();
|
||||
unclosed_string |= validate_string();
|
||||
// Operator
|
||||
} else if (char_is_operator(buf[idx])) {
|
||||
add_structural();
|
||||
// Primitive or invalid character (invalid characters will be checked in stage 2)
|
||||
} else {
|
||||
// Anything else, add the structural and go until we find the next one
|
||||
add_structural();
|
||||
while (idx+1<len && !char_is_space_or_operator(buf[idx+1])) {
|
||||
idx++;
|
||||
};
|
||||
}
|
||||
}
|
||||
// We pad beyond.
|
||||
|
||||
+3015
-287
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -77,6 +77,7 @@ namespace builder_tests {
|
||||
Car c = {"Toyota", "Corolla", 2017, {30.0,30.2,30.513,30.79}};
|
||||
append(sb, c);
|
||||
std::string_view p{sb};
|
||||
(void)p; // to avoid unused variable warning
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
bool car_test_exception2() {
|
||||
@@ -85,12 +86,18 @@ namespace builder_tests {
|
||||
Car c = {"Toyota", "Corolla", 2017, {30.0,30.2,30.513,30.79}};
|
||||
sb << c;
|
||||
std::string_view p{sb};
|
||||
(void)p; // to avoid unused variable warning
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
void car_test_to_json_exception() {
|
||||
bool car_test_to_json_exception() {
|
||||
TEST_START();
|
||||
Car c = {"Toyota", "Corolla", 2017, {30.0,30.2,30.513,30.79}};
|
||||
std::string json = simdjson::builder::to_json_string(c);
|
||||
std::string json = simdjson::to_json(c);
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
bool car_test_to_json_exception_value() {
|
||||
TEST_START();
|
||||
std::string json = simdjson::to_json(Car{"Toyota", "Corolla", 2017, {30.0,30.2,30.513,30.79}});
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
#endif // SIMDJSON_EXCEPTIONS
|
||||
@@ -165,6 +172,7 @@ bool serialize_deserialize_x_y_z() {
|
||||
car_test_exception() &&
|
||||
car_test_exception2() &&
|
||||
car_test_to_json_exception() &&
|
||||
car_test_to_json_exception_value() &&
|
||||
#endif // SIMDJSON_EXCEPTIONS
|
||||
car_test() &&
|
||||
serialize_deserialize_kid() &&
|
||||
|
||||
@@ -132,7 +132,9 @@ if(
|
||||
)
|
||||
message(STATUS "compiler id: ${CMAKE_CXX_COMPILER_ID} version: ${CMAKE_CXX_COMPILER_VERSION}")
|
||||
add_cpp_test(ranges_test LABELS dom acceptance per_implementation)
|
||||
set_target_properties(ranges_test PROPERTIES CXX_STANDARD 20 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF)
|
||||
if(NOT SIMDJSON_STATIC_REFLECTION)
|
||||
set_target_properties(ranges_test PROPERTIES CXX_STANDARD 20 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WIN32 AND BUILD_SHARED_LIBS)
|
||||
|
||||
@@ -33,6 +33,7 @@ add_cpp_test(ondemand_iterate_many_csv LABELS ondemand acceptance
|
||||
add_cpp_test(ondemand_custom_types_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_custom_types_document_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_stl_types_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_convert_tests LABELS ondemand acceptance per_implementation)
|
||||
if(NOT SIMDJSON_SANITIZE)
|
||||
add_cpp_test(ondemand_cacheline LABELS ondemand acceptance per_implementation)
|
||||
endif()
|
||||
|
||||
@@ -0,0 +1,336 @@
|
||||
#include "simdjson.h"
|
||||
#include "simdjson/convert.h"
|
||||
#include "test_ondemand.h"
|
||||
|
||||
#include <ranges>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#ifdef __cpp_lib_ranges
|
||||
|
||||
namespace convert_tests {
|
||||
#if SIMDJSON_EXCEPTIONS && SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||
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");
|
||||
|
||||
static_assert(std::input_or_output_iterator<simdjson::auto_iterator>,
|
||||
"Must be a valid input iterator");
|
||||
static_assert(std::semiregular<simdjson::auto_iterator>,
|
||||
"Should be kinda regular");
|
||||
// static_assert(std::ranges::__access::__member_end<simdjson::auto_parser<>>,
|
||||
// "Must be a valid input iterator");
|
||||
// static_assert(std::ranges::views::__adaptor::__is_range_adaptor_closure<
|
||||
// simdjson::auto_parser<>>,
|
||||
// "Parser need to be range adaptor closure.");
|
||||
// static_assert(std::ranges::views::__adaptor::__adaptor_invocable<
|
||||
// decltype(simdjson::to<Car>()),
|
||||
// simdjson::auto_parser<>>,
|
||||
// "I don't even know!");
|
||||
static_assert(std::ranges::range<simdjson::auto_parser<>>,
|
||||
"Parser need to be a range.");
|
||||
static_assert(std::ranges::forward_range<simdjson::auto_parser<>>,
|
||||
"Parser need to be an input range.");
|
||||
static_assert(
|
||||
requires(simdjson::auto_parser<> &parser) {
|
||||
{ parser.begin() } -> std::input_or_output_iterator;
|
||||
}, "Must be valid iterator.");
|
||||
|
||||
simdjson::padded_string json_car =
|
||||
R"( {
|
||||
"make": "Toyota",
|
||||
"model": "Camry",
|
||||
"year": 2018,
|
||||
"tire_pressure": [ 40.1, 39.9 ]
|
||||
} )"_padded;
|
||||
simdjson::padded_string json_cars =
|
||||
R"( [ { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||
"tire_pressure": [ 40.1, 39.9 ] },
|
||||
{ "make": "Kia", "model": "Soul", "year": 2012,
|
||||
"tire_pressure": [ 30.1, 31.0 ] },
|
||||
{ "make": "Toyota", "model": "Tercel", "year": 1999,
|
||||
"tire_pressure": [ 29.8, 30.0 ] }
|
||||
])"_padded;
|
||||
|
||||
bool simple() {
|
||||
TEST_START();
|
||||
Car car = simdjson::from(json_car);
|
||||
if (car.make != "Toyota" || car.model != "Camry" || car.year != 2018) {
|
||||
return false;
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool broken() {
|
||||
TEST_START();
|
||||
simdjson::padded_string short_json_cars = R"( { "make )"_padded;
|
||||
try {
|
||||
Car car = simdjson::from(json_cars);
|
||||
TEST_FAIL("Should not have succeeded");
|
||||
} catch (...) {
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool simple_optional() {
|
||||
TEST_START();
|
||||
auto car = simdjson::from(json_car).optional<Car>();
|
||||
if (!car.has_value() || car->make != "Toyota" || car->model != "Camry" ||
|
||||
car->year != 2018) {
|
||||
return false;
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool with_parser() {
|
||||
TEST_START();
|
||||
simdjson::ondemand::parser parser;
|
||||
Car car = simdjson::from(parser, json_car);
|
||||
if (car.make != "Toyota" || car.model != "Camry" || car.year != 2018) {
|
||||
return false;
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool to_array() {
|
||||
TEST_START();
|
||||
auto parser = simdjson::from(json_cars);
|
||||
for (auto val : parser.array()) {
|
||||
Car car{};
|
||||
if (auto const error = val.get(car)) {
|
||||
std::cerr << simdjson::error_message(error) << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (car.year < 1998) {
|
||||
std::cerr << car.make << " " << car.model << " " << car.year << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool to_array_shortcut() {
|
||||
TEST_START();
|
||||
simdjson::ondemand::parser parser;
|
||||
for (auto val : simdjson::from(parser, json_cars)) {
|
||||
Car car{};
|
||||
if (auto const error = val.get(car)) {
|
||||
std::cerr << simdjson::error_message(error) << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (car.year < 1998) {
|
||||
std::cerr << car.make << " " << car.model << " " << car.year << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool to_bad_array() {
|
||||
TEST_START();
|
||||
auto parser = simdjson::from(json_car);
|
||||
try {
|
||||
auto array_result = parser.array();
|
||||
// Check if array_result has an error
|
||||
if (array_result.error() != simdjson::SUCCESS) {
|
||||
// This is expected - trying to get array from an object should fail
|
||||
if (array_result.error() != simdjson::INCORRECT_TYPE) {
|
||||
std::cerr << "Expected INCORRECT_TYPE but got: " << array_result.error()
|
||||
<< " (" << simdjson::error_message(array_result.error()) << ")" << std::endl;
|
||||
return false;
|
||||
}
|
||||
// Got expected error, test passes
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
// If we get here without error, try to iterate
|
||||
// This might throw when we try to use the array
|
||||
for (auto val : array_result) {
|
||||
static_cast<void>(val);
|
||||
// Should not reach here - the JSON is an object, not an array
|
||||
std::cerr << "Unexpectedly succeeded in iterating over non-array JSON" << std::endl;
|
||||
return false;
|
||||
}
|
||||
// Also should not reach here
|
||||
std::cerr << "array() succeeded on object JSON without throwing" << std::endl;
|
||||
return false;
|
||||
} catch (simdjson::simdjson_error &e) {
|
||||
if (e.error() != simdjson::INCORRECT_TYPE) {
|
||||
std::cerr << "Expected INCORRECT_TYPE but got: " << e.error() << " (" << simdjson::error_message(e.error()) << ")" << std::endl;
|
||||
return false;
|
||||
}
|
||||
// Got expected exception, test passes
|
||||
} catch (...) {
|
||||
std::cerr << "Unexpected exception type" << std::endl;
|
||||
return false;
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool test_basic_adaptor() {
|
||||
TEST_START();
|
||||
for (Car car : simdjson::from(json_cars) | simdjson::as<Car>()) {
|
||||
if (car.year < 1998) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool test_no_errors() {
|
||||
TEST_START();
|
||||
auto cars = simdjson::from(json_cars) | simdjson::no_errors;
|
||||
for (auto val : cars) {
|
||||
Car car = val.get<Car>();
|
||||
if (car.year < 1998) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool to_clean_array() {
|
||||
TEST_START();
|
||||
for (auto val : simdjson::from(json_cars) | simdjson::no_errors) {
|
||||
Car car = val.get<Car>();
|
||||
if (car.year < 1998) {
|
||||
std::cerr << car.make << " " << car.model << " " << car.year << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool test_to_adaptor_basic() {
|
||||
TEST_START();
|
||||
// Test 1: Basic usage of to<T> with a value reference
|
||||
simdjson::ondemand::parser parser;
|
||||
auto doc_result = parser.iterate(json_car);
|
||||
if (doc_result.error()) {
|
||||
return false;
|
||||
}
|
||||
simdjson::ondemand::document doc = std::move(doc_result.value());
|
||||
simdjson::simdjson_result<simdjson::ondemand::value> val = doc.get_value();
|
||||
|
||||
// to<T> converts a simdjson_result<value>& to T
|
||||
Car car = simdjson::to<Car>(val);
|
||||
if (car.make != "Toyota" || car.model != "Camry" || car.year != 2018) {
|
||||
return false;
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool test_to_adaptor_with_single_value() {
|
||||
TEST_START();
|
||||
// Test 2: Using to<T> to convert individual values
|
||||
simdjson::ondemand::parser parser;
|
||||
auto doc_result = parser.iterate(json_car);
|
||||
if (doc_result.error()) {
|
||||
return false;
|
||||
}
|
||||
simdjson::ondemand::document doc = std::move(doc_result.value());
|
||||
|
||||
// Get individual field and convert it
|
||||
auto obj_result = doc.get_object();
|
||||
if (obj_result.error()) {
|
||||
return false;
|
||||
}
|
||||
simdjson::ondemand::object obj = std::move(obj_result.value());
|
||||
|
||||
auto year_val = obj["year"];
|
||||
int64_t year = simdjson::to<int64_t>(year_val);
|
||||
if (year != 2018) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool test_to_vs_from_equivalence() {
|
||||
TEST_START();
|
||||
// Test 3: Verify that simdjson::to<> and simdjson::from behave equivalently
|
||||
// Both are instances of to_adaptor - from is just to<void>
|
||||
|
||||
// These should produce identical auto_parser objects
|
||||
auto parser1 = simdjson::from(json_car);
|
||||
// simdjson::from is an alias for simdjson::to<void>
|
||||
auto parser2 = simdjson::from(json_car); // Same as parser1
|
||||
|
||||
// Both should parse the same way
|
||||
Car car1 = parser1;
|
||||
Car car2 = parser2;
|
||||
|
||||
if (car1.make != car2.make || car1.model != car2.model || car1.year != car2.year) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
#endif // SIMDJSON_EXCEPTIONS
|
||||
bool run() {
|
||||
return
|
||||
#if SIMDJSON_EXCEPTIONS && SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||
test_basic_adaptor() && broken() && simple() && simple_optional() && with_parser() && to_array() &&
|
||||
to_array_shortcut() && to_bad_array() && test_no_errors() &&
|
||||
to_clean_array() && test_to_adaptor_basic() &&
|
||||
test_to_adaptor_with_single_value() && test_to_vs_from_equivalence() &&
|
||||
#endif // SIMDJSON_EXCEPTIONS
|
||||
true;
|
||||
}
|
||||
|
||||
} // namespace convert_tests
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
return test_main(argc, argv, convert_tests::run);
|
||||
}
|
||||
#else
|
||||
int main() { return 0; }
|
||||
#endif
|
||||
Reference in New Issue
Block a user