#ifndef SIMDJSON_CONVERT_H #define SIMDJSON_CONVERT_H #if __cpp_concepts #include "simdjson/ondemand.h" #include #ifdef __cpp_lib_ranges #include #endif namespace simdjson { struct [[nodiscard]] auto_iterator_end {}; /** * A Wrapper for simdjson_result 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; using value_type = simdjson_result; // 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 struct [[nodiscard]] auto_parser #if __cpp_lib_ranges : std::ranges::view_interface> #endif { using value_type = simdjson_result; 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 static constexpr bool is_nothrow_gettable = requires(ondemand::document doc) { { doc.get() } noexcept; }; public: // non-pointer constructors: explicit auto_parser(ParserType &&parser, ondemand::document &&doc) noexcept requires(!std::is_pointer_v) : 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) : 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) : auto_parser{ParserType{}, str} {} // pointer constructors: explicit auto_parser(std::remove_pointer_t &parser, ondemand::document &&doc) noexcept requires(std::is_pointer_v) : m_parser{&parser}, m_doc{std::move(doc)} {} explicit auto_parser(std::remove_pointer_t &parser, padded_string_view const str) noexcept requires(std::is_pointer_v) : 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) : 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 &parser() noexcept { if constexpr (std::is_pointer_v) { return *m_parser; } else { return m_parser; } } template [[nodiscard]] simdjson_inline simdjson_result result() noexcept(is_nothrow_gettable) { 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(); } [[nodiscard]] simdjson_inline simdjson_result array() noexcept { return result(); } [[nodiscard]] simdjson_inline simdjson_result object() noexcept { return result(); } [[nodiscard]] simdjson_inline simdjson_result number() noexcept { return result(); } template [[nodiscard]] simdjson_inline explicit(false) operator simdjson_result() noexcept(is_nothrow_gettable) { return result(); } template [[nodiscard]] simdjson_inline explicit(false) operator T() noexcept(false) { if (m_error != SUCCESS) { throw simdjson_error(m_error); } return m_doc.get(); } // We can't have "operator std::optional" 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 [[nodiscard]] simdjson_inline std::optional optional() noexcept(is_nothrow_gettable) { if (m_error != SUCCESS) { return std::nullopt; } T value; // For std::optional if (m_doc.get().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 const &val) const noexcept { return val.error() == SUCCESS; } template auto operator()(Range &&rng) const noexcept { return std::forward(rng) | std::views::filter(*this); } } no_errors; template struct [[nodiscard]] to_adaptor { /// Convert to T [[nodiscard]] T operator()(simdjson_result &val) const noexcept { return val.get(); } /// Make it an adaptor template auto operator()(Range &&rng) const noexcept { return std::forward(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{parser, str}; } }; template static constexpr to_adaptor to{}; static constexpr to_adaptor<> from{}; template using as = to_adaptor; // For C++20 ranges without range_adaptor_closure, we need to define pipe operators template inline auto operator|(Range&& range, const no_errors_adaptor& adaptor) { return adaptor(std::forward(range)); } template inline auto operator|(Range&& range, const to_adaptor& adaptor) { return adaptor(std::forward(range)); } #endif // __cpp_lib_ranges } // namespace simdjson #endif // __cpp_concepts #endif // SIMDJSON_CONVERT_H