From 9c220c71c36b28b1247332d3ee8c58c8d9aca6ff Mon Sep 17 00:00:00 2001 From: tang zhixiong Date: Sat, 31 Jan 2026 11:32:18 +0800 Subject: [PATCH] update jsoncons, release --- build.py | 2 +- include/jsoncons/allocator_holder.hpp | 2 +- include/jsoncons/allocator_set.hpp | 65 +- include/jsoncons/basic_json.hpp | 1208 ++++++---- include/jsoncons/config/compiler_support.hpp | 102 +- include/jsoncons/config/jsoncons_config.hpp | 66 +- include/jsoncons/config/version.hpp | 19 +- include/jsoncons/conv_error.hpp | 71 +- include/jsoncons/conversion_result.hpp | 69 + include/jsoncons/decode_json.hpp | 433 ++-- include/jsoncons/detail/endian.hpp | 2 +- include/jsoncons/detail/expected.hpp | 470 ++++ .../detail/make_obj_using_allocator.hpp | 76 + include/jsoncons/detail/optional.hpp | 12 +- include/jsoncons/detail/span.hpp | 2 +- include/jsoncons/detail/string_view.hpp | 2 +- include/jsoncons/detail/utility.hpp | 35 + include/jsoncons/diagnostics_visitor.hpp | 198 ++ include/jsoncons/diagnostics_visitor2.hpp | 120 + include/jsoncons/encode_json.hpp | 474 ++-- include/jsoncons/item_event_visitor.hpp | 116 +- include/jsoncons/json.hpp | 4 +- include/jsoncons/json_array.hpp | 2 +- include/jsoncons/json_cursor.hpp | 138 +- include/jsoncons/json_decoder.hpp | 8 +- include/jsoncons/json_encoder.hpp | 576 ++--- include/jsoncons/json_encoders.hpp | 182 ++ include/jsoncons/json_error.hpp | 7 +- include/jsoncons/json_exception.hpp | 114 +- include/jsoncons/json_filter.hpp | 4 +- include/jsoncons/json_fwd.hpp | 2 +- include/jsoncons/json_object.hpp | 60 +- include/jsoncons/json_options.hpp | 93 +- include/jsoncons/json_parser.hpp | 1064 ++++----- include/jsoncons/json_reader.hpp | 76 +- include/jsoncons/json_type.hpp | 84 +- include/jsoncons/json_type_traits.hpp | 1893 +--------------- include/jsoncons/json_visitor.hpp | 179 +- include/jsoncons/pretty_print.hpp | 3 +- include/jsoncons/reflect/decode_traits.hpp | 733 ++++++ include/jsoncons/reflect/encode_traits.hpp | 405 ++++ include/jsoncons/reflect/json_conv_traits.hpp | 2008 +++++++++++++++++ .../jsoncons/reflect/reflect_traits_gen.hpp | 1939 ++++++++++++++++ include/jsoncons/semantic_tag.hpp | 2 +- include/jsoncons/ser_util.hpp | 123 + include/jsoncons/sink.hpp | 49 +- include/jsoncons/source.hpp | 270 ++- include/jsoncons/source_adaptor.hpp | 2 +- include/jsoncons/staj_cursor.hpp | 335 ++- include/jsoncons/staj_event.hpp | 84 +- include/jsoncons/staj_event_reader.hpp | 9 +- include/jsoncons/staj_iterator.hpp | 246 +- include/jsoncons/text_source_adaptor.hpp | 2 +- include/jsoncons/typed_array_view.hpp | 9 +- include/jsoncons/utility/bigint.hpp | 1814 +++++++++------ include/jsoncons/utility/byte_string.hpp | 57 +- include/jsoncons/utility/conversion.hpp | 61 + include/jsoncons/utility/heap_string.hpp | 271 ++- include/jsoncons/utility/more_type_traits.hpp | 107 +- include/jsoncons/utility/read_number.hpp | 1037 +++++++++ include/jsoncons/utility/unicode_traits.hpp | 3 +- include/jsoncons/utility/uri.hpp | 28 +- include/jsoncons/utility/write_number.hpp | 581 +++++ include/jsoncons_ext/bson/bson.hpp | 2 +- include/jsoncons_ext/bson/bson_cursor.hpp | 51 +- include/jsoncons_ext/bson/bson_decimal128.hpp | 22 +- include/jsoncons_ext/bson/bson_encoder.hpp | 14 +- include/jsoncons_ext/bson/bson_error.hpp | 2 +- include/jsoncons_ext/bson/bson_options.hpp | 33 +- include/jsoncons_ext/bson/bson_parser.hpp | 18 +- include/jsoncons_ext/bson/bson_reader.hpp | 10 +- include/jsoncons_ext/bson/bson_type.hpp | 2 +- include/jsoncons_ext/bson/decode_bson.hpp | 414 ++-- include/jsoncons_ext/bson/encode_bson.hpp | 217 +- include/jsoncons_ext/cbor/cbor.hpp | 2 +- include/jsoncons_ext/cbor/cbor_cursor.hpp | 51 +- include/jsoncons_ext/cbor/cbor_detail.hpp | 2 +- include/jsoncons_ext/cbor/cbor_encoder.hpp | 68 +- include/jsoncons_ext/cbor/cbor_error.hpp | 2 +- .../jsoncons_ext/cbor/cbor_event_reader.hpp | 53 +- include/jsoncons_ext/cbor/cbor_options.hpp | 41 +- include/jsoncons_ext/cbor/cbor_parser.hpp | 28 +- include/jsoncons_ext/cbor/cbor_reader.hpp | 2 +- include/jsoncons_ext/cbor/decode_cbor.hpp | 412 ++-- include/jsoncons_ext/cbor/encode_cbor.hpp | 239 +- include/jsoncons_ext/csv/csv.hpp | 2 +- include/jsoncons_ext/csv/csv_cursor.hpp | 124 +- include/jsoncons_ext/csv/csv_encoder.hpp | 39 +- include/jsoncons_ext/csv/csv_error.hpp | 7 +- include/jsoncons_ext/csv/csv_options.hpp | 2 +- include/jsoncons_ext/csv/csv_parser.hpp | 153 +- include/jsoncons_ext/csv/csv_reader.hpp | 68 +- include/jsoncons_ext/csv/decode_csv.hpp | 388 ++-- include/jsoncons_ext/csv/encode_csv.hpp | 197 +- include/jsoncons_ext/jmespath/jmespath.hpp | 106 +- .../jsoncons_ext/jmespath/jmespath_error.hpp | 2 +- include/jsoncons_ext/jsonpatch/jsonpatch.hpp | 10 +- .../jsonpatch/jsonpatch_error.hpp | 15 +- include/jsoncons_ext/jsonpath/flatten.hpp | 16 +- .../jsoncons_ext/jsonpath/json_location.hpp | 16 +- include/jsoncons_ext/jsonpath/json_query.hpp | 40 +- include/jsoncons_ext/jsonpath/jsonpath.hpp | 2 +- .../jsoncons_ext/jsonpath/jsonpath_error.hpp | 6 +- .../jsonpath/jsonpath_expression.hpp | 40 +- .../jsoncons_ext/jsonpath/jsonpath_parser.hpp | 30 +- .../jsonpath/jsonpath_selector.hpp | 6 +- .../jsonpath/jsonpath_utilities.hpp | 2 +- include/jsoncons_ext/jsonpath/path_node.hpp | 10 +- .../jsoncons_ext/jsonpath/token_evaluator.hpp | 63 +- .../jsoncons_ext/jsonpointer/jsonpointer.hpp | 312 +-- .../jsonpointer/jsonpointer_error.hpp | 13 +- .../jsonschema/common/compilation_context.hpp | 2 +- .../jsonschema/common/eval_context.hpp | 2 +- .../jsoncons_ext/jsonschema/common/format.hpp | 3 +- .../jsonschema/common/keyword_validator.hpp | 81 +- .../common/keyword_validator_factory.hpp | 25 +- .../jsonschema/common/schema_validator.hpp | 10 +- .../common/schema_validator_factory_base.hpp | 10 +- .../jsonschema/common/uri_wrapper.hpp | 4 +- .../jsonschema/common/validator.hpp | 2 +- .../draft201909/schema_draft201909.hpp | 2 +- .../schema_validator_factory_201909.hpp | 15 +- .../draft202012/schema_draft202012.hpp | 2 +- .../schema_validator_factory_202012.hpp | 13 +- .../jsonschema/draft4/schema_draft4.hpp | 2 +- .../draft4/schema_validator_factory_4.hpp | 17 +- .../jsonschema/draft6/schema_draft6.hpp | 2 +- .../draft6/schema_validator_factory_6.hpp | 17 +- .../jsonschema/draft7/schema_draft7.hpp | 2 +- .../draft7/schema_validator_factory_7.hpp | 17 +- .../jsonschema/evaluation_options.hpp | 2 +- .../jsoncons_ext/jsonschema/json_schema.hpp | 2 +- .../jsonschema/json_schema_factory.hpp | 2 +- .../jsoncons_ext/jsonschema/jsonschema.hpp | 2 +- .../jsonschema/jsonschema_error.hpp | 2 +- .../jsonschema/validation_message.hpp | 4 +- .../jsoncons_ext/mergepatch/mergepatch.hpp | 2 +- .../jsoncons_ext/msgpack/decode_msgpack.hpp | 411 ++-- .../jsoncons_ext/msgpack/encode_msgpack.hpp | 133 +- include/jsoncons_ext/msgpack/msgpack.hpp | 2 +- .../jsoncons_ext/msgpack/msgpack_cursor.hpp | 53 +- .../jsoncons_ext/msgpack/msgpack_encoder.hpp | 32 +- .../jsoncons_ext/msgpack/msgpack_error.hpp | 2 +- .../msgpack/msgpack_event_reader.hpp | 51 +- .../jsoncons_ext/msgpack/msgpack_options.hpp | 33 +- .../jsoncons_ext/msgpack/msgpack_parser.hpp | 12 +- .../jsoncons_ext/msgpack/msgpack_reader.hpp | 2 +- include/jsoncons_ext/msgpack/msgpack_type.hpp | 2 +- include/jsoncons_ext/toon/encode_toon.hpp | 1248 ++++++++++ include/jsoncons_ext/toon/toon.hpp | 13 + include/jsoncons_ext/toon/toon_options.hpp | 168 ++ include/jsoncons_ext/ubjson/decode_ubjson.hpp | 412 ++-- include/jsoncons_ext/ubjson/encode_ubjson.hpp | 224 +- include/jsoncons_ext/ubjson/ubjson.hpp | 2 +- include/jsoncons_ext/ubjson/ubjson_cursor.hpp | 51 +- .../jsoncons_ext/ubjson/ubjson_encoder.hpp | 18 +- include/jsoncons_ext/ubjson/ubjson_error.hpp | 2 +- .../jsoncons_ext/ubjson/ubjson_options.hpp | 40 +- include/jsoncons_ext/ubjson/ubjson_parser.hpp | 30 +- include/jsoncons_ext/ubjson/ubjson_reader.hpp | 2 +- include/jsoncons_ext/ubjson/ubjson_type.hpp | 2 +- 161 files changed, 17029 insertions(+), 7722 deletions(-) create mode 100644 include/jsoncons/conversion_result.hpp create mode 100644 include/jsoncons/detail/expected.hpp create mode 100644 include/jsoncons/detail/make_obj_using_allocator.hpp create mode 100644 include/jsoncons/detail/utility.hpp create mode 100644 include/jsoncons/diagnostics_visitor.hpp create mode 100644 include/jsoncons/diagnostics_visitor2.hpp create mode 100644 include/jsoncons/json_encoders.hpp create mode 100644 include/jsoncons/reflect/decode_traits.hpp create mode 100644 include/jsoncons/reflect/encode_traits.hpp create mode 100644 include/jsoncons/reflect/json_conv_traits.hpp create mode 100644 include/jsoncons/reflect/reflect_traits_gen.hpp create mode 100644 include/jsoncons/ser_util.hpp create mode 100644 include/jsoncons/utility/conversion.hpp create mode 100644 include/jsoncons/utility/read_number.hpp create mode 100644 include/jsoncons/utility/write_number.hpp create mode 100644 include/jsoncons_ext/toon/encode_toon.hpp create mode 100644 include/jsoncons_ext/toon/toon.hpp create mode 100644 include/jsoncons_ext/toon/toon_options.hpp diff --git a/build.py b/build.py index da41822..84b8fb5 100644 --- a/build.py +++ b/build.py @@ -6,7 +6,7 @@ from setuptools import setup, find_packages name = "cubao_headers" -version = "0.0.8" +version = "0.1.0" pattern = ["*"] diff --git a/include/jsoncons/allocator_holder.hpp b/include/jsoncons/allocator_holder.hpp index 5a34345..81aec2e 100644 --- a/include/jsoncons/allocator_holder.hpp +++ b/include/jsoncons/allocator_holder.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/include/jsoncons/allocator_set.hpp b/include/jsoncons/allocator_set.hpp index c6e1fb5..f877a06 100644 --- a/include/jsoncons/allocator_set.hpp +++ b/include/jsoncons/allocator_set.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -13,17 +13,17 @@ namespace jsoncons { -template +template class allocator_set { - Allocator result_alloc_; - TempAllocator temp_alloc_; + Alloc result_alloc_; + TempAlloc temp_alloc_; public: - using allocator_type = Allocator; - using temp_allocator_type = TempAllocator; + using allocator_type = Alloc; + using temp_allocator_type = TempAlloc; - allocator_set(const Allocator& alloc=Allocator(), - const TempAllocator& temp_alloc=TempAllocator()) + allocator_set(const Alloc& alloc=Alloc(), + const TempAlloc& temp_alloc=TempAlloc()) : result_alloc_(alloc), temp_alloc_(temp_alloc) { } @@ -34,32 +34,59 @@ class allocator_set allocator_set& operator=(allocator_set&&) = delete; ~allocator_set() = default; - Allocator get_allocator() const {return result_alloc_;} - TempAllocator get_temp_allocator() const {return temp_alloc_;} + Alloc get_allocator() const {return result_alloc_;} + TempAlloc get_temp_allocator() const {return temp_alloc_;} }; +#if !defined(JSONCONS_NO_DEPRECATED) + inline allocator_set,std::allocator> combine_allocators() { return allocator_set,std::allocator>(std::allocator(), std::allocator()); } -template -allocator_set> combine_allocators(const Allocator& alloc) +template +allocator_set> combine_allocators(const Alloc& alloc) +{ + return allocator_set>(alloc, std::allocator()); +} + +template +allocator_set combine_allocators(const Alloc& alloc, const TempAlloc& temp_alloc) +{ + return allocator_set(alloc, temp_alloc); +} + +template +allocator_set,TempAlloc> temp_allocator_only(const TempAlloc& temp_alloc) +{ + return allocator_set,TempAlloc>(std::allocator(), temp_alloc); +} +#endif // !defined(JSONCONS_NO_DEPRECATED) + +inline +allocator_set,std::allocator> make_alloc_set() +{ + return allocator_set,std::allocator>(std::allocator(), std::allocator()); +} + +template +allocator_set> make_alloc_set(const Alloc& alloc) { - return allocator_set>(alloc, std::allocator()); + return allocator_set>(alloc, std::allocator()); } -template -allocator_set combine_allocators(const Allocator& alloc, const TempAllocator& temp_alloc) +template +allocator_set make_alloc_set(const Alloc& alloc, const TempAlloc& temp_alloc) { - return allocator_set(alloc, temp_alloc); + return allocator_set(alloc, temp_alloc); } -template -allocator_set,TempAllocator> temp_allocator_only(const TempAllocator& temp_alloc) +template +allocator_set,TempAlloc> make_alloc_set(temp_alloc_arg_t, const TempAlloc& temp_alloc) { - return allocator_set,TempAllocator>(std::allocator(), temp_alloc); + return allocator_set,TempAlloc>(std::allocator(), temp_alloc); } } // namespace jsoncons diff --git a/include/jsoncons/basic_json.hpp b/include/jsoncons/basic_json.hpp index d887902..135fb6b 100644 --- a/include/jsoncons/basic_json.hpp +++ b/include/jsoncons/basic_json.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -8,10 +8,11 @@ #define JSONCONS_BASIC_JSON_HPP #include // std::swap -#include #include +#include #include #include // std::initializer_list +#include #include // std::basic_istream #include // std::numeric_limits #include // std::allocator @@ -27,6 +28,8 @@ #include #include #include +#include +#include #include #include #include @@ -37,10 +40,10 @@ #include #include #include -#include +#include #include #include -#include +#include #include #include #include @@ -126,7 +129,7 @@ namespace jsoncons { random_access_iterator_wrapper& operator=(random_access_iterator_wrapper&&) = default; template ::value && std::is_convertible::value>::type> + typename=typename std::enable_if::value && std::is_convertible::value>::type> random_access_iterator_wrapper(const random_access_iterator_wrapper& other) : it_(other.it_), has_value_(other.has_value_) { @@ -395,7 +398,6 @@ namespace jsoncons { using allocator_type = Allocator; using policy_type = Policy; - using char_type = CharT; using char_traits_type = std::char_traits; using string_view_type = jsoncons::basic_string_view; @@ -611,7 +613,7 @@ namespace jsoncons { // long_string_storage struct long_string_storage { - using heap_string_factory_type = jsoncons::utility::heap_string_factory; + using heap_string_factory_type = jsoncons::heap::heap_string_factory; using pointer = typename heap_string_factory_type::pointer; uint8_t storage_kind_:4; @@ -660,7 +662,7 @@ namespace jsoncons { // byte_string_storage struct byte_string_storage { - using heap_string_factory_type = jsoncons::utility::heap_string_factory; + using heap_string_factory_type = jsoncons::heap::heap_string_factory; using pointer = typename heap_string_factory_type::pointer; uint8_t storage_kind_:4; @@ -824,7 +826,7 @@ namespace jsoncons { std::reference_wrapper ref_; json_const_reference_storage(const basic_json& ref) - : storage_kind_(static_cast(json_storage_kind::json_const_reference)), short_str_length_(0), tag_(ref.tag()), + : storage_kind_(static_cast(json_storage_kind::json_const_ref)), short_str_length_(0), tag_(ref.tag()), ref_(ref) { } @@ -843,7 +845,7 @@ namespace jsoncons { std::reference_wrapper ref_; json_reference_storage(basic_json& ref) - : storage_kind_(static_cast(json_storage_kind::json_reference)), short_str_length_(0), tag_(ref.tag()), + : storage_kind_(static_cast(json_storage_kind::json_ref)), short_str_length_(0), tag_(ref.tag()), ref_(ref) { } @@ -900,10 +902,10 @@ namespace jsoncons { { if (cast().ptr_ != nullptr) { - auto& storage = cast(); - typename array_storage::allocator_type alloc{storage.ptr_->get_allocator()}; - std::allocator_traits::destroy(alloc, ext_traits::to_plain_pointer(storage.ptr_)); - std::allocator_traits::deallocate(alloc, storage.ptr_,1); + auto& stor = cast(); + typename array_storage::allocator_type alloc{stor.ptr_->get_allocator()}; + std::allocator_traits::destroy(alloc, ext_traits::to_plain_pointer(stor.ptr_)); + std::allocator_traits::deallocate(alloc, stor.ptr_,1); } break; } @@ -911,10 +913,10 @@ namespace jsoncons { { if (cast().ptr_ != nullptr) { - auto& storage = cast(); - typename object_storage::allocator_type alloc{storage.ptr_->get_allocator()}; - std::allocator_traits::destroy(alloc, ext_traits::to_plain_pointer(storage.ptr_)); - std::allocator_traits::deallocate(alloc, storage.ptr_,1); + auto& stor = cast(); + typename object_storage::allocator_type alloc{stor.ptr_->get_allocator()}; + std::allocator_traits::destroy(alloc, ext_traits::to_plain_pointer(stor.ptr_)); + std::allocator_traits::deallocate(alloc, stor.ptr_,1); } break; } @@ -925,14 +927,14 @@ namespace jsoncons { typename long_string_storage::pointer create_long_string(const allocator_type& alloc, const char_type* data, std::size_t length) { - using heap_string_factory_type = jsoncons::utility::heap_string_factory; + using heap_string_factory_type = jsoncons::heap::heap_string_factory; return heap_string_factory_type::create(data, length, null_type(), alloc); } typename byte_string_storage::pointer create_byte_string(const allocator_type& alloc, const uint8_t* data, std::size_t length, uint64_t ext_tag) { - using heap_string_factory_type = jsoncons::utility::heap_string_factory; + using heap_string_factory_type = jsoncons::heap::heap_string_factory; return heap_string_factory_type::create(data, length, ext_tag, alloc); } @@ -1166,8 +1168,8 @@ namespace jsoncons { case json_storage_kind::byte_str : swap_l_r(other); break; case json_storage_kind::array : swap_l_r(other); break; case json_storage_kind::object : swap_l_r(other); break; - case json_storage_kind::json_const_reference : swap_l_r(other); break; - case json_storage_kind::json_reference : swap_l_r(other); break; + case json_storage_kind::json_const_ref : swap_l_r(other); break; + case json_storage_kind::json_ref : swap_l_r(other); break; default: JSONCONS_UNREACHABLE(); break; @@ -1186,17 +1188,17 @@ namespace jsoncons { { case json_storage_kind::long_str: { - const auto& storage = other.cast(); - auto ptr = create_long_string(std::allocator_traits::select_on_container_copy_construction(storage.get_allocator()), - storage.data(), storage.length()); + const auto& stor = other.cast(); + auto ptr = create_long_string(std::allocator_traits::select_on_container_copy_construction(stor.get_allocator()), + stor.data(), stor.length()); construct(ptr, other.tag()); break; } case json_storage_kind::byte_str: { - const auto& storage = other.cast(); - auto ptr = create_byte_string(std::allocator_traits::select_on_container_copy_construction(storage.get_allocator()), - storage.data(), storage.length(), storage.ext_tag()); + const auto& stor = other.cast(); + auto ptr = create_byte_string(std::allocator_traits::select_on_container_copy_construction(stor.get_allocator()), + stor.data(), stor.length(), stor.ext_tag()); construct(ptr, other.tag()); break; } @@ -1438,30 +1440,30 @@ namespace jsoncons { switch(storage_kind()) { case json_storage_kind::null: - return json_type::null_value; + return json_type::null; case json_storage_kind::boolean: - return json_type::bool_value; + return json_type::boolean; case json_storage_kind::int64: - return json_type::int64_value; + return json_type::int64; case json_storage_kind::uint64: - return json_type::uint64_value; + return json_type::uint64; case json_storage_kind::half_float: - return json_type::half_value; + return json_type::float16; case json_storage_kind::float64: - return json_type::double_value; + return json_type::float64; case json_storage_kind::short_str: case json_storage_kind::long_str: - return json_type::string_value; + return json_type::string; case json_storage_kind::byte_str: - return json_type::byte_string_value; + return json_type::byte_string; case json_storage_kind::array: - return json_type::array_value; + return json_type::array; case json_storage_kind::empty_object: case json_storage_kind::object: - return json_type::object_value; - case json_storage_kind::json_const_reference: + return json_type::object; + case json_storage_kind::json_const_ref: return cast().value().type(); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return cast().value().type(); default: JSONCONS_UNREACHABLE(); @@ -1477,9 +1479,9 @@ namespace jsoncons { // as defined in 11.4-25 of the Standard. switch(storage_kind()) { - case json_storage_kind::json_const_reference: + case json_storage_kind::json_const_ref: return cast().value().tag(); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return cast().value().tag(); default: return common_.tag_; @@ -1496,9 +1498,9 @@ namespace jsoncons { return 0; case json_storage_kind::object: return cast().value().size(); - case json_storage_kind::json_const_reference: + case json_storage_kind::json_const_ref: return cast().value().size(); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return cast().value().size(); default: return 0; @@ -1507,64 +1509,116 @@ namespace jsoncons { string_view_type as_string_view() const { + auto result = try_as_string_view(); + if (!result) + { + JSONCONS_THROW(conv_error(result.error().code())); + } + return *result; + } + + conversion_result try_as_string_view() const + { + using result_type = conversion_result; + switch (storage_kind()) { case json_storage_kind::short_str: - return string_view_type(cast().data(),cast().length()); + return result_type(in_place, cast().data(),cast().length()); case json_storage_kind::long_str: - return string_view_type(cast().data(),cast().length()); - case json_storage_kind::json_const_reference: - return cast().value().as_string_view(); - case json_storage_kind::json_reference: - return cast().value().as_string_view(); + return result_type(in_place, cast().data(),cast().length()); + case json_storage_kind::json_const_ref: + return result_type(cast().value().as_string_view()); + case json_storage_kind::json_ref: + return result_type(cast().value().as_string_view()); default: - JSONCONS_THROW(json_runtime_error("Not a string")); + return result_type(jsoncons::unexpect, conv_errc::not_string); } } - template > - basic_byte_string as_byte_string() const + template + typename std::enable_if::value,conversion_result>::type + try_as_byte_string(const allocator_set& aset) const { - using byte_string_type = basic_byte_string; - std::error_code ec; + using value_type = T; + using result_type = conversion_result; switch (storage_kind()) { case json_storage_kind::short_str: + { + value_type bytes = jsoncons::make_obj_using_allocator(aset.get_allocator()); + const auto& stor = cast(); + auto res = string_to_bytes(stor.data(), stor.data()+stor.length(), tag(), bytes); + if (JSONCONS_UNLIKELY(res.ec != conv_errc{})) + { + return result_type(jsoncons::unexpect, conv_errc::not_byte_string); + } + return result_type(std::move(bytes)); + } case json_storage_kind::long_str: { - value_converter converter; - byte_string_type v = converter.convert(as_string_view(),tag(), ec); - if (JSONCONS_UNLIKELY(ec)) + value_type bytes = jsoncons::make_obj_using_allocator(aset.get_allocator()); + const auto& stor = cast(); + auto res = string_to_bytes(stor.data(), stor.data()+stor.length(), tag(), bytes); + if (JSONCONS_UNLIKELY(res.ec != conv_errc{})) { - JSONCONS_THROW(ser_error(ec)); + return result_type(jsoncons::unexpect, conv_errc::not_byte_string); } - return v; + return result_type(std::move(bytes)); } case json_storage_kind::byte_str: - return basic_byte_string(cast().data(),cast().length()); - case json_storage_kind::json_const_reference: - return cast().value().as_byte_string(); - case json_storage_kind::json_reference: - return cast().value().as_byte_string(); + { + auto& bs = cast(); + auto val = jsoncons::make_obj_using_allocator(aset.get_allocator(), + bs.data(), bs.length()); + return result_type(std::move(val)); + } + case json_storage_kind::json_const_ref: + return cast().value().template try_as_byte_string(aset); + case json_storage_kind::json_ref: + return cast().value().template try_as_byte_string(aset); default: - JSONCONS_THROW(json_runtime_error("Not a byte string")); + return result_type(jsoncons::unexpect, conv_errc::not_byte_string); } } - byte_string_view as_byte_string_view() const + template > + basic_byte_string as_byte_string() const + { + auto result = try_as_byte_string>(make_alloc_set(BytesAlloc())); + if (!result) + { + JSONCONS_THROW(conv_error(result.error().code())); + } + return *result; + } + + conversion_result try_as_byte_string_view() const { + using result_type = conversion_result; + switch (storage_kind()) { case json_storage_kind::byte_str: - return byte_string_view(cast().data(),cast().length()); - case json_storage_kind::json_const_reference: - return cast().value().as_byte_string_view(); - case json_storage_kind::json_reference: - return cast().value().as_byte_string_view(); + return result_type(in_place, cast().data(),cast().length()); + case json_storage_kind::json_const_ref: + return cast().value().try_as_byte_string_view(); + case json_storage_kind::json_ref: + return cast().value().try_as_byte_string_view(); default: - JSONCONS_THROW(json_runtime_error("Not a byte string")); + return result_type(jsoncons::unexpect, conv_errc::not_byte_string); + } + } + + byte_string_view as_byte_string_view() const + { + auto result = try_as_byte_string_view(); + if (!result) + { + JSONCONS_THROW(conv_error(result.error().code())); } + return *result; } int compare(const basic_json& rhs) const noexcept @@ -1575,19 +1629,19 @@ namespace jsoncons { } switch (storage_kind()) { - case json_storage_kind::json_const_reference: + case json_storage_kind::json_const_ref: switch (rhs.storage_kind()) { - case json_storage_kind::json_const_reference: + case json_storage_kind::json_const_ref: return cast().value().compare(rhs.cast().value()); default: return cast().value().compare(rhs); } break; - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: switch (rhs.storage_kind()) { - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return cast().value().compare(rhs.cast().value()); default: return cast().value().compare(rhs); @@ -1602,9 +1656,9 @@ namespace jsoncons { return 0; case json_storage_kind::object: return rhs.empty() ? 0 : -1; - case json_storage_kind::json_const_reference: + case json_storage_kind::json_const_ref: return compare(rhs.cast().value()); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return compare(rhs.cast().value()); default: return static_cast(storage_kind()) - static_cast(rhs.storage_kind()); @@ -1615,9 +1669,9 @@ namespace jsoncons { { case json_storage_kind::boolean: return static_cast(cast().value()) - static_cast(rhs.cast().value()); - case json_storage_kind::json_const_reference: + case json_storage_kind::json_const_ref: return compare(rhs.cast().value()); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return compare(rhs.cast().value()); default: return static_cast(storage_kind()) - static_cast(rhs.storage_kind()); @@ -1644,9 +1698,9 @@ namespace jsoncons { double r = static_cast(cast().value()) - rhs.cast().value(); return r == 0.0 ? 0 : (r < 0.0 ? -1 : 1); } - case json_storage_kind::json_const_reference: + case json_storage_kind::json_const_ref: return compare(rhs.cast().value()); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return compare(rhs.cast().value()); default: return static_cast(storage_kind()) - static_cast(rhs.storage_kind()); @@ -1672,9 +1726,9 @@ namespace jsoncons { auto r = static_cast(cast().value()) - rhs.cast().value(); return r == 0 ? 0 : (r < 0.0 ? -1 : 1); } - case json_storage_kind::json_const_reference: + case json_storage_kind::json_const_ref: return compare(rhs.cast().value()); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return compare(rhs.cast().value()); default: return static_cast(storage_kind()) - static_cast(rhs.storage_kind()); @@ -1698,17 +1752,25 @@ namespace jsoncons { auto r = cast().value() - rhs.cast().value(); return r == 0 ? 0 : (r < 0.0 ? -1 : 1); } - case json_storage_kind::json_const_reference: + case json_storage_kind::json_const_ref: return compare(rhs.cast().value()); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return compare(rhs.cast().value()); default: if (is_string_storage(rhs.storage_kind()) && is_number_tag(rhs.tag())) { double val1 = as_double(); double val2 = rhs.as_double(); - auto r = val1 - val2; - return r == 0 ? 0 : (r < 0.0 ? -1 : 1); + if (val1 < 0 && val2 >= 0) + { + return -1; + } + if (val2 < 0 && val1 >= 0) + { + return 1; + } + + return val1 == val2 ? 0 : (val1 < val2 ? -1 : 1); } else { @@ -1738,14 +1800,18 @@ namespace jsoncons { auto r = val1 - rhs.cast().value(); return r == 0 ? 0 : (r < 0.0 ? -1 : 1); } - case json_storage_kind::json_const_reference: + case json_storage_kind::json_const_ref: return compare(rhs.cast().value()); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return compare(rhs.cast().value()); default: - if (is_string_storage(rhs.storage_kind())) + if (is_string_storage(rhs.storage_kind()) && is_number_tag(rhs.tag())) { double val2 = rhs.as_double(); + if (val1 == val2) + { + return 0; + } auto r = val1 - val2; return r == 0 ? 0 : (r < 0.0 ? -1 : 1); } @@ -1764,9 +1830,9 @@ namespace jsoncons { return as_string_view().compare(rhs.as_string_view()); case json_storage_kind::long_str: return as_string_view().compare(rhs.as_string_view()); - case json_storage_kind::json_const_reference: + case json_storage_kind::json_const_ref: return compare(rhs.cast().value()); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return compare(rhs.cast().value()); default: return static_cast(storage_kind()) - static_cast(rhs.storage_kind()); @@ -1780,9 +1846,9 @@ namespace jsoncons { { return as_byte_string_view().compare(rhs.as_byte_string_view()); } - case json_storage_kind::json_const_reference: + case json_storage_kind::json_const_ref: return compare(rhs.cast().value()); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return compare(rhs.cast().value()); default: return static_cast(storage_kind()) - static_cast(rhs.storage_kind()); @@ -1798,9 +1864,9 @@ namespace jsoncons { else return cast().value() < rhs.cast().value() ? -1 : 1; } - case json_storage_kind::json_const_reference: + case json_storage_kind::json_const_ref: return compare(rhs.cast().value()); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return compare(rhs.cast().value()); default: return static_cast(storage_kind()) - static_cast(rhs.storage_kind()); @@ -1818,9 +1884,9 @@ namespace jsoncons { else return cast().value() < rhs.cast().value() ? -1 : 1; } - case json_storage_kind::json_const_reference: + case json_storage_kind::json_const_ref: return compare(rhs.cast().value()); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return compare(rhs.cast().value()); default: return static_cast(storage_kind()) - static_cast(rhs.storage_kind()); @@ -1861,8 +1927,8 @@ namespace jsoncons { case json_storage_kind::byte_str: swap_l(other); break; case json_storage_kind::array: swap_l(other); break; case json_storage_kind::object: swap_l(other); break; - case json_storage_kind::json_const_reference: swap_l(other); break; - case json_storage_kind::json_reference: swap_l(other); break; + case json_storage_kind::json_const_ref: swap_l(other); break; + case json_storage_kind::json_ref: swap_l(other); break; default: JSONCONS_UNREACHABLE(); break; @@ -1873,8 +1939,8 @@ namespace jsoncons { template static - typename std::enable_if::value,basic_json>::type - parse(const Source& source, + typename std::enable_if::value,basic_json>::type + parse(const Source& source, const basic_json_decode_options& options = basic_json_options()) { json_decoder decoder; @@ -1890,21 +1956,21 @@ namespace jsoncons { parser.parse_some(decoder); parser.finish_parse(decoder); parser.check_done(); - if (!decoder.is_valid()) + if (JSONCONS_UNLIKELY(!decoder.is_valid())) { JSONCONS_THROW(ser_error(json_errc::source_error, "Failed to parse json string")); } return decoder.get_result(); } - template + template static - typename std::enable_if::value,basic_json>::type - parse(const allocator_set& alloc_set, const Source& source, + typename std::enable_if::value,basic_json>::type + parse(const allocator_set& aset, const Source& source, const basic_json_decode_options& options = basic_json_options()) { - json_decoder decoder(alloc_set.get_allocator(), alloc_set.get_temp_allocator()); - basic_json_parser parser(options, alloc_set.get_temp_allocator()); + json_decoder decoder(aset.get_allocator(), aset.get_temp_allocator()); + basic_json_parser parser(options, aset.get_temp_allocator()); auto r = unicode_traits::detect_encoding_from_bom(source.data(), source.size()); if (!(r.encoding == unicode_traits::encoding_kind::utf8 || r.encoding == unicode_traits::encoding_kind::undetected)) @@ -1916,7 +1982,7 @@ namespace jsoncons { parser.parse_some(decoder); parser.finish_parse(decoder); parser.check_done(); - if (!decoder.is_valid()) + if (JSONCONS_UNLIKELY(!decoder.is_valid())) { JSONCONS_THROW(ser_error(json_errc::source_error, "Failed to parse json string")); } @@ -1935,19 +2001,19 @@ namespace jsoncons { return parse(jsoncons::basic_string_view(source), options); } - template - static basic_json parse(const allocator_set& alloc_set, const char_type* source, + template + static basic_json parse(const allocator_set& aset, const char_type* source, const basic_json_decode_options& options = basic_json_options()) { - return parse(alloc_set, jsoncons::basic_string_view(source), options); + return parse(aset, jsoncons::basic_string_view(source), options); } - template - static basic_json parse(const allocator_set& alloc_set, + template + static basic_json parse(const allocator_set& aset, const char_type* str, std::size_t length, const basic_json_decode_options& options = basic_json_options()) { - return parse(alloc_set, jsoncons::basic_string_view(str, length), options); + return parse(aset, jsoncons::basic_string_view(str, length), options); } // from stream @@ -1959,22 +2025,22 @@ namespace jsoncons { basic_json_reader,Allocator> reader(is, decoder, options); reader.read_next(); reader.check_done(); - if (!decoder.is_valid()) + if (JSONCONS_UNLIKELY(!decoder.is_valid())) { JSONCONS_THROW(ser_error(json_errc::source_error, "Failed to parse json stream")); } return decoder.get_result(); } - template - static basic_json parse(const allocator_set& alloc_set, std::basic_istream& is, + template + static basic_json parse(const allocator_set& aset, std::basic_istream& is, const basic_json_decode_options& options = basic_json_options()) { - json_decoder decoder(alloc_set.get_allocator(), alloc_set.get_temp_allocator()); - basic_json_reader,Allocator> reader(is, decoder, options, alloc_set.get_temp_allocator()); + json_decoder decoder(aset.get_allocator(), aset.get_temp_allocator()); + basic_json_reader,Allocator> reader(is, decoder, options, aset.get_temp_allocator()); reader.read_next(); reader.check_done(); - if (!decoder.is_valid()) + if (JSONCONS_UNLIKELY(!decoder.is_valid())) { JSONCONS_THROW(ser_error(json_errc::source_error, "Failed to parse json stream")); } @@ -1992,24 +2058,24 @@ namespace jsoncons { std::forward(last)), decoder, options); reader.read_next(); reader.check_done(); - if (!decoder.is_valid()) + if (JSONCONS_UNLIKELY(!decoder.is_valid())) { JSONCONS_THROW(ser_error(json_errc::source_error, "Failed to parse json from iterator pair")); } return decoder.get_result(); } - template - static basic_json parse(const allocator_set& alloc_set, InputIt first, InputIt last, + template + static basic_json parse(const allocator_set& aset, InputIt first, InputIt last, const basic_json_decode_options& options = basic_json_options()) { - json_decoder decoder(alloc_set.get_allocator(), alloc_set.get_temp_allocator()); + json_decoder decoder(aset.get_allocator(), aset.get_temp_allocator()); basic_json_reader,Allocator> reader(iterator_source(std::forward(first), std::forward(last)), - decoder, options, alloc_set.get_temp_allocator()); + decoder, options, aset.get_temp_allocator()); reader.read_next(); reader.check_done(); - if (!decoder.is_valid()) + if (JSONCONS_UNLIKELY(!decoder.is_valid())) { JSONCONS_THROW(ser_error(json_errc::source_error, "Failed to parse json from iterator pair")); } @@ -2039,7 +2105,7 @@ namespace jsoncons { basic_json_reader> reader(is, decoder, options, err_handler); reader.read_next(); reader.check_done(); - if (!decoder.is_valid()) + if (JSONCONS_UNLIKELY(!decoder.is_valid())) { JSONCONS_THROW(ser_error(json_errc::source_error, "Failed to parse json stream")); } @@ -2060,7 +2126,7 @@ namespace jsoncons { basic_json_reader> reader(iterator_source(std::forward(first),std::forward(last)), decoder, options, err_handler); reader.read_next(); reader.check_done(); - if (!decoder.is_valid()) + if (JSONCONS_UNLIKELY(!decoder.is_valid())) { JSONCONS_THROW(ser_error(json_errc::source_error, "Failed to parse json from iterator pair")); } @@ -2087,7 +2153,7 @@ namespace jsoncons { parser.parse_some(decoder); parser.finish_parse(decoder); parser.check_done(); - if (!decoder.is_valid()) + if (JSONCONS_UNLIKELY(!decoder.is_valid())) { JSONCONS_THROW(ser_error(json_errc::source_error, "Failed to parse json string")); } @@ -2115,9 +2181,9 @@ namespace jsoncons { return basic_json(array()); } - static basic_json make_array(const array& alloc) + static basic_json make_array(const array& a) { - return basic_json(alloc); + return basic_json(a); } static basic_json make_array(const array& a, allocator_type alloc) @@ -2224,7 +2290,13 @@ namespace jsoncons { construct(ptr, tag); } - explicit basic_json(json_object_arg_t, const Allocator& alloc = Allocator()) + explicit basic_json(json_object_arg_t) + { + auto ptr = create_object(Allocator{}); + construct(ptr, semantic_tag::none); + } + + basic_json(json_object_arg_t, const Allocator& alloc) { auto ptr = create_object(alloc); construct(ptr, semantic_tag::none); @@ -2233,8 +2305,17 @@ namespace jsoncons { template basic_json(json_object_arg_t, InputIt first, InputIt last, - semantic_tag tag = semantic_tag::none, - const Allocator& alloc = Allocator()) + semantic_tag tag = semantic_tag::none) + { + auto ptr = create_object(Allocator(), first, last); + construct(ptr, tag); + } + + template + basic_json(json_object_arg_t, + InputIt first, InputIt last, + semantic_tag tag, + const Allocator& alloc) { auto ptr = create_object(alloc, first, last); construct(ptr, tag); @@ -2242,30 +2323,57 @@ namespace jsoncons { basic_json(json_object_arg_t, std::initializer_list,basic_json>> init, - semantic_tag tag = semantic_tag::none, - const Allocator& alloc = Allocator()) + semantic_tag tag = semantic_tag::none) + { + auto ptr = create_object(Allocator(), init); + construct(ptr, tag); + } + + basic_json(json_object_arg_t, + std::initializer_list,basic_json>> init, + semantic_tag tag, + const Allocator& alloc) { - //construct(object(init,alloc), tag); auto ptr = create_object(alloc, init); construct(ptr, tag); } - explicit basic_json(json_array_arg_t, const Allocator& alloc = Allocator()) + explicit basic_json(json_array_arg_t) + { + auto ptr = create_array(Allocator{}); + construct(ptr, semantic_tag::none); + } + + basic_json(json_array_arg_t, const Allocator& alloc) { auto ptr = create_array(alloc); construct(ptr, semantic_tag::none); } basic_json(json_array_arg_t, std::size_t count, const basic_json& value, - semantic_tag tag = semantic_tag::none, const Allocator& alloc = Allocator()) + semantic_tag tag = semantic_tag::none) + { + auto ptr = create_array(Allocator(), count, value); + construct(ptr, tag); + } + + basic_json(json_array_arg_t, std::size_t count, const basic_json& value, + semantic_tag tag, const Allocator& alloc) { auto ptr = create_array(alloc, count, value); construct(ptr, tag); } + basic_json(json_array_arg_t, + semantic_tag tag) + { + auto ptr = create_array(Allocator()); + construct(ptr, tag); + } + basic_json(json_array_arg_t, semantic_tag tag, - const Allocator& alloc = Allocator()) + const Allocator& alloc) { auto ptr = create_array(alloc); construct(ptr, tag); @@ -2274,8 +2382,17 @@ namespace jsoncons { template basic_json(json_array_arg_t, InputIt first, InputIt last, - semantic_tag tag = semantic_tag::none, - const Allocator& alloc = Allocator()) + semantic_tag tag = semantic_tag::none) + { + auto ptr = create_array(Allocator(), first, last); + construct(ptr, tag); + } + + template + basic_json(json_array_arg_t, + InputIt first, InputIt last, + semantic_tag tag, + const Allocator& alloc) { auto ptr = create_array(alloc, first, last); construct(ptr, tag); @@ -2283,8 +2400,16 @@ namespace jsoncons { basic_json(json_array_arg_t, std::initializer_list init, - semantic_tag tag = semantic_tag::none, - const Allocator& alloc = Allocator()) + semantic_tag tag = semantic_tag::none) + { + auto ptr = create_array(Allocator(), init); + construct(ptr, tag); + } + + basic_json(json_array_arg_t, + std::initializer_list init, + semantic_tag tag, + const Allocator& alloc) { auto ptr = create_array(alloc, init); construct(ptr, tag); @@ -2322,6 +2447,12 @@ namespace jsoncons { construct(ptr, tag); } + basic_json(const array& val, semantic_tag tag, allocator_type alloc) + { + auto ptr = create_array(alloc, val); + construct(ptr, tag); + } + basic_json(array&& val, semantic_tag tag = semantic_tag::none) { auto alloc = val.get_allocator(); @@ -2337,6 +2468,12 @@ namespace jsoncons { construct(ptr, tag); } + basic_json(const object& val, semantic_tag tag, allocator_type alloc) + { + auto ptr = create_object(alloc, val); + construct(ptr, tag); + } + basic_json(object&& val, semantic_tag tag = semantic_tag::none) { auto alloc = val.get_allocator(); @@ -2344,37 +2481,46 @@ namespace jsoncons { construct(ptr, tag); } - template ::value>::type> + template basic_json(const T& val) - : basic_json(json_type_traits::to_json(val)) + : basic_json(reflect::json_conv_traits::to_json(make_alloc_set(), val)) { } - template ::value>::type> + template basic_json(const T& val, const Allocator& alloc) - : basic_json(json_type_traits::to_json(val,alloc)) + : basic_json(reflect::json_conv_traits::to_json(make_alloc_set(alloc), val)) { } - basic_json(const string_type& s) - : basic_json(s.data(), s.size(), semantic_tag::none, s.get_allocator()) + template + basic_json(const std::basic_string,SAlloc>& s, + semantic_tag tag = semantic_tag::none) + : basic_json(s.data(), s.size(), tag, Allocator()) { } - basic_json(const string_view_type& s, const allocator_type& alloc = allocator_type()) + template + basic_json(const std::basic_string, SAlloc>& s, + const allocator_type& alloc) : basic_json(s.data(), s.size(), semantic_tag::none, alloc) { } - basic_json(const string_type& s, semantic_tag tag) - : basic_json(s.data(), s.size(), tag, s.get_allocator()) + template + basic_json(const std::basic_string, SAlloc>& s, + semantic_tag tag, const allocator_type& alloc) + : basic_json(s.data(), s.size(), tag, alloc) + { + } + + basic_json(const string_view_type& s, semantic_tag tag = semantic_tag::none) + : basic_json(s.data(), s.size(), tag, allocator_type()) { } - basic_json(const string_type& s, semantic_tag tag, const allocator_type& alloc) - : basic_json(s.data(), s.size(), tag, alloc) + basic_json(const string_view_type& s, const allocator_type& alloc) + : basic_json(s.data(), s.size(), semantic_tag::none, alloc) { } @@ -2394,16 +2540,8 @@ namespace jsoncons { } basic_json(const char_type* s, std::size_t length, semantic_tag tag = semantic_tag::none) + : basic_json(s, length, tag, Allocator()) { - if (length <= short_string_storage::max_length) - { - construct(s, static_cast(length), tag); - } - else - { - auto ptr = create_long_string(allocator_type{}, s, length); - construct(ptr, tag); - } } basic_json(const char_type* s, std::size_t length, semantic_tag tag, const Allocator& alloc) @@ -2458,7 +2596,7 @@ namespace jsoncons { typename std::enable_if::value && sizeof(uint64_t) < sizeof(IntegerType), int>::type = 0) { std::basic_string s; - jsoncons::detail::from_integer(val, s); + jsoncons::from_integer(val, s); if (s.length() <= short_string_storage::max_length) { construct(s.data(), static_cast(s.length()), semantic_tag::bigint); @@ -2485,11 +2623,28 @@ namespace jsoncons { } template - basic_json(IntegerType val, semantic_tag, const Allocator& alloc = Allocator(), + basic_json(IntegerType val, semantic_tag, typename std::enable_if::value && sizeof(int64_t) < sizeof(IntegerType),int>::type = 0) { std::basic_string s; - jsoncons::detail::from_integer(val, s); + jsoncons::from_integer(val, s); + if (s.length() <= short_string_storage::max_length) + { + construct(s.data(), static_cast(s.length()), semantic_tag::bigint); + } + else + { + auto ptr = create_long_string(Allocator(), s.data(), s.length()); + construct(ptr, semantic_tag::bigint); + } + } + + template + basic_json(IntegerType val, semantic_tag, const Allocator& alloc, + typename std::enable_if::value && sizeof(int64_t) < sizeof(IntegerType),int>::type = 0) + { + std::basic_string s; + jsoncons::from_integer(val, s); if (s.length() <= short_string_storage::max_length) { construct(s.data(), static_cast(s.length()), semantic_tag::bigint); @@ -2521,7 +2676,7 @@ namespace jsoncons { construct(val,tag); } - basic_json(const string_view_type& sv, semantic_tag tag, const allocator_type& alloc = allocator_type()) + basic_json(const string_view_type& sv, semantic_tag tag, const allocator_type& alloc) : basic_json(sv.data(), sv.length(), tag, alloc) { } @@ -2529,11 +2684,22 @@ namespace jsoncons { template basic_json(byte_string_arg_t, const Source& source, semantic_tag tag = semantic_tag::none, - const Allocator& alloc = Allocator(), typename std::enable_if::value,int>::type = 0) { auto bytes = jsoncons::span(reinterpret_cast(source.data()), source.size()); + auto ptr = create_byte_string(Allocator(), bytes.data(), bytes.size(), 0); + construct(ptr, tag); + } + + template + basic_json(byte_string_arg_t, const Source& source, + semantic_tag tag, + const Allocator& alloc, + typename std::enable_if::value,int>::type = 0) + { + auto bytes = jsoncons::span(reinterpret_cast(source.data()), source.size()); + auto ptr = create_byte_string(alloc, bytes.data(), bytes.size(), 0); construct(ptr, tag); } @@ -2541,7 +2707,18 @@ namespace jsoncons { template basic_json(byte_string_arg_t, const Source& source, uint64_t ext_tag, - const Allocator& alloc = Allocator(), + typename std::enable_if::value,int>::type = 0) + { + auto bytes = jsoncons::span(reinterpret_cast(source.data()), source.size()); + + auto ptr = create_byte_string(Allocator(), bytes.data(), bytes.size(), ext_tag); + construct(ptr, semantic_tag::ext); + } + + template + basic_json(byte_string_arg_t, const Source& source, + uint64_t ext_tag, + const Allocator& alloc, typename std::enable_if::value,int>::type = 0) { auto bytes = jsoncons::span(reinterpret_cast(source.data()), source.size()); @@ -2550,6 +2727,18 @@ namespace jsoncons { construct(ptr, semantic_tag::ext); } + template + basic_json(InputIterator first, InputIterator last) + : basic_json(json_array_arg,first,last,Allocator()) + { + } + + template + basic_json(InputIterator first, InputIterator last, const Allocator& alloc) + : basic_json(json_array_arg,first,last,alloc) + { + } + ~basic_json() noexcept { destroy(); @@ -2558,7 +2747,7 @@ namespace jsoncons { template basic_json& operator=(const T& val) { - *this = json_type_traits::to_json(val); + *this = reflect::json_conv_traits::to_json(make_alloc_set(), val); return *this; } @@ -2597,7 +2786,7 @@ namespace jsoncons { } break; } - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return cast().value()[key]; default: JSONCONS_THROW(not_an_object(key.data(),key.length())); @@ -2625,9 +2814,9 @@ namespace jsoncons { } break; } - case json_storage_kind::json_const_reference: + case json_storage_kind::json_const_ref: return cast().value().at(key); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return cast().value()[key]; default: JSONCONS_THROW(not_an_object(key.data(),key.length())); @@ -2637,8 +2826,21 @@ namespace jsoncons { template typename std::enable_if::value>::type dump(CharContainer& cont, - const basic_json_encode_options& options = basic_json_options(), - indenting indent = indenting::no_indent) const + const basic_json_encode_options& options = basic_json_options()) const + { + std::error_code ec; + dump(cont, options, indenting::no_indent, ec); + if (JSONCONS_UNLIKELY(ec)) + { + JSONCONS_THROW(ser_error(ec)); + } + } + + template + typename std::enable_if::value>::type + dump(CharContainer& cont, + const basic_json_encode_options& options, + indenting indent) const { std::error_code ec; dump(cont, options, indent, ec); @@ -2662,8 +2864,19 @@ namespace jsoncons { } void dump(std::basic_ostream& os, - const basic_json_encode_options& options = basic_json_options(), - indenting indent = indenting::no_indent) const + const basic_json_encode_options& options = basic_json_options()) const + { + std::error_code ec; + dump(os, options, indenting::no_indent, ec); + if (JSONCONS_UNLIKELY(ec)) + { + JSONCONS_THROW(ser_error(ec)); + } + } + + void dump(std::basic_ostream& os, + const basic_json_encode_options& options, + indenting indent) const { std::error_code ec; dump(os, options, indent, ec); @@ -2764,8 +2977,7 @@ namespace jsoncons { template typename std::enable_if::value>::type - dump(CharContainer& cont, - std::error_code& ec) const + dump(CharContainer& cont, std::error_code& ec) const { basic_compact_json_encoder> encoder(cont); dump(encoder, ec); @@ -2861,15 +3073,22 @@ namespace jsoncons { visitor.flush(); } + write_result try_dump(basic_json_visitor& visitor) const + { + auto r = try_dump_noflush(visitor); + visitor.flush(); + return r; + } + bool is_null() const noexcept { switch (storage_kind()) { case json_storage_kind::null: return true; - case json_storage_kind::json_const_reference: + case json_storage_kind::json_const_ref: return cast().value().is_null(); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return cast().value().is_null(); default: return false; @@ -2899,7 +3118,7 @@ namespace jsoncons { return cast().get_allocator(); case json_storage_kind::object: return cast().get_allocator(); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return cast().value().get_allocator(); default: return get_default_allocator(typename std::allocator_traits::is_always_equal()); @@ -2914,9 +3133,9 @@ namespace jsoncons { { return cast().ext_tag(); } - case json_storage_kind::json_const_reference: + case json_storage_kind::json_const_ref: return cast().value().ext_tag(); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return cast().value().ext_tag(); default: return 0; @@ -2932,9 +3151,9 @@ namespace jsoncons { auto it = cast().value().find(key); return it != cast().value().end(); } - case json_storage_kind::json_const_reference: + case json_storage_kind::json_const_ref: return cast().value().contains(key); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return cast().value().contains(key); default: return false; @@ -2960,9 +3179,9 @@ namespace jsoncons { } return count; } - case json_storage_kind::json_const_reference: + case json_storage_kind::json_const_ref: return cast().value().count(key); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return cast().value().count(key); default: return 0; @@ -2972,23 +3191,24 @@ namespace jsoncons { template bool is(Args&&... args) const noexcept { - return json_type_traits::is(*this,std::forward(args)...); + return reflect::json_conv_traits::is(*this,std::forward(args)...); } bool is_string() const noexcept { - switch (storage_kind()) + if (is_string_storage(storage_kind())) { - case json_storage_kind::short_str: - case json_storage_kind::long_str: - return true; - case json_storage_kind::json_const_reference: - return cast().value().is_string(); - case json_storage_kind::json_reference: - return cast().value().is_string(); - default: - return false; + return true; } + if (storage_kind() == json_storage_kind::json_const_ref) + { + return cast().value().is_string(); + } + if (storage_kind() == json_storage_kind::json_ref) + { + return cast().value().is_string(); + } + return false; } bool is_string_view() const noexcept @@ -3002,9 +3222,9 @@ namespace jsoncons { { case json_storage_kind::byte_str: return true; - case json_storage_kind::json_const_reference: + case json_storage_kind::json_const_ref: return cast().value().is_byte_string(); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return cast().value().is_byte_string(); default: return false; @@ -3022,11 +3242,10 @@ namespace jsoncons { { case json_storage_kind::short_str: case json_storage_kind::long_str: - return jsoncons::detail::is_base10(as_string_view().data(), as_string_view().length()); - case json_storage_kind::int64: - case json_storage_kind::uint64: - return true; - case json_storage_kind::json_reference: + return is_number_tag(tag()); + case json_storage_kind::json_const_ref: + return cast().value().is_bignum(); + case json_storage_kind::json_ref: return cast().value().is_bignum(); default: return false; @@ -3039,9 +3258,9 @@ namespace jsoncons { { case json_storage_kind::boolean: return true; - case json_storage_kind::json_const_reference: + case json_storage_kind::json_const_ref: return cast().value().is_bool(); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return cast().value().is_bool(); default: return false; @@ -3055,9 +3274,9 @@ namespace jsoncons { case json_storage_kind::empty_object: case json_storage_kind::object: return true; - case json_storage_kind::json_const_reference: + case json_storage_kind::json_const_ref: return cast().value().is_object(); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return cast().value().is_object(); default: return false; @@ -3070,9 +3289,9 @@ namespace jsoncons { { case json_storage_kind::array: return true; - case json_storage_kind::json_const_reference: + case json_storage_kind::json_const_ref: return cast().value().is_array(); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return cast().value().is_array(); default: return false; @@ -3087,9 +3306,9 @@ namespace jsoncons { return true; case json_storage_kind::uint64: return as_integer() <= static_cast((std::numeric_limits::max)()); - case json_storage_kind::json_const_reference: + case json_storage_kind::json_const_ref: return cast().value().is_int64(); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return cast().value().is_int64(); default: return false; @@ -3104,9 +3323,9 @@ namespace jsoncons { return true; case json_storage_kind::int64: return as_integer() >= 0; - case json_storage_kind::json_const_reference: + case json_storage_kind::json_const_ref: return cast().value().is_uint64(); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return cast().value().is_uint64(); default: return false; @@ -3119,9 +3338,9 @@ namespace jsoncons { { case json_storage_kind::half_float: return true; - case json_storage_kind::json_const_reference: + case json_storage_kind::json_const_ref: return cast().value().is_half(); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return cast().value().is_half(); default: return false; @@ -3134,9 +3353,9 @@ namespace jsoncons { { case json_storage_kind::float64: return true; - case json_storage_kind::json_const_reference: + case json_storage_kind::json_const_ref: return cast().value().is_double(); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return cast().value().is_double(); default: return false; @@ -3154,12 +3373,10 @@ namespace jsoncons { return true; case json_storage_kind::short_str: case json_storage_kind::long_str: - return tag() == semantic_tag::bigint || - tag() == semantic_tag::bigdec || - tag() == semantic_tag::bigfloat; - case json_storage_kind::json_const_reference: + return is_number_tag(tag()); + case json_storage_kind::json_const_ref: return cast().value().is_number(); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return cast().value().is_number(); default: return false; @@ -3183,9 +3400,9 @@ namespace jsoncons { return true; case json_storage_kind::object: return cast().value().empty(); - case json_storage_kind::json_const_reference: + case json_storage_kind::json_const_ref: return cast().value().empty(); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return cast().value().empty(); default: return false; @@ -3200,9 +3417,9 @@ namespace jsoncons { return cast().value().capacity(); case json_storage_kind::object: return cast().value().capacity(); - case json_storage_kind::json_const_reference: + case json_storage_kind::json_const_ref: return cast().value().capacity(); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return cast().value().capacity(); default: return 0; @@ -3241,7 +3458,7 @@ namespace jsoncons { case json_storage_kind::object: cast().value().reserve(n); break; - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: cast().value().reserve(n); break; default: @@ -3257,7 +3474,7 @@ namespace jsoncons { case json_storage_kind::array: cast().value().resize(n); break; - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: cast().value().resize(n); break; default: @@ -3273,7 +3490,7 @@ namespace jsoncons { case json_storage_kind::array: cast().value().resize(n, val); break; - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: cast().value().resize(n, val); break; default: @@ -3282,11 +3499,41 @@ namespace jsoncons { } template - typename std::enable_if::value,T>::type + typename std::enable_if::value,T>::type as() const { - T val = json_type_traits::as(*this); - return val; + auto r = reflect::json_conv_traits::try_as(make_alloc_set(), *this); + if (!r) + { + JSONCONS_THROW(conv_error(r.error().code(), r.error().message_arg())); + } + return std::move(r.value()); + } + + template + typename std::enable_if::value,T>::type + as(const allocator_set& aset) const + { + auto r = reflect::json_conv_traits::try_as(aset, *this); + if (!r) + { + JSONCONS_THROW(conv_error(r.error().code(), r.error().message_arg())); + } + return std::move(r.value()); + } + + template + typename std::enable_if::value,conversion_result>::type + try_as() const + { + return reflect::json_conv_traits::try_as(make_alloc_set(), *this); + } + + template + typename std::enable_if::value,conversion_result>::type + try_as(const allocator_set& aset) const + { + return reflect::json_conv_traits::try_as(aset, *this); } template @@ -3307,23 +3554,25 @@ namespace jsoncons { case semantic_tag::base64: case semantic_tag::base64url: { - value_converter,T> converter; - T v = converter.convert(as_string_view(),tag(), ec); - if (JSONCONS_UNLIKELY(ec)) + T v; + auto sv = as_string_view(); + auto r = string_to_bytes(sv.begin(), sv.end(), tag(), v); + if (JSONCONS_UNLIKELY(r.ec != conv_errc{})) { - JSONCONS_THROW(ser_error(ec)); + JSONCONS_THROW(ser_error(conv_errc::not_byte_string)); } return v; } default: { - value_converter, T> converter; - T v = converter.convert(as_string_view(), hint, ec); - if (JSONCONS_UNLIKELY(ec)) + T v; + auto sv = as_string_view(); + auto r = string_to_bytes(sv.begin(), sv.end(), hint, v); + if (JSONCONS_UNLIKELY(r.ec != conv_errc{})) { - JSONCONS_THROW(ser_error(ec)); + JSONCONS_THROW(ser_error(conv_errc::not_byte_string)); } - return T(v.begin(),v.end()); + return v; } break; } @@ -3331,9 +3580,9 @@ namespace jsoncons { } case json_storage_kind::byte_str: return T(as_byte_string_view().begin(), as_byte_string_view().end()); - case json_storage_kind::json_const_reference: + case json_storage_kind::json_const_ref: return cast().value().template as(byte_string_arg, hint); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return cast().value().template as(byte_string_arg, hint); default: JSONCONS_THROW(json_runtime_error("Not a byte string")); @@ -3350,71 +3599,84 @@ namespace jsoncons { return cast().value() != 0; case json_storage_kind::uint64: return cast().value() != 0; - case json_storage_kind::json_const_reference: + case json_storage_kind::json_const_ref: return cast().value().as_bool(); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return cast().value().as_bool(); default: JSONCONS_THROW(json_runtime_error("Not a bool")); } } - template - IntegerType as_integer() const + template + conversion_result try_as_integer() const { + using result_type = conversion_result; + switch (storage_kind()) { case json_storage_kind::short_str: case json_storage_kind::long_str: { - IntegerType val; - auto result = jsoncons::detail::to_integer(as_string_view().data(), as_string_view().length(), val); + T val; + auto result = jsoncons::to_integer(as_string_view().data(), as_string_view().length(), val); if (!result) { - JSONCONS_THROW(json_runtime_error(result.error_code().message())); + return result_type(jsoncons::unexpect, conv_errc::not_integer); } return val; } case json_storage_kind::half_float: - return static_cast(cast().value()); + return result_type(static_cast(cast().value())); case json_storage_kind::float64: - return static_cast(cast().value()); + return result_type(static_cast(cast().value())); case json_storage_kind::int64: - return static_cast(cast().value()); + return result_type(static_cast(cast().value())); case json_storage_kind::uint64: - return static_cast(cast().value()); + return result_type(static_cast(cast().value())); case json_storage_kind::boolean: - return static_cast(cast().value() ? 1 : 0); - case json_storage_kind::json_const_reference: - return cast().value().template as_integer(); - case json_storage_kind::json_reference: - return cast().value().template as_integer(); + return result_type(static_cast(cast().value() ? 1 : 0)); + case json_storage_kind::json_const_ref: + return cast().value().template try_as_integer(); + case json_storage_kind::json_ref: + return cast().value().template try_as_integer(); default: - JSONCONS_THROW(json_runtime_error("Not an integer")); + return result_type(jsoncons::unexpect, conv_errc::not_integer); } } - template - typename std::enable_if::value && sizeof(IntegerType) <= sizeof(int64_t),bool>::type + template + T as_integer() const + { + auto result = try_as_integer(); + if (!result) + { + JSONCONS_THROW(conv_error(result.error().code())); + } + return *result; + } + + template + typename std::enable_if::value && sizeof(T) <= sizeof(int64_t),bool>::type is_integer() const noexcept { switch (storage_kind()) { case json_storage_kind::int64: - return (as_integer() >= (ext_traits::integer_limits::lowest)()) && (as_integer() <= (ext_traits::integer_limits::max)()); + return (as_integer() >= (ext_traits::integer_limits::lowest)()) && (as_integer() <= (ext_traits::integer_limits::max)()); case json_storage_kind::uint64: - return as_integer() <= static_cast((ext_traits::integer_limits::max)()); - case json_storage_kind::json_const_reference: - return cast().value().template is_integer(); - case json_storage_kind::json_reference: - return cast().value().template is_integer(); + return as_integer() <= static_cast((ext_traits::integer_limits::max)()); + case json_storage_kind::json_const_ref: + return cast().value().template is_integer(); + case json_storage_kind::json_ref: + return cast().value().template is_integer(); default: return false; } } - template - typename std::enable_if::value && sizeof(int64_t) < sizeof(IntegerType),bool>::type + template + typename std::enable_if::value && sizeof(int64_t) < sizeof(T),bool>::type is_integer() const noexcept { switch (storage_kind()) @@ -3422,18 +3684,18 @@ namespace jsoncons { case json_storage_kind::short_str: case json_storage_kind::long_str: { - IntegerType val; - auto result = jsoncons::detail::to_integer(as_string_view().data(), as_string_view().length(), val); + T val; + auto result = jsoncons::to_integer(as_string_view().data(), as_string_view().length(), val); return result ? true : false; } case json_storage_kind::int64: - return (as_integer() >= (ext_traits::integer_limits::lowest)()) && (as_integer() <= (ext_traits::integer_limits::max)()); + return (as_integer() >= (ext_traits::integer_limits::lowest)()) && (as_integer() <= (ext_traits::integer_limits::max)()); case json_storage_kind::uint64: - return as_integer() <= static_cast((ext_traits::integer_limits::max)()); - case json_storage_kind::json_const_reference: - return cast().value().template is_integer(); - case json_storage_kind::json_reference: - return cast().value().template is_integer(); + return as_integer() <= static_cast((ext_traits::integer_limits::max)()); + case json_storage_kind::json_const_ref: + return cast().value().template is_integer(); + case json_storage_kind::json_ref: + return cast().value().template is_integer(); default: return false; } @@ -3449,9 +3711,9 @@ namespace jsoncons { return as_integer() >= 0 && static_cast(as_integer()) <= (ext_traits::integer_limits::max)(); case json_storage_kind::uint64: return as_integer() <= (ext_traits::integer_limits::max)(); - case json_storage_kind::json_const_reference: + case json_storage_kind::json_const_ref: return cast().value().template is_integer(); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return cast().value().template is_integer(); default: return false; @@ -3468,102 +3730,157 @@ namespace jsoncons { case json_storage_kind::long_str: { IntegerType val; - auto result = jsoncons::detail::to_integer(as_string_view().data(), as_string_view().length(), val); + auto result = jsoncons::to_integer(as_string_view().data(), as_string_view().length(), val); return result ? true : false; } case json_storage_kind::int64: return as_integer() >= 0 && static_cast(as_integer()) <= (ext_traits::integer_limits::max)(); case json_storage_kind::uint64: return as_integer() <= (ext_traits::integer_limits::max)(); - case json_storage_kind::json_const_reference: + case json_storage_kind::json_const_ref: return cast().value().template is_integer(); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return cast().value().template is_integer(); default: return false; } } - double as_double() const + conversion_result try_as_double() const { + using result_type = conversion_result; + switch (storage_kind()) { case json_storage_kind::short_str: case json_storage_kind::long_str: { - const jsoncons::detail::chars_to to_double; - // to_double() throws std::invalid_argument if conversion fails - return to_double(as_cstring(), as_string_view().length()); + double x{0}; + const char_type* s = as_cstring(); + std::size_t len = as_string_view().length(); + if (JSONCONS_UNLIKELY(len > 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))) + { + auto result = jsoncons::hexstr_to_double(s, len, x); + if (result.ec == std::errc::invalid_argument) + { + return result_type(jsoncons::unexpect, conv_errc::not_double); + } + } + else if (JSONCONS_UNLIKELY(len > 3 && s[0] == '-' && s[1] == '0' && (s[2] == 'x' || s[2] == 'X'))) + { + auto result = jsoncons::hexstr_to_double(s, len, x); + if (result.ec == std::errc::invalid_argument) + { + return result_type(jsoncons::unexpect, conv_errc::not_double); + } + } + else + { + auto result = jsoncons::decstr_to_double(as_cstring(), as_string_view().length(), x); + if (result.ec == std::errc::invalid_argument) + { + return result_type(jsoncons::unexpect, conv_errc::not_double); + } + } + + return result_type(x); } case json_storage_kind::half_float: - return binary::decode_half(cast().value()); + return result_type(binary::decode_half(cast().value())); case json_storage_kind::float64: - return cast().value(); + return result_type(cast().value()); case json_storage_kind::int64: - return static_cast(cast().value()); + return result_type(static_cast(cast().value())); case json_storage_kind::uint64: - return static_cast(cast().value()); - case json_storage_kind::json_const_reference: - return cast().value().as_double(); - case json_storage_kind::json_reference: - return cast().value().as_double(); + return result_type(static_cast(cast().value())); + case json_storage_kind::json_const_ref: + return cast().value().try_as_double(); + case json_storage_kind::json_ref: + return cast().value().try_as_double(); default: - JSONCONS_THROW(json_runtime_error("Not a double")); + return result_type(jsoncons::unexpect, conv_errc::not_double); } } - template > - std::basic_string as_string() const + double as_double() const { - return as_string(SAllocator()); + auto result = try_as_double(); + if (!result) + { + JSONCONS_THROW(conv_error(result.error().code())); + } + return *result; } - template > - std::basic_string as_string(const SAllocator& alloc) const + template + typename std::enable_if::value && + std::is_same::value,conversion_result>::type + try_as_string(const allocator_set& aset) const { - using string_type2 = std::basic_string; + using value_type = T; + using result_type = conversion_result; - std::error_code ec; switch (storage_kind()) { case json_storage_kind::short_str: + { + auto& stor = cast(); + return result_type(jsoncons::make_obj_using_allocator(aset.get_allocator(), stor.data(), stor.length())); + } case json_storage_kind::long_str: { - return string_type2(as_string_view().data(),as_string_view().length(),alloc); + auto& stor = cast(); + return result_type(jsoncons::make_obj_using_allocator(aset.get_allocator(), stor.data(), stor.length())); } case json_storage_kind::byte_str: { - value_converter converter; - auto s = converter.convert(as_byte_string_view(), tag(), ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec)); - } - return s; + auto& stor = cast(); + value_type s = jsoncons::make_obj_using_allocator(aset.get_allocator()); + bytes_to_string(stor.data(), stor.data()+stor.length(), tag(), s); + return result_type(std::move(s)); } - case json_storage_kind::array: + case json_storage_kind::json_const_ref: + return cast().value().template try_as_string(aset); + case json_storage_kind::json_ref: + return cast().value().template try_as_string(aset); + default: { - string_type2 s(alloc); + value_type s = jsoncons::make_obj_using_allocator(aset.get_allocator()); + basic_compact_json_encoder,TempAlloc> encoder(s, aset.get_temp_allocator()); + std::error_code ec; + dump(encoder, ec); + if (JSONCONS_UNLIKELY(ec)) { - basic_compact_json_encoder> encoder(s); - dump(encoder); + return result_type(jsoncons::unexpect, ec); } - return s; - } - case json_storage_kind::json_const_reference: - return cast().value().as_string(alloc); - case json_storage_kind::json_reference: - return cast().value().as_string(alloc); - default: - { - string_type2 s(alloc); - basic_compact_json_encoder> encoder(s); - dump(encoder); - return s; + return result_type(std::move(s)); } } } + template + std::basic_string as_string(const CharsAlloc& alloc) const + { + using value_type = std::basic_string; + auto r = try_as_string(make_alloc_set(alloc)); + if (!r) + { + JSONCONS_THROW(conv_error(r.error().code())); + } + return *r; + } + + template > + std::basic_string as_string() const + { + return as_string(CharsAlloc()); + } + + std::basic_string as_string() const + { + return as_string(std::allocator()); + } + const char_type* as_cstring() const { switch (storage_kind()) @@ -3572,9 +3889,9 @@ namespace jsoncons { return cast().c_str(); case json_storage_kind::long_str: return cast().c_str(); - case json_storage_kind::json_const_reference: + case json_storage_kind::json_const_ref: return cast().value().as_cstring(); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return cast().value().as_cstring(); default: JSONCONS_THROW(json_runtime_error("Not a cstring")); @@ -3596,7 +3913,7 @@ namespace jsoncons { } return (*it).value(); } - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return cast().value().at(key); default: JSONCONS_THROW(not_an_object(key.data(),key.length())); @@ -3618,9 +3935,9 @@ namespace jsoncons { } return (*it).value(); } - case json_storage_kind::json_const_reference: + case json_storage_kind::json_const_ref: return cast().value().at(key); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return cast().value().at(key); default: JSONCONS_THROW(not_an_object(key.data(),key.length())); @@ -3639,7 +3956,7 @@ namespace jsoncons { return cast().value().operator[](i); case json_storage_kind::object: return cast().value().at(i); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return cast().value().at(i); default: JSONCONS_THROW(json_runtime_error("Index on non-array value not supported")); @@ -3658,9 +3975,9 @@ namespace jsoncons { return cast().value().operator[](i); case json_storage_kind::object: return cast().value().at(i); - case json_storage_kind::json_const_reference: + case json_storage_kind::json_const_ref: return cast().value().at(i); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return cast().value().at(i); default: JSONCONS_THROW(json_runtime_error("Index on non-array value not supported")); @@ -3675,7 +3992,7 @@ namespace jsoncons { return object_range().end(); case json_storage_kind::object: return object_iterator(cast().value().find(key)); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return cast().value().find(key); default: JSONCONS_THROW(not_an_object(key.data(),key.length())); @@ -3690,9 +4007,9 @@ namespace jsoncons { return object_range().end(); case json_storage_kind::object: return const_object_iterator(cast().value().find(key)); - case json_storage_kind::json_const_reference: + case json_storage_kind::json_const_ref: return cast().value().find(key); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return cast().value().find(key); default: JSONCONS_THROW(not_an_object(key.data(),key.length())); @@ -3720,9 +4037,9 @@ namespace jsoncons { return null(); } } - case json_storage_kind::json_const_reference: + case json_storage_kind::json_const_ref: return cast().value().at_or_null(key); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return cast().value().at_or_null(key); default: JSONCONS_THROW(not_an_object(key.data(),key.length())); @@ -3753,9 +4070,9 @@ namespace jsoncons { return static_cast(std::forward(default_value)); } } - case json_storage_kind::json_const_reference: + case json_storage_kind::json_const_ref: return cast().value().template get_value_or(key,std::forward(default_value)); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return cast().value().template get_value_or(key,std::forward(default_value)); default: JSONCONS_THROW(not_an_object(key.data(),key.length())); @@ -3774,7 +4091,7 @@ namespace jsoncons { case json_storage_kind::object: cast().value().shrink_to_fit(); break; - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: cast().value().shrink_to_fit(); break; default: @@ -3792,7 +4109,7 @@ namespace jsoncons { case json_storage_kind::object: cast().value().clear(); break; - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: cast().value().clear(); break; default: @@ -3808,7 +4125,7 @@ namespace jsoncons { return object_range().end(); case json_storage_kind::object: return object_iterator(cast().value().erase(pos)); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return cast().value().erase(pos); default: JSONCONS_THROW(json_runtime_error("Not an object")); @@ -3823,7 +4140,7 @@ namespace jsoncons { return object_range().end(); case json_storage_kind::object: return object_iterator(cast().value().erase(first, last)); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return cast().value().erase(first, last); default: JSONCONS_THROW(json_runtime_error("Not an object")); @@ -3836,7 +4153,7 @@ namespace jsoncons { { case json_storage_kind::array: return cast().value().erase(pos); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return cast().value().erase(pos); default: JSONCONS_THROW(json_runtime_error("Not an array")); @@ -3849,7 +4166,7 @@ namespace jsoncons { { case json_storage_kind::array: return cast().value().erase(first, last); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return cast().value().erase(first, last); default: JSONCONS_THROW(json_runtime_error("Not an array")); @@ -3867,7 +4184,7 @@ namespace jsoncons { case json_storage_kind::object: cast().value().erase(key); break; - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return cast().value().erase(key); default: JSONCONS_THROW(not_an_object(key.data(),key.length())); @@ -3890,7 +4207,7 @@ namespace jsoncons { auto result = cast().value().insert_or_assign(key, std::forward(val)); return std::make_pair(object_iterator(result.first), result.second); } - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return cast().value().insert_or_assign(key, std::forward(val)); default: JSONCONS_THROW(not_an_object(key.data(),key.length())); @@ -3913,7 +4230,7 @@ namespace jsoncons { auto result = cast().value().try_emplace(key, std::forward(args)...); return std::make_pair(object_iterator(result.first),result.second); } - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return cast().value().try_emplace(key, std::forward(args)...); default: JSONCONS_THROW(not_an_object(key.data(),key.length())); @@ -3938,14 +4255,14 @@ namespace jsoncons { case json_storage_kind::object: cast().value().merge(source.cast().value()); break; - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: cast().value().merge(source); break; default: JSONCONS_THROW(json_runtime_error("Attempting to merge a value that is not an object")); } break; - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: merge(source.cast().value()); break; default: @@ -3969,14 +4286,14 @@ namespace jsoncons { case json_storage_kind::object: cast().value().merge(std::move(source.cast().value())); break; - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: cast().value().merge(std::move(source)); break; default: JSONCONS_THROW(json_runtime_error("Attempting to merge a value that is not an object")); } break; - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: merge(std::move(source.cast().value())); break; default: @@ -4000,14 +4317,14 @@ namespace jsoncons { case json_storage_kind::object: cast().value().merge(hint, source.cast().value()); break; - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: cast().value().merge(hint, source); break; default: JSONCONS_THROW(json_runtime_error("Attempting to merge a value that is not an object")); } break; - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: merge(hint, source.cast().value()); break; default: @@ -4031,14 +4348,14 @@ namespace jsoncons { case json_storage_kind::object: cast().value().merge(hint, std::move(source.cast().value())); break; - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: cast().value().merge(hint, std::move(source)); break; default: JSONCONS_THROW(json_runtime_error("Attempting to merge a value that is not an object")); } break; - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: merge(hint, std::move(source.cast().value())); break; default: @@ -4064,14 +4381,14 @@ namespace jsoncons { case json_storage_kind::object: cast().value().merge_or_update(source.cast().value()); break; - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: cast().value().merge_or_update(source); break; default: JSONCONS_THROW(json_runtime_error("Attempting to merge or update a value that is not an object")); } break; - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: merge_or_update(source.cast().value()); break; default: @@ -4095,14 +4412,14 @@ namespace jsoncons { case json_storage_kind::object: cast().value().merge_or_update(std::move(source.cast().value())); break; - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: cast().value().merge_or_update(std::move(source)); break; default: JSONCONS_THROW(json_runtime_error("Attempting to merge or update a value that is not an object")); } break; - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: merge_or_update(std::move(source.cast().value())); break; default: @@ -4126,14 +4443,14 @@ namespace jsoncons { case json_storage_kind::object: cast().value().merge_or_update(hint, source.cast().value()); break; - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: cast().value().merge_or_update(hint, source); break; default: JSONCONS_THROW(json_runtime_error("Attempting to merge or update a value that is not an object")); } break; - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: merge_or_update(hint, source.cast().value()); break; default: @@ -4157,14 +4474,14 @@ namespace jsoncons { case json_storage_kind::object: cast().value().merge_or_update(hint, std::move(source.cast().value())); break; - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: cast().value().merge_or_update(hint, std::move(source)); break; default: JSONCONS_THROW(json_runtime_error("Attempting to merge or update a value that is not an object")); } break; - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: merge_or_update(hint, std::move(source.cast().value())); break; default: @@ -4182,7 +4499,7 @@ namespace jsoncons { return object_iterator(cast().value().insert_or_assign(hint, name, std::forward(val))); case json_storage_kind::object: return object_iterator(cast().value().insert_or_assign(hint, name, std::forward(val))); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return object_iterator(cast().value().insert_or_assign(hint, name, std::forward(val))); default: JSONCONS_THROW(not_an_object(name.data(),name.length())); @@ -4199,7 +4516,7 @@ namespace jsoncons { return object_iterator(cast().value().try_emplace(hint, name, std::forward(args)...)); case json_storage_kind::object: return object_iterator(cast().value().try_emplace(hint, name, std::forward(args)...)); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return object_iterator(cast().value().try_emplace(hint, name, std::forward(args)...)); default: JSONCONS_THROW(not_an_object(name.data(),name.length())); @@ -4214,7 +4531,7 @@ namespace jsoncons { case json_storage_kind::array: return cast().value().insert(pos, std::forward(val)); break; - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return cast().value().insert(pos, std::forward(val)); break; default: @@ -4230,7 +4547,7 @@ namespace jsoncons { case json_storage_kind::array: return cast().value().insert(pos, first, last); break; - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return cast().value().insert(pos, first, last); break; default: @@ -4250,7 +4567,7 @@ namespace jsoncons { case json_storage_kind::object: cast().value().insert(first, last); break; - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: cast().value().insert(first, last); break; default: @@ -4270,7 +4587,7 @@ namespace jsoncons { case json_storage_kind::object: cast().value().insert(tag, first, last); break; - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: cast().value().insert(tag, first, last); break; default: @@ -4286,7 +4603,7 @@ namespace jsoncons { case json_storage_kind::array: return cast().value().emplace(pos, std::forward(args)...); break; - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return cast().value().emplace(pos, std::forward(args)...); default: JSONCONS_THROW(json_runtime_error("Attempting to insert into a value that is not an array")); @@ -4300,7 +4617,7 @@ namespace jsoncons { { case json_storage_kind::array: return cast().value().emplace_back(std::forward(args)...); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return cast().value().emplace_back(std::forward(args)...); default: JSONCONS_THROW(json_runtime_error("Attempting to insert into a value that is not an array")); @@ -4320,7 +4637,7 @@ namespace jsoncons { case json_storage_kind::array: cast().value().push_back(std::forward(val)); break; - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: cast().value().push_back(std::forward(val)); break; default: @@ -4335,7 +4652,7 @@ namespace jsoncons { case json_storage_kind::array: cast().value().push_back(std::move(val)); break; - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: cast().value().push_back(std::move(val)); break; default: @@ -4352,12 +4669,6 @@ namespace jsoncons { return s; } - template - basic_json(InputIterator first, InputIterator last, const Allocator& alloc = Allocator()) - : basic_json(json_array_arg,first,last,alloc) - { - } - object_range_type object_range() { switch (storage_kind()) @@ -4367,7 +4678,7 @@ namespace jsoncons { case json_storage_kind::object: return object_range_type(object_iterator(cast().value().begin()), object_iterator(cast().value().end())); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return cast().value().object_range(); default: JSONCONS_THROW(json_runtime_error("Not an object")); @@ -4383,9 +4694,9 @@ namespace jsoncons { case json_storage_kind::object: return const_object_range_type(const_object_iterator(cast().value().begin()), const_object_iterator(cast().value().end())); - case json_storage_kind::json_const_reference: + case json_storage_kind::json_const_ref: return cast().value().object_range(); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return cast().value().object_range(); default: JSONCONS_THROW(json_runtime_error("Not an object")); @@ -4399,7 +4710,7 @@ namespace jsoncons { case json_storage_kind::array: return array_range_type(cast().value().begin(), cast().value().end()); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return cast().value().array_range(); default: JSONCONS_THROW(json_runtime_error("Not an array")); @@ -4413,9 +4724,9 @@ namespace jsoncons { case json_storage_kind::array: return const_array_range_type(cast().value().begin(), cast().value().end()); - case json_storage_kind::json_const_reference: + case json_storage_kind::json_const_ref: return cast().value().array_range(); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return cast().value().array_range(); default: JSONCONS_THROW(json_runtime_error("Not an array")); @@ -4489,15 +4800,107 @@ namespace jsoncons { visitor.end_array(context, ec); break; } - case json_storage_kind::json_const_reference: + case json_storage_kind::json_const_ref: return cast().value().dump_noflush(visitor, ec); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return cast().value().dump_noflush(visitor, ec); default: break; } } + write_result try_dump_noflush(basic_json_visitor& visitor) const + { + std::error_code ec; + const ser_context context{}; + switch (storage_kind()) + { + case json_storage_kind::short_str: + case json_storage_kind::long_str: + visitor.string_value(as_string_view(), tag(), context, ec); + return ec ? write_result{unexpect, ec} : write_result{}; + case json_storage_kind::byte_str: + if (tag() == semantic_tag::ext) + { + visitor.byte_string_value(as_byte_string_view(), ext_tag(), context, ec); + } + else + { + visitor.byte_string_value(as_byte_string_view(), tag(), context, ec); + } + return ec ? write_result{unexpect, ec} : write_result{}; + case json_storage_kind::half_float: + visitor.half_value(cast().value(), tag(), context, ec); + return ec ? write_result{unexpect, ec} : write_result{}; + case json_storage_kind::float64: + visitor.double_value(cast().value(), + tag(), context, ec); + return ec ? write_result{unexpect, ec} : write_result{}; + case json_storage_kind::int64: + visitor.int64_value(cast().value(), tag(), context, ec); + return ec ? write_result{unexpect, ec} : write_result{}; + case json_storage_kind::uint64: + visitor.uint64_value(cast().value(), tag(), context, ec); + return ec ? write_result{unexpect, ec} : write_result{}; + case json_storage_kind::boolean: + visitor.bool_value(cast().value(), tag(), context, ec); + return ec ? write_result{unexpect, ec} : write_result{}; + case json_storage_kind::null: + visitor.null_value(tag(), context, ec); + return ec ? write_result{unexpect, ec} : write_result{}; + case json_storage_kind::empty_object: + visitor.begin_object(0, tag(), context, ec); + visitor.end_object(context, ec); + return ec ? write_result{unexpect, ec} : write_result{}; + case json_storage_kind::object: + { + visitor.begin_object(size(), tag(), context, ec); + const object& o = cast().value(); + for (auto it = o.begin(); it != o.end(); ++it) + { + visitor.key(string_view_type(((*it).key()).data(),(*it).key().length()), context, ec); + (*it).value().dump_noflush(visitor, ec); + if (JSONCONS_UNLIKELY(ec)) + { + return write_result{unexpect, ec}; + } + } + visitor.end_object(context, ec); + if (JSONCONS_UNLIKELY(ec)) + { + return write_result{unexpect, ec}; + } + return write_result{}; + } + case json_storage_kind::array: + { + visitor.begin_array(size(), tag(), context, ec); + const array& o = cast().value(); + for (const_array_iterator it = o.begin(); it != o.end(); ++it) + { + auto r = (*it).try_dump_noflush(visitor); + if (JSONCONS_UNLIKELY(!r)) + { + return r; + } + } + visitor.end_array(context, ec); + if (JSONCONS_UNLIKELY(ec)) + { + return write_result{unexpect, ec}; + } + return write_result{}; + } + case json_storage_kind::json_const_ref: + return cast().value().try_dump_noflush(visitor); + case json_storage_kind::json_ref: + return cast().value().try_dump_noflush(visitor); + default: + JSONCONS_UNREACHABLE(); + break; + } + } + friend std::basic_ostream& operator<<(std::basic_ostream& os, const basic_json& o) { o.dump(os); @@ -4544,9 +4947,9 @@ namespace jsoncons { } return j; } - case json_storage_kind::json_const_reference: + case json_storage_kind::json_const_ref: return deep_copy(other.cast().value()); - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: return deep_copy(other.cast().value()); default: return other; @@ -4744,6 +5147,7 @@ namespace jsoncons { } // namespace pmr #endif + } // namespace jsoncons #endif // JSONCONS_BASIC_JSON_HPP diff --git a/include/jsoncons/config/compiler_support.hpp b/include/jsoncons/config/compiler_support.hpp index c67bcfe..7af6188 100644 --- a/include/jsoncons/config/compiler_support.hpp +++ b/include/jsoncons/config/compiler_support.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -12,6 +12,25 @@ #include // std::memcpy #include // std::numeric_limits +#if (!defined(JSONCONS_NO_EXCEPTIONS)) +// Check if exceptions are disabled. +# if defined( __cpp_exceptions) && __cpp_exceptions == 0 +# define JSONCONS_NO_EXCEPTIONS 1 +# endif +#endif + +#if !defined(JSONCONS_NO_EXCEPTIONS) +#if defined(__GNUC__) && !defined(__EXCEPTIONS) +# define JSONCONS_NO_EXCEPTIONS 1 +#elif defined(_MSC_VER) +#if defined(_HAS_EXCEPTIONS) && _HAS_EXCEPTIONS == 0 +# define JSONCONS_NO_EXCEPTIONS 1 +#elif !defined(_CPPUNWIND) +# define JSONCONS_NO_EXCEPTIONS 1 +#endif +#endif +#endif + #if !defined(JSONCONS_NO_EXCEPTIONS) #define JSONCONS_THROW(exception) throw exception #define JSONCONS_RETHROW throw @@ -129,6 +148,23 @@ #endif #endif +#if !defined(JSONCONS_HAS_STD_FROM_CHARS) +# if defined(__GNUC__) +# if (__GNUC__ >= 11) +# if (__cplusplus >= 201703) +# if !defined(__MINGW32__) +# define JSONCONS_HAS_STD_FROM_CHARS 1 +# endif // !defined(__MINGW32__) +# endif // (__cplusplus >= 201703) +# endif // (__GNUC__ >= 11) +# endif // defined(__GNUC__) +# if defined(_MSC_VER) +# if (_MSC_VER >= 1924 && _MSVC_LANG >= 201703) +# define JSONCONS_HAS_STD_FROM_CHARS 1 +# endif // (_MSC_VER >= 1924 && MSVC_LANG >= 201703) +# endif // defined(_MSC_VER) +#endif + #if defined(JSONCONS_HAS_STD_FROM_CHARS) && JSONCONS_HAS_STD_FROM_CHARS #include #endif @@ -229,26 +265,6 @@ # endif // defined(JSONCONS_HAS_2017) #endif // !defined(JSONCONS_HAS_FILESYSTEM) -#if (!defined(JSONCONS_NO_EXCEPTIONS)) -// Check if exceptions are disabled. -# if defined( __cpp_exceptions) && __cpp_exceptions == 0 -# define JSONCONS_NO_EXCEPTIONS 1 -# endif -#endif - -#if !defined(JSONCONS_NO_EXCEPTIONS) - -#if defined(__GNUC__) && !defined(__EXCEPTIONS) -# define JSONCONS_NO_EXCEPTIONS 1 -#elif defined(_MSC_VER) -#if defined(_HAS_EXCEPTIONS) && _HAS_EXCEPTIONS == 0 -# define JSONCONS_NO_EXCEPTIONS 1 -#elif !defined(_CPPUNWIND) -# define JSONCONS_NO_EXCEPTIONS 1 -#endif -#endif -#endif - #if !defined(JSONCONS_HAS_STD_MAKE_UNIQUE) #if defined(__clang__) && defined(__cplusplus) #if defined(__APPLE__) @@ -291,19 +307,24 @@ // gcc and clang #if !defined(__CUDA_ARCH__) + #if (defined(__clang__) || defined(__GNUC__)) && defined(__cplusplus) + #if defined(__SIZEOF_INT128__) && !defined(_MSC_VER) # define JSONCONS_HAS_INT128 #endif #if (defined(linux) || defined(__linux) || defined(__linux__) || defined(__GNU__) || defined(__GLIBC__)) && !defined(_CRAYC) +#if defined(__clang__) #if (__clang_major__ >= 4) && defined(__has_include) #if __has_include() # define JSONCONS_HAS_FLOAT128 #endif #endif #endif -#endif +#endif + +#endif // (__clang__ || __GNUC__) && __cplusplus #if defined(__GNUC__) #if defined(_GLIBCXX_USE_FLOAT128) @@ -320,6 +341,7 @@ #endif #endif #endif + #endif // __CUDA_ARCH__ #ifndef JSONCONS_FORCE_INLINE @@ -369,30 +391,6 @@ # endif #endif -/** noinline for compiler */ -#ifndef JSONCONS_NOINLINE -# if YYJSON_MSC_VER >= 1400 -# define JSONCONS_NOINLINE __declspec(noinline) -# elif JSONCONS_HAS_ATTRIBUTE(noinline) || YYJSON_GCC_VER >= 4 -# define JSONCONS_NOINLINE __attribute__((noinline)) -# else -# define JSONCONS_NOINLINE -# endif -#endif - -/** align for compiler */ -#ifndef JSONCONS_ALIGN -# if YYJSON_MSC_VER >= 1300 -# define JSONCONS_ALIGN(x) __declspec(align(x)) -# elif JSONCONS_HAS_ATTRIBUTE(aligned) || defined(__GNUC__) -# define JSONCONS_ALIGN(x) __attribute__((aligned(x))) -# elif YYJSON_CPP_VER >= 201103L -# define JSONCONS_ALIGN(x) alignas(x) -# else -# define JSONCONS_ALIGN(x) -# endif -#endif - // Follows boost config/detail/suffix.hpp #if defined(JSONCONS_HAS_INT128) && defined(__cplusplus) namespace jsoncons{ @@ -560,4 +558,16 @@ namespace binary { } // namespace jsoncons // allow to disable exceptions +#if defined(JSONCONS_HAS_2017) +# define JSONCONS_FALLTHROUGH [[fallthrough]] +#elif defined(__clang__) +# define JSONCONS_FALLTHROUGH [[clang::fallthrough]] +#elif defined(__GNUC__) && ((__GNUC__ >= 7)) +# define JSONCONS_FALLTHROUGH __attribute__((fallthrough)) +#elif defined (__GNUC__) +# define JSONCONS_FALLTHROUGH // FALLTHRU +#else +# define JSONCONS_FALLTHROUGH +#endif + #endif // JSONCONS_CONFIG_COMPILER_SUPPORT_HPP diff --git a/include/jsoncons/config/jsoncons_config.hpp b/include/jsoncons/config/jsoncons_config.hpp index 45d6711..b30beee 100644 --- a/include/jsoncons/config/jsoncons_config.hpp +++ b/include/jsoncons/config/jsoncons_config.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -47,25 +47,41 @@ namespace jsoncons { JSONCONS_STR( 0 ))); } #endif // _DEBUG -#if defined(JSONCONS_HAS_2017) -# define JSONCONS_FALLTHROUGH [[fallthrough]] -#elif defined(__clang__) -# define JSONCONS_FALLTHROUGH [[clang::fallthrough]] -#elif defined(__GNUC__) && ((__GNUC__ >= 7)) -# define JSONCONS_FALLTHROUGH __attribute__((fallthrough)) -#elif defined (__GNUC__) -# define JSONCONS_FALLTHROUGH // FALLTHRU +#include +namespace jsoncons { +using jsoncons::detail::in_place_t; +JSONCONS_INLINE_CONSTEXPR in_place_t in_place{}; +} // namespace jsoncons + +#if !defined(JSONCONS_HAS_STD_EXPECTED) + #include + namespace jsoncons { + using jsoncons::detail::expected; + using jsoncons::detail::unexpect_t; + using jsoncons::detail::unexpect; + } // namespace jsoncons #else -# define JSONCONS_FALLTHROUGH + #include + namespace jsoncons { + template + using expected = std::expected; + using unexpect_t = std::unexpect_t; + JSONCONS_INLINE_CONSTEXPR unexpect_t unexpect{}; + } // namespace jsoncons #endif +#include +namespace jsoncons { +using jsoncons::detail::make_obj_using_allocator; +} // namespace jsoncons + #if !defined(JSONCONS_HAS_STD_STRING_VIEW) #include namespace jsoncons { using jsoncons::detail::basic_string_view; using string_view = jsoncons::detail::string_view; using wstring_view = jsoncons::detail::wstring_view; -} +} // namespace jsoncons #else #include namespace jsoncons { @@ -217,15 +233,31 @@ namespace jsoncons { return jsoncons::wstring_view(w); } + // From boost 1_71 + template + T launder_cast(U* u) + { + #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606 + return std::launder(reinterpret_cast(u)); + #elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) > 800 + return __builtin_launder(reinterpret_cast(u)); + #else + return reinterpret_cast(u); + #endif + } + } // namespace jsoncons -#define JSONCONS_EXPAND(X) X -#define JSONCONS_QUOTE(Prefix, A) JSONCONS_EXPAND(Prefix ## #A) -#define JSONCONS_WIDEN(A) JSONCONS_EXPAND(L ## A) +// Preprocessor macros + +#define JSONCONS_PP_EXPAND(X) X +#define JSONCONS_PP_STRINGIFY(a) #a +#define JSONCONS_PP_QUOTE(Prefix, A) JSONCONS_PP_EXPAND(Prefix ## #A) +#define JSONCONS_PP_WIDEN(A) JSONCONS_PP_EXPAND(L ## A) -#define JSONCONS_CSTRING_CONSTANT(CharT, Str) cstring_constant_of_type(Str, JSONCONS_WIDEN(Str)) -#define JSONCONS_STRING_CONSTANT(CharT, Str) string_constant_of_type(Str, JSONCONS_WIDEN(Str)) -#define JSONCONS_STRING_VIEW_CONSTANT(CharT, Str) string_view_constant_of_type(Str, JSONCONS_WIDEN(Str)) +#define JSONCONS_CSTRING_CONSTANT(CharT, Str) cstring_constant_of_type(Str, JSONCONS_PP_WIDEN(Str)) +#define JSONCONS_STRING_CONSTANT(CharT, Str) string_constant_of_type(Str, JSONCONS_PP_WIDEN(Str)) +#define JSONCONS_STRING_VIEW_CONSTANT(CharT, Str) string_view_constant_of_type(Str, JSONCONS_PP_WIDEN(Str)) #if defined(JSONCONS_VISITOR_VOID_RETURN) diff --git a/include/jsoncons/config/version.hpp b/include/jsoncons/config/version.hpp index 3faba53..6e44c5a 100644 --- a/include/jsoncons/config/version.hpp +++ b/include/jsoncons/config/version.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -7,11 +7,20 @@ #ifndef JSONCONS_CONFIG_VERSION_HPP #define JSONCONS_CONFIG_VERSION_HPP -#include +#include #define JSONCONS_VERSION_MAJOR 1 -#define JSONCONS_VERSION_MINOR 3 -#define JSONCONS_VERSION_PATCH 2 +#define JSONCONS_VERSION_MINOR 5 +#define JSONCONS_VERSION_PATCH 0 + +#define JSONCONS_VERSION_CONCAT_EX(major, minor, patch) \ + # major ## "." ## # minor ## "." ## # patch + +#define JSONCONS_VERSION_CONCAT(major, minor, patch) \ + JSONCONS_VERSION_CONCAT_EX(major, minor, patch) + +#define JSONCONS_VERSION_STRING \ + JSONCONS_VERSION_CONCAT(JSONCONS_VERSION_MAJOR, JSONCONS_VERSION_MINOR, JSONCONS_VERSION_PATCH) namespace jsoncons { @@ -27,7 +36,7 @@ struct versioning_info << ver.minor << '.' << ver.patch; return os; - } + } }; constexpr versioning_info version() diff --git a/include/jsoncons/conv_error.hpp b/include/jsoncons/conv_error.hpp index 2f52427..5f4f6c1 100644 --- a/include/jsoncons/conv_error.hpp +++ b/include/jsoncons/conv_error.hpp @@ -1,4 +1,4 @@ -/// Copyright 2013-2025 Daniel Parker +/// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -20,9 +20,6 @@ namespace jsoncons { class conv_error : public std::system_error, public virtual json_exception { - std::size_t line_number_{0}; - std::size_t column_number_{0}; - mutable std::string what_; public: conv_error(std::error_code ec) : std::system_error(ec) @@ -32,58 +29,17 @@ namespace jsoncons { : std::system_error(ec, what_arg) { } - conv_error(std::error_code ec, std::size_t position) - : std::system_error(ec), column_number_(position) - { - } - conv_error(std::error_code ec, std::size_t line, std::size_t column) - : std::system_error(ec), line_number_(line), column_number_(column) + conv_error(std::error_code ec, const char* what_arg) + : std::system_error(ec, what_arg) { } conv_error(const conv_error& other) = default; conv_error(conv_error&& other) = default; - - const char* what() const noexcept override + + const char* what() const noexcept final { - if (what_.empty()) - { - JSONCONS_TRY - { - what_.append(std::system_error::what()); - if (line_number_ != 0 && column_number_ != 0) - { - what_.append(" at line "); - what_.append(std::to_string(line_number_)); - what_.append(" and column "); - what_.append(std::to_string(column_number_)); - } - else if (column_number_ != 0) - { - what_.append(" at position "); - what_.append(std::to_string(column_number_)); - } - return what_.c_str(); - } - JSONCONS_CATCH(...) - { - return std::system_error::what(); - } - } - else - { - return what_.c_str(); - } - } - - std::size_t line() const noexcept - { - return line_number_; - } - - std::size_t column() const noexcept - { - return column_number_; + return std::system_error::what(); } }; @@ -113,14 +69,9 @@ namespace jsoncons { not_bitset, not_base64, not_base64url, - not_base16 - }; - - template - struct decode_result - { - InputIt it; - conv_errc ec; + not_base16, + not_epoch, + missing_required_member }; } // namespace jsoncons @@ -196,6 +147,10 @@ namespace detail { return "Input is not a base64url encoded string"; case conv_errc::not_base16: return "Input is not a base16 encoded string"; + case conv_errc::not_epoch: + return "Cannot convert to epoch"; + case conv_errc::missing_required_member: + return "Missing required JSON object member"; default: return "Unknown conversion error"; } diff --git a/include/jsoncons/conversion_result.hpp b/include/jsoncons/conversion_result.hpp new file mode 100644 index 0000000..02baf90 --- /dev/null +++ b/include/jsoncons/conversion_result.hpp @@ -0,0 +1,69 @@ +/// Copyright 2013-2026 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons2 for latest version + +#ifndef JSONCONS_CONVERSION_RESULT_HPP +#define JSONCONS_CONVERSION_RESULT_HPP + +#include +#include +#include + +#include +#include +#include + +namespace jsoncons { + +class conversion_error +{ + std::error_code ec_; + std::string message_arg_; +public: + conversion_error(std::error_code ec) + : ec_(ec) + { + } + conversion_error(std::error_code ec, const jsoncons::string_view& message_arg) + : ec_(ec), message_arg_(message_arg) + { + } + + conversion_error(const conversion_error& other) = default; + + conversion_error(conversion_error&& other) = default; + + conversion_error& operator=(const conversion_error& other) = default; + + conversion_error& operator=(conversion_error&& other) = default; + + std::error_code code() const + { + return ec_; + } + + const std::string& message_arg() const + { + return message_arg_; + } + + std::string message() const + { + std::string str{message_arg_}; + if (!str.empty()) + { + str.append(": "); + } + str.append(ec_.message()); + return str; + } +}; + +template +using conversion_result = expected; + +} // namespace jsoncons + +#endif // JSONCONS_CONVERSION_RESULT_HPP diff --git a/include/jsoncons/decode_json.hpp b/include/jsoncons/decode_json.hpp index af76b71..1906988 100644 --- a/include/jsoncons/decode_json.hpp +++ b/include/jsoncons/decode_json.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -7,206 +7,263 @@ #ifndef JSONCONS_DECODE_JSON_HPP #define JSONCONS_DECODE_JSON_HPP -#include #include // std::basic_istream #include +#include #include #include #include -#include #include #include #include +#include +#include namespace jsoncons { - // decode_json - - template - typename std::enable_if::value && - ext_traits::is_sequence_of::value,T>::type - decode_json(const Source& s, - const basic_json_decode_options& options = basic_json_decode_options()) - { - using char_type = typename Source::value_type; - - jsoncons::json_decoder decoder; - basic_json_reader> reader(s, decoder, options); - reader.read(); - if (!decoder.is_valid()) - { - JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); - } - return decoder.get_result(); - } - - template - typename std::enable_if::value && - ext_traits::is_char_sequence::value,T>::type - decode_json(const Source& s, - const basic_json_decode_options& options = basic_json_decode_options()) - { - using char_type = typename Source::value_type; - - basic_json_cursor> cursor(s, options, default_json_parsing()); - jsoncons::json_decoder> decoder; - std::error_code ec; - T val = decode_traits::decode(cursor, decoder, ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); - } - return val; - } - - template - typename std::enable_if::value,T>::type - decode_json(std::basic_istream& is, - const basic_json_decode_options& options = basic_json_decode_options()) - { - jsoncons::json_decoder decoder; - basic_json_reader> reader(is, decoder, options); - reader.read(); - if (!decoder.is_valid()) - { - JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); - } - return decoder.get_result(); - } - - template - typename std::enable_if::value,T>::type - decode_json(std::basic_istream& is, - const basic_json_decode_options& options = basic_json_decode_options()) - { - basic_json_cursor cursor(is, options, default_json_parsing()); - json_decoder> decoder{}; - - std::error_code ec; - T val = decode_traits::decode(cursor, decoder, ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec, cursor.line(), cursor.column())); - } - return val; - } - - template - typename std::enable_if::value,T>::type - decode_json(InputIt first, InputIt last, - const basic_json_decode_options::value_type>& options = - basic_json_decode_options::value_type>()) - { - using char_type = typename std::iterator_traits::value_type; - - jsoncons::json_decoder decoder; - basic_json_reader> reader(iterator_source(first,last), decoder, options); - reader.read(); - if (!decoder.is_valid()) - { - JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); - } - return decoder.get_result(); - } - - template - typename std::enable_if::value,T>::type - decode_json(InputIt first, InputIt last, - const basic_json_decode_options::value_type>& options = - basic_json_decode_options::value_type>()) - { - using char_type = typename std::iterator_traits::value_type; - - basic_json_cursor> cursor(iterator_source(first, last), options, default_json_parsing()); - jsoncons::json_decoder> decoder; - std::error_code ec; - T val = decode_traits::decode(cursor, decoder, ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec, cursor.line(), cursor.column())); - } - return val; - } - - // With leading allocator_set parameter - - template - typename std::enable_if::value && - ext_traits::is_sequence_of::value,T>::type - decode_json(const allocator_set& alloc_set, - const Source& s, - const basic_json_decode_options& options = basic_json_decode_options()) - { - using char_type = typename Source::value_type; - - json_decoder decoder(alloc_set.get_allocator(), alloc_set.get_temp_allocator()); - - basic_json_reader,TempAllocator> reader(s, decoder, options, alloc_set.get_temp_allocator()); - reader.read(); - if (!decoder.is_valid()) - { - JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); - } - return decoder.get_result(); - } - - template - typename std::enable_if::value && - ext_traits::is_char_sequence::value,T>::type - decode_json(const allocator_set& alloc_set, - const Source& s, - const basic_json_decode_options& options = basic_json_decode_options()) - { - using char_type = typename Source::value_type; - - basic_json_cursor,TempAllocator> cursor(s, options, default_json_parsing(), alloc_set.get_temp_allocator()); - json_decoder,TempAllocator> decoder(alloc_set.get_temp_allocator(), alloc_set.get_temp_allocator()); - - std::error_code ec; - T val = decode_traits::decode(cursor, decoder, ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); - } - return val; - } - - template - typename std::enable_if::value,T>::type - decode_json(const allocator_set& alloc_set, - std::basic_istream& is, - const basic_json_decode_options& options = basic_json_decode_options()) - { - json_decoder decoder(alloc_set.get_allocator(), alloc_set.get_temp_allocator()); - - basic_json_reader,TempAllocator> reader(is, decoder, options, alloc_set.get_temp_allocator()); - reader.read(); - if (!decoder.is_valid()) - { - JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); - } - return decoder.get_result(); - } - - template - typename std::enable_if::value,T>::type - decode_json(const allocator_set& alloc_set, - std::basic_istream& is, - const basic_json_decode_options& options = basic_json_decode_options()) - { - basic_json_cursor,TempAllocator> cursor(is, options, default_json_parsing(), alloc_set.get_temp_allocator()); - json_decoder,TempAllocator> decoder(alloc_set.get_temp_allocator(),alloc_set.get_temp_allocator()); - - std::error_code ec; - T val = decode_traits::decode(cursor, decoder, ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); - } - return val; +// try_decode_json + +template +typename std::enable_if::value && + ext_traits::is_sequence_of::value,read_result>::type +try_decode_json(const CharsLike& s, + const basic_json_decode_options& options = basic_json_decode_options()) +{ + using value_type = T; + using result_type = read_result; + using char_type = typename CharsLike::value_type; + + std::error_code ec; + jsoncons::json_decoder decoder; + basic_json_reader> reader(s, decoder, options); + reader.read(ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, reader.line(), reader.column()}; } - + if (JSONCONS_UNLIKELY(!decoder.is_valid())) + { + return result_type{jsoncons::unexpect, conv_errc::conversion_failed, reader.line(), reader.column()}; + } + return result_type{decoder.get_result()}; +} + +template +typename std::enable_if::value && + ext_traits::is_char_sequence::value,read_result>::type +try_decode_json(const CharsLike& s, + const basic_json_decode_options& options = basic_json_decode_options()) +{ + using value_type = T; + using result_type = read_result; + using char_type = typename CharsLike::value_type; + + std::error_code ec; + basic_json_cursor> cursor(s, options, ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, cursor.line(), cursor.column()}; + } + return reflect::decode_traits::try_decode(make_alloc_set(), cursor); +} + +template +typename std::enable_if::value,read_result>::type +try_decode_json(std::basic_istream& is, + const basic_json_decode_options& options = basic_json_decode_options()) +{ + using value_type = T; + using result_type = read_result; + + std::error_code ec; + jsoncons::json_decoder decoder; + basic_json_reader> reader(is, decoder, options); + reader.read(ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, reader.line(), reader.column()}; + } + if (JSONCONS_UNLIKELY(!decoder.is_valid())) + { + return result_type(jsoncons::unexpect, conv_errc::conversion_failed, reader.line(), reader.column()); + } + return result_type{decoder.get_result()}; +} + +template +typename std::enable_if::value,read_result>::type +try_decode_json(std::basic_istream& is, + const basic_json_decode_options& options = basic_json_decode_options()) +{ + using value_type = T; + using result_type = read_result; + + std::error_code ec; + basic_json_cursor cursor(is, options, ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, cursor.line(), cursor.column()}; + } + return reflect::decode_traits::try_decode(make_alloc_set(), cursor); +} + +template +typename std::enable_if::value,read_result>::type +try_decode_json(InputIt first, InputIt last, + const basic_json_decode_options::value_type>& options = + basic_json_decode_options::value_type>()) +{ + using value_type = T; + using result_type = read_result; + using char_type = typename std::iterator_traits::value_type; + + std::error_code ec; + jsoncons::json_decoder decoder; + basic_json_reader> reader(iterator_source(first,last), decoder, options); + reader.read(ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, reader.line(), reader.column()}; + } + if (JSONCONS_UNLIKELY(!decoder.is_valid())) + { + return result_type(jsoncons::unexpect, conv_errc::conversion_failed, reader.line(), reader.column()); + } + return result_type{decoder.get_result()}; +} + +template +typename std::enable_if::value,read_result>::type +try_decode_json(InputIt first, InputIt last, + const basic_json_decode_options::value_type>& options = + basic_json_decode_options::value_type>()) +{ + using value_type = T; + using result_type = read_result; + using char_type = typename std::iterator_traits::value_type; + + std::error_code ec; + basic_json_cursor> cursor(iterator_source(first, last), + options, ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, cursor.line(), cursor.column()}; + } + return reflect::decode_traits::try_decode(make_alloc_set(), cursor); +} + +// With leading allocator_set parameter + +template +typename std::enable_if::value && + ext_traits::is_sequence_of::value,read_result>::type +try_decode_json(const allocator_set& aset, + const CharsLike& s, + const basic_json_decode_options& options = basic_json_decode_options()) +{ + using value_type = T; + using result_type = read_result; + using char_type = typename CharsLike::value_type; + + json_decoder decoder(aset.get_allocator(), aset.get_temp_allocator()); + + std::error_code ec; + basic_json_reader,TempAlloc> reader(s, decoder, options, aset.get_temp_allocator()); + reader.read(ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, reader.line(), reader.column()}; + } + if (JSONCONS_UNLIKELY(!decoder.is_valid())) + { + return result_type(jsoncons::unexpect, conv_errc::conversion_failed, reader.line(), reader.column()); + } + return result_type{decoder.get_result()}; +} + +template +typename std::enable_if::value && + ext_traits::is_char_sequence::value,read_result>::type +try_decode_json(const allocator_set& aset, + const CharsLike& s, + const basic_json_decode_options& options = basic_json_decode_options()) +{ + using value_type = T; + using result_type = read_result; + using char_type = typename CharsLike::value_type; + + std::error_code ec; + basic_json_cursor,TempAlloc> cursor( + std::allocator_arg, aset.get_temp_allocator(), s, options, ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, cursor.line(), cursor.column()}; + } + return reflect::decode_traits::try_decode(aset, cursor); +} + +template +typename std::enable_if::value,read_result>::type +try_decode_json(const allocator_set& aset, + std::basic_istream& is, + const basic_json_decode_options& options = basic_json_decode_options()) +{ + using value_type = T; + using result_type = read_result; + using char_allocator_type = typename std::allocator_traits:: template rebind_alloc; + using stream_source_type = stream_source; + + json_decoder decoder(aset.get_allocator(), aset.get_temp_allocator()); + + std::error_code ec; + basic_json_reader reader(stream_source_type(is,aset.get_temp_allocator()), + decoder, options, aset.get_temp_allocator()); + reader.read(ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, reader.line(), reader.column()}; + } + if (JSONCONS_UNLIKELY(!decoder.is_valid())) + { + return result_type(jsoncons::unexpect, conv_errc::conversion_failed, reader.line(), reader.column()); + } + return result_type{decoder.get_result()}; +} + +template +typename std::enable_if::value,read_result>::type +try_decode_json(const allocator_set& aset, + std::basic_istream& is, + const basic_json_decode_options& options = basic_json_decode_options()) +{ + using value_type = T; + using result_type = read_result; + using char_type = CharT; + using char_allocator_type = typename std::allocator_traits:: template rebind_alloc; + using stream_source_type = stream_source; + + std::error_code ec; + basic_json_cursor cursor( + std::allocator_arg, aset.get_temp_allocator(), + stream_source_type(is,aset.get_temp_allocator()), options, ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, cursor.line(), cursor.column()}; + } + return reflect::decode_traits::try_decode(aset, cursor); +} + +template +T decode_json(Args&& ... args) +{ + auto result = try_decode_json(std::forward(args)...); + if (!result) + { + JSONCONS_THROW(ser_error(result.error().code(), result.error().message_arg(), result.error().line(), result.error().column())); + } + return std::move(*result); +} } // namespace jsoncons diff --git a/include/jsoncons/detail/endian.hpp b/include/jsoncons/detail/endian.hpp index b29e7d3..7bcd80c 100644 --- a/include/jsoncons/detail/endian.hpp +++ b/include/jsoncons/detail/endian.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/include/jsoncons/detail/expected.hpp b/include/jsoncons/detail/expected.hpp new file mode 100644 index 0000000..4f4f9c9 --- /dev/null +++ b/include/jsoncons/detail/expected.hpp @@ -0,0 +1,470 @@ +/// Copyright 2013-2026 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons2 for latest version + +#ifndef JSONCONS_DETAIL_EXPECTED_HPP +#define JSONCONS_DETAIL_EXPECTED_HPP + +#include +#include +#include +#include +#include + +namespace jsoncons { +namespace detail { + +struct unexpect_t +{ + explicit unexpect_t() = default; +}; + +JSONCONS_INLINE_CONSTEXPR unexpect_t unexpect{}; + +template +class expected +{ +public: + using value_type = T; + using error_type = E; +private: + bool has_value_; + union { + E error_; + T value_; + }; +public: + template + expected(typename std::enable_if::value, int>::type = 0) + : expected(T{}) + { + } + + expected(const T& value) + : has_value_(true) + { + construct(value); + } + + expected(T&& value) noexcept + : has_value_(true) + { + construct(std::move(value)); + } + + template + expected(jsoncons::detail::in_place_t, Args&& ... args) noexcept + : has_value_(true) + { + ::new (&value_) T(std::forward(args)...); + } + + template + expected(unexpect_t, Args&& ... args) noexcept + : has_value_(false) + { + ::new (&error_) E(std::forward(args)...); + } + + // copy constructors + expected(const expected& other) + : has_value_(other.has_value()) + { + if (other) + { + construct(other.value_); + } + else + { + ::new (&error_) E(other.error_); + } + } + + // move constructors + expected(expected&& other) noexcept + : has_value_(other.has_value()) + { + if (other) + { + construct(std::move(other.value_)); + } + else + { + ::new (&error_) E(other.error_); + } + } + + ~expected() noexcept + { + destroy(); + } + + expected& operator=(const expected& other) + { + if (other) + { + assign(*other); + } + else + { + destroy(); + ::new (&error_) E(other.error_); + } + return *this; + } + + expected& operator=(expected&& other) + { + if (other) + { + assign(std::move(*other)); + } + else + { + destroy(); + ::new (&error_) E(other.error_); + } + return *this; + } + + // value assignment + expected& operator=(const T& v) + { + assign(v); + return *this; + } + + expected& operator=(T&& v) + { + assign(std::move(v)); + return *this; + } + + constexpr operator bool() const noexcept + { + return has_value_; + } + + constexpr bool has_value() const noexcept + { + return has_value_; + } + + JSONCONS_CPP14_CONSTEXPR T& value() & + { + if (has_value_) + { + return this->value_; + } + JSONCONS_THROW(std::runtime_error("Bad expected access")); + } + + JSONCONS_CPP14_CONSTEXPR const T& value() const & + { + if (has_value_) + { + return this->value_; + } + JSONCONS_THROW(std::runtime_error("Bad expected access")); + } + + JSONCONS_CPP14_CONSTEXPR T&& value() && + { + if (has_value_) + { + return std::move(this->value_); + } + JSONCONS_THROW(std::runtime_error("Bad expected access")); + } + + JSONCONS_CPP14_CONSTEXPR const T&& value() const && + { + if (has_value_) + { + return std::move(this->value_); + } + JSONCONS_THROW(std::runtime_error("Bad expected access")); + } + + JSONCONS_CPP14_CONSTEXPR E& error() & noexcept + { + assert(!has_value_); + return this->error_; + } + + JSONCONS_CPP14_CONSTEXPR const E& error() const& noexcept + { + assert(!has_value_); + return this->error_; + } + + JSONCONS_CPP14_CONSTEXPR E&& error() && noexcept + { + assert(!has_value_); + return std::move(this->error_); + } + + JSONCONS_CPP14_CONSTEXPR const E&& error() const && noexcept + { + assert(!has_value_); + return std::move(this->error_); + } + + JSONCONS_CPP14_CONSTEXPR const T* operator->() const noexcept + { + return std::addressof(this->value_); + } + + JSONCONS_CPP14_CONSTEXPR T* operator->() noexcept + { + return std::addressof(this->value_); + } + + JSONCONS_CPP14_CONSTEXPR const T& operator*() const & noexcept + { + return this->value_; + } + + JSONCONS_CPP14_CONSTEXPR T& operator*() & noexcept + { + return this->value_; + } + + JSONCONS_CPP14_CONSTEXPR const T&& operator*() const && noexcept + { + return this->value_; + } + + JSONCONS_CPP14_CONSTEXPR T&& operator*() && noexcept + { + return this->value_; + } + + void swap(expected& other) noexcept(std::is_nothrow_move_constructible::value /*&& + std::is_nothrow_swappable::value*/) + { + const bool contains_a_value = has_value(); + if (contains_a_value == other.has_value()) + { + if (contains_a_value) + { + using std::swap; + swap(**this, *other); + } + } + else + { + expected& source = contains_a_value ? *this : other; + expected& target = contains_a_value ? other : *this; + target = expected(*source); + source.destroy(); + source.error_ = target.error_; + } + } +private: + void construct(const T& value) + { + ::new (&value_) T(value); + has_value_ = true; + } + + void construct(T&& value) noexcept + { + ::new (&value_) T(std::move(value)); + has_value_ = true; + } + + void destroy() noexcept + { + if (has_value_) + { + value_.~T(); + has_value_ = false; + } + else + { + error_.~E(); + } + } + + void assign(const T& u) + { + if (has_value_) + { + value_ = u; + } + else + { + construct(u); + } + } + + void assign(T&& u) + { + if (has_value_) + { + value_ = std::move(u); + } + else + { + construct(std::move(u)); + } + } +}; + +template +class expected +{ +public: + using value_type = void; + using error_type = E; +private: + bool has_value_; + union { + char dummy_; + E error_; + }; +public: + + expected() + : has_value_(true), dummy_{} + { + } + + template + expected(unexpect_t, Args&& ... args) noexcept + : has_value_(false) + { + ::new (&error_) E(std::forward(args)...); + } + + // copy constructors + expected(const expected& other) + : has_value_(other.has_value()), dummy_{} + { + if (!other) + { + ::new (&error_) E(other.error_); + } + } + + // move constructors + expected(expected&& other) noexcept + : has_value_(other.has_value()), dummy_{} + { + if (!other) + { + ::new (&error_) E(other.error_); + } + } + + ~expected() noexcept + { + destroy(); + } + + expected& operator=(const expected& other) + { + if (other) + { + assign(*other); + } + else + { + destroy(); + ::new (&error_) E(other.error_); + } + return *this; + } + + expected& operator=(expected&& other) + { + if (other) + { + assign(std::move(*other)); + } + else + { + destroy(); + ::new (&error_) E(other.error_); + } + return *this; + } + + constexpr operator bool() const noexcept + { + return has_value_; + } + + constexpr bool has_value() const noexcept + { + return has_value_; + } + + JSONCONS_CPP14_CONSTEXPR E& error() & noexcept + { + assert(!has_value_); + return this->error_; + } + + JSONCONS_CPP14_CONSTEXPR const E& error() const& noexcept + { + assert(!has_value_); + return this->error_; + } + + JSONCONS_CPP14_CONSTEXPR E&& error() && noexcept + { + assert(!has_value_); + return std::move(this->error_); + } + + JSONCONS_CPP14_CONSTEXPR const E&& error() const && noexcept + { + assert(!has_value_); + return std::move(this->error_); + } + + void swap(expected& other) noexcept + { + const bool contains_a_value = has_value(); + if (contains_a_value == other.has_value()) + { + if (contains_a_value) + { + using std::swap; + swap(**this, *other); + } + } + else + { + expected& source = contains_a_value ? *this : other; + expected& target = contains_a_value ? other : *this; + target = expected(*source); + source.destroy(); + source.error_ = target.error_; + } + } +private: + void destroy() noexcept + { + if (!has_value_) + { + error_.~E(); + } + } +}; + +template +typename std::enable_if::value,void>::type +swap(expected& lhs, expected& rhs) noexcept +{ + lhs.swap(rhs); +} + +} // namespace detail +} // namespace jsoncons + +#endif // JSONCONS_DETAIL_EXPECTED_HPP diff --git a/include/jsoncons/detail/make_obj_using_allocator.hpp b/include/jsoncons/detail/make_obj_using_allocator.hpp new file mode 100644 index 0000000..c14808f --- /dev/null +++ b/include/jsoncons/detail/make_obj_using_allocator.hpp @@ -0,0 +1,76 @@ +#ifndef JSONCONS_DETAIL_MAKE_OBJ_USING_ALLOCATOR +#define JSONCONS_DETAIL_MAKE_OBJ_USING_ALLOCATOR + +#include // for placement operator new +#include // for tuple, make_tuple, make_from_tuple +#include +#include + +namespace jsoncons { +namespace detail { + +template +typename std::enable_if::value && std::uses_allocator::value + && std::is_constructible::value, T>::type +make_obj_using_allocator(const Alloc& alloc, Args&&... args) +{ + return T(std::forward(args)..., alloc); +} + +template +typename std::enable_if::value && std::uses_allocator::value + && std::is_constructible::value, T>::type +make_obj_using_allocator(const Alloc& alloc, Args&&... args) +{ + return T(std::allocator_arg, alloc, std::forward(args)...); +} + +template +typename std::enable_if::value, T>::type +make_obj_using_allocator(const Alloc&, Args&&... args) +{ + return T(std::forward(args)...); +} + +// std::pair + +template +typename std::enable_if::value, T>::type +make_obj_using_allocator(const Alloc& alloc) +{ + return T( + jsoncons::detail::make_obj_using_allocator(alloc), + jsoncons::detail::make_obj_using_allocator(alloc)); +} + +template +typename std::enable_if::value, T>::type +make_obj_using_allocator(const Alloc& alloc, U&& u, V&& v) +{ + return T( + jsoncons::detail::make_obj_using_allocator(alloc,std::forward(u)), + jsoncons::detail::make_obj_using_allocator(alloc,std::forward(v))); +} + +template +typename std::enable_if::value, T>::type +make_obj_using_allocator(const Alloc& alloc, const std::pair& pr) +{ + return T( + jsoncons::detail::make_obj_using_allocator(alloc,pr.first), + jsoncons::detail::make_obj_using_allocator(alloc,pr.second)); +} + +template +typename std::enable_if::value, T>::type +make_obj_using_allocator(const Alloc& alloc, std::pair&& pr) +{ + return T( + jsoncons::detail::make_obj_using_allocator(alloc,std::move(pr.first)), + jsoncons::detail::make_obj_using_allocator(alloc,std::move(pr.second))); +} + +} // namespace detail +} // namespace jsoncons + +#endif // JSONCONS_DETAIL_MAKE_OBJ_USING_ALLOCATOR diff --git a/include/jsoncons/detail/optional.hpp b/include/jsoncons/detail/optional.hpp index dcc2691..ae90ed5 100644 --- a/include/jsoncons/detail/optional.hpp +++ b/include/jsoncons/detail/optional.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -13,6 +13,7 @@ #include // std::swap #include +#include namespace jsoncons { @@ -153,6 +154,13 @@ namespace detail { } + template::value,int>::type> + optional(jsoncons::detail::in_place_t, Args&&... args) + : has_value_(true), value_(std::forward(args)...) + { + } + ~optional() noexcept { destroy(); @@ -171,7 +179,7 @@ namespace detail return *this; } - optional& operator=(optional&& other ) + optional& operator=(optional&& other ) noexcept { if (other) { diff --git a/include/jsoncons/detail/span.hpp b/include/jsoncons/detail/span.hpp index 4a1b79e..6d24b1a 100644 --- a/include/jsoncons/detail/span.hpp +++ b/include/jsoncons/detail/span.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/include/jsoncons/detail/string_view.hpp b/include/jsoncons/detail/string_view.hpp index 4c76dfd..91d8b14 100644 --- a/include/jsoncons/detail/string_view.hpp +++ b/include/jsoncons/detail/string_view.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/include/jsoncons/detail/utility.hpp b/include/jsoncons/detail/utility.hpp new file mode 100644 index 0000000..17d3edf --- /dev/null +++ b/include/jsoncons/detail/utility.hpp @@ -0,0 +1,35 @@ +/// Copyright 2013-2026 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons2 for latest version + +#ifndef JSONCONS_DETAIL_UTILITY_HPP +#define JSONCONS_DETAIL_UTILITY_HPP + +#include +#include +#include + +namespace jsoncons { +namespace detail { + +#if (defined(JSONCONS_HAS_2017)) + + using in_place_t = std::in_place_t; + +#else + + struct in_place_t + { + explicit in_place_t() = default; + }; + +#endif + +JSONCONS_INLINE_CONSTEXPR in_place_t in_place{}; + +} // namespace detail +} // namespace jsoncons + +#endif // JSONCONS_DETAIL_UTILITY_HPP diff --git a/include/jsoncons/diagnostics_visitor.hpp b/include/jsoncons/diagnostics_visitor.hpp new file mode 100644 index 0000000..5b7b2da --- /dev/null +++ b/include/jsoncons/diagnostics_visitor.hpp @@ -0,0 +1,198 @@ +// Copyright 2013-2026 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_DIAGNOSTICS_VISITOR_HPP +#define JSONCONS_DIAGNOSTICS_VISITOR_HPP + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace jsoncons { + + template + class basic_json_diagnostics_visitor : public basic_default_json_visitor + { + public: + using stream_type = std::basic_ostream; + using string_type = std::basic_string; + + private: + using supertype = basic_default_json_visitor; + using string_view_type = typename supertype::string_view_type; + + struct enabler {}; + + static constexpr CharT visit_begin_array_name[] = {'v','i','s','i','t','_','b','e','g','i','n','_','a','r','r','a','y', 0}; + static constexpr CharT visit_end_array_name[] = {'v','i','s','i','t','_','e','n','d','_','a','r','r','a','y', 0}; + static constexpr CharT visit_begin_object_name[] = {'v','i','s','i','t','_','b','e','g','i','n','_','o','b','j','e','c','t', 0}; + static constexpr CharT visit_end_object_name[] = {'v','i','s','i','t','_','e','n','d','_','o','b','j','e','c','t', 0}; + static constexpr CharT visit_key_name[] = {'v','i','s','i','t','_','k','e','y', 0}; + static constexpr CharT visit_string_name[] = {'v','i','s','i','t','_','s','t','r','i','n','g', 0}; + static constexpr CharT visit_byte_string_name[] = {'v','i','s','i','t','_','b','y','t','e','_','s','t','r','i','n','g', 0}; + static constexpr CharT visit_null_name[] = {'v','i','s','i','t','_','n','u','l','l', 0}; + static constexpr CharT visit_bool_name[] = {'v','i','s','i','t','_','b','o','o','l', 0}; + static constexpr CharT visit_uint64_name[] = {'v','i','s','i','t','_','u','i','n','t','6','4', 0}; + static constexpr CharT visit_int64_name[] = {'v','i','s','i','t','_','i','n','t','6','4', 0}; + static constexpr CharT visit_half_name[] = {'v','i','s','i','t','_','h','a','l','f', 0}; + static constexpr CharT visit_double_name[] = {'v','i','s','i','t','_','d','o','u','b','l','e', 0}; + + static constexpr CharT separator_ = ':'; + + stream_type& output_; + string_type indentation_; + long level_{0}; + + public: + // If CharT is char, then enable the default constructor which binds to + // std::cout. + template + basic_json_diagnostics_visitor( + typename std::enable_if::value, U>::type = enabler{}) + : basic_json_diagnostics_visitor(std::cout) + { + } + + // If CharT is wchar_t, then enable the default constructor which binds + // to std::wcout. + template + basic_json_diagnostics_visitor( + typename std::enable_if::value, U>::type = enabler{}) + : basic_json_diagnostics_visitor(std::wcout) + { + } + + explicit basic_json_diagnostics_visitor( + stream_type& output, + string_type indentation = string_type()) + : output_(output), + indentation_(std::move(indentation)) + { + } + + private: + void indent() + { + for (long i=0; i= 201703L +// not needed for C++17 +#else + template constexpr C basic_json_diagnostics_visitor::visit_begin_array_name[]; + template constexpr C basic_json_diagnostics_visitor::visit_end_array_name[]; + template constexpr C basic_json_diagnostics_visitor::visit_begin_object_name[]; + template constexpr C basic_json_diagnostics_visitor::visit_end_object_name[]; + template constexpr C basic_json_diagnostics_visitor::visit_key_name[]; + template constexpr C basic_json_diagnostics_visitor::visit_string_name[]; + template constexpr C basic_json_diagnostics_visitor::visit_byte_string_name[]; + template constexpr C basic_json_diagnostics_visitor::visit_null_name[]; + template constexpr C basic_json_diagnostics_visitor::visit_bool_name[]; + template constexpr C basic_json_diagnostics_visitor::visit_uint64_name[]; + template constexpr C basic_json_diagnostics_visitor::visit_int64_name[]; + template constexpr C basic_json_diagnostics_visitor::visit_half_name[]; + template constexpr C basic_json_diagnostics_visitor::visit_double_name[]; +#endif // C++17 check + + using json_diagnostics_visitor = basic_json_diagnostics_visitor; + using wjson_diagnostics_visitor = basic_json_diagnostics_visitor; + +} // namespace jsoncons + +#endif // JSONCONS_JSON_VISITOR_HPP diff --git a/include/jsoncons/diagnostics_visitor2.hpp b/include/jsoncons/diagnostics_visitor2.hpp new file mode 100644 index 0000000..c428377 --- /dev/null +++ b/include/jsoncons/diagnostics_visitor2.hpp @@ -0,0 +1,120 @@ +// Copyright 2013-2026 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_DIAGNOSTICS_VISITOR2_HPP +#define JSONCONS_DIAGNOSTICS_VISITOR2_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace jsoncons { + + class diagnostics_visitor2 : public basic_default_item_event_visitor + { + JSONCONS_VISITOR_RETURN_TYPE visit_begin_object(semantic_tag, const ser_context&, std::error_code&) override + { + std::cout << "visit_begin_object" << '\n'; + JSONCONS_VISITOR_RETURN; + } + + JSONCONS_VISITOR_RETURN_TYPE visit_begin_object(std::size_t length, semantic_tag, const ser_context&, std::error_code&) override + { + std::cout << "visit_begin_object " << length << '\n'; + JSONCONS_VISITOR_RETURN; + } + + JSONCONS_VISITOR_RETURN_TYPE visit_end_object(const ser_context&, std::error_code&) override + { + std::cout << "visit_end_object" << '\n'; + JSONCONS_VISITOR_RETURN; + } + + JSONCONS_VISITOR_RETURN_TYPE visit_begin_array(semantic_tag, const ser_context&, std::error_code&) override + { + std::cout << "visit_begin_array" << '\n'; + JSONCONS_VISITOR_RETURN; + } + + JSONCONS_VISITOR_RETURN_TYPE visit_begin_array(std::size_t length, semantic_tag, const ser_context&, std::error_code&) override + { + std::cout << "visit_begin_array " << length << '\n'; + JSONCONS_VISITOR_RETURN; + } + + JSONCONS_VISITOR_RETURN_TYPE visit_end_array(const ser_context&, std::error_code&) override + { + std::cout << "visit_end_array" << '\n'; + JSONCONS_VISITOR_RETURN; + } + + JSONCONS_VISITOR_RETURN_TYPE visit_string(const string_view_type& s, semantic_tag, const ser_context&, std::error_code&) override + { + std::cout << "visit_string " << s << '\n'; + JSONCONS_VISITOR_RETURN; + } + JSONCONS_VISITOR_RETURN_TYPE visit_int64(int64_t val, semantic_tag, const ser_context&, std::error_code&) override + { + std::cout << "visit_int64 " << val << '\n'; + JSONCONS_VISITOR_RETURN; + } + JSONCONS_VISITOR_RETURN_TYPE visit_uint64(uint64_t val, semantic_tag, const ser_context&, std::error_code&) override + { + std::cout << "visit_uint64 " << val << '\n'; + JSONCONS_VISITOR_RETURN; + } + JSONCONS_VISITOR_RETURN_TYPE visit_bool(bool val, semantic_tag, const ser_context&, std::error_code&) override + { + std::cout << "visit_bool " << val << '\n'; + JSONCONS_VISITOR_RETURN; + } + JSONCONS_VISITOR_RETURN_TYPE visit_null(semantic_tag, const ser_context&, std::error_code&) override + { + std::cout << "visit_null " << '\n'; + JSONCONS_VISITOR_RETURN; + } + + JSONCONS_VISITOR_RETURN_TYPE visit_typed_array(const jsoncons::span& s, + semantic_tag tag, + const ser_context&, + std::error_code&) override + { + std::cout << "visit_typed_array uint16_t " << tag << '\n'; + for (auto val : s) + { + std::cout << val << "" << '\n'; + } + std::cout << "" << '\n'; + JSONCONS_VISITOR_RETURN; + } + + JSONCONS_VISITOR_RETURN_TYPE visit_typed_array(half_arg_t, const jsoncons::span& s, + semantic_tag tag, + const ser_context&, + std::error_code&) override + { + std::cout << "visit_typed_array half_arg_t uint16_t " << tag << "" << '\n'; + for (auto val : s) + { + std::cout << val << "" << '\n'; + } + std::cout << "" << '\n'; + JSONCONS_VISITOR_RETURN; + } + }; + +} // namespace jsoncons + +#endif // JSONCONS_DIAGNOSTICS_VISITOR2_HPP diff --git a/include/jsoncons/encode_json.hpp b/include/jsoncons/encode_json.hpp index 059f3b8..777e2cc 100644 --- a/include/jsoncons/encode_json.hpp +++ b/include/jsoncons/encode_json.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -7,283 +7,291 @@ #ifndef JSONCONS_ENCODE_JSON_HPP #define JSONCONS_ENCODE_JSON_HPP -#include -#include -#include +#include -#include -#include -#include #include +#include +#include +#include +#include +#include namespace jsoncons { - // to string +// encode_json + +template +typename std::enable_if::value,write_result>::type + try_encode_json(const T& val, basic_json_visitor& encoder) +{ + return val.try_dump(encoder); +} + +template +typename std::enable_if::value,write_result>::type + try_encode_json(const T& val, basic_json_visitor& encoder) +{ + auto r = reflect::encode_traits::try_encode(make_alloc_set(), val, encoder); + encoder.flush(); + return r; +} + +template +typename std::enable_if::value, write_result>::type + try_encode_json(const allocator_set&, + const T& val, basic_json_visitor& encoder) +{ + return val.try_dump(encoder); +} + +template +typename std::enable_if::value, write_result>::type + try_encode_json(const allocator_set& aset, + const T& val, basic_json_visitor& encoder) +{ + auto r = reflect::encode_traits::try_encode(aset, val, encoder); + encoder.flush(); + return r; +} + +// to string + +template +typename std::enable_if::value,write_result>::type +try_encode_json(const T& val, CharContainer& cont, + const basic_json_encode_options& options + = basic_json_encode_options()) +{ + using char_type = typename CharContainer::value_type; + + basic_compact_json_encoder> encoder(cont, options); + return try_encode_json(val, encoder); +} + +// to stream + +template +write_result try_encode_json(const T& val, std::basic_ostream& os, + const basic_json_encode_options& options + = basic_json_encode_options()) +{ + basic_compact_json_encoder encoder(os, options); + return try_encode_json(val, encoder); +} + +// to string with allocator_set + +template +typename std::enable_if::value,write_result>::type +try_encode_json(const allocator_set& aset, + const T& val, CharContainer& cont, + const basic_json_encode_options& options + = basic_json_encode_options()) +{ + using char_type = typename CharContainer::value_type; + + basic_compact_json_encoder,TempAlloc> encoder(cont, options, + aset.get_temp_allocator()); + return try_encode_json(aset, val, encoder); +} + +// to stream with allocator_set + +template +write_result try_encode_json(const allocator_set& aset, + const T& val, std::basic_ostream& os, + const basic_json_encode_options& options + = basic_json_encode_options()) +{ + basic_compact_json_encoder encoder(os, options, aset.get_temp_allocator()); + return try_encode_json(aset, val, encoder); +} + +// try_encode_json_pretty + +template +typename std::enable_if::value,write_result>::type +try_encode_json_pretty(const T& val, + CharContainer& cont, + const basic_json_encode_options& options + = basic_json_encode_options()) +{ + using char_type = typename CharContainer::value_type; + basic_json_encoder> encoder(cont, options); + return try_encode_json(val, encoder); +} + +template +write_result try_encode_json_pretty(const T& val, + std::basic_ostream& os, + const basic_json_encode_options& options + = basic_json_encode_options()) +{ + basic_json_encoder encoder(os, options); + return try_encode_json(val, encoder); +} + +template +typename std::enable_if::value,write_result>::type +try_encode_json_pretty(const allocator_set& aset, const T& val, + CharContainer& cont, + const basic_json_encode_options& options + = basic_json_encode_options()) +{ + using char_type = typename CharContainer::value_type; + basic_json_encoder> encoder(cont, options, + aset.get_temp_allocator()); + return try_encode_json(aset, val, encoder); +} + +template +write_result try_encode_json_pretty(const allocator_set& aset,const T& val, + std::basic_ostream& os, + const basic_json_encode_options& options + = basic_json_encode_options()) +{ + basic_json_encoder encoder(os, options, aset.get_temp_allocator()); + return try_encode_json(aset, val, encoder); +} + +// legacy - template - typename std::enable_if::value && - ext_traits::is_back_insertable_char_container::value>::type - encode_json(const T& val, CharContainer& cont, - const basic_json_encode_options& options = - basic_json_encode_options(), - indenting indent = indenting::no_indent) +template +write_result try_encode_json(const T& val, CharContainer& cont, indenting indent) +{ + if (indent == indenting::indent) { - using char_type = typename CharContainer::value_type; - - if (indent == indenting::no_indent) - { - basic_compact_json_encoder> encoder(cont, options); - val.dump(encoder); - } - else - { - basic_json_encoder> encoder(cont, options); - val.dump(encoder); - } + return try_encode_json_pretty(val,cont); } - - template - typename std::enable_if::value && - ext_traits::is_back_insertable_char_container::value>::type - encode_json(const T& val, CharContainer& cont, - const basic_json_encode_options& options = - basic_json_encode_options(), - indenting indent = indenting::no_indent) + else { - using char_type = typename CharContainer::value_type; - - if (indent == indenting::no_indent) - { - basic_compact_json_encoder> encoder(cont, options); - encode_json(val, encoder); - } - else - { - basic_json_encoder> encoder(cont, options); - encode_json(val, encoder); - } + return try_encode_json(val,cont); } - - // to stream - - template - typename std::enable_if::value>::type - encode_json(const T& val, std::basic_ostream& os, - const basic_json_encode_options& options = basic_json_encode_options(), - indenting indent = indenting::no_indent) +} + +template +write_result try_encode_json(const T& val, + std::basic_ostream& os, + indenting indent) +{ + if (indent == indenting::indent) { - if (indent == indenting::no_indent) - { - basic_compact_json_encoder encoder(os, options); - val.dump(encoder); - } - else - { - basic_json_encoder encoder(os, options); - val.dump(encoder); - } + return try_encode_json_pretty(val, os); } - - template - typename std::enable_if::value>::type - encode_json(const T& val, std::basic_ostream& os, - const basic_json_encode_options& options = basic_json_encode_options(), - indenting indent = indenting::no_indent) + else { - if (indent == indenting::no_indent) - { - basic_compact_json_encoder encoder(os, options); - encode_json(val, encoder); - } - else - { - basic_json_encoder encoder(os, options); - encode_json(val, encoder); - } + return try_encode_json(val, os); } +} + + +// to string - // to string with allocator_set +template +typename std::enable_if::value,write_result>::type +try_encode_json(const T& val, CharContainer& cont, + const basic_json_encode_options& options, + indenting indent) +{ + using char_type = typename CharContainer::value_type; - template - typename std::enable_if::value && - ext_traits::is_back_insertable_char_container::value>::type - encode_json(const allocator_set& alloc_set, - const T& val, CharContainer& cont, - const basic_json_encode_options& options = - basic_json_encode_options(), - indenting indent = indenting::no_indent) + if (indent == indenting::no_indent) { - using char_type = typename CharContainer::value_type; - - if (indent == indenting::no_indent) - { - basic_compact_json_encoder,TempAllocator> encoder(cont, options, alloc_set.get_temp_allocator()); - val.dump(encoder); - } - else - { - basic_json_encoder,TempAllocator> encoder(cont, options, alloc_set.get_temp_allocator()); - val.dump(encoder); - } + basic_compact_json_encoder> encoder(cont, options); + return try_encode_json(val, encoder); } - - template - typename std::enable_if::value && - ext_traits::is_back_insertable_char_container::value>::type - encode_json(const allocator_set& alloc_set, - const T& val, CharContainer& cont, - const basic_json_encode_options& options = - basic_json_encode_options(), - indenting indent = indenting::no_indent) + else { - using char_type = typename CharContainer::value_type; - - if (indent == indenting::no_indent) - { - basic_compact_json_encoder,TempAllocator> encoder(cont, options, - alloc_set.get_temp_allocator()); - encode_json(val, encoder); - } - else - { - basic_json_encoder, TempAllocator> encoder(cont, options, - alloc_set.get_temp_allocator()); - encode_json(val, encoder); - } + basic_json_encoder> encoder(cont, options); + return try_encode_json(val, encoder); } +} - // to stream with allocator_set +// to stream - template - typename std::enable_if::value>::type - encode_json(const allocator_set& alloc_set, - const T& val, std::basic_ostream& os, - const basic_json_encode_options& options = basic_json_encode_options(), - indenting indent = indenting::no_indent) +template +write_result try_encode_json(const T& val, std::basic_ostream& os, + const basic_json_encode_options& options, + indenting indent) +{ + if (indent == indenting::no_indent) { - if (indent == indenting::no_indent) - { - basic_compact_json_encoder encoder(os, options, alloc_set.get_temp_allocator()); - val.dump(encoder); - } - else - { - basic_json_encoder encoder(os, options, alloc_set.get_temp_allocator()); - val.dump(encoder); - } + basic_compact_json_encoder encoder(os, options); + return try_encode_json(val, encoder); } - - template - typename std::enable_if::value>::type - encode_json(const allocator_set& alloc_set, - const T& val, std::basic_ostream& os, - const basic_json_encode_options& options = basic_json_encode_options(), - indenting indent = indenting::no_indent) + else { - if (indent == indenting::no_indent) - { - basic_compact_json_encoder encoder(os, options, alloc_set.get_temp_allocator()); - encode_json(val, encoder); - } - else - { - basic_json_encoder encoder(os, options, alloc_set.get_temp_allocator()); - encode_json(val, encoder); - } + basic_json_encoder encoder(os, options); + return try_encode_json(val, encoder); } +} - // to encoder +// to string with allocator_set - template - void encode_json(const T& val, basic_json_visitor& encoder) - { - std::error_code ec; - encode_traits::encode(val, encoder, basic_json(), ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec)); - } - encoder.flush(); - } +template +typename std::enable_if::value,write_result>::type +try_encode_json(const allocator_set& aset, + const T& val, CharContainer& cont, + const basic_json_encode_options& options, + indenting indent) +{ + using char_type = typename CharContainer::value_type; - // encode_json_pretty - - template - typename std::enable_if::value && - ext_traits::is_back_insertable_char_container::value>::type - encode_json_pretty(const T& val, - CharContainer& cont, - const basic_json_encode_options& options = basic_json_encode_options()) + if (indent == indenting::no_indent) { - using char_type = typename CharContainer::value_type; - - basic_json_encoder> encoder(cont, options); - val.dump(encoder); + basic_compact_json_encoder,TempAlloc> encoder(cont, options, + aset.get_temp_allocator()); + return try_encode_json(aset, val, encoder); } - - template - typename std::enable_if::value && - ext_traits::is_back_insertable_char_container::value>::type - encode_json_pretty(const T& val, - CharContainer& cont, - const basic_json_encode_options& options = basic_json_encode_options()) + else { - using char_type = typename CharContainer::value_type; - basic_json_encoder> encoder(cont, options); - encode_json(val, encoder); + basic_json_encoder, TempAlloc> encoder(cont, options, + aset.get_temp_allocator()); + return try_encode_json(aset, val, encoder); } +} + +// to stream with allocator_set - template - typename std::enable_if::value>::type - encode_json_pretty(const T& val, - std::basic_ostream& os, - const basic_json_encode_options& options = basic_json_encode_options()) +template +write_result try_encode_json(const allocator_set& aset, + const T& val, std::basic_ostream& os, + const basic_json_encode_options& options, + indenting indent) +{ + if (indent == indenting::no_indent) { - basic_json_encoder encoder(os, options); - val.dump(encoder); + basic_compact_json_encoder encoder(os, options, aset.get_temp_allocator()); + return try_encode_json(aset, val, encoder); } - - template - typename std::enable_if::value>::type - encode_json_pretty(const T& val, - std::basic_ostream& os, - const basic_json_encode_options& options = basic_json_encode_options()) + else { - basic_json_encoder encoder(os, options); - encode_json(val, encoder); + basic_json_encoder encoder(os, options, aset.get_temp_allocator()); + return try_encode_json(aset, val, encoder); } +} -// legacy +//end legacy - template - void encode_json(const T& val, CharContainer& cont, indenting indent) +template +void encode_json(Args&& ... args) +{ + auto result = try_encode_json(std::forward(args)...); + if (!result) { - if (indent == indenting::indent) - { - encode_json_pretty(val,cont); - } - else - { - encode_json(val,cont); - } + JSONCONS_THROW(ser_error(result.error())); } +} - template - void encode_json(const T& val, - std::basic_ostream& os, - indenting indent) +template +void encode_json_pretty(Args&& ... args) +{ + auto result = try_encode_json_pretty(std::forward(args)...); + if (!result) { - if (indent == indenting::indent) - { - encode_json_pretty(val, os); - } - else - { - encode_json(val, os); - } + JSONCONS_THROW(ser_error(result.error())); } - -//end legacy +} } // namespace jsoncons diff --git a/include/jsoncons/item_event_visitor.hpp b/include/jsoncons/item_event_visitor.hpp index 6506342..84c0c45 100644 --- a/include/jsoncons/item_event_visitor.hpp +++ b/include/jsoncons/item_event_visitor.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -9,7 +9,6 @@ #include #include -#include #include #include #include @@ -18,13 +17,13 @@ #include #include -#include +#include #include #include #include #include #include -#include +#include #include #include #include @@ -1127,13 +1126,13 @@ namespace jsoncons { switch (tag) { case semantic_tag::base64: - encode_base64(value.begin(), value.end(), key_); + bytes_to_base64(value.begin(), value.end(), key_); break; case semantic_tag::base16: - encode_base16(value.begin(), value.end(),key_); + bytes_to_base16(value.begin(), value.end(),key_); break; default: - encode_base64url(value.begin(), value.end(),key_); + bytes_to_base64url(value.begin(), value.end(),key_); break; } } @@ -1188,7 +1187,7 @@ namespace jsoncons { if (level_stack_.back().is_key() || level_stack_.back().target() == target_t::buffer) { key_.clear(); - encode_base64url(value.begin(), value.end(),key_); + bytes_to_base64url(value.begin(), value.end(),key_); } if (level_stack_.back().is_key()) @@ -1238,7 +1237,7 @@ namespace jsoncons { if (level_stack_.back().is_key() || level_stack_.back().target() == target_t::buffer) { key_.clear(); - jsoncons::detail::from_integer(value,key_); + jsoncons::from_integer(value,key_); } if (level_stack_.back().is_key()) @@ -1284,7 +1283,7 @@ namespace jsoncons { if (level_stack_.back().is_key() || level_stack_.back().target() == target_t::buffer) { key_.clear(); - jsoncons::detail::from_integer(value,key_); + jsoncons::from_integer(value,key_); } if (level_stack_.back().is_key()) @@ -1331,7 +1330,7 @@ namespace jsoncons { { key_.clear(); jsoncons::string_sink sink(key_); - jsoncons::detail::write_double f{float_chars_format::general,0}; + jsoncons::write_double f{float_chars_format::general,0}; double x = binary::decode_half(value); f(x, sink); } @@ -1380,7 +1379,7 @@ namespace jsoncons { { key_.clear(); string_sink sink(key_); - jsoncons::detail::write_double f{float_chars_format::general,0}; + jsoncons::write_double f{float_chars_format::general,0}; f(value, sink); } @@ -1945,99 +1944,6 @@ namespace jsoncons { } }; - class diagnostics_visitor2 : public basic_default_item_event_visitor - { - JSONCONS_VISITOR_RETURN_TYPE visit_begin_object(semantic_tag, const ser_context&, std::error_code&) override - { - std::cout << "visit_begin_object" << '\n'; - JSONCONS_VISITOR_RETURN; - } - - JSONCONS_VISITOR_RETURN_TYPE visit_begin_object(std::size_t length, semantic_tag, const ser_context&, std::error_code&) override - { - std::cout << "visit_begin_object " << length << '\n'; - JSONCONS_VISITOR_RETURN; - } - - JSONCONS_VISITOR_RETURN_TYPE visit_end_object(const ser_context&, std::error_code&) override - { - std::cout << "visit_end_object" << '\n'; - JSONCONS_VISITOR_RETURN; - } - - JSONCONS_VISITOR_RETURN_TYPE visit_begin_array(semantic_tag, const ser_context&, std::error_code&) override - { - std::cout << "visit_begin_array" << '\n'; - JSONCONS_VISITOR_RETURN; - } - - JSONCONS_VISITOR_RETURN_TYPE visit_begin_array(std::size_t length, semantic_tag, const ser_context&, std::error_code&) override - { - std::cout << "visit_begin_array " << length << '\n'; - JSONCONS_VISITOR_RETURN; - } - - JSONCONS_VISITOR_RETURN_TYPE visit_end_array(const ser_context&, std::error_code&) override - { - std::cout << "visit_end_array" << '\n'; - JSONCONS_VISITOR_RETURN; - } - - JSONCONS_VISITOR_RETURN_TYPE visit_string(const string_view_type& s, semantic_tag, const ser_context&, std::error_code&) override - { - std::cout << "visit_string " << s << '\n'; - JSONCONS_VISITOR_RETURN; - } - JSONCONS_VISITOR_RETURN_TYPE visit_int64(int64_t val, semantic_tag, const ser_context&, std::error_code&) override - { - std::cout << "visit_int64 " << val << '\n'; - JSONCONS_VISITOR_RETURN; - } - JSONCONS_VISITOR_RETURN_TYPE visit_uint64(uint64_t val, semantic_tag, const ser_context&, std::error_code&) override - { - std::cout << "visit_uint64 " << val << '\n'; - JSONCONS_VISITOR_RETURN; - } - JSONCONS_VISITOR_RETURN_TYPE visit_bool(bool val, semantic_tag, const ser_context&, std::error_code&) override - { - std::cout << "visit_bool " << val << '\n'; - JSONCONS_VISITOR_RETURN; - } - JSONCONS_VISITOR_RETURN_TYPE visit_null(semantic_tag, const ser_context&, std::error_code&) override - { - std::cout << "visit_null " << '\n'; - JSONCONS_VISITOR_RETURN; - } - - JSONCONS_VISITOR_RETURN_TYPE visit_typed_array(const jsoncons::span& s, - semantic_tag tag, - const ser_context&, - std::error_code&) override - { - std::cout << "visit_typed_array uint16_t " << tag << '\n'; - for (auto val : s) - { - std::cout << val << "" << '\n'; - } - std::cout << "" << '\n'; - JSONCONS_VISITOR_RETURN; - } - - JSONCONS_VISITOR_RETURN_TYPE visit_typed_array(half_arg_t, const jsoncons::span& s, - semantic_tag tag, - const ser_context&, - std::error_code&) override - { - std::cout << "visit_typed_array half_arg_t uint16_t " << tag << "" << '\n'; - for (auto val : s) - { - std::cout << val << "" << '\n'; - } - std::cout << "" << '\n'; - JSONCONS_VISITOR_RETURN; - } - }; - using item_event_visitor = basic_item_event_visitor; using default_item_event_visitor = basic_default_item_event_visitor; using item_event_visitor_to_visitor_adaptor = basic_item_event_visitor_to_json_visitor; diff --git a/include/jsoncons/json.hpp b/include/jsoncons/json.hpp index 902a724..e608976 100644 --- a/include/jsoncons/json.hpp +++ b/include/jsoncons/json.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include #endif // JSONCONS_JSON_HPP diff --git a/include/jsoncons/json_array.hpp b/include/jsoncons/json_array.hpp index 70780d7..8cd52c5 100644 --- a/include/jsoncons/json_array.hpp +++ b/include/jsoncons/json_array.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/include/jsoncons/json_cursor.hpp b/include/jsoncons/json_cursor.hpp index 20ba535..895aa94 100644 --- a/include/jsoncons/json_cursor.hpp +++ b/include/jsoncons/json_cursor.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include #include @@ -48,18 +48,16 @@ class basic_json_cursor : public basic_staj_cursor, private virtual ser_c public: // Constructors that throw parse exceptions - template basic_json_cursor(Sourceable&& source, const basic_json_decode_options& options = basic_json_decode_options(), - std::function err_handler = default_json_parsing(), const Allocator& alloc = Allocator(), typename std::enable_if,Sourceable>::value>::type* = 0) : source_(std::forward(source)), - parser_(options,err_handler,alloc) + parser_(options, alloc) { parser_.cursor_mode(true); - if (!done()) + if (!read_done()) { std::error_code local_ec; read_next(local_ec); @@ -76,20 +74,59 @@ class basic_json_cursor : public basic_staj_cursor, private virtual ser_c } } } - template basic_json_cursor(Sourceable&& source, const basic_json_decode_options& options = basic_json_decode_options(), - std::function err_handler = default_json_parsing(), const Allocator& alloc = Allocator(), typename std::enable_if,Sourceable>::value>::type* = 0) : source_(), - parser_(options, err_handler, alloc) + parser_(options, alloc) { parser_.cursor_mode(true); initialize_with_string_view(std::forward(source)); } +#if !defined(JSONCONS_NO_DEPRECATED) + template + basic_json_cursor(Sourceable&& source, + const basic_json_decode_options& options, + std::function err_handler, + const Allocator& alloc = Allocator(), + typename std::enable_if,Sourceable>::value>::type* = 0) + : source_(std::forward(source)), + parser_(options,err_handler,alloc) + { + parser_.cursor_mode(true); + if (!read_done()) + { + std::error_code local_ec; + read_next(local_ec); + if (local_ec) + { + if (local_ec == json_errc::unexpected_eof) + { + done_ = true; + } + else + { + JSONCONS_THROW(ser_error(local_ec, 1, 1)); + } + } + } + } + template + basic_json_cursor(Sourceable&& source, + const basic_json_decode_options& options, + std::function err_handler, + const Allocator& alloc = Allocator(), + typename std::enable_if,Sourceable>::value>::type* = 0) + : source_(), + parser_(options, err_handler, alloc) + { + parser_.cursor_mode(true); + initialize_with_string_view(std::forward(source)); + } +#endif // Constructors that set parse error codes template @@ -97,7 +134,6 @@ class basic_json_cursor : public basic_staj_cursor, private virtual ser_c : basic_json_cursor(std::allocator_arg, Allocator(), std::forward(source), basic_json_decode_options(), - default_json_parsing(), ec) { } @@ -109,11 +145,11 @@ class basic_json_cursor : public basic_staj_cursor, private virtual ser_c : basic_json_cursor(std::allocator_arg, Allocator(), std::forward(source), options, - default_json_parsing(), ec) { } +#if !defined(JSONCONS_NO_DEPRECATED) template basic_json_cursor(Sourceable&& source, const basic_json_decode_options& options, @@ -139,7 +175,7 @@ class basic_json_cursor : public basic_staj_cursor, private virtual ser_c { parser_.cursor_mode(true); - if (!done()) + if (!read_done()) { std::error_code local_ec; read_next(local_ec); @@ -156,6 +192,37 @@ class basic_json_cursor : public basic_staj_cursor, private virtual ser_c } } } +#endif + + template + basic_json_cursor(std::allocator_arg_t, const Allocator& alloc, + Sourceable&& source, + const basic_json_decode_options& options, + std::error_code& ec, + typename std::enable_if,Sourceable>::value>::type* = 0) + : source_(std::forward(source)), + parser_(options, alloc) + { + parser_.cursor_mode(true); + + if (!read_done()) + { + std::error_code local_ec; + read_next(local_ec); + if (local_ec) + { + if (local_ec == json_errc::unexpected_eof) + { + done_ = true; + } + else + { + ec = local_ec; + } + } + } + } +#if !defined(JSONCONS_NO_DEPRECATED) template basic_json_cursor(std::allocator_arg_t, const Allocator& alloc, @@ -170,6 +237,19 @@ class basic_json_cursor : public basic_staj_cursor, private virtual ser_c parser_.cursor_mode(true); initialize_with_string_view(std::forward(source), ec); } +#endif + template + basic_json_cursor(std::allocator_arg_t, const Allocator& alloc, + Sourceable&& source, + const basic_json_decode_options& options, + std::error_code& ec, + typename std::enable_if,Sourceable>::value>::type* = 0) + : source_(), + parser_(options, alloc) + { + parser_.cursor_mode(true); + initialize_with_string_view(std::forward(source), ec); + } basic_json_cursor(const basic_json_cursor&) = delete; basic_json_cursor(basic_json_cursor&&) = default; @@ -186,9 +266,9 @@ class basic_json_cursor : public basic_staj_cursor, private virtual ser_c parser_.reset(); cursor_visitor_.reset(); done_ = false; - if (!done()) + if (!read_done()) { - next(); + read_next(); } } @@ -200,9 +280,9 @@ class basic_json_cursor : public basic_staj_cursor, private virtual ser_c parser_.reinitialize(); cursor_visitor_.reset(); done_ = false; - if (!done()) + if (!read_done()) { - next(); + read_next(); } } @@ -222,9 +302,9 @@ class basic_json_cursor : public basic_staj_cursor, private virtual ser_c parser_.reset(); cursor_visitor_.reset(); done_ = false; - if (!done()) + if (!read_done()) { - next(ec); + read_next(ec); } } @@ -236,9 +316,9 @@ class basic_json_cursor : public basic_staj_cursor, private virtual ser_c parser_.reinitialize(); cursor_visitor_.reset(); done_ = false; - if (!done()) + if (!read_done()) { - next(ec); + read_next(ec); } } @@ -304,12 +384,7 @@ class basic_json_cursor : public basic_staj_cursor, private virtual ser_c void next() override { - std::error_code ec; - next(ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column())); - } + read_next(); } void next(std::error_code& ec) override @@ -384,13 +459,18 @@ class basic_json_cursor : public basic_staj_cursor, private virtual ser_c friend basic_staj_filter_view operator|(basic_json_cursor& cursor, - std::function&, const ser_context&)> pred) + std::function&, const ser_context&)> pred) { return basic_staj_filter_view(cursor, pred); } private: + bool read_done() const + { + return parser_.done() || done_; + } + void initialize_with_string_view(string_view_type sv) { std::error_code local_ec; @@ -411,8 +491,8 @@ class basic_json_cursor : public basic_staj_cursor, private virtual ser_c } std::size_t offset = (r.ptr - sv.data()); parser_.update(sv.data()+offset,sv.size()-offset); - bool is_done = parser_.done() || done_; - if (!is_done) + bool read_done = parser_.done() || done_; + if (!read_done) { std::error_code local_ec; read_next(local_ec); diff --git a/include/jsoncons/json_decoder.hpp b/include/jsoncons/json_decoder.hpp index 6c19291..15dfde4 100644 --- a/include/jsoncons/json_decoder.hpp +++ b/include/jsoncons/json_decoder.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -18,11 +18,11 @@ #include #include #include -#include +#include namespace jsoncons { -template > +template > class json_decoder final : public basic_json_visitor { public: @@ -54,7 +54,7 @@ class json_decoder final : public basic_json_visitor ~structure_info() = default; }; - using temp_allocator_type = TempAllocator; + using temp_allocator_type = TempAlloc; using stack_item_allocator_type = typename std::allocator_traits:: template rebind_alloc>; using structure_info_allocator_type = typename std::allocator_traits:: template rebind_alloc; diff --git a/include/jsoncons/json_encoder.hpp b/include/jsoncons/json_encoder.hpp index 22af243..eaaf14d 100644 --- a/include/jsoncons/json_encoder.hpp +++ b/include/jsoncons/json_encoder.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -19,201 +19,57 @@ #include #include -#include +#include #include #include #include #include #include #include -#include +#include #include #include #include #include +#include namespace jsoncons { -namespace detail { - - inline - bool is_control_character(uint32_t c) - { - return c <= 0x1F || c == 0x7f; - } - - inline - bool is_non_ascii_codepoint(uint32_t cp) - { - return cp >= 0x80; - } - - template - std::size_t escape_string(const CharT* s, std::size_t length, - bool escape_all_non_ascii, bool escape_solidus, - Sink& sink) - { - std::size_t count = 0; - const CharT* begin = s; - const CharT* end = s + length; - for (const CharT* it = begin; it != end; ++it) - { - CharT c = *it; - switch (c) - { - case '\\': - sink.push_back('\\'); - sink.push_back('\\'); - count += 2; - break; - case '"': - sink.push_back('\\'); - sink.push_back('\"'); - count += 2; - break; - case '\b': - sink.push_back('\\'); - sink.push_back('b'); - count += 2; - break; - case '\f': - sink.push_back('\\'); - sink.push_back('f'); - count += 2; - break; - case '\n': - sink.push_back('\\'); - sink.push_back('n'); - count += 2; - break; - case '\r': - sink.push_back('\\'); - sink.push_back('r'); - count += 2; - break; - case '\t': - sink.push_back('\\'); - sink.push_back('t'); - count += 2; - break; - default: - if (escape_solidus && c == '/') - { - sink.push_back('\\'); - sink.push_back('/'); - count += 2; - } - else if (is_control_character(c) || escape_all_non_ascii) - { - // convert to codepoint - uint32_t cp; - auto r = unicode_traits::to_codepoint(it, end, cp, unicode_traits::conv_flags::strict); - if (r.ec != unicode_traits::conv_errc()) - { - JSONCONS_THROW(ser_error(json_errc::illegal_codepoint)); - } - it = r.ptr - 1; - if (is_non_ascii_codepoint(cp) || is_control_character(c)) - { - if (cp > 0xFFFF) - { - cp -= 0x10000; - uint32_t first = (cp >> 10) + 0xD800; - uint32_t second = ((cp & 0x03FF) + 0xDC00); - - sink.push_back('\\'); - sink.push_back('u'); - sink.push_back(jsoncons::detail::to_hex_character(first >> 12 & 0x000F)); - sink.push_back(jsoncons::detail::to_hex_character(first >> 8 & 0x000F)); - sink.push_back(jsoncons::detail::to_hex_character(first >> 4 & 0x000F)); - sink.push_back(jsoncons::detail::to_hex_character(first & 0x000F)); - sink.push_back('\\'); - sink.push_back('u'); - sink.push_back(jsoncons::detail::to_hex_character(second >> 12 & 0x000F)); - sink.push_back(jsoncons::detail::to_hex_character(second >> 8 & 0x000F)); - sink.push_back(jsoncons::detail::to_hex_character(second >> 4 & 0x000F)); - sink.push_back(jsoncons::detail::to_hex_character(second & 0x000F)); - count += 12; - } - else - { - sink.push_back('\\'); - sink.push_back('u'); - sink.push_back(jsoncons::detail::to_hex_character(cp >> 12 & 0x000F)); - sink.push_back(jsoncons::detail::to_hex_character(cp >> 8 & 0x000F)); - sink.push_back(jsoncons::detail::to_hex_character(cp >> 4 & 0x000F)); - sink.push_back(jsoncons::detail::to_hex_character(cp & 0x000F)); - count += 6; - } - } - else - { - sink.push_back(c); - ++count; - } - } - else - { - sink.push_back(c); - ++count; - } - break; - } - } - return count; - } - - inline - byte_string_chars_format resolve_byte_string_chars_format(byte_string_chars_format format1, - byte_string_chars_format format2, - byte_string_chars_format default_format = byte_string_chars_format::base64url) - { - byte_string_chars_format sink; - switch (format1) - { - case byte_string_chars_format::base16: - case byte_string_chars_format::base64: - case byte_string_chars_format::base64url: - sink = format1; - break; - default: - switch (format2) - { - case byte_string_chars_format::base64url: - case byte_string_chars_format::base64: - case byte_string_chars_format::base16: - sink = format2; - break; - default: // base64url - { - sink = default_format; - break; - } - } - break; - } - return sink; - } - -} // namespace detail template ,typename Allocator=std::allocator> class basic_json_encoder final : public basic_json_visitor { - static const jsoncons::basic_string_view null_constant() + static const jsoncons::basic_string_view null_literal() { static const jsoncons::basic_string_view k = JSONCONS_STRING_VIEW_CONSTANT(CharT, "null"); return k; } - static const jsoncons::basic_string_view true_constant() + static const jsoncons::basic_string_view true_literal() { static const jsoncons::basic_string_view k = JSONCONS_STRING_VIEW_CONSTANT(CharT, "true"); return k; } - static const jsoncons::basic_string_view false_constant() + static const jsoncons::basic_string_view false_literal() { static const jsoncons::basic_string_view k = JSONCONS_STRING_VIEW_CONSTANT(CharT, "false"); return k; } + + static const std::array colon; + static const std::array colon_space; + static const std::array space_colon; + static const std::array space_colon_space; + static const std::array comma; + static const std::array comma_space; + static const std::array space_comma; + static const std::array space_comma_space; + static const std::array left_brace; + static const std::array right_brace; + static const std::array left_brace_space; + static const std::array space_right_brace; + static const std::array left_bracket; + static const std::array right_bracket; + static const std::array left_bracket_space; + static const std::array space_right_bracket; public: using allocator_type = Allocator; using char_type = CharT; @@ -227,7 +83,7 @@ namespace detail { class encoding_context { container_type type_; - line_split_kind line_splits_; + line_split_kind split_kind_; bool indent_before_; bool new_line_after_; std::size_t begin_pos_{0}; @@ -236,7 +92,7 @@ namespace detail { public: encoding_context(container_type type, line_split_kind split_lines, bool indent_once, std::size_t begin_pos, std::size_t data_pos) noexcept - : type_(type), line_splits_(split_lines), indent_before_(indent_once), new_line_after_(false), + : type_(type), split_kind_(split_lines), indent_before_(indent_once), new_line_after_(false), begin_pos_(begin_pos), data_pos_(data_pos) { } @@ -292,19 +148,14 @@ namespace detail { return type_ == container_type::array; } - bool is_same_line() const - { - return line_splits_ == line_split_kind::same_line; - } - - bool is_new_line() const + line_split_kind split_kind() const { - return line_splits_ == line_split_kind::new_line; + return split_kind_; } bool is_multi_line() const { - return line_splits_ == line_split_kind::multi_line; + return split_kind_ == line_split_kind::multi_line; } bool is_indent_once() const @@ -317,17 +168,18 @@ namespace detail { Sink sink_; basic_json_encode_options options_; - jsoncons::detail::write_double fp_; + char_type indent_char_{' '}; + jsoncons::write_double fp_; std::vector stack_; int indent_amount_{0}; std::size_t column_{0}; - std::basic_string colon_str_; - std::basic_string comma_str_; - std::basic_string open_object_brace_str_; - std::basic_string close_object_brace_str_; - std::basic_string open_array_bracket_str_; - std::basic_string close_array_bracket_str_; + jsoncons::basic_string_view colon_str_; + jsoncons::basic_string_view comma_str_; + jsoncons::basic_string_view open_brace_str_; + jsoncons::basic_string_view close_brace_str_; + jsoncons::basic_string_view open_bracket_str_; + jsoncons::basic_string_view close_bracket_str_; int nesting_depth_{0}; public: @@ -346,58 +198,59 @@ namespace detail { const Allocator& alloc = Allocator()) : sink_(std::forward(sink)), options_(options), + indent_char_(options.indent_char()), fp_(options.float_format(), options.precision()), stack_(alloc) { switch (options.spaces_around_colon()) { case spaces_option::space_after: - colon_str_ = std::basic_string({':',' '}); + colon_str_ = jsoncons::basic_string_view(colon_space.data(), colon_space.size()); break; case spaces_option::space_before: - colon_str_ = std::basic_string({' ',':'}); + colon_str_ = jsoncons::basic_string_view(space_colon.data(), space_colon.size()); break; case spaces_option::space_before_and_after: - colon_str_ = std::basic_string({' ',':',' '}); + colon_str_ = jsoncons::basic_string_view(space_colon_space.data(), space_colon_space.size()); break; default: - colon_str_.push_back(':'); + colon_str_ = jsoncons::basic_string_view(colon.data(), colon.size()); break; } switch (options.spaces_around_comma()) { case spaces_option::space_after: - comma_str_ = std::basic_string({',',' '}); + comma_str_ = jsoncons::basic_string_view(comma_space.data(), colon_space.size()); break; case spaces_option::space_before: - comma_str_ = std::basic_string({' ',','}); + comma_str_ = jsoncons::basic_string_view(space_comma.data(), space_comma.size()); break; case spaces_option::space_before_and_after: - comma_str_ = std::basic_string({' ',',',' '}); + comma_str_ = jsoncons::basic_string_view(space_comma_space.data(), space_comma_space.size()); break; default: - comma_str_.push_back(','); + comma_str_ = jsoncons::basic_string_view(comma.data(), comma.size()); break; } if (options.pad_inside_object_braces()) { - open_object_brace_str_ = std::basic_string({'{', ' '}); - close_object_brace_str_ = std::basic_string({' ', '}'}); + open_brace_str_ = jsoncons::basic_string_view(left_brace_space.data(), left_brace_space.size()); + close_brace_str_ = jsoncons::basic_string_view(space_right_brace.data(), space_right_brace.size()); } else { - open_object_brace_str_.push_back('{'); - close_object_brace_str_.push_back('}'); + open_brace_str_ = jsoncons::basic_string_view(left_brace.data(), left_brace.size()); + close_brace_str_ = jsoncons::basic_string_view(right_brace.data(), right_brace.size()); } if (options.pad_inside_array_brackets()) { - open_array_bracket_str_ = std::basic_string({'[', ' '}); - close_array_bracket_str_ = std::basic_string({' ', ']'}); + open_bracket_str_ = jsoncons::basic_string_view(left_bracket_space.data(), left_bracket_space.size()); + close_bracket_str_ = jsoncons::basic_string_view(space_right_bracket.data(), space_right_bracket.size()); } else { - open_array_bracket_str_.push_back('['); - close_array_bracket_str_.push_back(']'); + open_bracket_str_ = jsoncons::basic_string_view(left_bracket.data(), left_bracket.size()); + close_bracket_str_ = jsoncons::basic_string_view(right_bracket.data(), right_bracket.size()); } } @@ -453,7 +306,9 @@ namespace detail { { if (stack_.back().is_object()) { - switch (options_.object_object_line_splits()) + line_split_kind split_kind = static_cast(options_.object_object_line_splits()) >= static_cast(stack_.back().split_kind()) ? + options_.object_object_line_splits() : stack_.back().split_kind(); + switch (split_kind) { case line_split_kind::same_line: case line_split_kind::new_line: @@ -465,12 +320,14 @@ namespace detail { default: // multi_line break; } - stack_.emplace_back(container_type::object,options_.object_object_line_splits(), false, - column_, column_+open_object_brace_str_.length()); + stack_.emplace_back(container_type::object,split_kind, false, + column_, column_+open_brace_str_.length()); } else // array { - switch (options_.array_object_line_splits()) + line_split_kind split_kind = static_cast(options_.array_object_line_splits()) >= static_cast(stack_.back().split_kind()) ? + options_.array_object_line_splits() : stack_.back().split_kind(); + switch (split_kind) { case line_split_kind::same_line: if (column_ >= options_.line_length_limit()) @@ -493,19 +350,19 @@ namespace detail { new_line(); break; } - stack_.emplace_back(container_type::object,options_.array_object_line_splits(), false, - column_, column_+open_object_brace_str_.length()); + stack_.emplace_back(container_type::object,split_kind, false, + column_, column_+open_brace_str_.length()); } } else { - stack_.emplace_back(container_type::object, options_.line_splits(), false, - column_, column_+open_object_brace_str_.length()); + stack_.emplace_back(container_type::object, options_.root_line_splits(), false, + column_, column_+open_brace_str_.length()); } indent(); - sink_.append(open_object_brace_str_.data(), open_object_brace_str_.length()); - column_ += open_object_brace_str_.length(); + sink_.append(open_brace_str_.data(), open_brace_str_.length()); + column_ += open_brace_str_.length(); JSONCONS_VISITOR_RETURN; } @@ -520,8 +377,8 @@ namespace detail { new_line(); } stack_.pop_back(); - sink_.append(close_object_brace_str_.data(), close_object_brace_str_.length()); - column_ += close_object_brace_str_.length(); + sink_.append(close_brace_str_.data(), close_brace_str_.length()); + column_ += close_brace_str_.length(); end_value(); JSONCONS_VISITOR_RETURN; @@ -543,27 +400,32 @@ namespace detail { { if (stack_.back().is_object()) { - switch (options_.object_array_line_splits()) + line_split_kind split_kind = static_cast(options_.object_array_line_splits()) >= static_cast(stack_.back().split_kind()) ? + options_.object_array_line_splits() : + stack_.back().split_kind(); + switch (split_kind) { case line_split_kind::same_line: - stack_.emplace_back(container_type::array,options_.object_array_line_splits(),false, - column_, column_ + open_array_bracket_str_.length()); + stack_.emplace_back(container_type::array,split_kind,false, + column_, column_ + open_bracket_str_.length()); break; case line_split_kind::new_line: { - stack_.emplace_back(container_type::array,options_.object_array_line_splits(),true, - column_, column_+open_array_bracket_str_.length()); + stack_.emplace_back(container_type::array,split_kind,true, + column_, column_+open_bracket_str_.length()); break; } default: // multi_line - stack_.emplace_back(container_type::array,options_.object_array_line_splits(),true, - column_, column_+open_array_bracket_str_.length()); + stack_.emplace_back(container_type::array,split_kind,true, + column_, column_+open_bracket_str_.length()); break; } } else // array { - switch (options_.array_array_line_splits()) + line_split_kind split_kind = static_cast(options_.array_array_line_splits()) >= static_cast(stack_.back().split_kind()) ? + options_.array_array_line_splits() : stack_.back().split_kind(); + switch (split_kind) { case line_split_kind::same_line: if (stack_.back().is_multi_line()) @@ -571,33 +433,32 @@ namespace detail { stack_.back().new_line_after(true); new_line(); } - stack_.emplace_back(container_type::array,options_.array_array_line_splits(), false, - column_, column_+open_array_bracket_str_.length()); + stack_.emplace_back(container_type::array,split_kind, false, + column_, column_+open_bracket_str_.length()); break; case line_split_kind::new_line: stack_.back().new_line_after(true); new_line(); - stack_.emplace_back(container_type::array,options_.array_array_line_splits(), false, - column_, column_+open_array_bracket_str_.length()); + stack_.emplace_back(container_type::array,split_kind, true, + column_, column_+open_bracket_str_.length()); break; default: // multi_line stack_.back().new_line_after(true); new_line(); - stack_.emplace_back(container_type::array,options_.array_array_line_splits(), false, - column_, column_+open_array_bracket_str_.length()); - //new_line(); + stack_.emplace_back(container_type::array,split_kind, false, + column_, column_+open_bracket_str_.length()); break; } } } else { - stack_.emplace_back(container_type::array, options_.line_splits(), false, - column_, column_+open_array_bracket_str_.length()); + stack_.emplace_back(container_type::array, options_.root_line_splits(), false, + column_, column_+open_bracket_str_.length()); } indent(); - sink_.append(open_array_bracket_str_.data(), open_array_bracket_str_.length()); - column_ += open_array_bracket_str_.length(); + sink_.append(open_bracket_str_.data(), open_bracket_str_.length()); + column_ += open_bracket_str_.length(); JSONCONS_VISITOR_RETURN; } @@ -612,8 +473,8 @@ namespace detail { new_line(); } stack_.pop_back(); - sink_.append(close_array_bracket_str_.data(), close_array_bracket_str_.length()); - column_ += close_array_bracket_str_.length(); + sink_.append(close_bracket_str_.data(), close_bracket_str_.length()); + column_ += close_bracket_str_.length(); end_value(); JSONCONS_VISITOR_RETURN; } @@ -664,8 +525,8 @@ namespace detail { } } - sink_.append(null_constant().data(), null_constant().size()); - column_ += null_constant().size(); + sink_.append(null_literal().data(), null_literal().size()); + column_ += null_literal().size(); end_value(); JSONCONS_VISITOR_RETURN; @@ -693,30 +554,36 @@ namespace detail { void write_string(const string_view_type& sv, semantic_tag tag, const ser_context&, std::error_code&) { - switch (tag) + if (JSONCONS_LIKELY(tag == semantic_tag::noesc && !options_.escape_all_non_ascii() && !options_.escape_solidus())) { - case semantic_tag::bigint: - write_bigint_value(sv); - break; - case semantic_tag::bigdec: + //std::cout << "noesc\n"; + sink_.push_back('\"'); + const CharT* begin = sv.data(); + const CharT* end = begin + sv.length(); + for (const CharT* it = begin; it != end; ++it) { - // output lossless number - if (options_.bignum_format() == bignum_format_kind::raw) - { - write_bigint_value(sv); - break; - } - JSONCONS_FALLTHROUGH; - } - default: - { - sink_.push_back('\"'); - std::size_t length = jsoncons::detail::escape_string(sv.data(), sv.length(),options_.escape_all_non_ascii(),options_.escape_solidus(),sink_); - sink_.push_back('\"'); - column_ += (length+2); - break; + sink_.push_back(*it); } + sink_.push_back('\"'); + column_ += (sv.length()+2); + } + else if (tag == semantic_tag::bigint) + { + write_bignum_value(sv); + } + else if (tag == semantic_tag::bigdec && options_.bignum_format() == bignum_format_kind::raw) + { + write_bignum_value(sv); } + else + { + //if (tag != semantic_tag::bigdec) + // std::cout << "esc\n"; + sink_.push_back('\"'); + std::size_t length = jsoncons::detail::escape_string(sv.data(), sv.length(),options_.escape_all_non_ascii(),options_.escape_solidus(),sink_); + sink_.push_back('\"'); + column_ += (length+2); + } } JSONCONS_VISITOR_RETURN_TYPE visit_byte_string(const byte_string_view& b, @@ -761,7 +628,7 @@ namespace detail { case byte_string_chars_format::base16: { sink_.push_back('\"'); - std::size_t length = encode_base16(b.begin(),b.end(),sink_); + std::size_t length = bytes_to_base16(b.begin(),b.end(),sink_); sink_.push_back('\"'); column_ += (length + 2); break; @@ -769,7 +636,7 @@ namespace detail { case byte_string_chars_format::base64: { sink_.push_back('\"'); - std::size_t length = encode_base64(b.begin(), b.end(), sink_); + std::size_t length = bytes_to_base64(b.begin(), b.end(), sink_); sink_.push_back('\"'); column_ += (length + 2); break; @@ -777,7 +644,7 @@ namespace detail { case byte_string_chars_format::base64url: { sink_.push_back('\"'); - std::size_t length = encode_base64url(b.begin(),b.end(),sink_); + std::size_t length = bytes_to_base64url(b.begin(),b.end(),sink_); sink_.push_back('\"'); column_ += (length + 2); break; @@ -824,8 +691,8 @@ namespace detail { } else { - sink_.append(null_constant().data(), null_constant().size()); - column_ += null_constant().size(); + sink_.append(null_literal().data(), null_literal().size()); + column_ += null_literal().size(); } } else if (value == std::numeric_limits::infinity()) @@ -841,8 +708,8 @@ namespace detail { } else { - sink_.append(null_constant().data(), null_constant().size()); - column_ += null_constant().size(); + sink_.append(null_literal().data(), null_literal().size()); + column_ += null_literal().size(); } } else @@ -858,8 +725,8 @@ namespace detail { } else { - sink_.append(null_constant().data(), null_constant().size()); - column_ += null_constant().size(); + sink_.append(null_literal().data(), null_literal().size()); + column_ += null_literal().size(); } } } @@ -889,7 +756,7 @@ namespace detail { break_line(); } } - std::size_t length = jsoncons::detail::from_integer(value, sink_); + std::size_t length = jsoncons::from_integer(value, sink_); column_ += length; end_value(); JSONCONS_VISITOR_RETURN; @@ -911,7 +778,7 @@ namespace detail { break_line(); } } - std::size_t length = jsoncons::detail::from_integer(value, sink_); + std::size_t length = jsoncons::from_integer(value, sink_); column_ += length; end_value(); JSONCONS_VISITOR_RETURN; @@ -933,13 +800,13 @@ namespace detail { if (value) { - sink_.append(true_constant().data(), true_constant().size()); - column_ += true_constant().size(); + sink_.append(true_literal().data(), true_literal().size()); + column_ += true_literal().size(); } else { - sink_.append(false_constant().data(), false_constant().size()); - column_ += false_constant().size(); + sink_.append(false_literal().data(), false_literal().size()); + column_ += false_literal().size(); } end_value(); @@ -963,7 +830,7 @@ namespace detail { } } - void write_bigint_value(const string_view_type& sv) + void write_bignum_value(const string_view_type& sv) { switch (options_.bignum_format()) { @@ -975,7 +842,7 @@ namespace detail { } case bignum_format_kind::base64: { - bigint n = bigint::from_string(sv.data(), sv.length()); + bigint n(sv.data(), sv.length()); bool is_neg = n < 0; if (is_neg) { @@ -991,14 +858,14 @@ namespace detail { sink_.push_back('~'); ++column_; } - std::size_t length = encode_base64(v.begin(), v.end(), sink_); + std::size_t length = bytes_to_base64(v.begin(), v.end(), sink_); sink_.push_back('\"'); column_ += (length+2); break; } case bignum_format_kind::base64url: { - bigint n = bigint::from_string(sv.data(), sv.length()); + bigint n(sv.data(), sv.length()); bool is_neg = n < 0; if (is_neg) { @@ -1014,7 +881,7 @@ namespace detail { sink_.push_back('~'); ++column_; } - std::size_t length = encode_base64url(v.begin(), v.end(), sink_); + std::size_t length = bytes_to_base64url(v.begin(), v.end(), sink_); sink_.push_back('\"'); column_ += (length+2); break; @@ -1040,12 +907,12 @@ namespace detail { void indent() { - indent_amount_ += static_cast(options_.indent_size()); + indent_amount_ += static_cast(options_.indent_size()); } void unindent() { - indent_amount_ -= static_cast(options_.indent_size()); + indent_amount_ -= static_cast(options_.indent_size()); } void new_line() @@ -1053,7 +920,7 @@ namespace detail { sink_.append(options_.new_line_chars().data(),options_.new_line_chars().length()); for (int i = 0; i < indent_amount_; ++i) { - sink_.push_back(' '); + sink_.push_back(indent_char_); } column_ = indent_amount_; } @@ -1075,20 +942,56 @@ namespace detail { } }; + template + const std::array basic_json_encoder::colon = {':'}; + template + const std::array basic_json_encoder::colon_space = {':', ' '}; + template + const std::array basic_json_encoder::space_colon = {' ', ':'}; + template + const std::array basic_json_encoder::space_colon_space = {' ', ':', ' '}; + + template + const std::array basic_json_encoder::comma = {','}; + template + const std::array basic_json_encoder::comma_space = {',', ' '}; + template + const std::array basic_json_encoder::space_comma = {' ', ','}; + template + const std::array basic_json_encoder::space_comma_space = {' ', ',', ' '}; + + template + const std::array basic_json_encoder::left_brace = {'{'}; + template + const std::array basic_json_encoder::right_brace = {'}'}; + template + const std::array basic_json_encoder::left_brace_space = {'{', ' '}; + template + const std::array basic_json_encoder::space_right_brace = {' ', '}'}; + + template + const std::array basic_json_encoder::left_bracket = {'['}; + template + const std::array basic_json_encoder::right_bracket = {']'}; + template + const std::array basic_json_encoder::left_bracket_space = {'[', ' '}; + template + const std::array basic_json_encoder::space_right_bracket = {' ', ']'}; + template ,typename Allocator=std::allocator> class basic_compact_json_encoder final : public basic_json_visitor { - static const std::array& null_constant() + static const std::array& null_literal() { static constexpr std::array k{{'n','u','l','l'}}; return k; } - static const std::array& true_constant() + static const std::array& true_literal() { static constexpr std::array k{{'t','r','u','e'}}; return k; } - static const std::array& false_constant() + static const std::array& false_literal() { static constexpr std::array k{{'f','a','l','s','e'}}; return k; @@ -1132,7 +1035,7 @@ namespace detail { Sink sink_; basic_json_encode_options options_; - jsoncons::detail::write_double fp_; + jsoncons::write_double fp_; std::vector stack_; int nesting_depth_; public: @@ -1275,7 +1178,7 @@ namespace detail { sink_.push_back(','); } - sink_.append(null_constant().data(), null_constant().size()); + sink_.append(null_literal().data(), null_literal().size()); if (!stack_.empty()) { @@ -1284,7 +1187,7 @@ namespace detail { JSONCONS_VISITOR_RETURN; } - void write_bigint_value(const string_view_type& sv) + void write_bignum_value(const string_view_type& sv) { switch (options_.bignum_format()) { @@ -1295,7 +1198,7 @@ namespace detail { } case bignum_format_kind::base64: { - bigint n = bigint::from_string(sv.data(), sv.length()); + bigint n(sv.data(), sv.length()); bool is_neg = n < 0; if (is_neg) { @@ -1310,13 +1213,13 @@ namespace detail { { sink_.push_back('~'); } - encode_base64(v.begin(), v.end(), sink_); + bytes_to_base64(v.begin(), v.end(), sink_); sink_.push_back('\"'); break; } case bignum_format_kind::base64url: { - bigint n = bigint::from_string(sv.data(), sv.length()); + bigint n(sv.data(), sv.length()); bool is_neg = n < 0; if (is_neg) { @@ -1331,7 +1234,7 @@ namespace detail { { sink_.push_back('~'); } - encode_base64url(v.begin(), v.end(), sink_); + bytes_to_base64url(v.begin(), v.end(), sink_); sink_.push_back('\"'); break; } @@ -1345,36 +1248,14 @@ namespace detail { } } - JSONCONS_VISITOR_RETURN_TYPE visit_string(const string_view_type& sv, semantic_tag tag, const ser_context&, std::error_code&) final + JSONCONS_VISITOR_RETURN_TYPE visit_string(const string_view_type& sv, semantic_tag tag, const ser_context& context, std::error_code& ec) final { if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0) { sink_.push_back(','); } - switch (tag) - { - case semantic_tag::bigint: - write_bigint_value(sv); - break; - case semantic_tag::bigdec: - { - // output lossless number - if (options_.bignum_format() == bignum_format_kind::raw) - { - write_bigint_value(sv); - break; - } - JSONCONS_FALLTHROUGH; - } - default: - { - sink_.push_back('\"'); - jsoncons::detail::escape_string(sv.data(), sv.length(),options_.escape_all_non_ascii(),options_.escape_solidus(),sink_); - sink_.push_back('\"'); - break; - } - } + write_string(sv, tag, context, ec); if (!stack_.empty()) { @@ -1385,35 +1266,40 @@ namespace detail { void write_string(const string_view_type& sv, semantic_tag tag, const ser_context&, std::error_code&) { - switch (tag) + if (JSONCONS_LIKELY(tag == semantic_tag::noesc && !options_.escape_all_non_ascii() && !options_.escape_solidus())) { - case semantic_tag::bigint: - write_bigint_value(sv); - break; - case semantic_tag::bigdec: - { - // output lossless number - if (options_.bignum_format() == bignum_format_kind::raw) - { - write_bigint_value(sv); - break; - } - JSONCONS_FALLTHROUGH; - } - default: + //std::cout << "noesc\n"; + sink_.push_back('\"'); + const CharT* begin = sv.data(); + const CharT* end = begin + sv.length(); + for (const CharT* it = begin; it != end; ++it) { - sink_.push_back('\"'); - jsoncons::detail::escape_string(sv.data(), sv.length(),options_.escape_all_non_ascii(),options_.escape_solidus(),sink_); - sink_.push_back('\"'); - break; + sink_.push_back(*it); } + sink_.push_back('\"'); } + else if (tag == semantic_tag::bigint) + { + write_bignum_value(sv); + } + else if (tag == semantic_tag::bigdec && options_.bignum_format() == bignum_format_kind::raw) + { + write_bignum_value(sv); + } + else + { + //if (tag != semantic_tag::bigdec) + // std::cout << "esc\n"; + sink_.push_back('\"'); + jsoncons::detail::escape_string(sv.data(), sv.length(),options_.escape_all_non_ascii(),options_.escape_solidus(),sink_); + sink_.push_back('\"'); + } } JSONCONS_VISITOR_RETURN_TYPE visit_byte_string(const byte_string_view& b, - semantic_tag tag, - const ser_context&, - std::error_code&) final + semantic_tag tag, + const ser_context&, + std::error_code&) final { if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0) { @@ -1445,21 +1331,21 @@ namespace detail { case byte_string_chars_format::base16: { sink_.push_back('\"'); - encode_base16(b.begin(),b.end(),sink_); + bytes_to_base16(b.begin(),b.end(),sink_); sink_.push_back('\"'); break; } case byte_string_chars_format::base64: { sink_.push_back('\"'); - encode_base64(b.begin(), b.end(), sink_); + bytes_to_base64(b.begin(), b.end(), sink_); sink_.push_back('\"'); break; } case byte_string_chars_format::base64url: { sink_.push_back('\"'); - encode_base64url(b.begin(),b.end(),sink_); + bytes_to_base64url(b.begin(),b.end(),sink_); sink_.push_back('\"'); break; } @@ -1500,7 +1386,7 @@ namespace detail { } else { - sink_.append(null_constant().data(), null_constant().size()); + sink_.append(null_literal().data(), null_literal().size()); } } else if (value == std::numeric_limits::infinity()) @@ -1515,7 +1401,7 @@ namespace detail { } else { - sink_.append(null_constant().data(), null_constant().size()); + sink_.append(null_literal().data(), null_literal().size()); } } else @@ -1530,7 +1416,7 @@ namespace detail { } else { - sink_.append(null_constant().data(), null_constant().size()); + sink_.append(null_literal().data(), null_literal().size()); } } } @@ -1555,7 +1441,7 @@ namespace detail { { sink_.push_back(','); } - jsoncons::detail::from_integer(value, sink_); + jsoncons::from_integer(value, sink_); if (!stack_.empty()) { stack_.back().increment_count(); @@ -1572,7 +1458,7 @@ namespace detail { { sink_.push_back(','); } - jsoncons::detail::from_integer(value, sink_); + jsoncons::from_integer(value, sink_); if (!stack_.empty()) { stack_.back().increment_count(); @@ -1589,11 +1475,11 @@ namespace detail { if (value) { - sink_.append(true_constant().data(), true_constant().size()); + sink_.append(true_literal().data(), true_literal().size()); } else { - sink_.append(false_constant().data(), false_constant().size()); + sink_.append(false_literal().data(), false_literal().size()); } if (!stack_.empty()) diff --git a/include/jsoncons/json_encoders.hpp b/include/jsoncons/json_encoders.hpp new file mode 100644 index 0000000..f8fe88e --- /dev/null +++ b/include/jsoncons/json_encoders.hpp @@ -0,0 +1,182 @@ +// Copyright 2013-2026 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSON_ENCODERS_HPP +#define JSONCONS_JSON_ENCODERS_HPP + +#include + +#include +#include +#include +#include + +namespace jsoncons { +namespace detail { + +inline +bool is_control_character(uint32_t c) +{ + return c <= 0x1F || c == 0x7f; +} + +inline +bool is_non_ascii_codepoint(uint32_t cp) +{ + return cp >= 0x80; +} +template +std::size_t escape_string(const CharT* s, std::size_t length, + bool escape_all_non_ascii, bool escape_solidus, + Sink& sink) +{ + std::size_t count = 0; + const CharT* begin = s; + const CharT* end = s + length; + for (const CharT* it = begin; it != end; ++it) + { + CharT c = *it; + switch (c) + { + case '\\': + sink.push_back('\\'); + sink.push_back('\\'); + count += 2; + break; + case '"': + sink.push_back('\\'); + sink.push_back('\"'); + count += 2; + break; + case '\b': + sink.push_back('\\'); + sink.push_back('b'); + count += 2; + break; + case '\f': + sink.push_back('\\'); + sink.push_back('f'); + count += 2; + break; + case '\n': + sink.push_back('\\'); + sink.push_back('n'); + count += 2; + break; + case '\r': + sink.push_back('\\'); + sink.push_back('r'); + count += 2; + break; + case '\t': + sink.push_back('\\'); + sink.push_back('t'); + count += 2; + break; + default: + if (escape_solidus && c == '/') + { + sink.push_back('\\'); + sink.push_back('/'); + count += 2; + } + else if (is_control_character(c) || escape_all_non_ascii) + { + // convert to codepoint + uint32_t cp; + auto r = unicode_traits::to_codepoint(it, end, cp, unicode_traits::conv_flags::strict); + if (r.ec != unicode_traits::conv_errc()) + { + JSONCONS_THROW(ser_error(json_errc::illegal_codepoint)); + } + it = r.ptr - 1; + if (is_non_ascii_codepoint(cp) || is_control_character(c)) + { + if (cp > 0xFFFF) + { + cp -= 0x10000; + uint32_t first = (cp >> 10) + 0xD800; + uint32_t second = ((cp & 0x03FF) + 0xDC00); + + sink.push_back('\\'); + sink.push_back('u'); + sink.push_back(jsoncons::to_hex_character(first >> 12 & 0x000F)); + sink.push_back(jsoncons::to_hex_character(first >> 8 & 0x000F)); + sink.push_back(jsoncons::to_hex_character(first >> 4 & 0x000F)); + sink.push_back(jsoncons::to_hex_character(first & 0x000F)); + sink.push_back('\\'); + sink.push_back('u'); + sink.push_back(jsoncons::to_hex_character(second >> 12 & 0x000F)); + sink.push_back(jsoncons::to_hex_character(second >> 8 & 0x000F)); + sink.push_back(jsoncons::to_hex_character(second >> 4 & 0x000F)); + sink.push_back(jsoncons::to_hex_character(second & 0x000F)); + count += 12; + } + else + { + sink.push_back('\\'); + sink.push_back('u'); + sink.push_back(jsoncons::to_hex_character(cp >> 12 & 0x000F)); + sink.push_back(jsoncons::to_hex_character(cp >> 8 & 0x000F)); + sink.push_back(jsoncons::to_hex_character(cp >> 4 & 0x000F)); + sink.push_back(jsoncons::to_hex_character(cp & 0x000F)); + count += 6; + } + } + else + { + sink.push_back(c); + ++count; + } + } + else + { + sink.push_back(c); + ++count; + } + break; + } + } + return count; +} + +inline +byte_string_chars_format resolve_byte_string_chars_format(byte_string_chars_format format1, + byte_string_chars_format format2, + byte_string_chars_format default_format = byte_string_chars_format::base64url) +{ + byte_string_chars_format sink; + switch (format1) + { + case byte_string_chars_format::base16: + case byte_string_chars_format::base64: + case byte_string_chars_format::base64url: + sink = format1; + break; + default: + switch (format2) + { + case byte_string_chars_format::base64url: + case byte_string_chars_format::base64: + case byte_string_chars_format::base16: + sink = format2; + break; + default: // base64url + { + sink = default_format; + break; + } + } + break; + } + return sink; +} + +} // namespace detail +} // namespace jsoncons + +#endif // JSONCONS_JSON_ENCODERS_HPP + diff --git a/include/jsoncons/json_error.hpp b/include/jsoncons/json_error.hpp index 6c2c0a9..a6370cc 100644 --- a/include/jsoncons/json_error.hpp +++ b/include/jsoncons/json_error.hpp @@ -1,4 +1,4 @@ -/// Copyright 2013-2025 Daniel Parker +/// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -47,7 +47,8 @@ namespace jsoncons { illegal_codepoint, illegal_surrogate_value, unpaired_high_surrogate, - illegal_unicode_character + illegal_unicode_character, + unexpected_character }; class json_error_category_impl @@ -122,6 +123,8 @@ namespace jsoncons { return "Expected low surrogate following the high surrogate"; case json_errc::illegal_unicode_character: return "Illegal unicode character"; + case json_errc::unexpected_character: + return "Unexpected character"; default: return "Unknown JSON parser error"; } diff --git a/include/jsoncons/json_exception.hpp b/include/jsoncons/json_exception.hpp index 20b46a6..0c05bf1 100644 --- a/include/jsoncons/json_exception.hpp +++ b/include/jsoncons/json_exception.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -153,74 +153,104 @@ namespace jsoncons { } }; - class ser_error : public std::system_error, public virtual json_exception + class ser_error : public std::exception, public virtual json_exception { - std::size_t line_number_{0}; - std::size_t column_number_{0}; - mutable std::string what_; + std::string err_; + std::error_code ec_; + std::size_t line_{0}; + std::size_t column_{0}; public: ser_error(std::error_code ec) - : std::system_error(ec) + : ec_(ec) { + err_ = to_what_arg(ec); } ser_error(std::error_code ec, const std::string& what_arg) - : std::system_error(ec, what_arg) + : ec_(ec) + { + err_ = to_what_arg(ec, what_arg.c_str()); + } + ser_error(std::error_code ec, const char* what_arg) + : ec_(ec) { + err_ = to_what_arg(ec, what_arg); } ser_error(std::error_code ec, std::size_t position) - : std::system_error(ec), column_number_(position) + : ec_(ec), column_(position) + { + err_ = to_what_arg(ec, "", 0, position); + } + ser_error(std::error_code ec, const std::string& what_arg, std::size_t position) + : ec_(ec), column_(position) { + err_ = to_what_arg(ec, what_arg.c_str(), 0, position); + } + ser_error(std::error_code ec, const char* what_arg, std::size_t position) + : ec_(ec), column_(position) + { + err_ = to_what_arg(ec, what_arg, 0, position); } ser_error(std::error_code ec, std::size_t line, std::size_t column) - : std::system_error(ec), line_number_(line), column_number_(column) + : ec_(ec), line_(line), column_(column) { + err_ = to_what_arg(ec, "", line, column); + } + ser_error(std::error_code ec, const std::string& what_arg, std::size_t line, std::size_t column) + : ec_(ec), line_(line), column_(column) + { + err_ = to_what_arg(ec, what_arg.c_str(), line, column); + } + ser_error(std::error_code ec, const char* what_arg, std::size_t line, std::size_t column) + : ec_(ec), line_(line), column_(column) + { + err_ = to_what_arg(ec, what_arg, line, column); } ser_error(const ser_error& other) = default; - ser_error(ser_error&& other) = default; + ser_error& operator=(const ser_error& other) = default; - const char* what() const noexcept override + const char* what() const noexcept final { - if (what_.empty()) - { - JSONCONS_TRY - { - what_.append(std::system_error::what()); - if (line_number_ != 0 && column_number_ != 0) - { - what_.append(" at line "); - what_.append(std::to_string(line_number_)); - what_.append(" and column "); - what_.append(std::to_string(column_number_)); - } - else if (column_number_ != 0) - { - what_.append(" at position "); - what_.append(std::to_string(column_number_)); - } - return what_.c_str(); - } - JSONCONS_CATCH(...) - { - return std::system_error::what(); - } - } - else - { - return what_.c_str(); - } + return err_.c_str(); + } + + std::error_code code() const + { + return ec_; } std::size_t line() const noexcept { - return line_number_; + return line_; } std::size_t column() const noexcept { - return column_number_; + return column_; + } + private: + static std::string to_what_arg(std::error_code ec, const char* s="", std::size_t line=0, std::size_t column=0) + { + std::string what_arg(s); + if (!what_arg.empty()) + { + what_arg.append(": "); + } + what_arg.append(ec.message()); + if (line != 0 && column != 0) + { + what_arg.append(" at line "); + what_arg.append(std::to_string(line)); + what_arg.append(" and column "); + what_arg.append(std::to_string(column)); + } + else if (column != 0) + { + what_arg.append(" at position "); + what_arg.append(std::to_string(column)); + } + return what_arg; } - }; } // namespace jsoncons diff --git a/include/jsoncons/json_filter.hpp b/include/jsoncons/json_filter.hpp index 58f5236..00e0f48 100644 --- a/include/jsoncons/json_filter.hpp +++ b/include/jsoncons/json_filter.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include #include diff --git a/include/jsoncons/json_fwd.hpp b/include/jsoncons/json_fwd.hpp index 97bf090..20fa4e1 100644 --- a/include/jsoncons/json_fwd.hpp +++ b/include/jsoncons/json_fwd.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/include/jsoncons/json_object.hpp b/include/jsoncons/json_object.hpp index 33170ac..6f7a865 100644 --- a/include/jsoncons/json_object.hpp +++ b/include/jsoncons/json_object.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -81,10 +81,6 @@ namespace jsoncons { value_type value_; public: - key_value() noexcept - { - } - template key_value(key_type&& name, Args&& ... args) : key_(std::move(name)), value_(std::forward(args)...) @@ -249,20 +245,36 @@ namespace std namespace jsoncons { template - struct get_key_value + struct make_key_value { using key_value_type = key_value; template - key_value_type operator()(const std::pair& p) // Remove + key_value_type operator()(const std::pair& p) { - return key_value_type(p.first,p.second); + return key_value_type(KeyT(p.first),p.second); } + + template + key_value_type operator()(const std::pair& p, const Alloc& alloc) + { + return key_value_type(jsoncons::make_obj_using_allocator(alloc, p.first), + p.second, alloc); + } + template key_value_type operator()(std::pair&& p) { return key_value_type(std::forward(p.first),std::forward(p.second)); } + + template + key_value_type operator()(std::pair&& p, const Alloc& alloc) + { + return key_value_type(jsoncons::make_obj_using_allocator(alloc, std::forward(p.first)), + std::forward(p.second), alloc); + } + template const key_value_type& operator()(const key_value& p) { @@ -365,7 +377,8 @@ namespace jsoncons { members_.reserve(count); for (auto it = first; it != last; ++it) { - members_.emplace_back(key_type((*it).first,get_allocator()), (*it).second); + auto kv = make_key_value()(*it); + members_.emplace_back(std::move(kv)); } std::stable_sort(members_.begin(),members_.end(), [](const key_value_type& a, const key_value_type& b) -> bool {return a.key().compare(b.key()) < 0;}); @@ -375,8 +388,7 @@ namespace jsoncons { } template - sorted_json_object(InputIt first, InputIt last, - const allocator_type& alloc) + sorted_json_object(InputIt first, InputIt last, const allocator_type& alloc) : allocator_holder(alloc), members_(key_value_allocator_type(alloc)) { @@ -384,7 +396,8 @@ namespace jsoncons { members_.reserve(count); for (auto it = first; it != last; ++it) { - members_.emplace_back(key_type((*it).first.c_str(), (*it).first.size(), get_allocator()), (*it).second); + auto kv = make_key_value()(*it, alloc); + members_.emplace_back(std::move(kv)); } std::stable_sort(members_.begin(), members_.end(), [](const key_value_type& a, const key_value_type& b) -> bool {return a.key().compare(b.key()) < 0;}); @@ -838,7 +851,7 @@ namespace jsoncons { { auto pos = std::lower_bound(members_.begin(),members_.end(), (*it).key(), Comp()); - if (pos == members_.end() ) + if (pos == members_.end()) { members_.emplace_back(*it); } @@ -874,7 +887,7 @@ namespace jsoncons { pos = std::lower_bound(members_.begin(),members_.end(), (*it).key(), Comp()); } - if (pos == members_.end() ) + if (pos == members_.end()) { members_.emplace_back(*it); hint = members_.begin() + (members_.size() - 1); @@ -904,7 +917,7 @@ namespace jsoncons { { auto pos = std::lower_bound(members_.begin(),members_.end(), (*it).key(), Comp()); - if (pos == members_.end() ) + if (pos == members_.end()) { members_.emplace_back(*it); } @@ -940,7 +953,7 @@ namespace jsoncons { pos = std::lower_bound(members_.begin(),members_.end(), (*it).key(), Comp()); } - if (pos == members_.end() ) + if (pos == members_.end()) { members_.emplace_back(*it); hint = members_.begin() + (members_.size() - 1); @@ -1075,10 +1088,12 @@ namespace jsoncons { template order_preserving_json_object(InputIt first, InputIt last) { + std::size_t count = std::distance(first,last); + members_.reserve(count); std::unordered_set keys; for (auto it = first; it != last; ++it) { - auto kv = get_key_value()(*it); + auto kv = make_key_value()(*it); if (keys.find(kv.key()) == keys.end()) { keys.emplace(kv.key()); @@ -1088,15 +1103,14 @@ namespace jsoncons { } template - order_preserving_json_object(InputIt first, InputIt last, - const allocator_type& alloc) + order_preserving_json_object(InputIt first, InputIt last, const allocator_type& alloc) : allocator_holder(alloc), members_(key_value_allocator_type(alloc)) { std::unordered_set keys; for (auto it = first; it != last; ++it) { - auto kv = get_key_value()(*it); + auto kv = make_key_value()(*it, alloc); if (keys.find(kv.key()) == keys.end()) { keys.emplace(kv.key()); @@ -1343,7 +1357,7 @@ namespace jsoncons { { for (auto it = first; it != last; ++it) { - members_.emplace_back(get_key_value()(*it)); + members_.emplace_back(make_key_value()(*it)); } } @@ -1452,7 +1466,7 @@ namespace jsoncons { for (; it != end; ++it) { auto pos = find((*it).key()); - if (pos == members_.end() ) + if (pos == members_.end()) { try_emplace((*it).key(),std::move((*it).value())); } @@ -1517,7 +1531,7 @@ namespace jsoncons { for (; it != end; ++it) { auto pos = find((*it).key()); - if (pos == members_.end() ) + if (pos == members_.end()) { insert_or_assign((*it).key(),std::move((*it).value())); } diff --git a/include/jsoncons/json_options.hpp b/include/jsoncons/json_options.hpp index ed6c8ac..f974077 100644 --- a/include/jsoncons/json_options.hpp +++ b/include/jsoncons/json_options.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,7 +15,7 @@ #include #include -#include +#include namespace jsoncons { @@ -23,7 +23,7 @@ enum class float_chars_format : uint8_t {general,fixed,scientific,hex}; enum class indenting : uint8_t {no_indent = 0, indent = 1}; -enum class line_split_kind : uint8_t {same_line=1, new_line, multi_line}; +enum class line_split_kind : uint8_t {multi_line=0, new_line=1, same_line=2}; enum class bignum_format_kind : uint8_t {raw, #if !defined(JSONCONS_NO_DEPRECATED) @@ -50,6 +50,8 @@ struct default_json_parsing } }; +#if !defined(JSONCONS_NO_DEPRECATED) + struct strict_json_parsing { bool operator()(json_errc, const ser_context&) noexcept @@ -66,6 +68,8 @@ struct allow_trailing_commas } }; +#endif + template class basic_json_options; @@ -253,6 +257,7 @@ class basic_json_decode_options : public virtual basic_json_options_common err_handler_; @@ -267,6 +272,7 @@ class basic_json_decode_options : public virtual basic_json_options_common& err_handler() const { return err_handler_; } - +#endif }; template @@ -317,7 +328,7 @@ class basic_json_encode_options : public virtual basic_json_options_common, using basic_json_decode_options::neginf_to_num; using basic_json_decode_options::lossless_number; + using basic_json_decode_options::lossless_bignum; using basic_json_decode_options::allow_comments; using basic_json_decode_options::allow_trailing_comma; +#if !defined(JSONCONS_NO_DEPRECATED) using basic_json_decode_options::err_handler; - +#endif using basic_json_encode_options::byte_string_format; using basic_json_encode_options::bignum_format; + +#if !defined(JSONCONS_NO_DEPRECATED) using basic_json_encode_options::line_splits; +#endif + using basic_json_encode_options::root_line_splits; using basic_json_encode_options::object_object_line_splits; using basic_json_encode_options::array_object_line_splits; using basic_json_encode_options::object_array_line_splits; @@ -564,12 +592,16 @@ class basic_json_options final: public basic_json_decode_options, #if !defined(JSONCONS_NO_DEPRECATED) JSONCONS_DEPRECATED_MSG("Instead, use bignum_format") - basic_json_options& bigint_format(bignum_format_kind value) {this->bignum_format_ = value; return *this;} + basic_json_options& bigint_format(bignum_format_kind value) {this->bignum_format_ = value; return *this;} #endif - basic_json_options& bignum_format(bignum_format_kind value) {this->bignum_format_ = value; return *this;} + basic_json_options& bignum_format(bignum_format_kind value) {this->bignum_format_ = value; return *this;} - basic_json_options& line_splits(line_split_kind value) {this->line_splits_ = value; return *this;} +#if !defined(JSONCONS_NO_DEPRECATED) + basic_json_options& line_splits(line_split_kind value) {this->root_line_splits_ = value; return *this;} +#endif + + basic_json_options& root_line_splits(line_split_kind value) {this->root_line_splits_ = value; return *this;} basic_json_options& object_object_line_splits(line_split_kind value) {this->object_object_line_splits_ = value; return *this;} @@ -597,6 +629,12 @@ class basic_json_options final: public basic_json_decode_options, return *this; } + basic_json_options& indent_char(char_type value) + { + this->indent_char_ = value; + return *this; + } + basic_json_options& pad_inside_object_braces(bool value) { this->pad_inside_object_braces_ = value; @@ -621,6 +659,12 @@ class basic_json_options final: public basic_json_decode_options, return *this; } + basic_json_options& lossless_bignum(bool value) + { + this->lossless_bignum_ = value; + return *this; + } + basic_json_options& allow_comments(bool value) { this->allow_comments_ = value; @@ -633,12 +677,13 @@ class basic_json_options final: public basic_json_decode_options, return *this; } +#if !defined(JSONCONS_NO_DEPRECATED) basic_json_options& err_handler(const std::function& value) { this->err_handler_ = value; return *this; } - +#endif basic_json_options& line_length_limit(std::size_t value) { this->line_length_limit_ = value; diff --git a/include/jsoncons/json_parser.hpp b/include/jsoncons/json_parser.hpp index 63ea549..471ee88 100644 --- a/include/jsoncons/json_parser.hpp +++ b/include/jsoncons/json_parser.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -19,7 +19,7 @@ #include #include -#include +#include #include #include #include @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include #define JSONCONS_ILLEGAL_CONTROL_CHARACTER \ @@ -103,7 +103,7 @@ enum class parse_number_state : uint8_t exp3 }; -template > +template > class basic_json_parser : public ser_context { public: @@ -120,17 +120,18 @@ class basic_json_parser : public ser_context } }; - using temp_allocator_type = TempAllocator; + using temp_allocator_type = TempAlloc; using char_allocator_type = typename std::allocator_traits:: template rebind_alloc; using parse_state_allocator_type = typename std::allocator_traits:: template rebind_alloc; - static constexpr std::size_t initial_string_buffer_capacity = 256; + static constexpr std::size_t initial_buffer_capacity = 256; static constexpr int default_initial_stack_capacity = 66; int max_nesting_depth_; bool allow_trailing_comma_; bool allow_comments_; bool lossless_number_; + bool lossless_bignum_; std::function err_handler_; int level_{0}; @@ -140,8 +141,7 @@ class basic_json_parser : public ser_context std::size_t position_{0}; std::size_t mark_position_{0}; std::size_t begin_position_{0}; - const char_type* begin_input_{nullptr}; - const char_type* end_input_{nullptr}; + const char_type* input_end_{nullptr}; const char_type* input_ptr_{nullptr}; parse_state state_{parse_state::start}; parse_string_state string_state_{}; @@ -151,8 +151,8 @@ class basic_json_parser : public ser_context bool cursor_mode_{false}; int mark_level_{0}; - std::basic_string,char_allocator_type> string_buffer_; - jsoncons::detail::chars_to to_double_; + semantic_tag escape_tag_; + std::basic_string,char_allocator_type> buffer_; std::vector state_stack_; std::vector,double>> string_double_map_; @@ -162,35 +162,71 @@ class basic_json_parser : public ser_context basic_json_parser& operator=(const basic_json_parser&) = delete; public: - basic_json_parser(const TempAllocator& temp_alloc = TempAllocator()) - : basic_json_parser(basic_json_decode_options(), default_json_parsing(), temp_alloc) + + basic_json_parser() + : basic_json_parser(basic_json_decode_options()) { } - basic_json_parser(std::function err_handler, - const TempAllocator& temp_alloc = TempAllocator()) - : basic_json_parser(basic_json_decode_options(), err_handler, temp_alloc) + explicit basic_json_parser(const TempAlloc& temp_alloc) + : basic_json_parser(basic_json_decode_options(), temp_alloc) { } - basic_json_parser(const basic_json_decode_options& options, - const TempAllocator& temp_alloc = TempAllocator()) - : basic_json_parser(options, options.err_handler(), temp_alloc) + basic_json_parser(const basic_json_decode_options& options, + const TempAlloc& temp_alloc = TempAlloc()) + : max_nesting_depth_(options.max_nesting_depth()), + allow_trailing_comma_(options.allow_trailing_comma()), + allow_comments_(options.allow_comments()), + lossless_number_(options.lossless_number()), + lossless_bignum_(options.lossless_bignum()), +#if !defined(JSONCONS_NO_DEPRECATED) + err_handler_(options.err_handler()), +#else + err_handler_(default_json_parsing()), +#endif + buffer_(temp_alloc), + state_stack_(temp_alloc) { + buffer_.reserve(initial_buffer_capacity); + + std::size_t initial_stack_capacity = options.max_nesting_depth() <= (default_initial_stack_capacity-2) ? (options.max_nesting_depth()+2) : default_initial_stack_capacity; + state_stack_.reserve(initial_stack_capacity ); + push_state(parse_state::root); + + if (options.enable_str_to_nan()) + { + string_double_map_.emplace_back(options.nan_to_str(),std::nan("")); + } + if (options.enable_str_to_inf()) + { + string_double_map_.emplace_back(options.inf_to_str(),std::numeric_limits::infinity()); + } + if (options.enable_str_to_neginf()) + { + string_double_map_.emplace_back(options.neginf_to_str(),-std::numeric_limits::infinity()); + } } +#if !defined(JSONCONS_NO_DEPRECATED) + basic_json_parser(std::function err_handler, + const TempAlloc& temp_alloc = TempAlloc()) + : basic_json_parser(basic_json_decode_options(), err_handler, temp_alloc) + { + } basic_json_parser(const basic_json_decode_options& options, - std::function err_handler, - const TempAllocator& temp_alloc = TempAllocator()) + std::function err_handler, + const TempAlloc& temp_alloc = TempAlloc()) : max_nesting_depth_(options.max_nesting_depth()), allow_trailing_comma_(options.allow_trailing_comma()), allow_comments_(options.allow_comments()), lossless_number_(options.lossless_number()), + lossless_bignum_(options.lossless_bignum()), err_handler_(err_handler), - string_buffer_(temp_alloc), + buffer_(temp_alloc), state_stack_(temp_alloc) { - string_buffer_.reserve(initial_string_buffer_capacity); + buffer_.reserve(initial_buffer_capacity); std::size_t initial_stack_capacity = options.max_nesting_depth() <= (default_initial_stack_capacity-2) ? (options.max_nesting_depth()+2) : default_initial_stack_capacity; state_stack_.reserve(initial_stack_capacity ); @@ -209,6 +245,7 @@ class basic_json_parser : public ser_context string_double_map_.emplace_back(options.neginf_to_str(),-std::numeric_limits::infinity()); } } +#endif void cursor_mode(bool value) { @@ -232,7 +269,12 @@ class basic_json_parser : public ser_context bool source_exhausted() const { - return input_ptr_ == end_input_; + return input_ptr_ == input_end_; + } + + const char_type* current() const + { + return input_ptr_; } ~basic_json_parser() noexcept @@ -275,24 +317,9 @@ class basic_json_parser : public ser_context return !more_ && state_ != parse_state::accept; } - const char_type* first() const - { - return begin_input_; - } - - const char_type* current() const - { - return input_ptr_; - } - - const char_type* last() const - { - return end_input_; - } - void skip_whitespace() { - const char_type* local_input_end = end_input_; + const char_type* local_input_end = input_end_; while (input_ptr_ != local_input_end) { @@ -464,10 +491,9 @@ class basic_json_parser : public ser_context cp_ = 0; cp2_ = 0; begin_position_ = 0; - begin_input_ = nullptr; - end_input_ = nullptr; + input_end_ = nullptr; input_ptr_ = nullptr; - string_buffer_.clear(); + buffer_.clear(); } void reset() @@ -500,7 +526,7 @@ class basic_json_parser : public ser_context void check_done(std::error_code& ec) { - for (; input_ptr_ != end_input_; ++input_ptr_) + for (; input_ptr_ != input_end_; ++input_ptr_) { char_type curr_char_ = *input_ptr_; switch (curr_char_) @@ -529,9 +555,8 @@ class basic_json_parser : public ser_context void update(const char_type* data, std::size_t length) { - begin_input_ = data; - end_input_ = data + length; - input_ptr_ = begin_input_; + input_end_ = data + length; + input_ptr_ = data; } void parse_some(basic_json_visitor& visitor) @@ -577,7 +602,7 @@ class basic_json_parser : public ser_context more_ = false; return; } - const char_type* local_input_end = end_input_; + const char_type* local_input_end = input_end_; if (input_ptr_ == local_input_end && more_) { @@ -691,41 +716,42 @@ class basic_json_parser : public ser_context begin_position_ = position_; ++input_ptr_; ++position_; - string_buffer_.clear(); - parse_string(visitor, ec); + escape_tag_ = semantic_tag::noesc; + buffer_.clear(); + input_ptr_ = parse_string(input_ptr_, visitor, ec); if (JSONCONS_UNLIKELY(ec)) return; break; case '-': - string_buffer_.clear(); - string_buffer_.push_back('-'); + buffer_.clear(); + buffer_.push_back('-'); begin_position_ = position_; ++input_ptr_; ++position_; state_ = parse_state::number; number_state_ = parse_number_state::minus; - parse_number(visitor, ec); + input_ptr_ = parse_number(input_ptr_, visitor, ec); if (JSONCONS_UNLIKELY(ec)) {return;} break; case '0': - string_buffer_.clear(); - string_buffer_.push_back(static_cast(*input_ptr_)); + buffer_.clear(); + buffer_.push_back(static_cast(*input_ptr_)); state_ = parse_state::number; number_state_ = parse_number_state::zero; begin_position_ = position_; ++input_ptr_; ++position_; - parse_number(visitor, ec); + input_ptr_ = parse_number(input_ptr_, visitor, ec); if (JSONCONS_UNLIKELY(ec)) {return;} break; case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': - string_buffer_.clear(); - string_buffer_.push_back(static_cast(*input_ptr_)); + buffer_.clear(); + buffer_.push_back(static_cast(*input_ptr_)); begin_position_ = position_; ++input_ptr_; ++position_; state_ = parse_state::number; number_state_ = parse_number_state::integer; - parse_number(visitor, ec); + input_ptr_ = parse_number(input_ptr_, visitor, ec); if (JSONCONS_UNLIKELY(ec)) {return;} break; case 'n': @@ -733,11 +759,11 @@ class basic_json_parser : public ser_context if (JSONCONS_UNLIKELY(ec)) {return;} break; case 't': - parse_true(visitor, ec); + input_ptr_ = parse_true(input_ptr_, visitor, ec); if (JSONCONS_UNLIKELY(ec)) {return;} break; case 'f': - parse_false(visitor, ec); + input_ptr_ = parse_false(input_ptr_, visitor, ec); if (JSONCONS_UNLIKELY(ec)) {return;} break; case '}': @@ -820,6 +846,15 @@ class basic_json_parser : public ser_context return; } } + else + { + more_ = err_handler_(json_errc::unexpected_character, *this); + if (!more_) + { + ec = json_errc::unexpected_character; + return; + } + } ++input_ptr_; ++position_; break; @@ -867,8 +902,9 @@ class basic_json_parser : public ser_context push_state(parse_state::member_name); state_ = parse_state::string; string_state_ = parse_string_state{}; - string_buffer_.clear(); - parse_string(visitor, ec); + escape_tag_ = semantic_tag::noesc; + buffer_.clear(); + input_ptr_ = parse_string(input_ptr_, visitor, ec); if (JSONCONS_UNLIKELY(ec)) return; break; case '\'': @@ -924,8 +960,9 @@ class basic_json_parser : public ser_context push_state(parse_state::member_name); state_ = parse_state::string; string_state_ = parse_string_state{}; - string_buffer_.clear(); - parse_string(visitor, ec); + escape_tag_ = semantic_tag::noesc; + buffer_.clear(); + input_ptr_ = parse_string(input_ptr_, visitor, ec); if (JSONCONS_UNLIKELY(ec)) return; break; case '}': @@ -1051,41 +1088,42 @@ class basic_json_parser : public ser_context ++position_; state_ = parse_state::string; string_state_ = parse_string_state{}; - string_buffer_.clear(); - parse_string(visitor, ec); + escape_tag_ = semantic_tag::noesc; + buffer_.clear(); + input_ptr_ = parse_string(input_ptr_, visitor, ec); if (JSONCONS_UNLIKELY(ec)) return; break; case '-': - string_buffer_.clear(); - string_buffer_.push_back('-'); + buffer_.clear(); + buffer_.push_back('-'); begin_position_ = position_; ++input_ptr_; ++position_; state_ = parse_state::number; number_state_ = parse_number_state::minus; - parse_number(visitor, ec); + input_ptr_ = parse_number(input_ptr_, visitor, ec); if (JSONCONS_UNLIKELY(ec)) {return;} break; case '0': - string_buffer_.clear(); - string_buffer_.push_back(static_cast(*input_ptr_)); + buffer_.clear(); + buffer_.push_back(static_cast(*input_ptr_)); begin_position_ = position_; ++input_ptr_; ++position_; state_ = parse_state::number; number_state_ = parse_number_state::zero; - parse_number(visitor, ec); + input_ptr_ = parse_number(input_ptr_, visitor, ec); if (JSONCONS_UNLIKELY(ec)) {return;} break; case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': - string_buffer_.clear(); - string_buffer_.push_back(static_cast(*input_ptr_)); + buffer_.clear(); + buffer_.push_back(static_cast(*input_ptr_)); begin_position_ = position_; ++input_ptr_; ++position_; state_ = parse_state::number; number_state_ = parse_number_state::integer; - parse_number(visitor, ec); + input_ptr_ = parse_number(input_ptr_, visitor, ec); if (JSONCONS_UNLIKELY(ec)) {return;} break; case 'n': @@ -1093,11 +1131,11 @@ class basic_json_parser : public ser_context if (JSONCONS_UNLIKELY(ec)) {return;} break; case 't': - parse_true(visitor, ec); + input_ptr_ = parse_true(input_ptr_, visitor, ec); if (JSONCONS_UNLIKELY(ec)) {return;} break; case 'f': - parse_false(visitor, ec); + input_ptr_ = parse_false(input_ptr_, visitor, ec); if (JSONCONS_UNLIKELY(ec)) {return;} break; case ']': @@ -1202,41 +1240,42 @@ class basic_json_parser : public ser_context ++position_; state_ = parse_state::string; string_state_ = parse_string_state{}; - string_buffer_.clear(); - parse_string(visitor, ec); + escape_tag_ = semantic_tag::noesc; + buffer_.clear(); + input_ptr_ = parse_string(input_ptr_, visitor, ec); if (JSONCONS_UNLIKELY(ec)) return; break; case '-': - string_buffer_.clear(); - string_buffer_.push_back('-'); + buffer_.clear(); + buffer_.push_back('-'); begin_position_ = position_; ++input_ptr_; ++position_; state_ = parse_state::number; number_state_ = parse_number_state::minus; - parse_number(visitor, ec); + input_ptr_ = parse_number(input_ptr_, visitor, ec); if (JSONCONS_UNLIKELY(ec)) {return;} break; case '0': - string_buffer_.clear(); - string_buffer_.push_back(static_cast(*input_ptr_)); + buffer_.clear(); + buffer_.push_back(static_cast(*input_ptr_)); begin_position_ = position_; ++input_ptr_; ++position_; state_ = parse_state::number; number_state_ = parse_number_state::zero; - parse_number(visitor, ec); + input_ptr_ = parse_number(input_ptr_, visitor, ec); if (JSONCONS_UNLIKELY(ec)) {return;} break; case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': - string_buffer_.clear(); - string_buffer_.push_back(static_cast(*input_ptr_)); + buffer_.clear(); + buffer_.push_back(static_cast(*input_ptr_)); begin_position_ = position_; ++input_ptr_; ++position_; state_ = parse_state::number; number_state_ = parse_number_state::integer; - parse_number(visitor, ec); + input_ptr_ = parse_number(input_ptr_, visitor, ec); if (JSONCONS_UNLIKELY(ec)) {return;} break; case 'n': @@ -1244,11 +1283,11 @@ class basic_json_parser : public ser_context if (JSONCONS_UNLIKELY(ec)) {return;} break; case 't': - parse_true(visitor, ec); + input_ptr_ = parse_true(input_ptr_, visitor, ec); if (JSONCONS_UNLIKELY(ec)) {return;} break; case 'f': - parse_false(visitor, ec); + input_ptr_ = parse_false(input_ptr_, visitor, ec); if (JSONCONS_UNLIKELY(ec)) {return;} break; case '\'': @@ -1275,11 +1314,11 @@ class basic_json_parser : public ser_context } break; case parse_state::string: - parse_string(visitor, ec); + input_ptr_ = parse_string(input_ptr_, visitor, ec); if (JSONCONS_UNLIKELY(ec)) return; break; case parse_state::number: - parse_number(visitor, ec); + input_ptr_ = parse_number(input_ptr_, visitor, ec); if (JSONCONS_UNLIKELY(ec)) return; break; case parse_state::t: @@ -1568,14 +1607,14 @@ class basic_json_parser : public ser_context } } - void parse_true(basic_json_visitor& visitor, std::error_code& ec) + const char_type* parse_true(const char_type* cur, basic_json_visitor& visitor, std::error_code& ec) { begin_position_ = position_; - if (JSONCONS_LIKELY(end_input_ - input_ptr_ >= 4)) + if (JSONCONS_LIKELY(input_end_ - cur >= 4)) { - if (*(input_ptr_+1) == 'r' && *(input_ptr_+2) == 'u' && *(input_ptr_+3) == 'e') + if (*(cur+1) == 'r' && *(cur+2) == 'u' && *(cur+3) == 'e') { - input_ptr_ += 4; + cur += 4; position_ += 4; visitor.bool_value(true, semantic_tag::none, *this, ec); if (level_ == 0) @@ -1593,21 +1632,22 @@ class basic_json_parser : public ser_context err_handler_(json_errc::invalid_value, *this); ec = json_errc::invalid_value; more_ = false; - return; + return cur; } } else { - ++input_ptr_; + ++cur; ++position_; state_ = parse_state::t; } + return cur; } void parse_null(basic_json_visitor& visitor, std::error_code& ec) { begin_position_ = position_; - if (JSONCONS_LIKELY(end_input_ - input_ptr_ >= 4)) + if (JSONCONS_LIKELY(input_end_ - input_ptr_ >= 4)) { if (*(input_ptr_+1) == 'u' && *(input_ptr_+2) == 'l' && *(input_ptr_+3) == 'l') { @@ -1640,14 +1680,14 @@ class basic_json_parser : public ser_context } } - void parse_false(basic_json_visitor& visitor, std::error_code& ec) + const char_type* parse_false(const char_type* cur, basic_json_visitor& visitor, std::error_code& ec) { begin_position_ = position_; - if (JSONCONS_LIKELY(end_input_ - input_ptr_ >= 5)) + if (JSONCONS_LIKELY(input_end_ - cur >= 5)) { - if (*(input_ptr_+1) == 'a' && *(input_ptr_+2) == 'l' && *(input_ptr_+3) == 's' && *(input_ptr_+4) == 'e') + if (*(cur+1) == 'a' && *(cur+2) == 'l' && *(cur+3) == 's' && *(cur+4) == 'e') { - input_ptr_ += 5; + cur += 5; position_ += 5; visitor.bool_value(false, semantic_tag::none, *this, ec); more_ = !cursor_mode_; @@ -1665,20 +1705,22 @@ class basic_json_parser : public ser_context err_handler_(json_errc::invalid_value, *this); ec = json_errc::invalid_value; more_ = false; - return; + return cur; } } else { - ++input_ptr_; + ++cur; ++position_; state_ = parse_state::f; } + return cur; } - void parse_number(basic_json_visitor& visitor, std::error_code& ec) + const char_type* parse_number(const char_type* hdr, basic_json_visitor& visitor, std::error_code& ec) { - const char_type* local_input_end = end_input_; + const char_type* cur = hdr; + const char_type* local_input_end = input_end_; switch (number_state_) { @@ -1702,393 +1744,207 @@ class basic_json_parser : public ser_context JSONCONS_UNREACHABLE(); } minus_sign: - if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + if (JSONCONS_UNLIKELY(cur >= local_input_end)) // Buffer exhausted { number_state_ = parse_number_state::minus; - return; + buffer_.append(hdr, cur); + position_ += (cur - hdr); + return cur; } - switch (*input_ptr_) + if (jsoncons::is_nonzero_digit(*cur)) { - case '0': - string_buffer_.push_back(static_cast(*input_ptr_)); - ++input_ptr_; - ++position_; - goto zero; - case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': - string_buffer_.push_back(static_cast(*input_ptr_)); - ++input_ptr_; - ++position_; - goto integer; - default: - err_handler_(json_errc::invalid_number, *this); - ec = json_errc::expected_value; - more_ = false; - return; + ++cur; + goto integer; } + if (*cur == '0') + { + ++cur; + goto zero; + } + err_handler_(json_errc::invalid_number, *this); + ec = json_errc::invalid_number; + more_ = false; + position_ += (cur - hdr); + return cur; zero: - if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + if (JSONCONS_UNLIKELY(cur >= local_input_end)) // Buffer exhausted { - number_state_ = parse_number_state::zero; - return; + number_state_ = parse_number_state::integer; + buffer_.append(hdr, cur); + position_ += (cur - hdr); + return cur; } - switch (*input_ptr_) + if (*cur == '.') { - case '\r': - end_integer_value(visitor, ec); - if (JSONCONS_UNLIKELY(ec)) return; - ++input_ptr_; - ++position_; - push_state(state_); - state_ = parse_state::cr; - return; - case '\n': - end_integer_value(visitor, ec); - if (JSONCONS_UNLIKELY(ec)) return; - ++input_ptr_; - ++line_; - ++position_; - mark_position_ = position_; - return; - case ' ':case '\t': - end_integer_value(visitor, ec); - if (JSONCONS_UNLIKELY(ec)) return; - skip_space(&input_ptr_); - return; - case '/': - end_integer_value(visitor, ec); - if (JSONCONS_UNLIKELY(ec)) return; - ++input_ptr_; - ++position_; - push_state(state_); - state_ = parse_state::slash; - return; - case '}': - case ']': - end_integer_value(visitor, ec); - if (JSONCONS_UNLIKELY(ec)) return; - state_ = parse_state::expect_comma_or_end; - return; - case '.': - string_buffer_.push_back(to_double_.get_decimal_point()); - ++input_ptr_; - ++position_; - goto fraction1; - case 'e':case 'E': - string_buffer_.push_back(static_cast(*input_ptr_)); - ++input_ptr_; - ++position_; - goto exp1; - case ',': - end_integer_value(visitor, ec); - if (JSONCONS_UNLIKELY(ec)) return; - begin_member_or_element(ec); - if (JSONCONS_UNLIKELY(ec)) return; - ++input_ptr_; - ++position_; - return; - case '0': case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': - err_handler_(json_errc::leading_zero, *this); - ec = json_errc::leading_zero; - more_ = false; - number_state_ = parse_number_state::zero; - return; - default: - err_handler_(json_errc::invalid_number, *this); - ec = json_errc::invalid_number; - more_ = false; - number_state_ = parse_number_state::zero; - return; + ++cur; + goto fraction1; } -integer: - if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + if (jsoncons::is_exp(*cur)) { - number_state_ = parse_number_state::integer; - return; + ++cur; + goto exp1; } - switch (*input_ptr_) + if (jsoncons::is_digit(*cur)) { - case '\r': - end_integer_value(visitor, ec); - if (JSONCONS_UNLIKELY(ec)) return; - push_state(state_); - ++input_ptr_; - ++position_; - state_ = parse_state::cr; - return; - case '\n': - end_integer_value(visitor, ec); - if (JSONCONS_UNLIKELY(ec)) return; - ++input_ptr_; - ++line_; - ++position_; - mark_position_ = position_; - return; - case ' ':case '\t': - end_integer_value(visitor, ec); - if (JSONCONS_UNLIKELY(ec)) return; - skip_space(&input_ptr_); - return; - case '/': - end_integer_value(visitor, ec); - if (JSONCONS_UNLIKELY(ec)) return; - push_state(state_); - ++input_ptr_; - ++position_; - state_ = parse_state::slash; - return; - case '}': - case ']': - end_integer_value(visitor, ec); - if (JSONCONS_UNLIKELY(ec)) return; - state_ = parse_state::expect_comma_or_end; - return; - case '0': case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': - string_buffer_.push_back(static_cast(*input_ptr_)); - ++input_ptr_; - ++position_; - goto integer; - case '.': - string_buffer_.push_back(to_double_.get_decimal_point()); - ++input_ptr_; - ++position_; - goto fraction1; - case 'e':case 'E': - string_buffer_.push_back(static_cast(*input_ptr_)); - ++input_ptr_; - ++position_; - goto exp1; - case ',': - end_integer_value(visitor, ec); - if (JSONCONS_UNLIKELY(ec)) return; - begin_member_or_element(ec); - if (JSONCONS_UNLIKELY(ec)) return; - ++input_ptr_; - ++position_; - return; - default: - err_handler_(json_errc::invalid_number, *this); - ec = json_errc::invalid_number; - more_ = false; + err_handler_(json_errc::leading_zero, *this); + ec = json_errc::leading_zero; + more_ = false; + number_state_ = parse_number_state::zero; + + position_ += (cur - hdr); + return cur; + } + buffer_.append(hdr, cur); + position_ += (cur - hdr); + end_integer_value(visitor, ec); + return cur; +integer: + while (true) + { + if (JSONCONS_UNLIKELY(cur >= local_input_end)) // Buffer exhausted + { number_state_ = parse_number_state::integer; - return; + buffer_.append(hdr, cur); + position_ += (cur - hdr); + return cur; + } + if (JSONCONS_UNLIKELY(!jsoncons::is_digit(*cur))) + { + break; + } + ++cur; } + if (*cur == '.') + { + ++cur; + goto fraction1; + } + if (jsoncons::is_exp(*cur)) + { + ++cur; + goto exp1; + } + buffer_.append(hdr, cur); + position_ += (cur - hdr); + end_integer_value(visitor, ec); + return cur; fraction1: - if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + if (JSONCONS_UNLIKELY(cur >= local_input_end)) // Buffer exhausted { number_state_ = parse_number_state::fraction1; - return; + buffer_.append(hdr, cur); + position_ += (cur - hdr); + return cur; } - switch (*input_ptr_) + if (jsoncons::is_digit(*cur)) { - case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': - string_buffer_.push_back(static_cast(*input_ptr_)); - ++input_ptr_; - ++position_; - goto fraction2; - default: - err_handler_(json_errc::invalid_number, *this); - ec = json_errc::invalid_number; - more_ = false; - number_state_ = parse_number_state::fraction1; - return; + ++cur; + goto fraction2; } + err_handler_(json_errc::invalid_number, *this); + ec = json_errc::invalid_number; + more_ = false; + number_state_ = parse_number_state::fraction1; + position_ += (cur - hdr); + return cur; fraction2: - if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + while (true) { - number_state_ = parse_number_state::fraction2; - return; + if (JSONCONS_UNLIKELY(cur >= local_input_end)) // Buffer exhausted + { + number_state_ = parse_number_state::fraction2; + buffer_.append(hdr, cur); + position_ += (cur - hdr); + return cur; + } + if (JSONCONS_UNLIKELY(!jsoncons::is_digit(*cur))) + { + break; + } + ++cur; } - switch (*input_ptr_) + if (jsoncons::is_exp(*cur)) { - case '\r': - end_fraction_value(visitor, ec); - if (JSONCONS_UNLIKELY(ec)) return; - push_state(state_); - ++input_ptr_; - ++position_; - state_ = parse_state::cr; - return; - case '\n': - end_fraction_value(visitor, ec); - if (JSONCONS_UNLIKELY(ec)) return; - ++input_ptr_; - ++line_; - ++position_; - mark_position_ = position_; - return; - case ' ':case '\t': - end_fraction_value(visitor, ec); - if (JSONCONS_UNLIKELY(ec)) return; - skip_space(&input_ptr_); - return; - case '/': - end_fraction_value(visitor, ec); - if (JSONCONS_UNLIKELY(ec)) return; - push_state(state_); - ++input_ptr_; - ++position_; - state_ = parse_state::slash; - return; - case '}': - end_fraction_value(visitor, ec); - if (JSONCONS_UNLIKELY(ec)) return; - state_ = parse_state::expect_comma_or_end; - return; - case ']': - end_fraction_value(visitor, ec); - if (JSONCONS_UNLIKELY(ec)) return; - state_ = parse_state::expect_comma_or_end; - return; - case ',': - end_fraction_value(visitor, ec); - if (JSONCONS_UNLIKELY(ec)) return; - begin_member_or_element(ec); - if (JSONCONS_UNLIKELY(ec)) return; - ++input_ptr_; - ++position_; - return; - case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': - string_buffer_.push_back(static_cast(*input_ptr_)); - ++input_ptr_; - ++position_; - goto fraction2; - case 'e':case 'E': - string_buffer_.push_back(static_cast(*input_ptr_)); - ++input_ptr_; - ++position_; - goto exp1; - default: - err_handler_(json_errc::invalid_number, *this); - ec = json_errc::invalid_number; - more_ = false; - number_state_ = parse_number_state::fraction2; - return; + ++cur; + goto exp1; } + buffer_.append(hdr, cur); + position_ += (cur - hdr); + end_fraction_value(visitor, ec); + return cur; exp1: - if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + if (JSONCONS_UNLIKELY(cur >= local_input_end)) // Buffer exhausted { number_state_ = parse_number_state::exp1; - return; + buffer_.append(hdr, cur); + position_ += (cur - hdr); + return cur; } - switch (*input_ptr_) + if (*cur == '-') { - case '+': - ++input_ptr_; - ++position_; - goto exp2; - case '-': - string_buffer_.push_back(static_cast(*input_ptr_)); - ++input_ptr_; - ++position_; - goto exp2; - case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': - string_buffer_.push_back(static_cast(*input_ptr_)); - ++input_ptr_; - ++position_; - goto exp3; - default: - err_handler_(json_errc::invalid_number, *this); - ec = json_errc::expected_value; - more_ = false; - number_state_ = parse_number_state::exp1; - return; + ++cur; + goto exp2; + } + if (jsoncons::is_digit(*cur)) + { + ++cur; + goto exp3; + } + if (*cur == '+') + { + ++cur; + goto exp2; } + err_handler_(json_errc::invalid_number, *this); + ec = json_errc::invalid_number; + more_ = false; + position_ += (cur - hdr); + return cur; exp2: - if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + if (JSONCONS_UNLIKELY(cur >= local_input_end)) // Buffer exhausted { number_state_ = parse_number_state::exp2; - return; + buffer_.append(hdr, cur); + position_ += (cur - hdr); + return cur; } - switch (*input_ptr_) + if (jsoncons::is_digit(*cur)) { - case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': - string_buffer_.push_back(static_cast(*input_ptr_)); - ++input_ptr_; - ++position_; - goto exp3; - default: - err_handler_(json_errc::invalid_number, *this); - ec = json_errc::expected_value; - more_ = false; - number_state_ = parse_number_state::exp2; - return; + ++cur; + goto exp3; } + err_handler_(json_errc::invalid_number, *this); + ec = json_errc::invalid_number; + more_ = false; + position_ += (cur - hdr); + return cur; exp3: - if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + while (true) { - number_state_ = parse_number_state::exp3; - return; - } - switch (*input_ptr_) - { - case '\r': - end_fraction_value(visitor, ec); - if (JSONCONS_UNLIKELY(ec)) return; - ++input_ptr_; - ++position_; - push_state(state_); - state_ = parse_state::cr; - return; - case '\n': - end_fraction_value(visitor, ec); - if (JSONCONS_UNLIKELY(ec)) return; - ++input_ptr_; - ++line_; - ++position_; - mark_position_ = position_; - return; - case ' ':case '\t': - end_fraction_value(visitor, ec); - if (JSONCONS_UNLIKELY(ec)) return; - skip_space(&input_ptr_); - return; - case '/': - end_fraction_value(visitor, ec); - if (JSONCONS_UNLIKELY(ec)) return; - push_state(state_); - ++input_ptr_; - ++position_; - state_ = parse_state::slash; - return; - case '}': - end_fraction_value(visitor, ec); - if (JSONCONS_UNLIKELY(ec)) return; - state_ = parse_state::expect_comma_or_end; - return; - case ']': - end_fraction_value(visitor, ec); - if (JSONCONS_UNLIKELY(ec)) return; - state_ = parse_state::expect_comma_or_end; - return; - case ',': - end_fraction_value(visitor, ec); - if (JSONCONS_UNLIKELY(ec)) return; - begin_member_or_element(ec); - if (JSONCONS_UNLIKELY(ec)) return; - ++input_ptr_; - ++position_; - return; - case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': - string_buffer_.push_back(static_cast(*input_ptr_)); - ++input_ptr_; - ++position_; - goto exp3; - default: - err_handler_(json_errc::invalid_number, *this); - ec = json_errc::invalid_number; - more_ = false; + if (JSONCONS_UNLIKELY(cur >= local_input_end)) // Buffer exhausted + { number_state_ = parse_number_state::exp3; - return; + buffer_.append(hdr, cur); + position_ += (cur - hdr); + return cur; + } + if (JSONCONS_UNLIKELY(!jsoncons::is_digit(*cur))) + { + break; + } + ++cur; } - - JSONCONS_UNREACHABLE(); + buffer_.append(hdr, cur); + position_ += (cur - hdr); + end_fraction_value(visitor, ec); + return cur; } - void parse_string(basic_json_visitor& visitor, std::error_code& ec) + const char_type* parse_string(const char_type* cur, basic_json_visitor& visitor, std::error_code& ec) { - const char_type* local_input_end = end_input_; - const char_type* sb = input_ptr_; + const char_type* local_input_end = input_end_; + const char_type* sb = cur; switch (string_state_) { @@ -2121,131 +1977,132 @@ class basic_json_parser : public ser_context } text: - while (input_ptr_ < local_input_end) + while (cur < local_input_end) { - switch (*input_ptr_) + switch (*cur) { JSONCONS_ILLEGAL_CONTROL_CHARACTER: { - position_ += (input_ptr_ - sb + 1); + position_ += (cur - sb + 1); more_ = err_handler_(json_errc::illegal_control_character, *this); if (!more_) { ec = json_errc::illegal_control_character; string_state_ = parse_string_state{}; - return; + return cur; } // recovery - skip - string_buffer_.append(sb,input_ptr_-sb); - ++input_ptr_; + buffer_.append(sb,cur-sb); + ++cur; string_state_ = parse_string_state{}; - return; + return cur; } case '\n': case '\r': case '\t': { - position_ += (input_ptr_ - sb + 1); + position_ += (cur - sb + 1); if (!err_handler_(json_errc::illegal_character_in_string, *this)) { more_ = false; ec = json_errc::illegal_character_in_string; - return; + return cur; } // recovery - skip - string_buffer_.append(sb,input_ptr_-sb); - sb = input_ptr_ + 1; + buffer_.append(sb,cur-sb); + sb = cur + 1; break; } case '\\': { - string_buffer_.append(sb,input_ptr_-sb); - position_ += (input_ptr_ - sb + 1); - ++input_ptr_; + buffer_.append(sb,cur-sb); + position_ += (cur - sb + 1); + ++cur; + escape_tag_ = semantic_tag{}; goto escape; } case '\"': { - position_ += (input_ptr_ - sb + 1); - if (string_buffer_.length() == 0) + position_ += (cur - sb + 1); + if (buffer_.empty()) { - end_string_value(sb,input_ptr_-sb, visitor, ec); - if (JSONCONS_UNLIKELY(ec)) {return;} + end_string_value(sb,cur-sb, visitor, ec); + if (JSONCONS_UNLIKELY(ec)) {return cur;} } else { - string_buffer_.append(sb,input_ptr_-sb); - end_string_value(string_buffer_.data(),string_buffer_.length(), visitor, ec); - if (JSONCONS_UNLIKELY(ec)) {return;} + buffer_.append(sb,cur-sb); + end_string_value(buffer_.data(), buffer_.length(), visitor, ec); + if (JSONCONS_UNLIKELY(ec)) {return cur;} } - ++input_ptr_; - return; + ++cur; + return cur; } default: break; } - ++input_ptr_; + ++cur; } // Buffer exhausted { - string_buffer_.append(sb,input_ptr_-sb); - position_ += (input_ptr_ - sb); + buffer_.append(sb,cur-sb); + position_ += (cur - sb); string_state_ = parse_string_state{}; - return; + return cur; } escape: - if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + if (JSONCONS_UNLIKELY(cur >= local_input_end)) // Buffer exhausted { string_state_ = parse_string_state::escape; - return; + return cur; } - switch (*input_ptr_) + switch (*cur) { case '\"': - string_buffer_.push_back('\"'); - sb = ++input_ptr_; + buffer_.push_back('\"'); + sb = ++cur; ++position_; goto text; case '\\': - string_buffer_.push_back('\\'); - sb = ++input_ptr_; + buffer_.push_back('\\'); + sb = ++cur; ++position_; goto text; case '/': - string_buffer_.push_back('/'); - sb = ++input_ptr_; + buffer_.push_back('/'); + sb = ++cur; ++position_; goto text; case 'b': - string_buffer_.push_back('\b'); - sb = ++input_ptr_; + buffer_.push_back('\b'); + sb = ++cur; ++position_; goto text; case 'f': - string_buffer_.push_back('\f'); - sb = ++input_ptr_; + buffer_.push_back('\f'); + sb = ++cur; ++position_; goto text; case 'n': - string_buffer_.push_back('\n'); - sb = ++input_ptr_; + buffer_.push_back('\n'); + sb = ++cur; ++position_; goto text; case 'r': - string_buffer_.push_back('\r'); - sb = ++input_ptr_; + buffer_.push_back('\r'); + sb = ++cur; ++position_; goto text; case 't': - string_buffer_.push_back('\t'); - sb = ++input_ptr_; + buffer_.push_back('\t'); + sb = ++cur; ++position_; goto text; case 'u': cp_ = 0; - ++input_ptr_; + ++cur; ++position_; goto escape_u1; default: @@ -2253,104 +2110,104 @@ class basic_json_parser : public ser_context ec = json_errc::illegal_escaped_character; more_ = false; string_state_ = parse_string_state::escape; - return; + return cur; } escape_u1: - if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + if (JSONCONS_UNLIKELY(cur >= local_input_end)) // Buffer exhausted { string_state_ = parse_string_state::escape_u1; - return; + return cur; } { - cp_ = append_to_codepoint(0, *input_ptr_, ec); + cp_ = append_to_codepoint(0, *cur, ec); if (JSONCONS_UNLIKELY(ec)) { string_state_ = parse_string_state::escape_u1; - return; + return cur; } - ++input_ptr_; + ++cur; ++position_; goto escape_u2; } escape_u2: - if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + if (JSONCONS_UNLIKELY(cur >= local_input_end)) // Buffer exhausted { string_state_ = parse_string_state::escape_u2; - return; + return cur; } { - cp_ = append_to_codepoint(cp_, *input_ptr_, ec); + cp_ = append_to_codepoint(cp_, *cur, ec); if (JSONCONS_UNLIKELY(ec)) { string_state_ = parse_string_state::escape_u2; - return; + return cur; } - ++input_ptr_; + ++cur; ++position_; goto escape_u3; } escape_u3: - if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + if (JSONCONS_UNLIKELY(cur >= local_input_end)) // Buffer exhausted { string_state_ = parse_string_state::escape_u3; - return; + return cur; } { - cp_ = append_to_codepoint(cp_, *input_ptr_, ec); + cp_ = append_to_codepoint(cp_, *cur, ec); if (JSONCONS_UNLIKELY(ec)) { string_state_ = parse_string_state::escape_u3; - return; + return cur; } - ++input_ptr_; + ++cur; ++position_; goto escape_u4; } escape_u4: - if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + if (JSONCONS_UNLIKELY(cur >= local_input_end)) // Buffer exhausted { string_state_ = parse_string_state::escape_u4; - return; + return cur; } { - cp_ = append_to_codepoint(cp_, *input_ptr_, ec); + cp_ = append_to_codepoint(cp_, *cur, ec); if (JSONCONS_UNLIKELY(ec)) { string_state_ = parse_string_state::escape_u4; - return; + return cur; } if (unicode_traits::is_high_surrogate(cp_)) { - ++input_ptr_; + ++cur; ++position_; goto escape_expect_surrogate_pair1; } else { - unicode_traits::convert(&cp_, 1, string_buffer_); - sb = ++input_ptr_; + unicode_traits::convert(&cp_, 1, buffer_); + sb = ++cur; ++position_; string_state_ = parse_string_state{}; - return; + return cur; } } escape_expect_surrogate_pair1: - if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + if (JSONCONS_UNLIKELY(cur >= local_input_end)) // Buffer exhausted { string_state_ = parse_string_state::escape_expect_surrogate_pair1; - return; + return cur; } { - switch (*input_ptr_) + switch (*cur) { case '\\': cp2_ = 0; - ++input_ptr_; + ++cur; ++position_; goto escape_expect_surrogate_pair2; default: @@ -2358,21 +2215,21 @@ class basic_json_parser : public ser_context ec = json_errc::expected_codepoint_surrogate_pair; more_ = false; string_state_ = parse_string_state::escape_expect_surrogate_pair1; - return; + return cur; } } escape_expect_surrogate_pair2: - if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + if (JSONCONS_UNLIKELY(cur >= local_input_end)) // Buffer exhausted { string_state_ = parse_string_state::escape_expect_surrogate_pair2; - return; + return cur; } { - switch (*input_ptr_) + switch (*cur) { case 'u': - ++input_ptr_; + ++cur; ++position_; goto escape_u5; default: @@ -2380,80 +2237,80 @@ class basic_json_parser : public ser_context ec = json_errc::expected_codepoint_surrogate_pair; more_ = false; string_state_ = parse_string_state::escape_expect_surrogate_pair2; - return; + return cur; } } escape_u5: - if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + if (JSONCONS_UNLIKELY(cur >= local_input_end)) // Buffer exhausted { string_state_ = parse_string_state::escape_u5; - return; + return cur; } { - cp2_ = append_to_codepoint(0, *input_ptr_, ec); + cp2_ = append_to_codepoint(0, *cur, ec); if (JSONCONS_UNLIKELY(ec)) { string_state_ = parse_string_state::escape_u5; - return; + return cur; } } - ++input_ptr_; + ++cur; ++position_; goto escape_u6; escape_u6: - if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + if (JSONCONS_UNLIKELY(cur >= local_input_end)) // Buffer exhausted { string_state_ = parse_string_state::escape_u6; - return; + return cur; } { - cp2_ = append_to_codepoint(cp2_, *input_ptr_, ec); + cp2_ = append_to_codepoint(cp2_, *cur, ec); if (JSONCONS_UNLIKELY(ec)) { string_state_ = parse_string_state::escape_u6; - return; + return cur; } - ++input_ptr_; + ++cur; ++position_; goto escape_u7; } escape_u7: - if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + if (JSONCONS_UNLIKELY(cur >= local_input_end)) // Buffer exhausted { string_state_ = parse_string_state::escape_u7; - return; + return cur; } { - cp2_ = append_to_codepoint(cp2_, *input_ptr_, ec); + cp2_ = append_to_codepoint(cp2_, *cur, ec); if (JSONCONS_UNLIKELY(ec)) { string_state_ = parse_string_state::escape_u7; - return; + return cur; } - ++input_ptr_; + ++cur; ++position_; goto escape_u8; } escape_u8: - if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + if (JSONCONS_UNLIKELY(cur >= local_input_end)) // Buffer exhausted { string_state_ = parse_string_state::escape_u8; - return; + return cur; } { - cp2_ = append_to_codepoint(cp2_, *input_ptr_, ec); + cp2_ = append_to_codepoint(cp2_, *cur, ec); if (JSONCONS_UNLIKELY(ec)) { string_state_ = parse_string_state::escape_u8; - return; + return cur; } uint32_t cp = 0x10000 + ((cp_ & 0x3FF) << 10) + (cp2_ & 0x3FF); - unicode_traits::convert(&cp, 1, string_buffer_); - sb = ++input_ptr_; + unicode_traits::convert(&cp, 1, buffer_); + sb = ++cur; ++position_; goto text; } @@ -2535,15 +2392,11 @@ class basic_json_parser : public ser_context return position_; } - std::size_t offset() const - { - return input_ptr_ - begin_input_; - } private: void skip_space(char_type const ** ptr) { - const char_type* local_input_end = end_input_; + const char_type* local_input_end = input_end_; const char_type* cur = *ptr; while (cur < local_input_end) @@ -2592,7 +2445,7 @@ class basic_json_parser : public ser_context void end_integer_value(basic_json_visitor& visitor, std::error_code& ec) { - if (string_buffer_[0] == '-') + if (buffer_[0] == '-') { end_negative_value(visitor, ec); } @@ -2605,65 +2458,114 @@ class basic_json_parser : public ser_context void end_negative_value(basic_json_visitor& visitor, std::error_code& ec) { int64_t val; - auto result = jsoncons::detail::to_integer_unchecked(string_buffer_.data(), string_buffer_.length(), val); + auto result = jsoncons::dec_to_integer(buffer_.data(), buffer_.length(), val); if (result) { visitor.int64_value(val, semantic_tag::none, *this, ec); - more_ = !cursor_mode_; } else // Must be overflow { - visitor.string_value(string_buffer_, semantic_tag::bigint, *this, ec); - more_ = !cursor_mode_; + if (lossless_bignum_) + { + visitor.string_value(buffer_, semantic_tag::bigint, *this, ec); + } + else + { + double d{0}; + result = jsoncons::decstr_to_double(&buffer_[0], buffer_.length(), d); + if (JSONCONS_LIKELY(result)) + { + visitor.double_value(d, semantic_tag::none, *this, ec); + } + else if (result.ec == std::errc::result_out_of_range) + { + visitor.double_value(d, semantic_tag{}, *this, ec); // REVISIT + } + else + { + ec = json_errc::invalid_number; + more_ = false; + return; + } + } + } + more_ = !cursor_mode_; after_value(ec); } void end_positive_value(basic_json_visitor& visitor, std::error_code& ec) { uint64_t val; - auto result = jsoncons::detail::to_integer_unchecked(string_buffer_.data(), string_buffer_.length(), val); + auto result = jsoncons::dec_to_integer(buffer_.data(), buffer_.length(), val); if (result) { visitor.uint64_value(val, semantic_tag::none, *this, ec); - more_ = !cursor_mode_; } else // Must be overflow { - visitor.string_value(string_buffer_, semantic_tag::bigint, *this, ec); - more_ = !cursor_mode_; + if (lossless_bignum_) + { + visitor.string_value(buffer_, semantic_tag::bigint, *this, ec); + } + else + { + double d{0}; + result = jsoncons::decstr_to_double(&buffer_[0], buffer_.length(), d); + if (JSONCONS_LIKELY(result)) + { + visitor.double_value(d, semantic_tag::none, *this, ec); + } + else if (result.ec == std::errc::result_out_of_range) + { + visitor.double_value(d, semantic_tag{}, *this, ec); // REVISIT + } + else + { + ec = json_errc::invalid_number; + more_ = false; + return; + } + } } + more_ = !cursor_mode_; after_value(ec); } void end_fraction_value(basic_json_visitor& visitor, std::error_code& ec) { - JSONCONS_TRY + if (lossless_number_) { - if (lossless_number_) + visitor.string_value(buffer_, semantic_tag::bigdec, *this, ec); + } + else + { + double d{0}; + auto result = jsoncons::decstr_to_double(&buffer_[0], buffer_.length(), d); + if (JSONCONS_LIKELY(result)) { - visitor.string_value(string_buffer_, semantic_tag::bigdec, *this, ec); - more_ = !cursor_mode_; + visitor.double_value(d, semantic_tag::none, *this, ec); } - else + else if (result.ec == std::errc::result_out_of_range) { - double d = to_double_(string_buffer_.c_str(), string_buffer_.length()); - visitor.double_value(d, semantic_tag::none, *this, ec); - more_ = !cursor_mode_; + if (lossless_bignum_) + { + visitor.string_value(buffer_, semantic_tag::bigdec, *this, ec); + } + else + { + visitor.double_value(d, semantic_tag{}, *this, ec); // REVISIT + } } - } - JSONCONS_CATCH(...) - { - more_ = err_handler_(json_errc::invalid_number, *this); - if (!more_) + else { ec = json_errc::invalid_number; + more_ = false; return; } - visitor.null_value(semantic_tag::none, *this, ec); // recovery - more_ = !cursor_mode_; } + more_ = !cursor_mode_; after_value(ec); } @@ -2696,7 +2598,7 @@ class basic_json_parser : public ser_context } else { - visitor.string_value(sv, semantic_tag::none, *this, ec); + visitor.string_value(sv, escape_tag_, *this, ec); more_ = !cursor_mode_; } state_ = parse_state::expect_comma_or_end; @@ -2712,7 +2614,7 @@ class basic_json_parser : public ser_context } else { - visitor.string_value(sv, semantic_tag::none, *this, ec); + visitor.string_value(sv, escape_tag_, *this, ec); more_ = !cursor_mode_; } state_ = parse_state::accept; diff --git a/include/jsoncons/json_reader.hpp b/include/jsoncons/json_reader.hpp index 7258338..75d2064 100644 --- a/include/jsoncons/json_reader.hpp +++ b/include/jsoncons/json_reader.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include #include #include @@ -166,7 +166,7 @@ namespace jsoncons { } }; - template ,typename TempAllocator =std::allocator> + template ,typename TempAlloc =std::allocator> class basic_json_reader { public: @@ -174,88 +174,64 @@ namespace jsoncons { using source_type = Source; using string_view_type = jsoncons::basic_string_view; private: - using char_allocator_type = typename std::allocator_traits:: template rebind_alloc; + using char_allocator_type = typename std::allocator_traits:: template rebind_alloc; static constexpr size_t default_max_buffer_size = 16384; json_source_adaptor source_; basic_default_json_visitor default_visitor_; basic_json_visitor& visitor_; - basic_json_parser parser_; + basic_json_parser parser_; // Noncopyable and nonmoveable basic_json_reader(const basic_json_reader&) = delete; basic_json_reader& operator=(const basic_json_reader&) = delete; public: - template - explicit basic_json_reader(Sourceable&& source, const TempAllocator& temp_alloc = TempAllocator()) - : basic_json_reader(std::forward(source), - default_visitor_, - basic_json_decode_options(), - default_json_parsing(), - temp_alloc) - { - } template basic_json_reader(Sourceable&& source, - const basic_json_decode_options& options, - const TempAllocator& temp_alloc = TempAllocator()) + const basic_json_decode_options& options = basic_json_decode_options{}, + const TempAlloc& temp_alloc = TempAlloc()) : basic_json_reader(std::forward(source), default_visitor_, options, - options.err_handler(), - temp_alloc) - { - } - - template - basic_json_reader(Sourceable&& source, - std::function err_handler, - const TempAllocator& temp_alloc = TempAllocator()) - : basic_json_reader(std::forward(source), - default_visitor_, - basic_json_decode_options(), - err_handler, temp_alloc) { } template basic_json_reader(Sourceable&& source, - const basic_json_decode_options& options, - std::function err_handler, - const TempAllocator& temp_alloc = TempAllocator()) + basic_json_visitor& visitor, + const TempAlloc& temp_alloc = TempAlloc()) : basic_json_reader(std::forward(source), - default_visitor_, - options, - err_handler, + visitor, + basic_json_decode_options(), temp_alloc) { } template basic_json_reader(Sourceable&& source, - basic_json_visitor& visitor, - const TempAllocator& temp_alloc = TempAllocator()) - : basic_json_reader(std::forward(source), - visitor, - basic_json_decode_options(), - default_json_parsing(), - temp_alloc) + basic_json_visitor& visitor, + const basic_json_decode_options& options, + const TempAlloc& temp_alloc = TempAlloc()) + : source_(std::forward(source)), + visitor_(visitor), + parser_(options, temp_alloc) { } +#if !defined(JSONCONS_NO_DEPRECATED) template basic_json_reader(Sourceable&& source, - basic_json_visitor& visitor, - const basic_json_decode_options& options, - const TempAllocator& temp_alloc = TempAllocator()) + const basic_json_decode_options& options, + std::function err_handler, + const TempAlloc& temp_alloc = TempAlloc()) : basic_json_reader(std::forward(source), - visitor, + default_visitor_, options, - options.err_handler(), + err_handler, temp_alloc) { } @@ -264,7 +240,7 @@ namespace jsoncons { basic_json_reader(Sourceable&& source, basic_json_visitor& visitor, std::function err_handler, - const TempAllocator& temp_alloc = TempAllocator()) + const TempAlloc& temp_alloc = TempAlloc()) : basic_json_reader(std::forward(source), visitor, basic_json_decode_options(), @@ -278,13 +254,13 @@ namespace jsoncons { basic_json_visitor& visitor, const basic_json_decode_options& options, std::function err_handler, - const TempAllocator& temp_alloc = TempAllocator()) + const TempAlloc& temp_alloc = TempAlloc()) : source_(std::forward(source)), visitor_(visitor), parser_(options,err_handler,temp_alloc) { } - +#endif void read_next() { std::error_code ec; diff --git a/include/jsoncons/json_type.hpp b/include/jsoncons/json_type.hpp index d4514fa..44260ae 100644 --- a/include/jsoncons/json_type.hpp +++ b/include/jsoncons/json_type.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -16,27 +16,39 @@ namespace jsoncons { enum class json_type : uint8_t { - null_value, - bool_value, - int64_value, - uint64_value, - half_value, - double_value, - string_value, - byte_string_value, - array_value, - object_value + null, + boolean, + int64, + uint64, + float16, + float64, + string, + byte_string, + array, + object + #if !defined(JSONCONS_NO_DEPRECATED) + , null_value = null, + bool_value = boolean, + int64_value = int64, + uint64_value = uint64, + half_value = float16, + double_value = float64, + string_value = string, + byte_string_value = byte_string, + array_value = array, + object_value = object + #endif }; template std::basic_ostream& operator<<(std::basic_ostream& os, json_type type) { static constexpr const CharT* null_value = JSONCONS_CSTRING_CONSTANT(CharT, "null"); - static constexpr const CharT* bool_value = JSONCONS_CSTRING_CONSTANT(CharT, "bool"); + static constexpr const CharT* bool_value = JSONCONS_CSTRING_CONSTANT(CharT, "boolean"); static constexpr const CharT* int64_value = JSONCONS_CSTRING_CONSTANT(CharT, "int64"); static constexpr const CharT* uint64_value = JSONCONS_CSTRING_CONSTANT(CharT, "uint64"); - static constexpr const CharT* half_value = JSONCONS_CSTRING_CONSTANT(CharT, "half"); - static constexpr const CharT* double_value = JSONCONS_CSTRING_CONSTANT(CharT, "double"); + static constexpr const CharT* half_value = JSONCONS_CSTRING_CONSTANT(CharT, "float16"); + static constexpr const CharT* double_value = JSONCONS_CSTRING_CONSTANT(CharT, "float64"); static constexpr const CharT* string_value = JSONCONS_CSTRING_CONSTANT(CharT, "string"); static constexpr const CharT* byte_string_value = JSONCONS_CSTRING_CONSTANT(CharT, "byte_string"); static constexpr const CharT* array_value = JSONCONS_CSTRING_CONSTANT(CharT, "array"); @@ -44,52 +56,52 @@ namespace jsoncons { switch (type) { - case json_type::null_value: + case json_type::null: { os << null_value; break; } - case json_type::bool_value: + case json_type::boolean: { os << bool_value; break; } - case json_type::int64_value: + case json_type::int64: { os << int64_value; break; } - case json_type::uint64_value: + case json_type::uint64: { os << uint64_value; break; } - case json_type::half_value: + case json_type::float16: { os << half_value; break; } - case json_type::double_value: + case json_type::float64: { os << double_value; break; } - case json_type::string_value: + case json_type::string: { os << string_value; break; } - case json_type::byte_string_value: + case json_type::byte_string: { os << byte_string_value; break; } - case json_type::array_value: + case json_type::array: { os << array_value; break; } - case json_type::object_value: + case json_type::object: { os << object_value; break; @@ -105,11 +117,15 @@ namespace jsoncons { JSONCONS_INLINE_CONSTEXPR null_type null_arg{}; - struct temp_allocator_arg_t + struct temp_alloc_arg_t { - explicit temp_allocator_arg_t() = default; + explicit temp_alloc_arg_t() = default; }; + JSONCONS_INLINE_CONSTEXPR temp_alloc_arg_t temp_alloc_arg{}; + + using temp_allocator_arg_t = temp_alloc_arg_t; + JSONCONS_INLINE_CONSTEXPR temp_allocator_arg_t temp_allocator_arg{}; struct half_arg_t @@ -176,8 +192,8 @@ namespace jsoncons { float64 = 5, // 0101 half_float = 6, // 0110 short_str = 7, // 0111 - json_const_reference = 8, // 1000 - json_reference = 9, // 1001 + json_const_ref = 8, // 1000 + json_ref = 9, // 1001 byte_str = 12, // 1100 object = 13, // 1101 array = 14, // 1110 @@ -212,8 +228,8 @@ namespace jsoncons { static constexpr const CharT* array_value = JSONCONS_CSTRING_CONSTANT(CharT, "array"); static constexpr const CharT* empty_object_value = JSONCONS_CSTRING_CONSTANT(CharT, "empty_object"); static constexpr const CharT* object_value = JSONCONS_CSTRING_CONSTANT(CharT, "object"); - static constexpr const CharT* json_const_reference = JSONCONS_CSTRING_CONSTANT(CharT, "json_const_reference"); - static constexpr const CharT* json_reference = JSONCONS_CSTRING_CONSTANT(CharT, "json_reference"); + static constexpr const CharT* json_const_ref = JSONCONS_CSTRING_CONSTANT(CharT, "json_const_ref"); + static constexpr const CharT* json_ref = JSONCONS_CSTRING_CONSTANT(CharT, "json_ref"); switch (storage) { @@ -277,14 +293,14 @@ namespace jsoncons { os << object_value; break; } - case json_storage_kind::json_const_reference: + case json_storage_kind::json_const_ref: { - os << json_const_reference; + os << json_const_ref; break; } - case json_storage_kind::json_reference: + case json_storage_kind::json_ref: { - os << json_reference; + os << json_ref; break; } } diff --git a/include/jsoncons/json_type_traits.hpp b/include/jsoncons/json_type_traits.hpp index a98fca4..10ab472 100644 --- a/include/jsoncons/json_type_traits.hpp +++ b/include/jsoncons/json_type_traits.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -33,7 +33,7 @@ #include #include #include -#include +#include #if defined(JSONCONS_HAS_STD_VARIANT) #include @@ -41,1881 +41,66 @@ namespace jsoncons { - template - struct is_json_type_traits_declared : public std::false_type - {}; +template +struct is_json_type_traits_declared : public std::false_type +{}; - // json_type_traits +// json_type_traits - template - struct unimplemented : std::false_type - {}; +template +struct unimplemented : std::false_type +{}; - template - struct json_type_traits - { - using allocator_type = typename Json::allocator_type; - - static constexpr bool is_compatible = false; - - static constexpr bool is(const Json&) noexcept - { - return false; - } - - static T as(const Json&) - { - static_assert(unimplemented::value, "as not implemented"); - } - - static Json to_json(const T&) - { - static_assert(unimplemented::value, "to_json not implemented"); - } - - static Json to_json(const T&, const allocator_type&) - { - static_assert(unimplemented::value, "to_json not implemented"); - } - }; - -namespace detail { - -template -using -traits_can_convert_t = decltype(json_type_traits::can_convert(Json())); - -template -using -has_can_convert = ext_traits::is_detected; - - template - struct invoke_can_convert - { - template - static - typename std::enable_if::value,bool>::type - can_convert(const Json& j) noexcept - { - return json_type_traits::can_convert(j); - } - template - static - typename std::enable_if::value,bool>::type - can_convert(const Json& j) noexcept - { - return json_type_traits::is(j); - } - }; - - // is_json_type_traits_unspecialized - template - struct is_json_type_traits_unspecialized : std::false_type {}; - - // is_json_type_traits_unspecialized - template - struct is_json_type_traits_unspecialized::is_compatible>::value>::type - > : std::true_type {}; - - // is_compatible_array_type - template - struct is_compatible_array_type : std::false_type {}; - - template - struct is_compatible_array_type::value && - ext_traits::is_array_like::value && - !is_json_type_traits_unspecialized::value_type>::value - >::type> : std::true_type {}; - -} // namespace detail - - // is_json_type_traits_specialized - template - struct is_json_type_traits_specialized : std::false_type {}; - - template - struct is_json_type_traits_specialized::value - >::type> : std::true_type {}; - - template - struct json_type_traits::type*> - { - using char_type = typename Json::char_type; - using allocator_type = typename Json::allocator_type; - - static bool is(const Json& j) noexcept - { - return j.is_string(); - } - static const char_type* as(const Json& j) - { - return j.as_cstring(); - } - template - static Json to_json(const char_type* s, Args&&... args) - { - return Json(s, semantic_tag::none, std::forward(args)...); - } - }; - - template - struct json_type_traits::type*> - { - using char_type = typename Json::char_type; - using allocator_type = typename Json::allocator_type; - - static bool is(const Json& j) noexcept - { - return j.is_string(); - } - template - static Json to_json(const char_type* s, Args&&... args) - { - return Json(s, semantic_tag::none, std::forward(args)...); - } - }; - - // integer - - template - struct json_type_traits::value && sizeof(T) <= sizeof(int64_t)) || (ext_traits::is_unsigned_integer::value && sizeof(T) <= sizeof(uint64_t)) - >::type> - { - using allocator_type = typename Json::allocator_type; - - static bool is(const Json& j) noexcept - { - return j.template is_integer(); - } - static T as(const Json& j) - { - return j.template as_integer(); - } - - static Json to_json(T val) - { - return Json(val, semantic_tag::none); - } - - static Json to_json(T val, const allocator_type&) - { - return Json(val, semantic_tag::none); - } - }; - - template - struct json_type_traits::value && sizeof(T) > sizeof(int64_t)) || (ext_traits::is_unsigned_integer::value && sizeof(T) > sizeof(uint64_t)) - >::type> - { - using allocator_type = typename Json::allocator_type; - - static bool is(const Json& j) noexcept - { - return j.template is_integer(); - } - static T as(const Json& j) - { - return j.template as_integer(); - } - - static Json to_json(T val, const allocator_type& alloc = allocator_type()) - { - return Json(val, semantic_tag::none, alloc); - } - }; - - template - struct json_type_traits::value - >::type> - { - using allocator_type = typename Json::allocator_type; - - static bool is(const Json& j) noexcept - { - return j.is_double(); - } - static T as(const Json& j) - { - return static_cast(j.as_double()); - } - static Json to_json(T val) - { - return Json(val, semantic_tag::none); - } - static Json to_json(T val, const allocator_type&) - { - return Json(val, semantic_tag::none); - } - }; - - template - struct json_type_traits - { - using json_object = typename Json::object; - using allocator_type = typename Json::allocator_type; - - static bool is(const Json& j) noexcept - { - return j.is_object(); - } - static Json to_json(const json_object& o) - { - return Json(o,semantic_tag::none); - } - static Json to_json(const json_object& o, const allocator_type&) - { - return Json(o,semantic_tag::none); - } - }; - - template - struct json_type_traits - { - using json_array = typename Json::array; - using allocator_type = typename Json::allocator_type; - - static bool is(const Json& j) noexcept - { - return j.is_array(); - } - static Json to_json(const json_array& a) - { - return Json(a, semantic_tag::none); - } - static Json to_json(const json_array& a, const allocator_type&) - { - return Json(a, semantic_tag::none); - } - }; - - template - struct json_type_traits - { - using allocator_type = typename Json::allocator_type; - - static bool is(const Json&) noexcept - { - return true; - } - static Json as(Json j) - { - return j; - } - static Json to_json(const Json& val) - { - return val; - } - static Json to_json(const Json& val, const allocator_type&) - { - return val; - } - }; - - template - struct json_type_traits - { - using allocator_type = typename Json::allocator_type; - - static bool is(const Json& j) noexcept - { - return j.is_null(); - } - static typename jsoncons::null_type as(const Json& j) - { - if (!j.is_null()) - { - JSONCONS_THROW(conv_error(conv_errc::not_jsoncons_null_type)); - } - return jsoncons::null_type(); - } - static Json to_json(jsoncons::null_type) - { - return Json(jsoncons::null_type{}, semantic_tag::none); - } - static Json to_json(jsoncons::null_type, const allocator_type&) - { - return Json(jsoncons::null_type{}, semantic_tag::none); - } - }; - - template - struct json_type_traits - { - using allocator_type = typename Json::allocator_type; - - static bool is(const Json& j) noexcept - { - return j.is_bool(); - } - static bool as(const Json& j) - { - return j.as_bool(); - } - static Json to_json(bool val) - { - return Json(val, semantic_tag::none); - } - static Json to_json(bool val, const allocator_type&) - { - return Json(val, semantic_tag::none); - } - }; - - template - struct json_type_traits::const_reference>::value, - std::vector::const_reference, - void>::type>::value>::type> - { - using allocator_type = typename Json::allocator_type; - - static bool is(const Json& j) noexcept - { - return j.is_bool(); - } - static bool as(const Json& j) - { - return j.as_bool(); - } - static Json to_json(bool val) - { - return Json(val, semantic_tag::none); - } - static Json to_json(bool val, const allocator_type&) - { - return Json(val, semantic_tag::none); - } - }; - - template - struct json_type_traits::reference> - { - using allocator_type = typename Json::allocator_type; - - static bool is(const Json& j) noexcept - { - return j.is_bool(); - } - static bool as(const Json& j) - { - return j.as_bool(); - } - static Json to_json(bool val) - { - return Json(val, semantic_tag::none); - } - static Json to_json(bool val, const allocator_type&) - { - return Json(val, semantic_tag::none); - } - }; - - template - struct json_type_traits::value && - ext_traits::is_string::value && - std::is_same::value>::type> - { - using allocator_type = typename Json::allocator_type; - - static bool is(const Json& j) noexcept - { - return j.is_string(); - } - - static T as(const Json& j) - { - return T(j.as_string()); - } - - static Json to_json(const T& val) - { - return Json(val, semantic_tag::none); - } - - static Json to_json(const T& val, const allocator_type& alloc) - { - return Json(val, semantic_tag::none, alloc); - } - }; - - template - struct json_type_traits::value && - ext_traits::is_string::value && - !std::is_same::value>::type> - { - using char_type = typename Json::char_type; - using allocator_type = typename Json::allocator_type; - - static bool is(const Json& j) noexcept - { - return j.is_string(); - } - - static T as(const Json& j) - { - auto s = j.as_string(); - T val; - unicode_traits::convert(s.data(), s.size(), val); - return val; - } - - static Json to_json(const T& val) - { - std::basic_string s; - unicode_traits::convert(val.data(), val.size(), s); - - return Json(s, semantic_tag::none); - } - - static Json to_json(const T& val, const allocator_type& alloc) - { - std::basic_string s; - unicode_traits::convert(val.data(), val.size(), s); - return Json(s, semantic_tag::none, alloc); - } - }; - - template - struct json_type_traits::value && - ext_traits::is_string_view::value && - std::is_same::value>::type> - { - using allocator_type = typename Json::allocator_type; - - static bool is(const Json& j) noexcept - { - return j.is_string_view(); - } - - static T as(const Json& j) - { - return T(j.as_string_view().data(),j.as_string_view().size()); - } - - static Json to_json(const T& val) - { - return Json(val, semantic_tag::none); - } - - static Json to_json(const T& val, const allocator_type& alloc) - { - return Json(val, semantic_tag::none, alloc); - } - }; - - // array back insertable - - template - struct json_type_traits::value && - jsoncons::detail::is_compatible_array_type::value && - ext_traits::is_back_insertable::value - >::type> - { - typedef typename std::iterator_traits::value_type value_type; - using allocator_type = typename Json::allocator_type; - - static bool is(const Json& j) noexcept - { - bool result = j.is_array(); - if (result) - { - for (auto e : j.array_range()) - { - if (!e.template is()) - { - result = false; - break; - } - } - } - return result; - } - - // array back insertable non-byte container - - template - static typename std::enable_if::value,Container>::type - as(const Json& j) - { - if (j.is_array()) - { - T result; - visit_reserve_(typename std::integral_constant::value>::type(),result,j.size()); - for (const auto& item : j.array_range()) - { - result.push_back(item.template as()); - } - - return result; - } - else - { - JSONCONS_THROW(conv_error(conv_errc::not_vector)); - } - } - - // array back insertable byte container - - template - static typename std::enable_if::value,Container>::type - as(const Json& j) - { - std::error_code ec; - if (j.is_array()) - { - T result; - visit_reserve_(typename std::integral_constant::value>::type(),result,j.size()); - for (const auto& item : j.array_range()) - { - result.push_back(item.template as()); - } - - return result; - } - else if (j.is_byte_string_view()) - { - value_converter converter; - auto v = converter.convert(j.as_byte_string_view(),j.tag(), ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(conv_error(ec)); - } - return v; - } - else if (j.is_string()) - { - value_converter,T> converter; - auto v = converter.convert(j.as_string_view(),j.tag(), ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(conv_error(ec)); - } - return v; - } - else - { - JSONCONS_THROW(conv_error(conv_errc::not_vector)); - } - } - - template - static typename std::enable_if::value,Json>::type - to_json(const T& val) - { - Json j(json_array_arg); - auto first = std::begin(val); - auto last = std::end(val); - std::size_t size = std::distance(first,last); - j.reserve(size); - for (auto it = first; it != last; ++it) - { - j.push_back(*it); - } - return j; - } - - template - static typename std::enable_if::value,Json>::type - to_json(const T& val, const allocator_type& alloc) - { - Json j(json_array_arg, alloc); - auto first = std::begin(val); - auto last = std::end(val); - std::size_t size = std::distance(first, last); - j.reserve(size); - for (auto it = first; it != last; ++it) - { - j.push_back(*it); - } - return j; - } - - template - static typename std::enable_if::value,Json>::type - to_json(const T& val) - { - Json j(byte_string_arg, val); - return j; - } - - template - static typename std::enable_if::value,Json>::type - to_json(const T& val, const allocator_type& alloc) - { - Json j(byte_string_arg, val, semantic_tag::none, alloc); - return j; - } - - static void visit_reserve_(std::true_type, T& v, std::size_t size) - { - v.reserve(size); - } - - static void visit_reserve_(std::false_type, T&, std::size_t) - { - } - }; - - // array, not back insertable but insertable - - template - struct json_type_traits::value && - jsoncons::detail::is_compatible_array_type::value && - !ext_traits::is_back_insertable::value && - ext_traits::is_insertable::value>::type> - { - typedef typename std::iterator_traits::value_type value_type; - using allocator_type = typename Json::allocator_type; - - static bool is(const Json& j) noexcept - { - bool result = j.is_array(); - if (result) - { - for (auto e : j.array_range()) - { - if (!e.template is()) - { - result = false; - break; - } - } - } - return result; - } - - static T as(const Json& j) - { - if (j.is_array()) - { - T result; - for (const auto& item : j.array_range()) - { - result.insert(item.template as()); - } - - return result; - } - else - { - JSONCONS_THROW(conv_error(conv_errc::not_vector)); - } - } - - static Json to_json(const T& val) - { - Json j(json_array_arg); - auto first = std::begin(val); - auto last = std::end(val); - std::size_t size = std::distance(first,last); - j.reserve(size); - for (auto it = first; it != last; ++it) - { - j.push_back(*it); - } - return j; - } - - static Json to_json(const T& val, const allocator_type& alloc) - { - Json j(json_array_arg, alloc); - auto first = std::begin(val); - auto last = std::end(val); - std::size_t size = std::distance(first, last); - j.reserve(size); - for (auto it = first; it != last; ++it) - { - j.push_back(*it); - } - return j; - } - }; - - // array not back insertable or insertable, but front insertable - - template - struct json_type_traits::value && - jsoncons::detail::is_compatible_array_type::value && - !ext_traits::is_back_insertable::value && - !ext_traits::is_insertable::value && - ext_traits::is_front_insertable::value>::type> - { - typedef typename std::iterator_traits::value_type value_type; - using allocator_type = typename Json::allocator_type; - - static bool is(const Json& j) noexcept - { - bool result = j.is_array(); - if (result) - { - for (auto e : j.array_range()) - { - if (!e.template is()) - { - result = false; - break; - } - } - } - return result; - } - - static T as(const Json& j) - { - if (j.is_array()) - { - T result; - - auto it = j.array_range().rbegin(); - auto end = j.array_range().rend(); - for (; it != end; ++it) - { - result.push_front((*it).template as()); - } - - return result; - } - else - { - JSONCONS_THROW(conv_error(conv_errc::not_vector)); - } - } - - static Json to_json(const T& val) - { - Json j(json_array_arg); - auto first = std::begin(val); - auto last = std::end(val); - std::size_t size = std::distance(first,last); - j.reserve(size); - for (auto it = first; it != last; ++it) - { - j.push_back(*it); - } - return j; - } - - static Json to_json(const T& val, const allocator_type& alloc) - { - Json j(json_array_arg, alloc); - auto first = std::begin(val); - auto last = std::end(val); - std::size_t size = std::distance(first, last); - j.reserve(size); - for (auto it = first; it != last; ++it) - { - j.push_back(*it); - } - return j; - } - }; - - // std::array - - template - struct json_type_traits> - { - using allocator_type = typename Json::allocator_type; - - using value_type = E; - - static bool is(const Json& j) noexcept - { - bool result = j.is_array() && j.size() == N; - if (result) - { - for (auto e : j.array_range()) - { - if (!e.template is()) - { - result = false; - break; - } - } - } - return result; - } - - static std::array as(const Json& j) - { - std::array buff; - if (j.size() != N) - { - JSONCONS_THROW(conv_error(conv_errc::not_array)); - } - for (std::size_t i = 0; i < N; i++) - { - buff[i] = j[i].template as(); - } - return buff; - } - - static Json to_json(const std::array& val) - { - Json j(json_array_arg); - j.reserve(N); - for (auto it = val.begin(); it != val.end(); ++it) - { - j.push_back(*it); - } - return j; - } - - static Json to_json(const std::array& val, - const allocator_type& alloc) - { - Json j(json_array_arg, alloc); - j.reserve(N); - for (auto it = val.begin(); it != val.end(); ++it) - { - j.push_back(*it); - } - return j; - } - }; - - // map like - template - struct json_type_traits::value && - ext_traits::is_map_like::value && - ext_traits::is_constructible_from_const_pointer_and_size::value && - is_json_type_traits_specialized::value>::type - > - { - using mapped_type = typename T::mapped_type; - using value_type = typename T::value_type; - using key_type = typename T::key_type; - using allocator_type = typename Json::allocator_type; - - static bool is(const Json& j) noexcept - { - bool result = j.is_object(); - for (auto member : j.object_range()) - { - if (!member.value().template is()) - { - result = false; - } - } - return result; - } - - static T as(const Json& j) - { - if (!j.is_object()) - { - JSONCONS_THROW(conv_error(conv_errc::not_map)); - } - T result; - for (const auto& item : j.object_range()) - { - result.emplace(key_type(item.key().data(),item.key().size()), item.value().template as()); - } - - return result; - } - - static Json to_json(const T& val) - { - Json j(json_object_arg, val.begin(), val.end()); - return j; - } - - static Json to_json(const T& val, const allocator_type& alloc) - { - Json j(json_object_arg, val.begin(), val.end(), alloc); - return j; - } - }; - - template - struct json_type_traits::value && - ext_traits::is_map_like::value && - !ext_traits::is_constructible_from_const_pointer_and_size::value && - is_json_type_traits_specialized::value && - is_json_type_traits_specialized::value>::type - > - { - using mapped_type = typename T::mapped_type; - using value_type = typename T::value_type; - using key_type = typename T::key_type; - using allocator_type = typename Json::allocator_type; - - static bool is(const Json& val) noexcept - { - if (!val.is_object()) - return false; - for (const auto& item : val.object_range()) - { - Json j(item.key()); - if (!j.template is()) - { - return false; - } - if (!item.value().template is()) - { - return false; - } - } - return true; - } - - static T as(const Json& val) - { - T result; - for (const auto& item : val.object_range()) - { - Json j(item.key()); - auto key = json_type_traits::as(j); - result.emplace(std::move(key), item.value().template as()); - } - - return result; - } - - static Json to_json(const T& val) - { - Json j(json_object_arg); - j.reserve(val.size()); - for (const auto& item : val) - { - auto temp = json_type_traits::to_json(item.first); - if (temp.is_string_view()) - { - j.try_emplace(typename Json::key_type(temp.as_string_view()), item.second); - } - else - { - typename Json::key_type key; - temp.dump(key); - j.try_emplace(std::move(key), item.second); - } - } - return j; - } - - static Json to_json(const T& val, const allocator_type& alloc) - { - Json j(json_object_arg, semantic_tag::none, alloc); - j.reserve(val.size()); - for (const auto& item : val) - { - auto temp = json_type_traits::to_json(item.first, alloc); - if (temp.is_string_view()) - { - j.try_emplace(typename Json::key_type(temp.as_string_view(), alloc), item.second); - } - else - { - typename Json::key_type key(alloc); - temp.dump(key); - j.try_emplace(std::move(key), item.second, alloc); - } - } - return j; - } - }; - - namespace tuple_detail - { - template - struct json_tuple_helper - { - using element_type = typename std::tuple_element::type; - using next = json_tuple_helper; - - static bool is(const Json& j) noexcept - { - if (j[Size-Pos].template is()) - { - return next::is(j); - } - else - { - return false; - } - } - - static void as(Tuple& tuple, const Json& j) - { - std::get(tuple) = j[Size-Pos].template as(); - next::as(tuple, j); - } - - static void to_json(const Tuple& tuple, Json& j) - { - j.push_back(json_type_traits::to_json(std::get(tuple))); - next::to_json(tuple, j); - } - }; - - template - struct json_tuple_helper<0, Size, Json, Tuple> - { - static bool is(const Json&) noexcept - { - return true; - } - - static void as(Tuple&, const Json&) - { - } - - static void to_json(const Tuple&, Json&) - { - } - }; - } // namespace tuple_detail - - template - struct json_type_traits> - { - private: - using helper = tuple_detail::json_tuple_helper>; - - public: - using allocator_type = typename Json::allocator_type; - - static bool is(const Json& j) noexcept - { - return helper::is(j); - } - - static std::tuple as(const Json& j) - { - std::tuple buff; - helper::as(buff, j); - return buff; - } - - static Json to_json(const std::tuple& val) - { - Json j(json_array_arg); - j.reserve(sizeof...(E)); - helper::to_json(val, j); - return j; - } - - static Json to_json(const std::tuple& val, - const allocator_type& alloc) - { - Json j(json_array_arg, alloc); - j.reserve(sizeof...(E)); - helper::to_json(val, j); - return j; - } - }; - - template - struct json_type_traits> - { - public: - using allocator_type = typename Json::allocator_type; - - static bool is(const Json& j) noexcept - { - return j.is_array() && j.size() == 2; - } - - static std::pair as(const Json& j) - { - return std::make_pair(j[0].template as(),j[1].template as()); - } - - static Json to_json(const std::pair& val) - { - Json j(json_array_arg); - j.reserve(2); - j.push_back(val.first); - j.push_back(val.second); - return j; - } - - static Json to_json(const std::pair& val, const allocator_type& alloc) - { - Json j(json_array_arg, alloc); - j.reserve(2); - j.push_back(val.first); - j.push_back(val.second); - return j; - } - }; - - template - struct json_type_traits::value>::type> - { - public: - using allocator_type = typename Json::allocator_type; - - static bool is(const Json& j) noexcept - { - return j.is_byte_string(); - } - - static T as(const Json& j) - { - return j.template as_byte_string(); - } - - static Json to_json(const T& val, - const allocator_type& alloc = allocator_type()) - { - return Json(byte_string_arg, val, semantic_tag::none, alloc); - } - }; - - template - struct json_type_traits, - typename std::enable_if>::value && - !std::is_polymorphic::value - >::type> - { - using allocator_type = typename Json::allocator_type; - - static bool is(const Json& j) noexcept - { - return j.is_null() || j.template is(); - } - - static std::shared_ptr as(const Json& j) - { - return j.is_null() ? std::shared_ptr(nullptr) : std::make_shared(j.template as()); - } - - static Json to_json(const std::shared_ptr& ptr, - const allocator_type& alloc = allocator_type()) - { - if (ptr.get() != nullptr) - { - Json j(*ptr, alloc); - return j; - } - else - { - return Json::null(); - } - } - }; - - template - struct json_type_traits, - typename std::enable_if>::value && - !std::is_polymorphic::value - >::type> - { - using allocator_type = typename Json::allocator_type; - - static bool is(const Json& j) noexcept - { - return j.is_null() || j.template is(); - } - - static std::unique_ptr as(const Json& j) - { - return j.is_null() ? std::unique_ptr(nullptr) : jsoncons::make_unique(j.template as()); - } - - static Json to_json(const std::unique_ptr& ptr, - const allocator_type& alloc = allocator_type()) - { - if (ptr.get() != nullptr) - { - Json j(*ptr, alloc); - return j; - } - else - { - return Json::null(); - } - } - }; - - template - struct json_type_traits, - typename std::enable_if>::value>::type> - { - using allocator_type = typename Json::allocator_type; - public: - static bool is(const Json& j) noexcept - { - return j.is_null() || j.template is(); - } - - static jsoncons::optional as(const Json& j) - { - return j.is_null() ? jsoncons::optional() : jsoncons::optional(j.template as()); - } - - static Json to_json(const jsoncons::optional& val, - const allocator_type& alloc = allocator_type()) - { - return val.has_value() ? Json(*val, alloc) : Json::null(); - } - }; - - template - struct json_type_traits - { - using allocator_type = typename Json::allocator_type; - - public: - static bool is(const Json& j) noexcept - { - return j.is_byte_string_view(); - } - - static byte_string_view as(const Json& j) - { - return j.as_byte_string_view(); - } - - static Json to_json(const byte_string_view& val, const allocator_type& alloc = allocator_type()) - { - return Json(byte_string_arg, val, semantic_tag::none, alloc); - } - }; - - // basic_bigint - - template - struct json_type_traits> - { - public: - using allocator_type = typename Json::allocator_type; - using char_type = typename Json::char_type; - - static bool is(const Json& j) noexcept - { - switch (j.type()) - { - case json_type::string_value: - return jsoncons::detail::is_base10(j.as_string_view().data(), j.as_string_view().length()); - case json_type::int64_value: - case json_type::uint64_value: - return true; - default: - return false; - } - } - - static basic_bigint as(const Json& j) - { - switch (j.type()) - { - case json_type::string_value: - if (!jsoncons::detail::is_base10(j.as_string_view().data(), j.as_string_view().length())) - { - JSONCONS_THROW(conv_error(conv_errc::not_bigint)); - } - return basic_bigint::from_string(j.as_string_view().data(), j.as_string_view().length()); - case json_type::half_value: - case json_type::double_value: - return basic_bigint(j.template as()); - case json_type::int64_value: - return basic_bigint(j.template as()); - case json_type::uint64_value: - return basic_bigint(j.template as()); - default: - JSONCONS_THROW(conv_error(conv_errc::not_bigint)); - } - } - - static Json to_json(const basic_bigint& val) - { - std::basic_string s; - val.write_string(s); - return Json(s,semantic_tag::bigint); - } - - static Json to_json(const basic_bigint& val, const allocator_type&) - { - std::basic_string s; - val.write_string(s); - return Json(s,semantic_tag::bigint); - } - }; - - // std::valarray - - template - struct json_type_traits> - { - using allocator_type = typename Json::allocator_type; - - static bool is(const Json& j) noexcept - { - bool result = j.is_array(); - if (result) - { - for (auto e : j.array_range()) - { - if (!e.template is()) - { - result = false; - break; - } - } - } - return result; - } - - static std::valarray as(const Json& j) - { - if (j.is_array()) - { - std::valarray v(j.size()); - for (std::size_t i = 0; i < j.size(); ++i) - { - v[i] = j[i].template as(); - } - return v; - } - else - { - JSONCONS_THROW(conv_error(conv_errc::not_array)); - } - } - - static Json to_json(const std::valarray& val) - { - Json j(json_array_arg); - auto first = std::begin(val); - auto last = std::end(val); - std::size_t size = std::distance(first,last); - j.reserve(size); - for (auto it = first; it != last; ++it) - { - j.push_back(*it); - } - return j; - } - - static Json to_json(const std::valarray& val, const allocator_type& alloc) - { - Json j(json_array_arg, alloc); - auto first = std::begin(val); - auto last = std::end(val); - std::size_t size = std::distance(first,last); - j.reserve(size); - for (auto it = first; it != last; ++it) - { - j.push_back(*it); - } - return j; - } - }; +template +struct json_type_traits +{ + using allocator_type = typename Json::allocator_type; -#if defined(JSONCONS_HAS_STD_VARIANT) + static constexpr bool is_compatible = false; -namespace variant_detail -{ - template - typename std::enable_if, bool>::type - is_variant(const Json& /*j*/) + static constexpr bool is(const Json&) noexcept { return false; } - template - typename std::enable_if, bool>::type - is_variant(const Json& j) + static T as(const Json&) { - if (j.template is()) - { - return true; - } - else - { - return is_variant(j); - } + static_assert(unimplemented::value, "as not implemented"); } - template - typename std::enable_if, Variant>::type - as_variant(const Json& /*j*/) + static Json to_json(const T&) { - JSONCONS_THROW(conv_error(conv_errc::not_variant)); + static_assert(unimplemented::value, "to_json not implemented"); } - template - typename std::enable_if, Variant>::type - as_variant(const Json& j) + static Json to_json(const T&, const allocator_type&) { - if (j.template is()) - { - Variant var(j.template as()); - return var; - } - else - { - return as_variant(j); - } + static_assert(unimplemented::value, "to_json not implemented"); } +}; - template - struct variant_to_json_visitor - { - Json& j_; - - variant_to_json_visitor(Json& j) : j_(j) {} - - template - void operator()(const T& value) const - { - j_ = value; - } - }; - -} // namespace variant_detail - - template - struct json_type_traits> - { - public: - using variant_type = typename std::variant; - using allocator_type = typename Json::allocator_type; - - static bool is(const Json& j) noexcept - { - return variant_detail::is_variant<0,Json,variant_type, VariantTypes...>(j); - } - - static std::variant as(const Json& j) - { - return variant_detail::as_variant<0,Json,variant_type, VariantTypes...>(j); - } - - static Json to_json(const std::variant& var) - { - Json j(json_array_arg); - variant_detail::variant_to_json_visitor visitor(j); - std::visit(visitor, var); - return j; - } - - static Json to_json(const std::variant& var, - const allocator_type& alloc) - { - Json j(json_array_arg, alloc); - variant_detail::variant_to_json_visitor visitor(j); - std::visit(visitor, var); - return j; - } - }; -#endif - - // std::chrono::duration - template - struct json_type_traits> - { - using duration_type = std::chrono::duration; - - using allocator_type = typename Json::allocator_type; - - static constexpr int64_t nanos_in_milli = 1000000; - static constexpr int64_t nanos_in_second = 1000000000; - static constexpr int64_t millis_in_second = 1000; - - static bool is(const Json& j) noexcept - { - return (j.tag() == semantic_tag::epoch_second || j.tag() == semantic_tag::epoch_milli || j.tag() == semantic_tag::epoch_nano); - } - - static duration_type as(const Json& j) - { - return from_json_(j); - } - - static Json to_json(const duration_type& val, const allocator_type& = allocator_type()) - { - return to_json_(val); - } - - template - static - typename std::enable_if>::value, duration_type>::type - from_json_(const Json& j) - { - if (j.is_int64() || j.is_uint64() || j.is_double()) - { - auto count = j.template as(); - switch (j.tag()) - { - case semantic_tag::epoch_second: - return duration_type(count); - case semantic_tag::epoch_milli: - return duration_type(count == 0 ? 0 : count/millis_in_second); - case semantic_tag::epoch_nano: - return duration_type(count == 0 ? 0 : count/nanos_in_second); - default: - return duration_type(count); - } - } - else if (j.is_string()) - { - switch (j.tag()) - { - case semantic_tag::epoch_second: - { - auto count = j.template as(); - return duration_type(count); - } - case semantic_tag::epoch_milli: - { - auto sv = j.as_string_view(); - bigint n = bigint::from_string(sv.data(), sv.length()); - if (n != 0) - { - n = n / millis_in_second; - } - return duration_type(static_cast(n)); - } - case semantic_tag::epoch_nano: - { - auto sv = j.as_string_view(); - bigint n = bigint::from_string(sv.data(), sv.length()); - if (n != 0) - { - n = n / nanos_in_second; - } - return duration_type(static_cast(n)); - } - default: - { - auto count = j.template as(); - return duration_type(count); - } - } - } - else - { - return duration_type(); - } - } - - template - static - typename std::enable_if::value, duration_type>::type - from_json_(const Json& j) - { - if (j.is_int64() || j.is_uint64()) - { - auto count = j.template as(); - switch (j.tag()) - { - case semantic_tag::epoch_second: - return duration_type(count*millis_in_second); - case semantic_tag::epoch_milli: - return duration_type(count); - case semantic_tag::epoch_nano: - return duration_type(count == 0 ? 0 : count/nanos_in_milli); - default: - return duration_type(count); - } - } - else if (j.is_double()) - { - auto count = j.template as(); - switch (j.tag()) - { - case semantic_tag::epoch_second: - return duration_type(static_cast(count * millis_in_second)); - case semantic_tag::epoch_milli: - return duration_type(static_cast(count)); - case semantic_tag::epoch_nano: - return duration_type(count == 0 ? 0 : static_cast(count / nanos_in_milli)); - default: - return duration_type(static_cast(count)); - } - } - else if (j.is_string()) - { - switch (j.tag()) - { - case semantic_tag::epoch_second: - { - auto count = j.template as(); - return duration_type(count*millis_in_second); - } - case semantic_tag::epoch_milli: - { - auto sv = j.as_string_view(); - Rep n{0}; - auto result = jsoncons::detail::dec_to_integer(sv.data(), sv.size(), n); - if (!result) - { - return duration_type(); - } - return duration_type(n); - } - case semantic_tag::epoch_nano: - { - auto sv = j.as_string_view(); - bigint n = bigint::from_string(sv.data(), sv.length()); - if (n != 0) - { - n = n / nanos_in_milli; - } - return duration_type(static_cast(n)); - } - default: - { - auto count = j.template as(); - return duration_type(count); - } - } - } - else - { - return duration_type(); - } - } - - template - static - typename std::enable_if::value, duration_type>::type - from_json_(const Json& j) - { - if (j.is_int64() || j.is_uint64() || j.is_double()) - { - auto count = j.template as(); - switch (j.tag()) - { - case semantic_tag::epoch_second: - return duration_type(count*nanos_in_second); - case semantic_tag::epoch_milli: - return duration_type(count*nanos_in_milli); - case semantic_tag::epoch_nano: - return duration_type(count); - default: - return duration_type(count); - } - } - else if (j.is_double()) - { - auto count = j.template as(); - switch (j.tag()) - { - case semantic_tag::epoch_second: - return duration_type(static_cast(count * nanos_in_second)); - case semantic_tag::epoch_milli: - return duration_type(static_cast(count * nanos_in_milli)); - case semantic_tag::epoch_nano: - return duration_type(static_cast(count)); - default: - return duration_type(static_cast(count)); - } - } - else if (j.is_string()) - { - auto count = j.template as(); - switch (j.tag()) - { - case semantic_tag::epoch_second: - return duration_type(count*nanos_in_second); - case semantic_tag::epoch_milli: - return duration_type(count*nanos_in_milli); - case semantic_tag::epoch_nano: - return duration_type(count); - default: - return duration_type(count); - } - } - else - { - return duration_type(); - } - } - - template - static - typename std::enable_if>::value,Json>::type - to_json_(const duration_type& val) - { - return Json(val.count(), semantic_tag::epoch_second); - } - - template - static - typename std::enable_if::value,Json>::type - to_json_(const duration_type& val) - { - return Json(val.count(), semantic_tag::epoch_milli); - } - - template - static - typename std::enable_if::value,Json>::type - to_json_(const duration_type& val) - { - return Json(val.count(), semantic_tag::epoch_nano); - } - }; - - // std::nullptr_t - template - struct json_type_traits - { - using allocator_type = typename Json::allocator_type; - - static bool is(const Json& j) noexcept - { - return j.is_null(); - } - - static std::nullptr_t as(const Json& j) - { - if (!j.is_null()) - { - JSONCONS_THROW(conv_error(conv_errc::not_nullptr)); - } - return nullptr; - } - - static Json to_json(const std::nullptr_t&, const allocator_type& = allocator_type()) - { - return Json::null(); - } - }; - - // std::bitset - - struct null_back_insertable_byte_container - { - using value_type = uint8_t; - - void push_back(value_type) - { - } - }; - - template - struct json_type_traits> - { - using allocator_type = typename Json::allocator_type; - - static bool is(const Json& j) noexcept - { - if (j.is_byte_string()) - { - return true; - } - else if (j.is_string()) - { - jsoncons::string_view sv = j.as_string_view(); - null_back_insertable_byte_container cont; - auto result = decode_base16(sv.begin(), sv.end(), cont); - return result.ec == conv_errc::success ? true : false; - } - return false; - } - - static std::bitset as(const Json& j) - { - if (j.template is()) - { - auto bits = j.template as(); - std::bitset bs = static_cast(bits); - return bs; - } - else if (j.is_byte_string() || j.is_string()) - { - std::bitset bs; - std::vector bits; - if (j.is_byte_string()) - { - bits = j.template as>(); - } - else - { - jsoncons::string_view sv = j.as_string_view(); - auto result = decode_base16(sv.begin(), sv.end(), bits); - if (result.ec != conv_errc::success) - { - JSONCONS_THROW(conv_error(conv_errc::not_bitset)); - } - } - std::uint8_t byte = 0; - std::uint8_t mask = 0; - - std::size_t pos = 0; - for (std::size_t i = 0; i < N; ++i) - { - if (mask == 0) - { - if (pos >= bits.size()) - { - JSONCONS_THROW(conv_error(conv_errc::not_bitset)); - } - byte = bits.at(pos++); - mask = 0x80; - } - - if (byte & mask) - { - bs[i] = 1; - } - - mask = static_cast(mask >> 1); - } - return bs; - } - else - { - JSONCONS_THROW(conv_error(conv_errc::not_bitset)); - } - } - - static Json to_json(const std::bitset& val, - const allocator_type& alloc = allocator_type()) - { - std::vector bits; - - uint8_t byte = 0; - uint8_t mask = 0x80; +namespace detail { - for (std::size_t i = 0; i < N; ++i) - { - if (val[i]) - { - byte |= mask; - } +// is_json_type_traits_unspecialized +template +struct is_json_type_traits_unspecialized : std::false_type {}; - mask = static_cast(mask >> 1); +// is_json_type_traits_unspecialized +template +struct is_json_type_traits_unspecialized::is_compatible>::value>::type +> : std::true_type {}; - if (mask == 0) - { - bits.push_back(byte); - byte = 0; - mask = 0x80; - } - } +} // namespace detail - // Encode remainder - if (mask != 0x80) - { - bits.push_back(byte); - } +// is_json_type_traits_specialized +template +struct is_json_type_traits_specialized : std::false_type {}; - Json j(byte_string_arg, bits, semantic_tag::base16, alloc); - return j; - } - }; +template +struct is_json_type_traits_specialized::value +>::type> : std::true_type {}; } // namespace jsoncons diff --git a/include/jsoncons/json_visitor.hpp b/include/jsoncons/json_visitor.hpp index 959b403..bb2a618 100644 --- a/include/jsoncons/json_visitor.hpp +++ b/include/jsoncons/json_visitor.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -9,7 +9,6 @@ #include #include -#include #include #include #include @@ -20,7 +19,7 @@ #include #include #include -#include +#include #include #include #include @@ -816,186 +815,12 @@ namespace jsoncons { } }; - template - class basic_json_diagnostics_visitor : public basic_default_json_visitor - { - public: - using stream_type = std::basic_ostream; - using string_type = std::basic_string; - - private: - using supertype = basic_default_json_visitor; - using string_view_type = typename supertype::string_view_type; - - struct enabler {}; - - static constexpr CharT visit_begin_array_name[] = {'v','i','s','i','t','_','b','e','g','i','n','_','a','r','r','a','y', 0}; - static constexpr CharT visit_end_array_name[] = {'v','i','s','i','t','_','e','n','d','_','a','r','r','a','y', 0}; - static constexpr CharT visit_begin_object_name[] = {'v','i','s','i','t','_','b','e','g','i','n','_','o','b','j','e','c','t', 0}; - static constexpr CharT visit_end_object_name[] = {'v','i','s','i','t','_','e','n','d','_','o','b','j','e','c','t', 0}; - static constexpr CharT visit_key_name[] = {'v','i','s','i','t','_','k','e','y', 0}; - static constexpr CharT visit_string_name[] = {'v','i','s','i','t','_','s','t','r','i','n','g', 0}; - static constexpr CharT visit_byte_string_name[] = {'v','i','s','i','t','_','b','y','t','e','_','s','t','r','i','n','g', 0}; - static constexpr CharT visit_null_name[] = {'v','i','s','i','t','_','n','u','l','l', 0}; - static constexpr CharT visit_bool_name[] = {'v','i','s','i','t','_','b','o','o','l', 0}; - static constexpr CharT visit_uint64_name[] = {'v','i','s','i','t','_','u','i','n','t','6','4', 0}; - static constexpr CharT visit_int64_name[] = {'v','i','s','i','t','_','i','n','t','6','4', 0}; - static constexpr CharT visit_half_name[] = {'v','i','s','i','t','_','h','a','l','f', 0}; - static constexpr CharT visit_double_name[] = {'v','i','s','i','t','_','d','o','u','b','l','e', 0}; - - static constexpr CharT separator_ = ':'; - - stream_type& output_; - string_type indentation_; - long level_{0}; - - public: - // If CharT is char, then enable the default constructor which binds to - // std::cout. - template - basic_json_diagnostics_visitor( - typename std::enable_if::value, U>::type = enabler{}) - : basic_json_diagnostics_visitor(std::cout) - { - } - - // If CharT is wchar_t, then enable the default constructor which binds - // to std::wcout. - template - basic_json_diagnostics_visitor( - typename std::enable_if::value, U>::type = enabler{}) - : basic_json_diagnostics_visitor(std::wcout) - { - } - - explicit basic_json_diagnostics_visitor( - stream_type& output, - string_type indentation = string_type()) - : output_(output), - indentation_(std::move(indentation)) - { - } - - private: - void indent() - { - for (long i=0; i= 201703L -// not needed for C++17 -#else - template constexpr C basic_json_diagnostics_visitor::visit_begin_array_name[]; - template constexpr C basic_json_diagnostics_visitor::visit_end_array_name[]; - template constexpr C basic_json_diagnostics_visitor::visit_begin_object_name[]; - template constexpr C basic_json_diagnostics_visitor::visit_end_object_name[]; - template constexpr C basic_json_diagnostics_visitor::visit_key_name[]; - template constexpr C basic_json_diagnostics_visitor::visit_string_name[]; - template constexpr C basic_json_diagnostics_visitor::visit_byte_string_name[]; - template constexpr C basic_json_diagnostics_visitor::visit_null_name[]; - template constexpr C basic_json_diagnostics_visitor::visit_bool_name[]; - template constexpr C basic_json_diagnostics_visitor::visit_uint64_name[]; - template constexpr C basic_json_diagnostics_visitor::visit_int64_name[]; - template constexpr C basic_json_diagnostics_visitor::visit_half_name[]; - template constexpr C basic_json_diagnostics_visitor::visit_double_name[]; -#endif // C++17 check - using json_visitor = basic_json_visitor; using wjson_visitor = basic_json_visitor; using default_json_visitor = basic_default_json_visitor; using wdefault_json_visitor = basic_default_json_visitor; - using json_diagnostics_visitor = basic_json_diagnostics_visitor; - using wjson_diagnostics_visitor = basic_json_diagnostics_visitor; - } // namespace jsoncons #endif // JSONCONS_JSON_VISITOR_HPP diff --git a/include/jsoncons/pretty_print.hpp b/include/jsoncons/pretty_print.hpp index 75427b5..f50d8ca 100644 --- a/include/jsoncons/pretty_print.hpp +++ b/include/jsoncons/pretty_print.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -14,7 +14,6 @@ #include #include #include -#include namespace jsoncons { diff --git a/include/jsoncons/reflect/decode_traits.hpp b/include/jsoncons/reflect/decode_traits.hpp new file mode 100644 index 0000000..04ab2c8 --- /dev/null +++ b/include/jsoncons/reflect/decode_traits.hpp @@ -0,0 +1,733 @@ +// Copyright 2013-2026 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_REFLECT_DECODE_TRAITS_HPP +#define JSONCONS_REFLECT_DECODE_TRAITS_HPP + +#include +#include +#include +#include +#include +#include +#include // std::enable_if, std::true_type, std::false_type +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace jsoncons { +namespace reflect { + +// decode_traits + +template +struct decode_traits +{ + using value_type = T; + using result_type = read_result; + + template + static result_type try_decode(const allocator_set& aset, + basic_staj_cursor& cursor) + { + std::size_t line = cursor.line(); + std::size_t column = cursor.column(); + + using json_type = basic_json; + auto r1 = try_to_json(make_alloc_set(aset.get_temp_allocator(), aset.get_temp_allocator()), + cursor); + if (JSONCONS_UNLIKELY(!r1)) + { + return result_type(jsoncons::unexpect, r1.error().code(), r1.error().message_arg(), line, column); + } + auto r2 = (*r1).template try_as(aset); + if (JSONCONS_UNLIKELY(!r2)) + { + return result_type(jsoncons::unexpect, r2.error().code(), r2.error().message_arg(), line, column); + } + return result_type(std::move(*r2)); + } +}; + +template +struct decode_traits::value +>::type> +{ + using value_type = T; + using result_type = read_result; + + template + static result_type try_decode(const allocator_set& aset, basic_staj_cursor& cursor) + { + auto j_result = try_to_json(aset, cursor); + if (JSONCONS_UNLIKELY(!j_result)) + { + return result_type(jsoncons::unexpect, std::move(j_result.error())); + } + return j_result; + } +}; + +// specializations + +// primitive + +template +struct decode_traits::value +>::type> +{ + using value_type = T; + using result_type = read_result; + + template + static result_type try_decode(const allocator_set&, basic_staj_cursor& cursor) + { + std::error_code ec; + + T v = cursor.current().template get(ec); + return ec ? result_type{jsoncons::unexpect, ec, cursor.line(), cursor.column()} : result_type{std::move(v)}; + } +}; + +// string + +template +struct decode_traits::value>::type> +{ + using value_type = T; + using result_type = read_result; + using char_type = typename T::value_type; + using string_view_type = basic_string_view; + + template + static result_type try_decode(const allocator_set& aset, + basic_staj_cursor& cursor, + typename std::enable_if::value,int>::type = 0) + { + std::error_code ec; + + auto sv = cursor.current().template get(ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, cursor.line(), cursor.column()}; + } + + return result_type{jsoncons::make_obj_using_allocator(aset.get_allocator(), sv.data(), sv.size())}; + } + + template + static result_type try_decode(const allocator_set& aset, + basic_staj_cursor& cursor, + typename std::enable_if::value,int>::type = 0) + { + using temp_char_allocator_type = typename std::allocator_traits:: template rebind_alloc; + using buffer_type = std::basic_string,temp_char_allocator_type>; + + std::error_code ec; + + auto val = cursor.current().template get>(ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type(jsoncons::unexpect, ec, cursor.line(), cursor.column()); + } + buffer_type buf(aset.get_temp_allocator());; + unicode_traits::convert(val.data(), val.size(), buf); + return result_type{in_place, buf.data(), buf.size()}; + } +}; + +// std::pair + +template +struct decode_traits> +{ + using value_type = std::pair; + using result_type = read_result; + + template + static result_type try_decode(const allocator_set& aset, basic_staj_cursor& cursor) + { + std::error_code ec; + + cursor.array_expected(ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type(jsoncons::unexpect, ec, cursor.line(), cursor.column()); + } + if (cursor.current().event_type() != staj_event_type::begin_array) + { + return result_type(jsoncons::unexpect, conv_errc::not_pair, cursor.line(), cursor.column()); + } + cursor.next(ec); // skip past array + if (JSONCONS_UNLIKELY(ec)) + { + return result_type(jsoncons::unexpect, ec, cursor.line(), cursor.column()); + } + + auto r1 = decode_traits::try_decode(aset, cursor); + if (JSONCONS_UNLIKELY(!r1)) + { + return result_type(jsoncons::unexpect, r1.error()); + } + cursor.next(ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type(jsoncons::unexpect, ec, cursor.line(), cursor.column()); + } + auto r2 = decode_traits::try_decode(aset, cursor); + if (JSONCONS_UNLIKELY(!r2)) + { + return result_type(jsoncons::unexpect, r2.error()); + } + cursor.next(ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type(jsoncons::unexpect, ec, cursor.line(), cursor.column()); + } + + if (cursor.current().event_type() != staj_event_type::end_array) + { + return result_type(jsoncons::unexpect, conv_errc::not_pair, cursor.line(), cursor.column()); + } + return result_type{jsoncons::make_obj_using_allocator(aset.get_allocator(), std::move(*r1), std::move(*r2))}; + } +}; + +// vector like +template +struct decode_traits::value && + ext_traits::is_array_like::value && + ext_traits::is_back_insertable::value && + !ext_traits::is_typed_array::value +>::type> +{ + using element_type = typename T::value_type; + using value_type = T; + using result_type = read_result; + + template + static result_type try_decode(const allocator_set& aset, basic_staj_cursor& cursor) + { + std::error_code ec; + T v(jsoncons::make_obj_using_allocator(aset.get_allocator())); + + cursor.array_expected(ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type(jsoncons::unexpect, ec, cursor.line(), cursor.column()); + } + if (cursor.current().event_type() != staj_event_type::begin_array) + { + return result_type(jsoncons::unexpect, conv_errc::not_vector, cursor.line(), cursor.column()); + } + cursor.next(ec); + if (JSONCONS_UNLIKELY(ec)) { return result_type(jsoncons::unexpect, ec, cursor.line(), cursor.column()); } + while (cursor.current().event_type() != staj_event_type::end_array && !ec) + { + auto r = decode_traits::try_decode(aset, cursor); + if (!r) + { + return result_type(jsoncons::unexpect, r.error()); + } + v.push_back(std::move(*r)); + cursor.next(ec); + if (JSONCONS_UNLIKELY(ec)) { return result_type(jsoncons::unexpect, ec, cursor.line(), cursor.column()); } + } + return result_type{std::move(v)}; + } +}; + +template +struct decode_traits::value && + ext_traits::is_array_like::value && + ext_traits::is_back_insertable_byte_container::value && + ext_traits::is_typed_array::value +>::type> +{ + using element_type = typename T::value_type; + using value_type = T; + using result_type = read_result; + + template + static result_type try_decode(const allocator_set& aset, + basic_staj_cursor& cursor) + { + std::error_code ec; + + cursor.array_expected(ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type(jsoncons::unexpect, ec, cursor.line(), cursor.column()); + } + switch (cursor.current().event_type()) + { + case staj_event_type::byte_string_value: + { + auto bytes = cursor.current().template get(ec); + if (!ec) + { + T v; + if (cursor.current().size() > 0) + { + reserve_storage(typename std::integral_constant::value>::type(), v, cursor.current().size()); + } + for (auto ch : bytes) + { + v.push_back(static_cast(ch)); + } + cursor.next(ec); + return result_type{std::move(v)}; + } + else + { + return result_type(jsoncons::unexpect, ec, cursor.line(), cursor.column()); + } + } + case staj_event_type::begin_array: + { + T v = jsoncons::make_obj_using_allocator(aset.get_allocator()); + if (cursor.current().size() > 0) + { + reserve_storage(typename std::integral_constant::value>::type(), v, cursor.current().size()); + } + cursor.next(ec); + while (cursor.current().event_type() != staj_event_type::end_array && !ec) + { + auto r = decode_traits::try_decode(aset, cursor); + if (!r) + { + return result_type(jsoncons::unexpect, r.error()); + } + v.push_back(*r); + //v[i] = std::move(*r); + cursor.next(ec); + } + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, conv_errc::not_vector, cursor.line(), cursor.column()}; + } + + return result_type{std::move(v)}; + } + default: + { + return result_type(jsoncons::unexpect, conv_errc::not_vector, cursor.line(), cursor.column()); + } + } + } + + static void reserve_storage(std::true_type, T& v, std::size_t new_cap) + { + v.reserve(new_cap); + } + + static void reserve_storage(std::false_type, T&, std::size_t) + { + } +}; + +template +struct decode_traits::value && + ext_traits::is_array_like::value && + ext_traits::is_back_insertable::value && + !ext_traits::is_back_insertable_byte_container::value && + ext_traits::is_typed_array::value +>::type> +{ + using element_type = typename T::value_type; + using value_type = T; + using result_type = read_result; + + template + static result_type try_decode(const allocator_set& aset, basic_staj_cursor& cursor) + { + std::error_code ec; + + cursor.array_expected(ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type(jsoncons::unexpect, ec, cursor.line(), cursor.column()); + } + switch (cursor.current().event_type()) + { + case staj_event_type::begin_array: + { + T v = jsoncons::make_obj_using_allocator(aset.get_allocator()); + if (cursor.current().size() > 0) + { + reserve_storage(typename std::integral_constant::value>::type(), v, cursor.current().size()); + } + cursor.next(ec); + while (cursor.current().event_type() != staj_event_type::end_array && !ec) + { + auto r = decode_traits::try_decode(aset, cursor); + if (!r) + { + return result_type(jsoncons::unexpect, r.error()); + } + v.push_back(*r); + //v[i] = std::move(*r); + cursor.next(ec); + } + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, conv_errc::not_vector, cursor.line(), cursor.column()}; + } + return result_type{std::move(v)}; + } + default: + { + return result_type(jsoncons::unexpect, conv_errc::not_vector, cursor.line(), cursor.column()); + } + } + } + + static void reserve_storage(std::true_type, T& v, std::size_t new_cap) + { + v.reserve(new_cap); + } + + static void reserve_storage(std::false_type, T&, std::size_t) + { + } +}; + +// set like +template +struct decode_traits::value && + ext_traits::is_array_like::value && + !ext_traits::is_back_insertable::value && + ext_traits::is_insertable::value +>::type> +{ + using element_type = typename T::value_type; + using value_type = T; + using result_type = read_result; + + template + static result_type try_decode(const allocator_set& aset, basic_staj_cursor& cursor) + { + std::error_code ec; + T v = jsoncons::make_obj_using_allocator(aset.get_allocator()); + + cursor.array_expected(ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type(jsoncons::unexpect, ec, cursor.line(), cursor.column()); + } + if (cursor.current().event_type() != staj_event_type::begin_array) + { + return result_type(jsoncons::unexpect, conv_errc::not_vector, cursor.line(), cursor.column()); + } + if (cursor.current().size() > 0) + { + reserve_storage(typename std::integral_constant::value>::type(), v, cursor.current().size()); + } + cursor.next(ec); + while (cursor.current().event_type() != staj_event_type::end_array && !ec) + { + auto r = decode_traits::try_decode(aset, cursor); + if (!r) + { + return result_type(jsoncons::unexpect, r.error()); + } + v.insert(std::move(*r)); + if (JSONCONS_UNLIKELY(ec)) {return result_type(jsoncons::unexpect, ec, cursor.line(), cursor.column());} + cursor.next(ec); + if (JSONCONS_UNLIKELY(ec)) {return result_type{jsoncons::unexpect, ec, cursor.line(), cursor.column()};} + } + return result_type{std::move(v)}; + } + + static void reserve_storage(std::true_type, T& v, std::size_t new_cap) + { + v.reserve(new_cap); + } + + static void reserve_storage(std::false_type, T&, std::size_t) + { + } +}; + +// std::forward_list +template +struct decode_traits::value && + ext_traits::is_array_like::value && + !ext_traits::is_back_insertable::value && + !ext_traits::is_insertable::value && + ext_traits::is_front_insertable::value +>::type> +{ + using element_type = typename T::value_type; + using value_type = T; + using result_type = read_result; + + template + static result_type try_decode(const allocator_set& aset, basic_staj_cursor& cursor) + { + std::error_code ec; + + cursor.array_expected(ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type(jsoncons::unexpect, ec, cursor.line(), cursor.column()); + } + + T v = jsoncons::make_obj_using_allocator(aset.get_allocator()); + if (cursor.current().event_type() != staj_event_type::begin_array) + { + return result_type(jsoncons::unexpect, conv_errc::not_vector, cursor.line(), cursor.column()); + } + if (cursor.current().size() > 0) + { + reserve_storage(typename std::integral_constant::value>::type(), v, cursor.current().size()); + } + cursor.next(ec); + while (cursor.current().event_type() != staj_event_type::end_array && !ec) + { + auto r = decode_traits::try_decode(aset, cursor); + if (!r) + { + return result_type(jsoncons::unexpect, r.error()); + } + v.push_front(std::move(*r)); + if (JSONCONS_UNLIKELY(ec)) {return result_type(jsoncons::unexpect, ec, cursor.line(), cursor.column());} + cursor.next(ec); + if (JSONCONS_UNLIKELY(ec)) {return result_type{jsoncons::unexpect, ec, cursor.line(), cursor.column()};} + } + return result_type{std::move(v)}; + } + + static void reserve_storage(std::true_type, T& v, std::size_t new_cap) + { + v.reserve(new_cap); + } + + static void reserve_storage(std::false_type, T&, std::size_t) + { + } +}; + +// std::array + +template +struct decode_traits> +{ + using element_type = typename std::array::value_type; + using value_type = typename std::array; + using result_type = read_result; + + template + static result_type try_decode(const allocator_set& aset, basic_staj_cursor& cursor) + { + std::error_code ec; + + std::array v; + cursor.array_expected(ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type(jsoncons::unexpect, ec, cursor.line(), cursor.column()); + } + if (cursor.current().event_type() != staj_event_type::begin_array) + { + return result_type{jsoncons::unexpect, conv_errc::not_vector, cursor.line(), cursor.column()}; + } + cursor.next(ec); + for (std::size_t i = 0; i < N && cursor.current().event_type() != staj_event_type::end_array && !ec; ++i) + { + auto r = decode_traits::try_decode(aset, cursor); + if (!r) + { + return result_type(jsoncons::unexpect, r.error()); + } + v[i] = std::move(*r); + cursor.next(ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, conv_errc::not_vector, cursor.line(), cursor.column()}; + } + } + return v; + } +}; + +// map like + +template +struct decode_traits::value && + ext_traits::is_map_like::value && + ext_traits::is_string::value +>::type> +{ + using mapped_type = typename T::mapped_type; + using value_type = T; + using key_type = typename T::key_type; + using result_type = read_result; + + template + static result_type try_decode(const allocator_set& aset, basic_staj_cursor& cursor) + { + std::error_code ec; + + auto val = jsoncons::make_obj_using_allocator(aset.get_allocator()); + if (cursor.current().event_type() != staj_event_type::begin_object) + { + return result_type{jsoncons::unexpect, conv_errc::not_map, cursor.line(), cursor.column()}; + } + if (cursor.current().size() > 0) + { + reserve_storage(typename std::integral_constant::value>::type(), val, cursor.current().size()); + } + cursor.next(ec); + + while (cursor.current().event_type() != staj_event_type::end_object && !ec) + { + if (cursor.current().event_type() != staj_event_type::key) + { + return result_type{jsoncons::unexpect, json_errc::expected_key, cursor.line(), cursor.column()}; + } + auto r1 = decode_traits::try_decode(aset, cursor); + if (!r1) + { + return result_type(jsoncons::unexpect, r1.error()); + } + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, cursor.line(), cursor.column()}; + } + cursor.next(ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, cursor.line(), cursor.column()}; + } + auto r2 = decode_traits::try_decode(aset, cursor); + if (!r2) + { + return result_type(jsoncons::unexpect, r2.error()); + } + val.emplace(std::move(*r1), std::move(*r2)); + cursor.next(ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, cursor.line(), cursor.column()}; + } + } + return result_type{std::move(val)}; + } + + static void reserve_storage(std::true_type, T& v, std::size_t new_cap) + { + v.reserve(new_cap); + } + + static void reserve_storage(std::false_type, T&, std::size_t) + { + } +}; + +template +struct decode_traits::value && + ext_traits::is_map_like::value && + std::is_integral::value +>::type> +{ + using mapped_type = typename T::mapped_type; + using value_type = T; + using key_type = typename T::key_type; + using result_type = read_result; + + template + static result_type try_decode(const allocator_set& aset, basic_staj_cursor& cursor) + { + std::error_code ec; + + T val; + if (cursor.current().event_type() != staj_event_type::begin_object) + { + return result_type{jsoncons::unexpect, conv_errc::not_map, cursor.line(), cursor.column()}; + } + if (cursor.current().size() > 0) + { + reserve_storage(typename std::integral_constant::value>::type(), val, cursor.current().size()); + } + cursor.next(ec); + + while (cursor.current().event_type() != staj_event_type::end_object && !ec) + { + if (cursor.current().event_type() != staj_event_type::key) + { + return result_type{jsoncons::unexpect, json_errc::expected_key, cursor.line(), cursor.column()}; + } + auto s = cursor.current().template get>(ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, cursor.line(), cursor.column()}; + } + key_type n{0}; + auto r = jsoncons::to_integer(s.data(), s.size(), n); + if (r.ec != std::errc{}) + { + return result_type{jsoncons::unexpect, json_errc::invalid_number, cursor.line(), cursor.column()}; + } + cursor.next(ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, cursor.line(), cursor.column()}; + } + auto r1 = decode_traits::try_decode(aset, cursor); + if (!r1) + { + return result_type(jsoncons::unexpect, r1.error()); + } + val.emplace(n, std::move(*r1)); + cursor.next(ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, cursor.line(), cursor.column()}; + } + } + return result_type{std::move(val)}; + } + + static void reserve_storage(std::true_type, T& v, std::size_t new_cap) + { + v.reserve(new_cap); + } + + static void reserve_storage(std::false_type, T&, std::size_t) + { + } +}; + +} // namespace reflect +} // namespace jsoncons + +#endif // JSONCONS_REFLECT_DECODE_TRAITS_HPP + diff --git a/include/jsoncons/reflect/encode_traits.hpp b/include/jsoncons/reflect/encode_traits.hpp new file mode 100644 index 0000000..5866a3c --- /dev/null +++ b/include/jsoncons/reflect/encode_traits.hpp @@ -0,0 +1,405 @@ +// Copyright 2013-2026 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_REFLECT_ENCODE_TRAITS_HPP +#define JSONCONS_REFLECT_ENCODE_TRAITS_HPP + +#include +#include +#include +#include +#include +#include +#include +#include // std::enable_if, std::true_type, std::false_type + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace jsoncons { +namespace reflect { + +// encode_traits + +template +struct encode_traits +{ +public: + template + static write_result try_encode(const allocator_set& aset, + const T& val, + basic_json_visitor& encoder) + { + auto j = json_conv_traits,T>::to_json( + make_alloc_set(aset.get_temp_allocator(), aset.get_temp_allocator()), val); + return j.try_dump(encoder); + } +}; + +// specializations + +// bool +template +struct encode_traits::value +>::type> +{ + template + static write_result try_encode(const allocator_set&, const T& val, + basic_json_visitor& encoder) + { + std::error_code ec; + encoder.bool_value(val,semantic_tag::none,ser_context(),ec); + if (JSONCONS_UNLIKELY(ec)) {return write_result{unexpect, ec};} + return write_result{}; + } +}; + +// uint +template +struct encode_traits::value +>::type> +{ + template + static write_result try_encode(const allocator_set&, const T& val, + basic_json_visitor& encoder) + { + std::error_code ec; + encoder.uint64_value(val,semantic_tag::none,ser_context(),ec); + if (JSONCONS_UNLIKELY(ec)) {return write_result{unexpect, ec};} + return write_result{}; + } +}; + +// int +template +struct encode_traits::value +>::type> +{ + template + static write_result try_encode(const allocator_set&, const T& val, + basic_json_visitor& encoder) + { + std::error_code ec; + encoder.int64_value(val,semantic_tag::none,ser_context(),ec); + if (JSONCONS_UNLIKELY(ec)) {return write_result{unexpect, ec};} + return write_result{}; + } +}; + +// float or double +template +struct encode_traits::value +>::type> +{ + template + static write_result try_encode(const allocator_set&, const T& val, + basic_json_visitor& encoder) + { + std::error_code ec; + encoder.double_value(val,semantic_tag::none,ser_context(),ec); + if (JSONCONS_UNLIKELY(ec)) {return write_result{unexpect, ec};} + return write_result{}; + } +}; + +// string +template +struct encode_traits::value /*&& + std::is_same::value*/ +>::type> +{ + template + static + typename std::enable_if::value,write_result>::type + try_encode(const allocator_set&, const T& val, + basic_json_visitor& encoder) + { + std::error_code ec; + encoder.string_value(val,semantic_tag::none,ser_context(),ec); + if (JSONCONS_UNLIKELY(ec)) {return write_result{unexpect, ec};} + return write_result{}; + } + + template + static + typename std::enable_if::value,write_result>::type + try_encode(const allocator_set& aset, const T& val, + basic_json_visitor& encoder) + { + std::error_code ec; + using temp_alloc_type = typename std::allocator_traits:: template rebind_alloc; + std::basic_string,temp_alloc_type> s(aset.get_temp_allocator()); + unicode_traits::convert(val.data(), val.size(), s); + encoder.string_value(basic_string_view(s.data(), s.length()), semantic_tag::none, ser_context(), ec); + if (JSONCONS_UNLIKELY(ec)) {return write_result{unexpect, ec};} + return write_result{}; + } +}; + +// std::pair + +template +struct encode_traits> +{ + using value_type = std::pair; + + template + static write_result try_encode(const allocator_set& aset, const value_type& val, + basic_json_visitor& encoder) + { + std::error_code ec; + encoder.begin_array(2,semantic_tag::none,ser_context(),ec); + if (JSONCONS_UNLIKELY(ec)) {return write_result{unexpect, ec};} + auto r1 = encode_traits::try_encode(aset, val.first, encoder); + if (JSONCONS_UNLIKELY(!r1)) {return r1;} + auto r2 = encode_traits::try_encode(aset, val.second, encoder); + if (JSONCONS_UNLIKELY(!r2)) {return r2;} + encoder.end_array(ser_context(),ec); + if (JSONCONS_UNLIKELY(ec)) {return write_result{unexpect, ec};} + return write_result{}; + } +}; + +// std::tuple + +namespace detail +{ + template + struct json_serialize_tuple_helper + { + using element_type = typename std::tuple_element::type; + using next = json_serialize_tuple_helper; + + template + static write_result try_encode(const allocator_set& aset, const Tuple& tuple, + basic_json_visitor& encoder) + { + auto r1 = encode_traits::try_encode(aset, std::get(tuple), encoder); + if (JSONCONS_UNLIKELY(!r1)) {return r1;} + auto r2 = next::try_encode(aset, tuple, encoder); + if (JSONCONS_UNLIKELY(!r2)) {return r2;} + return write_result{}; + } + }; + + template + struct json_serialize_tuple_helper<0, Size, Tuple> + { + template + static write_result try_encode(const allocator_set&, const Tuple&, + basic_json_visitor&) + { + return write_result{}; + } + }; +} // namespace detail + + +template +struct encode_traits> +{ + using value_type = std::tuple; + static constexpr std::size_t size = sizeof...(E); + + template + static write_result try_encode(const allocator_set& aset, const value_type& val, + basic_json_visitor& encoder) + { + using helper = detail::json_serialize_tuple_helper>; + + std::error_code ec; + encoder.begin_array(size,semantic_tag::none,ser_context(),ec); + if (JSONCONS_UNLIKELY(ec)) {return write_result{unexpect, ec};} + auto r = helper::try_encode(aset, val, encoder); + if (JSONCONS_UNLIKELY(!r)) {return r;} + encoder.end_array(ser_context(),ec); + if (JSONCONS_UNLIKELY(ec)) {return write_result{unexpect, ec};} + return write_result{}; + } +}; + +// vector like +template +struct encode_traits::value && + ext_traits::is_array_like_with_size::value && + !ext_traits::is_typed_array::value +>::type> +{ + using value_type = typename T::value_type; + + template + static write_result try_encode(const allocator_set& aset, const T& val, + basic_json_visitor& encoder) + { + std::error_code ec; + + encoder.begin_array(val.size(),semantic_tag::none,ser_context(),ec); + if (JSONCONS_UNLIKELY(ec)) {return write_result{unexpect, ec};} + for (auto it = std::begin(val); it != std::end(val); ++it) + { + auto r = encode_traits::try_encode(aset, *it, encoder); + if (JSONCONS_UNLIKELY(!r)) {return r;} + } + encoder.end_array(ser_context(), ec); + if (JSONCONS_UNLIKELY(ec)) {return write_result{unexpect, ec};} + return write_result{}; + } +}; + +template +struct encode_traits::value && + ext_traits::is_array_like_without_size::value && + !ext_traits::is_typed_array::value +>::type> +{ + using value_type = typename T::value_type; + + template + static write_result try_encode(const allocator_set& aset, const T& val, + basic_json_visitor& encoder) + { + std::error_code ec; + encoder.begin_array(std::distance(val.begin(), val.end()), semantic_tag::none,ser_context(),ec); + if (JSONCONS_UNLIKELY(ec)) {return write_result{unexpect, ec};} + for (auto it = std::begin(val); it != std::end(val); ++it) + { + auto r = encode_traits::try_encode(aset, *it, encoder); + if (JSONCONS_UNLIKELY(!r)) {return r;} + } + encoder.end_array(ser_context(), ec); + if (JSONCONS_UNLIKELY(ec)) {return write_result{unexpect, ec};} + return write_result{}; + } +}; + +template +struct encode_traits::value && + ext_traits::is_array_like::value && + ext_traits::is_typed_array::value +>::type> +{ + using value_type = typename T::value_type; + + template + static write_result try_encode(const allocator_set&, const T& val, + basic_json_visitor& encoder) + { + std::error_code ec; + encoder.typed_array(jsoncons::span(val), semantic_tag::none, ser_context(), ec); + if (JSONCONS_UNLIKELY(ec)) {return write_result{unexpect, ec};} + return write_result{}; + } +}; + +// std::array + +template +struct encode_traits> +{ + using value_type = typename std::array::value_type; + + template + static write_result try_encode(const allocator_set& aset, const std::array& val, + basic_json_visitor& encoder) + { + std::error_code ec; + encoder.begin_array(val.size(),semantic_tag::none,ser_context(),ec); + if (JSONCONS_UNLIKELY(ec)) {return write_result{unexpect, ec};} + for (auto it = std::begin(val); it != std::end(val); ++it) + { + auto r = encode_traits::try_encode(aset, *it, encoder); + if (JSONCONS_UNLIKELY(!r)) {return r;} + } + encoder.end_array(ser_context(),ec); + if (JSONCONS_UNLIKELY(ec)) {return write_result{unexpect, ec};} + return write_result{}; + } +}; + +// map like + +template +struct encode_traits::value && + ext_traits::is_map_like::value && + ext_traits::is_constructible_from_const_pointer_and_size::value +>::type> +{ + using mapped_type = typename T::mapped_type; + using value_type = typename T::value_type; + using key_type = typename T::key_type; + + template + static write_result try_encode(const allocator_set& aset, const T& val, + basic_json_visitor& encoder) + { + std::error_code ec; + encoder.begin_object(val.size(), semantic_tag::none, ser_context(), ec); + if (JSONCONS_UNLIKELY(ec)) {return write_result{unexpect, ec};} + for (auto it = std::begin(val); it != std::end(val); ++it) + { + encoder.key((*it).first); + auto r = encode_traits::try_encode(aset, (*it).second, encoder); + if (JSONCONS_UNLIKELY(!r)) {return r;} + } + encoder.end_object(ser_context(), ec); + if (JSONCONS_UNLIKELY(ec)) {return write_result{unexpect, ec};} + return write_result{}; + } +}; + +template +struct encode_traits::value && + ext_traits::is_map_like::value && + std::is_integral::value +>::type> +{ + using mapped_type = typename T::mapped_type; + using value_type = typename T::value_type; + using key_type = typename T::key_type; + + template + static write_result try_encode(const allocator_set& aset, const T& val, + basic_json_visitor& encoder) + { + std::error_code ec; + encoder.begin_object(val.size(), semantic_tag::none, ser_context(), ec); + if (JSONCONS_UNLIKELY(ec)) {return write_result{unexpect, ec};} + for (auto it = std::begin(val); it != std::end(val); ++it) + { + using temp_alloc_type = typename std::allocator_traits:: template rebind_alloc; + std::basic_string,temp_alloc_type> s(aset.get_temp_allocator()); + jsoncons::from_integer((*it).first,s); + encoder.key(basic_string_view(s.data(), s.size())); + auto r = encode_traits::try_encode(aset, (*it).second, encoder); + if (JSONCONS_UNLIKELY(!r)) {return r;} + } + encoder.end_object(ser_context(), ec); + if (JSONCONS_UNLIKELY(ec)) {return write_result{unexpect, ec};} + return write_result{}; + } +}; + +} // namespace reflect +} // namespace jsoncons + +#endif // JSONCONS_REFLECT_ENCODE_TRAITS_HPP + diff --git a/include/jsoncons/reflect/json_conv_traits.hpp b/include/jsoncons/reflect/json_conv_traits.hpp new file mode 100644 index 0000000..9ef81e2 --- /dev/null +++ b/include/jsoncons/reflect/json_conv_traits.hpp @@ -0,0 +1,2008 @@ +// Copyright 2013-2026 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_REFLECT_JSON_CONV_TRAITS_HPP +#define JSONCONS_REFLECT_JSON_CONV_TRAITS_HPP + +#include // std::swap +#include +#include // std::bitset +#include +#include +#include +#include +#include // std::iterator_traits, std::input_iterator_tag +#include +#include +#include +#include +#include // std::enable_if +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(JSONCONS_HAS_STD_VARIANT) + #include +#endif + +namespace jsoncons { +namespace reflect { + + template + struct is_json_conv_traits_declared : public is_json_type_traits_declared + {}; + + // json_conv_traits + + template + struct unimplemented : std::false_type + {}; + + template + struct json_conv_traits + { + using result_type = conversion_result; + + static constexpr bool is_compatible = false; + + static constexpr bool is(const Json& j) noexcept + { + return json_type_traits::is(j); + } + + template + static result_type try_as(const allocator_set&, const Json& j) + { + + JSONCONS_TRY + { + return result_type(json_type_traits::as(j)); + } + JSONCONS_CATCH (...) + { + return result_type(jsoncons::unexpect, conv_errc::conversion_failed ); + } + } + + template + static Json to_json(const allocator_set& aset, const T& val) + { + return json_type_traits::to_json(val, aset.get_allocator()); + } + }; + +namespace detail { + +template +using +traits_can_convert_t = decltype(json_conv_traits::can_convert(Json())); + +template +using +has_can_convert = ext_traits::is_detected; + + template + struct invoke_can_convert + { + template + static + typename std::enable_if::value,bool>::type + can_convert(const Json& j) noexcept + { + return json_conv_traits::can_convert(j); + } + template + static + typename std::enable_if::value,bool>::type + can_convert(const Json& j) noexcept + { + return json_conv_traits::is(j); + } + }; + + // is_json_conv_traits_unspecialized + template + struct is_json_conv_traits_unspecialized : std::false_type {}; + + // is_json_conv_traits_unspecialized + template + struct is_json_conv_traits_unspecialized::is_compatible>::value>::type + > : std::true_type {}; + + // is_compatible_array_type + template + struct is_compatible_array_type : std::false_type {}; + + template + struct is_compatible_array_type::value && + ext_traits::is_array_like::value + >::type> : std::true_type {}; + +} // namespace detail + + // is_json_conv_traits_specialized + template + struct is_json_conv_traits_specialized : is_json_type_traits_specialized {}; + + template + struct is_json_conv_traits_specialized::value + >::type> : std::true_type {}; + + template + struct json_conv_traits::type*> + { + using char_type = typename Json::char_type; + using result_type = conversion_result; + + static bool is(const Json& j) noexcept + { + return j.is_string(); + } + template + static result_type try_as(const allocator_set&, const Json& j) + { + return result_type{j.as_cstring()}; + } + + template + static Json to_json(const allocator_set& aset, const char_type* s) + { + return jsoncons::make_obj_using_allocator(aset.get_allocator(), s, semantic_tag::none); + } + }; + + template + struct json_conv_traits::type*> + { + using char_type = typename Json::char_type; + + static bool is(const Json& j) noexcept + { + return j.is_string(); + } + + template + static Json to_json(const allocator_set& aset, const char_type* s) + { + return jsoncons::make_obj_using_allocator(aset.get_allocator(), s, semantic_tag::none); + } + }; + + // enum + + template + struct json_conv_traits::value && std::is_enum::value + >::type> + { + using result_type = conversion_result; + + static bool is(const Json& j) noexcept + { + return j.template is_integer(); + } + template + static result_type try_as(const allocator_set&, const Json& j) + { + auto r = j.template try_as_integer(); + if (r) + { + return result_type{static_cast(*r)}; + } + return result_type{unexpect, r.error()}; + } + + template + static Json to_json(const allocator_set&, T val) + { + return Json(static_cast(val), semantic_tag::none); + } + }; + + // integer + + template + struct json_conv_traits::value && sizeof(T) <= sizeof(int64_t)) || (ext_traits::is_unsigned_integer::value && sizeof(T) <= sizeof(uint64_t)) + >::type> + { + using result_type = conversion_result; + + static bool is(const Json& j) noexcept + { + return j.template is_integer(); + } + template + static result_type try_as(const allocator_set&, const Json& j) + { + return j.template try_as_integer(); + } + + template + static Json to_json(const allocator_set&, T val) + { + return Json(val, semantic_tag::none); + } + }; + + template + struct json_conv_traits::value && sizeof(T) > sizeof(int64_t)) || (ext_traits::is_unsigned_integer::value && sizeof(T) > sizeof(uint64_t)) + >::type> + { + using result_type = conversion_result; + + static bool is(const Json& j) noexcept + { + return j.template is_integer(); + } + template + static result_type try_as(const allocator_set&, const Json& j) + { + return j.template try_as_integer(); + } + + template + static Json to_json(const allocator_set& aset, T val) + { + return jsoncons::make_obj_using_allocator(aset.get_allocator(), val, semantic_tag::none); + } + }; + + template + struct json_conv_traits::value + >::type> + { + using result_type = conversion_result; + + static bool is(const Json& j) noexcept + { + return j.is_double(); + } + template + static result_type try_as(const allocator_set&, const Json& j) + { + auto result = j.try_as_double(); + if (JSONCONS_UNLIKELY(!result)) + { + return result_type(jsoncons::unexpect, result.error().code()); + } + return result_type(static_cast(*result)); + } + + template + static Json to_json(const allocator_set&, T val) + { + return Json(val, semantic_tag::none); + } + }; + + template + struct json_conv_traits + { + using json_object = typename Json::object; + + static bool is(const Json& j) noexcept + { + return j.is_object(); + } + + template + static Json to_json(const allocator_set&, const json_object& o) + { + return Json(o,semantic_tag::none); + } + }; + + template + struct json_conv_traits + { + using json_array = typename Json::array; + + static bool is(const Json& j) noexcept + { + return j.is_array(); + } + + template + static Json to_json(const allocator_set&, const json_array& a) + { + return Json(a, semantic_tag::none); + } + }; + + template + struct json_conv_traits + { + using result_type = conversion_result; + + static bool is(const Json&) noexcept + { + return true; + } + template + static result_type try_as(const allocator_set&, const Json& j) + { + return result_type{j}; + } + + template + static Json to_json(const allocator_set& aset, const Json& j) + { + return jsoncons::make_obj_using_allocator(aset.get_allocator(), j); + } + }; + + template + struct json_conv_traits + { + using result_type = conversion_result; + + static bool is(const Json& j) noexcept + { + return j.is_null(); + } + template + static result_type try_as(const allocator_set&, const Json& j) + { + if (!j.is_null()) + { + return result_type{jsoncons::unexpect, conv_errc::not_jsoncons_null_type}; + } + return result_type{jsoncons::null_type()}; + } + + template + static Json to_json(const allocator_set&, jsoncons::null_type) + { + return Json(jsoncons::null_type{}, semantic_tag::none); + } + }; + + template + struct json_conv_traits + { + using result_type = conversion_result; + + static bool is(const Json& j) noexcept + { + return j.is_bool(); + } + template + static result_type try_as(const allocator_set&, const Json& j) + { + return result_type{j.as_bool()}; + } + + template + static Json to_json(const allocator_set&, bool val) + { + return Json(val, semantic_tag::none); + } + }; + + template + struct json_conv_traits::const_reference>::value, + std::vector::const_reference, + void>::type>::value>::type> + { + using result_type = conversion_result; + + static bool is(const Json& j) noexcept + { + return j.is_bool(); + } + template + static result_type try_as(const allocator_set&, const Json& j) + { + return result_type{j.as_bool()}; + } + + template + static Json to_json(const allocator_set&, bool val) + { + return Json(val, semantic_tag::none); + } + }; + + template + struct json_conv_traits::reference> + { + using result_type = conversion_result; + + static bool is(const Json& j) noexcept + { + return j.is_bool(); + } + template + static result_type try_as(const allocator_set&, const Json& j) + { + return result_type{j.as_bool()}; + } + + template + static Json to_json(const allocator_set&, bool val) + { + return Json(val, semantic_tag::none); + } + }; + + template + struct json_conv_traits::value && + ext_traits::is_string::value && + std::is_same::value>::type> + { + using result_type = conversion_result; + using char_type = typename T::value_type; + + static bool is(const Json& j) noexcept + { + return j.is_string(); + } + + template + static result_type try_as(const allocator_set& aset, const Json& j) + { + return j.template try_as_string(aset); + } + + template + static Json to_json(const allocator_set& aset, const T& val) + { + return jsoncons::make_obj_using_allocator(aset.get_allocator(), val, semantic_tag::none); + } + }; + + template + struct json_conv_traits::value && + ext_traits::is_string::value && + !std::is_same::value>::type> + { + using char_type = typename Json::char_type; + using result_type = conversion_result; + + static bool is(const Json& j) noexcept + { + return j.is_string(); + } + + template + static result_type try_as(const allocator_set& aset, const Json& j) + { + if (!j.is_string()) + { + return result_type{jsoncons::unexpect, conv_errc::not_string}; + } + auto sv = j.as_string_view(); + T val = jsoncons::make_obj_using_allocator(aset.get_allocator()); + unicode_traits::convert(sv.data(), sv.size(), val); + return result_type(std::move(val)); + } + + template + static Json to_json(const allocator_set& aset, const T& val) + { + using temp_alloc_type = typename std::allocator_traits:: template rebind_alloc; + std::basic_string,temp_alloc_type> s(aset.get_temp_allocator()); + unicode_traits::convert(val.data(), val.size(), s); + return jsoncons::make_obj_using_allocator(aset.get_allocator(), s, semantic_tag::none); + } + }; + + template + struct json_conv_traits::value && + ext_traits::is_string_view::value && + std::is_same::value>::type> + { + using result_type = conversion_result; + + static bool is(const Json& j) noexcept + { + return j.is_string_view(); + } + + template + static result_type try_as(const allocator_set&, const Json& j) + { + auto result = j.try_as_string_view(); + return result ? result_type(in_place, result.value().data(), result.value().size()) : result_type(unexpect, conv_errc::not_string); + } + + template + static Json to_json(const allocator_set& aset, const T& val) + { + return jsoncons::make_obj_using_allocator(aset.get_allocator(), val, semantic_tag::none); + } + }; + + // array back insertable + + template + struct json_conv_traits::value && + detail::is_compatible_array_type::value && + ext_traits::is_back_insertable::value + >::type> + { + typedef typename std::iterator_traits::value_type value_type; + using result_type = conversion_result; + + static bool is(const Json& j) noexcept + { + bool result = j.is_array(); + if (result) + { + for (const auto& e : j.array_range()) + { + if (!e.template is()) + { + result = false; + break; + } + } + } + return result; + } + + // array back insertable non-byte container + + template + static typename std::enable_if::value,result_type>::type + try_as(const allocator_set& aset, const Json& j) + { + if (!j.is_array()) + { + return result_type(jsoncons::unexpect, conv_errc::not_vector); + } + T result{jsoncons::make_obj_using_allocator(aset.get_allocator())}; + visit_reserve_(typename std::integral_constant::value>::type(),result,j.size()); + for (const auto& item : j.array_range()) + { + auto res = item.template try_as(aset); + if (JSONCONS_UNLIKELY(!res)) + { + return result_type(jsoncons::unexpect, res.error().code(), res.error().message_arg()); + } + result.push_back(std::move(*res)); + } + + return result_type(std::move(result)); + } + + // array back insertable byte container + + template + static typename std::enable_if::value,result_type>::type + try_as(const allocator_set& aset, const Json& j) + { + std::error_code ec; + if (j.is_array()) + { + T result; + visit_reserve_(typename std::integral_constant::value>::type(),result,j.size()); + for (const auto& item : j.array_range()) + { + auto res = item.template try_as(aset); + if (JSONCONS_UNLIKELY(!res)) + { + return result_type(jsoncons::unexpect, conv_errc::not_vector); + } + result.push_back(std::move(*res)); + } + + return result_type(std::move(result)); + } + else if (j.is_byte_string_view()) + { + auto bs = j.as_byte_string_view(); + auto v = jsoncons::make_obj_using_allocator(aset.get_allocator(), bs.begin(), bs.end()); + return result_type(std::move(v)); + } + else if (j.is_string()) + { + T v; + auto sv = j.as_string_view(); + auto r = string_to_bytes(sv.begin(), sv.end(), j.tag(), v); + if (JSONCONS_UNLIKELY(r.ec != conv_errc{})) + { + return result_type(jsoncons::unexpect, conv_errc::not_byte_string); + } + return result_type(std::move(v)); + } + else + { + return result_type(jsoncons::unexpect, conv_errc::not_vector); + } + } + + template + static typename std::enable_if::value,Json>::type + to_json(const allocator_set& aset, const T& val) + { + auto j = jsoncons::make_obj_using_allocator(aset.get_allocator(), json_array_arg); + auto first = std::begin(val); + auto last = std::end(val); + std::size_t size = std::distance(first, last); + j.reserve(size); + for (auto it = first; it != last; ++it) + { + j.push_back(*it); + } + return j; + } + + template + static typename std::enable_if::value,Json>::type + to_json(const allocator_set& aset, const T& val) + { + return jsoncons::make_obj_using_allocator(aset.get_allocator(), byte_string_arg, val, semantic_tag::none); + } + + static void visit_reserve_(std::true_type, T& v, std::size_t size) + { + v.reserve(size); + } + + static void visit_reserve_(std::false_type, T&, std::size_t) + { + } + }; + + // array, not back insertable but insertable + + template + struct json_conv_traits::value && + detail::is_compatible_array_type::value && + !ext_traits::is_back_insertable::value && + ext_traits::is_insertable::value>::type> + { + typedef typename std::iterator_traits::value_type value_type; + using result_type = conversion_result; + + static bool is(const Json& j) noexcept + { + bool result = j.is_array(); + if (result) + { + for (const auto& e : j.array_range()) + { + if (!e.template is()) + { + result = false; + break; + } + } + } + return result; + } + + template + static result_type try_as(const allocator_set& aset, const Json& j) + { + if (j.is_array()) + { + T result = jsoncons::make_obj_using_allocator(aset.get_allocator()); + for (const auto& item : j.array_range()) + { + auto res = item.template try_as(aset); + if (JSONCONS_UNLIKELY(!res)) + { + return result_type(jsoncons::unexpect, conv_errc::not_vector); + } + result.insert(std::move(*res)); + } + + return result_type(std::move(result)); + } + else + { + return result_type(jsoncons::unexpect, conv_errc::not_vector); + } + } + + template + static Json to_json(const allocator_set& aset, const T& val) + { + auto j = jsoncons::make_obj_using_allocator(aset.get_allocator(), json_array_arg); + auto first = std::begin(val); + auto last = std::end(val); + std::size_t size = std::distance(first, last); + j.reserve(size); + for (auto it = first; it != last; ++it) + { + j.push_back(*it); + } + return j; + } + }; + + // array not back insertable or insertable, but front insertable + + template + struct json_conv_traits::value && + detail::is_compatible_array_type::value && + !ext_traits::is_back_insertable::value && + !ext_traits::is_insertable::value && + ext_traits::is_front_insertable::value>::type> + { + typedef typename std::iterator_traits::value_type value_type; + using result_type = conversion_result; + + static bool is(const Json& j) noexcept + { + bool result = j.is_array(); + if (result) + { + for (const auto& e : j.array_range()) + { + if (!e.template is()) + { + result = false; + break; + } + } + } + return result; + } + + template + static result_type try_as(const allocator_set& aset, const Json& j) + { + if (j.is_array()) + { + T result = jsoncons::make_obj_using_allocator(aset.get_allocator()); + + auto it = j.array_range().rbegin(); + auto end = j.array_range().rend(); + for (; it != end; ++it) + { + auto res = (*it).template try_as(aset); + if (JSONCONS_UNLIKELY(!res)) + { + return result_type(jsoncons::unexpect, conv_errc::not_vector); + } + result.push_front(std::move(*res)); + } + + return result_type(std::move(result)); + } + else + { + return result_type(jsoncons::unexpect, conv_errc::not_vector); + } + } + + template + static Json to_json(const allocator_set& aset, const T& val) + { + auto j = jsoncons::make_obj_using_allocator(aset.get_allocator(), json_array_arg); + auto first = std::begin(val); + auto last = std::end(val); + std::size_t size = std::distance(first, last); + j.reserve(size); + for (auto it = first; it != last; ++it) + { + j.push_back(*it); + } + return j; + } + }; + + // std::array + + template + struct json_conv_traits> + { + using result_type = conversion_result>; + + using value_type = E; + + static bool is(const Json& j) noexcept + { + bool result = j.is_array() && j.size() == N; + if (result) + { + for (const auto& e : j.array_range()) + { + if (!e.template is()) + { + result = false; + break; + } + } + } + return result; + } + + template + static result_type try_as(const allocator_set& aset, const Json& j) + { + std::array buff; + if (j.size() != N) + { + return result_type(jsoncons::unexpect, conv_errc::not_array); + } + for (std::size_t i = 0; i < N; i++) + { + auto res = j[i].template try_as(aset); + if (JSONCONS_UNLIKELY(!res)) + { + return result_type(jsoncons::unexpect, conv_errc::not_array); + } + buff[i] = std::move(*res); + } + return result_type(std::move(buff)); + } + + template + static Json to_json(const allocator_set& aset, const std::array& val) + { + auto j = jsoncons::make_obj_using_allocator(aset.get_allocator(), json_array_arg); + j.reserve(N); + for (auto it = val.begin(); it != val.end(); ++it) + { + j.push_back(*it); + } + return j; + } + }; + + // map like + template + struct json_conv_traits::value && + ext_traits::is_map_like::value && + ext_traits::is_string::value && + is_json_conv_traits_specialized::value>::type + > + { + using mapped_type = typename T::mapped_type; + using value_type = typename T::value_type; + using key_type = typename T::key_type; + using result_type = conversion_result; + + static bool is(const Json& j) noexcept + { + if (!j.is_object()) + { + return false; + } + for (const auto& member : j.object_range()) + { + if (!member.value().template is()) + { + return false; + } + } + return true; + } + + template + static result_type try_as(const allocator_set& aset, const Json& j) + { + if (!j.is_object()) + { + return result_type(jsoncons::unexpect, conv_errc::not_map); + } + auto val = jsoncons::make_obj_using_allocator(aset.get_allocator()); + for (const auto& item : j.object_range()) + { + auto key = jsoncons::make_obj_using_allocator(aset.get_allocator(), + item.key().data(), item.key().size()); + auto r2 = item.value().template try_as(aset); + if (!r2) + { + return result_type(jsoncons::unexpect, r2.error().code(), r2.error().message_arg()); + } + val.emplace(std::move(key), std::move(*r2)); + } + + return result_type(std::move(val)); + } + + template + static Json to_json(const allocator_set& aset, const T& val) + { + Json j = jsoncons::make_obj_using_allocator(aset.get_allocator(), json_object_arg, semantic_tag::none); + j.reserve(val.size()); + for (const auto& item : val) + { + j.try_emplace(jsoncons::make_obj_using_allocator(aset.get_allocator(), item.first.data(), item.first.length()), item.second); + } + return j; + } + }; + + template + struct json_conv_traits::value && + ext_traits::is_map_like::value && + !ext_traits::is_string::value && + is_json_conv_traits_specialized::value && + is_json_conv_traits_specialized::value>::type + > + { + using mapped_type = typename T::mapped_type; + using value_type = typename T::value_type; + using key_type = typename T::key_type; + using result_type = conversion_result; + + static bool is(const Json& j) noexcept + { + if (!j.is_object()) + return false; + for (const auto& item : j.object_range()) + { + Json k(item.key()); + if (!k.template is()) + { + return false; + } + if (!item.value().template is()) + { + return false; + } + } + return true; + } + + template + static result_type try_as(const allocator_set& aset, const Json& j) + { + if (!j.is_object()) + { + return result_type(jsoncons::unexpect, conv_errc::not_map); + } + + auto val = jsoncons::make_obj_using_allocator(aset.get_allocator()); + for (const auto& item : j.object_range()) + { + auto k = jsoncons::make_obj_using_allocator(j.get_allocator(), item.key()); + auto r1 = k.template try_as(aset); + if (!r1) + { + return result_type(jsoncons::unexpect, r1.error()); + } + auto r2 = item.value().template try_as(aset); + if (!r2) + { + return result_type(jsoncons::unexpect, r2.error()); + } + val.emplace(std::move(*r1), std::move(*r2)); + } + + return result_type(std::move(val)); + } + + template + static Json to_json(const allocator_set& aset, const T& val) + { + Json j = jsoncons::make_obj_using_allocator(aset.get_allocator(), json_object_arg, semantic_tag::none); + j.reserve(val.size()); + for (const auto& item : val) + { + auto temp = json_conv_traits::to_json(aset, item.first); + if (temp.is_string_view()) + { + auto sv = temp.as_string_view(); + j.try_emplace(jsoncons::make_obj_using_allocator(aset.get_allocator(), sv.data(), sv.length()), item.second); + } + else + { + auto key = jsoncons::make_obj_using_allocator(aset.get_allocator()); + temp.dump(key); + j.try_emplace(std::move(key), item.second); + } + } + return j; + } + }; + + namespace tuple_detail + { + template + struct json_tuple_helper + { + using element_type = typename std::tuple_element::type; + using next = json_tuple_helper; + + static bool is(const Json& j) noexcept + { + if (j[Size-Pos].template is()) + { + return next::is(j); + } + else + { + return false; + } + } + + template + static void try_as(Tuple& tuple, const allocator_set& aset, const Json& j, std::error_code& ec) + { + auto res = j[Size-Pos].template try_as(aset); + if (!res) + { + ec = res.error().code(); + return; + } + std::get(tuple) = *res; + next::try_as(tuple, aset, j, ec); + } + + template + static void to_json(const allocator_set& aset, const Tuple& tuple, Json& j) + { + j.push_back(json_conv_traits::to_json(aset, std::get(tuple))); + next::to_json(aset, tuple, j); + } + }; + + template + struct json_tuple_helper<0, Size, Json, Tuple> + { + static bool is(const Json&) noexcept + { + return true; + } + + template + static void try_as(Tuple&, const allocator_set&, const Json&, std::error_code&) + { + } + + template + static void to_json(const allocator_set&, const Tuple&, Json&) + { + } + }; + } // namespace tuple_detail + + template + struct json_conv_traits> + { + private: + using helper = tuple_detail::json_tuple_helper>; + + public: + using result_type = conversion_result>; + + static bool is(const Json& j) noexcept + { + return helper::is(j); + } + + template + static result_type try_as(const allocator_set& aset, const Json& j) + { + std::error_code ec; + std::tuple val; + helper::try_as(val, aset, j, ec); + if (ec) + { + return result_type(jsoncons::unexpect, ec); + } + return result_type(std::move(val)); + } + + template + static Json to_json(const allocator_set& aset, const std::tuple& val) + { + Json j = jsoncons::make_obj_using_allocator(aset.get_allocator(), json_array_arg, semantic_tag::none); + j.reserve(sizeof...(E)); + helper::to_json(aset, val, j); + return j; + } + }; + + template + struct json_conv_traits> + { + public: + using result_type = conversion_result>; + + static bool is(const Json& j) noexcept + { + return j.is_array() && j.size() == 2; + } + + template + static result_type try_as(const allocator_set& aset, const Json& j) + { + if (!j.is_array() || j.size() != 2) + { + return result_type(jsoncons::unexpect, conv_errc::not_pair); + } + auto res1 = j[0].template try_as(aset); + if (!res1) + { + return result_type(jsoncons::unexpect, res1.error().code()); + } + auto res2 = j[1].template try_as(aset); + if (!res2) + { + return result_type(jsoncons::unexpect, res2.error().code()); + } + return result_type(std::make_pair(std::move(*res1), std::move(*res2))); + } + + template + static Json to_json(const allocator_set& aset, const std::pair& val) + { + Json j = jsoncons::make_obj_using_allocator(aset.get_allocator(), json_array_arg, semantic_tag::none); + j.reserve(2); + j.push_back(val.first); + j.push_back(val.second); + return j; + } + }; + + template + struct json_conv_traits::value>::type> + { + public: + using result_type = conversion_result; + + static bool is(const Json& j) noexcept + { + return j.is_byte_string(); + } + + template + static result_type try_as(const allocator_set& aset, const Json& j) + { + return j.template try_as_byte_string(aset); + } + + template + static Json to_json(const allocator_set& aset, const T& val) + { + return jsoncons::make_obj_using_allocator(aset.get_allocator(), byte_string_arg, val, semantic_tag::none); + } + }; + + template + struct json_conv_traits, + typename std::enable_if>::value && + !std::is_polymorphic::value + >::type> + { + using result_type = conversion_result>; + + static bool is(const Json& j) noexcept + { + return j.is_null() || j.template is(); + } + + template + static result_type try_as(const allocator_set& aset, const Json& j) + { + if (j.is_null()) + { + return result_type(std::shared_ptr(nullptr)); + } + auto r = j.template try_as(aset); + if (!r) + { + return result_type(jsoncons::unexpect, r.error()); + } + return result_type(std::allocate_shared(aset.get_allocator(), std::move(r.value()))); + } + + template + static Json to_json(const allocator_set& aset, const std::shared_ptr& ptr) + { + if (ptr.get() != nullptr) + { + return jsoncons::make_obj_using_allocator(aset.get_allocator(), *ptr); + } + else + { + return Json::null(); + } + } + }; + + template + struct json_conv_traits, + typename std::enable_if>::value && + !std::is_polymorphic::value + >::type> + { + using result_type = conversion_result>; + + static bool is(const Json& j) noexcept + { + return j.is_null() || j.template is(); + } + + template + static result_type try_as(const allocator_set& aset, const Json& j) + { + if (j.is_null()) + { + return result_type(std::unique_ptr(nullptr)); + } + auto r = j.template try_as(aset); + if (!r) + { + return result_type(jsoncons::unexpect, r.error()); + } + return result_type(jsoncons::make_unique(std::move(r.value()))); + } + + template + static Json to_json(const allocator_set& aset, const std::unique_ptr& ptr) + { + if (ptr.get() != nullptr) + { + return jsoncons::make_obj_using_allocator(aset.get_allocator(), *ptr, semantic_tag::none); + } + else + { + return Json::null(); + } + } + }; + + template + struct json_conv_traits::value && !is_json_conv_traits_declared::value>::type> + { + public: + using result_type = conversion_result; + + static bool is(const Json& j) noexcept + { + return j.is_null() || j.template is(); + } + + template + static result_type try_as(const allocator_set& aset, const Json& j) + { + if (j.is_null()) + { + return result_type(T()); + } + auto r = j.template try_as(aset); + if (!r) + { + return result_type(jsoncons::unexpect, r.error()); + } + return result_type(T(std::move(r.value()))); + } + + template + static Json to_json(const allocator_set& aset, const T& val) + { + return val.has_value() ? jsoncons::make_obj_using_allocator(aset.get_allocator(), *val) : Json::null(); + } + }; + + template + struct json_conv_traits + { + public: + using result_type = conversion_result; + + static bool is(const Json& j) noexcept + { + return j.is_byte_string_view(); + } + + template + static result_type try_as(const allocator_set&, const Json& j) + { + return result_type(j.try_as_byte_string_view()); + } + + template + static Json to_json(const allocator_set& aset, const byte_string_view& val) + { + return jsoncons::make_obj_using_allocator(aset.get_allocator(), byte_string_arg, val, semantic_tag::none); + } + }; + + // basic_bigint + + template + struct json_conv_traits> + { + public: + using char_type = typename Json::char_type; + using result_type = conversion_result>; + + static bool is(const Json& j) noexcept + { + switch (j.type()) + { + case json_type::string: + return jsoncons::is_base10(j.as_string_view().data(), j.as_string_view().length()); + case json_type::int64: + case json_type::uint64: + return true; + default: + return false; + } + } + + template + static result_type try_as(const allocator_set& aset, const Json& j) + { + switch (j.type()) + { + case json_type::string: + { + auto sv = j.as_string_view(); + std::error_code ec; + basic_bigint val; + auto r = to_bigint(sv.data(), sv.length(), val); + if (JSONCONS_UNLIKELY(!r)) + { + return result_type(jsoncons::unexpect, conv_errc::not_bigint); + } + return result_type(std::move(val)); + } + case json_type::float16: + case json_type::float64: + { + auto res = j.template try_as(aset); + return res ? result_type(jsoncons::in_place, *res) : result_type(jsoncons::unexpect, conv_errc::not_bigint); + } + case json_type::int64: + { + auto res = j.template try_as(aset); + return res ? result_type(jsoncons::in_place, *res) : result_type(jsoncons::unexpect, conv_errc::not_bigint); + } + case json_type::uint64: + { + auto res = j.template try_as(aset); + return res ? result_type(jsoncons::in_place, *res) : result_type(jsoncons::unexpect, conv_errc::not_bigint); + } + default: + return result_type(jsoncons::unexpect, conv_errc::not_bigint); + } + } + + template + static Json to_json(const allocator_set& aset, const basic_bigint& val) + { + using temp_alloc_type = typename std::allocator_traits:: template rebind_alloc; + std::basic_string,temp_alloc_type> s{aset.get_temp_allocator()}; + val.write_string(s); + return jsoncons::make_obj_using_allocator(aset.get_allocator(), s, semantic_tag::bigint); + } + }; + + // std::valarray + + template + struct json_conv_traits> + { + using result_type = conversion_result>; + + static bool is(const Json& j) noexcept + { + bool result = j.is_array(); + if (result) + { + for (const auto& e : j.array_range()) + { + if (!e.template is()) + { + result = false; + break; + } + } + } + return result; + } + + template + static result_type try_as(const allocator_set& aset, const Json& j) + { + if (j.is_array()) + { + std::valarray v(j.size()); + for (std::size_t i = 0; i < j.size(); ++i) + { + auto res = j[i].template try_as(aset); + if (JSONCONS_UNLIKELY(!res)) + { + return result_type(jsoncons::unexpect, conv_errc::not_array); + } + v[i] = std::move(*res); + } + return result_type(std::move(v)); + } + else + { + return result_type(jsoncons::unexpect, conv_errc::not_array); + } + } + + template + static Json to_json(const allocator_set& aset, const std::valarray& val) + { + Json j = jsoncons::make_obj_using_allocator(aset.get_allocator(), json_array_arg, semantic_tag::none); + auto first = std::begin(val); + auto last = std::end(val); + std::size_t size = std::distance(first,last); + j.reserve(size); + for (auto it = first; it != last; ++it) + { + j.push_back(*it); + } + return j; + } + }; + +#if defined(JSONCONS_HAS_STD_VARIANT) + +namespace variant_detail +{ + template + typename std::enable_if, bool>::type + is_variant(const Json& /*j*/) + { + return false; + } + + template + typename std::enable_if, bool>::type + is_variant(const Json& j) + { + if (j.template is()) + { + return true; + } + else + { + return is_variant(j); + } + } + + template + typename std::enable_if, conversion_result>::type + as_variant(const allocator_set&, const Json& /*j*/) + { + return conversion_result(jsoncons::unexpect, conv_errc::not_variant); + } + + template + typename std::enable_if, conversion_result>::type + as_variant(const allocator_set& aset, const Json& j) + { + using result_type = conversion_result; + if (j.template is()) + { + auto res = j.template try_as(aset); + if (JSONCONS_UNLIKELY(!res)) + { + return result_type(jsoncons::unexpect, conv_errc::not_variant); + } + return conversion_result(jsoncons::in_place, std::move(*res)); + } + else + { + return as_variant(aset, j); + } + } + + template + struct variant_to_json_visitor + { + Json& j_; + + variant_to_json_visitor(Json& j) : j_(j) {} + + template + void operator()(const T& value) const + { + j_ = value; + } + }; + +} // namespace variant_detail + + template + struct json_conv_traits> + { + public: + using variant_type = typename std::variant; + using result_type = conversion_result>; + + static bool is(const Json& j) noexcept + { + return variant_detail::is_variant<0,Json,variant_type, VariantTypes...>(j); + } + + template + static result_type try_as(const allocator_set& aset, const Json& j) + { + return result_type(variant_detail::as_variant<0,Json,variant_type,Alloc,TempAlloc,VariantTypes...>(aset, j)); + } + + template + static Json to_json(const allocator_set& aset, const std::variant& var) + { + Json j = jsoncons::make_obj_using_allocator(aset.get_allocator(), json_array_arg, semantic_tag::none); + variant_detail::variant_to_json_visitor visitor(j); + std::visit(visitor, var); + return j; + } + }; +#endif + + // std::chrono::duration + template + struct json_conv_traits> + { + using duration_type = std::chrono::duration; + using result_type = conversion_result; + + static constexpr int64_t nanos_in_milli = 1000000; + static constexpr int64_t nanos_in_second = 1000000000; + static constexpr int64_t millis_in_second = 1000; + + static bool is(const Json& j) noexcept + { + return (j.tag() == semantic_tag::epoch_second || j.tag() == semantic_tag::epoch_milli || j.tag() == semantic_tag::epoch_nano); + } + + template + static result_type try_as(const allocator_set& aset, const Json& j) + { + return from_json_(aset, j); + } + + template + static Json to_json(const allocator_set& aset, const duration_type& val) + { + return to_json_(aset, val); + } + + template + static + typename std::enable_if>::value, result_type>::type + from_json_(const allocator_set& aset, const Json& j) + { + if (j.is_int64() || j.is_uint64() || j.is_double()) + { + auto count = j.template as(); + switch (j.tag()) + { + case semantic_tag::epoch_second: + return result_type(in_place, count); + case semantic_tag::epoch_milli: + return result_type(in_place, count == 0 ? 0 : count/millis_in_second); + case semantic_tag::epoch_nano: + return result_type(in_place, count == 0 ? 0 : count/nanos_in_second); + default: + return result_type(in_place, count); + } + } + else if (j.is_string()) + { + switch (j.tag()) + { + case semantic_tag::epoch_second: + { + auto res = j.template try_as(); + if (!res) + { + return result_type(jsoncons::unexpect, conv_errc::not_epoch); + } + return result_type(in_place, *res); + } + case semantic_tag::epoch_milli: + { + auto sv = j.as_string_view(); + bigint n; + auto r = to_bigint(sv.data(), sv.length(), n); + if (!r) {return result_type(jsoncons::unexpect, conv_errc::not_epoch);} + if (n != 0) + { + n = n / millis_in_second; + } + return result_type(in_place, static_cast(n)); + } + case semantic_tag::epoch_nano: + { + auto sv = j.as_string_view(); + bigint n; + auto r = to_bigint(sv.data(), sv.length(), n); + if (!r) {return result_type(jsoncons::unexpect, conv_errc::not_epoch);} + if (n != 0) + { + n = n / nanos_in_second; + } + return result_type(in_place, static_cast(n)); + } + default: + { + auto res = j.template try_as(aset); + if (!res) + { + return result_type(jsoncons::unexpect, conv_errc::not_epoch); + } + return result_type(in_place, *res); + } + } + } + else + { + return result_type(jsoncons::unexpect, conv_errc::not_epoch); + } + } + + template + static + typename std::enable_if::value, result_type>::type + from_json_(const allocator_set& aset, const Json& j) + { + if (j.is_int64() || j.is_uint64()) + { + auto res = j.template try_as(); + if (!res) + { + return result_type(jsoncons::unexpect, conv_errc::not_epoch); + } + switch (j.tag()) + { + case semantic_tag::epoch_second: + return result_type(in_place, *res*millis_in_second); + case semantic_tag::epoch_milli: + return result_type(in_place, *res); + case semantic_tag::epoch_nano: + return result_type(in_place, *res == 0 ? 0 : *res/nanos_in_milli); + default: + return result_type(in_place, *res); + } + } + else if (j.is_double()) + { + auto res = j.template try_as(); + if (!res) + { + return result_type(jsoncons::unexpect, conv_errc::not_epoch); + } + switch (j.tag()) + { + case semantic_tag::epoch_second: + return result_type(in_place, static_cast(*res * millis_in_second)); + case semantic_tag::epoch_milli: + return result_type(in_place, static_cast(*res)); + case semantic_tag::epoch_nano: + return result_type(in_place, *res == 0 ? 0 : static_cast(*res / nanos_in_milli)); + default: + return result_type(in_place, static_cast(*res)); + } + } + else if (j.is_string()) + { + switch (j.tag()) + { + case semantic_tag::epoch_second: + { + auto res = j.template try_as(); + if (!res) + { + return result_type(jsoncons::unexpect, conv_errc::not_epoch); + } + return result_type(in_place, *res*millis_in_second); + } + case semantic_tag::epoch_milli: + { + auto res = j.try_as_string_view(); + if (!res) + { + return result_type(jsoncons::unexpect, conv_errc::not_epoch); + } + Rep n{0}; + auto result = jsoncons::dec_to_integer((*res).data(), (*res).size(), n); + if (!result) + { + return result_type(jsoncons::unexpect, conv_errc::not_epoch); + } + return result_type(in_place, n); + } + case semantic_tag::epoch_nano: + { + auto sv = j.as_string_view(); + bigint n; + auto r = to_bigint(sv.data(), sv.length(), n); + if (!r) {return result_type(jsoncons::unexpect, conv_errc::not_epoch);} + if (n != 0) + { + n = n / nanos_in_milli; + } + return result_type(in_place, static_cast(n)); + } + default: + { + auto res = j.template try_as(aset); + if (!res) + { + return result_type(jsoncons::unexpect, conv_errc::not_epoch); + } + return result_type(in_place, *res); + } + } + } + else + { + return result_type(jsoncons::unexpect, conv_errc::not_epoch); + } + } + + template + static + typename std::enable_if::value, result_type>::type + from_json_(const allocator_set& aset, const Json& j) + { + if (j.is_int64() || j.is_uint64() || j.is_double()) + { + auto count = j.template as(); + switch (j.tag()) + { + case semantic_tag::epoch_second: + return result_type(in_place, count*nanos_in_second); + case semantic_tag::epoch_milli: + return result_type(in_place, count*nanos_in_milli); + case semantic_tag::epoch_nano: + return result_type(in_place, count); + default: + return result_type(in_place, count); + } + } + else if (j.is_double()) + { + auto count = j.template as(); + switch (j.tag()) + { + case semantic_tag::epoch_second: + return result_type(in_place, static_cast(count * nanos_in_second)); + case semantic_tag::epoch_milli: + return result_type(in_place, static_cast(count * nanos_in_milli)); + case semantic_tag::epoch_nano: + return result_type(in_place, static_cast(count)); + default: + return result_type(in_place, static_cast(count)); + } + } + else if (j.is_string()) + { + auto res = j.template try_as(aset); + if (!res) + { + return result_type(jsoncons::unexpect, conv_errc::not_epoch); + } + switch (j.tag()) + { + case semantic_tag::epoch_second: + return result_type(in_place, *res*nanos_in_second); + case semantic_tag::epoch_milli: + return result_type(in_place, *res*nanos_in_milli); + case semantic_tag::epoch_nano: + return result_type(in_place, *res); + default: + return result_type(in_place, *res); + } + } + else + { + return result_type(jsoncons::unexpect, conv_errc::not_epoch); + } + } + + template + static + typename std::enable_if>::value,Json>::type + to_json_(const allocator_set& aset, const duration_type& val) + { + return jsoncons::make_obj_using_allocator(aset.get_allocator(), val.count(), semantic_tag::epoch_second); + } + + template + static + typename std::enable_if::value,Json>::type + to_json_(const allocator_set& aset, const duration_type& val) + { + return jsoncons::make_obj_using_allocator(aset.get_allocator(), val.count(), semantic_tag::epoch_milli); + } + + template + static + typename std::enable_if::value,Json>::type + to_json_(const allocator_set& aset, const duration_type& val) + { + return jsoncons::make_obj_using_allocator(aset.get_allocator(), val.count(), semantic_tag::epoch_nano); + } + }; + + // std::nullptr_t + template + struct json_conv_traits + { + using result_type = conversion_result; + + static bool is(const Json& j) noexcept + { + return j.is_null(); + } + + template + static result_type try_as(const allocator_set&, const Json& j) + { + if (!j.is_null()) + { + return result_type(jsoncons::unexpect, conv_errc::not_nullptr); + } + return result_type(nullptr); + } + + template + static Json to_json(const allocator_set&, const std::nullptr_t&) + { + return Json::null(); + } + }; + + // std::bitset + + struct null_back_insertable_byte_container + { + using value_type = uint8_t; + + void push_back(value_type) + { + } + }; + + template + struct json_conv_traits> + { + using result_type = conversion_result>; + + static bool is(const Json& j) noexcept + { + if (j.is_byte_string()) + { + return true; + } + else if (j.is_string()) + { + jsoncons::string_view sv = j.as_string_view(); + null_back_insertable_byte_container cont; + auto result = base16_to_bytes(sv.begin(), sv.end(), cont); + return result.ec == conv_errc::success ? true : false; + } + return false; + } + + template + static result_type try_as(const allocator_set&, const Json& j) + { + if (j.template is()) + { + auto bits = j.template as(); + std::bitset bs = static_cast(bits); + return result_type(std::move(bs)); + } + else if (j.is_byte_string() || j.is_string()) + { + std::bitset bs; + std::vector bits; + if (j.is_byte_string()) + { + bits = j.template as>(); + } + else + { + jsoncons::string_view sv = j.as_string_view(); + auto result = base16_to_bytes(sv.begin(), sv.end(), bits); + if (result.ec != conv_errc::success) + { + return result_type(jsoncons::unexpect, conv_errc::not_bitset); + } + } + std::uint8_t byte = 0; + std::uint8_t mask = 0; + + std::size_t pos = 0; + for (std::size_t i = 0; i < N; ++i) + { + if (mask == 0) + { + if (pos >= bits.size()) + { + return result_type(jsoncons::unexpect, conv_errc::not_bitset); + } + byte = bits.at(pos++); + mask = 0x80; + } + + if (byte & mask) + { + bs[i] = 1; + } + + mask = static_cast(mask >> 1); + } + return result_type(std::move(bs)); + } + else + { + return result_type(jsoncons::unexpect, conv_errc::not_bitset); + } + } + + template + static Json to_json(const allocator_set& aset, const std::bitset& val) + { + using temp_alloc_type = typename std::allocator_traits:: template rebind_alloc; + std::vector bits(aset.get_temp_allocator()); + + uint8_t byte = 0; + uint8_t mask = 0x80; + + for (std::size_t i = 0; i < N; ++i) + { + if (val[i]) + { + byte |= mask; + } + + mask = static_cast(mask >> 1); + + if (mask == 0) + { + bits.push_back(byte); + byte = 0; + mask = 0x80; + } + } + + // Encode remainder + if (mask != 0x80) + { + bits.push_back(byte); + } + + return jsoncons::make_obj_using_allocator(aset.get_allocator(), byte_string_arg, bits, semantic_tag::base16); + } + }; + +} // namespace reflect +} // namespace jsoncons + +#endif // JSONCONS_REFLECT_JSON_CONV_TRAITS_HPP diff --git a/include/jsoncons/reflect/reflect_traits_gen.hpp b/include/jsoncons/reflect/reflect_traits_gen.hpp new file mode 100644 index 0000000..51730c6 --- /dev/null +++ b/include/jsoncons/reflect/reflect_traits_gen.hpp @@ -0,0 +1,1939 @@ +// Copyright 2013-2026 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_REFLECT_REFLECT_TRAITS_GEN_HPP +#define JSONCONS_REFLECT_REFLECT_TRAITS_GEN_HPP + +#include + +#include +#include // JSONCONS_PP_EXPAND, JSONCONS_PP_QUOTE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define JSONCONS_RDONLY(X) + +#define JSONCONS_RDWR(X) X + +namespace jsoncons { +namespace reflect { + +struct always_true +{ + template< typename T> + constexpr bool operator()(const T&) const noexcept + { + return true; + } +}; + +struct identity +{ + template< typename T> + constexpr T&& operator()(T&& val) const noexcept + { + return std::forward(val); + } +}; + +template +struct json_object_name_members +{}; + +template +struct reflect_type_properties +{}; + +template +void set_member(T&&, const U&) +{ +} +template +void set_member(T&& val, U& result) +{ + result = std::forward(val); +} + +template +struct json_traits_helper +{ + using string_view_type = typename Json::string_view_type; + + template + static conversion_result try_get_member(const allocator_set& aset, + const Json& j, string_view_type key) + { + auto it = j.find(key); + if (it == j.object_range().end()) + { + return conversion_result(unexpect, conv_errc::missing_required_member); + } + auto result = it->value().template try_as(aset); + if (!result) + { + return conversion_result(unexpect, result.error()); + } + return conversion_result(std::move(*result)); + } + + template + static void set_optional_json_member(string_view_type key, const std::shared_ptr& val, Json& j) + { + if (val) j.try_emplace(key, val); + } + template + static void set_optional_json_member(string_view_type key, const std::unique_ptr& val, Json& j) + { + if (val) j.try_emplace(key, val); + } + template + static + typename std::enable_if::value, void>::type + set_optional_json_member(string_view_type key, const U& val, Json& j) + { + if (val.has_value()) j.try_emplace(key, val); + } + template + static + typename std::enable_if::value, void>::type + set_optional_json_member(string_view_type key, const U& val, Json& j) + { + j.try_emplace(key, val); + } +}; + +template +write_result try_encode_member(const basic_string_view& key, const T& val, basic_json_visitor& encoder) +{ + encoder.key(key); + return encode_traits::try_encode(make_alloc_set(), val, encoder); +} + +template +write_result try_encode_optional_member(const basic_string_view& key, const std::shared_ptr& val, basic_json_visitor& encoder) +{ + if (val) + { + encoder.key(key); + return encode_traits::try_encode(make_alloc_set(), *val, encoder); + } + return write_result{}; +} + +template +write_result try_encode_optional_member(const basic_string_view& key, const std::unique_ptr& val, basic_json_visitor& encoder) +{ + if (val) + { + encoder.key(key); + return encode_traits::try_encode(make_alloc_set(), *val, encoder); + } + return write_result{}; +} + +template +typename std::enable_if::value, write_result>::type +try_encode_optional_member(const basic_string_view& key, const T& val, basic_json_visitor& encoder) +{ + if (val.has_value()) + { + encoder.key(key); + return encode_traits::try_encode(make_alloc_set(), *val, encoder); + } + return write_result{}; +} + +template +typename std::enable_if::value, write_result>::type +try_encode_optional_member(const basic_string_view& key, const T& val, basic_json_visitor& encoder) +{ + encoder.key(key); + return encode_traits::try_encode(make_alloc_set(), val, encoder); +} + +template +bool is_optional_value_set(const std::shared_ptr& val) +{ + return val ? true : false; +} +template +bool is_optional_value_set(const std::unique_ptr& val) +{ + return val ? true : false; +} +template +typename std::enable_if::value, bool>::type +is_optional_value_set(const T& val) +{ + return val.has_value(); +} +template +typename std::enable_if::value, bool>::type +is_optional_value_set(const T&) +{ + return true; +} + +} // namespace reflect + +using always_true = reflect::always_true; +using identity = reflect::identity; + +} // namespace jsoncons + +#if defined(_MSC_VER) +#pragma warning( disable : 4127) +#endif + +#define JSONCONS_PP_CONCAT_IMPL(a, b) a ## b +#define JSONCONS_PP_CONCAT(a, b) JSONCONS_PP_CONCAT_IMPL(a, b) + +// Inspired by https://github.com/Loki-Astari/ThorsSerializer/blob/master/src/Serialize/Traits.h + +#define JSONCONS_NARGS(...) JSONCONS_NARG_(__VA_ARGS__, 70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0) +#define JSONCONS_NARG_(...) JSONCONS_PP_EXPAND( JSONCONS_ARG_N(__VA_ARGS__) ) +#define JSONCONS_ARG_N(e1,e2,e3,e4,e5,e6,e7,e8,e9,e10,e11,e12,e13,e14,e15,e16,e17,e18,e19,e20,e21,e22,e23,e24,e25,e26,e27,e28,e29,e30,e31,e32,e33,e34,e35,e36,e37,e38,e39,e40,e41,e42,e43,e44,e45,e46,e47,e48,e49,e50,e51,e52,e53,e54,e55,e56,e57,e58,e59,e60,e61,e62,e63,e64,e65,e66,e67,e68,e69,e70,N,...)N + +#define JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, Count) Call(P1, P2, P3, P4, Count) + +#define JSONCONS_VARIADIC_FOR_EACH(Call, P1, P2, P3, ...) JSONCONS_VARIADIC_REP_OF_N(Call, P1,P2, P3, JSONCONS_NARGS(__VA_ARGS__), __VA_ARGS__) +#define JSONCONS_VARIADIC_REP_OF_N(Call, P1, P2, P3, Count, ...) JSONCONS_VARIADIC_REP_OF_N_(Call, P1, P2, P3, Count, __VA_ARGS__) +#define JSONCONS_VARIADIC_REP_OF_N_(Call, P1, P2, P3, Count, ...) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_ ## Count(Call, P1, P2, P3, __VA_ARGS__)) + +#define JSONCONS_VARIADIC_REP_OF_70(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 70) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_69(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_69(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 69) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_68(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_68(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 68) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_67(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_67(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 67) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_66(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_66(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 66) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_65(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_65(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 65) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_64(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_64(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 64) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_63(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_63(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 63) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_62(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_62(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 62) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_61(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_61(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 61) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_60(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_60(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 60) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_59(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_59(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 59) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_58(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_58(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 58) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_57(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_57(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 57) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_56(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_56(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 56) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_55(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_55(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 55) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_54(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_54(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 54) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_53(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_53(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 53) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_52(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_52(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 52) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_51(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_51(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 51) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_50(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_50(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 50) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_49(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_49(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 49) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_48(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_48(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 48) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_47(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_47(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 47) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_46(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_46(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 46) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_45(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_45(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 45) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_44(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_44(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 44) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_43(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_43(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 43) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_42(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_42(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 42) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_41(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_41(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 41) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_40(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_40(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 40) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_39(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_39(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 39) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_38(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_38(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 38) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_37(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_37(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 37) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_36(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_36(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 36) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_35(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_35(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 35) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_34(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_34(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 34) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_33(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_33(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 33) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_32(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_32(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 32) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_31(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_31(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 31) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_30(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_30(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 30) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_29(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_29(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 29) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_28(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_28(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 28) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_27(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_27(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 27) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_26(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_26(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 26) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_25(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_25(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 25) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_24(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_24(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 24) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_23(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_23(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 23) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_22(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_22(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 22) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_21(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_21(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 21) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_20(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_20(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 20) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_19(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_19(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 19) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_18(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_18(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 18) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_17(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_17(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 17) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_16(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_16(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 16) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_15(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_15(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 15) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_14(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_14(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 14) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_13(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_13(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 13) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_12(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_12(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 12) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_11(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_11(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 11) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_10(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_10(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 10) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_9(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_9(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 9) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_8(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_8(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 8) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_7(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_7(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 7) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_6(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_6(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 6) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_5(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_5(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 5) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_4(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_4(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 4) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_3(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_3(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 3) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_2(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_2(Call, P1, P2, P3, P4, ...) JSONCONS_EXPAND_CALL5(Call, P1, P2, P3, P4, 2) JSONCONS_PP_EXPAND(JSONCONS_VARIADIC_REP_OF_1(Call, P1, P2, P3, __VA_ARGS__)) +#define JSONCONS_VARIADIC_REP_OF_1(Call, P1, P2, P3, P4) JSONCONS_PP_EXPAND(Call ## _LAST(P1, P2, P3, P4, 1)) + +#define JSONCONS_TYPE_TRAITS_FRIEND \ + template \ + friend struct jsoncons::json_type_traits; \ + template \ + friend struct jsoncons::reflect::json_conv_traits; \ + template \ + friend struct jsoncons::reflect::encode_traits; \ + template \ + friend struct jsoncons::reflect::decode_traits; + +#define JSONCONS_CONV_TRAITS_FRIEND \ + template \ + friend struct jsoncons::reflect::json_conv_traits; \ + template \ + friend struct jsoncons::reflect::encode_traits; \ + template \ + friend struct jsoncons::reflect::decode_traits; + +#define JSONCONS_EXPAND_CALL2(Call, Expr, Id) JSONCONS_PP_EXPAND(Call(Expr, Id)) + +#define JSONCONS_REP_OF_N(Call, Expr, Pre, App, Count) JSONCONS_REP_OF_ ## Count(Call, Expr, Pre, App) + +#define JSONCONS_REP_OF_50(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 50) JSONCONS_REP_OF_49(Call, Expr, , App) +#define JSONCONS_REP_OF_49(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 49) JSONCONS_REP_OF_48(Call, Expr, , App) +#define JSONCONS_REP_OF_48(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 48) JSONCONS_REP_OF_47(Call, Expr, , App) +#define JSONCONS_REP_OF_47(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 47) JSONCONS_REP_OF_46(Call, Expr, , App) +#define JSONCONS_REP_OF_46(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 46) JSONCONS_REP_OF_45(Call, Expr, , App) +#define JSONCONS_REP_OF_45(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 45) JSONCONS_REP_OF_44(Call, Expr, , App) +#define JSONCONS_REP_OF_44(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 44) JSONCONS_REP_OF_43(Call, Expr, , App) +#define JSONCONS_REP_OF_43(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 43) JSONCONS_REP_OF_42(Call, Expr, , App) +#define JSONCONS_REP_OF_42(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 42) JSONCONS_REP_OF_41(Call, Expr, , App) +#define JSONCONS_REP_OF_41(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 41) JSONCONS_REP_OF_40(Call, Expr, , App) +#define JSONCONS_REP_OF_40(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 40) JSONCONS_REP_OF_39(Call, Expr, , App) +#define JSONCONS_REP_OF_39(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 39) JSONCONS_REP_OF_38(Call, Expr, , App) +#define JSONCONS_REP_OF_38(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 38) JSONCONS_REP_OF_37(Call, Expr, , App) +#define JSONCONS_REP_OF_37(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 37) JSONCONS_REP_OF_36(Call, Expr, , App) +#define JSONCONS_REP_OF_36(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 36) JSONCONS_REP_OF_35(Call, Expr, , App) +#define JSONCONS_REP_OF_35(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 35) JSONCONS_REP_OF_34(Call, Expr, , App) +#define JSONCONS_REP_OF_34(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 34) JSONCONS_REP_OF_33(Call, Expr, , App) +#define JSONCONS_REP_OF_33(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 33) JSONCONS_REP_OF_32(Call, Expr, , App) +#define JSONCONS_REP_OF_32(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 32) JSONCONS_REP_OF_31(Call, Expr, , App) +#define JSONCONS_REP_OF_31(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 31) JSONCONS_REP_OF_30(Call, Expr, , App) +#define JSONCONS_REP_OF_30(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 30) JSONCONS_REP_OF_29(Call, Expr, , App) +#define JSONCONS_REP_OF_29(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 29) JSONCONS_REP_OF_28(Call, Expr, , App) +#define JSONCONS_REP_OF_28(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 28) JSONCONS_REP_OF_27(Call, Expr, , App) +#define JSONCONS_REP_OF_27(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 27) JSONCONS_REP_OF_26(Call, Expr, , App) +#define JSONCONS_REP_OF_26(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 26) JSONCONS_REP_OF_25(Call, Expr, , App) +#define JSONCONS_REP_OF_25(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 25) JSONCONS_REP_OF_24(Call, Expr, , App) +#define JSONCONS_REP_OF_24(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 24) JSONCONS_REP_OF_23(Call, Expr, , App) +#define JSONCONS_REP_OF_23(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 23) JSONCONS_REP_OF_22(Call, Expr, , App) +#define JSONCONS_REP_OF_22(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 22) JSONCONS_REP_OF_21(Call, Expr, , App) +#define JSONCONS_REP_OF_21(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 21) JSONCONS_REP_OF_20(Call, Expr, , App) +#define JSONCONS_REP_OF_20(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 20) JSONCONS_REP_OF_19(Call, Expr, , App) +#define JSONCONS_REP_OF_19(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 19) JSONCONS_REP_OF_18(Call, Expr, , App) +#define JSONCONS_REP_OF_18(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 18) JSONCONS_REP_OF_17(Call, Expr, , App) +#define JSONCONS_REP_OF_17(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 17) JSONCONS_REP_OF_16(Call, Expr, , App) +#define JSONCONS_REP_OF_16(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 16) JSONCONS_REP_OF_15(Call, Expr, , App) +#define JSONCONS_REP_OF_15(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 15) JSONCONS_REP_OF_14(Call, Expr, , App) +#define JSONCONS_REP_OF_14(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 14) JSONCONS_REP_OF_13(Call, Expr, , App) +#define JSONCONS_REP_OF_13(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 13) JSONCONS_REP_OF_12(Call, Expr, , App) +#define JSONCONS_REP_OF_12(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 12) JSONCONS_REP_OF_11(Call, Expr, , App) +#define JSONCONS_REP_OF_11(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 11) JSONCONS_REP_OF_10(Call, Expr, , App) +#define JSONCONS_REP_OF_10(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 10) JSONCONS_REP_OF_9(Call, Expr, , App) +#define JSONCONS_REP_OF_9(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 9) JSONCONS_REP_OF_8(Call, Expr, , App) +#define JSONCONS_REP_OF_8(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 8) JSONCONS_REP_OF_7(Call, Expr, , App) +#define JSONCONS_REP_OF_7(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 7) JSONCONS_REP_OF_6(Call, Expr, , App) +#define JSONCONS_REP_OF_6(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 6) JSONCONS_REP_OF_5(Call, Expr, , App) +#define JSONCONS_REP_OF_5(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 5) JSONCONS_REP_OF_4(Call, Expr, , App) +#define JSONCONS_REP_OF_4(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 4) JSONCONS_REP_OF_3(Call, Expr, , App) +#define JSONCONS_REP_OF_3(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 3) JSONCONS_REP_OF_2(Call, Expr, , App) +#define JSONCONS_REP_OF_2(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call, Expr, 2) JSONCONS_REP_OF_1(Call, Expr, , App) +#define JSONCONS_REP_OF_1(Call, Expr, Pre, App) Pre JSONCONS_EXPAND_CALL2(Call ## _LAST, Expr, 1) App +#define JSONCONS_REP_OF_0(Call, Expr, Pre, App) + +#define JSONCONS_GENERATE_TPL_PARAMS(Call, Count) JSONCONS_REP_OF_N(Call, , , ,Count) +#define JSONCONS_GENERATE_TPL_ARGS(Call, Count) JSONCONS_REP_OF_N(Call, ,<,>,Count) +#define JSONCONS_GENERATE_TPL_PARAM(Expr, Id) typename T ## Id, +#define JSONCONS_GENERATE_TPL_PARAM_LAST(Expr, Id) typename T ## Id +#define JSONCONS_GENERATE_MORE_TPL_PARAM(Expr, Id) ,typename T ## Id +#define JSONCONS_GENERATE_MORE_TPL_PARAM_LAST(Expr, Id) ,typename T ## Id +#define JSONCONS_GENERATE_TPL_ARG(Expr, Id) T ## Id, +#define JSONCONS_GENERATE_TPL_ARG_LAST(Ex, Id) T ## Id + +#define JSONCONS_GENERATE_NAME_STR(Prefix, P2, P3, Member, Count) JSONCONS_GENERATE_NAME_STR_LAST(Prefix, P2, P3, Member, Count) +#define JSONCONS_GENERATE_NAME_STR_LAST(Prefix, P2, P3, Member, Count) \ + static inline const string_view& Member(char) {static const string_view sv = JSONCONS_PP_QUOTE(,Member); return sv;} \ + static inline const wstring_view& Member(wchar_t) {static const wstring_view sv = JSONCONS_PP_QUOTE(L,Member); return sv;} \ + static inline const string_view& Member(unexpect_t) {static const string_view sv = # Prefix "::" # Member; return sv;} \ + /**/ + +#define JSONCONS_N_MEMBER_IS(Prefix, P2, P3, Member, Count) JSONCONS_N_MEMBER_IS_LAST(Prefix, P2, P3, Member, Count) +#define JSONCONS_N_MEMBER_IS_LAST(Prefix, P2, P3, Member, Count) if ((num_params-Count) < num_mandatory_params1 && !ajson.contains(json_object_name_members::Member(char_type{}))) return false; + +#define JSONCONS_N_MEMBER_AS(Prefix,P2,P3, Member, Count) JSONCONS_N_MEMBER_AS_LAST(Prefix,P2,P3, Member, Count) +#define JSONCONS_N_MEMBER_AS_LAST(Prefix,P2,P3, Member, Count) { \ + auto result = json_traits_helper::template try_get_member::type>(aset, ajson, json_object_name_members::Member(char_type{})); \ + if (result) { \ + set_member(std::move(*result), class_instance.Member); \ + } \ + else if ((num_params-Count) < num_mandatory_params2) {return result_type(jsoncons::unexpect, result.error().code(), json_object_name_members::Member(unexpect));} \ + else if (result.error().code() != conv_errc::missing_required_member){return result_type(jsoncons::unexpect, result.error().code(), json_object_name_members::Member(unexpect));} \ +} + +#define JSONCONS_ALL_MEMBER_AS(Prefix, P2,P3,Member, Count) JSONCONS_ALL_MEMBER_AS_LAST(Prefix,P2,P3, Member, Count) +#define JSONCONS_ALL_MEMBER_AS_LAST(Prefix,P2,P3, Member, Count) { \ + auto result = json_traits_helper::template try_get_member::type>(aset, ajson, json_object_name_members::Member(char_type{})); \ + if (result) { \ + set_member(std::move(*result), class_instance.Member); \ + } \ + else {return result_type(jsoncons::unexpect, result.error().code(), json_object_name_members::Member(unexpect));} \ +} + +#define JSONCONS_TO_JSON(Prefix, P2, P3, Member, Count) JSONCONS_TO_JSON_LAST(Prefix, P2, P3, Member, Count) +#define JSONCONS_TO_JSON_LAST(Prefix, P2, P3, Member, Count) if ((num_params-Count) < num_mandatory_params2) \ + {ajson.try_emplace(json_object_name_members::Member(char_type{}),class_instance.Member);} \ + else {json_traits_helper::set_optional_json_member(json_object_name_members::Member(char_type{}),class_instance.Member, ajson);} + +#define JSONCONS_ALL_TO_JSON(Prefix, P2, P3, Member, Count) JSONCONS_ALL_TO_JSON_LAST(Prefix, P2, P3, Member, Count) +#define JSONCONS_ALL_TO_JSON_LAST(Prefix, P2, P3, Member, Count) \ + ajson.try_emplace(json_object_name_members::Member(char_type{}),class_instance.Member); + +#define JSONCONS_N_MEMBER_ENCODE(Prefix, P2, P3, Member, Count) JSONCONS_N_MEMBER_ENCODE_LAST(Prefix, P2, P3, Member, Count) +#define JSONCONS_N_MEMBER_ENCODE_LAST(Prefix, P2, P3, Member, Count) \ +if ((num_params-Count) < num_mandatory_params2) \ + { \ + auto r = try_encode_member(json_object_name_members::Member(char_type{}), val.Member, encoder); \ + if (JSONCONS_UNLIKELY(!r)) {return r;} \ + } \ + else \ + { \ + auto r = try_encode_optional_member(json_object_name_members::Member(char_type{}), val.Member, encoder); \ + if (JSONCONS_UNLIKELY(!r)) {return r;} \ + } + +#define JSONCONS_ALL_MEMBER_ENCODE(Prefix, P2, P3, Member, Count) JSONCONS_ALL_MEMBER_ENCODE_LAST(Prefix, P2, P3, Member, Count) +#define JSONCONS_ALL_MEMBER_ENCODE_LAST(Prefix, P2, P3, Member, Count) \ + {auto r = try_encode_member(json_object_name_members::Member(char_type{}), val.Member, encoder); \ + if (JSONCONS_UNLIKELY(!r)) {return r;}} + +#define JSONCONS_MEMBER_COUNT(Prefix, P2, P3, Member, Count) JSONCONS_MEMBER_COUNT_LAST(Prefix, P2, P3, Member, Count) +#define JSONCONS_MEMBER_COUNT_LAST(Prefix, P2, P3, Member, Count) \ +if ((num_params-Count) < num_mandatory_params2) \ +{ \ + ++member_count; \ +} \ +else \ +{ \ + if (is_optional_value_set(val.Member)) \ + { \ + ++member_count; \ + } \ +} + +#define JSONCONS_MEMBER_TRAITS_BASE(ToJson,Encode,NumTemplateParams,ClassName,NumMandatoryParams1,NumMandatoryParams2, ...) \ +namespace jsoncons { \ +namespace reflect { \ + template \ + struct json_object_name_members \ + { \ + JSONCONS_VARIADIC_FOR_EACH(JSONCONS_GENERATE_NAME_STR,ClassName,,, __VA_ARGS__)\ + }; \ + template \ + struct json_conv_traits \ + { \ + using value_type = ClassName JSONCONS_GENERATE_TPL_ARGS(JSONCONS_GENERATE_TPL_ARG, NumTemplateParams); \ + using result_type = conversion_result; \ + using char_type = typename Json::char_type; \ + using string_view_type = typename Json::string_view_type; \ + constexpr static size_t num_params = JSONCONS_NARGS(__VA_ARGS__); \ + constexpr static size_t num_mandatory_params1 = NumMandatoryParams1; \ + constexpr static size_t num_mandatory_params2 = NumMandatoryParams2; \ + static bool is(const Json& ajson) noexcept \ + { \ + if (!ajson.is_object()) return false; \ + JSONCONS_VARIADIC_FOR_EACH(JSONCONS_N_MEMBER_IS, ,,, __VA_ARGS__)\ + return true; \ + } \ + template \ + static result_type try_as(const allocator_set& aset, const Json& ajson) \ + { \ + if (!ajson.is_object()) return result_type(jsoncons::unexpect, conv_errc::not_map, # ClassName); \ + value_type class_instance = jsoncons::make_obj_using_allocator(aset.get_allocator()); \ + if (num_params == num_mandatory_params2) \ + { \ + JSONCONS_VARIADIC_FOR_EACH(JSONCONS_ALL_MEMBER_AS,,,, __VA_ARGS__) \ + } \ + else \ + { \ + JSONCONS_VARIADIC_FOR_EACH(JSONCONS_N_MEMBER_AS,,,, __VA_ARGS__) \ + } \ + return result_type(std::move(class_instance)); \ + } \ + template \ + static Json to_json(const allocator_set& aset, const value_type& class_instance) \ + { \ + Json ajson = jsoncons::make_obj_using_allocator(aset.get_allocator(), json_object_arg, semantic_tag::none); \ + JSONCONS_VARIADIC_FOR_EACH(ToJson, ,,, __VA_ARGS__) \ + return ajson; \ + } \ + }; \ + template \ + struct encode_traits \ + { \ + using value_type = ClassName JSONCONS_GENERATE_TPL_ARGS(JSONCONS_GENERATE_TPL_ARG, NumTemplateParams); \ + constexpr static size_t num_params = JSONCONS_NARGS(__VA_ARGS__); \ + constexpr static size_t num_mandatory_params1 = NumMandatoryParams1; \ + constexpr static size_t num_mandatory_params2 = NumMandatoryParams2; \ + template \ + static write_result try_encode(const allocator_set&, const value_type& val, \ + basic_json_visitor& encoder) \ + { \ + std::error_code ec; \ + using char_type = CharT; \ + (void)num_params; (void)num_mandatory_params1; (void)num_mandatory_params2; \ + std::size_t member_count{0}; \ + JSONCONS_VARIADIC_FOR_EACH(JSONCONS_MEMBER_COUNT, ,,, __VA_ARGS__) \ + encoder.begin_object(member_count, semantic_tag::none, ser_context(), ec); \ + if (JSONCONS_UNLIKELY(ec)) {return write_result{unexpect, ec};} \ + JSONCONS_VARIADIC_FOR_EACH(Encode, ,,, __VA_ARGS__) \ + encoder.end_object(ser_context(), ec); \ + if (JSONCONS_UNLIKELY(ec)) {return write_result{unexpect, ec};} \ + return write_result{}; \ + } \ + }; \ +} \ +} \ + /**/ + +#define JSONCONS_N_MEMBER_TRAITS(ClassName,NumMandatoryParams,...) \ + JSONCONS_MEMBER_TRAITS_BASE(JSONCONS_TO_JSON, JSONCONS_N_MEMBER_ENCODE, 0, ClassName,NumMandatoryParams,NumMandatoryParams, __VA_ARGS__) \ + namespace jsoncons { template <> struct is_json_type_traits_declared : public std::true_type {}; } \ + /**/ + +#define JSONCONS_TPL_N_MEMBER_TRAITS(NumTemplateParams, ClassName,NumMandatoryParams, ...) \ + JSONCONS_MEMBER_TRAITS_BASE(JSONCONS_TO_JSON, JSONCONS_N_MEMBER_ENCODE,NumTemplateParams, ClassName,NumMandatoryParams,NumMandatoryParams, __VA_ARGS__) \ + namespace jsoncons { template struct is_json_type_traits_declared : public std::true_type {}; } \ + /**/ + +#define JSONCONS_ALL_MEMBER_TRAITS(ClassName, ...) \ + JSONCONS_MEMBER_TRAITS_BASE(JSONCONS_ALL_TO_JSON, JSONCONS_ALL_MEMBER_ENCODE,0,ClassName, JSONCONS_NARGS(__VA_ARGS__), JSONCONS_NARGS(__VA_ARGS__),__VA_ARGS__) \ + namespace jsoncons { template <> struct is_json_type_traits_declared : public std::true_type {}; } \ + /**/ + +#define JSONCONS_TPL_ALL_MEMBER_TRAITS(NumTemplateParams, ClassName, ...) \ + JSONCONS_MEMBER_TRAITS_BASE(JSONCONS_ALL_TO_JSON, JSONCONS_ALL_MEMBER_ENCODE,NumTemplateParams,ClassName, JSONCONS_NARGS(__VA_ARGS__), JSONCONS_NARGS(__VA_ARGS__),__VA_ARGS__) \ + namespace jsoncons { template struct is_json_type_traits_declared : public std::true_type {}; } \ + /**/ + +#define JSONCONS_MEMBER_NAME_IS(P1, P2, P3, Seq, Count) JSONCONS_MEMBER_NAME_IS_LAST(P1, P2, P3, Seq, Count) +#define JSONCONS_MEMBER_NAME_IS_LAST(P1, P2, P3, Seq, Count) if ((num_params-Count) < num_mandatory_params1 && JSONCONS_PP_EXPAND(JSONCONS_PP_CONCAT(JSONCONS_MEMBER_NAME_IS_,JSONCONS_NARGS Seq) Seq) +#define JSONCONS_MEMBER_NAME_IS_2(Member, Name) !ajson.contains(Name)) return false; +#define JSONCONS_MEMBER_NAME_IS_3(Member, Name, Mode) JSONCONS_MEMBER_NAME_IS_2(Member, Name) +#define JSONCONS_MEMBER_NAME_IS_4(Member, Name, Mode, Match) JSONCONS_MEMBER_NAME_IS_6(Member, Name, Mode, Match, , ) +#define JSONCONS_MEMBER_NAME_IS_5(Member, Name, Mode, Match, Into) JSONCONS_MEMBER_NAME_IS_6(Member, Name, Mode, Match, Into, ) +#define JSONCONS_MEMBER_NAME_IS_6(Member, Name, Mode, Match, Into, From) !ajson.contains(Name)) return false; \ + JSONCONS_TRY{if (!Match(ajson.at(Name).template as())->Member))>::type>())) return false;} \ + JSONCONS_CATCH(...) {return false;} + +#define JSONCONS_N_MEMBER_NAME_AS(P1, P2, P3, Seq, Count) JSONCONS_N_MEMBER_NAME_AS_LAST(P1, P2, P3, Seq, Count) +#define JSONCONS_N_MEMBER_NAME_AS_LAST(P1, P2, P3, Seq, Count) index = num_params-Count; JSONCONS_PP_EXPAND(JSONCONS_PP_CONCAT(JSONCONS_N_MEMBER_NAME_AS_,JSONCONS_NARGS Seq) Seq) +#define JSONCONS_N_MEMBER_NAME_AS_2(Member, Name) JSONCONS_N_MEMBER_NAME_AS_7(Member, Name,JSONCONS_RDWR,always_true(),,) +#define JSONCONS_N_MEMBER_NAME_AS_3(Member, Name, Mode) Mode(JSONCONS_N_MEMBER_NAME_AS_7(Member, Name, Mode,always_true(),,)) +#define JSONCONS_N_MEMBER_NAME_AS_4(Member, Name, Mode, Match) JSONCONS_N_MEMBER_NAME_AS_7(Member, Name, Mode, Match,,) +#define JSONCONS_N_MEMBER_NAME_AS_5(Member, Name, Mode, Match, Into) JSONCONS_N_MEMBER_NAME_AS_7(Member, Name, Mode, Match, Into,) +#define JSONCONS_N_MEMBER_NAME_AS_6(Member, Name, Mode, Match, Into, From) JSONCONS_N_MEMBER_NAME_AS_7(Member, Name, Mode, Match, Into, From) +#define JSONCONS_N_MEMBER_NAME_AS_7(Member, Name, Mode, Match, Into, From) { \ + auto result = json_traits_helper::template try_get_member::type>(aset, ajson, Name); \ + if (result && !Match(From(* result))) {return result_type(jsoncons::unexpect, conv_errc::conversion_failed, class_name);} \ + Mode(JSONCONS_N_MEMBER_NAME_AS_8(Member, Name, Mode, Match, Into, From)) } +#define JSONCONS_N_MEMBER_NAME_AS_8(Member, Name, Mode, Match, Into, From) \ + if (result) { \ + set_member(From(std::move(*result)), class_instance.Member); \ + } \ + else if (index < num_mandatory_params2) {return result_type(jsoncons::unexpect, result.error().code(), class_name);} \ + else if (result.error().code() != conv_errc::missing_required_member){return result_type(jsoncons::unexpect, result.error().code(), class_name);} + +#define JSONCONS_ALL_MEMBER_NAME_AS(P1, P2, P3, Seq, Count) JSONCONS_ALL_MEMBER_NAME_AS_LAST(P1, P2, P3, Seq, Count) +#define JSONCONS_ALL_MEMBER_NAME_AS_LAST(P1, P2, P3, Seq, Count) JSONCONS_PP_EXPAND(JSONCONS_PP_CONCAT(JSONCONS_ALL_MEMBER_NAME_AS_,JSONCONS_NARGS Seq) Seq) +#define JSONCONS_ALL_MEMBER_NAME_AS_2(Member, Name) JSONCONS_ALL_MEMBER_NAME_AS_7(Member, Name,JSONCONS_RDWR,always_true(),,) +#define JSONCONS_ALL_MEMBER_NAME_AS_3(Member, Name, Mode) Mode(JSONCONS_ALL_MEMBER_NAME_AS_7(Member, Name,Mode,always_true(),,)) +#define JSONCONS_ALL_MEMBER_NAME_AS_4(Member, Name, Mode, Match) JSONCONS_ALL_MEMBER_NAME_AS_7(Member, Name, Mode, Match,,) +#define JSONCONS_ALL_MEMBER_NAME_AS_5(Member, Name, Mode, Match, Into) JSONCONS_ALL_MEMBER_NAME_AS_7(Member, Name, Mode, Match, Into,) +#define JSONCONS_ALL_MEMBER_NAME_AS_6(Member, Name, Mode, Match, Into, From) JSONCONS_ALL_MEMBER_NAME_AS_7(Member, Name, Mode, Match, Into, From) +#define JSONCONS_ALL_MEMBER_NAME_AS_7(Member, Name, Mode, Match, Into, From) { \ + auto result = json_traits_helper::template try_get_member::type>(aset, ajson, Name); \ + if (result && !Match(From(* result))) {return result_type(jsoncons::unexpect, conv_errc::conversion_failed, class_name);} \ + Mode(JSONCONS_ALL_MEMBER_NAME_AS_8(Member, Name, Mode, Match, Into, From)) } +#define JSONCONS_ALL_MEMBER_NAME_AS_8(Member, Name, Mode, Match, Into, From) \ + if (result) { \ + set_member(From(std::move(*result)), class_instance.Member); \ + } \ + else {return result_type(jsoncons::unexpect, result.error().code(), class_name);} + +#define JSONCONS_N_MEMBER_NAME_TO_JSON(P1, P2, P3, Seq, Count) JSONCONS_N_MEMBER_NAME_TO_JSON_LAST(P1, P2, P3, Seq, Count) +#define JSONCONS_N_MEMBER_NAME_TO_JSON_LAST(P1, P2, P3, Seq, Count) if ((num_params-Count) < num_mandatory_params2) JSONCONS_PP_EXPAND(JSONCONS_PP_CONCAT(JSONCONS_N_MEMBER_NAME_TO_JSON_,JSONCONS_NARGS Seq) Seq) +#define JSONCONS_N_MEMBER_NAME_TO_JSON_2(Member, Name) \ + {ajson.try_emplace(Name,class_instance.Member);} \ +else \ + {json_traits_helper::set_optional_json_member(string_view_type(Name),class_instance.Member, ajson);} +#define JSONCONS_N_MEMBER_NAME_TO_JSON_3(Member, Name, Mode) JSONCONS_N_MEMBER_NAME_TO_JSON_2(Member, Name) +#define JSONCONS_N_MEMBER_NAME_TO_JSON_4(Member, Name, Mode, Match) JSONCONS_N_MEMBER_NAME_TO_JSON_6(Member, Name, Mode, Match,,) +#define JSONCONS_N_MEMBER_NAME_TO_JSON_5(Member, Name, Mode, Match, Into) JSONCONS_N_MEMBER_NAME_TO_JSON_6(Member, Name, Mode, Match, Into, ) +#define JSONCONS_N_MEMBER_NAME_TO_JSON_6(Member, Name, Mode, Match, Into, From) \ + {ajson.try_emplace(Name, Into(class_instance.Member));} \ +else \ + {json_traits_helper::set_optional_json_member(string_view_type(Name), Into(class_instance.Member), ajson);} + +#define JSONCONS_ALL_MEMBER_NAME_TO_JSON(P1, P2, P3, Seq, Count) JSONCONS_ALL_MEMBER_NAME_TO_JSON_LAST(P1, P2, P3, Seq, Count) +#define JSONCONS_ALL_MEMBER_NAME_TO_JSON_LAST(P1, P2, P3, Seq, Count) JSONCONS_PP_EXPAND(JSONCONS_PP_CONCAT(JSONCONS_ALL_MEMBER_NAME_TO_JSON_,JSONCONS_NARGS Seq) Seq) +#define JSONCONS_ALL_MEMBER_NAME_TO_JSON_2(Member, Name) ajson.try_emplace(Name,class_instance.Member); +#define JSONCONS_ALL_MEMBER_NAME_TO_JSON_3(Member, Name, Mode) JSONCONS_ALL_MEMBER_NAME_TO_JSON_2(Member, Name) +#define JSONCONS_ALL_MEMBER_NAME_TO_JSON_4(Member, Name, Mode, Match) JSONCONS_ALL_MEMBER_NAME_TO_JSON_6(Member, Name, Mode, Match,,) +#define JSONCONS_ALL_MEMBER_NAME_TO_JSON_5(Member, Name, Mode, Match, Into) JSONCONS_ALL_MEMBER_NAME_TO_JSON_6(Member, Name, Mode, Match, Into, ) +#define JSONCONS_ALL_MEMBER_NAME_TO_JSON_6(Member, Name, Mode, Match, Into, From) ajson.try_emplace(Name, Into(class_instance.Member)); + +#define JSONCONS_N_MEMBER_NAME_ENCODE(P1, P2, P3, Seq, Count) JSONCONS_N_MEMBER_NAME_ENCODE_LAST(P1, P2, P3, Seq, Count) +#define JSONCONS_N_MEMBER_NAME_ENCODE_LAST(P1, P2, P3, Seq, Count) if ((num_params-Count) < num_mandatory_params2) JSONCONS_PP_EXPAND(JSONCONS_PP_CONCAT(JSONCONS_N_MEMBER_NAME_ENCODE_,JSONCONS_NARGS Seq) Seq) +#define JSONCONS_N_MEMBER_NAME_ENCODE_2(Member, Name) \ + { \ + auto r = try_encode_member(string_view_type(Name), val.Member, encoder); \ + if (JSONCONS_UNLIKELY(!r)) {return r;} \ + } \ + else \ + { \ + auto r = try_encode_optional_member(string_view_type(Name), val.Member, encoder); \ + if (JSONCONS_UNLIKELY(!r)) {return r;} \ + } +#define JSONCONS_N_MEMBER_NAME_ENCODE_3(Member, Name, Mode) JSONCONS_N_MEMBER_NAME_ENCODE_2(Member, Name) +#define JSONCONS_N_MEMBER_NAME_ENCODE_4(Member, Name, Mode, Match) JSONCONS_N_MEMBER_NAME_ENCODE_6(Member, Name, Mode, Match,,) +#define JSONCONS_N_MEMBER_NAME_ENCODE_5(Member, Name, Mode, Match, Into) JSONCONS_N_MEMBER_NAME_ENCODE_6(Member, Name, Mode, Match, Into, ) +#define JSONCONS_N_MEMBER_NAME_ENCODE_6(Member, Name, Mode, Match, Into, From) \ +{ \ + auto r = try_encode_member(string_view_type(Name), Into(val.Member), encoder); \ + if (JSONCONS_UNLIKELY(!r)) {return r;} \ +} \ +else \ +{ \ + auto r = try_encode_optional_member(string_view_type(Name), Into(val.Member), encoder); \ + if (JSONCONS_UNLIKELY(!r)) {return r;} \ +} + +#define JSONCONS_ALL_MEMBER_ENCODE_NAME(P1, P2, P3, Seq, Count) JSONCONS_ALL_MEMBER_ENCODE_NAME_LAST(P1, P2, P3, Seq, Count) +#define JSONCONS_ALL_MEMBER_ENCODE_NAME_LAST(P1, P2, P3, Seq, Count) JSONCONS_PP_EXPAND(JSONCONS_PP_CONCAT(JSONCONS_ALL_MEMBER_ENCODE_NAME_,JSONCONS_NARGS Seq) Seq) +#define JSONCONS_ALL_MEMBER_ENCODE_NAME_2(Member, Name) \ + {auto r = try_encode_member(string_view_type(Name), val.Member, encoder); if (JSONCONS_UNLIKELY(!r)) {return r;}} +#define JSONCONS_ALL_MEMBER_ENCODE_NAME_3(Member, Name, Mode) JSONCONS_ALL_MEMBER_ENCODE_NAME_2(Member, Name) +#define JSONCONS_ALL_MEMBER_ENCODE_NAME_4(Member, Name, Mode, Match) JSONCONS_ALL_MEMBER_ENCODE_NAME_6(Member, Name, Mode, Match,,) +#define JSONCONS_ALL_MEMBER_ENCODE_NAME_5(Member, Name, Mode, Match, Into) JSONCONS_ALL_MEMBER_ENCODE_NAME_6(Member, Name, Mode, Match, Into, ) +#define JSONCONS_ALL_MEMBER_ENCODE_NAME_6(Member, Name, Mode, Match, Into, From) \ + {auto r = try_encode_member(string_view_type(Name), Into(val.Member), encoder); if (JSONCONS_UNLIKELY(!r)) {return r;}} + +#define JSONCONS_MEMBER_NAME_COUNT(P1, P2, P3, Seq, Count) JSONCONS_MEMBER_NAME_COUNT_LAST(P1, P2, P3, Seq, Count) +#define JSONCONS_MEMBER_NAME_COUNT_LAST(P1, P2, P3, Seq, Count) if ((num_params-Count) < num_mandatory_params2) JSONCONS_PP_EXPAND(JSONCONS_PP_CONCAT(JSONCONS_MEMBER_NAME_COUNT_,JSONCONS_NARGS Seq) Seq) +#define JSONCONS_MEMBER_NAME_COUNT_2(Member, Name) \ +{ \ + ++member_count; \ +} \ +else \ +{ \ + if (is_optional_value_set(val.Member)) \ + { \ + ++member_count; \ + } \ +} +#define JSONCONS_MEMBER_NAME_COUNT_3(Member, Name, Mode) JSONCONS_MEMBER_NAME_COUNT_2(Member, Name) +#define JSONCONS_MEMBER_NAME_COUNT_4(Member, Name, Mode, Match) JSONCONS_MEMBER_NAME_COUNT_6(Member, Name, Mode, Match,,) +#define JSONCONS_MEMBER_NAME_COUNT_5(Member, Name, Mode, Match, Into) JSONCONS_MEMBER_NAME_COUNT_6(Member, Name, Mode, Match, Into, ) +#define JSONCONS_MEMBER_NAME_COUNT_6(Member, Name, Mode, Match, Into, From) \ +{ \ + ++member_count; \ +} \ +else \ +{ \ + if (is_optional_value_set(val.Member)) \ + { \ + ++member_count; \ + } \ +} + +#define JSONCONS_MEMBER_NAME_TRAITS_BASE(ToJson,Encode, NumTemplateParams, ClassName,NumMandatoryParams1,NumMandatoryParams2, ...) \ +namespace jsoncons { \ +namespace reflect { \ + template \ + struct json_conv_traits \ + { \ + using value_type = ClassName JSONCONS_GENERATE_TPL_ARGS(JSONCONS_GENERATE_TPL_ARG, NumTemplateParams); \ + using result_type = conversion_result; \ + using char_type = typename Json::char_type; \ + using string_view_type = typename Json::string_view_type; \ + constexpr static size_t num_params = JSONCONS_NARGS(__VA_ARGS__); \ + constexpr static size_t num_mandatory_params1 = NumMandatoryParams1; \ + constexpr static size_t num_mandatory_params2 = NumMandatoryParams2; \ + static bool is(const Json& ajson) noexcept \ + { \ + if (!ajson.is_object()) return false; \ + JSONCONS_VARIADIC_FOR_EACH(JSONCONS_MEMBER_NAME_IS,,,, __VA_ARGS__)\ + return true; \ + } \ + template \ + static result_type try_as(const allocator_set& aset, const Json& ajson) \ + { \ + const char* class_name = # ClassName; \ + std::error_code ec; \ + if (!ajson.is_object()) return result_type(jsoncons::unexpect, conv_errc::not_map, # ClassName); \ + value_type class_instance = jsoncons::make_obj_using_allocator(aset.get_allocator()); \ + if (num_params == num_mandatory_params2) \ + { \ + JSONCONS_VARIADIC_FOR_EACH(JSONCONS_ALL_MEMBER_NAME_AS,,,, __VA_ARGS__) \ + } \ + else \ + { \ + std::size_t index = 0; \ + JSONCONS_VARIADIC_FOR_EACH(JSONCONS_N_MEMBER_NAME_AS,,,, __VA_ARGS__) \ + } \ + return result_type(std::move(class_instance)); \ + } \ + template \ + static Json to_json(const allocator_set& aset, const value_type& class_instance) \ + { \ + Json ajson = jsoncons::make_obj_using_allocator(aset.get_allocator(), json_object_arg, semantic_tag::none); \ + JSONCONS_VARIADIC_FOR_EACH(ToJson,,,, __VA_ARGS__) \ + return ajson; \ + } \ + }; \ + template \ + struct encode_traits \ + { \ + using value_type = ClassName JSONCONS_GENERATE_TPL_ARGS(JSONCONS_GENERATE_TPL_ARG, NumTemplateParams); \ + constexpr static size_t num_params = JSONCONS_NARGS(__VA_ARGS__); \ + constexpr static size_t num_mandatory_params1 = NumMandatoryParams1; \ + constexpr static size_t num_mandatory_params2 = NumMandatoryParams2; \ + template \ + static write_result try_encode(const allocator_set&, const value_type& val, \ + basic_json_visitor& encoder) \ + { \ + using char_type = CharT; \ + using string_view_type = basic_string_view; \ + (void)num_params; (void)num_mandatory_params1; (void)num_mandatory_params2; \ + std::error_code ec; \ + std::size_t member_count{0}; \ + JSONCONS_VARIADIC_FOR_EACH(JSONCONS_MEMBER_NAME_COUNT, ,,, __VA_ARGS__) \ + encoder.begin_object(member_count, semantic_tag::none, ser_context(), ec); \ + if (JSONCONS_UNLIKELY(ec)) {return write_result{unexpect, ec};} \ + JSONCONS_VARIADIC_FOR_EACH(Encode, ,,, __VA_ARGS__) \ + encoder.end_object(ser_context(), ec); \ + if (JSONCONS_UNLIKELY(ec)) {return write_result{unexpect, ec};} \ + return write_result{}; \ + } \ + }; \ +} \ +} \ + /**/ + + +#define JSONCONS_N_MEMBER_NAME_TRAITS(ClassName,NumMandatoryParams, ...) \ + JSONCONS_MEMBER_NAME_TRAITS_BASE(JSONCONS_N_MEMBER_NAME_TO_JSON, JSONCONS_N_MEMBER_NAME_ENCODE, 0, ClassName,NumMandatoryParams,NumMandatoryParams, __VA_ARGS__) \ + namespace jsoncons { template <> struct is_json_type_traits_declared : public std::true_type {}; } \ + /**/ + +#define JSONCONS_TPL_N_MEMBER_NAME_TRAITS(NumTemplateParams, ClassName,NumMandatoryParams, ...) \ + JSONCONS_MEMBER_NAME_TRAITS_BASE(JSONCONS_N_MEMBER_NAME_TO_JSON, JSONCONS_N_MEMBER_NAME_ENCODE, NumTemplateParams, ClassName,NumMandatoryParams,NumMandatoryParams, __VA_ARGS__) \ + namespace jsoncons { template struct is_json_type_traits_declared : public std::true_type {}; } \ + /**/ + +#define JSONCONS_ALL_MEMBER_NAME_TRAITS(ClassName, ...) \ + JSONCONS_MEMBER_NAME_TRAITS_BASE(JSONCONS_ALL_MEMBER_NAME_TO_JSON, JSONCONS_ALL_MEMBER_ENCODE_NAME, 0, ClassName, JSONCONS_NARGS(__VA_ARGS__), JSONCONS_NARGS(__VA_ARGS__), __VA_ARGS__) \ + namespace jsoncons { template <> struct is_json_type_traits_declared : public std::true_type {}; } \ + /**/ + +#define JSONCONS_TPL_ALL_MEMBER_NAME_TRAITS(NumTemplateParams, ClassName, ...) \ + JSONCONS_MEMBER_NAME_TRAITS_BASE(JSONCONS_ALL_MEMBER_NAME_TO_JSON, JSONCONS_ALL_MEMBER_ENCODE_NAME, NumTemplateParams, ClassName, JSONCONS_NARGS(__VA_ARGS__), JSONCONS_NARGS(__VA_ARGS__), __VA_ARGS__) \ + namespace jsoncons { template struct is_json_type_traits_declared : public std::true_type {}; } \ + /**/ + +#define JSONCONS_CTOR_GETTER_IS(Prefix, P2, P3, Getter, Count) JSONCONS_CTOR_GETTER_IS_LAST(Prefix, P2, P3, Getter, Count) +#define JSONCONS_CTOR_GETTER_IS_LAST(Prefix, P2, P3, Getter, Count) if ((num_params-Count) < num_mandatory_params1 && !ajson.contains(json_object_name_members::Getter(char_type{}))) return false; + +#define JSONCONS_CTOR_GETTER_GET(Prefix, P2, P3, Getter, Count) JSONCONS_CTOR_GETTER_GET_LAST(Prefix, P2, P3, Getter, Count) +#define JSONCONS_CTOR_GETTER_GET_LAST(Prefix, P2, P3, Getter, Count) \ + auto _r ## Getter = json_traits_helper::template try_get_member())->Getter())>::type>(aset, ajson, json_object_name_members::Getter(char_type{})); \ + if (!_r ## Getter && (num_params-Count) < num_mandatory_params2) {return result_type(jsoncons::unexpect, _r ## Getter.error().code(), json_object_name_members::Getter(unexpect));} + +#define JSONCONS_CTOR_GETTER_AS(Prefix, P2, P3, Getter, Count) JSONCONS_CTOR_GETTER_AS_LAST(Prefix, P2, P3, Getter, Count), +#define JSONCONS_CTOR_GETTER_AS_LAST(Prefix, P2, P3, Getter, Count) \ + _r ## Getter ? std::move(*_r ## Getter) : jsoncons::make_obj_using_allocator())->Getter())>::type>(aset.get_allocator()) + +#define JSONCONS_CTOR_GETTER_TO_JSON(Prefix, P2, P3, Getter, Count) JSONCONS_CTOR_GETTER_TO_JSON_LAST(Prefix, P2, P3, Getter, Count) +#define JSONCONS_CTOR_GETTER_TO_JSON_LAST(Prefix, P2, P3, Getter, Count) \ +if ((num_params-Count) < num_mandatory_params2) { \ + ajson.try_emplace(json_object_name_members::Getter(char_type{}),class_instance.Getter()); \ + } \ +else { \ + json_traits_helper::set_optional_json_member(json_object_name_members::Getter(char_type{}),class_instance.Getter(), ajson); \ +} + +#define JSONCONS_CTOR_GETTER_COUNT(Prefix, P2, P3, Getter, Count) JSONCONS_CTOR_GETTER_COUNT_LAST(Prefix, P2, P3, Getter, Count) +#define JSONCONS_CTOR_GETTER_COUNT_LAST(Prefix, P2, P3, Getter, Count) \ +if ((num_params-Count) < num_mandatory_params2) \ +{ \ + ++member_count; \ +} \ +else \ +{ \ + if (is_optional_value_set(val.Getter())) \ + { \ + ++member_count; \ + } \ +} + +#define JSONCONS_CTOR_GETTER_ENCODE(Prefix, P2, P3, Getter, Count) JSONCONS_CTOR_GETTER_ENCODE_LAST(Prefix, P2, P3, Getter, Count) +#define JSONCONS_CTOR_GETTER_ENCODE_LAST(Prefix, P2, P3, Getter, Count) \ +if ((num_params-Count) < num_mandatory_params2) \ +{ \ + auto r = try_encode_member(json_object_name_members::Getter(char_type{}), val.Getter(), encoder); \ + if (JSONCONS_UNLIKELY(!r)) {return r;} \ +} \ +else \ +{ \ + auto r = try_encode_optional_member(json_object_name_members::Getter(char_type{}), val.Getter(), encoder); \ + if (JSONCONS_UNLIKELY(!r)) {return r;} \ +} + +#define JSONCONS_CTOR_GETTER_TRAITS_BASE(NumTemplateParams, ClassName,NumMandatoryParams1,NumMandatoryParams2, ...) \ +namespace jsoncons { \ +namespace reflect { \ + template \ + struct json_object_name_members \ + { \ + JSONCONS_VARIADIC_FOR_EACH(JSONCONS_GENERATE_NAME_STR,ClassName,,, __VA_ARGS__)\ + }; \ + template \ + struct json_conv_traits \ + { \ + using value_type = ClassName JSONCONS_GENERATE_TPL_ARGS(JSONCONS_GENERATE_TPL_ARG, NumTemplateParams); \ + using result_type = conversion_result; \ + using char_type = typename Json::char_type; \ + using string_view_type = typename Json::string_view_type; \ + constexpr static size_t num_params = JSONCONS_NARGS(__VA_ARGS__); \ + constexpr static size_t num_mandatory_params1 = NumMandatoryParams1; \ + constexpr static size_t num_mandatory_params2 = NumMandatoryParams2; \ + static bool is(const Json& ajson) noexcept \ + { \ + if (!ajson.is_object()) return false; \ + JSONCONS_VARIADIC_FOR_EACH(JSONCONS_CTOR_GETTER_IS, ,,, __VA_ARGS__)\ + return true; \ + } \ + template \ + static result_type try_as(const allocator_set& aset, const Json& ajson) \ + { \ + if (!ajson.is_object()) return result_type(jsoncons::unexpect, conv_errc::not_map, # ClassName); \ + JSONCONS_VARIADIC_FOR_EACH(JSONCONS_CTOR_GETTER_GET,ClassName,,, __VA_ARGS__) \ + return result_type(jsoncons::make_obj_using_allocator(aset.get_allocator(), JSONCONS_VARIADIC_FOR_EACH(JSONCONS_CTOR_GETTER_AS, ,,, __VA_ARGS__) )); \ + } \ + template \ + static Json to_json(const allocator_set& aset, const value_type& class_instance) \ + { \ + Json ajson = jsoncons::make_obj_using_allocator(aset.get_allocator(), json_object_arg, semantic_tag::none); \ + JSONCONS_VARIADIC_FOR_EACH(JSONCONS_CTOR_GETTER_TO_JSON, ,,, __VA_ARGS__) \ + return ajson; \ + } \ + }; \ + template \ + struct encode_traits \ + { \ + using value_type = ClassName JSONCONS_GENERATE_TPL_ARGS(JSONCONS_GENERATE_TPL_ARG, NumTemplateParams); \ + constexpr static size_t num_params = JSONCONS_NARGS(__VA_ARGS__); \ + constexpr static size_t num_mandatory_params1 = NumMandatoryParams1; \ + constexpr static size_t num_mandatory_params2 = NumMandatoryParams2; \ + template \ + static write_result try_encode(const allocator_set&, const value_type& val, \ + basic_json_visitor& encoder) \ + { \ + using char_type = CharT; \ + (void)num_params; (void)num_mandatory_params1; (void)num_mandatory_params2; \ + std::error_code ec; \ + std::size_t member_count{0}; \ + JSONCONS_VARIADIC_FOR_EACH(JSONCONS_CTOR_GETTER_COUNT, ,,, __VA_ARGS__) \ + encoder.begin_object(member_count, semantic_tag::none, ser_context(), ec); \ + if (JSONCONS_UNLIKELY(ec)) {return write_result{unexpect, ec};} \ + JSONCONS_VARIADIC_FOR_EACH(JSONCONS_CTOR_GETTER_ENCODE, ,,, __VA_ARGS__) \ + encoder.end_object(ser_context(), ec); \ + if (JSONCONS_UNLIKELY(ec)) {return write_result{unexpect, ec};} \ + return write_result{}; \ + } \ + }; \ +} \ +} \ + /**/ + +#define JSONCONS_ALL_CTOR_GETTER_TRAITS(ClassName, ...) \ + JSONCONS_CTOR_GETTER_TRAITS_BASE(0, ClassName, JSONCONS_NARGS(__VA_ARGS__), JSONCONS_NARGS(__VA_ARGS__), __VA_ARGS__) \ + namespace jsoncons { template <> struct is_json_type_traits_declared : public std::true_type {}; } \ + /**/ + +#define JSONCONS_TPL_ALL_CTOR_GETTER_TRAITS(NumTemplateParams, ClassName, ...) \ + JSONCONS_CTOR_GETTER_TRAITS_BASE(NumTemplateParams, ClassName, JSONCONS_NARGS(__VA_ARGS__), JSONCONS_NARGS(__VA_ARGS__), __VA_ARGS__) \ + namespace jsoncons { template struct is_json_type_traits_declared : public std::true_type {}; } \ + /**/ + +#define JSONCONS_N_CTOR_GETTER_TRAITS(ClassName,NumMandatoryParams, ...) \ + JSONCONS_CTOR_GETTER_TRAITS_BASE(0, ClassName,NumMandatoryParams,NumMandatoryParams, __VA_ARGS__) \ + namespace jsoncons { template <> struct is_json_type_traits_declared : public std::true_type {}; } \ + /**/ + +#define JSONCONS_N_ALL_CTOR_GETTER_TRAITS(NumTemplateParams, ClassName,NumMandatoryParams, ...) \ + JSONCONS_CTOR_GETTER_TRAITS_BASE(NumTemplateParams, ClassName,NumMandatoryParams,NumMandatoryParams, __VA_ARGS__) \ + namespace jsoncons { template <> struct is_json_type_traits_declared : public std::true_type {}; } \ + /**/ + +#define JSONCONS_CTOR_GETTER_NAME_IS(P1, P2, P3, Seq, Count) JSONCONS_CTOR_GETTER_NAME_IS_LAST(P1, P2, P3, Seq, Count) +#define JSONCONS_CTOR_GETTER_NAME_IS_LAST(P1, P2, P3, Seq, Count) if ((num_params-Count) < num_mandatory_params1 && JSONCONS_PP_EXPAND(JSONCONS_PP_CONCAT(JSONCONS_CTOR_GETTER_NAME_IS_,JSONCONS_NARGS Seq) Seq) +#define JSONCONS_CTOR_GETTER_NAME_IS_2(Getter, Name) !ajson.contains(Name)) return false; +#define JSONCONS_CTOR_GETTER_NAME_IS_3(Getter, Name, Mode) JSONCONS_CTOR_GETTER_NAME_IS_2(Getter, Name) +#define JSONCONS_CTOR_GETTER_NAME_IS_4(Getter, Name, Mode, Match) JSONCONS_CTOR_GETTER_NAME_IS_6(Getter, Name, Mode, Match, , ) +#define JSONCONS_CTOR_GETTER_NAME_IS_5(Getter, Name, Mode, Match, Into) JSONCONS_CTOR_GETTER_NAME_IS_6(Getter, Name, Mode, Match, Into, ) +#define JSONCONS_CTOR_GETTER_NAME_IS_6(Getter, Name, Mode, Match, Into, From) !ajson.contains(Name)) return false; \ + JSONCONS_TRY{if (!Match(ajson.at(Name).template as())->Getter()))>::type>())) return false;} \ + JSONCONS_CATCH(...) {return false;} + +#define JSONCONS_CTOR_GETTER_NAME_MATCH(P1, P2, P3, Seq, Count) JSONCONS_CTOR_GETTER_NAME_MATCH_LAST(P1, P2, P3, Seq, Count) +#define JSONCONS_CTOR_GETTER_NAME_MATCH_LAST(P1, P2, P3, Seq, Count) JSONCONS_PP_EXPAND(JSONCONS_PP_CONCAT(JSONCONS_CTOR_GETTER_NAME_MATCH_,JSONCONS_NARGS Seq) Seq) +#define JSONCONS_CTOR_GETTER_NAME_MATCH_2(Getter, Name) +#define JSONCONS_CTOR_GETTER_NAME_MATCH_3(Getter, Name, Mode) +#define JSONCONS_CTOR_GETTER_NAME_MATCH_4(Getter, Name, Mode, Match) JSONCONS_CTOR_GETTER_NAME_MATCH_6(Getter, Name, Mode, Match, , ) +#define JSONCONS_CTOR_GETTER_NAME_MATCH_5(Getter, Name, Mode, Match, Into) JSONCONS_CTOR_GETTER_NAME_MATCH_6(Getter, Name, Mode, Match, Into, ) +#define JSONCONS_CTOR_GETTER_NAME_MATCH_6(Getter, Name, Mode, Match, Into, From) { \ + auto result = json_traits_helper::template try_get_member())->Getter()))>::type>(aset, ajson, Name); \ + if (result && !Match(* result)) {return result_type(jsoncons::unexpect, conv_errc::conversion_failed, class_name);} \ +} + +#define JSONCONS_COMMA , + +#define JSONCONS_CTOR_GETTER_NAME_GET(P1, P2, P3, Seq, Count) JSONCONS_CTOR_GETTER_NAME_GET_LAST(P1, P2, P3, Seq, Count) +#define JSONCONS_CTOR_GETTER_NAME_GET_LAST(P1, P2, P3, Seq, Count) index = num_params-Count; JSONCONS_PP_EXPAND(JSONCONS_PP_CONCAT(JSONCONS_CTOR_GETTER_NAME_GET_,JSONCONS_NARGS Seq) Seq) +#define JSONCONS_CTOR_GETTER_NAME_GET_2(Getter, Name) JSONCONS_CTOR_GETTER_NAME_GET_7(Getter, Name,JSONCONS_RDWR,always_true(),,) +#define JSONCONS_CTOR_GETTER_NAME_GET_3(Getter, Name, Mode) Mode(JSONCONS_CTOR_GETTER_NAME_GET_7(Getter, Name, Mode, always_true(),,)) +#define JSONCONS_CTOR_GETTER_NAME_GET_4(Getter, Name, Mode, Match) Mode(JSONCONS_CTOR_GETTER_NAME_GET_7(Getter, Name, Mode, Match,,)) +#define JSONCONS_CTOR_GETTER_NAME_GET_5(Getter, Name, Mode, Match, Into) Mode(JSONCONS_CTOR_GETTER_NAME_GET_7(Getter, Name, Mode, Match, Into,)) +#define JSONCONS_CTOR_GETTER_NAME_GET_6(Getter, Name, Mode, Match, Into, From) Mode(JSONCONS_CTOR_GETTER_NAME_GET_7(Getter, Name, Mode, Match, Into, From)) +#define JSONCONS_CTOR_GETTER_NAME_GET_7(Getter, Name, Mode, Match, Into, From) \ + auto _r ## Getter = json_traits_helper::template try_get_member())->Getter()))>::type>(aset, ajson, Name); \ + if (!_r ## Getter && index < num_mandatory_params2) {return result_type(jsoncons::unexpect, _r ## Getter.error().code(), class_name);} \ + if (_r ## Getter && !Match(* _r ## Getter)) {return result_type(jsoncons::unexpect, conv_errc::conversion_failed, class_name);} + +#define JSONCONS_CTOR_GETTER_NAME_AS(P1, P2, P3, Seq, Count) JSONCONS_PP_EXPAND(JSONCONS_PP_CONCAT(JSONCONS_CTOR_GETTER_NAME_AS_,JSONCONS_NARGS Seq) Seq) +#define JSONCONS_CTOR_GETTER_NAME_AS_2(Getter, Name) JSONCONS_CTOR_GETTER_NAME_AS_LAST_7(Getter, Name,,,, ) JSONCONS_COMMA +#define JSONCONS_CTOR_GETTER_NAME_AS_3(Getter, Name, Mode) Mode(JSONCONS_CTOR_GETTER_NAME_AS_LAST_7(Getter, Name, Mode,,, ) JSONCONS_COMMA) +#define JSONCONS_CTOR_GETTER_NAME_AS_4(Getter, Name, Mode, Match) Mode(JSONCONS_CTOR_GETTER_NAME_AS_LAST_7(Getter, Name, Mode, Match,, ) JSONCONS_COMMA) +#define JSONCONS_CTOR_GETTER_NAME_AS_5(Getter, Name, Mode, Match, Into) Mode(JSONCONS_CTOR_GETTER_NAME_AS_LAST_7(Getter, Name, Mode, Match, Into, ) JSONCONS_COMMA) +#define JSONCONS_CTOR_GETTER_NAME_AS_6(Getter, Name, Mode, Match, Into, From) Mode(JSONCONS_CTOR_GETTER_NAME_AS_LAST_7(Getter,Name,Mode,Match,Into,From) JSONCONS_COMMA) + +#define JSONCONS_CTOR_GETTER_NAME_AS_LAST(P1, P2, P3, Seq, Count) JSONCONS_PP_EXPAND(JSONCONS_PP_CONCAT(JSONCONS_CTOR_GETTER_NAME_AS_LAST_,JSONCONS_NARGS Seq) Seq) +#define JSONCONS_CTOR_GETTER_NAME_AS_LAST_2(Getter, Name) JSONCONS_CTOR_GETTER_NAME_AS_LAST_7(Getter, Name,,,,) +#define JSONCONS_CTOR_GETTER_NAME_AS_LAST_3(Getter, Name, Mode) Mode(JSONCONS_CTOR_GETTER_NAME_AS_LAST_7(Getter, Name,Mode,,,)) +#define JSONCONS_CTOR_GETTER_NAME_AS_LAST_4(Getter, Name, Mode, Match) Mode(JSONCONS_CTOR_GETTER_NAME_AS_LAST_6(Getter, Name, Mode, Match,,)) +#define JSONCONS_CTOR_GETTER_NAME_AS_LAST_5(Getter, Name, Mode, Match, Into) Mode(JSONCONS_CTOR_GETTER_NAME_AS_LAST_6(Getter, Name, Mode, Match, Into, )) +#define JSONCONS_CTOR_GETTER_NAME_AS_LAST_6(Getter, Name, Mode, Match, Into, From) Mode(JSONCONS_CTOR_GETTER_NAME_AS_LAST_7(Getter, Name, Mode, Match, Into, From)) +#define JSONCONS_CTOR_GETTER_NAME_AS_LAST_7(Getter, Name, Mode, Match, Into, From) \ + _r ## Getter ? From(std::move(*_r ## Getter)) : From(jsoncons::make_obj_using_allocator())->Getter()))>::type>(aset.get_allocator())) + +#define JSONCONS_CTOR_GETTER_NAME_TO_JSON(P1, P2, P3, Seq, Count) JSONCONS_CTOR_GETTER_NAME_TO_JSON_LAST(P1, P2, P3, Seq, Count) +#define JSONCONS_CTOR_GETTER_NAME_TO_JSON_LAST(P1, P2, P3, Seq, Count) if ((num_params-Count) < num_mandatory_params2) JSONCONS_PP_EXPAND(JSONCONS_PP_CONCAT(JSONCONS_CTOR_GETTER_NAME_TO_JSON_,JSONCONS_NARGS Seq) Seq) +#define JSONCONS_CTOR_GETTER_NAME_TO_JSON_2(Getter, Name) \ +{ \ + ajson.try_emplace(Name,class_instance.Getter()); \ +} \ +else { \ + json_traits_helper::set_optional_json_member(string_view_type(Name),class_instance.Getter(), ajson); \ +} +#define JSONCONS_CTOR_GETTER_NAME_TO_JSON_3(Getter, Name, Mode) JSONCONS_CTOR_GETTER_NAME_TO_JSON_2(Getter, Name) +#define JSONCONS_CTOR_GETTER_NAME_TO_JSON_4(Getter, Name, Mode, Match) JSONCONS_CTOR_GETTER_NAME_TO_JSON_2(Getter, Name) +#define JSONCONS_CTOR_GETTER_NAME_TO_JSON_5(Getter, Name, Mode, Match, Into) JSONCONS_CTOR_GETTER_NAME_TO_JSON_6(Getter, Name, Mode, Match, Into, ) +#define JSONCONS_CTOR_GETTER_NAME_TO_JSON_6(Getter, Name, Mode, Match, Into, From) \ +{ \ + ajson.try_emplace(Name, Into(class_instance.Getter()) ); \ +} \ +else { \ + json_traits_helper::set_optional_json_member(string_view_type(Name), Into(class_instance.Getter()), ajson); \ +} + +#define JSONCONS_CTOR_GETTER_NAME_COUNT(P1, P2, P3, Seq, Count) JSONCONS_CTOR_GETTER_NAME_COUNT_LAST(P1, P2, P3, Seq, Count) +#define JSONCONS_CTOR_GETTER_NAME_COUNT_LAST(P1, P2, P3, Seq, Count) if ((num_params-Count) < num_mandatory_params2) JSONCONS_PP_EXPAND(JSONCONS_PP_CONCAT(JSONCONS_CTOR_GETTER_NAME_COUNT_,JSONCONS_NARGS Seq) Seq) +#define JSONCONS_CTOR_GETTER_NAME_COUNT_2(Getter, Name) \ +{ \ + ++member_count; \ +} \ +else \ +{ \ + if (is_optional_value_set(val.Getter())) \ + { \ + ++member_count; \ + } \ +} +#define JSONCONS_CTOR_GETTER_NAME_COUNT_3(Getter, Name, Mode) JSONCONS_CTOR_GETTER_NAME_COUNT_2(Getter, Name) +#define JSONCONS_CTOR_GETTER_NAME_COUNT_4(Getter, Name, Mode, Match) JSONCONS_CTOR_GETTER_NAME_COUNT_2(Getter, Name) +#define JSONCONS_CTOR_GETTER_NAME_COUNT_5(Getter, Name, Mode, Match, Into) JSONCONS_CTOR_GETTER_NAME_COUNT_6(Getter, Name, Mode, Match, Into, ) +#define JSONCONS_CTOR_GETTER_NAME_COUNT_6(Getter, Name, Mode, Match, Into, From) \ +{ \ + ++member_count; \ +} \ +else \ +{ \ + if (is_optional_value_set(val.Getter())) \ + { \ + ++member_count; \ + } \ +} + +#define JSONCONS_CTOR_GETTER_NAME_ENCODE(P1, P2, P3, Seq, Count) JSONCONS_CTOR_GETTER_NAME_ENCODE_LAST(P1, P2, P3, Seq, Count) +#define JSONCONS_CTOR_GETTER_NAME_ENCODE_LAST(P1, P2, P3, Seq, Count) if ((num_params-Count) < num_mandatory_params2) JSONCONS_PP_EXPAND(JSONCONS_PP_CONCAT(JSONCONS_CTOR_GETTER_NAME_ENCODE_,JSONCONS_NARGS Seq) Seq) +#define JSONCONS_CTOR_GETTER_NAME_ENCODE_2(Getter, Name) \ +{ \ + auto r = try_encode_member(string_view_type(Name), val.Getter(), encoder); \ + if (JSONCONS_UNLIKELY(!r)) {return r;} \ +} \ +else \ +{ \ + auto r = try_encode_optional_member(string_view_type(Name), val.Getter(), encoder); \ + if (JSONCONS_UNLIKELY(!r)) {return r;} \ +} +#define JSONCONS_CTOR_GETTER_NAME_ENCODE_3(Getter, Name, Mode) JSONCONS_CTOR_GETTER_NAME_ENCODE_2(Getter, Name) +#define JSONCONS_CTOR_GETTER_NAME_ENCODE_4(Getter, Name, Mode, Match) JSONCONS_CTOR_GETTER_NAME_ENCODE_2(Getter, Name) +#define JSONCONS_CTOR_GETTER_NAME_ENCODE_5(Getter, Name, Mode, Match, Into) JSONCONS_CTOR_GETTER_NAME_ENCODE_6(Getter, Name, Mode, Match, Into, ) +#define JSONCONS_CTOR_GETTER_NAME_ENCODE_6(Getter, Name, Mode, Match, Into, From) \ +{ \ + auto r = try_encode_member(string_view_type(Name), Into(val.Getter()), encoder); \ + if (JSONCONS_UNLIKELY(!r)) {return r;} \ +} \ +else \ +{ \ + auto r = try_encode_optional_member(string_view_type(Name), Into(val.Getter()), encoder); \ + if (JSONCONS_UNLIKELY(!r)) {return r;} \ +} + +#define JSONCONS_CTOR_GETTER_NAME_TRAITS_BASE(NumTemplateParams, ClassName,NumMandatoryParams1,NumMandatoryParams2, ...) \ +namespace jsoncons { \ +namespace reflect { \ + template \ + struct json_conv_traits \ + { \ + using value_type = ClassName JSONCONS_GENERATE_TPL_ARGS(JSONCONS_GENERATE_TPL_ARG, NumTemplateParams); \ + using result_type = conversion_result; \ + using char_type = typename Json::char_type; \ + using string_view_type = typename Json::string_view_type; \ + constexpr static size_t num_params = JSONCONS_NARGS(__VA_ARGS__); \ + constexpr static size_t num_mandatory_params1 = NumMandatoryParams1; \ + constexpr static size_t num_mandatory_params2 = NumMandatoryParams2; \ + static bool is(const Json& ajson) noexcept \ + { \ + if (!ajson.is_object()) return false; \ + JSONCONS_VARIADIC_FOR_EACH(JSONCONS_CTOR_GETTER_NAME_IS,,,, __VA_ARGS__)\ + return true; \ + } \ + template \ + static result_type try_as(const allocator_set& aset, const Json& ajson) \ + { \ + const char* class_name = # ClassName; \ + if (!ajson.is_object()) return result_type(jsoncons::unexpect, conv_errc::not_map, # ClassName); \ + JSONCONS_VARIADIC_FOR_EACH(JSONCONS_CTOR_GETTER_NAME_MATCH,,,, __VA_ARGS__)\ + std::size_t index = 0; \ + JSONCONS_VARIADIC_FOR_EACH(JSONCONS_CTOR_GETTER_NAME_GET,ClassName,,, __VA_ARGS__) \ + return result_type(jsoncons::make_obj_using_allocator(aset.get_allocator(), JSONCONS_VARIADIC_FOR_EACH(JSONCONS_CTOR_GETTER_NAME_AS,,,, __VA_ARGS__))); \ + } \ + template \ + static Json to_json(const allocator_set& aset, const value_type& class_instance) \ + { \ + Json ajson = jsoncons::make_obj_using_allocator(aset.get_allocator(), json_object_arg, semantic_tag::none); \ + JSONCONS_VARIADIC_FOR_EACH(JSONCONS_CTOR_GETTER_NAME_TO_JSON,,,, __VA_ARGS__) \ + return ajson; \ + } \ + }; \ + template \ + struct encode_traits \ + { \ + using value_type = ClassName JSONCONS_GENERATE_TPL_ARGS(JSONCONS_GENERATE_TPL_ARG, NumTemplateParams); \ + constexpr static size_t num_params = JSONCONS_NARGS(__VA_ARGS__); \ + constexpr static size_t num_mandatory_params1 = NumMandatoryParams1; \ + constexpr static size_t num_mandatory_params2 = NumMandatoryParams2; \ + template \ + static write_result try_encode(const allocator_set&, const value_type& val, \ + basic_json_visitor& encoder) \ + { \ + using char_type = CharT; \ + using string_view_type = basic_string_view; \ + (void)num_params; (void)num_mandatory_params1; (void)num_mandatory_params2; \ + std::error_code ec; \ + std::size_t member_count{0}; \ + JSONCONS_VARIADIC_FOR_EACH(JSONCONS_CTOR_GETTER_NAME_COUNT,,,, __VA_ARGS__) \ + encoder.begin_object(member_count, semantic_tag::none, ser_context(), ec); \ + if (JSONCONS_UNLIKELY(ec)) {return write_result{unexpect, ec};} \ + JSONCONS_VARIADIC_FOR_EACH(JSONCONS_CTOR_GETTER_NAME_ENCODE,,,, __VA_ARGS__) \ + encoder.end_object(ser_context(), ec); \ + if (JSONCONS_UNLIKELY(ec)) {return write_result{unexpect, ec};} \ + return write_result{}; \ + } \ + }; \ +} \ +} \ + /**/ + +#define JSONCONS_ALL_CTOR_GETTER_NAME_TRAITS(ClassName, ...) \ + JSONCONS_CTOR_GETTER_NAME_TRAITS_BASE(0, ClassName, JSONCONS_NARGS(__VA_ARGS__), JSONCONS_NARGS(__VA_ARGS__), __VA_ARGS__) \ + namespace jsoncons { template <> struct is_json_type_traits_declared : public std::true_type {}; } \ + /**/ + +#define JSONCONS_TPL_ALL_CTOR_GETTER_NAME_TRAITS(NumTemplateParams, ClassName, ...) \ + JSONCONS_CTOR_GETTER_NAME_TRAITS_BASE(NumTemplateParams, ClassName, JSONCONS_NARGS(__VA_ARGS__), JSONCONS_NARGS(__VA_ARGS__), __VA_ARGS__) \ + namespace jsoncons { template struct is_json_type_traits_declared : public std::true_type {}; } \ + /**/ + +#define JSONCONS_N_CTOR_GETTER_NAME_TRAITS(ClassName,NumMandatoryParams, ...) \ + JSONCONS_CTOR_GETTER_NAME_TRAITS_BASE(0, ClassName,NumMandatoryParams,NumMandatoryParams, __VA_ARGS__) \ + namespace jsoncons { template <> struct is_json_type_traits_declared : public std::true_type {}; } \ + /**/ + +#define JSONCONS_TPL_N_CTOR_GETTER_NAME_TRAITS(NumTemplateParams, ClassName,NumMandatoryParams, ...) \ +JSONCONS_CTOR_GETTER_NAME_TRAITS_BASE(NumTemplateParams, ClassName,NumMandatoryParams,NumMandatoryParams, __VA_ARGS__) \ + namespace jsoncons { template struct is_json_type_traits_declared : public std::true_type {}; } \ + /**/ + +#define JSONCONS_ENUM_PAIR(Prefix, P2, P3, Member, Count) JSONCONS_ENUM_PAIR_LAST(Prefix, P2, P3, Member, Count), +#define JSONCONS_ENUM_PAIR_LAST(Prefix, P2, P3, Member, Count) {value_type::Member, json_object_name_members::Member(char_type{})} + +#define JSONCONS_ENUM_TRAITS_BASE(EnumType, ...) \ +namespace jsoncons { \ +namespace reflect { \ + template<> \ + struct json_object_name_members \ + { \ + JSONCONS_VARIADIC_FOR_EACH(JSONCONS_GENERATE_NAME_STR, ,,, __VA_ARGS__)\ + }; \ + template<> \ + struct reflect_type_properties \ + { \ + using value_type = EnumType; \ + static constexpr std::size_t count = JSONCONS_NARGS(__VA_ARGS__); \ + template \ + static const std::pair>* values() \ + { \ + using char_type = CharT; \ + static const std::pair> values[] = { \ + JSONCONS_VARIADIC_FOR_EACH(JSONCONS_ENUM_PAIR, ,,, __VA_ARGS__)\ + };\ + return values; \ + } \ + }; \ + template \ + struct json_conv_traits \ + { \ + static_assert(std::is_enum::value, # EnumType " must be an enum"); \ + using value_type = EnumType; \ + using result_type = conversion_result; \ + using char_type = typename Json::char_type; \ + using string_type = std::basic_string; \ + using string_view_type = basic_string_view; \ + using mapped_type = std::pair; \ + \ + static bool is(const Json& ajson) noexcept \ + { \ + if (!ajson.is_string()) return false; \ + auto first = reflect_type_properties::values(); \ + auto last = first + reflect_type_properties::count; \ + auto rs = ajson.try_as_string_view(); \ + if (!rs) return false; \ + const string_view_type s = *rs; \ + if (s.empty() && std::find_if(first, last, \ + [](const mapped_type& item) -> bool \ + { return item.first == value_type(); }) == last) \ + { \ + return true; \ + } \ + auto it = std::find_if(first, last, \ + [&](const mapped_type& item) -> bool \ + { return item.second == s; }); \ + return it != last; \ + } \ + template \ + static result_type try_as(const allocator_set& /*aset*/, const Json& ajson) \ + { \ + if (!is(ajson)) return result_type(jsoncons::unexpect, conv_errc::conversion_failed, # EnumType); \ + auto rs = ajson.try_as_string_view(); \ + if (!rs) return result_type(jsoncons::unexpect, conv_errc::conversion_failed, # EnumType); \ + const string_view_type s = *rs; \ + auto first = reflect_type_properties::values(); \ + auto last = first + reflect_type_properties::count; \ + if (s.empty() && std::find_if(first, last, \ + [](const mapped_type& item) -> bool \ + { return item.first == value_type(); }) == last) \ + { \ + return value_type(); \ + } \ + auto it = std::find_if(first, last, \ + [&](const mapped_type& item) -> bool \ + { return item.second == s; }); \ + if (it == last) \ + { \ + if (s.empty()) \ + { \ + return result_type(value_type()); \ + } \ + else \ + { \ + return result_type(jsoncons::unexpect, conv_errc::conversion_failed, # EnumType); \ + } \ + } \ + return result_type((*it).first); \ + } \ + template \ + static Json to_json(const allocator_set& aset, value_type class_instance) \ + { \ + static constexpr char_type empty_string[] = {0}; \ + auto first = reflect_type_properties::values(); \ + auto last = first + reflect_type_properties::count; \ + auto it = std::find_if(first, last, \ + [class_instance](const mapped_type& item) -> bool \ + { return item.first == class_instance; }); \ + if (it == last) \ + { \ + if (class_instance == value_type()) \ + { \ + return Json(empty_string); \ + } \ + else \ + { \ + JSONCONS_THROW(conv_error(conv_errc::conversion_failed, # EnumType)); \ + } \ + } \ + return jsoncons::make_obj_using_allocator(aset.get_allocator(), (*it).second, semantic_tag::none); \ + } \ + }; \ + template <> struct encode_traits \ + { \ + using value_type = EnumType; \ + using result_type = conversion_result; \ + template \ + static write_result try_encode(const allocator_set&, const value_type& val, \ + basic_json_visitor& encoder) \ + { \ + using char_type = CharT; \ + using string_view_type = basic_string_view; \ + using mapped_type = std::pair; \ + static const char_type empty_string[] = {0}; \ + std::error_code ec; \ + auto first = reflect_type_properties::values(); \ + auto last = first + reflect_type_properties::count; \ + auto it = std::find_if(first, last, \ + [val](const mapped_type& item) -> bool \ + { return item.first == val; }); \ + if (it == last) \ + { \ + if (val == value_type()) \ + { \ + encoder.string_value(empty_string, semantic_tag::none, ser_context(), ec); \ + if (JSONCONS_UNLIKELY(ec)) {return write_result{unexpect, ec};} \ + return write_result{}; \ + } \ + else \ + { \ + return write_result{unexpect, conv_errc::conversion_failed}; \ + } \ + } \ + encoder.string_value((*it).second, semantic_tag::none, ser_context(), ec); \ + return write_result{}; \ + } \ + }; \ + template <> struct decode_traits \ + { \ + using value_type = EnumType; \ + using result_type = read_result; \ + template \ + static result_type try_decode(const allocator_set&, basic_staj_cursor& cursor) \ + { \ + using char_type = CharT; \ + using string_view_type = basic_string_view; \ + using mapped_type = std::pair; \ + std::error_code ec; \ + auto sv = cursor.current().template get(ec); \ + if (ec) \ + { \ + return result_type(jsoncons::unexpect, conv_errc::conversion_failed, # EnumType, cursor.line(), cursor.column()); \ + } \ + auto first = reflect_type_properties::values(); \ + auto last = first + reflect_type_properties::count; \ + if (sv.empty() && std::find_if(first, last, \ + [](const mapped_type& item) -> bool \ + { return item.first == value_type(); }) == last) \ + { \ + return value_type(); \ + } \ + auto it = std::find_if(first, last, \ + [&](const mapped_type& item) -> bool \ + { return item.second == sv; }); \ + if (it == last) \ + { \ + if (sv.empty()) \ + { \ + return result_type(value_type()); \ + } \ + else \ + { \ + return result_type(jsoncons::unexpect, conv_errc::conversion_failed, # EnumType, cursor.line(), cursor.column()); \ + } \ + } \ + return result_type((*it).first); \ + } \ + }; \ +} \ +} \ + /**/ + +#define JSONCONS_ENUM_TRAITS(EnumType, ...) \ + JSONCONS_ENUM_TRAITS_BASE(EnumType,__VA_ARGS__) \ + namespace jsoncons { template <> struct is_json_type_traits_declared : public std::true_type {}; } \ + /**/ + +#define JSONCONS_NAME_ENUM_PAIR(P1, P2, P3, Seq, Count) JSONCONS_PP_EXPAND(JSONCONS_NAME_ENUM_PAIR_ Seq), +#define JSONCONS_NAME_ENUM_PAIR_LAST(P1, P2, P3, Seq, Count) JSONCONS_PP_EXPAND(JSONCONS_NAME_ENUM_PAIR_ Seq) +#define JSONCONS_NAME_ENUM_PAIR_(Member, Name) {value_type::Member, Name} + +#define JSONCONS_ENUM_NAME_TRAITS(EnumType, ...) \ +namespace jsoncons { \ +namespace reflect { \ + template<> \ + struct reflect_type_properties \ + { \ + using value_type = EnumType; \ + static constexpr std::size_t count = JSONCONS_NARGS(__VA_ARGS__); \ + template \ + static const std::pair>* values() \ + { \ + static const std::pair> values[] = { \ + JSONCONS_VARIADIC_FOR_EACH(JSONCONS_NAME_ENUM_PAIR, ,,, __VA_ARGS__)\ + };\ + return values; \ + } \ + }; \ + template \ + struct json_conv_traits \ + { \ + static_assert(std::is_enum::value, # EnumType " must be an enum"); \ + using value_type = EnumType; \ + using result_type = conversion_result; \ + using char_type = typename Json::char_type; \ + using string_type = std::basic_string; \ + using string_view_type = basic_string_view; \ + using mapped_type = std::pair; \ + \ + static bool is(const Json& ajson) noexcept \ + { \ + auto rs = ajson.try_as_string_view(); \ + if (!rs) {return false;} \ + const string_view_type s = *rs; \ + auto first = reflect_type_properties::values(); \ + auto last = first + reflect_type_properties::count; \ + if (s.empty() && std::find_if(first, last, \ + [](const mapped_type& item) -> bool \ + { return item.first == value_type(); }) == last) \ + { \ + return true; \ + } \ + auto it = std::find_if(first, last, \ + [&](const mapped_type& item) -> bool \ + { return item.second == s; }); \ + return it != last; \ + } \ + template \ + static result_type try_as(const allocator_set& /*aset*/, const Json& ajson) \ + { \ + auto rs = ajson.try_as_string_view(); \ + if (!rs) {return result_type(jsoncons::unexpect, conv_errc::conversion_failed, # EnumType);} \ + const string_view_type s = *rs; \ + auto first = reflect_type_properties::values(); \ + auto last = first + reflect_type_properties::count; \ + if (s.empty() && std::find_if(first, last, \ + [](const mapped_type& item) -> bool \ + { return item.first == value_type(); }) == last) \ + { \ + return result_type(value_type()); \ + } \ + auto it = std::find_if(first, last, \ + [&](const mapped_type& item) -> bool \ + { return item.second == s; }); \ + if (it == last) \ + { \ + if (s.empty()) \ + { \ + return result_type(value_type()); \ + } \ + else \ + { \ + return result_type(jsoncons::unexpect, conv_errc::conversion_failed, # EnumType); \ + } \ + } \ + return (*it).first; \ + } \ + template \ + static Json to_json(const allocator_set& aset, value_type class_instance) \ + { \ + static constexpr char_type empty_string[] = {0}; \ + auto first = reflect_type_properties::values(); \ + auto last = first + reflect_type_properties::count; \ + auto it = std::find_if(first, last, \ + [class_instance](const mapped_type& item) -> bool \ + { return item.first == class_instance; }); \ + if (it == last) \ + { \ + if (class_instance == value_type()) \ + { \ + return Json(empty_string); \ + } \ + else \ + { \ + JSONCONS_THROW(conv_error(conv_errc::conversion_failed, # EnumType)); \ + } \ + } \ + return jsoncons::make_obj_using_allocator(aset.get_allocator(), (*it).second, semantic_tag::none); \ + } \ + }; \ + template <> struct encode_traits \ + { \ + using value_type = EnumType; \ + using result_type = conversion_result; \ + template \ + static write_result try_encode(const allocator_set&, const value_type& val, \ + basic_json_visitor& encoder) \ + { \ + using char_type = CharT; \ + using string_view_type = basic_string_view; \ + using mapped_type = std::pair; \ + static const char_type empty_string[] = {0}; \ + std::error_code ec; \ + auto first = reflect_type_properties::values(); \ + auto last = first + reflect_type_properties::count; \ + auto it = std::find_if(first, last, \ + [val](const mapped_type& item) -> bool \ + { return item.first == val; }); \ + if (it == last) \ + { \ + if (val == value_type()) \ + { \ + encoder.string_value(empty_string, semantic_tag::none, ser_context(), ec); \ + if (JSONCONS_UNLIKELY(ec)) return write_result{unexpect, ec}; \ + return write_result{}; \ + } \ + else \ + { \ + return write_result{unexpect, conv_errc::conversion_failed}; \ + } \ + } \ + encoder.string_value((*it).second, semantic_tag::none, ser_context(), ec); \ + return write_result{}; \ + } \ + }; \ + template <> struct decode_traits \ + { \ + using value_type = EnumType; \ + using result_type = read_result; \ + template \ + static result_type try_decode(const allocator_set&, basic_staj_cursor& cursor) \ + { \ + using char_type = CharT; \ + using string_view_type = basic_string_view; \ + using mapped_type = std::pair; \ + std::error_code ec; \ + auto sv = cursor.current().template get(ec); \ + if (ec) \ + { \ + return result_type(jsoncons::unexpect, conv_errc::conversion_failed, # EnumType, cursor.line(), cursor.column()); \ + } \ + auto first = reflect_type_properties::values(); \ + auto last = first + reflect_type_properties::count; \ + if (sv.empty() && std::find_if(first, last, \ + [](const mapped_type& item) -> bool \ + { return item.first == value_type(); }) == last) \ + { \ + return value_type(); \ + } \ + auto it = std::find_if(first, last, \ + [&](const mapped_type& item) -> bool \ + { return item.second == sv; }); \ + if (it == last) \ + { \ + if (sv.empty()) \ + { \ + return result_type(value_type()); \ + } \ + else \ + { \ + return result_type(jsoncons::unexpect, conv_errc::conversion_failed, # EnumType, cursor.line(), cursor.column()); \ + } \ + } \ + return result_type((*it).first); \ + } \ + }; \ +} \ + template <> struct is_json_type_traits_declared : public std::true_type {}; \ +} \ + /**/ + +#define JSONCONS_N_GETTER_SETTER_AS(Prefix, GetPrefix, SetPrefix, Property, Count) JSONCONS_N_GETTER_SETTER_AS_(Prefix, GetPrefix ## Property, SetPrefix ## Property, Property, Count) +#define JSONCONS_N_GETTER_SETTER_AS_LAST(Prefix, GetPrefix, SetPrefix, Property, Count) JSONCONS_N_GETTER_SETTER_AS_(Prefix, GetPrefix ## Property, SetPrefix ## Property, Property, Count) +#define JSONCONS_N_GETTER_SETTER_AS_(Prefix, Getter, Setter, Property, Count) { \ + auto result = json_traits_helper::template try_get_member::type>(aset, ajson, json_object_name_members::Property(char_type{})); \ + if (result) {class_instance.Setter(std::move(* result));} \ + else if ((num_params-Count) < num_mandatory_params2) {return result_type(jsoncons::unexpect, result.error().code(), # Prefix);} \ + else if (result.error().code() != conv_errc::missing_required_member){return result_type(jsoncons::unexpect, result.error().code(), # Prefix);} \ +} + +#define JSONCONS_ALL_GETTER_SETTER_AS(Prefix, GetPrefix, SetPrefix, Property, Count) JSONCONS_ALL_GETTER_SETTER_AS_(Prefix, GetPrefix ## Property, SetPrefix ## Property, Property, Count) +#define JSONCONS_ALL_GETTER_SETTER_AS_LAST(Prefix, GetPrefix, SetPrefix, Property, Count) JSONCONS_ALL_GETTER_SETTER_AS_(Prefix, GetPrefix ## Property, SetPrefix ## Property, Property, Count) +#define JSONCONS_ALL_GETTER_SETTER_AS_(Prefix, Getter, Setter, Property, Count) { \ + auto result = json_traits_helper::template try_get_member::type>(aset, ajson, json_object_name_members::Property(char_type{})); \ + if (!result) {return result_type(jsoncons::unexpect, result.error().code(), # Prefix "::" # Property);} \ + class_instance.Setter(std::move(* result)); \ +} + +#define JSONCONS_N_GETTER_SETTER_TO_JSON(Prefix, GetPrefix, SetPrefix, Property, Count) JSONCONS_N_GETTER_SETTER_TO_JSON_(Prefix, GetPrefix ## Property, SetPrefix ## Property, Property, Count) +#define JSONCONS_N_GETTER_SETTER_TO_JSON_LAST(Prefix, GetPrefix, SetPrefix, Property, Count) JSONCONS_N_GETTER_SETTER_TO_JSON_(Prefix, GetPrefix ## Property, SetPrefix ## Property, Property, Count) +#define JSONCONS_N_GETTER_SETTER_TO_JSON_(Prefix, Getter, Setter, Property, Count) \ +if ((num_params-Count) < num_mandatory_params2) \ + {ajson.try_emplace(json_object_name_members::Property(char_type{}),class_instance.Getter());} \ +else \ + {json_traits_helper::set_optional_json_member(json_object_name_members::Property(char_type{}),class_instance.Getter(), ajson);} + +#define JSONCONS_ALL_GETTER_SETTER_TO_JSON(Prefix, GetPrefix, SetPrefix, Property, Count) JSONCONS_ALL_GETTER_SETTER_TO_JSON_(Prefix, GetPrefix ## Property, SetPrefix ## Property, Property, Count) +#define JSONCONS_ALL_GETTER_SETTER_TO_JSON_LAST(Prefix, GetPrefix, SetPrefix, Property, Count) JSONCONS_ALL_GETTER_SETTER_TO_JSON_(Prefix, GetPrefix ## Property, SetPrefix ## Property, Property, Count) +#define JSONCONS_ALL_GETTER_SETTER_TO_JSON_(Prefix, Getter, Setter, Property, Count) ajson.try_emplace(json_object_name_members::Property(char_type{}),class_instance.Getter()); + +#define JSONCONS_N_GETTER_SETTER_COUNT(Prefix, GetPrefix, SetPrefix, Property, Count) JSONCONS_N_GETTER_SETTER_COUNT_(Prefix, GetPrefix ## Property, SetPrefix ## Property, Property, Count) +#define JSONCONS_N_GETTER_SETTER_COUNT_LAST(Prefix, GetPrefix, SetPrefix, Property, Count) JSONCONS_N_GETTER_SETTER_COUNT_(Prefix, GetPrefix ## Property, SetPrefix ## Property, Property, Count) +#define JSONCONS_N_GETTER_SETTER_COUNT_(Prefix, Getter, Setter, Property, Count) \ +if ((num_params-Count) < num_mandatory_params2) \ +{ \ + ++member_count; \ +} \ +else \ +{ \ + if (is_optional_value_set(val.Getter())) \ + { \ + ++member_count; \ + } \ +} + +#define JSONCONS_N_GETTER_SETTER_ENCODE(Prefix, GetPrefix, SetPrefix, Property, Count) JSONCONS_N_GETTER_SETTER_ENCODE_(Prefix, GetPrefix ## Property, SetPrefix ## Property, Property, Count) +#define JSONCONS_N_GETTER_SETTER_ENCODE_LAST(Prefix, GetPrefix, SetPrefix, Property, Count) JSONCONS_N_GETTER_SETTER_ENCODE_(Prefix, GetPrefix ## Property, SetPrefix ## Property, Property, Count) +#define JSONCONS_N_GETTER_SETTER_ENCODE_(Prefix, Getter, Setter, Property, Count) \ +if ((num_params-Count) < num_mandatory_params2) \ +{ \ + auto r = try_encode_member(json_object_name_members::Property(char_type{}), val.Getter(), encoder); \ + if (JSONCONS_UNLIKELY(!r)) {return r;} \ +} \ +else \ +{ \ + auto r = try_encode_optional_member(json_object_name_members::Property(char_type{}), val.Getter(), encoder); \ + if (JSONCONS_UNLIKELY(!r)) {return r;} \ +} + +#define JSONCONS_GETTER_SETTER_TRAITS_BASE(ToJson,NumTemplateParams, ClassName,GetPrefix,SetPrefix,NumMandatoryParams1,NumMandatoryParams2, ...) \ +namespace jsoncons { \ +namespace reflect { \ + template \ + struct json_object_name_members \ + { \ + JSONCONS_VARIADIC_FOR_EACH(JSONCONS_GENERATE_NAME_STR, ,,, __VA_ARGS__)\ + }; \ + template \ + struct json_conv_traits \ + { \ + using value_type = ClassName JSONCONS_GENERATE_TPL_ARGS(JSONCONS_GENERATE_TPL_ARG, NumTemplateParams); \ + using result_type = conversion_result; \ + using char_type = typename Json::char_type; \ + using string_view_type = typename Json::string_view_type; \ + constexpr static size_t num_params = JSONCONS_NARGS(__VA_ARGS__); \ + constexpr static size_t num_mandatory_params1 = NumMandatoryParams1; \ + constexpr static size_t num_mandatory_params2 = NumMandatoryParams2; \ + static bool is(const Json& ajson) noexcept \ + { \ + if (!ajson.is_object()) return false; \ + JSONCONS_VARIADIC_FOR_EACH(JSONCONS_N_MEMBER_IS, ,GetPrefix,SetPrefix, __VA_ARGS__)\ + return true; \ + } \ + template \ + static result_type try_as(const allocator_set& aset, const Json& ajson) \ + { \ + if (!ajson.is_object()) return result_type(jsoncons::unexpect, conv_errc::not_map, # ClassName); \ + value_type class_instance = jsoncons::make_obj_using_allocator(aset.get_allocator()); \ + if (num_params == num_mandatory_params2) \ + { \ + JSONCONS_VARIADIC_FOR_EACH(JSONCONS_ALL_GETTER_SETTER_AS,ClassName,GetPrefix,SetPrefix, __VA_ARGS__) \ + } \ + else \ + { \ + JSONCONS_VARIADIC_FOR_EACH(JSONCONS_N_GETTER_SETTER_AS,ClassName,GetPrefix,SetPrefix, __VA_ARGS__) \ + } \ + return result_type(std::move(class_instance)); \ + } \ + template \ + static Json to_json(const allocator_set& aset, const value_type& class_instance) \ + { \ + Json ajson = jsoncons::make_obj_using_allocator(aset.get_allocator(), json_object_arg, semantic_tag::none); \ + JSONCONS_VARIADIC_FOR_EACH(ToJson, ,GetPrefix,SetPrefix, __VA_ARGS__) \ + return ajson; \ + } \ + }; \ + template \ + struct encode_traits \ + { \ + using value_type = ClassName JSONCONS_GENERATE_TPL_ARGS(JSONCONS_GENERATE_TPL_ARG, NumTemplateParams); \ + constexpr static size_t num_params = JSONCONS_NARGS(__VA_ARGS__); \ + constexpr static size_t num_mandatory_params1 = NumMandatoryParams1; \ + constexpr static size_t num_mandatory_params2 = NumMandatoryParams2; \ + template \ + static write_result try_encode(const allocator_set&, const value_type& val, \ + basic_json_visitor& encoder) \ + { \ + using char_type = CharT; \ + (void)num_params; (void)num_mandatory_params1; (void)num_mandatory_params2; \ + std::error_code ec; \ + std::size_t member_count{0}; \ + JSONCONS_VARIADIC_FOR_EACH(JSONCONS_N_GETTER_SETTER_COUNT, ,GetPrefix,SetPrefix, __VA_ARGS__) \ + encoder.begin_object(member_count, semantic_tag::none, ser_context(), ec); \ + if (JSONCONS_UNLIKELY(ec)) {return write_result{unexpect, ec};} \ + JSONCONS_VARIADIC_FOR_EACH(JSONCONS_N_GETTER_SETTER_ENCODE, ,GetPrefix,SetPrefix, __VA_ARGS__) \ + encoder.end_object(ser_context(), ec); \ + if (JSONCONS_UNLIKELY(ec)) {return write_result{unexpect, ec};} \ + return write_result{}; \ + } \ + }; \ +} \ +} \ + /**/ + +#define JSONCONS_N_GETTER_SETTER_TRAITS(ClassName,GetPrefix,SetPrefix,NumMandatoryParams, ...) \ + JSONCONS_GETTER_SETTER_TRAITS_BASE(JSONCONS_N_GETTER_SETTER_TO_JSON,0, ClassName,GetPrefix,SetPrefix,NumMandatoryParams,NumMandatoryParams, __VA_ARGS__) \ + namespace jsoncons { template <> struct is_json_type_traits_declared : public std::true_type {}; } \ + /**/ + +#define JSONCONS_TPL_N_GETTER_SETTER_TRAITS(NumTemplateParams, ClassName,GetPrefix,SetPrefix,NumMandatoryParams, ...) \ + JSONCONS_GETTER_SETTER_TRAITS_BASE(JSONCONS_N_GETTER_SETTER_TO_JSON,NumTemplateParams, ClassName,GetPrefix,SetPrefix,NumMandatoryParams,NumMandatoryParams, __VA_ARGS__) \ + namespace jsoncons { template struct is_json_type_traits_declared : public std::true_type {}; } \ + /**/ + +#define JSONCONS_ALL_GETTER_SETTER_TRAITS(ClassName,GetPrefix,SetPrefix, ...) \ + JSONCONS_GETTER_SETTER_TRAITS_BASE(JSONCONS_ALL_GETTER_SETTER_TO_JSON,0,ClassName,GetPrefix,SetPrefix, JSONCONS_NARGS(__VA_ARGS__), JSONCONS_NARGS(__VA_ARGS__),__VA_ARGS__) \ + namespace jsoncons { template <> struct is_json_type_traits_declared : public std::true_type {}; } \ + /**/ + +#define JSONCONS_TPL_ALL_GETTER_SETTER_TRAITS(NumTemplateParams, ClassName,GetPrefix,SetPrefix, ...) \ + JSONCONS_GETTER_SETTER_TRAITS_BASE(JSONCONS_ALL_GETTER_SETTER_TO_JSON,NumTemplateParams,ClassName,GetPrefix,SetPrefix, JSONCONS_NARGS(__VA_ARGS__), JSONCONS_NARGS(__VA_ARGS__),__VA_ARGS__) \ + namespace jsoncons { template struct is_json_type_traits_declared : public std::true_type {}; } \ + /**/ + +#define JSONCONS_GETTER_SETTER_NAME_IS(P1, P2, P3, Seq, Count) JSONCONS_GETTER_SETTER_NAME_IS_LAST(P1, P2, P3, Seq, Count) +#define JSONCONS_GETTER_SETTER_NAME_IS_LAST(P1, P2, P3, Seq, Count) if ((num_params-Count) < num_mandatory_params1 && JSONCONS_PP_EXPAND(JSONCONS_PP_CONCAT(JSONCONS_GETTER_SETTER_NAME_IS_,JSONCONS_NARGS Seq) Seq) +#define JSONCONS_GETTER_SETTER_NAME_IS_3(Getter, Setter, Name) !ajson.contains(Name)) return false; +#define JSONCONS_GETTER_SETTER_NAME_IS_5(Getter, Setter, Name, Mode, Match) JSONCONS_GETTER_SETTER_NAME_IS_7(Getter, Setter, Name, Mode, Match,, ) +#define JSONCONS_GETTER_SETTER_NAME_IS_6(Getter, Setter, Name, Mode, Match, Into) JSONCONS_GETTER_SETTER_NAME_IS_7(Getter, Setter, Name, Mode, Match, Into, ) +#define JSONCONS_GETTER_SETTER_NAME_IS_7(Getter, Setter, Name, Mode, Match, Into, From) !ajson.contains(Name)) return false; \ + JSONCONS_TRY{if (!Match(ajson.at(Name).template as())->Getter()))>::type>())) return false;} \ + JSONCONS_CATCH(...) {return false;} + +#define JSONCONS_N_GETTER_SETTER_NAME_AS(P1, P2, P3, Seq, Count) JSONCONS_N_GETTER_SETTER_NAME_AS_LAST(P1, P2, P3, Seq, Count) +#define JSONCONS_N_GETTER_SETTER_NAME_AS_LAST(P1, P2, P3, Seq, Count) index = num_params-Count; JSONCONS_PP_EXPAND(JSONCONS_PP_CONCAT(JSONCONS_N_GETTER_SETTER_NAME_AS_,JSONCONS_NARGS Seq) Seq) +#define JSONCONS_N_GETTER_SETTER_NAME_AS_3(Getter, Setter, Name) JSONCONS_N_GETTER_SETTER_NAME_AS_7(Getter, Setter, Name,JSONCONS_RDWR,always_true(),,) +#define JSONCONS_N_GETTER_SETTER_NAME_AS_4(Getter, Setter, Name, Mode) Mode(JSONCONS_N_GETTER_SETTER_NAME_AS_7(Getter, Setter, Name, Mode, always_true(),,)) +#define JSONCONS_N_GETTER_SETTER_NAME_AS_5(Getter, Setter, Name, Mode, Match) JSONCONS_N_GETTER_SETTER_NAME_AS_7(Getter, Setter, Name, Mode, Match, , ) +#define JSONCONS_N_GETTER_SETTER_NAME_AS_6(Getter, Setter, Name, Mode, Match, Into) JSONCONS_N_GETTER_SETTER_NAME_AS_7(Getter, Setter, Name, Mode, Match, Into, ) +#define JSONCONS_N_GETTER_SETTER_NAME_AS_7(Getter, Setter, Name, Mode, Match, Into, From) { \ + auto result = json_traits_helper::template try_get_member::type>(aset, ajson, Name); \ + if (result && !Match(From(* result))) {return result_type(jsoncons::unexpect, conv_errc::conversion_failed, class_name);} \ + Mode(JSONCONS_N_GETTER_SETTER_NAME_AS_8(Getter, Setter, Name, Mode, Match, Into, From)) \ +} +#define JSONCONS_N_GETTER_SETTER_NAME_AS_8(Getter, Setter, Name, Mode, Match, Into, From) \ + if (result) { \ + class_instance.Setter(From(std::move(* result))); \ + } \ + else if (index < num_mandatory_params2) {return result_type(jsoncons::unexpect, result.error().code(), class_name);} \ + else if (result.error().code() != conv_errc::missing_required_member){return result_type(jsoncons::unexpect, result.error().code(), class_name);} + +#define JSONCONS_N_GETTER_SETTER_NAME_TO_JSON(P1, P2, P3, Seq, Count) JSONCONS_N_GETTER_SETTER_NAME_TO_JSON_LAST(P1, P2, P3, Seq, Count) +#define JSONCONS_N_GETTER_SETTER_NAME_TO_JSON_LAST(P1, P2, P3, Seq, Count) JSONCONS_PP_EXPAND(JSONCONS_PP_CONCAT(JSONCONS_N_GETTER_SETTER_NAME_TO_JSON_,JSONCONS_NARGS Seq) Seq) +#define JSONCONS_N_GETTER_SETTER_NAME_TO_JSON_3(Getter, Setter, Name) \ + ajson.try_emplace(Name,class_instance.Getter()); +#define JSONCONS_N_GETTER_SETTER_NAME_TO_JSON_5(Getter, Setter, Name, Mode, Match) JSONCONS_N_GETTER_SETTER_NAME_TO_JSON_7(Getter, Setter, Name, Mode, Match, , ) +#define JSONCONS_N_GETTER_SETTER_NAME_TO_JSON_6(Getter, Setter, Name, Mode, Match, Into) JSONCONS_N_GETTER_SETTER_NAME_TO_JSON_7(Getter, Setter, Name, Mode, Match, Into, ) +#define JSONCONS_N_GETTER_SETTER_NAME_TO_JSON_7(Getter, Setter, Name, Mode, Match, Into, From) \ + ajson.try_emplace(Name, Into(class_instance.Getter()) ); + +#define JSONCONS_ALL_GETTER_SETTER_NAME_AS(P1, P2, P3, Seq, Count) JSONCONS_ALL_GETTER_SETTER_NAME_AS_LAST(P1, P2, P3, Seq, Count) +#define JSONCONS_ALL_GETTER_SETTER_NAME_AS_LAST(P1, P2, P3, Seq, Count) JSONCONS_PP_EXPAND(JSONCONS_PP_CONCAT(JSONCONS_ALL_GETTER_SETTER_NAME_AS_,JSONCONS_NARGS Seq) Seq) +#define JSONCONS_ALL_GETTER_SETTER_NAME_AS_3(Getter, Setter, Name) JSONCONS_ALL_GETTER_SETTER_NAME_AS_7(Getter, Setter, Name,JSONCONS_RDWR, always_true(),,) +#define JSONCONS_ALL_GETTER_SETTER_NAME_AS_4(Getter, Setter, Name, Mode) Mode(JSONCONS_ALL_GETTER_SETTER_NAME_AS_7(Getter, Setter, Name,Mode, always_true(),,)) +#define JSONCONS_ALL_GETTER_SETTER_NAME_AS_5(Getter, Setter, Name, Mode, Match) JSONCONS_ALL_GETTER_SETTER_NAME_AS_7(Getter, Setter, Name, Mode, Match,,) +#define JSONCONS_ALL_GETTER_SETTER_NAME_AS_6(Getter, Setter, Name, Mode, Match, Into) JSONCONS_ALL_GETTER_SETTER_NAME_AS_7(Getter, Setter, Name, Mode, Match, Into,) +#define JSONCONS_ALL_GETTER_SETTER_NAME_AS_7(Getter, Setter, Name, Mode, Match, Into, From) { \ + auto result = json_traits_helper::template try_get_member::type>(aset, ajson, Name); \ + if (result && !Match(From(* result))) {return result_type(jsoncons::unexpect, conv_errc::conversion_failed, class_name);} \ + Mode(JSONCONS_ALL_GETTER_SETTER_NAME_AS_8(Getter, Setter, Name, Mode, Match, Into, From)) \ +} +#define JSONCONS_ALL_GETTER_SETTER_NAME_AS_8(Getter, Setter, Name, Mode, Match, Into, From) \ + if (result) { \ + class_instance.Setter(From(std::move(* result))); \ + } \ + else {return result_type(jsoncons::unexpect, result.error().code(), class_name);} + +#define JSONCONS_ALL_GETTER_SETTER_NAME_TO_JSON(P1, P2, P3, Seq, Count) JSONCONS_ALL_GETTER_SETTER_NAME_TO_JSON_LAST(P1, P2, P3, Seq, Count) +#define JSONCONS_ALL_GETTER_SETTER_NAME_TO_JSON_LAST(P1, P2, P3, Seq, Count) if ((num_params-Count) < num_mandatory_params2) JSONCONS_PP_EXPAND(JSONCONS_PP_CONCAT(JSONCONS_ALL_GETTER_SETTER_NAME_TO_JSON_,JSONCONS_NARGS Seq) Seq) +#define JSONCONS_ALL_GETTER_SETTER_NAME_TO_JSON_3(Getter, Setter, Name) \ + ajson.try_emplace(Name,class_instance.Getter()); \ +else \ + {json_traits_helper::set_optional_json_member(string_view_type(Name),class_instance.Getter(), ajson);} +#define JSONCONS_ALL_GETTER_SETTER_NAME_TO_JSON_5(Getter, Setter, Name, Mode, Match) JSONCONS_ALL_GETTER_SETTER_NAME_TO_JSON_7(Getter, Setter, Name, Mode, Match, , ) +#define JSONCONS_ALL_GETTER_SETTER_NAME_TO_JSON_6(Getter, Setter, Name, Mode, Match, Into) JSONCONS_ALL_GETTER_SETTER_NAME_TO_JSON_7(Getter, Setter, Name, Mode, Match, Into, ) +#define JSONCONS_ALL_GETTER_SETTER_NAME_TO_JSON_7(Getter, Setter, Name, Mode, Match, Into, From) \ + ajson.try_emplace(Name, Into(class_instance.Getter())); \ +else \ + {json_traits_helper::set_optional_json_member(string_view_type(Name), Into(class_instance.Getter()), ajson);} + +#define JSONCONS_N_GETTER_SETTER_NAME_COUNT(P1, P2, P3, Seq, Count) JSONCONS_N_GETTER_SETTER_NAME_COUNT_LAST(P1, P2, P3, Seq, Count) +#define JSONCONS_N_GETTER_SETTER_NAME_COUNT_LAST(P1, P2, P3, Seq, Count) if ((num_params-Count) < num_mandatory_params2) JSONCONS_PP_EXPAND(JSONCONS_PP_CONCAT(JSONCONS_N_GETTER_SETTER_NAME_COUNT_,JSONCONS_NARGS Seq) Seq) +#define JSONCONS_N_GETTER_SETTER_NAME_COUNT_3(Getter, Setter, Name) \ +{ \ + ++member_count; \ +} \ +else \ +{ \ + if (is_optional_value_set(val.Getter())) \ + { \ + ++member_count; \ + } \ +} +#define JSONCONS_N_GETTER_SETTER_NAME_COUNT_5(Getter, Setter, Name, Mode, Match) JSONCONS_N_GETTER_SETTER_NAME_COUNT_7(Getter, Setter, Name, Mode, Match, , ) +#define JSONCONS_N_GETTER_SETTER_NAME_COUNT_6(Getter, Setter, Name, Mode, Match, Into) JSONCONS_N_GETTER_SETTER_NAME_COUNT_7(Getter, Setter, Name, Mode, Match, Into, ) +#define JSONCONS_N_GETTER_SETTER_NAME_COUNT_7(Getter, Setter, Name, Mode, Match, Into, From) \ +{ \ + ++member_count; \ +} \ +else \ +{ \ + if (is_optional_value_set(val.Getter())) \ + { \ + ++member_count; \ + } \ +} + +#define JSONCONS_N_GETTER_SETTER_NAME_ENCODE(P1, P2, P3, Seq, Count) JSONCONS_N_GETTER_SETTER_NAME_ENCODE_LAST(P1, P2, P3, Seq, Count) +#define JSONCONS_N_GETTER_SETTER_NAME_ENCODE_LAST(P1, P2, P3, Seq, Count) if ((num_params-Count) < num_mandatory_params2) JSONCONS_PP_EXPAND(JSONCONS_PP_CONCAT(JSONCONS_N_GETTER_SETTER_NAME_ENCODE_,JSONCONS_NARGS Seq) Seq) +#define JSONCONS_N_GETTER_SETTER_NAME_ENCODE_3(Getter, Setter, Name) \ +{ \ + auto r = try_encode_member(string_view_type(Name), val.Getter(), encoder); \ + if (JSONCONS_UNLIKELY(!r)) {return r;} \ +} \ +else \ +{ \ + auto r = try_encode_optional_member(string_view_type(Name), val.Getter(), encoder); \ + if (JSONCONS_UNLIKELY(!r)) {return r;} \ +} + +#define JSONCONS_N_GETTER_SETTER_NAME_ENCODE_5(Getter, Setter, Name, Mode, Match) JSONCONS_N_GETTER_SETTER_NAME_ENCODE_7(Getter, Setter, Name, Mode, Match, , ) +#define JSONCONS_N_GETTER_SETTER_NAME_ENCODE_6(Getter, Setter, Name, Mode, Match, Into) JSONCONS_N_GETTER_SETTER_NAME_ENCODE_7(Getter, Setter, Name, Mode, Match, Into, ) +#define JSONCONS_N_GETTER_SETTER_NAME_ENCODE_7(Getter, Setter, Name, Mode, Match, Into, From) \ +{ \ + auto r = try_encode_member(string_view_type(Name), Into(val.Getter()), encoder); \ + if (JSONCONS_UNLIKELY(!r)) {return r;} \ +} \ +else \ +{ \ + auto r = try_encode_optional_member(string_view_type(Name), Into(val.Getter()), encoder); \ + if (JSONCONS_UNLIKELY(!r)) {return r;} \ +} + +#define JSONCONS_GETTER_SETTER_NAME_TRAITS_BASE(ToJson, NumTemplateParams, ClassName,NumMandatoryParams1,NumMandatoryParams2, ...) \ +namespace jsoncons { \ +namespace reflect { \ + template \ + struct json_conv_traits \ + { \ + using value_type = ClassName JSONCONS_GENERATE_TPL_ARGS(JSONCONS_GENERATE_TPL_ARG, NumTemplateParams); \ + using result_type = conversion_result; \ + using char_type = typename Json::char_type; \ + using string_view_type = typename Json::string_view_type; \ + constexpr static size_t num_params = JSONCONS_NARGS(__VA_ARGS__); \ + constexpr static size_t num_mandatory_params1 = NumMandatoryParams1; \ + constexpr static size_t num_mandatory_params2 = NumMandatoryParams2; \ + static bool is(const Json& ajson) noexcept \ + { \ + if (!ajson.is_object()) return false; \ + JSONCONS_VARIADIC_FOR_EACH(JSONCONS_GETTER_SETTER_NAME_IS,,,, __VA_ARGS__)\ + return true; \ + } \ + template \ + static result_type try_as(const allocator_set& aset, const Json& ajson) \ + { \ + const char* class_name = # ClassName; \ + std::error_code ec; \ + if (!ajson.is_object()) return result_type(jsoncons::unexpect, conv_errc::not_map, class_name); \ + value_type class_instance = jsoncons::make_obj_using_allocator(aset.get_allocator()); \ + if (num_params == num_mandatory_params2) \ + { \ + JSONCONS_VARIADIC_FOR_EACH(JSONCONS_ALL_GETTER_SETTER_NAME_AS,,,, __VA_ARGS__) \ + } \ + else \ + { \ + std::size_t index = 0; \ + JSONCONS_VARIADIC_FOR_EACH(JSONCONS_N_GETTER_SETTER_NAME_AS,,,, __VA_ARGS__) \ + } \ + return result_type(std::move(class_instance)); \ + } \ + template \ + static Json to_json(const allocator_set& aset, const value_type& class_instance) \ + { \ + Json ajson = jsoncons::make_obj_using_allocator(aset.get_allocator(), json_object_arg, semantic_tag::none); \ + JSONCONS_VARIADIC_FOR_EACH(ToJson,,,, __VA_ARGS__) \ + return ajson; \ + } \ + }; \ + template \ + struct encode_traits \ + { \ + using value_type = ClassName JSONCONS_GENERATE_TPL_ARGS(JSONCONS_GENERATE_TPL_ARG, NumTemplateParams); \ + constexpr static size_t num_params = JSONCONS_NARGS(__VA_ARGS__); \ + constexpr static size_t num_mandatory_params1 = NumMandatoryParams1; \ + constexpr static size_t num_mandatory_params2 = NumMandatoryParams2; \ + template \ + static write_result try_encode(const allocator_set&, const value_type& val, \ + basic_json_visitor& encoder) \ + { \ + using char_type = CharT; \ + using string_view_type = basic_string_view; \ + (void)num_params; (void)num_mandatory_params1; (void)num_mandatory_params2; \ + std::error_code ec; \ + std::size_t member_count{0}; \ + JSONCONS_VARIADIC_FOR_EACH(JSONCONS_N_GETTER_SETTER_NAME_COUNT,,,, __VA_ARGS__) \ + encoder.begin_object(member_count, semantic_tag::none, ser_context(), ec); \ + if (JSONCONS_UNLIKELY(ec)) {return write_result{unexpect, ec};} \ + JSONCONS_VARIADIC_FOR_EACH(JSONCONS_N_GETTER_SETTER_NAME_ENCODE,,,, __VA_ARGS__) \ + encoder.end_object(ser_context(), ec); \ + if (JSONCONS_UNLIKELY(ec)) {return write_result{unexpect, ec};} \ + return write_result{}; \ + } \ + }; \ +} \ +} \ + /**/ + +#define JSONCONS_N_GETTER_SETTER_NAME_TRAITS(ClassName,NumMandatoryParams, ...) \ + JSONCONS_GETTER_SETTER_NAME_TRAITS_BASE(JSONCONS_N_GETTER_SETTER_NAME_TO_JSON, 0, ClassName,NumMandatoryParams,NumMandatoryParams, __VA_ARGS__) \ + namespace jsoncons { template <> struct is_json_type_traits_declared : public std::true_type {}; } \ + /**/ + +#define JSONCONS_TPL_N_GETTER_SETTER_NAME_TRAITS(NumTemplateParams, ClassName,NumMandatoryParams, ...) \ + JSONCONS_GETTER_SETTER_NAME_TRAITS_BASE(JSONCONS_N_GETTER_SETTER_NAME_TO_JSON, NumTemplateParams, ClassName,NumMandatoryParams,NumMandatoryParams, __VA_ARGS__) \ + namespace jsoncons { template struct is_json_type_traits_declared : public std::true_type {}; } \ + /**/ + +#define JSONCONS_ALL_GETTER_SETTER_NAME_TRAITS(ClassName, ...) \ + JSONCONS_GETTER_SETTER_NAME_TRAITS_BASE(JSONCONS_ALL_GETTER_SETTER_NAME_TO_JSON, 0, ClassName, JSONCONS_NARGS(__VA_ARGS__), JSONCONS_NARGS(__VA_ARGS__), __VA_ARGS__) \ + namespace jsoncons { template <> struct is_json_type_traits_declared : public std::true_type {}; } \ + /**/ + +#define JSONCONS_TPL_ALL_GETTER_SETTER_NAME_TRAITS(NumTemplateParams, ClassName, ...) \ + JSONCONS_GETTER_SETTER_NAME_TRAITS_BASE(JSONCONS_ALL_GETTER_SETTER_NAME_TO_JSON, NumTemplateParams, ClassName, JSONCONS_NARGS(__VA_ARGS__), JSONCONS_NARGS(__VA_ARGS__), __VA_ARGS__) \ + namespace jsoncons { template struct is_json_type_traits_declared : public std::true_type {}; } \ + /**/ + +#define JSONCONS_POLYMORPHIC_IS(BaseClass, P2, P3, DerivedClass, Count) if (ajson.template is()) return true; +#define JSONCONS_POLYMORPHIC_IS_LAST(BaseClass, P2, P3, DerivedClass, Count) if (ajson.template is()) return true; + +#define JSONCONS_POLYMORPHIC_AS_UNIQUE_PTR(BaseClass, P2, P3, DerivedClass, Count) { \ + auto result = ajson.template try_as(aset); \ + if (result) { \ + using rebind = typename std::allocator_traits::template rebind_alloc; \ + auto alloc = rebind(aset.get_allocator()); \ + auto* ptr = alloc.allocate(1); \ + JSONCONS_TRY {ptr = new(ptr) DerivedClass(*result);} JSONCONS_CATCH(...) {alloc.deallocate(ptr,1); throw;} \ + return result_type{jsoncons::in_place, ptr, jsoncons::make_obj_using_allocator(alloc)};} \ +} /**/ + +#define JSONCONS_POLYMORPHIC_AS_UNIQUE_PTR_LAST(BaseClass, P2, P3, DerivedClass, Count) { \ + auto result = ajson.template try_as(aset); \ + if (result) { \ + using rebind = typename std::allocator_traits::template rebind_alloc; \ + auto alloc = rebind(aset.get_allocator()); \ + auto* ptr = alloc.allocate(1); \ + JSONCONS_TRY {ptr = new(ptr) DerivedClass(*result);} JSONCONS_CATCH(...) {alloc.deallocate(ptr,1); throw;} \ + return result_type{jsoncons::in_place, ptr, jsoncons::make_obj_using_allocator(alloc)};} \ +} /**/ + +#define JSONCONS_POLYMORPHIC_AS_SHARED_PTR(BaseClass, P2, P3, DerivedClass, Count) { \ + auto result = ajson.template try_as(aset); \ + if (result) {return result_type(std::allocate_shared(aset.get_allocator(), std::move(*result)));} \ +} /**/ + +#define JSONCONS_POLYMORPHIC_AS_SHARED_PTR_LAST(BaseClass, P2, P3, DerivedClass, Count) { \ + auto result = ajson.template try_as(aset); \ + if (result) {return result_type(std::allocate_shared(aset.get_allocator(), std::move(*result)));} \ +} /**/ + +#define JSONCONS_POLYMORPHIC_TO_JSON(BaseClass, P2, P3, DerivedClass, Count) if (DerivedClass* p = dynamic_cast(ptr.get())) {return jsoncons::make_obj_using_allocator(aset.get_allocator(), *p);} +#define JSONCONS_POLYMORPHIC_TO_JSON_LAST(BaseClass, P2, P3, DerivedClass, Count) if (DerivedClass* p = dynamic_cast(ptr.get())) {return jsoncons::make_obj_using_allocator(aset.get_allocator(), *p);} + +#define JSONCONS_POLYMORPHIC_TRAITS(BaseClass, ...) \ +namespace jsoncons { \ +namespace reflect { \ + template \ + struct json_conv_traits> { \ + using value_type = std::shared_ptr; \ + using result_type = conversion_result; \ + static bool is(const Json& ajson) noexcept { \ + if (!ajson.is_object()) return false; \ + JSONCONS_VARIADIC_FOR_EACH(JSONCONS_POLYMORPHIC_IS, BaseClass,,, __VA_ARGS__)\ + return false; \ + } \ +\ + template \ + static result_type try_as(const allocator_set& aset, const Json& ajson) { \ + if (!ajson.is_object()) return result_type(jsoncons::unexpect, conv_errc::not_map); \ + JSONCONS_VARIADIC_FOR_EACH(JSONCONS_POLYMORPHIC_AS_SHARED_PTR, BaseClass,,, __VA_ARGS__)\ + return result_type(jsoncons::unexpect, conv_errc::conversion_failed); \ + } \ +\ + template \ + static Json to_json(const allocator_set& aset, const value_type& ptr) { \ + if (ptr.get() == nullptr) {return Json::null();} \ + JSONCONS_VARIADIC_FOR_EACH(JSONCONS_POLYMORPHIC_TO_JSON, BaseClass,,, __VA_ARGS__)\ + return Json::null(); \ + } \ + }; \ + template \ + struct json_conv_traits> { \ + using value_type = std::unique_ptr; \ + using result_type = conversion_result; \ + static bool is(const Json& ajson) noexcept { \ + if (!ajson.is_object()) return false; \ + JSONCONS_VARIADIC_FOR_EACH(JSONCONS_POLYMORPHIC_IS, BaseClass,,, __VA_ARGS__)\ + return false; \ + } \ + template \ + static result_type try_as(const allocator_set& aset, const Json& ajson) { \ + if (!ajson.is_object()) return result_type(jsoncons::unexpect, conv_errc::not_map); \ + JSONCONS_VARIADIC_FOR_EACH(JSONCONS_POLYMORPHIC_AS_UNIQUE_PTR, BaseClass,,, __VA_ARGS__)\ + return result_type(jsoncons::unexpect, conv_errc::conversion_failed); \ + } \ + template \ + static Json to_json(const allocator_set& aset, const value_type& ptr) { \ + if (ptr.get() == nullptr) {return Json::null();} \ + JSONCONS_VARIADIC_FOR_EACH(JSONCONS_POLYMORPHIC_TO_JSON, BaseClass,,, __VA_ARGS__)\ + return Json::null(); \ + } \ + }; \ +} \ +} \ + /**/ + +#endif // JSONCONS_REFLECT_REFLECT_TRAITS_GEN_HPP diff --git a/include/jsoncons/semantic_tag.hpp b/include/jsoncons/semantic_tag.hpp index 0928ab0..3036854 100644 --- a/include/jsoncons/semantic_tag.hpp +++ b/include/jsoncons/semantic_tag.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/include/jsoncons/ser_util.hpp b/include/jsoncons/ser_util.hpp new file mode 100644 index 0000000..14f1792 --- /dev/null +++ b/include/jsoncons/ser_util.hpp @@ -0,0 +1,123 @@ +/// Copyright 2013-2026 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_SER_UTIL_HPP +#define JSONCONS_SER_UTIL_HPP + +#include +#include +#include + +#include + +namespace jsoncons { + +class ser_context +{ +public: + virtual ~ser_context() = default; + + virtual size_t line() const + { + return 0; + } + + virtual size_t column() const + { + return 0; + } + + virtual size_t position() const + { + return 0; + } + + virtual size_t begin_position() const + { + return 0; + } + + virtual size_t end_position() const + { + return 0; + } +}; + +class read_error +{ + std::error_code ec_{}; + std::string message_arg_; + std::size_t line_{}; + std::size_t column_{}; + +public: + read_error(std::error_code ec, std::size_t line, std::size_t column) + : ec_{ec}, line_{line}, column_{column} + { + } + + read_error(std::error_code ec, const std::string& message_arg, std::size_t line, std::size_t column) + : ec_{ec}, message_arg_(message_arg), line_{line}, column_{column} + { + } + + read_error(const read_error& other) = default; + + read_error(read_error&& other) = default; + + read_error& operator=(const read_error& other) = default; + + read_error& operator=(read_error&& other) = default; + + const std::error_code& code() const noexcept + { + return ec_; + } + const std::string& message_arg() const noexcept + { + return message_arg_; + } + std::size_t line() const noexcept + { + return line_; + } + std::size_t column() const noexcept + { + return column_; + } + + std::string message() const + { + std::string str(message_arg_); + if (!str.empty()) + { + str.append(": "); + } + str.append(ec_.message()); + if (line_ != 0 && column_ != 0) + { + str.append(" at line "); + str.append(std::to_string(line_)); + str.append(" and column "); + str.append(std::to_string(column_)); + } + else if (column_ != 0) + { + str.append(" at position "); + str.append(std::to_string(column_)); + } + return str; + } +}; + +template +using read_result = expected; + +using write_result = jsoncons::expected; + +} // namespace jsoncons + +#endif // JSONCONS_SER_UTIL_HPP diff --git a/include/jsoncons/sink.hpp b/include/jsoncons/sink.hpp index dfe8ea4..fff11c6 100644 --- a/include/jsoncons/sink.hpp +++ b/include/jsoncons/sink.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -84,6 +84,24 @@ namespace jsoncons { } } + void append(std::size_t count, const CharT ch) + { + if (count <= std::size_t(end_buffer_ - p_)) + { + for (std::size_t i = 0; i < count; ++i) + { + *p_++ = ch; + } + } + else + { + for (std::size_t i = 0; i < count; ++i) + { + push_back(ch); + } + } + } + void push_back(CharT ch) { if (p_ < end_buffer_) @@ -94,7 +112,7 @@ namespace jsoncons { { stream_ptr_->write(begin_buffer_, buffer_length()); p_ = begin_buffer_; - push_back(ch); + *p_++ = ch; } } private: @@ -175,6 +193,24 @@ namespace jsoncons { } } + void append(std::size_t count, uint8_t ch) + { + if (count <= std::size_t(end_buffer_ - p_)) + { + for (std::size_t i = 0; i < count; ++i) + { + *p_++ = ch; + } + } + else + { + for (std::size_t i = 0; i < count; ++i) + { + push_back(ch); + } + } + } + void push_back(uint8_t ch) { if (p_ < end_buffer_) @@ -185,7 +221,7 @@ namespace jsoncons { { stream_ptr_->write((char*)begin_buffer_, buffer_length()); p_ = begin_buffer_; - push_back(ch); + *p_++ = ch; } } private: @@ -239,7 +275,12 @@ namespace jsoncons { void append(const value_type* s, std::size_t length) { - buf_ptr->insert(buf_ptr->end(), s, s+length); + buf_ptr->append(s, length); + } + + void append(std::size_t count, value_type ch) + { + buf_ptr->append(count, ch); } void push_back(value_type ch) diff --git a/include/jsoncons/source.hpp b/include/jsoncons/source.hpp index 914938c..3d9adfc 100644 --- a/include/jsoncons/source.hpp +++ b/include/jsoncons/source.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -25,6 +25,9 @@ namespace jsoncons { + // The source data must be padded by at least `buffer_padding_size` bytes. + JSONCONS_INLINE_CONSTEXPR uint8_t buffer_padding_size = 4; + template class basic_null_istream : public std::basic_istream { @@ -41,7 +44,7 @@ namespace jsoncons { null_buffer& operator=(const null_buffer&) = delete; null_buffer& operator=(null_buffer&&) = default; - int_type overflow( int_type ch = typename std::basic_streambuf::traits_type::eof() ) override + int_type overflow( int_type ch = typename std::basic_streambuf::traits_type::eof()) override { return ch; } @@ -73,26 +76,34 @@ namespace jsoncons { // text sources - template - class stream_source + template > + class stream_source { public: using value_type = CharT; static constexpr std::size_t default_max_buffer_size = 16384; private: using char_type = typename std::conditional::type; + using char_allocator_type = typename std::allocator_traits:: template rebind_alloc; + + Allocator alloc_; basic_null_istream null_is_; std::basic_istream* stream_ptr_; std::basic_streambuf* sbuf_; std::size_t position_{0}; - std::vector buffer_; - const value_type* buffer_data_; - std::size_t buffer_length_{0}; + value_type* buffer_{nullptr}; + std::size_t buffer_size_{0}; + value_type* data_{nullptr}; + std::size_t length_{0}; public: - stream_source() - : stream_ptr_(&null_is_), sbuf_(null_is_.rdbuf()), - buffer_(1), buffer_data_(buffer_.data()) + const Allocator& get_allocator() const + { + return alloc_; + } + + stream_source(const Allocator& alloc = Allocator()) + : alloc_(alloc), stream_ptr_(&null_is_), sbuf_(null_is_.rdbuf()) { } @@ -100,57 +111,165 @@ namespace jsoncons { stream_source(const stream_source&) = delete; stream_source(stream_source&& other) noexcept - : stream_ptr_(&null_is_), sbuf_(null_is_.rdbuf()), - buffer_(), buffer_data_(buffer_.data()) + : alloc_(other.alloc_), stream_ptr_(&null_is_), sbuf_(null_is_.rdbuf()) { + buffer_ = other.buffer_; + data_ = other.data_; + buffer_size_ = other.buffer_size_; + length_ = other.length_; + other.buffer_ = nullptr; + other.data_ = nullptr; + other.buffer_size_ = 0; + other.length_ = 0; + if (other.stream_ptr_ != &other.null_is_) { stream_ptr_ = other.stream_ptr_; sbuf_ = other.sbuf_; position_ = other.position_; - buffer_ = std::move(other.buffer_); - buffer_data_ = buffer_.data() + (other.buffer_data_ - other.buffer_.data()); - buffer_length_ = other.buffer_length_; - other = stream_source(); + other.stream_ptr_ = &other.null_is_; + other.sbuf_ = other.null_is_.rdbuf(); + other.position_ = 0; } } - stream_source(std::basic_istream& is, std::size_t buf_size = default_max_buffer_size) - : stream_ptr_(std::addressof(is)), sbuf_(is.rdbuf()), - buffer_(buf_size), buffer_data_(buffer_.data()) + stream_source(stream_source&& other, const Allocator& alloc) noexcept + : alloc_(alloc), stream_ptr_(&null_is_), sbuf_(null_is_.rdbuf()), + buffer_size_(other.buffer_size_), length_(other.length_) { + if (alloc == other.get_allocator()) + { + buffer_ = other.buffer_; + data_ = other.data_; + length_ = other.length_; + other.buffer_ = nullptr; + other.data_ = nullptr; + other.length_ = 0; + } + else if (other.buffer_ != nullptr) + { + buffer_ = std::allocator_traits::allocate(alloc_, buffer_size_); + data_ = buffer_ + (other.data_ - other.buffer_); + std::memcpy(data_, other.data_, sizeof(value_type)*other.length_); + } + if (other.stream_ptr_ != &other.null_is_) + { + stream_ptr_ = other.stream_ptr_; + sbuf_ = other.sbuf_; + position_ = other.position_; + } + else + { + stream_ptr_ = &null_is_; + sbuf_ = null_is_.rdbuf(); + position_ = 0; + } + } + + stream_source(std::basic_istream& is, + const Allocator& alloc = Allocator()) + : alloc_(alloc), stream_ptr_(std::addressof(is)), sbuf_(is.rdbuf()), + buffer_size_(default_max_buffer_size) + { + buffer_ = std::allocator_traits::allocate(alloc_, buffer_size_); + data_ = buffer_; } - ~stream_source() = default; + stream_source(std::basic_istream& is, std::size_t buf_size, + const Allocator& alloc = Allocator()) + : alloc_(alloc), stream_ptr_(std::addressof(is)), sbuf_(is.rdbuf()), + buffer_size_(buf_size) + { + buffer_ = std::allocator_traits::allocate(alloc_, buffer_size_); + data_ = buffer_; + } + + ~stream_source() noexcept + { + if (buffer_) + { + std::allocator_traits::deallocate(alloc_, buffer_, buffer_size_); + } + } stream_source& operator=(const stream_source&) = delete; + + void move_assignment(std::true_type, // propagate_on_container_move_assignment + stream_source&& other) noexcept + { + auto alloc = other.alloc_; + other.alloc_ = alloc_; + alloc_ = alloc; + std::swap(buffer_, other.buffer_); + std::swap(buffer_size_, other.buffer_size_); + std::swap(data_, other.data_); + std::swap(length_, other.length_); + if (other.stream_ptr_ != &other.null_is_) + { + stream_ptr_ = other.stream_ptr_; + sbuf_ = other.sbuf_; + position_ = other.position_; + } + else + { + stream_ptr_ = &null_is_; + sbuf_ = null_is_.rdbuf(); + position_ = 0; + } + } - stream_source& operator=(stream_source&& other) noexcept + void move_assignment(std::false_type, // not propagate_on_container_move_assignment + stream_source&& other) noexcept { + buffer_size_ = other.buffer_size_; + buffer_ = std::allocator_traits::allocate(alloc_, buffer_size_); + data_ = buffer_ + (other.data_ - other.buffer_); + length_ = other.length_; + std::memcpy(buffer_, other.buffer_, sizeof(value_type)*other.length_); if (other.stream_ptr_ != &other.null_is_) { stream_ptr_ = other.stream_ptr_; sbuf_ = other.sbuf_; position_ = other.position_; - buffer_ = std::move(other.buffer_); - buffer_data_ = buffer_.data() + (other.buffer_data_ - other.buffer_.data()); - buffer_length_ = other.buffer_length_; - other = stream_source(); } else { stream_ptr_ = &null_is_; sbuf_ = null_is_.rdbuf(); position_ = 0; - buffer_data_ = buffer_.data(); - buffer_length_ = 0; } + } + + stream_source& operator=(stream_source&& other) noexcept + { + move_assignment(typename std::allocator_traits::propagate_on_container_move_assignment(), + std::move(other)); return *this; } + const value_type* buffer() const + { + return buffer_; + } + + std::size_t buffer_size() const + { + return buffer_size_; + } + + const value_type* data() const + { + return data_; + } + + std::size_t length() const + { + return length_; + } + bool eof() const { - return buffer_length_ == 0 && stream_ptr_->eof(); + return length_ == 0 && stream_ptr_->eof(); } bool is_error() const @@ -166,37 +285,37 @@ namespace jsoncons { void ignore(std::size_t length) { std::size_t len = 0; - if (buffer_length_ > 0) + if (length_ > 0) { - len = (std::min)(buffer_length_, length); + len = (std::min)(length_, length); position_ += len; - buffer_data_ += len; - buffer_length_ -= len; + data_ += len; + length_ -= len; } while (len < length) { fill_buffer(); - if (buffer_length_ == 0) + if (length_ == 0) { break; } - std::size_t len2 = (std::min)(buffer_length_, length-len); + std::size_t len2 = (std::min)(length_, length-len); position_ += len2; - buffer_data_ += len2; - buffer_length_ -= len2; + data_ += len2; + length_ -= len2; len += len2; } } char_result peek() { - if (buffer_length_ == 0) + if (length_ == 0) { fill_buffer(); } - if (buffer_length_ > 0) + if (length_ > 0) { - value_type c = *buffer_data_; + value_type c = *data_; return char_result{c, false}; } else @@ -207,15 +326,15 @@ namespace jsoncons { span read_buffer() { - if (buffer_length_ == 0) + if (length_ == 0) { fill_buffer(); } - const value_type* data = buffer_data_; - std::size_t length = buffer_length_; - buffer_data_ += buffer_length_; - position_ += buffer_length_; - buffer_length_ = 0; + const value_type* data = data_; + std::size_t length = length_; + data_ += length_; + position_ += length_; + length_ = 0; return span(data, length); } @@ -223,27 +342,27 @@ namespace jsoncons { std::size_t read(value_type* p, std::size_t length) { std::size_t len = 0; - if (buffer_length_ > 0) + if (length_ > 0) { - len = (std::min)(buffer_length_, length); - std::memcpy(p, buffer_data_, len*sizeof(value_type)); - buffer_data_ += len; - buffer_length_ -= len; + len = (std::min)(length_, length); + std::memcpy(p, data_, len*sizeof(value_type)); + data_ += len; + length_ -= len; position_ += len; } if (length - len == 0) { return len; } - else if (length - len < buffer_.size()) + else if (length - len < buffer_size_) { fill_buffer(); - if (buffer_length_ > 0) + if (length_ > 0) { - std::size_t len2 = (std::min)(buffer_length_, length-len); - std::memcpy(p+len, buffer_data_, len2*sizeof(value_type)); - buffer_data_ += len2; - buffer_length_ -= len2; + std::size_t len2 = (std::min)(length_, length-len); + std::memcpy(p+len, data_, len2*sizeof(value_type)); + data_ += len2; + length_ -= len2; position_ += len2; len += len2; } @@ -253,7 +372,7 @@ namespace jsoncons { { if (stream_ptr_->eof()) { - buffer_length_ = 0; + length_ = 0; return 0; } JSONCONS_TRY @@ -280,17 +399,17 @@ namespace jsoncons { { if (stream_ptr_->eof()) { - buffer_length_ = 0; + length_ = 0; return; } - buffer_data_ = buffer_.data(); + data_ = buffer_; JSONCONS_TRY { - std::streamsize count = sbuf_->sgetn(reinterpret_cast(buffer_.data()), buffer_.size()); - buffer_length_ = static_cast(count); + std::streamsize count = sbuf_->sgetn(reinterpret_cast(buffer_), buffer_size_); + length_ = static_cast(count); - if (buffer_length_ < buffer_.size()) + if (length_ < buffer_size_) { stream_ptr_->clear(stream_ptr_->rdstate() | std::ios::eofbit); } @@ -298,11 +417,14 @@ namespace jsoncons { JSONCONS_CATCH(const std::exception&) { stream_ptr_->clear(stream_ptr_->rdstate() | std::ios::badbit | std::ios::eofbit); - buffer_length_ = 0; + length_ = 0; } } }; + template + constexpr std::size_t stream_source::default_max_buffer_size; + // string_source template @@ -412,7 +534,7 @@ namespace jsoncons { IteratorT end_; std::size_t position_{0}; std::vector buffer_; - std::size_t buffer_length_{0}; + std::size_t buffer_len_{0}; using difference_type = typename std::iterator_traits::difference_type; using iterator_category = typename std::iterator_traits::iterator_category; @@ -464,12 +586,12 @@ namespace jsoncons { span read_buffer() { - if (buffer_length_ == 0) + if (buffer_len_ == 0) { - buffer_length_ = read(buffer_.data(), buffer_.size()); + buffer_len_ = read(buffer_.data(), buffer_.size()); } - std::size_t length = buffer_length_; - buffer_length_ = 0; + std::size_t length = buffer_len_; + buffer_len_ = 0; return span(buffer_.data(), length); } @@ -621,7 +743,7 @@ namespace jsoncons { IteratorT end_; std::size_t position_{0}; std::vector buffer_; - std::size_t buffer_length_{0}; + std::size_t buffer_len_{0}; using difference_type = typename std::iterator_traits::difference_type; using iterator_category = typename std::iterator_traits::iterator_category; @@ -671,12 +793,12 @@ namespace jsoncons { span read_buffer() { - if (buffer_length_ == 0) + if (buffer_len_ == 0) { - buffer_length_ = read(buffer_.data(), buffer_.size()); + buffer_len_ = read(buffer_.data(), buffer_.size()); } - std::size_t length = buffer_length_; - buffer_length_ = 0; + std::size_t length = buffer_len_; + buffer_len_ = 0; return span(buffer_.data(), length); } @@ -767,7 +889,7 @@ namespace jsoncons { std::size_t actual = 0; while (actual < n) { - typename Source::value_type c; + typename Source::value_type c{}; if (source.read(&c,1) != 1) { break; diff --git a/include/jsoncons/source_adaptor.hpp b/include/jsoncons/source_adaptor.hpp index 5fc8e62..4dd2fc2 100644 --- a/include/jsoncons/source_adaptor.hpp +++ b/include/jsoncons/source_adaptor.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/include/jsoncons/staj_cursor.hpp b/include/jsoncons/staj_cursor.hpp index 825c5df..6109bda 100644 --- a/include/jsoncons/staj_cursor.hpp +++ b/include/jsoncons/staj_cursor.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,18 +15,19 @@ #include // std::allocator #include -#include +#include +#include #include #include -#include #include #include -#include +#include #include #include #include #include -#include +#include +#include namespace jsoncons { @@ -689,6 +690,11 @@ class basic_staj_cursor virtual void next(std::error_code& ec) = 0; virtual const ser_context& context() const = 0; + + virtual std::size_t line() const = 0; + + virtual std::size_t column() const = 0; + }; template @@ -751,6 +757,16 @@ class basic_staj_filter_view : basic_staj_cursor return cursor_->context(); } + std::size_t line() const override + { + return cursor_->line(); + } + + std::size_t column() const override + { + return cursor_->column(); + } + friend basic_staj_filter_view operator|(basic_staj_filter_view& cursor, std::function&, const ser_context&)> pred) @@ -759,6 +775,315 @@ class basic_staj_filter_view : basic_staj_cursor } }; +template +read_result to_json_single(const allocator_set& aset, + basic_staj_cursor& cursor) +{ + using result_type = read_result; + + std::error_code ec; + switch (cursor.current().event_type()) + { + case staj_event_type::string_value: + { + auto sv = cursor.current().template get>(ec); + if (ec) return result_type(jsoncons::unexpect, ec, cursor.line(), cursor.column()); + return result_type{jsoncons::make_obj_using_allocator(aset.get_allocator(), + sv.data(), sv.length(), cursor.current().tag())}; + } + case staj_event_type::byte_string_value: + { + auto j = jsoncons::make_obj_using_allocator(aset.get_allocator(), + byte_string_arg, cursor.current().template get(ec), cursor.current().tag()); + return !ec ? result_type(std::move(j)) : result_type(jsoncons::unexpect, ec, cursor.line(), cursor.column()); + } + case staj_event_type::null_value: + { + return result_type(Json{null_arg, semantic_tag::none}); + } + case staj_event_type::bool_value: + { + auto j = Json{cursor.current().template get(ec), cursor.current().tag()}; + return !ec ? result_type(std::move(j)) : result_type(jsoncons::unexpect, ec, cursor.line(), cursor.column()); + } + case staj_event_type::int64_value: + { + auto j = Json{cursor.current().template get(ec), cursor.current().tag()}; + return !ec ? result_type(std::move(j)) : result_type(jsoncons::unexpect, ec, cursor.line(), cursor.column()); + } + case staj_event_type::uint64_value: + { + auto j = Json{cursor.current().template get(ec), cursor.current().tag()}; + return !ec ? result_type(std::move(j)) : result_type(jsoncons::unexpect, ec, cursor.line(), cursor.column()); + } + case staj_event_type::half_value: + { + auto j = Json{half_arg, cursor.current().template get(ec), cursor.current().tag()}; + return !ec ? result_type(std::move(j)) : result_type(jsoncons::unexpect, ec, cursor.line(), cursor.column()); + } + case staj_event_type::double_value: + { + auto j = Json{cursor.current().template get(ec), cursor.current().tag()}; + return !ec ? result_type(std::move(j)) : result_type(jsoncons::unexpect, ec, cursor.line(), cursor.column()); + } + default: + return result_type(jsoncons::unexpect, conv_errc::conversion_failed, cursor.line(), cursor.column()); + } +} + +template +read_result to_json_container(const allocator_set& aset, + basic_staj_cursor& cursor) +{ + using result_type = read_result; + using json_ptr_allocator_type = typename std::allocator_traits:: template rebind_alloc; + using char_type = typename Json::char_type; + using char_allocator_type = typename std::allocator_traits:: template rebind_alloc; + using key_type = std::basic_string, char_allocator_type>; + + std::error_code ec; + + auto cont = cursor.current().event_type() == staj_event_type::begin_object ? + jsoncons::make_obj_using_allocator(aset.get_allocator(), json_object_arg, semantic_tag::none) : + jsoncons::make_obj_using_allocator(aset.get_allocator(), json_array_arg, semantic_tag::none); + std::vector stack(aset.get_temp_allocator()); + stack.push_back(std::addressof(cont)); + key_type key(aset.get_temp_allocator()); + if (cursor.current().event_type() == staj_event_type::begin_object) + { + goto begin_object; + } + goto begin_array; + +begin_object: + cursor.next(ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type(jsoncons::unexpect, ec, cursor.line(), cursor.column()); + } + while (!cursor.done() && !stack.empty()) + { + switch (cursor.current().event_type()) + { + case staj_event_type::begin_object: + { + auto result = stack.back()->try_emplace(key, json_object_arg); + stack.push_back(std::addressof(result.first->value())); + goto begin_object; + } + case staj_event_type::begin_array: + { + auto result = stack.back()->try_emplace(key, json_array_arg); + stack.push_back(std::addressof(result.first->value())); + goto begin_array; + } + case staj_event_type::key: + { + auto sv = cursor.current().template get>(ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type(jsoncons::unexpect, ec, cursor.line(), cursor.column()); + } + key = key_type(sv.data(), sv.length(), aset.get_temp_allocator()); + break; + } + case staj_event_type::string_value: + stack.back()->try_emplace(key, cursor.current().template get>(ec), cursor.current().tag()); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type(jsoncons::unexpect, ec, cursor.line(), cursor.column()); + } + break; + case staj_event_type::byte_string_value: + stack.back()->try_emplace(key, byte_string_arg, cursor.current().template get(ec), cursor.current().tag()); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type(jsoncons::unexpect, ec, cursor.line(), cursor.column()); + } + break; + case staj_event_type::null_value: + stack.back()->try_emplace(key, null_arg); + break; + case staj_event_type::bool_value: + stack.back()->try_emplace(key, cursor.current().template get(ec), cursor.current().tag()); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type(jsoncons::unexpect, ec, cursor.line(), cursor.column()); + } + break; + case staj_event_type::int64_value: + stack.back()->try_emplace(key, cursor.current().template get(ec), cursor.current().tag()); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type(jsoncons::unexpect, ec, cursor.line(), cursor.column()); + } + break; + case staj_event_type::uint64_value: + stack.back()->try_emplace(key, cursor.current().template get(ec), cursor.current().tag()); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type(jsoncons::unexpect, ec, cursor.line(), cursor.column()); + } + break; + case staj_event_type::half_value: + stack.back()->try_emplace(key, half_arg, cursor.current().template get(ec), cursor.current().tag()); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type(jsoncons::unexpect, ec, cursor.line(), cursor.column()); + } + break; + case staj_event_type::double_value: + stack.back()->try_emplace(key, cursor.current().template get(ec), cursor.current().tag()); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type(jsoncons::unexpect, ec, cursor.line(), cursor.column()); + } + break; + case staj_event_type::end_object: + stack.pop_back(); + if (stack.empty()) + { + return result_type(std::move(cont)); + } + if (stack.back()->type() == json_type::object) + { + goto begin_object; + } + goto begin_array; + break; + default: + return result_type(jsoncons::unexpect, conv_errc::conversion_failed, cursor.line(), cursor.column()); + } + cursor.next(ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type(jsoncons::unexpect, ec, cursor.line(), cursor.column()); + } + } + return result_type(std::move(cont)); + +begin_array: + cursor.next(ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type(jsoncons::unexpect, ec, cursor.line(), cursor.column()); + } + while (!cursor.done() && !stack.empty()) + { + switch (cursor.current().event_type()) + { + case staj_event_type::begin_object: + { + auto& result = stack.back()->emplace_back(json_object_arg); + stack.push_back(std::addressof(result)); + goto begin_object; + } + case staj_event_type::begin_array: + { + auto& result = stack.back()->emplace_back(json_array_arg); + stack.push_back(std::addressof(result)); + goto begin_array; + } + case staj_event_type::string_value: + stack.back()->emplace_back(cursor.current().template get>(ec), cursor.current().tag()); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type(jsoncons::unexpect, ec, cursor.line(), cursor.column()); + } + break; + case staj_event_type::byte_string_value: + stack.back()->emplace_back(byte_string_arg, cursor.current().template get(ec), cursor.current().tag()); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type(jsoncons::unexpect, ec, cursor.line(), cursor.column()); + } + break; + case staj_event_type::null_value: + stack.back()->emplace_back(null_arg); + break; + case staj_event_type::bool_value: + stack.back()->emplace_back(cursor.current().template get(ec), cursor.current().tag()); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type(jsoncons::unexpect, ec, cursor.line(), cursor.column()); + } + break; + case staj_event_type::int64_value: + stack.back()->emplace_back(cursor.current().template get(ec), cursor.current().tag()); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type(jsoncons::unexpect, ec, cursor.line(), cursor.column()); + } + break; + case staj_event_type::uint64_value: + stack.back()->emplace_back(cursor.current().template get(ec), cursor.current().tag()); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type(jsoncons::unexpect, ec, cursor.line(), cursor.column()); + } + break; + case staj_event_type::half_value: + stack.back()->emplace_back(half_arg, cursor.current().template get(ec), cursor.current().tag()); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type(jsoncons::unexpect, ec, cursor.line(), cursor.column()); + } + break; + case staj_event_type::double_value: + stack.back()->emplace_back(cursor.current().template get(ec), cursor.current().tag()); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type(jsoncons::unexpect, ec, cursor.line(), cursor.column()); + } + break; + case staj_event_type::end_array: + stack.pop_back(); + if (stack.empty()) + { + return cont; + } + if (stack.back()->type() == json_type::object) + { + goto begin_object; + } + goto begin_array; + break; + default: + return result_type(jsoncons::unexpect, conv_errc::conversion_failed, cursor.line(), cursor.column()); + } + cursor.next(ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type(jsoncons::unexpect, ec, cursor.line(), cursor.column()); + } + } + + JSONCONS_UNREACHABLE(); +} + +template +read_result try_to_json(const allocator_set& aset, + basic_staj_cursor& cursor) +{ + using result_type = read_result; + + if (JSONCONS_UNLIKELY(is_end_container(cursor.current().event_type()))) + { + return result_type(jsoncons::unexpect, conv_errc::conversion_failed, cursor.line(), cursor.column()); + } + if (!is_begin_container(cursor.current().event_type())) + { + return to_json_single(aset, cursor); + } + return to_json_container(aset, cursor); +} + +template +read_result try_to_json(basic_staj_cursor& cursor) +{ + return try_to_json(allocator_set>(), cursor); +} + using staj_event = basic_staj_event; using wstaj_event = basic_staj_event; diff --git a/include/jsoncons/staj_event.hpp b/include/jsoncons/staj_event.hpp index b08f5fc..2d99be3 100644 --- a/include/jsoncons/staj_event.hpp +++ b/include/jsoncons/staj_event.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -12,27 +12,27 @@ #include #include // std::function #include +#include #include // std::allocator #include #include // std::enable_if #include #include -#include +#include #include #include #include #include -#include #include #include -#include +#include #include #include #include #include -#include +#include namespace jsoncons { @@ -171,7 +171,8 @@ class basic_staj_event } value_; std::size_t length_{0}; public: - using string_view_type = jsoncons::basic_string_view; + using char_type = CharT; + using string_view_type = jsoncons::basic_string_view; basic_staj_event(staj_event_type event_type, semantic_tag tag = semantic_tag::none) : event_type_(event_type), tag_(tag), value_() @@ -269,50 +270,59 @@ class basic_staj_event template typename std::enable_if::value && std::is_same::value, T>::type - get_(Allocator,std::error_code& ec) const + get_(Allocator alloc,std::error_code& ec) const { + constexpr const char_type* true_constant = JSONCONS_CSTRING_CONSTANT(char_type,"true"); + constexpr const char_type* false_constant = JSONCONS_CSTRING_CONSTANT(char_type,"false"); + constexpr const char_type* null_constant = JSONCONS_CSTRING_CONSTANT(char_type,"null"); + switch (event_type_) { case staj_event_type::key: case staj_event_type::string_value: { - value_converter,T> converter; - return converter.convert(jsoncons::basic_string_view(value_.string_data_, length_), tag(), ec); + return jsoncons::make_obj_using_allocator(alloc, value_.string_data_, length_); } case staj_event_type::byte_string_value: { - value_converter converter; - return converter.convert(byte_string_view(value_.byte_string_data_,length_),tag(),ec); + auto s = jsoncons::make_obj_using_allocator(alloc); + bytes_to_string(value_.byte_string_data_, value_.byte_string_data_+length_, tag(), s); + return s; } case staj_event_type::uint64_value: { - value_converter converter; - return converter.convert(value_.uint64_value_, tag(), ec); + auto s = jsoncons::make_obj_using_allocator(alloc); + jsoncons::from_integer(value_.uint64_value_, s); + return s; } case staj_event_type::int64_value: { - value_converter converter; - return converter.convert(value_.int64_value_, tag(), ec); + auto s = jsoncons::make_obj_using_allocator(alloc); + jsoncons::from_integer(value_.int64_value_, s); + return s; } case staj_event_type::half_value: { - value_converter converter; - return converter.convert(value_.half_value_, tag(), ec); + auto s = jsoncons::make_obj_using_allocator(alloc); + jsoncons::write_double f{float_chars_format::general,0}; + double x = binary::decode_half(value_.half_value_); + f(x, s); + return s; } case staj_event_type::double_value: { - value_converter converter; - return converter.convert(value_.double_value_, tag(), ec); + auto s = jsoncons::make_obj_using_allocator(alloc); + jsoncons::write_double f{float_chars_format::general,0}; + f(value_.double_value_, s); + return s; } case staj_event_type::bool_value: { - value_converter converter; - return converter.convert(value_.bool_value_,tag(),ec); + return jsoncons::make_obj_using_allocator(alloc, value_.bool_value_ ? true_constant : false_constant); } case staj_event_type::null_value: { - value_converter converter; - return converter.convert(tag(), ec); + return jsoncons::make_obj_using_allocator(alloc, null_constant); } default: { @@ -360,19 +370,26 @@ class basic_staj_event template typename std::enable_if::value && std::is_same::value,T>::type - get_(Allocator, std::error_code& ec) const + get_(Allocator alloc, std::error_code& ec) const { switch (event_type_) { - case staj_event_type::byte_string_value: + case staj_event_type::byte_string_value: { - value_converter converter; - return converter.convert(byte_string_view(value_.byte_string_data_, length_), tag(), ec); + auto v = jsoncons::make_obj_using_allocator(alloc, + value_.byte_string_data_, + value_.byte_string_data_+length_); + return v; } - case staj_event_type::string_value: + case staj_event_type::string_value: { - value_converter,T> converter; - return converter.convert(jsoncons::basic_string_view(value_.string_data_, length_), tag(), ec); + auto v = jsoncons::make_obj_using_allocator(alloc); + auto r = string_to_bytes(value_.string_data_, value_.string_data_+length_, tag(), v); + if (r.ec != conv_errc{}) + { + ec = conv_errc::not_byte_string; + } + return v; } default: ec = conv_errc::not_byte_string; @@ -389,7 +406,7 @@ class basic_staj_event case staj_event_type::string_value: { IntegerType val; - auto result = jsoncons::detail::to_integer(value_.string_data_, length_, val); + auto result = jsoncons::to_integer(value_.string_data_, length_, val); if (!result) { ec = conv_errc::not_integer; @@ -442,8 +459,9 @@ class basic_staj_event case staj_event_type::key: case staj_event_type::string_value: { - jsoncons::detail::chars_to f; - return f(value_.string_data_, length_); + double val{0}; + jsoncons::decstr_to_double(value_.string_data_, length_, val); + return val; } case staj_event_type::double_value: return value_.double_value_; diff --git a/include/jsoncons/staj_event_reader.hpp b/include/jsoncons/staj_event_reader.hpp index d8db89a..3454728 100644 --- a/include/jsoncons/staj_event_reader.hpp +++ b/include/jsoncons/staj_event_reader.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -16,18 +16,17 @@ #include #include -#include +#include #include #include #include -#include #include -#include +#include #include #include #include #include -#include +#include namespace jsoncons { diff --git a/include/jsoncons/staj_iterator.hpp b/include/jsoncons/staj_iterator.hpp index c946bd4..ededd0d 100644 --- a/include/jsoncons/staj_iterator.hpp +++ b/include/jsoncons/staj_iterator.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -19,22 +19,19 @@ #include #include -#include +#include #include #include #include namespace jsoncons { - // staj_array_view + // staj_array_iterator - template - class staj_array_view; - - template + template class staj_array_iterator { - using char_type = typename Json::char_type; + using char_type = CharT; using value_type = T; using difference_type = std::ptrdiff_t; using pointer = T*; @@ -42,24 +39,21 @@ namespace jsoncons { using iterator_category = std::input_iterator_tag; private: - staj_array_view* view_{nullptr}; - bool done_{false}; - std::exception_ptr eptr_; + basic_staj_cursor* cursor_ptr_{nullptr}; + jsoncons::optional value_; + bool done_{true}; public: - staj_array_iterator() noexcept - : done_(true) - { - } + staj_array_iterator() noexcept = default; - staj_array_iterator(staj_array_view& view) - : view_(std::addressof(view)) + staj_array_iterator(basic_staj_cursor& cursor) + : cursor_ptr_(std::addressof(cursor)), done_(false) { - if (view_->cursor_->done()) + if (cursor_ptr_->done()) { done_ = true; } - else if (view_->cursor_->current().event_type() == staj_event_type::begin_array) + else if (cursor_ptr_->current().event_type() == staj_event_type::begin_array) { next(); } @@ -69,17 +63,16 @@ namespace jsoncons { } } - staj_array_iterator(staj_array_view& view, std::error_code& ec) - : view_(std::addressof(view)) + staj_array_iterator(basic_staj_cursor& cursor, std::error_code& ec) + : cursor_ptr_(std::addressof(cursor)), done_(false) { - if (view_->cursor_->done()) + if (cursor_ptr_->done()) { done_ = true; } - else if (view_->cursor_->current().event_type() == staj_event_type::begin_array) + else if (cursor_ptr_->current().event_type() == staj_event_type::begin_array) { next(ec); - if (JSONCONS_UNLIKELY(ec)) {done_ = true;} } else { @@ -87,29 +80,20 @@ namespace jsoncons { } } + staj_array_iterator(const staj_array_iterator& iter) = default; + ~staj_array_iterator() noexcept = default; - bool has_value() const - { - return !eptr_; - } + staj_array_iterator& operator=(const staj_array_iterator& iter) = default; const T& operator*() const { - if (JSONCONS_UNLIKELY(eptr_)) - { - std::rethrow_exception(eptr_); - } - return *view_->value_; + return *value_; } const T* operator->() const { - if (JSONCONS_UNLIKELY(eptr_)) - { - std::rethrow_exception(eptr_); - } - return view_->value_.operator->(); + return value_.operator->(); } staj_array_iterator& operator++() @@ -159,81 +143,61 @@ namespace jsoncons { next(ec); if (JSONCONS_UNLIKELY(ec)) { - JSONCONS_THROW(ser_error(ec, view_->cursor_->context().line(), view_->cursor_->context().column())); + JSONCONS_THROW(ser_error(ec, cursor_ptr_->context().line(), cursor_ptr_->context().column())); } } void next(std::error_code& ec) { + ec.clear(); if (JSONCONS_UNLIKELY(done_)) { return; } - if (view_->cursor_->done()) + if (cursor_ptr_->done()) { done_ = true; return; } - view_->cursor_->next(ec); + cursor_ptr_->next(ec); if (JSONCONS_UNLIKELY(ec)) { done_ = true; return; } - if (JSONCONS_UNLIKELY(view_->cursor_->current().event_type() == staj_event_type::end_array)) + if (JSONCONS_UNLIKELY(cursor_ptr_->current().event_type() == staj_event_type::end_array)) { done_ = true; return; } - eptr_ = std::exception_ptr(); - JSONCONS_TRY + auto result = reflect::decode_traits::try_decode(make_alloc_set(), *cursor_ptr_); + if (JSONCONS_UNLIKELY(!result)) { - view_->value_ = decode_traits::decode(*view_->cursor_, view_->decoder_, ec); + ec = result.error().code(); + return; } - JSONCONS_CATCH(const conv_error&) - { - eptr_ = std::current_exception(); - } + value_ = std::move(*result); } }; - template - class staj_array_view + template + staj_array_iterator begin(staj_array_iterator iter) { - friend class staj_array_iterator; - public: - using char_type = typename Json::char_type; - using iterator = staj_array_iterator; - private: - basic_staj_cursor* cursor_; - json_decoder decoder_; - jsoncons::optional value_; - public: - staj_array_view(basic_staj_cursor& cursor) - : cursor_(std::addressof(cursor)) - { - } - - iterator begin() - { - return staj_array_iterator(*this); - } + return iter; + } - iterator end() - { - return staj_array_iterator(); - } - }; + template + staj_array_iterator end(staj_array_iterator) noexcept + { + return staj_array_iterator(); + } - // staj_object_view + // staj_object_iterator - template - class staj_object_view; - - template + template class staj_object_iterator { - using char_type = typename Json::char_type; + using char_type = CharT; using key_type = std::basic_string; using value_type = std::pair; using difference_type = std::ptrdiff_t; @@ -242,24 +206,21 @@ namespace jsoncons { using iterator_category = std::input_iterator_tag; private: - staj_object_view* view_{nullptr}; - bool done_{false}; - std::exception_ptr eptr_; + basic_staj_cursor* cursor_ptr_{nullptr}; + jsoncons::optional key_value_; + bool done_{true}; public: - staj_object_iterator() noexcept - : done_(true) - { - } + staj_object_iterator() noexcept = default; - staj_object_iterator(staj_object_view& view) - : view_(std::addressof(view)) + staj_object_iterator(basic_staj_cursor& cursor) + : cursor_ptr_(std::addressof(cursor)), done_(false) { - if (view_->cursor_->done()) + if (cursor_ptr_->done()) { done_ = true; } - else if (view_->cursor_->current().event_type() == staj_event_type::begin_object) + else if (cursor_ptr_->current().event_type() == staj_event_type::begin_object) { next(); } @@ -269,15 +230,14 @@ namespace jsoncons { } } - staj_object_iterator(staj_object_view& view, - std::error_code& ec) - : view_(std::addressof(view)) + staj_object_iterator(basic_staj_cursor& cursor, std::error_code& ec) + : cursor_ptr_(std::addressof(cursor)), done_(false) { - if (view_->cursor_->done()) + if (cursor_ptr_->done()) { done_ = true; } - else if (view_->cursor_->current().event_type() == staj_event_type::begin_object) + else if (cursor_ptr_->current().event_type() == staj_event_type::begin_object) { next(ec); if (JSONCONS_UNLIKELY(ec)) {done_ = true;} @@ -288,32 +248,20 @@ namespace jsoncons { } } + staj_object_iterator(const staj_object_iterator& iter) = default; + ~staj_object_iterator() noexcept = default; - bool has_value() const - { - return !eptr_; - } + staj_object_iterator& operator=(const staj_object_iterator& iter) = default; const value_type& operator*() const { - if (JSONCONS_UNLIKELY(eptr_)) - { - std::rethrow_exception(eptr_); - } - return *view_->key_value_; + return *key_value_; } const value_type* operator->() const { - if (JSONCONS_UNLIKELY(eptr_)) - { - std::rethrow_exception(eptr_); - } - else - { - return view_->key_value_.operator->(); - } + return key_value_.operator->(); } staj_object_iterator& operator++() @@ -363,7 +311,7 @@ namespace jsoncons { next(ec); if (JSONCONS_UNLIKELY(ec)) { - JSONCONS_THROW(ser_error(ec, view_->cursor_->context().line(), view_->cursor_->context().column())); + JSONCONS_THROW(ser_error(ec, cursor_ptr_->context().line(), cursor_ptr_->context().column())); } } @@ -373,86 +321,52 @@ namespace jsoncons { { return; } - if (view_->cursor_->done()) + if (cursor_ptr_->done()) { done_ = true; return; } - view_->cursor_->next(ec); + cursor_ptr_->next(ec); if (JSONCONS_UNLIKELY(ec)) { done_ = true; return; } - if (JSONCONS_UNLIKELY(view_->cursor_->current().event_type() == staj_event_type::end_object)) + if (JSONCONS_UNLIKELY(cursor_ptr_->current().event_type() == staj_event_type::end_object)) { done_ = true; return; } - JSONCONS_ASSERT(view_->cursor_->current().event_type() == staj_event_type::key); - auto key = view_->cursor_->current(). template get(); - view_->cursor_->next(ec); + JSONCONS_ASSERT(cursor_ptr_->current().event_type() == staj_event_type::key); + auto key = cursor_ptr_->current(). template get(); + cursor_ptr_->next(ec); if (JSONCONS_UNLIKELY(ec)) { done_ = true; return; } - eptr_ = std::exception_ptr(); - JSONCONS_TRY - { - view_->key_value_ = value_type(std::move(key),decode_traits::decode(*view_->cursor_, view_->decoder_, ec)); - } - JSONCONS_CATCH(const conv_error&) + auto result = reflect::decode_traits::try_decode(make_alloc_set(), *cursor_ptr_); + if (JSONCONS_UNLIKELY(!result)) { - eptr_ = std::current_exception(); + ec = result.error().code(); + done_ = true; + return; } + key_value_ = value_type(std::move(key), std::move(*result)); } }; - // staj_object_view - - template - class staj_object_view - { - friend class staj_object_iterator; - public: - using char_type = typename Json::char_type; - using iterator = staj_object_iterator; - using key_type = std::basic_string; - using mapped_type = Json; - using value_type = std::pair; - private: - basic_staj_cursor* cursor_; - json_decoder decoder_; - jsoncons::optional key_value_; - public: - staj_object_view(basic_staj_cursor& cursor) - : cursor_(std::addressof(cursor)) - { - } - - iterator begin() - { - return staj_object_iterator(*this); - } - - iterator end() - { - return staj_object_iterator(); - } - }; - - template ::value,T,basic_json>::type> - staj_array_view staj_array(basic_staj_cursor& cursor) + template + staj_object_iterator begin(staj_object_iterator iter) { - return staj_array_view(cursor); + return iter; } - template ::value,T,basic_json>::type> - staj_object_view staj_object(basic_staj_cursor& cursor) + template + staj_object_iterator end(staj_object_iterator) noexcept { - return staj_object_view(cursor); + return staj_object_iterator(); } } // namespace jsoncons diff --git a/include/jsoncons/text_source_adaptor.hpp b/include/jsoncons/text_source_adaptor.hpp index 56bdd58..205e98a 100644 --- a/include/jsoncons/text_source_adaptor.hpp +++ b/include/jsoncons/text_source_adaptor.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/include/jsoncons/typed_array_view.hpp b/include/jsoncons/typed_array_view.hpp index 6be2f92..fe6a8f0 100644 --- a/include/jsoncons/typed_array_view.hpp +++ b/include/jsoncons/typed_array_view.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -14,14 +14,13 @@ #include #include -#include +#include #include -#include #include -#include +#include #include #include -#include +#include namespace jsoncons { diff --git a/include/jsoncons/utility/bigint.hpp b/include/jsoncons/utility/bigint.hpp index cfb5473..a6bb94f 100644 --- a/include/jsoncons/utility/bigint.hpp +++ b/include/jsoncons/utility/bigint.hpp @@ -1,4 +1,4 @@ -// Copyright 2018 vDaniel Parker +// Copyright 2018 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -13,202 +13,204 @@ #include // std::fmod #include #include // std::memcpy -#include +#include #include // std::numeric_limits #include // std::allocator #include // std::string +#include #include // std::enable_if #include // std::vector #include #include +//#include +#include namespace jsoncons { -/* -This implementation is based on Chapter 2 and Appendix A of -Ammeraal, L. (1996) Algorithms and Data Structures in C++, -Chichester: John Wiley. +namespace detail { -*/ +// bits per digit in the given radix times 1024 +// Rounded up to avoid underallocation. +JSONCONS_INLINE_CONSTEXPR uint64_t bits_per_digit[] = { 0, 0, + 1024, 1624, 2048, 2378, 2648, 2875, 3072, 3247, 3402, 3543, 3672, + 3790, 3899, 4001, 4096, 4186, 4271, 4350, 4426, 4498, 4567, 4633, + 4696, 4756, 4814, 4870, 4923, 4975, 5025, 5074, 5120, 5166, 5210, + 5253, 5295}; -namespace detail { +template +class bigint_storage : private std::allocator_traits:: template rebind_alloc +{ +public: + using word_allocator_type = typename std::allocator_traits:: template rebind_alloc; + using size_type = typename std::allocator_traits::size_type; + using word_type = typename std::allocator_traits::value_type; + static constexpr word_type max_word = (std::numeric_limits::max)(); + static constexpr size_type mem_unit = sizeof(word_type); + static constexpr size_type word_type_bits = sizeof(word_type) * 8; // Number of bits + static constexpr size_type word_type_half_bits = word_type_bits/2; + static constexpr size_type inlined_capacity = 2; +public: - template - class basic_bigint_base + template + class storage_view { - public: - using allocator_type = Allocator; - using basic_type_allocator_type = typename std::allocator_traits:: template rebind_alloc; + ValueType* data_; + size_type size_; - private: - basic_type_allocator_type alloc_; public: - using allocator_traits_type = std::allocator_traits; - using stored_allocator_type = allocator_type; - using pointer = typename allocator_traits_type::pointer; - using value_type = typename allocator_traits_type::value_type; - using size_type = std::size_t; - using pointer_traits = std::pointer_traits; - - basic_bigint_base() - : alloc_() + storage_view(ValueType* data, size_type size) + : data_(data), size_(size) { } - explicit basic_bigint_base(const allocator_type& alloc) - : alloc_(basic_type_allocator_type(alloc)) + + ValueType& operator[](size_type i) { + return data_[i]; } - basic_type_allocator_type get_allocator() const + ValueType operator[](size_type i) const { - return alloc_; + return data_[i]; } - }; - -} // namespace detail -template > -class basic_bigint : protected detail::basic_bigint_base -{ - using base_t = detail::basic_bigint_base; - - static constexpr uint64_t max_short_storage_size = 2; -public: - - using size_type = typename base_t::size_type; - using value_type = typename base_t::value_type; - using base_t::get_allocator; - using bigint_type = basic_bigint; - - static constexpr uint64_t max_basic_type = (std::numeric_limits::max)(); - static constexpr uint64_t basic_type_bits = sizeof(uint64_t) * 8; // Number of bits - static constexpr uint64_t basic_type_halfBits = basic_type_bits/2; + ValueType* data() + { + return data_; + } - static constexpr uint16_t word_length = 4; // Use multiples of word_length words - static constexpr uint64_t r_mask = (uint64_t(1) << basic_type_halfBits) - 1; - static constexpr uint64_t l_mask = max_basic_type - r_mask; - static constexpr uint64_t l_bit = max_basic_type - (max_basic_type >> 1); - static constexpr uint64_t max_uint64_div_10 = (std::numeric_limits::max)()/10u ; - static constexpr uint64_t max_uint64_div_16 = (std::numeric_limits::max)()/16u ; + size_type size() const + { + return size_; + } + ValueType* begin() + { + return data_; + } -private: + ValueType* end() + { + return data_ + size_; + } + }; struct common_storage { - uint8_t is_dynamic_:1; - uint8_t is_negative_:1; - size_type length_; + uint8_t is_allocated_ : 1; + uint8_t is_negative_ : 1; + size_type size_; }; - struct short_storage + struct inlined_storage { - uint8_t is_dynamic_:1; - uint8_t is_negative_:1; - size_type length_; - uint64_t values_[max_short_storage_size]; + uint8_t is_allocated_ : 1; + uint8_t is_negative_ : 1; + size_type size_; + word_type values_[inlined_capacity]; - short_storage() - : is_dynamic_(false), - is_negative_(false), - length_(0), - values_{0,0} + inlined_storage() + : is_allocated_(false), + is_negative_(false), + size_(0), + values_{0, 0} { } template - short_storage(T n, - typename std::enable_if::value && - sizeof(T) <= sizeof(int64_t) && - std::is_signed::value>::type* = 0) - : is_dynamic_(false), - is_negative_(n < 0), - length_(n == 0 ? 0 : 1) - { - values_[0] = n < 0 ? (uint64_t(0)-static_cast(n)) : static_cast(n); + inlined_storage(T n, + typename std::enable_if::value && + sizeof(T) <= sizeof(int64_t) && + std::is_signed::value>::type* = 0) + : is_allocated_(false), + is_negative_(n < 0), + size_(n == 0 ? 0 : 1) + { + values_[0] = n < 0 ? (word_type(0) - static_cast(n)) : static_cast(n); values_[1] = 0; } template - short_storage(T n, - typename std::enable_if::value && - sizeof(T) <= sizeof(int64_t) && - !std::is_signed::value>::type* = 0) - : is_dynamic_(false), - is_negative_(false), - length_(n == 0 ? 0 : 1) + inlined_storage(T n, + typename std::enable_if::value && + sizeof(T) <= sizeof(int64_t) && + !std::is_signed::value>::type* = 0) + : is_allocated_(false), + is_negative_(false), + size_(n == 0 ? 0 : 1) { values_[0] = n; values_[1] = 0; } template - short_storage(T n, - typename std::enable_if::value && - sizeof(int64_t) < sizeof(T) && - std::is_signed::value>::type* = 0) - : is_dynamic_(false), - is_negative_(n < 0), - length_(n == 0 ? 0 : max_short_storage_size) + inlined_storage(T n, + typename std::enable_if < std::is_integral::value && + sizeof(int64_t) < sizeof(T) && + std::is_signed::value > ::type* = 0) + : is_allocated_(false), + is_negative_(n < 0), + size_(n == 0 ? 0 : inlined_capacity) { using unsigned_type = typename std::make_unsigned::type; - auto u = n < 0 ? (unsigned_type(0)-static_cast(n)) : static_cast(n); - values_[0] = uint64_t(u & max_basic_type);; - u >>= basic_type_bits; - values_[1] = uint64_t(u & max_basic_type);; + auto u = n < 0 ? (unsigned_type(0) - static_cast(n)) : static_cast(n); + values_[0] = word_type(u & max_word);; + u >>= word_type_bits; + values_[1] = word_type(u & max_word);; } template - short_storage(T n, - typename std::enable_if::value && - sizeof(int64_t) < sizeof(T) && - !std::is_signed::value>::type* = 0) - : is_dynamic_(false), - is_negative_(false), - length_(n == 0 ? 0 : max_short_storage_size) + inlined_storage(T n, + typename std::enable_if < std::is_integral::value && + sizeof(int64_t) < sizeof(T) && + !std::is_signed::value > ::type* = 0) + : is_allocated_(false), + is_negative_(false), + size_(n == 0 ? 0 : inlined_capacity) { - values_[0] = uint64_t(n & max_basic_type);; - n >>= basic_type_bits; - values_[1] = uint64_t(n & max_basic_type);; + values_[0] = word_type(n & max_word);; + n >>= word_type_bits; + values_[1] = word_type(n & max_word);; } - short_storage(const short_storage& stor) - : is_dynamic_(false), - is_negative_(stor.is_negative_), - length_(stor.length_) + inlined_storage(const inlined_storage& stor) + : is_allocated_(false), + is_negative_(stor.is_negative_), + size_(stor.size_) { values_[0] = stor.values_[0]; values_[1] = stor.values_[1]; } - short_storage& operator=(const short_storage& stor) = delete; - short_storage& operator=(short_storage&& stor) = delete; + inlined_storage& operator=(const inlined_storage& stor) = delete; + inlined_storage& operator=(inlined_storage&& stor) = delete; }; - struct dynamic_storage + struct allocated_storage { - using real_allocator_type = typename std::allocator_traits:: template rebind_alloc; + using real_allocator_type = typename std::allocator_traits:: template rebind_alloc; using pointer = typename std::allocator_traits::pointer; - uint8_t is_dynamic_:1; - uint8_t is_negative_:1; - size_type length_{0}; + uint8_t is_allocated_ : 1; + uint8_t is_negative_ : 1; + size_type size_{0}; size_type capacity_{0}; pointer data_{nullptr}; - dynamic_storage() - : is_dynamic_(true), - is_negative_(false) + allocated_storage() + : is_allocated_(true), + is_negative_(false) { } - dynamic_storage(const dynamic_storage& stor, real_allocator_type alloc) - : is_dynamic_(true), + allocated_storage(const allocated_storage& stor, const real_allocator_type& a) + : is_allocated_(true), is_negative_(stor.is_negative_), - length_(stor.length_), - capacity_(round_up(stor.length_)) + size_(stor.size_), + capacity_(round_up(stor.size_)) { + real_allocator_type alloc(a); + data_ = std::allocator_traits::allocate(alloc, capacity_); JSONCONS_TRY { @@ -217,20 +219,23 @@ class basic_bigint : protected detail::basic_bigint_base JSONCONS_CATCH(...) { std::allocator_traits::deallocate(alloc, data_, capacity_); + data_ = nullptr; JSONCONS_RETHROW; } JSONCONS_ASSERT(stor.data_ != nullptr); - std::memcpy(data_, stor.data_, size_type(stor.length_*sizeof(uint64_t))); + std::memcpy(data_, stor.data_, size_type(stor.size_ * sizeof(word_type))); } - dynamic_storage(dynamic_storage&& stor) noexcept - : is_dynamic_(true), - is_negative_(stor.is_negative_), - length_(stor.length_), - capacity_(stor.capacity_), - data_(stor.data_) + allocated_storage(allocated_storage&& stor) noexcept + : is_allocated_(stor.is_allocated_), + is_negative_(stor.is_negative_), + size_(stor.size_), + capacity_(stor.capacity_), + data_(stor.data_) { - stor.length_ = 0; + stor.is_allocated_ = false; + stor.is_negative_ = false; + stor.size_ = 0; stor.capacity_ = 0; stor.data_ = nullptr; } @@ -240,185 +245,498 @@ class basic_bigint : protected detail::basic_bigint_base if (data_ != nullptr) { real_allocator_type alloc(a); - - std::allocator_traits::destroy(alloc, ext_traits::to_plain_pointer(data_)); - std::allocator_traits::deallocate(alloc, data_,capacity_); + std::allocator_traits::deallocate(alloc, data_, capacity_); } } void reserve(size_type n, const real_allocator_type& a) { - real_allocator_type alloc(a); - size_type capacity_new = round_up(n); - uint64_t* data_old = data_; - data_ = std::allocator_traits::allocate(alloc, capacity_new); - if (length_ > 0) + + real_allocator_type alloc(a); + word_type* data_new = std::allocator_traits::allocate(alloc, capacity_new); + if (size_ > 0) { - std::memcpy( data_, data_old, size_type(length_*sizeof(uint64_t))); + std::memcpy(data_new, data_, size_type(size_ * sizeof(word_type))); } - if (capacity_ > 0 && data_ != nullptr) + if (data_ != nullptr) { - std::allocator_traits::deallocate(alloc, data_old, capacity_); + std::allocator_traits::deallocate(alloc, data_, capacity_); } capacity_ = capacity_new; + data_ = data_new; } // Find suitable new block size - constexpr size_type round_up(size_type i) const noexcept + constexpr size_type round_up(size_type i) const noexcept { - return (i/word_length + 1) * word_length; + return ((i + 1/3) / mem_unit + 1) * mem_unit; } }; union { - common_storage common_stor_; - short_storage short_stor_; - dynamic_storage dynamic_stor_; + common_storage common_; + inlined_storage inlined_; + allocated_storage allocated_; }; -public: - basic_bigint() + explicit bigint_storage(const Allocator& alloc = Allocator{}) + : word_allocator_type(alloc) { - ::new (&short_stor_) short_storage(); + ::new (&inlined_) inlined_storage(); } - explicit basic_bigint(const Allocator& alloc) - : base_t(alloc) + bigint_storage(const bigint_storage& other) + : word_allocator_type(other.get_allocator()) { - ::new (&short_stor_) short_storage(); + if (!other.is_allocated()) + { + ::new (&inlined_) inlined_storage(other.inlined_); + } + else + { + ::new (&allocated_) allocated_storage(other.allocated_, get_allocator()); + } } + bigint_storage(const bigint_storage& other, const Allocator& alloc) + : word_allocator_type(alloc) + { + if (!other.is_allocated()) + { + ::new (&inlined_) inlined_storage(other.inlined_); + } + else + { + ::new (&allocated_) allocated_storage(other.allocated_, alloc); + } + } - basic_bigint(const basic_bigint& n) - : base_t(n.get_allocator()) + bigint_storage(bigint_storage&& other) noexcept + : word_allocator_type(other.get_allocator()) { - if (!n.is_dynamic()) + if (!other.is_allocated()) { - ::new (&short_stor_) short_storage(n.short_stor_); + ::new (&inlined_) inlined_storage(other.inlined_); } else { - ::new (&dynamic_stor_) dynamic_storage(n.dynamic_stor_, get_allocator()); + ::new (&allocated_) allocated_storage(std::move(other.allocated_)); } } - basic_bigint(basic_bigint&& other) noexcept - : base_t(other.get_allocator()) + bigint_storage(bigint_storage&& other, const Allocator& alloc) noexcept + : word_allocator_type(alloc) { - if (!other.is_dynamic()) + if (!other.is_allocated()) { - ::new (&short_stor_) short_storage(other.short_stor_); + ::new (&inlined_) inlined_storage(other.inlined_); } else { - ::new (&dynamic_stor_) dynamic_storage(std::move(other.dynamic_stor_)); + ::new (&allocated_) allocated_storage(std::move(other.allocated_), get_allocator()); } } template - basic_bigint(Integer n, - typename std::enable_if::value>::type* = 0) + bigint_storage(Integer n, const Allocator& alloc = Allocator(), + typename std::enable_if::value>::type* = 0) + : word_allocator_type(alloc) { - ::new (&short_stor_) short_storage(n); + ::new (&inlined_) inlined_storage(n); } - ~basic_bigint() noexcept + bigint_storage& operator=(const bigint_storage& other) + { + if (this != &other) + { + auto other_view = other.get_storage_view(); + resize(other_view.size()); + auto this_view = get_storage_view(); + if (other_view.size() > 0) + { + common_.is_negative_ = other.common_.is_negative_; + std::memcpy(this_view.data(), other_view.data(), size_type(other_view.size()*sizeof(word_type))); + } + } + return *this; + } + + bigint_storage& operator&=(const bigint_storage& a) + { + auto this_view = get_storage_view(); + auto a_view = a.get_storage_view(); + + const size_type old_length = this_view.size(); + const size_type new_length = (std::min)(old_length, a_view.size()); + + if (new_length != old_length) + { + resize(new_length); + this_view = get_storage_view(); + } + + if (new_length > 0) + { + const word_type* first = this_view.begin(); + word_type* p = this_view.end() - 1; + const word_type* q = a_view.begin() + this_view.size() - 1; + + while ( p >= first ) + { + *p-- &= *q--; + } + + if (old_length > new_length) + { + if (is_allocated()) + { + std::memset(allocated_.data_ + new_length, 0, size_type(old_length - new_length*sizeof(word_type))); + } + else + { + JSONCONS_ASSERT(new_length <= inlined_capacity); + for (size_type i = new_length; i < inlined_capacity; ++i) + { + inlined_.values_[i] = 0; + } + } + } + } + + reduce(); + + return *this; + } + + void reduce() { - destroy(); + if (common_.size_ > 0) + { + auto this_view = get_storage_view(); + word_type* p = this_view.end() - 1; + word_type* first = this_view.begin(); + while ( p >= first ) + { + if ( *p ) + { + break; + } + --common_.size_; + --p; + } + } + if (common_.size_ == 0) + { + common_.is_negative_ = false; + } } - constexpr bool is_dynamic() const + void reserve(size_type n) { - return common_stor_.is_dynamic_; + if (capacity() < n) + { + if (!is_allocated()) + { + size_type size = inlined_.size_; + size_type is_neg = inlined_.is_negative_; + word_type values[inlined_capacity] = {inlined_.values_[0], inlined_.values_[1]}; + + ::new (&allocated_) allocated_storage(); + allocated_.reserve(n, get_allocator()); + allocated_.size_ = size; + allocated_.is_negative_ = is_neg; + if (n >= 1) + { + allocated_.data_[0] = values[0]; + } + if (n >= 2) + { + allocated_.data_[1] = values[1]; + } + } + else + { + allocated_.reserve(n, get_allocator()); + } + } + } + + const word_allocator_type& get_allocator() const + { + return static_cast(*this); + } + + void destroy() noexcept + { + if (is_allocated()) + { + allocated_.destroy(get_allocator()); + allocated_.~allocated_storage(); + } + else + { + inlined_.~inlined_storage(); + } } - constexpr size_type length() const + constexpr bool is_allocated() const { - return common_stor_.length_; + return common_.is_allocated_; } constexpr size_type capacity() const { - return is_dynamic() ? dynamic_stor_.capacity_ : max_short_storage_size; + return is_allocated() ? allocated_.capacity_ : inlined_capacity; } bool is_negative() const { - return common_stor_.is_negative_; + return common_.is_negative_; } - void is_negative(bool value) + void set_negative(bool value) { - common_stor_.is_negative_ = value; + common_.is_negative_ = value; } - const uint64_t* data() const + storage_view get_storage_view() { - const uint64_t* p = is_dynamic() ? dynamic_stor_.data_ : short_stor_.values_; - JSONCONS_ASSERT(p != nullptr); - return p; + return common_.is_allocated_ ? + storage_view{allocated_.data_, allocated_.size_} : + storage_view{inlined_.values_, inlined_.size_}; } - uint64_t* data() + storage_view get_storage_view() const { - uint64_t* p = is_dynamic() ? dynamic_stor_.data_ : short_stor_.values_; - JSONCONS_ASSERT(p != nullptr); - return p; + return common_.is_allocated_ ? + storage_view{allocated_.data_, allocated_.size_} : + storage_view{inlined_.values_, inlined_.size_}; } - template - static basic_bigint from_string(const std::basic_string& s) + void resize(size_type new_length) { - return from_string(s.data(), s.length()); + size_type old_length = common_.size_; + reserve(new_length); + common_.size_ = new_length; + + if (old_length < new_length) + { + if (is_allocated()) + { + std::memset(allocated_.data_+old_length, 0, size_type((new_length-old_length)*sizeof(word_type))); + } + else + { + JSONCONS_ASSERT(new_length <= inlined_capacity); + for (size_type i = old_length; i < inlined_capacity; ++i) + { + inlined_.values_[i] = 0; + } + } + } } +}; - template - static basic_bigint from_string(const CharT* s) +} // namespace detail + +template +struct to_bigint_result +{ + const CharT* ptr; + std::errc ec; + constexpr to_bigint_result(const CharT* ptr_) + : ptr(ptr_), ec(std::errc{}) + { + } + constexpr to_bigint_result(const CharT* ptr_, std::errc ec_) + : ptr(ptr_), ec(ec_) + { + } + + to_bigint_result(const to_bigint_result&) = default; + + to_bigint_result& operator=(const to_bigint_result&) = default; + + constexpr explicit operator bool() const noexcept + { + return ec == std::errc{}; + } + std::error_code error_code() const + { + return make_error_code(ec); + } +}; + +template +class basic_bigint; + +template +to_bigint_result to_bigint(const CharT* data, std::size_t length, + basic_bigint& value, const Allocator& alloc); + +template +to_bigint_result to_bigint(const CharT* data, std::size_t length, + basic_bigint>& value); + +/* +This implementation is based on Chapter 2 and Appendix A of +Ammeraal, L. (1996) Algorithms and Data Structures in C++, +Chichester: John Wiley. + +*/ + + +template > +class basic_bigint +{ + detail::bigint_storage storage_; +public: + + using allocator_type = Allocator; + using word_allocator_type = typename detail::bigint_storage::word_allocator_type; + using allocator_traits_type = std::allocator_traits; + using stored_allocator_type = allocator_type; + using pointer = typename allocator_traits_type::pointer; + using size_type = typename detail::bigint_storage::size_type; + using ssize_type = typename std::make_signed::type; + using word_type = typename detail::bigint_storage::word_type; + using storage_view_type = typename detail::bigint_storage::template storage_view; + using const_storage_view_type = typename detail::bigint_storage::template storage_view; + + static constexpr size_type inlined_capacity = 2; + + static constexpr word_type max_word = (std::numeric_limits::max)(); + static constexpr size_type word_type_bits = sizeof(word_type) * 8; // Number of bits + static constexpr size_type word_type_half_bits = word_type_bits/2; + + static constexpr uint16_t word_length = 8; // Use multiples of word_length words + static constexpr word_type r_mask = (word_type(1) << word_type_half_bits) - 1; + static constexpr word_type l_mask = max_word - r_mask; + static constexpr word_type l_bit = max_word - (max_word >> 1); + static constexpr word_type max_word_type_div_10 = (std::numeric_limits::max)()/10u ; + static constexpr word_type max_word_type_div_16 = (std::numeric_limits::max)()/16u ; + static constexpr word_type max_unsigned_power_10 = 10000000000000000000u; // max_unsigned_power_10 = ::pow(10, imax_unsigned_power_10) + static constexpr size_type imax_unsigned_power_10 = 19u; + static constexpr word_type max_unsigned_power_16 = 1152921504606846976u; // max_unsigned_power_16 = ::pow(16, imax_unsigned_power_16) + static constexpr size_type imax_unsigned_power_16 = 15; + +public: + basic_bigint() = default; + + explicit basic_bigint(const Allocator& alloc) + : storage_(alloc) { - return from_string(s, std::char_traits::length(s)); } template - static basic_bigint from_string(const CharT* data, size_type length) + basic_bigint(const CharT* s, const Allocator& alloc = Allocator()) + : storage_(alloc) { - bool neg; - if (*data == '-') + auto r = jsoncons::to_bigint(s, std::char_traits::length(s), *this, alloc); + if (r.ec != std::errc{}) { - neg = true; - data++; - --length; + JSONCONS_THROW(std::system_error((int)r.ec, std::system_category())); } - else + } + + template + basic_bigint(const CharT* s, size_type length, const Allocator& alloc = Allocator()) + : storage_(alloc) + { + auto r = jsoncons::to_bigint(s, length, *this, alloc); + if (r.ec != std::errc{}) { - neg = false; + JSONCONS_THROW(std::system_error((int)r.ec, std::system_category())); } + } - basic_bigint v = 0; - for (size_type i = 0; i < length; i++) + basic_bigint(const basic_bigint& other) + : storage_(other.storage_) + { + } + + basic_bigint(const basic_bigint& other, const Allocator& alloc) + : storage_(other.storage_, alloc) + { + } + + basic_bigint(basic_bigint&& other) noexcept + : storage_(std::move(other.storage_)) + { + } + + basic_bigint(basic_bigint&& other, const Allocator& alloc) noexcept + : storage_(std::move(other.storage_), alloc) + { + } + + template + basic_bigint(Integer n, const Allocator& alloc = Allocator(), + typename std::enable_if::value>::type* = 0) + : storage_(n, alloc) + { + } + + template ::value>::type> + basic_bigint(const StringViewLike& s) + { + auto r = jsoncons::to_bigint(s.data(), s.size(), *this); + if (r.ec != std::errc{}) { - CharT c = data[i]; - switch (c) - { - case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': - v = (v * 10u) + (uint64_t)(c - '0'); - break; - default: - JSONCONS_THROW(std::runtime_error(std::string("Invalid digit ") + "\'" + (char)c + "\'")); - } + JSONCONS_THROW(std::system_error((int)r.ec, std::system_category())); } + } + + ~basic_bigint() noexcept + { + storage_.destroy(); + } + + word_allocator_type get_allocator() const + { + return storage_.get_allocator(); + } + + storage_view_type get_storage_view() + { + return storage_.get_storage_view(); + } + + const_storage_view_type get_storage_view() const + { + return storage_.get_storage_view(); + } + + bool is_negative() const + { + return storage_.is_negative(); + } - if (neg) + void set_negative(bool value) + { + storage_.set_negative(value); + } + + template + static to_bigint_result parse(const std::basic_string& s, basic_bigint& value) + { + return parse(s.data(), s.size(), value); + } + + template + static to_bigint_result parse(const CharT* s, basic_bigint& value) + { + auto r = parse(s, std::char_traits::length(s), value); + if (r.ec != std::errc{}) { - v.common_stor_.is_negative_ = true; + JSONCONS_THROW(std::system_error((int)r.ec, std::system_category())); } - - return v; } template - static basic_bigint from_string_radix(const CharT* data, size_type length, uint8_t radix) + static basic_bigint parse_radix(const CharT* data, size_type length, uint8_t radix) { if (!(radix >= 2 && radix <= 16u)) { @@ -441,17 +759,17 @@ class basic_bigint : protected detail::basic_bigint_base for (size_type i = 0; i < length; i++) { CharT c = data[i]; - uint64_t d; + word_type d; switch (c) { case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': - d = (uint64_t)(c - '0'); + d = (word_type)(c - '0'); break; case 'a':case 'b':case 'c':case 'd':case 'e':case 'f': - d = (uint64_t)(c - ('a' - 10u)); + d = (word_type)(c - ('a' - 10u)); break; case 'A':case 'B':case 'C':case 'D':case 'E':case 'F': - d = (uint64_t)(c - ('A' - 10u)); + d = (word_type)(c - ('A' - 10u)); break; default: JSONCONS_THROW(std::runtime_error(std::string("Invalid digit in radix ") + std::to_string(radix) + ": \'" + (char)c + "\'")); @@ -465,12 +783,12 @@ class basic_bigint : protected detail::basic_bigint_base if ( neg ) { - v.common_stor_.is_negative_ = true; + v.set_negative(true); } return v; } - static basic_bigint from_bytes_be(int signum, const uint8_t* str, std::size_t n) + static basic_bigint from_bytes_be(int signum, const uint8_t* str, size_type n) { static const double radix_log2 = std::log2(next_power_of_two(256)); // Estimate how big the result will be, so we can pre-allocate it. @@ -478,321 +796,295 @@ class basic_bigint : protected detail::basic_bigint_base double big_digits = std::ceil(bits / 64.0); //std::cout << "ESTIMATED: " << big_digits << "\n"; - bigint_type v = 0; - v.reserve(static_cast(big_digits)); + basic_bigint v = 0; + v.reserve(static_cast(big_digits)); if (n > 0) { - for (std::size_t i = 0; i < n; i++) + for (size_type i = 0; i < n; i++) { - v = (v * 256) + (uint64_t)(str[i]); + v = (v * 256) + (word_type)(str[i]); } } - //std::cout << "ACTUAL: " << v.length() << "\n"; + //std::cout << "ACTUAL: " << v.size() << "\n"; if (signum < 0) { - v.common_stor_.is_negative_ = true; + v.set_negative(true); } return v; } - uint64_t* begin() { return is_dynamic() ? dynamic_stor_.data_ : short_stor_.values_; } - const uint64_t* begin() const { return is_dynamic() ? dynamic_stor_.data_ : short_stor_.values_; } - uint64_t* end() { return begin() + length(); } - const uint64_t* end() const { return begin() + length(); } - void resize(size_type new_length) { - size_type old_length = common_stor_.length_; - reserve(new_length); - common_stor_.length_ = new_length; - - if (old_length < new_length) - { - if (is_dynamic()) - { - std::memset(dynamic_stor_.data_+old_length, 0, size_type((new_length-old_length)*sizeof(uint64_t))); - } - else - { - JSONCONS_ASSERT(new_length <= max_short_storage_size); - for (size_type i = old_length; i < max_short_storage_size; ++i) - { - short_stor_.values_[i] = 0; - } - } - } + storage_.resize(new_length); } void reserve(size_type n) { - if (capacity() < n) - { - if (!is_dynamic()) - { - size_type size = short_stor_.length_; - size_type is_neg = short_stor_.is_negative_; - uint64_t values[max_short_storage_size] = {short_stor_.values_[0], short_stor_.values_[1]}; - - ::new (&dynamic_stor_) dynamic_storage(); - dynamic_stor_.reserve(n, get_allocator()); - dynamic_stor_.length_ = size; - dynamic_stor_.is_negative_ = is_neg; - dynamic_stor_.data_[0] = values[0]; - dynamic_stor_.data_[1] = values[1]; - } - else - { - dynamic_stor_.reserve(n, get_allocator()); - } - } + storage_.reserve(n); } // operators bool operator!() const { - return length() == 0 ? true : false; + return get_storage_view().size() == 0 ? true : false; } basic_bigint operator-() const { basic_bigint v(*this); - v.common_stor_.is_negative_ = !v.is_negative(); + v.set_negative(!v.is_negative()); return v; } - basic_bigint& operator=( const basic_bigint& y ) + basic_bigint& operator=( const basic_bigint& y ) { - if ( this != &y ) - { - resize( y.length() ); - common_stor_.is_negative_ = y.is_negative(); - if ( y.length() > 0 ) - { - std::memcpy( data(), y.data(), size_type(y.length()*sizeof(uint64_t)) ); - } - } + storage_ = y.storage_; return *this; } - basic_bigint& operator+=( const basic_bigint& y ) + basic_bigint& operator+=( const basic_bigint& y ) { - const uint64_t* y_data = y.data(); + auto y_view = y.get_storage_view(); - if ( is_negative() != y.is_negative() ) + if ( is_negative() != y.is_negative()) return *this -= -y; - uint64_t d; - uint64_t carry = 0; + word_type d; + word_type carry = 0; - resize( (std::max)(y.length(), length()) + 1 ); - uint64_t* this_data = data(); + auto this_view = get_storage_view(); + resize( (std::max)(y_view.size(), this_view.size()) + 1 ); + this_view = get_storage_view(); - for (size_type i = 0; i < length(); i++ ) + for (size_type i = 0; i < this_view.size(); i++ ) { - if ( i >= y.length() && carry == 0 ) + if ( i >= y_view.size() && carry == 0 ) break; - d = this_data[i] + carry; + d = this_view[i] + carry; carry = d < carry; - if ( i < y.length() ) + if ( i < y_view.size()) { - this_data[i] = d + y_data[i]; - if ( this_data[i] < d ) + this_view[i] = d + y_view[i]; + if (this_view[i] < d) carry = 1; } else - this_data[i] = d; + { + this_view[i] = d; + } } reduce(); return *this; } - basic_bigint& operator-=( const basic_bigint& y ) + basic_bigint& operator-=(const basic_bigint& y) { - const uint64_t* y_data = y.data(); + auto y_view = y.get_storage_view(); - if ( is_negative() != y.is_negative() ) + if ( is_negative() != y.is_negative()) return *this += -y; if ( (!is_negative() && y > *this) || (is_negative() && y < *this) ) return *this = -(y - *this); - uint64_t borrow = 0; - uint64_t d; - for (size_type i = 0; i < length(); i++ ) + word_type borrow = 0; + word_type d; + auto this_view = get_storage_view(); + for (size_type i = 0; i < this_view.size(); i++ ) { - if ( i >= y.length() && borrow == 0 ) + if ( i >= y_view.size() && borrow == 0 ) break; - d = data()[i] - borrow; - borrow = d > data()[i]; - if ( i < y.length()) + d = this_view[i] - borrow; + borrow = d > this_view[i]; + if ( i < y_view.size()) { - data()[i] = d - y_data[i]; - if ( data()[i] > d ) + this_view[i] = d - y_view[i]; + if ( this_view[i] > d ) borrow = 1; } else - data()[i] = d; + { + this_view[i] = d; + } } reduce(); return *this; } - basic_bigint& operator*=( int64_t y ) + template + typename std::enable_if::value, basic_bigint&>::type + operator*=(IntegerType y) { - *this *= uint64_t(y < 0 ? -y : y); + *this *= word_type(y < 0 ? -y : y); if ( y < 0 ) - common_stor_.is_negative_ = !is_negative(); + set_negative(!is_negative()); return *this; } - basic_bigint& operator*=( uint64_t y ) + template + typename std::enable_if::value, basic_bigint&>::type + operator*=(IntegerType y) { - size_type len0 = length(); - uint64_t hi; - uint64_t lo; - uint64_t dig = data()[0]; - uint64_t carry = 0; + auto this_view = get_storage_view(); + size_type len0 = this_view.size(); + word_type dig = this_view[0]; + word_type carry = 0; - resize( length() + 1 ); - uint64_t* this_data = data(); + resize(this_view.size() + 1); + this_view = get_storage_view(); size_type i = 0; - for (i = 0; i < len0; i++ ) + for (; i < len0; i++ ) { + word_type hi; + word_type lo; DDproduct( dig, y, hi, lo ); - this_data[i] = lo + carry; - dig = this_data[i+1]; - carry = hi + (this_data[i] < lo); + this_view[i] = lo + carry; + dig = this_view[i+1]; + carry = hi + (this_view[i] < lo); } - this_data[i] = carry; + this_view[i] = carry; reduce(); return *this; } - basic_bigint& operator*=(const basic_bigint& y) + basic_bigint& operator*=(basic_bigint y) { - const uint64_t* y_data = y.data(); + auto this_view = get_storage_view(); + auto y_view = y.get_storage_view(); + + if (this_view.size() == 0 || y_view.size() == 0) + { + return *this = 0; + } - if ( length() == 0 || y.length() == 0 ) - return *this = 0; bool difSigns = is_negative() != y.is_negative(); - if ( length() + y.length() == max_short_storage_size ) // length() = y.length() = 1 + if ( this_view.size() + y_view.size() == 2 ) // size() = y.size() = 1 { - uint64_t a = data()[0], b = y_data[0]; - data()[0] = a * b; - if ( data()[0] / a != b ) + word_type a = this_view[0], b = y_view[0]; + this_view[0] = a * b; + if ( this_view[0] / a != b ) { - resize( max_short_storage_size ); - DDproduct( a, b, data()[1], data()[0] ); + resize(2); + this_view = get_storage_view(); + DDproduct( a, b, this_view[1], this_view[0] ); } - common_stor_.is_negative_ = difSigns; + set_negative(difSigns); return *this; } - if ( length() == 1 ) // && y.length() > 1 + + if ( this_view.size() == 1 ) // && y.size() > 1 { - uint64_t digit = data()[0]; + word_type digit = this_view[0]; *this = y; *this *= digit; } else { - if ( y.length() == 1 ) - *this *= y_data[0]; + if (y_view.size() == 1) + { + *this *= y_view[0]; + } else { - size_type lenProd = length() + y.length(), jA, jB; - uint64_t sumHi = 0, sumLo, hi, lo, + size_type lenProd = this_view.size() + y_view.size(); + word_type sumHi = 0, sumLo, hi, lo, sumLo_old, sumHi_old, carry=0; basic_bigint x = *this; - const uint64_t* x_data = x.data(); + auto x_view = x.get_storage_view(); resize( lenProd ); // Give *this length lenProd - uint64_t* this_data = data(); + this_view = get_storage_view(); for (size_type i = 0; i < lenProd; i++ ) { sumLo = sumHi; sumHi = carry; carry = 0; - for ( jA=0; jA < x.length(); jA++ ) + for (size_type jA=0; jA < x_view.size(); jA++) { - jB = i - jA; - if ( jB >= 0 && jB < y.length() ) + if (JSONCONS_LIKELY(i >= jA)) { - DDproduct( x_data[jA], y_data[jB], hi, lo ); - sumLo_old = sumLo; - sumHi_old = sumHi; - sumLo += lo; - if ( sumLo < sumLo_old ) - sumHi++; - sumHi += hi; - carry += (sumHi < sumHi_old); + size_type jB = i - jA; + if (jB < y_view.size()) + { + DDproduct( x_view[jA], y_view[jB], hi, lo ); + sumLo_old = sumLo; + sumHi_old = sumHi; + sumLo += lo; + if ( sumLo < sumLo_old ) + sumHi++; + sumHi += hi; + carry += (sumHi < sumHi_old); + } } } - this_data[i] = sumLo; + this_view[i] = sumLo; } } } reduce(); - common_stor_.is_negative_ = difSigns; + set_negative(difSigns); return *this; } - basic_bigint& operator/=( const basic_bigint& divisor ) + basic_bigint& operator/=( const basic_bigint& divisor ) { basic_bigint r; divide( divisor, *this, r, false ); return *this; } - basic_bigint& operator%=( const basic_bigint& divisor ) + basic_bigint& operator%=( const basic_bigint& divisor ) { basic_bigint q; divide( divisor, q, *this, true ); return *this; } - basic_bigint& operator<<=( uint64_t k ) + basic_bigint& operator<<=(size_type k) { - size_type q = size_type(k / basic_type_bits); - if ( q ) // Increase common_stor_.length_ by q: + auto this_view = get_storage_view(); + size_type q = k / word_type_bits; + if ( q ) // Increase storage_.size() by q: { - resize(length() + q); - uint64_t* this_data = data(); - for (size_type i = length(); i-- > 0; ) - this_data[i] = ( i < q ? 0 : this_data[i - q]); - k %= basic_type_bits; + resize(this_view.size() + q); + this_view = get_storage_view(); + for (size_type i = this_view.size(); i-- > 0; ) + this_view[i] = ( i < q ? 0 : this_view[i - q]); + k %= word_type_bits; } - if ( k ) // 0 < k < basic_type_bits: + if ( k ) // 0 < k < word_type_bits: { - uint64_t k1 = basic_type_bits - k; - uint64_t mask = (uint64_t(1) << k) - uint64_t(1); - resize( length() + 1 ); - uint64_t* this_data = data(); - for (size_type i = length(); i-- > 0; ) + size_type k1 = word_type_bits - k; + word_type mask = (word_type(1) << k) - word_type(1); + resize( this_view.size() + 1 ); + this_view = get_storage_view(); + for (size_type i = this_view.size(); i-- > 0; ) { - this_data[i] <<= k; + this_view[i] <<= k; if ( i > 0 ) - this_data[i] |= (this_data[i-1] >> k1) & mask; + this_view[i] |= (this_view[i-1] >> k1) & mask; } } reduce(); return *this; } - basic_bigint& operator>>=(uint64_t k) + basic_bigint& operator>>=(size_type k) { - size_type q = size_type(k / basic_type_bits); - if ( q >= length() ) + auto this_view = get_storage_view(); + size_type q = k / word_type_bits; + if ( q >= this_view.size()) { resize( 0 ); return *this; } if (q > 0) { - memmove( data(), data()+q, size_type((length() - q)*sizeof(uint64_t)) ); - resize( size_type(length() - q) ); - k %= basic_type_bits; + memmove( this_view.data(), this_view.data()+q, size_type((this_view.size() - q)*sizeof(word_type)) ); + resize( size_type(this_view.size() - q) ); + k %= word_type_bits; if ( k == 0 ) { reduce(); @@ -800,15 +1092,15 @@ class basic_bigint : protected detail::basic_bigint_base } } - uint64_t* this_data = data(); - size_type n = size_type(length() - 1); - int64_t k1 = basic_type_bits - k; - uint64_t mask = (uint64_t(1) << k) - 1; + this_view = get_storage_view(); + size_type n = size_type(this_view.size() - 1); + ssize_type k1 = word_type_bits - k; + word_type mask = (word_type(1) << k) - 1; for (size_type i = 0; i <= n; i++) { - this_data[i] >>= k; + this_view[i] >>= k; if ( i < n ) - this_data[i] |= ((this_data[i+1] & mask) << k1); + this_view[i] |= ((this_view[i+1] & mask) << k1); } reduce(); return *this; @@ -827,7 +1119,7 @@ class basic_bigint : protected detail::basic_bigint_base return old; } - basic_bigint& operator--() + basic_bigint& operator--() { *this -= 1; return *this; @@ -840,120 +1132,96 @@ class basic_bigint : protected detail::basic_bigint_base return old; } - basic_bigint& operator|=( const basic_bigint& a ) + basic_bigint& operator|=( const basic_bigint& a ) { - if ( length() < a.length() ) - { - resize( a.length() ); - } - - const uint64_t* qBegin = a.begin(); - const uint64_t* q = a.end() - 1; - uint64_t* p = begin() + a.length() - 1; + auto a_view = a.get_storage_view(); - while ( q >= qBegin ) + if (a_view.size() > 0) { - *p-- |= *q--; - } - - reduce(); - - return *this; - } + auto this_view = get_storage_view(); - basic_bigint& operator^=( const basic_bigint& a ) - { - if ( length() < a.length() ) - { - resize( a.length() ); - } + if ( this_view.size() < a_view.size()) + { + resize( a_view.size()); + this_view = get_storage_view(); + } - const uint64_t* qBegin = a.begin(); - const uint64_t* q = a.end() - 1; - uint64_t* p = begin() + a.length() - 1; + const word_type* qfirst = a_view.begin(); + const word_type* q = a_view.end() - 1; + word_type* p = this_view.begin() + a_view.size() - 1; - while ( q >= qBegin ) - { - *p-- ^= *q--; + while (q >= qfirst) + { + *p-- |= *q--; + } + reduce(); } - reduce(); - return *this; } - basic_bigint& operator&=( const basic_bigint& a ) + basic_bigint& operator^=( const basic_bigint& a ) { - size_type old_length = length(); + auto a_view = a.get_storage_view(); - resize( (std::min)( length(), a.length() ) ); - - const uint64_t* pBegin = begin(); - uint64_t* p = end() - 1; - const uint64_t* q = a.begin() + length() - 1; - - while ( p >= pBegin ) + if (a_view.size() > 0) { - *p-- &= *q--; - } - - const size_type new_length = length(); - if ( old_length > new_length ) - { - if (is_dynamic()) + auto this_view = get_storage_view(); + if (this_view.size() < a_view.size()) { - std::memset( dynamic_stor_.data_ + new_length, 0, size_type(old_length - new_length*sizeof(uint64_t)) ); + resize(a_view.size()); + this_view = get_storage_view(); } - else + + const word_type* qfirst = a_view.begin(); + const word_type* q = a_view.end() - 1; + word_type* p = this_view.begin() + a_view.size() - 1; + + while (q >= qfirst) { - JSONCONS_ASSERT(new_length <= max_short_storage_size); - for (size_type i = new_length; i < max_short_storage_size; ++i) - { - short_stor_.values_[i] = 0; - } + *p-- ^= *q--; } + reduce(); } - reduce(); - return *this; } - explicit operator bool() const + basic_bigint& operator&=( const basic_bigint& a ) { - return length() != 0 ? true : false; + storage_ &= a.storage_; + + return *this; } - explicit operator int64_t() const + explicit operator bool() const { - int64_t x = 0; - if ( length() > 0 ) - { - x = static_cast(data()[0]); - } - - return is_negative() ? -x : x; + return get_storage_view().size() != 0 ? true : false; } - explicit operator uint64_t() const + template ::value && sizeof(Integer) <= sizeof(int64_t)>::type> + explicit operator Integer() const { - uint64_t u = 0; - if ( length() > 0 ) - { - u = data() [0]; - } + auto this_view = get_storage_view(); + Integer x = 0; + if (this_view.size() > 0) + { + x = static_cast(this_view[0]); + } - return u; + return is_negative() ? x*(-1) : x; } explicit operator double() const { double x = 0.0; double factor = 1.0; - double values = (double)max_basic_type + 1.0; + double values = (double)max_word + 1.0; + + auto this_view = get_storage_view(); - const uint64_t* p = begin(); - const uint64_t* pEnd = end(); + const word_type* p = this_view.begin(); + const word_type* pEnd = this_view.end(); while ( p < pEnd ) { x += *p*factor; @@ -968,10 +1236,12 @@ class basic_bigint : protected detail::basic_bigint_base { long double x = 0.0; long double factor = 1.0; - long double values = (long double)max_basic_type + 1.0; + long double values = (long double)max_word + 1.0; - const uint64_t* p = begin(); - const uint64_t* pEnd = end(); + auto this_view = get_storage_view(); + + const word_type* p = this_view.begin(); + const word_type* pEnd = this_view.end(); while ( p < pEnd ) { x += *p*factor; @@ -996,12 +1266,13 @@ class basic_bigint : protected detail::basic_bigint_base basic_bigint r; n.divide(divisor, q, r, true); n = q; - data.push_back((uint8_t)(uint64_t)r); + data.push_back((uint8_t)(word_type)r); } if (n >= 0) { - data.push_back((uint8_t)(uint64_t)n); + data.push_back((uint8_t)(word_type)n); } + std::reverse(data.begin(),data.end()); } @@ -1016,45 +1287,38 @@ class basic_bigint : protected detail::basic_bigint_base void write_string(std::basic_string& data) const { basic_bigint v(*this); + auto v_view = v.get_storage_view(); - std::size_t len = (v.length() * basic_type_bits / 3) + 2; + size_type len = (v_view.size() * word_type_bits / 3) + 2; data.reserve(len); - static uint64_t p10 = 1; - static uint64_t ip10 = 0; - - if ( v.length() == 0 ) + if ( v_view.size() == 0 ) { data.push_back('0'); } else { - uint64_t r; - if ( p10 == 1 ) - { - while ( p10 <= max_uint64_div_10) - { - p10 *= 10u; - ip10++; - } - } - // p10 is max unsigned power of 10 - basic_bigint R; - basic_bigint LP10 = p10; // LP10 = p10 = ::pow(10, ip10) + word_type r; + basic_bigint R(get_allocator()); + basic_bigint LP10(max_unsigned_power_10, get_allocator()); do { v.divide( LP10, v, R, true ); - r = (R.length() ? R.data()[0] : 0); - for ( size_type j=0; j < ip10; j++ ) + v_view = v.get_storage_view(); + + auto R_view = R.get_storage_view(); + r = (R_view.size() ? R_view[0] : 0); + for ( size_type j=0; j < imax_unsigned_power_10; j++ ) { data.push_back(char(r % 10u + '0')); r /= 10u; - if ( r + v.length() == 0 ) + if ( r + v_view.size() == 0 ) break; } } - while ( v.length() ); + while ( v_view.size() > 0); + if (is_negative()) { data.push_back('-'); @@ -1073,45 +1337,39 @@ class basic_bigint : protected detail::basic_bigint_base template void write_string_hex(std::basic_string& data) const { + + basic_bigint v(*this); + auto v_view = v.get_storage_view(); - std::size_t len = (v.length() * basic_bigint::basic_type_bits / 3) + 2; + size_type len = (v_view.size() * basic_bigint::word_type_bits / 3) + 2; data.reserve(len); - // 1/3 > ln(2)/ln(10) - static uint64_t p10 = 1; - static uint64_t ip10 = 0; - if ( v.length() == 0 ) + if ( v_view.size() == 0 ) { data.push_back('0'); } else { - uint64_t r; - if ( p10 == 1 ) - { - while ( p10 <= max_uint64_div_16) - { - p10 *= 16u; - ip10++; - } - } // p10 is max unsigned power of 16 + word_type r; basic_bigint R; - basic_bigint LP10 = p10; // LP10 = p10 = ::pow(16, ip10) + basic_bigint LP10 = max_unsigned_power_16; // LP10 = max_unsigned_power_16 = ::pow(16, imax_unsigned_power_16) do { v.divide( LP10, v, R, true ); - r = (R.length() ? R.data()[0] : 0); - for ( size_type j=0; j < ip10; j++ ) + v_view = v.get_storage_view(); + auto R_view = R.get_storage_view(); + r = (R_view.size() ? R_view[0] : 0); + for ( size_type j=0; j < imax_unsigned_power_16; j++ ) { uint8_t c = r % 16u; data.push_back((c < 10u) ? ('0' + c) : ('A' - 10u + c)); r /= 16u; - if ( r + v.length() == 0 ) + if ( r + v_view.size() == 0 ) break; } } - while (v.length()); + while (v_view.size() > 0); if (is_negative()) { @@ -1123,92 +1381,92 @@ class basic_bigint : protected detail::basic_bigint_base // Global Operators - friend bool operator==( const basic_bigint& x, const basic_bigint& y ) noexcept + friend bool operator==( const basic_bigint& x, const basic_bigint& y ) noexcept { return x.compare(y) == 0 ? true : false; } - friend bool operator==( const basic_bigint& x, int y ) noexcept + friend bool operator==( const basic_bigint& x, int y ) noexcept { return x.compare(y) == 0 ? true : false; } - friend bool operator!=( const basic_bigint& x, const basic_bigint& y ) noexcept + friend bool operator!=( const basic_bigint& x, const basic_bigint& y ) noexcept { return x.compare(y) != 0 ? true : false; } - friend bool operator!=( const basic_bigint& x, int y ) noexcept + friend bool operator!=( const basic_bigint& x, int y ) noexcept { return x.compare(basic_bigint(y)) != 0 ? true : false; } - friend bool operator<( const basic_bigint& x, const basic_bigint& y ) noexcept + friend bool operator<( const basic_bigint& x, const basic_bigint& y ) noexcept { return x.compare(y) < 0 ? true : false; } - friend bool operator<( const basic_bigint& x, int64_t y ) noexcept + friend bool operator<( const basic_bigint& x, int64_t y ) noexcept { return x.compare(y) < 0 ? true : false; } - friend bool operator>( const basic_bigint& x, const basic_bigint& y ) noexcept + friend bool operator>( const basic_bigint& x, const basic_bigint& y ) noexcept { return x.compare(y) > 0 ? true : false; } - friend bool operator>( const basic_bigint& x, int y ) noexcept + friend bool operator>( const basic_bigint& x, int y ) noexcept { return x.compare(basic_bigint(y)) > 0 ? true : false; } - friend bool operator<=( const basic_bigint& x, const basic_bigint& y ) noexcept + friend bool operator<=( const basic_bigint& x, const basic_bigint& y ) noexcept { return x.compare(y) <= 0 ? true : false; } - friend bool operator<=( const basic_bigint& x, int y ) noexcept + friend bool operator<=( const basic_bigint& x, int y ) noexcept { return x.compare(y) <= 0 ? true : false; } - friend bool operator>=( const basic_bigint& x, const basic_bigint& y ) noexcept + friend bool operator>=( const basic_bigint& x, const basic_bigint& y ) noexcept { return x.compare(y) >= 0 ? true : false; } - friend bool operator>=( const basic_bigint& x, int y ) noexcept + friend bool operator>=( const basic_bigint& x, int y ) noexcept { return x.compare(y) >= 0 ? true : false; } - friend basic_bigint operator+( basic_bigint x, const basic_bigint& y ) + friend basic_bigint operator+( basic_bigint x, const basic_bigint& y ) { return x += y; } - friend basic_bigint operator+( basic_bigint x, int64_t y ) + friend basic_bigint operator+( basic_bigint x, int64_t y ) { return x += y; } - friend basic_bigint operator-( basic_bigint x, const basic_bigint& y ) + friend basic_bigint operator-( basic_bigint x, const basic_bigint& y ) { return x -= y; } - friend basic_bigint operator-( basic_bigint x, int64_t y ) + friend basic_bigint operator-( basic_bigint x, int64_t y ) { return x -= y; } - friend basic_bigint operator*( int64_t x, const basic_bigint& y ) + friend basic_bigint operator*( int64_t x, const basic_bigint& y ) { return basic_bigint(y) *= x; } - friend basic_bigint operator*( basic_bigint x, const basic_bigint& y ) + friend basic_bigint operator*( basic_bigint x, const basic_bigint& y ) { return x *= y; } @@ -1218,7 +1476,7 @@ class basic_bigint : protected detail::basic_bigint_base return x *= y; } - friend basic_bigint operator/( basic_bigint x, const basic_bigint& y ) + friend basic_bigint operator/( basic_bigint x, const basic_bigint& y ) { return x /= y; } @@ -1228,7 +1486,7 @@ class basic_bigint : protected detail::basic_bigint_base return x /= y; } - friend basic_bigint operator%( basic_bigint x, const basic_bigint& y ) + friend basic_bigint operator%( basic_bigint x, const basic_bigint& y ) { return x %= y; } @@ -1253,7 +1511,7 @@ class basic_bigint : protected detail::basic_bigint_base return u >>= k; } - friend basic_bigint operator|( basic_bigint x, const basic_bigint& y ) + friend basic_bigint operator|( basic_bigint x, const basic_bigint& y ) { return x |= y; } @@ -1268,7 +1526,7 @@ class basic_bigint : protected detail::basic_bigint_base return x |= y; } - friend basic_bigint operator^( basic_bigint x, const basic_bigint& y ) + friend basic_bigint operator^( basic_bigint x, const basic_bigint& y ) { return x ^= y; } @@ -1283,7 +1541,7 @@ class basic_bigint : protected detail::basic_bigint_base return x ^= y; } - friend basic_bigint operator&( basic_bigint x, const basic_bigint& y ) + friend basic_bigint operator&( basic_bigint x, const basic_bigint& y ) { return x &= y; } @@ -1298,53 +1556,8 @@ class basic_bigint : protected detail::basic_bigint_base return x &= y; } - friend basic_bigint abs( const basic_bigint& a ) - { - if ( a.is_negative() ) - { - return -a; - } - return a; - } - - friend basic_bigint power( basic_bigint x, unsigned n ) - { - basic_bigint y = 1; - - while ( n ) - { - if ( n & 1 ) - { - y *= x; - } - x *= x; - n >>= 1; - } - - return y; - } - - friend basic_bigint sqrt( const basic_bigint& a ) - { - basic_bigint x = a; - basic_bigint b = a; - basic_bigint q; - - b <<= 1; - while ( (void)(b >>= 2), b > 0 ) - { - x >>= 1; - } - while ( x > (q = a/x) + 1 || x < q - 1 ) - { - x += q; - x >>= 1; - } - return x < q ? x : q; - } - template - friend std::basic_ostream& operator<<(std::basic_ostream& os, const basic_bigint& v) + friend std::basic_ostream& operator<<(std::basic_ostream& os, const basic_bigint& v) { std::basic_string s; v.write_string(s); @@ -1353,29 +1566,30 @@ class basic_bigint : protected detail::basic_bigint_base return os; } - int compare( const basic_bigint& y ) const noexcept + int compare( const basic_bigint& y ) const noexcept { - const uint64_t* y_data = y.data(); + auto this_view = get_storage_view(); + auto y_view = y.get_storage_view(); - if ( is_negative() != y.is_negative() ) + if ( this_view.size() == 0 && y_view.size() == 0 ) + return 0; + if ( is_negative() != y.is_negative()) return y.is_negative() - is_negative(); int code = 0; - if ( length() == 0 && y.length() == 0 ) - code = 0; - else if ( length() < y.length() ) + if ( this_view.size() < y_view.size()) code = -1; - else if ( length() > y.length() ) + else if ( this_view.size() > y_view.size()) code = +1; else { - for (size_type i = length(); i-- > 0; ) + for (size_type i = this_view.size(); i-- > 0; ) { - if (data()[i] > y_data[i]) + if (this_view[i] > y_view[i]) { code = 1; break; } - else if (data()[i] < y_data[i]) + else if (this_view[i] < y_view[i]) { code = -1; break; @@ -1385,124 +1599,142 @@ class basic_bigint : protected detail::basic_bigint_base return is_negative() ? -code : code; } - void divide( basic_bigint denom, basic_bigint& quot, basic_bigint& rem, bool remDesired ) const + void divide(const basic_bigint& denom_, basic_bigint& quot, basic_bigint& rem, bool remDesired ) const { - if ( denom.length() == 0 ) + basic_bigint denom(denom_, get_allocator()); + auto denom_view = denom.get_storage_view(); + + if (denom_view.size() == 0) { JSONCONS_THROW(std::runtime_error( "Zero divide." )); } bool quot_neg = is_negative() ^ denom.is_negative(); bool rem_neg = is_negative(); - int x = 0; - basic_bigint num = *this; - num.common_stor_.is_negative_ = denom.common_stor_.is_negative_ = false; + basic_bigint num(*this, get_allocator()); + num.set_negative(false); + denom.set_negative(false); if ( num < denom ) { - quot = uint64_t(0); + quot = word_type(0); + quot.set_negative(quot_neg); rem = num; - rem.common_stor_.is_negative_ = rem_neg; + rem.set_negative(rem_neg); return; } - if ( denom.length() == 1 && num.length() == 1 ) + + auto num_view = num.get_storage_view(); + auto quot_view = quot.get_storage_view(); + auto this_view = get_storage_view(); + + if ( denom_view.size() == 1 && num_view.size() == 1 ) { - quot = uint64_t( num.data()[0]/denom.data()[0] ); - rem = uint64_t( num.data()[0]%denom.data()[0] ); - quot.common_stor_.is_negative_ = quot_neg; - rem.common_stor_.is_negative_ = rem_neg; + quot = word_type( num_view[0]/denom_view[0] ); + rem = word_type( num_view[0]%denom_view[0] ); + quot.set_negative(quot_neg); + rem.set_negative(rem_neg); return; } - else if (denom.length() == 1 && (denom.data()[0] & l_mask) == 0 ) + if (denom_view.size() == 1 && (denom_view[0] & l_mask) == 0 ) { // Denominator fits into a half word - uint64_t divisor = denom.data()[0], dHi = 0, - q1, r, q2, dividend; - quot.resize(length()); - for (size_type i=length(); i-- > 0; ) + word_type divisor = denom_view[0], dHi = 0, q1, r, q2, dividend; + quot.resize(this_view.size()); + quot_view = quot.get_storage_view(); + for (size_type i=this_view.size(); i-- > 0; ) { - dividend = (dHi << basic_type_halfBits) | (data()[i] >> basic_type_halfBits); + dividend = (dHi << word_type_half_bits) | (this_view[i] >> word_type_half_bits); q1 = dividend/divisor; r = dividend % divisor; - dividend = (r << basic_type_halfBits) | (data()[i] & r_mask); + dividend = (r << word_type_half_bits) | (this_view[i] & r_mask); q2 = dividend/divisor; dHi = dividend % divisor; - quot.data()[i] = (q1 << basic_type_halfBits) | q2; + quot_view[i] = (q1 << word_type_half_bits) | q2; } quot.reduce(); rem = dHi; - quot.common_stor_.is_negative_ = quot_neg; - rem.common_stor_.is_negative_ = rem_neg; + quot.set_negative(quot_neg); + rem.set_negative(rem_neg); return; } - basic_bigint num0 = num, denom0 = denom; - int second_done = normalize(denom, num, x); - size_type l = denom.length() - 1; - size_type n = num.length() - 1; + basic_bigint num0(num, get_allocator()); + basic_bigint denom0(denom, get_allocator()); + int x = 0; + bool second_done = normalize(denom, num, x); + denom_view = denom.get_storage_view(); + num_view = num.get_storage_view(); + + size_type l = denom_view.size() - 1; + size_type n = num_view.size() - 1; quot.resize(n - l); - for (size_type i=quot.length(); i-- > 0; ) - quot.data()[i] = 0; + quot_view = quot.get_storage_view(); + for (size_type i = quot_view.size(); i-- > 0; ) + { + quot_view[i] = 0; + } rem = num; - if ( rem.data()[n] >= denom.data()[l] ) + auto rem_view = rem.get_storage_view(); + if ( rem_view[n] >= denom_view[l] ) { - rem.resize(rem.length() + 1); + rem.resize(rem_view.size() + 1); + rem_view = rem.get_storage_view(); n++; - quot.resize(quot.length() + 1); + quot.resize(quot_view.size() + 1); + quot_view = quot.get_storage_view(); } - uint64_t d = denom.data()[l]; + word_type d = denom_view[l]; + for ( size_type k = n; k > l; k-- ) { - uint64_t q = DDquotient(rem.data()[k], rem.data()[k-1], d); - subtractmul( rem.data() + k - l - 1, denom.data(), l + 1, q ); - quot.data()[k - l - 1] = q; + word_type q = DDquotient(rem_view[k], rem_view[k-1], d); + subtractmul( rem_view.data() + (k - l - 1), denom_view.data(), l + 1, q ); + quot_view[k - l - 1] = q; } quot.reduce(); - quot.common_stor_.is_negative_ = quot_neg; - if ( remDesired ) + quot.set_negative(quot_neg); + if (remDesired) { unnormalize(rem, x, second_done); - rem.common_stor_.is_negative_ = rem_neg; + rem.set_negative(rem_neg); } } private: + void destroy() noexcept { - if (is_dynamic()) - { - dynamic_stor_.destroy(get_allocator()); - } + storage_.destroy(); } - void DDproduct( uint64_t A, uint64_t B, - uint64_t& hi, uint64_t& lo ) const + void DDproduct( word_type A, word_type B, + word_type& hi, word_type& lo ) const // Multiplying two digits: (hi, lo) = A * B { - uint64_t hiA = A >> basic_type_halfBits, loA = A & r_mask, - hiB = B >> basic_type_halfBits, loB = B & r_mask, - mid1, mid2, old; + word_type hiA = A >> word_type_half_bits, loA = A & r_mask, + hiB = B >> word_type_half_bits, loB = B & r_mask; lo = loA * loB; hi = hiA * hiB; - mid1 = loA * hiB; - mid2 = hiA * loB; - old = lo; - lo += mid1 << basic_type_halfBits; - hi += (lo < old) + (mid1 >> basic_type_halfBits); + word_type mid1 = loA * hiB; + word_type mid2 = hiA * loB; + word_type old = lo; + lo += mid1 << word_type_half_bits; + hi += (lo < old) + (mid1 >> word_type_half_bits); old = lo; - lo += mid2 << basic_type_halfBits; - hi += (lo < old) + (mid2 >> basic_type_halfBits); + lo += mid2 << word_type_half_bits; + hi += (lo < old) + (mid2 >> word_type_half_bits); } - uint64_t DDquotient( uint64_t A, uint64_t B, uint64_t d ) const + word_type DDquotient( word_type A, word_type B, word_type d ) const // Divide double word (A, B) by d. Quotient = (qHi, qLo) { - uint64_t left, middle, right, qHi, qLo, x, dLo1, - dHi = d >> basic_type_halfBits, dLo = d & r_mask; + word_type left, middle, right, qHi, qLo, x, dLo1, + dHi = d >> word_type_half_bits, dLo = d & r_mask; qHi = A/(dHi + 1); // This initial guess of qHi may be too small. middle = qHi * dLo; left = qHi * dHi; - x = B - (middle << basic_type_halfBits); - A -= (middle >> basic_type_halfBits) + left + (x > B); + x = B - (middle << word_type_half_bits); + A -= (middle >> word_type_half_bits) + left + (x > B); B = x; - dLo1 = dLo << basic_type_halfBits; + dLo1 = dLo << word_type_half_bits; // Increase qHi if necessary: while ( A > dHi || (A == dHi && B >= dLo1) ) { @@ -1511,15 +1743,15 @@ class basic_bigint : protected detail::basic_bigint_base B = x; qHi++; } - qLo = ((A << basic_type_halfBits) | (B >> basic_type_halfBits))/(dHi + 1); + qLo = ((A << word_type_half_bits) | (B >> word_type_half_bits))/(dHi + 1); // This initial guess of qLo may be too small. right = qLo * dLo; middle = qLo * dHi; x = B - right; A -= (x > B); B = x; - x = B - (middle << basic_type_halfBits); - A -= (middle >> basic_type_halfBits) + (x > B); + x = B - (middle << word_type_half_bits); + A -= (middle >> word_type_half_bits) + (x > B); B = x; // Increase qLo if necessary: while ( A || B >= d ) @@ -1529,13 +1761,13 @@ class basic_bigint : protected detail::basic_bigint_base B = x; qLo++; } - return (qHi << basic_type_halfBits) + qLo; + return (qHi << word_type_half_bits) + qLo; } - void subtractmul( uint64_t* a, uint64_t* b, size_type n, uint64_t& q ) const + void subtractmul( word_type* a, word_type* b, size_type n, word_type& q ) const // a -= q * b: b in n positions; correct q if necessary { - uint64_t hi, lo, d, carry = 0; + word_type hi, lo, d, carry = 0; size_type i; for ( i = 0; i < n; i++ ) { @@ -1564,10 +1796,15 @@ class basic_bigint : protected detail::basic_bigint_base } } - int normalize( basic_bigint& denom, basic_bigint& num, int& x ) const + bool normalize(basic_bigint& denom, basic_bigint& num, int& x) const { - size_type r = denom.length() - 1; - uint64_t y = denom.data()[r]; + auto denom_view = denom.get_storage_view(); + if (denom_view.size() == 0) + { + return false; + } + size_type r = denom_view.size() - 1; + word_type y = denom_view[r]; x = 0; while ( (y & l_bit) == 0 ) @@ -1577,20 +1814,22 @@ class basic_bigint : protected detail::basic_bigint_base } denom <<= x; num <<= x; - if ( r > 0 && denom.data()[r] < denom.data()[r-1] ) + + denom_view = denom.get_storage_view(); + if ( r > 0 && denom_view[r] < denom_view[r-1] ) { - denom *= max_basic_type; - num *= max_basic_type; - return 1; + denom *= max_word; + num *= max_word; + return true; } - return 0; + return false; } - void unnormalize( basic_bigint& rem, int x, int secondDone ) const + void unnormalize(basic_bigint& rem, int x, bool secondDone) const { - if ( secondDone ) + if (secondDone) { - rem /= max_basic_type; + rem /= max_word; } if ( x > 0 ) { @@ -1602,31 +1841,12 @@ class basic_bigint : protected detail::basic_bigint_base } } - size_type round_up(size_type i) const // Find suitable new block size - { - return (i/word_length + 1) * word_length; - } - void reduce() { - uint64_t* p = end() - 1; - uint64_t* pBegin = begin(); - while ( p >= pBegin ) - { - if ( *p ) - { - break; - } - --common_stor_.length_; - --p; - } - if ( length() == 0 ) - { - common_stor_.is_negative_ = false; - } + storage_.reduce(); } - static uint64_t next_power_of_two(uint64_t n) { + static word_type next_power_of_two(word_type n) { n = n - 1; n |= n >> 1u; n |= n >> 2u; @@ -1636,9 +1856,231 @@ class basic_bigint : protected detail::basic_bigint_base n |= n >> 32u; return n + 1; } + + template + friend to_bigint_result to_bigint(const CharT* s, basic_bigint& val, int radix) + { + return to_bigint(s, std::char_traits::length(s), val, radix); + } + + template + friend to_bigint_result to_bigint(const CharT* s, size_type length, basic_bigint& val, int radix) + { + if (!(radix >= 2 && radix <= 16)) + { + JSONCONS_THROW(std::runtime_error("Unsupported radix")); + } + + const CharT* cur = s; + const CharT* last = s + length; + + bool neg; + if (*cur == '-') + { + neg = true; + cur++; + } + else + { + neg = false; + } + + while (cur < last) + { + CharT c = *cur; + word_type d; + switch (c) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + d = (word_type)(c - '0'); + break; + case 'a':case 'b':case 'c':case 'd':case 'e':case 'f': + d = (word_type)(c - ('a' - 10u)); + break; + case 'A':case 'B':case 'C':case 'D':case 'E':case 'F': + d = (word_type)(c - ('A' - 10u)); + break; + default: + return to_bigint_result(cur, std::errc::invalid_argument); + } + if ((int)d >= radix) + { + return to_bigint_result(cur, std::errc::invalid_argument); + } + val = (val * radix) + d; + ++cur; + } + + if ( neg ) + { + val.set_negative(true); + } + return to_bigint_result(cur, std::errc{}); + } }; -using bigint = basic_bigint>; +template +basic_bigint babs( const basic_bigint& a ) +{ + if ( a.is_negative()) + { + return -a; + } + return a; +} + +template +basic_bigint bpow(basic_bigint x, unsigned n) +{ + basic_bigint y = 1; + + while ( n ) + { + if ( n & 1 ) + { + y *= x; + } + x *= x; + n >>= 1; + } + + return y; +} + +template +basic_bigint bsqrt(const basic_bigint& a) +{ + basic_bigint x = a; + basic_bigint b = a; + basic_bigint q; + + b <<= 1; + while ( (void)(b >>= 2), b > 0 ) + { + x >>= 1; + } + while ( x > (q = a/x) + 1 || x < q - 1 ) + { + x += q; + x >>= 1; + } + return x < q ? x : q; +} + +namespace detail { + +template +to_bigint_result to_bigint(const CharT* data, std::size_t length, + bool neg, basic_bigint& value, const Allocator& alloc) +{ + if (JSONCONS_UNLIKELY(length == 0)) + { + return to_bigint_result(data, std::errc::invalid_argument); + } + + using word_type = typename basic_bigint::word_type; + + const CharT* last = data + length; + const CharT* p = data; + + while (p < last && *p == '0') + { + ++p; + } + if (p == last) + { + value = std::move(basic_bigint{0, alloc}); + return to_bigint_result(last, std::errc{}); + } + std::size_t num_digits = last - data; + std::size_t num_words; + if (length < 10) + { + num_words = 1; + } + else + { + std::size_t num_bits = (std::size_t)(((num_digits * detail::bits_per_digit[10]) >> 10) + 1); + num_words = (num_bits + 63) >> 6; + } + + basic_bigint v(0, alloc); + v.reserve(num_words); + for (std::size_t i = 0; i < length; i++) + { + CharT c = data[i]; + switch (c) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + v = (v * 10u) + (word_type)(c - '0'); + break; + default: + return to_bigint_result(data + i, std::errc::invalid_argument); + } + } + + //auto view = v.get_storage_view(); + //if (num_words != view.size()) + //{ + // std::cout << "Unexpected num_words! num_words: " << num_words << ", " << num_words << ", size: " << view.size() << "\n"; + //} + + if (neg) + { + v.set_negative(true); + } + + value = std::move(v); + return to_bigint_result(last, std::errc{}); +} + +} // namespace detail + +template +to_bigint_result to_bigint(const CharT* data, std::size_t length, + basic_bigint& value, const Allocator& alloc) +{ + if (JSONCONS_UNLIKELY(length == 0)) + { + return to_bigint_result(data, std::errc::invalid_argument); + } + + if (*data == '-') + { + return jsoncons::detail::to_bigint(data + 1, length - 1, true, value, alloc); + } + else + { + return jsoncons::detail::to_bigint(data, length, false, value, alloc); + } +} + +template +to_bigint_result to_bigint(const CharT* s, basic_bigint>& value) +{ + return to_bigint(s, std::char_traits::length(s), value); +} + +template +to_bigint_result to_bigint(const CharT* data, std::size_t length, + basic_bigint>& value) +{ + if (JSONCONS_UNLIKELY(length == 0)) + { + return to_bigint_result(data, std::errc::invalid_argument); + } + + if (*data == '-') + { + return jsoncons::detail::to_bigint(data+1, length-1, true, value, std::allocator{}); + } + else + { + return jsoncons::detail::to_bigint(data, length, false, value, std::allocator{}); + } +} + +using bigint = basic_bigint>; } // namespace jsoncons diff --git a/include/jsoncons/utility/byte_string.hpp b/include/jsoncons/utility/byte_string.hpp index 00b0dc5..adb2f1b 100644 --- a/include/jsoncons/utility/byte_string.hpp +++ b/include/jsoncons/utility/byte_string.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -27,13 +27,20 @@ namespace jsoncons { + template + struct to_bytes_result + { + InputIt it; + conv_errc ec; + }; + // Algorithms namespace detail { template typename std::enable_if::value_type,uint8_t>::value,size_t>::type - encode_base64_generic(InputIt first, InputIt last, const char alphabet[65], Container& result) + bytes_to_base64_generic(InputIt first, InputIt last, const char alphabet[65], Container& result) { std::size_t count = 0; unsigned char a3[3]; @@ -92,8 +99,8 @@ namespace detail { } template - typename std::enable_if::value,decode_result>::type - decode_base64_generic(InputIt first, InputIt last, + typename std::enable_if::value,to_bytes_result>::type + base64_to_bytes_generic(InputIt first, InputIt last, const uint8_t reverse_alphabet[256], F f, Container& result) @@ -106,7 +113,7 @@ namespace detail { { if (!f(*first)) { - return decode_result{first, conv_errc::conversion_failed}; + return to_bytes_result{first, conv_errc::conversion_failed}; } a4[i++] = static_cast(*first++); @@ -144,14 +151,14 @@ namespace detail { result.push_back(a3[j]); } } - return decode_result{last, conv_errc::success}; + return to_bytes_result{last, conv_errc::success}; } } // namespace detail template typename std::enable_if::value_type,uint8_t>::value,size_t>::type - encode_base16(InputIt first, InputIt last, Container& result) + bytes_to_base16(InputIt first, InputIt last, Container& result) { static constexpr char characters[] = "0123456789ABCDEF"; @@ -166,24 +173,24 @@ namespace detail { template typename std::enable_if::value_type,uint8_t>::value,size_t>::type - encode_base64url(InputIt first, InputIt last, Container& result) + bytes_to_base64url(InputIt first, InputIt last, Container& result) { static constexpr char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789-_" "\0"; - return detail::encode_base64_generic(first, last, alphabet, result); + return detail::bytes_to_base64_generic(first, last, alphabet, result); } template typename std::enable_if::value_type,uint8_t>::value,size_t>::type - encode_base64(InputIt first, InputIt last, Container& result) + bytes_to_base64(InputIt first, InputIt last, Container& result) { static constexpr char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789+/" "="; - return detail::encode_base64_generic(first, last, alphabet, result); + return detail::bytes_to_base64_generic(first, last, alphabet, result); } template @@ -207,8 +214,8 @@ namespace detail { // decode template - typename std::enable_if::value,decode_result>::type - decode_base64url(InputIt first, InputIt last, Container& result) + typename std::enable_if::value,to_bytes_result>::type + base64url_to_bytes(InputIt first, InputIt last, Container& result) { static constexpr uint8_t reverse_alphabet[256] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, @@ -228,15 +235,15 @@ namespace detail { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - auto retval = jsoncons::detail::decode_base64_generic(first, last, reverse_alphabet, + auto retval = jsoncons::detail::base64_to_bytes_generic(first, last, reverse_alphabet, is_base64url::value_type>, result); - return retval.ec == conv_errc::success ? retval : decode_result{retval.it, conv_errc::not_base64url}; + return retval.ec == conv_errc::success ? retval : to_bytes_result{retval.it, conv_errc::not_base64url}; } template - typename std::enable_if::value,decode_result>::type - decode_base64(InputIt first, InputIt last, Container& result) + typename std::enable_if::value,to_bytes_result>::type + base64_to_bytes(InputIt first, InputIt last, Container& result) { static constexpr uint8_t reverse_alphabet[256] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, @@ -256,20 +263,20 @@ namespace detail { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - auto retval = jsoncons::detail::decode_base64_generic(first, last, reverse_alphabet, + auto retval = jsoncons::detail::base64_to_bytes_generic(first, last, reverse_alphabet, is_base64::value_type>, result); - return retval.ec == conv_errc::success ? retval : decode_result{retval.it, conv_errc::not_base64}; + return retval.ec == conv_errc::success ? retval : to_bytes_result{retval.it, conv_errc::not_base64}; } template - typename std::enable_if::value,decode_result>::type - decode_base16(InputIt first, InputIt last, Container& result) + typename std::enable_if::value,to_bytes_result>::type + base16_to_bytes(InputIt first, InputIt last, Container& result) { std::size_t len = std::distance(first,last); if (len & 1) { - return decode_result{first, conv_errc::not_base16}; + return to_bytes_result{first, conv_errc::not_base16}; } InputIt it = first; @@ -287,7 +294,7 @@ namespace detail { } else { - return decode_result{first, conv_errc::not_base16}; + return to_bytes_result{first, conv_errc::not_base16}; } auto b = *it++; @@ -301,12 +308,12 @@ namespace detail { } else { - return decode_result{first, conv_errc::not_base16}; + return to_bytes_result{first, conv_errc::not_base16}; } result.push_back(val); } - return decode_result{last, conv_errc::success}; + return to_bytes_result{last, conv_errc::success}; } struct byte_traits diff --git a/include/jsoncons/utility/conversion.hpp b/include/jsoncons/utility/conversion.hpp new file mode 100644 index 0000000..744db36 --- /dev/null +++ b/include/jsoncons/utility/conversion.hpp @@ -0,0 +1,61 @@ +// Copyright 2013-2026 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_UTILITY_CONVERSION_HPP +#define JSONCONS_UTILITY_CONVERSION_HPP + +#include +#include +#include // std::error_code + +#include +#include +#include +#include +#include +#include +#include +#include // from_integer + +namespace jsoncons { + +template +typename std::enable_if::value_type,uint8_t>::value + && ext_traits::is_string::value,size_t>::type +bytes_to_string(InputIt first, InputIt last, semantic_tag tag, Container& str) +{ + switch (tag) + { + case semantic_tag::base64: + return bytes_to_base64(first, last, str); + case semantic_tag::base16: + return bytes_to_base16(first, last, str); + default: + return bytes_to_base64url(first, last, str); + } +} + +template +typename std::enable_if::value,to_bytes_result>::type +string_to_bytes(InputIt first, InputIt last, semantic_tag tag, Container& bytes) +{ + switch (tag) + { + case semantic_tag::base16: + return base16_to_bytes(first, last, bytes); + case semantic_tag::base64: + return base64_to_bytes(first, last, bytes); + case semantic_tag::base64url: + return base64url_to_bytes(first, last, bytes); + default: + return to_bytes_result{first, conv_errc::conversion_failed}; + } +} + +} // namespace jsoncons + +#endif // JSONCONS_UTILITY_CONVERSION_HPP + diff --git a/include/jsoncons/utility/heap_string.hpp b/include/jsoncons/utility/heap_string.hpp index 1efafd8..aa98819 100644 --- a/include/jsoncons/utility/heap_string.hpp +++ b/include/jsoncons/utility/heap_string.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -16,186 +16,173 @@ #include namespace jsoncons { -namespace utility { +namespace heap { + +inline char* +align_up(char* ptr, std::size_t alignment) noexcept +{ + return reinterpret_cast(~(alignment - 1) & + (reinterpret_cast(ptr) + alignment - 1)); +} + +template +struct heap_string_base +{ + Extra extra_; + Allocator alloc_; + + Allocator& get_allocator() + { + return alloc_; + } - inline char* - align_up(char* ptr, std::size_t alignment) noexcept + const Allocator& get_allocator() const { - return reinterpret_cast(~(alignment - 1) & - (reinterpret_cast(ptr) + alignment - 1)); + return alloc_; } - template - struct heap_string_base + heap_string_base(const Extra& extra, const Allocator& alloc) + : extra_(extra), alloc_(alloc) { - Extra extra_; - Allocator alloc_; + } - Allocator& get_allocator() - { - return alloc_; - } + ~heap_string_base() = default; +}; - const Allocator& get_allocator() const - { - return alloc_; - } +template +struct heap_string : public heap_string_base +{ + using char_type = CharT; + using allocator_type = typename std::allocator_traits::template rebind_alloc; + using allocator_traits_type = std::allocator_traits; + using pointer = typename allocator_traits_type::pointer; - heap_string_base(const Extra& extra, const Allocator& alloc) - : extra_(extra), alloc_(alloc) - { - } + pointer p_{nullptr}; + std::size_t length_{0}; + uint8_t offset_{0}; + uint8_t align_pad_{0}; - ~heap_string_base() = default; - }; + heap_string(const heap_string&) = delete; + heap_string(heap_string&&) = delete; - template - struct heap_string : public heap_string_base + heap_string(Extra extra, const Allocator& alloc) + : heap_string_base(extra, alloc) { - using char_type = CharT; - using allocator_type = typename std::allocator_traits::template rebind_alloc; - using allocator_traits_type = std::allocator_traits; - using pointer = typename allocator_traits_type::pointer; + } - pointer p_{nullptr}; - std::size_t length_{0}; - uint8_t offset_{0}; - uint8_t align_pad_{0}; + ~heap_string() = default; - heap_string(const heap_string&) = delete; - heap_string(heap_string&&) = delete; + const char_type* c_str() const { return ext_traits::to_plain_pointer(p_); } + const char_type* data() const { return ext_traits::to_plain_pointer(p_); } + std::size_t length() const { return length_; } + Extra extra() const { return this->extra_; } - heap_string(Extra extra, const Allocator& alloc) - : heap_string_base(extra, alloc) - { - } + heap_string& operator=(const heap_string&) = delete; + heap_string& operator=(heap_string&&) = delete; - ~heap_string() = default; +}; - const char_type* c_str() const { return ext_traits::to_plain_pointer(p_); } - const char_type* data() const { return ext_traits::to_plain_pointer(p_); } - std::size_t length() const { return length_; } - Extra extra() const { return this->extra_; } +template +struct jsoncons_aligned_storage +{ + struct type + { + alignas(Align) unsigned char data[Len]; + }; +}; - heap_string& operator=(const heap_string&) = delete; - heap_string& operator=(heap_string&&) = delete; +// heap_string_factory - }; +template +class heap_string_factory +{ +public: + using char_type = CharT; + using heap_string_type = heap_string; +private: + + using byte_allocator_type = typename std::allocator_traits::template rebind_alloc; + using byte_pointer = typename std::allocator_traits::pointer; - template - struct jsoncons_aligned_storage + using heap_string_allocator_type = typename std::allocator_traits::template rebind_alloc; +public: + using pointer = typename std::allocator_traits::pointer; + + struct storage_t { - struct type - { - alignas(Align) unsigned char data[Len]; - }; + heap_string_type data; + char_type c[1]; }; + typedef typename jsoncons_aligned_storage::type storage_type; - // From boost 1_71 - template - T launder_cast(U* u) + static size_t aligned_size(std::size_t n) { - #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606 - return std::launder(reinterpret_cast(u)); - #elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) > 800 - return __builtin_launder(reinterpret_cast(u)); - #else - return reinterpret_cast(u); - #endif + return sizeof(storage_type) + n; } - // heap_string_factory +public: - template - class heap_string_factory + static pointer create(const char_type* s, std::size_t length, Extra extra, const Allocator& alloc) { - public: - using char_type = CharT; - using heap_string_type = heap_string; - private: - - using byte_allocator_type = typename std::allocator_traits::template rebind_alloc; - using byte_pointer = typename std::allocator_traits::pointer; - - using heap_string_allocator_type = typename std::allocator_traits::template rebind_alloc; - public: - using pointer = typename std::allocator_traits::pointer; - - struct storage_t - { - heap_string_type data; - char_type c[1]; - }; - typedef typename jsoncons_aligned_storage::type storage_type; - - static size_t aligned_size(std::size_t n) - { - return sizeof(storage_type) + n; - } - - public: - - static pointer create(const char_type* s, std::size_t length, Extra extra, const Allocator& alloc) - { - std::size_t len = aligned_size(length*sizeof(char_type)); - - std::size_t align = alignof(storage_type); - char* q = nullptr; - char* storage = nullptr; - byte_allocator_type byte_alloc(alloc); - uint8_t align_pad = 0; - - if (align <= 8) { - byte_pointer ptr = byte_alloc.allocate(len); - q = ext_traits::to_plain_pointer(ptr); - - if (reinterpret_cast(q) % align == 0) { - storage = q; - } else { - byte_alloc.deallocate(ptr, len); - } + std::size_t len = aligned_size(length*sizeof(char_type)); + + std::size_t align = alignof(storage_type); + char* q = nullptr; + char* storage = nullptr; + byte_allocator_type byte_alloc(alloc); + uint8_t align_pad = 0; + + if (align <= 8) { + byte_pointer ptr = byte_alloc.allocate(len); + q = ext_traits::to_plain_pointer(ptr); + + if (reinterpret_cast(q) % align == 0) { + storage = q; + } else { + byte_alloc.deallocate(ptr, len); } + } - if (storage == nullptr) { - align_pad = uint8_t(align-1); - byte_pointer ptr = byte_alloc.allocate(align_pad+len); - q = ext_traits::to_plain_pointer(ptr); - storage = align_up(q, align); - JSONCONS_ASSERT(storage >= q); - } + if (storage == nullptr) { + align_pad = uint8_t(align-1); + byte_pointer ptr = byte_alloc.allocate(align_pad+len); + q = ext_traits::to_plain_pointer(ptr); + storage = align_up(q, align); + JSONCONS_ASSERT(storage >= q); + } - heap_string_type* ps = new(storage)heap_string_type(extra, byte_alloc); + heap_string_type* ps = new(storage)heap_string_type(extra, byte_alloc); - auto psa = launder_cast(storage); + auto psa = launder_cast(storage); - CharT* p = new(&psa->c)char_type[length + 1]; - std::memcpy(p, s, length*sizeof(char_type)); - p[length] = 0; - ps->p_ = std::pointer_traits::pointer_to(*p); - ps->length_ = length; - ps->offset_ = (uint8_t)(storage - q); - ps->align_pad_ = align_pad; - return std::pointer_traits::pointer_to(*ps); - } + CharT* p = new(&psa->c)char_type[length + 1]; + std::memcpy(p, s, length*sizeof(char_type)); + p[length] = 0; + ps->p_ = std::pointer_traits::pointer_to(*p); + ps->length_ = length; + ps->offset_ = (uint8_t)(storage - q); + ps->align_pad_ = align_pad; + return std::pointer_traits::pointer_to(*ps); + } - static void destroy(pointer ptr) + static void destroy(pointer ptr) + { + if (ptr != nullptr) { - if (ptr != nullptr) - { - heap_string_type* rawp = ext_traits::to_plain_pointer(ptr); + heap_string_type* rawp = ext_traits::to_plain_pointer(ptr); - char* q = launder_cast(rawp); + char* q = launder_cast(rawp); - char* p = q - ptr->offset_; + char* p = q - ptr->offset_; - std::size_t mem_size = ptr->align_pad_ + aligned_size(ptr->length_*sizeof(char_type)); - byte_allocator_type byte_alloc(ptr->get_allocator()); - byte_alloc.deallocate(p,mem_size + ptr->offset_); - } + std::size_t mem_size = ptr->align_pad_ + aligned_size(ptr->length_*sizeof(char_type)); + byte_allocator_type byte_alloc(ptr->get_allocator()); + byte_alloc.deallocate(p,mem_size); } - }; + } +}; -} // namespace utility +} // namespace heap } // namespace jsoncons #endif // JSONCONS_UTILITY_HEAP_STRING_HPP diff --git a/include/jsoncons/utility/more_type_traits.hpp b/include/jsoncons/utility/more_type_traits.hpp index 0d69ea7..84e0324 100644 --- a/include/jsoncons/utility/more_type_traits.hpp +++ b/include/jsoncons/utility/more_type_traits.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -27,6 +27,12 @@ namespace jsoncons { namespace ext_traits { + + template + struct is_std_pair : public std::false_type {}; + + template + struct is_std_pair > : public std::true_type {}; // is_char8 template @@ -90,7 +96,8 @@ namespace ext_traits { static constexpr bool is_signed = std::numeric_limits::is_signed; static constexpr int digits = std::numeric_limits::digits; static constexpr std::size_t buffer_size = static_cast(sizeof(T)*CHAR_BIT*0.302) + 3; - + static constexpr int digits10 = std::numeric_limits::digits10; + static constexpr T(max)() noexcept { return (std::numeric_limits::max)(); @@ -113,6 +120,7 @@ namespace ext_traits { static constexpr bool is_signed = true; static constexpr int digits = sizeof(T)*CHAR_BIT - 1; static constexpr std::size_t buffer_size = (sizeof(T)*CHAR_BIT*0.302) + 3; + static constexpr int digits10 = 38; static constexpr T(max)() noexcept { @@ -135,6 +143,7 @@ namespace ext_traits { static constexpr bool is_specialized = true; static constexpr bool is_signed = false; static constexpr int digits = sizeof(T)*CHAR_BIT; + static constexpr int digits10 = 38; static constexpr T(max)() noexcept { @@ -450,6 +459,26 @@ namespace ext_traits { using container_push_back_t = decltype(std::declval().push_back(std::declval())); + template + using + optional_has_value_t = decltype(std::declval().has_value()); + + template + using + value_type_t = typename T::value_type; + + template + using + optional_value_t = decltype(std::declval().value()); + + template + using + optional_operator_star_t = decltype(std::declval().operator *()); + + template + using + optional_operator_bool_t = decltype(std::declval().operator bool()); + template using container_push_front_t = decltype(std::declval().push_front(std::declval())); @@ -535,21 +564,6 @@ namespace ext_traits { template struct is_std_array> : std::true_type {}; - // is_array_like - - template - struct is_array_like : std::false_type {}; - - template - struct is_array_like::value && - is_detected::value && - !is_std_array::value && - !is_detected_exact::value && - !is_map_like::value - >::type> - : std::true_type {}; - // is_constructible_from_const_pointer_and_size template @@ -573,6 +587,32 @@ namespace ext_traits { using is_back_insertable = is_detected; + template + using + has_has_value = is_detected_exact; + + template + using + has_value_type = is_detected; + + template + using + has_value = is_detected; + + template + using + has_operator_star = is_detected; + + template + using + has_operator_bool = is_detected_exact; + + template + struct is_optional + { + static constexpr bool value = has_value_type::value && has_has_value::value && has_value::value && has_operator_bool::value && has_operator_star::value; + }; + // is_front_insertable template @@ -609,6 +649,33 @@ namespace ext_traits { static constexpr bool value = has_data::value && has_size::value; }; + // is_array_like + + template + struct is_array_like : std::false_type {}; + + template + struct is_array_like::value && + is_detected::value && + !is_std_array::value && + !is_detected_exact::value && + !is_map_like::value + >::type> + : std::true_type {}; + + template + struct is_array_like_with_size + { + static constexpr bool value = is_array_like::value && has_size::value; + }; + + template + struct is_array_like_without_size + { + static constexpr bool value = is_array_like::value && !has_size::value; + }; + // is_byte_sequence template @@ -707,8 +774,8 @@ namespace impl { std::is_same::type,int16_t>::value || std::is_same::type,int32_t>::value || std::is_same::type,int64_t>::value || - std::is_same::type,float_t>::value || - std::is_same::type,double_t>::value)>::type + std::is_same::type,float>::value || + std::is_same::type,double>::value)>::type > : std::true_type{}; } // namespace impl @@ -726,7 +793,7 @@ namespace impl { < Container, Element, typename std::enable_if::value>::type> - : std::is_convertible< typename std::remove_pointer().data() )>::type(*)[], Element(*)[]> + : std::is_convertible< typename std::remove_pointer().data())>::type(*)[], Element(*)[]> {}; template diff --git a/include/jsoncons/utility/read_number.hpp b/include/jsoncons/utility/read_number.hpp new file mode 100644 index 0000000..cbce50a --- /dev/null +++ b/include/jsoncons/utility/read_number.hpp @@ -0,0 +1,1037 @@ +// Copyright 2013-2026 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_UTILITY_READ_NUMBER_HPP +#define JSONCONS_UTILITY_READ_NUMBER_HPP + +#include +#include +#include +#include +#include +#include +#include +#include // std::enable_if +#include + +#include +#include +#include +#include + +namespace jsoncons { + +// Inspired by yyjson https://github.com/ibireme/yyjson + +// Digit: '0'. +JSONCONS_INLINE_CONSTEXPR uint8_t DIGIT_TYPE_ZERO = 1 << 0; + +// Digit: [1-9]. +JSONCONS_INLINE_CONSTEXPR uint8_t DIGIT_TYPE_NONZERO = 1 << 1; + +// Plus sign (positive): '+'. +JSONCONS_INLINE_CONSTEXPR uint8_t DIGIT_TYPE_POS = 1 << 2; + +// Minus sign (negative): '-'. +JSONCONS_INLINE_CONSTEXPR uint8_t DIGIT_TYPE_NEG = 1 << 3; + +// Decimal point: '.' +JSONCONS_INLINE_CONSTEXPR uint8_t DIGIT_TYPE_DOT = 1 << 4; + +// Exponent sign: 'e, 'E'. +JSONCONS_INLINE_CONSTEXPR uint8_t DIGIT_TYPE_EXP = 1 << 5; + +// Digit type table (generate with misc/make_tables.c) +JSONCONS_INLINE_CONSTEXPR uint8_t digi_table[256] = { + 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, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x04, 0x00, 0x08, 0x10, 0x00, + 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 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, 0x20, 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 +}; + +/** Match a character with specified type. */ +constexpr bool is_type(uint8_t d, uint8_t type) { + return (digi_table[d] & type) != 0; +} + +// Match a sign: '+', '-' +constexpr bool is_sign(char d) { + return is_type(static_cast(d), (uint8_t)(DIGIT_TYPE_POS | DIGIT_TYPE_NEG)); +} + +// Match a none zero digit: [1-9] +constexpr bool is_nonzero_digit(char d) { + return is_type(static_cast(d), (uint8_t)DIGIT_TYPE_NONZERO); +} + +// Match a digit: [0-9] +constexpr bool is_digit(char d) { + return is_type(static_cast(d), (uint8_t)(DIGIT_TYPE_ZERO | DIGIT_TYPE_NONZERO)); +} + +// Match an exponent sign: 'e', 'E'. +constexpr bool is_exp(char d) { + return is_type(static_cast(d), (uint8_t)DIGIT_TYPE_EXP); +} + +// Match a floating point indicator: '.', 'e', 'E'. +constexpr bool is_fp_indicator(char d) { + return is_type(static_cast(d), (uint8_t)(DIGIT_TYPE_DOT | DIGIT_TYPE_EXP)); +} + +// Match a digit or floating point indicator: [0-9], '.', 'e', 'E'. +constexpr bool is_digit_or_fp(char d) { + return is_type(static_cast(d), (uint8_t)(DIGIT_TYPE_ZERO | DIGIT_TYPE_NONZERO | + DIGIT_TYPE_DOT | DIGIT_TYPE_EXP)); +} +constexpr bool is_sign(wchar_t d) { + return d == '+' || d == '-'; +} + +// Match a none zero digit: [1-9] +constexpr bool is_nonzero_digit(wchar_t d) { + return d >= '1' && d <= '9'; +} + +// Match a digit: [0-9] +constexpr bool is_digit(wchar_t d) { + return d >= '0' && d <= '9'; +} + +// Match an exponent sign: 'e', 'E'. +constexpr bool is_exp(wchar_t d) { + return d == 'e' || d == 'E'; +} + +// Match a floating point indicator: '.', 'e', 'E'. +constexpr bool is_fp(wchar_t d) { + return d == '.' || d == 'e' || d == 'E'; +} + +// Match a digit or floating point indicator: [0-9], '.', 'e', 'E'. +constexpr bool is_digit_or_fp(wchar_t d) { + return is_digit(d) || is_fp(d); +} + +template +struct to_number_result +{ + const CharT* ptr; + std::errc ec; + constexpr to_number_result(const CharT* ptr_) + : ptr(ptr_), ec(std::errc{}) + { + } + constexpr to_number_result(const CharT* ptr_, std::errc ec_) + : ptr(ptr_), ec(ec_) + { + } + + to_number_result(const to_number_result&) = default; + + to_number_result& operator=(const to_number_result&) = default; + + constexpr explicit operator bool() const noexcept + { + return ec == std::errc{}; + } + std::error_code error_code() const + { + return make_error_code(ec); + } +}; + +enum class integer_chars_format : uint8_t {decimal=1,hex}; +enum class integer_chars_state {initial,minus,integer,binary,octal,decimal,base16}; + +template +bool is_base10(const CharT* s, std::size_t length) +{ + integer_chars_state state = integer_chars_state::initial; + + const CharT* end = s + length; + for (;s < end; ++s) + { + switch(state) + { + case integer_chars_state::initial: + { + switch(*s) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + state = integer_chars_state::decimal; + break; + case '-': + state = integer_chars_state::minus; + break; + default: + return false; + } + break; + } + case integer_chars_state::minus: + { + switch(*s) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + state = integer_chars_state::decimal; + break; + default: + return false; + } + break; + } + case integer_chars_state::decimal: + { + switch(*s) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + break; + default: + return false; + } + break; + } + default: + break; + } + } + return state == integer_chars_state::decimal ? true : false; +} + +template +bool is_base16(const CharT* s, std::size_t length) +{ + integer_chars_state state = integer_chars_state::initial; + + const CharT* end = s + length; + for (;s < end; ++s) + { + switch(state) + { + case integer_chars_state::initial: + { + switch(*s) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': // Must be base16 + case 'a':case 'b':case 'c':case 'd':case 'e':case 'f': + case 'A':case 'B':case 'C':case 'D':case 'E':case 'F': + state = integer_chars_state::base16; + break; + default: + return false; + } + break; + } + case integer_chars_state::base16: + { + switch(*s) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': // Must be base16 + case 'a':case 'b':case 'c':case 'd':case 'e':case 'f': + case 'A':case 'B':case 'C':case 'D':case 'E':case 'F': + state = integer_chars_state::base16; + break; + default: + return false; + } + break; + } + default: + break; + } + } + return state == integer_chars_state::base16 ? true : false; +} + +template +typename std::enable_if::is_specialized && !ext_traits::integer_limits::is_signed,to_number_result>::type +dec_to_integer(const CharT* s, std::size_t length, T& value) +{ + if (JSONCONS_UNLIKELY(length == 0)) + { + return to_number_result(s, std::errc::invalid_argument); + } + + static constexpr T max_value = (ext_traits::integer_limits::max)(); + static constexpr T max_value_div_10 = max_value / 10; + + T num = 0; + const CharT* cur = s; + const CharT* last = s + length; + static constexpr std::size_t digits10 = static_cast(ext_traits::integer_limits::digits10); + const std::size_t n = (std::min)(digits10, length); + const CharT* stop = s + n; + + while (cur < stop) + { + uint8_t d; + if (JSONCONS_LIKELY((d = static_cast(*cur - '0')) <= 9) ) + { + num = static_cast(d) + num*10; + } + else + { + return to_number_result(cur, std::errc::invalid_argument); + } + ++cur; + } + if (cur == last) + { + value = num; + return to_number_result(cur, std::errc{}); + } + if (cur+1 != last) + { + return to_number_result(cur, std::errc::result_out_of_range); + } + if (is_digit(*cur)) + { + if (num > max_value_div_10) + { + return to_number_result(cur, std::errc::result_out_of_range); + } + uint8_t d = static_cast(*cur - '0'); + num = num*10; + if (num > max_value - d) + { + return to_number_result(s, std::errc::result_out_of_range); + } + num += d; + ++cur; + value = num; + return to_number_result(cur, std::errc{}); + } + + return to_number_result(cur, std::errc::invalid_argument); +} + +template +typename std::enable_if::is_specialized && ext_traits::integer_limits::is_signed,to_number_result>::type +dec_to_integer(const CharT* s, std::size_t length, T& value) +{ + if (length == 0) + { + return to_number_result(s, std::errc::invalid_argument); + } + + bool sign = *s == '-'; + s += sign; + length -= sign; + + using U = typename ext_traits::make_unsigned::type; + + U num; + auto ru = dec_to_integer(s, length, num); + if (ru.ec != std::errc{}) + { + return to_number_result(ru.ptr, ru.ec); + } + if (sign) + { + if (num > static_cast(-((ext_traits::integer_limits::lowest)()+T(1))) + U(1)) + { + return to_number_result(ru.ptr, std::errc::result_out_of_range); + } + else + { + value = static_cast(U(0) - num); + return to_number_result(ru.ptr, std::errc{}); + } + } + else + { + if (num > static_cast((ext_traits::integer_limits::max)())) + { + return to_number_result(ru.ptr, std::errc::result_out_of_range); + } + else + { + value = static_cast(num); + return to_number_result(ru.ptr, std::errc{}); + } + } +} + +template +typename std::enable_if::is_specialized && !ext_traits::integer_limits::is_signed,to_number_result>::type +to_integer(const CharT* s, std::size_t length, T& n) +{ + n = 0; + + integer_chars_state state = integer_chars_state::initial; + + const CharT* end = s + length; + while (s < end) + { + switch(state) + { + case integer_chars_state::initial: + { + switch(*s) + { + case '0': + state = integer_chars_state::integer; // Could be binary, octal, hex + ++s; + break; + case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': // Must be decimal + state = integer_chars_state::decimal; + break; + default: + return to_number_result(s, std::errc::invalid_argument); + } + break; + } + case integer_chars_state::integer: + { + switch(*s) + { + case 'b':case 'B': + { + state = integer_chars_state::binary; + ++s; + break; + } + case 'x':case 'X': + { + state = integer_chars_state::base16; + ++s; + break; + } + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + { + state = integer_chars_state::octal; + break; + } + default: + return to_number_result(s, std::errc::invalid_argument); + } + break; + } + case integer_chars_state::binary: + { + static constexpr T max_value = (ext_traits::integer_limits::max)(); + static constexpr T max_value_div_2 = max_value / 2; + for (; s < end; ++s) + { + T x = 0; + switch(*s) + { + case '0':case '1': + x = static_cast(*s) - static_cast('0'); + break; + default: + return to_number_result(s, std::errc::invalid_argument); + } + if (n > max_value_div_2) + { + return to_number_result(s, std::errc::result_out_of_range); + } + n = n * 2; + if (n > max_value - x) + { + return to_number_result(s, std::errc::result_out_of_range); + } + n += x; + } + break; + } + case integer_chars_state::octal: + { + static constexpr T max_value = (ext_traits::integer_limits::max)(); + static constexpr T max_value_div_8 = max_value / 8; + for (; s < end; ++s) + { + T x = 0; + switch(*s) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7': + x = static_cast(*s) - static_cast('0'); + break; + default: + return to_number_result(s, std::errc::invalid_argument); + } + if (n > max_value_div_8) + { + return to_number_result(s, std::errc::result_out_of_range); + } + n = n * 8; + if (n > max_value - x) + { + return to_number_result(s, std::errc::result_out_of_range); + } + n += x; + } + break; + } + case integer_chars_state::decimal: + { + static constexpr T max_value = (ext_traits::integer_limits::max)(); + static constexpr T max_value_div_10 = max_value / 10; + for (; s < end; ++s) + { + T x = 0; + switch(*s) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + x = static_cast(*s) - static_cast('0'); + break; + default: + return to_number_result(s, std::errc::invalid_argument); + } + if (n > max_value_div_10) + { + return to_number_result(s, std::errc::result_out_of_range); + } + n = n * 10; + if (n > max_value - x) + { + return to_number_result(s, std::errc::result_out_of_range); + } + n += x; + } + break; + } + case integer_chars_state::base16: + { + static constexpr T max_value = (ext_traits::integer_limits::max)(); + static constexpr T max_value_div_16 = max_value / 16; + for (; s < end; ++s) + { + CharT c = *s; + T x = 0; + switch (c) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + x = c - '0'; + break; + case 'a':case 'b':case 'c':case 'd':case 'e':case 'f': + x = c - ('a' - 10); + break; + case 'A':case 'B':case 'C':case 'D':case 'E':case 'F': + x = c - ('A' - 10); + break; + default: + return to_number_result(s, std::errc::invalid_argument); + } + if (n > max_value_div_16) + { + return to_number_result(s, std::errc::result_out_of_range); + } + n = n * 16; + if (n > max_value - x) + { + return to_number_result(s, std::errc::result_out_of_range); + } + + n += x; + } + break; + } + default: + JSONCONS_UNREACHABLE(); + break; + } + } + return (state == integer_chars_state::initial) ? to_number_result(s, std::errc::invalid_argument) : to_number_result(s, std::errc{}); +} + +template +typename std::enable_if::is_specialized && ext_traits::integer_limits::is_signed,to_number_result>::type +to_integer(const CharT* s, std::size_t length, T& n) +{ + n = 0; + + if (length == 0) + { + return to_number_result(s, std::errc::invalid_argument); + } + + bool is_negative = *s == '-' ? true : false; + if (is_negative) + { + ++s; + --length; + } + + using U = typename ext_traits::make_unsigned::type; + + U u; + auto ru = to_integer(s, length, u); + if (ru.ec != std::errc{}) + { + return to_number_result(ru.ptr, ru.ec); + } + if (is_negative) + { + if (u > static_cast(-((ext_traits::integer_limits::lowest)()+T(1))) + U(1)) + { + return to_number_result(ru.ptr, std::errc::result_out_of_range); + } + else + { + n = static_cast(U(0) - u); + return to_number_result(ru.ptr, std::errc{}); + } + } + else + { + if (u > static_cast((ext_traits::integer_limits::max)())) + { + return to_number_result(ru.ptr, std::errc::result_out_of_range); + } + else + { + n = static_cast(u); + return to_number_result(ru.ptr, std::errc{}); + } + } +} + +template +typename std::enable_if::is_specialized,to_number_result>::type +to_integer(const CharT* s, T& n) +{ + return to_integer(s, std::char_traits::length(s), n); +} + +// hex_to_integer + +template +typename std::enable_if::is_specialized && ext_traits::integer_limits::is_signed,to_number_result>::type +hex_to_integer(const CharT* s, std::size_t length, T& n) +{ + static_assert(ext_traits::integer_limits::is_specialized, "Integer type not specialized"); + JSONCONS_ASSERT(length > 0); + + n = 0; + + const CharT* end = s + length; + if (*s == '-') + { + static constexpr T min_value = (ext_traits::integer_limits::lowest)(); + static constexpr T min_value_div_16 = min_value / 16; + ++s; + for (; s < end; ++s) + { + CharT c = *s; + T x = 0; + switch (c) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + x = c - '0'; + break; + case 'a':case 'b':case 'c':case 'd':case 'e':case 'f': + x = c - ('a' - 10); + break; + case 'A':case 'B':case 'C':case 'D':case 'E':case 'F': + x = c - ('A' - 10); + break; + default: + return to_number_result(s, std::errc::invalid_argument); + } + if (n < min_value_div_16) + { + return to_number_result(s, std::errc::result_out_of_range); + } + n = n * 16; + if (n < min_value + x) + { + return to_number_result(s, std::errc::result_out_of_range); + } + n -= x; + } + } + else + { + static constexpr T max_value = (ext_traits::integer_limits::max)(); + static constexpr T max_value_div_16 = max_value / 16; + for (; s < end; ++s) + { + CharT c = *s; + T x = 0; + switch (c) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + x = c - '0'; + break; + case 'a':case 'b':case 'c':case 'd':case 'e':case 'f': + x = c - ('a' - 10); + break; + case 'A':case 'B':case 'C':case 'D':case 'E':case 'F': + x = c - ('A' - 10); + break; + default: + return to_number_result(s, std::errc::invalid_argument); + } + if (n > max_value_div_16) + { + return to_number_result(s, std::errc::result_out_of_range); + } + n = n * 16; + if (n > max_value - x) + { + return to_number_result(s, std::errc::result_out_of_range); + } + + n += x; + } + } + + return to_number_result(s, std::errc{}); +} + +template +typename std::enable_if::is_specialized && !ext_traits::integer_limits::is_signed,to_number_result>::type +hex_to_integer(const CharT* s, std::size_t length, T& n) +{ + static_assert(ext_traits::integer_limits::is_specialized, "Integer type not specialized"); + JSONCONS_ASSERT(length > 0); + + n = 0; + const CharT* end = s + length; + + static constexpr T max_value = (ext_traits::integer_limits::max)(); + static constexpr T max_value_div_16 = max_value / 16; + for (; s < end; ++s) + { + CharT c = *s; + T x = *s; + switch (c) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + x = c - '0'; + break; + case 'a':case 'b':case 'c':case 'd':case 'e':case 'f': + x = c - ('a' - 10); + break; + case 'A':case 'B':case 'C':case 'D':case 'E':case 'F': + x = c - ('A' - 10); + break; + default: + return to_number_result(s, std::errc::invalid_argument); + } + if (n > max_value_div_16) + { + return to_number_result(s, std::errc::result_out_of_range); + } + n = n * 16; + if (n > max_value - x) + { + return to_number_result(s, std::errc::result_out_of_range); + } + + n += x; + } + + return to_number_result(s, std::errc{}); +} + +// decstr_to_double + +#if defined(JSONCONS_HAS_STD_FROM_CHARS) && JSONCONS_HAS_STD_FROM_CHARS + +inline to_number_result decstr_to_double(const char* s, std::size_t length, double& val) +{ + const char* last = s+length; + const auto res = std::from_chars(s, last, val); + if (JSONCONS_LIKELY(res.ec == std::errc())) + { + return to_number_result{res.ptr,res.ec}; + } + if (JSONCONS_UNLIKELY(res.ptr != last)) + { + return to_number_result{res.ptr,std::errc::invalid_argument}; + } + if (res.ec == std::errc::result_out_of_range) + { + bool negative = (s < last && *s == '-') ? true : false; + val = negative ? -HUGE_VAL : HUGE_VAL; + } + + return to_number_result{res.ptr,res.ec}; +} + +inline to_number_result decstr_to_double(const wchar_t* s, std::size_t length, double& val) +{ + std::string buf(length,'0'); + for (size_t i = 0; i < length; ++i) + { + buf[i] = static_cast(s[i]); + } + + const auto res = std::from_chars(buf.data(), buf.data()+length, val); + if (JSONCONS_UNLIKELY(res.ptr != (buf.data()+length))) + { + return to_number_result{s+(res.ptr-buf.data()),std::errc::invalid_argument}; + } + return to_number_result{s+length,res.ec}; +} + +#elif defined(JSONCONS_HAS_MSC_STRTOD_L) + +inline to_number_result decstr_to_double(const char* s, std::size_t length, double& val) +{ + static _locale_t locale = _create_locale(LC_NUMERIC, "C"); + + const char* cur = s+length; + char *end = nullptr; + val = _strtod_l(s, &end, locale); + if (JSONCONS_UNLIKELY(end != cur)) + { + return to_number_result{end,std::errc::invalid_argument}; + } + if (JSONCONS_UNLIKELY(val <= -HUGE_VAL || val >= HUGE_VAL)) + { + return to_number_result{end, std::errc::result_out_of_range}; + } + return to_number_result{end}; +} + +inline to_number_result decstr_to_double(const wchar_t* s, std::size_t length, double& val) +{ + static _locale_t locale = _create_locale(LC_NUMERIC, "C"); + + const wchar_t* cur = s+length; + wchar_t* end = nullptr; + val = _wcstod_l(s, &end, locale); + if (JSONCONS_UNLIKELY(end != cur)) + { + return to_number_result{end,std::errc::invalid_argument}; + } + if (JSONCONS_UNLIKELY(val <= -HUGE_VAL || val >= HUGE_VAL)) + { + return to_number_result{end, std::errc::result_out_of_range}; + } + return to_number_result{end}; +} + + +#elif defined(JSONCONS_HAS_STRTOLD_L) + +inline to_number_result decstr_to_double(const char* s, std::size_t length, double& val) +{ + locale_t locale = newlocale(LC_ALL_MASK, "C", (locale_t) 0); + + const char* cur = s+length; + char *end = nullptr; + val = strtod_l(s, &end, locale); + if (JSONCONS_UNLIKELY(end != cur)) + { + return to_number_result{end,std::errc::invalid_argument}; + } + if (JSONCONS_UNLIKELY(val <= -HUGE_VAL || val >= HUGE_VAL)) + { + return to_number_result{end, std::errc::result_out_of_range}; + } + return to_number_result{end}; +} + +inline to_number_result decstr_to_double(const wchar_t* s, std::size_t length, double& val) +{ + locale_t locale = newlocale(LC_ALL_MASK, "C", (locale_t) 0); + + const wchar_t* cur = s+length; + wchar_t *end = nullptr; + val = wcstod_l(s, &end, locale); + if (JSONCONS_UNLIKELY(end != cur)) + { + return to_number_result{end,std::errc::invalid_argument}; + } + if (JSONCONS_UNLIKELY(val <= -HUGE_VAL || val >= HUGE_VAL)) + { + return to_number_result{end, std::errc::result_out_of_range}; + } + return to_number_result{end}; +} + +#else + +inline to_number_result decstr_to_double(const char* s, std::size_t length, double& val) +{ + const char* cur = s+length; + char *end = nullptr; + val = strtod(s, &end); + const char* str_end = end; + if (JSONCONS_UNLIKELY(end < cur)) + { + if (*end == '.') + { + std::string buf{s, length}; + char* dot_ptr = &buf[0] + (cur - end - 1); + *dot_ptr = ','; + end = nullptr; + val = strtod(buf.c_str(), &end); + str_end = s + (end - &buf[0]); + } + if (JSONCONS_UNLIKELY(str_end != cur)) + { + return to_number_result{str_end,std::errc::invalid_argument}; + } + } + if (JSONCONS_UNLIKELY(val <= -HUGE_VAL || val >= HUGE_VAL)) + { + return to_number_result{str_end, std::errc::result_out_of_range}; + } + return to_number_result{str_end}; +} + +inline to_number_result decstr_to_double(const wchar_t* s, std::size_t length, double& val) +{ + const wchar_t* cur = s+length; + wchar_t *end = nullptr; + val = wcstod(s, &end); + const wchar_t* str_end = end; + if (JSONCONS_UNLIKELY(end < cur)) + { + if (*end == '.') + { + std::wstring buf{s, length}; + wchar_t* dot_ptr = &buf[0] + (cur - end - 1); + *dot_ptr = ','; + end = nullptr; + val = wcstod(buf.c_str(), &end); + str_end = s + (end-&buf[0]); + } + if (JSONCONS_UNLIKELY(str_end != cur)) + { + return to_number_result{str_end,std::errc::invalid_argument}; + } + } + if (JSONCONS_UNLIKELY(val <= -HUGE_VAL || val >= HUGE_VAL)) + { + return to_number_result{str_end, std::errc::result_out_of_range}; + } + return to_number_result{str_end}; +} + +inline to_number_result decstr_to_double(char* s, std::size_t length) +{ + char* cur = s+length; + char *end = nullptr; + double val = strtod(s, &end); + if (JSONCONS_UNLIKELY(end < cur)) + { + if (*end == '.') + { + char* dot_ptr = end; + *end = ','; + val = strtod(s, &end); + *dot_ptr = '.'; + } + if (JSONCONS_UNLIKELY(end != cur)) + { + return to_number_result{end,std::errc::invalid_argument}; + } + } + if (JSONCONS_UNLIKELY(val <= -HUGE_VAL || val >= HUGE_VAL)) + { + return to_number_result{end, std::errc::result_out_of_range}; + } + return to_number_result{end}; +} + +inline to_number_result decstr_to_double(wchar_t* s, std::size_t length) +{ + wchar_t* cur = s+length; + wchar_t *end = nullptr; + double val = wcstod(s, &end); + if (JSONCONS_UNLIKELY(end < cur)) + { + if (*end == '.') + { + wchar_t* dot_ptr = end; + *end = ','; + val = wcstod(s, &end); + *dot_ptr = '.'; + } + if (JSONCONS_UNLIKELY(end != cur)) + { + return to_number_result{end,std::errc::invalid_argument}; + } + } + if (JSONCONS_UNLIKELY(val <= -HUGE_VAL || val >= HUGE_VAL)) + { + return to_number_result{end, std::errc::result_out_of_range}; + } + return to_number_result{end}; +} + +#endif + +inline to_number_result hexstr_to_double(const char* s, std::size_t length, double& val) +{ + const char* cur = s+length; + char *end = nullptr; + val = strtod(s, &end); + const char* str_end = end; + if (JSONCONS_UNLIKELY(end < cur)) + { + if (*end == '.') + { + std::string buf{s, length}; + char* dot_ptr = &buf[0] + (cur - end - 1); + *dot_ptr = ','; + end = nullptr; + val = strtod(buf.c_str(), &end); + str_end = s + (end - &buf[0]); + } + if (JSONCONS_UNLIKELY(str_end != cur)) + { + return to_number_result{str_end,std::errc::invalid_argument}; + } + } + if (JSONCONS_UNLIKELY(val <= -HUGE_VAL || val >= HUGE_VAL)) + { + return to_number_result{str_end, std::errc::result_out_of_range}; + } + return to_number_result{str_end}; +} + +inline to_number_result hexstr_to_double(const wchar_t* s, std::size_t length, double& val) +{ + const wchar_t* cur = s+length; + wchar_t *end = nullptr; + val = wcstod(s, &end); + const wchar_t* str_end = end; + if (JSONCONS_UNLIKELY(end < cur)) + { + if (*end == '.') + { + std::wstring buf{s, length}; + wchar_t* dot_ptr = &buf[0] + (cur - end - 1); + *dot_ptr = ','; + end = nullptr; + val = wcstod(buf.c_str(), &end); + str_end = s + (end-&buf[0]); + } + if (JSONCONS_UNLIKELY(str_end != cur)) + { + return to_number_result{str_end,std::errc::invalid_argument}; + } + } + if (JSONCONS_UNLIKELY(val <= -HUGE_VAL || val >= HUGE_VAL)) + { + return to_number_result{str_end, std::errc::result_out_of_range}; + } + return to_number_result{str_end}; +} + +} // namespace jsoncons + +#endif // JSONCONS_UTILITY_READ_NUMBER_HPP diff --git a/include/jsoncons/utility/unicode_traits.hpp b/include/jsoncons/utility/unicode_traits.hpp index c4da67e..33c03ea 100644 --- a/include/jsoncons/utility/unicode_traits.hpp +++ b/include/jsoncons/utility/unicode_traits.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -25,7 +25,6 @@ #include #include -#include #include namespace jsoncons { diff --git a/include/jsoncons/utility/uri.hpp b/include/jsoncons/utility/uri.hpp index 7051a71..f7aefb2 100644 --- a/include/jsoncons/utility/uri.hpp +++ b/include/jsoncons/utility/uri.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include // std::string #include #include @@ -18,8 +18,8 @@ #include #include -#include -#include +#include +#include #include namespace jsoncons { @@ -162,7 +162,7 @@ namespace jsoncons { *this = parse(str, ec); if (JSONCONS_UNLIKELY(ec)) { - JSONCONS_THROW(std::system_error(ec)); + JSONCONS_THROW(std::system_error(ec, std::string(str))); } } @@ -213,7 +213,7 @@ namespace jsoncons { { if (!validate_port(port)) { - JSONCONS_THROW(std::system_error(uri_errc::invalid_port)); + JSONCONS_THROW(std::system_error(uri_errc::invalid_port, std::string(port))); } uri_string_.append(":"); @@ -710,7 +710,7 @@ namespace jsoncons { auto hex = encoded.substr(i + 1, 2); uint8_t n; - jsoncons::detail::hex_to_integer(hex.data(), hex.size(), n); + jsoncons::hex_to_integer(hex.data(), hex.size(), n); decoded.push_back((char)n); i += 3; } @@ -1419,7 +1419,7 @@ namespace jsoncons { { encoded.push_back('0'); } - jsoncons::detail::integer_to_hex((uint8_t)ch, encoded); + jsoncons::integer_to_hex((uint8_t)ch, encoded); } else if (escaped) { @@ -1452,7 +1452,7 @@ namespace jsoncons { if (!is_unreserved(ch) && !is_punct(ch)) { encoded.push_back('%'); - jsoncons::detail::integer_to_hex((uint8_t)ch, encoded); + jsoncons::integer_to_hex((uint8_t)ch, encoded); } else { @@ -1484,7 +1484,7 @@ namespace jsoncons { { encoded.push_back('0'); } - jsoncons::detail::integer_to_hex((uint8_t)ch, encoded); + jsoncons::integer_to_hex((uint8_t)ch, encoded); } else if (escaped) { @@ -1506,7 +1506,7 @@ namespace jsoncons { if (!is_unreserved(ch) && !is_punct(ch)) { encoded.push_back('%'); - jsoncons::detail::integer_to_hex((uint8_t)ch, encoded); + jsoncons::integer_to_hex((uint8_t)ch, encoded); } else { @@ -1534,7 +1534,7 @@ namespace jsoncons { { encoded.push_back('0'); } - jsoncons::detail::integer_to_hex((uint8_t)ch, encoded); + jsoncons::integer_to_hex((uint8_t)ch, encoded); } else if (escaped) { @@ -1556,7 +1556,7 @@ namespace jsoncons { if (!is_unreserved(ch) && !is_reserved(ch)) { encoded.push_back('%'); - jsoncons::detail::integer_to_hex((uint8_t)ch, encoded); + jsoncons::integer_to_hex((uint8_t)ch, encoded); } else { @@ -1667,7 +1667,7 @@ namespace jsoncons { static bool validate_port(string_view port) { uint16_t p; - auto result = jsoncons::detail::to_integer(port.data(), port.length(), p); + auto result = jsoncons::to_integer(port.data(), port.length(), p); return static_cast(result); } diff --git a/include/jsoncons/utility/write_number.hpp b/include/jsoncons/utility/write_number.hpp new file mode 100644 index 0000000..3452ace --- /dev/null +++ b/include/jsoncons/utility/write_number.hpp @@ -0,0 +1,581 @@ +// Copyright 2013-2026 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_UTILITY_WRITE_NUMBER_HPP +#define JSONCONS_UTILITY_WRITE_NUMBER_HPP + +#include +#include +#include +#include +#include // std::numeric_limits +#include +#include +#include // snprintf +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace jsoncons { + +inline +char to_hex_character(uint8_t c) +{ + return (char)((c < 10) ? ('0' + c) : ('A' - 10 + c)); +} + +// from_integer + +template +typename std::enable_if::value,std::size_t>::type +from_integer(Integer value, Result& result) +{ + using char_type = typename Result::value_type; + + char_type buf[255]; + char_type *p = buf; + const char_type* last = buf+255; + + bool is_negative = value < 0; + + if (value < 0) + { + do + { + *p++ = static_cast(48 - (value % 10)); + } + while ((value /= 10) && (p < last)); + } + else + { + + do + { + *p++ = static_cast(48 + value % 10); + } + while ((value /= 10) && (p < last)); + } + JSONCONS_ASSERT(p != last); + + std::size_t count = (p - buf); + if (is_negative) + { + result.push_back('-'); + ++count; + } + while (--p >= buf) + { + result.push_back(*p); + } + + return count; +} + +// integer_to_hex + +template +typename std::enable_if::value,std::size_t>::type +integer_to_hex(Integer value, Result& result) +{ + using char_type = typename Result::value_type; + + char_type buf[255]; + char_type *p = buf; + const char_type* last = buf+255; + + bool is_negative = value < 0; + + if (value < 0) + { + do + { + *p++ = to_hex_character(0-(value % 16)); + } + while ((value /= 16) && (p < last)); + } + else + { + + do + { + *p++ = to_hex_character(value % 16); + } + while ((value /= 16) && (p < last)); + } + JSONCONS_ASSERT(p != last); + + std::size_t count = (p - buf); + if (is_negative) + { + result.push_back('-'); + ++count; + } + while (--p >= buf) + { + result.push_back(*p); + } + + return count; +} + +// write_double + +// fast exponent +template +void fill_exponent(int K, Result& result) +{ + if (K < 0) + { + result.push_back('-'); + K = -K; + } + else + { + result.push_back('+'); // compatibility with sprintf + } + + if (K < 10) + { + result.push_back('0'); // compatibility with sprintf + result.push_back((char)('0' + K)); + } + else if (K < 100) + { + result.push_back((char)('0' + K / 10)); K %= 10; + result.push_back((char)('0' + K)); + } + else if (K < 1000) + { + result.push_back((char)('0' + K / 100)); K %= 100; + result.push_back((char)('0' + K / 10)); K %= 10; + result.push_back((char)('0' + K)); + } + else + { + jsoncons::from_integer(K, result); + } +} + +template +void prettify_string(const char *buffer, std::size_t length, int k, int min_exp, int max_exp, Result& result) +{ + int nb_digits = (int)length; + int offset; + /* v = buffer * 10^k + kk is such that 10^(kk-1) <= v < 10^kk + this way kk gives the position of the decimal point. + */ + int kk = nb_digits + k; + + if (nb_digits <= kk && kk <= max_exp) + { + /* the first digits are already in. Add some 0s and call it a day. */ + /* the max_exp is a personal choice. Only 16 digits could possibly be relevant. + * Basically we want to print 12340000000 rather than 1234.0e7 or 1.234e10 */ + for (int i = 0; i < nb_digits; ++i) + { + result.push_back(buffer[i]); + } + for (int i = nb_digits; i < kk; ++i) + { + result.push_back('0'); + } + result.push_back('.'); + result.push_back('0'); + } + else if (0 < kk && kk <= max_exp) + { + /* comma number. Just insert a '.' at the correct location. */ + for (int i = 0; i < kk; ++i) + { + result.push_back(buffer[i]); + } + result.push_back('.'); + for (int i = kk; i < nb_digits; ++i) + { + result.push_back(buffer[i]); + } + } + else if (min_exp < kk && kk <= 0) + { + offset = 2 - kk; + + result.push_back('0'); + result.push_back('.'); + for (int i = 2; i < offset; ++i) + result.push_back('0'); + for (int i = 0; i < nb_digits; ++i) + { + result.push_back(buffer[i]); + } + } + else if (nb_digits == 1) + { + result.push_back(buffer[0]); + result.push_back('e'); + fill_exponent(kk - 1, result); + } + else + { + result.push_back(buffer[0]); + result.push_back('.'); + for (int i = 1; i < nb_digits; ++i) + { + result.push_back(buffer[i]); + } + result.push_back('e'); + fill_exponent(kk - 1, result); + } +} + +template +void dump_buffer(const char *buffer, std::size_t length, char decimal_point, Result& result) +{ + const char *sbeg = buffer; + const char *send = sbeg + length; + + if (sbeg != send) + { + bool needs_dot = true; + for (const char* q = sbeg; q < send; ++q) + { + switch (*q) + { + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '+': + result.push_back(*q); + break; + case 'e': + case 'E': + result.push_back('e'); + needs_dot = false; + break; + default: + if (*q == decimal_point) + { + needs_dot = false; + result.push_back('.'); + } + break; + } + } + if (needs_dot) + { + result.push_back('.'); + result.push_back('0'); + } + } +} + +template +bool dtoa_scientific(double val, char decimal_point, Result& result) +{ + if (val == 0) + { + result.push_back('0'); + result.push_back('.'); + result.push_back('0'); + return true; + } + + char buffer[100]; + int precision = std::numeric_limits::digits10; + int length = snprintf(buffer, sizeof(buffer), "%1.*e", precision, val); + if (length < 0) + { + return false; + } + double x{0}; + auto res = decstr_to_double(buffer, length, x); + if (res.ec == std::errc::invalid_argument) + { + return false; + } + if (x != val) + { + const int precision2 = std::numeric_limits::max_digits10; + length = snprintf(buffer, sizeof(buffer), "%1.*e", precision2, val); + if (length < 0) + { + return false; + } + } + dump_buffer(buffer, static_cast(length), decimal_point, result); + return true; +} + +template +bool dtoa_general(double val, char decimal_point, Result& result, std::false_type) +{ + if (val == 0) + { + result.push_back('0'); + result.push_back('.'); + result.push_back('0'); + return true; + } + + char buffer[100]; + int precision = std::numeric_limits::digits10; + int length = snprintf(buffer, sizeof(buffer), "%1.*g", precision, val); + if (length < 0) + { + return false; + } + double x{0}; + auto res = decstr_to_double(buffer, length, x); + if (res.ec == std::errc::invalid_argument) + { + return false; + } + if (x != val) + { + const int precision2 = std::numeric_limits::max_digits10; + length = snprintf(buffer, sizeof(buffer), "%1.*g", precision2, val); + if (length < 0) + { + return false; + } + } + dump_buffer(buffer, length, decimal_point, result); + return true; +} + +template +bool dtoa_general(double v, char decimal_point, Result& result, std::true_type) +{ + if (v == 0) + { + result.push_back('0'); + result.push_back('.'); + result.push_back('0'); + return true; + } + + int length = 0; + int k; + + char buffer[100]; + + double u = std::signbit(v) ? -v : v; + if (jsoncons::detail::grisu3(u, buffer, &length, &k)) + { + if (std::signbit(v)) + { + result.push_back('-'); + } + // min exp: -4 is consistent with sprintf + // max exp: std::numeric_limits::max_digits10 + jsoncons::prettify_string(buffer, length, k, -4, std::numeric_limits::max_digits10, result); + return true; + } + else + { + return dtoa_general(v, decimal_point, result, std::false_type()); + } +} + +template +bool dtoa_fixed(double val, char decimal_point, Result& result, std::false_type) +{ + if (val == 0) + { + result.push_back('0'); + result.push_back('.'); + result.push_back('0'); + return true; + } + + char buffer[100]; + int precision = std::numeric_limits::digits10; + int length = snprintf(buffer, sizeof(buffer), "%1.*f", precision, val); + if (length < 0) + { + return false; + } + double x{0}; + auto res = decstr_to_double(buffer, length, x); + if (res.ec == std::errc::invalid_argument) + { + return false; + } + if (x != val) + { + const int precision2 = std::numeric_limits::max_digits10; + length = snprintf(buffer, sizeof(buffer), "%1.*f", precision2, val); + if (length < 0) + { + return false; + } + } + dump_buffer(buffer, length, decimal_point, result); + return true; +} + +template +bool dtoa_fixed(double v, char decimal_point, Result& result, std::true_type) +{ + if (v == 0) + { + result.push_back('0'); + result.push_back('.'); + result.push_back('0'); + return true; + } + + int length = 0; + int k; + + char buffer[100]; + + double u = std::signbit(v) ? -v : v; + if (jsoncons::detail::grisu3(u, buffer, &length, &k)) + { + if (std::signbit(v)) + { + result.push_back('-'); + } + jsoncons::prettify_string(buffer, length, k, std::numeric_limits::lowest(), (std::numeric_limits::max)(), result); + return true; + } + else + { + return dtoa_fixed(v, decimal_point, result, std::false_type()); + } +} + +template +bool dtoa_fixed(double v, char decimal_point, Result& result) +{ + return dtoa_fixed(v, decimal_point, result, std::integral_constant::is_iec559>()); +} + +template +bool dtoa_general(double v, char decimal_point, Result& result) +{ + return dtoa_general(v, decimal_point, result, std::integral_constant::is_iec559>()); +} + +class write_double +{ +private: + float_chars_format float_format_; + int precision_; + char decimal_point_; +public: + write_double(float_chars_format float_format, int precision) + : float_format_(float_format), precision_(precision), decimal_point_('.') + { +#if !defined(JSONCONS_NO_LOCALECONV) + struct lconv *lc = localeconv(); + if (lc != nullptr && lc->decimal_point[0] != 0) + { + decimal_point_ = lc->decimal_point[0]; + } +#endif + } + write_double(const write_double&) = default; + + write_double& operator=(const write_double&) = default; + + template + std::size_t operator()(double val, Result& result) + { + std::size_t count = 0; + + char number_buffer[200]; + int length = 0; + + switch (float_format_) + { + case float_chars_format::fixed: + { + if (precision_ > 0) + { + length = snprintf(number_buffer, sizeof(number_buffer), "%1.*f", precision_, val); + if (length < 0) + { + JSONCONS_THROW(json_runtime_error("write_double failed.")); + } + dump_buffer(number_buffer, length, decimal_point_, result); + } + else + { + if (!dtoa_fixed(val, decimal_point_, result)) + { + JSONCONS_THROW(json_runtime_error("write_double failed.")); + } + } + } + break; + case float_chars_format::scientific: + { + if (precision_ > 0) + { + length = snprintf(number_buffer, sizeof(number_buffer), "%1.*e", precision_, val); + if (length < 0) + { + JSONCONS_THROW(json_runtime_error("write_double failed.")); + } + dump_buffer(number_buffer, length, decimal_point_, result); + } + else + { + if (!dtoa_scientific(val, decimal_point_, result)) + { + JSONCONS_THROW(json_runtime_error("write_double failed.")); + } + } + } + break; + case float_chars_format::general: + { + if (precision_ > 0) + { + length = snprintf(number_buffer, sizeof(number_buffer), "%1.*g", precision_, val); + if (length < 0) + { + JSONCONS_THROW(json_runtime_error("write_double failed.")); + } + dump_buffer(number_buffer, length, decimal_point_, result); + } + else + { + if (!dtoa_general(val, decimal_point_, result)) + { + JSONCONS_THROW(json_runtime_error("write_double failed.")); + } + } + break; + } + default: + JSONCONS_THROW(json_runtime_error("write_double failed.")); + break; + } + return count; + } +}; + +} // namespace jsoncons + +#endif // JSONCONS_UTILITY_WRITE_NUMBER_HPP diff --git a/include/jsoncons_ext/bson/bson.hpp b/include/jsoncons_ext/bson/bson.hpp index 0008bda..86dc6b6 100644 --- a/include/jsoncons_ext/bson/bson.hpp +++ b/include/jsoncons_ext/bson/bson.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/include/jsoncons_ext/bson/bson_cursor.hpp b/include/jsoncons_ext/bson/bson_cursor.hpp index 175ad74..9643768 100644 --- a/include/jsoncons_ext/bson/bson_cursor.hpp +++ b/include/jsoncons_ext/bson/bson_cursor.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include #include #include @@ -54,9 +54,9 @@ class basic_bson_cursor : public basic_staj_cursor, private virtual ser_co : parser_(std::forward(source), options, alloc) { parser_.cursor_mode(true); - if (!done()) + if (!read_done()) { - next(); + read_next(); } } @@ -91,9 +91,9 @@ class basic_bson_cursor : public basic_staj_cursor, private virtual ser_co : parser_(std::forward(source), options, alloc) { parser_.cursor_mode(true); - if (!done()) + if (!read_done()) { - next(ec); + read_next(ec); } } @@ -107,9 +107,9 @@ class basic_bson_cursor : public basic_staj_cursor, private virtual ser_co parser_.reset(); cursor_visitor_.reset(); eof_ = false; - if (!done()) + if (!read_done()) { - next(); + read_next(); } } @@ -119,9 +119,9 @@ class basic_bson_cursor : public basic_staj_cursor, private virtual ser_co parser_.reset(std::forward(source)); cursor_visitor_.reset(); eof_ = false; - if (!done()) + if (!read_done()) { - next(); + read_next(); } } @@ -130,9 +130,9 @@ class basic_bson_cursor : public basic_staj_cursor, private virtual ser_co parser_.reset(); cursor_visitor_.reset(); eof_ = false; - if (!done()) + if (!read_done()) { - next(ec); + read_next(ec); } } @@ -142,9 +142,9 @@ class basic_bson_cursor : public basic_staj_cursor, private virtual ser_co parser_.reset(std::forward(source)); cursor_visitor_.reset(); eof_ = false; - if (!done()) + if (!read_done()) { - next(ec); + read_next(ec); } } @@ -212,12 +212,7 @@ class basic_bson_cursor : public basic_staj_cursor, private virtual ser_co void next() override { - std::error_code ec; - next(ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column())); - } + read_next(); } void next(std::error_code& ec) override @@ -253,6 +248,22 @@ class basic_bson_cursor : public basic_staj_cursor, private virtual ser_co } private: + + bool read_done() const + { + return parser_.done(); + } + + void read_next() + { + std::error_code ec; + read_next(ec); + if (JSONCONS_UNLIKELY(ec)) + { + JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column())); + } + } + void read_next(std::error_code& ec) { parser_.restart(); diff --git a/include/jsoncons_ext/bson/bson_decimal128.hpp b/include/jsoncons_ext/bson/bson_decimal128.hpp index 45ec32e..683e9bb 100644 --- a/include/jsoncons_ext/bson/bson_decimal128.hpp +++ b/include/jsoncons_ext/bson/bson_decimal128.hpp @@ -40,8 +40,8 @@ #include #include -#include -#include +#include +#include namespace jsoncons { namespace bson { @@ -387,7 +387,7 @@ namespace bson { } *str_out = 0; //strcpy_s (str_out, last-str_out, bson_decimal128_inf.c_str()); - return decimal128_to_chars_result{str_out, std::errc()}; + return decimal128_to_chars_result{str_out, std::errc{}}; } else if (combination == combination_nan) { /* NaN */ /* first, not str_out, to erase the sign */ str_out = first; @@ -399,7 +399,7 @@ namespace bson { *str_out = 0; //strcpy_s (first, last-first, bson_decimal128_nan.c_str()); /* we don't care about the NaN payload. */ - return decimal128_to_chars_result{str_out, std::errc()}; + return decimal128_to_chars_result{str_out, std::errc{}}; } else { biased_exponent = (high >> 15) & exponent_mask; significand_msb = 0x8 + ((high >> 14) & 0x1); @@ -494,7 +494,7 @@ namespace bson { if (scientific_exponent >= 0) { s.push_back('+'); } - jsoncons::detail::from_integer(scientific_exponent, s); + jsoncons::from_integer(scientific_exponent, s); if (str_out + s.size() < last) { std::memcpy(str_out, s.data(), s.size()); @@ -536,7 +536,7 @@ namespace bson { } } } - return decimal128_to_chars_result{str_out, std::errc()}; + return decimal128_to_chars_result{str_out, std::errc{}}; } @@ -618,10 +618,10 @@ namespace bson { detail::dec128_istreq (str_read, last, infinity_str.data(), infinity_str.data()+infinity_str.length())) { dec = is_negative ? decimal128_limits::neg_infinity() : decimal128_limits::infinity(); - return decimal128_from_chars_result{str_read,std::errc()}; + return decimal128_from_chars_result{str_read,std::errc{}}; } else if (detail::dec128_istreq (str_read, last, nan_str.data(), nan_str.data()+nan_str.length())) { dec = decimal128_limits::nan(); - return decimal128_from_chars_result{str_read,std::errc()}; + return decimal128_from_chars_result{str_read,std::errc{}}; } dec = decimal128_limits::nan(); @@ -677,8 +677,8 @@ namespace bson { if (*str_read == '+') { ++str_read; } - auto result = jsoncons::detail::to_integer(str_read, last - str_read, exponent); - if (result.ec != jsoncons::detail::to_integer_errc()) + auto result = jsoncons::to_integer(str_read, last - str_read, exponent); + if (result.ec != std::errc{}) { dec = decimal128_limits::nan(); return decimal128_from_chars_result{str_read,std::errc::invalid_argument}; @@ -865,7 +865,7 @@ namespace bson { dec.high |= 0x8000000000000000ull; } - return decimal128_from_chars_result{str_read,std::errc()}; + return decimal128_from_chars_result{str_read,std::errc{}}; } } // namespace bson diff --git a/include/jsoncons_ext/bson/bson_encoder.hpp b/include/jsoncons_ext/bson/bson_encoder.hpp index be35436..a27f9a9 100644 --- a/include/jsoncons_ext/bson/bson_encoder.hpp +++ b/include/jsoncons_ext/bson/bson_encoder.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include #include @@ -92,7 +92,7 @@ class basic_bson_encoder final : public basic_json_visitor }; sink_type sink_; - const bson_encode_options options_; + int max_nesting_depth_; allocator_type alloc_; std::vector stack_; @@ -116,7 +116,7 @@ class basic_bson_encoder final : public basic_json_visitor const bson_encode_options& options, const Allocator& alloc = Allocator()) : sink_(std::forward(sink)), - options_(options), + max_nesting_depth_(options.max_nesting_depth()), alloc_(alloc) { } @@ -152,7 +152,7 @@ class basic_bson_encoder final : public basic_json_visitor JSONCONS_VISITOR_RETURN_TYPE visit_begin_object(semantic_tag, const ser_context&, std::error_code& ec) override { - if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth())) + if (JSONCONS_UNLIKELY(++nesting_depth_ > max_nesting_depth_)) { ec = bson_errc::max_nesting_depth_exceeded; JSONCONS_VISITOR_RETURN; @@ -196,7 +196,7 @@ class basic_bson_encoder final : public basic_json_visitor JSONCONS_VISITOR_RETURN_TYPE visit_begin_array(semantic_tag, const ser_context&, std::error_code& ec) override { - if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth())) + if (JSONCONS_UNLIKELY(++nesting_depth_ > max_nesting_depth_)) { ec = bson_errc::max_nesting_depth_exceeded; JSONCONS_VISITOR_RETURN; @@ -302,7 +302,7 @@ class basic_bson_encoder final : public basic_json_visitor before_value(jsoncons::bson::bson_type::decimal128_type); decimal128_t dec; auto rc = decimal128_from_chars(sv.data(), sv.data()+sv.size(), dec); - if (rc.ec != std::errc()) + if (rc.ec != std::errc{}) { ec = bson_errc::invalid_decimal128_string; JSONCONS_VISITOR_RETURN; diff --git a/include/jsoncons_ext/bson/bson_error.hpp b/include/jsoncons_ext/bson/bson_error.hpp index 03724aa..d07117e 100644 --- a/include/jsoncons_ext/bson/bson_error.hpp +++ b/include/jsoncons_ext/bson/bson_error.hpp @@ -1,4 +1,4 @@ -/// Copyright 2013-2025 Daniel Parker +/// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/include/jsoncons_ext/bson/bson_options.hpp b/include/jsoncons_ext/bson/bson_options.hpp index a78b51a..3fd3d5c 100644 --- a/include/jsoncons_ext/bson/bson_options.hpp +++ b/include/jsoncons_ext/bson/bson_options.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -18,19 +18,14 @@ class bson_options_common { friend class bson_options; - int max_nesting_depth_; + int max_nesting_depth_{1024}; protected: + bson_options_common() = default; + bson_options_common(const bson_options_common&) = default; virtual ~bson_options_common() = default; - bson_options_common() - : max_nesting_depth_(1024) - { - } - - bson_options_common(const bson_options_common&) = default; bson_options_common& operator=(const bson_options_common&) = default; - bson_options_common(bson_options_common&&) = default; - bson_options_common& operator=(bson_options_common&&) = default; + public: int max_nesting_depth() const { @@ -42,18 +37,20 @@ class bson_decode_options : public virtual bson_options_common { friend class bson_options; public: - bson_decode_options() - { - } + bson_decode_options() = default; + bson_decode_options(const bson_decode_options& other) = default; +protected: + bson_decode_options& operator=(const bson_decode_options& other) = default; }; class bson_encode_options : public virtual bson_options_common { friend class bson_options; public: - bson_encode_options() - { - } + bson_encode_options() = default; + bson_encode_options(const bson_encode_options& other) = default; +protected: + bson_encode_options& operator=(const bson_encode_options& other) = default; }; class bson_options final : public bson_decode_options, public bson_encode_options @@ -61,6 +58,10 @@ class bson_options final : public bson_decode_options, public bson_encode_option public: using bson_options_common::max_nesting_depth; + bson_options() = default; + bson_options(const bson_options& other) = default; + bson_options& operator=(const bson_options& other) = default; + bson_options& max_nesting_depth(int value) { this->max_nesting_depth_ = value; diff --git a/include/jsoncons_ext/bson/bson_parser.hpp b/include/jsoncons_ext/bson/bson_parser.hpp index 9bd8985..a21cab1 100644 --- a/include/jsoncons_ext/bson/bson_parser.hpp +++ b/include/jsoncons_ext/bson/bson_parser.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include #include @@ -55,12 +55,12 @@ struct parse_state parse_state& operator=(parse_state&&) = default; }; -template > +template > class basic_bson_parser : public ser_context { using char_type = char; using char_traits_type = std::char_traits; - using temp_allocator_type = TempAllocator; + using temp_allocator_type = TempAlloc; using char_allocator_type = typename std::allocator_traits:: template rebind_alloc; using byte_allocator_type = typename std::allocator_traits:: template rebind_alloc; using parse_state_allocator_type = typename std::allocator_traits:: template rebind_alloc; @@ -72,7 +72,7 @@ class basic_bson_parser : public ser_context int mark_level_{0}; Source source_; - bson_decode_options options_; + int max_nesting_depth_; std::vector bytes_buffer_; string_type name_buffer_; string_type text_buffer_; @@ -81,9 +81,9 @@ class basic_bson_parser : public ser_context template basic_bson_parser(Sourceable&& source, const bson_decode_options& options = bson_decode_options(), - const TempAllocator& temp_alloc = TempAllocator()) + const TempAlloc& temp_alloc = TempAlloc()) : source_(std::forward(source)), - options_(options), + max_nesting_depth_(options.max_nesting_depth()), bytes_buffer_(temp_alloc), name_buffer_(temp_alloc), text_buffer_(temp_alloc), @@ -248,7 +248,7 @@ class basic_bson_parser : public ser_context void begin_document(json_visitor& visitor, std::error_code& ec) { - if (JSONCONS_UNLIKELY(static_cast(state_stack_.size()) > options_.max_nesting_depth())) + if (JSONCONS_UNLIKELY(static_cast(state_stack_.size()) > max_nesting_depth_)) { ec = bson_errc::max_nesting_depth_exceeded; more_ = false; @@ -294,7 +294,7 @@ class basic_bson_parser : public ser_context void begin_array(json_visitor& visitor, std::error_code& ec) { - if (JSONCONS_UNLIKELY(static_cast(state_stack_.size()) > options_.max_nesting_depth())) + if (JSONCONS_UNLIKELY(static_cast(state_stack_.size()) > max_nesting_depth_)) { ec = bson_errc::max_nesting_depth_exceeded; more_ = false; diff --git a/include/jsoncons_ext/bson/bson_reader.hpp b/include/jsoncons_ext/bson/bson_reader.hpp index 48e7ec0..f476e01 100644 --- a/include/jsoncons_ext/bson/bson_reader.hpp +++ b/include/jsoncons_ext/bson/bson_reader.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -21,16 +21,16 @@ namespace jsoncons { namespace bson { -template > +template > class basic_bson_reader { - basic_bson_parser parser_; + basic_bson_parser parser_; json_visitor& visitor_; public: template basic_bson_reader(Sourceable&& source, json_visitor& visitor, - const TempAllocator& temp_alloc) + const TempAlloc& temp_alloc) : basic_bson_reader(std::forward(source), visitor, bson_decode_options(), @@ -42,7 +42,7 @@ class basic_bson_reader basic_bson_reader(Sourceable&& source, json_visitor& visitor, const bson_decode_options& options = bson_decode_options(), - const TempAllocator& temp_alloc=TempAllocator()) + const TempAlloc& temp_alloc=TempAlloc()) : parser_(std::forward(source), options, temp_alloc), visitor_(visitor) { diff --git a/include/jsoncons_ext/bson/bson_type.hpp b/include/jsoncons_ext/bson/bson_type.hpp index 46e165d..557138a 100644 --- a/include/jsoncons_ext/bson/bson_type.hpp +++ b/include/jsoncons_ext/bson/bson_type.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/include/jsoncons_ext/bson/decode_bson.hpp b/include/jsoncons_ext/bson/decode_bson.hpp index 288fde9..78e253c 100644 --- a/include/jsoncons_ext/bson/decode_bson.hpp +++ b/include/jsoncons_ext/bson/decode_bson.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -11,14 +11,15 @@ #include // std::enable_if #include -#include -#include #include +#include #include #include -#include #include +#include +#include #include +#include #include #include @@ -27,180 +28,239 @@ namespace jsoncons { namespace bson { - template - typename std::enable_if::value && - ext_traits::is_byte_sequence::value,T>::type - decode_bson(const Source& v, - const bson_decode_options& options = bson_decode_options()) - { - jsoncons::json_decoder decoder; - auto adaptor = make_json_visitor_adaptor(decoder); - basic_bson_reader reader(v, adaptor, options); - reader.read(); - if (!decoder.is_valid()) - { - JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); - } - return decoder.get_result(); - } - - template - typename std::enable_if::value && - ext_traits::is_byte_sequence::value,T>::type - decode_bson(const Source& v, - const bson_decode_options& options = bson_decode_options()) - { - basic_bson_cursor cursor(v, options); - json_decoder> decoder{}; - - std::error_code ec; - T val = decode_traits::decode(cursor, decoder, ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); - } - return val; - } - - template - typename std::enable_if::value,T>::type - decode_bson(std::istream& is, - const bson_decode_options& options = bson_decode_options()) - { - jsoncons::json_decoder decoder; - auto adaptor = make_json_visitor_adaptor(decoder); - bson_stream_reader reader(is, adaptor, options); - reader.read(); - if (!decoder.is_valid()) - { - JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); - } - return decoder.get_result(); - } - - template - typename std::enable_if::value,T>::type - decode_bson(std::istream& is, - const bson_decode_options& options = bson_decode_options()) - { - basic_bson_cursor cursor(is, options); - json_decoder> decoder{}; - - std::error_code ec; - T val = decode_traits::decode(cursor, decoder, ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); - } - return val; - } - - template - typename std::enable_if::value,T>::type - decode_bson(InputIt first, InputIt last, - const bson_decode_options& options = bson_decode_options()) - { - jsoncons::json_decoder decoder; - auto adaptor = make_json_visitor_adaptor(decoder); - basic_bson_reader> reader(binary_iterator_source(first, last), adaptor, options); - reader.read(); - if (!decoder.is_valid()) - { - JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); - } - return decoder.get_result(); - } - - template - typename std::enable_if::value,T>::type - decode_bson(InputIt first, InputIt last, - const bson_decode_options& options = bson_decode_options()) - { - basic_bson_cursor> cursor(binary_iterator_source(first, last), options); - json_decoder> decoder{}; - - std::error_code ec; - T val = decode_traits::decode(cursor, decoder, ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); - } - return val; - } - - // With leading allocator_set parameter - - template - typename std::enable_if::value && - ext_traits::is_byte_sequence::value,T>::type - decode_bson(const allocator_set& alloc_set, - const Source& v, - const bson_decode_options& options = bson_decode_options()) - { - json_decoder decoder(alloc_set.get_allocator(), alloc_set.get_temp_allocator()); - auto adaptor = make_json_visitor_adaptor(decoder); - basic_bson_reader reader(v, adaptor, options, alloc_set.get_temp_allocator()); - reader.read(); - if (!decoder.is_valid()) - { - JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); - } - return decoder.get_result(); - } - - template - typename std::enable_if::value && - ext_traits::is_byte_sequence::value,T>::type - decode_bson(const allocator_set& alloc_set, - const Source& v, - const bson_decode_options& options = bson_decode_options()) - { - basic_bson_cursor cursor(v, options, alloc_set.get_temp_allocator()); - json_decoder,TempAllocator> decoder(alloc_set.get_temp_allocator(), alloc_set.get_temp_allocator()); - - std::error_code ec; - T val = decode_traits::decode(cursor, decoder, ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); - } - return val; - } - - template - typename std::enable_if::value,T>::type - decode_bson(const allocator_set& alloc_set, - std::istream& is, - const bson_decode_options& options = bson_decode_options()) - { - json_decoder decoder(alloc_set.get_allocator(), alloc_set.get_temp_allocator()); - auto adaptor = make_json_visitor_adaptor(decoder); - basic_bson_reader reader(is, adaptor, options, alloc_set.get_temp_allocator()); - reader.read(); - if (!decoder.is_valid()) - { - JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); - } - return decoder.get_result(); - } - - template - typename std::enable_if::value,T>::type - decode_bson(const allocator_set& alloc_set, - std::istream& is, - const bson_decode_options& options = bson_decode_options()) - { - basic_bson_cursor cursor(is, options, alloc_set.get_temp_allocator()); - json_decoder,TempAllocator> decoder(alloc_set.get_temp_allocator(), alloc_set.get_temp_allocator()); - - std::error_code ec; - T val = decode_traits::decode(cursor, decoder, ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); - } - return val; +template +typename std::enable_if::value && + ext_traits::is_byte_sequence::value,read_result>::type +try_decode_bson(const BytesLike& v, + const bson_decode_options& options = bson_decode_options()) +{ + using value_type = T; + using result_type = read_result; + + std::error_code ec; + jsoncons::json_decoder decoder; + auto adaptor = make_json_visitor_adaptor(decoder); + basic_bson_reader reader(v, adaptor, options); + reader.read(ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, reader.line(), reader.column()}; + } + if (JSONCONS_UNLIKELY(!decoder.is_valid())) + { + return result_type{jsoncons::unexpect, conv_errc::conversion_failed, reader.line(), reader.column()}; + } + return result_type{decoder.get_result()}; +} + +template +typename std::enable_if::value && + ext_traits::is_byte_sequence::value,read_result>::type +try_decode_bson(const BytesLike& v, + const bson_decode_options& options = bson_decode_options()) +{ + using value_type = T; + using result_type = read_result; + + std::error_code ec; + basic_bson_cursor cursor(v, options, ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, cursor.line(), cursor.column()}; + } + + return reflect::decode_traits::try_decode(make_alloc_set(), cursor); +} + +template +typename std::enable_if::value,read_result>::type +try_decode_bson(std::istream& is, + const bson_decode_options& options = bson_decode_options()) +{ + using value_type = T; + using result_type = read_result; + + std::error_code ec; + jsoncons::json_decoder decoder; + auto adaptor = make_json_visitor_adaptor(decoder); + bson_stream_reader reader(is, adaptor, options); + reader.read(ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, reader.line(), reader.column()}; + } + if (JSONCONS_UNLIKELY(!decoder.is_valid())) + { + return result_type{jsoncons::unexpect, conv_errc::conversion_failed, reader.line(), reader.column()}; + } + return result_type{decoder.get_result()}; +} + +template +typename std::enable_if::value,read_result>::type +try_decode_bson(std::istream& is, + const bson_decode_options& options = bson_decode_options()) +{ + using value_type = T; + using result_type = read_result; + + std::error_code ec; + basic_bson_cursor cursor(is, options, ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, cursor.line(), cursor.column()}; + } + + return reflect::decode_traits::try_decode(make_alloc_set(), cursor); +} + +template +typename std::enable_if::value,read_result>::type +try_decode_bson(InputIt first, InputIt last, + const bson_decode_options& options = bson_decode_options()) +{ + using value_type = T; + using result_type = read_result; + + std::error_code ec; + jsoncons::json_decoder decoder; + auto adaptor = make_json_visitor_adaptor(decoder); + basic_bson_reader> reader(binary_iterator_source(first, last), adaptor, options); + reader.read(ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, reader.line(), reader.column()}; + } + if (JSONCONS_UNLIKELY(!decoder.is_valid())) + { + return result_type{jsoncons::unexpect, conv_errc::conversion_failed, reader.line(), reader.column()}; + } + return result_type{decoder.get_result()}; +} + +template +typename std::enable_if::value,read_result>::type +try_decode_bson(InputIt first, InputIt last, + const bson_decode_options& options = bson_decode_options()) +{ + using value_type = T; + using result_type = read_result; + + std::error_code ec; + basic_bson_cursor> cursor(binary_iterator_source(first, last), options, ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, cursor.line(), cursor.column()}; + } + + return reflect::decode_traits::try_decode(make_alloc_set(), cursor); +} + +// With leading allocator_set parameter + +template +typename std::enable_if::value && + ext_traits::is_byte_sequence::value,read_result>::type +try_decode_bson(const allocator_set& aset, + const BytesLike& v, + const bson_decode_options& options = bson_decode_options()) +{ + using value_type = T; + using result_type = read_result; + + std::error_code ec; + json_decoder decoder(aset.get_allocator(), aset.get_temp_allocator()); + auto adaptor = make_json_visitor_adaptor(decoder); + basic_bson_reader reader(v, adaptor, options, aset.get_temp_allocator()); + reader.read(ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, reader.line(), reader.column()}; + } + if (JSONCONS_UNLIKELY(!decoder.is_valid())) + { + return result_type{jsoncons::unexpect, conv_errc::conversion_failed, reader.line(), reader.column()}; + } + return result_type{decoder.get_result()}; +} + +template +typename std::enable_if::value && + ext_traits::is_byte_sequence::value,read_result>::type +try_decode_bson(const allocator_set& aset, + const BytesLike& v, + const bson_decode_options& options = bson_decode_options()) +{ + using value_type = T; + using result_type = read_result; + + std::error_code ec; + basic_bson_cursor cursor(std::allocator_arg, aset.get_temp_allocator(), v, options, ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, cursor.line(), cursor.column()}; + } + + return reflect::decode_traits::try_decode(aset, cursor); +} + +template +typename std::enable_if::value,read_result>::type +try_decode_bson(const allocator_set& aset, + std::istream& is, + const bson_decode_options& options = bson_decode_options()) +{ + using value_type = T; + using result_type = read_result; + using byte_allocator_type = typename std::allocator_traits:: template rebind_alloc; + using stream_source_type = stream_source; + + std::error_code ec; + json_decoder decoder(aset.get_allocator(), aset.get_temp_allocator()); + auto adaptor = make_json_visitor_adaptor(decoder); + basic_bson_reader reader(stream_source_type(is,aset.get_temp_allocator()), + adaptor, options, aset.get_temp_allocator()); + reader.read(ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, reader.line(), reader.column()}; + } + if (JSONCONS_UNLIKELY(!decoder.is_valid())) + { + return result_type{jsoncons::unexpect, conv_errc::conversion_failed, reader.line(), reader.column()}; + } + return result_type{decoder.get_result()}; +} + +template +typename std::enable_if::value,read_result>::type +try_decode_bson(const allocator_set& aset, + std::istream& is, + const bson_decode_options& options = bson_decode_options()) +{ + using value_type = T; + using result_type = read_result; + + std::error_code ec; + basic_bson_cursor cursor(std::allocator_arg, aset.get_temp_allocator(), is, options, ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, cursor.line(), cursor.column()}; + } + + return reflect::decode_traits::try_decode(aset, cursor); +} + +template +T decode_bson(Args&& ... args) +{ + auto result = try_decode_bson(std::forward(args)...); + if (!result) + { + JSONCONS_THROW(ser_error(result.error().code(), result.error().line(), result.error().column())); } + return std::move(*result); +} } // namespace bson } // namespace jsoncons diff --git a/include/jsoncons_ext/bson/encode_bson.hpp b/include/jsoncons_ext/bson/encode_bson.hpp index 0557350..92b5ff9 100644 --- a/include/jsoncons_ext/bson/encode_bson.hpp +++ b/include/jsoncons_ext/bson/encode_bson.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -11,12 +11,13 @@ #include #include // std::enable_if -#include #include #include -#include +#include #include #include +#include +#include #include #include @@ -26,123 +27,115 @@ namespace jsoncons { namespace bson { - template - typename std::enable_if::value && - ext_traits::is_back_insertable_byte_container::value,void>::type - encode_bson(const T& j, - ByteContainer& cont, - const bson_encode_options& options = bson_encode_options()) - { - using char_type = typename T::char_type; - basic_bson_encoder> encoder(cont, options); - auto adaptor = make_json_visitor_adaptor>(encoder); - j.dump(adaptor); - } +template +typename std::enable_if::value && + ext_traits::is_back_insertable_byte_container::value,write_result>::type +try_encode_bson(const T& j, + ByteContainer& cont, + const bson_encode_options& options = bson_encode_options()) +{ + using char_type = typename T::char_type; + basic_bson_encoder> encoder(cont, options); + auto adaptor = make_json_visitor_adaptor>(encoder); + return j.try_dump(adaptor); +} - template - typename std::enable_if::value && - ext_traits::is_back_insertable_byte_container::value,void>::type - encode_bson(const T& val, - ByteContainer& cont, - const bson_encode_options& options = bson_encode_options()) - { - basic_bson_encoder> encoder(cont, options); - std::error_code ec; - encode_traits::encode(val, encoder, json(), ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec)); - } - } +template +typename std::enable_if::value && + ext_traits::is_back_insertable_byte_container::value,write_result>::type +try_encode_bson(const T& val, + ByteContainer& cont, + const bson_encode_options& options = bson_encode_options()) +{ + basic_bson_encoder> encoder(cont, options); + std::error_code ec; + return reflect::encode_traits::try_encode(make_alloc_set(), val, encoder); +} - template - typename std::enable_if::value,void>::type - encode_bson(const T& j, - std::ostream& os, - const bson_encode_options& options = bson_encode_options()) - { - using char_type = typename T::char_type; - bson_stream_encoder encoder(os, options); - auto adaptor = make_json_visitor_adaptor>(encoder); - j.dump(adaptor); - } +template +typename std::enable_if::value,write_result>::type +try_encode_bson(const T& j, + std::ostream& os, + const bson_encode_options& options = bson_encode_options()) +{ + using char_type = typename T::char_type; + bson_stream_encoder encoder(os, options); + auto adaptor = make_json_visitor_adaptor>(encoder); + return j.try_dump(adaptor); +} - template - typename std::enable_if::value,void>::type - encode_bson(const T& val, - std::ostream& os, - const bson_encode_options& options = bson_encode_options()) - { - bson_stream_encoder encoder(os, options); - std::error_code ec; - encode_traits::encode(val, encoder, json(), ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec)); - } - } - - // with temp_allocator_rag - - template - typename std::enable_if::value && - ext_traits::is_back_insertable_byte_container::value,void>::type - encode_bson(const allocator_set& alloc_set, - const T& j, - ByteContainer& cont, - const bson_encode_options& options = bson_encode_options()) - { - using char_type = typename T::char_type; - basic_bson_encoder,TempAllocator> encoder(cont, options, alloc_set.get_temp_allocator()); - auto adaptor = make_json_visitor_adaptor>(encoder); - j.dump(adaptor); - } +template +typename std::enable_if::value,write_result>::type +try_encode_bson(const T& val, + std::ostream& os, + const bson_encode_options& options = bson_encode_options()) +{ + bson_stream_encoder encoder(os, options); + std::error_code ec; + return reflect::encode_traits::try_encode(make_alloc_set(), val, encoder); +} - template - typename std::enable_if::value && - ext_traits::is_back_insertable_byte_container::value,void>::type - encode_bson(const allocator_set& alloc_set, - const T& val, - ByteContainer& cont, - const bson_encode_options& options = bson_encode_options()) - { - basic_bson_encoder,TempAllocator> encoder(cont, options, alloc_set.get_temp_allocator()); - std::error_code ec; - encode_traits::encode(val, encoder, json(), ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec)); - } - } +template +typename std::enable_if::value && + ext_traits::is_back_insertable_byte_container::value,write_result>::type +try_encode_bson(const allocator_set& aset, + const T& j, + ByteContainer& cont, + const bson_encode_options& options = bson_encode_options()) +{ + using char_type = typename T::char_type; + basic_bson_encoder,TempAlloc> encoder(cont, options, aset.get_temp_allocator()); + auto adaptor = make_json_visitor_adaptor>(encoder); + return j.try_dump(adaptor); +} - template - typename std::enable_if::value,void>::type - encode_bson(const allocator_set& alloc_set, - const T& j, - std::ostream& os, - const bson_encode_options& options = bson_encode_options()) - { - using char_type = typename T::char_type; - basic_bson_encoder encoder(os, options, alloc_set.get_temp_allocator()); - auto adaptor = make_json_visitor_adaptor>(encoder); - j.dump(adaptor); - } +template +typename std::enable_if::value && + ext_traits::is_back_insertable_byte_container::value,write_result>::type +try_encode_bson(const allocator_set& aset, + const T& val, + ByteContainer& cont, + const bson_encode_options& options = bson_encode_options()) +{ + basic_bson_encoder,TempAlloc> encoder(cont, options, aset.get_temp_allocator()); + std::error_code ec; + return reflect::encode_traits::try_encode(aset, val, encoder); +} + +template +typename std::enable_if::value,write_result>::type +try_encode_bson(const allocator_set& aset, + const T& j, + std::ostream& os, + const bson_encode_options& options = bson_encode_options()) +{ + using char_type = typename T::char_type; + basic_bson_encoder encoder(os, options, aset.get_temp_allocator()); + auto adaptor = make_json_visitor_adaptor>(encoder); + return j.try_dump(adaptor); +} + +template +typename std::enable_if::value,write_result>::type +try_encode_bson(const allocator_set& aset, + const T& val, + std::ostream& os, + const bson_encode_options& options = bson_encode_options()) +{ + basic_bson_encoder encoder(os, options, aset.get_temp_allocator()); + std::error_code ec; + return reflect::encode_traits::try_encode(aset, val, encoder); +} - template - typename std::enable_if::value,void>::type - encode_bson(const allocator_set& alloc_set, - const T& val, - std::ostream& os, - const bson_encode_options& options = bson_encode_options()) +template +void encode_bson(Args&& ... args) +{ + auto r = try_encode_bson(std::forward(args)...); + if (!r) { - basic_bson_encoder encoder(os, options, alloc_set.get_temp_allocator()); - std::error_code ec; - encode_traits::encode(val, encoder, json(), ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec)); - } + JSONCONS_THROW(ser_error(r.error())); } +} } // namespace bson } // namespace jsoncons diff --git a/include/jsoncons_ext/cbor/cbor.hpp b/include/jsoncons_ext/cbor/cbor.hpp index 990fa97..107767f 100644 --- a/include/jsoncons_ext/cbor/cbor.hpp +++ b/include/jsoncons_ext/cbor/cbor.hpp @@ -1,4 +1,4 @@ -// Copyright 2017-2025 Daniel Parker +// Copyright 2017-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/include/jsoncons_ext/cbor/cbor_cursor.hpp b/include/jsoncons_ext/cbor/cbor_cursor.hpp index 6b10d63..4a1f387 100644 --- a/include/jsoncons_ext/cbor/cbor_cursor.hpp +++ b/include/jsoncons_ext/cbor/cbor_cursor.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include #include #include @@ -53,9 +53,9 @@ class basic_cbor_cursor : public basic_staj_cursor, private virtual ser_co cursor_handler_adaptor_(cursor_visitor_, alloc) { parser_.cursor_mode(true); - if (!done()) + if (!read_done()) { - next(); + read_next(); } } @@ -92,9 +92,9 @@ class basic_cbor_cursor : public basic_staj_cursor, private virtual ser_co eof_(false) { parser_.cursor_mode(true); - if (!done()) + if (!read_done()) { - next(ec); + read_next(ec); } } @@ -109,9 +109,9 @@ class basic_cbor_cursor : public basic_staj_cursor, private virtual ser_co cursor_visitor_.reset(); cursor_handler_adaptor_.reset(); eof_ = false; - if (!done()) + if (!read_done()) { - next(); + read_next(); } } @@ -122,9 +122,9 @@ class basic_cbor_cursor : public basic_staj_cursor, private virtual ser_co cursor_visitor_.reset(); cursor_handler_adaptor_.reset(); eof_ = false; - if (!done()) + if (!read_done()) { - next(); + read_next(); } } @@ -134,9 +134,9 @@ class basic_cbor_cursor : public basic_staj_cursor, private virtual ser_co cursor_visitor_.reset(); cursor_handler_adaptor_.reset(); eof_ = false; - if (!done()) + if (!read_done()) { - next(ec); + read_next(ec); } } @@ -147,9 +147,9 @@ class basic_cbor_cursor : public basic_staj_cursor, private virtual ser_co cursor_visitor_.reset(); cursor_handler_adaptor_.reset(); eof_ = false; - if (!done()) + if (!read_done()) { - next(ec); + read_next(ec); } } @@ -219,12 +219,7 @@ class basic_cbor_cursor : public basic_staj_cursor, private virtual ser_co void next() override { - std::error_code ec; - next(ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column())); - } + read_next(); } void next(std::error_code& ec) override @@ -260,6 +255,22 @@ class basic_cbor_cursor : public basic_staj_cursor, private virtual ser_co } private: + + bool read_done() const + { + return parser_.done(); + } + + void read_next() + { + std::error_code ec; + read_next(ec); + if (JSONCONS_UNLIKELY(ec)) + { + JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column())); + } + } + void read_next(std::error_code& ec) { if (cursor_visitor_.in_available()) diff --git a/include/jsoncons_ext/cbor/cbor_detail.hpp b/include/jsoncons_ext/cbor/cbor_detail.hpp index dc8ae79..997280f 100644 --- a/include/jsoncons_ext/cbor/cbor_detail.hpp +++ b/include/jsoncons_ext/cbor/cbor_detail.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/include/jsoncons_ext/cbor/cbor_encoder.hpp b/include/jsoncons_ext/cbor/cbor_encoder.hpp index 9b67bf3..f3b60ed 100644 --- a/include/jsoncons_ext/cbor/cbor_encoder.hpp +++ b/include/jsoncons_ext/cbor/cbor_encoder.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -21,12 +21,12 @@ #include #include -#include +#include #include // jsoncons::ser_error #include #include #include -#include +#include #include #include #include @@ -103,7 +103,9 @@ class basic_cbor_encoder final : public basic_json_visitor using stack_item_allocator_type = typename std::allocator_traits:: template rebind_alloc; Sink sink_; - const cbor_encode_options options_; + int max_nesting_depth_; + bool pack_strings_; + bool use_typed_arrays_; allocator_type alloc_; std::vector stack_; @@ -126,7 +128,9 @@ class basic_cbor_encoder final : public basic_json_visitor const cbor_encode_options& options, const Allocator& alloc = Allocator()) : sink_(std::forward(sink)), - options_(options), + max_nesting_depth_(options.max_nesting_depth()), + pack_strings_(options.pack_strings()), + use_typed_arrays_(options.use_typed_arrays()), alloc_(alloc), stack_(alloc), stringref_map_(alloc), @@ -259,7 +263,7 @@ class basic_cbor_encoder final : public basic_json_visitor JSONCONS_VISITOR_RETURN_TYPE visit_begin_object(semantic_tag, const ser_context&, std::error_code& ec) override { - if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth())) + if (JSONCONS_UNLIKELY(++nesting_depth_ > max_nesting_depth_)) { ec = cbor_errc::max_nesting_depth_exceeded; JSONCONS_VISITOR_RETURN; @@ -272,7 +276,7 @@ class basic_cbor_encoder final : public basic_json_visitor JSONCONS_VISITOR_RETURN_TYPE visit_begin_object(std::size_t length, semantic_tag, const ser_context&, std::error_code& ec) override { - if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth())) + if (JSONCONS_UNLIKELY(++nesting_depth_ > max_nesting_depth_)) { ec = cbor_errc::max_nesting_depth_exceeded; JSONCONS_VISITOR_RETURN; @@ -347,7 +351,7 @@ class basic_cbor_encoder final : public basic_json_visitor JSONCONS_VISITOR_RETURN_TYPE visit_begin_array(semantic_tag, const ser_context&, std::error_code& ec) override { - if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth())) + if (JSONCONS_UNLIKELY(++nesting_depth_ > max_nesting_depth_)) { ec = cbor_errc::max_nesting_depth_exceeded; JSONCONS_VISITOR_RETURN; @@ -359,7 +363,7 @@ class basic_cbor_encoder final : public basic_json_visitor JSONCONS_VISITOR_RETURN_TYPE visit_begin_array(std::size_t length, semantic_tag, const ser_context&, std::error_code& ec) override { - if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth())) + if (JSONCONS_UNLIKELY(++nesting_depth_ > max_nesting_depth_)) { ec = cbor_errc::max_nesting_depth_exceeded; JSONCONS_VISITOR_RETURN; @@ -459,7 +463,7 @@ class basic_cbor_encoder final : public basic_json_visitor JSONCONS_THROW(ser_error(cbor_errc::invalid_utf8_text_string)); } - if (options_.pack_strings() && sv.size() >= jsoncons::cbor::detail::min_length_for_stringref(next_stringref_)) + if (pack_strings_ && sv.size() >= jsoncons::cbor::detail::min_length_for_stringref(next_stringref_)) { string_type s(sv.data(), sv.size(), alloc_); auto it = stringref_map_.find(s); @@ -696,7 +700,7 @@ class basic_cbor_encoder final : public basic_json_visitor if (exponent.length() > 0) { int64_t val{}; - auto r = jsoncons::detail::to_integer(exponent.data(), exponent.length(), val); + auto r = jsoncons::to_integer(exponent.data(), exponent.length(), val); if (!r) { ec = r.error_code(); @@ -708,15 +712,15 @@ class basic_cbor_encoder final : public basic_json_visitor if (JSONCONS_UNLIKELY(ec)) {return;} int64_t val{ 0 }; - auto r = jsoncons::detail::to_integer(s.data(),s.length(), val); + auto r = jsoncons::to_integer(s.data(),s.length(), val); if (r) { visit_int64(val, semantic_tag::none, context, ec); if (JSONCONS_UNLIKELY(ec)) {return;} } - else if (r.error_code() == jsoncons::detail::to_integer_errc::overflow) + else if (r.error_code() == std::errc::result_out_of_range) { - bigint n = bigint::from_string(s.data(), s.length()); + bigint n(s.data(), s.length()); write_bignum(n); end_value(); } @@ -871,7 +875,7 @@ class basic_cbor_encoder final : public basic_json_visitor if (exponent.length() > 0) { int64_t val{ 0 }; - auto r = jsoncons::detail::hex_to_integer(exponent.data(), exponent.length(), val); + auto r = jsoncons::hex_to_integer(exponent.data(), exponent.length(), val); if (!r) { ec = r.error_code(); @@ -883,15 +887,15 @@ class basic_cbor_encoder final : public basic_json_visitor if (JSONCONS_UNLIKELY(ec)) return; int64_t val{ 0 }; - auto r = jsoncons::detail::hex_to_integer(s.data(),s.length(), val); + auto r = jsoncons::hex_to_integer(s.data(),s.length(), val); if (r) { visit_int64(val, semantic_tag::none, context, ec); if (JSONCONS_UNLIKELY(ec)) return; } - else if (r.error_code() == jsoncons::detail::to_integer_errc::overflow) + else if (r.error_code() == std::errc::result_out_of_range) { - bigint n = bigint::from_string_radix(s.data(), s.length(), 16); + bigint n = bigint::parse_radix(s.data(), s.length(), 16); write_bignum(n); end_value(); } @@ -909,7 +913,7 @@ class basic_cbor_encoder final : public basic_json_visitor { case semantic_tag::bigint: { - bigint n = bigint::from_string(sv.data(), sv.length()); + bigint n(sv.data(), sv.length()); write_bignum(n); end_value(); break; @@ -998,7 +1002,7 @@ class basic_cbor_encoder final : public basic_json_visitor default: break; } - if (options_.pack_strings() && b.size() >= jsoncons::cbor::detail::min_length_for_stringref(next_stringref_)) + if (pack_strings_ && b.size() >= jsoncons::cbor::detail::min_length_for_stringref(next_stringref_)) { byte_string_type bs(b.data(), b.size(), alloc_); auto it = bytestringref_map_.find(bs); @@ -1027,7 +1031,7 @@ class basic_cbor_encoder final : public basic_json_visitor const ser_context&, std::error_code&) override { - if (options_.pack_strings() && b.size() >= jsoncons::cbor::detail::min_length_for_stringref(next_stringref_)) + if (pack_strings_ && b.size() >= jsoncons::cbor::detail::min_length_for_stringref(next_stringref_)) { byte_string_type bs(b.data(), b.size(), alloc_); auto it = bytestringref_map_.find(bs); @@ -1347,7 +1351,7 @@ class basic_cbor_encoder final : public basic_json_visitor const ser_context& context, std::error_code& ec) override { - if (options_.use_typed_arrays()) + if (use_typed_arrays_) { switch (tag) { @@ -1380,7 +1384,7 @@ class basic_cbor_encoder final : public basic_json_visitor const ser_context& context, std::error_code& ec) override { - if (options_.use_typed_arrays()) + if (use_typed_arrays_) { write_typed_array_tag(std::integral_constant(), uint16_t(), @@ -1409,7 +1413,7 @@ class basic_cbor_encoder final : public basic_json_visitor const ser_context& context, std::error_code& ec) override { - if (options_.use_typed_arrays()) + if (use_typed_arrays_) { write_typed_array_tag(std::integral_constant(), uint32_t(), @@ -1438,7 +1442,7 @@ class basic_cbor_encoder final : public basic_json_visitor const ser_context& context, std::error_code& ec) override { - if (options_.use_typed_arrays()) + if (use_typed_arrays_) { write_typed_array_tag(std::integral_constant(), uint64_t(), @@ -1467,7 +1471,7 @@ class basic_cbor_encoder final : public basic_json_visitor const ser_context& context, std::error_code& ec) override { - if (options_.use_typed_arrays()) + if (use_typed_arrays_) { write_tag(0x48); std::vector v(data.size()*sizeof(int8_t)); @@ -1494,7 +1498,7 @@ class basic_cbor_encoder final : public basic_json_visitor const ser_context& context, std::error_code& ec) override { - if (options_.use_typed_arrays()) + if (use_typed_arrays_) { write_typed_array_tag(std::integral_constant(), int16_t(), @@ -1523,7 +1527,7 @@ class basic_cbor_encoder final : public basic_json_visitor const ser_context& context, std::error_code& ec) override { - if (options_.use_typed_arrays()) + if (use_typed_arrays_) { write_typed_array_tag(std::integral_constant(), int32_t(), @@ -1552,7 +1556,7 @@ class basic_cbor_encoder final : public basic_json_visitor const ser_context& context, std::error_code& ec) override { - if (options_.use_typed_arrays()) + if (use_typed_arrays_) { write_typed_array_tag(std::integral_constant(), int64_t(), @@ -1582,7 +1586,7 @@ class basic_cbor_encoder final : public basic_json_visitor const ser_context& context, std::error_code& ec) override { - if (options_.use_typed_arrays()) + if (use_typed_arrays_) { write_typed_array_tag(std::integral_constant(), half_arg, @@ -1611,7 +1615,7 @@ class basic_cbor_encoder final : public basic_json_visitor const ser_context& context, std::error_code& ec) override { - if (options_.use_typed_arrays()) + if (use_typed_arrays_) { write_typed_array_tag(std::integral_constant(), float(), @@ -1640,7 +1644,7 @@ class basic_cbor_encoder final : public basic_json_visitor const ser_context& context, std::error_code& ec) override { - if (options_.use_typed_arrays()) + if (use_typed_arrays_) { write_typed_array_tag(std::integral_constant(), double(), diff --git a/include/jsoncons_ext/cbor/cbor_error.hpp b/include/jsoncons_ext/cbor/cbor_error.hpp index d6debbf..cb597d3 100644 --- a/include/jsoncons_ext/cbor/cbor_error.hpp +++ b/include/jsoncons_ext/cbor/cbor_error.hpp @@ -1,4 +1,4 @@ -/// Copyright 2013-2025 Daniel Parker +/// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/include/jsoncons_ext/cbor/cbor_event_reader.hpp b/include/jsoncons_ext/cbor/cbor_event_reader.hpp index b9c4408..94c2899 100644 --- a/include/jsoncons_ext/cbor/cbor_event_reader.hpp +++ b/include/jsoncons_ext/cbor/cbor_event_reader.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include #include #include @@ -51,9 +51,9 @@ namespace cbor { : parser_(std::forward(source), options, alloc) { parser_.cursor_mode(true); - if (!done()) + if (!parser_.done()) { - next(); + read_next(); } } @@ -88,9 +88,9 @@ namespace cbor { eof_(false) { parser_.cursor_mode(true); - if (!done()) + if (!parser_.done()) { - next(ec); + read_next(ec); } } @@ -104,9 +104,9 @@ namespace cbor { parser_.reset(); cursor_visitor_.reset(); eof_ = false; - if (!done()) + if (!read_done()) { - next(); + read_next(); } } @@ -116,9 +116,9 @@ namespace cbor { parser_.reset(std::forward(source)); cursor_visitor_.reset(); eof_ = false; - if (!done()) + if (!read_done()) { - next(); + read_next(); } } @@ -127,9 +127,9 @@ namespace cbor { parser_.reset(); cursor_visitor_.reset(); eof_ = false; - if (!done()) + if (!read_done()) { - next(ec); + read_next(ec); } } @@ -139,15 +139,15 @@ namespace cbor { parser_.reset(std::forward(source)); cursor_visitor_.reset(); eof_ = false; - if (!done()) + if (!read_done()) { - next(ec); + read_next(ec); } } bool done() const override { - return parser_.done(); + return read_done(); } bool is_typed_array() const @@ -206,12 +206,7 @@ namespace cbor { void next() override { - std::error_code ec; - next(ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column())); - } + read_next(); } void next(std::error_code& ec) override @@ -247,6 +242,22 @@ namespace cbor { } private: + + bool read_done() const + { + return parser_.done(); + } + + void read_next() + { + std::error_code ec; + read_next(ec); + if (JSONCONS_UNLIKELY(ec)) + { + JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column())); + } + } + void read_next(std::error_code& ec) { if (cursor_visitor_.in_available()) diff --git a/include/jsoncons_ext/cbor/cbor_options.hpp b/include/jsoncons_ext/cbor/cbor_options.hpp index ef454a8..11f31b9 100644 --- a/include/jsoncons_ext/cbor/cbor_options.hpp +++ b/include/jsoncons_ext/cbor/cbor_options.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -20,19 +20,14 @@ class cbor_options_common { friend class cbor_options; - int max_nesting_depth_; + int max_nesting_depth_{1024}; protected: + cbor_options_common() = default; + cbor_options_common(const cbor_options_common&) = default; virtual ~cbor_options_common() = default; - cbor_options_common() - : max_nesting_depth_(1024) - { - } - - cbor_options_common(const cbor_options_common&) = default; cbor_options_common& operator=(const cbor_options_common&) = default; - cbor_options_common(cbor_options_common&&) = default; - cbor_options_common& operator=(cbor_options_common&&) = default; + public: int max_nesting_depth() const { @@ -44,24 +39,24 @@ class cbor_decode_options : public virtual cbor_options_common { friend class cbor_options; public: - cbor_decode_options() - { - } + cbor_decode_options() = default; + cbor_decode_options(const cbor_decode_options& other) = default; +protected: + cbor_decode_options& operator=(const cbor_decode_options& other) = default; }; class cbor_encode_options : public virtual cbor_options_common { friend class cbor_options; - bool use_stringref_; - bool use_typed_arrays_; + bool use_stringref_{false}; + bool use_typed_arrays_{false}; +public: + cbor_encode_options() = default; + cbor_encode_options(const cbor_encode_options& other) = default; +protected: + cbor_encode_options& operator=(const cbor_encode_options& other) = default; public: - cbor_encode_options() - : use_stringref_(false), - use_typed_arrays_(false) - { - } - bool pack_strings() const { return use_stringref_; @@ -80,6 +75,10 @@ class cbor_options final : public cbor_decode_options, public cbor_encode_option using cbor_encode_options::pack_strings; using cbor_encode_options::use_typed_arrays; + cbor_options() = default; + cbor_options(const cbor_options& other) = default; + cbor_options& operator=(const cbor_options& other) = default; + cbor_options& max_nesting_depth(int value) { this->max_nesting_depth_ = value; diff --git a/include/jsoncons_ext/cbor/cbor_parser.hpp b/include/jsoncons_ext/cbor/cbor_parser.hpp index 7cf9db9..8867a56 100644 --- a/include/jsoncons_ext/cbor/cbor_parser.hpp +++ b/include/jsoncons_ext/cbor/cbor_parser.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include #include #include @@ -136,7 +136,7 @@ class basic_cbor_parser : public ser_context std::bitset other_tags_; allocator_type alloc_; Source source_; - cbor_decode_options options_; + int max_nesting_depth_; string_type text_buffer_; byte_string_type bytes_buffer_; std::vector state_stack_; @@ -186,7 +186,7 @@ class basic_cbor_parser : public ser_context const Allocator& alloc = Allocator()) : alloc_(alloc), source_(std::forward(source)), - options_(options), + max_nesting_depth_(options.max_nesting_depth()), text_buffer_(alloc), bytes_buffer_(alloc), state_stack_(alloc), @@ -657,7 +657,7 @@ class basic_cbor_parser : public ser_context void begin_array(item_event_visitor& visitor, uint8_t info, std::error_code& ec) { - if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth())) + if (JSONCONS_UNLIKELY(++nesting_depth_ > max_nesting_depth_)) { ec = cbor_errc::max_nesting_depth_exceeded; more_ = false; @@ -715,7 +715,7 @@ class basic_cbor_parser : public ser_context void begin_object(item_event_visitor& visitor, uint8_t info, std::error_code& ec) { - if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth())) + if (JSONCONS_UNLIKELY(++nesting_depth_ > max_nesting_depth_)) { ec = cbor_errc::max_nesting_depth_exceeded; more_ = false; @@ -1221,7 +1221,7 @@ class basic_cbor_parser : public ser_context { return; } - jsoncons::detail::from_integer(val, str); + jsoncons::from_integer(val, str); break; } case jsoncons::cbor::detail::cbor_major_type::negative_integer: @@ -1231,7 +1231,7 @@ class basic_cbor_parser : public ser_context { return; } - jsoncons::detail::from_integer(val, str); + jsoncons::from_integer(val, str); break; } case jsoncons::cbor::detail::cbor_major_type::semantic_tag: @@ -1296,11 +1296,11 @@ class basic_cbor_parser : public ser_context if (str[0] == '-') { result.push_back('-'); - jsoncons::detail::prettify_string(str.c_str()+1, str.size()-1, (int)exponent, -4, 17, result); + jsoncons::prettify_string(str.c_str()+1, str.size()-1, (int)exponent, -4, 17, result); } else { - jsoncons::detail::prettify_string(str.c_str(), str.size(), (int)exponent, -4, 17, result); + jsoncons::prettify_string(str.c_str(), str.size(), (int)exponent, -4, 17, result); } } else @@ -1379,7 +1379,7 @@ class basic_cbor_parser : public ser_context } str.push_back('0'); str.push_back('x'); - jsoncons::detail::integer_to_hex(val, str); + jsoncons::integer_to_hex(val, str); break; } case jsoncons::cbor::detail::cbor_major_type::negative_integer: @@ -1392,7 +1392,7 @@ class basic_cbor_parser : public ser_context str.push_back('-'); str.push_back('0'); str.push_back('x'); - jsoncons::detail::integer_to_hex(static_cast(-val), str); + jsoncons::integer_to_hex(static_cast(-val), str); break; } case jsoncons::cbor::detail::cbor_major_type::semantic_tag: @@ -1453,12 +1453,12 @@ class basic_cbor_parser : public ser_context str.push_back('p'); if (exponent >=0) { - jsoncons::detail::integer_to_hex(static_cast(exponent), str); + jsoncons::integer_to_hex(static_cast(exponent), str); } else { str.push_back('-'); - jsoncons::detail::integer_to_hex(static_cast(-exponent), str); + jsoncons::integer_to_hex(static_cast(-exponent), str); } } diff --git a/include/jsoncons_ext/cbor/cbor_reader.hpp b/include/jsoncons_ext/cbor/cbor_reader.hpp index 3cebf2d..e09776d 100644 --- a/include/jsoncons_ext/cbor/cbor_reader.hpp +++ b/include/jsoncons_ext/cbor/cbor_reader.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/include/jsoncons_ext/cbor/decode_cbor.hpp b/include/jsoncons_ext/cbor/decode_cbor.hpp index d7b2b3b..c108ffe 100644 --- a/include/jsoncons_ext/cbor/decode_cbor.hpp +++ b/include/jsoncons_ext/cbor/decode_cbor.hpp @@ -1,4 +1,4 @@ -// Copyright 2017-2025 Daniel Parker +// Copyright 2017-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -12,12 +12,13 @@ #include #include +#include #include #include #include -#include #include -#include +#include +#include #include #include @@ -26,180 +27,239 @@ namespace jsoncons { namespace cbor { - template - typename std::enable_if::value && - ext_traits::is_byte_sequence::value,T>::type - decode_cbor(const Source& v, - const cbor_decode_options& options = cbor_decode_options()) - { - jsoncons::json_decoder decoder; - auto adaptor = make_json_visitor_adaptor(decoder); - basic_cbor_reader reader(v, adaptor, options); - reader.read(); - if (!decoder.is_valid()) - { - JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); - } - return decoder.get_result(); - } - - template - typename std::enable_if::value && - ext_traits::is_byte_sequence::value,T>::type - decode_cbor(const Source& v, - const cbor_decode_options& options = cbor_decode_options()) - { - basic_cbor_cursor cursor(v, options); - json_decoder> decoder{}; - - std::error_code ec; - T val = decode_traits::decode(cursor, decoder, ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); - } - return val; - } - - template - typename std::enable_if::value,T>::type - decode_cbor(std::istream& is, - const cbor_decode_options& options = cbor_decode_options()) - { - jsoncons::json_decoder decoder; - auto adaptor = make_json_visitor_adaptor(decoder); - cbor_stream_reader reader(is, adaptor, options); - reader.read(); - if (!decoder.is_valid()) - { - JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); - } - return decoder.get_result(); - } - - template - typename std::enable_if::value,T>::type - decode_cbor(std::istream& is, - const cbor_decode_options& options = cbor_decode_options()) - { - basic_cbor_cursor cursor(is, options); - json_decoder> decoder{}; - - std::error_code ec; - T val = decode_traits::decode(cursor, decoder, ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); - } - return val; - } - - template - typename std::enable_if::value,T>::type - decode_cbor(InputIt first, InputIt last, - const cbor_decode_options& options = cbor_decode_options()) - { - jsoncons::json_decoder decoder; - auto adaptor = make_json_visitor_adaptor(decoder); - basic_cbor_reader> reader(binary_iterator_source(first, last), adaptor, options); - reader.read(); - if (!decoder.is_valid()) - { - JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); - } - return decoder.get_result(); - } - - template - typename std::enable_if::value,T>::type - decode_cbor(InputIt first, InputIt last, - const cbor_decode_options& options = cbor_decode_options()) - { - basic_cbor_cursor> cursor(binary_iterator_source(first, last), options); - json_decoder> decoder{}; - - std::error_code ec; - T val = decode_traits::decode(cursor, decoder, ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); - } - return val; - } - - // With leading allocator_set parameter - - template - typename std::enable_if::value && - ext_traits::is_byte_sequence::value,T>::type - decode_cbor(const allocator_set& alloc_set, - const Source& v, - const cbor_decode_options& options = cbor_decode_options()) - { - json_decoder decoder(alloc_set.get_allocator(), alloc_set.get_temp_allocator()); - auto adaptor = make_json_visitor_adaptor(decoder); - basic_cbor_reader reader(v, adaptor, options, alloc_set.get_temp_allocator()); - reader.read(); - if (!decoder.is_valid()) - { - JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); - } - return decoder.get_result(); - } - - template - typename std::enable_if::value && - ext_traits::is_byte_sequence::value,T>::type - decode_cbor(const allocator_set& alloc_set, - const Source& v, - const cbor_decode_options& options = cbor_decode_options()) - { - basic_cbor_cursor cursor(v, options, alloc_set.get_temp_allocator()); - json_decoder,TempAllocator> decoder(alloc_set.get_temp_allocator(), alloc_set.get_temp_allocator()); - - std::error_code ec; - T val = decode_traits::decode(cursor, decoder, ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); - } - return val; - } - - template - typename std::enable_if::value,T>::type - decode_cbor(const allocator_set& alloc_set, - std::istream& is, - const cbor_decode_options& options = cbor_decode_options()) - { - json_decoder decoder(alloc_set.get_allocator(), alloc_set.get_temp_allocator()); - auto adaptor = make_json_visitor_adaptor(decoder); - basic_cbor_reader reader(is, adaptor, options, alloc_set.get_temp_allocator()); - reader.read(); - if (!decoder.is_valid()) - { - JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); - } - return decoder.get_result(); - } - - template - typename std::enable_if::value,T>::type - decode_cbor(const allocator_set& alloc_set, - std::istream& is, - const cbor_decode_options& options = cbor_decode_options()) - { - basic_cbor_cursor cursor(is, options, alloc_set.get_temp_allocator()); - json_decoder,TempAllocator> decoder(alloc_set.get_temp_allocator(), alloc_set.get_temp_allocator()); - - std::error_code ec; - T val = decode_traits::decode(cursor, decoder, ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); - } - return val; +template +typename std::enable_if::value && + ext_traits::is_byte_sequence::value,read_result>::type +try_decode_cbor(const BytesLike& v, + const cbor_decode_options& options = cbor_decode_options()) +{ + using value_type = T; + using result_type = read_result; + + std::error_code ec; + jsoncons::json_decoder decoder; + auto adaptor = make_json_visitor_adaptor(decoder); + basic_cbor_reader reader(v, adaptor, options); + reader.read(ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, reader.line(), reader.column()}; + } + if (JSONCONS_UNLIKELY(!decoder.is_valid())) + { + return result_type{jsoncons::unexpect, conv_errc::conversion_failed, reader.line(), reader.column()}; + } + return result_type{decoder.get_result()}; +} + +template +typename std::enable_if::value && + ext_traits::is_byte_sequence::value,read_result>::type +try_decode_cbor(const BytesLike& v, + const cbor_decode_options& options = cbor_decode_options()) +{ + using value_type = T; + using result_type = read_result; + + std::error_code ec; + basic_cbor_cursor cursor(v, options, ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, cursor.line(), cursor.column()}; + } + + return reflect::decode_traits::try_decode(make_alloc_set(), cursor); +} + +template +typename std::enable_if::value,read_result>::type +try_decode_cbor(std::istream& is, + const cbor_decode_options& options = cbor_decode_options()) +{ + using value_type = T; + using result_type = read_result; + + std::error_code ec; + jsoncons::json_decoder decoder; + auto adaptor = make_json_visitor_adaptor(decoder); + cbor_stream_reader reader(is, adaptor, options); + reader.read(ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, reader.line(), reader.column()}; + } + if (JSONCONS_UNLIKELY(!decoder.is_valid())) + { + return result_type{jsoncons::unexpect, conv_errc::conversion_failed, reader.line(), reader.column()}; + } + return result_type{decoder.get_result()}; +} + +template +typename std::enable_if::value,read_result>::type +try_decode_cbor(std::istream& is, + const cbor_decode_options& options = cbor_decode_options()) +{ + using value_type = T; + using result_type = read_result; + + std::error_code ec; + basic_cbor_cursor cursor(is, options, ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, cursor.line(), cursor.column()}; + } + + return reflect::decode_traits::try_decode(make_alloc_set(), cursor); +} + +template +typename std::enable_if::value,read_result>::type +try_decode_cbor(InputIt first, InputIt last, + const cbor_decode_options& options = cbor_decode_options()) +{ + using value_type = T; + using result_type = read_result; + + std::error_code ec; + jsoncons::json_decoder decoder; + auto adaptor = make_json_visitor_adaptor(decoder); + basic_cbor_reader> reader(binary_iterator_source(first, last), adaptor, options); + reader.read(ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, reader.line(), reader.column()}; + } + if (JSONCONS_UNLIKELY(!decoder.is_valid())) + { + return result_type{jsoncons::unexpect, conv_errc::conversion_failed, reader.line(), reader.column()}; + } + return result_type{decoder.get_result()}; +} + +template +typename std::enable_if::value,read_result>::type +try_decode_cbor(InputIt first, InputIt last, + const cbor_decode_options& options = cbor_decode_options()) +{ + using value_type = T; + using result_type = read_result; + + std::error_code ec; + basic_cbor_cursor> cursor(binary_iterator_source(first, last), options, ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, cursor.line(), cursor.column()}; + } + + return reflect::decode_traits::try_decode(make_alloc_set(), cursor); +} + +// With leading allocator_set parameter + +template +typename std::enable_if::value && + ext_traits::is_byte_sequence::value,read_result>::type +try_decode_cbor(const allocator_set& aset, + const BytesLike& v, + const cbor_decode_options& options = cbor_decode_options()) +{ + using value_type = T; + using result_type = read_result; + + std::error_code ec; + json_decoder decoder(aset.get_allocator(), aset.get_temp_allocator()); + auto adaptor = make_json_visitor_adaptor(decoder); + basic_cbor_reader reader(v, adaptor, options, aset.get_temp_allocator()); + reader.read(ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, reader.line(), reader.column()}; + } + if (JSONCONS_UNLIKELY(!decoder.is_valid())) + { + return result_type{jsoncons::unexpect, conv_errc::conversion_failed, reader.line(), reader.column()}; + } + return result_type{decoder.get_result()}; +} + +template +typename std::enable_if::value && + ext_traits::is_byte_sequence::value,read_result>::type +try_decode_cbor(const allocator_set& aset, + const BytesLike& v, + const cbor_decode_options& options = cbor_decode_options()) +{ + using value_type = T; + using result_type = read_result; + + std::error_code ec; + basic_cbor_cursor cursor(std::allocator_arg, aset.get_temp_allocator(), v, options, ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, cursor.line(), cursor.column()}; + } + + return reflect::decode_traits::try_decode(aset, cursor); +} + +template +typename std::enable_if::value,read_result>::type +try_decode_cbor(const allocator_set& aset, + std::istream& is, + const cbor_decode_options& options = cbor_decode_options()) +{ + using value_type = T; + using result_type = read_result; + using byte_allocator_type = typename std::allocator_traits:: template rebind_alloc; + using stream_source_type = stream_source; + + std::error_code ec; + json_decoder decoder(aset.get_allocator(), aset.get_temp_allocator()); + auto adaptor = make_json_visitor_adaptor(decoder); + basic_cbor_reader reader(stream_source_type(is,aset.get_temp_allocator()), + adaptor, options, aset.get_temp_allocator()); + reader.read(ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, reader.line(), reader.column()}; + } + if (JSONCONS_UNLIKELY(!decoder.is_valid())) + { + return result_type{jsoncons::unexpect, conv_errc::conversion_failed, reader.line(), reader.column()}; + } + return result_type{decoder.get_result()}; +} + +template +typename std::enable_if::value,read_result>::type +try_decode_cbor(const allocator_set& aset, + std::istream& is, + const cbor_decode_options& options = cbor_decode_options()) +{ + using value_type = T; + using result_type = read_result; + + std::error_code ec; + basic_cbor_cursor cursor(std::allocator_arg, aset.get_temp_allocator(), is, options, ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, cursor.line(), cursor.column()}; + } + + return reflect::decode_traits::try_decode(aset, cursor); +} + +template +T decode_cbor(Args&& ... args) +{ + auto result = try_decode_cbor(std::forward(args)...); + if (!result) + { + JSONCONS_THROW(ser_error(result.error().code(), result.error().line(), result.error().column())); } + return std::move(*result); +} } // namespace cbor } // namespace jsoncons diff --git a/include/jsoncons_ext/cbor/encode_cbor.hpp b/include/jsoncons_ext/cbor/encode_cbor.hpp index 72a1351..98dc23b 100644 --- a/include/jsoncons_ext/cbor/encode_cbor.hpp +++ b/include/jsoncons_ext/cbor/encode_cbor.hpp @@ -1,4 +1,4 @@ -// Copyright 2017-2025 Daniel Parker +// Copyright 2017-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -10,141 +10,130 @@ #include // std::basic_ostream #include // std::enable_if +#include #include #include -#include #include -#include +#include +#include #include namespace jsoncons { namespace cbor { - // to bytes - - template - typename std::enable_if::value && - ext_traits::is_back_insertable_byte_container::value,void>::type - encode_cbor(const T& j, - ByteContainer& cont, - const cbor_encode_options& options = cbor_encode_options()) - { - using char_type = typename T::char_type; - basic_cbor_encoder> encoder(cont, options); - auto adaptor = make_json_visitor_adaptor>(encoder); - j.dump(adaptor); - } - - template - typename std::enable_if::value && - ext_traits::is_back_insertable_byte_container::value,void>::type - encode_cbor(const T& val, ByteContainer& cont, - const cbor_encode_options& options = cbor_encode_options()) - { - basic_cbor_encoder> encoder(cont, options); - std::error_code ec; - encode_traits::encode(val, encoder, json(), ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec)); - } - } - - // stream - - template - typename std::enable_if::value,void>::type - encode_cbor(const T& j, - std::ostream& os, - const cbor_encode_options& options = cbor_encode_options()) - { - using char_type = typename T::char_type; - cbor_stream_encoder encoder(os, options); - auto adaptor = make_json_visitor_adaptor>(encoder); - j.dump(adaptor); - } - - template - typename std::enable_if::value,void>::type - encode_cbor(const T& val, - std::ostream& os, - const cbor_encode_options& options = cbor_encode_options()) - { - cbor_stream_encoder encoder(os, options); - std::error_code ec; - encode_traits::encode(val, encoder, json(), ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec)); - } - } - - // temp_allocator_arg - - // to bytes - - template - typename std::enable_if::value && - ext_traits::is_back_insertable_byte_container::value,void>::type - encode_cbor(const allocator_set& alloc_set, - const T& j, - ByteContainer& cont, - const cbor_encode_options& options = cbor_encode_options()) - { - using char_type = typename T::char_type; - basic_cbor_encoder,TempAllocator> encoder(cont, options, alloc_set.get_temp_allocator()); - auto adaptor = make_json_visitor_adaptor>(encoder); - j.dump(adaptor); - } - - template - typename std::enable_if::value && - ext_traits::is_back_insertable_byte_container::value,void>::type - encode_cbor(const allocator_set& alloc_set, - const T& val, - ByteContainer& cont, - const cbor_encode_options& options = cbor_encode_options()) - { - basic_cbor_encoder,TempAllocator> encoder(cont, options, alloc_set.get_temp_allocator()); - std::error_code ec; - encode_traits::encode(val, encoder, json(), ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec)); - } - } - - // stream - - template - typename std::enable_if::value,void>::type - encode_cbor(const allocator_set& alloc_set, - const T& j, - std::ostream& os, - const cbor_encode_options& options = cbor_encode_options()) - { - using char_type = typename T::char_type; - basic_cbor_encoder encoder(os, options, alloc_set.get_temp_allocator()); - auto adaptor = make_json_visitor_adaptor>(encoder); - j.dump(adaptor); - } - - template - typename std::enable_if::value,void>::type - encode_cbor(const allocator_set& alloc_set, - const T& val, - std::ostream& os, - const cbor_encode_options& options = cbor_encode_options()) +// to bytes + +template +typename std::enable_if::value && + ext_traits::is_back_insertable_byte_container::value,write_result>::type +try_encode_cbor(const T& j, + ByteContainer& cont, + const cbor_encode_options& options = cbor_encode_options()) +{ + using char_type = typename T::char_type; + basic_cbor_encoder> encoder(cont, options); + auto adaptor = make_json_visitor_adaptor>(encoder); + return j.try_dump(adaptor); +} + +template +typename std::enable_if::value && + ext_traits::is_back_insertable_byte_container::value,write_result>::type +try_encode_cbor(const T& val, ByteContainer& cont, + const cbor_encode_options& options = cbor_encode_options()) +{ + basic_cbor_encoder> encoder(cont, options); + return reflect::encode_traits::try_encode(make_alloc_set(), val, encoder); +} + +// stream + +template +typename std::enable_if::value,write_result>::type +try_encode_cbor(const T& j, + std::ostream& os, + const cbor_encode_options& options = cbor_encode_options()) +{ + using char_type = typename T::char_type; + cbor_stream_encoder encoder(os, options); + auto adaptor = make_json_visitor_adaptor>(encoder); + return j.try_dump(adaptor); +} + +template +typename std::enable_if::value,write_result>::type +try_encode_cbor(const T& val, + std::ostream& os, + const cbor_encode_options& options = cbor_encode_options()) +{ + cbor_stream_encoder encoder(os, options); + return reflect::encode_traits::try_encode(make_alloc_set(), val, encoder); +} + +// to bytes + +template +typename std::enable_if::value && + ext_traits::is_back_insertable_byte_container::value,write_result>::type +try_encode_cbor(const allocator_set& aset, + const T& j, + ByteContainer& cont, + const cbor_encode_options& options = cbor_encode_options()) +{ + using char_type = typename T::char_type; + basic_cbor_encoder,TempAlloc> encoder(cont, options, aset.get_temp_allocator()); + auto adaptor = make_json_visitor_adaptor>(encoder); + return j.try_dump(adaptor); +} + +template +typename std::enable_if::value && + ext_traits::is_back_insertable_byte_container::value,write_result>::type +try_encode_cbor(const allocator_set& aset, + const T& val, + ByteContainer& cont, + const cbor_encode_options& options = cbor_encode_options()) +{ + basic_cbor_encoder,TempAlloc> encoder(cont, options, aset.get_temp_allocator()); + return reflect::encode_traits::try_encode(aset, val, encoder); +} + +// stream + +template +typename std::enable_if::value,write_result>::type +try_encode_cbor(const allocator_set& aset, + const T& j, + std::ostream& os, + const cbor_encode_options& options = cbor_encode_options()) +{ + using char_type = typename T::char_type; + basic_cbor_encoder encoder(os, options, aset.get_temp_allocator()); + auto adaptor = make_json_visitor_adaptor>(encoder); + return j.try_dump(adaptor); +} + +template +typename std::enable_if::value,write_result>::type +try_encode_cbor(const allocator_set& aset, + const T& val, + std::ostream& os, + const cbor_encode_options& options = cbor_encode_options()) +{ + basic_cbor_encoder encoder(os, options, aset.get_temp_allocator()); + return reflect::encode_traits::try_encode(aset, val, encoder); +} + +template +void encode_cbor(Args&& ... args) +{ + auto r = try_encode_cbor(std::forward(args)...); + if (!r) { - basic_cbor_encoder encoder(os, options, alloc_set.get_temp_allocator()); - std::error_code ec; - encode_traits::encode(val, encoder, json(), ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec)); - } + JSONCONS_THROW(ser_error(r.error())); } +} } // namespace cbor } // namespace jsoncons diff --git a/include/jsoncons_ext/csv/csv.hpp b/include/jsoncons_ext/csv/csv.hpp index 8e8754f..c86779a 100644 --- a/include/jsoncons_ext/csv/csv.hpp +++ b/include/jsoncons_ext/csv/csv.hpp @@ -1,4 +1,4 @@ -/// Copyright 2013-2025 Daniel Parker +/// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/include/jsoncons_ext/csv/csv_cursor.hpp b/include/jsoncons_ext/csv/csv_cursor.hpp index 11bda47..0677c09 100644 --- a/include/jsoncons_ext/csv/csv_cursor.hpp +++ b/include/jsoncons_ext/csv/csv_cursor.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include #include #include @@ -56,33 +56,63 @@ class basic_csv_cursor : public basic_staj_cursor, private virtual ser_co template basic_csv_cursor(Sourceable&& source, const basic_csv_decode_options& options = basic_csv_decode_options(), - std::function err_handler = default_csv_parsing(), const Allocator& alloc = Allocator(), typename std::enable_if,Sourceable>::value>::type* = 0) : source_(std::forward(source)), - parser_(options,err_handler,alloc) + parser_(options, alloc) { parser_.cursor_mode(true); - if (!done()) + if (!read_done()) { - next(); + read_next(); } } template basic_csv_cursor(Sourceable&& source, const basic_csv_decode_options& options = basic_csv_decode_options(), - std::function err_handler = default_csv_parsing(), const Allocator& alloc = Allocator(), typename std::enable_if,Sourceable>::value>::type* = 0) : source_(), - parser_(options,err_handler,alloc) + parser_(options,alloc) { parser_.cursor_mode(true); jsoncons::basic_string_view sv(std::forward(source)); initialize_with_string_view(sv); } +#if !defined(JSONCONS_NO_DEPRECATED) + + template + basic_csv_cursor(Sourceable&& source, + const basic_csv_decode_options& options, + std::function err_handler, + const Allocator& alloc = Allocator(), + typename std::enable_if,Sourceable>::value>::type* = 0) + : source_(std::forward(source)), + parser_(options,err_handler,alloc) + { + parser_.cursor_mode(true); + if (!read_done()) + { + read_next(); + } + } + + template + basic_csv_cursor(Sourceable&& source, + const basic_csv_decode_options& options, + std::function err_handler, + const Allocator& alloc = Allocator(), + typename std::enable_if,Sourceable>::value>::type* = 0) + : source_(), + parser_(options,err_handler,alloc) + { + parser_.cursor_mode(true); + jsoncons::basic_string_view sv(std::forward(source)); + initialize_with_string_view(sv); + } +#endif // Constructors that set parse error codes template @@ -90,7 +120,6 @@ class basic_csv_cursor : public basic_staj_cursor, private virtual ser_co : basic_csv_cursor(std::allocator_arg, Allocator(), std::forward(source), basic_csv_decode_options(), - default_csv_parsing(), ec) { } @@ -102,11 +131,42 @@ class basic_csv_cursor : public basic_staj_cursor, private virtual ser_co : basic_csv_cursor(std::allocator_arg, Allocator(), std::forward(source), options, - default_csv_parsing(), ec) { } + template + basic_csv_cursor(std::allocator_arg_t, const Allocator& alloc, + Sourceable&& source, + const basic_csv_decode_options& options, + std::error_code& ec, + typename std::enable_if,Sourceable>::value>::type* = 0) + : source_(std::forward(source)), + parser_(options,alloc) + { + parser_.cursor_mode(true); + if (!read_done()) + { + read_next(ec); + } + } + + template + basic_csv_cursor(std::allocator_arg_t, const Allocator& alloc, + Sourceable&& source, + const basic_csv_decode_options& options, + std::error_code& ec, + typename std::enable_if,Sourceable>::value>::type* = 0) + : source_(), + parser_(options,alloc) + { + parser_.cursor_mode(true); + jsoncons::basic_string_view sv(std::forward(source)); + initialize_with_string_view(sv, ec); + } + +#if !defined(JSONCONS_NO_DEPRECATED) + template basic_csv_cursor(Sourceable&& source, const basic_csv_decode_options& options, @@ -131,9 +191,9 @@ class basic_csv_cursor : public basic_staj_cursor, private virtual ser_co parser_(options,err_handler,alloc) { parser_.cursor_mode(true); - if (!done()) + if (!read_done()) { - next(ec); + read_next(ec); } } @@ -151,7 +211,7 @@ class basic_csv_cursor : public basic_staj_cursor, private virtual ser_co jsoncons::basic_string_view sv(std::forward(source)); initialize_with_string_view(sv, ec); } - +#endif ~basic_csv_cursor() = default; basic_csv_cursor& operator=(const basic_csv_cursor&) = delete; @@ -164,9 +224,9 @@ class basic_csv_cursor : public basic_staj_cursor, private virtual ser_co source_ = std::forward(source); parser_.reinitialize(); cursor_visitor_.reset(); - if (!done()) + if (!read_done()) { - next(); + read_next(); } } @@ -187,9 +247,9 @@ class basic_csv_cursor : public basic_staj_cursor, private virtual ser_co source_ = std::forward(source); parser_.reinitialize(); cursor_visitor_.reset(); - if (!done()) + if (!read_done()) { - next(ec); + read_next(ec); } } @@ -253,12 +313,7 @@ class basic_csv_cursor : public basic_staj_cursor, private virtual ser_co void next() override { - std::error_code ec; - next(ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column())); - } + read_next(); } void next(std::error_code& ec) override @@ -295,6 +350,11 @@ class basic_csv_cursor : public basic_staj_cursor, private virtual ser_co private: + bool read_done() const + { + return parser_.done(); + } + void initialize_with_string_view(string_view_type sv) { auto r = unicode_traits::detect_json_encoding(sv.data(), sv.size()); @@ -304,9 +364,9 @@ class basic_csv_cursor : public basic_staj_cursor, private virtual ser_co } std::size_t offset = (r.ptr - sv.data()); parser_.update(sv.data()+offset,sv.size()-offset); - if (!done()) + if (!read_done()) { - next(); + read_next(); } } @@ -320,9 +380,19 @@ class basic_csv_cursor : public basic_staj_cursor, private virtual ser_co } std::size_t offset = (r.ptr - sv.data()); parser_.update(sv.data()+offset,sv.size()-offset); - if (!done()) + if (!read_done()) { - next(ec); + read_next(ec); + } + } + + void read_next() + { + std::error_code ec; + read_next(ec); + if (JSONCONS_UNLIKELY(ec)) + { + JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column())); } } diff --git a/include/jsoncons_ext/csv/csv_encoder.hpp b/include/jsoncons_ext/csv/csv_encoder.hpp index 5a4c8ed..4e3a9e2 100644 --- a/include/jsoncons_ext/csv/csv_encoder.hpp +++ b/include/jsoncons_ext/csv/csv_encoder.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -10,13 +10,12 @@ #include // std::array #include // std::numeric_limits #include // std::allocator -#include #include #include // std::unordered_map #include // std::move #include -#include +#include #include #include #include @@ -44,17 +43,17 @@ class basic_csv_encoder final : public basic_json_visitor using column_type = std::vector; using column_path_column_map_type = std::unordered_map,std::equal_to,string_vector_allocator_type>; private: - static jsoncons::basic_string_view null_constant() + static jsoncons::basic_string_view null_literal() { static jsoncons::basic_string_view k = JSONCONS_STRING_VIEW_CONSTANT(CharT,"null"); return k; } - static jsoncons::basic_string_view true_constant() + static jsoncons::basic_string_view true_literal() { static jsoncons::basic_string_view k = JSONCONS_STRING_VIEW_CONSTANT(CharT,"true"); return k; } - static jsoncons::basic_string_view false_constant() + static jsoncons::basic_string_view false_literal() { static jsoncons::basic_string_view k = JSONCONS_STRING_VIEW_CONSTANT(CharT,"false"); return k; @@ -128,7 +127,7 @@ class basic_csv_encoder final : public basic_json_visitor allocator_type alloc_; std::vector stack_; - jsoncons::detail::write_double fp_; + jsoncons::write_double fp_; std::vector column_names_; std::vector column_paths_; @@ -517,7 +516,7 @@ class basic_csv_encoder final : public basic_json_visitor { string_type str{alloc_}; str.push_back('/'); - jsoncons::detail::from_integer(index, str); + jsoncons::from_integer(index, str); column_paths_.emplace_back(str); column_path_value_map_.emplace(str, string_type{alloc_}); column_path_name_map_.emplace(std::move(str), item); @@ -803,7 +802,7 @@ class basic_csv_encoder final : public basic_json_visitor { stack_.back().column_path_ = parent(stack_).column_path_; stack_.back().column_path_.push_back('/'); - jsoncons::detail::from_integer(stack_.back().count_, stack_.back().column_path_); + jsoncons::from_integer(stack_.back().count_, stack_.back().column_path_); if (stack_[0].count_ == 0) { if (!has_column_mapping_) @@ -990,19 +989,19 @@ class basic_csv_encoder final : public basic_json_visitor { case byte_string_chars_format::base16: { - encode_base16(b.begin(),b.end(),s); + bytes_to_base16(b.begin(),b.end(),s); visit_string(s, semantic_tag::none, context, ec); break; } case byte_string_chars_format::base64: { - encode_base64(b.begin(),b.end(),s); + bytes_to_base64(b.begin(),b.end(),s); visit_string(s, semantic_tag::none, context, ec); break; } case byte_string_chars_format::base64url: { - encode_base64url(b.begin(),b.end(),s); + bytes_to_base64url(b.begin(),b.end(),s); visit_string(s, semantic_tag::none, context, ec); break; } @@ -1344,7 +1343,7 @@ class basic_csv_encoder final : public basic_json_visitor } else { - str.append(null_constant().data(), null_constant().size()); + str.append(null_literal().data(), null_literal().size()); } } else if (val == std::numeric_limits::infinity()) @@ -1359,7 +1358,7 @@ class basic_csv_encoder final : public basic_json_visitor } else { - str.append(null_constant().data(), null_constant().size()); + str.append(null_literal().data(), null_literal().size()); } } else @@ -1374,7 +1373,7 @@ class basic_csv_encoder final : public basic_json_visitor } else { - str.append(null_constant().data(), null_constant().size()); + str.append(null_literal().data(), null_literal().size()); } } } @@ -1386,29 +1385,29 @@ class basic_csv_encoder final : public basic_json_visitor void write_int64_value(int64_t val, string_type& str) { - jsoncons::detail::from_integer(val,str); + jsoncons::from_integer(val,str); } void write_uint64_value(uint64_t val, string_type& str) { - jsoncons::detail::from_integer(val,str); + jsoncons::from_integer(val,str); } void write_bool_value(bool val, string_type& str) { if (val) { - str.append(true_constant().data(), true_constant().size()); + str.append(true_literal().data(), true_literal().size()); } else { - str.append(false_constant().data(), false_constant().size()); + str.append(false_literal().data(), false_literal().size()); } } void write_null_value(string_type& str) { - str.append(null_constant().data(), null_constant().size()); + str.append(null_literal().data(), null_literal().size()); } }; diff --git a/include/jsoncons_ext/csv/csv_error.hpp b/include/jsoncons_ext/csv/csv_error.hpp index d95151f..8a4445f 100644 --- a/include/jsoncons_ext/csv/csv_error.hpp +++ b/include/jsoncons_ext/csv/csv_error.hpp @@ -1,4 +1,4 @@ -/// Copyright 2013-2025 Daniel Parker +/// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -24,7 +24,8 @@ namespace csv { invalid_parse_state, invalid_escaped_char, unexpected_char_between_fields, - max_nesting_depth_exceeded + max_nesting_depth_exceeded, + invalid_number }; class csv_error_category_impl @@ -55,6 +56,8 @@ class csv_error_category_impl return "Unexpected character between fields"; case csv_errc::max_nesting_depth_exceeded: return "Data item nesting exceeds limit in options"; + case csv_errc::invalid_number: + return "Invalid number"; default: return "Unknown CSV parser error"; } diff --git a/include/jsoncons_ext/csv/csv_options.hpp b/include/jsoncons_ext/csv/csv_options.hpp index 148c7fd..d6c59af 100644 --- a/include/jsoncons_ext/csv/csv_options.hpp +++ b/include/jsoncons_ext/csv/csv_options.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/include/jsoncons_ext/csv/csv_parser.hpp b/include/jsoncons_ext/csv/csv_parser.hpp index e2a89bb..6330fca 100644 --- a/include/jsoncons_ext/csv/csv_parser.hpp +++ b/include/jsoncons_ext/csv/csv_parser.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -19,14 +19,14 @@ #include #include -#include +#include #include #include #include #include #include #include -#include +#include #include #include @@ -102,10 +102,10 @@ struct default_csv_parsing namespace detail { - template + template class parse_event { - using temp_allocator_type = TempAllocator; + using temp_allocator_type = TempAlloc; using string_view_type = typename basic_json_visitor::string_view_type; using char_allocator_type = typename std::allocator_traits:: template rebind_alloc; using byte_allocator_type = typename std::allocator_traits:: template rebind_alloc; @@ -124,7 +124,7 @@ namespace detail { }; semantic_tag tag; public: - parse_event(staj_event_type event_type, semantic_tag tag, const TempAllocator& alloc) + parse_event(staj_event_type event_type, semantic_tag tag, const TempAlloc& alloc) : event_type(event_type), string_value(alloc), byte_string_value(alloc), @@ -132,7 +132,7 @@ namespace detail { { } - parse_event(const string_view_type& value, semantic_tag tag, const TempAllocator& alloc) + parse_event(const string_view_type& value, semantic_tag tag, const TempAlloc& alloc) : event_type(staj_event_type::string_value), string_value(value.data(),value.length(),alloc), byte_string_value(alloc), @@ -140,7 +140,7 @@ namespace detail { { } - parse_event(const byte_string_view& value, semantic_tag tag, const TempAllocator& alloc) + parse_event(const byte_string_view& value, semantic_tag tag, const TempAlloc& alloc) : event_type(staj_event_type::byte_string_value), string_value(alloc), byte_string_value(value.data(),value.size(),alloc), @@ -148,7 +148,7 @@ namespace detail { { } - parse_event(bool value, semantic_tag tag, const TempAllocator& alloc) + parse_event(bool value, semantic_tag tag, const TempAlloc& alloc) : event_type(staj_event_type::bool_value), string_value(alloc), byte_string_value(alloc), @@ -157,7 +157,7 @@ namespace detail { { } - parse_event(int64_t value, semantic_tag tag, const TempAllocator& alloc) + parse_event(int64_t value, semantic_tag tag, const TempAlloc& alloc) : event_type(staj_event_type::int64_value), string_value(alloc), byte_string_value(alloc), @@ -166,7 +166,7 @@ namespace detail { { } - parse_event(uint64_t value, semantic_tag tag, const TempAllocator& alloc) + parse_event(uint64_t value, semantic_tag tag, const TempAlloc& alloc) : event_type(staj_event_type::uint64_value), string_value(alloc), byte_string_value(alloc), @@ -175,7 +175,7 @@ namespace detail { { } - parse_event(double value, semantic_tag tag, const TempAllocator& alloc) + parse_event(double value, semantic_tag tag, const TempAlloc& alloc) : event_type(staj_event_type::double_value), string_value(alloc), byte_string_value(alloc), @@ -226,23 +226,23 @@ namespace detail { } }; - template + template class m_columns_filter : public basic_json_visitor { public: using string_view_type = typename basic_json_visitor::string_view_type; using char_type = CharT; - using temp_allocator_type = TempAllocator; + using temp_allocator_type = TempAlloc; using char_allocator_type = typename std::allocator_traits:: template rebind_alloc; using string_type = std::basic_string,char_allocator_type>; using string_allocator_type = typename std::allocator_traits:: template rebind_alloc; - using parse_event_allocator_type = typename std::allocator_traits:: template rebind_alloc>; - using parse_event_vector_type = std::vector, parse_event_allocator_type>; + using parse_event_allocator_type = typename std::allocator_traits:: template rebind_alloc>; + using parse_event_vector_type = std::vector, parse_event_allocator_type>; using parse_event_vector_allocator_type = typename std::allocator_traits:: template rebind_alloc; private: - TempAllocator alloc_; + TempAlloc alloc_; std::size_t name_index_{0}; int level_{0}; int level2_{0}; @@ -254,7 +254,7 @@ namespace detail { std::vector cached_events_; public: - m_columns_filter(const TempAllocator& alloc) + m_columns_filter(const TempAlloc& alloc) : alloc_(alloc), column_names_(alloc), cached_events_(alloc) @@ -532,7 +532,7 @@ namespace detail { } // namespace detail -template > +template > class basic_csv_parser : public ser_context { public: @@ -549,7 +549,7 @@ class basic_csv_parser : public ser_context } }; - using temp_allocator_type = TempAllocator; + using temp_allocator_type = TempAlloc; typedef typename std::allocator_traits:: template rebind_alloc char_allocator_type; using string_type = std::basic_string,char_allocator_type>; typedef typename std::allocator_traits:: template rebind_alloc string_allocator_type; @@ -589,7 +589,6 @@ class basic_csv_parser : public ser_context int level_{0}; std::size_t depth_{0}; std::size_t offset_{0}; - jsoncons::detail::chars_to to_double_; const CharT* begin_input_{nullptr}; const CharT* input_end_{nullptr}; const CharT* input_ptr_{nullptr}; @@ -600,7 +599,7 @@ class basic_csv_parser : public ser_context int mark_level_{0}; std::size_t header_line_offset_{0}; - detail::m_columns_filter m_columns_filter_; + detail::m_columns_filter m_columns_filter_; std::vector stack_; std::vector column_names_; std::vector column_types_; @@ -610,23 +609,77 @@ class basic_csv_parser : public ser_context std::vector,double>> string_double_map_; public: - basic_csv_parser(const TempAllocator& alloc = TempAllocator()) + basic_csv_parser() + : basic_csv_parser(basic_csv_decode_options()) + { + } + + explicit basic_csv_parser(const TempAlloc& alloc) : basic_csv_parser(basic_csv_decode_options(), default_csv_parsing(), alloc) { } + explicit basic_csv_parser(const basic_csv_decode_options& options) + : basic_csv_parser(options, TempAlloc{}) + { + } + basic_csv_parser(const basic_csv_decode_options& options, - const TempAllocator& alloc = TempAllocator()) - : basic_csv_parser(options, - default_csv_parsing(), - alloc) + const TempAlloc& alloc) + : alloc_(alloc), + state_(csv_parse_state::start), + err_handler_(default_csv_parsing()), + assume_header_(options.assume_header()), + comment_starter_(options.comment_starter()), + field_delimiter_(options.field_delimiter()), + header_lines_(options.header_lines()), + ignore_empty_values_(options.ignore_empty_values()), + ignore_empty_lines_(options.ignore_empty_lines()), + infer_types_(options.infer_types()), + lossless_number_(options.lossless_number()), + mapping_kind_(options.mapping_kind()), + max_lines_(options.max_lines()), + quote_char_(options.quote_char()), + quote_escape_char_(options.quote_escape_char()), + subfield_delimiter_(options.subfield_delimiter()), + trim_leading_(options.trim_leading()), + trim_leading_inside_quotes_(options.trim_leading_inside_quotes()), + trim_trailing_(options.trim_trailing()), + trim_trailing_inside_quotes_(options.trim_trailing_inside_quotes()), + unquoted_empty_value_is_null_(options.unquoted_empty_value_is_null()), + m_columns_filter_(alloc), + stack_(alloc), + column_names_(alloc), + column_types_(alloc), + column_defaults_(alloc), + state_stack_(alloc), + buffer_(alloc) { + if (options.enable_str_to_nan()) + { + string_double_map_.emplace_back(options.nan_to_str(),std::nan("")); + } + if (options.enable_str_to_inf()) + { + string_double_map_.emplace_back(options.inf_to_str(),std::numeric_limits::infinity()); + } + if (options.enable_str_to_neginf()) + { + string_double_map_.emplace_back(options.neginf_to_str(),-std::numeric_limits::infinity()); + } + + jsoncons::csv::detail::parse_column_types(options.column_types(), column_types_); + jsoncons::csv::detail::parse_column_names(options.column_defaults(), column_defaults_); + jsoncons::csv::detail::parse_column_names(options.column_names(), column_names_); + min_column_names_ = column_names_.size(); + initialize(); } +#if !defined(JSONCONS_NO_DEPRECATED) basic_csv_parser(std::function err_handler, - const TempAllocator& alloc = TempAllocator()) + const TempAlloc& alloc = TempAlloc()) : basic_csv_parser(basic_csv_decode_options(), err_handler, alloc) @@ -635,7 +688,7 @@ class basic_csv_parser : public ser_context basic_csv_parser(const basic_csv_decode_options& options, std::function err_handler, - const TempAllocator& alloc = TempAllocator()) + const TempAlloc& alloc = TempAlloc()) : alloc_(alloc), state_(csv_parse_state::start), err_handler_(err_handler), @@ -684,6 +737,7 @@ class basic_csv_parser : public ser_context min_column_names_ = column_names_.size(); initialize(); } +#endif ~basic_csv_parser() noexcept { @@ -1707,7 +1761,7 @@ class basic_csv_parser : public ser_context switch (mapping_kind_) { case csv_mapping_kind::n_rows: - if (unquoted_empty_value_is_null_ && buffer_.length() == 0) + if (unquoted_empty_value_is_null_ && buffer_.empty()) { visitor.null_value(semantic_tag::none, *this, ec); more_ = !cursor_mode_; @@ -1722,7 +1776,7 @@ class basic_csv_parser : public ser_context { if (column_index_ < column_names_.size() + offset_) { - if (unquoted_empty_value_is_null_ && buffer_.length() == 0) + if (unquoted_empty_value_is_null_ && buffer_.empty()) { visitor.null_value(semantic_tag::none, *this, ec); more_ = !cursor_mode_; @@ -1734,7 +1788,7 @@ class basic_csv_parser : public ser_context } else if (depth_ > 0) { - if (unquoted_empty_value_is_null_ && buffer_.length() == 0) + if (unquoted_empty_value_is_null_ && buffer_.empty()) { visitor.null_value(semantic_tag::none, *this, ec); more_ = !cursor_mode_; @@ -1784,7 +1838,7 @@ class basic_csv_parser : public ser_context { if (column_index_ < column_names_.size() + offset_) { - if (unquoted_empty_value_is_null_ && buffer_.length() == 0) + if (unquoted_empty_value_is_null_ && buffer_.empty()) { visitor.null_value(semantic_tag::none, *this, ec); more_ = !cursor_mode_; @@ -1796,7 +1850,7 @@ class basic_csv_parser : public ser_context } else if (depth_ > 0) { - if (unquoted_empty_value_is_null_ && buffer_.length() == 0) + if (unquoted_empty_value_is_null_ && buffer_.empty()) { visitor.null_value(semantic_tag::none, *this, ec); more_ = !cursor_mode_; @@ -2112,9 +2166,11 @@ class basic_csv_parser : public ser_context { switch (*p) { - case '.': - buffer.push_back(to_double_.get_decimal_point()); - state = numeric_check_state::fraction1; + case '.': + { + buffer.push_back('.'); + state = numeric_check_state::fraction1; + } break; case 'e':case 'E': buffer.push_back(*p); @@ -2135,7 +2191,7 @@ class basic_csv_parser : public ser_context buffer.push_back(*p); break; case '.': - buffer.push_back(to_double_.get_decimal_point()); + buffer.push_back('.'); state = numeric_check_state::fraction1; break; case 'e':case 'E': @@ -2260,7 +2316,7 @@ class basic_csv_parser : public ser_context if (is_negative) { int64_t val{ 0 }; - auto result = jsoncons::detail::dec_to_integer(buffer_.data(), buffer_.length(), val); + auto result = jsoncons::dec_to_integer(buffer_.data(), buffer_.length(), val); if (result) { visitor.int64_value(val, semantic_tag::none, *this, ec); @@ -2275,20 +2331,20 @@ class basic_csv_parser : public ser_context else { uint64_t val{ 0 }; - auto result = jsoncons::detail::dec_to_integer(buffer_.data(), buffer_.length(), val); + auto result = jsoncons::dec_to_integer(buffer_.data(), buffer_.length(), val); if (result) { visitor.uint64_value(val, semantic_tag::none, *this, ec); more_ = !cursor_mode_; } - else if (result.ec == jsoncons::detail::to_integer_errc::overflow) + else if (result.ec == std::errc::result_out_of_range) { visitor.string_value(buffer_, semantic_tag::bigint, *this, ec); more_ = !cursor_mode_; } else { - ec = result.ec; + ec = csv_errc::invalid_number; more_ = false; return; } @@ -2305,7 +2361,18 @@ class basic_csv_parser : public ser_context } else { - double d = to_double_(buffer.c_str(), buffer.length()); + double d{0}; + auto result = jsoncons::decstr_to_double(buffer.c_str(), buffer.length(), d); + if (result.ec == std::errc::result_out_of_range) + { + d = buffer.front() == '-' ? -HUGE_VAL : HUGE_VAL; + } + else if (result.ec == std::errc::invalid_argument) + { + ec = csv_errc::invalid_number; + more_ = false; + return; + } visitor.double_value(d, semantic_tag::none, *this, ec); more_ = !cursor_mode_; } diff --git a/include/jsoncons_ext/csv/csv_reader.hpp b/include/jsoncons_ext/csv/csv_reader.hpp index 15f0ebf..bbc1999 100644 --- a/include/jsoncons_ext/csv/csv_reader.hpp +++ b/include/jsoncons_ext/csv/csv_reader.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include #include @@ -54,59 +54,37 @@ namespace csv { basic_csv_parser parser_; public: - // Structural characters - static constexpr size_t default_max_buffer_size = 16384; - //! Parse an input stream of CSV text into a json object - /*! - \param is The input stream to read from - */ - - template - basic_csv_reader(Sourceable&& source, - basic_json_visitor& visitor, - const Allocator& alloc = Allocator()) - - : basic_csv_reader(std::forward(source), - visitor, - basic_csv_decode_options(), - default_csv_parsing(), - alloc) - { - } - template basic_csv_reader(Sourceable&& source, - basic_json_visitor& visitor, - const basic_csv_decode_options& options, - const Allocator& alloc = Allocator()) + basic_json_visitor& visitor, + const basic_csv_decode_options& options = basic_csv_decode_options{}, + const Allocator& alloc = Allocator()) + : source_(std::forward(source)), + visitor_(visitor), + parser_(options, alloc) - : basic_csv_reader(std::forward(source), - visitor, - options, - default_csv_parsing(), - alloc) { } template basic_csv_reader(Sourceable&& source, - basic_json_visitor& visitor, - std::function err_handler, - const Allocator& alloc = Allocator()) + basic_json_visitor& visitor, + const Allocator& alloc = Allocator()) : basic_csv_reader(std::forward(source), visitor, basic_csv_decode_options(), - err_handler, alloc) { } +#if !defined(JSONCONS_NO_DEPRECATED) + template basic_csv_reader(Sourceable&& source, - basic_json_visitor& visitor, - const basic_csv_decode_options& options, - std::function err_handler, - const Allocator& alloc = Allocator()) + basic_json_visitor& visitor, + const basic_csv_decode_options& options, + std::function err_handler, + const Allocator& alloc = Allocator()) : source_(std::forward(source)), visitor_(visitor), parser_(options, err_handler, alloc) @@ -114,6 +92,20 @@ namespace csv { { } + template + basic_csv_reader(Sourceable&& source, + basic_json_visitor& visitor, + std::function err_handler, + const Allocator& alloc = Allocator()) + : basic_csv_reader(std::forward(source), + visitor, + basic_csv_decode_options(), + err_handler, + alloc) + { + } +#endif + ~basic_csv_reader() noexcept = default; void read() diff --git a/include/jsoncons_ext/csv/decode_csv.hpp b/include/jsoncons_ext/csv/decode_csv.hpp index fbbad04..a05256d 100644 --- a/include/jsoncons_ext/csv/decode_csv.hpp +++ b/include/jsoncons_ext/csv/decode_csv.hpp @@ -1,4 +1,4 @@ -/// Copyright 2013-2025 Daniel Parker +/// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -7,13 +7,15 @@ #ifndef JSONCONS_EXT_CSV_DECODE_CSV_HPP #define JSONCONS_EXT_CSV_DECODE_CSV_HPP +#include #include #include -#include #include -#include +#include #include +#include +#include #include #include @@ -24,192 +26,254 @@ namespace jsoncons { namespace csv { - template - typename std::enable_if::value && - ext_traits::is_sequence_of::value,T>::type - decode_csv(const Source& s, const basic_csv_decode_options& options = basic_csv_decode_options()) - { - using char_type = typename Source::value_type; +template +typename std::enable_if::value && + ext_traits::is_sequence_of::value,read_result>::type +try_decode_csv(const CharsLike& s, const basic_csv_decode_options& options = basic_csv_decode_options()) +{ + using char_type = typename CharsLike::value_type; + using value_type = T; + using result_type = read_result; - json_decoder decoder; + std::error_code ec; - basic_csv_reader> reader(s,decoder,options); - reader.read(); - if (!decoder.is_valid()) - { - JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); - } - return decoder.get_result(); - } + json_decoder decoder; - template - typename std::enable_if::value && - ext_traits::is_char_sequence::value,T>::type - decode_csv(const Source& s, const basic_csv_decode_options& options = basic_csv_decode_options()) + basic_csv_reader> reader(s,decoder,options); + reader.read(ec); + if (JSONCONS_UNLIKELY(ec)) { - using char_type = typename Source::value_type; + return result_type{jsoncons::unexpect, ec, reader.line(), reader.column()}; + } + if (JSONCONS_UNLIKELY(!decoder.is_valid())) + { + return result_type{jsoncons::unexpect, conv_errc::conversion_failed, reader.line(), reader.column()}; + } + return result_type{decoder.get_result()}; +} + +template +typename std::enable_if::value && + ext_traits::is_char_sequence::value,read_result>::type +try_decode_csv(const CharsLike& s, const basic_csv_decode_options& options = basic_csv_decode_options()) +{ + using char_type = typename CharsLike::value_type; + using value_type = T; + using result_type = read_result; + + std::error_code ec; + + basic_csv_cursor cursor(s, options, ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, cursor.line(), cursor.column()}; + } - basic_csv_cursor cursor(s, options); - jsoncons::json_decoder> decoder; + return reflect::decode_traits::try_decode(make_alloc_set(), cursor); +} - std::error_code ec; - T val = decode_traits::decode(cursor, decoder, ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); - } - return val; - } +template +typename std::enable_if::value,read_result>::type +try_decode_csv(std::basic_istream& is, const basic_csv_decode_options& options = basic_csv_decode_options()) +{ + using char_type = CharT; + using value_type = T; + using result_type = read_result; - template - typename std::enable_if::value,T>::type - decode_csv(std::basic_istream& is, const basic_csv_decode_options& options = basic_csv_decode_options()) - { - using char_type = CharT; + std::error_code ec; - json_decoder decoder; + json_decoder decoder; - basic_csv_reader> reader(is,decoder,options); - reader.read(); - if (!decoder.is_valid()) - { - JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); - } - return decoder.get_result(); + basic_csv_reader> reader(is,decoder,options); + reader.read(ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, reader.line(), reader.column()}; } - - template - typename std::enable_if::value,T>::type - decode_csv(std::basic_istream& is, const basic_csv_decode_options& options = basic_csv_decode_options()) + if (JSONCONS_UNLIKELY(!decoder.is_valid())) { - basic_csv_cursor cursor(is, options); - jsoncons::json_decoder> decoder; - - std::error_code ec; - T val = decode_traits::decode(cursor, decoder, ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); - } - return val; + return result_type{jsoncons::unexpect, conv_errc::conversion_failed, reader.line(), reader.column()}; } - - template - typename std::enable_if::value,T>::type - decode_csv(InputIt first, InputIt last, - const basic_csv_decode_options::value_type>& options = - basic_csv_decode_options::value_type>()) + return result_type{decoder.get_result()}; +} + +template +typename std::enable_if::value,read_result>::type +try_decode_csv(std::basic_istream& is, const basic_csv_decode_options& options = basic_csv_decode_options()) +{ + using value_type = T; + using result_type = read_result; + + std::error_code ec; + basic_csv_cursor cursor(is, options, ec); + if (JSONCONS_UNLIKELY(ec)) { - using char_type = typename std::iterator_traits::value_type; - - jsoncons::json_decoder decoder; - basic_csv_reader> reader(iterator_source(first,last), decoder, options); - reader.read(); - if (!decoder.is_valid()) - { - JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); - } - return decoder.get_result(); + return result_type{jsoncons::unexpect, ec, cursor.line(), cursor.column()}; } - template - typename std::enable_if::value,T>::type - decode_csv(InputIt first, InputIt last, - const basic_csv_decode_options::value_type>& options = - basic_csv_decode_options::value_type>()) + return reflect::decode_traits::try_decode(make_alloc_set(), cursor); +} + +template +typename std::enable_if::value,read_result>::type +try_decode_csv(InputIt first, InputIt last, + const basic_csv_decode_options::value_type>& options = + basic_csv_decode_options::value_type>()) +{ + using char_type = typename std::iterator_traits::value_type; + using value_type = T; + using result_type = read_result; + + std::error_code ec; + + jsoncons::json_decoder decoder; + basic_csv_reader> reader(iterator_source(first,last), decoder, options); + reader.read(ec); + if (JSONCONS_UNLIKELY(ec)) { - using char_type = typename std::iterator_traits::value_type; - - basic_csv_cursor> cursor(iterator_source(first, last), options); - jsoncons::json_decoder> decoder; - std::error_code ec; - T val = decode_traits::decode(cursor, decoder, ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); - } - return val; + return result_type{jsoncons::unexpect, ec, reader.line(), reader.column()}; + } + if (JSONCONS_UNLIKELY(!decoder.is_valid())) + { + return result_type{jsoncons::unexpect, conv_errc::conversion_failed, reader.line(), reader.column()}; + } + return result_type{decoder.get_result()}; +} + +template +typename std::enable_if::value,read_result>::type +try_decode_csv(InputIt first, InputIt last, + const basic_csv_decode_options::value_type>& options = + basic_csv_decode_options::value_type>()) +{ + using char_type = typename std::iterator_traits::value_type; + using value_type = T; + using result_type = read_result; + + std::error_code ec; + + basic_csv_cursor> cursor(iterator_source(first, last), options, ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, cursor.line(), cursor.column()}; } - // With leading allocator_set parameter + return reflect::decode_traits::try_decode(make_alloc_set(), cursor); +} - template - typename std::enable_if::value && - ext_traits::is_sequence_of::value,T>::type - decode_csv(const allocator_set& alloc_set, - const Source& s, - const basic_csv_decode_options& options = basic_csv_decode_options()) - { - using char_type = typename Source::value_type; +// With leading allocator_set parameter + +template +typename std::enable_if::value && + ext_traits::is_sequence_of::value,read_result>::type +try_decode_csv(const allocator_set& aset, + const CharsLike& s, + const basic_csv_decode_options& options = basic_csv_decode_options()) +{ + using char_type = typename CharsLike::value_type; + using value_type = T; + using result_type = read_result; - json_decoder decoder(alloc_set.get_allocator(), alloc_set.get_temp_allocator()); + std::error_code ec; - basic_csv_reader,TempAllocator> reader(s,decoder,options,alloc_set.get_temp_allocator()); - reader.read(); - if (!decoder.is_valid()) - { - JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); - } - return decoder.get_result(); + json_decoder decoder(aset.get_allocator(), aset.get_temp_allocator()); + + basic_csv_reader,TempAlloc> reader(s,decoder,options,aset.get_temp_allocator()); + reader.read(ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, reader.line(), reader.column()}; + } + if (JSONCONS_UNLIKELY(!decoder.is_valid())) + { + return result_type{jsoncons::unexpect, conv_errc::conversion_failed, reader.line(), reader.column()}; } - - template - typename std::enable_if::value && - ext_traits::is_char_sequence::value,T>::type - decode_csv(const allocator_set& alloc_set, - const Source& s, - const basic_csv_decode_options& options = basic_csv_decode_options()) + return result_type{decoder.get_result()}; +} + +template +typename std::enable_if::value && + ext_traits::is_char_sequence::value,read_result>::type +try_decode_csv(const allocator_set& aset, + const CharsLike& s, + const basic_csv_decode_options& options = basic_csv_decode_options()) +{ + using char_type = typename CharsLike::value_type; + using value_type = T; + using result_type = read_result; + + std::error_code ec; + + basic_csv_cursor,TempAlloc> cursor( + std::allocator_arg, aset.get_temp_allocator(), s, options, ec); + if (JSONCONS_UNLIKELY(ec)) { - using char_type = typename Source::value_type; - - basic_csv_cursor,TempAllocator> cursor(s, options, alloc_set.get_temp_allocator()); - json_decoder,TempAllocator> decoder(alloc_set.get_temp_allocator(), alloc_set.get_temp_allocator()); + return result_type{jsoncons::unexpect, ec, cursor.line(), cursor.column()}; + } - std::error_code ec; - T val = decode_traits::decode(cursor, decoder, ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); - } - return val; - } - - template - typename std::enable_if::value,T>::type - decode_csv(const allocator_set& alloc_set, - std::basic_istream& is, - const basic_csv_decode_options& options = basic_csv_decode_options()) - { - using char_type = CharT; - - json_decoder decoder(alloc_set.get_allocator(), alloc_set.get_temp_allocator()); - - basic_csv_reader,TempAllocator> reader(is,decoder,options,alloc_set.get_temp_allocator()); - reader.read(); - if (!decoder.is_valid()) - { - JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); - } - return decoder.get_result(); + return reflect::decode_traits::try_decode(aset, cursor); +} + +template +typename std::enable_if::value,read_result>::type +try_decode_csv(const allocator_set& aset, + std::basic_istream& is, + const basic_csv_decode_options& options = basic_csv_decode_options()) +{ + using char_type = CharT; + using value_type = T; + using result_type = read_result; + using char_allocator_type = typename std::allocator_traits:: template rebind_alloc; + using stream_source_type = stream_source; + + std::error_code ec; + + json_decoder decoder(aset.get_allocator(), aset.get_temp_allocator()); + + basic_csv_reader reader(stream_source_type(is,aset.get_temp_allocator()), + decoder, options, aset.get_temp_allocator()); + reader.read(ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, reader.line(), reader.column()}; + } + if (JSONCONS_UNLIKELY(!decoder.is_valid())) + { + return result_type{jsoncons::unexpect, conv_errc::conversion_failed, reader.line(), reader.column()}; + } + return result_type{decoder.get_result()}; +} + +template +typename std::enable_if::value,read_result>::type +try_decode_csv(const allocator_set& aset, + std::basic_istream& is, + const basic_csv_decode_options& options = basic_csv_decode_options()) +{ + using value_type = T; + using result_type = read_result; + + std::error_code ec; + basic_csv_cursor,TempAlloc> cursor( + std::allocator_arg, aset.get_temp_allocator(), is, options, ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, cursor.line(), cursor.column()}; } - template - typename std::enable_if::value,T>::type - decode_csv(const allocator_set& alloc_set, - std::basic_istream& is, - const basic_csv_decode_options& options = basic_csv_decode_options()) - { - basic_csv_cursor,TempAllocator> cursor(is, options, alloc_set.get_temp_allocator()); - json_decoder,TempAllocator> decoder(alloc_set.get_temp_allocator(), alloc_set.get_temp_allocator()); - - std::error_code ec; - T val = decode_traits::decode(cursor, decoder, ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); - } - return val; + return reflect::decode_traits::try_decode(aset, cursor); +} + +template +T decode_csv(Args&& ... args) +{ + auto result = try_decode_csv(std::forward(args)...); + if (!result) + { + JSONCONS_THROW(ser_error(result.error().code(), result.error().line(), result.error().column())); } + return std::move(*result); +} } // namespace csv } // namespace jsoncons diff --git a/include/jsoncons_ext/csv/encode_csv.hpp b/include/jsoncons_ext/csv/encode_csv.hpp index eb3847e..86226b1 100644 --- a/include/jsoncons_ext/csv/encode_csv.hpp +++ b/include/jsoncons_ext/csv/encode_csv.hpp @@ -1,4 +1,4 @@ -/// Copyright 2013-2025 Daniel Parker +/// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -12,118 +12,109 @@ #include #include +#include +#include +#include +#include #include #include #include #include -#include -#include -#include namespace jsoncons { namespace csv { - template - typename std::enable_if::value && - ext_traits::is_back_insertable_char_container::value>::type - encode_csv(const T& j, CharContainer& cont, const basic_csv_encode_options& options = basic_csv_encode_options()) - { - using char_type = typename CharContainer::value_type; - basic_csv_encoder>> encoder(cont,options); - j.dump(encoder); - } - - template - typename std::enable_if::value && - ext_traits::is_back_insertable_char_container::value>::type - encode_csv(const T& val, CharContainer& cont, const basic_csv_encode_options& options = basic_csv_encode_options()) - { - using char_type = typename CharContainer::value_type; - basic_csv_encoder>> encoder(cont,options); - std::error_code ec; - encode_traits::encode(val, encoder, json(), ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec)); - } - } - - template - typename std::enable_if::value,void>::type - encode_csv(const T& j, std::basic_ostream& os, const basic_csv_encode_options& options = basic_csv_encode_options()) - { - using char_type = CharT; - basic_csv_encoder> encoder(os,options); - j.dump(encoder); - } - - template - typename std::enable_if::value,void>::type - encode_csv(const T& val, std::basic_ostream& os, const basic_csv_encode_options& options = basic_csv_encode_options()) - { - using char_type = CharT; - basic_csv_encoder> encoder(os,options); - std::error_code ec; - encode_traits::encode(val, encoder, json(), ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec)); - } - } - - // with alloc_set.get_temp_allocator()ator_arg_t - - template - typename std::enable_if::value && - ext_traits::is_back_insertable_char_container::value>::type - encode_csv(const allocator_set& alloc_set, - const T& j, CharContainer& cont, const basic_csv_encode_options& options = basic_csv_encode_options()) - { - using char_type = typename CharContainer::value_type; - basic_csv_encoder>,TempAllocator> encoder(cont, options, alloc_set.get_temp_allocator()); - j.dump(encoder); - } - - template - typename std::enable_if::value && - ext_traits::is_back_insertable_char_container::value>::type - encode_csv(const allocator_set& alloc_set, - const T& val, CharContainer& cont, const basic_csv_encode_options& options = basic_csv_encode_options()) - { - using char_type = typename CharContainer::value_type; - basic_csv_encoder>,TempAllocator> encoder(cont, options, alloc_set.get_temp_allocator()); - std::error_code ec; - encode_traits::encode(val, encoder, json(), ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec)); - } - } - - template - typename std::enable_if::value,void>::type - encode_csv(const allocator_set& alloc_set, - const T& j, std::basic_ostream& os, const basic_csv_encode_options& options = basic_csv_encode_options()) - { - using char_type = CharT; - basic_csv_encoder,TempAllocator> encoder(os, options, alloc_set.get_temp_allocator()); - j.dump(encoder); - } - - template - typename std::enable_if::value,void>::type - encode_csv(const allocator_set& alloc_set, - const T& val, std::basic_ostream& os, const basic_csv_encode_options& options = basic_csv_encode_options()) +template +typename std::enable_if::value && + ext_traits::is_back_insertable_char_container::value,write_result>::type +try_encode_csv(const T& j, CharContainer& cont, const basic_csv_encode_options& options = basic_csv_encode_options()) +{ + using char_type = typename CharContainer::value_type; + basic_csv_encoder>> encoder(cont,options); + return j.try_dump(encoder); +} + +template +typename std::enable_if::value && + ext_traits::is_back_insertable_char_container::value,write_result>::type +try_encode_csv(const T& val, CharContainer& cont, const basic_csv_encode_options& options = basic_csv_encode_options()) +{ + using char_type = typename CharContainer::value_type; + basic_csv_encoder>> encoder(cont,options); + return reflect::encode_traits::try_encode(make_alloc_set(), val, encoder); +} + +template +typename std::enable_if::value,write_result>::type +try_encode_csv(const T& j, std::basic_ostream& os, const basic_csv_encode_options& options = basic_csv_encode_options()) +{ + using char_type = CharT; + basic_csv_encoder> encoder(os,options); + return j.try_dump(encoder); +} + +template +typename std::enable_if::value,write_result>::type +try_encode_csv(const T& val, std::basic_ostream& os, const basic_csv_encode_options& options = basic_csv_encode_options()) +{ + using char_type = CharT; + basic_csv_encoder> encoder(os,options); + return reflect::encode_traits::try_encode(make_alloc_set(), val, encoder); +} + +// with aset.get_temp_allocator()ator_arg_t + +template +typename std::enable_if::value && + ext_traits::is_back_insertable_char_container::value,write_result>::type +try_encode_csv(const allocator_set& aset, + const T& j, CharContainer& cont, const basic_csv_encode_options& options = basic_csv_encode_options()) +{ + using char_type = typename CharContainer::value_type; + basic_csv_encoder>,TempAlloc> encoder(cont, options, aset.get_temp_allocator()); + return j.try_dump(encoder); +} + +template +typename std::enable_if::value && + ext_traits::is_back_insertable_char_container::value,write_result>::type +try_encode_csv(const allocator_set& aset, + const T& val, CharContainer& cont, const basic_csv_encode_options& options = basic_csv_encode_options()) +{ + using char_type = typename CharContainer::value_type; + basic_csv_encoder>,TempAlloc> encoder(cont, options, aset.get_temp_allocator()); + return reflect::encode_traits::try_encode(aset, val, encoder); +} + +template +typename std::enable_if::value,write_result>::type +try_encode_csv(const allocator_set& aset, + const T& j, std::basic_ostream& os, const basic_csv_encode_options& options = basic_csv_encode_options()) +{ + using char_type = CharT; + basic_csv_encoder,TempAlloc> encoder(os, options, aset.get_temp_allocator()); + return j.try_dump(encoder); +} + +template +typename std::enable_if::value,write_result>::type +try_encode_csv(const allocator_set& aset, + const T& val, std::basic_ostream& os, const basic_csv_encode_options& options = basic_csv_encode_options()) +{ + using char_type = CharT; + basic_csv_encoder,TempAlloc> encoder(os, options, aset.get_temp_allocator()); + return reflect::encode_traits::try_encode(aset, val, encoder); +} + +template +void encode_csv(Args&& ... args) +{ + auto r = try_encode_csv(std::forward(args)...); + if (!r) { - using char_type = CharT; - basic_csv_encoder,TempAllocator> encoder(os, options, alloc_set.get_temp_allocator()); - std::error_code ec; - encode_traits::encode(val, encoder, json(), ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec)); - } + JSONCONS_THROW(ser_error(r.error())); } +} } // namespace csv } // namespace jsoncons diff --git a/include/jsoncons_ext/jmespath/jmespath.hpp b/include/jsoncons_ext/jmespath/jmespath.hpp index b17971d..04133d8 100644 --- a/include/jsoncons_ext/jmespath/jmespath.hpp +++ b/include/jsoncons_ext/jmespath/jmespath.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -23,7 +23,7 @@ #include #include -#include +#include #include #include #include @@ -1300,13 +1300,13 @@ namespace detail { reference arg0 = args[0].value(); switch (arg0.type()) { - case json_type::uint64_value: + case json_type::uint64: return arg0; - case json_type::int64_value: + case json_type::int64: { return arg0.template as() >= 0 ? arg0 : *context.create_json(std::abs(arg0.template as())); } - case json_type::double_value: + case json_type::float64: { return arg0.template as() >= 0 ? arg0 : *context.create_json(std::abs(arg0.template as())); } @@ -1384,12 +1384,12 @@ namespace detail { reference arg0 = args[0].value(); switch (arg0.type()) { - case json_type::uint64_value: - case json_type::int64_value: + case json_type::uint64: + case json_type::int64: { return *context.create_json(arg0.template as()); } - case json_type::double_value: + case json_type::float64: { return *context.create_json(std::ceil(arg0.template as())); } @@ -1424,7 +1424,7 @@ namespace detail { switch (arg0.type()) { - case json_type::array_value: + case json_type::array: for (auto& j : arg0.array_range()) { if (j == arg1) @@ -1433,7 +1433,7 @@ namespace detail { } } return context.false_value(); - case json_type::string_value: + case json_type::string: { if (!arg1.is_string()) { @@ -1520,12 +1520,12 @@ namespace detail { reference arg0 = args[0].value(); switch (arg0.type()) { - case json_type::uint64_value: - case json_type::int64_value: + case json_type::uint64: + case json_type::int64: { return *context.create_json(arg0.template as()); } - case json_type::double_value: + case json_type::float64: { return *context.create_json(std::floor(arg0.template as())); } @@ -1617,10 +1617,10 @@ namespace detail { switch (arg0.type()) { - case json_type::object_value: - case json_type::array_value: + case json_type::object: + case json_type::array: return *context.create_json(arg0.size()); - case json_type::string_value: + case json_type::string: { auto sv0 = arg0.template as(); auto length = unicode_traits::count_codepoints(sv0.data(), sv0.size()); @@ -1989,17 +1989,17 @@ namespace detail { switch (arg0.type()) { - case json_type::int64_value: - case json_type::uint64_value: - case json_type::double_value: + case json_type::int64: + case json_type::uint64: + case json_type::float64: return context.number_type_name(); - case json_type::bool_value: + case json_type::boolean: return context.boolean_type_name(); - case json_type::string_value: + case json_type::string: return context.string_type_name(); - case json_type::object_value: + case json_type::object: return context.object_type_name(); - case json_type::array_value: + case json_type::array: return context.array_type_name(); default: return context.null_type_name(); @@ -2210,7 +2210,7 @@ namespace detail { reference arg0 = args[0].value(); switch (arg0.type()) { - case json_type::string_value: + case json_type::string: { string_view_type sv = arg0.as_string_view(); std::basic_string buf; @@ -2220,7 +2220,7 @@ namespace detail { unicode_traits::convert(buf.data(), buf.size(), s); return *context.create_json(s); } - case json_type::array_value: + case json_type::array: { auto result = context.create_json(arg0); std::reverse(result->array_range().begin(),result->array_range().end()); @@ -2371,33 +2371,33 @@ namespace detail { reference arg0 = args[0].value(); switch (arg0.type()) { - case json_type::int64_value: - case json_type::uint64_value: - case json_type::double_value: + case json_type::int64: + case json_type::uint64: + case json_type::float64: return arg0; - case json_type::string_value: + case json_type::string: { auto sv = arg0.as_string_view(); uint64_t uval{ 0 }; - auto result1 = jsoncons::detail::to_integer(sv.data(), sv.length(), uval); + auto result1 = jsoncons::to_integer(sv.data(), sv.length(), uval); if (result1) { return *context.create_json(uval); } int64_t sval{ 0 }; - auto result2 = jsoncons::detail::to_integer(sv.data(), sv.length(), sval); + auto result2 = jsoncons::to_integer(sv.data(), sv.length(), sval); if (result2) { return *context.create_json(sval); } - const jsoncons::detail::chars_to to_double; - try + auto s = arg0.as_string(); + double d{0}; + auto result3 = jsoncons::decstr_to_double(s.c_str(), s.length(), d); + if (result3) { - auto s = arg0.as_string(); - double d = to_double(s.c_str(), s.length()); return *context.create_json(d); } - catch (const std::exception&) + else { return context.null_value(); } @@ -2999,6 +2999,16 @@ namespace detail { { if (!val.is_array()) { + eval_context new_context{ context.temp_storage_, context.variables_ }; + Json j(json_const_pointer_arg, evaluate_tokens(val, token_list_, new_context, ec)); + if (is_true(j)) + { + reference jj = this->apply_expressions(val, context, ec); + if (!jj.is_null()) + { + return jj; + } + } return context.null_value(); } auto result = context.create_json(json_array_arg); @@ -3435,7 +3445,7 @@ namespace detail { std::size_t line_{1}; std::size_t column_{1}; const char_type* begin_input_{nullptr}; - const char_type* end_input_{nullptr}; + const char_type* input_end_{nullptr}; const char_type* p_{nullptr}; std::vector> operator_stack_; @@ -3473,13 +3483,13 @@ namespace detail { uint32_t cp2 = 0; begin_input_ = path; - end_input_ = path + length; + input_end_ = path + length; p_ = begin_input_; slice slic{}; bool done = false; - while (p_ < end_input_ && !done) + while (p_ < input_end_ && !done) { switch (state_stack.back()) { @@ -3819,7 +3829,7 @@ namespace detail { ++p_; ++column_; } - else if (*p_ == 'i' && (p_ + 1) < end_input_ && *(p_ + 1) == 'n') + else if (*p_ == 'i' && (p_ + 1) < input_end_ && *(p_ + 1) == 'n') { p_ += 2; column_ += 2; @@ -3925,7 +3935,7 @@ namespace detail { // check no-args function bool is_no_args_func = true; bool isEnd = false; - for (const char_type *p2_ = p_ + 1; p2_ < end_input_ && !isEnd; ++p2_) + for (const char_type *p2_ = p_ + 1; p2_ < input_end_ && !isEnd; ++p2_) { switch (*p2_) @@ -4307,7 +4317,7 @@ namespace detail { break; } case '\\': - if (p_+1 < end_input_) + if (p_+1 < input_end_) { ++p_; ++column_; @@ -4407,7 +4417,7 @@ namespace detail { switch(*p_) { case '*': - if (p_+1 >= end_input_) + if (p_+1 >= input_end_) { ec = jmespath_errc::unexpected_end_of_input; return jmespath_expression{}; @@ -4498,7 +4508,7 @@ namespace detail { else { int64_t val{ 0 }; - auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), val); + auto r = jsoncons::to_integer(buffer.data(), buffer.size(), val); if (!r) { ec = jmespath_errc::invalid_number; @@ -4519,7 +4529,7 @@ namespace detail { if (!buffer.empty()) { int64_t val{}; - auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), val); + auto r = jsoncons::to_integer(buffer.data(), buffer.size(), val); if (!r) { ec = jmespath_errc::invalid_number; @@ -4544,7 +4554,7 @@ namespace detail { if (!buffer.empty()) { int64_t val{ 0 }; - auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), val); + auto r = jsoncons::to_integer(buffer.data(), buffer.size(), val); if (!r) { ec = jmespath_errc::invalid_number; @@ -4580,7 +4590,7 @@ namespace detail { if (!buffer.empty()) { int64_t val{ 0 }; - auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), val); + auto r = jsoncons::to_integer(buffer.data(), buffer.size(), val); if (!r) { ec = jmespath_errc::invalid_number; @@ -5025,7 +5035,7 @@ namespace detail { ++column_; break; case '\r': - if (p_+1 < end_input_) + if (p_+1 < input_end_) { if (*(p_ + 1) == '\n') ++p_; diff --git a/include/jsoncons_ext/jmespath/jmespath_error.hpp b/include/jsoncons_ext/jmespath/jmespath_error.hpp index c2d8b62..28eca4f 100644 --- a/include/jsoncons_ext/jmespath/jmespath_error.hpp +++ b/include/jsoncons_ext/jmespath/jmespath_error.hpp @@ -1,4 +1,4 @@ -/// Copyright 2013-2025 Daniel Parker +/// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/include/jsoncons_ext/jsonpatch/jsonpatch.hpp b/include/jsoncons_ext/jsonpatch/jsonpatch.hpp index a5b6c63..866b24d 100644 --- a/include/jsoncons_ext/jsonpatch/jsonpatch.hpp +++ b/include/jsoncons_ext/jsonpatch/jsonpatch.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -116,7 +116,7 @@ namespace detail { return location; } string_type last_token; - jsoncons::detail::from_integer(val.size(), last_token); + jsoncons::from_integer(val.size(), last_token); tokens.emplace_back(std::move(last_token)); return jsonpointer::basic_json_pointer(std::move(tokens)); @@ -219,7 +219,7 @@ namespace detail { { std::basic_string ss(path); ss.push_back('/'); - jsoncons::detail::from_integer(i,ss); + jsoncons::from_integer(i,ss); auto temp_diff = from_diff(source[i],target[i],ss); result.insert(result.array_range().end(),temp_diff.array_range().begin(),temp_diff.array_range().end()); } @@ -228,7 +228,7 @@ namespace detail { { std::basic_string ss(path); ss.push_back('/'); - jsoncons::detail::from_integer(i,ss); + jsoncons::from_integer(i,ss); Json val(json_object_arg); val.insert_or_assign(jsonpatch_names::op_name(), jsonpatch_names::remove_name()); val.insert_or_assign(jsonpatch_names::path_name(), ss); @@ -241,7 +241,7 @@ namespace detail { const auto& a = target[i]; std::basic_string ss(path); ss.push_back('/'); - jsoncons::detail::from_integer(i,ss); + jsoncons::from_integer(i,ss); Json val(json_object_arg); val.insert_or_assign(jsonpatch_names::op_name(), jsonpatch_names::add_name()); val.insert_or_assign(jsonpatch_names::path_name(), ss); diff --git a/include/jsoncons_ext/jsonpatch/jsonpatch_error.hpp b/include/jsoncons_ext/jsonpatch/jsonpatch_error.hpp index e5acd07..d3ca64f 100644 --- a/include/jsoncons_ext/jsonpatch/jsonpatch_error.hpp +++ b/include/jsoncons_ext/jsonpatch/jsonpatch_error.hpp @@ -1,4 +1,4 @@ -/// Copyright 2013-2025 Daniel Parker +/// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -87,19 +87,6 @@ namespace std { namespace jsoncons { namespace jsonpatch { -// allow to disable exceptions -#if !defined(JSONCONS_NO_EXCEPTIONS) - #define JSONCONS_THROW(exception) throw exception - #define JSONCONS_RETHROW throw - #define JSONCONS_TRY try - #define JSONCONS_CATCH(exception) catch(exception) -#else - #define JSONCONS_THROW(exception) std::terminate() - #define JSONCONS_RETHROW std::terminate() - #define JSONCONS_TRY if (true) - #define JSONCONS_CATCH(exception) if (false) -#endif - class jsonpatch_error : public std::system_error, public virtual json_exception { public: diff --git a/include/jsoncons_ext/jsonpath/flatten.hpp b/include/jsoncons_ext/jsonpath/flatten.hpp index 3cce551..9ea2e87 100644 --- a/include/jsoncons_ext/jsonpath/flatten.hpp +++ b/include/jsoncons_ext/jsonpath/flatten.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -12,8 +12,8 @@ #include #include -#include -#include +#include +#include #include #include @@ -33,7 +33,7 @@ namespace jsonpath { switch (parent_value.type()) { - case json_type::array_value: + case json_type::array: { if (parent_value.empty()) { @@ -45,7 +45,7 @@ namespace jsonpath { { string_type key(parent_key); key.push_back('['); - jsoncons::detail::from_integer(i,key); + jsoncons::from_integer(i,key); key.push_back(']'); flatten_(key, parent_value.at(i), result); } @@ -53,7 +53,7 @@ namespace jsonpath { break; } - case json_type::object_value: + case json_type::object: { if (parent_value.empty()) { @@ -113,7 +113,7 @@ namespace jsonpath { if (JSONCONS_UNLIKELY(!value.is_object())) { - JSONCONS_THROW(jsonpath_error(jsonpath_errc::argument_to_unflatten_invalid)); + JSONCONS_THROW(jsonpath_error(jsonpath_errc::invalid_argument_to_unflatten)); } Json result; @@ -315,7 +315,7 @@ namespace jsonpath { case ']': { std::size_t n{0}; - auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), n); + auto r = jsoncons::to_integer(buffer.data(), buffer.size(), n); if (r) { if (!part->is_array()) diff --git a/include/jsoncons_ext/jsonpath/json_location.hpp b/include/jsoncons_ext/jsonpath/json_location.hpp index 6ab9048..87cd1bd 100644 --- a/include/jsoncons_ext/jsonpath/json_location.hpp +++ b/include/jsoncons_ext/jsonpath/json_location.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -158,7 +158,7 @@ namespace jsonpath { allocator_type alloc_; std::size_t line_{1}; std::size_t column_{1}; - const char_type* end_input_{nullptr}; + const char_type* input_end_{nullptr}; const char_type* p_{nullptr}; public: @@ -200,7 +200,7 @@ namespace jsonpath { string_type buffer(alloc_); - end_input_ = path.data() + path.length(); + input_end_ = path.data() + path.length(); p_ = path.data(); @@ -208,7 +208,7 @@ namespace jsonpath { json_location_state state = json_location_state::start; - while (p_ < end_input_) + while (p_ < input_end_) { switch (state) { @@ -411,7 +411,7 @@ namespace jsonpath { break; default: std::size_t n{0}; - auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), n); + auto r = jsoncons::to_integer(buffer.data(), buffer.size(), n); if (!r) { ec = jsonpath_errc::invalid_number; @@ -510,7 +510,7 @@ namespace jsonpath { else if (state == json_location_state::digit) { std::size_t n{ 0 }; - auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), n); + auto r = jsoncons::to_integer(buffer.data(), buffer.size(), n); if (!r) { ec = jsonpath_errc::invalid_number; @@ -535,7 +535,7 @@ namespace jsonpath { ++column_; break; case '\r': - if ((p_+1 < end_input_) && (*(p_+1) == '\n')) + if ((p_+1 < input_end_) && (*(p_+1) == '\n')) { ++p_; } @@ -879,7 +879,7 @@ namespace jsonpath { else { buffer.push_back('['); - jsoncons::detail::from_integer(element.index(), buffer); + jsoncons::from_integer(element.index(), buffer); buffer.push_back(']'); } } diff --git a/include/jsoncons_ext/jsonpath/json_query.hpp b/include/jsoncons_ext/jsonpath/json_query.hpp index 1416cdb..1806c74 100644 --- a/include/jsoncons_ext/jsonpath/json_query.hpp +++ b/include/jsoncons_ext/jsonpath/json_query.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -11,7 +11,7 @@ #include #include -#include +#include #include #include @@ -63,30 +63,30 @@ namespace jsonpath { expr.evaluate(root, callback, options); } - template - Json json_query(const allocator_set& alloc_set, + template + Json json_query(const allocator_set& aset, const Json& root, const typename Json::string_view_type& path, result_options options = result_options(), const custom_functions& functions = custom_functions()) { - auto expr = make_expression(alloc_set, path, functions); + auto expr = make_expression(aset, path, functions); return expr.evaluate(root, options); } - template + template typename std::enable_if::value,void>::type - json_query(const allocator_set& alloc_set, + json_query(const allocator_set& aset, const Json& root, const typename Json::string_view_type& path, Callback callback, result_options options = result_options(), const custom_functions& functions = custom_functions()) { - auto expr = make_expression(alloc_set, path, functions); + auto expr = make_expression(aset, path, functions); expr.evaluate(root, callback, options); } template - typename std::enable_if::value,void>::type + typename std::enable_if::value,void>::type json_replace(Json& root, const typename Json::string_view_type& path, T&& new_value, const custom_functions& funcs = custom_functions()) { @@ -112,9 +112,9 @@ namespace jsonpath { expr.evaluate(context, root, path_node_type{}, root, callback, options); } - template - typename std::enable_if::value,void>::type - json_replace(const allocator_set& alloc_set, + template + typename std::enable_if::value,void>::type + json_replace(const allocator_set& aset, Json& root, const typename Json::string_view_type& path, T&& new_value, const custom_functions& funcs = custom_functions()) { @@ -126,11 +126,11 @@ namespace jsonpath { using path_expression_type = typename jsonpath_traits_type::path_expression_type; using path_node_type = typename jsonpath_traits_type::path_node_type; - auto resources = jsoncons::make_unique>(funcs, alloc_set.get_allocator()); - evaluator_type evaluator{alloc_set.get_allocator()}; + auto resources = jsoncons::make_unique>(funcs, aset.get_allocator()); + evaluator_type evaluator{aset.get_allocator()}; path_expression_type expr = evaluator.compile(*resources, path); - jsoncons::jsonpath::detail::eval_context context{alloc_set.get_allocator()}; + jsoncons::jsonpath::detail::eval_context context{aset.get_allocator()}; auto callback = [&new_value](const path_node_type&, reference v) { v = Json(std::forward(new_value), semantic_tag::none); @@ -166,9 +166,9 @@ namespace jsonpath { expr.evaluate(context, root, path_node_type{}, root, f, options); } - template + template typename std::enable_if::value,void>::type - json_replace(const allocator_set& alloc_set, + json_replace(const allocator_set& aset, Json& root, const typename Json::string_view_type& path , BinaryCallback callback, const custom_functions& funcs = custom_functions()) { @@ -180,11 +180,11 @@ namespace jsonpath { using path_expression_type = typename jsonpath_traits_type::path_expression_type; using path_node_type = typename jsonpath_traits_type::path_node_type; - auto resources = jsoncons::make_unique>(funcs, alloc_set.get_allocator()); - evaluator_type evaluator{alloc_set.get_allocator()}; + auto resources = jsoncons::make_unique>(funcs, aset.get_allocator()); + evaluator_type evaluator{aset.get_allocator()}; path_expression_type expr = evaluator.compile(*resources, path); - jsoncons::jsonpath::detail::eval_context context{alloc_set.get_allocator()}; + jsoncons::jsonpath::detail::eval_context context{aset.get_allocator()}; auto f = [&callback](const path_node_type& path, reference val) { diff --git a/include/jsoncons_ext/jsonpath/jsonpath.hpp b/include/jsoncons_ext/jsonpath/jsonpath.hpp index f4ad1dc..ae16daa 100644 --- a/include/jsoncons_ext/jsonpath/jsonpath.hpp +++ b/include/jsoncons_ext/jsonpath/jsonpath.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/include/jsoncons_ext/jsonpath/jsonpath_error.hpp b/include/jsoncons_ext/jsonpath/jsonpath_error.hpp index 4ff2379..94dcdff 100644 --- a/include/jsoncons_ext/jsonpath/jsonpath_error.hpp +++ b/include/jsoncons_ext/jsonpath/jsonpath_error.hpp @@ -1,4 +1,4 @@ -/// Copyright 2013-2025 Daniel Parker +/// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -44,7 +44,7 @@ namespace jsonpath { unidentified_error, unexpected_eof, expected_colon_dot_left_bracket_comma_or_rbracket, - argument_to_unflatten_invalid, + invalid_argument_to_unflatten, invalid_flattened_key, step_cannot_be_zero, invalid_number, @@ -118,7 +118,7 @@ namespace jsonpath { return "Unexpected EOF while parsing jsonpath expression"; case jsonpath_errc::expected_colon_dot_left_bracket_comma_or_rbracket: return "Expected ':', '.', '[', ',', or ']'"; - case jsonpath_errc::argument_to_unflatten_invalid: + case jsonpath_errc::invalid_argument_to_unflatten: return "Argument to unflatten must be an object"; case jsonpath_errc::invalid_flattened_key: return "Flattened key is invalid"; diff --git a/include/jsoncons_ext/jsonpath/jsonpath_expression.hpp b/include/jsoncons_ext/jsonpath/jsonpath_expression.hpp index 90b8238..dd12e76 100644 --- a/include/jsoncons_ext/jsonpath/jsonpath_expression.hpp +++ b/include/jsoncons_ext/jsonpath/jsonpath_expression.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -29,7 +29,7 @@ namespace jsoncons { namespace jsonpath { - template > + template > class jsonpath_expression { public: @@ -52,11 +52,11 @@ namespace jsonpath { const_path_expression_type const_expr_; path_expression_type expr_; public: - jsonpath_expression(const allocator_set& alloc_set, + jsonpath_expression(const allocator_set& aset, std::unique_ptr&& resources, const_path_expression_type&& const_expr, path_expression_type&& expr) - : alloc_(alloc_set.get_allocator()), + : alloc_(aset.get_allocator()), static_resources_(std::move(resources)), const_expr_(std::move(const_expr)), expr_(std::move(expr)) @@ -174,25 +174,25 @@ namespace jsonpath { evaluator_type evaluator; auto expr = evaluator.compile(*resources, path); - return jsonpath_expression(jsoncons::combine_allocators(), + return jsonpath_expression(jsoncons::make_alloc_set(), std::move(resources), std::move(const_expr), std::move(expr)); } template jsonpath_expression make_expression(const typename Json::string_view_type& expr, std::error_code& ec) { - return make_expression(jsoncons::combine_allocators(), expr, custom_functions(), ec); + return make_expression(jsoncons::make_alloc_set(), expr, custom_functions(), ec); } - template - jsonpath_expression make_expression(const allocator_set& alloc_set, + template + jsonpath_expression make_expression(const allocator_set& aset, const typename Json::string_view_type& expr, std::error_code& ec) { - return make_expression(alloc_set, expr, custom_functions(), ec); + return make_expression(aset, expr, custom_functions(), ec); } - template - jsonpath_expression make_expression(const allocator_set& alloc_set, + template + jsonpath_expression make_expression(const allocator_set& aset, const typename Json::string_view_type& path, const custom_functions& funcs = custom_functions()) { @@ -204,19 +204,19 @@ namespace jsonpath { using const_evaluator_type = typename jsoncons::jsonpath::detail::jsonpath_evaluator; auto resources = jsoncons::make_unique(funcs, - alloc_set.get_allocator()); - const_evaluator_type const_evaluator{alloc_set.get_allocator()}; + aset.get_allocator()); + const_evaluator_type const_evaluator{aset.get_allocator()}; auto const_expr = const_evaluator.compile(*resources, path); - evaluator_type evaluator{alloc_set.get_allocator()}; + evaluator_type evaluator{aset.get_allocator()}; auto expr = evaluator.compile(*resources, path); - return jsonpath_expression(alloc_set, + return jsonpath_expression(aset, std::move(resources), std::move(const_expr), std::move(expr)); } - template - jsonpath_expression make_expression(const allocator_set& alloc_set, + template + jsonpath_expression make_expression(const allocator_set& aset, const typename Json::string_view_type& path, const jsoncons::jsonpath::custom_functions::value_type>& funcs, std::error_code& ec) { @@ -228,13 +228,13 @@ namespace jsonpath { using const_evaluator_type = typename jsoncons::jsonpath::detail::jsonpath_evaluator; auto resources = jsoncons::make_unique(funcs); - const_evaluator_type const_evaluator{alloc_set.get_allocator()}; + const_evaluator_type const_evaluator{aset.get_allocator()}; auto const_expr = const_evaluator.compile(*resources, path, ec); - evaluator_type evaluator{alloc_set.get_allocator()}; + evaluator_type evaluator{aset.get_allocator()}; auto expr = evaluator.compile(*resources, path, ec); - return jsonpath_expression(alloc_set, + return jsonpath_expression(aset, std::move(resources), std::move(const_expr), std::move(expr)); } diff --git a/include/jsoncons_ext/jsonpath/jsonpath_parser.hpp b/include/jsoncons_ext/jsonpath/jsonpath_parser.hpp index 59d4e1f..572a373 100644 --- a/include/jsoncons_ext/jsonpath/jsonpath_parser.hpp +++ b/include/jsoncons_ext/jsonpath/jsonpath_parser.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include #include @@ -128,7 +128,7 @@ namespace detail { std::size_t line_{1}; std::size_t column_{1}; const char_type* begin_input_{nullptr}; - const char_type* end_input_{nullptr}; + const char_type* input_end_{nullptr}; const char_type* p_{nullptr}; using argument_type = std::vector; @@ -182,7 +182,7 @@ namespace detail { uint32_t cp2 = 0; begin_input_ = path.data(); - end_input_ = path.data() + path.length(); + input_end_ = path.data() + path.length(); p_ = begin_input_; slice slic; @@ -190,7 +190,7 @@ namespace detail { int ancestor_depth = 0; state_stack_.emplace_back(path_state::start); - while (p_ < end_input_ && !state_stack_.empty()) + while (p_ < input_end_ && !state_stack_.empty()) { switch (state_stack_.back()) { @@ -432,7 +432,7 @@ namespace detail { { json_decoder decoder(alloc_); basic_json_parser parser; - parser.update(p_,end_input_ - p_); + parser.update(p_,input_end_ - p_); parser.parse_some(decoder, ec); if (JSONCONS_UNLIKELY(ec)) { @@ -495,7 +495,7 @@ namespace detail { buffer.push_back(*p_); ++p_; ++column_; - if (p_ == end_input_) + if (p_ == input_end_) { ec = jsonpath_errc::unexpected_eof; return path_expression_type(alloc_); @@ -1426,7 +1426,7 @@ namespace detail { return path_expression_type(alloc_); } int64_t n{0}; - auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), n); + auto r = jsoncons::to_integer(buffer.data(), buffer.size(), n); if (!r) { ec = jsonpath_errc::invalid_number; @@ -1451,7 +1451,7 @@ namespace detail { } int64_t n{0}; - auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), n); + auto r = jsoncons::to_integer(buffer.data(), buffer.size(), n); if (!r) { ec = jsonpath_errc::invalid_number; @@ -1476,7 +1476,7 @@ namespace detail { if (!buffer.empty()) { int64_t n{0}; - auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), n); + auto r = jsoncons::to_integer(buffer.data(), buffer.size(), n); if (!r) { ec = jsonpath_errc::invalid_number; @@ -1504,7 +1504,7 @@ namespace detail { if (!buffer.empty()) { int64_t n{0}; - auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), n); + auto r = jsoncons::to_integer(buffer.data(), buffer.size(), n); if (!r) { ec = jsonpath_errc::invalid_number; @@ -1542,7 +1542,7 @@ namespace detail { if (!buffer.empty()) { int64_t n{0}; - auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), n); + auto r = jsoncons::to_integer(buffer.data(), buffer.size(), n); if (!r) { ec = jsonpath_errc::invalid_number; @@ -1728,7 +1728,7 @@ namespace detail { } int64_t n{0}; - auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), n); + auto r = jsoncons::to_integer(buffer.data(), buffer.size(), n); if (!r) { ec = jsonpath_errc::invalid_number; @@ -1747,7 +1747,7 @@ namespace detail { if (!buffer.empty()) { int64_t n{0}; - auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), n); + auto r = jsoncons::to_integer(buffer.data(), buffer.size(), n); if (!r) { ec = jsonpath_errc::invalid_number; @@ -2120,7 +2120,7 @@ namespace detail { ++column_; break; case '\r': - if (p_+1 < end_input_ && *(p_+1) == '\n') + if (p_+1 < input_end_ && *(p_+1) == '\n') { ++p_; } diff --git a/include/jsoncons_ext/jsonpath/jsonpath_selector.hpp b/include/jsoncons_ext/jsonpath/jsonpath_selector.hpp index 2b99204..1dd0c98 100644 --- a/include/jsoncons_ext/jsonpath/jsonpath_selector.hpp +++ b/include/jsoncons_ext/jsonpath/jsonpath_selector.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -269,7 +269,7 @@ namespace detail { else if (current.is_array()) { int64_t n{0}; - auto r = jsoncons::detail::dec_to_integer(identifier_.data(), identifier_.size(), n); + auto r = jsoncons::dec_to_integer(identifier_.data(), identifier_.size(), n); if (r) { auto index = (n >= 0) ? static_cast(n) : static_cast(static_cast(current.size()) + n); @@ -322,7 +322,7 @@ namespace detail { if (current.is_array()) { int64_t n{0}; - auto r = jsoncons::detail::dec_to_integer(identifier_.data(), identifier_.size(), n); + auto r = jsoncons::dec_to_integer(identifier_.data(), identifier_.size(), n); if (r) { auto index = (n >= 0) ? static_cast(n) : static_cast(static_cast(current.size()) + n); diff --git a/include/jsoncons_ext/jsonpath/jsonpath_utilities.hpp b/include/jsoncons_ext/jsonpath/jsonpath_utilities.hpp index 0da07de..5f39493 100644 --- a/include/jsoncons_ext/jsonpath/jsonpath_utilities.hpp +++ b/include/jsoncons_ext/jsonpath/jsonpath_utilities.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/include/jsoncons_ext/jsonpath/path_node.hpp b/include/jsoncons_ext/jsonpath/path_node.hpp index 02293fe..9d5a5b7 100644 --- a/include/jsoncons_ext/jsonpath/path_node.hpp +++ b/include/jsoncons_ext/jsonpath/path_node.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,7 +15,7 @@ #include #include -#include +#include #include #include @@ -242,7 +242,7 @@ namespace jsonpath { { if (node->node_kind() == path_node_kind::index) { - if (current->type() != json_type::array_value || node->index() >= current->size()) + if (current->type() != json_type::array || node->index() >= current->size()) { return nullptr; } @@ -250,7 +250,7 @@ namespace jsonpath { } else if (node->node_kind() == path_node_kind::name) { - if (current->type() != json_type::object_value) + if (current->type() != json_type::object) { return nullptr; } @@ -297,7 +297,7 @@ namespace jsonpath { break; case path_node_kind::index: buffer.push_back('['); - jsoncons::detail::from_integer(node->index(), buffer); + jsoncons::from_integer(node->index(), buffer); buffer.push_back(']'); break; } diff --git a/include/jsoncons_ext/jsonpath/token_evaluator.hpp b/include/jsoncons_ext/jsonpath/token_evaluator.hpp index 27f1124..af9a5cd 100644 --- a/include/jsoncons_ext/jsonpath/token_evaluator.hpp +++ b/include/jsoncons_ext/jsonpath/token_evaluator.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -21,7 +21,7 @@ #include #include -#include +#include #include #include #include @@ -158,7 +158,11 @@ namespace jsonpath { }; JSONCONS_INLINE_CONSTEXPR argument_arg_t argument_arg{}; - enum class result_options {value=0, nodups=1, sort=2, sort_descending=4, path=8}; + enum class result_options { +#if !defined(JSONCONS_NO_DEPRECATED) + value=0, +#endif + nodups=1, sort=2, sort_descending=4, path=8}; inline constexpr result_options operator~(result_options a) { @@ -1138,7 +1142,7 @@ namespace detail { switch (arg0.type()) { - case json_type::array_value: + case json_type::array: for (auto& j : arg0.array_range()) { if (j == arg1) @@ -1147,7 +1151,7 @@ namespace detail { } } return value_type(false, semantic_tag::none); - case json_type::string_value: + case json_type::string: { if (!arg1.is_string()) { @@ -1451,12 +1455,12 @@ namespace detail { auto arg0= args[0].value(); switch (arg0.type()) { - case json_type::uint64_value: - case json_type::int64_value: + case json_type::uint64: + case json_type::int64: { return value_type(arg0.template as(), semantic_tag::none); } - case json_type::double_value: + case json_type::float64: { return value_type(std::ceil(arg0.template as()), semantic_tag::none); } @@ -1503,12 +1507,12 @@ namespace detail { auto arg0= args[0].value(); switch (arg0.type()) { - case json_type::uint64_value: - case json_type::int64_value: + case json_type::uint64: + case json_type::int64: { return value_type(arg0.template as(), semantic_tag::none); } - case json_type::double_value: + case json_type::float64: { return value_type(std::floor(arg0.template as()), semantic_tag::none); } @@ -1555,36 +1559,33 @@ namespace detail { auto arg0= args[0].value(); switch (arg0.type()) { - case json_type::int64_value: - case json_type::uint64_value: - case json_type::double_value: + case json_type::int64: + case json_type::uint64: + case json_type::float64: return arg0; - case json_type::string_value: + case json_type::string: { auto sv = arg0.as_string_view(); uint64_t un{0}; - auto result1 = jsoncons::detail::to_integer(sv.data(), sv.length(), un); + auto result1 = jsoncons::to_integer(sv.data(), sv.length(), un); if (result1) { return value_type(un, semantic_tag::none); } int64_t sn{0}; - auto result2 = jsoncons::detail::to_integer(sv.data(), sv.length(), sn); + auto result2 = jsoncons::to_integer(sv.data(), sv.length(), sn); if (result2) { return value_type(sn, semantic_tag::none); } - const jsoncons::detail::chars_to to_double; - try - { - auto s = arg0.as_string(); - double d = to_double(s.c_str(), s.length()); - return value_type(d, semantic_tag::none); - } - catch (const std::exception&) + auto s = arg0.as_string(); + double d; + auto result = jsoncons::decstr_to_double(s.c_str(), s.length(), d); + if (result.ec == std::errc::invalid_argument) { return value_type::null(); } + return value_type(d, semantic_tag::none); } default: ec = jsonpath_errc::invalid_type; @@ -1883,13 +1884,13 @@ namespace detail { auto arg0= args[0].value(); switch (arg0.type()) { - case json_type::uint64_value: + case json_type::uint64: return arg0; - case json_type::int64_value: + case json_type::int64: { return arg0.template as() >= 0 ? arg0 : value_type(std::abs(arg0.template as()), semantic_tag::none); } - case json_type::double_value: + case json_type::float64: { return arg0.template as() >= 0 ? arg0 : value_type(std::abs(arg0.template as()), semantic_tag::none); } @@ -1941,11 +1942,11 @@ namespace detail { switch (arg0.type()) { - case json_type::object_value: - case json_type::array_value: + case json_type::object: + case json_type::array: //std::cout << "LENGTH ARG: " << arg0 << "\n"; return value_type(arg0.size(), semantic_tag::none); - case json_type::string_value: + case json_type::string: { auto sv0 = arg0.template as(); auto length = unicode_traits::count_codepoints(sv0.data(), sv0.size()); diff --git a/include/jsoncons_ext/jsonpointer/jsonpointer.hpp b/include/jsoncons_ext/jsonpointer/jsonpointer.hpp index 025ff31..9ebed37 100644 --- a/include/jsoncons_ext/jsonpointer/jsonpointer.hpp +++ b/include/jsoncons_ext/jsonpointer/jsonpointer.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -7,16 +7,18 @@ #ifndef JSONCONS_EXT_JSONPOINTER_JSONPOINTER_HPP #define JSONCONS_EXT_JSONPOINTER_JSONPOINTER_HPP +#include #include -#include #include +#include #include #include // system_error #include // std::enable_if, std::true_type #include // std::move #include +#include -#include +#include #include #include @@ -223,6 +225,21 @@ namespace jsonpointer { return basic_json_pointer(tokens); } + const std::vector& tokens() const + { + return tokens_; + } + + std::vector& tokens() + { + return tokens_; + } + + const string_type& back() const + { + return tokens_.back(); + } + // operator= basic_json_pointer& operator=(const basic_json_pointer&) = default; @@ -246,7 +263,7 @@ namespace jsonpointer { append(IntegerType val) { string_type s; - jsoncons::detail::from_integer(val, s); + jsoncons::from_integer(val, s); tokens_.push_back(s); return *this; @@ -263,7 +280,7 @@ namespace jsonpointer { operator/=(IntegerType val) { string_type s; - jsoncons::detail::from_integer(val, s); + jsoncons::from_integer(val, s); tokens_.push_back(s); return *this; @@ -360,8 +377,28 @@ namespace jsonpointer { return lhs.tokens_ != rhs.tokens_; } + friend bool operator<(const basic_json_pointer& lhs, const basic_json_pointer& rhs) + { + return lhs.tokens_ < rhs.tokens_; + } + + friend bool operator<=(const basic_json_pointer& lhs, const basic_json_pointer& rhs) + { + return lhs.tokens_ <= rhs.tokens_; + } + + friend bool operator>(const basic_json_pointer& lhs, const basic_json_pointer& rhs) + { + return lhs.tokens_ > rhs.tokens_; + } + + friend bool operator>=(const basic_json_pointer& lhs, const basic_json_pointer& rhs) + { + return lhs.tokens_ >= rhs.tokens_; + } + friend std::basic_ostream& - operator<<( std::basic_ostream& os, const basic_json_pointer& p ) + operator<<(std::basic_ostream& os, const basic_json_pointer& p ) { os << p.to_string(); return os; @@ -405,7 +442,7 @@ namespace jsonpointer { return current; } std::size_t index{0}; - auto result = jsoncons::detail::dec_to_integer(buffer.data(), buffer.length(), index); + auto result = jsoncons::dec_to_integer(buffer.data(), buffer.length(), index); if (!result) { ec = jsonpointer_errc::invalid_index; @@ -446,7 +483,7 @@ namespace jsonpointer { return current; } std::size_t index{0}; - auto result = jsoncons::detail::dec_to_integer(buffer.data(), buffer.length(), index); + auto result = jsoncons::dec_to_integer(buffer.data(), buffer.length(), index); if (!result) { ec = jsonpointer_errc::invalid_index; @@ -664,6 +701,12 @@ namespace jsonpointer { bool create_if_missing, std::error_code& ec) { + if (location.empty()) + { + root = std::forward(value); + return; + } + Json* current = std::addressof(root); std::basic_string buffer; @@ -690,7 +733,7 @@ namespace jsonpointer { else { std::size_t index{0}; - auto result = jsoncons::detail::dec_to_integer(buffer.data(), buffer.length(), index); + auto result = jsoncons::dec_to_integer(buffer.data(), buffer.length(), index); if (!result) { ec = jsonpointer_errc::invalid_index; @@ -799,6 +842,11 @@ namespace jsonpointer { bool create_if_missing, std::error_code& ec) { + if (location.empty()) + { + root = std::forward(value); + return; + } Json* current = std::addressof(root); std::basic_string buffer; @@ -826,7 +874,7 @@ namespace jsonpointer { else { std::size_t index{0}; - auto result = jsoncons::detail::dec_to_integer(buffer.data(), buffer.length(), index); + auto result = jsoncons::dec_to_integer(buffer.data(), buffer.length(), index); if (!result) { ec = jsonpointer_errc::invalid_index; @@ -938,6 +986,12 @@ namespace jsonpointer { template void remove(Json& root, const basic_json_pointer& location, std::error_code& ec) { + if (location.empty()) + { + ec = jsonpointer_errc::cannot_remove_root; + return; + } + Json* current = std::addressof(root); std::basic_string buffer; @@ -965,7 +1019,7 @@ namespace jsonpointer { else { std::size_t index{0}; - auto result = jsoncons::detail::dec_to_integer(buffer.data(), buffer.length(), index); + auto result = jsoncons::dec_to_integer(buffer.data(), buffer.length(), index); if (!result) { ec = jsonpointer_errc::invalid_index; @@ -1042,6 +1096,11 @@ namespace jsonpointer { bool create_if_missing, std::error_code& ec) { + if (location.empty()) + { + root = std::forward(value); + return; + } Json* current = std::addressof(root); std::basic_string buffer; @@ -1069,7 +1128,7 @@ namespace jsonpointer { else { std::size_t index{}; - auto result = jsoncons::detail::dec_to_integer(buffer.data(), buffer.length(), index); + auto result = jsoncons::dec_to_integer(buffer.data(), buffer.length(), index); if (!result) { ec = jsonpointer_errc::invalid_index; @@ -1209,7 +1268,7 @@ namespace jsonpointer { switch (parent_value.type()) { - case json_type::array_value: + case json_type::array: { if (parent_value.empty()) { @@ -1224,14 +1283,14 @@ namespace jsonpointer { { string_type key(parent_key); key.push_back('/'); - jsoncons::detail::from_integer(i,key); + jsoncons::from_integer(i,key); flatten_(key, parent_value.at(i), result); } } break; } - case json_type::object_value: + case json_type::object: { if (parent_value.empty()) { @@ -1275,168 +1334,167 @@ namespace jsonpointer { // unflatten - enum class unflatten_options {none,assume_object = 1 -}; + enum class unflatten_options {none,assume_object = 1}; - template - Json safe_unflatten (Json& value) + template + Iterator find_inner_last(Iterator first, Iterator last, std::size_t offset, const StringT& token) { - if (!value.is_object() || value.empty()) + Iterator it = first; + while (it != last && *(it->first.tokens().begin() + offset) == token) { - return value; - } - bool safe = true; - std::size_t index = 0; - for (const auto& item : value.object_range()) - { - std::size_t n; - auto r = jsoncons::detail::dec_to_integer(item.key().data(),item.key().size(), n); - if (!r || (index++ != n)) - { - safe = false; - break; - } + ++it; } + return it; + } + + template + jsoncons::optional try_unflatten_array(Iterator first, Iterator last, std::size_t offset); + + template + Json unflatten_object(Iterator first, Iterator last, std::size_t offset, unflatten_options options) + { + Json jo{json_object_arg}; + + std::size_t length = std::distance(first, last); - if (safe) + auto it = first; + while (it != last) { - Json j(json_array_arg); - j.reserve(value.size()); - for (auto& item : value.object_range()) + if (it->first.tokens().size() == offset && length == 1) { - j.emplace_back(std::move(item.value())); + return *(it->second); } - Json a(json_array_arg); - for (auto& item : j.array_range()) + if (it->first.tokens().size() == offset) { - a.emplace_back(safe_unflatten (item)); + ++it; } - return a; - } - else - { - Json o(json_object_arg); - for (auto& item : value.object_range()) + else if (it->first.tokens().size() < offset) { - o.try_emplace(item.key(), safe_unflatten (item.value())); + return jsoncons::optional{}; } - return o; - } - } - - template - jsoncons::optional try_unflatten_array(const Json& value) - { - using char_type = typename Json::char_type; - - if (JSONCONS_UNLIKELY(!value.is_object())) - { - JSONCONS_THROW(jsonpointer_error(jsonpointer_errc::argument_to_unflatten_invalid)); - } - Json result; - - for (const auto& item: value.object_range()) - { - Json* part = &result; - basic_json_pointer ptr(item.key()); - std::size_t index = 0; - for (auto it = ptr.begin(); it != ptr.end(); ) + else { - auto s = *it; - std::size_t n{0}; - auto r = jsoncons::detail::dec_to_integer(s.data(), s.size(), n); - if (r.ec == jsoncons::detail::to_integer_errc() && (index++ == n)) + auto jt = it->first.tokens().begin() + offset; + if (offset + 1 == it->first.tokens().size()) { - if (!part->is_array()) - { - *part = Json(json_array_arg); - } - if (++it != ptr.end()) + jo.try_emplace(*jt, *(it->second)); + ++it; + } + else + { + auto inner_last = find_inner_last(it, last, offset, *jt); + if (options == unflatten_options{}) { - if (n+1 > part->size()) + auto res = try_unflatten_array(it, inner_last, offset+1); + if (!res) { - Json& ref = part->emplace_back(); - part = std::addressof(ref); + jo.try_emplace(*jt, unflatten_object(it, inner_last, offset+1, options)); } else { - part = &part->at(n); + jo.try_emplace(*jt, std::move(*res)); } } else { - Json& ref = part->emplace_back(item.value()); - part = std::addressof(ref); - } - } - else if (part->is_object()) - { - if (++it != ptr.end()) - { - auto res = part->try_emplace(s,Json()); - part = &(res.first->value()); - } - else - { - auto res = part->try_emplace(s, item.value()); - part = &(res.first->value()); + jo.try_emplace(*jt, unflatten_object(it, inner_last, offset+1, options)); } - } - else - { - return jsoncons::optional(); + it = inner_last; } } } - - return result; + return jsoncons::optional{std::move(jo)}; } - template - Json unflatten_to_object(const Json& value, unflatten_options options = unflatten_options::none) + template + jsoncons::optional try_unflatten_array(Iterator first, Iterator last, std::size_t offset) { - using char_type = typename Json::char_type; - - if (JSONCONS_UNLIKELY(!value.is_object())) - { - JSONCONS_THROW(jsonpointer_error(jsonpointer_errc::argument_to_unflatten_invalid)); - } - Json result; + std::map m; - for (const auto& item: value.object_range()) + auto it = first; + while (it != last) { - Json* part = &result; - basic_json_pointer ptr(item.key()); - for (auto it = ptr.begin(); it != ptr.end(); ) + if (offset >= it->first.tokens().size()) + { + return unflatten_object(first, last, offset, unflatten_options{}); + } + auto jt = it->first.tokens().begin() + offset; + const auto& s = *jt; + std::size_t n; + auto r = jsoncons::dec_to_integer(s.data(), s.size(), n); + if (r.ec != std::errc{}) + { + return unflatten_object(first, last, offset, unflatten_options{}); + } + if (offset + 1 == it->first.tokens().size()) { - auto s = *it; - if (++it != ptr.end()) + m.emplace(std::make_pair(n,*(it->second))); + ++it; + } + else + { + auto inner_last = find_inner_last(it, last, offset, *jt); + auto res = try_unflatten_array(it, inner_last, offset+1); + if (!res) { - auto res = part->try_emplace(s,Json()); - part = &(res.first->value()); + m.emplace(std::make_pair(n,unflatten_object(it, inner_last, offset+1, unflatten_options{}))); } else { - auto res = part->try_emplace(s, item.value()); - part = &(res.first->value()); + m.emplace(std::make_pair(n,std::move(*res))); } + it = inner_last; + } + } + + Json ja{json_array_arg}; + ja.reserve(m.size()); + std::size_t index = 0; + for (const auto& item : m) + { + if (item.first != index) + { + break; } + ja.push_back(std::move(item.second)); + ++index; } - return options == unflatten_options::none ? safe_unflatten (result) : result; + if (index == m.size()) + { + return jsoncons::optional{std::move(ja)}; + } + else + { + return jsoncons::optional{unflatten_object(first, last, offset, unflatten_options{})}; + } } template Json unflatten(const Json& value, unflatten_options options = unflatten_options::none) { - if (options == unflatten_options::none) + using char_type = typename Json::char_type; + using map_type = std::map, const Json*>; + + if (JSONCONS_UNLIKELY(!value.is_object() || value.empty())) + { + JSONCONS_THROW(jsonpointer_error(jsonpointer_errc::invalid_argument_to_unflatten)); + } + + map_type jptrs; + for (const auto& item : value.object_range()) + { + jptrs.emplace(std::make_pair(item.key(), std::addressof(item.value()))); + } + + if (options == unflatten_options{}) { - jsoncons::optional j = try_unflatten_array(value); - return j ? *j : unflatten_to_object(value,options); + auto result = try_unflatten_array(jptrs.begin(), jptrs.end(), 0); + return result ? *result : unflatten_object(jptrs.begin(), jptrs.end(), 0, options); } else { - return unflatten_to_object(value,options); + return unflatten_object(jptrs.begin(), jptrs.end(), 0, options); } } diff --git a/include/jsoncons_ext/jsonpointer/jsonpointer_error.hpp b/include/jsoncons_ext/jsonpointer/jsonpointer_error.hpp index a0dbf55..512027a 100644 --- a/include/jsoncons_ext/jsonpointer/jsonpointer_error.hpp +++ b/include/jsoncons_ext/jsonpointer/jsonpointer_error.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -53,9 +53,10 @@ enum class jsonpointer_errc expected_object_or_array, end_of_input, unexpected_end_of_input, - argument_to_unflatten_invalid, + invalid_argument_to_unflatten, invalid_flattened_key, - invalid_uri_escaped_data + invalid_uri_escaped_data, + cannot_remove_root }; class jsonpointer_error_category_impl @@ -88,10 +89,12 @@ class jsonpointer_error_category_impl return "Unexpected end of input"; case jsonpointer_errc::unexpected_end_of_input: return "Unexpected end of jsonpointer input"; - case jsonpointer_errc::argument_to_unflatten_invalid: - return "Argument to unflatten must be an object"; + case jsonpointer_errc::invalid_argument_to_unflatten: + return "Argument to unflatten must be a non-empty object"; case jsonpointer_errc::invalid_flattened_key: return "Flattened key is invalid"; + case jsonpointer_errc::cannot_remove_root: + return "Cannot remove root of target document"; default: return "Unknown jsonpointer error"; } diff --git a/include/jsoncons_ext/jsonschema/common/compilation_context.hpp b/include/jsoncons_ext/jsonschema/common/compilation_context.hpp index 42ff5ad..984686e 100644 --- a/include/jsoncons_ext/jsonschema/common/compilation_context.hpp +++ b/include/jsoncons_ext/jsonschema/common/compilation_context.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/include/jsoncons_ext/jsonschema/common/eval_context.hpp b/include/jsoncons_ext/jsonschema/common/eval_context.hpp index 026ff91..24f0489 100644 --- a/include/jsoncons_ext/jsonschema/common/eval_context.hpp +++ b/include/jsoncons_ext/jsonschema/common/eval_context.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/include/jsoncons_ext/jsonschema/common/format.hpp b/include/jsoncons_ext/jsonschema/common/format.hpp index d7a0c43..6777657 100644 --- a/include/jsoncons_ext/jsonschema/common/format.hpp +++ b/include/jsoncons_ext/jsonschema/common/format.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include diff --git a/include/jsoncons_ext/jsonschema/common/keyword_validator.hpp b/include/jsoncons_ext/jsonschema/common/keyword_validator.hpp index 906f1d8..320f12d 100644 --- a/include/jsoncons_ext/jsonschema/common/keyword_validator.hpp +++ b/include/jsoncons_ext/jsonschema/common/keyword_validator.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -10,7 +10,6 @@ #include #include -#include #include #include #include @@ -456,7 +455,7 @@ namespace jsonschema { { auto s = instance.template as(); std::string content; - auto retval = jsoncons::decode_base64(s.begin(), s.end(), content); + auto retval = jsoncons::base64_to_bytes(s.begin(), s.end(), content); if (retval.ec != jsoncons::conv_errc::success) { walk_result result = reporter.error(this->make_validation_message( @@ -527,7 +526,7 @@ namespace jsonschema { if (content_encoding_ == "base64") { std::string content; - auto retval = jsoncons::decode_base64(str.begin(), str.end(), content); + auto retval = jsoncons::base64_to_bytes(str.begin(), str.end(), content); if (retval.ec != jsoncons::conv_errc::success) { return walk_result::advance; @@ -1225,14 +1224,16 @@ namespace jsonschema { eval_context this_context(context, this->keyword_name()); evaluation_results local_results1; + Json local_patch1(json_array_arg); std::size_t count = 0; for (std::size_t i = 0; i < validators_.size(); ++i) { evaluation_results local_results2; + Json local_patch2{json_array_arg}; eval_context item_context(this_context, i); std::size_t errors = local_reporter.errors.size(); - walk_result result = validators_[i]->validate(item_context, instance, instance_location, local_results2, local_reporter, patch); + walk_result result = validators_[i]->validate(item_context, instance, instance_location, local_results2, local_reporter, local_patch2); if (result == walk_result::abort) { return result; @@ -1240,6 +1241,10 @@ namespace jsonschema { if (errors == local_reporter.errors.size()) { local_results1.merge(local_results2); + for (auto& item : local_patch2.array_range()) + { + local_patch1.push_back(std::move(item)); + } ++count; } //std::cout << "success: " << i << " " << success << "\n"; @@ -1248,6 +1253,10 @@ namespace jsonschema { if (count > 0) { results.merge(local_results1); + for (auto& item : local_patch1.array_range()) + { + patch.push_back(std::move(item)); + } } else { @@ -1321,14 +1330,16 @@ namespace jsonschema { eval_context this_context(context, this->keyword_name()); evaluation_results local_results1; + Json local_patch1(json_array_arg); std::vector indices; for (std::size_t i = 0; i < validators_.size(); ++i) { evaluation_results local_results2; + Json local_patch2{json_array_arg}; eval_context item_context(this_context, i); std::size_t errors = local_reporter.errors.size(); - walk_result result = validators_[i]->validate(item_context, instance, instance_location, local_results2, local_reporter, patch); + walk_result result = validators_[i]->validate(item_context, instance, instance_location, local_results2, local_reporter, local_patch2); if (result == walk_result::abort) { return result; @@ -1337,14 +1348,21 @@ namespace jsonschema { { local_results1.merge(local_results2); indices.push_back(i); + for (auto& item : local_patch2.array_range()) + { + local_patch1.push_back(std::move(item)); + } } //std::cout << "success: " << i << " " << success << "\n"; } - - + if (indices.size() == 1) { results.merge(local_results1); + for (auto& item : local_patch1.array_range()) + { + patch.push_back(std::move(item)); + } } else { @@ -1568,9 +1586,9 @@ namespace jsonschema { else if (instance.is_string_view() && instance.tag() == semantic_tag::bigint) { auto sv1 = instance.as_string_view(); - bigint n1 = bigint::from_string(sv1.data(), sv1.length()); + bigint n1(sv1.data(), sv1.length()); auto s2 = value_.as_string(); - bigint n2 = bigint::from_string(s2.data(), s2.length()); + bigint n2(s2.data(), s2.length()); if (n1 > n2) { walk_result result = reporter.error(this->make_validation_message( @@ -1664,9 +1682,9 @@ namespace jsonschema { else if (instance.is_string_view() && instance.tag() == semantic_tag::bigint) { auto sv1 = instance.as_string_view(); - bigint n1 = bigint::from_string(sv1.data(), sv1.length()); + bigint n1(sv1.data(), sv1.length()); auto s2 = value_.as_string(); - bigint n2 = bigint::from_string(s2.data(), s2.length()); + bigint n2(s2.data(), s2.length()); if (n1 >= n2) { walk_result result = reporter.error(this->make_validation_message( @@ -1760,9 +1778,9 @@ namespace jsonschema { else if (instance.is_string_view() && instance.tag() == semantic_tag::bigint) { auto sv1 = instance.as_string_view(); - bigint n1 = bigint::from_string(sv1.data(), sv1.length()); + bigint n1(sv1.data(), sv1.length()); auto s2 = value_.as_string(); - bigint n2 = bigint::from_string(s2.data(), s2.length()); + bigint n2(s2.data(), s2.length()); if (n1 < n2) { walk_result result = reporter.error(this->make_validation_message( @@ -1856,9 +1874,9 @@ namespace jsonschema { else if (instance.is_string_view() && instance.tag() == semantic_tag::bigint) { auto sv1 = instance.as_string_view(); - bigint n1 = bigint::from_string(sv1.data(), sv1.length()); + bigint n1(sv1.data(), sv1.length()); auto s2 = value_.as_string(); - bigint n2 = bigint::from_string(s2.data(), s2.length()); + bigint n2(s2.data(), s2.length()); if (n1 <= n2) { walk_result result = reporter.error(this->make_validation_message( @@ -2491,7 +2509,7 @@ namespace jsonschema { message.append(to_string(expected_types_[i])); } message.append(", found "); - message.append(to_schema_type(instance.type())); + message.append(to_schema_type(instance.type(), instance.tag())); return reporter.error(this->make_validation_message( this_context.eval_path(), @@ -2501,37 +2519,37 @@ namespace jsonschema { return walk_result::advance; } - std::string to_schema_type(json_type type) const + std::string to_schema_type(json_type type, semantic_tag tag) const { switch (type) { - case json_type::null_value: + case json_type::null: { return "null"; } - case json_type::bool_value: + case json_type::boolean: { return "boolean"; } - case json_type::int64_value: - case json_type::uint64_value: + case json_type::int64: + case json_type::uint64: { return "integer"; } - case json_type::half_value: - case json_type::double_value: + case json_type::float16: + case json_type::float64: { return "number"; } - case json_type::string_value: + case json_type::string: { - return "string"; + return is_number_tag(tag) ? "number" : "string"; } - case json_type::array_value: + case json_type::array: { return "array"; } - case json_type::object_value: + case json_type::object: { return "object"; } @@ -2634,7 +2652,7 @@ namespace jsonschema { // If default value is available, update patch jsonpointer::json_pointer prop_location = instance_location / prop.first; - update_patch(patch, prop_location, std::move(*default_value)); + update_patch(patch, prop_location, *default_value); } } } @@ -2700,12 +2718,12 @@ namespace jsonschema { return walk(context, instance, instance_location, reporter, allowed_properties); } - void update_patch(Json& patch, const jsonpointer::json_pointer& instance_location, Json&& default_value) const + void update_patch(Json& patch, const jsonpointer::json_pointer& instance_location, const Json& default_value) const { Json j; j.try_emplace("op", "add"); j.try_emplace("path", instance_location.string()); - j.try_emplace("value", std::forward(default_value)); + j.try_emplace("value", default_value); patch.push_back(std::move(j)); } }; @@ -2918,7 +2936,6 @@ namespace jsonschema { { return result; } - break; } } } diff --git a/include/jsoncons_ext/jsonschema/common/keyword_validator_factory.hpp b/include/jsoncons_ext/jsonschema/common/keyword_validator_factory.hpp index 798906b..cf6fcfe 100644 --- a/include/jsoncons_ext/jsonschema/common/keyword_validator_factory.hpp +++ b/include/jsoncons_ext/jsonschema/common/keyword_validator_factory.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -87,10 +87,11 @@ namespace jsonschema { for (const auto& prop : sch.object_range()) { + std::string sub_keys[] = {keyword}; pattern_properties.emplace_back( std::make_pair( std::regex(prop.key(), std::regex::ECMAScript), - factory_->make_cross_draft_schema_validator(context, prop.value(), {}, anchor_dict))); + factory_->make_cross_draft_schema_validator(context, prop.value(), sub_keys, anchor_dict))); } @@ -237,7 +238,7 @@ namespace jsonschema { switch (sch.type()) { - case json_type::string_value: + case json_type::string: { auto type = sch.template as(); if (type == "null") @@ -275,7 +276,7 @@ namespace jsonschema { break; } - case json_type::array_value: // "type": ["type1", "type2"] + case json_type::array: // "type": ["type1", "type2"] { for (const auto& item : sch.array_range()) { @@ -619,7 +620,7 @@ namespace jsonschema { { switch (dep.value().type()) { - case json_type::array_value: + case json_type::array: { auto location = context.make_schema_location("dependencies"); dependent_required.emplace(dep.key(), @@ -627,8 +628,8 @@ namespace jsonschema { dep.value(), sch)); break; } - case json_type::bool_value: - case json_type::object_value: + case json_type::boolean: + case json_type::object: { std::string sub_keys[] = {"dependencies"}; dependent_schemas.emplace(dep.key(), @@ -671,7 +672,7 @@ namespace jsonschema { { switch (dep.value().type()) { - case json_type::array_value: + case json_type::array: { auto location = context.make_schema_location("dependentRequired"); dependent_required.emplace(dep.key(), @@ -700,8 +701,8 @@ namespace jsonschema { { switch (dep.value().type()) { - case json_type::bool_value: - case json_type::object_value: + case json_type::boolean: + case json_type::object: { std::string sub_keys[] = {"dependentSchemas"}; dependent_schemas.emplace(dep.key(), @@ -728,7 +729,7 @@ namespace jsonschema { uri schema_location{context.make_schema_location("items")}; - if (sch.type() == json_type::array_value) + if (sch.type() == json_type::array) { std::size_t c = 0; for (const auto& subsch : sch.array_range()) @@ -819,7 +820,7 @@ namespace jsonschema { uri schema_location{context.make_schema_location("prefixItems")}; - if (sch.type() == json_type::array_value) + if (sch.type() == json_type::array) { std::size_t c = 0; for (const auto& subsch : sch.array_range()) diff --git a/include/jsoncons_ext/jsonschema/common/schema_validator.hpp b/include/jsoncons_ext/jsonschema/common/schema_validator.hpp index 8fca53f..318724b 100644 --- a/include/jsoncons_ext/jsonschema/common/schema_validator.hpp +++ b/include/jsoncons_ext/jsonschema/common/schema_validator.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -236,7 +236,7 @@ namespace jsonschema { std::unique_ptr> unevaluated_properties_val_; std::unique_ptr> unevaluated_items_val_; std::map defs_; - Json default_value_; + jsoncons::optional default_value_; bool recursive_anchor_; jsoncons::optional dynamic_anchor_; anchor_schema_map_type anchor_dict_; @@ -252,7 +252,7 @@ namespace jsonschema { const jsoncons::optional& id, std::vector&& validators, std::map&& defs, - Json&& default_value) + jsoncons::optional&& default_value) : schema_location_(schema_location), id_(id), validators_(std::move(validators)), @@ -269,7 +269,7 @@ namespace jsonschema { std::unique_ptr>&& unevaluated_properties_val, std::unique_ptr>&& unevaluated_items_val, std::map&& defs, - Json&& default_value, bool recursive_anchor) + jsoncons::optional&& default_value, bool recursive_anchor) : schema_location_(schema_location), id_(id), validators_(std::move(validators)), @@ -288,7 +288,7 @@ namespace jsonschema { std::unique_ptr>&& unevaluated_properties_val, std::unique_ptr>&& unevaluated_items_val, std::map&& defs, - Json&& default_value, + jsoncons::optional&& default_value, jsoncons::optional&& dynamic_anchor, anchor_schema_map_type&& anchor_dict) : schema_location_(schema_location), diff --git a/include/jsoncons_ext/jsonschema/common/schema_validator_factory_base.hpp b/include/jsoncons_ext/jsonschema/common/schema_validator_factory_base.hpp index ef3fc36..56bbaef 100644 --- a/include/jsoncons_ext/jsonschema/common/schema_validator_factory_base.hpp +++ b/include/jsoncons_ext/jsonschema/common/schema_validator_factory_base.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -158,7 +158,7 @@ namespace jsonschema { } else { - JSONCONS_THROW(jsonschema::schema_error("Don't know how to load JSON Schema '" + loc.base().string() + "'" )); + JSONCONS_THROW(jsonschema::schema_error("Don't know how to load JSON Schema '" + loc.string() + "'" )); } } } @@ -214,7 +214,7 @@ namespace jsonschema { } // recursively add possible subschemas of unknown keywords - if (value.type() == json_type::object_value) + if (value.type() == json_type::object) { for (const auto& subsch : value.object_range()) { @@ -304,7 +304,7 @@ namespace jsonschema { schema_validator_ptr_type schema_val = schema_validator_ptr_type{}; switch (sch.type()) { - case json_type::object_value: + case json_type::object: { auto it = sch.find("$schema"); if (it != sch.object_range().end()) @@ -326,7 +326,7 @@ namespace jsonschema { } break; } - case json_type::bool_value: + case json_type::boolean: { return make_schema_validator(context, std::move(sch), keys, anchor_dict); } diff --git a/include/jsoncons_ext/jsonschema/common/uri_wrapper.hpp b/include/jsoncons_ext/jsonschema/common/uri_wrapper.hpp index 19c8715..a97f4d9 100644 --- a/include/jsoncons_ext/jsonschema/common/uri_wrapper.hpp +++ b/include/jsoncons_ext/jsonschema/common/uri_wrapper.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -114,7 +114,7 @@ namespace jsonschema { if (has_plain_name_fragment()) return *this; - jsoncons::jsonpointer::json_pointer pointer(std::string(uri_.fragment())); + jsoncons::jsonpointer::json_pointer pointer(std::string(uri_.encoded_fragment())); pointer /= field; jsoncons::uri new_uri(uri_, uri_fragment_part, pointer.to_string()); diff --git a/include/jsoncons_ext/jsonschema/common/validator.hpp b/include/jsoncons_ext/jsonschema/common/validator.hpp index a285bb0..56b130f 100644 --- a/include/jsoncons_ext/jsonschema/common/validator.hpp +++ b/include/jsoncons_ext/jsonschema/common/validator.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/include/jsoncons_ext/jsonschema/draft201909/schema_draft201909.hpp b/include/jsoncons_ext/jsonschema/draft201909/schema_draft201909.hpp index e033930..1eb54f1 100644 --- a/include/jsoncons_ext/jsonschema/draft201909/schema_draft201909.hpp +++ b/include/jsoncons_ext/jsonschema/draft201909/schema_draft201909.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/include/jsoncons_ext/jsonschema/draft201909/schema_validator_factory_201909.hpp b/include/jsoncons_ext/jsonschema/draft201909/schema_validator_factory_201909.hpp index 3322347..bc5452d 100644 --- a/include/jsoncons_ext/jsonschema/draft201909/schema_validator_factory_201909.hpp +++ b/include/jsoncons_ext/jsonschema/draft201909/schema_validator_factory_201909.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -8,7 +8,6 @@ #define JSONCONS_EXT_JSONSCHEMA_DRAFT201909_SCHEMA_VALIDATOR_FACTORY_201909_HPP #include -#include #include #include #include @@ -204,7 +203,7 @@ namespace draft201909 { switch (sch.type()) { - case json_type::bool_value: + case json_type::boolean: { schema_validator_ptr = this->make_boolean_schema(new_context, sch); schema_validator* p = schema_validator_ptr.get(); @@ -214,7 +213,7 @@ namespace draft201909 { } break; } - case json_type::object_value: + case json_type::object: { std::set known_keywords; @@ -245,7 +244,7 @@ namespace draft201909 { const compilation_context& context, const Json& sch, anchor_uri_map_type& anchor_dict) { jsoncons::optional id = context.id(); - Json default_value{ jsoncons::null_type()}; + jsoncons::optional default_value; std::vector validators; std::unique_ptr> unevaluated_properties_val; std::unique_ptr> unevaluated_items_val; @@ -396,12 +395,12 @@ namespace draft201909 { if (it != sch.object_range().end()) { - if ((*it).value().type() == json_type::array_value) + if ((*it).value().type() == json_type::array) { validators.emplace_back(factory_.make_prefix_items_validator_07(context, (*it).value(), sch, anchor_dict)); } - else if ((*it).value().type() == json_type::object_value || - (*it).value().type() == json_type::bool_value) + else if ((*it).value().type() == json_type::object || + (*it).value().type() == json_type::boolean) { validators.emplace_back(factory_.make_items_validator("items", context, (*it).value(), sch, anchor_dict)); } diff --git a/include/jsoncons_ext/jsonschema/draft202012/schema_draft202012.hpp b/include/jsoncons_ext/jsonschema/draft202012/schema_draft202012.hpp index 5d97fe8..a2d218a 100644 --- a/include/jsoncons_ext/jsonschema/draft202012/schema_draft202012.hpp +++ b/include/jsoncons_ext/jsonschema/draft202012/schema_draft202012.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/include/jsoncons_ext/jsonschema/draft202012/schema_validator_factory_202012.hpp b/include/jsoncons_ext/jsonschema/draft202012/schema_validator_factory_202012.hpp index 17ce203..d78c389 100644 --- a/include/jsoncons_ext/jsonschema/draft202012/schema_validator_factory_202012.hpp +++ b/include/jsoncons_ext/jsonschema/draft202012/schema_validator_factory_202012.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -8,7 +8,6 @@ #define JSONCONS_EXT_JSONSCHEMA_DRAFT202012_SCHEMA_VALIDATOR_FACTORY_202012_HPP #include -#include #include #include #include @@ -205,7 +204,7 @@ namespace draft202012 { switch (sch.type()) { - case json_type::bool_value: + case json_type::boolean: { schema_validator_ptr = this->make_boolean_schema(new_context, sch); schema_validator* p = schema_validator_ptr.get(); @@ -215,7 +214,7 @@ namespace draft202012 { } break; } - case json_type::object_value: + case json_type::object: { schema_validator_ptr = make_object_schema_validator(new_context, sch, anchor_dict); schema_validator* p = schema_validator_ptr.get(); @@ -245,7 +244,7 @@ namespace draft202012 { const Json& sch, anchor_uri_map_type& anchor_dict) { jsoncons::optional id = context.id(); - Json default_value{jsoncons::null_type()}; + jsoncons::optional default_value; std::vector validators; std::unique_ptr> unevaluated_properties_val; std::unique_ptr> unevaluated_items_val; @@ -396,7 +395,7 @@ namespace draft202012 { if (it != sch.object_range().end()) { - if ((*it).value().type() == json_type::array_value) + if ((*it).value().type() == json_type::array) { validators.emplace_back(factory_.make_prefix_items_validator(context, (*it).value(), sch, local_anchor_dict)); } @@ -406,7 +405,7 @@ namespace draft202012 { it = sch.find("items"); if (it != sch.object_range().end()) { - if ((*it).value().type() == json_type::object_value || (*it).value().type() == json_type::bool_value) + if ((*it).value().type() == json_type::object || (*it).value().type() == json_type::boolean) { validators.emplace_back(factory_.make_items_validator("items", context, (*it).value(), sch, local_anchor_dict)); } diff --git a/include/jsoncons_ext/jsonschema/draft4/schema_draft4.hpp b/include/jsoncons_ext/jsonschema/draft4/schema_draft4.hpp index 6698ec9..65bdd09 100644 --- a/include/jsoncons_ext/jsonschema/draft4/schema_draft4.hpp +++ b/include/jsoncons_ext/jsonschema/draft4/schema_draft4.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/include/jsoncons_ext/jsonschema/draft4/schema_validator_factory_4.hpp b/include/jsoncons_ext/jsonschema/draft4/schema_validator_factory_4.hpp index 75532b0..27493d3 100644 --- a/include/jsoncons_ext/jsonschema/draft4/schema_validator_factory_4.hpp +++ b/include/jsoncons_ext/jsonschema/draft4/schema_validator_factory_4.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -8,7 +8,6 @@ #define JSONCONS_EXT_JSONSCHEMA_DRAFT4_SCHEMA_VALIDATOR_FACTORY_4_HPP #include -#include #include #include #include @@ -128,7 +127,7 @@ namespace draft4 { switch (sch.type()) { - case json_type::bool_value: + case json_type::boolean: { schema_validator_ptr = this->make_boolean_schema(new_context, sch); schema_validator* p = schema_validator_ptr.get(); @@ -138,7 +137,7 @@ namespace draft4 { } break; } - case json_type::object_value: + case json_type::object: { auto it = sch.find("$ref"); if (it != sch.object_range().end()) // this schema is a reference @@ -156,7 +155,7 @@ namespace draft4 { } } - Json default_value{ jsoncons::null_type() }; + jsoncons::optional default_value; uri relative((*it).value().template as()); auto id = context.get_base_uri().resolve(relative); validators.push_back(this->get_or_create_reference(sch, uri_wrapper{id})); @@ -194,7 +193,7 @@ namespace draft4 { const Json& sch, anchor_uri_map_type& anchor_dict) { jsoncons::optional id = context.id(); - Json default_value{ jsoncons::null_type() }; + jsoncons::optional default_value; std::vector validators; std::map defs; @@ -267,12 +266,12 @@ namespace draft4 { if (it != sch.object_range().end()) { - if ((*it).value().type() == json_type::array_value) + if ((*it).value().type() == json_type::array) { validators.emplace_back(factory_.make_prefix_items_validator_07(context, (*it).value(), sch, anchor_dict)); } - else if ((*it).value().type() == json_type::object_value || - (*it).value().type() == json_type::bool_value) + else if ((*it).value().type() == json_type::object || + (*it).value().type() == json_type::boolean) { validators.emplace_back(factory_.make_items_validator("items", context, (*it).value(), sch, anchor_dict)); } diff --git a/include/jsoncons_ext/jsonschema/draft6/schema_draft6.hpp b/include/jsoncons_ext/jsonschema/draft6/schema_draft6.hpp index 1536720..1e9a959 100644 --- a/include/jsoncons_ext/jsonschema/draft6/schema_draft6.hpp +++ b/include/jsoncons_ext/jsonschema/draft6/schema_draft6.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/include/jsoncons_ext/jsonschema/draft6/schema_validator_factory_6.hpp b/include/jsoncons_ext/jsonschema/draft6/schema_validator_factory_6.hpp index 5a1e497..4c5e3fa 100644 --- a/include/jsoncons_ext/jsonschema/draft6/schema_validator_factory_6.hpp +++ b/include/jsoncons_ext/jsonschema/draft6/schema_validator_factory_6.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -8,7 +8,6 @@ #define JSONCONS_EXT_JSONSCHEMA_DRAFT6_SCHEMA_VALIDATOR_FACTORY_6_HPP #include -#include #include #include #include @@ -139,7 +138,7 @@ namespace draft6 { switch (sch.type()) { - case json_type::bool_value: + case json_type::boolean: { schema_validator_ptr = this->make_boolean_schema(new_context, sch); schema_validator* p = schema_validator_ptr.get(); @@ -149,7 +148,7 @@ namespace draft6 { } break; } - case json_type::object_value: + case json_type::object: { auto it = sch.find("$ref"); if (it != sch.object_range().end()) // this schema is a reference @@ -167,7 +166,7 @@ namespace draft6 { } } - Json default_value{ jsoncons::null_type() }; + jsoncons::optional default_value; uri relative((*it).value().template as()); auto id = context.get_base_uri().resolve(relative) ; validators.push_back(this->get_or_create_reference(sch, uri_wrapper{id})); @@ -205,7 +204,7 @@ namespace draft6 { const Json& sch, anchor_uri_map_type& anchor_dict) { jsoncons::optional id = context.id(); - Json default_value{ jsoncons::null_type() }; + jsoncons::optional default_value; std::vector validators; std::map defs; @@ -278,12 +277,12 @@ namespace draft6 { if (it != sch.object_range().end()) { - if ((*it).value().type() == json_type::array_value) + if ((*it).value().type() == json_type::array) { validators.emplace_back(factory_.make_prefix_items_validator_07(context, (*it).value(), sch, anchor_dict)); } - else if ((*it).value().type() == json_type::object_value || - (*it).value().type() == json_type::bool_value) + else if ((*it).value().type() == json_type::object || + (*it).value().type() == json_type::boolean) { validators.emplace_back(factory_.make_items_validator("items", context, (*it).value(), sch, anchor_dict)); } diff --git a/include/jsoncons_ext/jsonschema/draft7/schema_draft7.hpp b/include/jsoncons_ext/jsonschema/draft7/schema_draft7.hpp index 292207d..1193c8e 100644 --- a/include/jsoncons_ext/jsonschema/draft7/schema_draft7.hpp +++ b/include/jsoncons_ext/jsonschema/draft7/schema_draft7.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/include/jsoncons_ext/jsonschema/draft7/schema_validator_factory_7.hpp b/include/jsoncons_ext/jsonschema/draft7/schema_validator_factory_7.hpp index 23b75f0..9e11128 100644 --- a/include/jsoncons_ext/jsonschema/draft7/schema_validator_factory_7.hpp +++ b/include/jsoncons_ext/jsonschema/draft7/schema_validator_factory_7.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -8,7 +8,6 @@ #define JSONCONS_EXT_JSONSCHEMA_DRAFT7_SCHEMA_VALIDATOR_FACTORY_7_HPP #include -#include #include #include #include @@ -141,7 +140,7 @@ namespace draft7 { switch (sch.type()) { - case json_type::bool_value: + case json_type::boolean: { schema_validator_ptr = this->make_boolean_schema(new_context, sch); schema_validator* p = schema_validator_ptr.get(); @@ -151,7 +150,7 @@ namespace draft7 { } break; } - case json_type::object_value: + case json_type::object: { auto it = sch.find("$ref"); if (it != sch.object_range().end()) // this schema is a reference @@ -169,7 +168,7 @@ namespace draft7 { } } - Json default_value{ jsoncons::null_type() }; + jsoncons::optional default_value; uri relative((*it).value().template as()); auto id = context.get_base_uri().resolve(relative); validators.push_back(this->get_or_create_reference(sch, uri_wrapper{id})); @@ -207,7 +206,7 @@ namespace draft7 { const Json& sch, anchor_uri_map_type& anchor_dict) { jsoncons::optional id = context.id(); - Json default_value{ jsoncons::null_type() }; + jsoncons::optional default_value; std::vector validators; std::map defs; @@ -311,12 +310,12 @@ namespace draft7 { if (it != sch.object_range().end()) { - if ((*it).value().type() == json_type::array_value) + if ((*it).value().type() == json_type::array) { validators.emplace_back(factory_.make_prefix_items_validator_07(context, (*it).value(), sch, anchor_dict)); } - else if ((*it).value().type() == json_type::object_value || - (*it).value().type() == json_type::bool_value) + else if ((*it).value().type() == json_type::object || + (*it).value().type() == json_type::boolean) { validators.emplace_back(factory_.make_items_validator("items", context, (*it).value(), sch, anchor_dict)); } diff --git a/include/jsoncons_ext/jsonschema/evaluation_options.hpp b/include/jsoncons_ext/jsonschema/evaluation_options.hpp index 97e5b2d..afe978a 100644 --- a/include/jsoncons_ext/jsonschema/evaluation_options.hpp +++ b/include/jsoncons_ext/jsonschema/evaluation_options.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/include/jsoncons_ext/jsonschema/json_schema.hpp b/include/jsoncons_ext/jsonschema/json_schema.hpp index 2424ad9..6d7be9b 100644 --- a/include/jsoncons_ext/jsonschema/json_schema.hpp +++ b/include/jsoncons_ext/jsonschema/json_schema.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/include/jsoncons_ext/jsonschema/json_schema_factory.hpp b/include/jsoncons_ext/jsonschema/json_schema_factory.hpp index eb3a434..9df60d2 100644 --- a/include/jsoncons_ext/jsonschema/json_schema_factory.hpp +++ b/include/jsoncons_ext/jsonschema/json_schema_factory.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/include/jsoncons_ext/jsonschema/jsonschema.hpp b/include/jsoncons_ext/jsonschema/jsonschema.hpp index 3bdf457..fcfc96e 100644 --- a/include/jsoncons_ext/jsonschema/jsonschema.hpp +++ b/include/jsoncons_ext/jsonschema/jsonschema.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/include/jsoncons_ext/jsonschema/jsonschema_error.hpp b/include/jsoncons_ext/jsonschema/jsonschema_error.hpp index bc14332..84dafe7 100644 --- a/include/jsoncons_ext/jsonschema/jsonschema_error.hpp +++ b/include/jsoncons_ext/jsonschema/jsonschema_error.hpp @@ -1,4 +1,4 @@ -/// Copyright 2013-2025 Daniel Parker +/// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/include/jsoncons_ext/jsonschema/validation_message.hpp b/include/jsoncons_ext/jsonschema/validation_message.hpp index 5d0d222..7959c21 100644 --- a/include/jsoncons_ext/jsonschema/validation_message.hpp +++ b/include/jsoncons_ext/jsonschema/validation_message.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -9,7 +9,7 @@ #include #include -#include +#include #include #include #include diff --git a/include/jsoncons_ext/mergepatch/mergepatch.hpp b/include/jsoncons_ext/mergepatch/mergepatch.hpp index 16e2900..4fb118e 100644 --- a/include/jsoncons_ext/mergepatch/mergepatch.hpp +++ b/include/jsoncons_ext/mergepatch/mergepatch.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/include/jsoncons_ext/msgpack/decode_msgpack.hpp b/include/jsoncons_ext/msgpack/decode_msgpack.hpp index c3ce8bf..e6d45e3 100644 --- a/include/jsoncons_ext/msgpack/decode_msgpack.hpp +++ b/include/jsoncons_ext/msgpack/decode_msgpack.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,7 +15,8 @@ #include #include #include -#include +#include +#include #include #include @@ -25,180 +26,240 @@ namespace jsoncons { namespace msgpack { - template - typename std::enable_if::value && - ext_traits::is_byte_sequence::value,T>::type - decode_msgpack(const Source& v, - const msgpack_decode_options& options = msgpack_decode_options()) - { - jsoncons::json_decoder decoder; - auto adaptor = make_json_visitor_adaptor(decoder); - basic_msgpack_reader reader(v, adaptor, options); - reader.read(); - if (!decoder.is_valid()) - { - JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); - } - return decoder.get_result(); - } - - template - typename std::enable_if::value && - ext_traits::is_byte_sequence::value,T>::type - decode_msgpack(const Source& v, - const msgpack_decode_options& options = msgpack_decode_options()) - { - basic_msgpack_cursor cursor(v, options); - json_decoder> decoder{}; - - std::error_code ec; - T val = decode_traits::decode(cursor, decoder, ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); - } - return val; - } - - template - typename std::enable_if::value,T>::type - decode_msgpack(std::istream& is, - const msgpack_decode_options& options = msgpack_decode_options()) - { - jsoncons::json_decoder decoder; - auto adaptor = make_json_visitor_adaptor(decoder); - msgpack_stream_reader reader(is, adaptor, options); - reader.read(); - if (!decoder.is_valid()) - { - JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); - } - return decoder.get_result(); - } - - template - typename std::enable_if::value,T>::type - decode_msgpack(std::istream& is, - const msgpack_decode_options& options = msgpack_decode_options()) - { - basic_msgpack_cursor cursor(is, options); - json_decoder> decoder{}; - - std::error_code ec; - T val = decode_traits::decode(cursor, decoder, ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); - } - return val; - } - - template - typename std::enable_if::value,T>::type - decode_msgpack(InputIt first, InputIt last, - const msgpack_decode_options& options = msgpack_decode_options()) - { - jsoncons::json_decoder decoder; - auto adaptor = make_json_visitor_adaptor(decoder); - basic_msgpack_reader> reader(binary_iterator_source(first, last), adaptor, options); - reader.read(); - if (!decoder.is_valid()) - { - JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); - } - return decoder.get_result(); - } - - template - typename std::enable_if::value,T>::type - decode_msgpack(InputIt first, InputIt last, - const msgpack_decode_options& options = msgpack_decode_options()) - { - basic_msgpack_cursor> cursor(binary_iterator_source(first, last), options); - json_decoder> decoder{}; - - std::error_code ec; - T val = decode_traits::decode(cursor, decoder, ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); - } - return val; - } - - // With leading allocator_set parameter - - template - typename std::enable_if::value && - ext_traits::is_byte_sequence::value,T>::type - decode_msgpack(const allocator_set& alloc_set, - const Source& v, - const msgpack_decode_options& options = msgpack_decode_options()) - { - json_decoder decoder(alloc_set.get_allocator(), alloc_set.get_temp_allocator()); - auto adaptor = make_json_visitor_adaptor(decoder); - basic_msgpack_reader reader(v, adaptor, options, alloc_set.get_temp_allocator()); - reader.read(); - if (!decoder.is_valid()) - { - JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); - } - return decoder.get_result(); - } - - template - typename std::enable_if::value && - ext_traits::is_byte_sequence::value,T>::type - decode_msgpack(const allocator_set& alloc_set, - const Source& v, - const msgpack_decode_options& options = msgpack_decode_options()) - { - basic_msgpack_cursor cursor(v, options, alloc_set.get_temp_allocator()); - json_decoder,TempAllocator> decoder(alloc_set.get_temp_allocator(), alloc_set.get_temp_allocator()); - - std::error_code ec; - T val = decode_traits::decode(cursor, decoder, ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); - } - return val; - } - - template - typename std::enable_if::value,T>::type - decode_msgpack(const allocator_set& alloc_set, - std::istream& is, - const msgpack_decode_options& options = msgpack_decode_options()) - { - json_decoder decoder(alloc_set.get_allocator(), alloc_set.get_temp_allocator()); - auto adaptor = make_json_visitor_adaptor(decoder); - basic_msgpack_reader reader(is, adaptor, options, alloc_set.get_temp_allocator()); - reader.read(); - if (!decoder.is_valid()) - { - JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); - } - return decoder.get_result(); - } - - template - typename std::enable_if::value,T>::type - decode_msgpack(const allocator_set& alloc_set, - std::istream& is, - const msgpack_decode_options& options = msgpack_decode_options()) - { - basic_msgpack_cursor cursor(is, options, alloc_set.get_temp_allocator()); - json_decoder,TempAllocator> decoder(alloc_set.get_temp_allocator(), alloc_set.get_temp_allocator()); - - std::error_code ec; - T val = decode_traits::decode(cursor, decoder, ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); - } - return val; +template +typename std::enable_if::value && + ext_traits::is_byte_sequence::value,read_result>::type +try_decode_msgpack(const BytesLike& v, + const msgpack_decode_options& options = msgpack_decode_options()) +{ + using value_type = T; + using result_type = read_result; + + std::error_code ec; + jsoncons::json_decoder decoder; + auto adaptor = make_json_visitor_adaptor(decoder); + basic_msgpack_reader reader(v, adaptor, options); + reader.read(ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, reader.line(), reader.column()}; + } + if (JSONCONS_UNLIKELY(!decoder.is_valid())) + { + return result_type{jsoncons::unexpect, conv_errc::conversion_failed, reader.line(), reader.column()}; + } + return result_type{decoder.get_result()}; +} + +template +typename std::enable_if::value && + ext_traits::is_byte_sequence::value,read_result>::type +try_decode_msgpack(const BytesLike& v, + const msgpack_decode_options& options = msgpack_decode_options()) +{ + using value_type = T; + using result_type = read_result; + + std::error_code ec; + basic_msgpack_cursor cursor(v, options, ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, cursor.line(), cursor.column()}; + } + + return reflect::decode_traits::try_decode(make_alloc_set(), cursor); +} + +template +typename std::enable_if::value,read_result>::type +try_decode_msgpack(std::istream& is, + const msgpack_decode_options& options = msgpack_decode_options()) +{ + using value_type = T; + using result_type = read_result; + + std::error_code ec; + jsoncons::json_decoder decoder; + auto adaptor = make_json_visitor_adaptor(decoder); + msgpack_stream_reader reader(is, adaptor, options); + reader.read(ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, reader.line(), reader.column()}; + } + if (JSONCONS_UNLIKELY(!decoder.is_valid())) + { + return result_type{jsoncons::unexpect, conv_errc::conversion_failed, reader.line(), reader.column()}; + } + return result_type{decoder.get_result()}; +} + +template +typename std::enable_if::value,read_result>::type +try_decode_msgpack(std::istream& is, + const msgpack_decode_options& options = msgpack_decode_options()) +{ + using value_type = T; + using result_type = read_result; + + std::error_code ec; + basic_msgpack_cursor cursor(is, options, ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, cursor.line(), cursor.column()}; + } + + return reflect::decode_traits::try_decode(make_alloc_set(), cursor); +} + +template +typename std::enable_if::value,read_result>::type +try_decode_msgpack(InputIt first, InputIt last, + const msgpack_decode_options& options = msgpack_decode_options()) +{ + using value_type = T; + using result_type = read_result; + + std::error_code ec; + jsoncons::json_decoder decoder; + auto adaptor = make_json_visitor_adaptor(decoder); + basic_msgpack_reader> reader(binary_iterator_source(first, last), adaptor, options); + reader.read(ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, reader.line(), reader.column()}; + } + if (JSONCONS_UNLIKELY(!decoder.is_valid())) + { + return result_type{jsoncons::unexpect, conv_errc::conversion_failed, reader.line(), reader.column()}; + } + return result_type{decoder.get_result()}; +} + +template +typename std::enable_if::value,read_result>::type +try_decode_msgpack(InputIt first, InputIt last, + const msgpack_decode_options& options = msgpack_decode_options()) +{ + using value_type = T; + using result_type = read_result; + + std::error_code ec; + basic_msgpack_cursor> cursor(binary_iterator_source(first, last), options, ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, cursor.line(), cursor.column()}; + } + + return reflect::decode_traits::try_decode(make_alloc_set(), cursor); +} + +// With leading allocator_set parameter + +template +typename std::enable_if::value && + ext_traits::is_byte_sequence::value,read_result>::type +try_decode_msgpack(const allocator_set& aset, + const BytesLike& v, + const msgpack_decode_options& options = msgpack_decode_options()) +{ + using value_type = T; + using result_type = read_result; + + std::error_code ec; + json_decoder decoder(aset.get_allocator(), aset.get_temp_allocator()); + auto adaptor = make_json_visitor_adaptor(decoder); + basic_msgpack_reader reader(v, adaptor, options, aset.get_temp_allocator()); + reader.read(ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, reader.line(), reader.column()}; + } + if (JSONCONS_UNLIKELY(!decoder.is_valid())) + { + return result_type{jsoncons::unexpect, conv_errc::conversion_failed, reader.line(), reader.column()}; + } + return result_type{decoder.get_result()}; +} + +template +typename std::enable_if::value && + ext_traits::is_byte_sequence::value,read_result>::type +try_decode_msgpack(const allocator_set& aset, + const BytesLike& v, + const msgpack_decode_options& options = msgpack_decode_options()) +{ + using value_type = T; + using result_type = read_result; + + std::error_code ec; + basic_msgpack_cursor cursor(std::allocator_arg, aset.get_temp_allocator(), v, options, ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, cursor.line(), cursor.column()}; + } + + return reflect::decode_traits::try_decode(aset, cursor); +} + +template +typename std::enable_if::value,read_result>::type +try_decode_msgpack(const allocator_set& aset, + std::istream& is, + const msgpack_decode_options& options = msgpack_decode_options()) +{ + using value_type = T; + using result_type = read_result; + using byte_allocator_type = typename std::allocator_traits:: template rebind_alloc; + using stream_source_type = stream_source; + + std::error_code ec; + json_decoder decoder(aset.get_allocator(), aset.get_temp_allocator()); + auto adaptor = make_json_visitor_adaptor(decoder); + basic_msgpack_reader reader(stream_source_type(is,aset.get_temp_allocator()), + adaptor, options, aset.get_temp_allocator()); + reader.read(ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, reader.line(), reader.column()}; + } + if (JSONCONS_UNLIKELY(!decoder.is_valid())) + { + return result_type{jsoncons::unexpect, conv_errc::conversion_failed, reader.line(), reader.column()}; + } + return result_type{decoder.get_result()}; +} + +template +typename std::enable_if::value,read_result>::type +try_decode_msgpack(const allocator_set& aset, + std::istream& is, + const msgpack_decode_options& options = msgpack_decode_options()) +{ + using value_type = T; + using result_type = read_result; + + std::error_code ec; + basic_msgpack_cursor cursor( + std::allocator_arg, aset.get_temp_allocator(), is, options, ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, cursor.line(), cursor.column()}; + } + + return reflect::decode_traits::try_decode(aset, cursor); +} + +template +T decode_msgpack(Args&& ... args) +{ + auto result = try_decode_msgpack(std::forward(args)...); + if (!result) + { + JSONCONS_THROW(ser_error(result.error().code(), result.error().line(), result.error().column())); } + return std::move(*result); +} } // namespace msgpack } // namespace jsoncons diff --git a/include/jsoncons_ext/msgpack/encode_msgpack.hpp b/include/jsoncons_ext/msgpack/encode_msgpack.hpp index f59094c..7802016 100644 --- a/include/jsoncons_ext/msgpack/encode_msgpack.hpp +++ b/include/jsoncons_ext/msgpack/encode_msgpack.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -11,14 +11,15 @@ #include #include -#include -#include #include #include -#include +#include #include #include +#include +#include #include +#include #include #include @@ -28,119 +29,109 @@ namespace msgpack { template typename std::enable_if::value && - ext_traits::is_back_insertable_byte_container::value,void>::type - encode_msgpack(const T& j, - ByteContainer& cont, - const msgpack_encode_options& options = msgpack_encode_options()) + ext_traits::is_back_insertable_byte_container::value,write_result>::type + try_encode_msgpack(const T& j, + ByteContainer& cont, + const msgpack_encode_options& options = msgpack_encode_options()) { using char_type = typename T::char_type; basic_msgpack_encoder> encoder(cont, options); auto adaptor = make_json_visitor_adaptor>(encoder); - j.dump(adaptor); + return j.try_dump(adaptor); } template typename std::enable_if::value && - ext_traits::is_back_insertable_byte_container::value,void>::type - encode_msgpack(const T& val, - ByteContainer& cont, - const msgpack_encode_options& options = msgpack_encode_options()) + ext_traits::is_back_insertable_byte_container::value,write_result>::type + try_encode_msgpack(const T& val, + ByteContainer& cont, + const msgpack_encode_options& options = msgpack_encode_options()) { basic_msgpack_encoder> encoder(cont, options); - std::error_code ec; - encode_traits::encode(val, encoder, json(), ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec)); - } + return reflect::encode_traits::try_encode(make_alloc_set(), val, encoder); } template - typename std::enable_if::value,void>::type - encode_msgpack(const T& j, - std::ostream& os, - const msgpack_encode_options& options = msgpack_encode_options()) + typename std::enable_if::value,write_result>::type + try_encode_msgpack(const T& j, + std::ostream& os, + const msgpack_encode_options& options = msgpack_encode_options()) { using char_type = typename T::char_type; msgpack_stream_encoder encoder(os, options); auto adaptor = make_json_visitor_adaptor>(encoder); - j.dump(adaptor); + return j.try_dump(adaptor); } template - typename std::enable_if::value,void>::type - encode_msgpack(const T& val, - std::ostream& os, - const msgpack_encode_options& options = msgpack_encode_options()) + typename std::enable_if::value,write_result>::type + try_encode_msgpack(const T& val, + std::ostream& os, + const msgpack_encode_options& options = msgpack_encode_options()) { msgpack_stream_encoder encoder(os, options); - std::error_code ec; - encode_traits::encode(val, encoder, json(), ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec)); - } + return reflect::encode_traits::try_encode(make_alloc_set(), val, encoder); } // with temp_allocator_arg_t - template + template typename std::enable_if::value && - ext_traits::is_back_insertable_byte_container::value,void>::type - encode_msgpack(const allocator_set& alloc_set, const T& j, - ByteContainer& cont, - const msgpack_encode_options& options = msgpack_encode_options()) + ext_traits::is_back_insertable_byte_container::value,write_result>::type + try_encode_msgpack(const allocator_set& aset, const T& j, + ByteContainer& cont, + const msgpack_encode_options& options = msgpack_encode_options()) { using char_type = typename T::char_type; - basic_msgpack_encoder,TempAllocator> encoder(cont, options, alloc_set.get_temp_allocator()); + basic_msgpack_encoder,TempAlloc> encoder(cont, options, aset.get_temp_allocator()); auto adaptor = make_json_visitor_adaptor>(encoder); - j.dump(adaptor); + return j.try_dump(adaptor); } - template + template typename std::enable_if::value && - ext_traits::is_back_insertable_byte_container::value,void>::type - encode_msgpack(const allocator_set& alloc_set, - const T& val, ByteContainer& cont, - const msgpack_encode_options& options = msgpack_encode_options()) + ext_traits::is_back_insertable_byte_container::value,write_result>::type + try_encode_msgpack(const allocator_set& aset, + const T& val, ByteContainer& cont, + const msgpack_encode_options& options = msgpack_encode_options()) { - basic_msgpack_encoder,TempAllocator> encoder(cont, options, alloc_set.get_temp_allocator()); - std::error_code ec; - encode_traits::encode(val, encoder, json(), ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec)); - } + basic_msgpack_encoder,TempAlloc> encoder(cont, options, aset.get_temp_allocator()); + return reflect::encode_traits::try_encode(aset, val, encoder); } - template - typename std::enable_if::value,void>::type - encode_msgpack(const allocator_set& alloc_set, - const T& j, - std::ostream& os, - const msgpack_encode_options& options = msgpack_encode_options()) + template + typename std::enable_if::value,write_result>::type + try_encode_msgpack(const allocator_set& aset, + const T& j, + std::ostream& os, + const msgpack_encode_options& options = msgpack_encode_options()) { using char_type = typename T::char_type; - basic_msgpack_encoder encoder(os, options, alloc_set.get_temp_allocator()); + basic_msgpack_encoder encoder(os, options, aset.get_temp_allocator()); auto adaptor = make_json_visitor_adaptor>(encoder); - j.dump(adaptor); + return j.try_dump(adaptor); } - template - typename std::enable_if::value,void>::type - encode_msgpack(const allocator_set& alloc_set, + template + typename std::enable_if::value,write_result>::type + try_encode_msgpack(const allocator_set& aset, const T& val, std::ostream& os, const msgpack_encode_options& options = msgpack_encode_options()) { - basic_msgpack_encoder encoder(os, options, alloc_set.get_temp_allocator()); - std::error_code ec; - encode_traits::encode(val, encoder, json(), ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec)); - } + basic_msgpack_encoder encoder(os, options, aset.get_temp_allocator()); + return reflect::encode_traits::try_encode(aset, val, encoder); + } + +template +void encode_msgpack(Args&& ... args) +{ + auto r = try_encode_msgpack(std::forward(args)...); + if (!r) + { + JSONCONS_THROW(ser_error(r.error())); } +} } // namespace msgpack } // namespace jsoncons diff --git a/include/jsoncons_ext/msgpack/msgpack.hpp b/include/jsoncons_ext/msgpack/msgpack.hpp index 216a1a2..5d15b7a 100644 --- a/include/jsoncons_ext/msgpack/msgpack.hpp +++ b/include/jsoncons_ext/msgpack/msgpack.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/include/jsoncons_ext/msgpack/msgpack_cursor.hpp b/include/jsoncons_ext/msgpack/msgpack_cursor.hpp index 1c8d237..1100691 100644 --- a/include/jsoncons_ext/msgpack/msgpack_cursor.hpp +++ b/include/jsoncons_ext/msgpack/msgpack_cursor.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include #include @@ -55,9 +55,9 @@ class basic_msgpack_cursor : public basic_staj_cursor, private virtual ser cursor_handler_adaptor_(cursor_visitor_, alloc) { parser_.cursor_mode(true); - if (!done()) + if (!parser_.done()) { - next(); + read_next(); } } @@ -93,9 +93,9 @@ class basic_msgpack_cursor : public basic_staj_cursor, private virtual ser eof_(false) { parser_.cursor_mode(true); - if (!done()) + if (!parser_.done()) { - next(ec); + read_next(ec); } } @@ -110,9 +110,9 @@ class basic_msgpack_cursor : public basic_staj_cursor, private virtual ser cursor_visitor_.reset(); cursor_handler_adaptor_.reset(); eof_ = false; - if (!done()) + if (!read_done()) { - next(); + read_next(); } } @@ -123,9 +123,9 @@ class basic_msgpack_cursor : public basic_staj_cursor, private virtual ser cursor_visitor_.reset(); cursor_handler_adaptor_.reset(); eof_ = false; - if (!done()) + if (!read_done()) { - next(); + read_next(); } } @@ -135,9 +135,9 @@ class basic_msgpack_cursor : public basic_staj_cursor, private virtual ser cursor_visitor_.reset(); cursor_handler_adaptor_.reset(); eof_ = false; - if (!done()) + if (!read_done()) { - next(ec); + read_next(ec); } } @@ -148,15 +148,15 @@ class basic_msgpack_cursor : public basic_staj_cursor, private virtual ser cursor_visitor_.reset(); cursor_handler_adaptor_.reset(); eof_ = false; - if (!done()) + if (!read_done()) { - next(ec); + read_next(ec); } } bool done() const override { - return parser_.done(); + return read_done(); } const staj_event& current() const override @@ -206,12 +206,7 @@ class basic_msgpack_cursor : public basic_staj_cursor, private virtual ser void next() override { - std::error_code ec; - next(ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column())); - } + read_next(); } void next(std::error_code& ec) override @@ -246,6 +241,22 @@ class basic_msgpack_cursor : public basic_staj_cursor, private virtual ser return staj_filter_view(cursor, pred); } private: + + bool read_done() const + { + return parser_.done(); + } + + void read_next() + { + std::error_code ec; + read_next(ec); + if (JSONCONS_UNLIKELY(ec)) + { + JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column())); + } + } + void read_next(std::error_code& ec) { if (cursor_visitor_.in_available()) diff --git a/include/jsoncons_ext/msgpack/msgpack_encoder.hpp b/include/jsoncons_ext/msgpack/msgpack_encoder.hpp index 430d132..13c522c 100644 --- a/include/jsoncons_ext/msgpack/msgpack_encoder.hpp +++ b/include/jsoncons_ext/msgpack/msgpack_encoder.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -18,12 +18,12 @@ #include #include -#include +#include #include #include #include #include -#include +#include #include #include #include @@ -82,7 +82,7 @@ namespace msgpack { }; Sink sink_; - const msgpack_encode_options options_; + int max_nesting_depth_; allocator_type alloc_; std::vector stack_; @@ -102,7 +102,7 @@ namespace msgpack { const msgpack_encode_options& options, const Allocator& alloc = Allocator()) : sink_(std::forward(sink)), - options_(options), + max_nesting_depth_(options.max_nesting_depth()), alloc_(alloc) { } @@ -143,7 +143,7 @@ namespace msgpack { JSONCONS_VISITOR_RETURN_TYPE visit_begin_object(std::size_t length, semantic_tag, const ser_context&, std::error_code& ec) final { - if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth())) + if (JSONCONS_UNLIKELY(++nesting_depth_ > max_nesting_depth_)) { ec = msgpack_errc::max_nesting_depth_exceeded; JSONCONS_VISITOR_RETURN; @@ -202,7 +202,7 @@ namespace msgpack { JSONCONS_VISITOR_RETURN_TYPE visit_begin_array(std::size_t length, semantic_tag, const ser_context&, std::error_code& ec) final { - if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth())) + if (JSONCONS_UNLIKELY(++nesting_depth_ > max_nesting_depth_)) { ec = msgpack_errc::max_nesting_depth_exceeded; JSONCONS_VISITOR_RETURN; @@ -302,7 +302,7 @@ namespace msgpack { case semantic_tag::epoch_second: { int64_t seconds; - auto result = jsoncons::detail::to_integer(sv.data(), sv.length(), seconds); + auto result = jsoncons::to_integer(sv.data(), sv.length(), seconds); if (!result) { ec = msgpack_errc::invalid_timestamp; @@ -313,7 +313,13 @@ namespace msgpack { } case semantic_tag::epoch_milli: { - bigint n = bigint::from_string(sv.data(), sv.length()); + bigint n; + auto result = to_bigint(sv.data(), sv.length(), n); + if (!result) + { + ec = msgpack_errc::invalid_timestamp; + JSONCONS_VISITOR_RETURN; + } if (n != 0) { bigint q; @@ -335,7 +341,13 @@ namespace msgpack { } case semantic_tag::epoch_nano: { - bigint n = bigint::from_string(sv.data(), sv.length()); + bigint n; + auto result = to_bigint(sv.data(), sv.length(), n); + if (!result) + { + ec = msgpack_errc::invalid_timestamp; + JSONCONS_VISITOR_RETURN; + } if (n != 0) { bigint q; diff --git a/include/jsoncons_ext/msgpack/msgpack_error.hpp b/include/jsoncons_ext/msgpack/msgpack_error.hpp index 54bffb2..0cdb081 100644 --- a/include/jsoncons_ext/msgpack/msgpack_error.hpp +++ b/include/jsoncons_ext/msgpack/msgpack_error.hpp @@ -1,4 +1,4 @@ -/// Copyright 2013-2025 Daniel Parker +/// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/include/jsoncons_ext/msgpack/msgpack_event_reader.hpp b/include/jsoncons_ext/msgpack/msgpack_event_reader.hpp index ca1d741..3f7eaa4 100644 --- a/include/jsoncons_ext/msgpack/msgpack_event_reader.hpp +++ b/include/jsoncons_ext/msgpack/msgpack_event_reader.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include #include @@ -49,9 +49,9 @@ namespace msgpack { : parser_(std::forward(source), options, alloc) { parser_.cursor_mode(true); - if (!done()) + if (!parser_.done()) { - next(); + read_next(); } } @@ -91,9 +91,9 @@ namespace msgpack { eof_(false) { parser_.cursor_mode(true); - if (!done()) + if (!parser_.done()) { - next(ec); + read_next(ec); } } @@ -107,9 +107,9 @@ namespace msgpack { parser_.reset(); cursor_visitor_.reset(); eof_ = false; - if (!done()) + if (!read_done()) { - next(); + read_next(); } } @@ -119,9 +119,9 @@ namespace msgpack { parser_.reset(std::forward(source)); cursor_visitor_.reset(); eof_ = false; - if (!done()) + if (!read_done()) { - next(); + read_next(); } } @@ -130,9 +130,9 @@ namespace msgpack { parser_.reset(); cursor_visitor_.reset(); eof_ = false; - if (!done()) + if (!read_done()) { - next(ec); + read_next(ec); } } @@ -142,9 +142,9 @@ namespace msgpack { parser_.reset(std::forward(source)); cursor_visitor_.reset(); eof_ = false; - if (!done()) + if (!read_done()) { - next(ec); + read_next(ec); } } @@ -200,12 +200,7 @@ namespace msgpack { void next() override { - std::error_code ec; - next(ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column())); - } + read_next(); } void next(std::error_code& ec) override @@ -241,6 +236,22 @@ namespace msgpack { } private: + + bool read_done() const + { + return parser_.done(); + } + + void read_next() + { + std::error_code ec; + read_next(ec); + if (JSONCONS_UNLIKELY(ec)) + { + JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column())); + } + } + void read_next(std::error_code& ec) { if (cursor_visitor_.in_available()) diff --git a/include/jsoncons_ext/msgpack/msgpack_options.hpp b/include/jsoncons_ext/msgpack/msgpack_options.hpp index ec226a8..dfa408c 100644 --- a/include/jsoncons_ext/msgpack/msgpack_options.hpp +++ b/include/jsoncons_ext/msgpack/msgpack_options.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -18,19 +18,14 @@ class msgpack_options_common { friend class msgpack_options; - int max_nesting_depth_; + int max_nesting_depth_{1024}; protected: - virtual ~msgpack_options_common() = default; + msgpack_options_common() = default; + msgpack_options_common(const msgpack_options_common&) = default; - msgpack_options_common() - : max_nesting_depth_(1024) - { - } + virtual ~msgpack_options_common() = default; - msgpack_options_common(const msgpack_options_common&) = default; msgpack_options_common& operator=(const msgpack_options_common&) = default; - msgpack_options_common(msgpack_options_common&&) = default; - msgpack_options_common& operator=(msgpack_options_common&&) = default; public: int max_nesting_depth() const { @@ -42,18 +37,20 @@ class msgpack_decode_options : public virtual msgpack_options_common { friend class msgpack_options; public: - msgpack_decode_options() - { - } + msgpack_decode_options() = default; + msgpack_decode_options(const msgpack_decode_options& other) = default; +protected: + msgpack_decode_options& operator=(const msgpack_decode_options& other) = default; }; class msgpack_encode_options : public virtual msgpack_options_common { friend class msgpack_options; public: - msgpack_encode_options() - { - } + msgpack_encode_options() = default; + msgpack_encode_options(const msgpack_encode_options& other) = default; +protected: + msgpack_encode_options& operator=(const msgpack_encode_options& other) = default; }; class msgpack_options final : public msgpack_decode_options, public msgpack_encode_options @@ -61,6 +58,10 @@ class msgpack_options final : public msgpack_decode_options, public msgpack_enco public: using msgpack_options_common::max_nesting_depth; + msgpack_options() = default; + msgpack_options(const msgpack_options& other) = default; + msgpack_options& operator=(const msgpack_options& other) = default; + msgpack_options& max_nesting_depth(int value) { this->max_nesting_depth_ = value; diff --git a/include/jsoncons_ext/msgpack/msgpack_parser.hpp b/include/jsoncons_ext/msgpack/msgpack_parser.hpp index ed69b82..8b272fe 100644 --- a/include/jsoncons_ext/msgpack/msgpack_parser.hpp +++ b/include/jsoncons_ext/msgpack/msgpack_parser.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include #include @@ -73,7 +73,7 @@ class basic_msgpack_parser : public ser_context int mark_level_{0}; Source source_; - msgpack_decode_options options_; + int max_nesting_depth_; std::basic_string,char_allocator_type> text_buffer_; std::vector bytes_buffer_; std::vector state_stack_; @@ -84,7 +84,7 @@ class basic_msgpack_parser : public ser_context const msgpack_decode_options& options = msgpack_decode_options(), const Allocator& alloc = Allocator()) : source_(std::forward(source)), - options_(options), + max_nesting_depth_(options.max_nesting_depth()), text_buffer_(alloc), bytes_buffer_(alloc), state_stack_(alloc) @@ -671,7 +671,7 @@ class basic_msgpack_parser : public ser_context void begin_array(item_event_visitor& visitor, uint8_t type, std::error_code& ec) { - if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth())) + if (JSONCONS_UNLIKELY(++nesting_depth_ > max_nesting_depth_)) { ec = msgpack_errc::max_nesting_depth_exceeded; more_ = false; @@ -698,7 +698,7 @@ class basic_msgpack_parser : public ser_context void begin_object(item_event_visitor& visitor, uint8_t type, std::error_code& ec) { - if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth())) + if (JSONCONS_UNLIKELY(++nesting_depth_ > max_nesting_depth_)) { ec = msgpack_errc::max_nesting_depth_exceeded; more_ = false; diff --git a/include/jsoncons_ext/msgpack/msgpack_reader.hpp b/include/jsoncons_ext/msgpack/msgpack_reader.hpp index cf880d5..b122236 100644 --- a/include/jsoncons_ext/msgpack/msgpack_reader.hpp +++ b/include/jsoncons_ext/msgpack/msgpack_reader.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/include/jsoncons_ext/msgpack/msgpack_type.hpp b/include/jsoncons_ext/msgpack/msgpack_type.hpp index aff22d0..5ee1eb9 100644 --- a/include/jsoncons_ext/msgpack/msgpack_type.hpp +++ b/include/jsoncons_ext/msgpack/msgpack_type.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/include/jsoncons_ext/toon/encode_toon.hpp b/include/jsoncons_ext/toon/encode_toon.hpp new file mode 100644 index 0000000..f543589 --- /dev/null +++ b/include/jsoncons_ext/toon/encode_toon.hpp @@ -0,0 +1,1248 @@ +// Copyright 2013-2026 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_TOON_ENCODE_TOON_HPP +#define JSONCONS_TOON_ENCODE_TOON_HPP + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace jsoncons { +namespace toon { + +JSONCONS_INLINE_CONSTEXPR jsoncons::string_view null_literal{"null", 4}; +JSONCONS_INLINE_CONSTEXPR jsoncons::string_view true_literal{"true", 4}; +JSONCONS_INLINE_CONSTEXPR jsoncons::string_view false_literal{"false", 5}; + +namespace detail { + +enum class format_number_state{value_sign,coefficient,fraction,exponent_sign,exponent_value,err}; + +inline +std::string exponential_to_decimal_notation(jsoncons::string_view str) +{ + std::string result; + + std::string num_str; + std::string exponent_str; + + bool neg_value = false; + bool neg_exp = false; + + std::size_t decimal_places = 0; + + format_number_state state = format_number_state::value_sign; + for (std::size_t i = 0; i < str.size();) + { + char c = str[i]; + switch (state) + { + case format_number_state::value_sign: + if (c == '-') + { + neg_value = true; + ++i; + } + state = format_number_state::coefficient; + break; + case format_number_state::coefficient: + if ((c >= '0' && c <= '9') || c == '-') + { + num_str.push_back(c); + ++i; + } + else if (c == 'e' || c == 'E') + { + state = format_number_state::exponent_sign; + ++i; + } + else if (c == '.') + { + state = format_number_state::fraction; + ++i; + } + break; + case format_number_state::fraction: + if ((c >= '0' && c <= '9')) + { + ++decimal_places; + num_str.push_back(c); + ++i; + } + else if (c == 'e' || c == 'E') + { + state = format_number_state::exponent_sign; + ++i; + } + break; + case format_number_state::exponent_sign: + if (c == '-') + { + neg_exp = true; + state = format_number_state::exponent_value; + ++i; + } + else if (c == '+') + { + state = format_number_state::exponent_value; + ++i; + } + else + { + state = format_number_state::exponent_value; + } + break; + case format_number_state::exponent_value: + if ((c >= '0' && c <= '9')) + { + exponent_str.push_back(c); + ++i; + } + break; + case format_number_state::err: + i = str.size(); + break; + } + } + + std::size_t exponent; + dec_to_integer(exponent_str.data(), exponent_str.size(), exponent); + + std::size_t n = num_str.size(); + + if (neg_exp) // shift decimal point left + { + for (std::size_t i = n-decimal_places; i <= exponent; ++i) + { + num_str.insert(num_str.begin(), '0'); + } + std::size_t pos = num_str.size()-(decimal_places+exponent); + auto first_non_zero = num_str.find_first_not_of('0', pos); + if (first_non_zero == std::string::npos) + { + num_str.erase(num_str.begin()+pos, num_str.end()); + } + else + { + num_str.insert(num_str.begin()+(num_str.size()-decimal_places-exponent), '.'); + } + } + else // shift decimal point right + { + for (std::size_t i = decimal_places; i < exponent; ++i) + { + num_str.insert(num_str.begin()+(n-decimal_places), '0'); + } + if (decimal_places > exponent) + { + num_str.insert(num_str.begin() + (num_str.size() - exponent), '.'); + } + } + if (neg_value) + { + num_str.insert(num_str.begin(), '-'); + } + return num_str; +} + +inline +bool is_unquoted_key_valid(string_view key) +{ + if (key.empty()) + { + return false; + } + char c = key.front(); + if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_')) + { + return false; + } + for (auto it = key.begin()+1; it != key.end(); ++it) + { + c = *it; + if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || (c >= '0' && c <= '9') || c == '.')) + { + return false; + } + } + return true; +} + +enum class is_number_state{initial,negative,digits_or_dot_or_exp,octal,leading_zero, + leading_decimal_zero, decimal_zero, decimal_digit, exponent, digits_or_exp, digits, not_number}; + +inline +bool is_number(jsoncons::string_view str) +{ + is_number_state state = is_number_state::initial; + + for (std::size_t i = 0; i < str.size();) + { + char c = str[i]; + switch (state) + { + case is_number_state::initial: + if (c == '-') + { + state = is_number_state::negative; + ++i; + } + else if (c == '0') + { + state = is_number_state::leading_zero; + ++i; + } + else + { + state = is_number_state::digits_or_dot_or_exp; + } + break; + case is_number_state::leading_zero: + if (c == '.') + { + state = is_number_state::decimal_digit; + ++i; + } + else + { + state = is_number_state::octal; + } + break; + case is_number_state::leading_decimal_zero: + if (c == '.') + { + state = is_number_state::decimal_digit; + ++i; + } + else + { + state = is_number_state::not_number; + } + break; + case is_number_state::octal: + if (!(c >= '0' && c <= '7')) + { + state = is_number_state::not_number; + } + else + { + ++i; + } + break; + case is_number_state::negative: + if (c == '0') + { + state = is_number_state::leading_decimal_zero; + ++i; + } + else + { + state = is_number_state::digits_or_dot_or_exp; + } + break; + case is_number_state::decimal_zero: + if (c == '0') + { + state = is_number_state::not_number; + } + else if (c == '.') + { + state = is_number_state::decimal_digit; + ++i; + } + else if (c == 'e' || c == 'E') + { + state = is_number_state::digits_or_dot_or_exp; + ++i; + } + else if (c >= '1' && c <= '9') + { + state = is_number_state::digits_or_dot_or_exp; + ++i; + } + else + { + state = is_number_state::not_number; + } + break; + case is_number_state::decimal_digit: + if (c >= '0' && c <= '9') + { + state = is_number_state::digits_or_exp; + ++i; + } + else + { + state = is_number_state::not_number; + } + break; + case is_number_state::digits_or_dot_or_exp: + if (c == '.') + { + state = is_number_state::decimal_digit; + ++i; + } + else if (c == 'e' || c == 'E') + { + state = is_number_state::exponent; + ++i; + } + else if (!(c >= '0' && c <= '9')) + { + state = is_number_state::not_number; + } + else + { + ++i; + } + break; + case is_number_state::digits_or_exp: + if (c == 'e' || c == 'E') + { + state = is_number_state::exponent; + ++i; + } + else if (!(c >= '0' && c <= '9')) + { + state = is_number_state::not_number; + } + else + { + ++i; + } + break; + case is_number_state::exponent: + if ((c >= '0' && c <= '9') || c == '-') + { + state = is_number_state::digits; + ++i; + } + else + { + state = is_number_state::not_number; + } + break; + case is_number_state::digits: + if (!(c >= '0' && c <= '9')) + { + state = is_number_state::not_number; + } + else + { + ++i; + } + break; + default: + i = str.size(); + break; + } + } + if (state == is_number_state::digits_or_dot_or_exp || state == is_number_state::octal + || state == is_number_state::decimal_zero + || state == is_number_state::digits_or_exp || state == is_number_state::digits + || state == is_number_state::leading_zero || state == is_number_state::leading_decimal_zero) + { + return true; + } + return false; +} + +inline +bool is_unquoted_safe(jsoncons::string_view str, char delimiter = ',') +{ + if (str.empty()) + { + return false; + } + if (std::isspace(static_cast(str.front())) || std::isspace(static_cast(str.back()))) + { + return false; + } + if (is_number(str)) + { + return false; + } + if (str == null_literal || str == true_literal || str == false_literal) + { + return false; + } + if (str.front() == '-') + { + return false; + } + for (auto c : str) + { + switch (c) + { + case ':': + case '[': + case ']': + case '{': + case '}': + case '\"': + case '\\': + case '\n': + case '\r': + case '\t': + return false; + } + if (c == delimiter) + { + return false; + } + } + return true; +} + +template +void encode_string(jsoncons::string_view str, char delimiter, Sink& sink) +{ + if (is_unquoted_safe(str, delimiter)) + { + sink.append(str.data(), str.size()); + } + else + { + sink.push_back('\"'); + jsoncons::detail::escape_string(str.data(), str.size(), false, false, sink); + sink.push_back('\"'); + } +} + +template +void encode_key(jsoncons::string_view key, Sink& sink) +{ + if (is_unquoted_key_valid(key)) + { + sink.append(key.data(), key.size()); + } + else + { + sink.push_back('\"'); + jsoncons::detail::escape_string(key.data(), key.size(), false, false, sink); + sink.push_back('\"'); + } +} + +} // namespace detail + +// encode_toon + +template +bool is_json_primitive(const Json& val) +{ + if (val.is_array()) + { + return false; + } + if (val.is_object()) + { + return false; + } + return true; +} + +template +bool is_json_array(const Json& val) +{ + if (val.is_array()) + { + return true; + } + return false; +} + +template +bool is_json_object(const Json& val) +{ + if (val.is_object()) + { + return true; + } + return false; +} + +template +void write_header(jsoncons::optional key, + std::size_t length, + const jsoncons::span& fields, + char delimiter, + jsoncons::optional length_marker, + Sink& sink) +{ + if (key) + { + detail::encode_key(*key, sink); + } + if (!fields.empty()) + { + sink.push_back('['); + if (length_marker) + { + sink.push_back(*length_marker); + } + auto s = std::to_string(length); + sink.append(s.data(), s.size()); + if (delimiter != ',') + { + sink.push_back(delimiter); + } + sink.push_back(']'); + } + else + { + sink.push_back('['); + if (length_marker) + { + sink.push_back(*length_marker); + } + auto s = std::to_string(length); + sink.append(s.data(), s.size()); + if (delimiter != ',') + { + sink.push_back(delimiter); + } + sink.push_back(']'); + } + + if (!fields.empty()) + { + sink.push_back('{'); + bool first = true; + for (const auto& field : fields) + { + if (!first) + { + sink.push_back(delimiter); + } + else + { + first = false; + } + detail::encode_key(field, sink); // key + } + sink.push_back('}'); + } + sink.push_back(':'); +} + +template +bool is_array_of_arrays(const Json& val) +{ + if (!is_json_array(val)) + { + return false; + } + for (const auto& item : val.array_range()) + { + if (!is_json_array(item)) + { + return false; + } + } + return true; +} + +template +bool is_array_of_objects(const Json& val) +{ + if (!is_json_array(val)) + { + return false; + } + for (const auto& item : val.array_range()) + { + if (!is_json_object(item)) + { + return false; + } + } + return true; +} + +template +bool is_array_of_primitives(const Json& val) +{ + //if (val.empty()) + //{ + // return false; + //} + if (!is_json_array(val)) + { + return false; + } + for (const auto& item : val.array_range()) + { + if (is_json_array(item) || is_json_object(item)) + { + return false; + } + } + return true; +} + +template +std::vector try_get_tabular_header(const Json& val) +{ + if (val.empty()) + { + return std::vector{}; + } + + std::vector first_keys; + std::set first_keys_set; + + for (const auto& item : val[0].object_range()) + { + first_keys.push_back(item.key()); + first_keys_set.insert(item.key()); + } + + for (const auto& row : val.array_range()) + { + std::set keys_set; + for (const auto& item : row.object_range()) + { + if (is_json_array(item.value()) || is_json_object(item.value())) + { + return std::vector{}; + } + keys_set.insert(item.key()); + } + if (keys_set != first_keys_set) + { + return std::vector{}; + } + } + + return first_keys; +} + +template +write_result encode_primitive(const Json& val, char delimiter, Sink& sink) +{ + if (val.is_null()) + { + sink.append(null_literal.data(), null_literal.size()); + } + else if (val.is_bool()) + { + if (val.as_bool()) + { + sink.append(true_literal.data(), true_literal.size()); + } + else + { + sink.append(false_literal.data(), false_literal.size()); + } + } + else if (val.is_number()) + { + auto s = val.as_string(); + bool exponential_notation = false; + for (auto c : s) + { + if (c == 'e' || c == 'E') + { + exponential_notation = true; + break; + } + } + if (exponential_notation) + { + auto dec_str = detail::exponential_to_decimal_notation(s); + sink.append(dec_str.data(), dec_str.size()); + } + else + { + sink.append(s.data(), s.size()); + } + } + else if (val.is_string()) + { + detail::encode_string(val.as_string_view(), delimiter, sink); + } + return write_result{}; +} + +template +void encode_array_of_arrays(const Json& val, const toon_encode_options& options, + Sink& sink, int depth, int& line , jsoncons::optional key) +{ + if (line != 0) + { + sink.push_back('\n'); + } + sink.append(depth*options.indent(), ' '); + write_header(key, val.size(), jsoncons::span{}, + options.delimiter(), options.length_marker(), sink); + ++line; + for (const auto& item : val.array_range()) + { + if (is_array_of_primitives(item)) + { + sink.push_back('\n'); + sink.append((depth+1)*options.indent(), ' '); + sink.push_back('-'); + sink.push_back(' '); + sink.push_back('['); + if (options.length_marker()) + { + sink.push_back(*options.length_marker()); + } + auto s = std::to_string(item.size()); + sink.append(s.data(), s.size()); + if (options.delimiter() != ',') + { + sink.push_back(options.delimiter()); + } + sink.push_back(']'); + sink.push_back(':'); + if (!item.empty()) + { + sink.push_back(' '); + } + bool first2 = true; + for (const auto& item2 : item.array_range()) + { + if (!first2) + { + sink.push_back(options.delimiter()); + } + else + { + first2 = false; + } + encode_primitive(item2, options.delimiter(), sink); + } + } + else + { + encode_array(item, options, sink, depth+1, line, jsoncons::optional{}); + } + ++line; + } +} + +template +void encode_array_content(const Json& val, const toon_encode_options& options, + Sink& sink, int depth, int& line ) +{ + if (is_array_of_primitives(val)) + { + bool first_item = true; + for (const auto& item : val.array_range()) + { + if (!first_item) + { + sink.push_back(options.delimiter()); + } + else + { + sink.push_back(' '); + first_item = false; + } + encode_primitive(item, options.delimiter(), sink); + } + } + else if (is_array_of_arrays(val)) + { + for (const auto& item : val.array_range()) + { + if (is_array_of_primitives(item)) + { + sink.push_back('\n'); + sink.append((depth+1)*options.indent(), ' '); + sink.push_back('-'); + sink.push_back(' '); + sink.push_back('['); + if (options.length_marker()) + { + sink.push_back(*options.length_marker()); + } + auto s = std::to_string(item.size()); + sink.append(s.data(), s.size()); + if (options.delimiter() != ',') + { + sink.push_back(options.delimiter()); + } + sink.push_back(']'); + sink.push_back(':'); + if (!item.empty()) + { + sink.push_back(' '); + } + bool first2 = true; + for (const auto& item2 : item.array_range()) + { + if (!first2) + { + sink.push_back(options.delimiter()); + } + else + { + first2 = false; + } + encode_primitive(item2, options.delimiter(), sink); + } + } + else + { + encode_array(item, options, sink, depth+1, line, jsoncons::optional{}); + } + ++line; + } + } + else if (is_array_of_objects(val)) + { + auto fields = try_get_tabular_header(val); + if (!fields.empty()) + { + for (const auto& row : val.array_range()) + { + sink.push_back('\n'); + sink.append((depth+1)*options.indent(), ' '); + bool first_item = true; + for (auto field : fields) + { + if (!first_item) + { + sink.push_back(options.delimiter()); + } + else + { + first_item = false; + } + encode_primitive(row.at(field), options.delimiter(), sink); + } + ++line; + } + } + else + { + for (const auto& item : val.array_range()) + { + encode_object_as_list_item(item, options, sink, depth+1, line); + ++line; + } + } + } + else + { + for (const auto& item : val.array_range()) + { + if (is_json_object(item)) + { + encode_object_as_list_item(item, options, sink, depth+1, line); + } + else if (is_json_array(item)) + { + encode_array(item, options, sink, depth + 1, line, jsoncons::optional{}); + } + else + { + sink.push_back('\n'); + sink.append((depth+1)*options.indent(), ' '); + sink.push_back('-'); + sink.push_back(' '); + encode_primitive(item, options.delimiter(), sink); + } + ++line; + } + } +} + +template +void encode_array_of_objects_as_tabular(const Json& val, + const jsoncons::span& fields, + const toon_encode_options& options, + Sink& sink, int depth, int& line , jsoncons::optional key) +{ + if (line != 0) + { + sink.push_back('\n'); + } + sink.append(depth*options.indent(), ' '); + write_header(key, val.size(), fields, + options.delimiter(), options.length_marker(), sink); + ++line; + + for (const auto& row : val.array_range()) + { + sink.push_back('\n'); + sink.append((depth+1)*options.indent(), ' '); + bool first_item = true; + for (auto field : fields) + { + if (!first_item) + { + sink.push_back(options.delimiter()); + } + else + { + first_item = false; + } + encode_primitive(row.at(field), options.delimiter(), sink); + } + ++line; + } +} + +template +void encode_object_as_list_item(const Json& val, const toon_encode_options& options, + Sink& sink, int depth, int& line ) +{ + if (val.empty()) + { + if (line != 0) + { + sink.push_back('\n'); + } + sink.append(depth*options.indent(), ' '); + sink.push_back('-'); + return; + } + auto first = val.object_range().begin(); + auto last = val.object_range().end(); + + if (is_json_primitive(first->value())) + { + if (line != 0) + { + sink.push_back('\n'); + } + sink.append(depth*options.indent(), ' '); + sink.push_back('-'); + sink.push_back(' '); + detail::encode_key(first->key(), sink); + sink.push_back(':'); + sink.push_back(' '); + encode_primitive(first->value(), options.delimiter(), sink); + } + else if (is_json_array(first->value())) + { + if (is_array_of_primitives(first->value())) + { + if (line != 0) + { + sink.push_back('\n'); + } + sink.append(depth*options.indent(), ' '); + sink.push_back('-'); + sink.push_back(' '); + write_header(first->key(), first->value().size(), + jsoncons::span{}, + options.delimiter(), options.length_marker(), sink); + encode_array_content(first->value(), options, sink, depth, line); + } + else + { + if (line != 0) + { + sink.push_back('\n'); + } + sink.append(depth*options.indent(), ' '); + sink.push_back('-'); + sink.push_back(' '); + + std::vector fields; + if (is_array_of_objects(first->value())) + { + fields = try_get_tabular_header(first->value()); + } + write_header(first->key(), first->value().size(), + fields, + options.delimiter(), options.length_marker(), sink); + encode_array_content(first->value(), options, sink, depth+1, line); + } + ++line; + } + else // object, this is where problem is + { + if (line != 0) + { + sink.push_back('\n'); + } + sink.append(depth*options.indent(), ' '); + sink.push_back('-'); + encode_key_value_pair(first->key(), first->value(), options, sink, depth + 1, line); + ++line; + } + ++line; + for (auto it = first+1; it != last; ++it) + { + encode_key_value_pair(it->key(), it->value(), options, sink, depth + 1, line); + ++line; + } +} + +template +void encode_mixed_array_as_list_items(const Json& val, const toon_encode_options& options, + Sink& sink, int depth, int& line , jsoncons::optional key) +{ + if (line != 0) + { + sink.push_back('\n'); + } + sink.append(depth*options.indent(), ' '); + write_header(key, val.size(), jsoncons::span{}, + options.delimiter(), options.length_marker(), sink); + ++line; + + for (const auto& item : val.array_range()) + { + if (is_json_primitive(item)) + { + sink.push_back('\n'); + sink.append((depth+1)*options.indent(), ' '); + sink.push_back('-'); + sink.push_back(' '); + encode_primitive(item, options.delimiter(), sink); + } + else if (is_json_object(item)) + { + encode_object_as_list_item(item, options, sink, depth+1, line); + } + else if (is_json_array(item)) + { + if (is_array_of_primitives(item)) + { + if (line != 0) + { + sink.push_back('\n'); + } + sink.append((depth+1)*options.indent(), ' '); + sink.push_back('-'); + sink.push_back(' '); + write_header(jsoncons::optional{}, item.size(), + jsoncons::span{}, + options.delimiter(), options.length_marker(), sink); + encode_array_content(item, options, sink, depth, line); // +2 in toon-pthon + } + else + { + if (line > 0) + { + sink.push_back('\n'); + } + sink.append((depth+1)*options.indent(), ' '); + sink.push_back('-'); + sink.push_back(' '); + + std::vector fields; + if (is_array_of_objects(item)) + { + fields = try_get_tabular_header(item); + } + write_header(jsoncons::optional{}, item.size(), + fields, + options.delimiter(), options.length_marker(), sink); + encode_array_content(item, options, sink, depth+1, line); // +2 in toon-pthon + } + } + ++line; + } +} + +template +void encode_inline_primitive_array(const Json& val, const toon_encode_options& options, + Sink& sink, int depth, int& line , jsoncons::optional key) +{ + if (line != 0) + { + sink.push_back('\n'); + } + sink.append(depth*options.indent(), ' '); + write_header(key, val.size(), jsoncons::span{}, options.delimiter(), options.length_marker(), sink); + ++line; + + bool first_item = true; + for (const auto& item : val.array_range()) + { + if (!first_item) + { + sink.push_back(options.delimiter()); + } + else + { + sink.push_back(' '); + first_item = false; + } + encode_primitive(item, options.delimiter(), sink); + } +} + +template +void encode_array(const Json& val, const toon_encode_options& options, + Sink& sink, int depth, int& line , jsoncons::optional key) +{ + if (val.empty()) + { + if (line != 0) + { + sink.push_back('\n'); + } + sink.append(depth*options.indent(), ' '); + write_header(key, 0, jsoncons::span{}, options.delimiter(), options.length_marker(), sink); + ++line; + return; + } + + if (is_array_of_primitives(val)) + { + encode_inline_primitive_array(val, options, sink, depth, line, key); + } + else if (is_array_of_arrays(val)) + { + encode_array_of_arrays(val, options, sink, depth, line, key); + } + else if (is_array_of_objects(val)) + { + auto fields = try_get_tabular_header(val); + if (!fields.empty()) + { + encode_array_of_objects_as_tabular(val, jsoncons::span(fields.data(), fields.size()), + options, sink, depth, line, key); + } + else + { + encode_mixed_array_as_list_items(val, options, sink, depth, line, key); + } + } + else + { + encode_mixed_array_as_list_items(val, options, sink, depth, line, key); + } +} + +template +void encode_key_value_pair(string_view key, const Json& val, + const toon_encode_options& options, + Sink& sink, int depth, int& line ) +{ + if (is_json_array(val)) + { + encode_array(val, options, sink, depth, line, key); + } + else if (is_json_object(val)) + { + encode_object(val, options, sink, depth, line, key); + } + else + { + if (line != 0) + { + sink.push_back('\n'); + } + sink.append(depth*options.indent(), ' '); + detail::encode_key(key, sink); + sink.push_back(':'); + sink.push_back(' '); + encode_primitive(val, options.delimiter(), sink); + } +} + +template +void encode_object(const Json& val, const toon_encode_options& options, + Sink& sink, int depth, int& line , jsoncons::optional key) +{ + if (key) + { + if (line != 0) + { + sink.push_back('\n'); + } + sink.append(depth*options.indent(), ' '); + detail::encode_key(*key, sink); + sink.push_back(':'); + ++line; + for (const auto& item : val.object_range()) + { + encode_key_value_pair(item.key(), item.value(), options, sink, depth+1, line); + ++line; + } + } + else + { + for (const auto& item : val.object_range()) + { + encode_key_value_pair(item.key(), item.value(), options, sink, depth, line); + ++line; + } + } +} + +template +void encode_value(const Json& val, const toon_encode_options& options, Sink& sink, int depth) +{ + int line{0}; + if (is_json_array(val)) + { + encode_array(val, options, sink, depth, line, jsoncons::optional{}); + } + else if (is_json_object(val)) + { + encode_object(val, options, sink, depth, line, jsoncons::optional{}); + } + else + { + sink.append(depth*options.indent(), ' '); + encode_primitive(val, options.delimiter(), sink); + } +} + +template +typename std::enable_if::value, write_result>::type +try_encode_toon(const allocator_set&, const T& val, Sink& sink, + const toon_encode_options& options) +{ + encode_value(val, options, sink, 0); + return write_result{}; +} + +template +typename std::enable_if::value,write_result>::type +try_encode_toon(const T& val, CharContainer& cont, + const toon_encode_options& options = toon_encode_options()) +{ + string_sink sink{cont}; + encode_value(val, options, sink, 0); + return write_result{}; +} + +template +write_result try_encode_toon(const T& val, std::basic_ostream& os, + const toon_encode_options& options = toon_encode_options()) +{ + stream_sink sink{os}; + encode_value(val, options, sink); + return write_result{}; +} + +template +void encode_toon(Args&& ... args) +{ + auto result = try_encode_toon(std::forward(args)...); + if (!result) + { + JSONCONS_THROW(ser_error(result.error())); + } +} + +} // namespace toon +} // namespace jsoncons + +#endif // JSONCONS_TOON_ENCODE_TOON_HPP + diff --git a/include/jsoncons_ext/toon/toon.hpp b/include/jsoncons_ext/toon/toon.hpp new file mode 100644 index 0000000..a64046f --- /dev/null +++ b/include/jsoncons_ext/toon/toon.hpp @@ -0,0 +1,13 @@ +// Copyright 2013-2026 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_TOON_TOON_HPP +#define JSONCONS_TOON_TOON_HPP + +#include + +#endif // JSONCONS_TOON_TOON_HPP + diff --git a/include/jsoncons_ext/toon/toon_options.hpp b/include/jsoncons_ext/toon/toon_options.hpp new file mode 100644 index 0000000..54eb547 --- /dev/null +++ b/include/jsoncons_ext/toon/toon_options.hpp @@ -0,0 +1,168 @@ +// Copyright 2013-2026 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_TOON_TOON_OPTIONS_HPP +#define JSONCONS_TOON_TOON_OPTIONS_HPP + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace jsoncons { +namespace toon { + +class toon_options; + +class toon_options_common +{ + friend class toon_options; +public: + using char_type = char; + using string_type = std::string; +private: + + int indent_{2}; + char delimiter_{','}; + jsoncons::optional length_marker_; + int max_nesting_depth_{1024}; + +protected: + toon_options_common() = default; + + virtual ~toon_options_common() = default; + + toon_options_common(const toon_options_common&) = default; + toon_options_common& operator=(const toon_options_common&) = default; + toon_options_common(toon_options_common&&) = default; + //toon_options_common& operator=(toon_options_common&&) = default; + +public: + + int indent() const + { + return indent_; + } + + char delimiter() const + { + return delimiter_; + } + + jsoncons::optional length_marker() const + { + return length_marker_; + } + + int max_nesting_depth() const + { + return max_nesting_depth_; + } +}; + +class toon_decode_options : public virtual toon_options_common +{ + friend class toon_options; + using super_type = toon_options_common; +public: + using typename super_type::char_type; + using typename super_type::string_type; +private: +public: + toon_decode_options() = default; + + toon_decode_options(const toon_decode_options&) = default; + + toon_decode_options(toon_decode_options&& other) noexcept + : super_type(std::move(other)) + { + } +protected: + toon_decode_options& operator=(const toon_decode_options&) = default; + toon_decode_options& operator=(toon_decode_options&&) = default; +public: +}; + +class toon_encode_options : public virtual toon_options_common +{ + friend class toon_options; + using super_type = toon_options_common; +public: + using typename super_type::char_type; + using typename super_type::string_type; +private: +public: + toon_encode_options() = default; + + toon_encode_options(const toon_encode_options&) = default; + + toon_encode_options(toon_encode_options&& other) noexcept + : super_type(std::move(other)) + { + } + + ~toon_encode_options() = default; +protected: + toon_encode_options& operator=(const toon_encode_options&) = default; + toon_encode_options& operator=(toon_encode_options&&) = default; +public: +}; + +class toon_options final: public toon_decode_options, + public toon_encode_options +{ +public: + using char_type = char; + using string_type = std::string; + + using toon_options_common::indent; + using toon_options_common::delimiter; + using toon_options_common::length_marker; + using toon_options_common::max_nesting_depth; +public: + +// Constructors + + toon_options() = default; + toon_options(const toon_options&) = default; + toon_options(toon_options&&) = default; + toon_options& operator=(const toon_options&) = default; + toon_options& operator=(toon_options&&) = default; + + toon_options& indent(int value) + { + this->indent_ = value; + return *this; + } + + toon_options& delimiter(char value) + { + this->delimiter_ = value; + return *this; + } + + toon_options& length_marker(jsoncons::optional value) + { + this->length_marker_ = value; + return *this; + } + + toon_options& max_nesting_depth(int value) + { + this->max_nesting_depth_ = value; + return *this; + } +}; + +} // namespace toon +} // namespace jsoncons + +#endif // JSONCONS_TOON_TOON_OPTIONS_HPP diff --git a/include/jsoncons_ext/ubjson/decode_ubjson.hpp b/include/jsoncons_ext/ubjson/decode_ubjson.hpp index 735fb03..29dafb5 100644 --- a/include/jsoncons_ext/ubjson/decode_ubjson.hpp +++ b/include/jsoncons_ext/ubjson/decode_ubjson.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -11,12 +11,13 @@ #include // std::enable_if #include +#include #include #include -#include #include -#include #include +#include +#include #include #include @@ -25,180 +26,239 @@ namespace jsoncons { namespace ubjson { - template - typename std::enable_if::value && - ext_traits::is_byte_sequence::value,T>::type - decode_ubjson(const Source& v, - const ubjson_decode_options& options = ubjson_decode_options()) - { - jsoncons::json_decoder decoder; - auto adaptor = make_json_visitor_adaptor(decoder); - basic_ubjson_reader reader(v, adaptor, options); - reader.read(); - if (!decoder.is_valid()) - { - JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); - } - return decoder.get_result(); - } - - template - typename std::enable_if::value && - ext_traits::is_byte_sequence::value,T>::type - decode_ubjson(const Source& v, - const ubjson_decode_options& options = ubjson_decode_options()) - { - basic_ubjson_cursor cursor(v, options); - json_decoder> decoder{}; - - std::error_code ec; - T val = decode_traits::decode(cursor, decoder, ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); - } - return val; - } - - template - typename std::enable_if::value,T>::type - decode_ubjson(std::istream& is, - const ubjson_decode_options& options = ubjson_decode_options()) - { - jsoncons::json_decoder decoder; - auto adaptor = make_json_visitor_adaptor(decoder); - ubjson_stream_reader reader(is, adaptor, options); - reader.read(); - if (!decoder.is_valid()) - { - JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); - } - return decoder.get_result(); - } - - template - typename std::enable_if::value,T>::type - decode_ubjson(std::istream& is, - const ubjson_decode_options& options = ubjson_decode_options()) - { - basic_ubjson_cursor cursor(is, options); - json_decoder> decoder{}; - - std::error_code ec; - T val = decode_traits::decode(cursor, decoder, ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); - } - return val; - } - - template - typename std::enable_if::value,T>::type - decode_ubjson(InputIt first, InputIt last, - const ubjson_decode_options& options = ubjson_decode_options()) - { - jsoncons::json_decoder decoder; - auto adaptor = make_json_visitor_adaptor(decoder); - basic_ubjson_reader> reader(binary_iterator_source(first, last), adaptor, options); - reader.read(); - if (!decoder.is_valid()) - { - JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); - } - return decoder.get_result(); - } - - template - typename std::enable_if::value,T>::type - decode_ubjson(InputIt first, InputIt last, - const ubjson_decode_options& options = ubjson_decode_options()) - { - basic_ubjson_cursor> cursor(binary_iterator_source(first, last), options); - json_decoder> decoder{}; - - std::error_code ec; - T val = decode_traits::decode(cursor, decoder, ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); - } - return val; - } - - // With leading allocator_set parameter - - template - typename std::enable_if::value && - ext_traits::is_byte_sequence::value,T>::type - decode_ubjson(const allocator_set& alloc_set, - const Source& v, - const ubjson_decode_options& options = ubjson_decode_options()) - { - json_decoder decoder(alloc_set.get_allocator(), alloc_set.get_temp_allocator()); - auto adaptor = make_json_visitor_adaptor(decoder); - basic_ubjson_reader reader(v, adaptor, options, alloc_set.get_temp_allocator()); - reader.read(); - if (!decoder.is_valid()) - { - JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); - } - return decoder.get_result(); - } - - template - typename std::enable_if::value && - ext_traits::is_byte_sequence::value,T>::type - decode_ubjson(const allocator_set& alloc_set, - const Source& v, - const ubjson_decode_options& options = ubjson_decode_options()) - { - basic_ubjson_cursor cursor(v, options, alloc_set.get_temp_allocator()); - json_decoder,TempAllocator> decoder(alloc_set.get_temp_allocator(), alloc_set.get_temp_allocator()); - - std::error_code ec; - T val = decode_traits::decode(cursor, decoder, ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); - } - return val; - } - - template - typename std::enable_if::value,T>::type - decode_ubjson(const allocator_set& alloc_set, - std::istream& is, - const ubjson_decode_options& options = ubjson_decode_options()) - { - json_decoder decoder(alloc_set.get_allocator(), alloc_set.get_temp_allocator()); - auto adaptor = make_json_visitor_adaptor(decoder); - basic_ubjson_reader reader(is, adaptor, options, alloc_set.get_temp_allocator()); - reader.read(); - if (!decoder.is_valid()) - { - JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column())); - } - return decoder.get_result(); - } - - template - typename std::enable_if::value,T>::type - decode_ubjson(const allocator_set& alloc_set, - std::istream& is, - const ubjson_decode_options& options = ubjson_decode_options()) - { - basic_ubjson_cursor cursor(is, options, alloc_set.get_temp_allocator()); - json_decoder,TempAllocator> decoder(alloc_set.get_temp_allocator(), alloc_set.get_temp_allocator()); - - std::error_code ec; - T val = decode_traits::decode(cursor, decoder, ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column())); - } - return val; +template +typename std::enable_if::value && + ext_traits::is_byte_sequence::value,read_result>::type +try_decode_ubjson(const BytesLike& v, + const ubjson_decode_options& options = ubjson_decode_options()) +{ + using value_type = T; + using result_type = read_result; + + std::error_code ec; + jsoncons::json_decoder decoder; + auto adaptor = make_json_visitor_adaptor(decoder); + basic_ubjson_reader reader(v, adaptor, options); + reader.read(ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, reader.line(), reader.column()}; + } + if (JSONCONS_UNLIKELY(!decoder.is_valid())) + { + return result_type{jsoncons::unexpect, conv_errc::conversion_failed, reader.line(), reader.column()}; + } + return result_type{decoder.get_result()}; +} + +template +typename std::enable_if::value && + ext_traits::is_byte_sequence::value,read_result>::type +try_decode_ubjson(const BytesLike& v, + const ubjson_decode_options& options = ubjson_decode_options()) +{ + using value_type = T; + using result_type = read_result; + + std::error_code ec; + basic_ubjson_cursor cursor(v, options, ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, cursor.line(), cursor.column()}; + } + + return reflect::decode_traits::try_decode(make_alloc_set(), cursor); +} + +template +typename std::enable_if::value,read_result>::type +try_decode_ubjson(std::istream& is, + const ubjson_decode_options& options = ubjson_decode_options()) +{ + using value_type = T; + using result_type = read_result; + + std::error_code ec; + jsoncons::json_decoder decoder; + auto adaptor = make_json_visitor_adaptor(decoder); + ubjson_stream_reader reader(is, adaptor, options); + reader.read(ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, reader.line(), reader.column()}; + } + if (JSONCONS_UNLIKELY(!decoder.is_valid())) + { + return result_type{jsoncons::unexpect, conv_errc::conversion_failed, reader.line(), reader.column()}; + } + return result_type{decoder.get_result()}; +} + +template +typename std::enable_if::value,read_result>::type +try_decode_ubjson(std::istream& is, + const ubjson_decode_options& options = ubjson_decode_options()) +{ + using value_type = T; + using result_type = read_result; + + std::error_code ec; + basic_ubjson_cursor cursor(is, options, ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, cursor.line(), cursor.column()}; + } + + return reflect::decode_traits::try_decode(make_alloc_set(), cursor); +} + +template +typename std::enable_if::value,read_result>::type +try_decode_ubjson(InputIt first, InputIt last, + const ubjson_decode_options& options = ubjson_decode_options()) +{ + using value_type = T; + using result_type = read_result; + + std::error_code ec; + jsoncons::json_decoder decoder; + auto adaptor = make_json_visitor_adaptor(decoder); + basic_ubjson_reader> reader(binary_iterator_source(first, last), adaptor, options); + reader.read(ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, reader.line(), reader.column()}; + } + if (JSONCONS_UNLIKELY(!decoder.is_valid())) + { + return result_type{jsoncons::unexpect, conv_errc::conversion_failed, reader.line(), reader.column()}; + } + return result_type{decoder.get_result()}; +} + +template +typename std::enable_if::value,read_result>::type +try_decode_ubjson(InputIt first, InputIt last, + const ubjson_decode_options& options = ubjson_decode_options()) +{ + using value_type = T; + using result_type = read_result; + + std::error_code ec; + basic_ubjson_cursor> cursor(binary_iterator_source(first, last), options, ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, cursor.line(), cursor.column()}; + } + + return reflect::decode_traits::try_decode(make_alloc_set(), cursor); +} + +// With leading allocator_set parameter + +template +typename std::enable_if::value && + ext_traits::is_byte_sequence::value,read_result>::type +try_decode_ubjson(const allocator_set& aset, + const BytesLike& v, + const ubjson_decode_options& options = ubjson_decode_options()) +{ + using value_type = T; + using result_type = read_result; + + std::error_code ec; + json_decoder decoder(aset.get_allocator(), aset.get_temp_allocator()); + auto adaptor = make_json_visitor_adaptor(decoder); + basic_ubjson_reader reader(v, adaptor, options, aset.get_temp_allocator()); + reader.read(ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, reader.line(), reader.column()}; + } + if (JSONCONS_UNLIKELY(!decoder.is_valid())) + { + return result_type{jsoncons::unexpect, conv_errc::conversion_failed, reader.line(), reader.column()}; + } + return result_type{decoder.get_result()}; +} + +template +typename std::enable_if::value && + ext_traits::is_byte_sequence::value,read_result>::type +try_decode_ubjson(const allocator_set& aset, + const BytesLike& v, + const ubjson_decode_options& options = ubjson_decode_options()) +{ + using value_type = T; + using result_type = read_result; + + std::error_code ec; + basic_ubjson_cursor cursor(std::allocator_arg, aset.get_temp_allocator(), v, options, ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, cursor.line(), cursor.column()}; + } + + return reflect::decode_traits::try_decode(aset, cursor); +} + +template +typename std::enable_if::value,read_result>::type +try_decode_ubjson(const allocator_set& aset, + std::istream& is, + const ubjson_decode_options& options = ubjson_decode_options()) +{ + using value_type = T; + using result_type = read_result; + using byte_allocator_type = typename std::allocator_traits:: template rebind_alloc; + using stream_source_type = stream_source; + + std::error_code ec; + json_decoder decoder(aset.get_allocator(), aset.get_temp_allocator()); + auto adaptor = make_json_visitor_adaptor(decoder); + basic_ubjson_reader reader(stream_source_type(is,aset.get_temp_allocator()), + adaptor, options, aset.get_temp_allocator()); + reader.read(ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, reader.line(), reader.column()}; + } + if (JSONCONS_UNLIKELY(!decoder.is_valid())) + { + return result_type{jsoncons::unexpect, conv_errc::conversion_failed, reader.line(), reader.column()}; + } + return result_type{decoder.get_result()}; +} + +template +typename std::enable_if::value,read_result>::type +try_decode_ubjson(const allocator_set& aset, + std::istream& is, + const ubjson_decode_options& options = ubjson_decode_options()) +{ + using value_type = T; + using result_type = read_result; + + std::error_code ec; + basic_ubjson_cursor cursor(std::allocator_arg, aset.get_temp_allocator(), is, options, ec); + if (JSONCONS_UNLIKELY(ec)) + { + return result_type{jsoncons::unexpect, ec, cursor.line(), cursor.column()}; + } + + return reflect::decode_traits::try_decode(aset, cursor); +} + +template +T decode_ubjson(Args&& ... args) +{ + auto result = try_decode_ubjson(std::forward(args)...); + if (!result) + { + JSONCONS_THROW(ser_error(result.error().code(), result.error().line(), result.error().column())); } + return std::move(*result); +} } // namespace ubjson } // namespace jsoncons diff --git a/include/jsoncons_ext/ubjson/encode_ubjson.hpp b/include/jsoncons_ext/ubjson/encode_ubjson.hpp index 32ad5ab..53641b3 100644 --- a/include/jsoncons_ext/ubjson/encode_ubjson.hpp +++ b/include/jsoncons_ext/ubjson/encode_ubjson.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -10,132 +10,122 @@ #include // std::basic_ostream #include // std::enable_if -#include -#include #include -#include +#include +#include #include +#include #include namespace jsoncons { namespace ubjson { - template - typename std::enable_if::value && - ext_traits::is_back_insertable_byte_container::value,void>::type - encode_ubjson(const T& j, - ByteContainer& cont, - const ubjson_encode_options& options = ubjson_encode_options()) - { - using char_type = typename T::char_type; - basic_ubjson_encoder> encoder(cont, options); - auto adaptor = make_json_visitor_adaptor>(encoder); - j.dump(adaptor); - } - - template - typename std::enable_if::value && - ext_traits::is_back_insertable_byte_container::value,void>::type - encode_ubjson(const T& val, - ByteContainer& cont, - const ubjson_encode_options& options = ubjson_encode_options()) - { - basic_ubjson_encoder> encoder(cont, options); - std::error_code ec; - encode_traits::encode(val, encoder, json(), ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec)); - } - } - - template - typename std::enable_if::value,void>::type - encode_ubjson(const T& j, - std::ostream& os, - const ubjson_encode_options& options = ubjson_encode_options()) - { - using char_type = typename T::char_type; - ubjson_stream_encoder encoder(os, options); - auto adaptor = make_json_visitor_adaptor>(encoder); - j.dump(adaptor); - } - - template - typename std::enable_if::value,void>::type - encode_ubjson(const T& val, - std::ostream& os, - const ubjson_encode_options& options = ubjson_encode_options()) - { - ubjson_stream_encoder encoder(os, options); - std::error_code ec; - encode_traits::encode(val, encoder, json(), ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec)); - } - } - - // with temp_allocator_arg_t - - template - typename std::enable_if::value && - ext_traits::is_back_insertable_byte_container::value,void>::type - encode_ubjson(const allocator_set& alloc_set,const T& j, - ByteContainer& cont, - const ubjson_encode_options& options = ubjson_encode_options()) - { - using char_type = typename T::char_type; - basic_ubjson_encoder,TempAllocator> encoder(cont, options, alloc_set.get_temp_allocator()); - auto adaptor = make_json_visitor_adaptor>(encoder); - j.dump(adaptor); - } - - template - typename std::enable_if::value && - ext_traits::is_back_insertable_byte_container::value,void>::type - encode_ubjson(const allocator_set& alloc_set,const T& val, - ByteContainer& cont, - const ubjson_encode_options& options = ubjson_encode_options()) - { - basic_ubjson_encoder,TempAllocator> encoder(cont, options, alloc_set.get_temp_allocator()); - std::error_code ec; - encode_traits::encode(val, encoder, json(), ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec)); - } - } - - template - typename std::enable_if::value,void>::type - encode_ubjson(const allocator_set& alloc_set, - const T& j, - std::ostream& os, - const ubjson_encode_options& options = ubjson_encode_options()) - { - using char_type = typename T::char_type; - basic_ubjson_encoder encoder(os, options, alloc_set.get_temp_allocator()); - auto adaptor = make_json_visitor_adaptor>(encoder); - j.dump(adaptor); - } - - template - typename std::enable_if::value,void>::type - encode_ubjson(const allocator_set& alloc_set, - const T& val, - std::ostream& os, - const ubjson_encode_options& options = ubjson_encode_options()) +template +typename std::enable_if::value && + ext_traits::is_back_insertable_byte_container::value,write_result>::type +try_encode_ubjson(const T& j, + ByteContainer& cont, + const ubjson_encode_options& options = ubjson_encode_options()) +{ + using char_type = typename T::char_type; + basic_ubjson_encoder> encoder(cont, options); + auto adaptor = make_json_visitor_adaptor>(encoder); + return j.try_dump(adaptor); +} + +template +typename std::enable_if::value && + ext_traits::is_back_insertable_byte_container::value,write_result>::type +try_encode_ubjson(const T& val, + ByteContainer& cont, + const ubjson_encode_options& options = ubjson_encode_options()) +{ + basic_ubjson_encoder> encoder(cont, options); + return reflect::encode_traits::try_encode(make_alloc_set(), val, encoder); +} + +template +typename std::enable_if::value,write_result>::type +try_encode_ubjson(const T& j, + std::ostream& os, + const ubjson_encode_options& options = ubjson_encode_options()) +{ + using char_type = typename T::char_type; + ubjson_stream_encoder encoder(os, options); + auto adaptor = make_json_visitor_adaptor>(encoder); + return j.try_dump(adaptor); +} + +template +typename std::enable_if::value,write_result>::type +try_encode_ubjson(const T& val, + std::ostream& os, + const ubjson_encode_options& options = ubjson_encode_options()) +{ + ubjson_stream_encoder encoder(os, options); + return reflect::encode_traits::try_encode(make_alloc_set(), val, encoder); +} + +// with temp_allocator_arg_t + +template +typename std::enable_if::value && + ext_traits::is_back_insertable_byte_container::value,write_result>::type +try_encode_ubjson(const allocator_set& aset,const T& j, + ByteContainer& cont, + const ubjson_encode_options& options = ubjson_encode_options()) +{ + using char_type = typename T::char_type; + basic_ubjson_encoder,TempAlloc> encoder(cont, options, aset.get_temp_allocator()); + auto adaptor = make_json_visitor_adaptor>(encoder); + return j.try_dump(adaptor); +} + +template +typename std::enable_if::value && + ext_traits::is_back_insertable_byte_container::value,write_result>::type +try_encode_ubjson(const allocator_set& aset,const T& val, + ByteContainer& cont, + const ubjson_encode_options& options = ubjson_encode_options()) +{ + basic_ubjson_encoder,TempAlloc> encoder(cont, options, aset.get_temp_allocator()); + return reflect::encode_traits::try_encode(aset, val, encoder); +} + +template +typename std::enable_if::value,write_result>::type +try_encode_ubjson(const allocator_set& aset, + const T& j, + std::ostream& os, + const ubjson_encode_options& options = ubjson_encode_options()) +{ + using char_type = typename T::char_type; + basic_ubjson_encoder encoder(os, options, aset.get_temp_allocator()); + auto adaptor = make_json_visitor_adaptor>(encoder); + return j.try_dump(adaptor); +} + +template +typename std::enable_if::value,write_result>::type +try_encode_ubjson(const allocator_set& aset, + const T& val, + std::ostream& os, + const ubjson_encode_options& options = ubjson_encode_options()) +{ + basic_ubjson_encoder encoder(os, options, aset.get_temp_allocator()); + return reflect::encode_traits::try_encode(aset, val, encoder); +} + +template +void encode_ubjson(Args&& ... args) +{ + auto r = try_encode_ubjson(std::forward(args)...); + if (!r) { - basic_ubjson_encoder encoder(os, options, alloc_set.get_temp_allocator()); - std::error_code ec; - encode_traits::encode(val, encoder, json(), ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec)); - } + JSONCONS_THROW(ser_error(r.error())); } +} } // namespace ubjson } // namespace jsoncons diff --git a/include/jsoncons_ext/ubjson/ubjson.hpp b/include/jsoncons_ext/ubjson/ubjson.hpp index 9f40e75..17090f6 100644 --- a/include/jsoncons_ext/ubjson/ubjson.hpp +++ b/include/jsoncons_ext/ubjson/ubjson.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/include/jsoncons_ext/ubjson/ubjson_cursor.hpp b/include/jsoncons_ext/ubjson/ubjson_cursor.hpp index ec6e995..10345f0 100644 --- a/include/jsoncons_ext/ubjson/ubjson_cursor.hpp +++ b/include/jsoncons_ext/ubjson/ubjson_cursor.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include #include #include @@ -52,9 +52,9 @@ class basic_ubjson_cursor : public basic_staj_cursor, private virtual ser_ : parser_(std::forward(source), options, alloc) { parser_.cursor_mode(true); - if (!done()) + if (!read_done()) { - next(); + read_next(); } } @@ -89,9 +89,9 @@ class basic_ubjson_cursor : public basic_staj_cursor, private virtual ser_ eof_(false) { parser_.cursor_mode(true); - if (!done()) + if (!read_done()) { - next(ec); + read_next(ec); } } @@ -105,9 +105,9 @@ class basic_ubjson_cursor : public basic_staj_cursor, private virtual ser_ parser_.reset(); cursor_visitor_.reset(); eof_ = false; - if (!done()) + if (!read_done()) { - next(); + read_next(); } } @@ -117,9 +117,9 @@ class basic_ubjson_cursor : public basic_staj_cursor, private virtual ser_ parser_.reset(std::forward(source)); cursor_visitor_.reset(); eof_ = false; - if (!done()) + if (!read_done()) { - next(); + read_next(); } } @@ -128,9 +128,9 @@ class basic_ubjson_cursor : public basic_staj_cursor, private virtual ser_ parser_.reset(); cursor_visitor_.reset(); eof_ = false; - if (!done()) + if (!read_done()) { - next(ec); + read_next(ec); } } @@ -140,9 +140,9 @@ class basic_ubjson_cursor : public basic_staj_cursor, private virtual ser_ parser_.reset(std::forward(source)); cursor_visitor_.reset(); eof_ = false; - if (!done()) + if (!read_done()) { - next(ec); + read_next(ec); } } @@ -198,12 +198,7 @@ class basic_ubjson_cursor : public basic_staj_cursor, private virtual ser_ void next() override { - std::error_code ec; - next(ec); - if (JSONCONS_UNLIKELY(ec)) - { - JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column())); - } + read_next(); } void next(std::error_code& ec) override @@ -239,6 +234,22 @@ class basic_ubjson_cursor : public basic_staj_cursor, private virtual ser_ } private: + + bool read_done() const + { + return parser_.done(); + } + + void read_next() + { + std::error_code ec; + read_next(ec); + if (JSONCONS_UNLIKELY(ec)) + { + JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column())); + } + } + void read_next(std::error_code& ec) { parser_.restart(); diff --git a/include/jsoncons_ext/ubjson/ubjson_encoder.hpp b/include/jsoncons_ext/ubjson/ubjson_encoder.hpp index c7fa55b..5d20787 100644 --- a/include/jsoncons_ext/ubjson/ubjson_encoder.hpp +++ b/include/jsoncons_ext/ubjson/ubjson_encoder.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -17,12 +17,12 @@ #include #include -#include +#include #include #include #include #include -#include +#include #include #include #include @@ -83,7 +83,7 @@ class basic_ubjson_encoder final : public basic_json_visitor }; Sink sink_; - const ubjson_encode_options options_; + int max_nesting_depth_; allocator_type alloc_; std::vector stack_; @@ -104,7 +104,7 @@ class basic_ubjson_encoder final : public basic_json_visitor const ubjson_encode_options& options, const Allocator& alloc = Allocator()) : sink_(std::forward(sink)), - options_(options), + max_nesting_depth_(options.max_nesting_depth()), alloc_(alloc) { } @@ -145,7 +145,7 @@ class basic_ubjson_encoder final : public basic_json_visitor JSONCONS_VISITOR_RETURN_TYPE visit_begin_object(semantic_tag, const ser_context&, std::error_code& ec) override { - if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth())) + if (JSONCONS_UNLIKELY(++nesting_depth_ > max_nesting_depth_)) { ec = ubjson_errc::max_nesting_depth_exceeded; JSONCONS_VISITOR_RETURN; @@ -158,7 +158,7 @@ class basic_ubjson_encoder final : public basic_json_visitor JSONCONS_VISITOR_RETURN_TYPE visit_begin_object(std::size_t length, semantic_tag, const ser_context&, std::error_code& ec) override { - if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth())) + if (JSONCONS_UNLIKELY(++nesting_depth_ > max_nesting_depth_)) { ec = ubjson_errc::max_nesting_depth_exceeded; JSONCONS_VISITOR_RETURN; @@ -200,7 +200,7 @@ class basic_ubjson_encoder final : public basic_json_visitor JSONCONS_VISITOR_RETURN_TYPE visit_begin_array(semantic_tag, const ser_context&, std::error_code& ec) override { - if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth())) + if (JSONCONS_UNLIKELY(++nesting_depth_ > max_nesting_depth_)) { ec = ubjson_errc::max_nesting_depth_exceeded; JSONCONS_VISITOR_RETURN; @@ -213,7 +213,7 @@ class basic_ubjson_encoder final : public basic_json_visitor JSONCONS_VISITOR_RETURN_TYPE visit_begin_array(std::size_t length, semantic_tag, const ser_context&, std::error_code& ec) override { - if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth())) + if (JSONCONS_UNLIKELY(++nesting_depth_ > max_nesting_depth_)) { ec = ubjson_errc::max_nesting_depth_exceeded; JSONCONS_VISITOR_RETURN; diff --git a/include/jsoncons_ext/ubjson/ubjson_error.hpp b/include/jsoncons_ext/ubjson/ubjson_error.hpp index 851eeaf..63a6b41 100644 --- a/include/jsoncons_ext/ubjson/ubjson_error.hpp +++ b/include/jsoncons_ext/ubjson/ubjson_error.hpp @@ -1,4 +1,4 @@ -/// Copyright 2013-2025 Daniel Parker +/// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/include/jsoncons_ext/ubjson/ubjson_options.hpp b/include/jsoncons_ext/ubjson/ubjson_options.hpp index cef5cab..e302f0d 100644 --- a/include/jsoncons_ext/ubjson/ubjson_options.hpp +++ b/include/jsoncons_ext/ubjson/ubjson_options.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -18,19 +18,11 @@ class ubjson_options_common { friend class ubjson_options; - int max_nesting_depth_; + int max_nesting_depth_{1024}; protected: - virtual ~ubjson_options_common() = default; - - ubjson_options_common() - : max_nesting_depth_(1024) - { - } - + ubjson_options_common() = default; ubjson_options_common(const ubjson_options_common&) = default; - ubjson_options_common& operator=(const ubjson_options_common&) = default; - ubjson_options_common(ubjson_options_common&&) = default; - ubjson_options_common& operator=(ubjson_options_common&&) = default; + virtual ~ubjson_options_common() = default; public: int max_nesting_depth() const { @@ -43,12 +35,11 @@ class ubjson_decode_options : public virtual ubjson_options_common friend class ubjson_options; std::size_t max_items_{1 << 24}; public: - ubjson_decode_options() - { - } - - ~ubjson_decode_options() = default; - + ubjson_decode_options() = default; + ubjson_decode_options(const ubjson_decode_options& other) = default; +protected: + ubjson_decode_options& operator=(const ubjson_decode_options& other) = default; +public: std::size_t max_items() const { return max_items_; @@ -59,15 +50,22 @@ class ubjson_encode_options : public virtual ubjson_options_common { friend class ubjson_options; public: - ubjson_encode_options() - { - } + ubjson_encode_options() = default; + ubjson_encode_options(const ubjson_encode_options& other) = default; +protected: + ubjson_encode_options& operator=(const ubjson_encode_options& other) = default; }; class ubjson_options final : public ubjson_decode_options, public ubjson_encode_options { public: using ubjson_options_common::max_nesting_depth; + using ubjson_decode_options::max_items; + + ubjson_options() = default; + ubjson_options(const ubjson_options& other) = default; + + ubjson_options& operator=(const ubjson_options& other) = default; ubjson_options& max_nesting_depth(int value) { diff --git a/include/jsoncons_ext/ubjson/ubjson_parser.hpp b/include/jsoncons_ext/ubjson/ubjson_parser.hpp index 8c00cb0..48252ec 100644 --- a/include/jsoncons_ext/ubjson/ubjson_parser.hpp +++ b/include/jsoncons_ext/ubjson/ubjson_parser.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -16,11 +16,11 @@ #include // std::move #include -#include +#include #include #include #include -#include +#include #include #include #include @@ -67,7 +67,8 @@ class basic_ubjson_parser : public ser_context int mark_level_{0}; Source source_; - ubjson_decode_options options_; + int max_nesting_depth_; + std::size_t max_items_; std::basic_string,char_allocator_type> text_buffer_; std::vector state_stack_; public: @@ -76,7 +77,8 @@ class basic_ubjson_parser : public ser_context const ubjson_decode_options& options = ubjson_decode_options(), const Allocator& alloc = Allocator()) : source_(std::forward(source)), - options_(options), + max_nesting_depth_(options.max_nesting_depth()), + max_items_(options.max_items()), text_buffer_(alloc), state_stack_(alloc) { @@ -205,7 +207,7 @@ class basic_ubjson_parser : public ser_context } else { - if (++state_stack_.back().index > options_.max_items()) + if (++state_stack_.back().index > max_items_) { ec = ubjson_errc::max_items_exceeded; more_ = false; @@ -295,7 +297,7 @@ class basic_ubjson_parser : public ser_context } else { - if (++state_stack_.back().index > options_.max_items()) + if (++state_stack_.back().index > max_items_) { ec = ubjson_errc::max_items_exceeded; more_ = false; @@ -544,7 +546,7 @@ class basic_ubjson_parser : public ser_context more_ = false; return; } - if (jsoncons::detail::is_base10(text_buffer_.data(),text_buffer_.length())) + if (jsoncons::is_base10(text_buffer_.data(),text_buffer_.length())) { visitor.string_value(jsoncons::basic_string_view(text_buffer_.data(),text_buffer_.length()), semantic_tag::bigint, *this, ec); more_ = !cursor_mode_; @@ -580,7 +582,7 @@ class basic_ubjson_parser : public ser_context void begin_array(json_visitor& visitor, std::error_code& ec) { - if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth())) + if (JSONCONS_UNLIKELY(++nesting_depth_ > max_nesting_depth_)) { ec = ubjson_errc::max_nesting_depth_exceeded; more_ = false; @@ -619,7 +621,7 @@ class basic_ubjson_parser : public ser_context { return; } - if (length > options_.max_items()) + if (length > max_items_) { ec = ubjson_errc::max_items_exceeded; more_ = false; @@ -644,7 +646,7 @@ class basic_ubjson_parser : public ser_context { return; } - if (length > options_.max_items()) + if (length > max_items_) { ec = ubjson_errc::max_items_exceeded; more_ = false; @@ -677,7 +679,7 @@ class basic_ubjson_parser : public ser_context void begin_object(json_visitor& visitor, std::error_code& ec) { - if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth())) + if (JSONCONS_UNLIKELY(++nesting_depth_ > max_nesting_depth_)) { ec = ubjson_errc::max_nesting_depth_exceeded; more_ = false; @@ -716,7 +718,7 @@ class basic_ubjson_parser : public ser_context { return; } - if (length > options_.max_items()) + if (length > max_items_) { ec = ubjson_errc::max_items_exceeded; more_ = false; @@ -750,7 +752,7 @@ class basic_ubjson_parser : public ser_context { return; } - if (length > options_.max_items()) + if (length > max_items_) { ec = ubjson_errc::max_items_exceeded; more_ = false; diff --git a/include/jsoncons_ext/ubjson/ubjson_reader.hpp b/include/jsoncons_ext/ubjson/ubjson_reader.hpp index e89adab..2c5500f 100644 --- a/include/jsoncons_ext/ubjson/ubjson_reader.hpp +++ b/include/jsoncons_ext/ubjson/ubjson_reader.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/include/jsoncons_ext/ubjson/ubjson_type.hpp b/include/jsoncons_ext/ubjson/ubjson_type.hpp index d3654a3..d759fe2 100644 --- a/include/jsoncons_ext/ubjson/ubjson_type.hpp +++ b/include/jsoncons_ext/ubjson/ubjson_type.hpp @@ -1,4 +1,4 @@ -// Copyright 2013-2025 Daniel Parker +// Copyright 2013-2026 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)