diff --git a/gbench/files.cpp b/gbench/files.cpp index b4536a1d..f7be9da2 100644 --- a/gbench/files.cpp +++ b/gbench/files.cpp @@ -3,6 +3,7 @@ #include "types.hpp" #include #include +#include template void execute_handler_rapid(std::string_view json) { diff --git a/include/tgbm/tools/json_tools/all.hpp b/include/tgbm/tools/json_tools/all.hpp index 9d0a00a8..4b67b428 100644 --- a/include/tgbm/tools/json_tools/all.hpp +++ b/include/tgbm/tools/json_tools/all.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include diff --git a/include/tgbm/tools/json_tools/boost_parse_dom.hpp b/include/tgbm/tools/json_tools/boost_parse_dom.hpp index 96085be1..20822656 100644 --- a/include/tgbm/tools/json_tools/boost_parse_dom.hpp +++ b/include/tgbm/tools/json_tools/boost_parse_dom.hpp @@ -1,8 +1,6 @@ #pragma once #include -#include "boost/pfr/core_name.hpp" -#include "boost/pfr/tuple_size.hpp" #include #include #include @@ -12,191 +10,115 @@ #include #include #include +#include +#include -namespace boost::json { +namespace tgbm::json { +struct boost_json_traits { + using type = ::boost::json::value; -template -T tag_invoke(const value_to_tag&, const value& jv) { - if (!jv.is_object()) { - TGBM_JSON_PARSE_ERROR; + static bool is_bool(const type& json) { + return json.is_bool(); } - auto& object = jv.as_object(); - auto construct_field = [&]() { - using Field = pfr::tuple_element_t; - auto it = object.find(pfr_extension::get_name()); - boost::json::value null(nullptr); - return boost::json::value_to(it != object.end() ? *it : null); - }; - - return [&](std::index_sequence) { - return T{construct_field.template operator()()...}; - }(std::make_index_sequence>{}); -} - -namespace details { - -template -auto construct_field(const boost::json::object& object) { - using Field = pfr::tuple_element_t; - constexpr std::string_view name = pfr_extension::get_name(); - auto it = object.find(name); - if (it == object.end()) { - if constexpr (T::is_optional_field(name)) { - return Field{}; - } else { - TGBM_JSON_PARSE_ERROR; - } + static bool is_integer(const type& json) { + return json.is_int64(); } - return boost::json::value_to(it->value()); -} -template -auto construct_req_field(const boost::json::object& object) { - using Field = pfr::tuple_element_t; - constexpr std::string_view name = pfr_extension::element_name_v; - auto it = object.find(name); - if (it == object.end()) { - TGBM_JSON_PARSE_ERROR; + static bool is_uinteger(const type& json) { + return json.is_uint64(); } - return boost::json::value_to(it->value()); -} -} // namespace details - -template -T tag_invoke(const value_to_tag&, const value& jv) { - if (!jv.is_object()) { - TGBM_JSON_PARSE_ERROR; + static bool is_floating(const type& json) { + return json.is_double(); } - auto& object = jv.as_object(); - return [&](std::index_sequence) { - return T(details::construct_field(object)...); - }(std::make_index_sequence>{}); -} - -inline tgbm::api::Integer tag_invoke(const value_to_tag&, const value& jv) { - tgbm::api::Integer result; - if (!jv.is_int64()) { - TGBM_JSON_PARSE_ERROR; + static bool is_string(const type& json) { + return json.is_string(); } - auto data = jv.as_int64(); - if (data > result.max) { - TGBM_JSON_PARSE_ERROR; + + static bool is_object(const type& json) { + return json.is_object(); } - return data; -} -template -T tag_invoke(const value_to_tag&, const value& jv) { - constexpr std::string_view discriminator = T::discriminator; - if (!jv.is_object()) { - TGBM_JSON_PARSE_ERROR; + static bool is_array(const type& json) { + return json.is_array(); } - auto& object = jv.as_object(); - auto d_value = object.find(discriminator); - if (d_value == object.end()) [[unlikely]] { - TGBM_JSON_PARSE_ERROR; + + static bool is_null(const type& json) { + return json.is_null(); } - if (!d_value->value().is_string()) [[unlikely]] { - TGBM_JSON_PARSE_ERROR; + + // Получение значений + static bool get_bool(const type& json) { + return json.as_bool(); } - return T::discriminate(d_value->value().as_string(), [&]() -> T { - if constexpr (std::same_as) { - TGBM_JSON_PARSE_ERROR; - } else { - return T{boost::json::value_to(jv)}; - } - }); -} -template -tgbm::box tag_invoke(const value_to_tag>&, const value& jv) { - if (jv.is_null()) [[unlikely]] { - return nullptr; + static std::int64_t get_integer(const type& json) { + return json.as_int64(); } - return tgbm::box{std::in_place, boost::json::value_to(jv)}; -} -template -std::vector tag_invoke(const value_to_tag>&, const value& jv) { - if (!jv.is_array()) { - TGBM_JSON_PARSE_ERROR; + static std::uint64_t get_uinteger(const type& json) { + return json.as_uint64(); } - auto ja = jv.as_array(); - std::vector result; - result.reserve(ja.size()); - for (auto& elem : ja) { - result.emplace_back(boost::json::value_to(elem)); + + static double get_floating(const type& json) { + return json.as_double(); } - return result; -} -template -std::optional tag_invoke(const value_to_tag>&, const value& jv) { - if (jv.is_null()) [[unlikely]] { - return std::nullopt; + static std::string_view get_string(const type& json) { + return std::string_view{json.as_string().data(), json.as_string().size()}; } - return std::optional{boost::json::value_to(jv)}; -} -template -tgbm::api::optional tag_invoke(const value_to_tag>&, const value& jv) { - if (jv.is_null()) [[unlikely]] { - return std::nullopt; + // Обработка ошибок + static void on_error() { + throw std::runtime_error("JSON Error"); } - return tgbm::api::optional{boost::json::value_to(jv)}; -} -inline tgbm::api::Double tag_invoke(const value_to_tag&, const value& jv) { - if (!jv.is_double()) { - TGBM_JSON_PARSE_ERROR; + // Поиск поля в объекте + static const type* find_field(const type& json, std::string_view key) { + assert(json.is_object()); + auto& obj = json.as_object(); + auto it = obj.find(key); + if (it != obj.end()) { + return &it->value(); + } + return nullptr; } - return jv.as_double(); -} -inline tgbm::api::True tag_invoke(const value_to_tag&, const value& jv) { - if (!jv.is_bool() || !jv.as_bool()) { - TGBM_JSON_PARSE_ERROR; + // Доступ к элементу по индексу + static const type& element_by_index(const type& json, std::size_t index) { + assert(json.is_array()); + auto& arr = json.as_array(); + if (index >= arr.size()) { + on_error(); + } + return arr[index]; } - return {}; -} -inline tgbm::nothing_t tag_invoke(const value_to_tag&, const value& jv) { - if (!jv.is_object()) { - TGBM_JSON_PARSE_ERROR; + // Размер объекта или массива + static std::size_t size(const type& json) { + assert(json.is_object() || json.is_array()); + if (json.is_object()) { + return json.as_object().size(); + } else { + return json.as_array().size(); + } + unreachable(); } - return {}; -} -template -T tag_invoke(const value_to_tag&, const value& jv) { - if (!jv.is_object()) { - TGBM_JSON_PARSE_ERROR; - } - auto& jv_obj = jv.as_object(); - constexpr std::size_t N = tgbm::oneof_field_utils::N; - - using Data = decltype(T::data); - auto construct_data_field = [&]() { - Data data; - for (auto&& [key, value] : jv_obj) { - if (!tgbm::oneof_field_utils::is_required(key)) { - tgbm::oneof_field_utils::emplace_field( - data, key, [&](Field& field) { field = boost::json::value_to(value); }, - [] { TGBM_JSON_PARSE_ERROR; }, [] {}); - } + // Диапазон членов объекта + static auto& member_range(const type& json) { + if (!json.is_object()) { + on_error(); } - return data; - }; - return tgbm::call_on_indexes([&] { - return T{details::construct_req_field(jv_obj)..., construct_data_field()}; - }); -} + return json.as_object(); + } +}; -} // namespace boost::json +static_assert(json_traits); +} // namespace tgbm::json namespace tgbm::json::boost { @@ -204,7 +126,9 @@ template T parse_dom(std::string_view val) { using namespace ::boost::json; value j = parse(val); - return value_to(j); + T out; + parse_dom::parser::template parse<::boost::json::value, boost_json_traits>(j, out); + return out; } } // namespace tgbm::json::boost diff --git a/include/tgbm/tools/json_tools/generator_parser/discriminated_api.hpp b/include/tgbm/tools/json_tools/generator_parser/discriminated_api.hpp index d73658a9..aae5ec8d 100644 --- a/include/tgbm/tools/json_tools/generator_parser/discriminated_api.hpp +++ b/include/tgbm/tools/json_tools/generator_parser/discriminated_api.hpp @@ -1,6 +1,6 @@ #pragma once #include -#include "tgbm/tools/json_tools/generator_parser/ignore_parser.hpp" +#include "tgbm/tools/traits.hpp" namespace tgbm::generator_parser { diff --git a/include/tgbm/tools/json_tools/json_traits.hpp b/include/tgbm/tools/json_tools/json_traits.hpp new file mode 100644 index 00000000..5073ceb9 --- /dev/null +++ b/include/tgbm/tools/json_tools/json_traits.hpp @@ -0,0 +1,54 @@ +#pragma once + +#include +#include +#include +#include + +namespace tgbm::json { +template +concept json_traits = requires(const Json& json, std::string_view str, std::size_t sz) { + requires std::same_as; + + { Traits::is_bool(json) } -> std::same_as; + + { Traits::is_integer(json) } -> std::same_as; + + { Traits::is_uinteger(json) } -> std::same_as; + + { Traits::is_floating(json) } -> std::same_as; + + { Traits::is_string(json) } -> std::same_as; + + { Traits::is_object(json) } -> std::same_as; + + { Traits::is_array(json) } -> std::same_as; + + { Traits::is_object(json) } -> std::same_as; + + { Traits::is_null(json) } -> std::same_as; + + { Traits::get_bool(json) } -> std::same_as; + + { Traits::on_error() }; + + { Traits::get_integer(json) } -> std::same_as; + + { Traits::get_uinteger(json) } -> std::same_as; + + { Traits::get_string(json) } -> std::convertible_to; + + { Traits::get_floating(json) } -> std::same_as; + + { Traits::find_field(json, str) }; + + { Traits::element_by_index(json, sz) } -> std::same_as; + + { Traits::size(json) } -> std::same_as; + + { Traits::member_range(json) } -> std::ranges::range; +}; + +template +struct default_json_traits {}; +} // namespace tgbm::json diff --git a/include/tgbm/tools/json_tools/parse_dom/all.hpp b/include/tgbm/tools/json_tools/parse_dom/all.hpp new file mode 100644 index 00000000..51a09478 --- /dev/null +++ b/include/tgbm/tools/json_tools/parse_dom/all.hpp @@ -0,0 +1,9 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include diff --git a/include/tgbm/tools/json_tools/parse_dom/array.hpp b/include/tgbm/tools/json_tools/parse_dom/array.hpp new file mode 100644 index 00000000..318f7369 --- /dev/null +++ b/include/tgbm/tools/json_tools/parse_dom/array.hpp @@ -0,0 +1,23 @@ +#pragma once +#include +#include +#include + +namespace tgbm::json::parse_dom { + +template +struct parser> { + template + static void parse(const Json& json, std::vector& out) { + if (!Traits::is_array(json)) { + Traits::on_error(); + } + std::size_t size = Traits::size(json); + out.reserve(size); + for (std::size_t i = 0; i < size; i++) { + auto& element_node = Traits::element_by_index(json, i); + parser::template parse(element_node, out.emplace_back()); + } + } +}; +} // namespace tgbm::json::parse_dom diff --git a/include/tgbm/tools/json_tools/parse_dom/basic.hpp b/include/tgbm/tools/json_tools/parse_dom/basic.hpp new file mode 100644 index 00000000..257980a4 --- /dev/null +++ b/include/tgbm/tools/json_tools/parse_dom/basic.hpp @@ -0,0 +1,12 @@ +#pragma once + +namespace tgbm::json::parse_dom { + +template +struct parser { + template + static void parse(const Json&, T&) { + static_assert(sizeof(Json) == 0, "Write your specialization"); + } +}; +} // namespace tgbm::json::parse_dom diff --git a/include/tgbm/tools/json_tools/parse_dom/common_api.hpp b/include/tgbm/tools/json_tools/parse_dom/common_api.hpp new file mode 100644 index 00000000..4458f052 --- /dev/null +++ b/include/tgbm/tools/json_tools/parse_dom/common_api.hpp @@ -0,0 +1,32 @@ +#pragma once +#include +#include +#include +#include "tgbm/tools/pfr_extension.hpp" +#include "tgbm/tools/traits.hpp" + +namespace tgbm::json::parse_dom { + +template +struct parser { + template + static void parse(const Json& json, T& out) { + if (!Traits::is_object(json)) { + Traits::on_error(); + } + pfr_extension::visit_object(out, [&](Field& field) { + constexpr std::string_view name = Info::name.AsStringView(); + auto json_field = Traits::find_field(json, name); + if (!json_field) [[unlikely]] { + if constexpr (T::is_optional_field(name)) { + field = Field{}; + return; + } else { + Traits::on_error(); + } + } + parser::template parse(*json_field, field); + }); + } +}; +} // namespace tgbm::json::parse_dom diff --git a/include/tgbm/tools/json_tools/parse_dom/discriminated_api.hpp b/include/tgbm/tools/json_tools/parse_dom/discriminated_api.hpp new file mode 100644 index 00000000..9a15b690 --- /dev/null +++ b/include/tgbm/tools/json_tools/parse_dom/discriminated_api.hpp @@ -0,0 +1,39 @@ +#pragma once +#include +#include +#include +#include "tgbm/tools/traits.hpp" + +namespace tgbm::json::parse_dom { + +template +struct parser { + template + static void parse(const Json& json, T& out) { + if (!Traits::is_object(json)) { + Traits::on_error(); + } + constexpr std::string_view discriminator = T::discriminator; + + auto d_node = Traits::find_field(json, discriminator); + if (!d_node) [[unlikely]] { + Traits::on_error(); + } + auto& d_ref = *d_node; + + if (!Traits::is_string(d_ref)) [[unlikely]] { + Traits::on_error(); + } + + auto str = Traits::get_string(d_ref); + + T::discriminate(str, [&]() { + if constexpr (std::same_as) { + Traits::on_error(); + } else { + parser::template parse(json, out.data.template emplace()); + } + }); + } +}; +} // namespace tgbm::json::parse_dom diff --git a/include/tgbm/tools/json_tools/parse_dom/fundamental.hpp b/include/tgbm/tools/json_tools/parse_dom/fundamental.hpp new file mode 100644 index 00000000..733f68a1 --- /dev/null +++ b/include/tgbm/tools/json_tools/parse_dom/fundamental.hpp @@ -0,0 +1,97 @@ +#pragma once +#include +#include +#include +#include +#include +#include "tgbm/tools/math.hpp" + +namespace tgbm::json::parse_dom { + +template <> +struct parser { + template > + static void parse(const Json& json, std::string& out) { + if (Traits::is_string(json)) { + out = Traits::get_string(json); + } else { + Traits::on_error(); + } + } +}; + +template +struct parser { + template > + static void parse(const Json& json, T& out) { + if (Traits::is_uinteger(json)) { + safe_write(out, Traits::get_uinteger(json)); + } else if (Traits::is_integer(json)) { + safe_write(out, Traits::get_integer(json)); + } else { + Traits::on_error(); + } + } +}; + +template +struct parser { + template + static void parse(const Json& json, T& out) { + if (Traits::is_floating(json)) { + out = Traits::get_floating(json); + } else { + Traits::on_error(); + } + } +}; + +template <> +struct parser { + template + static void parse(const Json& json, tgbm::api::Integer& out) { + if (Traits::is_integer(json)) { + safe_write(out, Traits::get_integer(json)); + } else if (Traits::is_uinteger(json)) { + safe_write(out, Traits::get_uinteger(json)); + } else { + Traits::on_error(); + } + } +}; + +template <> +struct parser { + template + static void parse(const Json& json, bool& out) { + if (Traits::is_bool(json)) { + out = Traits::get_bool(json); + } else { + Traits::on_error(); + } + } +}; + +template <> +struct parser { + template + static void parse(const Json& json, tgbm::api::True& out) { + if (!Traits::is_bool(json) || !Traits::get_bool(json)) { + Traits::on_error(); + } + } +}; + +template <> +struct parser { + template + static void parse(const Json& json, api::Double& out) { + if (Traits::is_floating(json)) { + out = Traits::get_floating(json); + } else { + Traits::on_error(); + } + } +}; + +} // namespace tgbm::json::parse_dom diff --git a/include/tgbm/tools/json_tools/parse_dom/oneof_field_api_type.hpp b/include/tgbm/tools/json_tools/parse_dom/oneof_field_api_type.hpp new file mode 100644 index 00000000..c5f4dd9f --- /dev/null +++ b/include/tgbm/tools/json_tools/parse_dom/oneof_field_api_type.hpp @@ -0,0 +1,49 @@ +#pragma once +#include +#include +#include +#include +#include + +namespace tgbm::json::parse_dom { + +template +struct parser { + template + static void parse(const Json& json, T& out) { + if (!Traits::is_object(json)) [[unlikely]] { + Traits::on_error(); + } + constexpr std::size_t N = tgbm::oneof_field_utils::N; + + using Data = decltype(T::data); + + // clang-format off + auto m = matcher{ + [&](Field& req_field) { + constexpr std::string_view name = Info::name.AsStringView(); + auto node = Traits::find_field(json, name); + if (!node || Traits::is_null(*node)){ + Traits::on_error(); + } + parser::template parse(*node, req_field); + }, + [&](Data& data){ + for (auto&& [key, value] : Traits::member_range(json)) { + if (!tgbm::oneof_field_utils::is_required(key)) { + tgbm::oneof_field_utils::emplace_field( + data, key, + [&](Field& field) { parser::template parse(value, field); }, + [] { Traits::on_error(); }, [] {}); + return; + } + } + } + }; + // clang-format on + + pfr_extension::visit_object(out, m); + } +}; + +} // namespace tgbm::json::parse_dom diff --git a/include/tgbm/tools/json_tools/parse_dom/optional.hpp b/include/tgbm/tools/json_tools/parse_dom/optional.hpp new file mode 100644 index 00000000..bdf1b9f5 --- /dev/null +++ b/include/tgbm/tools/json_tools/parse_dom/optional.hpp @@ -0,0 +1,39 @@ +#pragma once +#include +#include +#include + +namespace tgbm::json::parse_dom { + +template