mirror of
https://github.com/simdjson/simdjson
synced 2026-06-08 17:27:07 +00:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 677674d54a | |||
| 949bc5142c | |||
| b4d72ff70a | |||
| c8bde7cff0 | |||
| a4e3e386a0 | |||
| c343f6979c | |||
| bee5593cb2 | |||
| dfde6d5e44 | |||
| 414c5858ae | |||
| 142c9a93f8 | |||
| b4e7d717fc | |||
| bba20807be |
@@ -24,6 +24,35 @@ struct simdjson_ondemand {
|
|||||||
|
|
||||||
BENCHMARK_TEMPLATE(kostya, simdjson_ondemand)->UseManualTime();
|
BENCHMARK_TEMPLATE(kostya, simdjson_ondemand)->UseManualTime();
|
||||||
|
|
||||||
|
|
||||||
|
#if SIMDJSON_SUPPORTS_EXTRACT
|
||||||
|
using namespace simdjson::ondemand;
|
||||||
|
|
||||||
|
struct simdjson_ondemand_extract {
|
||||||
|
static constexpr diff_flags DiffFlags = diff_flags::NONE;
|
||||||
|
|
||||||
|
ondemand::parser parser{};
|
||||||
|
|
||||||
|
bool run(simdjson::padded_string &json, std::vector<point> &result) {
|
||||||
|
auto doc = parser.iterate(json);
|
||||||
|
for (ondemand::object object_point : doc.find_field("coordinates")) {
|
||||||
|
point p;
|
||||||
|
auto error = object_point.extract(
|
||||||
|
to{"x", p.x},
|
||||||
|
to{"y", p.y},
|
||||||
|
to{"z", p.z}
|
||||||
|
);
|
||||||
|
if(error) { return false; }
|
||||||
|
result.push_back(p);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BENCHMARK_TEMPLATE(kostya, simdjson_ondemand_extract)->UseManualTime();
|
||||||
|
|
||||||
|
#endif // SIMDJSON_SUPPORTS_EXTRACT
|
||||||
|
|
||||||
} // namespace kostya
|
} // namespace kostya
|
||||||
|
|
||||||
#endif // SIMDJSON_EXCEPTIONS
|
#endif // SIMDJSON_EXCEPTIONS
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ namespace partial_tweets {
|
|||||||
using namespace simdjson;
|
using namespace simdjson;
|
||||||
|
|
||||||
struct simdjson_ondemand {
|
struct simdjson_ondemand {
|
||||||
using StringType=std::string_view;
|
using StringType = std::string_view;
|
||||||
|
|
||||||
ondemand::parser parser{};
|
ondemand::parser parser{};
|
||||||
|
|
||||||
@@ -43,6 +43,48 @@ struct simdjson_ondemand {
|
|||||||
|
|
||||||
BENCHMARK_TEMPLATE(partial_tweets, simdjson_ondemand)->UseManualTime();
|
BENCHMARK_TEMPLATE(partial_tweets, simdjson_ondemand)->UseManualTime();
|
||||||
|
|
||||||
|
|
||||||
|
#if SIMDJSON_SUPPORTS_EXTRACT
|
||||||
|
|
||||||
|
using namespace simdjson::ondemand;
|
||||||
|
|
||||||
|
struct simdjson_ondemand_extract {
|
||||||
|
using StringType = std::string_view;
|
||||||
|
ondemand::parser parser{};
|
||||||
|
bool run(simdjson::padded_string &json, std::vector<tweet<std::string_view>> &result) {
|
||||||
|
// Walk the document, parsing the tweets as we go
|
||||||
|
auto doc = parser.iterate(json);
|
||||||
|
for (ondemand::object tweet_object : doc.find_field("statuses")) {
|
||||||
|
tweet<std::string_view> t;
|
||||||
|
auto error = tweet_object.extract(
|
||||||
|
to{"created_at", t.created_at},
|
||||||
|
to{"id", t.id},
|
||||||
|
to{"text", t.result},
|
||||||
|
to{"in_reply_to_status_id", [&t](auto val) {
|
||||||
|
if(val.is_null()) {
|
||||||
|
t.in_reply_to_status_id = 0;
|
||||||
|
} else {
|
||||||
|
t.in_reply_to_status_id = val;
|
||||||
|
}
|
||||||
|
}},
|
||||||
|
to{"user", sub{
|
||||||
|
to{"id", t.user.id},
|
||||||
|
to{"screen_name", t.user.screen_name},
|
||||||
|
}},
|
||||||
|
to{"retweet_count", t.retweet_count},
|
||||||
|
to{"favorite_count", t.favorite_count});
|
||||||
|
if(error) { return false; }
|
||||||
|
result.push_back(t);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BENCHMARK_TEMPLATE(partial_tweets, simdjson_ondemand_extract)->UseManualTime();
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace partial_tweets
|
} // namespace partial_tweets
|
||||||
|
|
||||||
#endif // SIMDJSON_EXCEPTIONS
|
#endif // SIMDJSON_EXCEPTIONS
|
||||||
|
|||||||
@@ -15,6 +15,134 @@ namespace simdjson {
|
|||||||
namespace SIMDJSON_IMPLEMENTATION {
|
namespace SIMDJSON_IMPLEMENTATION {
|
||||||
namespace ondemand {
|
namespace ondemand {
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef SIMDJSON_SUPPORTS_EXTRACT
|
||||||
|
|
||||||
|
template <endpoint ...Funcs>
|
||||||
|
simdjson_inline error_code object::extract(Funcs&&... endpoints)
|
||||||
|
#ifndef _MSC_VER // msvc thinks noexcept is not the same in definition
|
||||||
|
noexcept((nothrow_endpoint<Funcs> && ...))
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
raw_json_string field_key;
|
||||||
|
error_code error = SUCCESS;
|
||||||
|
for(auto pair : *this) {
|
||||||
|
if (error = pair.key().get(field_key); error) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
std::ignore = ((field_key.unsafe_is_equal(endpoints.key()) ? (error = endpoints(pair.value())) == SUCCESS : true) && ...);
|
||||||
|
if (error) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct to {
|
||||||
|
private:
|
||||||
|
T *pointer;
|
||||||
|
std::string_view m_key;
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr explicit(false)
|
||||||
|
to(std::string_view const inp_key, T &obj_ref) noexcept
|
||||||
|
: pointer{std::addressof(obj_ref)}, m_key{inp_key} {}
|
||||||
|
|
||||||
|
constexpr to(to const &) = default;
|
||||||
|
constexpr to(to &&) noexcept = default;
|
||||||
|
constexpr to &operator=(to const &) = default;
|
||||||
|
constexpr to &operator=(to &&) noexcept = default;
|
||||||
|
constexpr ~to() = default;
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr std::string_view key() const noexcept {
|
||||||
|
return m_key;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr error_code operator()(simdjson_result<value> val) noexcept(
|
||||||
|
std::is_nothrow_assignable_v<T, simdjson_result<value>>) {
|
||||||
|
return val.get<T>(*pointer);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Func>
|
||||||
|
requires(std::is_invocable_v<Func, simdjson_result<value>>)
|
||||||
|
struct to<Func> {
|
||||||
|
private:
|
||||||
|
Func func;
|
||||||
|
std::string_view m_key;
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr explicit(false)
|
||||||
|
to(std::string_view const inp_key,
|
||||||
|
Func &&inp_func) noexcept(std::is_nothrow_copy_assignable_v<Func>)
|
||||||
|
: func{std::forward<Func>(inp_func)}, m_key{inp_key} {}
|
||||||
|
|
||||||
|
constexpr to(to const &) = default;
|
||||||
|
constexpr to(to &&) noexcept = default;
|
||||||
|
constexpr to& operator=(to const &) = default;
|
||||||
|
constexpr to& operator=(to &&) noexcept = default;
|
||||||
|
constexpr ~to() = default;
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr std::string_view key() const noexcept {
|
||||||
|
return m_key;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr error_code operator()(simdjson_result<value> val) noexcept(
|
||||||
|
std::is_nothrow_invocable_v<Func, simdjson_result<value>>) {
|
||||||
|
if constexpr (std::is_invocable_r_v<error_code, Func, simdjson_result<value>>) {
|
||||||
|
return func(val);
|
||||||
|
} else {
|
||||||
|
static_cast<void>(func(val));
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Func>
|
||||||
|
requires(std::is_invocable_v<Func, simdjson_result<value>>)
|
||||||
|
to(std::string_view, Func &&) -> to<Func>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
to(std::string_view, T&) -> to<T>;
|
||||||
|
|
||||||
|
template <endpoint... Tos>
|
||||||
|
struct sub {
|
||||||
|
private:
|
||||||
|
using tuple_type = std::tuple<Tos...>;
|
||||||
|
tuple_type tos;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
// double templating to make perfect forwarding work
|
||||||
|
template <typename ...T>
|
||||||
|
requires ((std::same_as<T, Tos> && ...))
|
||||||
|
explicit constexpr sub(T&&...inp_tos) noexcept(std::is_nothrow_constructible_v<tuple_type, T...>)
|
||||||
|
: tos{std::forward<T>(inp_tos)...} {}
|
||||||
|
|
||||||
|
constexpr sub(sub const &) = default;
|
||||||
|
constexpr sub(sub &&) noexcept = default;
|
||||||
|
constexpr sub &operator=(sub const &) = default;
|
||||||
|
constexpr sub &operator=(sub &&) = default;
|
||||||
|
constexpr ~sub() = default;
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr error_code operator()(simdjson_result<value> val) noexcept((nothrow_endpoint<Tos> && ...)) {
|
||||||
|
object obj;
|
||||||
|
if (auto const err = val.get_object().get(obj); err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
return std::apply([&obj]<typename... T>(T &&...app_tos) {
|
||||||
|
return obj.extract(std::forward<T>(app_tos)...);
|
||||||
|
}, tos);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <endpoint... Tos>
|
||||||
|
sub(Tos&&...) -> sub<Tos...>;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
simdjson_inline simdjson_result<value> object::find_field_unordered(const std::string_view key) & noexcept {
|
simdjson_inline simdjson_result<value> object::find_field_unordered(const std::string_view key) & noexcept {
|
||||||
bool has_value;
|
bool has_value;
|
||||||
SIMDJSON_TRY( iter.find_field_unordered_raw(key).get(has_value) );
|
SIMDJSON_TRY( iter.find_field_unordered_raw(key).get(has_value) );
|
||||||
|
|||||||
@@ -11,6 +11,20 @@ namespace simdjson {
|
|||||||
namespace SIMDJSON_IMPLEMENTATION {
|
namespace SIMDJSON_IMPLEMENTATION {
|
||||||
namespace ondemand {
|
namespace ondemand {
|
||||||
|
|
||||||
|
#if defined(__cpp_concepts) && defined(__cpp_consteval)
|
||||||
|
#define SIMDJSON_SUPPORTS_EXTRACT 1
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
concept endpoint = std::is_invocable_r_v<error_code, T, simdjson_result<value>> &&
|
||||||
|
std::is_copy_constructible_v<T> && requires(T to) {
|
||||||
|
{ to.key() } noexcept -> std::convertible_to<std::string_view>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
concept nothrow_endpoint = endpoint<T> && std::is_nothrow_invocable_r_v<error_code, T, simdjson_result<value>>;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A forward-only JSON object field iterator.
|
* A forward-only JSON object field iterator.
|
||||||
*/
|
*/
|
||||||
@@ -65,6 +79,21 @@ public:
|
|||||||
/** @overload simdjson_inline simdjson_result<value> find_field(std::string_view key) & noexcept; */
|
/** @overload simdjson_inline simdjson_result<value> find_field(std::string_view key) & noexcept; */
|
||||||
simdjson_inline simdjson_result<value> find_field(std::string_view key) && noexcept;
|
simdjson_inline simdjson_result<value> find_field(std::string_view key) && noexcept;
|
||||||
|
|
||||||
|
#ifdef SIMDJSON_SUPPORTS_EXTRACT
|
||||||
|
/**
|
||||||
|
* Extract all the fields in one go
|
||||||
|
* Funcs are invocables that take a simdjson_result<value> as input.
|
||||||
|
*/
|
||||||
|
template <endpoint ...Funcs>
|
||||||
|
simdjson_inline error_code extract(Funcs&&... endpoints)
|
||||||
|
#ifndef _MSC_VER // msvc thinks noexcept is not the same in definition
|
||||||
|
noexcept((nothrow_endpoint<Funcs> && ...))
|
||||||
|
#endif
|
||||||
|
;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Look up a field by name on an object, without regard to key order.
|
* Look up a field by name on an object, without regard to key order.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ add_cpp_test(ondemand_to_string LABELS ondemand acceptance per_impl
|
|||||||
add_cpp_test(ondemand_twitter_tests LABELS ondemand acceptance per_implementation)
|
add_cpp_test(ondemand_twitter_tests LABELS ondemand acceptance per_implementation)
|
||||||
add_cpp_test(ondemand_wrong_type_error_tests LABELS ondemand acceptance per_implementation)
|
add_cpp_test(ondemand_wrong_type_error_tests LABELS ondemand acceptance per_implementation)
|
||||||
add_cpp_test(ondemand_iterate_many_csv LABELS ondemand acceptance per_implementation)
|
add_cpp_test(ondemand_iterate_many_csv LABELS ondemand acceptance per_implementation)
|
||||||
|
add_cpp_test(ondemand_extract_tests LABELS ondemand acceptance per_implementation)
|
||||||
if(NOT SIMDJSON_SANITIZE)
|
if(NOT SIMDJSON_SANITIZE)
|
||||||
add_cpp_test(ondemand_cacheline LABELS ondemand acceptance per_implementation)
|
add_cpp_test(ondemand_cacheline LABELS ondemand acceptance per_implementation)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -0,0 +1,116 @@
|
|||||||
|
#include "simdjson.h"
|
||||||
|
#include "test_ondemand.h"
|
||||||
|
|
||||||
|
using namespace simdjson;
|
||||||
|
|
||||||
|
namespace multi_get_tests {
|
||||||
|
#ifdef __cpp_concepts
|
||||||
|
struct Car {
|
||||||
|
std::string_view make;
|
||||||
|
std::string_view model;
|
||||||
|
std::int64_t year = 0;
|
||||||
|
struct wheel_size_type {
|
||||||
|
std::int64_t front = 0;
|
||||||
|
std::int64_t back = 0;
|
||||||
|
} wheels;
|
||||||
|
|
||||||
|
static simdjson_result<Car> create(auto& value) {
|
||||||
|
using ondemand::to;
|
||||||
|
using ondemand::sub;
|
||||||
|
|
||||||
|
simdjson::ondemand::object obj;
|
||||||
|
auto error = value.get_object().get(obj);
|
||||||
|
if (error) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
Car car{};
|
||||||
|
// Instead of this:
|
||||||
|
// 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;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// we can do this now:
|
||||||
|
error = obj.extract(
|
||||||
|
to{"wheels", sub{
|
||||||
|
to{"front", car.wheels.front},
|
||||||
|
to{"back", car.wheels.back},
|
||||||
|
}},
|
||||||
|
to{"make", car.make},
|
||||||
|
to{"model", car.model},
|
||||||
|
to{"year", [&car](auto val) {
|
||||||
|
car.year = val;
|
||||||
|
}});
|
||||||
|
if (error) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
return car;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
bool car_example() {
|
||||||
|
TEST_START();
|
||||||
|
simdjson::padded_string json =
|
||||||
|
R"( [ { "make": "Toyota", "model": "Camry", "year": 2018,
|
||||||
|
"tire_pressure": [ 40.1, 39.9 ], "wheels": { "front": 10, "back": 10 } },
|
||||||
|
{ "make": "Kia", "model": "Soul", "year": 2012,
|
||||||
|
"tire_pressure": [ 30.1, 31.0 ], "wheels": { "front": 10, "back": 10 } },
|
||||||
|
{ "make": "Toyota", "model": "Tercel", "year": 1999,
|
||||||
|
"tire_pressure": [ 29.8, 30.0 ], "wheels": { "front": 10, "back": 10 } }
|
||||||
|
])"_padded;
|
||||||
|
|
||||||
|
simdjson::ondemand::parser parser;
|
||||||
|
simdjson::ondemand::document doc = parser.iterate(json);
|
||||||
|
for (auto val : doc) {
|
||||||
|
auto car = Car::create(val);
|
||||||
|
ASSERT_EQUAL(car.error(), SUCCESS);
|
||||||
|
Car const c(car.value());
|
||||||
|
if (c.make != "Toyota" && c.make != "Kia") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (c.model != "Camry" && c.model != "Soul" && c.model != "Tercel") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (c.year != 2018 && c.year != 2012 && c.year != 1999) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (c.wheels.front != 10 || c.wheels.back != 10) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
bool run() {
|
||||||
|
return
|
||||||
|
#ifdef __cpp_concepts
|
||||||
|
car_example() &&
|
||||||
|
#endif
|
||||||
|
true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace multi_get_tests
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
return test_main(argc, argv, multi_get_tests::run);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user