aboutsummaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
authorRichard <q@1bpm.net>2022-09-04 00:32:56 +0100
committerRichard <q@1bpm.net>2022-09-04 00:32:56 +0100
commit1d055261b4144dbf86b2658437015b15d4dd9bff (patch)
tree6049b19d1bf953a650383de1a5e438b8b82679f6 /include
downloadcsound-json-1d055261b4144dbf86b2658437015b15d4dd9bff.tar.gz
csound-json-1d055261b4144dbf86b2658437015b15d4dd9bff.tar.bz2
csound-json-1d055261b4144dbf86b2658437015b15d4dd9bff.zip
initial
Diffstat (limited to 'include')
-rw-r--r--include/handling.h53
-rw-r--r--include/jsoncons/LICENSE28
-rw-r--r--include/jsoncons/allocator_holder.hpp38
-rw-r--r--include/jsoncons/basic_json.hpp5839
-rw-r--r--include/jsoncons/bigint.hpp1611
-rw-r--r--include/jsoncons/byte_string.hpp820
-rw-r--r--include/jsoncons/config/binary_config.hpp226
-rw-r--r--include/jsoncons/config/compiler_support.hpp389
-rw-r--r--include/jsoncons/config/jsoncons_config.hpp308
-rw-r--r--include/jsoncons/config/version.hpp40
-rw-r--r--include/jsoncons/conv_error.hpp218
-rw-r--r--include/jsoncons/converter.hpp296
-rw-r--r--include/jsoncons/decode_json.hpp209
-rw-r--r--include/jsoncons/decode_traits.hpp651
-rw-r--r--include/jsoncons/detail/endian.hpp44
-rw-r--r--include/jsoncons/detail/grisu3.hpp312
-rw-r--r--include/jsoncons/detail/optional.hpp483
-rw-r--r--include/jsoncons/detail/parse_number.hpp1133
-rw-r--r--include/jsoncons/detail/span.hpp188
-rw-r--r--include/jsoncons/detail/string_view.hpp537
-rw-r--r--include/jsoncons/detail/string_wrapper.hpp370
-rw-r--r--include/jsoncons/detail/write_number.hpp567
-rw-r--r--include/jsoncons/encode_json.hpp315
-rw-r--r--include/jsoncons/encode_traits.hpp378
-rw-r--r--include/jsoncons/json.hpp18
-rw-r--r--include/jsoncons/json_array.hpp324
-rw-r--r--include/jsoncons/json_content_handler.hpp12
-rw-r--r--include/jsoncons/json_cursor.hpp448
-rw-r--r--include/jsoncons/json_decoder.hpp420
-rw-r--r--include/jsoncons/json_encoder.hpp1587
-rw-r--r--include/jsoncons/json_error.hpp156
-rw-r--r--include/jsoncons/json_exception.hpp241
-rw-r--r--include/jsoncons/json_filter.hpp653
-rw-r--r--include/jsoncons/json_fwd.hpp23
-rw-r--r--include/jsoncons/json_object.hpp1772
-rw-r--r--include/jsoncons/json_options.hpp862
-rw-r--r--include/jsoncons/json_parser.hpp2871
-rw-r--r--include/jsoncons/json_reader.hpp731
-rw-r--r--include/jsoncons/json_traits_macros.hpp1072
-rw-r--r--include/jsoncons/json_traits_macros_deprecated.hpp144
-rw-r--r--include/jsoncons/json_type.hpp206
-rw-r--r--include/jsoncons/json_type_traits.hpp1829
-rw-r--r--include/jsoncons/json_visitor.hpp1560
-rw-r--r--include/jsoncons/json_visitor2.hpp2079
-rw-r--r--include/jsoncons/more_type_traits.hpp874
-rw-r--r--include/jsoncons/pretty_print.hpp89
-rw-r--r--include/jsoncons/ser_context.hpp57
-rw-r--r--include/jsoncons/sink.hpp289
-rw-r--r--include/jsoncons/source.hpp777
-rw-r--r--include/jsoncons/source_adaptor.hpp148
-rw-r--r--include/jsoncons/staj2_cursor.hpp1178
-rw-r--r--include/jsoncons/staj_cursor.hpp1233
-rw-r--r--include/jsoncons/staj_iterator.hpp449
-rw-r--r--include/jsoncons/tag_type.hpp245
-rw-r--r--include/jsoncons/text_source_adaptor.hpp144
-rw-r--r--include/jsoncons/typed_array_view.hpp250
-rw-r--r--include/jsoncons/unicode_traits.hpp1330
-rw-r--r--include/jsoncons/uri.hpp635
-rw-r--r--include/jsoncons_ext/LICENSE28
-rw-r--r--include/jsoncons_ext/bson/bson.hpp23
-rw-r--r--include/jsoncons_ext/bson/bson_cursor.hpp320
-rw-r--r--include/jsoncons_ext/bson/bson_decimal128.hpp865
-rw-r--r--include/jsoncons_ext/bson/bson_decimal128.hpp.bak816
-rw-r--r--include/jsoncons_ext/bson/bson_encoder.hpp585
-rw-r--r--include/jsoncons_ext/bson/bson_error.hpp103
-rw-r--r--include/jsoncons_ext/bson/bson_oid.hpp245
-rw-r--r--include/jsoncons_ext/bson/bson_options.hpp75
-rw-r--r--include/jsoncons_ext/bson/bson_parser.hpp645
-rw-r--r--include/jsoncons_ext/bson/bson_reader.hpp92
-rw-r--r--include/jsoncons_ext/bson/bson_type.hpp44
-rw-r--r--include/jsoncons_ext/bson/decode_bson.hpp201
-rw-r--r--include/jsoncons_ext/bson/encode_bson.hpp144
-rw-r--r--include/jsoncons_ext/cbor/cbor.hpp26
-rw-r--r--include/jsoncons_ext/cbor/cbor_cursor.hpp351
-rw-r--r--include/jsoncons_ext/cbor/cbor_cursor2.hpp265
-rw-r--r--include/jsoncons_ext/cbor/cbor_detail.hpp93
-rw-r--r--include/jsoncons_ext/cbor/cbor_encoder.hpp1766
-rw-r--r--include/jsoncons_ext/cbor/cbor_error.hpp105
-rw-r--r--include/jsoncons_ext/cbor/cbor_options.hpp113
-rw-r--r--include/jsoncons_ext/cbor/cbor_parser.hpp1942
-rw-r--r--include/jsoncons_ext/cbor/cbor_reader.hpp116
-rw-r--r--include/jsoncons_ext/cbor/decode_cbor.hpp203
-rw-r--r--include/jsoncons_ext/cbor/encode_cbor.hpp151
-rw-r--r--include/jsoncons_ext/csv/csv.hpp17
-rw-r--r--include/jsoncons_ext/csv/csv_cursor.hpp358
-rw-r--r--include/jsoncons_ext/csv/csv_encoder.hpp954
-rw-r--r--include/jsoncons_ext/csv/csv_error.hpp85
-rw-r--r--include/jsoncons_ext/csv/csv_options.hpp973
-rw-r--r--include/jsoncons_ext/csv/csv_parser.hpp2097
-rw-r--r--include/jsoncons_ext/csv/csv_reader.hpp348
-rw-r--r--include/jsoncons_ext/csv/csv_serializer.hpp12
-rw-r--r--include/jsoncons_ext/csv/decode_csv.hpp208
-rw-r--r--include/jsoncons_ext/csv/encode_csv.hpp122
-rw-r--r--include/jsoncons_ext/jmespath/jmespath.hpp5215
-rw-r--r--include/jsoncons_ext/jmespath/jmespath_error.hpp215
-rw-r--r--include/jsoncons_ext/jsonpatch/jsonpatch.hpp579
-rw-r--r--include/jsoncons_ext/jsonpatch/jsonpatch_error.hpp121
-rw-r--r--include/jsoncons_ext/jsonpath/expression.hpp3329
-rw-r--r--include/jsoncons_ext/jsonpath/flatten.hpp432
-rw-r--r--include/jsoncons_ext/jsonpath/json_location.hpp445
-rw-r--r--include/jsoncons_ext/jsonpath/json_query.hpp115
-rw-r--r--include/jsoncons_ext/jsonpath/jsonpath.hpp13
-rw-r--r--include/jsoncons_ext/jsonpath/jsonpath_error.hpp240
-rw-r--r--include/jsoncons_ext/jsonpath/jsonpath_expression.hpp2612
-rw-r--r--include/jsoncons_ext/jsonpath/jsonpath_selector.hpp1322
-rw-r--r--include/jsoncons_ext/jsonpointer/jsonpointer.hpp1577
-rw-r--r--include/jsoncons_ext/jsonpointer/jsonpointer_error.hpp119
-rw-r--r--include/jsoncons_ext/jsonschema/format_validator.hpp968
-rw-r--r--include/jsoncons_ext/jsonschema/json_validator.hpp120
-rw-r--r--include/jsoncons_ext/jsonschema/jsonschema.hpp13
-rw-r--r--include/jsoncons_ext/jsonschema/jsonschema_error.hpp105
-rw-r--r--include/jsoncons_ext/jsonschema/jsonschema_version.hpp18
-rw-r--r--include/jsoncons_ext/jsonschema/keyword_validator.hpp1745
-rw-r--r--include/jsoncons_ext/jsonschema/keyword_validator_factory.hpp556
-rw-r--r--include/jsoncons_ext/jsonschema/schema_draft7.hpp198
-rw-r--r--include/jsoncons_ext/jsonschema/schema_location.hpp200
-rw-r--r--include/jsoncons_ext/jsonschema/schema_version.hpp35
-rw-r--r--include/jsoncons_ext/jsonschema/subschema.hpp144
-rw-r--r--include/jsoncons_ext/mergepatch/mergepatch.hpp103
-rw-r--r--include/jsoncons_ext/msgpack/decode_msgpack.hpp202
-rw-r--r--include/jsoncons_ext/msgpack/encode_msgpack.hpp142
-rw-r--r--include/jsoncons_ext/msgpack/msgpack.hpp24
-rw-r--r--include/jsoncons_ext/msgpack/msgpack_cursor.hpp343
-rw-r--r--include/jsoncons_ext/msgpack/msgpack_cursor2.hpp259
-rw-r--r--include/jsoncons_ext/msgpack/msgpack_encoder.hpp753
-rw-r--r--include/jsoncons_ext/msgpack/msgpack_error.hpp94
-rw-r--r--include/jsoncons_ext/msgpack/msgpack_options.hpp74
-rw-r--r--include/jsoncons_ext/msgpack/msgpack_parser.hpp748
-rw-r--r--include/jsoncons_ext/msgpack/msgpack_reader.hpp116
-rw-r--r--include/jsoncons_ext/msgpack/msgpack_type.hpp63
-rw-r--r--include/jsoncons_ext/ubjson/decode_ubjson.hpp201
-rw-r--r--include/jsoncons_ext/ubjson/encode_ubjson.hpp142
-rw-r--r--include/jsoncons_ext/ubjson/ubjson.hpp23
-rw-r--r--include/jsoncons_ext/ubjson/ubjson_cursor.hpp307
-rw-r--r--include/jsoncons_ext/ubjson/ubjson_encoder.hpp502
-rw-r--r--include/jsoncons_ext/ubjson/ubjson_error.hpp100
-rw-r--r--include/jsoncons_ext/ubjson/ubjson_options.hpp87
-rw-r--r--include/jsoncons_ext/ubjson/ubjson_parser.hpp880
-rw-r--r--include/jsoncons_ext/ubjson/ubjson_reader.hpp92
-rw-r--r--include/jsoncons_ext/ubjson/ubjson_type.hpp43
140 files changed, 79984 insertions, 0 deletions
diff --git a/include/handling.h b/include/handling.h
new file mode 100644
index 0000000..c5e0691
--- /dev/null
+++ b/include/handling.h
@@ -0,0 +1,53 @@
+/*
+ handling.h
+ Copyright (C) 2021 Richard Knight
+
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ */
+#include <plugin.h>
+
+const char* handleIdentifier = "::%s%d";
+
+/*
+ * Obtain global object of typename from global variables by handle
+ */
+template <typename T>
+T* getHandle(csnd::Csound* csound, MYFLT handle, const char* name) {
+ char buffer[32];
+ snprintf(buffer, 32, handleIdentifier, name, (int)handle);
+ return (T*) csound->query_global_variable(buffer);
+}
+
+
+/*
+ * Create global object of typename in global variables, returning handle
+ */
+template <typename T>
+MYFLT createHandle(csnd::Csound* csound, T** data, const char* name) {
+ char buffer[32];
+ int handle = 0;
+ snprintf(buffer, 32, handleIdentifier, name, handle);
+ while ((*data = (T*) csound->query_global_variable(buffer)) != NULL) {
+ snprintf(buffer, 32, handleIdentifier, name, ++handle);
+ }
+ csound->create_global_variable(buffer, sizeof(T));
+ *data = (T*) csound->query_global_variable(buffer);
+
+ return FL(handle);
+}
+
+
diff --git a/include/jsoncons/LICENSE b/include/jsoncons/LICENSE
new file mode 100644
index 0000000..ecf46ab
--- /dev/null
+++ b/include/jsoncons/LICENSE
@@ -0,0 +1,28 @@
+// Copyright Daniel Parker 2013 - 2020.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/include/jsoncons/allocator_holder.hpp b/include/jsoncons/allocator_holder.hpp
new file mode 100644
index 0000000..b69fcfe
--- /dev/null
+++ b/include/jsoncons/allocator_holder.hpp
@@ -0,0 +1,38 @@
+// Copyright 2013 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_ALLOCATOR_HOLDER_HPP
+#define JSONCONS_ALLOCATOR_HOLDER_HPP
+
+namespace jsoncons {
+
+template <class Allocator>
+class allocator_holder
+{
+public:
+ using allocator_type = Allocator;
+private:
+ allocator_type alloc_;
+public:
+ allocator_holder() = default;
+ allocator_holder(const allocator_holder&) = default;
+ allocator_holder(allocator_holder&&) = default;
+ allocator_holder& operator=(const allocator_holder&) = default;
+ allocator_holder& operator=(allocator_holder&&) = default;
+ allocator_holder(const allocator_type& alloc)
+ : alloc_(alloc)
+ {}
+ ~allocator_holder() = default;
+
+ allocator_type get_allocator() const
+ {
+ return alloc_;
+ }
+};
+
+}
+
+#endif
diff --git a/include/jsoncons/basic_json.hpp b/include/jsoncons/basic_json.hpp
new file mode 100644
index 0000000..31de47b
--- /dev/null
+++ b/include/jsoncons/basic_json.hpp
@@ -0,0 +1,5839 @@
+// Copyright 2013 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_BASIC_JSON_HPP
+#define JSONCONS_BASIC_JSON_HPP
+
+#include <limits> // std::numeric_limits
+#include <string>
+#include <vector>
+#include <exception>
+#include <cstring>
+#include <ostream>
+#include <memory> // std::allocator
+#include <typeinfo>
+#include <cstring> // std::memcpy
+#include <algorithm> // std::swap
+#include <initializer_list> // std::initializer_list
+#include <utility> // std::move
+#include <type_traits> // std::enable_if
+#include <istream> // std::basic_istream
+#include <jsoncons/json_fwd.hpp>
+#include <jsoncons/json_type.hpp>
+#include <jsoncons/config/version.hpp>
+#include <jsoncons/json_type.hpp>
+#include <jsoncons/json_exception.hpp>
+#include <jsoncons/pretty_print.hpp>
+#include <jsoncons/json_object.hpp>
+#include <jsoncons/json_array.hpp>
+#include <jsoncons/bigint.hpp>
+#include <jsoncons/json_options.hpp>
+#include <jsoncons/json_encoder.hpp>
+#include <jsoncons/json_decoder.hpp>
+#include <jsoncons/json_reader.hpp>
+#include <jsoncons/json_type_traits.hpp>
+#include <jsoncons/byte_string.hpp>
+#include <jsoncons/json_error.hpp>
+#include <jsoncons/detail/string_wrapper.hpp>
+
+namespace jsoncons {
+
+ namespace type_traits {
+
+ namespace detail {
+
+ template <class T>
+ using
+ basic_json_t = basic_json<typename T::char_type,typename T::implementation_policy,typename T::allocator_type>;
+
+ } // namespace detail
+
+ template<class T, class Enable = void>
+ struct is_basic_json : std::false_type {};
+
+ template<class T>
+ struct is_basic_json<T,
+ typename std::enable_if<type_traits::is_detected<detail::basic_json_t,typename std::decay<T>::type>::value>::type
+ > : std::true_type {};
+
+ } // namespace type_traits
+
+ namespace detail {
+
+ template <class Iterator,class Enable = void>
+ class random_access_iterator_wrapper
+ {
+ };
+
+ template <class Iterator>
+ class random_access_iterator_wrapper<Iterator,
+ typename std::enable_if<std::is_same<typename std::iterator_traits<Iterator>::iterator_category,
+ std::random_access_iterator_tag>::value>::type>
+ {
+ Iterator it_;
+ bool has_value_;
+
+ template <class Iter,class Enable>
+ friend class random_access_iterator_wrapper;
+ public:
+ using iterator_category = std::random_access_iterator_tag;
+
+ using value_type = typename std::iterator_traits<Iterator>::value_type;
+ using difference_type = typename std::iterator_traits<Iterator>::difference_type;
+ using pointer = typename std::iterator_traits<Iterator>::pointer;
+ using reference = typename std::iterator_traits<Iterator>::reference;
+
+ random_access_iterator_wrapper() : it_(), has_value_(false)
+ {
+ }
+
+ explicit random_access_iterator_wrapper(Iterator ptr) : it_(ptr), has_value_(true)
+ {
+ }
+
+ random_access_iterator_wrapper(const random_access_iterator_wrapper&) = default;
+ random_access_iterator_wrapper(random_access_iterator_wrapper&&) = default;
+ random_access_iterator_wrapper& operator=(const random_access_iterator_wrapper&) = default;
+ random_access_iterator_wrapper& operator=(random_access_iterator_wrapper&&) = default;
+
+ template <class Iter,
+ class=typename std::enable_if<!std::is_same<Iter,Iterator>::value && std::is_convertible<Iter,Iterator>::value>::type>
+ random_access_iterator_wrapper(const random_access_iterator_wrapper<Iter>& other)
+ : it_(other.it_), has_value_(other.has_value_)
+ {
+ }
+
+ operator Iterator() const
+ {
+ return it_;
+ }
+
+ reference operator*() const
+ {
+ return *it_;
+ }
+
+ pointer operator->() const
+ {
+ return &(*it_);
+ }
+
+ random_access_iterator_wrapper& operator++()
+ {
+ ++it_;
+ return *this;
+ }
+
+ random_access_iterator_wrapper operator++(int)
+ {
+ random_access_iterator_wrapper temp = *this;
+ ++*this;
+ return temp;
+ }
+
+ random_access_iterator_wrapper& operator--()
+ {
+ --it_;
+ return *this;
+ }
+
+ random_access_iterator_wrapper operator--(int)
+ {
+ random_access_iterator_wrapper temp = *this;
+ --*this;
+ return temp;
+ }
+
+ random_access_iterator_wrapper& operator+=(const difference_type offset)
+ {
+ it_ += offset;
+ return *this;
+ }
+
+ random_access_iterator_wrapper operator+(const difference_type offset) const
+ {
+ random_access_iterator_wrapper temp = *this;
+ return temp += offset;
+ }
+
+ random_access_iterator_wrapper& operator-=(const difference_type offset)
+ {
+ return *this += -offset;
+ }
+
+ random_access_iterator_wrapper operator-(const difference_type offset) const
+ {
+ random_access_iterator_wrapper temp = *this;
+ return temp -= offset;
+ }
+
+ difference_type operator-(const random_access_iterator_wrapper& rhs) const noexcept
+ {
+ return it_ - rhs.it_;
+ }
+
+ reference operator[](const difference_type offset) const noexcept
+ {
+ return *(*this + offset);
+ }
+
+ bool operator==(const random_access_iterator_wrapper& rhs) const noexcept
+ {
+ if (!has_value_ || !rhs.has_value_)
+ {
+ return has_value_ == rhs.has_value_ ? true : false;
+ }
+ else
+ {
+ return it_ == rhs.it_;
+ }
+ }
+
+ bool operator!=(const random_access_iterator_wrapper& rhs) const noexcept
+ {
+ return !(*this == rhs);
+ }
+
+ bool operator<(const random_access_iterator_wrapper& rhs) const noexcept
+ {
+ if (!has_value_ || !rhs.has_value_)
+ {
+ return has_value_ == rhs.has_value_ ? false :(has_value_ ? false : true);
+ }
+ else
+ {
+ return it_ < rhs.it_;
+ }
+ }
+
+ bool operator>(const random_access_iterator_wrapper& rhs) const noexcept
+ {
+ return rhs < *this;
+ }
+
+ bool operator<=(const random_access_iterator_wrapper& rhs) const noexcept
+ {
+ return !(rhs < *this);
+ }
+
+ bool operator>=(const random_access_iterator_wrapper& rhs) const noexcept
+ {
+ return !(*this < rhs);
+ }
+
+ inline
+ friend random_access_iterator_wrapper<Iterator> operator+(
+ difference_type offset, random_access_iterator_wrapper<Iterator> next)
+ {
+ return next += offset;
+ }
+
+ bool has_value() const
+ {
+ return has_value_;
+ }
+ };
+ } // namespace detail
+
+ struct sorted_policy
+ {
+ template <class KeyT,class Json>
+ using object = sorted_json_object<KeyT,Json,std::vector>;
+
+ template <class Json>
+ using array = json_array<Json,std::vector>;
+
+ using parse_error_handler_type = default_json_parsing;
+ };
+
+ struct order_preserving_policy
+ {
+ template <class KeyT,class Json>
+ using object = order_preserving_json_object<KeyT,Json,std::vector>;
+
+ template <class Json>
+ using array = json_array<Json,std::vector>;
+
+ using parse_error_handler_type = default_json_parsing;
+ };
+
+ #if !defined(JSONCONS_NO_DEPRECATED)
+ using preserve_order_policy = order_preserving_policy;
+ #endif
+
+ template <class IteratorT, class ConstIteratorT>
+ class range
+ {
+ public:
+ using iterator = IteratorT;
+ using const_iterator = ConstIteratorT;
+ using reverse_iterator = std::reverse_iterator<IteratorT>;
+ using const_reverse_iterator = std::reverse_iterator<ConstIteratorT>;
+ private:
+ iterator first_;
+ iterator last_;
+ public:
+ range(const IteratorT& first, const IteratorT& last)
+ : first_(first), last_(last)
+ {
+ }
+
+ iterator begin()
+ {
+ return first_;
+ }
+ iterator end()
+ {
+ return last_;
+ }
+ const_iterator cbegin()
+ {
+ return first_;
+ }
+ const_iterator cend()
+ {
+ return last_;
+ }
+ reverse_iterator rbegin()
+ {
+ return reverse_iterator(last_);
+ }
+ reverse_iterator rend()
+ {
+ return reverse_iterator(first_);
+ }
+ const_reverse_iterator crbegin()
+ {
+ return reverse_iterator(last_);
+ }
+ const_reverse_iterator crend()
+ {
+ return reverse_iterator(first_);
+ }
+ };
+
+ // is_proxy_of
+
+ template<class T, class Json, class Enable = void>
+ struct is_proxy_of : std::false_type {};
+
+ template<class Proxy,class Json>
+ struct is_proxy_of<Proxy,Json,
+ typename std::enable_if<std::is_same<typename Proxy::proxied_type,Json>::value>::type
+ > : std::true_type {};
+
+
+ // is_proxy
+
+ template<class T, class Enable = void>
+ struct is_proxy : std::false_type {};
+
+ template<class T>
+ struct is_proxy<T,typename std::enable_if<is_proxy_of<T,typename T::proxied_type>::value>::type
+ > : std::true_type {};
+
+ template <class CharT, class ImplementationPolicy, class Allocator>
+ class basic_json
+ {
+ public:
+
+ using allocator_type = Allocator;
+
+ using implementation_policy = ImplementationPolicy;
+
+ using parse_error_handler_type = typename ImplementationPolicy::parse_error_handler_type;
+
+ using char_type = CharT;
+ using char_traits_type = std::char_traits<char_type>;
+ using string_view_type = jsoncons::basic_string_view<char_type,char_traits_type>;
+
+ using char_allocator_type = typename std::allocator_traits<allocator_type>:: template rebind_alloc<char_type>;
+
+ using key_type = std::basic_string<char_type,char_traits_type,char_allocator_type>;
+
+
+ using reference = basic_json&;
+ using const_reference = const basic_json&;
+ using pointer = basic_json*;
+ using const_pointer = const basic_json*;
+
+ using key_value_type = key_value<key_type,basic_json>;
+
+ #if !defined(JSONCONS_NO_DEPRECATED)
+ JSONCONS_DEPRECATED_MSG("no replacement") typedef basic_json value_type;
+ JSONCONS_DEPRECATED_MSG("no replacement") typedef std::basic_string<char_type> string_type;
+ JSONCONS_DEPRECATED_MSG("Instead, use key_value_type") typedef key_value_type kvp_type;
+ JSONCONS_DEPRECATED_MSG("Instead, use key_value_type") typedef key_value_type member_type;
+ #endif
+
+ using array = typename ImplementationPolicy::template array<basic_json>;
+
+ using key_value_allocator_type = typename std::allocator_traits<allocator_type>:: template rebind_alloc<key_value_type>;
+
+ using object = typename ImplementationPolicy::template object<key_type,basic_json>;
+
+ using object_iterator = jsoncons::detail::random_access_iterator_wrapper<typename object::iterator>;
+ using const_object_iterator = jsoncons::detail::random_access_iterator_wrapper<typename object::const_iterator>;
+ using array_iterator = typename array::iterator;
+ using const_array_iterator = typename array::const_iterator;
+
+ private:
+
+ static constexpr uint8_t major_type_shift = 0x04;
+ static constexpr uint8_t additional_information_mask = (1U << 4) - 1;
+
+ class common_storage final
+ {
+ public:
+ uint8_t storage_kind_:4;
+ uint8_t length_:4;
+ semantic_tag tag_;
+ };
+
+ class null_storage final
+ {
+ public:
+ uint8_t storage_kind_:4;
+ uint8_t length_:4;
+ semantic_tag tag_;
+
+ null_storage(semantic_tag tag = semantic_tag::none)
+ : storage_kind_(static_cast<uint8_t>(json_storage_kind::null_value)), length_(0), tag_(tag)
+ {
+ }
+ };
+
+ class empty_object_storage final
+ {
+ public:
+ uint8_t storage_kind_:4;
+ uint8_t length_:4;
+ semantic_tag tag_;
+
+ empty_object_storage(semantic_tag tag)
+ : storage_kind_(static_cast<uint8_t>(json_storage_kind::empty_object_value)), length_(0), tag_(tag)
+ {
+ }
+ };
+
+ class bool_storage final
+ {
+ public:
+ uint8_t storage_kind_:4;
+ uint8_t length_:4;
+ semantic_tag tag_;
+ private:
+ bool val_;
+ public:
+ bool_storage(bool val, semantic_tag tag)
+ : storage_kind_(static_cast<uint8_t>(json_storage_kind::bool_value)), length_(0), tag_(tag),
+ val_(val)
+ {
+ }
+
+ bool value() const
+ {
+ return val_;
+ }
+
+ };
+
+ class int64_storage final
+ {
+ public:
+ uint8_t storage_kind_:4;
+ uint8_t length_:4;
+ semantic_tag tag_;
+ private:
+ int64_t val_;
+ public:
+ int64_storage(int64_t val,
+ semantic_tag tag = semantic_tag::none)
+ : storage_kind_(static_cast<uint8_t>(json_storage_kind::int64_value)), length_(0), tag_(tag),
+ val_(val)
+ {
+ }
+
+ int64_t value() const
+ {
+ return val_;
+ }
+ };
+
+ class uint64_storage final
+ {
+ public:
+ uint8_t storage_kind_:4;
+ uint8_t length_:4;
+ semantic_tag tag_;
+ private:
+ uint64_t val_;
+ public:
+ uint64_storage(uint64_t val,
+ semantic_tag tag = semantic_tag::none)
+ : storage_kind_(static_cast<uint8_t>(json_storage_kind::uint64_value)), length_(0), tag_(tag),
+ val_(val)
+ {
+ }
+
+ uint64_t value() const
+ {
+ return val_;
+ }
+ };
+
+ class half_storage final
+ {
+ public:
+ uint8_t storage_kind_:4;
+ uint8_t length_:4;
+ semantic_tag tag_;
+ private:
+ uint16_t val_;
+ public:
+ half_storage(uint16_t val, semantic_tag tag = semantic_tag::none)
+ : storage_kind_(static_cast<uint8_t>(json_storage_kind::half_value)), length_(0), tag_(tag),
+ val_(val)
+ {
+ }
+
+ uint16_t value() const
+ {
+ return val_;
+ }
+ };
+
+ class double_storage final
+ {
+ public:
+ uint8_t storage_kind_:4;
+ uint8_t length_:4;
+ semantic_tag tag_;
+ private:
+ double val_;
+ public:
+ double_storage(double val,
+ semantic_tag tag = semantic_tag::none)
+ : storage_kind_(static_cast<uint8_t>(json_storage_kind::double_value)), length_(0), tag_(tag),
+ val_(val)
+ {
+ }
+
+ double value() const
+ {
+ return val_;
+ }
+ };
+
+ class short_string_storage final
+ {
+ public:
+ uint8_t storage_kind_:4;
+ uint8_t length_:4;
+ semantic_tag tag_;
+ private:
+ static constexpr size_t capacity = (2*sizeof(uint64_t) - 2*sizeof(uint8_t))/sizeof(char_type);
+ char_type data_[capacity];
+ public:
+ static constexpr size_t max_length = capacity - 1;
+
+ short_string_storage(semantic_tag tag, const char_type* p, uint8_t length)
+ : storage_kind_(static_cast<uint8_t>(json_storage_kind::short_string_value)), length_(length), tag_(tag)
+ {
+ JSONCONS_ASSERT(length <= max_length);
+ std::memcpy(data_,p,length*sizeof(char_type));
+ data_[length] = 0;
+ }
+
+ short_string_storage(const short_string_storage& val)
+ : storage_kind_(val.storage_kind_), length_(val.length_), tag_(val.tag_)
+ {
+ std::memcpy(data_,val.data_,val.length_*sizeof(char_type));
+ data_[length_] = 0;
+ }
+
+ short_string_storage& operator=(const short_string_storage& val) = delete;
+
+ uint8_t length() const
+ {
+ return length_;
+ }
+
+ const char_type* data() const
+ {
+ return data_;
+ }
+
+ const char_type* c_str() const
+ {
+ return data_;
+ }
+ };
+
+ // long_string_storage
+ class long_string_storage final
+ {
+ public:
+ uint8_t storage_kind_:4;
+ uint8_t length_:4;
+ semantic_tag tag_;
+ private:
+ jsoncons::detail::string_wrapper<char_type,Allocator> s_;
+ public:
+
+ long_string_storage(semantic_tag tag, const char_type* data, std::size_t length, const Allocator& a)
+ : storage_kind_(static_cast<uint8_t>(json_storage_kind::long_string_value)), length_(0), tag_(tag),
+ s_(data, length, a)
+ {
+ }
+
+ long_string_storage(const long_string_storage& val)
+ : storage_kind_(val.storage_kind_), length_(0), tag_(val.tag_),
+ s_(val.s_)
+ {
+ }
+
+ long_string_storage(long_string_storage&& val) noexcept
+ : storage_kind_(val.storage_kind_), length_(0), tag_(val.tag_),
+ s_(nullptr)
+ {
+ swap(val);
+ }
+
+ long_string_storage(const long_string_storage& val, const Allocator& a)
+ : storage_kind_(val.storage_kind_), length_(0), tag_(val.tag_),
+ s_(val.s_, a)
+ {
+ }
+
+ ~long_string_storage() noexcept
+ {
+ }
+
+ long_string_storage& operator=(const long_string_storage& val) = delete;
+
+ long_string_storage& operator=(long_string_storage&& val) noexcept = delete;
+
+ void swap(long_string_storage& val) noexcept
+ {
+ s_.swap(val.s_);
+ }
+
+ const char_type* data() const
+ {
+ return s_.data();
+ }
+
+ const char_type* c_str() const
+ {
+ return s_.c_str();
+ }
+
+ std::size_t length() const
+ {
+ return s_.length();
+ }
+
+ allocator_type get_allocator() const
+ {
+ return s_.get_allocator();
+ }
+ };
+
+ // byte_string_storage
+ class byte_string_storage final
+ {
+ public:
+ uint8_t storage_kind_:4;
+ uint8_t length_:4;
+ semantic_tag tag_;
+ private:
+ jsoncons::detail::tagged_string_wrapper<uint8_t,Allocator> s_;
+ public:
+
+ byte_string_storage(semantic_tag tag, const uint8_t* data, std::size_t length, uint64_t ext_tag, const Allocator& alloc)
+ : storage_kind_(static_cast<uint8_t>(json_storage_kind::byte_string_value)), length_(0), tag_(tag),
+ s_(data, length, ext_tag, alloc)
+ {
+ }
+
+ byte_string_storage(const byte_string_storage& val)
+ : storage_kind_(val.storage_kind_), length_(0), tag_(val.tag_),
+ s_(val.s_)
+ {
+ }
+
+ byte_string_storage(byte_string_storage&& val) noexcept
+ : storage_kind_(val.storage_kind_), length_(0), tag_(val.tag_),
+ s_(nullptr)
+ {
+ swap(val);
+ }
+
+ byte_string_storage(const byte_string_storage& val, const Allocator& a)
+ : storage_kind_(val.storage_kind_), length_(0), tag_(val.tag_),
+ s_(val.s_, a)
+ {
+ }
+
+ ~byte_string_storage() noexcept
+ {
+ }
+
+ void swap(byte_string_storage& val) noexcept
+ {
+ s_.swap(val.s_);
+ }
+
+ const uint8_t* data() const
+ {
+ return s_.data();
+ }
+
+ std::size_t length() const
+ {
+ return s_.length();
+ }
+
+ uint64_t ext_tag() const
+ {
+ return s_.tag();
+ }
+
+ allocator_type get_allocator() const
+ {
+ return s_.get_allocator();
+ }
+ };
+
+ // array_storage
+ class array_storage final
+ {
+ public:
+ uint8_t storage_kind_:4;
+ uint8_t length_:4;
+ semantic_tag tag_;
+ private:
+ using array_allocator = typename std::allocator_traits<Allocator>:: template rebind_alloc<array>;
+ using pointer = typename std::allocator_traits<array_allocator>::pointer;
+
+ pointer ptr_;
+
+ template <typename... Args>
+ void create(array_allocator alloc, Args&& ... args)
+ {
+ ptr_ = std::allocator_traits<array_allocator>::allocate(alloc, 1);
+ JSONCONS_TRY
+ {
+ std::allocator_traits<array_allocator>::construct(alloc, type_traits::to_plain_pointer(ptr_), std::forward<Args>(args)...);
+ }
+ JSONCONS_CATCH(...)
+ {
+ std::allocator_traits<array_allocator>::deallocate(alloc, ptr_,1);
+ JSONCONS_RETHROW;
+ }
+ }
+
+ void destroy() noexcept
+ {
+ array_allocator alloc(ptr_->get_allocator());
+ std::allocator_traits<array_allocator>::destroy(alloc, type_traits::to_plain_pointer(ptr_));
+ std::allocator_traits<array_allocator>::deallocate(alloc, ptr_,1);
+ }
+ public:
+ array_storage(const array& val, semantic_tag tag)
+ : storage_kind_(static_cast<uint8_t>(json_storage_kind::array_value)), length_(0), tag_(tag)
+ {
+ create(val.get_allocator(), val);
+ }
+
+ array_storage(array&& val, semantic_tag tag)
+ : storage_kind_(static_cast<uint8_t>(json_storage_kind::array_value)), length_(0), tag_(tag)
+ {
+ create(val.get_allocator(), std::forward<array>(val));
+ }
+
+ array_storage(const array& val, semantic_tag tag, const Allocator& a)
+ : storage_kind_(val.storage_kind_), length_(0), tag_(tag)
+ {
+ create(array_allocator(a), val, a);
+ }
+
+ array_storage(const array_storage& val)
+ : storage_kind_(val.storage_kind_), length_(0), tag_(val.tag_)
+ {
+ create(val.ptr_->get_allocator(), *(val.ptr_));
+ }
+
+ array_storage(array_storage&& val) noexcept
+ : storage_kind_(val.storage_kind_), length_(0), tag_(val.tag_),
+ ptr_(nullptr)
+ {
+ std::swap(val.ptr_, ptr_);
+ }
+
+ array_storage(const array_storage& val, const Allocator& a)
+ : storage_kind_(val.storage_kind_), length_(0), tag_(val.tag_)
+ {
+ create(array_allocator(a), *(val.ptr_), a);
+ }
+ ~array_storage() noexcept
+ {
+ if (ptr_ != nullptr)
+ {
+ destroy();
+ }
+ }
+
+ allocator_type get_allocator() const
+ {
+ return ptr_->get_allocator();
+ }
+
+ void swap(array_storage& val) noexcept
+ {
+ std::swap(val.ptr_,ptr_);
+ }
+
+ array& value()
+ {
+ return *ptr_;
+ }
+
+ const array& value() const
+ {
+ return *ptr_;
+ }
+ };
+
+ // object_storage
+ class object_storage final
+ {
+ public:
+ uint8_t storage_kind_:4;
+ uint8_t length_:4;
+ semantic_tag tag_;
+ private:
+ using object_allocator = typename std::allocator_traits<Allocator>:: template rebind_alloc<object>;
+ using pointer = typename std::allocator_traits<object_allocator>::pointer;
+
+ pointer ptr_;
+
+ template <typename... Args>
+ void create(object_allocator alloc, Args&& ... args)
+ {
+ ptr_ = std::allocator_traits<object_allocator>::allocate(alloc, 1);
+ JSONCONS_TRY
+ {
+ std::allocator_traits<object_allocator>::construct(alloc, type_traits::to_plain_pointer(ptr_), std::forward<Args>(args)...);
+ }
+ JSONCONS_CATCH(...)
+ {
+ std::allocator_traits<object_allocator>::deallocate(alloc, ptr_,1);
+ JSONCONS_RETHROW;
+ }
+ }
+ public:
+ explicit object_storage(const object& val, semantic_tag tag)
+ : storage_kind_(static_cast<uint8_t>(json_storage_kind::object_value)), length_(0), tag_(tag)
+ {
+ create(val.get_allocator(), val);
+ }
+
+ explicit object_storage(object&& val, semantic_tag tag)
+ : storage_kind_(static_cast<uint8_t>(json_storage_kind::object_value)), length_(0), tag_(tag)
+ {
+ create(val.get_allocator(), std::forward<object>(val));
+ }
+
+ explicit object_storage(const object& val, semantic_tag tag, const Allocator& a)
+ : storage_kind_(val.storage_kind_), length_(0), tag_(tag)
+ {
+ create(object_allocator(a), val, a);
+ }
+
+ explicit object_storage(const object_storage& val)
+ : storage_kind_(val.storage_kind_), length_(0), tag_(val.tag_)
+ {
+ create(val.ptr_->get_allocator(), *(val.ptr_));
+ }
+
+ explicit object_storage(object_storage&& val) noexcept
+ : storage_kind_(val.storage_kind_), length_(0), tag_(val.tag_),
+ ptr_(nullptr)
+ {
+ std::swap(val.ptr_,ptr_);
+ }
+
+ explicit object_storage(const object_storage& val, const Allocator& a)
+ : storage_kind_(val.storage_kind_), length_(0), tag_(val.tag_)
+ {
+ create(object_allocator(a), *(val.ptr_), a);
+ }
+
+ ~object_storage() noexcept
+ {
+ if (ptr_ != nullptr)
+ {
+ destroy();
+ }
+ }
+
+ void swap(object_storage& val) noexcept
+ {
+ std::swap(val.ptr_,ptr_);
+ }
+
+ object& value()
+ {
+ return *ptr_;
+ }
+
+ const object& value() const
+ {
+ return *ptr_;
+ }
+
+ allocator_type get_allocator() const
+ {
+ return ptr_->get_allocator();
+ }
+ private:
+
+ void destroy() noexcept
+ {
+ object_allocator alloc(ptr_->get_allocator());
+ std::allocator_traits<object_allocator>::destroy(alloc, type_traits::to_plain_pointer(ptr_));
+ std::allocator_traits<object_allocator>::deallocate(alloc, ptr_,1);
+ }
+ };
+
+ class json_const_pointer_storage final
+ {
+ public:
+ uint8_t storage_kind_:4;
+ uint8_t length_:4;
+ semantic_tag tag_;
+ private:
+ const basic_json* p_;
+ public:
+ json_const_pointer_storage(const basic_json* p)
+ : storage_kind_(static_cast<uint8_t>(json_storage_kind::json_const_pointer)), length_(0), tag_(p->tag()),
+ p_(p)
+ {
+ }
+
+ const basic_json* value() const
+ {
+ return p_;
+ }
+ };
+
+ template <class ParentType>
+ class proxy
+ {
+ friend class basic_json<char_type,implementation_policy,allocator_type>;
+
+ ParentType& parent_;
+ string_view_type key_;
+
+ proxy() = delete;
+
+ proxy(const proxy& other) = default;
+ proxy(proxy&& other) = default;
+ proxy& operator = (const proxy& other) = delete;
+ proxy& operator = (proxy&& other) = delete;
+
+ proxy(ParentType& parent, const string_view_type& key)
+ : parent_(parent), key_(key)
+ {
+ }
+
+ basic_json& evaluate_with_default()
+ {
+ basic_json& val = parent_.evaluate_with_default();
+ auto it = val.find(key_);
+ if (it == val.object_range().end())
+ {
+ auto r = val.try_emplace(key_, json_object_arg, semantic_tag::none, val.object_value().get_allocator());
+ return r.first->value();
+ }
+ else
+ {
+ return it->value();
+ }
+ }
+
+ basic_json& evaluate(std::size_t index)
+ {
+ return evaluate().at(index);
+ }
+
+ const basic_json& evaluate(std::size_t index) const
+ {
+ return evaluate().at(index);
+ }
+
+ basic_json& evaluate(const string_view_type& index)
+ {
+ return evaluate().at(index);
+ }
+
+ const basic_json& evaluate(const string_view_type& index) const
+ {
+ return evaluate().at(index);
+ }
+ public:
+ using proxied_type = basic_json;
+ using proxy_type = proxy<typename ParentType::proxy_type>;
+
+ basic_json& evaluate()
+ {
+ return parent_.evaluate(key_);
+ }
+
+ const basic_json& evaluate() const
+ {
+ return parent_.evaluate(key_);
+ }
+
+ operator basic_json&()
+ {
+ return evaluate();
+ }
+
+ operator const basic_json&() const
+ {
+ return evaluate();
+ }
+
+ range<object_iterator, const_object_iterator> object_range()
+ {
+ return evaluate().object_range();
+ }
+
+ range<const_object_iterator, const_object_iterator> object_range() const
+ {
+ return evaluate().object_range();
+ }
+
+ range<array_iterator, const_array_iterator> array_range()
+ {
+ return evaluate().array_range();
+ }
+
+ range<const_array_iterator, const_array_iterator> array_range() const
+ {
+ return evaluate().array_range();
+ }
+
+ std::size_t size() const noexcept
+ {
+ if (!parent_.contains(key_))
+ {
+ return 0;
+ }
+ return evaluate().size();
+ }
+
+ json_storage_kind storage_kind() const
+ {
+ return evaluate().storage_kind();
+ }
+
+ semantic_tag tag() const
+ {
+ return evaluate().tag();
+ }
+
+ json_type type() const
+ {
+ return evaluate().type();
+ }
+
+ std::size_t count(const string_view_type& name) const
+ {
+ return evaluate().count(name);
+ }
+
+ allocator_type get_allocator() const
+ {
+ return evaluate().get_allocator();
+ }
+
+ uint64_t ext_tag() const
+ {
+ return evaluate().ext_tag();
+ }
+
+ bool contains(const string_view_type& key) const noexcept
+ {
+ if (!parent_.contains(key_))
+ {
+ return false;
+ }
+
+ return evaluate().contains(key);
+ }
+
+ bool is_null() const noexcept
+ {
+ if (!parent_.contains(key_))
+ {
+ return false;
+ }
+ return evaluate().is_null();
+ }
+
+ bool empty() const noexcept
+ {
+ if (!parent_.contains(key_))
+ {
+ return true;
+ }
+ return evaluate().empty();
+ }
+
+ std::size_t capacity() const
+ {
+ return evaluate().capacity();
+ }
+
+ void reserve(std::size_t n)
+ {
+ evaluate().reserve(n);
+ }
+
+ void resize(std::size_t n)
+ {
+ evaluate().resize(n);
+ }
+
+ template <class T>
+ void resize(std::size_t n, T val)
+ {
+ evaluate().resize(n,val);
+ }
+
+ template<class T, class... Args>
+ bool is(Args&&... args) const noexcept
+ {
+ if (!parent_.contains(key_))
+ {
+ return false;
+ }
+ return evaluate().template is<T>(std::forward<Args>(args)...);
+ }
+
+ bool is_string() const noexcept
+ {
+ if (!parent_.contains(key_))
+ {
+ return false;
+ }
+ return evaluate().is_string();
+ }
+
+ bool is_string_view() const noexcept
+ {
+ if (!parent_.contains(key_))
+ {
+ return false;
+ }
+ return evaluate().is_string_view();
+ }
+
+ bool is_byte_string() const noexcept
+ {
+ if (!parent_.contains(key_))
+ {
+ return false;
+ }
+ return evaluate().is_byte_string();
+ }
+
+ bool is_byte_string_view() const noexcept
+ {
+ if (!parent_.contains(key_))
+ {
+ return false;
+ }
+ return evaluate().is_byte_string_view();
+ }
+
+ bool is_bignum() const noexcept
+ {
+ if (!parent_.contains(key_))
+ {
+ return false;
+ }
+ return evaluate().is_bignum();
+ }
+
+ bool is_number() const noexcept
+ {
+ if (!parent_.contains(key_))
+ {
+ return false;
+ }
+ return evaluate().is_number();
+ }
+ bool is_bool() const noexcept
+ {
+ if (!parent_.contains(key_))
+ {
+ return false;
+ }
+ return evaluate().is_bool();
+ }
+
+ bool is_object() const noexcept
+ {
+ if (!parent_.contains(key_))
+ {
+ return false;
+ }
+ return evaluate().is_object();
+ }
+
+ bool is_array() const noexcept
+ {
+ if (!parent_.contains(key_))
+ {
+ return false;
+ }
+ return evaluate().is_array();
+ }
+
+ bool is_int64() const noexcept
+ {
+ if (!parent_.contains(key_))
+ {
+ return false;
+ }
+ return evaluate().is_int64();
+ }
+
+ bool is_uint64() const noexcept
+ {
+ if (!parent_.contains(key_))
+ {
+ return false;
+ }
+ return evaluate().is_uint64();
+ }
+
+ bool is_half() const noexcept
+ {
+ if (!parent_.contains(key_))
+ {
+ return false;
+ }
+ return evaluate().is_half();
+ }
+
+ bool is_double() const noexcept
+ {
+ if (!parent_.contains(key_))
+ {
+ return false;
+ }
+ return evaluate().is_double();
+ }
+
+ string_view_type as_string_view() const
+ {
+ return evaluate().as_string_view();
+ }
+
+ byte_string_view as_byte_string_view() const
+ {
+ return evaluate().as_byte_string_view();
+ }
+
+ template <class SAllocator=std::allocator<char_type>>
+ std::basic_string<char_type,char_traits_type,SAllocator> as_string() const
+ {
+ return evaluate().as_string();
+ }
+
+ template <class SAllocator=std::allocator<char_type>>
+ std::basic_string<char_type,char_traits_type,SAllocator> as_string(const SAllocator& alloc) const
+ {
+ return evaluate().as_string(alloc);
+ }
+
+ template <typename BAllocator=std::allocator<uint8_t>>
+ basic_byte_string<BAllocator> as_byte_string() const
+ {
+ return evaluate().template as_byte_string<BAllocator>();
+ }
+
+ template<class T>
+ typename std::enable_if<is_json_type_traits_specialized<basic_json,T>::value,T>::type
+ as() const
+ {
+ return evaluate().template as<T>();
+ }
+
+ template<class T>
+ typename std::enable_if<std::is_convertible<uint8_t,typename T::value_type>::value,T>::type
+ as(byte_string_arg_t, semantic_tag hint) const
+ {
+ return evaluate().template as<T>(byte_string_arg, hint);
+ }
+
+ bool as_bool() const
+ {
+ return evaluate().as_bool();
+ }
+
+ double as_double() const
+ {
+ return evaluate().as_double();
+ }
+
+ template <class T>
+ T as_integer() const
+ {
+ return evaluate().template as_integer<T>();
+ }
+
+ template <class T>
+ proxy& operator=(T&& val)
+ {
+ parent_.evaluate_with_default().insert_or_assign(key_, std::forward<T>(val));
+ return *this;
+ }
+
+ basic_json& operator[](std::size_t i)
+ {
+ return evaluate_with_default().at(i);
+ }
+
+ const basic_json& operator[](std::size_t i) const
+ {
+ return evaluate().at(i);
+ }
+
+ proxy_type operator[](const string_view_type& key)
+ {
+ return proxy_type(*this,key);
+ }
+
+ const basic_json& operator[](const string_view_type& name) const
+ {
+ return at(name);
+ }
+
+ basic_json& at(const string_view_type& name)
+ {
+ return evaluate().at(name);
+ }
+
+ const basic_json& at(const string_view_type& name) const
+ {
+ return evaluate().at(name);
+ }
+
+ const basic_json& at_or_null(const string_view_type& name) const
+ {
+ return evaluate().at_or_null(name);
+ }
+
+ const basic_json& at(std::size_t index)
+ {
+ return evaluate().at(index);
+ }
+
+ const basic_json& at(std::size_t index) const
+ {
+ return evaluate().at(index);
+ }
+
+ object_iterator find(const string_view_type& name)
+ {
+ return evaluate().find(name);
+ }
+
+ const_object_iterator find(const string_view_type& name) const
+ {
+ return evaluate().find(name);
+ }
+
+ template <class T,class U>
+ T get_value_or(const string_view_type& name, U&& default_value) const
+ {
+ static_assert(std::is_copy_constructible<T>::value,
+ "get_value_or: T must be copy constructible");
+ static_assert(std::is_convertible<U&&, T>::value,
+ "get_value_or: U must be convertible to T");
+ return evaluate().template get_value_or<T,U>(name,std::forward<U>(default_value));
+ }
+
+ void shrink_to_fit()
+ {
+ evaluate_with_default().shrink_to_fit();
+ }
+
+ void clear()
+ {
+ evaluate().clear();
+ }
+ // Remove all elements from an array or object
+
+ object_iterator erase(const_object_iterator pos)
+ {
+ return evaluate().erase(pos);
+ }
+ // Remove a range of elements from an object
+
+ object_iterator erase(const_object_iterator first, const_object_iterator last)
+ {
+ return evaluate().erase(first, last);
+ }
+ // Remove a range of elements from an object
+
+ void erase(const string_view_type& name)
+ {
+ evaluate().erase(name);
+ }
+
+ array_iterator erase(const_array_iterator pos)
+ {
+ return evaluate().erase(pos);
+ }
+ // Removes the element at pos
+
+ array_iterator erase(const_array_iterator first, const_array_iterator last)
+ {
+ return evaluate().erase(first, last);
+ }
+ // Remove a range of elements from an array
+
+ // merge
+
+ void merge(const basic_json& source)
+ {
+ return evaluate().merge(source);
+ }
+
+ void merge(basic_json&& source)
+ {
+ return evaluate().merge(std::forward<basic_json>(source));
+ }
+
+ void merge(object_iterator hint, const basic_json& source)
+ {
+ return evaluate().merge(hint, source);
+ }
+
+ void merge(object_iterator hint, basic_json&& source)
+ {
+ return evaluate().merge(hint, std::forward<basic_json>(source));
+ }
+
+ // merge_or_update
+
+ void merge_or_update(const basic_json& source)
+ {
+ return evaluate().merge_or_update(source);
+ }
+
+ void merge_or_update(basic_json&& source)
+ {
+ return evaluate().merge_or_update(std::forward<basic_json>(source));
+ }
+
+ void merge_or_update(object_iterator hint, const basic_json& source)
+ {
+ return evaluate().merge_or_update(hint, source);
+ }
+
+ void merge_or_update(object_iterator hint, basic_json&& source)
+ {
+ return evaluate().merge_or_update(hint, std::forward<basic_json>(source));
+ }
+
+ template <class T>
+ std::pair<object_iterator,bool> insert_or_assign(const string_view_type& name, T&& val)
+ {
+ return evaluate().insert_or_assign(name,std::forward<T>(val));
+ }
+
+ // emplace
+
+ template <class ... Args>
+ std::pair<object_iterator,bool> try_emplace(const string_view_type& name, Args&&... args)
+ {
+ return evaluate().try_emplace(name,std::forward<Args>(args)...);
+ }
+
+ template <class T>
+ object_iterator insert_or_assign(object_iterator hint, const string_view_type& name, T&& val)
+ {
+ return evaluate().insert_or_assign(hint, name, std::forward<T>(val));
+ }
+
+ template <class ... Args>
+ object_iterator try_emplace(object_iterator hint, const string_view_type& name, Args&&... args)
+ {
+ return evaluate().try_emplace(hint, name, std::forward<Args>(args)...);
+ }
+
+ template <class... Args>
+ array_iterator emplace(const_array_iterator pos, Args&&... args)
+ {
+ evaluate_with_default().emplace(pos, std::forward<Args>(args)...);
+ }
+
+ template <class... Args>
+ basic_json& emplace_back(Args&&... args)
+ {
+ return evaluate_with_default().emplace_back(std::forward<Args>(args)...);
+ }
+
+ template <class T>
+ void push_back(T&& val)
+ {
+ evaluate_with_default().push_back(std::forward<T>(val));
+ }
+
+ template <class T>
+ array_iterator insert(const_array_iterator pos, T&& val)
+ {
+ return evaluate_with_default().insert(pos, std::forward<T>(val));
+ }
+
+ template <class InputIt>
+ array_iterator insert(const_array_iterator pos, InputIt first, InputIt last)
+ {
+ return evaluate_with_default().insert(pos, first, last);
+ }
+
+ template <class InputIt>
+ void insert(InputIt first, InputIt last)
+ {
+ evaluate_with_default().insert(first, last);
+ }
+
+ template <class InputIt>
+ void insert(sorted_unique_range_tag tag, InputIt first, InputIt last)
+ {
+ evaluate_with_default().insert(tag, first, last);
+ }
+
+ template <typename... Args>
+ void dump(Args&& ... args) const
+ {
+ evaluate().dump(std::forward<Args>(args)...);
+ }
+
+ template <typename... Args>
+ void dump_pretty(Args&& ... args) const
+ {
+ evaluate().dump_pretty(std::forward<Args>(args)...);
+ }
+
+ void swap(basic_json& val)
+ {
+ evaluate_with_default().swap(val);
+ }
+
+ friend std::basic_ostream<char_type>& operator<<(std::basic_ostream<char_type>& os, const proxy& o)
+ {
+ o.dump(os);
+ return os;
+ }
+
+ template <class T>
+ T get_with_default(const string_view_type& name, const T& default_value) const
+ {
+ return evaluate().template get_with_default<T>(name,default_value);
+ }
+
+ template <class T = std::basic_string<char_type>>
+ T get_with_default(const string_view_type& name, const char_type* default_value) const
+ {
+ return evaluate().template get_with_default<T>(name,default_value);
+ }
+
+ std::basic_string<char_type> to_string() const
+ {
+ return evaluate().to_string();
+ }
+
+ template <class IntegerType>
+ bool is_integer() const noexcept
+ {
+ if (!parent_.contains(key_))
+ {
+ return false;
+ }
+ return evaluate().template is_integer<IntegerType>();
+ }
+
+ #if !defined(JSONCONS_NO_DEPRECATED)
+
+ const basic_json& get_with_default(const string_view_type& name) const
+ {
+ return evaluate().at_or_null(name);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use tag()")
+ semantic_tag get_semantic_tag() const
+ {
+ return evaluate().tag();
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use tag() == semantic_tag::datetime")
+ bool is_datetime() const noexcept
+ {
+ if (!parent_.contains(key_))
+ {
+ return false;
+ }
+ return evaluate().tag() == semantic_tag::datetime;
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use tag() == semantic_tag::epoch_second")
+ bool is_epoch_time() const noexcept
+ {
+ if (!parent_.contains(key_))
+ {
+ return false;
+ }
+ return evaluate().tag() == semantic_tag::epoch_second;
+ }
+
+ template <class T>
+ JSONCONS_DEPRECATED_MSG("Instead, use push_back(T&&)")
+ void add(T&& val)
+ {
+ evaluate_with_default().push_back(std::forward<T>(val));
+ }
+
+ template <class T>
+ JSONCONS_DEPRECATED_MSG("Instead, use insert(const_array_iterator, T&&)")
+ array_iterator add(const_array_iterator pos, T&& val)
+ {
+ return evaluate_with_default().insert(pos, std::forward<T>(val));
+ }
+
+ template <class T>
+ JSONCONS_DEPRECATED_MSG("Instead, use insert_or_assign(const string_view_type&, T&&)")
+ std::pair<object_iterator,bool> set(const string_view_type& name, T&& val)
+ {
+ return evaluate().insert_or_assign(name,std::forward<T>(val));
+ }
+
+ template <class T>
+ JSONCONS_DEPRECATED_MSG("Instead, use insert_or_assign(object_iterator, const string_view_type&, T&&)")
+ object_iterator set(object_iterator hint, const string_view_type& name, T&& val)
+ {
+ return evaluate().insert_or_assign(hint, name, std::forward<T>(val));
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use contains(const string_view_type&)")
+ bool has_key(const string_view_type& name) const noexcept
+ {
+ return contains(name);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use is<unsigned long long>()")
+ bool is_ulonglong() const noexcept
+ {
+ if (!parent_.contains(key_))
+ {
+ return false;
+ }
+ return evaluate().template is<unsigned long long>();
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use is<long long>()")
+ bool is_longlong() const noexcept
+ {
+ if (!parent_.contains(key_))
+ {
+ return false;
+ }
+ return evaluate().template is<long long>();
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use as<int>()")
+ int as_int() const
+ {
+ return evaluate().template as<int>();
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use as<unsigned int>()")
+ unsigned int as_uint() const
+ {
+ return evaluate().template as<unsigned int>();
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use as<long>()")
+ long as_long() const
+ {
+ return evaluate().template as<long>();
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use as<unsigned long>()")
+ unsigned long as_ulong() const
+ {
+ return evaluate().template as<unsigned long>();
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use as<long long>()")
+ long long as_longlong() const
+ {
+ return evaluate().template as<long long>();
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use as<unsigned long long>()")
+ unsigned long long as_ulonglong() const
+ {
+ return evaluate().template as<unsigned long long>();
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use as<uint64_t>()")
+ uint64_t as_uinteger() const
+ {
+ return evaluate().template as<uint64_t>();
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use dump(basic_json_visitor<char_type>&)")
+ void write(basic_json_visitor<char_type>& visitor) const
+ {
+ evaluate().dump(visitor);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use dump(std::basic_ostream<char_type>&)")
+ void write(std::basic_ostream<char_type>& os) const
+ {
+ evaluate().dump(os);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use dump(std::basic_ostream<char_type>&, const basic_json_encode_options<char_type>&)")
+ void write(std::basic_ostream<char_type>& os, const basic_json_encode_options<char_type>& options) const
+ {
+ evaluate().dump(os, options);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use dump_pretty(std::basic_ostream<char_type>&, const basic_json_encode_options<char_type>&)")
+ void write(std::basic_ostream<char_type>& os, const basic_json_encode_options<char_type>& options, bool pprint) const
+ {
+ if (pprint)
+ {
+ evaluate().dump_pretty(os, options);
+ }
+ else
+ {
+ evaluate().dump(os, options);
+ }
+ }
+ JSONCONS_DEPRECATED_MSG("Instead, use dump(basic_json_visitor<char_type>&)")
+ void to_stream(basic_json_visitor<char_type>& visitor) const
+ {
+ evaluate().dump(visitor);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use dump(std::basic_ostream<char_type>&)")
+ void to_stream(std::basic_ostream<char_type>& os) const
+ {
+ evaluate().dump(os);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use dump(std::basic_ostream<char_type>&, const basic_json_encode_options<char_type>&)")
+ void to_stream(std::basic_ostream<char_type>& os, const basic_json_encode_options<char_type>& options) const
+ {
+ evaluate().dump(os,options);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use dump_pretty(std::basic_ostream<char_type>&, const basic_json_encode_options<char_type>&)")
+ void to_stream(std::basic_ostream<char_type>& os, const basic_json_encode_options<char_type>& options, bool pprint) const
+ {
+ if (pprint)
+ {
+ evaluate().dump_pretty(os,options);
+ }
+ else
+ {
+ evaluate().dump(os,options);
+ }
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use resize(std::size_t)")
+ void resize_array(std::size_t n)
+ {
+ evaluate().resize(n);
+ }
+
+ template <class T>
+ JSONCONS_DEPRECATED_MSG("Instead, use resize(std::size_t, T)")
+ void resize_array(std::size_t n, T val)
+ {
+ evaluate().resize(n,val);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use object_range()")
+ range<object_iterator, const_object_iterator> members()
+ {
+ return evaluate().object_range();
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use object_range()")
+ range<const_object_iterator, const_object_iterator> members() const
+ {
+ return evaluate().object_range();
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use array_range()")
+ range<array_iterator, const_array_iterator> elements()
+ {
+ return evaluate().array_range();
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use array_range()")
+ range<const_array_iterator, const_array_iterator> elements() const
+ {
+ return evaluate().array_range();
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use object_range().begin()")
+ object_iterator begin_members()
+ {
+ return evaluate().object_range().begin();
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use object_range().begin()")
+ const_object_iterator begin_members() const
+ {
+ return evaluate().object_range().begin();
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use object_range().end()")
+ object_iterator end_members()
+ {
+ return evaluate().object_range().end();
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use object_range().end()")
+ const_object_iterator end_members() const
+ {
+ return evaluate().object_range().end();
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use array_range().begin()")
+ array_iterator begin_elements()
+ {
+ return evaluate().array_range().begin();
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use array_range().begin()")
+ const_array_iterator begin_elements() const
+ {
+ return evaluate().array_range().begin();
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use array_range().end()")
+ array_iterator end_elements()
+ {
+ return evaluate().array_range().end();
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use array_range().end()")
+ const_array_iterator end_elements() const
+ {
+ return evaluate().array_range().end();
+ }
+
+ template <class T>
+ JSONCONS_DEPRECATED_MSG("Instead, use get_with_default(const string_view_type&, T&&)")
+ basic_json get(const string_view_type& name, T&& default_value) const
+ {
+ return evaluate().get_with_default(name,std::forward<T>(default_value));
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use at_or_null(const string_view_type&)")
+ const basic_json& get(const string_view_type& name) const
+ {
+ return evaluate().at_or_null(name);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use contains(const string_view_type&)")
+ bool has_member(const string_view_type& name) const noexcept
+ {
+ return contains(name);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use erase(const_object_iterator, const_object_iterator)")
+ void remove_range(std::size_t from_index, std::size_t to_index)
+ {
+ evaluate().remove_range(from_index, to_index);
+ }
+ JSONCONS_DEPRECATED_MSG("Instead, use erase(const string_view_type& name)")
+ void remove(const string_view_type& name)
+ {
+ evaluate().remove(name);
+ }
+ JSONCONS_DEPRECATED_MSG("Instead, use erase(const string_view_type& name)")
+ void remove_member(const string_view_type& name)
+ {
+ evaluate().remove(name);
+ }
+ JSONCONS_DEPRECATED_MSG("Instead, use empty()")
+ bool is_empty() const noexcept
+ {
+ return empty();
+ }
+ JSONCONS_DEPRECATED_MSG("Instead, use is_number()")
+ bool is_numeric() const noexcept
+ {
+ if (!parent_.contains(key_))
+ {
+ return false;
+ }
+ return is_number();
+ }
+ #endif
+ };
+
+ using proxy_type = proxy<basic_json>;
+
+ union
+ {
+ common_storage common_stor_;
+ null_storage null_stor_;
+ bool_storage bool_stor_;
+ int64_storage int64_stor_;
+ uint64_storage uint64_stor_;
+ half_storage half_stor_;
+ double_storage double_stor_;
+ short_string_storage short_string_stor_;
+ long_string_storage long_string_stor_;
+ byte_string_storage byte_string_stor_;
+ array_storage array_stor_;
+ object_storage object_stor_;
+ empty_object_storage empty_object_stor_;
+ json_const_pointer_storage json_const_pointer_stor_;
+ };
+
+ void Destroy_()
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::long_string_value:
+ destroy_var<long_string_storage>();
+ break;
+ case json_storage_kind::byte_string_value:
+ destroy_var<byte_string_storage>();
+ break;
+ case json_storage_kind::array_value:
+ destroy_var<array_storage>();
+ break;
+ case json_storage_kind::object_value:
+ destroy_var<object_storage>();
+ break;
+ default:
+ break;
+ }
+ }
+
+ template <class VariantType, class... Args>
+ void construct(Args&&... args)
+ {
+ ::new (&cast<VariantType>()) VariantType(std::forward<Args>(args)...);
+ }
+
+ template <class T>
+ void destroy_var()
+ {
+ cast<T>().~T();
+ }
+
+ template <class T>
+ struct identity { using type = T*; };
+
+ template <class T>
+ T& cast()
+ {
+ return cast(identity<T>());
+ }
+
+ template <class T>
+ const T& cast() const
+ {
+ return cast(identity<T>());
+ }
+
+ null_storage& cast(identity<null_storage>)
+ {
+ return null_stor_;
+ }
+
+ const null_storage& cast(identity<null_storage>) const
+ {
+ return null_stor_;
+ }
+
+ empty_object_storage& cast(identity<empty_object_storage>)
+ {
+ return empty_object_stor_;
+ }
+
+ const empty_object_storage& cast(identity<empty_object_storage>) const
+ {
+ return empty_object_stor_;
+ }
+
+ bool_storage& cast(identity<bool_storage>)
+ {
+ return bool_stor_;
+ }
+
+ const bool_storage& cast(identity<bool_storage>) const
+ {
+ return bool_stor_;
+ }
+
+ int64_storage& cast(identity<int64_storage>)
+ {
+ return int64_stor_;
+ }
+
+ const int64_storage& cast(identity<int64_storage>) const
+ {
+ return int64_stor_;
+ }
+
+ uint64_storage& cast(identity<uint64_storage>)
+ {
+ return uint64_stor_;
+ }
+
+ const uint64_storage& cast(identity<uint64_storage>) const
+ {
+ return uint64_stor_;
+ }
+
+ half_storage& cast(identity<half_storage>)
+ {
+ return half_stor_;
+ }
+
+ const half_storage& cast(identity<half_storage>) const
+ {
+ return half_stor_;
+ }
+
+ double_storage& cast(identity<double_storage>)
+ {
+ return double_stor_;
+ }
+
+ const double_storage& cast(identity<double_storage>) const
+ {
+ return double_stor_;
+ }
+
+ short_string_storage& cast(identity<short_string_storage>)
+ {
+ return short_string_stor_;
+ }
+
+ const short_string_storage& cast(identity<short_string_storage>) const
+ {
+ return short_string_stor_;
+ }
+
+ long_string_storage& cast(identity<long_string_storage>)
+ {
+ return long_string_stor_;
+ }
+
+ const long_string_storage& cast(identity<long_string_storage>) const
+ {
+ return long_string_stor_;
+ }
+
+ byte_string_storage& cast(identity<byte_string_storage>)
+ {
+ return byte_string_stor_;
+ }
+
+ const byte_string_storage& cast(identity<byte_string_storage>) const
+ {
+ return byte_string_stor_;
+ }
+
+ object_storage& cast(identity<object_storage>)
+ {
+ return object_stor_;
+ }
+
+ const object_storage& cast(identity<object_storage>) const
+ {
+ return object_stor_;
+ }
+
+ array_storage& cast(identity<array_storage>)
+ {
+ return array_stor_;
+ }
+
+ const array_storage& cast(identity<array_storage>) const
+ {
+ return array_stor_;
+ }
+
+ json_const_pointer_storage& cast(identity<json_const_pointer_storage>)
+ {
+ return json_const_pointer_stor_;
+ }
+
+ const json_const_pointer_storage& cast(identity<json_const_pointer_storage>) const
+ {
+ return json_const_pointer_stor_;
+ }
+
+ template <class TypeA, class TypeB>
+ void swap_a_b(basic_json& other)
+ {
+ TypeA& curA = cast<TypeA>();
+ TypeB& curB = other.cast<TypeB>();
+ TypeB tmpB(std::move(curB));
+ other.construct<TypeA>(std::move(curA));
+ construct<TypeB>(std::move(tmpB));
+ }
+
+ template <class TypeA>
+ void swap_a(basic_json& other)
+ {
+ switch (other.storage_kind())
+ {
+ case json_storage_kind::null_value : swap_a_b<TypeA, null_storage>(other); break;
+ case json_storage_kind::empty_object_value : swap_a_b<TypeA, empty_object_storage>(other); break;
+ case json_storage_kind::bool_value : swap_a_b<TypeA, bool_storage>(other); break;
+ case json_storage_kind::int64_value : swap_a_b<TypeA, int64_storage>(other); break;
+ case json_storage_kind::uint64_value : swap_a_b<TypeA, uint64_storage>(other); break;
+ case json_storage_kind::half_value : swap_a_b<TypeA, half_storage>(other); break;
+ case json_storage_kind::double_value : swap_a_b<TypeA, double_storage>(other); break;
+ case json_storage_kind::short_string_value : swap_a_b<TypeA, short_string_storage>(other); break;
+ case json_storage_kind::long_string_value : swap_a_b<TypeA, long_string_storage>(other); break;
+ case json_storage_kind::byte_string_value : swap_a_b<TypeA, byte_string_storage>(other); break;
+ case json_storage_kind::array_value : swap_a_b<TypeA, array_storage>(other); break;
+ case json_storage_kind::object_value : swap_a_b<TypeA, object_storage>(other); break;
+ case json_storage_kind::json_const_pointer : swap_a_b<TypeA, json_const_pointer_storage>(other); break;
+ default:
+ JSONCONS_UNREACHABLE();
+ break;
+ }
+ }
+
+ void Init_(const basic_json& val)
+ {
+ switch (val.storage_kind())
+ {
+ case json_storage_kind::null_value:
+ construct<null_storage>(val.cast<null_storage>());
+ break;
+ case json_storage_kind::empty_object_value:
+ construct<empty_object_storage>(val.cast<empty_object_storage>());
+ break;
+ case json_storage_kind::bool_value:
+ construct<bool_storage>(val.cast<bool_storage>());
+ break;
+ case json_storage_kind::int64_value:
+ construct<int64_storage>(val.cast<int64_storage>());
+ break;
+ case json_storage_kind::uint64_value:
+ construct<uint64_storage>(val.cast<uint64_storage>());
+ break;
+ case json_storage_kind::half_value:
+ construct<half_storage>(val.cast<half_storage>());
+ break;
+ case json_storage_kind::double_value:
+ construct<double_storage>(val.cast<double_storage>());
+ break;
+ case json_storage_kind::short_string_value:
+ construct<short_string_storage>(val.cast<short_string_storage>());
+ break;
+ case json_storage_kind::long_string_value:
+ construct<long_string_storage>(val.cast<long_string_storage>());
+ break;
+ case json_storage_kind::byte_string_value:
+ construct<byte_string_storage>(val.cast<byte_string_storage>());
+ break;
+ case json_storage_kind::object_value:
+ construct<object_storage>(val.cast<object_storage>());
+ break;
+ case json_storage_kind::array_value:
+ construct<array_storage>(val.cast<array_storage>());
+ break;
+ case json_storage_kind::json_const_pointer:
+ construct<json_const_pointer_storage>(val.cast<json_const_pointer_storage>());
+ break;
+ default:
+ break;
+ }
+ }
+
+ void Init_(const basic_json& val, const Allocator& a)
+ {
+ switch (val.storage_kind())
+ {
+ case json_storage_kind::null_value:
+ case json_storage_kind::empty_object_value:
+ case json_storage_kind::bool_value:
+ case json_storage_kind::int64_value:
+ case json_storage_kind::uint64_value:
+ case json_storage_kind::half_value:
+ case json_storage_kind::double_value:
+ case json_storage_kind::short_string_value:
+ case json_storage_kind::json_const_pointer:
+ Init_(val);
+ break;
+ case json_storage_kind::long_string_value:
+ construct<long_string_storage>(val.cast<long_string_storage>(),a);
+ break;
+ case json_storage_kind::byte_string_value:
+ construct<byte_string_storage>(val.cast<byte_string_storage>(),a);
+ break;
+ case json_storage_kind::array_value:
+ construct<array_storage>(val.cast<array_storage>(),a);
+ break;
+ case json_storage_kind::object_value:
+ construct<object_storage>(val.cast<object_storage>(),a);
+ break;
+ default:
+ break;
+ }
+ }
+
+ void Init_rv_(basic_json&& val) noexcept
+ {
+ switch (val.storage_kind())
+ {
+ case json_storage_kind::null_value:
+ case json_storage_kind::empty_object_value:
+ case json_storage_kind::half_value:
+ case json_storage_kind::double_value:
+ case json_storage_kind::int64_value:
+ case json_storage_kind::uint64_value:
+ case json_storage_kind::bool_value:
+ case json_storage_kind::short_string_value:
+ case json_storage_kind::json_const_pointer:
+ Init_(val);
+ break;
+ case json_storage_kind::long_string_value:
+ case json_storage_kind::byte_string_value:
+ case json_storage_kind::array_value:
+ case json_storage_kind::object_value:
+ {
+ construct<null_storage>();
+ swap(val);
+ break;
+ }
+ default:
+ JSONCONS_UNREACHABLE();
+ break;
+ }
+ }
+
+ void Init_rv_(basic_json&& val, const Allocator&, std::true_type) noexcept
+ {
+ Init_rv_(std::forward<basic_json>(val));
+ }
+
+ void Init_rv_(basic_json&& val, const Allocator& a, std::false_type) noexcept
+ {
+ switch (val.storage_kind())
+ {
+ case json_storage_kind::null_value:
+ case json_storage_kind::empty_object_value:
+ case json_storage_kind::half_value:
+ case json_storage_kind::double_value:
+ case json_storage_kind::int64_value:
+ case json_storage_kind::uint64_value:
+ case json_storage_kind::bool_value:
+ case json_storage_kind::short_string_value:
+ case json_storage_kind::json_const_pointer:
+ Init_(std::forward<basic_json>(val));
+ break;
+ case json_storage_kind::long_string_value:
+ {
+ if (a == val.cast<long_string_storage>().get_allocator())
+ {
+ Init_rv_(std::forward<basic_json>(val), a, std::true_type());
+ }
+ else
+ {
+ Init_(val,a);
+ }
+ break;
+ }
+ case json_storage_kind::byte_string_value:
+ {
+ if (a == val.cast<byte_string_storage>().get_allocator())
+ {
+ Init_rv_(std::forward<basic_json>(val), a, std::true_type());
+ }
+ else
+ {
+ Init_(val,a);
+ }
+ break;
+ }
+ case json_storage_kind::object_value:
+ {
+ if (a == val.cast<object_storage>().get_allocator())
+ {
+ Init_rv_(std::forward<basic_json>(val), a, std::true_type());
+ }
+ else
+ {
+ Init_(val,a);
+ }
+ break;
+ }
+ case json_storage_kind::array_value:
+ {
+ if (a == val.cast<array_storage>().get_allocator())
+ {
+ Init_rv_(std::forward<basic_json>(val), a, std::true_type());
+ }
+ else
+ {
+ Init_(val,a);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ basic_json& evaluate_with_default()
+ {
+ return *this;
+ }
+
+ basic_json& evaluate(const string_view_type& name)
+ {
+ return at(name);
+ }
+
+ const basic_json& evaluate(const string_view_type& name) const
+ {
+ return at(name);
+ }
+
+ public:
+
+ basic_json& evaluate()
+ {
+ return *this;
+ }
+
+ const basic_json& evaluate() const
+ {
+ return *this;
+ }
+
+ basic_json& operator=(const basic_json& val)
+ {
+ if (this != &val)
+ {
+ Destroy_();
+ Init_(val);
+ }
+ return *this;
+ }
+
+ basic_json& operator=(basic_json&& val) noexcept
+ {
+ if (this !=&val)
+ {
+ swap(val);
+ }
+ return *this;
+ }
+
+ json_storage_kind storage_kind() const
+ {
+ // It is legal to access 'common_stor_.storage_kind_' even though
+ // common_stor_ is not the active member of the union because 'storage_kind_'
+ // is a part of the common initial sequence of all union members
+ // as defined in 11.4-25 of the Standard.
+ return static_cast<json_storage_kind>(common_stor_.storage_kind_);
+ }
+
+ json_type type() const
+ {
+ switch(storage_kind())
+ {
+ case json_storage_kind::null_value:
+ return json_type::null_value;
+ case json_storage_kind::bool_value:
+ return json_type::bool_value;
+ case json_storage_kind::int64_value:
+ return json_type::int64_value;
+ case json_storage_kind::uint64_value:
+ return json_type::uint64_value;
+ case json_storage_kind::half_value:
+ return json_type::half_value;
+ case json_storage_kind::double_value:
+ return json_type::double_value;
+ case json_storage_kind::short_string_value:
+ case json_storage_kind::long_string_value:
+ return json_type::string_value;
+ case json_storage_kind::byte_string_value:
+ return json_type::byte_string_value;
+ case json_storage_kind::array_value:
+ return json_type::array_value;
+ case json_storage_kind::empty_object_value:
+ case json_storage_kind::object_value:
+ return json_type::object_value;
+ case json_storage_kind::json_const_pointer:
+ return cast<json_const_pointer_storage>().value()->type();
+ default:
+ JSONCONS_UNREACHABLE();
+ break;
+ }
+ }
+
+ semantic_tag tag() const
+ {
+ // It is legal to access 'common_stor_.tag_' even though
+ // common_stor_ is not the active member of the union because 'tag_'
+ // is a part of the common initial sequence of all union members
+ // as defined in 11.4-25 of the Standard.
+ switch(storage_kind())
+ {
+ case json_storage_kind::json_const_pointer:
+ return cast<json_const_pointer_storage>().value()->tag();
+ default:
+ return common_stor_.tag_;
+ }
+ }
+
+ std::size_t size() const
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::array_value:
+ return cast<array_storage>().value().size();
+ case json_storage_kind::empty_object_value:
+ return 0;
+ case json_storage_kind::object_value:
+ return cast<object_storage>().value().size();
+ case json_storage_kind::json_const_pointer:
+ return cast<json_const_pointer_storage>().value()->size();
+ default:
+ return 0;
+ }
+ }
+
+ string_view_type as_string_view() const
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::short_string_value:
+ return string_view_type(cast<short_string_storage>().data(),cast<short_string_storage>().length());
+ case json_storage_kind::long_string_value:
+ return string_view_type(cast<long_string_storage>().data(),cast<long_string_storage>().length());
+ case json_storage_kind::json_const_pointer:
+ return cast<json_const_pointer_storage>().value()->as_string_view();
+ default:
+ JSONCONS_THROW(json_runtime_error<std::domain_error>("Not a string"));
+ }
+ }
+
+ template <typename BAllocator=std::allocator<uint8_t>>
+ basic_byte_string<BAllocator> as_byte_string() const
+ {
+ using byte_string_type = basic_byte_string<BAllocator>;
+ converter<byte_string_type> convert;
+ std::error_code ec;
+
+ switch (storage_kind())
+ {
+ case json_storage_kind::short_string_value:
+ case json_storage_kind::long_string_value:
+ {
+ byte_string_type v = convert.from(as_string_view(),tag(),ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec));
+ }
+ return v;
+ }
+ case json_storage_kind::byte_string_value:
+ return basic_byte_string<BAllocator>(cast<byte_string_storage>().data(),cast<byte_string_storage>().length());
+ case json_storage_kind::json_const_pointer:
+ return cast<json_const_pointer_storage>().value()->as_byte_string();
+ default:
+ JSONCONS_THROW(json_runtime_error<std::domain_error>("Not a byte string"));
+ }
+ }
+
+ byte_string_view as_byte_string_view() const
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::byte_string_value:
+ return byte_string_view(cast<byte_string_storage>().data(),cast<byte_string_storage>().length());
+ case json_storage_kind::json_const_pointer:
+ return cast<json_const_pointer_storage>().value()->as_byte_string_view();
+ default:
+ JSONCONS_THROW(json_runtime_error<std::domain_error>("Not a byte string"));
+ }
+ }
+
+ int compare(const basic_json& rhs) const noexcept
+ {
+ if (this == &rhs)
+ {
+ return 0;
+ }
+ switch (storage_kind())
+ {
+ case json_storage_kind::json_const_pointer:
+ switch (rhs.storage_kind())
+ {
+ case json_storage_kind::json_const_pointer:
+ return (cast<json_const_pointer_storage>().value())->compare(*(rhs.cast<json_const_pointer_storage>().value()));
+ default:
+ return (cast<json_const_pointer_storage>().value())->compare(rhs);
+ }
+ break;
+ case json_storage_kind::null_value:
+ return static_cast<int>(storage_kind()) - static_cast<int>((int)rhs.storage_kind());
+ case json_storage_kind::empty_object_value:
+ switch (rhs.storage_kind())
+ {
+ case json_storage_kind::empty_object_value:
+ return 0;
+ case json_storage_kind::object_value:
+ return rhs.empty() ? 0 : -1;
+ case json_storage_kind::json_const_pointer:
+ return compare(*(rhs.cast<json_const_pointer_storage>().value()));
+ default:
+ return static_cast<int>(storage_kind()) - static_cast<int>((int)rhs.storage_kind());
+ }
+ break;
+ case json_storage_kind::bool_value:
+ switch (rhs.storage_kind())
+ {
+ case json_storage_kind::bool_value:
+ return static_cast<int>(cast<bool_storage>().value()) - static_cast<int>(rhs.cast<bool_storage>().value());
+ case json_storage_kind::json_const_pointer:
+ return compare(*(rhs.cast<json_const_pointer_storage>().value()));
+ default:
+ return static_cast<int>(storage_kind()) - static_cast<int>((int)rhs.storage_kind());
+ }
+ break;
+ case json_storage_kind::int64_value:
+ switch (rhs.storage_kind())
+ {
+ case json_storage_kind::int64_value:
+ {
+ if (cast<int64_storage>().value() == rhs.cast<int64_storage>().value())
+ return 0;
+ return cast<int64_storage>().value() < rhs.cast<int64_storage>().value() ? -1 : 1;
+ }
+ case json_storage_kind::uint64_value:
+ if (cast<int64_storage>().value() < 0)
+ return -1;
+ else if (static_cast<uint64_t>(cast<int64_storage>().value()) == rhs.cast<uint64_storage>().value())
+ return 0;
+ else
+ return static_cast<uint64_t>(cast<int64_storage>().value()) < rhs.cast<uint64_storage>().value() ? -1 : 1;
+ case json_storage_kind::double_value:
+ {
+ double r = static_cast<double>(cast<int64_storage>().value()) - rhs.cast<double_storage>().value();
+ return r == 0.0 ? 0 : (r < 0.0 ? -1 : 1);
+ }
+ case json_storage_kind::json_const_pointer:
+ return compare(*(rhs.cast<json_const_pointer_storage>().value()));
+ default:
+ return static_cast<int>(storage_kind()) - static_cast<int>((int)rhs.storage_kind());
+ }
+ break;
+ case json_storage_kind::uint64_value:
+ switch (rhs.storage_kind())
+ {
+ case json_storage_kind::int64_value:
+ if (rhs.cast<int64_storage>().value() < 0)
+ return 1;
+ else if (cast<uint64_storage>().value() == static_cast<uint64_t>(rhs.cast<int64_storage>().value()))
+ return 0;
+ else
+ return cast<uint64_storage>().value() < static_cast<uint64_t>(rhs.cast<int64_storage>().value()) ? -1 : 1;
+ case json_storage_kind::uint64_value:
+ if (cast<uint64_storage>().value() == static_cast<uint64_t>(rhs.cast<int64_storage>().value()))
+ return 0;
+ else
+ return cast<uint64_storage>().value() < static_cast<uint64_t>(rhs.cast<int64_storage>().value()) ? -1 : 1;
+ case json_storage_kind::double_value:
+ {
+ auto r = static_cast<double>(cast<uint64_storage>().value()) - rhs.cast<double_storage>().value();
+ return r == 0 ? 0 : (r < 0.0 ? -1 : 1);
+ }
+ case json_storage_kind::json_const_pointer:
+ return compare(*(rhs.cast<json_const_pointer_storage>().value()));
+ default:
+ return static_cast<int>(storage_kind()) - static_cast<int>((int)rhs.storage_kind());
+ }
+ break;
+ case json_storage_kind::double_value:
+ switch (rhs.storage_kind())
+ {
+ case json_storage_kind::int64_value:
+ {
+ auto r = cast<double_storage>().value() - static_cast<double>(rhs.cast<int64_storage>().value());
+ return r == 0 ? 0 : (r < 0.0 ? -1 : 1);
+ }
+ case json_storage_kind::uint64_value:
+ {
+ auto r = cast<double_storage>().value() - static_cast<double>(rhs.cast<uint64_storage>().value());
+ return r == 0 ? 0 : (r < 0.0 ? -1 : 1);
+ }
+ case json_storage_kind::double_value:
+ {
+ auto r = cast<double_storage>().value() - rhs.cast<double_storage>().value();
+ return r == 0 ? 0 : (r < 0.0 ? -1 : 1);
+ }
+ case json_storage_kind::json_const_pointer:
+ return compare(*(rhs.cast<json_const_pointer_storage>().value()));
+ default:
+ return static_cast<int>(storage_kind()) - static_cast<int>((int)rhs.storage_kind());
+ }
+ break;
+ case json_storage_kind::short_string_value:
+ case json_storage_kind::long_string_value:
+ switch (rhs.storage_kind())
+ {
+ case json_storage_kind::short_string_value:
+ return as_string_view().compare(rhs.as_string_view());
+ case json_storage_kind::long_string_value:
+ return as_string_view().compare(rhs.as_string_view());
+ case json_storage_kind::json_const_pointer:
+ return compare(*(rhs.cast<json_const_pointer_storage>().value()));
+ default:
+ return static_cast<int>(storage_kind()) - static_cast<int>((int)rhs.storage_kind());
+ }
+ break;
+ case json_storage_kind::byte_string_value:
+ switch (rhs.storage_kind())
+ {
+ case json_storage_kind::byte_string_value:
+ {
+ return as_byte_string_view().compare(rhs.as_byte_string_view());
+ }
+ case json_storage_kind::json_const_pointer:
+ return compare(*(rhs.cast<json_const_pointer_storage>().value()));
+ default:
+ return static_cast<int>(storage_kind()) - static_cast<int>((int)rhs.storage_kind());
+ }
+ break;
+ case json_storage_kind::array_value:
+ switch (rhs.storage_kind())
+ {
+ case json_storage_kind::array_value:
+ {
+ if (cast<array_storage>().value() == rhs.cast<array_storage>().value())
+ return 0;
+ else
+ return cast<array_storage>().value() < rhs.cast<array_storage>().value() ? -1 : 1;
+ }
+ case json_storage_kind::json_const_pointer:
+ return compare(*(rhs.cast<json_const_pointer_storage>().value()));
+ default:
+ return static_cast<int>(storage_kind()) - static_cast<int>((int)rhs.storage_kind());
+ }
+ break;
+ case json_storage_kind::object_value:
+ switch (rhs.storage_kind())
+ {
+ case json_storage_kind::empty_object_value:
+ return empty() ? 0 : 1;
+ case json_storage_kind::object_value:
+ {
+ if (cast<object_storage>().value() == rhs.cast<object_storage>().value())
+ return 0;
+ else
+ return cast<object_storage>().value() < rhs.cast<object_storage>().value() ? -1 : 1;
+ }
+ case json_storage_kind::json_const_pointer:
+ return compare(*(rhs.cast<json_const_pointer_storage>().value()));
+ default:
+ return static_cast<int>(storage_kind()) - static_cast<int>((int)rhs.storage_kind());
+ }
+ break;
+ default:
+ JSONCONS_UNREACHABLE();
+ break;
+ }
+ }
+
+ void swap(basic_json& other) noexcept
+ {
+ if (this == &other)
+ {
+ return;
+ }
+
+ switch (storage_kind())
+ {
+ case json_storage_kind::null_value: swap_a<null_storage>(other); break;
+ case json_storage_kind::empty_object_value : swap_a<empty_object_storage>(other); break;
+ case json_storage_kind::bool_value: swap_a<bool_storage>(other); break;
+ case json_storage_kind::int64_value: swap_a<int64_storage>(other); break;
+ case json_storage_kind::uint64_value: swap_a<uint64_storage>(other); break;
+ case json_storage_kind::half_value: swap_a<half_storage>(other); break;
+ case json_storage_kind::double_value: swap_a<double_storage>(other); break;
+ case json_storage_kind::short_string_value: swap_a<short_string_storage>(other); break;
+ case json_storage_kind::long_string_value: swap_a<long_string_storage>(other); break;
+ case json_storage_kind::byte_string_value: swap_a<byte_string_storage>(other); break;
+ case json_storage_kind::array_value: swap_a<array_storage>(other); break;
+ case json_storage_kind::object_value: swap_a<object_storage>(other); break;
+ case json_storage_kind::json_const_pointer: swap_a<json_const_pointer_storage>(other); break;
+ default:
+ JSONCONS_UNREACHABLE();
+ break;
+ }
+ }
+ // from string
+
+ template <class Source>
+ static
+ typename std::enable_if<type_traits::is_sequence_of<Source,char_type>::value,basic_json>::type
+ parse(const Source& s,
+ const basic_json_decode_options<char_type>& options = basic_json_decode_options<CharT>(),
+ std::function<bool(json_errc,const ser_context&)> err_handler = default_json_parsing())
+ {
+ json_decoder<basic_json> decoder;
+ basic_json_parser<char_type> parser(options,err_handler);
+
+ auto r = unicode_traits::detect_encoding_from_bom(s.data(), s.size());
+ if (!(r.encoding == unicode_traits::encoding_kind::utf8 || r.encoding == unicode_traits::encoding_kind::undetected))
+ {
+ JSONCONS_THROW(ser_error(json_errc::illegal_unicode_character,parser.line(),parser.column()));
+ }
+ std::size_t offset = (r.ptr - s.data());
+ parser.update(s.data()+offset,s.size()-offset);
+ parser.parse_some(decoder);
+ parser.finish_parse(decoder);
+ parser.check_done();
+ if (!decoder.is_valid())
+ {
+ JSONCONS_THROW(ser_error(json_errc::source_error, "Failed to parse json string"));
+ }
+ return decoder.get_result();
+ }
+
+ template <class Source>
+ static
+ typename std::enable_if<type_traits::is_sequence_of<Source,char_type>::value,basic_json>::type
+ parse(const Source& s,
+ std::function<bool(json_errc,const ser_context&)> err_handler)
+ {
+ return parse(s, basic_json_decode_options<CharT>(), err_handler);
+ }
+
+ static basic_json parse(const char_type* s,
+ const basic_json_decode_options<char_type>& options = basic_json_decode_options<char_type>(),
+ std::function<bool(json_errc,const ser_context&)> err_handler = default_json_parsing())
+ {
+ return parse(jsoncons::basic_string_view<char_type>(s), options, err_handler);
+ }
+
+ static basic_json parse(const char_type* s,
+ std::function<bool(json_errc,const ser_context&)> err_handler)
+ {
+ return parse(jsoncons::basic_string_view<char_type>(s), basic_json_decode_options<char_type>(), err_handler);
+ }
+
+ // from stream
+
+ static basic_json parse(std::basic_istream<char_type>& is,
+ const basic_json_decode_options<char_type>& options = basic_json_decode_options<CharT>(),
+ std::function<bool(json_errc,const ser_context&)> err_handler = default_json_parsing())
+ {
+ json_decoder<basic_json> visitor;
+ basic_json_reader<char_type,stream_source<char_type>> reader(is, visitor, options, err_handler);
+ reader.read_next();
+ reader.check_done();
+ if (!visitor.is_valid())
+ {
+ JSONCONS_THROW(ser_error(json_errc::source_error, "Failed to parse json stream"));
+ }
+ return visitor.get_result();
+ }
+
+ static basic_json parse(std::basic_istream<char_type>& is, std::function<bool(json_errc,const ser_context&)> err_handler)
+ {
+ return parse(is, basic_json_decode_options<CharT>(), err_handler);
+ }
+
+ // from iterator
+
+ template <class InputIt>
+ static basic_json parse(InputIt first, InputIt last,
+ const basic_json_decode_options<char_type>& options = basic_json_decode_options<CharT>(),
+ std::function<bool(json_errc,const ser_context&)> err_handler = default_json_parsing())
+ {
+ json_decoder<basic_json> visitor;
+ basic_json_reader<char_type,iterator_source<InputIt>> reader(iterator_source<InputIt>(std::forward<InputIt>(first),std::forward<InputIt>(last)), visitor, options, err_handler);
+ reader.read_next();
+ reader.check_done();
+ if (!visitor.is_valid())
+ {
+ JSONCONS_THROW(ser_error(json_errc::source_error, "Failed to parse json from iterator pair"));
+ }
+ return visitor.get_result();
+ }
+
+ template <class InputIt>
+ static basic_json parse(InputIt first, InputIt last,
+ std::function<bool(json_errc,const ser_context&)> err_handler)
+ {
+ return parse(first, last, basic_json_decode_options<CharT>(), err_handler);
+ }
+
+ static basic_json make_array()
+ {
+ return basic_json(array());
+ }
+
+ static basic_json make_array(const array& a)
+ {
+ return basic_json(a);
+ }
+
+ static basic_json make_array(const array& a, allocator_type alloc)
+ {
+ return basic_json(a, semantic_tag::none, alloc);
+ }
+
+ static basic_json make_array(std::initializer_list<basic_json> init, const Allocator& alloc = Allocator())
+ {
+ return array(std::move(init),alloc);
+ }
+
+ static basic_json make_array(std::size_t n, const Allocator& alloc = Allocator())
+ {
+ return array(n,alloc);
+ }
+
+ template <class T>
+ static basic_json make_array(std::size_t n, const T& val, const Allocator& alloc = Allocator())
+ {
+ return basic_json::array(n, val,alloc);
+ }
+
+ template <std::size_t dim>
+ static typename std::enable_if<dim==1,basic_json>::type make_array(std::size_t n)
+ {
+ return array(n);
+ }
+
+ template <std::size_t dim, class T>
+ static typename std::enable_if<dim==1,basic_json>::type make_array(std::size_t n, const T& val, const Allocator& alloc = Allocator())
+ {
+ return array(n,val,alloc);
+ }
+
+ template <std::size_t dim, typename... Args>
+ static typename std::enable_if<(dim>1),basic_json>::type make_array(std::size_t n, Args... args)
+ {
+ const size_t dim1 = dim - 1;
+
+ basic_json val = make_array<dim1>(std::forward<Args>(args)...);
+ val.resize(n);
+ for (std::size_t i = 0; i < n; ++i)
+ {
+ val[i] = make_array<dim1>(std::forward<Args>(args)...);
+ }
+ return val;
+ }
+
+ static const basic_json& null()
+ {
+ static const basic_json a_null = basic_json(null_type(), semantic_tag::none);
+ return a_null;
+ }
+
+ basic_json()
+ {
+ construct<empty_object_storage>(semantic_tag::none);
+ }
+
+ basic_json(semantic_tag tag)
+ {
+ construct<empty_object_storage>(tag);
+ }
+
+ #if !defined(JSONCONS_NO_DEPRECATED)
+
+ JSONCONS_DEPRECATED_MSG("Instead, use basic_json(json_object_t,semantic_tag,const Allocator&)")
+ explicit basic_json(const Allocator& alloc, semantic_tag tag = semantic_tag::none)
+ {
+ construct<object_storage>(object(alloc), tag);
+ }
+
+ #endif
+
+ basic_json(const basic_json& other)
+ {
+ Init_(other);
+ }
+
+ basic_json(const basic_json& other, const Allocator& alloc)
+ {
+ Init_(other,alloc);
+ }
+
+ basic_json(basic_json&& other) noexcept
+ {
+ Init_rv_(std::forward<basic_json>(other));
+ }
+
+ basic_json(basic_json&& other, const Allocator&) noexcept
+ {
+ Init_rv_(std::forward<basic_json>(other));
+ }
+
+ explicit basic_json(json_object_arg_t,
+ semantic_tag tag = semantic_tag::none,
+ const Allocator& alloc = Allocator())
+ {
+ construct<object_storage>(object(alloc), tag);
+ }
+
+ template<class InputIt>
+ basic_json(json_object_arg_t,
+ InputIt first, InputIt last,
+ semantic_tag tag = semantic_tag::none,
+ const Allocator& alloc = Allocator())
+ {
+ construct<object_storage>(object(first,last,alloc), tag);
+ }
+
+ basic_json(json_object_arg_t,
+ std::initializer_list<std::pair<std::basic_string<char_type>,basic_json>> init,
+ semantic_tag tag = semantic_tag::none,
+ const Allocator& alloc = Allocator())
+ {
+ construct<object_storage>(object(init,alloc), tag);
+ }
+
+ explicit basic_json(json_array_arg_t,
+ semantic_tag tag = semantic_tag::none,
+ const Allocator& alloc = Allocator())
+ {
+ construct<array_storage>(array(alloc), tag);
+ }
+
+ template<class InputIt>
+ basic_json(json_array_arg_t,
+ InputIt first, InputIt last,
+ semantic_tag tag = semantic_tag::none,
+ const Allocator& alloc = Allocator())
+ {
+ construct<array_storage>(array(first,last,alloc), tag);
+ }
+
+ basic_json(json_array_arg_t,
+ std::initializer_list<basic_json> init,
+ semantic_tag tag = semantic_tag::none,
+ const Allocator& alloc = Allocator())
+ {
+ construct<array_storage>(array(init,alloc), tag);
+ }
+
+ basic_json(json_const_pointer_arg_t, const basic_json* p) noexcept
+ {
+ if (p == nullptr)
+ {
+ construct<null_storage>(semantic_tag::none);
+ }
+ else
+ {
+ construct<json_const_pointer_storage>(p);
+ }
+ }
+
+ basic_json(const array& val, semantic_tag tag = semantic_tag::none)
+ {
+ construct<array_storage>(val, tag);
+ }
+
+ basic_json(array&& val, semantic_tag tag = semantic_tag::none)
+ {
+ construct<array_storage>(std::forward<array>(val), tag);
+ }
+
+ basic_json(const object& val, semantic_tag tag = semantic_tag::none)
+ {
+ construct<object_storage>(val, tag);
+ }
+
+ basic_json(object&& val, semantic_tag tag = semantic_tag::none)
+ {
+ construct<object_storage>(std::forward<object>(val), tag);
+ }
+
+ template <class T,
+ class = typename std::enable_if<!is_proxy_of<T,basic_json>::value && !type_traits::is_basic_json<T>::value>::type>
+ basic_json(const T& val)
+ : basic_json(json_type_traits<basic_json,T>::to_json(val))
+ {
+ }
+
+ template <class T,
+ class = typename std::enable_if<!is_proxy_of<T,basic_json>::value && !type_traits::is_basic_json<T>::value>::type>
+ basic_json(const T& val, const Allocator& alloc)
+ : basic_json(json_type_traits<basic_json,T>::to_json(val,alloc))
+ {
+ }
+
+ basic_json(const char_type* s, semantic_tag tag = semantic_tag::none)
+ : basic_json(s, char_traits_type::length(s), tag)
+ {
+ }
+
+ basic_json(const char_type* s, const Allocator& alloc)
+ : basic_json(s, char_traits_type::length(s), semantic_tag::none, alloc)
+ {
+ }
+
+ basic_json(const char_type* s, std::size_t length, semantic_tag tag = semantic_tag::none)
+ {
+ if (length <= short_string_storage::max_length)
+ {
+ construct<short_string_storage>(tag, s, static_cast<uint8_t>(length));
+ }
+ else
+ {
+ construct<long_string_storage>(tag, s, length, char_allocator_type());
+ }
+ }
+
+ basic_json(const char_type* s, std::size_t length, semantic_tag tag, const Allocator& alloc)
+ {
+ if (length <= short_string_storage::max_length)
+ {
+ construct<short_string_storage>(tag, s, static_cast<uint8_t>(length));
+ }
+ else
+ {
+ construct<long_string_storage>(tag, s, length, alloc);
+ }
+ }
+
+ basic_json(half_arg_t, uint16_t val, semantic_tag tag = semantic_tag::none)
+ {
+ construct<half_storage>(val, tag);
+ }
+
+ basic_json(double val, semantic_tag tag)
+ {
+ construct<double_storage>(val, tag);
+ }
+
+ template <class IntegerType>
+ basic_json(IntegerType val, semantic_tag tag,
+ typename std::enable_if<type_traits::is_unsigned_integer<IntegerType>::value && sizeof(IntegerType) <= sizeof(uint64_t), int>::type = 0)
+ {
+ construct<uint64_storage>(val, tag);
+ }
+
+ template <class IntegerType>
+ basic_json(IntegerType val, semantic_tag tag, Allocator,
+ typename std::enable_if<type_traits::is_unsigned_integer<IntegerType>::value && sizeof(IntegerType) <= sizeof(uint64_t), int>::type = 0)
+ {
+ construct<uint64_storage>(val, tag);
+ }
+
+ template <class IntegerType>
+ basic_json(IntegerType val, semantic_tag, Allocator alloc = Allocator(),
+ typename std::enable_if<type_traits::is_unsigned_integer<IntegerType>::value && sizeof(uint64_t) < sizeof(IntegerType), int>::type = 0)
+ {
+ std::basic_string<CharT> s;
+ jsoncons::detail::from_integer(val, s);
+ if (s.length() <= short_string_storage::max_length)
+ {
+ construct<short_string_storage>(semantic_tag::bigint, s.data(), static_cast<uint8_t>(s.length()));
+ }
+ else
+ {
+ construct<long_string_storage>(semantic_tag::bigint, s.data(), s.length(), alloc);
+ }
+ }
+
+ template <class IntegerType>
+ basic_json(IntegerType val, semantic_tag tag,
+ typename std::enable_if<type_traits::is_signed_integer<IntegerType>::value && sizeof(IntegerType) <= sizeof(int64_t),int>::type = 0)
+ {
+ construct<int64_storage>(val, tag);
+ }
+
+ template <class IntegerType>
+ basic_json(IntegerType val, semantic_tag tag, Allocator,
+ typename std::enable_if<type_traits::is_signed_integer<IntegerType>::value && sizeof(IntegerType) <= sizeof(int64_t),int>::type = 0)
+ {
+ construct<int64_storage>(val, tag);
+ }
+
+ template <class IntegerType>
+ basic_json(IntegerType val, semantic_tag, Allocator alloc = Allocator(),
+ typename std::enable_if<type_traits::is_signed_integer<IntegerType>::value && sizeof(int64_t) < sizeof(IntegerType),int>::type = 0)
+ {
+ std::basic_string<CharT> s;
+ jsoncons::detail::from_integer(val, s);
+ if (s.length() <= short_string_storage::max_length)
+ {
+ construct<short_string_storage>(semantic_tag::bigint, s.data(), static_cast<uint8_t>(s.length()));
+ }
+ else
+ {
+ construct<long_string_storage>(semantic_tag::bigint, s.data(), s.length(), alloc);
+ }
+ }
+
+ basic_json(const string_view_type& sv, semantic_tag tag)
+ : basic_json(sv.data(), sv.length(), tag)
+ {
+ }
+
+ basic_json(null_type, semantic_tag tag)
+ {
+ construct<null_storage>(tag);
+ }
+
+ basic_json(bool val, semantic_tag tag)
+ {
+ construct<bool_storage>(val,tag);
+ }
+
+ basic_json(const string_view_type& sv, semantic_tag tag, const Allocator& alloc)
+ : basic_json(sv.data(), sv.length(), tag, alloc)
+ {
+ }
+
+ template <class Source>
+ basic_json(byte_string_arg_t, const Source& source,
+ semantic_tag tag = semantic_tag::none,
+ const Allocator& alloc = Allocator(),
+ typename std::enable_if<type_traits::is_byte_sequence<Source>::value,int>::type = 0)
+ {
+ auto bytes = jsoncons::span<const uint8_t>(reinterpret_cast<const uint8_t*>(source.data()), source.size());
+ construct<byte_string_storage>(tag, bytes.data(), bytes.size(), 0, alloc);
+ }
+
+ template <class Source>
+ basic_json(byte_string_arg_t, const Source& source,
+ uint64_t ext_tag,
+ const Allocator& alloc = Allocator(),
+ typename std::enable_if<type_traits::is_byte_sequence<Source>::value,int>::type = 0)
+ {
+ auto bytes = jsoncons::span<const uint8_t>(reinterpret_cast<const uint8_t*>(source.data()), source.size());
+ construct<byte_string_storage>(semantic_tag::ext, bytes.data(), bytes.size(), ext_tag, alloc);
+ }
+
+ ~basic_json() noexcept
+ {
+ Destroy_();
+ }
+
+ template <class T>
+ basic_json& operator=(const T& val)
+ {
+ *this = json_type_traits<basic_json,T>::to_json(val);
+ return *this;
+ }
+
+ basic_json& operator=(const char_type* s)
+ {
+ *this = basic_json(s, char_traits_type::length(s), semantic_tag::none);
+ return *this;
+ }
+
+ basic_json& operator[](std::size_t i)
+ {
+ return at(i);
+ }
+
+ const basic_json& operator[](std::size_t i) const
+ {
+ return at(i);
+ }
+
+ proxy_type operator[](const string_view_type& name)
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::empty_object_value:
+ create_object_implicitly();
+ JSONCONS_FALLTHROUGH;
+ case json_storage_kind::object_value:
+ return proxy_type(*this, name);
+ break;
+ default:
+ JSONCONS_THROW(not_an_object(name.data(),name.length()));
+ break;
+ }
+ }
+
+ const basic_json& operator[](const string_view_type& name) const
+ {
+ return at(name);
+ }
+
+
+ template <class Container>
+ typename std::enable_if<type_traits::is_back_insertable_char_container<Container>::value>::type
+ dump(Container& s,
+ const basic_json_encode_options<char_type>& options = basic_json_encode_options<CharT>()) const
+ {
+ std::error_code ec;
+ dump(s, options, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec));
+ }
+ }
+
+ template <class Container>
+ typename std::enable_if<type_traits::is_back_insertable_char_container<Container>::value>::type
+ dump_pretty(Container& s,
+ const basic_json_encode_options<char_type>& options = basic_json_encode_options<CharT>()) const
+ {
+ std::error_code ec;
+ dump_pretty(s, options, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec));
+ }
+ }
+
+ void dump(std::basic_ostream<char_type>& os,
+ const basic_json_encode_options<char_type>& options = basic_json_encode_options<CharT>()) const
+ {
+ std::error_code ec;
+ dump(os, options, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec));
+ }
+ }
+
+ void dump_pretty(std::basic_ostream<char_type>& os,
+ const basic_json_encode_options<char_type>& options = basic_json_encode_options<CharT>()) const
+ {
+ std::error_code ec;
+ dump_pretty(os, options, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec));
+ }
+ }
+
+ // Legacy
+
+ template <class Container>
+ typename std::enable_if<type_traits::is_back_insertable_char_container<Container>::value>::type
+ dump(Container& s,
+ indenting line_indent) const
+ {
+ std::error_code ec;
+
+ dump(s, line_indent, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec));
+ }
+ }
+
+ template <class Container>
+ typename std::enable_if<type_traits::is_back_insertable_char_container<Container>::value>::type
+ dump(Container& s,
+ const basic_json_encode_options<char_type>& options,
+ indenting line_indent) const
+ {
+ std::error_code ec;
+
+ dump(s, options, line_indent, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec));
+ }
+ }
+
+ void dump(std::basic_ostream<char_type>& os,
+ indenting line_indent) const
+ {
+ std::error_code ec;
+
+ dump(os, line_indent, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec));
+ }
+ }
+
+ void dump(std::basic_ostream<char_type>& os,
+ const basic_json_encode_options<char_type>& options,
+ indenting line_indent) const
+ {
+ std::error_code ec;
+
+ dump(os, options, line_indent, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec));
+ }
+ }
+
+ // end legacy
+
+ void dump(basic_json_visitor<char_type>& visitor) const
+ {
+ std::error_code ec;
+ dump(visitor, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec));
+ }
+ }
+
+ // dump
+ template <class Container>
+ typename std::enable_if<type_traits::is_back_insertable_char_container<Container>::value>::type
+ dump(Container& s,
+ const basic_json_encode_options<char_type>& options,
+ std::error_code& ec) const
+ {
+ basic_compact_json_encoder<char_type,jsoncons::string_sink<Container>> encoder(s, options);
+ dump(encoder, ec);
+ }
+
+ template <class Container>
+ typename std::enable_if<type_traits::is_back_insertable_char_container<Container>::value>::type
+ dump(Container& s,
+ std::error_code& ec) const
+ {
+ basic_compact_json_encoder<char_type,jsoncons::string_sink<Container>> encoder(s);
+ dump(encoder, ec);
+ }
+
+ void dump(std::basic_ostream<char_type>& os,
+ const basic_json_encode_options<char_type>& options,
+ std::error_code& ec) const
+ {
+ basic_compact_json_encoder<char_type> encoder(os, options);
+ dump(encoder, ec);
+ }
+
+ void dump(std::basic_ostream<char_type>& os,
+ std::error_code& ec) const
+ {
+ basic_compact_json_encoder<char_type> encoder(os);
+ dump(encoder, ec);
+ }
+
+ // dump_pretty
+
+ template <class Container>
+ typename std::enable_if<type_traits::is_back_insertable_char_container<Container>::value>::type
+ dump_pretty(Container& s,
+ const basic_json_encode_options<char_type>& options,
+ std::error_code& ec) const
+ {
+ basic_json_encoder<char_type,jsoncons::string_sink<Container>> encoder(s, options);
+ dump(encoder, ec);
+ }
+
+ template <class Container>
+ typename std::enable_if<type_traits::is_back_insertable_char_container<Container>::value>::type
+ dump_pretty(Container& s,
+ std::error_code& ec) const
+ {
+ dump_pretty(s, basic_json_encode_options<char_type>(), ec);
+ }
+
+ void dump_pretty(std::basic_ostream<char_type>& os,
+ const basic_json_encode_options<char_type>& options,
+ std::error_code& ec) const
+ {
+ basic_json_encoder<char_type> encoder(os, options);
+ dump(encoder, ec);
+ }
+
+ void dump_pretty(std::basic_ostream<char_type>& os,
+ std::error_code& ec) const
+ {
+ dump_pretty(os, basic_json_encode_options<char_type>(), ec);
+ }
+
+ // legacy
+ template <class Container>
+ typename std::enable_if<type_traits::is_back_insertable_char_container<Container>::value>::type
+ dump(Container& s,
+ const basic_json_encode_options<char_type>& options,
+ indenting line_indent,
+ std::error_code& ec) const
+ {
+ if (line_indent == indenting::indent)
+ {
+ dump_pretty(s, options, ec);
+ }
+ else
+ {
+ dump(s, options, ec);
+ }
+ }
+
+ template <class Container>
+ typename std::enable_if<type_traits::is_back_insertable_char_container<Container>::value>::type
+ dump(Container& s,
+ indenting line_indent,
+ std::error_code& ec) const
+ {
+ if (line_indent == indenting::indent)
+ {
+ dump_pretty(s, ec);
+ }
+ else
+ {
+ dump(s, ec);
+ }
+ }
+
+ void dump(std::basic_ostream<char_type>& os,
+ const basic_json_encode_options<char_type>& options,
+ indenting line_indent,
+ std::error_code& ec) const
+ {
+ if (line_indent == indenting::indent)
+ {
+ dump_pretty(os, options, ec);
+ }
+ else
+ {
+ dump(os, options, ec);
+ }
+ }
+
+ void dump(std::basic_ostream<char_type>& os,
+ indenting line_indent,
+ std::error_code& ec) const
+ {
+ if (line_indent == indenting::indent)
+ {
+ dump_pretty(os, ec);
+ }
+ else
+ {
+ dump(os, ec);
+ }
+ }
+ // end legacy
+
+ void dump(basic_json_visitor<char_type>& visitor,
+ std::error_code& ec) const
+ {
+ dump_noflush(visitor, ec);
+ if (ec)
+ {
+ return;
+ }
+ visitor.flush();
+ }
+
+ bool is_null() const noexcept
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::null_value:
+ return true;
+ case json_storage_kind::json_const_pointer:
+ return cast<json_const_pointer_storage>().value()->is_null();
+ default:
+ return false;
+ }
+ }
+
+ allocator_type get_allocator() const
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::long_string_value:
+ {
+ return cast<long_string_storage>().get_allocator();
+ }
+ case json_storage_kind::byte_string_value:
+ {
+ return cast<byte_string_storage>().get_allocator();
+ }
+ case json_storage_kind::array_value:
+ {
+ return cast<array_storage>().get_allocator();
+ }
+ case json_storage_kind::object_value:
+ {
+ return cast<object_storage>().get_allocator();
+ }
+ default:
+ return allocator_type();
+ }
+ }
+
+ uint64_t ext_tag() const
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::byte_string_value:
+ {
+ return cast<byte_string_storage>().ext_tag();
+ }
+ case json_storage_kind::json_const_pointer:
+ return cast<json_const_pointer_storage>().value()->ext_tag();
+ default:
+ return 0;
+ }
+ }
+
+ bool contains(const string_view_type& key) const noexcept
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::object_value:
+ {
+ auto it = object_value().find(key);
+ return it != object_value().end();
+ }
+ case json_storage_kind::json_const_pointer:
+ return cast<json_const_pointer_storage>().value()->contains(key);
+ default:
+ return false;
+ }
+ }
+
+ std::size_t count(const string_view_type& key) const
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::object_value:
+ {
+ auto it = object_value().find(key);
+ if (it == object_value().end())
+ {
+ return 0;
+ }
+ std::size_t count = 0;
+ while (it != object_value().end()&& it->key() == key)
+ {
+ ++count;
+ ++it;
+ }
+ return count;
+ }
+ case json_storage_kind::json_const_pointer:
+ return cast<json_const_pointer_storage>().value()->count(key);
+ default:
+ return 0;
+ }
+ }
+
+ template<class T, class... Args>
+ bool is(Args&&... args) const noexcept
+ {
+ return json_type_traits<basic_json,T>::is(*this,std::forward<Args>(args)...);
+ }
+
+ bool is_string() const noexcept
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::short_string_value:
+ case json_storage_kind::long_string_value:
+ return true;
+ case json_storage_kind::json_const_pointer:
+ return cast<json_const_pointer_storage>().value()->is_string();
+ default:
+ return false;
+ }
+ }
+
+ bool is_string_view() const noexcept
+ {
+ return is_string();
+ }
+
+ bool is_byte_string() const noexcept
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::byte_string_value:
+ return true;
+ case json_storage_kind::json_const_pointer:
+ return cast<json_const_pointer_storage>().value()->is_byte_string();
+ default:
+ return false;
+ }
+ }
+
+ bool is_byte_string_view() const noexcept
+ {
+ return is_byte_string();
+ }
+
+ bool is_bignum() const
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::short_string_value:
+ case json_storage_kind::long_string_value:
+ return jsoncons::detail::is_base10(as_string_view().data(), as_string_view().length());
+ case json_storage_kind::int64_value:
+ case json_storage_kind::uint64_value:
+ return true;
+ case json_storage_kind::json_const_pointer:
+ return cast<json_const_pointer_storage>().value()->is_bignum();
+ default:
+ return false;
+ }
+ }
+
+ bool is_bool() const noexcept
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::bool_value:
+ return true;
+ case json_storage_kind::json_const_pointer:
+ return cast<json_const_pointer_storage>().value()->is_bool();
+ default:
+ return false;
+ }
+ }
+
+ bool is_object() const noexcept
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::empty_object_value:
+ case json_storage_kind::object_value:
+ return true;
+ case json_storage_kind::json_const_pointer:
+ return cast<json_const_pointer_storage>().value()->is_object();
+ default:
+ return false;
+ }
+ }
+
+ bool is_array() const noexcept
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::array_value:
+ return true;
+ case json_storage_kind::json_const_pointer:
+ return cast<json_const_pointer_storage>().value()->is_array();
+ default:
+ return false;
+ }
+ }
+
+ bool is_int64() const noexcept
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::int64_value:
+ return true;
+ case json_storage_kind::uint64_value:
+ return as_integer<uint64_t>() <= static_cast<uint64_t>((std::numeric_limits<int64_t>::max)());
+ case json_storage_kind::json_const_pointer:
+ return cast<json_const_pointer_storage>().value()->is_int64();
+ default:
+ return false;
+ }
+ }
+
+ bool is_uint64() const noexcept
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::uint64_value:
+ return true;
+ case json_storage_kind::int64_value:
+ return as_integer<int64_t>() >= 0;
+ case json_storage_kind::json_const_pointer:
+ return cast<json_const_pointer_storage>().value()->is_uint64();
+ default:
+ return false;
+ }
+ }
+
+ bool is_half() const noexcept
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::half_value:
+ return true;
+ case json_storage_kind::json_const_pointer:
+ return cast<json_const_pointer_storage>().value()->is_half();
+ default:
+ return false;
+ }
+ }
+
+ bool is_double() const noexcept
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::double_value:
+ return true;
+ case json_storage_kind::json_const_pointer:
+ return cast<json_const_pointer_storage>().value()->is_double();
+ default:
+ return false;
+ }
+ }
+
+ bool is_number() const noexcept
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::int64_value:
+ case json_storage_kind::uint64_value:
+ case json_storage_kind::half_value:
+ case json_storage_kind::double_value:
+ return true;
+ case json_storage_kind::short_string_value:
+ case json_storage_kind::long_string_value:
+ return tag() == semantic_tag::bigint ||
+ tag() == semantic_tag::bigdec ||
+ tag() == semantic_tag::bigfloat;
+ case json_storage_kind::json_const_pointer:
+ return cast<json_const_pointer_storage>().value()->is_number();
+ default:
+ return false;
+ }
+ }
+
+ bool empty() const noexcept
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::byte_string_value:
+ return cast<byte_string_storage>().length() == 0;
+ break;
+ case json_storage_kind::short_string_value:
+ return cast<short_string_storage>().length() == 0;
+ case json_storage_kind::long_string_value:
+ return cast<long_string_storage>().length() == 0;
+ case json_storage_kind::array_value:
+ return array_value().empty();
+ case json_storage_kind::empty_object_value:
+ return true;
+ case json_storage_kind::object_value:
+ return object_value().empty();
+ case json_storage_kind::json_const_pointer:
+ return cast<json_const_pointer_storage>().value()->empty();
+ default:
+ return false;
+ }
+ }
+
+ std::size_t capacity() const
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::array_value:
+ return array_value().capacity();
+ case json_storage_kind::object_value:
+ return object_value().capacity();
+ case json_storage_kind::json_const_pointer:
+ return cast<json_const_pointer_storage>().value()->capacity();
+ default:
+ return 0;
+ }
+ }
+
+ template<class U=Allocator>
+ void create_object_implicitly()
+ {
+ create_object_implicitly(type_traits::is_stateless<U>());
+ }
+
+ void create_object_implicitly(std::false_type)
+ {
+ static_assert(std::true_type::value, "Cannot create object implicitly - alloc is stateful.");
+ }
+
+ void create_object_implicitly(std::true_type)
+ {
+ *this = basic_json(json_object_arg, tag());
+ }
+
+ void reserve(std::size_t n)
+ {
+ if (n > 0)
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::array_value:
+ array_value().reserve(n);
+ break;
+ case json_storage_kind::empty_object_value:
+ {
+ create_object_implicitly();
+ object_value().reserve(n);
+ }
+ break;
+ case json_storage_kind::object_value:
+ {
+ object_value().reserve(n);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ void resize(std::size_t n)
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::array_value:
+ array_value().resize(n);
+ break;
+ default:
+ break;
+ }
+ }
+
+ template <class T>
+ void resize(std::size_t n, T val)
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::array_value:
+ array_value().resize(n, val);
+ break;
+ default:
+ break;
+ }
+ }
+
+ template<class T>
+ typename std::enable_if<is_json_type_traits_specialized<basic_json,T>::value,T>::type
+ as() const
+ {
+ T val = json_type_traits<basic_json,T>::as(*this);
+ return val;
+ }
+
+ template<class T>
+ typename std::enable_if<(!type_traits::is_basic_string<T>::value &&
+ type_traits::is_back_insertable_byte_container<T>::value) ||
+ type_traits::is_basic_byte_string<T>::value,T>::type
+ as(byte_string_arg_t, semantic_tag hint) const
+ {
+ converter<T> convert;
+ std::error_code ec;
+ switch (storage_kind())
+ {
+ case json_storage_kind::short_string_value:
+ case json_storage_kind::long_string_value:
+ {
+ switch (tag())
+ {
+ case semantic_tag::base16:
+ case semantic_tag::base64:
+ case semantic_tag::base64url:
+ {
+ T v = convert.from(as_string_view(),tag(),ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec));
+ }
+ return v;
+ }
+ default:
+ {
+ T v = convert.from(as_string_view(), hint, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec));
+ }
+ return T(v.begin(),v.end());
+ }
+ break;
+ }
+ break;
+ }
+ case json_storage_kind::byte_string_value:
+ return T(as_byte_string_view().begin(), as_byte_string_view().end());
+ case json_storage_kind::json_const_pointer:
+ return cast<json_const_pointer_storage>().value()->template as<T>(byte_string_arg, hint);
+ default:
+ JSONCONS_THROW(json_runtime_error<std::domain_error>("Not a byte string"));
+ }
+ }
+
+ bool as_bool() const
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::bool_value:
+ return cast<bool_storage>().value();
+ case json_storage_kind::int64_value:
+ return cast<int64_storage>().value() != 0;
+ case json_storage_kind::uint64_value:
+ return cast<uint64_storage>().value() != 0;
+ case json_storage_kind::json_const_pointer:
+ return cast<json_const_pointer_storage>().value()->as_bool();
+ default:
+ JSONCONS_THROW(json_runtime_error<std::domain_error>("Not a bool"));
+ }
+ }
+
+ template <class IntegerType>
+ IntegerType as_integer() const
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::short_string_value:
+ case json_storage_kind::long_string_value:
+ {
+ IntegerType val;
+ auto result = jsoncons::detail::to_integer(as_string_view().data(), as_string_view().length(), val);
+ if (!result)
+ {
+ JSONCONS_THROW(json_runtime_error<std::runtime_error>(result.error_code().message()));
+ }
+ return val;
+ }
+ case json_storage_kind::half_value:
+ return static_cast<IntegerType>(cast<half_storage>().value());
+ case json_storage_kind::double_value:
+ return static_cast<IntegerType>(cast<double_storage>().value());
+ case json_storage_kind::int64_value:
+ return static_cast<IntegerType>(cast<int64_storage>().value());
+ case json_storage_kind::uint64_value:
+ return static_cast<IntegerType>(cast<uint64_storage>().value());
+ case json_storage_kind::bool_value:
+ return static_cast<IntegerType>(cast<bool_storage>().value() ? 1 : 0);
+ case json_storage_kind::json_const_pointer:
+ return cast<json_const_pointer_storage>().value()->template as_integer<IntegerType>();
+ default:
+ JSONCONS_THROW(json_runtime_error<std::domain_error>("Not an integer"));
+ }
+ }
+
+ template <class IntegerType>
+ typename std::enable_if<type_traits::is_signed_integer<IntegerType>::value && sizeof(IntegerType) <= sizeof(int64_t),bool>::type
+ is_integer() const noexcept
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::int64_value:
+ return (as_integer<int64_t>() >= (type_traits::integer_limits<IntegerType>::lowest)()) && (as_integer<int64_t>() <= (type_traits::integer_limits<IntegerType>::max)());
+ case json_storage_kind::uint64_value:
+ return as_integer<uint64_t>() <= static_cast<uint64_t>((type_traits::integer_limits<IntegerType>::max)());
+ case json_storage_kind::json_const_pointer:
+ return cast<json_const_pointer_storage>().value()->template is_integer<IntegerType>();
+ default:
+ return false;
+ }
+ }
+
+ template <class IntegerType>
+ typename std::enable_if<type_traits::is_signed_integer<IntegerType>::value && sizeof(int64_t) < sizeof(IntegerType),bool>::type
+ is_integer() const noexcept
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::short_string_value:
+ case json_storage_kind::long_string_value:
+ {
+ IntegerType val;
+ auto result = jsoncons::detail::to_integer(as_string_view().data(), as_string_view().length(), val);
+ return result ? true : false;
+ }
+ case json_storage_kind::int64_value:
+ return (as_integer<int64_t>() >= (type_traits::integer_limits<IntegerType>::lowest)()) && (as_integer<int64_t>() <= (type_traits::integer_limits<IntegerType>::max)());
+ case json_storage_kind::uint64_value:
+ return as_integer<uint64_t>() <= static_cast<uint64_t>((type_traits::integer_limits<IntegerType>::max)());
+ case json_storage_kind::json_const_pointer:
+ return cast<json_const_pointer_storage>().value()->template is_integer<IntegerType>();
+ default:
+ return false;
+ }
+ }
+
+ template <class IntegerType>
+ typename std::enable_if<type_traits::is_unsigned_integer<IntegerType>::value && sizeof(IntegerType) <= sizeof(int64_t),bool>::type
+ is_integer() const noexcept
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::int64_value:
+ return as_integer<int64_t>() >= 0 && static_cast<uint64_t>(as_integer<int64_t>()) <= (type_traits::integer_limits<IntegerType>::max)();
+ case json_storage_kind::uint64_value:
+ return as_integer<uint64_t>() <= (type_traits::integer_limits<IntegerType>::max)();
+ case json_storage_kind::json_const_pointer:
+ return cast<json_const_pointer_storage>().value()->template is_integer<IntegerType>();
+ default:
+ return false;
+ }
+ }
+
+ template <class IntegerType>
+ typename std::enable_if<type_traits::is_unsigned_integer<IntegerType>::value && sizeof(int64_t) < sizeof(IntegerType),bool>::type
+ is_integer() const noexcept
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::short_string_value:
+ case json_storage_kind::long_string_value:
+ {
+ IntegerType val;
+ auto result = jsoncons::detail::to_integer(as_string_view().data(), as_string_view().length(), val);
+ return result ? true : false;
+ }
+ case json_storage_kind::int64_value:
+ return as_integer<int64_t>() >= 0 && static_cast<uint64_t>(as_integer<int64_t>()) <= (type_traits::integer_limits<IntegerType>::max)();
+ case json_storage_kind::uint64_value:
+ return as_integer<uint64_t>() <= (type_traits::integer_limits<IntegerType>::max)();
+ case json_storage_kind::json_const_pointer:
+ return cast<json_const_pointer_storage>().value()->template is_integer<IntegerType>();
+ default:
+ return false;
+ }
+ }
+
+ double as_double() const
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::short_string_value:
+ case json_storage_kind::long_string_value:
+ {
+ jsoncons::detail::chars_to to_double;
+ // to_double() throws std::invalid_argument if conversion fails
+ return to_double(as_cstring(), as_string_view().length());
+ }
+ case json_storage_kind::half_value:
+ return binary::decode_half(cast<half_storage>().value());
+ case json_storage_kind::double_value:
+ return cast<double_storage>().value();
+ case json_storage_kind::int64_value:
+ return static_cast<double>(cast<int64_storage>().value());
+ case json_storage_kind::uint64_value:
+ return static_cast<double>(cast<uint64_storage>().value());
+ case json_storage_kind::json_const_pointer:
+ return cast<json_const_pointer_storage>().value()->as_double();
+ default:
+ JSONCONS_THROW(json_runtime_error<std::invalid_argument>("Not a double"));
+ }
+ }
+
+ template <class SAllocator=std::allocator<char_type>>
+ std::basic_string<char_type,char_traits_type,SAllocator> as_string() const
+ {
+ return as_string(SAllocator());
+ }
+
+ template <class SAllocator=std::allocator<char_type>>
+ std::basic_string<char_type,char_traits_type,SAllocator> as_string(const SAllocator& alloc) const
+ {
+ using string_type = std::basic_string<char_type,char_traits_type,SAllocator>;
+
+ converter<string_type> convert;
+ std::error_code ec;
+ switch (storage_kind())
+ {
+ case json_storage_kind::short_string_value:
+ case json_storage_kind::long_string_value:
+ {
+ return string_type(as_string_view().data(),as_string_view().length(),alloc);
+ }
+ case json_storage_kind::byte_string_value:
+ {
+ auto s = convert.from(as_byte_string_view(), tag(), alloc, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec));
+ }
+ return s;
+ }
+ case json_storage_kind::array_value:
+ {
+ string_type s(alloc);
+ {
+ basic_compact_json_encoder<char_type,jsoncons::string_sink<string_type>> encoder(s);
+ dump(encoder);
+ }
+ return s;
+ }
+ case json_storage_kind::json_const_pointer:
+ return cast<json_const_pointer_storage>().value()->as_string(alloc);
+ default:
+ {
+ string_type s(alloc);
+ basic_compact_json_encoder<char_type,jsoncons::string_sink<string_type>> encoder(s);
+ dump(encoder);
+ return s;
+ }
+ }
+ }
+
+ const char_type* as_cstring() const
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::short_string_value:
+ return cast<short_string_storage>().c_str();
+ case json_storage_kind::long_string_value:
+ return cast<long_string_storage>().c_str();
+ case json_storage_kind::json_const_pointer:
+ return cast<json_const_pointer_storage>().value()->as_cstring();
+ default:
+ JSONCONS_THROW(json_runtime_error<std::domain_error>("Not a cstring"));
+ }
+ }
+
+ basic_json& at(const string_view_type& name)
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::empty_object_value:
+ JSONCONS_THROW(key_not_found(name.data(),name.length()));
+ case json_storage_kind::object_value:
+ {
+ auto it = object_value().find(name);
+ if (it == object_value().end())
+ {
+ JSONCONS_THROW(key_not_found(name.data(),name.length()));
+ }
+ return it->value();
+ }
+ default:
+ {
+ JSONCONS_THROW(not_an_object(name.data(),name.length()));
+ }
+ }
+ }
+
+ const basic_json& at(const string_view_type& key) const
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::empty_object_value:
+ JSONCONS_THROW(key_not_found(key.data(),key.length()));
+ case json_storage_kind::object_value:
+ {
+ auto it = object_value().find(key);
+ if (it == object_value().end())
+ {
+ JSONCONS_THROW(key_not_found(key.data(),key.length()));
+ }
+ return it->value();
+ }
+ case json_storage_kind::json_const_pointer:
+ return cast<json_const_pointer_storage>().value()->at(key);
+ default:
+ {
+ JSONCONS_THROW(not_an_object(key.data(),key.length()));
+ }
+ }
+ }
+
+ basic_json& at(std::size_t i)
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::array_value:
+ if (i >= array_value().size())
+ {
+ JSONCONS_THROW(json_runtime_error<std::out_of_range>("Invalid array subscript"));
+ }
+ return array_value().operator[](i);
+ case json_storage_kind::object_value:
+ return object_value().at(i);
+ default:
+ JSONCONS_THROW(json_runtime_error<std::domain_error>("Index on non-array value not supported"));
+ }
+ }
+
+ const basic_json& at(std::size_t i) const
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::array_value:
+ if (i >= array_value().size())
+ {
+ JSONCONS_THROW(json_runtime_error<std::out_of_range>("Invalid array subscript"));
+ }
+ return array_value().operator[](i);
+ case json_storage_kind::object_value:
+ return object_value().at(i);
+ case json_storage_kind::json_const_pointer:
+ return cast<json_const_pointer_storage>().value()->at(i);
+ default:
+ JSONCONS_THROW(json_runtime_error<std::domain_error>("Index on non-array value not supported"));
+ }
+ }
+
+ object_iterator find(const string_view_type& name)
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::empty_object_value:
+ return object_range().end();
+ case json_storage_kind::object_value:
+ return object_iterator(object_value().find(name));
+ default:
+ {
+ JSONCONS_THROW(not_an_object(name.data(),name.length()));
+ }
+ }
+ }
+
+ const_object_iterator find(const string_view_type& key) const
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::empty_object_value:
+ return object_range().end();
+ case json_storage_kind::object_value:
+ return const_object_iterator(object_value().find(key));
+ case json_storage_kind::json_const_pointer:
+ return cast<json_const_pointer_storage>().value()->find(key);
+ default:
+ {
+ JSONCONS_THROW(not_an_object(key.data(),key.length()));
+ }
+ }
+ }
+
+ const basic_json& at_or_null(const string_view_type& key) const
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::null_value:
+ case json_storage_kind::empty_object_value:
+ {
+ return null();
+ }
+ case json_storage_kind::object_value:
+ {
+ auto it = object_value().find(key);
+ if (it != object_value().end())
+ {
+ return it->value();
+ }
+ else
+ {
+ return null();
+ }
+ }
+ case json_storage_kind::json_const_pointer:
+ return cast<json_const_pointer_storage>().value()->at_or_null(key);
+ default:
+ {
+ JSONCONS_THROW(not_an_object(key.data(),key.length()));
+ }
+ }
+ }
+
+ template <class T,class U>
+ T get_value_or(const string_view_type& key, U&& default_value) const
+ {
+ static_assert(std::is_copy_constructible<T>::value,
+ "get_value_or: T must be copy constructible");
+ static_assert(std::is_convertible<U&&, T>::value,
+ "get_value_or: U must be convertible to T");
+ switch (storage_kind())
+ {
+ case json_storage_kind::null_value:
+ case json_storage_kind::empty_object_value:
+ {
+ return static_cast<T>(std::forward<U>(default_value));
+ }
+ case json_storage_kind::object_value:
+ {
+ auto it = object_value().find(key);
+ if (it != object_value().end())
+ {
+ return it->value().template as<T>();
+ }
+ else
+ {
+ return static_cast<T>(std::forward<U>(default_value));
+ }
+ }
+ case json_storage_kind::json_const_pointer:
+ return cast<json_const_pointer_storage>().value()->template get_value_or<T,U>(key,std::forward<U>(default_value));
+ default:
+ {
+ JSONCONS_THROW(not_an_object(key.data(),key.length()));
+ }
+ }
+ }
+
+ // Modifiers
+
+ void shrink_to_fit()
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::array_value:
+ array_value().shrink_to_fit();
+ break;
+ case json_storage_kind::object_value:
+ object_value().shrink_to_fit();
+ break;
+ default:
+ break;
+ }
+ }
+
+ void clear()
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::array_value:
+ array_value().clear();
+ break;
+ case json_storage_kind::object_value:
+ object_value().clear();
+ break;
+ default:
+ break;
+ }
+ }
+
+ object_iterator erase(const_object_iterator pos)
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::empty_object_value:
+ return object_range().end();
+ case json_storage_kind::object_value:
+ return object_iterator(object_value().erase(pos));
+ default:
+ JSONCONS_THROW(json_runtime_error<std::domain_error>("Not an object"));
+ break;
+ }
+ }
+
+ object_iterator erase(const_object_iterator first, const_object_iterator last)
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::empty_object_value:
+ return object_range().end();
+ case json_storage_kind::object_value:
+ return object_iterator(object_value().erase(first, last));
+ default:
+ JSONCONS_THROW(json_runtime_error<std::domain_error>("Not an object"));
+ break;
+ }
+ }
+
+ array_iterator erase(const_array_iterator pos)
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::array_value:
+ return array_value().erase(pos);
+ default:
+ JSONCONS_THROW(json_runtime_error<std::domain_error>("Not an array"));
+ }
+ }
+
+ array_iterator erase(const_array_iterator first, const_array_iterator last)
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::array_value:
+ return array_value().erase(first, last);
+ default:
+ JSONCONS_THROW(json_runtime_error<std::domain_error>("Not an array"));
+ break;
+ }
+ }
+
+ // Removes all elements from an array value whose index is between from_index, inclusive, and to_index, exclusive.
+
+ void erase(const string_view_type& name)
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::empty_object_value:
+ break;
+ case json_storage_kind::object_value:
+ object_value().erase(name);
+ break;
+ default:
+ JSONCONS_THROW(not_an_object(name.data(),name.length()));
+ break;
+ }
+ }
+
+ template <class T>
+ std::pair<object_iterator,bool> insert_or_assign(const string_view_type& name, T&& val)
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::empty_object_value:
+ create_object_implicitly();
+ JSONCONS_FALLTHROUGH;
+ case json_storage_kind::object_value:
+ {
+ auto result = object_value().insert_or_assign(name, std::forward<T>(val));
+ return std::make_pair(object_iterator(result.first), result.second);
+ }
+ default:
+ {
+ JSONCONS_THROW(not_an_object(name.data(),name.length()));
+ }
+ }
+ }
+
+ template <class ... Args>
+ std::pair<object_iterator,bool> try_emplace(const string_view_type& name, Args&&... args)
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::empty_object_value:
+ create_object_implicitly();
+ JSONCONS_FALLTHROUGH;
+ case json_storage_kind::object_value:
+ {
+ auto result = object_value().try_emplace(name, std::forward<Args>(args)...);
+ return std::make_pair(object_iterator(result.first),result.second);
+ }
+ default:
+ {
+ JSONCONS_THROW(not_an_object(name.data(),name.length()));
+ }
+ }
+ }
+
+ // merge
+
+ void merge(const basic_json& source)
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::empty_object_value:
+ create_object_implicitly();
+ JSONCONS_FALLTHROUGH;
+ case json_storage_kind::object_value:
+ object_value().merge(source.object_value());
+ break;
+ default:
+ {
+ JSONCONS_THROW(json_runtime_error<std::domain_error>("Attempting to merge a value that is not an object"));
+ }
+ }
+ }
+
+ void merge(basic_json&& source)
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::empty_object_value:
+ create_object_implicitly();
+ JSONCONS_FALLTHROUGH;
+ case json_storage_kind::object_value:
+ object_value().merge(std::move(source.object_value()));
+ break;
+ default:
+ {
+ JSONCONS_THROW(json_runtime_error<std::domain_error>("Attempting to merge a value that is not an object"));
+ }
+ }
+ }
+
+ void merge(object_iterator hint, const basic_json& source)
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::empty_object_value:
+ create_object_implicitly();
+ JSONCONS_FALLTHROUGH;
+ case json_storage_kind::object_value:
+ object_value().merge(hint, source.object_value());
+ break;
+ default:
+ {
+ JSONCONS_THROW(json_runtime_error<std::domain_error>("Attempting to merge a value that is not an object"));
+ }
+ }
+ }
+
+ void merge(object_iterator hint, basic_json&& source)
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::empty_object_value:
+ create_object_implicitly();
+ JSONCONS_FALLTHROUGH;
+ case json_storage_kind::object_value:
+ object_value().merge(hint, std::move(source.object_value()));
+ break;
+ default:
+ {
+ JSONCONS_THROW(json_runtime_error<std::domain_error>("Attempting to merge a value that is not an object"));
+ }
+ }
+ }
+
+ // merge_or_update
+
+ void merge_or_update(const basic_json& source)
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::empty_object_value:
+ create_object_implicitly();
+ JSONCONS_FALLTHROUGH;
+ case json_storage_kind::object_value:
+ object_value().merge_or_update(source.object_value());
+ break;
+ default:
+ {
+ JSONCONS_THROW(json_runtime_error<std::domain_error>("Attempting to merge or update a value that is not an object"));
+ }
+ }
+ }
+
+ void merge_or_update(basic_json&& source)
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::empty_object_value:
+ create_object_implicitly();
+ JSONCONS_FALLTHROUGH;
+ case json_storage_kind::object_value:
+ object_value().merge_or_update(std::move(source.object_value()));
+ break;
+ default:
+ {
+ JSONCONS_THROW(json_runtime_error<std::domain_error>("Attempting to merge or update a value that is not an object"));
+ }
+ }
+ }
+
+ void merge_or_update(object_iterator hint, const basic_json& source)
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::empty_object_value:
+ create_object_implicitly();
+ JSONCONS_FALLTHROUGH;
+ case json_storage_kind::object_value:
+ object_value().merge_or_update(hint, source.object_value());
+ break;
+ default:
+ {
+ JSONCONS_THROW(json_runtime_error<std::domain_error>("Attempting to merge or update a value that is not an object"));
+ }
+ }
+ }
+
+ void merge_or_update(object_iterator hint, basic_json&& source)
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::empty_object_value:
+ create_object_implicitly();
+ JSONCONS_FALLTHROUGH;
+ case json_storage_kind::object_value:
+ object_value().merge_or_update(hint, std::move(source.object_value()));
+ break;
+ default:
+ {
+ JSONCONS_THROW(json_runtime_error<std::domain_error>("Attempting to merge or update a value that is not an object"));
+ }
+ }
+ }
+
+ template <class T>
+ object_iterator insert_or_assign(object_iterator hint, const string_view_type& name, T&& val)
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::empty_object_value:
+ create_object_implicitly();
+ JSONCONS_FALLTHROUGH;
+ case json_storage_kind::object_value:
+ return object_iterator(object_value().insert_or_assign(hint, name, std::forward<T>(val)));
+ default:
+ {
+ JSONCONS_THROW(not_an_object(name.data(),name.length()));
+ }
+ }
+ }
+
+ template <class ... Args>
+ object_iterator try_emplace(object_iterator hint, const string_view_type& name, Args&&... args)
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::empty_object_value:
+ create_object_implicitly();
+ JSONCONS_FALLTHROUGH;
+ case json_storage_kind::object_value:
+ return object_iterator(object_value().try_emplace(hint, name, std::forward<Args>(args)...));
+ default:
+ {
+ JSONCONS_THROW(not_an_object(name.data(),name.length()));
+ }
+ }
+ }
+
+ template <class T>
+ array_iterator insert(const_array_iterator pos, T&& val)
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::array_value:
+ return array_value().insert(pos, std::forward<T>(val));
+ break;
+ default:
+ {
+ JSONCONS_THROW(json_runtime_error<std::domain_error>("Attempting to insert into a value that is not an array"));
+ }
+ }
+ }
+
+ template <class InputIt>
+ array_iterator insert(const_array_iterator pos, InputIt first, InputIt last)
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::array_value:
+ return array_value().insert(pos, first, last);
+ break;
+ default:
+ {
+ JSONCONS_THROW(json_runtime_error<std::domain_error>("Attempting to insert into a value that is not an array"));
+ }
+ }
+ }
+
+ template <class InputIt>
+ void insert(InputIt first, InputIt last)
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::empty_object_value:
+ case json_storage_kind::object_value:
+ object_value().insert(first, last, get_key_value<key_type,basic_json>());
+ break;
+ default:
+ {
+ JSONCONS_THROW(json_runtime_error<std::domain_error>("Attempting to insert into a value that is not an object"));
+ }
+ }
+ }
+
+ template <class InputIt>
+ void insert(sorted_unique_range_tag tag, InputIt first, InputIt last)
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::empty_object_value:
+ case json_storage_kind::object_value:
+ object_value().insert(tag, first, last, get_key_value<key_type,basic_json>());
+ break;
+ default:
+ {
+ JSONCONS_THROW(json_runtime_error<std::domain_error>("Attempting to insert into a value that is not an object"));
+ }
+ }
+ }
+
+ template <class... Args>
+ array_iterator emplace(const_array_iterator pos, Args&&... args)
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::array_value:
+ return array_value().emplace(pos, std::forward<Args>(args)...);
+ break;
+ default:
+ {
+ JSONCONS_THROW(json_runtime_error<std::domain_error>("Attempting to insert into a value that is not an array"));
+ }
+ }
+ }
+
+ template <class... Args>
+ basic_json& emplace_back(Args&&... args)
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::array_value:
+ return array_value().emplace_back(std::forward<Args>(args)...);
+ default:
+ {
+ JSONCONS_THROW(json_runtime_error<std::domain_error>("Attempting to insert into a value that is not an array"));
+ }
+ }
+ }
+
+ friend void swap(basic_json& a, basic_json& b) noexcept
+ {
+ a.swap(b);
+ }
+
+ template <class T>
+ void push_back(T&& val)
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::array_value:
+ array_value().push_back(std::forward<T>(val));
+ break;
+ default:
+ {
+ JSONCONS_THROW(json_runtime_error<std::domain_error>("Attempting to insert into a value that is not an array"));
+ }
+ }
+ }
+
+ template<class T>
+ T get_with_default(const string_view_type& key, const T& default_value) const
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::null_value:
+ case json_storage_kind::empty_object_value:
+ {
+ return default_value;
+ }
+ case json_storage_kind::object_value:
+ {
+ auto it = object_value().find(key);
+ if (it != object_value().end())
+ {
+ return it->value().template as<T>();
+ }
+ else
+ {
+ return default_value;
+ }
+ }
+ case json_storage_kind::json_const_pointer:
+ return cast<json_const_pointer_storage>().value()->get_with_default(key, default_value);
+ default:
+ {
+ JSONCONS_THROW(not_an_object(key.data(),key.length()));
+ }
+ }
+ }
+
+ template<class T = std::basic_string<char_type>>
+ T get_with_default(const string_view_type& key, const char_type* default_value) const
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::null_value:
+ case json_storage_kind::empty_object_value:
+ {
+ return T(default_value);
+ }
+ case json_storage_kind::object_value:
+ {
+ auto it = object_value().find(key);
+ if (it != object_value().end())
+ {
+ return it->value().template as<T>();
+ }
+ else
+ {
+ return T(default_value);
+ }
+ }
+ case json_storage_kind::json_const_pointer:
+ return cast<json_const_pointer_storage>().value()->get_with_default(key, default_value);
+ default:
+ {
+ JSONCONS_THROW(not_an_object(key.data(),key.length()));
+ }
+ }
+ }
+
+ std::basic_string<char_type> to_string() const noexcept
+ {
+ using string_type = std::basic_string<char_type>;
+ string_type s;
+ basic_compact_json_encoder<char_type, jsoncons::string_sink<string_type>> encoder(s);
+ dump(encoder);
+ return s;
+ }
+
+ #if !defined(JSONCONS_NO_DEPRECATED)
+ JSONCONS_DEPRECATED_MSG("Instead, use basic_json(byte_string_arg_t, const Source&, semantic_tag=semantic_tag::none,const Allocator& = Allocator())")
+ basic_json(const byte_string_view& bytes,
+ semantic_tag tag,
+ const Allocator& alloc = Allocator())
+ : basic_json(byte_string_arg, bytes, tag, alloc)
+ {
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use at_or_null(const string_view_type&)")
+ const basic_json& get_with_default(const string_view_type& name) const
+ {
+ return at_or_null(name);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use parse(const string_view_type&)")
+ static basic_json parse(const char_type* s, std::size_t length)
+ {
+ parse_error_handler_type err_handler;
+ return parse(s,length,err_handler);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use parse(const string_view_type&, parse_error_handler)")
+ static basic_json parse(const char_type* s, std::size_t length, std::function<bool(json_errc,const ser_context&)> err_handler)
+ {
+ return parse(string_view_type(s,length),err_handler);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use parse(std::basic_istream<char_type>&)")
+ static basic_json parse_file(const std::basic_string<char_type,char_traits_type>& filename)
+ {
+ parse_error_handler_type err_handler;
+ return parse_file(filename,err_handler);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use parse(std::basic_istream<char_type>&, std::function<bool(json_errc,const ser_context&)>)")
+ static basic_json parse_file(const std::basic_string<char_type,char_traits_type>& filename,
+ std::function<bool(json_errc,const ser_context&)> err_handler)
+ {
+ std::basic_ifstream<char_type> is(filename);
+ return parse(is,err_handler);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use parse(std::basic_istream<char_type>&)")
+ static basic_json parse_stream(std::basic_istream<char_type>& is)
+ {
+ return parse(is);
+ }
+ JSONCONS_DEPRECATED_MSG("Instead, use parse(std::basic_istream<char_type>&, std::function<bool(json_errc,const ser_context&)>)")
+ static basic_json parse_stream(std::basic_istream<char_type>& is, std::function<bool(json_errc,const ser_context&)> err_handler)
+ {
+ return parse(is,err_handler);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use parse(const string_view_type&)")
+ static basic_json parse_string(const string_view_type& s)
+ {
+ return parse(s);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use parse(parse(const string_view_type&, std::function<bool(json_errc,const ser_context&)>)")
+ static const basic_json parse_string(const string_view_type& s, std::function<bool(json_errc,const ser_context&)> err_handler)
+ {
+ return parse(s,err_handler);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use basic_json(double)")
+ basic_json(double val, uint8_t)
+ : basic_json(val, semantic_tag::none)
+ {
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use basic_json(const byte_string_view& ,semantic_tag)")
+ basic_json(const byte_string_view& bytes,
+ byte_string_chars_format encoding_hint,
+ semantic_tag tag = semantic_tag::none)
+ : basic_json(bytes, tag)
+ {
+ switch (encoding_hint)
+ {
+ case byte_string_chars_format::base16:
+ *this = basic_json(bytes, semantic_tag::base16);
+ break;
+ case byte_string_chars_format::base64:
+ *this = basic_json(bytes, semantic_tag::base64);
+ break;
+ case byte_string_chars_format::base64url:
+ *this = basic_json(bytes, semantic_tag::base64url);
+ break;
+ default:
+ break;
+ }
+ }
+
+ template<class InputIterator>
+ basic_json(InputIterator first, InputIterator last, const Allocator& alloc = Allocator())
+ : basic_json(json_array_arg,first,last,alloc)
+ {
+ }
+ JSONCONS_DEPRECATED_MSG("Instead, use dump(basic_json_visitor<char_type>&)")
+ void dump_fragment(basic_json_visitor<char_type>& visitor) const
+ {
+ dump(visitor);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use dump(basic_json_visitor<char_type>&)")
+ void dump_body(basic_json_visitor<char_type>& visitor) const
+ {
+ dump(visitor);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use dump(std::basic_ostream<char_type>&, indenting)")
+ void dump(std::basic_ostream<char_type>& os, bool pprint) const
+ {
+ if (pprint)
+ {
+ basic_json_encoder<char_type> encoder(os);
+ dump(encoder);
+ }
+ else
+ {
+ basic_compact_json_encoder<char_type> encoder(os);
+ dump(encoder);
+ }
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use dump(std::basic_ostream<char_type>&, const basic_json_encode_options<char_type>&, indenting)")
+ void dump(std::basic_ostream<char_type>& os, const basic_json_encode_options<char_type>& options, bool pprint) const
+ {
+ if (pprint)
+ {
+ basic_json_encoder<char_type> encoder(os, options);
+ dump(encoder);
+ }
+ else
+ {
+ basic_compact_json_encoder<char_type> encoder(os, options);
+ dump(encoder);
+ }
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use dump(basic_json_visitor<char_type>&)")
+ void write_body(basic_json_visitor<char_type>& visitor) const
+ {
+ dump(visitor);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use dump(basic_json_visitor<char_type>&)")
+ void write(basic_json_visitor<char_type>& visitor) const
+ {
+ dump(visitor);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use dump(std::basic_ostream<char_type>&)")
+ void write(std::basic_ostream<char_type>& os) const
+ {
+ dump(os);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use dump(std::basic_ostream<char_type>&, const basic_json_encode_options<char_type>&)")
+ void write(std::basic_ostream<char_type>& os, const basic_json_encode_options<char_type>& options) const
+ {
+ dump(os,options);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use dump(std::basic_ostream<char_type>&, const basic_json_encode_options<char_type>&, indenting)")
+ void write(std::basic_ostream<char_type>& os, const basic_json_encode_options<char_type>& options, bool pprint) const
+ {
+ dump(os,options,pprint);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use dump(basic_json_visitor<char_type>&)")
+ void to_stream(basic_json_visitor<char_type>& visitor) const
+ {
+ dump(visitor);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use dump(std::basic_ostream<char_type>&)")
+ void to_stream(std::basic_ostream<char_type>& os) const
+ {
+ dump(os);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use dump(std::basic_ostream<char_type>&, const basic_json_encode_options<char_type>&)")
+ void to_stream(std::basic_ostream<char_type>& os, const basic_json_encode_options<char_type>& options) const
+ {
+ dump(os,options);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use dump(std::basic_ostream<char_type>&, const basic_json_encode_options<char_type>&, indenting)")
+ void to_stream(std::basic_ostream<char_type>& os, const basic_json_encode_options<char_type>& options, bool pprint) const
+ {
+ dump(os,options,pprint ? indenting::indent : indenting::no_indent);
+ }
+
+ JSONCONS_DEPRECATED_MSG("No replacement")
+ std::size_t precision() const
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::double_value:
+ return 0;
+ default:
+ JSONCONS_THROW(json_runtime_error<std::domain_error>("Not a double"));
+ }
+ }
+
+ JSONCONS_DEPRECATED_MSG("No replacement")
+ std::size_t decimal_places() const
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::double_value:
+ return 0;
+ default:
+ JSONCONS_THROW(json_runtime_error<std::domain_error>("Not a double"));
+ }
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use tag() == semantic_tag::datetime")
+ bool is_datetime() const noexcept
+ {
+ return tag() == semantic_tag::datetime;
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use tag() == semantic_tag::epoch_second")
+ bool is_epoch_time() const noexcept
+ {
+ return tag() == semantic_tag::epoch_second;
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use contains(const string_view_type&)")
+ bool has_key(const string_view_type& name) const noexcept
+ {
+ return contains(name);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use as<uint64_t>()")
+ uint64_t as_uinteger() const
+ {
+ return as<uint64_t>();
+ }
+
+ JSONCONS_DEPRECATED_MSG("No replacement")
+ std::size_t double_precision() const
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::double_value:
+ return 0;
+ default:
+ JSONCONS_THROW(json_runtime_error<std::domain_error>("Not a double"));
+ }
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use insert(const_array_iterator, T&&)")
+ void add(std::size_t index, const basic_json& value)
+ {
+ evaluate_with_default().add(index, value);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use insert(const_array_iterator, T&&)")
+ void add(std::size_t index, basic_json&& value)
+ {
+ evaluate_with_default().add(index, std::forward<basic_json>(value));
+ }
+
+ template <class T>
+ JSONCONS_DEPRECATED_MSG("Instead, use push_back(T&&)")
+ void add(T&& val)
+ {
+ push_back(std::forward<T>(val));
+ }
+
+ template <class T>
+ JSONCONS_DEPRECATED_MSG("Instead, use insert(const_array_iterator, T&&)")
+ array_iterator add(const_array_iterator pos, T&& val)
+ {
+ return insert(pos, std::forward<T>(val));
+ }
+
+ template <class T>
+ JSONCONS_DEPRECATED_MSG("Instead, use insert_or_assign(const string_view_type&, T&&)")
+ std::pair<object_iterator,bool> set(const string_view_type& name, T&& val)
+ {
+ return insert_or_assign(name, std::forward<T>(val));
+ }
+
+ template <class T>
+ JSONCONS_DEPRECATED_MSG("Instead, use insert_or_assign(const string_view_type&, T&&)")
+ object_iterator set(object_iterator hint, const string_view_type& name, T&& val)
+ {
+ return insert_or_assign(hint, name, std::forward<T>(val));
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use resize(std::size_t)")
+ void resize_array(std::size_t n)
+ {
+ resize(n);
+ }
+
+ template <class T>
+ JSONCONS_DEPRECATED_MSG("Instead, use resize(std::size_t, T)")
+ void resize_array(std::size_t n, T val)
+ {
+ resize(n,val);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use object_range().begin()")
+ object_iterator begin_members()
+ {
+ return object_range().begin();
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use object_range().begin()")
+ const_object_iterator begin_members() const
+ {
+ return object_range().begin();
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use object_range().end()")
+ object_iterator end_members()
+ {
+ return object_range().end();
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use object_range().end()")
+ const_object_iterator end_members() const
+ {
+ return object_range().end();
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use array_range().begin()")
+ array_iterator begin_elements()
+ {
+ return array_range().begin();
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use array_range().begin()")
+ const_array_iterator begin_elements() const
+ {
+ return array_range().begin();
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use array_range().end()")
+ array_iterator end_elements()
+ {
+ return array_range().end();
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use array_range().end()")
+ const_array_iterator end_elements() const
+ {
+ return array_range().end();
+ }
+
+ template<class T>
+ JSONCONS_DEPRECATED_MSG("Instead, use get_with_default(const string_view_type&, T&&)")
+ basic_json get(const string_view_type& name, T&& default_value) const
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::empty_object_value:
+ {
+ return basic_json(std::forward<T>(default_value));
+ }
+ case json_storage_kind::object_value:
+ {
+ auto it = object_value().find(name);
+ if (it != object_value().end())
+ {
+ return it->value();
+ }
+ else
+ {
+ return basic_json(std::forward<T>(default_value));
+ }
+ }
+ default:
+ {
+ JSONCONS_THROW(not_an_object(name.data(),name.length()));
+ }
+ }
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use at_or_null(const string_view_type&)")
+ const basic_json& get(const string_view_type& name) const
+ {
+ static const basic_json a_null = null_type();
+
+ switch (storage_kind())
+ {
+ case json_storage_kind::empty_object_value:
+ return a_null;
+ case json_storage_kind::object_value:
+ {
+ auto it = object_value().find(name);
+ return it != object_value().end() ? it->value() : a_null;
+ }
+ default:
+ {
+ JSONCONS_THROW(not_an_object(name.data(),name.length()));
+ }
+ }
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use is<long long>()")
+ bool is_longlong() const noexcept
+ {
+ return is<long long>();
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use is<unsigned long long>()")
+ bool is_ulonglong() const noexcept
+ {
+ return is<unsigned long long>();
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use as<long long>()")
+ long long as_longlong() const
+ {
+ return as<long long>();
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use as<unsigned long long>()")
+ unsigned long long as_ulonglong() const
+ {
+ return as<unsigned long long>();
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use as<int>()")
+ int as_int() const
+ {
+ return as<int>();
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use as<unsigned int>()")
+ unsigned int as_uint() const
+ {
+ return as<unsigned int>();
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use as<long>()")
+ long as_long() const
+ {
+ return as<long>();
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use as<unsigned long>()")
+ unsigned long as_ulong() const
+ {
+ return as<unsigned long>();
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use contains(const string_view_type&)")
+ bool has_member(const string_view_type& key) const noexcept
+ {
+ return contains(key);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use erase(const_object_iterator, const_object_iterator)")
+ void remove_range(std::size_t from_index, std::size_t to_index)
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::array_value:
+ array_value().remove_range(from_index, to_index);
+ break;
+ default:
+ break;
+ }
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use erase(const string_view_type& name)")
+ void remove(const string_view_type& name)
+ {
+ erase(name);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use erase(const string_view_type& name)")
+ void remove_member(const string_view_type& name)
+ {
+ erase(name);
+ }
+ // Removes a member from an object value
+
+ JSONCONS_DEPRECATED_MSG("Instead, use empty()")
+ bool is_empty() const noexcept
+ {
+ return empty();
+ }
+ JSONCONS_DEPRECATED_MSG("Instead, use is_number()")
+ bool is_numeric() const noexcept
+ {
+ return is_number();
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use object_range()")
+ range<object_iterator, const_object_iterator> members()
+ {
+ return object_range();
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use object_range()")
+ range<const_object_iterator, const_object_iterator> members() const
+ {
+ return object_range();
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use array_range()")
+ range<array_iterator, const_array_iterator> elements()
+ {
+ return array_range();
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use array_range()")
+ range<const_array_iterator, const_array_iterator> elements() const
+ {
+ return array_range();
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use storage_kind()")
+ json_storage_kind get_stor_type() const
+ {
+ return storage_kind();
+ }
+ #endif
+
+ range<object_iterator, const_object_iterator> object_range()
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::empty_object_value:
+ return range<object_iterator, const_object_iterator>(object_iterator(), object_iterator());
+ case json_storage_kind::object_value:
+ return range<object_iterator, const_object_iterator>(object_iterator(object_value().begin()),
+ object_iterator(object_value().end()));
+ default:
+ JSONCONS_THROW(json_runtime_error<std::domain_error>("Not an object"));
+ }
+ }
+
+ range<const_object_iterator, const_object_iterator> object_range() const
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::empty_object_value:
+ return range<const_object_iterator, const_object_iterator>(const_object_iterator(), const_object_iterator());
+ case json_storage_kind::object_value:
+ return range<const_object_iterator, const_object_iterator>(const_object_iterator(object_value().begin()),
+ const_object_iterator(object_value().end()));
+ case json_storage_kind::json_const_pointer:
+ return cast<json_const_pointer_storage>().value()->object_range();
+ default:
+ JSONCONS_THROW(json_runtime_error<std::domain_error>("Not an object"));
+ }
+ }
+
+ range<array_iterator, const_array_iterator> array_range()
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::array_value:
+ return range<array_iterator, const_array_iterator>(array_value().begin(),array_value().end());
+ default:
+ JSONCONS_THROW(json_runtime_error<std::domain_error>("Not an array"));
+ }
+ }
+
+ range<const_array_iterator, const_array_iterator> array_range() const
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::array_value:
+ return range<const_array_iterator, const_array_iterator>(array_value().begin(),array_value().end());
+ case json_storage_kind::json_const_pointer:
+ return cast<json_const_pointer_storage>().value()->array_range();
+ default:
+ JSONCONS_THROW(json_runtime_error<std::domain_error>("Not an array"));
+ }
+ }
+
+ array& array_value()
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::array_value:
+ return cast<array_storage>().value();
+ default:
+ JSONCONS_THROW(json_runtime_error<std::domain_error>("Bad array cast"));
+ break;
+ }
+ }
+
+ const array& array_value() const
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::array_value:
+ return cast<array_storage>().value();
+ case json_storage_kind::json_const_pointer:
+ return cast<json_const_pointer_storage>().value()->array_value();
+ default:
+ JSONCONS_THROW(json_runtime_error<std::domain_error>("Bad array cast"));
+ break;
+ }
+ }
+
+ object& object_value()
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::empty_object_value:
+ create_object_implicitly();
+ JSONCONS_FALLTHROUGH;
+ case json_storage_kind::object_value:
+ return cast<object_storage>().value();
+ default:
+ JSONCONS_THROW(json_runtime_error<std::domain_error>("Bad object cast"));
+ break;
+ }
+ }
+
+ const object& object_value() const
+ {
+ switch (storage_kind())
+ {
+ case json_storage_kind::empty_object_value:
+ const_cast<basic_json*>(this)->create_object_implicitly(); // HERE
+ JSONCONS_FALLTHROUGH;
+ case json_storage_kind::object_value:
+ return cast<object_storage>().value();
+ case json_storage_kind::json_const_pointer:
+ return cast<json_const_pointer_storage>().value()->object_value();
+ default:
+ JSONCONS_THROW(json_runtime_error<std::domain_error>("Bad object cast"));
+ break;
+ }
+ }
+
+ private:
+
+ void dump_noflush(basic_json_visitor<char_type>& visitor, std::error_code& ec) const
+ {
+ const ser_context context{};
+ switch (storage_kind())
+ {
+ case json_storage_kind::short_string_value:
+ case json_storage_kind::long_string_value:
+ visitor.string_value(as_string_view(), tag(), context, ec);
+ break;
+ case json_storage_kind::byte_string_value:
+ 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);
+ }
+ break;
+ case json_storage_kind::half_value:
+ visitor.half_value(cast<half_storage>().value(), tag(), context, ec);
+ break;
+ case json_storage_kind::double_value:
+ visitor.double_value(cast<double_storage>().value(),
+ tag(), context, ec);
+ break;
+ case json_storage_kind::int64_value:
+ visitor.int64_value(cast<int64_storage>().value(), tag(), context, ec);
+ break;
+ case json_storage_kind::uint64_value:
+ visitor.uint64_value(cast<uint64_storage>().value(), tag(), context, ec);
+ break;
+ case json_storage_kind::bool_value:
+ visitor.bool_value(cast<bool_storage>().value(), tag(), context, ec);
+ break;
+ case json_storage_kind::null_value:
+ visitor.null_value(tag(), context, ec);
+ break;
+ case json_storage_kind::empty_object_value:
+ visitor.begin_object(0, tag(), context, ec);
+ visitor.end_object(context, ec);
+ break;
+ case json_storage_kind::object_value:
+ {
+ bool more = visitor.begin_object(size(), tag(), context, ec);
+ const object& o = object_value();
+ for (auto it = o.begin(); more && it != o.end(); ++it)
+ {
+ visitor.key(string_view_type((it->key()).data(),it->key().length()), context, ec);
+ it->value().dump_noflush(visitor, ec);
+ }
+ if (more)
+ {
+ visitor.end_object(context, ec);
+ }
+ break;
+ }
+ case json_storage_kind::array_value:
+ {
+ bool more = visitor.begin_array(size(), tag(), context, ec);
+ const array& o = array_value();
+ for (const_array_iterator it = o.begin(); more && it != o.end(); ++it)
+ {
+ it->dump_noflush(visitor, ec);
+ }
+ if (more)
+ {
+ visitor.end_array(context, ec);
+ }
+ break;
+ }
+ case json_storage_kind::json_const_pointer:
+ return cast<json_const_pointer_storage>().value()->dump_noflush(visitor, ec);
+ default:
+ break;
+ }
+ }
+
+ friend std::basic_ostream<char_type>& operator<<(std::basic_ostream<char_type>& os, const basic_json& o)
+ {
+ o.dump(os);
+ return os;
+ }
+
+ friend std::basic_istream<char_type>& operator>>(std::basic_istream<char_type>& is, basic_json& o)
+ {
+ json_decoder<basic_json> visitor;
+ basic_json_reader<char_type,stream_source<char_type>> reader(is, visitor);
+ reader.read_next();
+ reader.check_done();
+ if (!visitor.is_valid())
+ {
+ JSONCONS_THROW(json_runtime_error<std::runtime_error>("Failed to parse json stream"));
+ }
+ o = visitor.get_result();
+ return is;
+ }
+
+ friend basic_json deep_copy(const basic_json& other)
+ {
+ switch (other.storage_kind())
+ {
+ case json_storage_kind::array_value:
+ {
+ basic_json j(json_array_arg, other.tag(), other.get_allocator());
+ j.reserve(other.size());
+
+ for (const auto& item : other.array_range())
+ {
+ j.push_back(deep_copy(item));
+ }
+ return j;
+ }
+ case json_storage_kind::object_value:
+ {
+ basic_json j(json_object_arg, other.tag(), other.get_allocator());
+ j.reserve(other.size());
+
+ for (const auto& item : other.object_range())
+ {
+ j.try_emplace(item.key(), deep_copy(item.value()));
+ }
+ return j;
+ }
+ case json_storage_kind::json_const_pointer:
+ return deep_copy(*(other.cast<json_const_pointer_storage>().value()));
+ default:
+ return other;
+ }
+ }
+ };
+
+ // operator==
+
+ template <class Json>
+ typename std::enable_if<type_traits::is_basic_json<Json>::value,bool>::type
+ operator==(const Json& lhs, const Json& rhs) noexcept
+ {
+ return lhs.compare(rhs) == 0;
+ }
+
+ template <class Json, class T>
+ typename std::enable_if<type_traits::is_basic_json<Json>::value && std::is_convertible<T,Json>::value,bool>::type
+ operator==(const Json& lhs, const T& rhs)
+ {
+ return lhs.compare(rhs) == 0;
+ }
+
+ template <class Json, class T>
+ typename std::enable_if<is_proxy<Json>::value && std::is_convertible<T,typename Json::proxied_type>::value,bool>::type
+ operator==(const Json& lhs, const T& rhs)
+ {
+ return lhs.evaluate().compare(rhs) == 0;
+ }
+
+ template <class Json, class T>
+ typename std::enable_if<type_traits::is_basic_json<Json>::value && !is_proxy<T>::value && !type_traits::is_basic_json<T>::value && std::is_convertible<T,Json>::value,bool>::type
+ operator==(const T& lhs, const Json& rhs)
+ {
+ return rhs.compare(lhs) == 0;
+ }
+
+ template <class Json, class T>
+ typename std::enable_if<is_proxy<Json>::value && !is_proxy<T>::value && !type_traits::is_basic_json<T>::value && std::is_convertible<T,typename Json::proxied_type>::value,bool>::type
+ operator==(const T& lhs, const Json& rhs)
+ {
+ return rhs.evaluate().compare(lhs) == 0;
+ }
+
+ // operator!=
+
+ template <class Json>
+ typename std::enable_if<type_traits::is_basic_json<Json>::value,bool>::type
+ operator!=(const Json& lhs, const Json& rhs) noexcept
+ {
+ return lhs.compare(rhs) != 0;
+ }
+
+ template <class Json, class T>
+ typename std::enable_if<type_traits::is_basic_json<Json>::value && std::is_convertible<T,Json>::value,bool>::type
+ operator!=(const Json& lhs, const T& rhs)
+ {
+ return lhs.compare(rhs) != 0;
+ }
+
+ template <class Json, class T>
+ typename std::enable_if<is_proxy<Json>::value && std::is_convertible<T,typename Json::proxied_type>::value,bool>::type
+ operator!=(const Json& lhs, const T& rhs)
+ {
+ return lhs.evaluate().compare(rhs) != 0;
+ }
+
+ template <class Json, class T>
+ typename std::enable_if<type_traits::is_basic_json<Json>::value && !is_proxy<T>::value && !type_traits::is_basic_json<T>::value && std::is_convertible<T,Json>::value,bool>::type
+ operator!=(const T& lhs, const Json& rhs)
+ {
+ return rhs.compare(lhs) != 0;
+ }
+
+ template <class Json, class T>
+ typename std::enable_if<is_proxy<Json>::value && !is_proxy<T>::value && !type_traits::is_basic_json<T>::value && std::is_convertible<T,typename Json::proxied_type>::value,bool>::type
+ operator!=(const T& lhs, const Json& rhs)
+ {
+ return rhs.evaluate().compare(lhs) != 0;
+ }
+
+ // operator<
+
+ template <class Json>
+ typename std::enable_if<type_traits::is_basic_json<Json>::value,bool>::type
+ operator<(const Json& lhs, const Json& rhs) noexcept
+ {
+ return lhs.compare(rhs) < 0;
+ }
+
+ template <class Json, class T>
+ typename std::enable_if<type_traits::is_basic_json<Json>::value && std::is_convertible<T,Json>::value,bool>::type
+ operator<(const Json& lhs, const T& rhs)
+ {
+ return lhs.compare(rhs) < 0;
+ }
+
+ template <class Json, class T>
+ typename std::enable_if<is_proxy<Json>::value && std::is_convertible<T,typename Json::proxied_type>::value,bool>::type
+ operator<(const Json& lhs, const T& rhs)
+ {
+ return lhs.evaluate().compare(rhs) < 0;
+ }
+
+ template <class Json, class T>
+ typename std::enable_if<type_traits::is_basic_json<Json>::value && !is_proxy<T>::value && !type_traits::is_basic_json<T>::value && std::is_convertible<T,Json>::value,bool>::type
+ operator<(const T& lhs, const Json& rhs)
+ {
+ return rhs.compare(lhs) > 0;
+ }
+
+ template <class Json, class T>
+ typename std::enable_if<is_proxy<Json>::value && !is_proxy<T>::value && !type_traits::is_basic_json<T>::value && std::is_convertible<T,typename Json::proxied_type>::value,bool>::type
+ operator<(const T& lhs, const Json& rhs)
+ {
+ return rhs.evaluate().compare(lhs) > 0;
+ }
+
+ // operator<=
+
+ template <class Json>
+ typename std::enable_if<type_traits::is_basic_json<Json>::value,bool>::type
+ operator<=(const Json& lhs, const Json& rhs) noexcept
+ {
+ return lhs.compare(rhs) <= 0;
+ }
+
+ template <class Json, class T>
+ typename std::enable_if<type_traits::is_basic_json<Json>::value && std::is_convertible<T,Json>::value,bool>::type
+ operator<=(const Json& lhs, const T& rhs)
+ {
+ return lhs.compare(rhs) <= 0;
+ }
+
+ template <class Json, class T>
+ typename std::enable_if<is_proxy<Json>::value && std::is_convertible<T,typename Json::proxied_type>::value,bool>::type
+ operator<=(const Json& lhs, const T& rhs)
+ {
+ return lhs.evaluate().compare(rhs) <= 0;
+ }
+
+ template <class Json, class T>
+ typename std::enable_if<type_traits::is_basic_json<Json>::value && !is_proxy<T>::value && !type_traits::is_basic_json<T>::value && std::is_convertible<T,Json>::value,bool>::type
+ operator<=(const T& lhs, const Json& rhs)
+ {
+ return rhs.compare(lhs) >= 0;
+ }
+
+ template <class Json, class T>
+ typename std::enable_if<is_proxy<Json>::value && !is_proxy<T>::value && !type_traits::is_basic_json<T>::value && std::is_convertible<T,typename Json::proxied_type>::value,bool>::type
+ operator<=(const T& lhs, const Json& rhs)
+ {
+ return rhs.evaluate().compare(lhs) >= 0;
+ }
+
+ // operator>
+
+ template <class Json>
+ typename std::enable_if<type_traits::is_basic_json<Json>::value,bool>::type
+ operator>(const Json& lhs, const Json& rhs) noexcept
+ {
+ return lhs.compare(rhs) > 0;
+ }
+
+ template <class Json, class T>
+ typename std::enable_if<type_traits::is_basic_json<Json>::value && std::is_convertible<T,Json>::value,bool>::type
+ operator>(const Json& lhs, const T& rhs)
+ {
+ return lhs.compare(rhs) > 0;
+ }
+
+ template <class Json, class T>
+ typename std::enable_if<is_proxy<Json>::value && std::is_convertible<T,typename Json::proxied_type>::value,bool>::type
+ operator>(const Json& lhs, const T& rhs)
+ {
+ return lhs.evaluate().compare(rhs) > 0;
+ }
+
+ template <class Json, class T>
+ typename std::enable_if<type_traits::is_basic_json<Json>::value && !is_proxy<T>::value && !type_traits::is_basic_json<T>::value && std::is_convertible<T,Json>::value,bool>::type
+ operator>(const T& lhs, const Json& rhs)
+ {
+ return rhs.compare(lhs) < 0;
+ }
+
+ template <class Json, class T>
+ typename std::enable_if<is_proxy<Json>::value && !is_proxy<T>::value && !type_traits::is_basic_json<T>::value && std::is_convertible<T,typename Json::proxied_type>::value,bool>::type
+ operator>(const T& lhs, const Json& rhs)
+ {
+ return rhs.evaluate().compare(lhs) < 0;
+ }
+
+ // operator>=
+
+ template <class Json>
+ typename std::enable_if<type_traits::is_basic_json<Json>::value,bool>::type
+ operator>=(const Json& lhs, const Json& rhs) noexcept
+ {
+ return lhs.compare(rhs) >= 0;
+ }
+
+ template <class Json, class T>
+ typename std::enable_if<type_traits::is_basic_json<Json>::value && std::is_convertible<T,Json>::value,bool>::type
+ operator>=(const Json& lhs, const T& rhs)
+ {
+ return lhs.compare(rhs) >= 0;
+ }
+
+ template <class Json, class T>
+ typename std::enable_if<is_proxy<Json>::value && std::is_convertible<T,typename Json::proxied_type>::value,bool>::type
+ operator>=(const Json& lhs, const T& rhs)
+ {
+ return lhs.evaluate().compare(rhs) >= 0;
+ }
+
+ template <class Json, class T>
+ typename std::enable_if<type_traits::is_basic_json<Json>::value && !is_proxy<T>::value && !type_traits::is_basic_json<T>::value && std::is_convertible<T,Json>::value,bool>::type
+ operator>=(const T& lhs, const Json& rhs)
+ {
+ return rhs.compare(lhs) <= 0;
+ }
+
+ template <class Json, class T>
+ typename std::enable_if<is_proxy<Json>::value && !is_proxy<T>::value && !type_traits::is_basic_json<T>::value && std::is_convertible<T,typename Json::proxied_type>::value,bool>::type
+ operator>=(const T& lhs, const Json& rhs)
+ {
+ return rhs.evaluate().compare(lhs) <= 0;
+ }
+
+ // swap
+
+ template <class Json>
+ void swap(typename Json::key_value_type& a, typename Json::key_value_type& b) noexcept
+ {
+ a.swap(b);
+ }
+
+ using json = basic_json<char,sorted_policy,std::allocator<char>>;
+ using wjson = basic_json<wchar_t,sorted_policy,std::allocator<char>>;
+ using ojson = basic_json<char, order_preserving_policy, std::allocator<char>>;
+ using wojson = basic_json<wchar_t, order_preserving_policy, std::allocator<char>>;
+
+ #if !defined(JSONCONS_NO_DEPRECATED)
+ JSONCONS_DEPRECATED_MSG("Instead, use wojson") typedef basic_json<wchar_t, order_preserving_policy, std::allocator<wchar_t>> owjson;
+ JSONCONS_DEPRECATED_MSG("Instead, use json_decoder<json>") typedef json_decoder<json> json_deserializer;
+ JSONCONS_DEPRECATED_MSG("Instead, use json_decoder<wjson>") typedef json_decoder<wjson> wjson_deserializer;
+ JSONCONS_DEPRECATED_MSG("Instead, use json_decoder<ojson>") typedef json_decoder<ojson> ojson_deserializer;
+ JSONCONS_DEPRECATED_MSG("Instead, use json_decoder<wojson>") typedef json_decoder<wojson> wojson_deserializer;
+ #endif
+
+ inline namespace literals {
+
+ inline
+ jsoncons::json operator "" _json(const char* s, std::size_t n)
+ {
+ return jsoncons::json::parse(jsoncons::json::string_view_type(s, n));
+ }
+
+ inline
+ jsoncons::wjson operator "" _json(const wchar_t* s, std::size_t n)
+ {
+ return jsoncons::wjson::parse(jsoncons::wjson::string_view_type(s, n));
+ }
+
+ inline
+ jsoncons::ojson operator "" _ojson(const char* s, std::size_t n)
+ {
+ return jsoncons::ojson::parse(jsoncons::ojson::string_view_type(s, n));
+ }
+
+ inline
+ jsoncons::wojson operator "" _ojson(const wchar_t* s, std::size_t n)
+ {
+ return jsoncons::wojson::parse(jsoncons::wojson::string_view_type(s, n));
+ }
+
+ } // inline namespace literals
+
+} // namespace jsoncons
+
+#endif
diff --git a/include/jsoncons/bigint.hpp b/include/jsoncons/bigint.hpp
new file mode 100644
index 0000000..223b697
--- /dev/null
+++ b/include/jsoncons/bigint.hpp
@@ -0,0 +1,1611 @@
+// Copyright 2018 vDaniel 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_BIGINT_HPP
+#define JSONCONS_BIGINT_HPP
+
+#include <cstdint>
+#include <vector> // std::vector
+#include <iostream>
+#include <climits>
+#include <cassert> // assert
+#include <limits> // std::numeric_limits
+#include <algorithm> // std::max, std::min, std::reverse
+#include <string> // std::string
+#include <cstring> // std::memcpy
+#include <cmath> // std::fmod
+#include <memory> // std::allocator
+#include <initializer_list> // std::initializer_list
+#include <type_traits> // std::enable_if
+#include <jsoncons/config/jsoncons_config.hpp>
+
+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 {
+
+ template <class Allocator>
+ class basic_bigint_base
+ {
+ public:
+ using allocator_type = Allocator;
+ using basic_type_allocator_type = typename std::allocator_traits<allocator_type>:: template rebind_alloc<uint64_t>;
+
+ private:
+ basic_type_allocator_type alloc_;
+ public:
+ using allocator_traits_type = std::allocator_traits<allocator_type>;
+ 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 = typename allocator_traits_type::size_type;
+ using pointer_traits = std::pointer_traits<pointer>;
+
+ basic_bigint_base()
+ : alloc_()
+ {
+ }
+ explicit basic_bigint_base(const allocator_type& alloc)
+ : alloc_(basic_type_allocator_type(alloc))
+ {
+ }
+
+ basic_type_allocator_type get_allocator() const
+ {
+ return alloc_;
+ }
+ };
+
+} // namespace detail
+
+template <class Allocator = std::allocator<uint64_t>>
+class basic_bigint : protected detail::basic_bigint_base<Allocator>
+{
+ using base_t = detail::basic_bigint_base<Allocator>;
+
+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<Allocator>;
+
+ static constexpr uint64_t max_basic_type = (std::numeric_limits<uint64_t>::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;
+
+ 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);
+
+private:
+
+ struct common_storage
+ {
+ uint8_t is_dynamic_:1;
+ uint8_t is_negative_:1;
+ size_type length_;
+ };
+
+ struct short_storage
+ {
+ uint8_t is_dynamic_:1;
+ uint8_t is_negative_:1;
+ size_type length_;
+ uint64_t values_[2];
+
+ short_storage()
+ : is_dynamic_(false),
+ is_negative_(false),
+ length_(0),
+ values_{0,0}
+ {
+ }
+
+ template <class T>
+ short_storage(T n,
+ typename std::enable_if<std::is_integral<T>::value &&
+ sizeof(T) <= sizeof(int64_t) &&
+ std::is_signed<T>::value>::type* = 0)
+ : is_dynamic_(false),
+ is_negative_(n < 0),
+ length_(n == 0 ? 0 : 1)
+ {
+ values_[0] = n < 0 ? (uint64_t(0)-static_cast<uint64_t>(n)) : static_cast<uint64_t>(n);
+ values_[1] = 0;
+ }
+
+ template <class T>
+ short_storage(T n,
+ typename std::enable_if<std::is_integral<T>::value &&
+ sizeof(T) <= sizeof(int64_t) &&
+ !std::is_signed<T>::value>::type* = 0)
+ : is_dynamic_(false),
+ is_negative_(false),
+ length_(n == 0 ? 0 : 1)
+ {
+ values_[0] = n;
+ values_[1] = 0;
+ }
+
+ template <class T>
+ short_storage(T n,
+ typename std::enable_if<std::is_integral<T>::value &&
+ sizeof(int64_t) < sizeof(T) &&
+ std::is_signed<T>::value>::type* = 0)
+ : is_dynamic_(false),
+ is_negative_(n < 0),
+ length_(n == 0 ? 0 : 2)
+ {
+ using unsigned_type = typename std::make_unsigned<T>::type;
+
+ auto u = n < 0 ? (unsigned_type(0)-static_cast<unsigned_type>(n)) : static_cast<unsigned_type>(n);
+ values_[0] = uint64_t(u & max_basic_type);;
+ u >>= basic_type_bits;
+ values_[1] = uint64_t(u & max_basic_type);;
+ }
+
+ template <class T>
+ short_storage(T n,
+ typename std::enable_if<std::is_integral<T>::value &&
+ sizeof(int64_t) < sizeof(T) &&
+ !std::is_signed<T>::value>::type* = 0)
+ : is_dynamic_(false),
+ is_negative_(false),
+ length_(n == 0 ? 0 : 2)
+ {
+ values_[0] = uint64_t(n & max_basic_type);;
+ n >>= basic_type_bits;
+ values_[1] = uint64_t(n & max_basic_type);;
+ }
+
+ short_storage(const short_storage& stor)
+ : is_dynamic_(false),
+ is_negative_(stor.is_negative_),
+ length_(stor.length_)
+ {
+ values_[0] = stor.values_[0];
+ values_[1] = stor.values_[1];
+ }
+
+ short_storage& operator=(const short_storage& stor) = delete;
+ };
+
+ struct dynamic_storage
+ {
+ using real_allocator_type = typename std::allocator_traits<Allocator>:: template rebind_alloc<uint64_t>;
+ using pointer = typename std::allocator_traits<real_allocator_type>::pointer;
+
+ uint8_t is_dynamic_:1;
+ uint8_t is_negative_:1;
+ size_type length_;
+ size_type capacity_;
+ pointer data_;
+
+ dynamic_storage()
+ : is_dynamic_(true),
+ is_negative_(false),
+ length_(0),
+ capacity_(0),
+ data_(nullptr)
+ {
+ }
+
+ dynamic_storage(const dynamic_storage& stor, const real_allocator_type& alloc)
+ : is_dynamic_(true),
+ is_negative_(stor.is_negative_),
+ length_(stor.length_),
+ capacity_(0),
+ data_(nullptr)
+ {
+ create(stor.length_, alloc);
+ std::memcpy(data_, stor.data_, stor.length_*sizeof(uint64_t));
+ }
+
+ dynamic_storage(dynamic_storage&& stor) noexcept
+ : is_dynamic_(true),
+ is_negative_(stor.is_negative_),
+ length_(stor.length_),
+ capacity_(stor.capacity_),
+ data_(stor.data_)
+ {
+ stor.length_ = 0;
+ stor.capacity_ = 0;
+ stor.data_ = nullptr;
+ }
+
+ void create(size_type length, real_allocator_type alloc)
+ {
+ capacity_ = round_up(length);
+ data_ = std::allocator_traits<real_allocator_type>::allocate(alloc, capacity_);
+ JSONCONS_TRY
+ {
+ std::allocator_traits<real_allocator_type>::construct(alloc, type_traits::to_plain_pointer(data_));
+ }
+ JSONCONS_CATCH(...)
+ {
+ std::allocator_traits<real_allocator_type>::deallocate(alloc, data_, capacity_);
+ JSONCONS_RETHROW;
+ }
+ }
+
+ void destroy(const real_allocator_type& a) noexcept
+ {
+ if (data_ != nullptr)
+ {
+ real_allocator_type alloc(a);
+
+ std::allocator_traits<real_allocator_type>::destroy(alloc, type_traits::to_plain_pointer(data_));
+ std::allocator_traits<real_allocator_type>::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<real_allocator_type>::allocate(alloc, capacity_new);
+ if (length_ > 0)
+ {
+ std::memcpy( data_, data_old, length_*sizeof(uint64_t));
+ }
+ if (capacity_ > 0 && data_ != nullptr)
+ {
+ std::allocator_traits<real_allocator_type>::deallocate(alloc, data_old, capacity_);
+ }
+ capacity_ = capacity_new;
+ }
+
+ // Find suitable new block size
+ constexpr size_type round_up(size_type i) const
+ {
+ return (i/word_length + 1) * word_length;
+ }
+ };
+
+ union
+ {
+ common_storage common_stor_;
+ short_storage short_stor_;
+ dynamic_storage dynamic_stor_;
+ };
+
+public:
+ basic_bigint()
+ {
+ ::new (&short_stor_) short_storage();
+ }
+
+ explicit basic_bigint(const Allocator& alloc)
+ : base_t(alloc)
+ {
+ ::new (&short_stor_) short_storage();
+ }
+
+
+ basic_bigint(const basic_bigint<Allocator>& n)
+ : base_t(n.get_allocator())
+ {
+ if (!n.is_dynamic())
+ {
+ ::new (&short_stor_) short_storage(n.short_stor_);
+ }
+ else
+ {
+ ::new (&dynamic_stor_) dynamic_storage(n.dynamic_stor_, get_allocator());
+ }
+ }
+
+ basic_bigint(basic_bigint<Allocator>&& other) noexcept
+ : base_t(other.get_allocator())
+ {
+ if (!other.is_dynamic())
+ {
+ ::new (&short_stor_) short_storage(other.short_stor_);
+ }
+ else
+ {
+ ::new (&dynamic_stor_) dynamic_storage(std::move(other.dynamic_stor_));
+ }
+ }
+
+ template <class Integer>
+ basic_bigint(Integer n,
+ typename std::enable_if<std::is_integral<Integer>::value>::type* = 0)
+ {
+ ::new (&short_stor_) short_storage(n);
+ }
+
+ ~basic_bigint() noexcept
+ {
+ destroy();
+ }
+
+ constexpr bool is_dynamic() const
+ {
+ return common_stor_.is_dynamic_;
+ }
+
+ constexpr size_type length() const
+ {
+ return common_stor_.length_;
+ }
+
+ constexpr size_type capacity() const
+ {
+ return is_dynamic() ? dynamic_stor_.capacity_ : 2;
+ }
+
+ bool is_negative() const
+ {
+ return common_stor_.is_negative_;
+ }
+
+ void is_negative(bool value)
+ {
+ common_stor_.is_negative_ = value;
+ }
+
+ constexpr const uint64_t* data() const
+ {
+ return is_dynamic() ? dynamic_stor_.data_ : short_stor_.values_;
+ }
+
+ uint64_t* data()
+ {
+ return is_dynamic() ? dynamic_stor_.data_ : short_stor_.values_;
+ }
+
+ template <class CharT>
+ static basic_bigint<Allocator> from_string(const std::basic_string<CharT>& s)
+ {
+ return from_string(s.data(), s.length());
+ }
+
+ template <class CharT>
+ static basic_bigint<Allocator> from_string(const CharT* s)
+ {
+ return from_string(s, std::char_traits<CharT>::length(s));
+ }
+
+ template <class CharT>
+ static basic_bigint<Allocator> from_string(const CharT* data, size_type length)
+ {
+ bool neg;
+ if (*data == '-')
+ {
+ neg = true;
+ data++;
+ --length;
+ }
+ else
+ {
+ neg = false;
+ }
+
+ basic_bigint<Allocator> v = 0;
+ for (size_type 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 * 10) + (uint64_t)(c - '0');
+ break;
+ default:
+ JSONCONS_THROW(std::runtime_error(std::string("Invalid digit ") + "\'" + (char)c + "\'"));
+ }
+ }
+
+ if (neg)
+ {
+ v.common_stor_.is_negative_ = true;
+ }
+
+ return v;
+ }
+
+ template <class CharT>
+ static basic_bigint<Allocator> from_string_radix(const CharT* data, size_type length, uint8_t radix)
+ {
+ if (!(radix >= 2 && radix <= 16))
+ {
+ JSONCONS_THROW(std::runtime_error("Unsupported radix"));
+ }
+
+ bool neg;
+ if (*data == '-')
+ {
+ neg = true;
+ data++;
+ --length;
+ }
+ else
+ {
+ neg = false;
+ }
+
+ basic_bigint<Allocator> v = 0;
+ for (size_type i = 0; i < length; i++)
+ {
+ CharT c = data[i];
+ uint64_t 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');
+ break;
+ case 'a':case 'b':case 'c':case 'd':case 'e':case 'f':
+ d = (uint64_t)(c - ('a' - 10));
+ break;
+ case 'A':case 'B':case 'C':case 'D':case 'E':case 'F':
+ d = (uint64_t)(c - ('A' - 10));
+ break;
+ default:
+ JSONCONS_THROW(std::runtime_error(std::string("Invalid digit in radix ") + std::to_string(radix) + ": \'" + (char)c + "\'"));
+ }
+ if (d >= radix)
+ {
+ JSONCONS_THROW(std::runtime_error(std::string("Invalid digit in radix ") + std::to_string(radix) + ": \'" + (char)c + "\'"));
+ }
+ v = (v * radix) + d;
+ }
+
+ if ( neg )
+ {
+ v.common_stor_.is_negative_ = true;
+ }
+ return v;
+ }
+
+ static basic_bigint from_bytes_be(int signum, const uint8_t* str, std::size_t 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.
+ double bits = radix_log2 * n;
+ double big_digits = std::ceil(bits / 64.0);
+ //std::cout << "ESTIMATED: " << big_digits << "\n";
+
+ bigint_type v = 0;
+ v.reserve(static_cast<std::size_t>(big_digits));
+
+ if (n > 0)
+ {
+ for (std::size_t i = 0; i < n; i++)
+ {
+ v = (v * 256) + (uint64_t)(str[i]);
+ }
+ }
+ //std::cout << "ACTUAL: " << v.length() << "\n";
+
+ if (signum < 0)
+ {
+ v.common_stor_.is_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 n)
+ {
+ size_type len_old = common_stor_.length_;
+ reserve(n);
+ common_stor_.length_ = n;
+ if ( common_stor_.length_ > len_old )
+ {
+ memset( data()+len_old, 0, (common_stor_.length_ - len_old)*sizeof(uint64_t) );
+ }
+ }
+
+ 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[2] = {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());
+ }
+ }
+ }
+
+ // operators
+
+ bool operator!() const
+ {
+ return length() == 0 ? true : false;
+ }
+
+ basic_bigint operator-() const
+ {
+ basic_bigint<Allocator> v(*this);
+ v.common_stor_.is_negative_ = !v.is_negative();
+ return v;
+ }
+
+ basic_bigint& operator=( const basic_bigint<Allocator>& y )
+ {
+ if ( this != &y )
+ {
+ resize( y.length() );
+ common_stor_.is_negative_ = y.is_negative();
+ if ( y.length() > 0 )
+ {
+ std::memcpy( data(), y.data(), y.length()*sizeof(uint64_t) );
+ }
+ }
+ return *this;
+ }
+
+ basic_bigint& operator+=( const basic_bigint<Allocator>& y )
+ {
+ if ( is_negative() != y.is_negative() )
+ return *this -= -y;
+ uint64_t d;
+ uint64_t carry = 0;
+
+ resize( (std::max)(y.length(), length()) + 1 );
+
+ for (size_type i = 0; i < length(); i++ )
+ {
+ if ( i >= y.length() && carry == 0 )
+ break;
+ d = data()[i] + carry;
+ carry = d < carry;
+ if ( i < y.length() )
+ {
+ data()[i] = d + y.data()[i];
+ if ( data()[i] < d )
+ carry = 1;
+ }
+ else
+ data()[i] = d;
+ }
+ reduce();
+ return *this;
+ }
+
+ basic_bigint& operator-=( const basic_bigint<Allocator>& y )
+ {
+ 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++ )
+ {
+ if ( i >= y.length() && borrow == 0 )
+ break;
+ d = data()[i] - borrow;
+ borrow = d > data()[i];
+ if ( i < y.length())
+ {
+ data()[i] = d - y.data()[i];
+ if ( data()[i] > d )
+ borrow = 1;
+ }
+ else
+ data()[i] = d;
+ }
+ reduce();
+ return *this;
+ }
+
+ basic_bigint& operator*=( int64_t y )
+ {
+ *this *= uint64_t(y < 0 ? -y : y);
+ if ( y < 0 )
+ common_stor_.is_negative_ = !is_negative();
+ return *this;
+ }
+
+ basic_bigint& operator*=( uint64_t y )
+ {
+ size_type len0 = length();
+ uint64_t hi;
+ uint64_t lo;
+ uint64_t dig = data()[0];
+ uint64_t carry = 0;
+
+ resize( length() + 1 );
+
+ size_type i = 0;
+ for (i = 0; i < len0; i++ )
+ {
+ DDproduct( dig, y, hi, lo );
+ data()[i] = lo + carry;
+ dig = data()[i+1];
+ carry = hi + (data()[i] < lo);
+ }
+ data()[i] = carry;
+ reduce();
+ return *this;
+ }
+
+ basic_bigint& operator*=( basic_bigint<Allocator> y )
+ {
+ if ( length() == 0 || y.length() == 0 )
+ return *this = 0;
+ bool difSigns = is_negative() != y.is_negative();
+ if ( length() + y.length() == 2 ) // length() = y.length() = 1
+ {
+ uint64_t a = data()[0], b = y.data()[0];
+ data()[0] = a * b;
+ if ( data()[0] / a != b )
+ {
+ resize( 2 );
+ DDproduct( a, b, data()[1], data()[0] );
+ }
+ common_stor_.is_negative_ = difSigns;
+ return *this;
+ }
+ if ( length() == 1 ) // && y.length() > 1
+ {
+ uint64_t digit = data()[0];
+ *this = y;
+ *this *= digit;
+ }
+ else
+ {
+ if ( y.length() == 1 )
+ *this *= y.data()[0];
+ else
+ {
+ size_type lenProd = length() + y.length(), jA, jB;
+ uint64_t sumHi = 0, sumLo, hi, lo,
+ sumLo_old, sumHi_old, carry=0;
+ basic_bigint<Allocator> x = *this;
+ resize( lenProd ); // Give *this length lenProd
+
+ for (size_type i = 0; i < lenProd; i++ )
+ {
+ sumLo = sumHi;
+ sumHi = carry;
+ carry = 0;
+ for ( jA=0; jA < x.length(); jA++ )
+ {
+ jB = i - jA;
+ if ( jB >= 0 && jB < y.length() )
+ {
+ 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);
+ }
+ }
+ data()[i] = sumLo;
+ }
+ }
+ }
+ reduce();
+ common_stor_.is_negative_ = difSigns;
+ return *this;
+ }
+
+ basic_bigint& operator/=( const basic_bigint<Allocator>& divisor )
+ {
+ basic_bigint<Allocator> r;
+ divide( divisor, *this, r, false );
+ return *this;
+ }
+
+ basic_bigint& operator%=( const basic_bigint<Allocator>& divisor )
+ {
+ basic_bigint<Allocator> q;
+ divide( divisor, q, *this, true );
+ return *this;
+ }
+
+ basic_bigint& operator<<=( uint64_t k )
+ {
+ size_type q = (size_type)(k / basic_type_bits);
+ if ( q ) // Increase common_stor_.length_ by q:
+ {
+ resize(length() + q);
+ for (size_type i = length(); i-- > 0; )
+ data()[i] = ( i < q ? 0 : data()[i - q]);
+ k %= basic_type_bits;
+ }
+ if ( k ) // 0 < k < basic_type_bits:
+ {
+ uint64_t k1 = basic_type_bits - k;
+ uint64_t mask = (uint64_t(1) << k) - uint64_t(1);
+ resize( length() + 1 );
+ for (size_type i = length(); i-- > 0; )
+ {
+ data()[i] <<= k;
+ if ( i > 0 )
+ data()[i] |= (data()[i-1] >> k1) & mask;
+ }
+ }
+ reduce();
+ return *this;
+ }
+
+ basic_bigint& operator>>=(uint64_t k)
+ {
+ size_type q = (size_type)(k / basic_type_bits);
+ if ( q >= length() )
+ {
+ resize( 0 );
+ return *this;
+ }
+ if (q > 0)
+ {
+ memmove( data(), data()+q, (size_type)((length() - q)*sizeof(uint64_t)) );
+ resize( length() - q );
+ k %= basic_type_bits;
+ if ( k == 0 )
+ {
+ reduce();
+ return *this;
+ }
+ }
+
+ size_type n = (size_type)(length() - 1);
+ int64_t k1 = basic_type_bits - k;
+ uint64_t mask = (uint64_t(1) << k) - 1;
+ for (size_type i = 0; i <= n; i++)
+ {
+ data()[i] >>= k;
+ if ( i < n )
+ data()[i] |= ((data()[i+1] & mask) << k1);
+ }
+ reduce();
+ return *this;
+ }
+
+ basic_bigint& operator++()
+ {
+ *this += 1;
+ return *this;
+ }
+
+ basic_bigint<Allocator> operator++(int)
+ {
+ basic_bigint<Allocator> old = *this;
+ ++(*this);
+ return old;
+ }
+
+ basic_bigint<Allocator>& operator--()
+ {
+ *this -= 1;
+ return *this;
+ }
+
+ basic_bigint<Allocator> operator--(int)
+ {
+ basic_bigint<Allocator> old = *this;
+ --(*this);
+ return old;
+ }
+
+ basic_bigint& operator|=( const basic_bigint<Allocator>& 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;
+
+ while ( q >= qBegin )
+ {
+ *p-- |= *q--;
+ }
+
+ reduce();
+
+ return *this;
+ }
+
+ basic_bigint& operator^=( const basic_bigint<Allocator>& 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;
+
+ while ( q >= qBegin )
+ {
+ *p-- ^= *q--;
+ }
+
+ reduce();
+
+ return *this;
+ }
+
+ basic_bigint& operator&=( const basic_bigint<Allocator>& a )
+ {
+ size_type old_length = length();
+
+ 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 )
+ {
+ *p-- &= *q--;
+ }
+
+ if ( old_length > length() )
+ {
+ memset( data() + length(), 0, old_length - length() );
+ }
+
+ reduce();
+
+ return *this;
+ }
+
+ explicit operator bool() const
+ {
+ return length() != 0 ? true : false;
+ }
+
+ explicit operator int64_t() const
+ {
+ int64_t x = 0;
+ if ( length() > 0 )
+ {
+ x = data() [0];
+ }
+
+ return is_negative() ? -x : x;
+ }
+
+ explicit operator uint64_t() const
+ {
+ uint64_t u = 0;
+ if ( length() > 0 )
+ {
+ u = data() [0];
+ }
+
+ return u;
+ }
+
+ explicit operator double() const
+ {
+ double x = 0.0;
+ double factor = 1.0;
+ double values = (double)max_basic_type + 1.0;
+
+ const uint64_t* p = begin();
+ const uint64_t* pEnd = end();
+ while ( p < pEnd )
+ {
+ x += *p*factor;
+ factor *= values;
+ ++p;
+ }
+
+ return is_negative() ? -x : x;
+ }
+
+ explicit operator long double() const
+ {
+ long double x = 0.0;
+ long double factor = 1.0;
+ long double values = (long double)max_basic_type + 1.0;
+
+ const uint64_t* p = begin();
+ const uint64_t* pEnd = end();
+ while ( p < pEnd )
+ {
+ x += *p*factor;
+ factor *= values;
+ ++p;
+ }
+
+ return is_negative() ? -x : x;
+ }
+
+ template <typename Alloc>
+ void write_bytes_be(int& signum, std::vector<uint8_t,Alloc>& data) const
+ {
+ basic_bigint<Allocator> n(*this);
+ signum = (n < 0) ? -1 : (n > 0 ? 1 : 0);
+
+ basic_bigint<Allocator> divisor(256);
+
+ while (n >= 256)
+ {
+ basic_bigint<Allocator> q;
+ basic_bigint<Allocator> r;
+ n.divide(divisor, q, r, true);
+ n = q;
+ data.push_back((uint8_t)(uint64_t)r);
+ }
+ if (n >= 0)
+ {
+ data.push_back((uint8_t)(uint64_t)n);
+ }
+ std::reverse(data.begin(),data.end());
+ }
+
+ std::string to_string() const
+ {
+ std::string s;
+ write_string(s);
+ return s;
+ }
+
+ template <typename Ch, typename Traits, typename Alloc>
+ void write_string(std::basic_string<Ch,Traits,Alloc>& data) const
+ {
+ basic_bigint<Allocator> v(*this);
+
+ size_t len = (v.length() * basic_type_bits / 3) + 2;
+ data.reserve(len);
+
+ static uint64_t p10 = 1;
+ static uint64_t ip10 = 0;
+
+ if ( v.length() == 0 )
+ {
+ data.push_back('0');
+ }
+ else
+ {
+ uint64_t r;
+ if ( p10 == 1 )
+ {
+ while ( p10 <= (std::numeric_limits<uint64_t>::max)()/10 )
+ {
+ p10 *= 10;
+ ip10++;
+ }
+ }
+ // p10 is max unsigned power of 10
+ basic_bigint<Allocator> R;
+ basic_bigint<Allocator> LP10 = p10; // LP10 = p10 = ::pow(10, ip10)
+
+ do
+ {
+ v.divide( LP10, v, R, true );
+ r = (R.length() ? R.data()[0] : 0);
+ for ( size_type j=0; j < ip10; j++ )
+ {
+ data.push_back(char(r % 10 + '0'));
+ r /= 10;
+ if ( r + v.length() == 0 )
+ break;
+ }
+ }
+ while ( v.length() );
+ if (is_negative())
+ {
+ data.push_back('-');
+ }
+ std::reverse(data.begin(),data.end());
+ }
+ }
+
+ std::string to_string_hex() const
+ {
+ std::string s;
+ write_string_hex(s);
+ return s;
+ }
+
+ template <typename Ch, typename Traits, typename Alloc>
+ void write_string_hex(std::basic_string<Ch,Traits,Alloc>& data) const
+ {
+ basic_bigint<Allocator> v(*this);
+
+ std::size_t len = (v.length() * basic_bigint<Allocator>::basic_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 )
+ {
+ data.push_back('0');
+ }
+ else
+ {
+ uint64_t r;
+ if ( p10 == 1 )
+ {
+ while ( p10 <= (std::numeric_limits<uint64_t>::max)()/16 )
+ {
+ p10 *= 16;
+ ip10++;
+ }
+ } // p10 is max unsigned power of 16
+ basic_bigint<Allocator> R;
+ basic_bigint<Allocator> LP10 = p10; // LP10 = p10 = ::pow(16, ip10)
+ do
+ {
+ v.divide( LP10, v, R, true );
+ r = (R.length() ? R.data()[0] : 0);
+ for ( size_type j=0; j < ip10; j++ )
+ {
+ uint8_t c = r % 16;
+ data.push_back((c < 10) ? ('0' + c) : ('A' - 10 + c));
+ r /= 16;
+ if ( r + v.length() == 0 )
+ break;
+ }
+ }
+ while (v.length());
+
+ if (is_negative())
+ {
+ data.push_back('-');
+ }
+ std::reverse(data.begin(),data.end());
+ }
+ }
+
+// Global Operators
+
+ friend bool operator==( const basic_bigint<Allocator>& x, const basic_bigint<Allocator>& y ) noexcept
+ {
+ return x.compare(y) == 0 ? true : false;
+ }
+
+ friend bool operator==( const basic_bigint<Allocator>& x, int y ) noexcept
+ {
+ return x.compare(y) == 0 ? true : false;
+ }
+
+ friend bool operator!=( const basic_bigint<Allocator>& x, const basic_bigint<Allocator>& y ) noexcept
+ {
+ return x.compare(y) != 0 ? true : false;
+ }
+
+ friend bool operator!=( const basic_bigint<Allocator>& x, int y ) noexcept
+ {
+ return x.compare(basic_bigint<Allocator>(y)) != 0 ? true : false;
+ }
+
+ friend bool operator<( const basic_bigint<Allocator>& x, const basic_bigint<Allocator>& y ) noexcept
+ {
+ return x.compare(y) < 0 ? true : false;
+ }
+
+ friend bool operator<( const basic_bigint<Allocator>& x, int64_t y ) noexcept
+ {
+ return x.compare(y) < 0 ? true : false;
+ }
+
+ friend bool operator>( const basic_bigint<Allocator>& x, const basic_bigint<Allocator>& y ) noexcept
+ {
+ return x.compare(y) > 0 ? true : false;
+ }
+
+ friend bool operator>( const basic_bigint<Allocator>& x, int y ) noexcept
+ {
+ return x.compare(basic_bigint<Allocator>(y)) > 0 ? true : false;
+ }
+
+ friend bool operator<=( const basic_bigint<Allocator>& x, const basic_bigint<Allocator>& y ) noexcept
+ {
+ return x.compare(y) <= 0 ? true : false;
+ }
+
+ friend bool operator<=( const basic_bigint<Allocator>& x, int y ) noexcept
+ {
+ return x.compare(y) <= 0 ? true : false;
+ }
+
+ friend bool operator>=( const basic_bigint<Allocator>& x, const basic_bigint<Allocator>& y ) noexcept
+ {
+ return x.compare(y) >= 0 ? true : false;
+ }
+
+ friend bool operator>=( const basic_bigint<Allocator>& x, int y ) noexcept
+ {
+ return x.compare(y) >= 0 ? true : false;
+ }
+
+ friend basic_bigint<Allocator> operator+( basic_bigint<Allocator> x, const basic_bigint<Allocator>& y )
+ {
+ return x += y;
+ }
+
+ friend basic_bigint<Allocator> operator+( basic_bigint<Allocator> x, int64_t y )
+ {
+ return x += y;
+ }
+
+ friend basic_bigint<Allocator> operator-( basic_bigint<Allocator> x, const basic_bigint<Allocator>& y )
+ {
+ return x -= y;
+ }
+
+ friend basic_bigint<Allocator> operator-( basic_bigint<Allocator> x, int64_t y )
+ {
+ return x -= y;
+ }
+
+ friend basic_bigint<Allocator> operator*( int64_t x, const basic_bigint<Allocator>& y )
+ {
+ return basic_bigint<Allocator>(y) *= x;
+ }
+
+ friend basic_bigint<Allocator> operator*( basic_bigint<Allocator> x, const basic_bigint<Allocator>& y )
+ {
+ return x *= y;
+ }
+
+ friend basic_bigint<Allocator> operator*( basic_bigint<Allocator> x, int64_t y )
+ {
+ return x *= y;
+ }
+
+ friend basic_bigint<Allocator> operator/( basic_bigint<Allocator> x, const basic_bigint<Allocator>& y )
+ {
+ return x /= y;
+ }
+
+ friend basic_bigint<Allocator> operator/( basic_bigint<Allocator> x, int y )
+ {
+ return x /= y;
+ }
+
+ friend basic_bigint<Allocator> operator%( basic_bigint<Allocator> x, const basic_bigint<Allocator>& y )
+ {
+ return x %= y;
+ }
+
+ friend basic_bigint<Allocator> operator<<( basic_bigint<Allocator> u, unsigned k )
+ {
+ return u <<= k;
+ }
+
+ friend basic_bigint<Allocator> operator<<( basic_bigint<Allocator> u, int k )
+ {
+ return u <<= k;
+ }
+
+ friend basic_bigint<Allocator> operator>>( basic_bigint<Allocator> u, unsigned k )
+ {
+ return u >>= k;
+ }
+
+ friend basic_bigint<Allocator> operator>>( basic_bigint<Allocator> u, int k )
+ {
+ return u >>= k;
+ }
+
+ friend basic_bigint<Allocator> operator|( basic_bigint<Allocator> x, const basic_bigint<Allocator>& y )
+ {
+ return x |= y;
+ }
+
+ friend basic_bigint<Allocator> operator|( basic_bigint<Allocator> x, int y )
+ {
+ return x |= y;
+ }
+
+ friend basic_bigint<Allocator> operator|( basic_bigint<Allocator> x, unsigned y )
+ {
+ return x |= y;
+ }
+
+ friend basic_bigint<Allocator> operator^( basic_bigint<Allocator> x, const basic_bigint<Allocator>& y )
+ {
+ return x ^= y;
+ }
+
+ friend basic_bigint<Allocator> operator^( basic_bigint<Allocator> x, int y )
+ {
+ return x ^= y;
+ }
+
+ friend basic_bigint<Allocator> operator^( basic_bigint<Allocator> x, unsigned y )
+ {
+ return x ^= y;
+ }
+
+ friend basic_bigint<Allocator> operator&( basic_bigint<Allocator> x, const basic_bigint<Allocator>& y )
+ {
+ return x &= y;
+ }
+
+ friend basic_bigint<Allocator> operator&( basic_bigint<Allocator> x, int y )
+ {
+ return x &= y;
+ }
+
+ friend basic_bigint<Allocator> operator&( basic_bigint<Allocator> x, unsigned y )
+ {
+ return x &= y;
+ }
+
+ friend basic_bigint<Allocator> abs( const basic_bigint<Allocator>& a )
+ {
+ if ( a.is_negative() )
+ {
+ return -a;
+ }
+ return a;
+ }
+
+ friend basic_bigint<Allocator> power( basic_bigint<Allocator> x, unsigned n )
+ {
+ basic_bigint<Allocator> y = 1;
+
+ while ( n )
+ {
+ if ( n & 1 )
+ {
+ y *= x;
+ }
+ x *= x;
+ n >>= 1;
+ }
+
+ return y;
+ }
+
+ friend basic_bigint<Allocator> sqrt( const basic_bigint<Allocator>& a )
+ {
+ basic_bigint<Allocator> x = a;
+ basic_bigint<Allocator> b = a;
+ basic_bigint<Allocator> q;
+
+ b <<= 1;
+ while ( 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 <class CharT>
+ friend std::basic_ostream<CharT>& operator<<(std::basic_ostream<CharT>& os, const basic_bigint<Allocator>& v)
+ {
+ std::basic_string<CharT> s;
+ v.write_string(s);
+ os << s;
+
+ return os;
+ }
+
+ int compare( const basic_bigint<Allocator>& y ) const noexcept
+ {
+ 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() )
+ code = -1;
+ else if ( length() > y.length() )
+ code = +1;
+ else
+ {
+ for (size_type i = length(); i-- > 0; )
+ {
+ if (data()[i] > y.data()[i])
+ {
+ code = 1;
+ break;
+ }
+ else if (data()[i] < y.data()[i])
+ {
+ code = -1;
+ break;
+ }
+ }
+ }
+ return is_negative() ? -code : code;
+ }
+
+ void divide( basic_bigint<Allocator> denom, basic_bigint<Allocator>& quot, basic_bigint<Allocator>& rem, bool remDesired ) const
+ {
+ if ( denom.length() == 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<Allocator> num = *this;
+ num.common_stor_.is_negative_ = denom.common_stor_.is_negative_ = false;
+ if ( num < denom )
+ {
+ quot = uint64_t(0);
+ rem = num;
+ rem.common_stor_.is_negative_ = rem_neg;
+ return;
+ }
+ if ( denom.length() == 1 && num.length() == 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;
+ return;
+ }
+ else if (denom.length() == 1 && (denom.data()[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; )
+ {
+ dividend = (dHi << basic_type_halfBits) | (data()[i] >> basic_type_halfBits);
+ q1 = dividend/divisor;
+ r = dividend % divisor;
+ dividend = (r << basic_type_halfBits) | (data()[i] & r_mask);
+ q2 = dividend/divisor;
+ dHi = dividend % divisor;
+ quot.data()[i] = (q1 << basic_type_halfBits) | q2;
+ }
+ quot.reduce();
+ rem = dHi;
+ quot.common_stor_.is_negative_ = quot_neg;
+ rem.common_stor_.is_negative_ = rem_neg;
+ return;
+ }
+ basic_bigint<Allocator> num0 = num, denom0 = denom;
+ int second_done = normalize(denom, num, x);
+ size_type l = denom.length() - 1;
+ size_type n = num.length() - 1;
+ quot.resize(n - l);
+ for (size_type i=quot.length(); i-- > 0; )
+ quot.data()[i] = 0;
+ rem = num;
+ if ( rem.data()[n] >= denom.data()[l] )
+ {
+ rem.resize(rem.length() + 1);
+ n++;
+ quot.resize(quot.length() + 1);
+ }
+ uint64_t d = denom.data()[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;
+ }
+ quot.reduce();
+ quot.common_stor_.is_negative_ = quot_neg;
+ if ( remDesired )
+ {
+ unnormalize(rem, x, second_done);
+ rem.common_stor_.is_negative_ = rem_neg;
+ }
+ }
+private:
+ void destroy() noexcept
+ {
+ if (is_dynamic())
+ {
+ dynamic_stor_.destroy(get_allocator());
+ }
+ }
+ void DDproduct( uint64_t A, uint64_t B,
+ uint64_t& hi, uint64_t& 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;
+
+ 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);
+ old = lo;
+ lo += mid2 << basic_type_halfBits;
+ hi += (lo < old) + (mid2 >> basic_type_halfBits);
+ }
+
+ uint64_t DDquotient( uint64_t A, uint64_t B, uint64_t 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;
+ 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);
+ B = x;
+ dLo1 = dLo << basic_type_halfBits;
+ // Increase qHi if necessary:
+ while ( A > dHi || (A == dHi && B >= dLo1) )
+ {
+ x = B - dLo1;
+ A -= dHi + (x > B);
+ B = x;
+ qHi++;
+ }
+ qLo = ((A << basic_type_halfBits) | (B >> basic_type_halfBits))/(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);
+ B = x;
+ // Increase qLo if necessary:
+ while ( A || B >= d )
+ {
+ x = B - d;
+ A -= (x > B);
+ B = x;
+ qLo++;
+ }
+ return (qHi << basic_type_halfBits) + qLo;
+ }
+
+ void subtractmul( uint64_t* a, uint64_t* b, size_type n, uint64_t& q ) const
+ // a -= q * b: b in n positions; correct q if necessary
+ {
+ uint64_t hi, lo, d, carry = 0;
+ size_type i;
+ for ( i = 0; i < n; i++ )
+ {
+ DDproduct( b[i], q, hi, lo );
+ d = a[i];
+ a[i] -= lo;
+ if ( a[i] > d )
+ carry++;
+ d = a[i + 1];
+ a[i + 1] -= hi + carry;
+ carry = a[i + 1] > d;
+ }
+ if ( carry ) // q was too large
+ {
+ q--;
+ carry = 0;
+ for ( i = 0; i < n; i++ )
+ {
+ d = a[i] + carry;
+ carry = d < carry;
+ a[i] = d + b[i];
+ if ( a[i] < d )
+ carry = 1;
+ }
+ a[n] = 0;
+ }
+ }
+
+ int normalize( basic_bigint<Allocator>& denom, basic_bigint<Allocator>& num, int& x ) const
+ {
+ size_type r = denom.length() - 1;
+ uint64_t y = denom.data()[r];
+
+ x = 0;
+ while ( (y & l_bit) == 0 )
+ {
+ y <<= 1;
+ x++;
+ }
+ denom <<= x;
+ num <<= x;
+ if ( r > 0 && denom.data()[r] < denom.data()[r-1] )
+ {
+ denom *= max_basic_type;
+ num *= max_basic_type;
+ return 1;
+ }
+ return 0;
+ }
+
+ void unnormalize( basic_bigint<Allocator>& rem, int x, int secondDone ) const
+ {
+ if ( secondDone )
+ {
+ rem /= max_basic_type;
+ }
+ if ( x > 0 )
+ {
+ rem >>= x;
+ }
+ else
+ {
+ rem.reduce();
+ }
+ }
+
+ 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;
+ }
+ }
+
+ static uint64_t next_power_of_two(uint64_t n) {
+ n = n - 1;
+ n |= n >> 1;
+ n |= n >> 2;
+ n |= n >> 4;
+ n |= n >> 8;
+ n |= n >> 16;
+ n |= n >> 32;
+ return n + 1;
+ }
+};
+
+using bigint = basic_bigint<std::allocator<uint8_t>>;
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+JSONCONS_DEPRECATED_MSG("Instead, use bigint") typedef bigint bignum;
+#endif
+
+}
+
+#endif
diff --git a/include/jsoncons/byte_string.hpp b/include/jsoncons/byte_string.hpp
new file mode 100644
index 0000000..159dff9
--- /dev/null
+++ b/include/jsoncons/byte_string.hpp
@@ -0,0 +1,820 @@
+// Copyright 2013 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_BYTE_STRING_HPP
+#define JSONCONS_BYTE_STRING_HPP
+
+#include <sstream>
+#include <vector>
+#include <ostream>
+#include <cmath>
+#include <cstring> // std::memcmp
+#include <memory> // std::allocator
+#include <iterator>
+#include <exception>
+#include <iomanip> // std::setw
+#include <initializer_list>
+#include <utility> // std::move
+#include <jsoncons/config/jsoncons_config.hpp>
+#include <jsoncons/json_exception.hpp>
+#include <jsoncons/conv_error.hpp>
+#include <jsoncons/more_type_traits.hpp>
+
+namespace jsoncons {
+
+ // Algorithms
+
+namespace detail {
+
+ template <class InputIt, class Container>
+ typename std::enable_if<std::is_same<typename std::iterator_traits<InputIt>::value_type,uint8_t>::value,size_t>::type
+ encode_base64_generic(InputIt first, InputIt last, const char alphabet[65], Container& result)
+ {
+ std::size_t count = 0;
+ unsigned char a3[3];
+ unsigned char a4[4];
+ unsigned char fill = alphabet[64];
+ int i = 0;
+ int j = 0;
+
+ while (first != last)
+ {
+ a3[i++] = *first++;
+ if (i == 3)
+ {
+ a4[0] = (a3[0] & 0xfc) >> 2;
+ a4[1] = ((a3[0] & 0x03) << 4) + ((a3[1] & 0xf0) >> 4);
+ a4[2] = ((a3[1] & 0x0f) << 2) + ((a3[2] & 0xc0) >> 6);
+ a4[3] = a3[2] & 0x3f;
+
+ for (i = 0; i < 4; i++)
+ {
+ result.push_back(alphabet[a4[i]]);
+ ++count;
+ }
+ i = 0;
+ }
+ }
+
+ if (i > 0)
+ {
+ for (j = i; j < 3; ++j)
+ {
+ a3[j] = 0;
+ }
+
+ a4[0] = (a3[0] & 0xfc) >> 2;
+ a4[1] = ((a3[0] & 0x03) << 4) + ((a3[1] & 0xf0) >> 4);
+ a4[2] = ((a3[1] & 0x0f) << 2) + ((a3[2] & 0xc0) >> 6);
+
+ for (j = 0; j < i + 1; ++j)
+ {
+ result.push_back(alphabet[a4[j]]);
+ ++count;
+ }
+
+ if (fill != 0)
+ {
+ while (i++ < 3)
+ {
+ result.push_back(fill);
+ ++count;
+ }
+ }
+ }
+
+ return count;
+ }
+
+ template <class InputIt, class F, class Container>
+ typename std::enable_if<type_traits::is_back_insertable_byte_container<Container>::value,decode_result<InputIt>>::type
+ decode_base64_generic(InputIt first, InputIt last,
+ const uint8_t reverse_alphabet[256],
+ F f,
+ Container& result)
+ {
+ uint8_t a4[4], a3[3];
+ uint8_t i = 0;
+ uint8_t j = 0;
+
+ while (first != last && *first != '=')
+ {
+ if (!f(*first))
+ {
+ return decode_result<InputIt>{first, conv_errc::conversion_failed};
+ }
+
+ a4[i++] = *first++;
+ if (i == 4)
+ {
+ for (i = 0; i < 4; ++i)
+ {
+ a4[i] = reverse_alphabet[a4[i]];
+ }
+
+ a3[0] = (a4[0] << 2) + ((a4[1] & 0x30) >> 4);
+ a3[1] = ((a4[1] & 0xf) << 4) + ((a4[2] & 0x3c) >> 2);
+ a3[2] = ((a4[2] & 0x3) << 6) + a4[3];
+
+ for (i = 0; i < 3; i++)
+ {
+ result.push_back(a3[i]);
+ }
+ i = 0;
+ }
+ }
+
+ if (i > 0)
+ {
+ for (j = 0; j < i; ++j)
+ {
+ a4[j] = reverse_alphabet[a4[j]];
+ }
+
+ a3[0] = (a4[0] << 2) + ((a4[1] & 0x30) >> 4);
+ a3[1] = ((a4[1] & 0xf) << 4) + ((a4[2] & 0x3c) >> 2);
+
+ for (j = 0; j < i - 1; ++j)
+ {
+ result.push_back(a3[j]);
+ }
+ }
+ return decode_result<InputIt>{last, conv_errc::success};
+ }
+
+} // namespace detail
+
+ template <class InputIt, class Container>
+ typename std::enable_if<std::is_same<typename std::iterator_traits<InputIt>::value_type,uint8_t>::value,size_t>::type
+ encode_base16(InputIt first, InputIt last, Container& result)
+ {
+ static constexpr char characters[] = "0123456789ABCDEF";
+
+ for (auto it = first; it != last; ++it)
+ {
+ uint8_t c = *it;
+ result.push_back(characters[c >> 4]);
+ result.push_back(characters[c & 0xf]);
+ }
+ return (last-first)*2;
+ }
+
+ template <class InputIt, class Container>
+ typename std::enable_if<std::is_same<typename std::iterator_traits<InputIt>::value_type,uint8_t>::value,size_t>::type
+ encode_base64url(InputIt first, InputIt last, Container& result)
+ {
+ static constexpr char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789-_"
+ "\0";
+ return detail::encode_base64_generic(first, last, alphabet, result);
+ }
+
+ template <class InputIt, class Container>
+ typename std::enable_if<std::is_same<typename std::iterator_traits<InputIt>::value_type,uint8_t>::value,size_t>::type
+ encode_base64(InputIt first, InputIt last, Container& result)
+ {
+ static constexpr char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789+/"
+ "=";
+ return detail::encode_base64_generic(first, last, alphabet, result);
+ }
+
+ template <class Char>
+ bool is_base64(Char c)
+ {
+ return (c >= 0 && c < 128) && (isalnum((int)c) || c == '+' || c == '/');
+ }
+
+ template <class Char>
+ bool is_base64url(Char c)
+ {
+ return (c >= 0 && c < 128) && (isalnum((int)c) || c == '-' || c == '_');
+ }
+
+ inline
+ static bool is_base64url(int c)
+ {
+ return isalnum(c) || c == '-' || c == '_';
+ }
+
+ // decode
+
+ template <class InputIt, class Container>
+ typename std::enable_if<type_traits::is_back_insertable_byte_container<Container>::value,decode_result<InputIt>>::type
+ decode_base64url(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,
+ 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, 62, 0xff, 0xff,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0xff, 0xff, 0xff, 0xff, 63,
+ 0xff, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 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, 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, 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, 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, 0xff, 0xff, 0xff, 0xff, 0xff
+ };
+ auto retval = jsoncons::detail::decode_base64_generic(first, last, reverse_alphabet,
+ is_base64url<typename std::iterator_traits<InputIt>::value_type>,
+ result);
+ return retval.ec == conv_errc::success ? retval : decode_result<InputIt>{retval.it, conv_errc::not_base64url};
+ }
+
+ template <class InputIt, class Container>
+ typename std::enable_if<type_traits::is_back_insertable_byte_container<Container>::value,decode_result<InputIt>>::type
+ decode_base64(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,
+ 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, 62, 0xff, 0xff, 0xff, 63,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 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, 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, 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, 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, 0xff, 0xff, 0xff, 0xff, 0xff
+ };
+ auto retval = jsoncons::detail::decode_base64_generic(first, last, reverse_alphabet,
+ is_base64<typename std::iterator_traits<InputIt>::value_type>,
+ result);
+ return retval.ec == conv_errc::success ? retval : decode_result<InputIt>{retval.it, conv_errc::not_base64};
+ }
+
+ template <class InputIt,class Container>
+ typename std::enable_if<type_traits::is_back_insertable_byte_container<Container>::value,decode_result<InputIt>>::type
+ decode_base16(InputIt first, InputIt last, Container& result)
+ {
+ std::size_t len = std::distance(first,last);
+ if (len & 1)
+ {
+ return decode_result<InputIt>{first, conv_errc::not_base16};
+ }
+
+ InputIt it = first;
+ while (it != last)
+ {
+ uint8_t val;
+ auto a = *it++;
+ if (a >= '0' && a <= '9')
+ {
+ val = (a - '0') << 4;
+ }
+ else if ((a | 0x20) >= 'a' && (a | 0x20) <= 'f')
+ {
+ val = ((a | 0x20) - 'a' + 10) << 4;
+ }
+ else
+ {
+ return decode_result<InputIt>{first, conv_errc::not_base16};
+ }
+
+ auto b = *it++;
+ if (b >= '0' && b <= '9')
+ {
+ val |= (b - '0');
+ }
+ else if ((b | 0x20) >= 'a' && (b | 0x20) <= 'f')
+ {
+ val |= ((b | 0x20) - 'a' + 10);
+ }
+ else
+ {
+ return decode_result<InputIt>{first, conv_errc::not_base16};
+ }
+
+ result.push_back(val);
+ }
+ return decode_result<InputIt>{last, conv_errc::success};
+ }
+
+ struct byte_traits
+ {
+ using char_type = uint8_t;
+
+ static constexpr int eof()
+ {
+ return std::char_traits<char>::eof();
+ }
+
+ static int compare(const char_type* s1, const char_type* s2, std::size_t count) noexcept
+ {
+ return std::memcmp(s1,s2,count);
+ }
+ };
+
+ // basic_byte_string
+
+ template <class Allocator>
+ class basic_byte_string;
+
+ // byte_string_view
+ class byte_string_view
+ {
+ const uint8_t* data_;
+ std::size_t size_;
+ public:
+ using traits_type = byte_traits;
+
+ using const_iterator = const uint8_t*;
+ using iterator = const_iterator;
+ using size_type = std::size_t;
+ using value_type = uint8_t;
+ using reference = uint8_t&;
+ using const_reference = const uint8_t&;
+ using difference_type = std::ptrdiff_t;
+ using pointer = uint8_t*;
+ using const_pointer = const uint8_t*;
+
+ constexpr byte_string_view() noexcept
+ : data_(nullptr), size_(0)
+ {
+ }
+
+ constexpr byte_string_view(const uint8_t* data, std::size_t length) noexcept
+ : data_(data), size_(length)
+ {
+ }
+
+ template <class Container>
+ constexpr explicit byte_string_view(const Container& cont,
+ typename std::enable_if<type_traits::is_byte_sequence<Container>::value,int>::type = 0)
+ : data_(reinterpret_cast<const uint8_t*>(cont.data())), size_(cont.size())
+ {
+ }
+
+ template <class Allocator>
+ constexpr byte_string_view(const basic_byte_string<Allocator>& bytes);
+
+ constexpr byte_string_view(const byte_string_view&) noexcept = default;
+
+ JSONCONS_CPP14_CONSTEXPR byte_string_view(byte_string_view&& other) noexcept
+ : data_(nullptr), size_(0)
+ {
+ const_pointer temp_data = data_;
+ data_ = other.data_;
+ other.data_ = temp_data;
+
+ size_type temp_size = size_;
+ size_ = other.size_;
+ other.size_ = temp_size;
+ }
+
+ byte_string_view& operator=(const byte_string_view&) = default;
+
+ byte_string_view& operator=(byte_string_view&& other) noexcept
+ {
+ std::swap(data_, other.data_);
+ std::swap(size_, other.size_);
+ return *this;
+ }
+
+ constexpr const uint8_t* data() const noexcept
+ {
+ return data_;
+ }
+ #if !defined(JSONCONS_NO_DEPRECATED)
+ JSONCONS_DEPRECATED_MSG("Instead, use size()")
+ std::size_t length() const
+ {
+ return size_;
+ }
+ #endif
+ constexpr size_t size() const noexcept
+ {
+ return size_;
+ }
+
+ // iterator support
+ constexpr const_iterator begin() const noexcept
+ {
+ return data_;
+ }
+ constexpr const_iterator end() const noexcept
+ {
+ return data_ + size_;
+ }
+ constexpr const_iterator cbegin() const noexcept
+ {
+ return data_;
+ }
+ constexpr const_iterator cend() const noexcept
+ {
+ return data_ + size_;
+ }
+
+ constexpr uint8_t operator[](size_type pos) const
+ {
+ return data_[pos];
+ }
+
+ JSONCONS_CPP14_CONSTEXPR byte_string_view substr(size_type pos) const
+ {
+ if (pos > size_)
+ {
+ JSONCONS_THROW(std::out_of_range("pos exceeds size"));
+ }
+ std::size_t n = size_ - pos;
+ return byte_string_view(data_ + pos, n);
+ }
+
+ byte_string_view substr(size_type pos, size_type n) const
+ {
+ if (pos > size_)
+ {
+ JSONCONS_THROW(std::out_of_range("pos exceeds size"));
+ }
+ if (pos + n > size_)
+ {
+ n = size_ - pos;
+ }
+ return byte_string_view(data_ + pos, n);
+ }
+
+ int compare(const byte_string_view& s) const noexcept
+ {
+ const int rc = traits_type::compare(data_, s.data(), (std::min)(size_, s.size()));
+ return rc != 0 ? rc : (size_ == s.size() ? 0 : size_ < s.size() ? -1 : 1);
+ }
+
+ template <class Allocator>
+ int compare(const basic_byte_string<Allocator>& s) const noexcept
+ {
+ const int rc = traits_type::compare(data_, s.data(), (std::min)(size_, s.size()));
+ return rc != 0 ? rc : (size_ == s.size() ? 0 : size_ < s.size() ? -1 : 1);
+ }
+
+ template <class CharT>
+ friend std::basic_ostream<CharT>& operator<<(std::basic_ostream<CharT>& os, const byte_string_view& bstr)
+ {
+ std::basic_ostringstream<CharT> ss;
+ ss.flags(std::ios::hex);
+ ss.fill('0');
+
+ bool first = true;
+ for (auto b : bstr)
+ {
+ if (first)
+ {
+ first = false;
+ }
+ else
+ {
+ ss << ',';
+ }
+ ss << std::setw(2) << static_cast<int>(b);
+ }
+ os << ss.str();
+ return os;
+ }
+ };
+
+ // basic_byte_string
+ template <class Allocator = std::allocator<uint8_t>>
+ class basic_byte_string
+ {
+ using byte_allocator_type = typename std::allocator_traits<Allocator>:: template rebind_alloc<uint8_t>;
+ std::vector<uint8_t,byte_allocator_type> data_;
+ public:
+ using traits_type = byte_traits;
+ using allocator_type = byte_allocator_type;
+
+ using value_type = typename std::vector<uint8_t,byte_allocator_type>::value_type;
+ using size_type = typename std::vector<uint8_t,byte_allocator_type>::size_type;
+ using difference_type = typename std::vector<uint8_t,byte_allocator_type>::difference_type;
+ using reference = typename std::vector<uint8_t,byte_allocator_type>::reference;
+ using const_reference = typename std::vector<uint8_t,byte_allocator_type>::const_reference;
+ using pointer = typename std::vector<uint8_t,byte_allocator_type>::pointer;
+ using const_pointer = typename std::vector<uint8_t,byte_allocator_type>::const_pointer;
+ using iterator = typename std::vector<uint8_t,byte_allocator_type>::iterator;
+ using const_iterator = typename std::vector<uint8_t,byte_allocator_type>::const_iterator;
+
+ basic_byte_string() = default;
+
+ explicit basic_byte_string(const Allocator& alloc)
+ : data_(alloc)
+ {
+ }
+
+ basic_byte_string(std::initializer_list<uint8_t> init)
+ : data_(std::move(init))
+ {
+ }
+
+ basic_byte_string(std::initializer_list<uint8_t> init, const Allocator& alloc)
+ : data_(std::move(init), alloc)
+ {
+ }
+
+ explicit basic_byte_string(const byte_string_view& v)
+ : data_(v.begin(),v.end())
+ {
+ }
+
+ basic_byte_string(const basic_byte_string<Allocator>& v)
+ : data_(v.data_)
+ {
+ }
+
+ basic_byte_string(basic_byte_string<Allocator>&& v) noexcept
+ : data_(std::move(v.data_))
+ {
+ }
+
+ basic_byte_string(const byte_string_view& v, const Allocator& alloc)
+ : data_(v.begin(),v.end(),alloc)
+ {
+ }
+
+ basic_byte_string(const uint8_t* data, std::size_t length, const Allocator& alloc = Allocator())
+ : data_(data, data+length,alloc)
+ {
+ }
+
+ Allocator get_allocator() const
+ {
+ return data_.get_allocator();
+ }
+
+ basic_byte_string& operator=(const basic_byte_string& s) = default;
+
+ basic_byte_string& operator=(basic_byte_string&& other) noexcept
+ {
+ data_.swap(other.data_);
+ return *this;
+ }
+
+ void reserve(std::size_t new_cap)
+ {
+ data_.reserve(new_cap);
+ }
+
+ void push_back(uint8_t b)
+ {
+ data_.push_back(b);
+ }
+
+ void assign(const uint8_t* s, std::size_t count)
+ {
+ data_.clear();
+ data_.insert(s, s+count);
+ }
+
+ void append(const uint8_t* s, std::size_t count)
+ {
+ data_.insert(s, s+count);
+ }
+
+ void clear()
+ {
+ data_.clear();
+ }
+
+ uint8_t operator[](size_type pos) const
+ {
+ return data_[pos];
+ }
+
+ // iterator support
+ iterator begin() noexcept
+ {
+ return data_.begin();
+ }
+ iterator end() noexcept
+ {
+ return data_.end();
+ }
+
+ const_iterator begin() const noexcept
+ {
+ return data_.begin();
+ }
+ const_iterator end() const noexcept
+ {
+ return data_.end();
+ }
+
+ uint8_t* data()
+ {
+ return data_.data();
+ }
+
+ const uint8_t* data() const
+ {
+ return data_.data();
+ }
+
+ std::size_t size() const
+ {
+ return data_.size();
+ }
+
+ #if !defined(JSONCONS_NO_DEPRECATED)
+ JSONCONS_DEPRECATED_MSG("Instead, use size()")
+ std::size_t length() const
+ {
+ return data_.size();
+ }
+ #endif
+
+ int compare(const byte_string_view& s) const noexcept
+ {
+ const int rc = traits_type::compare(data(), s.data(), (std::min)(size(), s.size()));
+ return rc != 0 ? rc : (size() == s.size() ? 0 : size() < s.size() ? -1 : 1);
+ }
+
+ int compare(const basic_byte_string& s) const noexcept
+ {
+ const int rc = traits_type::compare(data(), s.data(), (std::min)(size(), s.size()));
+ return rc != 0 ? rc : (size() == s.size() ? 0 : size() < s.size() ? -1 : 1);
+ }
+
+ template <class CharT>
+ friend std::basic_ostream<CharT>& operator<<(std::basic_ostream<CharT>& os, const basic_byte_string& o)
+ {
+ os << byte_string_view(o);
+ return os;
+ }
+ };
+
+ template <class Allocator>
+ constexpr byte_string_view::byte_string_view(const basic_byte_string<Allocator>& bytes)
+ : data_(bytes.data()), size_(bytes.size())
+ {
+ }
+
+ // ==
+ inline
+ bool operator==(const byte_string_view& lhs, const byte_string_view& rhs) noexcept
+ {
+ return lhs.compare(rhs) == 0;
+ }
+ template<class Allocator>
+ bool operator==(const byte_string_view& lhs, const basic_byte_string<Allocator>& rhs) noexcept
+ {
+ return lhs.compare(rhs) == 0;
+ }
+ template<class Allocator>
+ bool operator==(const basic_byte_string<Allocator>& lhs, const byte_string_view& rhs) noexcept
+ {
+ return rhs.compare(lhs) == 0;
+ }
+ template<class Allocator>
+ bool operator==(const basic_byte_string<Allocator>& lhs, const basic_byte_string<Allocator>& rhs) noexcept
+ {
+ return rhs.compare(lhs) == 0;
+ }
+
+ // !=
+
+ inline
+ bool operator!=(const byte_string_view& lhs, const byte_string_view& rhs) noexcept
+ {
+ return lhs.compare(rhs) != 0;
+ }
+ template<class Allocator>
+ bool operator!=(const byte_string_view& lhs, const basic_byte_string<Allocator>& rhs) noexcept
+ {
+ return lhs.compare(rhs) != 0;
+ }
+ template<class Allocator>
+ bool operator!=(const basic_byte_string<Allocator>& lhs, const byte_string_view& rhs) noexcept
+ {
+ return rhs.compare(lhs) != 0;
+ }
+ template<class Allocator>
+ bool operator!=(const basic_byte_string<Allocator>& lhs, const basic_byte_string<Allocator>& rhs) noexcept
+ {
+ return rhs.compare(lhs) != 0;
+ }
+
+ // <=
+
+ inline
+ bool operator<=(const byte_string_view& lhs, const byte_string_view& rhs) noexcept
+ {
+ return lhs.compare(rhs) <= 0;
+ }
+ template<class Allocator>
+ bool operator<=(const byte_string_view& lhs, const basic_byte_string<Allocator>& rhs) noexcept
+ {
+ return lhs.compare(rhs) <= 0;
+ }
+ template<class Allocator>
+ bool operator<=(const basic_byte_string<Allocator>& lhs, const byte_string_view& rhs) noexcept
+ {
+ return rhs.compare(lhs) >= 0;
+ }
+ template<class Allocator>
+ bool operator<=(const basic_byte_string<Allocator>& lhs, const basic_byte_string<Allocator>& rhs) noexcept
+ {
+ return rhs.compare(lhs) >= 0;
+ }
+
+ // <
+
+ inline
+ bool operator<(const byte_string_view& lhs, const byte_string_view& rhs) noexcept
+ {
+ return lhs.compare(rhs) < 0;
+ }
+ template<class Allocator>
+ bool operator<(const byte_string_view& lhs, const basic_byte_string<Allocator>& rhs) noexcept
+ {
+ return lhs.compare(rhs) < 0;
+ }
+ template<class Allocator>
+ bool operator<(const basic_byte_string<Allocator>& lhs, const byte_string_view& rhs) noexcept
+ {
+ return rhs.compare(lhs) > 0;
+ }
+ template<class Allocator>
+ bool operator<(const basic_byte_string<Allocator>& lhs, const basic_byte_string<Allocator>& rhs) noexcept
+ {
+ return rhs.compare(lhs) > 0;
+ }
+
+ // >=
+
+ inline
+ bool operator>=(const byte_string_view& lhs, const byte_string_view& rhs) noexcept
+ {
+ return lhs.compare(rhs) >= 0;
+ }
+ template<class Allocator>
+ bool operator>=(const byte_string_view& lhs, const basic_byte_string<Allocator>& rhs) noexcept
+ {
+ return lhs.compare(rhs) >= 0;
+ }
+ template<class Allocator>
+ bool operator>=(const basic_byte_string<Allocator>& lhs, const byte_string_view& rhs) noexcept
+ {
+ return rhs.compare(lhs) <= 0;
+ }
+ template<class Allocator>
+ bool operator>=(const basic_byte_string<Allocator>& lhs, const basic_byte_string<Allocator>& rhs) noexcept
+ {
+ return rhs.compare(lhs) <= 0;
+ }
+
+ // >
+
+ inline
+ bool operator>(const byte_string_view& lhs, const byte_string_view& rhs) noexcept
+ {
+ return lhs.compare(rhs) > 0;
+ }
+ template<class Allocator>
+ bool operator>(const byte_string_view& lhs, const basic_byte_string<Allocator>& rhs) noexcept
+ {
+ return lhs.compare(rhs) > 0;
+ }
+ template<class Allocator>
+ bool operator>(const basic_byte_string<Allocator>& lhs, const byte_string_view& rhs) noexcept
+ {
+ return rhs.compare(lhs) < 0;
+ }
+ template<class Allocator>
+ bool operator>(const basic_byte_string<Allocator>& lhs, const basic_byte_string<Allocator>& rhs) noexcept
+ {
+ return rhs.compare(lhs) < 0;
+ }
+
+ using byte_string = basic_byte_string<std::allocator<uint8_t>>;
+
+ namespace type_traits {
+
+ template <typename T>
+ struct is_basic_byte_string
+ : std::false_type
+ {};
+
+ template <typename Allocator>
+ struct is_basic_byte_string<basic_byte_string<Allocator>>
+ : std::true_type
+ {};
+
+ } // namespace type_traits
+
+} // namespace jsoncons
+
+#endif
diff --git a/include/jsoncons/config/binary_config.hpp b/include/jsoncons/config/binary_config.hpp
new file mode 100644
index 0000000..51b52a8
--- /dev/null
+++ b/include/jsoncons/config/binary_config.hpp
@@ -0,0 +1,226 @@
+// Copyright 2017 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_CONFIG_BINARY_CONFIG_HPP
+#define JSONCONS_CONFIG_BINARY_CONFIG_HPP
+
+#include <cfloat>
+#include <cstddef>
+#include <cstdint>
+#include <cstring> // std::memcpy
+#include <memory>
+#include <type_traits> // std::enable_if
+
+// The definitions below follow the definitions in compiler_support_p.h, https://github.com/01org/tinycbor
+// MIT license
+
+#ifdef __F16C__
+# include <immintrin.h>
+#endif
+
+#ifndef __has_builtin
+# define __has_builtin(x) 0
+#endif
+
+#if defined(__GNUC__)
+#if (__GNUC__ * 100 + __GNUC_MINOR__ >= 403) || (__has_builtin(__builtin_bswap64) && __has_builtin(__builtin_bswap32))
+# define JSONCONS_BYTE_SWAP_64 __builtin_bswap64
+# define JSONCONS_BYTE_SWAP_32 __builtin_bswap32
+# ifdef __INTEL_COMPILER
+# define JSONCONS_BYTE_SWAP_16 _bswap16
+# elif (__GNUC__ * 100 + __GNUC_MINOR__ >= 608) || __has_builtin(__builtin_bswap16)
+# define JSONCONS_BYTE_SWAP_16 __builtin_bswap16
+# endif
+#endif
+#elif defined(__sun)
+# include <sys/byteorder.h>
+#elif defined(_MSC_VER)
+// MSVC, which implies sizeof(long) == 4
+# define JSONCONS_BYTE_SWAP_64 _byteswap_uint64
+# define JSONCONS_BYTE_SWAP_32 _byteswap_ulong
+# define JSONCONS_BYTE_SWAP_16 _byteswap_ushort
+#endif
+
+namespace jsoncons {
+namespace binary {
+
+ struct uint128_holder
+ {
+ uint64_t lo;
+ uint64_t hi;
+ };
+
+ static inline bool add_check_overflow(std::size_t v1, std::size_t v2, std::size_t *r)
+ {
+ #if ((defined(__GNUC__) && (__GNUC__ >= 5)) && !defined(__INTEL_COMPILER)) || __has_builtin(__builtin_add_overflow)
+ return __builtin_add_overflow(v1, v2, r);
+ #else
+ // unsigned additions are well-defined
+ *r = v1 + v2;
+ return v1 > v1 + v2;
+ #endif
+ }
+
+ #if defined(__apple_build_version__) && ((__clang_major__ < 8) || ((__clang_major__ == 8) && (__clang_minor__ < 1)))
+ #define APPLE_MISSING_INTRINSICS 1
+ #endif
+
+ inline
+ uint16_t encode_half(double val)
+ {
+ #if defined(__F16C__) && !defined(APPLE_MISSING_INTRINSICS)
+ return _cvtss_sh((float)val, 3);
+ #else
+ uint64_t v;
+ std::memcpy(&v, &val, sizeof(v));
+ int64_t sign = static_cast<int64_t>(v >> 63 << 15);
+ int64_t exp = (v >> 52) & 0x7ff;
+ int64_t mant = v << 12 >> 12 >> (53-11); /* keep only the 11 most significant bits of the mantissa */
+ exp -= 1023;
+ if (exp == 1024) {
+ /* infinity or NaN */
+ exp = 16;
+ mant >>= 1;
+ } else if (exp >= 16) {
+ /* overflow, as largest number */
+ exp = 15;
+ mant = 1023;
+ } else if (exp >= -14) {
+ /* regular normal */
+ } else if (exp >= -24) {
+ /* subnormal */
+ mant |= 1024;
+ mant >>= -(exp + 14);
+ exp = -15;
+ } else {
+ /* underflow, make zero */
+ return 0;
+ }
+
+ /* safe cast here as bit operations above guarantee not to overflow */
+ return static_cast<uint16_t>(sign | ((exp + 15) << 10) | mant);
+ #endif
+ }
+
+ /* this function was copied & adapted from RFC 7049 Appendix D */
+ inline
+ double decode_half(uint16_t half)
+ {
+ #if defined(__F16C__) && !defined(APPLE_MISSING_INTRINSICS)
+ return _cvtsh_ss(half);
+ #else
+ int64_t exp = (half >> 10) & 0x1f;
+ int64_t mant = half & 0x3ff;
+ double val;
+ if (exp == 0)
+ {
+ val = ldexp(static_cast<double>(mant), -24);
+ }
+ else if (exp != 31)
+ {
+ val = ldexp(static_cast<double>(mant) + 1024.0, static_cast<int>(exp - 25));
+ }
+ else
+ {
+ val = mant == 0 ? std::numeric_limits<double>::infinity() : std::nan("");
+ }
+ return half & 0x8000 ? -val : val;
+ #endif
+ }
+
+ // byte_swap
+
+ template<class T>
+ typename std::enable_if<std::is_integral<T>::value && sizeof(T) == sizeof(uint8_t),T>::type
+ byte_swap(T val)
+ {
+ return val;
+ }
+
+ template<class T>
+ typename std::enable_if<std::is_integral<T>::value && sizeof(T) == sizeof(uint16_t),T>::type
+ byte_swap(T val)
+ {
+ #if defined(JSONCONS_BYTE_SWAP_16)
+ return JSONCONS_BYTE_SWAP_16(val);
+ #else
+ return (static_cast<uint16_t>(val) >> 8) | (static_cast<uint16_t>(val) << 8);
+ #endif
+ }
+
+ template<class T>
+ typename std::enable_if<std::is_integral<T>::value && sizeof(T) == sizeof(uint32_t),T>::type
+ byte_swap(T val)
+ {
+ #if defined(JSONCONS_BYTE_SWAP_32)
+ return JSONCONS_BYTE_SWAP_32(val);
+ #else
+ uint32_t tmp = ((static_cast<uint32_t>(val) << 8) & 0xff00ff00) | ((static_cast<uint32_t>(val) >> 8) & 0xff00ff);
+ return (tmp << 16) | (tmp >> 16);
+ #endif
+ }
+
+ template<class T>
+ typename std::enable_if<std::is_integral<T>::value && sizeof(T) == sizeof(uint64_t),T>::type
+ byte_swap(T val)
+ {
+ #if defined(JSONCONS_BYTE_SWAP_64)
+ return JSONCONS_BYTE_SWAP_64(val);
+ #else
+ uint64_t tmp = ((static_cast<uint64_t>(val) & 0x00000000ffffffffull) << 32) | ((static_cast<uint64_t>(val) & 0xffffffff00000000ull) >> 32);
+ tmp = ((tmp & 0x0000ffff0000ffffull) << 16) | ((tmp & 0xffff0000ffff0000ull) >> 16);
+ return ((tmp & 0x00ff00ff00ff00ffull) << 8) | ((tmp & 0xff00ff00ff00ff00ull) >> 8);
+ #endif
+ }
+
+ template<class T>
+ typename std::enable_if<std::is_floating_point<T>::value && sizeof(T) == sizeof(uint32_t),T>::type
+ byte_swap(T val)
+ {
+ uint32_t x;
+ std::memcpy(&x,&val,sizeof(uint32_t));
+ uint32_t y = byte_swap(x);
+ T val2;
+ std::memcpy(&val2,&y,sizeof(uint32_t));
+ return val2;
+ }
+
+ template<class T>
+ typename std::enable_if<std::is_floating_point<T>::value && sizeof(T) == sizeof(uint64_t),T>::type
+ byte_swap(T val)
+ {
+ uint64_t x;
+ std::memcpy(&x,&val,sizeof(uint64_t));
+ uint64_t y = byte_swap(x);
+ T val2;
+ std::memcpy(&val2,&y,sizeof(uint64_t));
+ return val2;
+ }
+
+ template<class T>
+ typename std::enable_if<std::is_floating_point<T>::value && sizeof(T) == 2*sizeof(uint64_t),T>::type
+ byte_swap(T val)
+ {
+ uint128_holder x;
+ uint8_t buf[2*sizeof(uint64_t)];
+ std::memcpy(buf,&val,2*sizeof(uint64_t));
+ std::memcpy(&x.lo,buf,sizeof(uint64_t));
+ std::memcpy(&x.hi,buf+sizeof(uint64_t),sizeof(uint64_t));
+
+ uint128_holder y;
+ y.lo = byte_swap(x.hi);
+ y.hi = byte_swap(x.lo);
+
+ T val2;
+ std::memcpy(&val2,&y,2*sizeof(uint64_t));
+
+ return val2;
+ }
+
+} // binary
+} // jsoncons
+
+#endif
diff --git a/include/jsoncons/config/compiler_support.hpp b/include/jsoncons/config/compiler_support.hpp
new file mode 100644
index 0000000..15d30cf
--- /dev/null
+++ b/include/jsoncons/config/compiler_support.hpp
@@ -0,0 +1,389 @@
+// Copyright 2013 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_COMPILER_SUPPORT_HPP
+#define JSONCONS_COMPILER_SUPPORT_HPP
+
+#include <stdexcept>
+#include <string>
+#include <cmath>
+#include <exception>
+#include <ostream>
+
+#if defined (__clang__)
+#define JSONCONS_CLANG_VERSION (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__)
+#endif
+
+// Uncomment the following line to suppress deprecated names (recommended for new code)
+//#define JSONCONS_NO_DEPRECATED
+
+// The definitions below follow the definitions in compiler_support_p.h, https://github.com/01org/tinycbor
+// MIT license
+
+// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54577
+#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 9
+#define JSONCONS_NO_VECTOR_ERASE_TAKES_CONST_ITERATOR 1
+#define JSONCONS_NO_MAP_CONS_TAKES_ALLOCATOR 1
+#endif
+
+#if 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
+
+#if defined(__GNUC__) || defined(__clang__)
+#define JSONCONS_LIKELY(x) __builtin_expect(!!(x), 1)
+#define JSONCONS_UNLIKELY(x) __builtin_expect(!!(x), 0)
+#define JSONCONS_UNREACHABLE() __builtin_unreachable()
+#elif defined(_MSC_VER)
+#define JSONCONS_LIKELY(x) x
+#define JSONCONS_UNLIKELY(x) x
+#define JSONCONS_UNREACHABLE() __assume(0)
+#else
+#define JSONCONS_LIKELY(x) x
+#define JSONCONS_UNLIKELY(x) x
+#define JSONCONS_UNREACHABLE() do {} while (0)
+#endif
+
+// Deprecated symbols markup
+#if (defined(__cplusplus) && __cplusplus >= 201402L)
+#define JSONCONS_DEPRECATED_MSG(msg) [[deprecated(msg)]]
+#endif
+
+#if !defined(JSONCONS_DEPRECATED_MSG) && defined(__GNUC__) && defined(__has_extension)
+#if __has_extension(attribute_deprecated_with_message)
+#define JSONCONS_DEPRECATED_MSG(msg) __attribute__((deprecated(msg)))
+#endif
+#endif
+
+#if !defined(JSONCONS_DEPRECATED_MSG) && defined(_MSC_VER)
+#if (_MSC_VER) >= 1920
+#define JSONCONS_DEPRECATED_MSG(msg) [[deprecated(msg)]]
+#else
+#define JSONCONS_DEPRECATED_MSG(msg) __declspec(deprecated(msg))
+#endif
+#endif
+
+// Following boost/atomic/detail/config.hpp
+#if !defined(JSONCONS_DEPRECATED_MSG) && (\
+ (defined(__GNUC__) && ((__GNUC__ + 0) * 100 + (__GNUC_MINOR__ + 0)) >= 405) ||\
+ (defined(__SUNPRO_CC) && (__SUNPRO_CC + 0) >= 0x5130))
+ #define JSONCONS_DEPRECATED_MSG(msg) __attribute__((deprecated(msg)))
+#endif
+
+#if !defined(JSONCONS_DEPRECATED_MSG) && defined(__clang__) && defined(__has_extension)
+ #if __has_extension(attribute_deprecated_with_message)
+ #define JSONCONS_DEPRECATED_MSG(msg) __attribute__((deprecated(msg)))
+ #else
+ #define JSONCONS_DEPRECATED_MSG(msg) __attribute__((deprecated))
+ #endif
+#endif
+
+#if !defined(JSONCONS_DEPRECATED_MSG)
+#define JSONCONS_DEPRECATED_MSG(msg)
+#endif
+
+#if defined(ANDROID) || defined(__ANDROID__)
+#if __ANDROID_API__ >= 21
+#define JSONCONS_HAS_STRTOLD_L
+#else
+#define JSONCONS_NO_LOCALECONV
+#endif
+#endif
+
+#if defined(_MSC_VER)
+#define JSONCONS_HAS_MSC_STRTOD_L
+#define JSONCONS_HAS_FOPEN_S
+#endif
+
+#ifndef JSONCONS_HAS_CP14
+ #if defined(_MSVC_LANG)
+ #if _MSVC_LANG >= 201402L
+ #define JSONCONS_HAS_CP14
+ #endif
+ #elif __cplusplus >= 201402L
+ #define JSONCONS_HAS_CP14
+ #endif
+#endif
+
+#if !defined(JSONCONS_HAS_STD_FROM_CHARS)
+# if defined(__GNUC__)
+# if (__GNUC__ >= 11)
+# if (__cplusplus >= 201703)
+# define JSONCONS_HAS_STD_FROM_CHARS 1
+# 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)
+#include <charconv>
+#endif
+
+#if !defined(JSONCONS_HAS_2017)
+# if defined(__clang__)
+# if (__cplusplus >= 201703)
+# define JSONCONS_HAS_2017 1
+# endif // (__cplusplus >= 201703)
+# endif // defined(__clang__)
+# if defined(__GNUC__)
+# if (__GNUC__ >= 7)
+# if (__cplusplus >= 201703)
+# define JSONCONS_HAS_2017 1
+# endif // (__cplusplus >= 201703)
+# endif // (__GNUC__ >= 7)
+# endif // defined(__GNUC__)
+# if defined(_MSC_VER)
+# if (_MSC_VER >= 1910 && _MSVC_LANG >= 201703)
+# define JSONCONS_HAS_2017 1
+# endif // (_MSC_VER >= 1910 && MSVC_LANG >= 201703)
+# endif // defined(_MSC_VER)
+#endif
+
+#if defined(JSONCONS_HAS_2017)
+ #define JSONCONS_NODISCARD [[nodiscard]]
+ #define JSONCONS_IF_CONSTEXPR if constexpr
+#else
+ #define JSONCONS_NODISCARD
+ #define JSONCONS_IF_CONSTEXPR if
+#endif
+
+#if !defined(JSONCONS_HAS_STD_STRING_VIEW)
+# if (defined JSONCONS_HAS_2017)
+# if defined(__clang__)
+# if __has_include(<string_view>)
+# define JSONCONS_HAS_STD_STRING_VIEW 1
+# endif // __has_include(<string_view>)
+# else
+# define JSONCONS_HAS_STD_STRING_VIEW 1
+# endif
+# endif // defined(JSONCONS_HAS_2017)
+#endif // !defined(JSONCONS_HAS_STD_STRING_VIEW)
+
+#if !defined(JSONCONS_HAS_STD_BYTE)
+# if (defined JSONCONS_HAS_2017)
+# define JSONCONS_HAS_STD_BYTE 1
+# endif // defined(JSONCONS_HAS_2017)
+#endif // !defined(JSONCONS_HAS_STD_BYTE)
+
+#if !defined(JSONCONS_HAS_STD_OPTIONAL)
+# if (defined JSONCONS_HAS_2017)
+# if defined(__clang__)
+# if __has_include(<optional>)
+# define JSONCONS_HAS_STD_OPTIONAL 1
+# endif // __has_include(<string_view>)
+# else
+# define JSONCONS_HAS_STD_OPTIONAL 1
+# endif
+# endif // defined(JSONCONS_HAS_2017)
+#endif // !defined(JSONCONS_HAS_STD_OPTIONAL)
+
+#if !defined(JSONCONS_HAS_STD_VARIANT)
+# if (defined JSONCONS_HAS_2017)
+# if defined(__clang__)
+# if defined(__APPLE__)
+# if JSONCONS_CLANG_VERSION >= 100001
+# define JSONCONS_HAS_STD_VARIANT 1
+# endif
+# elif __has_include(<variant>)
+# define JSONCONS_HAS_STD_VARIANT 1
+# endif // __has_include(<variant>)
+# else
+# define JSONCONS_HAS_STD_VARIANT 1
+# endif
+# endif // defined(JSONCONS_HAS_2017)
+#endif // !defined(JSONCONS_HAS_STD_VARIANT)
+
+#if !defined(JSONCONS_HAS_FILESYSTEM)
+# if (defined JSONCONS_HAS_2017)
+# if defined(__clang__)
+# if __has_include(<filesystem>)
+# define JSONCONS_HAS_FILESYSTEM 1
+# endif // __has_include(<filesystem>)
+# else
+# define JSONCONS_HAS_FILESYSTEM 1
+# endif
+# 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__) && !__EXCEPTIONS
+# define JSONCONS_NO_EXCEPTIONS 1
+#elif _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
+
+// 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
+
+#if !defined(JSONCONS_HAS_STD_MAKE_UNIQUE)
+ #if defined(__clang__) && defined(__cplusplus)
+ #if defined(__APPLE__)
+ #if __clang_major__ >= 6 && __cplusplus >= 201402L // Xcode 6
+ #define JSONCONS_HAS_STD_MAKE_UNIQUE
+ #endif
+ #elif ((__clang_major__*100 +__clang_minor__) >= 340) && __cplusplus >= 201402L
+ #define JSONCONS_HAS_STD_MAKE_UNIQUE
+ #endif
+ #elif defined(__GNUC__)
+ #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 409 && __cplusplus > 201103L
+ #define JSONCONS_HAS_STD_MAKE_UNIQUE
+ #endif
+ #elif defined(_MSC_VER)
+ #if _MSC_VER >= 1800
+ #define JSONCONS_HAS_STD_MAKE_UNIQUE
+ #endif
+ #endif
+#endif // !defined(JSONCONS_HAS_STD_MAKE_UNIQUE)
+
+#ifndef JSONCONS_HAS_CP14_CONSTEXPR
+ #if defined(_MSC_VER)
+ #if _MSC_VER >= 1910
+ #define JSONCONS_HAS_CP14_CONSTEXPR
+ #endif
+ #elif defined(__GNUC__)
+ #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 600 && __cplusplus >= 201402L
+ #define JSONCONS_HAS_CP14_CONSTEXPR
+ #endif
+ #endif
+#endif
+
+#if defined(JSONCONS_HAS_CP14_CONSTEXPR)
+# define JSONCONS_CPP14_CONSTEXPR constexpr
+#else
+# define JSONCONS_CPP14_CONSTEXPR
+#endif
+
+// Follows boost
+
+// gcc and clang
+#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 (__clang_major__ >= 4) && defined(__has_include)
+#if __has_include(<quadmath.h>)
+# define JSONCONS_HAS_FLOAT128
+#endif
+#endif
+#endif
+#endif
+
+#if defined(__GNUC__)
+#if defined(_GLIBCXX_USE_FLOAT128)
+# define JSONCONS_HAS_FLOAT128
+#endif
+#endif
+
+#if defined(__clang__)
+#if (defined(linux) || defined(__linux) || defined(__linux__) || defined(__GNU__) || defined(__GLIBC__)) && !defined(_CRAYC)
+#if (__clang_major__ >= 4) && defined(__has_include)
+#if __has_include(<quadmath.h>)
+# define JSONCONS_HAS_FLOAT128
+#endif
+#endif
+#endif
+#endif
+
+// Follows boost config/detail/suffix.hpp
+#if defined(JSONCONS_HAS_INT128) && defined(__cplusplus)
+namespace jsoncons{
+# ifdef __GNUC__
+ __extension__ typedef __int128 int128_type;
+ __extension__ typedef unsigned __int128 uint128_type;
+# else
+ typedef __int128 int128_type;
+ typedef unsigned __int128 uint128_type;
+# endif
+}
+#endif
+#if defined(JSONCONS_HAS_FLOAT128) && defined(__cplusplus)
+namespace jsoncons {
+# ifdef __GNUC__
+ __extension__ typedef __float128 float128_type;
+# else
+ typedef __float128 float128_type;
+# endif
+}
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER <= 1900
+ #define JSONCONS_COPY(first,last,d_first) std::copy(first, last, stdext::make_checked_array_iterator(d_first, static_cast<std::size_t>(std::distance(first, last))))
+#else
+ #define JSONCONS_COPY(first,last,d_first) std::copy(first, last, d_first)
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER <= 1900
+#define JSONCONS_CONSTEXPR
+#else
+#define JSONCONS_CONSTEXPR constexpr
+#endif
+
+namespace jsoncons {
+
+ class assertion_error : public std::runtime_error
+ {
+ public:
+ assertion_error(const std::string& s) noexcept
+ : std::runtime_error(s)
+ {
+ }
+ const char* what() const noexcept override
+ {
+ return std::runtime_error::what();
+ }
+ };
+
+} // namespace jsoncons
+
+#define JSONCONS_STR2(x) #x
+#define JSONCONS_STR(x) JSONCONS_STR2(x)
+
+#ifdef _DEBUG
+#define JSONCONS_ASSERT(x) if (!(x)) { \
+ JSONCONS_THROW(jsoncons::assertion_error("assertion '" #x "' failed at " __FILE__ ":" \
+ JSONCONS_STR(__LINE__))); }
+#else
+#define JSONCONS_ASSERT(x) if (!(x)) { \
+ JSONCONS_THROW(jsoncons::assertion_error("assertion '" #x "' failed at <> :" \
+ JSONCONS_STR( 0 ))); }
+#endif // _DEBUG
+
+#endif // JSONCONS_COMPILER_SUPPORT_HPP
diff --git a/include/jsoncons/config/jsoncons_config.hpp b/include/jsoncons/config/jsoncons_config.hpp
new file mode 100644
index 0000000..63c437b
--- /dev/null
+++ b/include/jsoncons/config/jsoncons_config.hpp
@@ -0,0 +1,308 @@
+// Copyright 2019 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_CONFIG_JSONCONS_CONFIG_HPP
+#define JSONCONS_CONFIG_JSONCONS_CONFIG_HPP
+
+#include <type_traits>
+#include <limits>
+#include <jsoncons/config/compiler_support.hpp>
+#include <jsoncons/config/binary_config.hpp>
+
+#if !defined(JSONCONS_HAS_STD_STRING_VIEW)
+#include <jsoncons/detail/string_view.hpp>
+namespace jsoncons {
+using jsoncons::detail::basic_string_view;
+using string_view = basic_string_view<char, std::char_traits<char>>;
+using wstring_view = basic_string_view<wchar_t, std::char_traits<wchar_t>>;
+}
+#else
+#include <string_view>
+namespace jsoncons {
+using std::basic_string_view;
+using std::string_view;
+using std::wstring_view;
+}
+#endif
+
+#if !defined(JSONCONS_HAS_STD_SPAN)
+#include <jsoncons/detail/span.hpp>
+namespace jsoncons {
+using jsoncons::detail::span;
+}
+#else
+#include <span>
+namespace jsoncons {
+using std::span;
+}
+#endif
+
+#if defined(JSONCONS_HAS_STD_OPTIONAL)
+ #include <optional>
+ namespace jsoncons {
+ using std::optional;
+ }
+#elif defined(JSONCONS_HAS_BOOST_OPTIONAL)
+ #include <boost/optional.hpp>
+ namespace jsoncons {
+ using boost::optional;
+ }
+#else
+ #include <jsoncons/detail/optional.hpp>
+ namespace jsoncons {
+ using jsoncons::detail::optional;
+}
+#endif // !defined(JSONCONS_HAS_STD_OPTIONAL)
+
+#if !defined(JSONCONS_HAS_STD_ENDIAN)
+#include <jsoncons/detail/endian.hpp>
+namespace jsoncons {
+using jsoncons::detail::endian;
+}
+#else
+#include <bit>
+namespace jsoncons
+{
+ using std::endian;
+}
+#endif
+
+#if !defined(JSONCONS_HAS_STD_MAKE_UNIQUE)
+
+#include <cstddef>
+#include <memory>
+#include <type_traits>
+#include <utility>
+
+namespace jsoncons {
+
+ template<class T>
+ struct unique_if
+ {
+ using value_is_not_array = std::unique_ptr<T>;
+ };
+
+ template<class T>
+ struct unique_if<T[]>
+ {
+ typedef std::unique_ptr<T[]> value_is_array_of_unknown_bound;
+ };
+
+ template<class T, std::size_t N>
+ struct unique_if<T[N]> {
+ using value_is_array_of_known_bound = void;
+ };
+
+ template<class T, class... Args>
+ typename unique_if<T>::value_is_not_array
+ make_unique(Args&&... args)
+ {
+ return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
+ }
+
+ template<class T>
+ typename unique_if<T>::value_is_array_of_unknown_bound
+ make_unique(std::size_t n)
+ {
+ using U = typename std::remove_extent<T>::type;
+ return std::unique_ptr<T>(new U[n]());
+ }
+
+ template<class T, class... Args>
+ typename unique_if<T>::value_is_array_of_known_bound
+ make_unique(Args&&...) = delete;
+} // jsoncons
+
+#else
+
+#include <memory>
+namespace jsoncons
+{
+ using std::make_unique;
+}
+
+#endif // !defined(JSONCONS_HAS_STD_MAKE_UNIQUE)
+
+namespace jsoncons {
+namespace binary {
+
+ // native_to_big
+
+ template<typename T, class OutputIt, class Endian=endian>
+ typename std::enable_if<Endian::native == Endian::big,void>::type
+ native_to_big(T val, OutputIt d_first)
+ {
+ uint8_t buf[sizeof(T)];
+ std::memcpy(buf, &val, sizeof(T));
+ for (auto item : buf)
+ {
+ *d_first++ = item;
+ }
+ }
+
+ template<typename T, class OutputIt, class Endian=endian>
+ typename std::enable_if<Endian::native == Endian::little,void>::type
+ native_to_big(T val, OutputIt d_first)
+ {
+ T val2 = byte_swap(val);
+ uint8_t buf[sizeof(T)];
+ std::memcpy(buf, &val2, sizeof(T));
+ for (auto item : buf)
+ {
+ *d_first++ = item;
+ }
+ }
+
+ // native_to_little
+
+ template<typename T, class OutputIt, class Endian = endian>
+ typename std::enable_if<Endian::native == Endian::little,void>::type
+ native_to_little(T val, OutputIt d_first)
+ {
+ uint8_t buf[sizeof(T)];
+ std::memcpy(buf, &val, sizeof(T));
+ for (auto item : buf)
+ {
+ *d_first++ = item;
+ }
+ }
+
+ template<typename T, class OutputIt, class Endian=endian>
+ typename std::enable_if<Endian::native == Endian::big, void>::type
+ native_to_little(T val, OutputIt d_first)
+ {
+ T val2 = byte_swap(val);
+ uint8_t buf[sizeof(T)];
+ std::memcpy(buf, &val2, sizeof(T));
+ for (auto item : buf)
+ {
+ *d_first++ = item;
+ }
+ }
+
+ // big_to_native
+
+ template<class T,class Endian=endian>
+ typename std::enable_if<Endian::native == Endian::big,T>::type
+ big_to_native(const uint8_t* first, std::size_t count)
+ {
+ if (sizeof(T) > count)
+ {
+ return T{};
+ }
+ T val;
+ std::memcpy(&val,first,sizeof(T));
+ return val;
+ }
+
+ template<class T,class Endian=endian>
+ typename std::enable_if<Endian::native == Endian::little,T>::type
+ big_to_native(const uint8_t* first, std::size_t count)
+ {
+ if (sizeof(T) > count)
+ {
+ return T{};
+ }
+ T val;
+ std::memcpy(&val,first,sizeof(T));
+ return byte_swap(val);
+ }
+
+ // little_to_native
+
+ template<class T,class Endian=endian>
+ typename std::enable_if<Endian::native == Endian::little,T>::type
+ little_to_native(const uint8_t* first, std::size_t count)
+ {
+ if (sizeof(T) > count)
+ {
+ return T{};
+ }
+ T val;
+ std::memcpy(&val,first,sizeof(T));
+ return val;
+ }
+
+ template<class T,class Endian=endian>
+ typename std::enable_if<Endian::native == Endian::big,T>::type
+ little_to_native(const uint8_t* first, std::size_t count)
+ {
+ if (sizeof(T) > count)
+ {
+ return T{};
+ }
+ T val;
+ std::memcpy(&val,first,sizeof(T));
+ return byte_swap(val);
+ }
+
+} // binary
+} // jsoncons
+
+namespace jsoncons {
+
+ template<typename CharT>
+ constexpr const CharT* cstring_constant_of_type(const char* c, const wchar_t* w);
+
+ template<> inline
+ constexpr const char* cstring_constant_of_type<char>(const char* c, const wchar_t*)
+ {
+ return c;
+ }
+ template<> inline
+ constexpr const wchar_t* cstring_constant_of_type<wchar_t>(const char*, const wchar_t* w)
+ {
+ return w;
+ }
+
+ template<typename CharT>
+ std::basic_string<CharT> string_constant_of_type(const char* c, const wchar_t* w);
+
+ template<> inline
+ std::string string_constant_of_type<char>(const char* c, const wchar_t*)
+ {
+ return std::string(c);
+ }
+ template<> inline
+ std::wstring string_constant_of_type<wchar_t>(const char*, const wchar_t* w)
+ {
+ return std::wstring(w);
+ }
+
+ template<typename CharT>
+ jsoncons::basic_string_view<CharT> string_view_constant_of_type(const char* c, const wchar_t* w);
+
+ template<> inline
+ jsoncons::string_view string_view_constant_of_type<char>(const char* c, const wchar_t*)
+ {
+ return jsoncons::string_view(c);
+ }
+ template<> inline
+ jsoncons::wstring_view string_view_constant_of_type<wchar_t>(const char*, const wchar_t* w)
+ {
+ return jsoncons::wstring_view(w);
+ }
+
+} // jsoncons
+
+#define JSONCONS_EXPAND(X) X
+#define JSONCONS_QUOTE(Prefix, A) JSONCONS_EXPAND(Prefix ## #A)
+
+#define JSONCONS_CSTRING_CONSTANT(CharT, Str) cstring_constant_of_type<CharT>(Str, JSONCONS_QUOTE(L, Str))
+#define JSONCONS_STRING_CONSTANT(CharT, Str) string_constant_of_type<CharT>(Str, JSONCONS_QUOTE(L, Str))
+#define JSONCONS_STRING_VIEW_CONSTANT(CharT, Str) string_view_constant_of_type<CharT>(Str, JSONCONS_QUOTE(L, Str))
+
+#if defined(__clang__)
+#define JSONCONS_HAS_STD_REGEX 1
+#elif (defined(__GNUC__) && (__GNUC__ == 4)) && (defined(__GNUC__) && __GNUC_MINOR__ < 9)
+// GCC 4.8 has broken regex support: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53631
+#else
+#define JSONCONS_HAS_STD_REGEX 1
+#endif
+
+#endif // JSONCONS_CONFIG_JSONCONS_CONFIG_HPP
+
+
diff --git a/include/jsoncons/config/version.hpp b/include/jsoncons/config/version.hpp
new file mode 100644
index 0000000..7f11eef
--- /dev/null
+++ b/include/jsoncons/config/version.hpp
@@ -0,0 +1,40 @@
+// Copyright 2017 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_VERSION_HPP
+#define JSONCONS_VERSION_HPP
+
+#include <iostream>
+
+#define JSONCONS_VERSION_MAJOR 0
+#define JSONCONS_VERSION_MINOR 168
+#define JSONCONS_VERSION_PATCH 7
+
+namespace jsoncons {
+
+struct versioning_info
+{
+ unsigned int const major;
+ unsigned int const minor;
+ unsigned int const patch;
+
+ friend std::ostream& operator<<(std::ostream& os, const versioning_info& ver)
+ {
+ os << ver.major << '.'
+ << ver.minor << '.'
+ << ver.patch;
+ return os;
+ }
+};
+
+constexpr versioning_info version()
+{
+ return versioning_info{JSONCONS_VERSION_MAJOR, JSONCONS_VERSION_MINOR, JSONCONS_VERSION_PATCH};
+}
+
+}
+
+#endif
diff --git a/include/jsoncons/conv_error.hpp b/include/jsoncons/conv_error.hpp
new file mode 100644
index 0000000..cad5460
--- /dev/null
+++ b/include/jsoncons/conv_error.hpp
@@ -0,0 +1,218 @@
+/// Copyright 2019 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_CONV_ERROR_HPP
+#define JSONCONS_CONV_ERROR_HPP
+
+#include <system_error>
+#include <jsoncons/config/jsoncons_config.hpp>
+
+namespace jsoncons {
+
+ class conv_error : public std::system_error, public virtual json_exception
+ {
+ std::size_t line_number_;
+ std::size_t column_number_;
+ mutable std::string what_;
+ public:
+ conv_error(std::error_code ec)
+ : std::system_error(ec), line_number_(0), column_number_(0)
+ {
+ }
+ conv_error(std::error_code ec, const std::string& what_arg)
+ : std::system_error(ec, what_arg), line_number_(0), column_number_(0)
+ {
+ }
+ conv_error(std::error_code ec, std::size_t position)
+ : std::system_error(ec), line_number_(0), 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(const conv_error& other) = default;
+
+ conv_error(conv_error&& other) = default;
+
+ const char* what() const noexcept override
+ {
+ 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_;
+ }
+ };
+
+ enum class conv_errc
+ {
+ success = 0,
+ conversion_failed,
+ not_utf8,
+ not_wide_char,
+ not_vector,
+ not_array,
+ not_map,
+ not_pair,
+ not_string,
+ not_string_view,
+ not_byte_string,
+ not_byte_string_view,
+ not_integer,
+ not_signed_integer,
+ not_unsigned_integer,
+ not_bigint,
+ not_double,
+ not_bool,
+ not_variant,
+ not_nullptr,
+ not_jsoncons_null_type,
+ not_bitset,
+ not_base64,
+ not_base64url,
+ not_base16
+ };
+
+ template <class InputIt>
+ struct decode_result
+ {
+ InputIt it;
+ conv_errc ec;
+ };
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+JSONCONS_DEPRECATED_MSG("Instead, use conv_error") typedef conv_error convert_error;
+#endif
+
+} // namespace jsoncons
+
+namespace std {
+ template<>
+ struct is_error_code_enum<jsoncons::conv_errc> : public true_type
+ {
+ };
+}
+
+namespace jsoncons {
+
+namespace detail {
+ class conv_error_category_impl
+ : public std::error_category
+ {
+ public:
+ const char* name() const noexcept override
+ {
+ return "jsoncons/convert";
+ }
+ std::string message(int ev) const override
+ {
+ switch (static_cast<conv_errc>(ev))
+ {
+ case conv_errc::conversion_failed:
+ return "Unable to convert into the provided type";
+ case conv_errc::not_utf8:
+ return "Cannot convert string to UTF-8";
+ case conv_errc::not_wide_char:
+ return "Cannot convert string to wide characters";
+ case conv_errc::not_vector:
+ return "Cannot convert to vector";
+ case conv_errc::not_array:
+ return "Cannot convert to std::array";
+ case conv_errc::not_map:
+ return "Cannot convert to map";
+ case conv_errc::not_pair:
+ return "Cannot convert to std::pair";
+ case conv_errc::not_string:
+ return "Cannot convert to string";
+ case conv_errc::not_string_view:
+ return "Cannot convert to string_view";
+ case conv_errc::not_byte_string:
+ return "Cannot convert to byte_string";
+ case conv_errc::not_byte_string_view:
+ return "Cannot convert to byte_string_view";
+ case conv_errc::not_integer:
+ return "Cannot convert to integer";
+ case conv_errc::not_signed_integer:
+ return "Cannot convert to signed integer";
+ case conv_errc::not_unsigned_integer:
+ return "Cannot convert to unsigned integer";
+ case conv_errc::not_bigint:
+ return "Cannot convert to bigint";
+ case conv_errc::not_double:
+ return "Cannot convert to double";
+ case conv_errc::not_bool:
+ return "Cannot convert to bool";
+ case conv_errc::not_variant:
+ return "Cannot convert to std::variant";
+ case conv_errc::not_nullptr:
+ return "Cannot convert to std::nullptr_t";
+ case conv_errc::not_jsoncons_null_type:
+ return "Cannot convert to jsoncons::null_type";
+ case conv_errc::not_bitset:
+ return "Cannot convert to std::bitset";
+ case conv_errc::not_base64:
+ return "Input is not a base64 encoded string";
+ case conv_errc::not_base64url:
+ return "Input is not a base64url encoded string";
+ case conv_errc::not_base16:
+ return "Input is not a base16 encoded string";
+ default:
+ return "Unknown conversion error";
+ }
+ }
+ };
+} // detail
+
+extern inline
+const std::error_category& conv_error_category()
+{
+ static detail::conv_error_category_impl instance;
+ return instance;
+}
+
+inline
+std::error_code make_error_code(conv_errc result)
+{
+ return std::error_code(static_cast<int>(result),conv_error_category());
+}
+
+}
+
+#endif
diff --git a/include/jsoncons/converter.hpp b/include/jsoncons/converter.hpp
new file mode 100644
index 0000000..15d1917
--- /dev/null
+++ b/include/jsoncons/converter.hpp
@@ -0,0 +1,296 @@
+// Copyright 2020 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_CONVERTER_HPP
+#define JSONCONS_CONVERTER_HPP
+
+#include <system_error> // std::error_code
+#include <jsoncons/more_type_traits.hpp>
+#include <jsoncons/byte_string.hpp>
+#include <jsoncons/json_type.hpp>
+#include <jsoncons/conv_error.hpp>
+#include <jsoncons/detail/write_number.hpp> // from_integer
+
+namespace jsoncons {
+
+ template <class Into, class Enable=void>
+ class converter
+ {
+ };
+
+ // Into list like of bytes
+ template <class Into>
+ class converter<Into,typename std::enable_if<(!type_traits::is_basic_string<Into>::value &&
+ type_traits::is_back_insertable_byte_container<Into>::value) ||
+ type_traits::is_basic_byte_string<Into>::value>::type>
+ {
+ using value_type = typename Into::value_type;
+ using allocator_type = typename Into::allocator_type;
+
+ public:
+
+ JSONCONS_CPP14_CONSTEXPR
+ Into from(const byte_string_view& bstr, semantic_tag tag, std::error_code& ec) const
+ {
+ Into bytes;
+ from_(bytes, bstr, tag, ec);
+ return bytes;
+ }
+
+ JSONCONS_CPP14_CONSTEXPR
+ Into from(const byte_string_view& bstr, semantic_tag tag, const allocator_type& alloc, std::error_code& ec) const
+ {
+ Into bytes(alloc);
+ from_(bytes, bstr, tag, ec);
+ return bytes;
+ }
+
+ template <class CharT>
+ JSONCONS_CPP14_CONSTEXPR
+ Into from(const jsoncons::basic_string_view<CharT>& s, semantic_tag tag, std::error_code& ec) const
+ {
+ Into bytes;
+ from_(bytes, s, tag, ec);
+ return bytes;
+ }
+
+ template <class CharT>
+ JSONCONS_CPP14_CONSTEXPR
+ Into from(const jsoncons::basic_string_view<CharT>& s, semantic_tag tag, const allocator_type& alloc, std::error_code& ec) const
+ {
+ Into bytes(alloc);
+ from_(bytes, s, tag, ec);
+ return bytes;
+ }
+
+ private:
+ JSONCONS_CPP14_CONSTEXPR
+ void from_(Into& bytes, const byte_string_view& bstr, semantic_tag, std::error_code&) const
+ {
+ for (auto ch : bstr)
+ {
+ bytes.push_back(static_cast<value_type>(ch));
+ }
+ }
+
+ template <class CharT>
+ JSONCONS_CPP14_CONSTEXPR
+ typename std::enable_if<type_traits::is_narrow_character<CharT>::value>::type
+ from_(Into& bytes, const jsoncons::basic_string_view<CharT>& s, semantic_tag tag, std::error_code& ec) const
+ {
+ switch (tag)
+ {
+ case semantic_tag::base16:
+ {
+ auto res = decode_base16(s.begin(), s.end(), bytes);
+ if (res.ec != conv_errc::success)
+ {
+ ec = conv_errc::not_byte_string;
+ }
+ break;
+ }
+ case semantic_tag::base64:
+ {
+ decode_base64(s.begin(), s.end(), bytes);
+ break;
+ }
+ case semantic_tag::base64url:
+ {
+ decode_base64url(s.begin(), s.end(), bytes);
+ break;
+ }
+ default:
+ {
+ ec = conv_errc::not_byte_string;
+ break;
+ }
+ }
+ }
+
+ template <class CharT>
+ typename std::enable_if<type_traits::is_wide_character<CharT>::value>::type
+ from_(Into& bytes, const jsoncons::basic_string_view<CharT>& s, semantic_tag tag, std::error_code& ec) const
+ {
+ std::string u;
+ auto retval = unicode_traits::convert(s.data(), s.size(), u);
+ if (retval.ec != unicode_traits::conv_errc())
+ {
+ ec = conv_errc::not_utf8;
+ return;
+ }
+ from_(bytes, jsoncons::string_view(u), tag, ec);
+ }
+ };
+
+ // Into string
+ template <class Into>
+ class converter<Into,typename std::enable_if<type_traits::is_basic_string<Into>::value>::type>
+ {
+ using char_type = typename Into::value_type;
+ using allocator_type = typename Into::allocator_type;
+ int dummy_;
+ public:
+ explicit converter(int dummy = int())
+ : dummy_(dummy)
+ {}
+ template<class Integer>
+ JSONCONS_CPP14_CONSTEXPR
+ typename std::enable_if<type_traits::is_integer<Integer>::value,Into>::type
+ from(Integer val, semantic_tag, std::error_code&) const
+ {
+ Into s;
+ jsoncons::detail::from_integer(val, s);
+ return s;
+ }
+
+ template<class Integer>
+ JSONCONS_CPP14_CONSTEXPR
+ typename std::enable_if<type_traits::is_integer<Integer>::value,Into>::type
+ from(Integer val, semantic_tag, const allocator_type& alloc, std::error_code&) const
+ {
+ Into s(alloc);
+ jsoncons::detail::from_integer(val, s);
+ return s;
+ }
+
+ Into from(double val, semantic_tag, std::error_code&) const
+ {
+ Into s;
+ jsoncons::detail::write_double f{float_chars_format::general,0};
+ f(val, s);
+ return s;
+ }
+
+ Into from(double val, semantic_tag, const allocator_type& alloc, std::error_code&) const
+ {
+ Into s(alloc);
+ jsoncons::detail::write_double f{float_chars_format::general,0};
+ f(val, s);
+ return s;
+ }
+
+ Into from(half_arg_t, uint16_t val, semantic_tag, std::error_code&) const
+ {
+ Into s;
+ jsoncons::detail::write_double f{float_chars_format::general,0};
+ double x = binary::decode_half(val);
+ f(x, s);
+ return s;
+ }
+
+ Into from(half_arg_t, uint16_t val, semantic_tag, const allocator_type& alloc, std::error_code&) const
+ {
+ Into s(alloc);
+ jsoncons::detail::write_double f{float_chars_format::general,0};
+ double x = binary::decode_half(val);
+ f(x, s);
+ return s;
+ }
+
+ template <class ChT = char_type>
+ JSONCONS_CPP14_CONSTEXPR
+ Into from(const byte_string_view& bytes, semantic_tag tag, std::error_code& ec) const
+ {
+ Into s;
+ from_(s, bytes, tag, ec);
+ return s;
+ }
+
+ template <class ChT = char_type>
+ JSONCONS_CPP14_CONSTEXPR
+ Into from(const byte_string_view& bytes, semantic_tag tag, const allocator_type& alloc, std::error_code& ec) const
+ {
+ Into s(alloc);
+ from_(s, bytes, tag, ec);
+ return s;
+ }
+
+ constexpr
+ Into from(const jsoncons::basic_string_view<char_type>& s, semantic_tag, std::error_code&) const
+ {
+ return Into(s.data(), s.size());
+ }
+
+ constexpr
+ Into from(const jsoncons::basic_string_view<char_type>& s, semantic_tag, const allocator_type& alloc, std::error_code&) const
+ {
+ return Into(s.data(), s.size(), alloc);
+ }
+
+ JSONCONS_CPP14_CONSTEXPR
+ Into from(bool val, semantic_tag, std::error_code&) 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");
+
+ return val ? Into(true_constant,4) : Into(false_constant,5);
+ }
+
+ JSONCONS_CPP14_CONSTEXPR
+ Into from(bool val, semantic_tag, const allocator_type& alloc, std::error_code&) 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");
+
+ return val ? Into(true_constant,4,alloc) : Into(false_constant,5,alloc);
+ }
+
+ JSONCONS_CPP14_CONSTEXPR
+ Into from(null_type, semantic_tag, std::error_code&) const
+ {
+ constexpr const char_type* null_constant = JSONCONS_CSTRING_CONSTANT(char_type,"null");
+
+ return Into(null_constant,4);
+ }
+
+ JSONCONS_CPP14_CONSTEXPR
+ Into from(null_type, semantic_tag, const allocator_type& alloc, std::error_code&) const
+ {
+ constexpr const char_type* null_constant = JSONCONS_CSTRING_CONSTANT(char_type,"null");
+
+ return Into(null_constant,4,alloc);
+ }
+ private:
+
+ template <class ChT = char_type>
+ JSONCONS_CPP14_CONSTEXPR
+ typename std::enable_if<type_traits::is_byte<ChT>::value>::type
+ from_(Into& s, const byte_string_view& bytes, semantic_tag tag, std::error_code&) const
+ {
+ switch (tag)
+ {
+ case semantic_tag::base64:
+ encode_base64(bytes.begin(), bytes.end(), s);
+ break;
+ case semantic_tag::base16:
+ encode_base16(bytes.begin(), bytes.end(), s);
+ break;
+ default:
+ encode_base64url(bytes.begin(), bytes.end(), s);
+ break;
+ }
+ }
+
+ template <class ChT = char_type>
+ typename std::enable_if<!type_traits::is_byte<ChT>::value>::type
+ from_(Into& s, const byte_string_view& bytes, semantic_tag tag, std::error_code& ec) const
+ {
+ converter<std::string> convert{ dummy_ };
+ std::string u = convert.from(bytes, tag, ec);
+
+ auto retval = unicode_traits::convert(u.data(), u.size(), s);
+ if (retval.ec != unicode_traits::conv_errc())
+ {
+ ec = conv_errc::not_wide_char;
+ }
+ }
+
+ };
+
+} // namespace jsoncons
+
+#endif
+
diff --git a/include/jsoncons/decode_json.hpp b/include/jsoncons/decode_json.hpp
new file mode 100644
index 0000000..e6ce1a1
--- /dev/null
+++ b/include/jsoncons/decode_json.hpp
@@ -0,0 +1,209 @@
+// Copyright 2017 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_DECODE_JSON_HPP
+#define JSONCONS_DECODE_JSON_HPP
+
+#include <iostream>
+#include <string>
+#include <tuple>
+#include <memory>
+#include <istream> // std::basic_istream
+#include <jsoncons/decode_traits.hpp>
+#include <jsoncons/json_cursor.hpp>
+
+namespace jsoncons {
+
+ // decode_json
+
+ template <class T, class Source>
+ typename std::enable_if<type_traits::is_basic_json<T>::value &&
+ type_traits::is_sequence_of<Source,typename T::char_type>::value,T>::type
+ decode_json(const Source& s,
+ const basic_json_decode_options<typename Source::value_type>& options = basic_json_decode_options<typename Source::value_type>())
+ {
+ using char_type = typename Source::value_type;
+
+ jsoncons::json_decoder<T> decoder;
+ basic_json_reader<char_type, string_source<char_type>> 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 <class T, class Source>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value &&
+ type_traits::is_char_sequence<Source>::value,T>::type
+ decode_json(const Source& s,
+ const basic_json_decode_options<typename Source::value_type>& options = basic_json_decode_options<typename Source::value_type>())
+ {
+ using char_type = typename Source::value_type;
+
+ basic_json_cursor<char_type,string_source<char_type>> cursor(s, options, default_json_parsing());
+ jsoncons::json_decoder<basic_json<char_type>> decoder;
+ std::error_code ec;
+ T val = decode_traits<T,char_type>::decode(cursor, decoder, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column()));
+ }
+ return val;
+ }
+
+ template <class T, class CharT>
+ typename std::enable_if<type_traits::is_basic_json<T>::value,T>::type
+ decode_json(std::basic_istream<CharT>& is,
+ const basic_json_decode_options<CharT>& options = basic_json_decode_options<CharT>())
+ {
+ jsoncons::json_decoder<T> decoder;
+ basic_json_reader<CharT, stream_source<CharT>> 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 <class T, class CharT>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value,T>::type
+ decode_json(std::basic_istream<CharT>& is,
+ const basic_json_decode_options<CharT>& options = basic_json_decode_options<CharT>())
+ {
+ basic_json_cursor<CharT> cursor(is, options, default_json_parsing());
+ json_decoder<basic_json<CharT>> decoder{};
+
+ std::error_code ec;
+ T val = decode_traits<T,CharT>::decode(cursor, decoder, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, cursor.line(), cursor.column()));
+ }
+ return val;
+ }
+
+ template <class T, class InputIt>
+ typename std::enable_if<type_traits::is_basic_json<T>::value,T>::type
+ decode_json(InputIt first, InputIt last,
+ const basic_json_decode_options<typename std::iterator_traits<InputIt>::value_type>& options =
+ basic_json_decode_options<typename std::iterator_traits<InputIt>::value_type>())
+ {
+ using char_type = typename std::iterator_traits<InputIt>::value_type;
+
+ jsoncons::json_decoder<T> decoder;
+ basic_json_reader<char_type, iterator_source<InputIt>> reader(iterator_source<InputIt>(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 <class T, class InputIt>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value,T>::type
+ decode_json(InputIt first, InputIt last,
+ const basic_json_decode_options<typename std::iterator_traits<InputIt>::value_type>& options =
+ basic_json_decode_options<typename std::iterator_traits<InputIt>::value_type>())
+ {
+ using char_type = typename std::iterator_traits<InputIt>::value_type;
+
+ basic_json_cursor<char_type,iterator_source<InputIt>> cursor(iterator_source<InputIt>(first, last), options, default_json_parsing());
+ jsoncons::json_decoder<basic_json<char_type>> decoder;
+ std::error_code ec;
+ T val = decode_traits<T,char_type>::decode(cursor, decoder, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, cursor.line(), cursor.column()));
+ }
+ return val;
+ }
+
+ // With leading allocator parameter
+
+ template <class T,class Source,class TempAllocator>
+ typename std::enable_if<type_traits::is_basic_json<T>::value &&
+ type_traits::is_sequence_of<Source,typename T::char_type>::value,T>::type
+ decode_json(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ const Source& s,
+ const basic_json_decode_options<typename Source::value_type>& options = basic_json_decode_options<typename Source::value_type>())
+ {
+ using char_type = typename Source::value_type;
+
+ json_decoder<T,TempAllocator> decoder(temp_alloc);
+
+ basic_json_reader<char_type, string_source<char_type>,TempAllocator> reader(s, decoder, options, temp_alloc);
+ reader.read();
+ if (!decoder.is_valid())
+ {
+ JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column()));
+ }
+ return decoder.get_result();
+ }
+
+ template <class T,class Source,class TempAllocator>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value &&
+ type_traits::is_char_sequence<Source>::value,T>::type
+ decode_json(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ const Source& s,
+ const basic_json_decode_options<typename Source::value_type>& options = basic_json_decode_options<typename Source::value_type>())
+ {
+ using char_type = typename Source::value_type;
+
+ basic_json_cursor<char_type,string_source<char_type>,TempAllocator> cursor(s, options, default_json_parsing(), temp_alloc);
+ json_decoder<basic_json<char_type,sorted_policy,TempAllocator>,TempAllocator> decoder(result_allocator_arg, temp_alloc, temp_alloc);
+
+ std::error_code ec;
+ T val = decode_traits<T,char_type>::decode(cursor, decoder, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column()));
+ }
+ return val;
+ }
+
+ template <class T,class CharT,class TempAllocator>
+ typename std::enable_if<type_traits::is_basic_json<T>::value,T>::type
+ decode_json(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ std::basic_istream<CharT>& is,
+ const basic_json_decode_options<CharT>& options = basic_json_decode_options<CharT>())
+ {
+ json_decoder<T,TempAllocator> decoder(temp_alloc);
+
+ basic_json_reader<CharT, stream_source<CharT>,TempAllocator> reader(is, decoder, options, temp_alloc);
+ reader.read();
+ if (!decoder.is_valid())
+ {
+ JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column()));
+ }
+ return decoder.get_result();
+ }
+
+ template <class T,class CharT,class TempAllocator>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value,T>::type
+ decode_json(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ std::basic_istream<CharT>& is,
+ const basic_json_decode_options<CharT>& options = basic_json_decode_options<CharT>())
+ {
+ basic_json_cursor<CharT,stream_source<CharT>,TempAllocator> cursor(is, options, default_json_parsing(), temp_alloc);
+ json_decoder<basic_json<CharT,sorted_policy,TempAllocator>,TempAllocator> decoder(result_allocator_arg, temp_alloc,temp_alloc);
+
+ std::error_code ec;
+ T val = decode_traits<T,CharT>::decode(cursor, decoder, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column()));
+ }
+ return val;
+ }
+
+} // jsoncons
+
+#endif
+
diff --git a/include/jsoncons/decode_traits.hpp b/include/jsoncons/decode_traits.hpp
new file mode 100644
index 0000000..2c9ee6e
--- /dev/null
+++ b/include/jsoncons/decode_traits.hpp
@@ -0,0 +1,651 @@
+// Copyright 2017 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_DECODE_TRAITS_HPP
+#define JSONCONS_DECODE_TRAITS_HPP
+
+#include <string>
+#include <tuple>
+#include <array>
+#include <memory>
+#include <type_traits> // std::enable_if, std::true_type, std::false_type
+#include <jsoncons/json_visitor.hpp>
+#include <jsoncons/json_decoder.hpp>
+#include <jsoncons/json_type_traits.hpp>
+#include <jsoncons/staj_cursor.hpp>
+#include <jsoncons/conv_error.hpp>
+#include <jsoncons/more_type_traits.hpp>
+
+namespace jsoncons {
+
+ // decode_traits
+
+ template <class T, class CharT, class Enable = void>
+ struct decode_traits
+ {
+ template <class Json,class TempAllocator>
+ static T decode(basic_staj_cursor<CharT>& cursor,
+ json_decoder<Json,TempAllocator>& decoder,
+ std::error_code& ec)
+ {
+ decoder.reset();
+ cursor.read_to(decoder, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column()));
+ }
+ else if (!decoder.is_valid())
+ {
+ JSONCONS_THROW(ser_error(conv_errc::conversion_failed, cursor.context().line(), cursor.context().column()));
+ }
+ return decoder.get_result().template as<T>();
+ }
+ };
+
+ // specializations
+
+ // primitive
+
+ template <class T, class CharT>
+ struct decode_traits<T,CharT,
+ typename std::enable_if<type_traits::is_primitive<T>::value
+ >::type>
+ {
+ template <class Json,class TempAllocator>
+ static T decode(basic_staj_cursor<CharT>& cursor,
+ json_decoder<Json,TempAllocator>&,
+ std::error_code& ec)
+ {
+ T v = cursor.current().template get<T>(ec);
+ return v;
+ }
+ };
+
+ // string
+
+ template <class T, class CharT>
+ struct decode_traits<T,CharT,
+ typename std::enable_if<type_traits::is_basic_string<T>::value &&
+ std::is_same<typename T::value_type,CharT>::value
+ >::type>
+ {
+ template <class Json,class TempAllocator>
+ static T decode(basic_staj_cursor<CharT>& cursor,
+ json_decoder<Json,TempAllocator>&,
+ std::error_code& ec)
+ {
+ T v = cursor.current().template get<T>(ec);
+ return v;
+ }
+ };
+
+ template <class T, class CharT>
+ struct decode_traits<T,CharT,
+ typename std::enable_if<type_traits::is_basic_string<T>::value &&
+ !std::is_same<typename T::value_type,CharT>::value
+ >::type>
+ {
+ template <class Json,class TempAllocator>
+ static T decode(basic_staj_cursor<CharT>& cursor,
+ json_decoder<Json,TempAllocator>&,
+ std::error_code& ec)
+ {
+ auto val = cursor.current().template get<std::basic_string<CharT>>(ec);
+ T s;
+ if (!ec)
+ {
+ unicode_traits::convert(val.data(), val.size(), s);
+ }
+ return s;
+ }
+ };
+
+ // std::pair
+
+ template <class T1, class T2, class CharT>
+ struct decode_traits<std::pair<T1, T2>, CharT>
+ {
+ template <class Json, class TempAllocator>
+ static std::pair<T1, T2> decode(basic_staj_cursor<CharT>& cursor,
+ json_decoder<Json, TempAllocator>& decoder,
+ std::error_code& ec)
+ {
+ using value_type = std::pair<T1, T2>;
+ cursor.array_expected(ec);
+ if (ec)
+ {
+ return value_type{};
+ }
+ if (cursor.current().event_type() != staj_event_type::begin_array)
+ {
+ ec = conv_errc::not_pair;
+ return value_type();
+ }
+ cursor.next(ec); // skip past array
+ if (ec)
+ {
+ return value_type();
+ }
+
+ T1 v1 = decode_traits<T1,CharT>::decode(cursor, decoder, ec);
+ if (ec) {return value_type();}
+ cursor.next(ec);
+ if (ec) {return value_type();}
+ T2 v2 = decode_traits<T2, CharT>::decode(cursor, decoder, ec);
+ if (ec) {return value_type();}
+ cursor.next(ec);
+
+ if (cursor.current().event_type() != staj_event_type::end_array)
+ {
+ ec = conv_errc::not_pair;
+ return value_type();
+ }
+ return std::make_pair(v1, v2);
+ }
+ };
+
+ // vector like
+ template <class T, class CharT>
+ struct decode_traits<T,CharT,
+ typename std::enable_if<!is_json_type_traits_declared<T>::value &&
+ type_traits::is_list_like<T>::value &&
+ type_traits::is_back_insertable<T>::value &&
+ !type_traits::is_typed_array<T>::value
+ >::type>
+ {
+ using value_type = typename T::value_type;
+
+ template <class Json,class TempAllocator>
+ static T decode(basic_staj_cursor<CharT>& cursor,
+ json_decoder<Json,TempAllocator>& decoder,
+ std::error_code& ec)
+ {
+ T v;
+
+ cursor.array_expected(ec);
+ if (ec)
+ {
+ return T{};
+ }
+ if (cursor.current().event_type() != staj_event_type::begin_array)
+ {
+ ec = conv_errc::not_vector;
+ return v;
+ }
+ cursor.next(ec);
+ while (cursor.current().event_type() != staj_event_type::end_array && !ec)
+ {
+ v.push_back(decode_traits<value_type,CharT>::decode(cursor, decoder, ec));
+ if (ec) {return T{};}
+ cursor.next(ec);
+ }
+ return v;
+ }
+ };
+
+ template <class T>
+ struct typed_array_visitor : public default_json_visitor
+ {
+ T& v_;
+ int level_;
+ public:
+ using value_type = typename T::value_type;
+
+ typed_array_visitor(T& v)
+ : default_json_visitor(false,conv_errc::not_vector), v_(v), level_(0)
+ {
+ }
+ private:
+ bool visit_begin_array(semantic_tag,
+ const ser_context&,
+ std::error_code& ec) override
+ {
+ if (++level_ != 1)
+ {
+ ec = conv_errc::not_vector;
+ return false;
+ }
+ return true;
+ }
+
+ bool visit_begin_array(std::size_t size,
+ semantic_tag,
+ const ser_context&,
+ std::error_code& ec) override
+ {
+ if (++level_ != 1)
+ {
+ ec = conv_errc::not_vector;
+ return false;
+ }
+ if (size > 0)
+ {
+ reserve_storage(typename std::integral_constant<bool, type_traits::has_reserve<T>::value>::type(), v_, size);
+ }
+ return true;
+ }
+
+ bool visit_end_array(const ser_context&,
+ std::error_code& ec) override
+ {
+ if (level_ != 1)
+ {
+ ec = conv_errc::not_vector;
+ return false;
+ }
+ return false;
+ }
+
+ bool visit_uint64(uint64_t value,
+ semantic_tag,
+ const ser_context&,
+ std::error_code&) override
+ {
+ v_.push_back(static_cast<value_type>(value));
+ return true;
+ }
+
+ bool visit_int64(int64_t value,
+ semantic_tag,
+ const ser_context&,
+ std::error_code&) override
+ {
+ v_.push_back(static_cast<value_type>(value));
+ return true;
+ }
+
+ bool visit_half(uint16_t value,
+ semantic_tag,
+ const ser_context&,
+ std::error_code&) override
+ {
+ return visit_half_(typename std::integral_constant<bool, std::is_integral<value_type>::value>::type(), value);
+ }
+
+ bool visit_half_(std::true_type, uint16_t value)
+ {
+ v_.push_back(static_cast<value_type>(value));
+ return true;
+ }
+
+ bool visit_half_(std::false_type, uint16_t value)
+ {
+ v_.push_back(static_cast<value_type>(binary::decode_half(value)));
+ return true;
+ }
+
+ bool visit_double(double value,
+ semantic_tag,
+ const ser_context&,
+ std::error_code&) override
+ {
+ v_.push_back(static_cast<value_type>(value));
+ return true;
+ }
+
+ bool visit_typed_array(const jsoncons::span<const value_type>& data,
+ semantic_tag,
+ const ser_context&,
+ std::error_code&) override
+ {
+ v_ = std::vector<value_type>(data.begin(),data.end());
+ return false;
+ }
+
+ 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 <class T, class CharT>
+ struct decode_traits<T,CharT,
+ typename std::enable_if<!is_json_type_traits_declared<T>::value &&
+ type_traits::is_list_like<T>::value &&
+ type_traits::is_back_insertable_byte_container<T>::value &&
+ type_traits::is_typed_array<T>::value
+ >::type>
+ {
+ using value_type = typename T::value_type;
+
+ template <class Json,class TempAllocator>
+ static T decode(basic_staj_cursor<CharT>& cursor,
+ json_decoder<Json,TempAllocator>&,
+ std::error_code& ec)
+ {
+ cursor.array_expected(ec);
+ if (ec)
+ {
+ return T{};
+ }
+ switch (cursor.current().event_type())
+ {
+ case staj_event_type::byte_string_value:
+ {
+ auto bytes = cursor.current().template get<byte_string_view>(ec);
+ if (!ec)
+ {
+ T v;
+ if (cursor.current().size() > 0)
+ {
+ reserve_storage(typename std::integral_constant<bool, type_traits::has_reserve<T>::value>::type(), v, cursor.current().size());
+ }
+ for (auto ch : bytes)
+ {
+ v.push_back(static_cast<value_type>(ch));
+ }
+ cursor.next(ec);
+ return v;
+ }
+ else
+ {
+ return T{};
+ }
+ }
+ case staj_event_type::begin_array:
+ {
+ T v;
+ if (cursor.current().size() > 0)
+ {
+ reserve_storage(typename std::integral_constant<bool, type_traits::has_reserve<T>::value>::type(), v, cursor.current().size());
+ }
+ typed_array_visitor<T> visitor(v);
+ cursor.read_to(visitor, ec);
+ return v;
+ }
+ default:
+ {
+ ec = conv_errc::not_vector;
+ return T{};
+ }
+ }
+ }
+
+ 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 <class T, class CharT>
+ struct decode_traits<T,CharT,
+ typename std::enable_if<!is_json_type_traits_declared<T>::value &&
+ type_traits::is_list_like<T>::value &&
+ type_traits::is_back_insertable<T>::value &&
+ !type_traits::is_back_insertable_byte_container<T>::value &&
+ type_traits::is_typed_array<T>::value
+ >::type>
+ {
+ using value_type = typename T::value_type;
+
+ template <class Json,class TempAllocator>
+ static T decode(basic_staj_cursor<CharT>& cursor,
+ json_decoder<Json,TempAllocator>&,
+ std::error_code& ec)
+ {
+ cursor.array_expected(ec);
+ if (ec)
+ {
+ return T{};
+ }
+ switch (cursor.current().event_type())
+ {
+ case staj_event_type::begin_array:
+ {
+ T v;
+ if (cursor.current().size() > 0)
+ {
+ reserve_storage(typename std::integral_constant<bool, type_traits::has_reserve<T>::value>::type(), v, cursor.current().size());
+ }
+ typed_array_visitor<T> visitor(v);
+ cursor.read_to(visitor, ec);
+ return v;
+ }
+ default:
+ {
+ ec = conv_errc::not_vector;
+ return T{};
+ }
+ }
+ }
+
+ 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 <class T, class CharT>
+ struct decode_traits<T,CharT,
+ typename std::enable_if<!is_json_type_traits_declared<T>::value &&
+ type_traits::is_list_like<T>::value &&
+ !type_traits::is_back_insertable<T>::value &&
+ type_traits::is_insertable<T>::value
+ >::type>
+ {
+ using value_type = typename T::value_type;
+
+ template <class Json,class TempAllocator>
+ static T decode(basic_staj_cursor<CharT>& cursor,
+ json_decoder<Json,TempAllocator>& decoder,
+ std::error_code& ec)
+ {
+ T v;
+
+ cursor.array_expected(ec);
+ if (ec)
+ {
+ return T{};
+ }
+ if (cursor.current().event_type() != staj_event_type::begin_array)
+ {
+ ec = conv_errc::not_vector;
+ return v;
+ }
+ if (cursor.current().size() > 0)
+ {
+ reserve_storage(typename std::integral_constant<bool, type_traits::has_reserve<T>::value>::type(), v, cursor.current().size());
+ }
+ cursor.next(ec);
+ while (cursor.current().event_type() != staj_event_type::end_array && !ec)
+ {
+ v.insert(decode_traits<value_type,CharT>::decode(cursor, decoder, ec));
+ if (ec) {return T{};}
+ cursor.next(ec);
+ if (ec) {return T{};}
+ }
+ return 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 <class T, class CharT, std::size_t N>
+ struct decode_traits<std::array<T,N>,CharT>
+ {
+ using value_type = typename std::array<T,N>::value_type;
+
+ template <class Json,class TempAllocator>
+ static std::array<T, N> decode(basic_staj_cursor<CharT>& cursor,
+ json_decoder<Json,TempAllocator>& decoder,
+ std::error_code& ec)
+ {
+ std::array<T,N> v;
+ cursor.array_expected(ec);
+ if (ec)
+ {
+ v.fill(T());
+ return v;
+ }
+ v.fill(T{});
+ if (cursor.current().event_type() != staj_event_type::begin_array)
+ {
+ ec = conv_errc::not_vector;
+ return v;
+ }
+ cursor.next(ec);
+ for (std::size_t i = 0; i < N && cursor.current().event_type() != staj_event_type::end_array && !ec; ++i)
+ {
+ v[i] = decode_traits<value_type,CharT>::decode(cursor, decoder, ec);
+ if (ec) {return v;}
+ cursor.next(ec);
+ if (ec) {return v;}
+ }
+ return v;
+ }
+ };
+
+ // map like
+
+ template <class T, class CharT>
+ struct decode_traits<T,CharT,
+ typename std::enable_if<!is_json_type_traits_declared<T>::value &&
+ type_traits::is_map_like<T>::value &&
+ type_traits::is_constructible_from_const_pointer_and_size<typename T::key_type>::value
+ >::type>
+ {
+ using mapped_type = typename T::mapped_type;
+ using value_type = typename T::value_type;
+ using key_type = typename T::key_type;
+
+ template <class Json,class TempAllocator>
+ static T decode(basic_staj_cursor<CharT>& cursor,
+ json_decoder<Json,TempAllocator>& decoder,
+ std::error_code& ec)
+ {
+ T val;
+ if (cursor.current().event_type() != staj_event_type::begin_object)
+ {
+ ec = conv_errc::not_map;
+ return val;
+ }
+ if (cursor.current().size() > 0)
+ {
+ reserve_storage(typename std::integral_constant<bool, type_traits::has_reserve<T>::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)
+ {
+ ec = json_errc::expected_key;
+ return val;
+ }
+ auto key = cursor.current().template get<key_type>(ec);
+ if (ec) return val;
+ cursor.next(ec);
+ if (ec) return val;
+ val.emplace(std::move(key),decode_traits<mapped_type,CharT>::decode(cursor, decoder, ec));
+ if (ec) {return val;}
+ cursor.next(ec);
+ if (ec) {return val;}
+ }
+ return 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 <class T, class CharT>
+ struct decode_traits<T,CharT,
+ typename std::enable_if<!is_json_type_traits_declared<T>::value &&
+ type_traits::is_map_like<T>::value &&
+ std::is_integral<typename T::key_type>::value
+ >::type>
+ {
+ using mapped_type = typename T::mapped_type;
+ using value_type = typename T::value_type;
+ using key_type = typename T::key_type;
+
+ template <class Json,class TempAllocator>
+ static T decode(basic_staj_cursor<CharT>& cursor,
+ json_decoder<Json,TempAllocator>& decoder,
+ std::error_code& ec)
+ {
+ T val;
+ if (cursor.current().event_type() != staj_event_type::begin_object)
+ {
+ ec = conv_errc::not_map;
+ return val;
+ }
+ if (cursor.current().size() > 0)
+ {
+ reserve_storage(typename std::integral_constant<bool, type_traits::has_reserve<T>::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)
+ {
+ ec = json_errc::expected_key;
+ return val;
+ }
+ auto s = cursor.current().template get<jsoncons::basic_string_view<typename Json::char_type>>(ec);
+ if (ec) return val;
+ key_type n{0};
+ auto r = jsoncons::detail::to_integer(s.data(), s.size(), n);
+ if (r.ec != jsoncons::detail::to_integer_errc())
+ {
+ ec = json_errc::invalid_number;
+ return val;
+ }
+ cursor.next(ec);
+ if (ec) return val;
+ val.emplace(n, decode_traits<mapped_type,CharT>::decode(cursor, decoder, ec));
+ if (ec) {return val;}
+ cursor.next(ec);
+ if (ec) {return val;}
+ }
+ return 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)
+ {
+ }
+ };
+
+} // jsoncons
+
+#endif
+
diff --git a/include/jsoncons/detail/endian.hpp b/include/jsoncons/detail/endian.hpp
new file mode 100644
index 0000000..d47a0f7
--- /dev/null
+++ b/include/jsoncons/detail/endian.hpp
@@ -0,0 +1,44 @@
+// Copyright 2020 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_DETAIL_ENDIAN_HPP
+#define JSONCONS_DETAIL_ENDIAN_HPP
+
+#if defined(__sun)
+# include <sys/byteorder.h>
+#endif
+
+namespace jsoncons {
+namespace detail {
+
+ enum class endian
+ {
+ #if defined(_MSC_VER)
+ // MSVC, which implies Windows, which implies little-endian
+ little = 0,
+ big = 1,
+ native = little
+ #elif defined(__ORDER_LITTLE_ENDIAN__) && defined(__ORDER_BIG_ENDIAN__) && defined(__BYTE_ORDER__)
+ little = __ORDER_LITTLE_ENDIAN__,
+ big = __ORDER_BIG_ENDIAN__,
+ native = __BYTE_ORDER__
+ #elif defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN)
+ little = 0,
+ big = 1,
+ native = big
+ #elif !defined(_BIG_ENDIAN) && defined(_LITTLE_ENDIAN)
+ little = 0,
+ big = 1,
+ native = little
+ #else
+ #error "Unable to determine byte order!"
+ #endif
+ };
+
+} // namespace detail
+} // namespace jsoncons
+
+#endif
diff --git a/include/jsoncons/detail/grisu3.hpp b/include/jsoncons/detail/grisu3.hpp
new file mode 100644
index 0000000..771cf1d
--- /dev/null
+++ b/include/jsoncons/detail/grisu3.hpp
@@ -0,0 +1,312 @@
+/*
+Implements the Grisu3 algorithm for printing floating-point numbers.
+
+Follows Florian Loitsch's grisu3_59_56 implementation, available at
+http://florian.loitsch.com/publications, in bench.tar.gz, with
+minor modifications.
+*/
+
+/*
+ Copyright (c) 2009 Florian Loitsch
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation
+ files (the "Software"), to deal in the Software without
+ restriction, including without limitation the rights to use,
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following
+ conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef JSONCONS_GRISU3_HPP
+#define JSONCONS_GRISU3_HPP
+
+#pragma once
+#include <stdlib.h>
+#include <cinttypes>
+#include <cstdint>
+#include <cassert>
+#include <cmath>
+#include <cstring> // std::memmove
+#include <jsoncons/detail/write_number.hpp>
+
+namespace jsoncons { namespace detail {
+
+// diy_fp
+
+typedef struct diy_fp_t {
+ uint64_t f;
+ int e;
+} diy_fp_t;
+
+inline
+diy_fp_t minus(diy_fp_t x, diy_fp_t y)
+{
+ assert(x.e == y.e);
+ assert(x.f >= y.f);
+ diy_fp_t r = { x.f = x.f - y.f, x.e };
+ return r;
+}
+
+inline
+diy_fp_t multiply(diy_fp_t x, diy_fp_t y)
+{
+ uint64_t a, b, c, d, ac, bc, ad, bd, tmp;
+ diy_fp_t r; uint64_t M32 = 0xFFFFFFFF;
+ a = x.f >> 32; b = x.f & M32;
+ c = y.f >> 32; d = y.f & M32;
+ ac = a * c; bc = b * c; ad = a * d; bd = b * d;
+ tmp = (bd >> 32) + (ad & M32) + (bc & M32);
+ tmp += 1U << 31; /// mult_round
+ r.f = ac + (ad >> 32) + (bc >> 32) + (tmp >> 32);
+ r.e = x.e + y.e + 64;
+ return r;
+}
+
+// k_comp
+
+inline
+int k_comp(int e, int alpha, int /*gamma*/)
+{
+ constexpr double d_1_log2_10 = 0.30102999566398114; // 1 / lg(10)
+ int x = alpha - e + 63;
+ return static_cast<int>(std::ceil(x * d_1_log2_10));
+}
+
+// powers_ten_round64
+
+constexpr int diy_significand_size = 64;
+
+static constexpr uint64_t powers_ten[] = { 0xbf29dcaba82fdeae, 0xeef453d6923bd65a, 0x9558b4661b6565f8, 0xbaaee17fa23ebf76, 0xe95a99df8ace6f54, 0x91d8a02bb6c10594, 0xb64ec836a47146fa, 0xe3e27a444d8d98b8, 0x8e6d8c6ab0787f73, 0xb208ef855c969f50, 0xde8b2b66b3bc4724, 0x8b16fb203055ac76, 0xaddcb9e83c6b1794, 0xd953e8624b85dd79, 0x87d4713d6f33aa6c, 0xa9c98d8ccb009506, 0xd43bf0effdc0ba48, 0x84a57695fe98746d, 0xa5ced43b7e3e9188, 0xcf42894a5dce35ea, 0x818995ce7aa0e1b2, 0xa1ebfb4219491a1f, 0xca66fa129f9b60a7, 0xfd00b897478238d1, 0x9e20735e8cb16382, 0xc5a890362fddbc63, 0xf712b443bbd52b7c, 0x9a6bb0aa55653b2d, 0xc1069cd4eabe89f9, 0xf148440a256e2c77, 0x96cd2a865764dbca, 0xbc807527ed3e12bd, 0xeba09271e88d976c, 0x93445b8731587ea3, 0xb8157268fdae9e4c, 0xe61acf033d1a45df, 0x8fd0c16206306bac, 0xb3c4f1ba87bc8697, 0xe0b62e2929aba83c, 0x8c71dcd9ba0b4926, 0xaf8e5410288e1b6f, 0xdb71e91432b1a24b, 0x892731ac9faf056f, 0xab70fe17c79ac6ca, 0xd64d3d9db981787d, 0x85f0468293f0eb4e, 0xa76c582338ed2622, 0xd1476e2c07286faa, 0x82cca4db847945ca, 0xa37fce126597973d, 0xcc5fc196fefd7d0c, 0xff77b1fcbebcdc4f, 0x9faacf3df73609b1, 0xc795830d75038c1e, 0xf97ae3d0d2446f25, 0x9becce62836ac577, 0xc2e801fb244576d5, 0xf3a20279ed56d48a, 0x9845418c345644d7, 0xbe5691ef416bd60c, 0xedec366b11c6cb8f, 0x94b3a202eb1c3f39, 0xb9e08a83a5e34f08, 0xe858ad248f5c22ca, 0x91376c36d99995be, 0xb58547448ffffb2e, 0xe2e69915b3fff9f9, 0x8dd01fad907ffc3c, 0xb1442798f49ffb4b, 0xdd95317f31c7fa1d, 0x8a7d3eef7f1cfc52, 0xad1c8eab5ee43b67, 0xd863b256369d4a41, 0x873e4f75e2224e68, 0xa90de3535aaae202, 0xd3515c2831559a83, 0x8412d9991ed58092, 0xa5178fff668ae0b6, 0xce5d73ff402d98e4, 0x80fa687f881c7f8e, 0xa139029f6a239f72, 0xc987434744ac874f, 0xfbe9141915d7a922, 0x9d71ac8fada6c9b5, 0xc4ce17b399107c23, 0xf6019da07f549b2b, 0x99c102844f94e0fb, 0xc0314325637a193a, 0xf03d93eebc589f88, 0x96267c7535b763b5, 0xbbb01b9283253ca3, 0xea9c227723ee8bcb, 0x92a1958a7675175f, 0xb749faed14125d37, 0xe51c79a85916f485, 0x8f31cc0937ae58d3, 0xb2fe3f0b8599ef08, 0xdfbdcece67006ac9, 0x8bd6a141006042be, 0xaecc49914078536d, 0xda7f5bf590966849, 0x888f99797a5e012d, 0xaab37fd7d8f58179, 0xd5605fcdcf32e1d7, 0x855c3be0a17fcd26, 0xa6b34ad8c9dfc070, 0xd0601d8efc57b08c, 0x823c12795db6ce57, 0xa2cb1717b52481ed, 0xcb7ddcdda26da269, 0xfe5d54150b090b03, 0x9efa548d26e5a6e2, 0xc6b8e9b0709f109a, 0xf867241c8cc6d4c1, 0x9b407691d7fc44f8, 0xc21094364dfb5637, 0xf294b943e17a2bc4, 0x979cf3ca6cec5b5b, 0xbd8430bd08277231, 0xece53cec4a314ebe, 0x940f4613ae5ed137, 0xb913179899f68584, 0xe757dd7ec07426e5, 0x9096ea6f3848984f, 0xb4bca50b065abe63, 0xe1ebce4dc7f16dfc, 0x8d3360f09cf6e4bd, 0xb080392cc4349ded, 0xdca04777f541c568, 0x89e42caaf9491b61, 0xac5d37d5b79b6239, 0xd77485cb25823ac7, 0x86a8d39ef77164bd, 0xa8530886b54dbdec, 0xd267caa862a12d67, 0x8380dea93da4bc60, 0xa46116538d0deb78, 0xcd795be870516656, 0x806bd9714632dff6, 0xa086cfcd97bf97f4, 0xc8a883c0fdaf7df0, 0xfad2a4b13d1b5d6c, 0x9cc3a6eec6311a64, 0xc3f490aa77bd60fd, 0xf4f1b4d515acb93c, 0x991711052d8bf3c5, 0xbf5cd54678eef0b7, 0xef340a98172aace5, 0x9580869f0e7aac0f, 0xbae0a846d2195713, 0xe998d258869facd7, 0x91ff83775423cc06, 0xb67f6455292cbf08, 0xe41f3d6a7377eeca, 0x8e938662882af53e, 0xb23867fb2a35b28e, 0xdec681f9f4c31f31, 0x8b3c113c38f9f37f, 0xae0b158b4738705f, 0xd98ddaee19068c76, 0x87f8a8d4cfa417ca, 0xa9f6d30a038d1dbc, 0xd47487cc8470652b, 0x84c8d4dfd2c63f3b, 0xa5fb0a17c777cf0a, 0xcf79cc9db955c2cc, 0x81ac1fe293d599c0, 0xa21727db38cb0030, 0xca9cf1d206fdc03c, 0xfd442e4688bd304b, 0x9e4a9cec15763e2f, 0xc5dd44271ad3cdba, 0xf7549530e188c129, 0x9a94dd3e8cf578ba, 0xc13a148e3032d6e8, 0xf18899b1bc3f8ca2, 0x96f5600f15a7b7e5, 0xbcb2b812db11a5de, 0xebdf661791d60f56, 0x936b9fcebb25c996, 0xb84687c269ef3bfb, 0xe65829b3046b0afa, 0x8ff71a0fe2c2e6dc, 0xb3f4e093db73a093, 0xe0f218b8d25088b8, 0x8c974f7383725573, 0xafbd2350644eead0, 0xdbac6c247d62a584, 0x894bc396ce5da772, 0xab9eb47c81f5114f, 0xd686619ba27255a3, 0x8613fd0145877586, 0xa798fc4196e952e7, 0xd17f3b51fca3a7a1, 0x82ef85133de648c5, 0xa3ab66580d5fdaf6, 0xcc963fee10b7d1b3, 0xffbbcfe994e5c620, 0x9fd561f1fd0f9bd4, 0xc7caba6e7c5382c9, 0xf9bd690a1b68637b, 0x9c1661a651213e2d, 0xc31bfa0fe5698db8, 0xf3e2f893dec3f126, 0x986ddb5c6b3a76b8, 0xbe89523386091466, 0xee2ba6c0678b597f, 0x94db483840b717f0, 0xba121a4650e4ddec, 0xe896a0d7e51e1566, 0x915e2486ef32cd60, 0xb5b5ada8aaff80b8, 0xe3231912d5bf60e6, 0x8df5efabc5979c90, 0xb1736b96b6fd83b4, 0xddd0467c64bce4a1, 0x8aa22c0dbef60ee4, 0xad4ab7112eb3929e, 0xd89d64d57a607745, 0x87625f056c7c4a8b, 0xa93af6c6c79b5d2e, 0xd389b47879823479, 0x843610cb4bf160cc, 0xa54394fe1eedb8ff, 0xce947a3da6a9273e, 0x811ccc668829b887, 0xa163ff802a3426a9, 0xc9bcff6034c13053, 0xfc2c3f3841f17c68, 0x9d9ba7832936edc1, 0xc5029163f384a931, 0xf64335bcf065d37d, 0x99ea0196163fa42e, 0xc06481fb9bcf8d3a, 0xf07da27a82c37088, 0x964e858c91ba2655, 0xbbe226efb628afeb, 0xeadab0aba3b2dbe5, 0x92c8ae6b464fc96f, 0xb77ada0617e3bbcb, 0xe55990879ddcaabe, 0x8f57fa54c2a9eab7, 0xb32df8e9f3546564, 0xdff9772470297ebd, 0x8bfbea76c619ef36, 0xaefae51477a06b04, 0xdab99e59958885c5, 0x88b402f7fd75539b, 0xaae103b5fcd2a882, 0xd59944a37c0752a2, 0x857fcae62d8493a5, 0xa6dfbd9fb8e5b88f, 0xd097ad07a71f26b2, 0x825ecc24c8737830, 0xa2f67f2dfa90563b, 0xcbb41ef979346bca, 0xfea126b7d78186bd, 0x9f24b832e6b0f436, 0xc6ede63fa05d3144, 0xf8a95fcf88747d94, 0x9b69dbe1b548ce7d, 0xc24452da229b021c, 0xf2d56790ab41c2a3, 0x97c560ba6b0919a6, 0xbdb6b8e905cb600f, 0xed246723473e3813, 0x9436c0760c86e30c, 0xb94470938fa89bcf, 0xe7958cb87392c2c3, 0x90bd77f3483bb9ba, 0xb4ecd5f01a4aa828, 0xe2280b6c20dd5232, 0x8d590723948a535f, 0xb0af48ec79ace837, 0xdcdb1b2798182245, 0x8a08f0f8bf0f156b, 0xac8b2d36eed2dac6, 0xd7adf884aa879177, 0x86ccbb52ea94baeb, 0xa87fea27a539e9a5, 0xd29fe4b18e88640f, 0x83a3eeeef9153e89, 0xa48ceaaab75a8e2b, 0xcdb02555653131b6, 0x808e17555f3ebf12, 0xa0b19d2ab70e6ed6, 0xc8de047564d20a8c, 0xfb158592be068d2f, 0x9ced737bb6c4183d, 0xc428d05aa4751e4d, 0xf53304714d9265e0, 0x993fe2c6d07b7fac, 0xbf8fdb78849a5f97, 0xef73d256a5c0f77d, 0x95a8637627989aae, 0xbb127c53b17ec159, 0xe9d71b689dde71b0, 0x9226712162ab070e, 0xb6b00d69bb55c8d1, 0xe45c10c42a2b3b06, 0x8eb98a7a9a5b04e3, 0xb267ed1940f1c61c, 0xdf01e85f912e37a3, 0x8b61313bbabce2c6, 0xae397d8aa96c1b78, 0xd9c7dced53c72256, 0x881cea14545c7575, 0xaa242499697392d3, 0xd4ad2dbfc3d07788, 0x84ec3c97da624ab5, 0xa6274bbdd0fadd62, 0xcfb11ead453994ba, 0x81ceb32c4b43fcf5, 0xa2425ff75e14fc32, 0xcad2f7f5359a3b3e, 0xfd87b5f28300ca0e, 0x9e74d1b791e07e48, 0xc612062576589ddb, 0xf79687aed3eec551, 0x9abe14cd44753b53, 0xc16d9a0095928a27, 0xf1c90080baf72cb1, 0x971da05074da7bef, 0xbce5086492111aeb, 0xec1e4a7db69561a5, 0x9392ee8e921d5d07, 0xb877aa3236a4b449, 0xe69594bec44de15b, 0x901d7cf73ab0acd9, 0xb424dc35095cd80f, 0xe12e13424bb40e13, 0x8cbccc096f5088cc, 0xafebff0bcb24aaff, 0xdbe6fecebdedd5bf, 0x89705f4136b4a597, 0xabcc77118461cefd, 0xd6bf94d5e57a42bc, 0x8637bd05af6c69b6, 0xa7c5ac471b478423, 0xd1b71758e219652c, 0x83126e978d4fdf3b, 0xa3d70a3d70a3d70a, 0xcccccccccccccccd, 0x8000000000000000, 0xa000000000000000, 0xc800000000000000, 0xfa00000000000000, 0x9c40000000000000, 0xc350000000000000, 0xf424000000000000, 0x9896800000000000, 0xbebc200000000000, 0xee6b280000000000, 0x9502f90000000000, 0xba43b74000000000, 0xe8d4a51000000000, 0x9184e72a00000000, 0xb5e620f480000000, 0xe35fa931a0000000, 0x8e1bc9bf04000000, 0xb1a2bc2ec5000000, 0xde0b6b3a76400000, 0x8ac7230489e80000, 0xad78ebc5ac620000, 0xd8d726b7177a8000, 0x878678326eac9000, 0xa968163f0a57b400, 0xd3c21bcecceda100, 0x84595161401484a0, 0xa56fa5b99019a5c8, 0xcecb8f27f4200f3a, 0x813f3978f8940984, 0xa18f07d736b90be5, 0xc9f2c9cd04674edf, 0xfc6f7c4045812296, 0x9dc5ada82b70b59e, 0xc5371912364ce305, 0xf684df56c3e01bc7, 0x9a130b963a6c115c, 0xc097ce7bc90715b3, 0xf0bdc21abb48db20, 0x96769950b50d88f4, 0xbc143fa4e250eb31, 0xeb194f8e1ae525fd, 0x92efd1b8d0cf37be, 0xb7abc627050305ae, 0xe596b7b0c643c719, 0x8f7e32ce7bea5c70, 0xb35dbf821ae4f38c, 0xe0352f62a19e306f, 0x8c213d9da502de45, 0xaf298d050e4395d7, 0xdaf3f04651d47b4c, 0x88d8762bf324cd10, 0xab0e93b6efee0054, 0xd5d238a4abe98068, 0x85a36366eb71f041, 0xa70c3c40a64e6c52, 0xd0cf4b50cfe20766, 0x82818f1281ed44a0, 0xa321f2d7226895c8, 0xcbea6f8ceb02bb3a, 0xfee50b7025c36a08, 0x9f4f2726179a2245, 0xc722f0ef9d80aad6, 0xf8ebad2b84e0d58c, 0x9b934c3b330c8577, 0xc2781f49ffcfa6d5, 0xf316271c7fc3908b, 0x97edd871cfda3a57, 0xbde94e8e43d0c8ec, 0xed63a231d4c4fb27, 0x945e455f24fb1cf9, 0xb975d6b6ee39e437, 0xe7d34c64a9c85d44, 0x90e40fbeea1d3a4b, 0xb51d13aea4a488dd, 0xe264589a4dcdab15, 0x8d7eb76070a08aed, 0xb0de65388cc8ada8, 0xdd15fe86affad912, 0x8a2dbf142dfcc7ab, 0xacb92ed9397bf996, 0xd7e77a8f87daf7fc, 0x86f0ac99b4e8dafd, 0xa8acd7c0222311bd, 0xd2d80db02aabd62c, 0x83c7088e1aab65db, 0xa4b8cab1a1563f52, 0xcde6fd5e09abcf27, 0x80b05e5ac60b6178, 0xa0dc75f1778e39d6, 0xc913936dd571c84c, 0xfb5878494ace3a5f, 0x9d174b2dcec0e47b, 0xc45d1df942711d9a, 0xf5746577930d6501, 0x9968bf6abbe85f20, 0xbfc2ef456ae276e9, 0xefb3ab16c59b14a3, 0x95d04aee3b80ece6, 0xbb445da9ca61281f, 0xea1575143cf97227, 0x924d692ca61be758, 0xb6e0c377cfa2e12e, 0xe498f455c38b997a, 0x8edf98b59a373fec, 0xb2977ee300c50fe7, 0xdf3d5e9bc0f653e1, 0x8b865b215899f46d, 0xae67f1e9aec07188, 0xda01ee641a708dea, 0x884134fe908658b2, 0xaa51823e34a7eedf, 0xd4e5e2cdc1d1ea96, 0x850fadc09923329e, 0xa6539930bf6bff46, 0xcfe87f7cef46ff17, 0x81f14fae158c5f6e, 0xa26da3999aef774a, 0xcb090c8001ab551c, 0xfdcb4fa002162a63, 0x9e9f11c4014dda7e, 0xc646d63501a1511e, 0xf7d88bc24209a565, 0x9ae757596946075f, 0xc1a12d2fc3978937, 0xf209787bb47d6b85, 0x9745eb4d50ce6333, 0xbd176620a501fc00, 0xec5d3fa8ce427b00, 0x93ba47c980e98ce0, 0xb8a8d9bbe123f018, 0xe6d3102ad96cec1e, 0x9043ea1ac7e41393, 0xb454e4a179dd1877, 0xe16a1dc9d8545e95, 0x8ce2529e2734bb1d, 0xb01ae745b101e9e4, 0xdc21a1171d42645d, 0x899504ae72497eba, 0xabfa45da0edbde69, 0xd6f8d7509292d603, 0x865b86925b9bc5c2, 0xa7f26836f282b733, 0xd1ef0244af2364ff, 0x8335616aed761f1f, 0xa402b9c5a8d3a6e7, 0xcd036837130890a1, 0x802221226be55a65, 0xa02aa96b06deb0fe, 0xc83553c5c8965d3d, 0xfa42a8b73abbf48d, 0x9c69a97284b578d8, 0xc38413cf25e2d70e, 0xf46518c2ef5b8cd1, 0x98bf2f79d5993803, 0xbeeefb584aff8604, 0xeeaaba2e5dbf6785, 0x952ab45cfa97a0b3, 0xba756174393d88e0, 0xe912b9d1478ceb17, 0x91abb422ccb812ef, 0xb616a12b7fe617aa, 0xe39c49765fdf9d95, 0x8e41ade9fbebc27d, 0xb1d219647ae6b31c, 0xde469fbd99a05fe3, 0x8aec23d680043bee, 0xada72ccc20054aea, 0xd910f7ff28069da4, 0x87aa9aff79042287, 0xa99541bf57452b28, 0xd3fa922f2d1675f2, 0x847c9b5d7c2e09b7, 0xa59bc234db398c25, 0xcf02b2c21207ef2f, 0x8161afb94b44f57d, 0xa1ba1ba79e1632dc, 0xca28a291859bbf93, 0xfcb2cb35e702af78, 0x9defbf01b061adab, 0xc56baec21c7a1916, 0xf6c69a72a3989f5c, 0x9a3c2087a63f6399, 0xc0cb28a98fcf3c80, 0xf0fdf2d3f3c30b9f, 0x969eb7c47859e744, 0xbc4665b596706115, 0xeb57ff22fc0c795a, 0x9316ff75dd87cbd8, 0xb7dcbf5354e9bece, 0xe5d3ef282a242e82, 0x8fa475791a569d11, 0xb38d92d760ec4455, 0xe070f78d3927556b, 0x8c469ab843b89563, 0xaf58416654a6babb, 0xdb2e51bfe9d0696a, 0x88fcf317f22241e2, 0xab3c2fddeeaad25b, 0xd60b3bd56a5586f2, 0x85c7056562757457, 0xa738c6bebb12d16d, 0xd106f86e69d785c8, 0x82a45b450226b39d, 0xa34d721642b06084, 0xcc20ce9bd35c78a5, 0xff290242c83396ce, 0x9f79a169bd203e41, 0xc75809c42c684dd1, 0xf92e0c3537826146, 0x9bbcc7a142b17ccc, 0xc2abf989935ddbfe, 0xf356f7ebf83552fe, 0x98165af37b2153df, 0xbe1bf1b059e9a8d6, 0xeda2ee1c7064130c, 0x9485d4d1c63e8be8, 0xb9a74a0637ce2ee1, 0xe8111c87c5c1ba9a, 0x910ab1d4db9914a0, 0xb54d5e4a127f59c8, 0xe2a0b5dc971f303a, 0x8da471a9de737e24, 0xb10d8e1456105dad, 0xdd50f1996b947519, 0x8a5296ffe33cc930, 0xace73cbfdc0bfb7b, 0xd8210befd30efa5a, 0x8714a775e3e95c78, 0xa8d9d1535ce3b396, 0xd31045a8341ca07c, 0x83ea2b892091e44e, 0xa4e4b66b68b65d61, 0xce1de40642e3f4b9, 0x80d2ae83e9ce78f4, 0xa1075a24e4421731, 0xc94930ae1d529cfd, 0xfb9b7cd9a4a7443c, 0x9d412e0806e88aa6, 0xc491798a08a2ad4f, 0xf5b5d7ec8acb58a3, 0x9991a6f3d6bf1766, 0xbff610b0cc6edd3f, 0xeff394dcff8a948f, 0x95f83d0a1fb69cd9, 0xbb764c4ca7a44410, 0xea53df5fd18d5514, 0x92746b9be2f8552c, 0xb7118682dbb66a77, 0xe4d5e82392a40515, 0x8f05b1163ba6832d, 0xb2c71d5bca9023f8, 0xdf78e4b2bd342cf7, 0x8bab8eefb6409c1a, 0xae9672aba3d0c321, 0xda3c0f568cc4f3e9, 0x8865899617fb1871, 0xaa7eebfb9df9de8e, 0xd51ea6fa85785631, 0x8533285c936b35df, 0xa67ff273b8460357, 0xd01fef10a657842c, 0x8213f56a67f6b29c, 0xa298f2c501f45f43, 0xcb3f2f7642717713, 0xfe0efb53d30dd4d8, 0x9ec95d1463e8a507, 0xc67bb4597ce2ce49, 0xf81aa16fdc1b81db, 0x9b10a4e5e9913129, 0xc1d4ce1f63f57d73, 0xf24a01a73cf2dcd0, 0x976e41088617ca02, 0xbd49d14aa79dbc82, 0xec9c459d51852ba3, 0x93e1ab8252f33b46, 0xb8da1662e7b00a17, 0xe7109bfba19c0c9d, 0x906a617d450187e2, 0xb484f9dc9641e9db, 0xe1a63853bbd26451, 0x8d07e33455637eb3, 0xb049dc016abc5e60, 0xdc5c5301c56b75f7, 0x89b9b3e11b6329bb, 0xac2820d9623bf429, 0xd732290fbacaf134, 0x867f59a9d4bed6c0, 0xa81f301449ee8c70, 0xd226fc195c6a2f8c, 0x83585d8fd9c25db8, 0xa42e74f3d032f526, 0xcd3a1230c43fb26f, 0x80444b5e7aa7cf85, 0xa0555e361951c367, 0xc86ab5c39fa63441, 0xfa856334878fc151, 0x9c935e00d4b9d8d2, 0xc3b8358109e84f07, 0xf4a642e14c6262c9, 0x98e7e9cccfbd7dbe, 0xbf21e44003acdd2d, 0xeeea5d5004981478, 0x95527a5202df0ccb, 0xbaa718e68396cffe, 0xe950df20247c83fd, 0x91d28b7416cdd27e, 0xb6472e511c81471e, 0xe3d8f9e563a198e5, 0x8e679c2f5e44ff8f, 0xb201833b35d63f73, 0xde81e40a034bcf50, 0x8b112e86420f6192, 0xadd57a27d29339f6, 0xd94ad8b1c7380874, 0x87cec76f1c830549, 0xa9c2794ae3a3c69b, 0xd433179d9c8cb841, 0x849feec281d7f329, 0xa5c7ea73224deff3, 0xcf39e50feae16bf0, 0x81842f29f2cce376, 0xa1e53af46f801c53, 0xca5e89b18b602368, 0xfcf62c1dee382c42, 0x9e19db92b4e31ba9, 0xc5a05277621be294, 0xf70867153aa2db39, 0x9a65406d44a5c903, 0xc0fe908895cf3b44, 0xf13e34aabb430a15, 0x96c6e0eab509e64d, 0xbc789925624c5fe1, 0xeb96bf6ebadf77d9, 0x933e37a534cbaae8, 0xb80dc58e81fe95a1, 0xe61136f2227e3b0a, 0x8fcac257558ee4e6, 0xb3bd72ed2af29e20, 0xe0accfa875af45a8, 0x8c6c01c9498d8b89, 0xaf87023b9bf0ee6b, 0xdb68c2ca82ed2a06, 0x892179be91d43a44, 0xab69d82e364948d4 };
+static constexpr int powers_ten_e[] = { -1203, -1200, -1196, -1193, -1190, -1186, -1183, -1180, -1176, -1173, -1170, -1166, -1163, -1160, -1156, -1153, -1150, -1146, -1143, -1140, -1136, -1133, -1130, -1127, -1123, -1120, -1117, -1113, -1110, -1107, -1103, -1100, -1097, -1093, -1090, -1087, -1083, -1080, -1077, -1073, -1070, -1067, -1063, -1060, -1057, -1053, -1050, -1047, -1043, -1040, -1037, -1034, -1030, -1027, -1024, -1020, -1017, -1014, -1010, -1007, -1004, -1000, -997, -994, -990, -987, -984, -980, -977, -974, -970, -967, -964, -960, -957, -954, -950, -947, -944, -940, -937, -934, -931, -927, -924, -921, -917, -914, -911, -907, -904, -901, -897, -894, -891, -887, -884, -881, -877, -874, -871, -867, -864, -861, -857, -854, -851, -847, -844, -841, -838, -834, -831, -828, -824, -821, -818, -814, -811, -808, -804, -801, -798, -794, -791, -788, -784, -781, -778, -774, -771, -768, -764, -761, -758, -754, -751, -748, -744, -741, -738, -735, -731, -728, -725, -721, -718, -715, -711, -708, -705, -701, -698, -695, -691, -688, -685, -681, -678, -675, -671, -668, -665, -661, -658, -655, -651, -648, -645, -642, -638, -635, -632, -628, -625, -622, -618, -615, -612, -608, -605, -602, -598, -595, -592, -588, -585, -582, -578, -575, -572, -568, -565, -562, -558, -555, -552, -549, -545, -542, -539, -535, -532, -529, -525, -522, -519, -515, -512, -509, -505, -502, -499, -495, -492, -489, -485, -482, -479, -475, -472, -469, -465, -462, -459, -455, -452, -449, -446, -442, -439, -436, -432, -429, -426, -422, -419, -416, -412, -409, -406, -402, -399, -396, -392, -389, -386, -382, -379, -376, -372, -369, -366, -362, -359, -356, -353, -349, -346, -343, -339, -336, -333, -329, -326, -323, -319, -316, -313, -309, -306, -303, -299, -296, -293, -289, -286, -283, -279, -276, -273, -269, -266, -263, -259, -256, -253, -250, -246, -243, -240, -236, -233, -230, -226, -223, -220, -216, -213, -210, -206, -203, -200, -196, -193, -190, -186, -183, -180, -176, -173, -170, -166, -163, -160, -157, -153, -150, -147, -143, -140, -137, -133, -130, -127, -123, -120, -117, -113, -110, -107, -103, -100, -97, -93, -90, -87, -83, -80, -77, -73, -70, -67, -63, -60, -57, -54, -50, -47, -44, -40, -37, -34, -30, -27, -24, -20, -17, -14, -10, -7, -4, 0, 3, 6, 10, 13, 16, 20, 23, 26, 30, 33, 36, 39, 43, 46, 49, 53, 56, 59, 63, 66, 69, 73, 76, 79, 83, 86, 89, 93, 96, 99, 103, 106, 109, 113, 116, 119, 123, 126, 129, 132, 136, 139, 142, 146, 149, 152, 156, 159, 162, 166, 169, 172, 176, 179, 182, 186, 189, 192, 196, 199, 202, 206, 209, 212, 216, 219, 222, 226, 229, 232, 235, 239, 242, 245, 249, 252, 255, 259, 262, 265, 269, 272, 275, 279, 282, 285, 289, 292, 295, 299, 302, 305, 309, 312, 315, 319, 322, 325, 328, 332, 335, 338, 342, 345, 348, 352, 355, 358, 362, 365, 368, 372, 375, 378, 382, 385, 388, 392, 395, 398, 402, 405, 408, 412, 415, 418, 422, 425, 428, 431, 435, 438, 441, 445, 448, 451, 455, 458, 461, 465, 468, 471, 475, 478, 481, 485, 488, 491, 495, 498, 501, 505, 508, 511, 515, 518, 521, 524, 528, 531, 534, 538, 541, 544, 548, 551, 554, 558, 561, 564, 568, 571, 574, 578, 581, 584, 588, 591, 594, 598, 601, 604, 608, 611, 614, 617, 621, 624, 627, 631, 634, 637, 641, 644, 647, 651, 654, 657, 661, 664, 667, 671, 674, 677, 681, 684, 687, 691, 694, 697, 701, 704, 707, 711, 714, 717, 720, 724, 727, 730, 734, 737, 740, 744, 747, 750, 754, 757, 760, 764, 767, 770, 774, 777, 780, 784, 787, 790, 794, 797, 800, 804, 807, 810, 813, 817, 820, 823, 827, 830, 833, 837, 840, 843, 847, 850, 853, 857, 860, 863, 867, 870, 873, 877, 880, 883, 887, 890, 893, 897, 900, 903, 907, 910, 913, 916, 920, 923, 926, 930, 933, 936, 940, 943, 946, 950, 953, 956, 960, 963, 966, 970, 973, 976, 980, 983, 986, 990, 993, 996, 1000, 1003, 1006, 1009, 1013, 1016, 1019, 1023, 1026, 1029, 1033, 1036, 1039, 1043, 1046, 1049, 1053, 1056, 1059, 1063, 1066, 1069, 1073, 1076 };
+
+inline
+diy_fp_t cached_power(int k)
+{
+ diy_fp_t res;
+ int index = 343 + k;
+ res.f = powers_ten[index];
+ res.e = powers_ten_e[index];
+ return res;
+}
+
+// double
+
+/*
+typedef union {
+ double d;
+ uint64_t n;
+} converter_t;
+
+inline
+uint64_t double_to_uint64(double d) { converter_t tmp; tmp.d = d; return tmp.n; }
+
+inline
+double uint64_to_double(uint64_t d64) { converter_t tmp; tmp.n = d64; return tmp.d; }
+*/
+inline
+uint64_t double_to_uint64(double d) {uint64_t d64; std::memcpy(&d64,&d,sizeof(double)); return d64; }
+
+inline
+double uint64_to_double(uint64_t d64) {double d; std::memcpy(&d,&d64,sizeof(double)); return d; }
+
+constexpr int dp_significand_size = 52;
+constexpr int dp_exponent_bias = (0x3FF + dp_significand_size);
+constexpr int dp_min_exponent = (-dp_exponent_bias);
+constexpr uint64_t dp_exponent_mask = 0x7FF0000000000000;
+constexpr uint64_t dp_significand_mask = 0x000FFFFFFFFFFFFF;
+constexpr uint64_t dp_hidden_bit = 0x0010000000000000;
+
+inline
+diy_fp_t normalize_diy_fp(diy_fp_t in)
+{
+ diy_fp_t res = in;
+ /* Normalize now */
+ /* the original number could have been a denormal. */
+ while (!(res.f & dp_hidden_bit))
+ {
+ res.f <<= 1;
+ res.e--;
+ }
+ /* do the final shifts in one go. Don't forget the hidden bit (the '-1') */
+ res.f <<= (diy_significand_size - dp_significand_size - 1);
+ res.e = res.e - (diy_significand_size - dp_significand_size - 1);
+ return res;
+}
+
+inline
+diy_fp_t double2diy_fp(double d)
+{
+ uint64_t d64 = double_to_uint64(d);
+ int biased_e = (d64 & dp_exponent_mask) >> dp_significand_size;
+ uint64_t significand = (d64 & dp_significand_mask);
+ diy_fp_t res;
+ if (biased_e != 0)
+ {
+ res.f = significand + dp_hidden_bit;
+ res.e = biased_e - dp_exponent_bias;
+ }
+ else
+ {
+ res.f = significand;
+ res.e = dp_min_exponent + 1;
+ }
+ return res;
+}
+
+inline
+diy_fp_t normalize_boundary(diy_fp_t in)
+{
+ diy_fp_t res = in;
+ /* Normalize now */
+ /* the original number could have been a denormal. */
+ while (!(res.f & (dp_hidden_bit << 1)))
+ {
+ res.f <<= 1;
+ res.e--;
+ }
+ /* do the final shifts in one go. Don't forget the hidden bit (the '-1') */
+ res.f <<= (diy_significand_size - dp_significand_size - 2);
+ res.e = res.e - (diy_significand_size - dp_significand_size - 2);
+ return res;
+}
+
+inline
+void normalized_boundaries(double d, diy_fp_t *out_m_minus, diy_fp_t *out_m_plus)
+{
+ diy_fp_t v = double2diy_fp(d);
+ diy_fp_t pl, mi;
+ bool significand_is_zero = v.f == dp_hidden_bit;
+ pl.f = (v.f << 1) + 1; pl.e = v.e - 1;
+ pl = normalize_boundary(pl);
+ if (significand_is_zero)
+ {
+ mi.f = (v.f << 2) - 1;
+ mi.e = v.e - 2;
+ } else
+ {
+ mi.f = (v.f << 1) - 1;
+ mi.e = v.e - 1;
+ }
+ int x = mi.e - pl.e;
+ mi.f <<= x;
+ mi.e = pl.e;
+ *out_m_plus = pl;
+ *out_m_minus = mi;
+}
+
+inline
+double random_double()
+{
+ uint64_t tmp = 0;
+ int i;
+ for (i = 0; i < 8; i++)
+ {
+ tmp <<= 8;
+ tmp += rand() % 256;
+ }
+ return uint64_to_double(tmp);
+}
+
+// grisu3
+
+inline
+bool round_weed(char *buffer, int len,
+ uint64_t wp_W, uint64_t Delta,
+ uint64_t rest, uint64_t ten_kappa,
+ uint64_t ulp)
+{
+ uint64_t wp_Wup = wp_W - ulp;
+ uint64_t wp_Wdown = wp_W + ulp;
+ while (rest < wp_Wup && /// round1
+ Delta - rest >= ten_kappa &&
+ (rest + ten_kappa < wp_Wup || /// closer
+ wp_Wup - rest >= rest + ten_kappa - wp_Wup))
+ {
+ buffer[len - 1]--; rest += ten_kappa;
+ }
+ if (rest < wp_Wdown && /// round2
+ Delta - rest >= ten_kappa &&
+ (rest + ten_kappa < wp_Wdown ||
+ wp_Wdown - rest > rest + ten_kappa - wp_Wdown)) return 0;
+ return 2 * ulp <= rest && rest <= Delta - 4 * ulp; /// weed
+}
+
+inline
+bool digit_gen(diy_fp_t Wm, diy_fp_t W, diy_fp_t Wp,
+ char *buffer, int *len, int *K)
+{
+ const uint32_t TEN2 = 100;
+
+ uint32_t div, p1; uint64_t p2, tmp, unit = 1;
+ int d, kappa;
+ diy_fp_t one, wp_W, Delta;
+ Delta = minus(Wp, Wm); Delta.f += 2 * unit;
+ wp_W = minus(Wp, W); wp_W.f += unit;
+ one.f = ((uint64_t)1) << -Wp.e; one.e = Wp.e;
+ p1 = static_cast<uint32_t>((Wp.f + 1) >> -one.e);
+ p2 = (Wp.f + 1) & (one.f - 1);
+ *len = 0; kappa = 3; div = TEN2;
+ while (kappa > 0)
+ {
+ d = p1 / div;
+ if (d || *len) buffer[(*len)++] = (char)('0' + d);
+ p1 %= div; kappa--;
+ tmp = (((uint64_t)p1) << -one.e) + p2;
+ if (tmp < Delta.f)
+ {
+ *K += kappa;
+ return round_weed(buffer, *len, wp_W.f, Delta.f, tmp,
+ ((uint64_t)div) << -one.e, unit);
+ }
+ div /= 10;
+ }
+ while (1)
+ {
+ p2 *= 10; Delta.f *= 10; unit *= 10;
+ d = static_cast<int>(p2 >> -one.e);
+ if (d || *len) buffer[(*len)++] = (char)('0' + d);
+ p2 &= one.f - 1; kappa--;
+ if (p2 < Delta.f)
+ {
+ *K += kappa;
+ return round_weed(buffer, *len, wp_W.f * unit, Delta.f, p2,
+ one.f, unit);
+ }
+ }
+}
+
+inline
+bool grisu3(double v, char *buffer, int *length, int *K)
+{
+ diy_fp_t w_m, w_p;
+ int q = 64, alpha = -59, gamma = -56;
+ normalized_boundaries(v, &w_m, &w_p);
+ diy_fp_t w = normalize_diy_fp(double2diy_fp(v));
+ int mk = k_comp(w_p.e + q, alpha, gamma);
+ diy_fp_t c_mk = cached_power(mk);
+ diy_fp_t W = multiply(w, c_mk);
+ diy_fp_t Wp = multiply(w_p, c_mk);
+ diy_fp_t Wm = multiply(w_m, c_mk);
+ *K = -mk;
+ bool result = digit_gen(Wm, W, Wp, buffer, length, K);
+ buffer[*length] = 0;
+ return result;
+}
+
+}} // namespace detail namespace jsoncons
+
+#endif
diff --git a/include/jsoncons/detail/optional.hpp b/include/jsoncons/detail/optional.hpp
new file mode 100644
index 0000000..492cd76
--- /dev/null
+++ b/include/jsoncons/detail/optional.hpp
@@ -0,0 +1,483 @@
+// Copyright 2019 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_DETAIL_OPTIONAL_HPP
+#define JSONCONS_DETAIL_OPTIONAL_HPP
+
+#include <new> // placement new
+#include <memory>
+#include <utility> // std::swap
+#include <type_traits>
+#include <jsoncons/config/compiler_support.hpp>
+
+namespace jsoncons
+{
+namespace detail
+{
+ template <typename T>
+ class optional;
+
+ template <typename T1, typename T2>
+ struct is_constructible_or_convertible_from_optional
+ : std::integral_constant<
+ bool, std::is_constructible<T1, optional<T2>&>::value ||
+ std::is_constructible<T1, optional<T2>&&>::value ||
+ std::is_constructible<T1, const optional<T2>&>::value ||
+ std::is_constructible<T1, const optional<T2>&&>::value ||
+ std::is_convertible<optional<T2>&, T1>::value ||
+ std::is_convertible<optional<T2>&&, T1>::value ||
+ std::is_convertible<const optional<T2>&, T1>::value ||
+ std::is_convertible<const optional<T2>&&, T1>::value> {};
+
+ template <typename T1, typename T2>
+ struct is_constructible_convertible_or_assignable_from_optional
+ : std::integral_constant<
+ bool, is_constructible_or_convertible_from_optional<T1, T2>::value ||
+ std::is_assignable<T1&, optional<T2>&>::value ||
+ std::is_assignable<T1&, optional<T2>&&>::value ||
+ std::is_assignable<T1&, const optional<T2>&>::value ||
+ std::is_assignable<T1&, const optional<T2>&&>::value> {};
+
+ template <typename T>
+ class optional
+ {
+ public:
+ using value_type = T;
+ private:
+ bool has_value_;
+ union {
+ char dummy_;
+ T value_;
+ };
+ public:
+ constexpr optional() noexcept
+ : has_value_(false), dummy_{}
+ {
+ }
+
+ // copy constructors
+ optional(const optional<T>& other)
+ : has_value_(false), dummy_{}
+ {
+ if (other)
+ {
+ construct(*other);
+ }
+ }
+
+ // converting
+ template <class U,
+ typename std::enable_if<!std::is_same<T,U>::value &&
+ std::is_constructible<T, const U&>::value &&
+ std::is_convertible<const U&,T>::value &&
+ !is_constructible_or_convertible_from_optional<T,U>::value &&
+ std::is_copy_constructible<typename std::decay<U>::type>::value,int>::type = 0>
+ optional(const optional<U>& other)
+ : has_value_(false), dummy_{}
+ {
+ if (other)
+ {
+ construct(*other);
+ }
+ }
+
+ template <class U,
+ typename std::enable_if<!std::is_same<T,U>::value &&
+ std::is_constructible<T, const U&>::value &&
+ !std::is_convertible<const U&,T>::value &&
+ !is_constructible_or_convertible_from_optional<T,U>::value &&
+ std::is_copy_constructible<typename std::decay<U>::type>::value,int>::type = 0>
+ explicit optional(const optional<U>& other)
+ : has_value_(false), dummy_{}
+ {
+ if (other)
+ {
+ construct(*other);
+ }
+ }
+
+ // move constructors
+ template <class T2 = T>
+ optional(optional<T>&& other,
+ typename std::enable_if<std::is_move_constructible<typename std::decay<T2>::type>::value>::type* = 0)
+ : has_value_(false), dummy_{}
+ {
+ if (other)
+ {
+ construct(std::move(other.value_));
+ }
+ }
+
+ // converting
+ template <class U>
+ optional(optional<U>&& value,
+ typename std::enable_if<!std::is_same<T,U>::value &&
+ std::is_constructible<T, U&&>::value &&
+ !is_constructible_or_convertible_from_optional<T,U>::value &&
+ std::is_convertible<U&&,T>::value,int>::type = 0) // (8)
+ : has_value_(true), value_(std::forward<U>(value))
+ {
+ }
+
+ template <class U>
+ explicit optional(optional<U>&& value,
+ typename std::enable_if<!std::is_same<T,U>::value &&
+ std::is_constructible<T, U&&>::value &&
+ !is_constructible_or_convertible_from_optional<T,U>::value &&
+ !std::is_convertible<U&&,T>::value,int>::type = 0) // (8)
+ : has_value_(true), value_(std::forward<U>(value))
+ {
+ }
+
+
+ // value constructors
+ template <class T2>
+ optional(T2&& value,
+ typename std::enable_if<!std::is_same<optional<T>,typename std::decay<T2>::type>::value &&
+ std::is_constructible<T, T2>::value &&
+ std::is_convertible<T2,T>::value,int>::type = 0) // (8)
+ : has_value_(true), value_(std::forward<T2>(value))
+ {
+ }
+
+ template <class T2>
+ explicit optional(T2&& value,
+ typename std::enable_if<!std::is_same<optional<T>,typename std::decay<T2>::type>::value &&
+ std::is_constructible<T, T2>::value &&
+ !std::is_convertible<T2,T>::value,int>::type = 0) // (8)
+ : has_value_(true), value_(std::forward<T2>(value))
+ {
+ }
+
+ ~optional() noexcept
+ {
+ destroy();
+ }
+
+ optional& operator=(const optional& other)
+ {
+ if (other)
+ {
+ assign(*other);
+ }
+ else
+ {
+ reset();
+ }
+ return *this;
+ }
+
+ optional& operator=(optional&& other )
+ {
+ if (other)
+ {
+ assign(std::move(*other));
+ }
+ else
+ {
+ reset();
+ }
+ return *this;
+ }
+
+ template <typename U>
+ typename std::enable_if<!std::is_same<optional<T>, U>::value &&
+ std::is_constructible<T, const U&>::value &&
+ !is_constructible_convertible_or_assignable_from_optional<T,U>::value &&
+ std::is_assignable<T&, const U&>::value,
+ optional&>::type
+ operator=(const optional<U>& other)
+ {
+ if (other)
+ {
+ assign(*other);
+ }
+ else
+ {
+ destroy();
+ }
+ return *this;
+ }
+
+ template <typename U>
+ typename std::enable_if<!std::is_same<optional<T>, U>::value &&
+ std::is_constructible<T, U>::value &&
+ !is_constructible_convertible_or_assignable_from_optional<T,U>::value &&
+ std::is_assignable<T&, U>::value,
+ optional&>::type
+ operator=(optional<U>&& other)
+ {
+ if (other)
+ {
+ assign(std::move(*other));
+ }
+ else
+ {
+ destroy();
+ }
+ return *this;
+ }
+
+ // value assignment
+ template <typename T2>
+ typename std::enable_if<!std::is_same<optional<T>, typename std::decay<T2>::type>::value &&
+ std::is_constructible<T, T2>::value &&
+ std::is_assignable<T&, T2>::value &&
+ !(std::is_scalar<T>::value && std::is_same<T, typename std::decay<T2>::type>::value),
+ optional&>::type
+ operator=(T2&& v)
+ {
+ assign(std::forward<T2>(v));
+ return *this;
+ }
+
+ constexpr explicit operator bool() const noexcept
+ {
+ return has_value_;
+ }
+ constexpr bool has_value() const noexcept
+ {
+ return has_value_;
+ }
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4702)
+#endif // _MSC_VER
+
+ T& value() &
+ {
+ return static_cast<bool>(*this)
+ ? get()
+ : JSONCONS_THROW(std::runtime_error("Bad optional access")), get();
+ }
+
+ constexpr const T& value() const &
+ {
+ return static_cast<bool>(*this)
+ ? get()
+ : JSONCONS_THROW(std::runtime_error("Bad optional access")), get();
+ }
+
+ template <typename U>
+ constexpr T value_or(U&& default_value) const &
+ {
+ static_assert(std::is_copy_constructible<T>::value,
+ "get_value_or: T must be copy constructible");
+ static_assert(std::is_convertible<U&&, T>::value,
+ "get_value_or: U must be convertible to T");
+ return static_cast<bool>(*this)
+ ? **this
+ : static_cast<T>(std::forward<U>(default_value));
+ }
+
+ template <typename U>
+ T value_or(U&& default_value) &&
+ {
+ static_assert(std::is_move_constructible<T>::value,
+ "get_value_or: T must be move constructible");
+ static_assert(std::is_convertible<U&&, T>::value,
+ "get_value_or: U must be convertible to T");
+ return static_cast<bool>(*this) ? std::move(**this)
+ : static_cast<T>(std::forward<U>(default_value));
+ }
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif // _MSC_VER
+
+ const T* operator->() const
+ {
+ return std::addressof(this->value_);
+ }
+
+ T* operator->()
+ {
+ return std::addressof(this->value_);
+ }
+
+ constexpr const T& operator*() const&
+ {
+ return value();
+ }
+
+ T& operator*() &
+ {
+ return value();
+ }
+
+ void reset() noexcept
+ {
+ destroy();
+ }
+
+ void swap(optional& other) noexcept(std::is_nothrow_move_constructible<T>::value /*&&
+ std::is_nothrow_swappable<T>::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
+ {
+ optional& source = contains_a_value ? *this : other;
+ optional& target = contains_a_value ? other : *this;
+ target = optional<T>(*source);
+ source.reset();
+ }
+ }
+ private:
+ constexpr const T& get() const { return this->value_; }
+ T& get() { return this->value_; }
+
+ template <typename... Args>
+ void construct(Args&&... args)
+ {
+ ::new (static_cast<void*>(&this->value_)) T(std::forward<Args>(args)...);
+ has_value_ = true;
+ }
+
+ void destroy() noexcept
+ {
+ if (has_value_)
+ {
+ value_.~T();
+ has_value_ = false;
+ }
+ }
+
+ template <typename U>
+ void assign(U&& u)
+ {
+ if (has_value_)
+ {
+ value_ = std::forward<U>(u);
+ }
+ else
+ {
+ construct(std::forward<U>(u));
+ }
+ }
+ };
+
+ template <typename T>
+ typename std::enable_if<std::is_nothrow_move_constructible<T>::value,void>::type
+ swap(optional<T>& lhs, optional<T>& rhs) noexcept
+ {
+ lhs.swap(rhs);
+ }
+
+ template <class T1, class T2>
+ constexpr bool operator==(const optional<T1>& lhs, const optional<T2>& rhs) noexcept
+ {
+ return lhs.has_value() == rhs.has_value() && (!lhs.has_value() || *lhs == *rhs);
+ }
+
+ template <class T1, class T2>
+ constexpr bool operator!=(const optional<T1>& lhs, const optional<T2>& rhs) noexcept
+ {
+ return lhs.has_value() != rhs.has_value() || (lhs.has_value() && *lhs != *rhs);
+ }
+
+ template <class T1, class T2>
+ constexpr bool operator<(const optional<T1>& lhs, const optional<T2>& rhs) noexcept
+ {
+ return rhs.has_value() && (!lhs.has_value() || *lhs < *rhs);
+ }
+
+ template <class T1, class T2>
+ constexpr bool operator>(const optional<T1>& lhs, const optional<T2>& rhs) noexcept
+ {
+ return lhs.has_value() && (!rhs.has_value() || *lhs > *rhs);
+ }
+
+ template <class T1, class T2>
+ constexpr bool operator<=(const optional<T1>& lhs, const optional<T2>& rhs) noexcept
+ {
+ return !lhs.has_value() || (rhs.has_value() && *lhs <= *rhs);
+ }
+
+ template <class T1, class T2>
+ constexpr bool operator>=(const optional<T1>& lhs, const optional<T2>& rhs) noexcept
+ {
+ return !rhs.has_value() || (lhs.has_value() && *lhs >= *rhs);
+ }
+
+ template <class T1, class T2>
+ constexpr bool operator==(const optional<T1>& lhs, const T2& rhs) noexcept
+ {
+ return lhs ? *lhs == rhs : false;
+ }
+ template <class T1, class T2>
+ constexpr bool operator==(const T1& lhs, const optional<T2>& rhs) noexcept
+ {
+ return rhs ? lhs == *rhs : false;
+ }
+
+ template <class T1, class T2>
+ constexpr bool operator!=(const optional<T1>& lhs, const T2& rhs) noexcept
+ {
+ return lhs ? *lhs != rhs : true;
+ }
+ template <class T1, class T2>
+ constexpr bool operator!=(const T1& lhs, const optional<T2>& rhs) noexcept
+ {
+ return rhs ? lhs != *rhs : true;
+ }
+
+ template <class T1, class T2>
+ constexpr bool operator<(const optional<T1>& lhs, const T2& rhs) noexcept
+ {
+ return lhs ? *lhs < rhs : true;
+ }
+ template <class T1, class T2>
+ constexpr bool operator<(const T1& lhs, const optional<T2>& rhs) noexcept
+ {
+ return rhs ? lhs < *rhs : false;
+ }
+
+ template <class T1, class T2>
+ constexpr bool operator<=(const optional<T1>& lhs, const T2& rhs) noexcept
+ {
+ return lhs ? *lhs <= rhs : true;
+ }
+ template <class T1, class T2>
+ constexpr bool operator<=(const T1& lhs, const optional<T2>& rhs) noexcept
+ {
+ return rhs ? lhs <= *rhs : false;
+ }
+
+ template <class T1, class T2>
+ constexpr bool operator>(const optional<T1>& lhs, const T2& rhs) noexcept
+ {
+ return lhs ? *lhs > rhs : false;
+ }
+
+ template <class T1, class T2>
+ constexpr bool operator>(const T1& lhs, const optional<T2>& rhs) noexcept
+ {
+ return rhs ? lhs > *rhs : true;
+ }
+
+ template <class T1, class T2>
+ constexpr bool operator>=(const optional<T1>& lhs, const T2& rhs) noexcept
+ {
+ return lhs ? *lhs >= rhs : false;
+ }
+ template <class T1, class T2>
+ constexpr bool operator>=(const T1& lhs, const optional<T2>& rhs) noexcept
+ {
+ return rhs ? lhs >= *rhs : true;
+ }
+
+} // namespace detail
+} // namespace jsoncons
+
+#endif
diff --git a/include/jsoncons/detail/parse_number.hpp b/include/jsoncons/detail/parse_number.hpp
new file mode 100644
index 0000000..7417ff2
--- /dev/null
+++ b/include/jsoncons/detail/parse_number.hpp
@@ -0,0 +1,1133 @@
+// Copyright 2013 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_DETAIL_PARSE_NUMBER_HPP
+#define JSONCONS_DETAIL_PARSE_NUMBER_HPP
+
+#include <system_error>
+#include <stdexcept>
+#include <string>
+#include <vector>
+#include <locale>
+#include <string>
+#include <limits> // std::numeric_limits
+#include <type_traits> // std::enable_if
+#include <exception>
+#include <jsoncons/config/jsoncons_config.hpp>
+#include <jsoncons/json_exception.hpp>
+#include <cctype>
+
+namespace jsoncons { namespace detail {
+
+ enum class to_integer_errc : uint8_t {success=0, overflow, invalid_digit, invalid_number};
+
+ class to_integer_error_category_impl
+ : public std::error_category
+ {
+ public:
+ const char* name() const noexcept override
+ {
+ return "jsoncons/to_integer_unchecked";
+ }
+ std::string message(int ev) const override
+ {
+ switch (static_cast<to_integer_errc>(ev))
+ {
+ case to_integer_errc::overflow:
+ return "Integer overflow";
+ case to_integer_errc::invalid_digit:
+ return "Invalid digit";
+ case to_integer_errc::invalid_number:
+ return "Invalid number";
+ default:
+ return "Unknown to_integer_unchecked error";
+ }
+ }
+ };
+
+ inline
+ const std::error_category& to_integer_error_category()
+ {
+ static to_integer_error_category_impl instance;
+ return instance;
+ }
+
+ inline
+ std::error_code make_error_code(to_integer_errc e)
+ {
+ return std::error_code(static_cast<int>(e),to_integer_error_category());
+ }
+
+} // namespace detail
+} // namespace jsoncons
+
+namespace std {
+ template<>
+ struct is_error_code_enum<jsoncons::detail::to_integer_errc> : public true_type
+ {
+ };
+}
+
+namespace jsoncons { namespace detail {
+
+template <class T,class CharT>
+struct to_integer_result
+{
+ const CharT* ptr;
+ to_integer_errc ec;
+ constexpr to_integer_result(const CharT* ptr_)
+ : ptr(ptr_), ec(to_integer_errc())
+ {
+ }
+ constexpr to_integer_result(const CharT* ptr_, to_integer_errc ec_)
+ : ptr(ptr_), ec(ec_)
+ {
+ }
+
+ to_integer_result(const to_integer_result&) = default;
+
+ to_integer_result& operator=(const to_integer_result&) = default;
+
+ constexpr explicit operator bool() const noexcept
+ {
+ return ec == to_integer_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 <class CharT>
+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 <class T, class CharT>
+typename std::enable_if<type_traits::integer_limits<T>::is_specialized && !type_traits::integer_limits<T>::is_signed,to_integer_result<T,CharT>>::type
+to_integer_decimal(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':
+ if (++s == end)
+ {
+ return (++s == end) ? to_integer_result<T,CharT>(s) : to_integer_result<T, CharT>(s, to_integer_errc());
+ }
+ else
+ {
+ return to_integer_result<T,CharT>(s, to_integer_errc::invalid_digit);
+ }
+ 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_integer_result<T,CharT>(s, to_integer_errc::invalid_digit);
+ }
+ break;
+ }
+ case integer_chars_state::decimal:
+ {
+ static constexpr T max_value = (type_traits::integer_limits<T>::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<T>(*s) - static_cast<T>('0');
+ break;
+ default:
+ return to_integer_result<T,CharT>(s, to_integer_errc::invalid_digit);
+ }
+ if (n > max_value_div_10)
+ {
+ return to_integer_result<T,CharT>(s, to_integer_errc::overflow);
+ }
+ n = n * 10;
+ if (n > max_value - x)
+ {
+ return to_integer_result<T,CharT>(s, to_integer_errc::overflow);
+ }
+ n += x;
+ }
+ break;
+ }
+ default:
+ JSONCONS_UNREACHABLE();
+ break;
+ }
+ }
+ return (state == integer_chars_state::initial) ? to_integer_result<T,CharT>(s, to_integer_errc::invalid_number) : to_integer_result<T,CharT>(s, to_integer_errc());
+}
+
+template <class T, class CharT>
+typename std::enable_if<type_traits::integer_limits<T>::is_specialized && type_traits::integer_limits<T>::is_signed,to_integer_result<T,CharT>>::type
+to_integer_decimal(const CharT* s, std::size_t length, T& n)
+{
+ n = 0;
+
+ if (length == 0)
+ {
+ return to_integer_result<T,CharT>(s, to_integer_errc::invalid_number);
+ }
+
+ bool is_negative = *s == '-' ? true : false;
+ if (is_negative)
+ {
+ ++s;
+ --length;
+ }
+
+ using U = typename type_traits::make_unsigned<T>::type;
+
+ U u;
+ auto ru = to_integer_decimal(s, length, u);
+ if (ru.ec != to_integer_errc())
+ {
+ return to_integer_result<T,CharT>(ru.ptr, ru.ec);
+ }
+ if (is_negative)
+ {
+ if (u > static_cast<U>(-((type_traits::integer_limits<T>::lowest)()+T(1))) + U(1))
+ {
+ return to_integer_result<T,CharT>(ru.ptr, to_integer_errc::overflow);
+ }
+ else
+ {
+ n = static_cast<T>(U(0) - u);
+ return to_integer_result<T,CharT>(ru.ptr, to_integer_errc());
+ }
+ }
+ else
+ {
+ if (u > static_cast<U>((type_traits::integer_limits<T>::max)()))
+ {
+ return to_integer_result<T,CharT>(ru.ptr, to_integer_errc::overflow);
+ }
+ else
+ {
+ n = static_cast<T>(u);
+ return to_integer_result<T,CharT>(ru.ptr, to_integer_errc());
+ }
+ }
+}
+
+template <class T, class CharT>
+typename std::enable_if<type_traits::integer_limits<T>::is_specialized && !type_traits::integer_limits<T>::is_signed,to_integer_result<T,CharT>>::type
+to_integer_base16(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':
+ if (++s == end)
+ {
+ return (++s == end) ? to_integer_result<T,CharT>(s) : to_integer_result<T, CharT>(s, to_integer_errc());
+ }
+ else
+ {
+ return to_integer_result<T,CharT>(s, to_integer_errc::invalid_digit);
+ }
+ 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 to_integer_result<T,CharT>(s, to_integer_errc::invalid_digit);
+ }
+ break;
+ }
+ case integer_chars_state::base16:
+ {
+ static constexpr T max_value = (type_traits::integer_limits<T>::max)();
+ static constexpr T max_value_div_16 = max_value / 16;
+ for (; s < end; ++s)
+ {
+ CharT c = *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 = 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_integer_result<T,CharT>(s, to_integer_errc::invalid_digit);
+ }
+ if (n > max_value_div_16)
+ {
+ return to_integer_result<T,CharT>(s, to_integer_errc::overflow);
+ }
+ n = n * 16;
+ if (n > max_value - x)
+ {
+ return to_integer_result<T,CharT>(s, to_integer_errc::overflow);
+ }
+ n += x;
+ }
+ break;
+ }
+ default:
+ JSONCONS_UNREACHABLE();
+ break;
+ }
+ }
+ return (state == integer_chars_state::initial) ? to_integer_result<T,CharT>(s, to_integer_errc::invalid_number) : to_integer_result<T,CharT>(s, to_integer_errc());
+}
+
+template <class T, class CharT>
+typename std::enable_if<type_traits::integer_limits<T>::is_specialized && type_traits::integer_limits<T>::is_signed,to_integer_result<T,CharT>>::type
+to_integer_base16(const CharT* s, std::size_t length, T& n)
+{
+ n = 0;
+
+ if (length == 0)
+ {
+ return to_integer_result<T,CharT>(s, to_integer_errc::invalid_number);
+ }
+
+ bool is_negative = *s == '-' ? true : false;
+ if (is_negative)
+ {
+ ++s;
+ --length;
+ }
+
+ using U = typename type_traits::make_unsigned<T>::type;
+
+ U u;
+ auto ru = to_integer_base16(s, length, u);
+ if (ru.ec != to_integer_errc())
+ {
+ return to_integer_result<T,CharT>(ru.ptr, ru.ec);
+ }
+ if (is_negative)
+ {
+ if (u > static_cast<U>(-((type_traits::integer_limits<T>::lowest)()+T(1))) + U(1))
+ {
+ return to_integer_result<T,CharT>(ru.ptr, to_integer_errc::overflow);
+ }
+ else
+ {
+ n = static_cast<T>(U(0) - u);
+ return to_integer_result<T,CharT>(ru.ptr, to_integer_errc());
+ }
+ }
+ else
+ {
+ if (u > static_cast<U>((type_traits::integer_limits<T>::max)()))
+ {
+ return to_integer_result<T,CharT>(ru.ptr, to_integer_errc::overflow);
+ }
+ else
+ {
+ n = static_cast<T>(u);
+ return to_integer_result<T,CharT>(ru.ptr, to_integer_errc());
+ }
+ }
+}
+
+template <class T, class CharT>
+typename std::enable_if<type_traits::integer_limits<T>::is_specialized && !type_traits::integer_limits<T>::is_signed,to_integer_result<T,CharT>>::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_integer_result<T,CharT>(s, to_integer_errc::invalid_digit);
+ }
+ 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_integer_result<T,CharT>(s, to_integer_errc::invalid_digit);
+ }
+ break;
+ }
+ case integer_chars_state::binary:
+ {
+ static constexpr T max_value = (type_traits::integer_limits<T>::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<T>(*s) - static_cast<T>('0');
+ break;
+ default:
+ return to_integer_result<T,CharT>(s, to_integer_errc::invalid_digit);
+ }
+ if (n > max_value_div_2)
+ {
+ return to_integer_result<T,CharT>(s, to_integer_errc::overflow);
+ }
+ n = n * 2;
+ if (n > max_value - x)
+ {
+ return to_integer_result<T,CharT>(s, to_integer_errc::overflow);
+ }
+ n += x;
+ }
+ break;
+ }
+ case integer_chars_state::octal:
+ {
+ static constexpr T max_value = (type_traits::integer_limits<T>::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<T>(*s) - static_cast<T>('0');
+ break;
+ default:
+ return to_integer_result<T,CharT>(s, to_integer_errc::invalid_digit);
+ }
+ if (n > max_value_div_8)
+ {
+ return to_integer_result<T,CharT>(s, to_integer_errc::overflow);
+ }
+ n = n * 8;
+ if (n > max_value - x)
+ {
+ return to_integer_result<T,CharT>(s, to_integer_errc::overflow);
+ }
+ n += x;
+ }
+ break;
+ }
+ case integer_chars_state::decimal:
+ {
+ static constexpr T max_value = (type_traits::integer_limits<T>::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<T>(*s) - static_cast<T>('0');
+ break;
+ default:
+ return to_integer_result<T,CharT>(s, to_integer_errc::invalid_digit);
+ }
+ if (n > max_value_div_10)
+ {
+ return to_integer_result<T,CharT>(s, to_integer_errc::overflow);
+ }
+ n = n * 10;
+ if (n > max_value - x)
+ {
+ return to_integer_result<T,CharT>(s, to_integer_errc::overflow);
+ }
+ n += x;
+ }
+ break;
+ }
+ case integer_chars_state::base16:
+ {
+ static constexpr T max_value = (type_traits::integer_limits<T>::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_integer_result<T,CharT>(s, to_integer_errc::invalid_digit);
+ }
+ if (n > max_value_div_16)
+ {
+ return to_integer_result<T,CharT>(s, to_integer_errc::overflow);
+ }
+ n = n * 16;
+ if (n > max_value - x)
+ {
+ return to_integer_result<T,CharT>(s, to_integer_errc::overflow);
+ }
+
+ n += x;
+ }
+ break;
+ }
+ default:
+ JSONCONS_UNREACHABLE();
+ break;
+ }
+ }
+ return (state == integer_chars_state::initial) ? to_integer_result<T,CharT>(s, to_integer_errc::invalid_number) : to_integer_result<T,CharT>(s, to_integer_errc());
+}
+
+template <class T, class CharT>
+typename std::enable_if<type_traits::integer_limits<T>::is_specialized && type_traits::integer_limits<T>::is_signed,to_integer_result<T,CharT>>::type
+to_integer(const CharT* s, std::size_t length, T& n)
+{
+ n = 0;
+
+ if (length == 0)
+ {
+ return to_integer_result<T,CharT>(s, to_integer_errc::invalid_number);
+ }
+
+ bool is_negative = *s == '-' ? true : false;
+ if (is_negative)
+ {
+ ++s;
+ --length;
+ }
+
+ using U = typename type_traits::make_unsigned<T>::type;
+
+ U u;
+ auto ru = to_integer(s, length, u);
+ if (ru.ec != to_integer_errc())
+ {
+ return to_integer_result<T,CharT>(ru.ptr, ru.ec);
+ }
+ if (is_negative)
+ {
+ if (u > static_cast<U>(-((type_traits::integer_limits<T>::lowest)()+T(1))) + U(1))
+ {
+ return to_integer_result<T,CharT>(ru.ptr, to_integer_errc::overflow);
+ }
+ else
+ {
+ n = static_cast<T>(U(0) - u);
+ return to_integer_result<T,CharT>(ru.ptr, to_integer_errc());
+ }
+ }
+ else
+ {
+ if (u > static_cast<U>((type_traits::integer_limits<T>::max)()))
+ {
+ return to_integer_result<T,CharT>(ru.ptr, to_integer_errc::overflow);
+ }
+ else
+ {
+ n = static_cast<T>(u);
+ return to_integer_result<T,CharT>(ru.ptr, to_integer_errc());
+ }
+ }
+}
+
+template <class T, class CharT>
+typename std::enable_if<type_traits::integer_limits<T>::is_specialized,to_integer_result<T,CharT>>::type
+to_integer(const CharT* s, T& n)
+{
+ return to_integer<T,CharT>(s, std::char_traits<CharT>::length(s), n);
+}
+
+// Precondition: s satisfies
+
+// digit
+// digit1-digits
+// - digit
+// - digit1-digits
+
+template <class T, class CharT>
+typename std::enable_if<type_traits::integer_limits<T>::is_specialized && !type_traits::integer_limits<T>::is_signed,to_integer_result<T,CharT>>::type
+to_integer_unchecked(const CharT* s, std::size_t length, T& n)
+{
+ static_assert(type_traits::integer_limits<T>::is_specialized, "Integer type not specialized");
+ JSONCONS_ASSERT(length > 0);
+
+ n = 0;
+ const CharT* end = s + length;
+ if (*s == '-')
+ {
+ static constexpr T min_value = (type_traits::integer_limits<T>::lowest)();
+ static constexpr T min_value_div_10 = min_value / 10;
+ ++s;
+ for (; s < end; ++s)
+ {
+ T x = (T)*s - (T)('0');
+ if (n < min_value_div_10)
+ {
+ return to_integer_result<T,CharT>(s, to_integer_errc::overflow);
+ }
+ n = n * 10;
+ if (n < min_value + x)
+ {
+ return to_integer_result<T,CharT>(s, to_integer_errc::overflow);
+ }
+
+ n -= x;
+ }
+ }
+ else
+ {
+ static constexpr T max_value = (type_traits::integer_limits<T>::max)();
+ static constexpr T max_value_div_10 = max_value / 10;
+ for (; s < end; ++s)
+ {
+ T x = static_cast<T>(*s) - static_cast<T>('0');
+ if (n > max_value_div_10)
+ {
+ return to_integer_result<T,CharT>(s, to_integer_errc::overflow);
+ }
+ n = n * 10;
+ if (n > max_value - x)
+ {
+ return to_integer_result<T,CharT>(s, to_integer_errc::overflow);
+ }
+
+ n += x;
+ }
+ }
+
+ return to_integer_result<T,CharT>(s, to_integer_errc());
+}
+
+// Precondition: s satisfies
+
+// digit
+// digit1-digits
+// - digit
+// - digit1-digits
+
+template <class T, class CharT>
+typename std::enable_if<type_traits::integer_limits<T>::is_specialized && type_traits::integer_limits<T>::is_signed,to_integer_result<T,CharT>>::type
+to_integer_unchecked(const CharT* s, std::size_t length, T& n)
+{
+ static_assert(type_traits::integer_limits<T>::is_specialized, "Integer type not specialized");
+ JSONCONS_ASSERT(length > 0);
+
+ n = 0;
+
+ const CharT* end = s + length;
+ if (*s == '-')
+ {
+ static constexpr T min_value = (type_traits::integer_limits<T>::lowest)();
+ static constexpr T min_value_div_10 = min_value / 10;
+ ++s;
+ for (; s < end; ++s)
+ {
+ T x = (T)*s - (T)('0');
+ if (n < min_value_div_10)
+ {
+ return to_integer_result<T,CharT>(s, to_integer_errc::overflow);
+ }
+ n = n * 10;
+ if (n < min_value + x)
+ {
+ return to_integer_result<T,CharT>(s, to_integer_errc::overflow);
+ }
+
+ n -= x;
+ }
+ }
+ else
+ {
+ static constexpr T max_value = (type_traits::integer_limits<T>::max)();
+ static constexpr T max_value_div_10 = max_value / 10;
+ for (; s < end; ++s)
+ {
+ T x = static_cast<T>(*s) - static_cast<T>('0');
+ if (n > max_value_div_10)
+ {
+ return to_integer_result<T,CharT>(s, to_integer_errc::overflow);
+ }
+ n = n * 10;
+ if (n > max_value - x)
+ {
+ return to_integer_result<T,CharT>(s, to_integer_errc::overflow);
+ }
+
+ n += x;
+ }
+ }
+
+ return to_integer_result<T,CharT>(s, to_integer_errc());
+}
+
+// base16_to_integer
+
+template <class T, class CharT>
+typename std::enable_if<type_traits::integer_limits<T>::is_specialized && type_traits::integer_limits<T>::is_signed,to_integer_result<T,CharT>>::type
+base16_to_integer(const CharT* s, std::size_t length, T& n)
+{
+ static_assert(type_traits::integer_limits<T>::is_specialized, "Integer type not specialized");
+ JSONCONS_ASSERT(length > 0);
+
+ n = 0;
+
+ const CharT* end = s + length;
+ if (*s == '-')
+ {
+ static constexpr T min_value = (type_traits::integer_limits<T>::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_integer_result<T,CharT>(s, to_integer_errc::invalid_digit);
+ }
+ if (n < min_value_div_16)
+ {
+ return to_integer_result<T,CharT>(s, to_integer_errc::overflow);
+ }
+ n = n * 16;
+ if (n < min_value + x)
+ {
+ return to_integer_result<T,CharT>(s, to_integer_errc::overflow);
+ }
+ n -= x;
+ }
+ }
+ else
+ {
+ static constexpr T max_value = (type_traits::integer_limits<T>::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_integer_result<T,CharT>(s, to_integer_errc::invalid_digit);
+ }
+ if (n > max_value_div_16)
+ {
+ return to_integer_result<T,CharT>(s, to_integer_errc::overflow);
+ }
+ n = n * 16;
+ if (n > max_value - x)
+ {
+ return to_integer_result<T,CharT>(s, to_integer_errc::overflow);
+ }
+
+ n += x;
+ }
+ }
+
+ return to_integer_result<T,CharT>(s, to_integer_errc());
+}
+
+template <class T, class CharT>
+typename std::enable_if<type_traits::integer_limits<T>::is_specialized && !type_traits::integer_limits<T>::is_signed,to_integer_result<T,CharT>>::type
+base16_to_integer(const CharT* s, std::size_t length, T& n)
+{
+ static_assert(type_traits::integer_limits<T>::is_specialized, "Integer type not specialized");
+ JSONCONS_ASSERT(length > 0);
+
+ n = 0;
+ const CharT* end = s + length;
+
+ static constexpr T max_value = (type_traits::integer_limits<T>::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_integer_result<T,CharT>(s, to_integer_errc::invalid_digit);
+ }
+ if (n > max_value_div_16)
+ {
+ return to_integer_result<T,CharT>(s, to_integer_errc::overflow);
+ }
+ n = n * 16;
+ if (n > max_value - x)
+ {
+ return to_integer_result<T,CharT>(s, to_integer_errc::overflow);
+ }
+
+ n += x;
+ }
+
+ return to_integer_result<T,CharT>(s, to_integer_errc());
+}
+
+
+#if defined(JSONCONS_HAS_STD_FROM_CHARS)
+
+class chars_to
+{
+public:
+
+ char get_decimal_point() const
+ {
+ return '.';
+ }
+
+ template <class CharT>
+ typename std::enable_if<std::is_same<CharT,char>::value,double>::type
+ operator()(const CharT* s, std::size_t len) const
+ {
+ double val = 0;
+ const auto res = std::from_chars(s, s+len, val);
+ if (res.ec != std::errc())
+ {
+ JSONCONS_THROW(json_runtime_error<std::invalid_argument>("Convert chars to double failed"));
+ }
+ return val;
+ }
+
+ template <class CharT>
+ typename std::enable_if<std::is_same<CharT,wchar_t>::value,double>::type
+ operator()(const CharT* s, std::size_t len) const
+ {
+ std::string input(len,'0');
+ for (size_t i = 0; i < len; ++i)
+ {
+ input[i] = static_cast<char>(s[i]);
+ }
+
+ double val = 0;
+ const auto res = std::from_chars(input.data(), input.data() + len, val);
+ if (res.ec != std::errc())
+ {
+ JSONCONS_THROW(json_runtime_error<std::invalid_argument>("Convert chars to double failed"));
+ }
+ return val;
+ }
+};
+#elif defined(JSONCONS_HAS_MSC_STRTOD_L)
+
+class chars_to
+{
+private:
+ _locale_t locale_;
+public:
+ chars_to()
+ {
+ locale_ = _create_locale(LC_NUMERIC, "C");
+ }
+ ~chars_to() noexcept
+ {
+ _free_locale(locale_);
+ }
+
+ chars_to(const chars_to&)
+ {
+ locale_ = _create_locale(LC_NUMERIC, "C");
+ }
+
+ chars_to& operator=(const chars_to&)
+ {
+ // Don't assign locale
+ return *this;
+ }
+
+ char get_decimal_point() const
+ {
+ return '.';
+ }
+
+ template <class CharT>
+ typename std::enable_if<std::is_same<CharT,char>::value,double>::type
+ operator()(const CharT* s, std::size_t) const
+ {
+ CharT *end = nullptr;
+ double val = _strtod_l(s, &end, locale_);
+ if (s == end)
+ {
+ JSONCONS_THROW(json_runtime_error<std::invalid_argument>("Convert string to double failed"));
+ }
+ return val;
+ }
+
+ template <class CharT>
+ typename std::enable_if<std::is_same<CharT,wchar_t>::value,double>::type
+ operator()(const CharT* s, std::size_t) const
+ {
+ CharT *end = nullptr;
+ double val = _wcstod_l(s, &end, locale_);
+ if (s == end)
+ {
+ JSONCONS_THROW(json_runtime_error<std::invalid_argument>("Convert string to double failed"));
+ }
+ return val;
+ }
+};
+
+#elif defined(JSONCONS_HAS_STRTOLD_L)
+
+class chars_to
+{
+private:
+ locale_t locale_;
+public:
+ chars_to()
+ {
+ locale_ = newlocale(LC_ALL_MASK, "C", (locale_t) 0);
+ }
+ ~chars_to() noexcept
+ {
+ freelocale(locale_);
+ }
+
+ chars_to(const chars_to&)
+ {
+ locale_ = newlocale(LC_ALL_MASK, "C", (locale_t) 0);
+ }
+
+ chars_to& operator=(const chars_to&)
+ {
+ return *this;
+ }
+
+ char get_decimal_point() const
+ {
+ return '.';
+ }
+
+ template <class CharT>
+ typename std::enable_if<std::is_same<CharT,char>::value,double>::type
+ operator()(const CharT* s, std::size_t) const
+ {
+ char *end = nullptr;
+ double val = strtold_l(s, &end, locale_);
+ if (s == end)
+ {
+ JSONCONS_THROW(json_runtime_error<std::invalid_argument>("Convert string to double failed"));
+ }
+ return val;
+ }
+
+ template <class CharT>
+ typename std::enable_if<std::is_same<CharT,wchar_t>::value,double>::type
+ operator()(const CharT* s, std::size_t) const
+ {
+ CharT *end = nullptr;
+ double val = wcstold_l(s, &end, locale_);
+ if (s == end)
+ {
+ JSONCONS_THROW(json_runtime_error<std::invalid_argument>("Convert string to double failed"));
+ }
+ return val;
+ }
+};
+
+#else
+class chars_to
+{
+private:
+ std::vector<char> buffer_;
+ char decimal_point_;
+public:
+ chars_to()
+ : buffer_()
+ {
+ struct lconv * lc = localeconv();
+ if (lc != nullptr && lc->decimal_point[0] != 0)
+ {
+ decimal_point_ = lc->decimal_point[0];
+ }
+ else
+ {
+ decimal_point_ = '.';
+ }
+ buffer_.reserve(100);
+ }
+
+ chars_to(const chars_to&) = default;
+ chars_to& operator=(const chars_to&) = default;
+
+ char get_decimal_point() const
+ {
+ return decimal_point_;
+ }
+
+ template <class CharT>
+ typename std::enable_if<std::is_same<CharT,char>::value,double>::type
+ operator()(const CharT* s, std::size_t /*length*/) const
+ {
+ CharT *end = nullptr;
+ double val = strtod(s, &end);
+ if (s == end)
+ {
+ JSONCONS_THROW(json_runtime_error<std::invalid_argument>("Convert string to double failed"));
+ }
+ return val;
+ }
+
+ template <class CharT>
+ typename std::enable_if<std::is_same<CharT,wchar_t>::value,double>::type
+ operator()(const CharT* s, std::size_t /*length*/) const
+ {
+ CharT *end = nullptr;
+ double val = wcstod(s, &end);
+ if (s == end)
+ {
+ JSONCONS_THROW(json_runtime_error<std::invalid_argument>("Convert string to double failed"));
+ }
+ return val;
+ }
+};
+#endif
+
+}}
+
+#endif
diff --git a/include/jsoncons/detail/span.hpp b/include/jsoncons/detail/span.hpp
new file mode 100644
index 0000000..02c24ee
--- /dev/null
+++ b/include/jsoncons/detail/span.hpp
@@ -0,0 +1,188 @@
+#ifndef JSONCONS_DETAIL_SPAN_HPP
+#define JSONCONS_DETAIL_SPAN_HPP
+
+#include <utility> // std::swap
+#include <memory> // std::addressof
+#include <type_traits> // std::enable_if, std::true_type, std::false_type
+#include <jsoncons/config/compiler_support.hpp>
+#include <jsoncons/more_type_traits.hpp>
+#include <iterator>
+#include <limits>
+
+namespace jsoncons
+{
+namespace detail
+{
+ constexpr std::size_t dynamic_extent = (std::numeric_limits<std::size_t>::max)();
+
+ template< class T, std::size_t Extent = dynamic_extent>
+ class span;
+
+ template<class T>
+ struct is_span : std::false_type{};
+
+ template< class T>
+ struct is_span<span<T>> : std::true_type{};
+
+ template<
+ class T,
+ std::size_t Extent
+ > class span
+ {
+ public:
+ using element_type = T;
+ using value_type = typename std::remove_volatile<typename std::remove_const<T>::type>::type;
+ using size_type = std::size_t;
+ using difference_type = std::ptrdiff_t;
+ using pointer = T*;
+ using const_pointer = const T*;
+ using reference = T&;
+ using const_reference = const T&;
+ using iterator = pointer;
+ using const_iterator = const_pointer;
+ using reverse_iterator = std::reverse_iterator<iterator>;
+ using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+ private:
+ pointer data_;
+ size_type size_;
+ public:
+ static constexpr std::size_t extent = Extent;
+
+ constexpr span() noexcept
+ : data_(nullptr), size_(0)
+ {
+ }
+ constexpr span(pointer data, size_type size)
+ : data_(data), size_(size)
+ {
+ }
+
+ template <typename C>
+ constexpr span(C& c,
+ typename std::enable_if<!is_span<C>::value && !type_traits::is_std_array<C>::value && type_traits::is_compatible_element<C,element_type>::value && type_traits::has_data_and_size<C>::value>::type* = 0)
+ : data_(c.data()), size_(c.size())
+ {
+ }
+
+ template <std::size_t N>
+ span(element_type (&arr)[N]) noexcept
+ : data_(std::addressof(arr[0])), size_(N)
+ {
+ }
+
+ template <std::size_t N>
+ span(std::array<value_type, N>& arr,
+ typename std::enable_if<(extent == dynamic_extent || extent == N)>::type* = 0) noexcept
+ : data_(arr.data()), size_(arr.size())
+ {
+ }
+
+ template <std::size_t N>
+ span(const std::array<value_type, N>& arr,
+ typename std::enable_if<(extent == dynamic_extent || extent == N)>::type* = 0) noexcept
+ : data_(arr.data()), size_(arr.size())
+ {
+ }
+
+ template <typename C>
+ constexpr span(const C& c,
+ typename std::enable_if<!is_span<C>::value && !type_traits::is_std_array<C>::value && type_traits::is_compatible_element<C,element_type>::value && type_traits::has_data_and_size<C>::value>::type* = 0)
+ : data_(c.data()), size_(c.size())
+ {
+ }
+
+ template <class U, std::size_t N>
+ constexpr span(const span<U, N>& s,
+ typename std::enable_if<(N == dynamic_extent || N == extent) && std::is_convertible<U(*)[], element_type(*)[]>::value>::type* = 0) noexcept
+ : data_(s.data()), size_(s.size())
+ {
+ }
+
+ constexpr span(const span& other) noexcept = default;
+
+ span& operator=( const span& other ) noexcept = default;
+
+ constexpr pointer data() const noexcept
+ {
+ return data_;
+ }
+
+ constexpr size_type size() const noexcept
+ {
+ return size_;
+ }
+
+ constexpr bool empty() const noexcept
+ {
+ return size_ == 0;
+ }
+
+ constexpr reference operator[](size_type index) const
+ {
+ return data_[index];
+ }
+
+ // iterator support
+ const_iterator begin() const noexcept
+ {
+ return data_;
+ }
+ const_iterator end() const noexcept
+ {
+ return data_ + size_;
+ }
+ const_iterator cbegin() const noexcept
+ {
+ return data_;
+ }
+ const_iterator cend() const noexcept
+ {
+ return data_ + size_;
+ }
+ const_reverse_iterator rbegin() const noexcept
+ {
+ return const_reverse_iterator(end());
+ }
+ const_reverse_iterator rend() const noexcept
+ {
+ return const_reverse_iterator(begin());
+ }
+ const_reverse_iterator crbegin() const noexcept
+ {
+ return const_reverse_iterator(end());
+ }
+ const_reverse_iterator crend() const noexcept
+ {
+ return const_reverse_iterator(begin());
+ }
+
+ span<element_type, dynamic_extent>
+ first(std::size_t count) const
+ {
+ JSONCONS_ASSERT(count <= size());
+
+ return span< element_type, dynamic_extent >( data(), count );
+ }
+
+ span<element_type, dynamic_extent>
+ last(std::size_t count) const
+ {
+ JSONCONS_ASSERT(count <= size());
+
+ return span<element_type, dynamic_extent>(data() + ( size() - count ), count);
+ }
+
+ span<element_type, dynamic_extent>
+ subspan(std::size_t offset, std::size_t count = dynamic_extent) const
+ {
+ //JSONCONS_ASSERT((offset <= size() && (count == dynamic_extent || (offset + count <= size()))));
+
+ return span<element_type, dynamic_extent>(
+ data() + offset, count == dynamic_extent ? size() - offset : count );
+ }
+ };
+
+} // namespace detail
+} // namespace jsoncons
+
+#endif
diff --git a/include/jsoncons/detail/string_view.hpp b/include/jsoncons/detail/string_view.hpp
new file mode 100644
index 0000000..fc568a1
--- /dev/null
+++ b/include/jsoncons/detail/string_view.hpp
@@ -0,0 +1,537 @@
+// Copyright 2019 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_STRING_VIEW_HPP
+#define JSONCONS_STRING_VIEW_HPP
+
+#include <stdexcept>
+#include <string>
+#include <vector>
+#include <ostream>
+#include <cmath>
+#include <algorithm> // std::find, std::min, std::reverse
+#include <memory>
+#include <iterator>
+#include <exception>
+#include <stdexcept>
+#include <istream> // std::basic_istream
+#include <jsoncons/config/compiler_support.hpp>
+
+namespace jsoncons {
+namespace detail {
+
+ template <class CharT, class Traits = std::char_traits<CharT>>
+ class basic_string_view
+ {
+ private:
+ const CharT* data_;
+ std::size_t length_;
+ public:
+ using value_type = CharT;
+ using const_reference = const CharT&;
+ using traits_type = Traits;
+ using size_type = std::size_t;
+ static constexpr size_type npos = size_type(-1);
+ using const_iterator = const CharT*;
+ using iterator = const CharT*;
+ using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+
+ constexpr basic_string_view() noexcept
+ : data_(nullptr), length_(0)
+ {
+ }
+ constexpr basic_string_view(const CharT* data, std::size_t length)
+ : data_(data), length_(length)
+ {
+ }
+
+ basic_string_view(const CharT* data)
+ : data_(data), length_(Traits::length(data))
+ {
+ }
+ constexpr basic_string_view(const basic_string_view& other) noexcept = default;
+
+ template <class Tr, class Allocator>
+ JSONCONS_CPP14_CONSTEXPR basic_string_view(const std::basic_string<CharT,Tr,Allocator>& s) noexcept
+ : data_(s.data()), length_(s.length())
+ {
+ }
+
+ JSONCONS_CPP14_CONSTEXPR basic_string_view& operator=( const basic_string_view& view ) noexcept
+ {
+ data_ = view.data();
+ length_ = view.length();
+
+ return *this;
+ }
+
+ template <class Allocator>
+ explicit operator std::basic_string<CharT,Traits,Allocator>() const
+ {
+ return std::basic_string<CharT,Traits,Allocator>(data_,length_);
+ }
+
+ // iterator support
+ const_iterator begin() const noexcept
+ {
+ return data_;
+ }
+ const_iterator end() const noexcept
+ {
+ return data_ + length_;
+ }
+ const_iterator cbegin() const noexcept
+ {
+ return data_;
+ }
+ const_iterator cend() const noexcept
+ {
+ return data_ + length_;
+ }
+ const_reverse_iterator rbegin() const noexcept
+ {
+ return const_reverse_iterator(end());
+ }
+ const_reverse_iterator rend() const noexcept
+ {
+ return const_reverse_iterator(begin());
+ }
+ const_reverse_iterator crbegin() const noexcept
+ {
+ return const_reverse_iterator(end());
+ }
+ const_reverse_iterator crend() const noexcept
+ {
+ return const_reverse_iterator(begin());
+ }
+
+ // capacity
+
+ std::size_t size() const
+ {
+ return length_;
+ }
+
+ std::size_t length() const
+ {
+ return length_;
+ }
+ size_type max_size() const noexcept
+ {
+ return length_;
+ }
+ bool empty() const noexcept
+ {
+ return length_ == 0;
+ }
+
+ // element access
+
+ const_reference operator[](size_type pos) const
+ {
+ return data_[pos];
+ }
+
+ const_reference at(std::size_t pos) const
+ {
+ if (pos >= length_)
+ {
+ JSONCONS_THROW(std::out_of_range("pos exceeds length"));
+ }
+ return data_[pos];
+ }
+
+ const_reference front() const
+ {
+ return data_[0];
+ }
+ const_reference back() const
+ {
+ return data_[length_-1];
+ }
+
+ const CharT* data() const
+ {
+ return data_;
+ }
+
+ // string operations
+
+ basic_string_view substr(size_type pos, size_type n=npos) const
+ {
+ if (pos > length_)
+ {
+ JSONCONS_THROW(std::out_of_range("pos exceeds size"));
+ }
+ if (n == npos || pos + n > length_)
+ {
+ n = length_ - pos;
+ }
+ return basic_string_view(data_ + pos, n);
+ }
+
+ int compare(const basic_string_view& s) const noexcept
+ {
+ const int rc = Traits::compare(data_, s.data_, (std::min)(length_, s.length_));
+ return rc != 0 ? rc : (length_ == s.length_ ? 0 : length_ < s.length_ ? -1 : 1);
+ }
+
+ int compare(const CharT* data) const noexcept
+ {
+ const size_t length = Traits::length(data);
+ const int rc = Traits::compare(data_, data, (std::min)(length_, length));
+ return rc != 0 ? rc : (length_ == length? 0 : length_ < length? -1 : 1);
+ }
+
+ template <class Allocator>
+ int compare(const std::basic_string<CharT,Traits,Allocator>& s) const noexcept
+ {
+ const int rc = Traits::compare(data_, s.data(), (std::min)(length_, s.length()));
+ return rc != 0 ? rc : (length_ == s.length() ? 0 : length_ < s.length() ? -1 : 1);
+ }
+
+ size_type find(basic_string_view s, size_type pos = 0) const noexcept
+ {
+ if (pos > length_)
+ {
+ return npos;
+ }
+ if (s.length_ == 0)
+ {
+ return pos;
+ }
+ const_iterator it = std::search(cbegin() + pos, cend(),
+ s.cbegin(), s.cend(), Traits::eq);
+ return it == cend() ? npos : std::distance(cbegin(), it);
+ }
+ size_type find(CharT ch, size_type pos = 0) const noexcept
+ {
+ return find(basic_string_view(&ch, 1), pos);
+ }
+ size_type find(const CharT* s, size_type pos, size_type n) const noexcept
+ {
+ return find(basic_string_view(s, n), pos);
+ }
+ size_type find(const CharT* s, size_type pos = 0) const noexcept
+ {
+ return find(basic_string_view(s), pos);
+ }
+
+ size_type rfind(basic_string_view s, size_type pos = npos) const noexcept
+ {
+ if (length_ < s.length_)
+ {
+ return npos;
+ }
+ if (pos > length_ - s.length_)
+ {
+ pos = length_ - s.length_;
+ }
+ if (s.length_ == 0)
+ {
+ return pos;
+ }
+ for (const CharT* p = data_ + pos; true; --p)
+ {
+ if (Traits::compare(p, s.data_, s.length_) == 0)
+ {
+ return p - data_;
+ }
+ if (p == data_)
+ {
+ return npos;
+ }
+ };
+ }
+ size_type rfind(CharT ch, size_type pos = npos) const noexcept
+ {
+ return rfind(basic_string_view(&ch, 1), pos);
+ }
+ size_type rfind(const CharT* s, size_type pos, size_type n) const noexcept
+ {
+ return rfind(basic_string_view(s, n), pos);
+ }
+ size_type rfind(const CharT* s, size_type pos = npos) const noexcept
+ {
+ return rfind(basic_string_view(s), pos);
+ }
+
+ size_type find_first_of(basic_string_view s, size_type pos = 0) const noexcept
+ {
+ if (pos >= length_ || s.length_ == 0)
+ {
+ return npos;
+ }
+ const_iterator it = std::find_first_of
+ (cbegin() + pos, cend(), s.cbegin(), s.cend(), Traits::eq);
+ return it == cend() ? npos : std::distance (cbegin(), it);
+ }
+ size_type find_first_of(CharT ch, size_type pos = 0) const noexcept
+ {
+ return find_first_of(basic_string_view(&ch, 1), pos);
+ }
+ size_type find_first_of(const CharT* s, size_type pos, size_type n) const noexcept
+ {
+ return find_first_of(basic_string_view(s, n), pos);
+ }
+ size_type find_first_of(const CharT* s, size_type pos = 0) const noexcept
+ {
+ return find_first_of(basic_string_view(s), pos);
+ }
+
+ size_type find_last_of(basic_string_view s, size_type pos = npos) const noexcept
+ {
+ if (s.length_ == 0)
+ {
+ return npos;
+ }
+ if (pos >= length_)
+ {
+ pos = 0;
+ }
+ else
+ {
+ pos = length_ - (pos+1);
+ }
+ const_reverse_iterator it = std::find_first_of
+ (crbegin() + pos, crend(), s.cbegin(), s.cend(), Traits::eq);
+ return it == crend() ? npos : (length_ - 1 - std::distance(crbegin(), it));
+ }
+ size_type find_last_of(CharT ch, size_type pos = npos) const noexcept
+ {
+ return find_last_of(basic_string_view(&ch, 1), pos);
+ }
+ size_type find_last_of(const CharT* s, size_type pos, size_type n) const noexcept
+ {
+ return find_last_of(basic_string_view(s, n), pos);
+ }
+ size_type find_last_of(const CharT* s, size_type pos = npos) const noexcept
+ {
+ return find_last_of(basic_string_view(s), pos);
+ }
+
+ size_type find_first_not_of(basic_string_view s, size_type pos = 0) const noexcept
+ {
+ if (pos >= length_)
+ return npos;
+ if (s.length_ == 0)
+ return pos;
+
+ const_iterator it = cend();
+ for (auto p = cbegin()+pos; p != cend(); ++p)
+ {
+ if (Traits::find(s.data_, s.length_, *p) == 0)
+ {
+ it = p;
+ break;
+ }
+ }
+ return it == cend() ? npos : std::distance (cbegin(), it);
+ }
+ size_type find_first_not_of(CharT ch, size_type pos = 0) const noexcept
+ {
+ return find_first_not_of(basic_string_view(&ch, 1), pos);
+ }
+ size_type find_first_not_of(const CharT* s, size_type pos, size_type n) const noexcept
+ {
+ return find_first_not_of(basic_string_view(s, n), pos);
+ }
+ size_type find_first_not_of(const CharT* s, size_type pos = 0) const noexcept
+ {
+ return find_first_not_of(basic_string_view(s), pos);
+ }
+
+ size_type find_last_not_of(basic_string_view s, size_type pos = npos) const noexcept
+ {
+ if (pos >= length_)
+ {
+ pos = length_ - 1;
+ }
+ if (s.length_ == 0)
+ {
+ return pos;
+ }
+ pos = length_ - (pos+1);
+
+ const_iterator it = crend();
+ for (auto p = crbegin()+pos; p != crend(); ++p)
+ {
+ if (Traits::find(s.data_, s.length_, *p) == 0)
+ {
+ it = p;
+ break;
+ }
+ }
+ return it == crend() ? npos : (length_ - 1 - std::distance(crbegin(), it));
+ }
+ size_type find_last_not_of(CharT ch, size_type pos = npos) const noexcept
+ {
+ return find_last_not_of(basic_string_view(&ch, 1), pos);
+ }
+ size_type find_last_not_of(const CharT* s, size_type pos, size_type n) const noexcept
+ {
+ return find_last_not_of(basic_string_view(s, n), pos);
+ }
+ size_type find_last_not_of(const CharT* s, size_type pos = npos) const noexcept
+ {
+ return find_last_not_of(basic_string_view(s), pos);
+ }
+
+ friend std::basic_ostream<CharT>& operator<<(std::basic_ostream<CharT>& os, const basic_string_view& sv)
+ {
+ os.write(sv.data_,sv.length_);
+ return os;
+ }
+ };
+
+ // ==
+ template<class CharT,class Traits>
+ bool operator==(const basic_string_view<CharT,Traits>& lhs,
+ const basic_string_view<CharT,Traits>& rhs) noexcept
+ {
+ return lhs.compare(rhs) == 0;
+ }
+ template<class CharT,class Traits,class Allocator>
+ bool operator==(const basic_string_view<CharT,Traits>& lhs,
+ const std::basic_string<CharT,Traits,Allocator>& rhs) noexcept
+ {
+ return lhs.compare(rhs) == 0;
+ }
+ template<class CharT,class Traits,class Allocator>
+ bool operator==(const std::basic_string<CharT,Traits,Allocator>& lhs,
+ const basic_string_view<CharT,Traits>& rhs) noexcept
+ {
+ return rhs.compare(lhs) == 0;
+ }
+ template<class CharT,class Traits>
+ bool operator==(const basic_string_view<CharT,Traits>& lhs,
+ const CharT* rhs) noexcept
+ {
+ return lhs.compare(rhs) == 0;
+ }
+ template<class CharT,class Traits>
+ bool operator==(const CharT* lhs,
+ const basic_string_view<CharT,Traits>& rhs) noexcept
+ {
+ return rhs.compare(lhs) == 0;
+ }
+
+ // !=
+ template<class CharT,class Traits>
+ bool operator!=(const basic_string_view<CharT,Traits>& lhs,
+ const basic_string_view<CharT,Traits>& rhs) noexcept
+ {
+ return lhs.compare(rhs) != 0;
+ }
+ template<class CharT,class Traits,class Allocator>
+ bool operator!=(const basic_string_view<CharT,Traits>& lhs,
+ const std::basic_string<CharT,Traits,Allocator>& rhs) noexcept
+ {
+ return lhs.compare(rhs) != 0;
+ }
+ template<class CharT,class Traits,class Allocator>
+ bool operator!=(const std::basic_string<CharT,Traits,Allocator>& lhs,
+ const basic_string_view<CharT,Traits>& rhs) noexcept
+ {
+ return rhs.compare(lhs) != 0;
+ }
+ template<class CharT,class Traits>
+ bool operator!=(const basic_string_view<CharT,Traits>& lhs,
+ const CharT* rhs) noexcept
+ {
+ return lhs.compare(rhs) != 0;
+ }
+ template<class CharT,class Traits>
+ bool operator!=(const CharT* lhs,
+ const basic_string_view<CharT,Traits>& rhs) noexcept
+ {
+ return rhs.compare(lhs) != 0;
+ }
+
+ // <=
+ template<class CharT,class Traits>
+ bool operator<=(const basic_string_view<CharT,Traits>& lhs,
+ const basic_string_view<CharT,Traits>& rhs) noexcept
+ {
+ return lhs.compare(rhs) <= 0;
+ }
+ template<class CharT,class Traits,class Allocator>
+ bool operator<=(const basic_string_view<CharT,Traits>& lhs,
+ const std::basic_string<CharT,Traits,Allocator>& rhs) noexcept
+ {
+ return lhs.compare(rhs) <= 0;
+ }
+ template<class CharT,class Traits,class Allocator>
+ bool operator<=(const std::basic_string<CharT,Traits,Allocator>& lhs,
+ const basic_string_view<CharT,Traits>& rhs) noexcept
+ {
+ return rhs.compare(lhs) >= 0;
+ }
+
+ // <
+ template<class CharT,class Traits>
+ bool operator<(const basic_string_view<CharT,Traits>& lhs,
+ const basic_string_view<CharT,Traits>& rhs) noexcept
+ {
+ return lhs.compare(rhs) < 0;
+ }
+ template<class CharT,class Traits,class Allocator>
+ bool operator<(const basic_string_view<CharT,Traits>& lhs,
+ const std::basic_string<CharT,Traits,Allocator>& rhs) noexcept
+ {
+ return lhs.compare(rhs) < 0;
+ }
+ template<class CharT,class Traits,class Allocator>
+ bool operator<(const std::basic_string<CharT,Traits,Allocator>& lhs,
+ const basic_string_view<CharT,Traits>& rhs) noexcept
+ {
+ return rhs.compare(lhs) > 0;
+ }
+
+ // >=
+ template<class CharT,class Traits>
+ bool operator>=(const basic_string_view<CharT,Traits>& lhs,
+ const basic_string_view<CharT,Traits>& rhs) noexcept
+ {
+ return lhs.compare(rhs) >= 0;
+ }
+ template<class CharT,class Traits,class Allocator>
+ bool operator>=(const basic_string_view<CharT,Traits>& lhs,
+ const std::basic_string<CharT,Traits,Allocator>& rhs) noexcept
+ {
+ return lhs.compare(rhs) >= 0;
+ }
+ template<class CharT,class Traits,class Allocator>
+ bool operator>=(const std::basic_string<CharT,Traits,Allocator>& lhs,
+ const basic_string_view<CharT,Traits>& rhs) noexcept
+ {
+ return rhs.compare(lhs) <= 0;
+ }
+
+ // >
+ template<class CharT,class Traits>
+ bool operator>(const basic_string_view<CharT,Traits>& lhs,
+ const basic_string_view<CharT,Traits>& rhs) noexcept
+ {
+ return lhs.compare(rhs) > 0;
+ }
+ template<class CharT,class Traits,class Allocator>
+ bool operator>(const basic_string_view<CharT,Traits>& lhs,
+ const std::basic_string<CharT,Traits,Allocator>& rhs) noexcept
+ {
+ return lhs.compare(rhs) > 0;
+ }
+ template<class CharT,class Traits,class Allocator>
+ bool operator>(const std::basic_string<CharT,Traits,Allocator>& lhs,
+ const basic_string_view<CharT,Traits>& rhs) noexcept
+ {
+ return rhs.compare(lhs) < 0;
+ }
+
+} // namespace detail
+} // namespace jsoncons
+
+#endif
diff --git a/include/jsoncons/detail/string_wrapper.hpp b/include/jsoncons/detail/string_wrapper.hpp
new file mode 100644
index 0000000..88634b0
--- /dev/null
+++ b/include/jsoncons/detail/string_wrapper.hpp
@@ -0,0 +1,370 @@
+// Copyright 2013 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_DETAIL_STRING_WRAPPER_HPP
+#define JSONCONS_DETAIL_STRING_WRAPPER_HPP
+
+#include <stdexcept>
+#include <string>
+#include <exception>
+#include <ostream>
+#include <cstring> // std::memcpy
+#include <memory> // std::allocator
+#include <jsoncons/config/compiler_support.hpp>
+
+namespace jsoncons {
+namespace detail {
+
+ // From boost 1_71
+ template <class T, class U>
+ T launder_cast(U* u)
+ {
+ #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606
+ return std::launder(reinterpret_cast<T>(u));
+ #elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) > 800
+ return __builtin_launder(reinterpret_cast<T>(u));
+ #else
+ return reinterpret_cast<T>(u);
+ #endif
+ }
+
+ // string_wrapper
+
+ template <class CharT,class Allocator>
+ class string_wrapper
+ {
+ public:
+ using char_type = CharT;
+ private:
+ struct str_base_t
+ {
+ Allocator alloc_;
+
+ Allocator& get_allocator()
+ {
+ return alloc_;
+ }
+
+ const Allocator& get_allocator() const
+ {
+ return alloc_;
+ }
+
+ str_base_t(const Allocator& alloc)
+ : alloc_(alloc)
+ {
+ }
+
+ ~str_base_t() noexcept = default;
+ };
+
+ struct str_t : public str_base_t
+ {
+ typedef typename std::allocator_traits<Allocator>::template rebind_alloc<CharT> allocator_type;
+ using allocator_traits_type = std::allocator_traits<allocator_type>;
+ using pointer = typename allocator_traits_type::pointer;
+
+ pointer p_;
+ std::size_t length_;
+
+ ~str_t() noexcept = default;
+
+ const char_type* c_str() const { return type_traits::to_plain_pointer(p_); }
+ const char_type* data() const { return type_traits::to_plain_pointer(p_); }
+ std::size_t length() const { return length_; }
+
+ str_t(const Allocator& alloc)
+ : str_base_t(alloc), p_(nullptr), length_(0)
+ {
+
+ }
+
+ str_t(const str_t&) = delete;
+ str_t& operator=(const str_t&) = delete;
+
+ };
+
+ typedef typename std::allocator_traits<Allocator>::template rebind_alloc<char> byte_allocator_type;
+ using byte_pointer = typename std::allocator_traits<byte_allocator_type>::pointer;
+
+ typedef typename std::allocator_traits<Allocator>::template rebind_alloc<str_t> string_allocator_type;
+ using string_pointer = typename std::allocator_traits<string_allocator_type>::pointer;
+
+ struct storage_t
+ {
+ str_t data;
+ char_type c[1];
+ };
+ typedef typename std::aligned_storage<sizeof(storage_t), alignof(storage_t)>::type json_storage_kind;
+
+ string_pointer ptr_;
+ public:
+ string_wrapper() = default;
+
+ string_wrapper(string_pointer ptr)
+ : ptr_(ptr)
+ {
+ }
+
+ string_wrapper(const char_type* data, std::size_t length, const Allocator& a)
+ {
+ ptr_ = create(data,length,a);
+ }
+
+ string_wrapper(const string_wrapper& val)
+ {
+ ptr_ = create(val.data(),val.length(),val.get_allocator());
+ }
+
+ string_wrapper(const string_wrapper& val, const Allocator& a)
+ {
+ ptr_ = create(val.data(),val.length(),a);
+ }
+
+ ~string_wrapper() noexcept
+ {
+ if (ptr_ != nullptr)
+ {
+ destroy(ptr_);
+ }
+ }
+
+ void swap(string_wrapper& other) noexcept
+ {
+ std::swap(ptr_,other.ptr_);
+ }
+
+ const char_type* data() const
+ {
+ return ptr_->data();
+ }
+
+ const char_type* c_str() const
+ {
+ return ptr_->c_str();
+ }
+
+ std::size_t length() const
+ {
+ return ptr_->length();
+ }
+
+ Allocator get_allocator() const
+ {
+ return ptr_->get_allocator();
+ }
+ private:
+ static size_t aligned_size(std::size_t n)
+ {
+ return sizeof(json_storage_kind) + n;
+ }
+
+ static string_pointer create(const char_type* s, std::size_t length, const Allocator& alloc)
+ {
+ std::size_t mem_size = aligned_size(length*sizeof(char_type));
+
+ byte_allocator_type byte_alloc(alloc);
+ byte_pointer ptr = byte_alloc.allocate(mem_size);
+
+ char* storage = type_traits::to_plain_pointer(ptr);
+ str_t* ps = new(storage)str_t(byte_alloc);
+
+ auto psa = launder_cast<storage_t*>(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<typename str_t::pointer>::pointer_to(*p);
+ ps->length_ = length;
+ return std::pointer_traits<string_pointer>::pointer_to(*ps);
+ }
+
+ static void destroy(string_pointer ptr)
+ {
+ str_t* rawp = type_traits::to_plain_pointer(ptr);
+
+ char* p = launder_cast<char*>(rawp);
+
+ std::size_t mem_size = aligned_size(ptr->length_*sizeof(char_type));
+ byte_allocator_type byte_alloc(ptr->get_allocator());
+ byte_alloc.deallocate(p,mem_size);
+ }
+ };
+
+ // tagged_string_wrapper
+
+ template <class CharT,class Allocator>
+ class tagged_string_wrapper
+ {
+ public:
+ using char_type = CharT;
+ private:
+ struct str_base_t
+ {
+ Allocator alloc_;
+
+ Allocator& get_allocator()
+ {
+ return alloc_;
+ }
+
+ const Allocator& get_allocator() const
+ {
+ return alloc_;
+ }
+
+ str_base_t(const Allocator& alloc)
+ : alloc_(alloc)
+ {
+ }
+
+ ~str_base_t() noexcept = default;
+ };
+
+ struct str_t : public str_base_t
+ {
+ typedef typename std::allocator_traits<Allocator>::template rebind_alloc<CharT> allocator_type;
+ using allocator_traits_type = std::allocator_traits<allocator_type>;
+ using pointer = typename allocator_traits_type::pointer;
+
+ pointer p_;
+ std::size_t length_;
+ uint64_t tag_;
+
+ ~str_t() noexcept = default;
+
+ const char_type* c_str() const { return type_traits::to_plain_pointer(p_); }
+ const char_type* data() const { return type_traits::to_plain_pointer(p_); }
+ std::size_t length() const { return length_; }
+ uint64_t tag() const { return tag_; }
+
+ str_t(uint64_t tag, const Allocator& alloc)
+ : str_base_t(alloc), p_(nullptr), length_(0), tag_(tag)
+ {
+
+ }
+
+ str_t(const str_t&) = delete;
+ str_t& operator=(const str_t&) = delete;
+
+ };
+
+ typedef typename std::allocator_traits<Allocator>::template rebind_alloc<char> byte_allocator_type;
+ using byte_pointer = typename std::allocator_traits<byte_allocator_type>::pointer;
+
+ typedef typename std::allocator_traits<Allocator>::template rebind_alloc<str_t> string_allocator_type;
+ using string_pointer = typename std::allocator_traits<string_allocator_type>::pointer;
+
+ struct storage_t
+ {
+ str_t data;
+ char_type c[1];
+ };
+ typedef typename std::aligned_storage<sizeof(storage_t), alignof(storage_t)>::type json_storage_kind;
+
+ string_pointer ptr_;
+ public:
+ tagged_string_wrapper() = default;
+
+ tagged_string_wrapper(string_pointer ptr)
+ : ptr_(ptr)
+ {
+ }
+
+ tagged_string_wrapper(const char_type* data, std::size_t length, uint64_t tag, const Allocator& alloc)
+ {
+ ptr_ = create(data, length, tag, alloc);
+ }
+
+ tagged_string_wrapper(const tagged_string_wrapper& val)
+ {
+ ptr_ = create(val.data(), val.length(), val.tag(), val.get_allocator());
+ }
+
+ tagged_string_wrapper(const tagged_string_wrapper& val, const Allocator& alloc)
+ {
+ ptr_ = create(val.data(), val.length(), val.tag(), alloc);
+ }
+
+ ~tagged_string_wrapper() noexcept
+ {
+ if (ptr_ != nullptr)
+ {
+ destroy(ptr_);
+ }
+ }
+
+ void swap(tagged_string_wrapper& other) noexcept
+ {
+ std::swap(ptr_,other.ptr_);
+ }
+
+ const char_type* data() const
+ {
+ return ptr_->data();
+ }
+
+ const char_type* c_str() const
+ {
+ return ptr_->c_str();
+ }
+
+ std::size_t length() const
+ {
+ return ptr_->length();
+ }
+
+ uint64_t tag() const
+ {
+ return ptr_->tag();
+ }
+
+ Allocator get_allocator() const
+ {
+ return ptr_->get_allocator();
+ }
+ private:
+ static size_t aligned_size(std::size_t n)
+ {
+ return sizeof(json_storage_kind) + n;
+ }
+
+ static string_pointer create(const char_type* s, std::size_t length, uint64_t tag, const Allocator& alloc)
+ {
+ std::size_t mem_size = aligned_size(length*sizeof(char_type));
+
+ byte_allocator_type byte_alloc(alloc);
+ byte_pointer ptr = byte_alloc.allocate(mem_size);
+
+ char* storage = type_traits::to_plain_pointer(ptr);
+ str_t* ps = new(storage)str_t(tag, byte_alloc);
+
+ auto psa = launder_cast<storage_t*>(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<typename str_t::pointer>::pointer_to(*p);
+ ps->length_ = length;
+ return std::pointer_traits<string_pointer>::pointer_to(*ps);
+ }
+
+ static void destroy(string_pointer ptr)
+ {
+ str_t* rawp = type_traits::to_plain_pointer(ptr);
+
+ char* p = launder_cast<char*>(rawp);
+
+ std::size_t mem_size = aligned_size(ptr->length_*sizeof(char_type));
+ byte_allocator_type byte_alloc(ptr->get_allocator());
+ byte_alloc.deallocate(p,mem_size);
+ }
+ };
+
+} // namespace detail
+} // namespace jsoncons
+
+#endif
diff --git a/include/jsoncons/detail/write_number.hpp b/include/jsoncons/detail/write_number.hpp
new file mode 100644
index 0000000..2613467
--- /dev/null
+++ b/include/jsoncons/detail/write_number.hpp
@@ -0,0 +1,567 @@
+// Copyright 2013 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_DETAIL_WRITE_NUMBER_HPP
+#define JSONCONS_DETAIL_WRITE_NUMBER_HPP
+
+#include <stdexcept>
+#include <string>
+#include <cmath>
+#include <locale>
+#include <limits> // std::numeric_limits
+#include <exception>
+#include <stdio.h> // snprintf
+#include <jsoncons/config/jsoncons_config.hpp>
+#include <jsoncons/json_options.hpp>
+#include <jsoncons/detail/grisu3.hpp>
+#include <jsoncons/detail/parse_number.hpp>
+#include <jsoncons/more_type_traits.hpp>
+
+namespace jsoncons {
+namespace detail {
+
+ inline
+ char to_hex_character(uint8_t c)
+ {
+ return (char)((c < 10) ? ('0' + c) : ('A' - 10 + c));
+ }
+
+ // from_integer
+
+ template<class Integer,class Result>
+ typename std::enable_if<type_traits::is_integer<Integer>::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<char_type>(48 - (value % 10));
+ }
+ while ((value /= 10) && (p < last));
+ }
+ else
+ {
+
+ do
+ {
+ *p++ = static_cast<char_type>(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_string_hex
+
+ template<class Integer,class Result>
+ typename std::enable_if<type_traits::is_integer<Integer>::value,std::size_t>::type
+ integer_to_string_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 <class Result>
+ 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::detail::from_integer(K, result);
+ }
+ }
+
+ template <class Result>
+ 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<class Result>
+ 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');
+ needs_dot = true;
+ }
+ }
+ }
+
+ template<class Result>
+ 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;
+ }
+
+ jsoncons::detail::chars_to to_double_;
+
+ char buffer[100];
+ int precision = std::numeric_limits<double>::digits10;
+ int length = snprintf(buffer, sizeof(buffer), "%1.*e", precision, val);
+ if (length < 0)
+ {
+ return false;
+ }
+ if (to_double_(buffer, sizeof(buffer)) != val)
+ {
+ const int precision2 = std::numeric_limits<double>::max_digits10;
+ length = snprintf(buffer, sizeof(buffer), "%1.*e", precision2, val);
+ if (length < 0)
+ {
+ return false;
+ }
+ }
+ dump_buffer(buffer, static_cast<std::size_t>(length), decimal_point, result);
+ return true;
+ }
+
+ template<class Result>
+ 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;
+ }
+
+ jsoncons::detail::chars_to to_double_;
+
+ char buffer[100];
+ int precision = std::numeric_limits<double>::digits10;
+ int length = snprintf(buffer, sizeof(buffer), "%1.*g", precision, val);
+ if (length < 0)
+ {
+ return false;
+ }
+ if (to_double_(buffer, sizeof(buffer)) != val)
+ {
+ const int precision2 = std::numeric_limits<double>::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<class Result>
+ 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<double>::max_digits10
+ jsoncons::detail::prettify_string(buffer, length, k, -4, std::numeric_limits<double>::max_digits10, result);
+ return true;
+ }
+ else
+ {
+ return dtoa_general(v, decimal_point, result, std::false_type());
+ }
+ }
+
+ template<class Result>
+ 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;
+ }
+
+ jsoncons::detail::chars_to to_double_;
+
+ char buffer[100];
+ int precision = std::numeric_limits<double>::digits10;
+ int length = snprintf(buffer, sizeof(buffer), "%1.*f", precision, val);
+ if (length < 0)
+ {
+ return false;
+ }
+ if (to_double_(buffer, sizeof(buffer)) != val)
+ {
+ const int precision2 = std::numeric_limits<double>::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<class Result>
+ 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::detail::prettify_string(buffer, length, k, std::numeric_limits<int>::lowest(), (std::numeric_limits<int>::max)(), result);
+ return true;
+ }
+ else
+ {
+ return dtoa_fixed(v, decimal_point, result, std::false_type());
+ }
+ }
+
+ template<class Result>
+ bool dtoa_fixed(double v, char decimal_point, Result& result)
+ {
+ return dtoa_fixed(v, decimal_point, result, std::integral_constant<bool, std::numeric_limits<double>::is_iec559>());
+ }
+
+ template<class Result>
+ bool dtoa_general(double v, char decimal_point, Result& result)
+ {
+ return dtoa_general(v, decimal_point, result, std::integral_constant<bool, std::numeric_limits<double>::is_iec559>());
+ }
+
+ class write_double
+ {
+ private:
+ chars_to to_double_;
+ 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<class Result>
+ 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<std::invalid_argument>("write_double failed."));
+ }
+ dump_buffer(number_buffer, length, decimal_point_, result);
+ }
+ else
+ {
+ if (!dtoa_fixed(val, decimal_point_, result))
+ {
+ JSONCONS_THROW(json_runtime_error<std::invalid_argument>("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<std::invalid_argument>("write_double failed."));
+ }
+ dump_buffer(number_buffer, length, decimal_point_, result);
+ }
+ else
+ {
+ if (!dtoa_scientific(val, decimal_point_, result))
+ {
+ JSONCONS_THROW(json_runtime_error<std::invalid_argument>("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<std::invalid_argument>("write_double failed."));
+ }
+ dump_buffer(number_buffer, length, decimal_point_, result);
+ }
+ else
+ {
+ if (!dtoa_general(val, decimal_point_, result))
+ {
+ JSONCONS_THROW(json_runtime_error<std::invalid_argument>("write_double failed."));
+ }
+ }
+ break;
+ }
+ default:
+ JSONCONS_THROW(json_runtime_error<std::invalid_argument>("write_double failed."));
+ break;
+ }
+ return count;
+ }
+ };
+
+} // namespace detail
+} // namespace jsoncons
+
+#endif
diff --git a/include/jsoncons/encode_json.hpp b/include/jsoncons/encode_json.hpp
new file mode 100644
index 0000000..706dc44
--- /dev/null
+++ b/include/jsoncons/encode_json.hpp
@@ -0,0 +1,315 @@
+// Copyright 2017 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_ENCODE_JSON_HPP
+#define JSONCONS_ENCODE_JSON_HPP
+
+#include <iostream>
+#include <string>
+#include <tuple>
+#include <memory>
+#include <istream> // std::basic_istream
+#include <jsoncons/encode_traits.hpp>
+#include <jsoncons/json_cursor.hpp>
+
+namespace jsoncons {
+
+ // to string
+
+ template <class T, class Container>
+ typename std::enable_if<type_traits::is_basic_json<T>::value &&
+ type_traits::is_back_insertable_char_container<Container>::value>::type
+ encode_json(const T& val,
+ Container& s,
+ const basic_json_encode_options<typename Container::value_type>& options =
+ basic_json_encode_options<typename Container::value_type>())
+ {
+ using char_type = typename Container::value_type;
+
+ basic_compact_json_encoder<char_type, jsoncons::string_sink<Container>> encoder(s, options);
+ val.dump(encoder);
+ }
+
+ template <class T, class Container>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value &&
+ type_traits::is_back_insertable_char_container<Container>::value>::type
+ encode_json(const T& val,
+ Container& s,
+ const basic_json_encode_options<typename Container::value_type>& options = basic_json_encode_options<typename Container::value_type>())
+ {
+ using char_type = typename Container::value_type;
+
+ basic_compact_json_encoder<char_type, jsoncons::string_sink<Container>> encoder(s, options);
+ encode_json(val, encoder);
+ }
+
+ // to stream
+
+ template <class T, class CharT>
+ typename std::enable_if<type_traits::is_basic_json<T>::value>::type
+ encode_json(const T& val,
+ std::basic_ostream<CharT>& os,
+ const basic_json_encode_options<CharT>& options = basic_json_encode_options<CharT>())
+ {
+ basic_compact_json_encoder<CharT> encoder(os, options);
+ val.dump(encoder);
+ }
+
+ template <class T, class CharT>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value>::type
+ encode_json(const T& val,
+ std::basic_ostream<CharT>& os,
+ const basic_json_encode_options<CharT>& options = basic_json_encode_options<CharT>())
+ {
+ basic_compact_json_encoder<CharT> encoder(os, options);
+ encode_json(val, encoder);
+ }
+
+ // encode_json_pretty
+
+ template <class T, class Container>
+ typename std::enable_if<type_traits::is_basic_json<T>::value &&
+ type_traits::is_back_insertable_char_container<Container>::value>::type
+ encode_json_pretty(const T& val,
+ Container& s,
+ const basic_json_encode_options<typename Container::value_type>& options = basic_json_encode_options<typename Container::value_type>())
+ {
+ using char_type = typename Container::value_type;
+
+ basic_json_encoder<char_type,jsoncons::string_sink<Container>> encoder(s, options);
+ val.dump(encoder);
+ }
+
+ template <class T, class Container>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value &&
+ type_traits::is_back_insertable_char_container<Container>::value>::type
+ encode_json_pretty(const T& val,
+ Container& s,
+ const basic_json_encode_options<typename Container::value_type>& options = basic_json_encode_options<typename Container::value_type>())
+ {
+ using char_type = typename Container::value_type;
+ basic_json_encoder<char_type,jsoncons::string_sink<Container>> encoder(s, options);
+ encode_json(val, encoder);
+ }
+
+ template <class T, class CharT>
+ typename std::enable_if<type_traits::is_basic_json<T>::value>::type
+ encode_json_pretty(const T& val,
+ std::basic_ostream<CharT>& os,
+ const basic_json_encode_options<CharT>& options = basic_json_encode_options<CharT>())
+ {
+ basic_json_encoder<CharT> encoder(os, options);
+ val.dump(encoder);
+ }
+
+ template <class T, class CharT>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value>::type
+ encode_json_pretty(const T& val,
+ std::basic_ostream<CharT>& os,
+ const basic_json_encode_options<CharT>& options = basic_json_encode_options<CharT>())
+ {
+ basic_json_encoder<CharT> encoder(os, options);
+ encode_json(val, encoder);
+ }
+
+ template <class T, class CharT>
+ void encode_json(const T& val, basic_json_visitor<CharT>& encoder)
+ {
+ std::error_code ec;
+ encode_traits<T,CharT>::encode(val, encoder, basic_json<CharT>(), ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec));
+ }
+ encoder.flush();
+ }
+
+ template <class T, class Container, class TempAllocator>
+ void encode_json(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ const T& val,
+ Container& s,
+ indenting line_indent = indenting::no_indent)
+ {
+ encode_json(temp_allocator_arg, temp_alloc, val, s, basic_json_encode_options<typename Container::value_type>(), line_indent);
+ }
+
+// legacy
+
+ template <class T, class Container>
+ void encode_json(const T& val, Container& s, indenting line_indent)
+ {
+ if (line_indent == indenting::indent)
+ {
+ encode_json_pretty(val,s);
+ }
+ else
+ {
+ encode_json(val,s);
+ }
+ }
+
+ template <class T, class Container>
+ void encode_json(const T& val,
+ Container& s,
+ const basic_json_encode_options<typename Container::value_type>& options,
+ indenting line_indent)
+ {
+ if (line_indent == indenting::indent)
+ {
+ encode_json_pretty(val,s,options);
+ }
+ else
+ {
+ encode_json(val,s,options);
+ }
+ }
+
+ template <class T, class CharT>
+ void encode_json(const T& val,
+ std::basic_ostream<CharT>& os,
+ indenting line_indent)
+ {
+ if (line_indent == indenting::indent)
+ {
+ encode_json_pretty(val, os);
+ }
+ else
+ {
+ encode_json(val, os);
+ }
+ }
+
+ template <class T, class CharT>
+ void encode_json(const T& val,
+ std::basic_ostream<CharT>& os,
+ const basic_json_encode_options<CharT>& options,
+ indenting line_indent)
+ {
+ if (line_indent == indenting::indent)
+ {
+ encode_json_pretty(val, os, options);
+ }
+ else
+ {
+ encode_json(val, os, options);
+ }
+ }
+
+//end legacy
+
+ template <class T, class Container, class TempAllocator>
+ typename std::enable_if<type_traits::is_basic_json<T>::value &&
+ type_traits::is_back_insertable_char_container<Container>::value>::type
+ encode_json(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ const T& val,
+ Container& s,
+ const basic_json_encode_options<typename Container::value_type>& options,
+ indenting line_indent = indenting::no_indent)
+ {
+ using char_type = typename Container::value_type;
+ if (line_indent == indenting::indent)
+ {
+ basic_json_encoder<char_type,jsoncons::string_sink<Container>,TempAllocator> encoder(s, options, temp_alloc);
+ val.dump(encoder);
+ }
+ else
+ {
+ basic_compact_json_encoder<char_type, jsoncons::string_sink<Container>,TempAllocator> encoder(s, options, temp_alloc);
+ val.dump(encoder);
+ }
+ }
+
+ template <class T, class Container, class TempAllocator>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value &&
+ type_traits::is_back_insertable_char_container<Container>::value>::type
+ encode_json(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ const T& val,
+ Container& s,
+ const basic_json_encode_options<typename Container::value_type>& options,
+ indenting line_indent)
+ {
+ using char_type = typename Container::value_type;
+ if (line_indent == indenting::indent)
+ {
+ basic_json_encoder<char_type,jsoncons::string_sink<Container>,TempAllocator> encoder(s, options, temp_alloc);
+ encode_json(temp_allocator_arg, temp_alloc, val, encoder);
+ }
+ else
+ {
+ basic_compact_json_encoder<char_type,jsoncons::string_sink<Container>,TempAllocator> encoder(s, options, temp_alloc);
+ encode_json(temp_allocator_arg, temp_alloc, val, encoder);
+ }
+ }
+
+ template <class T, class CharT, class TempAllocator>
+ void encode_json(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ const T& val,
+ std::basic_ostream<CharT>& os,
+ indenting line_indent = indenting::no_indent)
+ {
+ encode_json(temp_allocator_arg, temp_alloc, val, os, basic_json_encode_options<CharT>(), line_indent);
+ }
+
+ template <class T, class CharT, class TempAllocator>
+ typename std::enable_if<type_traits::is_basic_json<T>::value>::type
+ encode_json(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ const T& val,
+ std::basic_ostream<CharT>& os,
+ const basic_json_encode_options<CharT>& options,
+ indenting line_indent = indenting::no_indent)
+ {
+ if (line_indent == indenting::indent)
+ {
+ basic_json_encoder<CharT,jsoncons::stream_sink<CharT>,TempAllocator> encoder(os, options, temp_alloc);
+ val.dump(encoder);
+ }
+ else
+ {
+ basic_compact_json_encoder<CharT,jsoncons::stream_sink<CharT>,TempAllocator> encoder(os, options, temp_alloc);
+ val.dump(encoder);
+ }
+ }
+
+ template <class T, class CharT, class TempAllocator>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value>::type
+ encode_json(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ const T& val,
+ std::basic_ostream<CharT>& os,
+ const basic_json_encode_options<CharT>& options,
+ indenting line_indent)
+ {
+ if (line_indent == indenting::indent)
+ {
+ basic_json_encoder<CharT> encoder(os, options);
+ encode_json(temp_allocator_arg, temp_alloc, val, encoder);
+ }
+ else
+ {
+ basic_compact_json_encoder<CharT> encoder(os, options);
+ encode_json(temp_allocator_arg, temp_alloc, val, encoder);
+ }
+ }
+
+ template <class T, class CharT, class TempAllocator>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value>::type
+ encode_json(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ const T& val,
+ basic_json_visitor<CharT>& encoder)
+ {
+ std::error_code ec;
+ basic_json<CharT,sorted_policy,TempAllocator> proto(temp_alloc);
+ encode_traits<T,CharT>::encode(val, encoder, proto, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec));
+ }
+ encoder.flush();
+ }
+
+} // jsoncons
+
+#endif
+
diff --git a/include/jsoncons/encode_traits.hpp b/include/jsoncons/encode_traits.hpp
new file mode 100644
index 0000000..ae981af
--- /dev/null
+++ b/include/jsoncons/encode_traits.hpp
@@ -0,0 +1,378 @@
+// Copyright 2017 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_ENCODE_TRAITS_HPP
+#define JSONCONS_ENCODE_TRAITS_HPP
+
+#include <string>
+#include <tuple>
+#include <array>
+#include <memory>
+#include <type_traits> // std::enable_if, std::true_type, std::false_type
+#include <jsoncons/json_visitor.hpp>
+#include <jsoncons/json_decoder.hpp>
+#include <jsoncons/json_options.hpp>
+#include <jsoncons/json_encoder.hpp>
+#include <jsoncons/json_type_traits.hpp>
+#include <jsoncons/conv_error.hpp>
+
+namespace jsoncons {
+
+ // encode_traits
+
+ template <class T, class CharT, class Enable = void>
+ struct encode_traits
+ {
+ template <class Json>
+ static void encode(const T& val,
+ basic_json_visitor<CharT>& encoder,
+ const Json& proto,
+ std::error_code& ec)
+ {
+ encode(std::integral_constant<bool, type_traits::is_stateless<typename Json::allocator_type>::value>(),
+ val, encoder, proto, ec);
+ }
+ private:
+ template <class Json>
+ static void encode(std::true_type,
+ const T& val,
+ basic_json_visitor<CharT>& encoder,
+ const Json& /*proto*/,
+ std::error_code& ec)
+ {
+ auto j = json_type_traits<Json,T>::to_json(val);
+ j.dump(encoder, ec);
+ }
+ template <class Json>
+ static void encode(std::false_type,
+ const T& val,
+ basic_json_visitor<CharT>& encoder,
+ const Json& proto,
+ std::error_code& ec)
+ {
+ auto j = json_type_traits<Json,T>::to_json(val, proto.get_allocator());
+ j.dump(encoder, ec);
+ }
+ };
+
+ // specializations
+
+ // bool
+ template <class T, class CharT>
+ struct encode_traits<T,CharT,
+ typename std::enable_if<type_traits::is_bool<T>::value
+ >::type>
+ {
+ template <class Json>
+ static void encode(const T& val,
+ basic_json_visitor<CharT>& encoder,
+ const Json&,
+ std::error_code& ec)
+ {
+ encoder.bool_value(val,semantic_tag::none,ser_context(),ec);
+ }
+ };
+
+ // uint
+ template <class T, class CharT>
+ struct encode_traits<T,CharT,
+ typename std::enable_if<type_traits::is_u8_u16_u32_or_u64<T>::value
+ >::type>
+ {
+ template <class Json>
+ static void encode(const T& val,
+ basic_json_visitor<CharT>& encoder,
+ const Json&,
+ std::error_code& ec)
+ {
+ encoder.uint64_value(val,semantic_tag::none,ser_context(),ec);
+ }
+ };
+
+ // int
+ template <class T, class CharT>
+ struct encode_traits<T,CharT,
+ typename std::enable_if<type_traits::is_i8_i16_i32_or_i64<T>::value
+ >::type>
+ {
+ template <class Json>
+ static void encode(const T& val,
+ basic_json_visitor<CharT>& encoder,
+ const Json&,
+ std::error_code& ec)
+ {
+ encoder.int64_value(val,semantic_tag::none,ser_context(),ec);
+ }
+ };
+
+ // float or double
+ template <class T, class CharT>
+ struct encode_traits<T,CharT,
+ typename std::enable_if<type_traits::is_float_or_double<T>::value
+ >::type>
+ {
+ template <class Json>
+ static void encode(const T& val,
+ basic_json_visitor<CharT>& encoder,
+ const Json&,
+ std::error_code& ec)
+ {
+ encoder.double_value(val,semantic_tag::none,ser_context(),ec);
+ }
+ };
+
+ // string
+ template <class T, class CharT>
+ struct encode_traits<T,CharT,
+ typename std::enable_if<type_traits::is_basic_string<T>::value &&
+ std::is_same<typename T::value_type,CharT>::value
+ >::type>
+ {
+ template <class Json>
+ static void encode(const T& val,
+ basic_json_visitor<CharT>& encoder,
+ const Json&,
+ std::error_code& ec)
+ {
+ encoder.string_value(val,semantic_tag::none,ser_context(),ec);
+ }
+ };
+ template <class T, class CharT>
+ struct encode_traits<T,CharT,
+ typename std::enable_if<type_traits::is_basic_string<T>::value &&
+ !std::is_same<typename T::value_type,CharT>::value
+ >::type>
+ {
+ template <class Json>
+ static void encode(const T& val,
+ basic_json_visitor<CharT>& encoder,
+ const Json&,
+ std::error_code& ec)
+ {
+ std::basic_string<CharT> s;
+ unicode_traits::convert(val.data(), val.size(), s);
+ encoder.string_value(s,semantic_tag::none,ser_context(),ec);
+ }
+ };
+
+ // std::pair
+
+ template <class T1, class T2, class CharT>
+ struct encode_traits<std::pair<T1, T2>, CharT>
+ {
+ using value_type = std::pair<T1, T2>;
+
+ template <class Json>
+ static void encode(const value_type& val,
+ basic_json_visitor<CharT>& encoder,
+ const Json& proto,
+ std::error_code& ec)
+ {
+ encoder.begin_array(2,semantic_tag::none,ser_context(),ec);
+ if (ec) return;
+ encode_traits<T1,CharT>::encode(val.first, encoder, proto, ec);
+ if (ec) return;
+ encode_traits<T2,CharT>::encode(val.second, encoder, proto, ec);
+ if (ec) return;
+ encoder.end_array(ser_context(),ec);
+ }
+ };
+
+ // std::tuple
+
+ namespace detail
+ {
+ template<size_t Pos, std::size_t Size, class Json, class Tuple>
+ struct json_serialize_tuple_helper
+ {
+ using char_type = typename Json::char_type;
+ using element_type = typename std::tuple_element<Size-Pos, Tuple>::type;
+ using next = json_serialize_tuple_helper<Pos-1, Size, Json, Tuple>;
+
+ static void encode(const Tuple& tuple,
+ basic_json_visitor<char_type>& encoder,
+ const Json& proto,
+ std::error_code& ec)
+ {
+ encode_traits<element_type,char_type>::encode(std::get<Size-Pos>(tuple), encoder, proto, ec);
+ if (ec) return;
+ next::encode(tuple, encoder, proto, ec);
+ }
+ };
+
+ template<size_t Size, class Json, class Tuple>
+ struct json_serialize_tuple_helper<0, Size, Json, Tuple>
+ {
+ using char_type = typename Json::char_type;
+ static void encode(const Tuple&,
+ basic_json_visitor<char_type>&,
+ const Json&,
+ std::error_code&)
+ {
+ }
+ };
+ } // namespace detail
+
+
+ template <class CharT, typename... E>
+ struct encode_traits<std::tuple<E...>, CharT>
+ {
+ using value_type = std::tuple<E...>;
+ static constexpr std::size_t size = sizeof...(E);
+
+ template <class Json>
+ static void encode(const value_type& val,
+ basic_json_visitor<CharT>& encoder,
+ const Json& proto,
+ std::error_code& ec)
+ {
+ using helper = jsoncons::detail::json_serialize_tuple_helper<size, size, Json, std::tuple<E...>>;
+ encoder.begin_array(size,semantic_tag::none,ser_context(),ec);
+ if (ec) return;
+ helper::encode(val, encoder, proto, ec);
+ if (ec) return;
+ encoder.end_array(ser_context(),ec);
+ }
+ };
+
+ // vector like
+ template <class T, class CharT>
+ struct encode_traits<T,CharT,
+ typename std::enable_if<!is_json_type_traits_declared<T>::value &&
+ type_traits::is_list_like<T>::value &&
+ !type_traits::is_typed_array<T>::value
+ >::type>
+ {
+ using value_type = typename T::value_type;
+
+ template <class Json>
+ static void encode(const T& val,
+ basic_json_visitor<CharT>& encoder,
+ const Json& proto,
+ std::error_code& ec)
+ {
+ encoder.begin_array(val.size(),semantic_tag::none,ser_context(),ec);
+ if (ec) return;
+ for (auto it = std::begin(val); it != std::end(val); ++it)
+ {
+ encode_traits<value_type,CharT>::encode(*it, encoder, proto, ec);
+ if (ec) return;
+ }
+ encoder.end_array(ser_context(), ec);
+ }
+ };
+
+ template <class T, class CharT>
+ struct encode_traits<T,CharT,
+ typename std::enable_if<!is_json_type_traits_declared<T>::value &&
+ type_traits::is_list_like<T>::value &&
+ type_traits::is_typed_array<T>::value
+ >::type>
+ {
+ using value_type = typename T::value_type;
+
+ template <class Json>
+ static void encode(const T& val,
+ basic_json_visitor<CharT>& encoder,
+ const Json&,
+ std::error_code& ec)
+ {
+ encoder.typed_array(jsoncons::span<const value_type>(val), semantic_tag::none, ser_context(), ec);
+ }
+ };
+
+ // std::array
+
+ template <class T, class CharT, std::size_t N>
+ struct encode_traits<std::array<T,N>,CharT>
+ {
+ using value_type = typename std::array<T,N>::value_type;
+
+ template <class Json>
+ static void encode(const std::array<T, N>& val,
+ basic_json_visitor<CharT>& encoder,
+ const Json& proto,
+ std::error_code& ec)
+ {
+ encoder.begin_array(val.size(),semantic_tag::none,ser_context(),ec);
+ if (ec) return;
+ for (auto it = std::begin(val); it != std::end(val); ++it)
+ {
+ encode_traits<value_type,CharT>::encode(*it, encoder, proto, ec);
+ if (ec) return;
+ }
+ encoder.end_array(ser_context(),ec);
+ }
+ };
+
+ // map like
+
+ template <class T, class CharT>
+ struct encode_traits<T,CharT,
+ typename std::enable_if<!is_json_type_traits_declared<T>::value &&
+ type_traits::is_map_like<T>::value &&
+ type_traits::is_constructible_from_const_pointer_and_size<typename T::key_type>::value
+ >::type>
+ {
+ using mapped_type = typename T::mapped_type;
+ using value_type = typename T::value_type;
+ using key_type = typename T::key_type;
+
+ template <class Json>
+ static void encode(const T& val,
+ basic_json_visitor<CharT>& encoder,
+ const Json& proto,
+ std::error_code& ec)
+ {
+ encoder.begin_object(val.size(), semantic_tag::none, ser_context(), ec);
+ if (ec) return;
+ for (auto it = std::begin(val); it != std::end(val); ++it)
+ {
+ encoder.key(it->first);
+ encode_traits<mapped_type,CharT>::encode(it->second, encoder, proto, ec);
+ if (ec) return;
+ }
+ encoder.end_object(ser_context(), ec);
+ if (ec) return;
+ }
+ };
+
+ template <class T, class CharT>
+ struct encode_traits<T,CharT,
+ typename std::enable_if<!is_json_type_traits_declared<T>::value &&
+ type_traits::is_map_like<T>::value &&
+ std::is_integral<typename T::key_type>::value
+ >::type>
+ {
+ using mapped_type = typename T::mapped_type;
+ using value_type = typename T::value_type;
+ using key_type = typename T::key_type;
+
+ template <class Json>
+ static void encode(const T& val,
+ basic_json_visitor<CharT>& encoder,
+ const Json& proto,
+ std::error_code& ec)
+ {
+ encoder.begin_object(val.size(), semantic_tag::none, ser_context(), ec);
+ if (ec) return;
+ for (auto it = std::begin(val); it != std::end(val); ++it)
+ {
+ std::basic_string<typename Json::char_type> s;
+ jsoncons::detail::from_integer(it->first,s);
+ encoder.key(s);
+ encode_traits<mapped_type,CharT>::encode(it->second, encoder, proto, ec);
+ if (ec) return;
+ }
+ encoder.end_object(ser_context(), ec);
+ if (ec) return;
+ }
+ };
+
+} // jsoncons
+
+#endif
+
diff --git a/include/jsoncons/json.hpp b/include/jsoncons/json.hpp
new file mode 100644
index 0000000..8362e77
--- /dev/null
+++ b/include/jsoncons/json.hpp
@@ -0,0 +1,18 @@
+// 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)
+
+// See https://github.com/danielaparker/jsoncons for latest version
+
+#ifndef JSONCONS_JSON_HPP
+#define JSONCONS_JSON_HPP
+
+#include <jsoncons/basic_json.hpp>
+#include <jsoncons/encode_json.hpp>
+#include <jsoncons/decode_json.hpp>
+#include <jsoncons/json_traits_macros.hpp>
+#include <jsoncons/json_traits_macros_deprecated.hpp>
+#include <jsoncons/staj_iterator.hpp>
+
+#endif
+
diff --git a/include/jsoncons/json_array.hpp b/include/jsoncons/json_array.hpp
new file mode 100644
index 0000000..5877f4d
--- /dev/null
+++ b/include/jsoncons/json_array.hpp
@@ -0,0 +1,324 @@
+// Copyright 2013 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_ARRAY_HPP
+#define JSONCONS_JSON_ARRAY_HPP
+
+#include <string>
+#include <vector>
+#include <exception>
+#include <cstring>
+#include <algorithm> // std::sort, std::stable_sort, std::lower_bound, std::unique
+#include <utility>
+#include <initializer_list>
+#include <iterator> // std::iterator_traits
+#include <memory> // std::allocator
+#include <utility> // std::move
+#include <cassert> // assert
+#include <type_traits> // std::enable_if
+#include <jsoncons/json_exception.hpp>
+#include <jsoncons/allocator_holder.hpp>
+
+namespace jsoncons {
+
+ // json_array
+
+ template <class Json,template<typename,typename> class SequenceContainer = std::vector>
+ class json_array : public allocator_holder<typename Json::allocator_type>
+ {
+ public:
+ using allocator_type = typename Json::allocator_type;
+ using value_type = Json;
+ private:
+ using value_allocator_type = typename std::allocator_traits<allocator_type>:: template rebind_alloc<value_type>;
+ using value_container_type = SequenceContainer<value_type,value_allocator_type>;
+ value_container_type elements_;
+ public:
+ using iterator = typename value_container_type::iterator;
+ using const_iterator = typename value_container_type::const_iterator;
+ using reference = typename std::iterator_traits<iterator>::reference;
+ using const_reference = typename std::iterator_traits<const_iterator>::reference;
+
+ using allocator_holder<allocator_type>::get_allocator;
+
+ json_array()
+ {
+ }
+
+ explicit json_array(const allocator_type& alloc)
+ : allocator_holder<allocator_type>(alloc),
+ elements_(value_allocator_type(alloc))
+ {
+ }
+
+ explicit json_array(std::size_t n,
+ const allocator_type& alloc = allocator_type())
+ : allocator_holder<allocator_type>(alloc),
+ elements_(n,Json(),value_allocator_type(alloc))
+ {
+ }
+
+ explicit json_array(std::size_t n,
+ const Json& value,
+ const allocator_type& alloc = allocator_type())
+ : allocator_holder<allocator_type>(alloc),
+ elements_(n,value,value_allocator_type(alloc))
+ {
+ }
+
+ template <class InputIterator>
+ json_array(InputIterator begin, InputIterator end, const allocator_type& alloc = allocator_type())
+ : allocator_holder<allocator_type>(alloc),
+ elements_(begin,end,value_allocator_type(alloc))
+ {
+ }
+ json_array(const json_array& val)
+ : allocator_holder<allocator_type>(val.get_allocator()),
+ elements_(val.elements_)
+ {
+ }
+ json_array(const json_array& val, const allocator_type& alloc)
+ : allocator_holder<allocator_type>(alloc),
+ elements_(val.elements_,value_allocator_type(alloc))
+ {
+ }
+
+ json_array(json_array&& val) noexcept
+ : allocator_holder<allocator_type>(val.get_allocator()),
+ elements_(std::move(val.elements_))
+ {
+ }
+ json_array(json_array&& val, const allocator_type& alloc)
+ : allocator_holder<allocator_type>(alloc),
+ elements_(std::move(val.elements_),value_allocator_type(alloc))
+ {
+ }
+
+ json_array(const std::initializer_list<Json>& init,
+ const allocator_type& alloc = allocator_type())
+ : allocator_holder<allocator_type>(alloc),
+ elements_(init,value_allocator_type(alloc))
+ {
+ }
+ ~json_array() noexcept
+ {
+ flatten_and_destroy();
+ }
+
+ reference back()
+ {
+ return elements_.back();
+ }
+
+ const_reference back() const
+ {
+ return elements_.back();
+ }
+
+ void pop_back()
+ {
+ elements_.pop_back();
+ }
+
+ bool empty() const
+ {
+ return elements_.empty();
+ }
+
+ void swap(json_array<Json>& val) noexcept
+ {
+ elements_.swap(val.elements_);
+ }
+
+ std::size_t size() const {return elements_.size();}
+
+ std::size_t capacity() const {return elements_.capacity();}
+
+ void clear() {elements_.clear();}
+
+ void shrink_to_fit()
+ {
+ for (std::size_t i = 0; i < elements_.size(); ++i)
+ {
+ elements_[i].shrink_to_fit();
+ }
+ elements_.shrink_to_fit();
+ }
+
+ void reserve(std::size_t n) {elements_.reserve(n);}
+
+ void resize(std::size_t n) {elements_.resize(n);}
+
+ void resize(std::size_t n, const Json& val) {elements_.resize(n,val);}
+
+ #if !defined(JSONCONS_NO_DEPRECATED)
+ JSONCONS_DEPRECATED_MSG("Instead, use erase(const_iterator, const_iterator)")
+ void remove_range(std::size_t from_index, std::size_t to_index)
+ {
+ JSONCONS_ASSERT(from_index <= to_index);
+ JSONCONS_ASSERT(to_index <= elements_.size());
+ elements_.erase(elements_.cbegin()+from_index,elements_.cbegin()+to_index);
+ }
+ #endif
+
+ iterator erase(const_iterator pos)
+ {
+ #if defined(JSONCONS_NO_VECTOR_ERASE_TAKES_CONST_ITERATOR)
+ iterator it = elements_.begin() + (pos - elements_.begin());
+ return elements_.erase(it);
+ #else
+ return elements_.erase(pos);
+ #endif
+ }
+
+ iterator erase(const_iterator first, const_iterator last)
+ {
+ #if defined(JSONCONS_NO_VECTOR_ERASE_TAKES_CONST_ITERATOR)
+ iterator it1 = elements_.begin() + (first - elements_.begin());
+ iterator it2 = elements_.begin() + (last - elements_.begin());
+ return elements_.erase(it1,it2);
+ #else
+ return elements_.erase(first,last);
+ #endif
+ }
+
+ Json& operator[](std::size_t i) {return elements_[i];}
+
+ const Json& operator[](std::size_t i) const {return elements_[i];}
+
+ // push_back
+
+ template <class T, class A=allocator_type>
+ typename std::enable_if<type_traits::is_stateless<A>::value,void>::type
+ push_back(T&& value)
+ {
+ elements_.emplace_back(std::forward<T>(value));
+ }
+
+ template <class T, class A=allocator_type>
+ typename std::enable_if<!type_traits::is_stateless<A>::value,void>::type
+ push_back(T&& value)
+ {
+ elements_.emplace_back(std::forward<T>(value),get_allocator());
+ }
+
+ template <class T, class A=allocator_type>
+ typename std::enable_if<type_traits::is_stateless<A>::value,iterator>::type
+ insert(const_iterator pos, T&& value)
+ {
+ #if defined(JSONCONS_NO_VECTOR_ERASE_TAKES_CONST_ITERATOR)
+ iterator it = elements_.begin() + (pos - elements_.begin());
+ return elements_.emplace(it, std::forward<T>(value));
+ #else
+ return elements_.emplace(pos, std::forward<T>(value));
+ #endif
+ }
+ template <class T, class A=allocator_type>
+ typename std::enable_if<!type_traits::is_stateless<A>::value,iterator>::type
+ insert(const_iterator pos, T&& value)
+ {
+ #if defined(JSONCONS_NO_VECTOR_ERASE_TAKES_CONST_ITERATOR)
+ iterator it = elements_.begin() + (pos - elements_.begin());
+ return elements_.emplace(it, std::forward<T>(value), get_allocator());
+ #else
+ return elements_.emplace(pos, std::forward<T>(value), get_allocator());
+ #endif
+ }
+
+ template <class InputIt>
+ iterator insert(const_iterator pos, InputIt first, InputIt last)
+ {
+ #if defined(JSONCONS_NO_VECTOR_ERASE_TAKES_CONST_ITERATOR)
+ iterator it = elements_.begin() + (pos - elements_.begin());
+ elements_.insert(it, first, last);
+ return first == last ? it : it + 1;
+ #else
+ return elements_.insert(pos, first, last);
+ #endif
+ }
+
+ template <class A=allocator_type, class... Args>
+ typename std::enable_if<type_traits::is_stateless<A>::value,iterator>::type
+ emplace(const_iterator pos, Args&&... args)
+ {
+ #if defined(JSONCONS_NO_VECTOR_ERASE_TAKES_CONST_ITERATOR)
+ iterator it = elements_.begin() + (pos - elements_.begin());
+ return elements_.emplace(it, std::forward<Args>(args)...);
+ #else
+ return elements_.emplace(pos, std::forward<Args>(args)...);
+ #endif
+ }
+
+ template <class... Args>
+ Json& emplace_back(Args&&... args)
+ {
+ elements_.emplace_back(std::forward<Args>(args)...);
+ return elements_.back();
+ }
+
+ iterator begin() {return elements_.begin();}
+
+ iterator end() {return elements_.end();}
+
+ const_iterator begin() const {return elements_.begin();}
+
+ const_iterator end() const {return elements_.end();}
+
+ bool operator==(const json_array<Json>& rhs) const noexcept
+ {
+ return elements_ == rhs.elements_;
+ }
+
+ bool operator<(const json_array<Json>& rhs) const noexcept
+ {
+ return elements_ < rhs.elements_;
+ }
+ private:
+
+ json_array& operator=(const json_array<Json>&) = delete;
+
+ void flatten_and_destroy() noexcept
+ {
+ while (!elements_.empty())
+ {
+ value_type current = std::move(elements_.back());
+ elements_.pop_back();
+ switch (current.storage_kind())
+ {
+ case json_storage_kind::array_value:
+ {
+ for (auto&& item : current.array_range())
+ {
+ if (item.size() > 0) // non-empty object or array
+ {
+ elements_.push_back(std::move(item));
+ }
+ }
+ current.clear();
+ break;
+ }
+ case json_storage_kind::object_value:
+ {
+ for (auto&& kv : current.object_range())
+ {
+ if (kv.value().size() > 0) // non-empty object or array
+ {
+ elements_.push_back(std::move(kv.value()));
+ }
+ }
+ current.clear();
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ }
+ };
+
+} // namespace jsoncons
+
+#endif
diff --git a/include/jsoncons/json_content_handler.hpp b/include/jsoncons/json_content_handler.hpp
new file mode 100644
index 0000000..edec19e
--- /dev/null
+++ b/include/jsoncons/json_content_handler.hpp
@@ -0,0 +1,12 @@
+// 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)
+
+// See https://github.com/danielaparker/jsoncons for latest version
+
+#ifndef JSONCONS_JSON_CONTENT_HANDLER_HPP
+#define JSONCONS_JSON_CONTENT_HANDLER_HPP
+
+#include <jsoncons/json_visitor.hpp>
+
+#endif
diff --git a/include/jsoncons/json_cursor.hpp b/include/jsoncons/json_cursor.hpp
new file mode 100644
index 0000000..a30c351
--- /dev/null
+++ b/include/jsoncons/json_cursor.hpp
@@ -0,0 +1,448 @@
+// 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)
+
+// See https://github.com/danielaparker/jsoncons for latest version
+
+#ifndef JSONCONS_JSON_CURSOR_HPP
+#define JSONCONS_JSON_CURSOR_HPP
+
+#include <memory> // std::allocator
+#include <string>
+#include <vector>
+#include <stdexcept>
+#include <system_error>
+#include <ios>
+#include <istream> // std::basic_istream
+#include <jsoncons/byte_string.hpp>
+#include <jsoncons/config/jsoncons_config.hpp>
+#include <jsoncons/json_visitor.hpp>
+#include <jsoncons/json_exception.hpp>
+#include <jsoncons/json_parser.hpp>
+#include <jsoncons/staj_cursor.hpp>
+#include <jsoncons/source.hpp>
+#include <jsoncons/source_adaptor.hpp>
+
+namespace jsoncons {
+
+template<class CharT,class Source=jsoncons::stream_source<CharT>,class Allocator=std::allocator<char>>
+class basic_json_cursor : public basic_staj_cursor<CharT>, private virtual ser_context
+{
+public:
+ using source_type = Source;
+ using char_type = CharT;
+ using allocator_type = Allocator;
+ using string_view_type = jsoncons::basic_string_view<CharT>;
+private:
+ typedef typename std::allocator_traits<allocator_type>:: template rebind_alloc<CharT> char_allocator_type;
+ static constexpr size_t default_max_buffer_size = 16384;
+
+ json_source_adaptor<Source> source_;
+ basic_json_parser<CharT,Allocator> parser_;
+ basic_staj_visitor<CharT> cursor_visitor_;
+ bool done_;
+
+ // Noncopyable and nonmoveable
+ basic_json_cursor(const basic_json_cursor&) = delete;
+ basic_json_cursor& operator=(const basic_json_cursor&) = delete;
+
+public:
+
+ // Constructors that throw parse exceptions
+
+ template <class Sourceable>
+ basic_json_cursor(Sourceable&& source,
+ const basic_json_decode_options<CharT>& options = basic_json_decode_options<CharT>(),
+ std::function<bool(json_errc,const ser_context&)> err_handler = default_json_parsing(),
+ const Allocator& alloc = Allocator(),
+ typename std::enable_if<!std::is_constructible<jsoncons::basic_string_view<CharT>,Sourceable>::value>::type* = 0)
+ : source_(std::forward<Sourceable>(source)),
+ parser_(options,err_handler,alloc),
+ cursor_visitor_(accept_all),
+ done_(false)
+ {
+ if (!done())
+ {
+ next();
+ }
+ }
+
+ template <class Sourceable>
+ basic_json_cursor(Sourceable&& source,
+ const basic_json_decode_options<CharT>& options = basic_json_decode_options<CharT>(),
+ std::function<bool(json_errc,const ser_context&)> err_handler = default_json_parsing(),
+ const Allocator& alloc = Allocator(),
+ typename std::enable_if<std::is_constructible<jsoncons::basic_string_view<CharT>,Sourceable>::value>::type* = 0)
+ : source_(),
+ parser_(options, err_handler, alloc),
+ cursor_visitor_(accept_all),
+ done_(false)
+ {
+ initialize_with_string_view(std::forward<Sourceable>(source));
+ }
+
+
+ // Constructors that set parse error codes
+ template <class Sourceable>
+ basic_json_cursor(Sourceable&& source,
+ std::error_code& ec)
+ : basic_json_cursor(std::allocator_arg, Allocator(),
+ std::forward<Sourceable>(source),
+ basic_json_decode_options<CharT>(),
+ default_json_parsing(),
+ ec)
+ {
+ }
+
+ template <class Sourceable>
+ basic_json_cursor(Sourceable&& source,
+ const basic_json_decode_options<CharT>& options,
+ std::error_code& ec)
+ : basic_json_cursor(std::allocator_arg, Allocator(),
+ std::forward<Sourceable>(source),
+ options,
+ default_json_parsing(),
+ ec)
+ {
+ }
+
+ template <class Sourceable>
+ basic_json_cursor(Sourceable&& source,
+ const basic_json_decode_options<CharT>& options,
+ std::function<bool(json_errc,const ser_context&)> err_handler,
+ std::error_code& ec)
+ : basic_json_cursor(std::allocator_arg, Allocator(),
+ std::forward<Sourceable>(source),
+ options,
+ err_handler,
+ ec)
+ {
+ }
+
+ template <class Sourceable>
+ basic_json_cursor(std::allocator_arg_t, const Allocator& alloc,
+ Sourceable&& source,
+ const basic_json_decode_options<CharT>& options,
+ std::function<bool(json_errc,const ser_context&)> err_handler,
+ std::error_code& ec,
+ typename std::enable_if<!std::is_constructible<jsoncons::basic_string_view<CharT>,Sourceable>::value>::type* = 0)
+ : source_(std::forward<Sourceable>(source)),
+ parser_(options,err_handler,alloc),
+ cursor_visitor_(accept_all),
+ done_(false)
+ {
+ if (!done())
+ {
+ next(ec);
+ }
+ }
+
+ template <class Sourceable>
+ basic_json_cursor(std::allocator_arg_t, const Allocator& alloc,
+ Sourceable&& source,
+ const basic_json_decode_options<CharT>& options,
+ std::function<bool(json_errc,const ser_context&)> err_handler,
+ std::error_code& ec,
+ typename std::enable_if<std::is_constructible<jsoncons::basic_string_view<CharT>,Sourceable>::value>::type* = 0)
+ : source_(),
+ parser_(options, err_handler, alloc),
+ cursor_visitor_(accept_all),
+ done_(false)
+ {
+ initialize_with_string_view(std::forward<Sourceable>(source), ec);
+ }
+
+ void reset()
+ {
+ parser_.reset();
+ cursor_visitor_.reset();
+ done_ = false;
+ if (!done())
+ {
+ next();
+ }
+ }
+
+ template <class Sourceable>
+ typename std::enable_if<!std::is_constructible<jsoncons::basic_string_view<CharT>,Sourceable>::value>::type
+ reset(Sourceable&& source)
+ {
+ source_ = std::forward<Sourceable>(source);
+ parser_.reinitialize();
+ cursor_visitor_.reset();
+ done_ = false;
+ if (!done())
+ {
+ next();
+ }
+ }
+
+ template <class Sourceable>
+ typename std::enable_if<std::is_constructible<jsoncons::basic_string_view<CharT>,Sourceable>::value>::type
+ reset(Sourceable&& source)
+ {
+ source_ = {};
+ parser_.reinitialize();
+ cursor_visitor_.reset();
+ done_ = false;
+ initialize_with_string_view(std::forward<Sourceable>(source));
+ }
+
+ void reset(std::error_code& ec)
+ {
+ parser_.reset();
+ cursor_visitor_.reset();
+ done_ = false;
+ if (!done())
+ {
+ next(ec);
+ }
+ }
+
+ template <class Sourceable>
+ typename std::enable_if<!std::is_constructible<jsoncons::basic_string_view<CharT>,Sourceable>::value>::type
+ reset(Sourceable&& source, std::error_code& ec)
+ {
+ source_ = std::forward<Sourceable>(source);
+ parser_.reinitialize();
+ cursor_visitor_.reset();
+ done_ = false;
+ if (!done())
+ {
+ next(ec);
+ }
+ }
+
+ template <class Sourceable>
+ typename std::enable_if<std::is_constructible<jsoncons::basic_string_view<CharT>,Sourceable>::value>::type
+ reset(Sourceable&& source, std::error_code& ec)
+ {
+ source_ = {};
+ parser_.reinitialize();
+ done_ = false;
+ initialize_with_string_view(std::forward<Sourceable>(source), ec);
+ }
+
+ bool done() const override
+ {
+ return parser_.done() || done_;
+ }
+
+ const basic_staj_event<CharT>& current() const override
+ {
+ return cursor_visitor_.event();
+ }
+
+ void read_to(basic_json_visitor<CharT>& visitor) override
+ {
+ std::error_code ec;
+ read_to(visitor, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column()));
+ }
+ }
+
+ void read_to(basic_json_visitor<CharT>& visitor,
+ std::error_code& ec) override
+ {
+ if (staj_to_saj_event(cursor_visitor_.event(), visitor, *this, ec))
+ {
+ read_next(visitor, ec);
+ }
+ }
+
+ void next() override
+ {
+ std::error_code ec;
+ next(ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column()));
+ }
+ }
+
+ void next(std::error_code& ec) override
+ {
+ read_next(ec);
+ }
+
+ void check_done()
+ {
+ std::error_code ec;
+ check_done(ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column()));
+ }
+ }
+
+ const ser_context& context() const override
+ {
+ return *this;
+ }
+
+ void check_done(std::error_code& ec)
+ {
+ if (source_.is_error())
+ {
+ ec = json_errc::source_error;
+ return;
+ }
+ if (source_.eof())
+ {
+ parser_.check_done(ec);
+ if (ec) return;
+ }
+ else
+ {
+ do
+ {
+ if (parser_.source_exhausted())
+ {
+ auto s = source_.read_buffer(ec);
+ if (ec) return;
+ if (s.size() > 0)
+ {
+ parser_.update(s.data(),s.size());
+ }
+ }
+ if (!parser_.source_exhausted())
+ {
+ parser_.check_done(ec);
+ if (ec) return;
+ }
+ }
+ while (!eof());
+ }
+ }
+
+ bool eof() const
+ {
+ return parser_.source_exhausted() && source_.eof();
+ }
+
+ std::size_t line() const override
+ {
+ return parser_.line();
+ }
+
+ std::size_t column() const override
+ {
+ return parser_.column();
+ }
+
+ friend
+ basic_staj_filter_view<CharT> operator|(basic_json_cursor& cursor,
+ std::function<bool(const basic_staj_event<CharT>&, const ser_context&)> pred)
+ {
+ return basic_staj_filter_view<CharT>(cursor, pred);
+ }
+
+private:
+
+ static bool accept_all(const basic_staj_event<CharT>&, const ser_context&)
+ {
+ return true;
+ }
+
+ void initialize_with_string_view(string_view_type sv)
+ {
+ auto r = unicode_traits::detect_json_encoding(sv.data(), sv.size());
+ if (!(r.encoding == unicode_traits::encoding_kind::utf8 || r.encoding == unicode_traits::encoding_kind::undetected))
+ {
+ JSONCONS_THROW(ser_error(json_errc::illegal_unicode_character,parser_.line(),parser_.column()));
+ }
+ std::size_t offset = (r.ptr - sv.data());
+ parser_.update(sv.data()+offset,sv.size()-offset);
+ if (!done())
+ {
+ next();
+ }
+ }
+
+ void initialize_with_string_view(string_view_type sv, std::error_code& ec)
+ {
+ auto r = unicode_traits::detect_json_encoding(sv.data(), sv.size());
+ if (!(r.encoding == unicode_traits::encoding_kind::utf8 || r.encoding == unicode_traits::encoding_kind::undetected))
+ {
+ ec = json_errc::illegal_unicode_character;
+ return;
+ }
+ std::size_t offset = (r.ptr - sv.data());
+ parser_.update(sv.data()+offset,sv.size()-offset);
+ if (!done())
+ {
+ next(ec);
+ }
+ }
+
+ void read_next(std::error_code& ec)
+ {
+ read_next(cursor_visitor_, ec);
+ }
+
+ void read_next(basic_json_visitor<CharT>& visitor, std::error_code& ec)
+ {
+ parser_.restart();
+ while (!parser_.stopped())
+ {
+ if (parser_.source_exhausted())
+ {
+ auto s = source_.read_buffer(ec);
+ if (ec) return;
+ if (s.size() > 0)
+ {
+ parser_.update(s.data(),s.size());
+ if (ec) return;
+ }
+ }
+ bool eof = parser_.source_exhausted() && source_.eof();
+ parser_.parse_some(visitor, ec);
+ if (ec) return;
+ if (eof)
+ {
+ if (parser_.enter())
+ {
+ done_ = true;
+ break;
+ }
+ else if (!parser_.accept())
+ {
+ ec = json_errc::unexpected_eof;
+ return;
+ }
+ }
+ }
+ }
+};
+
+using json_stream_cursor = basic_json_cursor<char,jsoncons::stream_source<char>>;
+using json_string_cursor = basic_json_cursor<char,jsoncons::string_source<char>>;
+using wjson_stream_cursor = basic_json_cursor<wchar_t,jsoncons::stream_source<wchar_t>>;
+using wjson_string_cursor = basic_json_cursor<wchar_t,jsoncons::string_source<wchar_t>>;
+
+using json_cursor = basic_json_cursor<char>;
+using wjson_cursor = basic_json_cursor<wchar_t>;
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+template<class CharT,class Source,class Allocator=std::allocator<CharT>>
+using basic_json_pull_reader = basic_json_cursor<CharT,Source,Allocator>;
+
+JSONCONS_DEPRECATED_MSG("Instead, use json_cursor") typedef json_cursor json_pull_reader;
+JSONCONS_DEPRECATED_MSG("Instead, use wjson_cursor") typedef wjson_cursor wjson_pull_reader;
+
+template<class CharT,class Source,class Allocator=std::allocator<CharT>>
+using basic_json_stream_reader = basic_json_cursor<CharT,Source,Allocator>;
+
+template<class CharT,class Source,class Allocator=std::allocator<CharT>>
+using basic_json_staj_cursor = basic_json_cursor<CharT,Source,Allocator>;
+
+JSONCONS_DEPRECATED_MSG("Instead, use json_cursor") typedef json_cursor json_staj_cursor;
+JSONCONS_DEPRECATED_MSG("Instead, use wjson_cursor") typedef wjson_cursor wjson_staj_cursor;
+#endif
+
+}
+
+#endif
+
diff --git a/include/jsoncons/json_decoder.hpp b/include/jsoncons/json_decoder.hpp
new file mode 100644
index 0000000..c5aa0b2
--- /dev/null
+++ b/include/jsoncons/json_decoder.hpp
@@ -0,0 +1,420 @@
+// Copyright 2013-2016 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_DECODER_HPP
+#define JSONCONS_JSON_DECODER_HPP
+
+#include <string>
+#include <vector>
+#include <type_traits> // std::true_type
+#include <memory> // std::allocator
+#include <iterator> // std::make_move_iterator
+#include <utility> // std::move
+#include <jsoncons/json_exception.hpp>
+#include <jsoncons/json_visitor.hpp>
+
+namespace jsoncons {
+
+template <class Json,class TempAllocator=std::allocator<char>>
+class json_decoder final : public basic_json_visitor<typename Json::char_type>
+{
+public:
+ using char_type = typename Json::char_type;
+ using typename basic_json_visitor<char_type>::string_view_type;
+
+ using key_value_type = typename Json::key_value_type;
+ using key_type = typename Json::key_type;
+ using array = typename Json::array;
+ using object = typename Json::object;
+ using result_allocator_type = typename Json::allocator_type;
+ using json_string_allocator = typename key_type::allocator_type;
+ using json_array_allocator = typename array::allocator_type;
+ using json_object_allocator = typename object::allocator_type;
+ typedef typename std::allocator_traits<result_allocator_type>:: template rebind_alloc<uint8_t> json_byte_allocator_type;
+private:
+ struct stack_item
+ {
+ key_type name_;
+ Json value_;
+
+ template <class... Args>
+ stack_item(key_type&& name, Args&& ... args)
+ : name_(std::move(name)), value_(std::forward<Args>(args)...)
+ {
+ }
+
+ stack_item() = default;
+ stack_item(const stack_item&) = default;
+ stack_item(stack_item&&) = default;
+ stack_item& operator=(const stack_item&) = default;
+ stack_item& operator=(stack_item&&) = default;
+ };
+
+ enum class structure_type {root_t, array_t, object_t};
+
+ struct structure_info
+ {
+ structure_type type_;
+ std::size_t container_index_;
+
+ structure_info(structure_type type, std::size_t offset) noexcept
+ : type_(type), container_index_(offset)
+ {
+ }
+
+ };
+
+ using temp_allocator_type = TempAllocator;
+ typedef typename std::allocator_traits<temp_allocator_type>:: template rebind_alloc<stack_item> stack_item_allocator_type;
+ typedef typename std::allocator_traits<temp_allocator_type>:: template rebind_alloc<structure_info> structure_info_allocator_type;
+
+ result_allocator_type result_allocator_;
+ temp_allocator_type temp_allocator_;
+
+ Json result_;
+
+ key_type name_;
+ std::vector<stack_item,stack_item_allocator_type> item_stack_;
+ std::vector<structure_info,structure_info_allocator_type> structure_stack_;
+ bool is_valid_;
+
+public:
+ json_decoder(const temp_allocator_type& temp_alloc = temp_allocator_type())
+ : result_allocator_(result_allocator_type()),
+ temp_allocator_(temp_alloc),
+ result_(),
+ name_(result_allocator_),
+ item_stack_(temp_allocator_),
+ structure_stack_(temp_allocator_),
+ is_valid_(false)
+ {
+ item_stack_.reserve(1000);
+ structure_stack_.reserve(100);
+ structure_stack_.emplace_back(structure_type::root_t, 0);
+ }
+
+ json_decoder(result_allocator_arg_t,
+ const result_allocator_type& result_alloc)
+ : result_allocator_(result_alloc),
+ temp_allocator_(),
+ result_(),
+ name_(result_allocator_),
+ item_stack_(),
+ structure_stack_(),
+ is_valid_(false)
+ {
+ item_stack_.reserve(1000);
+ structure_stack_.reserve(100);
+ structure_stack_.emplace_back(structure_type::root_t, 0);
+ }
+
+ json_decoder(result_allocator_arg_t,
+ const result_allocator_type& result_alloc,
+ const temp_allocator_type& temp_alloc)
+ : result_allocator_(result_alloc),
+ temp_allocator_(temp_alloc),
+ result_(),
+ name_(result_allocator_),
+ item_stack_(temp_allocator_),
+ structure_stack_(temp_allocator_),
+ is_valid_(false)
+ {
+ item_stack_.reserve(1000);
+ structure_stack_.reserve(100);
+ structure_stack_.emplace_back(structure_type::root_t, 0);
+ }
+
+ void reset()
+ {
+ is_valid_ = false;
+ item_stack_.clear();
+ structure_stack_.clear();
+ structure_stack_.emplace_back(structure_type::root_t, 0);
+ }
+
+ bool is_valid() const
+ {
+ return is_valid_;
+ }
+
+ Json get_result()
+ {
+ JSONCONS_ASSERT(is_valid_);
+ is_valid_ = false;
+ return std::move(result_);
+ }
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+ JSONCONS_DEPRECATED_MSG("Instead, use get_result()")
+ Json& root()
+ {
+ return result_;
+ }
+#endif
+
+private:
+
+ void visit_flush() override
+ {
+ }
+
+ bool visit_begin_object(semantic_tag tag, const ser_context&, std::error_code&) override
+ {
+ if (structure_stack_.back().type_ == structure_type::root_t)
+ {
+ item_stack_.clear();
+ is_valid_ = false;
+ }
+ item_stack_.emplace_back(std::forward<key_type>(name_), json_object_arg, tag, result_allocator_);
+ structure_stack_.emplace_back(structure_type::object_t, item_stack_.size()-1);
+ return true;
+ }
+
+ bool visit_end_object(const ser_context&, std::error_code&) override
+ {
+ JSONCONS_ASSERT(structure_stack_.size() > 0);
+ JSONCONS_ASSERT(structure_stack_.back().type_ == structure_type::object_t);
+ const size_t structure_index = structure_stack_.back().container_index_;
+ JSONCONS_ASSERT(item_stack_.size() > structure_index);
+ const size_t count = item_stack_.size() - (structure_index + 1);
+ auto first = item_stack_.begin() + (structure_index+1);
+ auto last = first + count;
+ item_stack_[structure_index].value_.object_value().insert(
+ std::make_move_iterator(first),
+ std::make_move_iterator(last),
+ [](stack_item&& val){return key_value_type(std::move(val.name_), std::move(val.value_));}
+ );
+ item_stack_.erase(item_stack_.begin()+structure_index+1, item_stack_.end());
+ structure_stack_.pop_back();
+ if (structure_stack_.back().type_ == structure_type::root_t)
+ {
+ result_.swap(item_stack_.front().value_);
+ item_stack_.pop_back();
+ is_valid_ = true;
+ return false;
+ }
+ return true;
+ }
+
+ bool visit_begin_array(semantic_tag tag, const ser_context&, std::error_code&) override
+ {
+ if (structure_stack_.back().type_ == structure_type::root_t)
+ {
+ item_stack_.clear();
+ is_valid_ = false;
+ }
+ item_stack_.emplace_back(std::forward<key_type>(name_), json_array_arg, tag, result_allocator_);
+ structure_stack_.emplace_back(structure_type::array_t, item_stack_.size()-1);
+ return true;
+ }
+
+ bool visit_end_array(const ser_context&, std::error_code&) override
+ {
+ JSONCONS_ASSERT(structure_stack_.size() > 1);
+ JSONCONS_ASSERT(structure_stack_.back().type_ == structure_type::array_t);
+ const size_t container_index = structure_stack_.back().container_index_;
+ JSONCONS_ASSERT(item_stack_.size() > container_index);
+
+ auto& container = item_stack_[container_index].value_;
+
+ const size_t size = item_stack_.size() - (container_index + 1);
+ //std::cout << "size on item stack: " << size << "\n";
+
+ if (size > 0)
+ {
+ container.reserve(size);
+ auto first = item_stack_.begin() + (container_index+1);
+ auto last = first + size;
+ for (auto it = first; it != last; ++it)
+ {
+ container.push_back(std::move(it->value_));
+ }
+ item_stack_.erase(first, item_stack_.end());
+ }
+
+ structure_stack_.pop_back();
+ if (structure_stack_.back().type_ == structure_type::root_t)
+ {
+ result_.swap(item_stack_.front().value_);
+ item_stack_.pop_back();
+ is_valid_ = true;
+ return false;
+ }
+ return true;
+ }
+
+ bool visit_key(const string_view_type& name, const ser_context&, std::error_code&) override
+ {
+ name_ = key_type(name.data(),name.length(),result_allocator_);
+ return true;
+ }
+
+ bool visit_string(const string_view_type& sv, semantic_tag tag, const ser_context&, std::error_code&) override
+ {
+ switch (structure_stack_.back().type_)
+ {
+ case structure_type::object_t:
+ case structure_type::array_t:
+ item_stack_.emplace_back(std::forward<key_type>(name_), sv, tag, result_allocator_);
+ break;
+ case structure_type::root_t:
+ result_ = Json(sv, tag, result_allocator_);
+ is_valid_ = true;
+ return false;
+ }
+ return true;
+ }
+
+ bool visit_byte_string(const byte_string_view& b,
+ semantic_tag tag,
+ const ser_context&,
+ std::error_code&) override
+ {
+ switch (structure_stack_.back().type_)
+ {
+ case structure_type::object_t:
+ case structure_type::array_t:
+ item_stack_.emplace_back(std::forward<key_type>(name_), byte_string_arg, b, tag, result_allocator_);
+ break;
+ case structure_type::root_t:
+ result_ = Json(byte_string_arg, b, tag, result_allocator_);
+ is_valid_ = true;
+ return false;
+ }
+ return true;
+ }
+
+ bool visit_byte_string(const byte_string_view& b,
+ uint64_t ext_tag,
+ const ser_context&,
+ std::error_code&) override
+ {
+ switch (structure_stack_.back().type_)
+ {
+ case structure_type::object_t:
+ case structure_type::array_t:
+ item_stack_.emplace_back(std::forward<key_type>(name_), byte_string_arg, b, ext_tag, result_allocator_);
+ break;
+ case structure_type::root_t:
+ result_ = Json(byte_string_arg, b, ext_tag, result_allocator_);
+ is_valid_ = true;
+ return false;
+ }
+ return true;
+ }
+
+ bool visit_int64(int64_t value,
+ semantic_tag tag,
+ const ser_context&,
+ std::error_code&) override
+ {
+ switch (structure_stack_.back().type_)
+ {
+ case structure_type::object_t:
+ case structure_type::array_t:
+ item_stack_.emplace_back(std::forward<key_type>(name_), value, tag);
+ break;
+ case structure_type::root_t:
+ result_ = Json(value,tag);
+ is_valid_ = true;
+ return false;
+ }
+ return true;
+ }
+
+ bool visit_uint64(uint64_t value,
+ semantic_tag tag,
+ const ser_context&,
+ std::error_code&) override
+ {
+ switch (structure_stack_.back().type_)
+ {
+ case structure_type::object_t:
+ case structure_type::array_t:
+ item_stack_.emplace_back(std::forward<key_type>(name_), value, tag);
+ break;
+ case structure_type::root_t:
+ result_ = Json(value,tag);
+ is_valid_ = true;
+ return false;
+ }
+ return true;
+ }
+
+ bool visit_half(uint16_t value,
+ semantic_tag tag,
+ const ser_context&,
+ std::error_code&) override
+ {
+ switch (structure_stack_.back().type_)
+ {
+ case structure_type::object_t:
+ case structure_type::array_t:
+ item_stack_.emplace_back(std::forward<key_type>(name_), half_arg, value, tag);
+ break;
+ case structure_type::root_t:
+ result_ = Json(half_arg, value, tag);
+ is_valid_ = true;
+ return false;
+ }
+ return true;
+ }
+
+ bool visit_double(double value,
+ semantic_tag tag,
+ const ser_context&,
+ std::error_code&) override
+ {
+ switch (structure_stack_.back().type_)
+ {
+ case structure_type::object_t:
+ case structure_type::array_t:
+ item_stack_.emplace_back(std::forward<key_type>(name_), value, tag);
+ break;
+ case structure_type::root_t:
+ result_ = Json(value, tag);
+ is_valid_ = true;
+ return false;
+ }
+ return true;
+ }
+
+ bool visit_bool(bool value, semantic_tag tag, const ser_context&, std::error_code&) override
+ {
+ switch (structure_stack_.back().type_)
+ {
+ case structure_type::object_t:
+ case structure_type::array_t:
+ item_stack_.emplace_back(std::forward<key_type>(name_), value, tag);
+ break;
+ case structure_type::root_t:
+ result_ = Json(value, tag);
+ is_valid_ = true;
+ return false;
+ }
+ return true;
+ }
+
+ bool visit_null(semantic_tag tag, const ser_context&, std::error_code&) override
+ {
+ switch (structure_stack_.back().type_)
+ {
+ case structure_type::object_t:
+ case structure_type::array_t:
+ item_stack_.emplace_back(std::forward<key_type>(name_), null_type(), tag);
+ break;
+ case structure_type::root_t:
+ result_ = Json(null_type(), tag);
+ is_valid_ = true;
+ return false;
+ }
+ return true;
+ }
+};
+
+}
+
+#endif
diff --git a/include/jsoncons/json_encoder.hpp b/include/jsoncons/json_encoder.hpp
new file mode 100644
index 0000000..df0d3fa
--- /dev/null
+++ b/include/jsoncons/json_encoder.hpp
@@ -0,0 +1,1587 @@
+// Copyright 2013 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_ENCODER_HPP
+#define JSONCONS_JSON_ENCODER_HPP
+
+#include <array> // std::array
+#include <string>
+#include <vector>
+#include <cmath> // std::isfinite, std::isnan
+#include <limits> // std::numeric_limits
+#include <memory>
+#include <utility> // std::move
+#include <jsoncons/config/jsoncons_config.hpp>
+#include <jsoncons/json_exception.hpp>
+#include <jsoncons/byte_string.hpp>
+#include <jsoncons/bigint.hpp>
+#include <jsoncons/json_options.hpp>
+#include <jsoncons/json_error.hpp>
+#include <jsoncons/json_visitor.hpp>
+#include <jsoncons/sink.hpp>
+#include <jsoncons/detail/write_number.hpp>
+
+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 <class CharT, class Sink>
+ 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<class CharT,class Sink=jsoncons::stream_sink<CharT>,class Allocator=std::allocator<char>>
+ class basic_json_encoder final : public basic_json_visitor<CharT>
+ {
+ static const jsoncons::basic_string_view<CharT> null_constant()
+ {
+ static const jsoncons::basic_string_view<CharT> k = JSONCONS_STRING_VIEW_CONSTANT(CharT, "null");
+ return k;
+ }
+ static const jsoncons::basic_string_view<CharT> true_constant()
+ {
+ static const jsoncons::basic_string_view<CharT> k = JSONCONS_STRING_VIEW_CONSTANT(CharT, "true");
+ return k;
+ }
+ static const jsoncons::basic_string_view<CharT> false_constant()
+ {
+ static const jsoncons::basic_string_view<CharT> k = JSONCONS_STRING_VIEW_CONSTANT(CharT, "false");
+ return k;
+ }
+ public:
+ using allocator_type = Allocator;
+ using char_type = CharT;
+ using typename basic_json_visitor<CharT>::string_view_type;
+ using sink_type = Sink;
+ using string_type = typename basic_json_encode_options<CharT>::string_type;
+
+ private:
+ enum class container_type {object, array};
+
+ class encoding_context
+ {
+ container_type type_;
+ std::size_t count_;
+ line_split_kind line_splits_;
+ bool indent_before_;
+ bool new_line_after_;
+ std::size_t begin_pos_;
+ std::size_t data_pos_;
+ 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), count_(0), line_splits_(split_lines), indent_before_(indent_once), new_line_after_(false),
+ begin_pos_(begin_pos), data_pos_(data_pos)
+ {
+ }
+
+ encoding_context(const encoding_context&) = default;
+ encoding_context& operator=(const encoding_context&) = default;
+
+ void set_position(std::size_t pos)
+ {
+ data_pos_ = pos;
+ }
+
+ std::size_t begin_pos() const
+ {
+ return begin_pos_;
+ }
+
+ std::size_t data_pos() const
+ {
+ return data_pos_;
+ }
+
+ std::size_t count() const
+ {
+ return count_;
+ }
+
+ void increment_count()
+ {
+ ++count_;
+ }
+
+ bool new_line_after() const
+ {
+ return new_line_after_;
+ }
+
+ void new_line_after(bool value)
+ {
+ new_line_after_ = value;
+ }
+
+ bool is_object() const
+ {
+ return type_ == container_type::object;
+ }
+
+ bool is_array() const
+ {
+ return type_ == container_type::array;
+ }
+
+ bool is_same_line() const
+ {
+ return line_splits_ == line_split_kind::same_line;
+ }
+
+ bool is_new_line() const
+ {
+ return line_splits_ == line_split_kind::new_line;
+ }
+
+ bool is_multi_line() const
+ {
+ return line_splits_ == line_split_kind::multi_line;
+ }
+
+ bool is_indent_once() const
+ {
+ return count_ == 0 ? indent_before_ : false;
+ }
+
+ };
+ typedef typename std::allocator_traits<allocator_type>:: template rebind_alloc<encoding_context> encoding_context_allocator_type;
+
+ Sink sink_;
+ basic_json_encode_options<CharT> options_;
+ jsoncons::detail::write_double fp_;
+
+ std::vector<encoding_context,encoding_context_allocator_type> stack_;
+ int indent_amount_;
+ std::size_t column_;
+ std::basic_string<CharT> colon_str_;
+ std::basic_string<CharT> comma_str_;
+ std::basic_string<CharT> open_object_brace_str_;
+ std::basic_string<CharT> close_object_brace_str_;
+ std::basic_string<CharT> open_array_bracket_str_;
+ std::basic_string<CharT> close_array_bracket_str_;
+ int nesting_depth_;
+
+ // Noncopyable and nonmoveable
+ basic_json_encoder(const basic_json_encoder&) = delete;
+ basic_json_encoder& operator=(const basic_json_encoder&) = delete;
+ public:
+ basic_json_encoder(Sink&& sink,
+ const Allocator& alloc = Allocator())
+ : basic_json_encoder(std::forward<Sink>(sink), basic_json_encode_options<CharT>(), alloc)
+ {
+ }
+
+ basic_json_encoder(Sink&& sink,
+ const basic_json_encode_options<CharT>& options,
+ const Allocator& alloc = Allocator())
+ : sink_(std::forward<Sink>(sink)),
+ options_(options),
+ fp_(options.float_format(), options.precision()),
+ stack_(alloc),
+ indent_amount_(0),
+ column_(0),
+ nesting_depth_(0)
+ {
+ switch (options.spaces_around_colon())
+ {
+ case spaces_option::space_after:
+ colon_str_ = std::basic_string<CharT>({':',' '});
+ break;
+ case spaces_option::space_before:
+ colon_str_ = std::basic_string<CharT>({' ',':'});
+ break;
+ case spaces_option::space_before_and_after:
+ colon_str_ = std::basic_string<CharT>({' ',':',' '});
+ break;
+ default:
+ colon_str_.push_back(':');
+ break;
+ }
+ switch (options.spaces_around_comma())
+ {
+ case spaces_option::space_after:
+ comma_str_ = std::basic_string<CharT>({',',' '});
+ break;
+ case spaces_option::space_before:
+ comma_str_ = std::basic_string<CharT>({' ',','});
+ break;
+ case spaces_option::space_before_and_after:
+ comma_str_ = std::basic_string<CharT>({' ',',',' '});
+ break;
+ default:
+ comma_str_.push_back(',');
+ break;
+ }
+ if (options.pad_inside_object_braces())
+ {
+ open_object_brace_str_ = std::basic_string<CharT>({'{', ' '});
+ close_object_brace_str_ = std::basic_string<CharT>({' ', '}'});
+ }
+ else
+ {
+ open_object_brace_str_.push_back('{');
+ close_object_brace_str_.push_back('}');
+ }
+ if (options.pad_inside_array_brackets())
+ {
+ open_array_bracket_str_ = std::basic_string<CharT>({'[', ' '});
+ close_array_bracket_str_ = std::basic_string<CharT>({' ', ']'});
+ }
+ else
+ {
+ open_array_bracket_str_.push_back('[');
+ close_array_bracket_str_.push_back(']');
+ }
+ }
+
+ ~basic_json_encoder() noexcept
+ {
+ JSONCONS_TRY
+ {
+ sink_.flush();
+ }
+ JSONCONS_CATCH(...)
+ {
+ }
+ }
+
+ void reset()
+ {
+ stack_.clear();
+ indent_amount_ = 0;
+ column_ = 0;
+ nesting_depth_ = 0;
+ }
+
+ void reset(Sink&& sink)
+ {
+ sink_ = std::move(sink);
+ reset();
+ }
+
+ private:
+ // Implementing methods
+ void visit_flush() override
+ {
+ sink_.flush();
+ }
+
+ bool visit_begin_object(semantic_tag, const ser_context&, std::error_code& ec) override
+ {
+ if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth()))
+ {
+ ec = json_errc::max_nesting_depth_exceeded;
+ return false;
+ }
+ if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0)
+ {
+ sink_.append(comma_str_.data(),comma_str_.length());
+ column_ += comma_str_.length();
+ }
+
+ if (!stack_.empty()) // object or array
+ {
+ if (stack_.back().is_object())
+ {
+ switch (options_.object_object_line_splits())
+ {
+ case line_split_kind::same_line:
+ if (column_ >= options_.line_length_limit())
+ {
+ break_line();
+ }
+ break;
+ case line_split_kind::new_line:
+ if (column_ >= options_.line_length_limit())
+ {
+ break_line();
+ }
+ break;
+ default: // multi_line
+ break;
+ }
+ stack_.emplace_back(container_type::object,options_.object_object_line_splits(), false,
+ column_, column_+open_object_brace_str_.length());
+ }
+ else // array
+ {
+ switch (options_.array_object_line_splits())
+ {
+ case line_split_kind::same_line:
+ if (column_ >= options_.line_length_limit())
+ {
+ //stack_.back().new_line_after(true);
+ new_line();
+ }
+ break;
+ case line_split_kind::new_line:
+ stack_.back().new_line_after(true);
+ new_line();
+ break;
+ default: // multi_line
+ stack_.back().new_line_after(true);
+ new_line();
+ break;
+ }
+ stack_.emplace_back(container_type::object,options_.array_object_line_splits(), false,
+ column_, column_+open_object_brace_str_.length());
+ }
+ }
+ else
+ {
+ stack_.emplace_back(container_type::object, line_split_kind::multi_line, false,
+ column_, column_+open_object_brace_str_.length());
+ }
+ indent();
+
+ sink_.append(open_object_brace_str_.data(), open_object_brace_str_.length());
+ column_ += open_object_brace_str_.length();
+ return true;
+ }
+
+ bool visit_end_object(const ser_context&, std::error_code&) override
+ {
+ JSONCONS_ASSERT(!stack_.empty());
+ --nesting_depth_;
+
+ unindent();
+ if (stack_.back().new_line_after())
+ {
+ new_line();
+ }
+ stack_.pop_back();
+ sink_.append(close_object_brace_str_.data(), close_object_brace_str_.length());
+ column_ += close_object_brace_str_.length();
+
+ end_value();
+ return true;
+ }
+
+ bool visit_begin_array(semantic_tag, const ser_context&, std::error_code& ec) override
+ {
+ if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth()))
+ {
+ ec = json_errc::max_nesting_depth_exceeded;
+ return false;
+ }
+ if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0)
+ {
+ sink_.append(comma_str_.data(),comma_str_.length());
+ column_ += comma_str_.length();
+ }
+ if (!stack_.empty())
+ {
+ if (stack_.back().is_object())
+ {
+ switch (options_.object_array_line_splits())
+ {
+ 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());
+ 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());
+ break;
+ }
+ default: // multi_line
+ stack_.emplace_back(container_type::array,options_.object_array_line_splits(),true,
+ column_, column_+open_array_bracket_str_.length());
+ break;
+ }
+ }
+ else // array
+ {
+ switch (options_.array_array_line_splits())
+ {
+ case line_split_kind::same_line:
+ if (stack_.back().is_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());
+ 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());
+ 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();
+ break;
+ }
+ }
+ }
+ else
+ {
+ stack_.emplace_back(container_type::array, line_split_kind::multi_line, false,
+ column_, column_+open_array_bracket_str_.length());
+ }
+ indent();
+ sink_.append(open_array_bracket_str_.data(), open_array_bracket_str_.length());
+ column_ += open_array_bracket_str_.length();
+ return true;
+ }
+
+ bool visit_end_array(const ser_context&, std::error_code&) override
+ {
+ JSONCONS_ASSERT(!stack_.empty());
+ --nesting_depth_;
+
+ unindent();
+ if (stack_.back().new_line_after())
+ {
+ new_line();
+ }
+ stack_.pop_back();
+ sink_.append(close_array_bracket_str_.data(), close_array_bracket_str_.length());
+ column_ += close_array_bracket_str_.length();
+ end_value();
+ return true;
+ }
+
+ bool visit_key(const string_view_type& name, const ser_context&, std::error_code&) override
+ {
+ JSONCONS_ASSERT(!stack_.empty());
+ if (stack_.back().count() > 0)
+ {
+ sink_.append(comma_str_.data(),comma_str_.length());
+ column_ += comma_str_.length();
+ }
+
+ if (stack_.back().is_multi_line())
+ {
+ stack_.back().new_line_after(true);
+ new_line();
+ }
+ else if (stack_.back().count() > 0 && column_ >= options_.line_length_limit())
+ {
+ //stack_.back().new_line_after(true);
+ new_line(stack_.back().data_pos());
+ }
+
+ if (stack_.back().count() == 0)
+ {
+ stack_.back().set_position(column_);
+ }
+ sink_.push_back('\"');
+ std::size_t length = jsoncons::detail::escape_string(name.data(), name.length(),options_.escape_all_non_ascii(),options_.escape_solidus(),sink_);
+ sink_.push_back('\"');
+ sink_.append(colon_str_.data(),colon_str_.length());
+ column_ += (length+2+colon_str_.length());
+ return true;
+ }
+
+ bool visit_null(semantic_tag, const ser_context&, std::error_code&) override
+ {
+ if (!stack_.empty())
+ {
+ if (stack_.back().is_array())
+ {
+ begin_scalar_value();
+ }
+ if (!stack_.back().is_multi_line() && column_ >= options_.line_length_limit())
+ {
+ break_line();
+ }
+ }
+
+ sink_.append(null_constant().data(), null_constant().size());
+ column_ += null_constant().size();
+
+ end_value();
+ return true;
+ }
+
+ bool visit_string(const string_view_type& sv, semantic_tag tag, const ser_context&, std::error_code&) override
+ {
+ if (!stack_.empty())
+ {
+ if (stack_.back().is_array())
+ {
+ begin_scalar_value();
+ }
+ if (!stack_.back().is_multi_line() && column_ >= options_.line_length_limit())
+ {
+ break_line();
+ }
+ }
+
+ switch (tag)
+ {
+ case semantic_tag::bigint:
+ write_bigint_value(sv);
+ break;
+ 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;
+ }
+ }
+
+ end_value();
+ return true;
+ }
+
+ bool visit_byte_string(const byte_string_view& b,
+ semantic_tag tag,
+ const ser_context&,
+ std::error_code&) override
+ {
+ if (!stack_.empty())
+ {
+ if (stack_.back().is_array())
+ {
+ begin_scalar_value();
+ }
+ if (!stack_.back().is_multi_line() && column_ >= options_.line_length_limit())
+ {
+ break_line();
+ }
+ }
+
+ byte_string_chars_format encoding_hint;
+ switch (tag)
+ {
+ case semantic_tag::base16:
+ encoding_hint = byte_string_chars_format::base16;
+ break;
+ case semantic_tag::base64:
+ encoding_hint = byte_string_chars_format::base64;
+ break;
+ case semantic_tag::base64url:
+ encoding_hint = byte_string_chars_format::base64url;
+ break;
+ default:
+ encoding_hint = byte_string_chars_format::none;
+ break;
+ }
+
+ byte_string_chars_format format = jsoncons::detail::resolve_byte_string_chars_format(options_.byte_string_format(),
+ encoding_hint,
+ byte_string_chars_format::base64url);
+ switch (format)
+ {
+ case byte_string_chars_format::base16:
+ {
+ sink_.push_back('\"');
+ std::size_t length = encode_base16(b.begin(),b.end(),sink_);
+ sink_.push_back('\"');
+ column_ += (length + 2);
+ break;
+ }
+ case byte_string_chars_format::base64:
+ {
+ sink_.push_back('\"');
+ std::size_t length = encode_base64(b.begin(), b.end(), sink_);
+ sink_.push_back('\"');
+ column_ += (length + 2);
+ break;
+ }
+ case byte_string_chars_format::base64url:
+ {
+ sink_.push_back('\"');
+ std::size_t length = encode_base64url(b.begin(),b.end(),sink_);
+ sink_.push_back('\"');
+ column_ += (length + 2);
+ break;
+ }
+ default:
+ {
+ JSONCONS_UNREACHABLE();
+ }
+ }
+
+ end_value();
+ return true;
+ }
+
+ bool visit_double(double value,
+ semantic_tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ if (!stack_.empty())
+ {
+ if (stack_.back().is_array())
+ {
+ begin_scalar_value();
+ }
+ if (!stack_.back().is_multi_line() && column_ >= options_.line_length_limit())
+ {
+ break_line();
+ }
+ }
+
+ if (!std::isfinite(value))
+ {
+ if ((std::isnan)(value))
+ {
+ if (options_.enable_nan_to_num())
+ {
+ sink_.append(options_.nan_to_num().data(), options_.nan_to_num().length());
+ column_ += options_.nan_to_num().length();
+ }
+ else if (options_.enable_nan_to_str())
+ {
+ visit_string(options_.nan_to_str(), semantic_tag::none, context, ec);
+ }
+ else
+ {
+ sink_.append(null_constant().data(), null_constant().size());
+ column_ += null_constant().size();
+ }
+ }
+ else if (value == std::numeric_limits<double>::infinity())
+ {
+ if (options_.enable_inf_to_num())
+ {
+ sink_.append(options_.inf_to_num().data(), options_.inf_to_num().length());
+ column_ += options_.inf_to_num().length();
+ }
+ else if (options_.enable_inf_to_str())
+ {
+ visit_string(options_.inf_to_str(), semantic_tag::none, context, ec);
+ }
+ else
+ {
+ sink_.append(null_constant().data(), null_constant().size());
+ column_ += null_constant().size();
+ }
+ }
+ else
+ {
+ if (options_.enable_neginf_to_num())
+ {
+ sink_.append(options_.neginf_to_num().data(), options_.neginf_to_num().length());
+ column_ += options_.neginf_to_num().length();
+ }
+ else if (options_.enable_neginf_to_str())
+ {
+ visit_string(options_.neginf_to_str(), semantic_tag::none, context, ec);
+ }
+ else
+ {
+ sink_.append(null_constant().data(), null_constant().size());
+ column_ += null_constant().size();
+ }
+ }
+ }
+ else
+ {
+ std::size_t length = fp_(value, sink_);
+ column_ += length;
+ }
+
+ end_value();
+ return true;
+ }
+
+ bool visit_int64(int64_t value,
+ semantic_tag,
+ const ser_context&,
+ std::error_code&) override
+ {
+ if (!stack_.empty())
+ {
+ if (stack_.back().is_array())
+ {
+ begin_scalar_value();
+ }
+ if (!stack_.back().is_multi_line() && column_ >= options_.line_length_limit())
+ {
+ break_line();
+ }
+ }
+ std::size_t length = jsoncons::detail::from_integer(value, sink_);
+ column_ += length;
+ end_value();
+ return true;
+ }
+
+ bool visit_uint64(uint64_t value,
+ semantic_tag,
+ const ser_context&,
+ std::error_code&) override
+ {
+ if (!stack_.empty())
+ {
+ if (stack_.back().is_array())
+ {
+ begin_scalar_value();
+ }
+ if (!stack_.back().is_multi_line() && column_ >= options_.line_length_limit())
+ {
+ break_line();
+ }
+ }
+ std::size_t length = jsoncons::detail::from_integer(value, sink_);
+ column_ += length;
+ end_value();
+ return true;
+ }
+
+ bool visit_bool(bool value, semantic_tag, const ser_context&, std::error_code&) override
+ {
+ if (!stack_.empty())
+ {
+ if (stack_.back().is_array())
+ {
+ begin_scalar_value();
+ }
+ if (!stack_.back().is_multi_line() && column_ >= options_.line_length_limit())
+ {
+ break_line();
+ }
+ }
+
+ if (value)
+ {
+ sink_.append(true_constant().data(), true_constant().size());
+ column_ += true_constant().size();
+ }
+ else
+ {
+ sink_.append(false_constant().data(), false_constant().size());
+ column_ += false_constant().size();
+ }
+
+ end_value();
+ return true;
+ }
+
+ void begin_scalar_value()
+ {
+ if (!stack_.empty())
+ {
+ if (stack_.back().count() > 0)
+ {
+ sink_.append(comma_str_.data(),comma_str_.length());
+ column_ += comma_str_.length();
+ }
+ if (stack_.back().is_multi_line() || stack_.back().is_indent_once())
+ {
+ stack_.back().new_line_after(true);
+ new_line();
+ }
+ }
+ }
+
+ void write_bigint_value(const string_view_type& sv)
+ {
+ switch (options_.bigint_format())
+ {
+ case bigint_chars_format::number:
+ {
+ sink_.append(sv.data(),sv.size());
+ column_ += sv.size();
+ break;
+ }
+ case bigint_chars_format::base64:
+ {
+ bigint n = bigint::from_string(sv.data(), sv.length());
+ bool is_neg = n < 0;
+ if (is_neg)
+ {
+ n = - n -1;
+ }
+ int signum;
+ std::vector<uint8_t> v;
+ n.write_bytes_be(signum, v);
+
+ sink_.push_back('\"');
+ if (is_neg)
+ {
+ sink_.push_back('~');
+ ++column_;
+ }
+ std::size_t length = encode_base64(v.begin(), v.end(), sink_);
+ sink_.push_back('\"');
+ column_ += (length+2);
+ break;
+ }
+ case bigint_chars_format::base64url:
+ {
+ bigint n = bigint::from_string(sv.data(), sv.length());
+ bool is_neg = n < 0;
+ if (is_neg)
+ {
+ n = - n -1;
+ }
+ int signum;
+ std::vector<uint8_t> v;
+ n.write_bytes_be(signum, v);
+
+ sink_.push_back('\"');
+ if (is_neg)
+ {
+ sink_.push_back('~');
+ ++column_;
+ }
+ std::size_t length = encode_base64url(v.begin(), v.end(), sink_);
+ sink_.push_back('\"');
+ column_ += (length+2);
+ break;
+ }
+ default:
+ {
+ sink_.push_back('\"');
+ sink_.append(sv.data(),sv.size());
+ sink_.push_back('\"');
+ column_ += (sv.size() + 2);
+ break;
+ }
+ }
+ }
+
+ void end_value()
+ {
+ if (!stack_.empty())
+ {
+ stack_.back().increment_count();
+ }
+ }
+
+ void indent()
+ {
+ indent_amount_ += static_cast<int>(options_.indent_size());
+ }
+
+ void unindent()
+ {
+ indent_amount_ -= static_cast<int>(options_.indent_size());
+ }
+
+ void new_line()
+ {
+ sink_.append(options_.new_line_chars().data(),options_.new_line_chars().length());
+ for (int i = 0; i < indent_amount_; ++i)
+ {
+ sink_.push_back(' ');
+ }
+ column_ = indent_amount_;
+ }
+
+ void new_line(std::size_t len)
+ {
+ sink_.append(options_.new_line_chars().data(),options_.new_line_chars().length());
+ for (std::size_t i = 0; i < len; ++i)
+ {
+ sink_.push_back(' ');
+ }
+ column_ = len;
+ }
+
+ void break_line()
+ {
+ stack_.back().new_line_after(true);
+ new_line();
+ }
+ };
+
+ template<class CharT,class Sink=jsoncons::stream_sink<CharT>,class Allocator=std::allocator<char>>
+ class basic_compact_json_encoder final : public basic_json_visitor<CharT>
+ {
+ static const std::array<CharT, 4>& null_constant()
+ {
+ static constexpr std::array<CharT,4> k{'n','u','l','l'};
+ return k;
+ }
+ static const std::array<CharT, 4>& true_constant()
+ {
+ static constexpr std::array<CharT,4> k{'t','r','u','e'};
+ return k;
+ }
+ static const std::array<CharT, 5>& false_constant()
+ {
+ static constexpr std::array<CharT,5> k{'f','a','l','s','e'};
+ return k;
+ }
+ public:
+ using allocator_type = Allocator;
+ using char_type = CharT;
+ using typename basic_json_visitor<CharT>::string_view_type;
+ using sink_type = Sink;
+ using string_type = typename basic_json_encode_options<CharT>::string_type;
+
+ private:
+ enum class container_type {object, array};
+
+ class encoding_context
+ {
+ container_type type_;
+ std::size_t count_;
+ public:
+ encoding_context(container_type type) noexcept
+ : type_(type), count_(0)
+ {
+ }
+
+ std::size_t count() const
+ {
+ return count_;
+ }
+
+ void increment_count()
+ {
+ ++count_;
+ }
+
+ bool is_array() const
+ {
+ return type_ == container_type::array;
+ }
+ };
+ typedef typename std::allocator_traits<allocator_type>:: template rebind_alloc<encoding_context> encoding_context_allocator_type;
+
+ Sink sink_;
+ basic_json_encode_options<CharT> options_;
+ jsoncons::detail::write_double fp_;
+ std::vector<encoding_context,encoding_context_allocator_type> stack_;
+ int nesting_depth_;
+
+ // Noncopyable
+ basic_compact_json_encoder(const basic_compact_json_encoder&) = delete;
+ basic_compact_json_encoder& operator=(const basic_compact_json_encoder&) = delete;
+ public:
+ basic_compact_json_encoder(Sink&& sink,
+ const Allocator& alloc = Allocator())
+ : basic_compact_json_encoder(std::forward<Sink>(sink), basic_json_encode_options<CharT>(), alloc)
+ {
+ }
+
+ basic_compact_json_encoder(Sink&& sink,
+ const basic_json_encode_options<CharT>& options,
+ const Allocator& alloc = Allocator())
+ : sink_(std::forward<Sink>(sink)),
+ options_(options),
+ fp_(options.float_format(), options.precision()),
+ stack_(alloc),
+ nesting_depth_(0)
+ {
+ }
+
+ basic_compact_json_encoder(basic_compact_json_encoder&&) = default;
+ basic_compact_json_encoder& operator=(basic_compact_json_encoder&&) = default;
+
+ ~basic_compact_json_encoder() noexcept
+ {
+ JSONCONS_TRY
+ {
+ sink_.flush();
+ }
+ JSONCONS_CATCH(...)
+ {
+ }
+ }
+
+ void reset()
+ {
+ stack_.clear();
+ nesting_depth_ = 0;
+ }
+
+ void reset(Sink&& sink)
+ {
+ sink_ = std::move(sink);
+ reset();
+ }
+
+ private:
+ // Implementing methods
+ void visit_flush() override
+ {
+ sink_.flush();
+ }
+
+ bool visit_begin_object(semantic_tag, const ser_context&, std::error_code& ec) override
+ {
+ if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth()))
+ {
+ ec = json_errc::max_nesting_depth_exceeded;
+ return false;
+ }
+ if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0)
+ {
+ sink_.push_back(',');
+ }
+
+ stack_.emplace_back(container_type::object);
+ sink_.push_back('{');
+ return true;
+ }
+
+ bool visit_end_object(const ser_context&, std::error_code&) override
+ {
+ JSONCONS_ASSERT(!stack_.empty());
+ --nesting_depth_;
+
+ stack_.pop_back();
+ sink_.push_back('}');
+
+ if (!stack_.empty())
+ {
+ stack_.back().increment_count();
+ }
+ return true;
+ }
+
+
+ bool visit_begin_array(semantic_tag, const ser_context&, std::error_code& ec) override
+ {
+ if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth()))
+ {
+ ec = json_errc::max_nesting_depth_exceeded;
+ return false;
+ }
+ if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0)
+ {
+ sink_.push_back(',');
+ }
+ stack_.emplace_back(container_type::array);
+ sink_.push_back('[');
+ return true;
+ }
+
+ bool visit_end_array(const ser_context&, std::error_code&) override
+ {
+ JSONCONS_ASSERT(!stack_.empty());
+ --nesting_depth_;
+
+ stack_.pop_back();
+ sink_.push_back(']');
+ if (!stack_.empty())
+ {
+ stack_.back().increment_count();
+ }
+ return true;
+ }
+
+ bool visit_key(const string_view_type& name, const ser_context&, std::error_code&) override
+ {
+ if (!stack_.empty() && stack_.back().count() > 0)
+ {
+ sink_.push_back(',');
+ }
+
+ sink_.push_back('\"');
+ jsoncons::detail::escape_string(name.data(), name.length(),options_.escape_all_non_ascii(),options_.escape_solidus(),sink_);
+ sink_.push_back('\"');
+ sink_.push_back(':');
+ return true;
+ }
+
+ bool visit_null(semantic_tag, const ser_context&, std::error_code&) override
+ {
+ if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0)
+ {
+ sink_.push_back(',');
+ }
+
+ sink_.append(null_constant().data(), null_constant().size());
+
+ if (!stack_.empty())
+ {
+ stack_.back().increment_count();
+ }
+ return true;
+ }
+
+ void write_bigint_value(const string_view_type& sv)
+ {
+ switch (options_.bigint_format())
+ {
+ case bigint_chars_format::number:
+ {
+ sink_.append(sv.data(),sv.size());
+ break;
+ }
+ case bigint_chars_format::base64:
+ {
+ bigint n = bigint::from_string(sv.data(), sv.length());
+ bool is_neg = n < 0;
+ if (is_neg)
+ {
+ n = - n -1;
+ }
+ int signum;
+ std::vector<uint8_t> v;
+ n.write_bytes_be(signum, v);
+
+ sink_.push_back('\"');
+ if (is_neg)
+ {
+ sink_.push_back('~');
+ }
+ encode_base64(v.begin(), v.end(), sink_);
+ sink_.push_back('\"');
+ break;
+ }
+ case bigint_chars_format::base64url:
+ {
+ bigint n = bigint::from_string(sv.data(), sv.length());
+ bool is_neg = n < 0;
+ if (is_neg)
+ {
+ n = - n -1;
+ }
+ int signum;
+ std::vector<uint8_t> v;
+ n.write_bytes_be(signum, v);
+
+ sink_.push_back('\"');
+ if (is_neg)
+ {
+ sink_.push_back('~');
+ }
+ encode_base64url(v.begin(), v.end(), sink_);
+ sink_.push_back('\"');
+ break;
+ }
+ default:
+ {
+ sink_.push_back('\"');
+ sink_.append(sv.data(),sv.size());
+ sink_.push_back('\"');
+ break;
+ }
+ }
+ }
+
+ bool visit_string(const string_view_type& sv, semantic_tag tag, const ser_context&, std::error_code&) override
+ {
+ 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;
+ 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;
+ }
+ }
+
+ if (!stack_.empty())
+ {
+ stack_.back().increment_count();
+ }
+ return true;
+ }
+
+ bool visit_byte_string(const byte_string_view& b,
+ semantic_tag tag,
+ const ser_context&,
+ std::error_code&) override
+ {
+ if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0)
+ {
+ sink_.push_back(',');
+ }
+
+ byte_string_chars_format encoding_hint;
+ switch (tag)
+ {
+ case semantic_tag::base16:
+ encoding_hint = byte_string_chars_format::base16;
+ break;
+ case semantic_tag::base64:
+ encoding_hint = byte_string_chars_format::base64;
+ break;
+ case semantic_tag::base64url:
+ encoding_hint = byte_string_chars_format::base64url;
+ break;
+ default:
+ encoding_hint = byte_string_chars_format::none;
+ break;
+ }
+
+ byte_string_chars_format format = jsoncons::detail::resolve_byte_string_chars_format(options_.byte_string_format(),
+ encoding_hint,
+ byte_string_chars_format::base64url);
+ switch (format)
+ {
+ case byte_string_chars_format::base16:
+ {
+ sink_.push_back('\"');
+ encode_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_);
+ sink_.push_back('\"');
+ break;
+ }
+ case byte_string_chars_format::base64url:
+ {
+ sink_.push_back('\"');
+ encode_base64url(b.begin(),b.end(),sink_);
+ sink_.push_back('\"');
+ break;
+ }
+ default:
+ {
+ JSONCONS_UNREACHABLE();
+ }
+ }
+
+ if (!stack_.empty())
+ {
+ stack_.back().increment_count();
+ }
+ return true;
+ }
+
+ bool visit_double(double value,
+ semantic_tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0)
+ {
+ sink_.push_back(',');
+ }
+
+ if (JSONCONS_UNLIKELY(!std::isfinite(value)))
+ {
+ if ((std::isnan)(value))
+ {
+ if (options_.enable_nan_to_num())
+ {
+ sink_.append(options_.nan_to_num().data(), options_.nan_to_num().length());
+ }
+ else if (options_.enable_nan_to_str())
+ {
+ visit_string(options_.nan_to_str(), semantic_tag::none, context, ec);
+ }
+ else
+ {
+ sink_.append(null_constant().data(), null_constant().size());
+ }
+ }
+ else if (value == std::numeric_limits<double>::infinity())
+ {
+ if (options_.enable_inf_to_num())
+ {
+ sink_.append(options_.inf_to_num().data(), options_.inf_to_num().length());
+ }
+ else if (options_.enable_inf_to_str())
+ {
+ visit_string(options_.inf_to_str(), semantic_tag::none, context, ec);
+ }
+ else
+ {
+ sink_.append(null_constant().data(), null_constant().size());
+ }
+ }
+ else
+ {
+ if (options_.enable_neginf_to_num())
+ {
+ sink_.append(options_.neginf_to_num().data(), options_.neginf_to_num().length());
+ }
+ else if (options_.enable_neginf_to_str())
+ {
+ visit_string(options_.neginf_to_str(), semantic_tag::none, context, ec);
+ }
+ else
+ {
+ sink_.append(null_constant().data(), null_constant().size());
+ }
+ }
+ }
+ else
+ {
+ fp_(value, sink_);
+ }
+
+ if (!stack_.empty())
+ {
+ stack_.back().increment_count();
+ }
+ return true;
+ }
+
+ bool visit_int64(int64_t value,
+ semantic_tag,
+ const ser_context&,
+ std::error_code&) override
+ {
+ if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0)
+ {
+ sink_.push_back(',');
+ }
+ jsoncons::detail::from_integer(value, sink_);
+ if (!stack_.empty())
+ {
+ stack_.back().increment_count();
+ }
+ return true;
+ }
+
+ bool visit_uint64(uint64_t value,
+ semantic_tag,
+ const ser_context&,
+ std::error_code&) override
+ {
+ if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0)
+ {
+ sink_.push_back(',');
+ }
+ jsoncons::detail::from_integer(value, sink_);
+ if (!stack_.empty())
+ {
+ stack_.back().increment_count();
+ }
+ return true;
+ }
+
+ bool visit_bool(bool value, semantic_tag, const ser_context&, std::error_code&) override
+ {
+ if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0)
+ {
+ sink_.push_back(',');
+ }
+
+ if (value)
+ {
+ sink_.append(true_constant().data(), true_constant().size());
+ }
+ else
+ {
+ sink_.append(false_constant().data(), false_constant().size());
+ }
+
+ if (!stack_.empty())
+ {
+ stack_.back().increment_count();
+ }
+ return true;
+ }
+ };
+
+ using json_stream_encoder = basic_json_encoder<char,jsoncons::stream_sink<char>>;
+ using wjson_stream_encoder = basic_json_encoder<wchar_t,jsoncons::stream_sink<wchar_t>>;
+ using compact_json_stream_encoder = basic_compact_json_encoder<char,jsoncons::stream_sink<char>>;
+ using compact_wjson_stream_encoder = basic_compact_json_encoder<wchar_t,jsoncons::stream_sink<wchar_t>>;
+
+ using json_string_encoder = basic_json_encoder<char,jsoncons::string_sink<std::string>>;
+ using wjson_string_encoder = basic_json_encoder<wchar_t,jsoncons::string_sink<std::wstring>>;
+ using compact_json_string_encoder = basic_compact_json_encoder<char,jsoncons::string_sink<std::string>>;
+ using compact_wjson_string_encoder = basic_compact_json_encoder<wchar_t,jsoncons::string_sink<std::wstring>>;
+
+ #if !defined(JSONCONS_NO_DEPRECATED)
+ template<class CharT,class Sink=jsoncons::stream_sink<CharT>>
+ using basic_json_serializer = basic_json_encoder<CharT,Sink>;
+
+ template<class CharT,class Sink=jsoncons::stream_sink<CharT>>
+ using basic_json_compressed_serializer = basic_compact_json_encoder<CharT,Sink>;
+
+ template<class CharT,class Sink=jsoncons::stream_sink<CharT>>
+ using basic_json_compressed_encoder = basic_compact_json_encoder<CharT,Sink>;
+
+ JSONCONS_DEPRECATED_MSG("Instead, use compact_json_stream_encoder") typedef basic_compact_json_encoder<char,jsoncons::stream_sink<char>> json_compressed_stream_encoder;
+ JSONCONS_DEPRECATED_MSG("Instead, use compact_wjson_stream_encoder")typedef basic_compact_json_encoder<wchar_t,jsoncons::stream_sink<wchar_t>> wjson_compressed_stream_encoder;
+ JSONCONS_DEPRECATED_MSG("Instead, use compact_json_string_encoder") typedef basic_compact_json_encoder<char,jsoncons::string_sink<char>> json_compressed_string_encoder;
+ JSONCONS_DEPRECATED_MSG("Instead, use compact_wjson_string_encoder")typedef basic_compact_json_encoder<wchar_t,jsoncons::string_sink<wchar_t>> wjson_compressed_string_encoder;
+
+ JSONCONS_DEPRECATED_MSG("Instead, use json_stream_encoder") typedef json_stream_encoder json_encoder;
+ JSONCONS_DEPRECATED_MSG("Instead, use wjson_stream_encoder") typedef wjson_stream_encoder wjson_encoder;
+ JSONCONS_DEPRECATED_MSG("Instead, use compact_json_stream_encoder") typedef compact_json_stream_encoder compact_json_encoder;
+ JSONCONS_DEPRECATED_MSG("Instead, use compact_wjson_stream_encoder") typedef compact_wjson_stream_encoder wcompact_json_encoder;
+
+ JSONCONS_DEPRECATED_MSG("Instead, use json_stream_encoder") typedef basic_json_encoder<char,jsoncons::stream_sink<char>> json_serializer;
+ JSONCONS_DEPRECATED_MSG("Instead, use wjson_stream_encoder") typedef basic_json_encoder<wchar_t,jsoncons::stream_sink<wchar_t>> wjson_serializer;
+
+ JSONCONS_DEPRECATED_MSG("Instead, use compact_json_stream_encoder") typedef basic_compact_json_encoder<char,jsoncons::stream_sink<char>> json_compressed_serializer;
+ JSONCONS_DEPRECATED_MSG("Instead, use compact_wjson_stream_encoder") typedef basic_compact_json_encoder<wchar_t,jsoncons::stream_sink<wchar_t>> wjson_compressed_serializer;
+
+ JSONCONS_DEPRECATED_MSG("Instead, use json_string_encoder") typedef basic_json_encoder<char,jsoncons::string_sink<std::string>> json_string_serializer;
+ JSONCONS_DEPRECATED_MSG("Instead, use wjson_string_encoder") typedef basic_json_encoder<wchar_t,jsoncons::string_sink<std::wstring>> wjson_string_serializer;
+
+ JSONCONS_DEPRECATED_MSG("Instead, use compact_json_string_encoder") typedef basic_compact_json_encoder<char,jsoncons::string_sink<std::string>> json_compressed_string_serializer;
+ JSONCONS_DEPRECATED_MSG("Instead, use wcompact_json_string_encoder") typedef basic_compact_json_encoder<wchar_t,jsoncons::string_sink<std::wstring>> wjson_compressed_string_serializer;
+ #endif
+
+} // namespace jsoncons
+
+#endif
diff --git a/include/jsoncons/json_error.hpp b/include/jsoncons/json_error.hpp
new file mode 100644
index 0000000..f305948
--- /dev/null
+++ b/include/jsoncons/json_error.hpp
@@ -0,0 +1,156 @@
+/// Copyright 2013 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_ERROR_HPP
+#define JSONCONS_JSON_ERROR_HPP
+
+#include <system_error>
+#include <jsoncons/config/jsoncons_config.hpp>
+
+namespace jsoncons {
+
+ enum class json_errc
+ {
+ success = 0,
+ unexpected_eof = 1,
+ source_error,
+ syntax_error,
+ extra_character,
+ max_nesting_depth_exceeded,
+ single_quote,
+ illegal_character_in_string,
+ extra_comma,
+ expected_key,
+ expected_value,
+ invalid_value,
+ expected_colon,
+ illegal_control_character,
+ illegal_escaped_character,
+ expected_codepoint_surrogate_pair,
+ invalid_hex_escape_sequence,
+ invalid_unicode_escape_sequence,
+ leading_zero,
+ invalid_number,
+ expected_comma_or_rbrace,
+ expected_comma_or_rbracket,
+ unexpected_rbracket,
+ unexpected_rbrace,
+ illegal_comment,
+ expected_continuation_byte,
+ over_long_utf8_sequence,
+ illegal_codepoint,
+ illegal_surrogate_value,
+ unpaired_high_surrogate,
+ illegal_unicode_character
+ };
+
+ class json_error_category_impl
+ : public std::error_category
+ {
+ public:
+ const char* name() const noexcept override
+ {
+ return "jsoncons/json";
+ }
+ std::string message(int ev) const override
+ {
+ switch (static_cast<json_errc>(ev))
+ {
+ case json_errc::unexpected_eof:
+ return "Unexpected end of file";
+ case json_errc::source_error:
+ return "Source error";
+ case json_errc::syntax_error:
+ return "JSON syntax_error";
+ case json_errc::extra_character:
+ return "Unexpected non-whitespace character after JSON text";
+ case json_errc::max_nesting_depth_exceeded:
+ return "Data item nesting exceeds limit in options";
+ case json_errc::single_quote:
+ return "JSON strings cannot be quoted with single quotes";
+ case json_errc::illegal_character_in_string:
+ return "Illegal character in string";
+ case json_errc::extra_comma:
+ return "Extra comma";
+ case json_errc::expected_key:
+ return "Expected object member key";
+ case json_errc::expected_value:
+ return "Expected value";
+ case json_errc::invalid_value:
+ return "Invalid value";
+ case json_errc::expected_colon:
+ return "Expected name separator ':'";
+ case json_errc::illegal_control_character:
+ return "Illegal control character in string";
+ case json_errc::illegal_escaped_character:
+ return "Illegal escaped character in string";
+ case json_errc::expected_codepoint_surrogate_pair:
+ return "Invalid codepoint, expected another \\u token to begin the second half of a codepoint surrogate pair.";
+ case json_errc::invalid_hex_escape_sequence:
+ return "Invalid codepoint, expected hexadecimal digit.";
+ case json_errc::invalid_unicode_escape_sequence:
+ return "Invalid codepoint, expected four hexadecimal digits.";
+ case json_errc::leading_zero:
+ return "A number cannot have a leading zero";
+ case json_errc::invalid_number:
+ return "Invalid number";
+ case json_errc::expected_comma_or_rbrace:
+ return "Expected comma or right brace '}'";
+ case json_errc::expected_comma_or_rbracket:
+ return "Expected comma or right bracket ']'";
+ case json_errc::unexpected_rbrace:
+ return "Unexpected right brace '}'";
+ case json_errc::unexpected_rbracket:
+ return "Unexpected right bracket ']'";
+ case json_errc::illegal_comment:
+ return "Illegal comment";
+ case json_errc::expected_continuation_byte:
+ return "Expected continuation byte";
+ case json_errc::over_long_utf8_sequence:
+ return "Over long UTF-8 sequence";
+ case json_errc::illegal_codepoint:
+ return "Illegal codepoint (>= 0xd800 && <= 0xdfff)";
+ case json_errc::illegal_surrogate_value:
+ return "UTF-16 surrogate values are illegal in UTF-32";
+ case json_errc::unpaired_high_surrogate:
+ return "Expected low surrogate following the high surrogate";
+ case json_errc::illegal_unicode_character:
+ return "Illegal unicode character";
+ default:
+ return "Unknown JSON parser error";
+ }
+ }
+ };
+
+ inline
+ const std::error_category& json_error_category()
+ {
+ static json_error_category_impl instance;
+ return instance;
+ }
+
+ inline
+ std::error_code make_error_code(json_errc result)
+ {
+ return std::error_code(static_cast<int>(result),json_error_category());
+ }
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+JSONCONS_DEPRECATED_MSG("Instead, use json_errc") typedef json_errc json_parser_errc;
+
+JSONCONS_DEPRECATED_MSG("Instead, use json_errc") typedef json_errc json_parse_errc;
+#endif
+
+} // jsoncons
+
+namespace std {
+ template<>
+ struct is_error_code_enum<jsoncons::json_errc> : public true_type
+ {
+ };
+}
+
+#endif
diff --git a/include/jsoncons/json_exception.hpp b/include/jsoncons/json_exception.hpp
new file mode 100644
index 0000000..e25d401
--- /dev/null
+++ b/include/jsoncons/json_exception.hpp
@@ -0,0 +1,241 @@
+// Copyright 2013 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 JSON_EXCEPTION_HPP
+#define JSON_EXCEPTION_HPP
+
+#include <string> // std::string
+#include <sstream> // std::ostringstream
+#include <system_error> // std::error_code
+#include <jsoncons/unicode_traits.hpp> // unicode_traits::convert
+#include <jsoncons/config/jsoncons_config.hpp>
+#include <jsoncons/more_type_traits.hpp>
+
+namespace jsoncons {
+
+ // json_exception
+
+ class json_exception
+ {
+ public:
+ virtual ~json_exception() noexcept = default;
+ virtual const char* what() const noexcept = 0;
+ };
+
+ // json_runtime_error
+
+ template <class Base, class Enable = void>
+ class json_runtime_error
+ {
+ };
+
+ template <class Base>
+ class json_runtime_error<Base,
+ typename std::enable_if<std::is_convertible<Base*,std::exception*>::value &&
+ type_traits::is_constructible_from_string<Base>::value>::type>
+ : public Base, public virtual json_exception
+ {
+ public:
+ json_runtime_error(const std::string& s) noexcept
+ : Base(s)
+ {
+ }
+ ~json_runtime_error() noexcept
+ {
+ }
+ const char* what() const noexcept override
+ {
+ return Base::what();
+ }
+ };
+
+ class key_not_found : public std::out_of_range, public virtual json_exception
+ {
+ std::string name_;
+ mutable std::string what_;
+ public:
+ template <class CharT>
+ explicit key_not_found(const CharT* key, std::size_t length) noexcept
+ : std::out_of_range("Key not found")
+ {
+ JSONCONS_TRY
+ {
+ unicode_traits::convert(key, length, name_,
+ unicode_traits::conv_flags::strict);
+ }
+ JSONCONS_CATCH(...)
+ {
+ }
+ }
+
+ virtual ~key_not_found() noexcept
+ {
+ }
+
+ const char* what() const noexcept override
+ {
+ if (what_.empty())
+ {
+ JSONCONS_TRY
+ {
+ what_.append(std::out_of_range::what());
+ what_.append(": '");
+ what_.append(name_);
+ what_.append("'");
+ return what_.c_str();
+ }
+ JSONCONS_CATCH(...)
+ {
+ return std::out_of_range::what();
+ }
+ }
+ else
+ {
+ return what_.c_str();
+ }
+ }
+ };
+
+ class not_an_object : public std::runtime_error, public virtual json_exception
+ {
+ std::string name_;
+ mutable std::string what_;
+ public:
+ template <class CharT>
+ explicit not_an_object(const CharT* key, std::size_t length) noexcept
+ : std::runtime_error("Attempting to access a member of a value that is not an object")
+ {
+ JSONCONS_TRY
+ {
+ unicode_traits::convert(key, length, name_,
+ unicode_traits::conv_flags::strict);
+ }
+ JSONCONS_CATCH(...)
+ {
+ }
+ }
+
+ virtual ~not_an_object() noexcept
+ {
+ }
+ const char* what() const noexcept override
+ {
+ if (what_.empty())
+ {
+ JSONCONS_TRY
+ {
+ what_.append(std::runtime_error::what());
+ what_.append(": '");
+ what_.append(name_);
+ what_.append("'");
+ return what_.c_str();
+ }
+ JSONCONS_CATCH(...)
+ {
+ return std::runtime_error::what();
+ }
+ }
+ else
+ {
+ return what_.c_str();
+ }
+ }
+ };
+
+ class ser_error : public std::system_error, public virtual json_exception
+ {
+ std::size_t line_number_;
+ std::size_t column_number_;
+ mutable std::string what_;
+ public:
+ ser_error(std::error_code ec)
+ : std::system_error(ec), line_number_(0), column_number_(0)
+ {
+ }
+ ser_error(std::error_code ec, const std::string& what_arg)
+ : std::system_error(ec, what_arg), line_number_(0), column_number_(0)
+ {
+ }
+ ser_error(std::error_code ec, std::size_t position)
+ : std::system_error(ec), line_number_(0), column_number_(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)
+ {
+ }
+ ser_error(const ser_error& other) = default;
+
+ ser_error(ser_error&& other) = default;
+
+ const char* what() const noexcept override
+ {
+ 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_;
+ }
+
+ #if !defined(JSONCONS_NO_DEPRECATED)
+ JSONCONS_DEPRECATED_MSG("Instead, use line()")
+ std::size_t line_number() const noexcept
+ {
+ return line();
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use column()")
+ std::size_t column_number() const noexcept
+ {
+ return column();
+ }
+ #endif
+ };
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+JSONCONS_DEPRECATED_MSG("Instead, use ser_error") typedef ser_error serialization_error;
+JSONCONS_DEPRECATED_MSG("Instead, use ser_error") typedef ser_error json_parse_exception;
+JSONCONS_DEPRECATED_MSG("Instead, use ser_error") typedef ser_error parse_exception;
+JSONCONS_DEPRECATED_MSG("Instead, use ser_error") typedef ser_error parse_error;
+typedef ser_error codec_error;
+#endif
+
+} // namespace jsoncons
+
+#endif
diff --git a/include/jsoncons/json_filter.hpp b/include/jsoncons/json_filter.hpp
new file mode 100644
index 0000000..cc93c73
--- /dev/null
+++ b/include/jsoncons/json_filter.hpp
@@ -0,0 +1,653 @@
+// Copyright 2013 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_FILTER_HPP
+#define JSONCONS_JSON_FILTER_HPP
+
+#include <string>
+
+#include <jsoncons/json_visitor.hpp>
+
+namespace jsoncons {
+
+template <class CharT>
+class basic_json_filter : public basic_json_visitor<CharT>
+{
+public:
+ using typename basic_json_visitor<CharT>::char_type;
+ using typename basic_json_visitor<CharT>::string_view_type;
+private:
+ basic_json_visitor<char_type>* destination_;
+
+ // noncopyable
+ basic_json_filter(const basic_json_filter&) = delete;
+ basic_json_filter& operator=(const basic_json_filter&) = delete;
+public:
+ basic_json_filter(basic_json_visitor<char_type>& visitor)
+ : destination_(std::addressof(visitor))
+ {
+ }
+
+ // moveable
+ basic_json_filter(basic_json_filter&&) = default;
+ basic_json_filter& operator=(basic_json_filter&&) = default;
+
+ basic_json_visitor<char_type>& destination()
+ {
+ return *destination_;
+ }
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+
+ JSONCONS_DEPRECATED_MSG("Instead, use destination()")
+ basic_json_visitor<char_type>& to_handler()
+ {
+ return destination_;
+ }
+ JSONCONS_DEPRECATED_MSG("Instead, use destination")
+ basic_json_visitor<char_type>& input_handler()
+ {
+ return destination_;
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use destination")
+ basic_json_visitor<char_type>& downstream_handler()
+ {
+ return destination_;
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use destination")
+ basic_json_visitor<char_type>& destination_handler()
+ {
+ return destination_;
+ }
+#endif
+
+private:
+ void visit_flush() override
+ {
+ destination_->flush();
+ }
+
+ bool visit_begin_object(semantic_tag tag, const ser_context& context, std::error_code& ec) override
+ {
+ return destination_->begin_object(tag, context, ec);
+ }
+
+ bool visit_begin_object(std::size_t length, semantic_tag tag, const ser_context& context, std::error_code& ec) override
+ {
+ return destination_->begin_object(length, tag, context, ec);
+ }
+
+ bool visit_end_object(const ser_context& context, std::error_code& ec) override
+ {
+ return destination_->end_object(context, ec);
+ }
+
+ bool visit_begin_array(semantic_tag tag, const ser_context& context, std::error_code& ec) override
+ {
+ return destination_->begin_array(tag, context, ec);
+ }
+
+ bool visit_begin_array(std::size_t length, semantic_tag tag, const ser_context& context, std::error_code& ec) override
+ {
+ return destination_->begin_array(length, tag, context, ec);
+ }
+
+ bool visit_end_array(const ser_context& context, std::error_code& ec) override
+ {
+ return destination_->end_array(context, ec);
+ }
+
+ bool visit_key(const string_view_type& name,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ return destination_->key(name, context, ec);
+ }
+
+ bool visit_string(const string_view_type& value,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ return destination_->string_value(value, tag, context, ec);
+ }
+
+ bool visit_byte_string(const byte_string_view& b,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ return destination_->byte_string_value(b, tag, context, ec);
+ }
+
+ bool visit_byte_string(const byte_string_view& b,
+ uint64_t ext_tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ return destination_->byte_string_value(b, ext_tag, context, ec);
+ }
+
+ bool visit_uint64(uint64_t value, semantic_tag tag, const ser_context& context, std::error_code& ec) override
+ {
+ return destination_->uint64_value(value, tag, context, ec);
+ }
+
+ bool visit_int64(int64_t value, semantic_tag tag, const ser_context& context, std::error_code& ec) override
+ {
+ return destination_->int64_value(value, tag, context, ec);
+ }
+
+ bool visit_half(uint16_t value, semantic_tag tag, const ser_context& context, std::error_code& ec) override
+ {
+ return destination_->half_value(value, tag, context, ec);
+ }
+
+ bool visit_double(double value, semantic_tag tag, const ser_context& context, std::error_code& ec) override
+ {
+ return destination_->double_value(value, tag, context, ec);
+ }
+
+ bool visit_bool(bool value, semantic_tag tag, const ser_context& context, std::error_code& ec) override
+ {
+ return destination_->bool_value(value, tag, context, ec);
+ }
+
+ bool visit_null(semantic_tag tag, const ser_context& context, std::error_code& ec) override
+ {
+ return destination_->null_value(tag, context, ec);
+ }
+
+ bool visit_typed_array(const jsoncons::span<const uint8_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ return destination_->typed_array(s, tag, context, ec);
+ }
+
+ bool visit_typed_array(const jsoncons::span<const uint16_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ return destination_->typed_array(s, tag, context, ec);
+ }
+
+ bool visit_typed_array(const jsoncons::span<const uint32_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ return destination_->typed_array(s, tag, context, ec);
+ }
+
+ bool visit_typed_array(const jsoncons::span<const uint64_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ return destination_->typed_array(s, tag, context, ec);
+ }
+
+ bool visit_typed_array(const jsoncons::span<const int8_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ return destination_->typed_array(s, tag, context, ec);
+ }
+
+ bool visit_typed_array(const jsoncons::span<const int16_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ return destination_->typed_array(s, tag, context, ec);
+ }
+
+ bool visit_typed_array(const jsoncons::span<const int32_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ return destination_->typed_array(s, tag, context, ec);
+ }
+
+ bool visit_typed_array(const jsoncons::span<const int64_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ return destination_->typed_array(s, tag, context, ec);
+ }
+
+ bool visit_typed_array(half_arg_t,
+ const jsoncons::span<const uint16_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ return destination_->typed_array(half_arg, s, tag, context, ec);
+ }
+
+ bool visit_typed_array(const jsoncons::span<const float>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ return destination_->typed_array(s, tag, context, ec);
+ }
+
+ bool visit_typed_array(const jsoncons::span<const double>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ return destination_->typed_array(s, tag, context, ec);
+ }
+
+ bool visit_begin_multi_dim(const jsoncons::span<const size_t>& shape,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ return destination_->begin_multi_dim(shape, tag, context, ec);
+ }
+
+ bool visit_end_multi_dim(const ser_context& context,
+ std::error_code& ec) override
+ {
+ return destination_->end_multi_dim(context, ec);
+ }
+};
+
+template <class CharT>
+class basic_rename_object_key_filter : public basic_json_filter<CharT>
+{
+public:
+ using typename basic_json_filter<CharT>::string_view_type;
+
+private:
+ std::basic_string<CharT> name_;
+ std::basic_string<CharT> new_name_;
+public:
+ basic_rename_object_key_filter(const std::basic_string<CharT>& name,
+ const std::basic_string<CharT>& new_name,
+ basic_json_visitor<CharT>& visitor)
+ : basic_json_filter<CharT>(visitor),
+ name_(name), new_name_(new_name)
+ {
+ }
+
+private:
+ bool visit_key(const string_view_type& name,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ if (name == name_)
+ {
+ return this->destination().key(new_name_,context, ec);
+ }
+ else
+ {
+ return this->destination().key(name,context,ec);
+ }
+ }
+};
+
+template <class From,class To>
+class json_visitor_adaptor_base : public From
+{
+public:
+ using typename From::string_view_type;
+private:
+ To* destination_;
+
+ // noncopyable
+ json_visitor_adaptor_base(const json_visitor_adaptor_base&) = delete;
+ json_visitor_adaptor_base& operator=(const json_visitor_adaptor_base&) = delete;
+public:
+ json_visitor_adaptor_base(To& visitor)
+ : destination_(std::addressof(visitor))
+ {
+ }
+
+ // moveable
+ json_visitor_adaptor_base(json_visitor_adaptor_base&&) = default;
+ json_visitor_adaptor_base& operator=(json_visitor_adaptor_base&&) = default;
+
+ To& destination()
+ {
+ return *destination_;
+ }
+
+private:
+ void visit_flush() override
+ {
+ destination_->flush();
+ }
+
+ bool visit_begin_object(semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ return destination_->begin_object(tag, context, ec);
+ }
+
+ bool visit_begin_object(std::size_t length,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ return destination_->begin_object(length, tag, context, ec);
+ }
+
+ bool visit_end_object(const ser_context& context, std::error_code& ec) override
+ {
+ return destination_->end_object(context, ec);
+ }
+
+ bool visit_begin_array(semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ return destination_->begin_array(tag, context, ec);
+ }
+
+ bool visit_begin_array(std::size_t length,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ return destination_->begin_array(length, tag, context, ec);
+ }
+
+ bool visit_end_array(const ser_context& context, std::error_code& ec) override
+ {
+ return destination_->end_array(context, ec);
+ }
+
+ bool visit_byte_string(const byte_string_view& b,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ return destination_->byte_string_value(b, tag, context, ec);
+ }
+
+ bool visit_byte_string(const byte_string_view& b,
+ uint64_t ext_tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ return destination_->byte_string_value(b, ext_tag, context, ec);
+ }
+
+ bool visit_half(uint16_t value, semantic_tag tag, const ser_context& context, std::error_code& ec) override
+ {
+ return destination_->half_value(value, tag, context, ec);
+ }
+
+ bool visit_double(double value, semantic_tag tag, const ser_context& context, std::error_code& ec) override
+ {
+ return destination_->double_value(value, tag, context, ec);
+ }
+
+ bool visit_int64(int64_t value, semantic_tag tag, const ser_context& context, std::error_code& ec) override
+ {
+ return destination_->int64_value(value, tag, context, ec);
+ }
+
+ bool visit_uint64(uint64_t value, semantic_tag tag, const ser_context& context, std::error_code& ec) override
+ {
+ return destination_->uint64_value(value, tag, context, ec);
+ }
+
+ bool visit_bool(bool value, semantic_tag tag, const ser_context& context, std::error_code& ec) override
+ {
+ return destination_->bool_value(value, tag, context, ec);
+ }
+
+ bool visit_null(semantic_tag tag, const ser_context& context, std::error_code& ec) override
+ {
+ return destination_->null_value(tag, context, ec);
+ }
+
+ bool visit_typed_array(const jsoncons::span<const uint8_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ return destination_->typed_array(s, tag, context, ec);
+ }
+
+ bool visit_typed_array(const jsoncons::span<const uint16_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ return destination_->typed_array(s, tag, context, ec);
+ }
+
+ bool visit_typed_array(const jsoncons::span<const uint32_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ return destination_->typed_array(s, tag, context, ec);
+ }
+
+ bool visit_typed_array(const jsoncons::span<const uint64_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ return destination_->typed_array(s, tag, context, ec);
+ }
+
+ bool visit_typed_array(const jsoncons::span<const int8_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ return destination_->typed_array(s, tag, context, ec);
+ }
+
+ bool visit_typed_array(const jsoncons::span<const int16_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ return destination_->typed_array(s, tag, context, ec);
+ }
+
+ bool visit_typed_array(const jsoncons::span<const int32_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ return destination_->typed_array(s, tag, context, ec);
+ }
+
+ bool visit_typed_array(const jsoncons::span<const int64_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ return destination_->typed_array(s, tag, context, ec);
+ }
+
+ bool visit_typed_array(half_arg_t,
+ const jsoncons::span<const uint16_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ return destination_->typed_array(half_arg, s, tag, context, ec);
+ }
+
+ bool visit_typed_array(const jsoncons::span<const float>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ return destination_->typed_array(s, tag, context, ec);
+ }
+
+ bool visit_typed_array(const jsoncons::span<const double>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ return destination_->typed_array(s, tag, context, ec);
+ }
+
+ bool visit_begin_multi_dim(const jsoncons::span<const size_t>& shape,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ return destination_->begin_multi_dim(shape, tag, context, ec);
+ }
+
+ bool visit_end_multi_dim(const ser_context& context,
+ std::error_code& ec) override
+ {
+ return destination_->end_multi_dim(context, ec);
+ }
+
+};
+
+template <class From,class To,class Enable=void>
+class json_visitor_adaptor
+{
+};
+
+template <class From,class To>
+class json_visitor_adaptor<From,To,typename std::enable_if<type_traits::is_narrow_character<typename From::char_type>::value &&
+ type_traits::is_narrow_character<typename To::char_type>::value>::type> : public json_visitor_adaptor_base<From,To>
+{
+ using supertype = json_visitor_adaptor_base<From,To>;
+ using to_char_type = typename To::char_type;
+ using from_char_type = typename From::char_type;
+public:
+ using typename From::string_view_type;
+ using supertype::destination;
+private:
+
+ // noncopyable
+ json_visitor_adaptor(const json_visitor_adaptor&) = delete;
+ json_visitor_adaptor& operator=(const json_visitor_adaptor&) = delete;
+public:
+ json_visitor_adaptor(To& visitor)
+ : supertype(visitor)
+ {
+ }
+
+ // moveable
+ json_visitor_adaptor(json_visitor_adaptor&&) = default;
+ json_visitor_adaptor& operator=(json_visitor_adaptor&&) = default;
+
+private:
+
+ bool visit_key(const string_view_type& key,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ return destination().key(string_view_type(reinterpret_cast<const to_char_type*>(key.data()),key.size()), context, ec);
+ }
+
+ bool visit_string(const string_view_type& value,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ return destination().string_value(string_view_type(reinterpret_cast<const to_char_type*>(value.data()),value.size()), tag, context, ec);
+ }
+};
+
+template <class From,class To>
+class json_visitor_adaptor<From,To,typename std::enable_if<!(type_traits::is_narrow_character<typename From::char_type>::value &&
+ type_traits::is_narrow_character<typename To::char_type>::value)>::type> : public json_visitor_adaptor_base<From,To>
+{
+ using supertype = json_visitor_adaptor_base<From,To>;
+public:
+ using typename From::string_view_type;
+ using supertype::destination;
+private:
+
+ // noncopyable
+ json_visitor_adaptor(const json_visitor_adaptor&) = delete;
+ json_visitor_adaptor& operator=(const json_visitor_adaptor&) = delete;
+public:
+ json_visitor_adaptor(To& visitor)
+ : supertype(visitor)
+ {
+ }
+
+ // moveable
+ json_visitor_adaptor(json_visitor_adaptor&&) = default;
+ json_visitor_adaptor& operator=(json_visitor_adaptor&&) = default;
+
+private:
+
+ bool visit_key(const string_view_type& name,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ std::basic_string<typename To::char_type> target;
+ auto result = unicode_traits::convert(name.data(), name.size(), target, unicode_traits::conv_flags::strict);
+ if (result.ec != unicode_traits::conv_errc())
+ {
+ ec = result.ec;
+ }
+ return destination().key(target, context, ec);
+ }
+
+ bool visit_string(const string_view_type& value,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ std::basic_string<typename To::char_type> target;
+ auto result = unicode_traits::convert(value.data(), value.size(),
+ target,unicode_traits::conv_flags::strict);
+ if (result.ec != unicode_traits::conv_errc())
+ {
+ JSONCONS_THROW(ser_error(result.ec));
+ }
+ return destination().string_value(target, tag, context, ec);
+ }
+};
+
+template <class From,class To>
+json_visitor_adaptor<From,To> make_json_visitor_adaptor(To& to)
+{
+ return json_visitor_adaptor<From, To>(to);
+}
+
+using json_filter = basic_json_filter<char>;
+using wjson_filter = basic_json_filter<wchar_t>;
+using rename_object_key_filter = basic_rename_object_key_filter<char>;
+using wrename_object_key_filter = basic_rename_object_key_filter<wchar_t>;
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+template <class CharT>
+using basic_json_content_filter = basic_json_filter<CharT>;
+JSONCONS_DEPRECATED_MSG("Instead, use json_filter") typedef basic_json_filter<char> json_content_filter;
+JSONCONS_DEPRECATED_MSG("Instead, use wjson_filter") typedef basic_json_filter<wchar_t> wjson_content_filter;
+JSONCONS_DEPRECATED_MSG("Instead, use rename_object_key_filter") typedef basic_rename_object_key_filter<char> rename_name_filter;
+JSONCONS_DEPRECATED_MSG("Instead, use wrename_object_key_filter") typedef basic_rename_object_key_filter<wchar_t> wrename_name_filter;
+JSONCONS_DEPRECATED_MSG("Instead, use rename_object_key_filter") typedef basic_rename_object_key_filter<char> rename_object_member_filter;
+JSONCONS_DEPRECATED_MSG("Instead, use wrename_object_key_filter") typedef basic_rename_object_key_filter<wchar_t> wrename_object_member_filter;
+#endif
+
+}
+
+#endif
diff --git a/include/jsoncons/json_fwd.hpp b/include/jsoncons/json_fwd.hpp
new file mode 100644
index 0000000..d9b44cf
--- /dev/null
+++ b/include/jsoncons/json_fwd.hpp
@@ -0,0 +1,23 @@
+// Copyright 2013 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_FWD_HPP
+#define JSONCONS_JSON_FWD_HPP
+
+#include <memory> // std::allocator
+
+namespace jsoncons {
+
+struct sorted_policy;
+
+template <class CharT,
+ class ImplementationPolicy = sorted_policy,
+ class Allocator = std::allocator<CharT>>
+class basic_json;
+
+}
+
+#endif
diff --git a/include/jsoncons/json_object.hpp b/include/jsoncons/json_object.hpp
new file mode 100644
index 0000000..76e2cd0
--- /dev/null
+++ b/include/jsoncons/json_object.hpp
@@ -0,0 +1,1772 @@
+// Copyright 2013 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_OBJECT_HPP
+#define JSONCONS_JSON_OBJECT_HPP
+
+#include <string>
+#include <vector>
+#include <tuple>
+#include <exception>
+#include <cstring>
+#include <algorithm> // std::sort, std::stable_sort, std::lower_bound, std::unique
+#include <utility>
+#include <initializer_list>
+#include <iterator> // std::iterator_traits
+#include <memory> // std::allocator
+#include <utility> // std::move
+#include <cassert> // assert
+#include <type_traits> // std::enable_if
+#include <jsoncons/json_exception.hpp>
+#include <jsoncons/allocator_holder.hpp>
+#include <jsoncons/json_array.hpp>
+
+namespace jsoncons {
+
+ struct sorted_unique_range_tag
+ {
+ explicit sorted_unique_range_tag() = default;
+ };
+
+ // key_value
+
+ template <class KeyT, class ValueT>
+ class key_value
+ {
+ public:
+ using key_type = KeyT;
+ using value_type = ValueT;
+ using allocator_type = typename ValueT::allocator_type;
+ using string_view_type = typename value_type::string_view_type;
+ private:
+
+ key_type key_;
+ value_type value_;
+ public:
+
+ key_value() noexcept
+ {
+ }
+
+ key_value(const key_type& name, const value_type& val)
+ : key_(name), value_(val)
+ {
+ }
+
+ key_value(const string_view_type& name)
+ : key_(name)
+ {
+ }
+
+ template <typename... Args>
+ key_value(const key_type& name, Args&& ... args)
+ : key_(name), value_(std::forward<Args>(args)...)
+ {
+ }
+
+ template <typename... Args>
+ key_value(key_type&& name, Args&& ... args) noexcept
+ : key_(std::forward<key_type>(name)), value_(std::forward<Args>(args)...)
+ {
+ }
+
+ key_value(const key_value& member)
+ : key_(member.key_), value_(member.value_)
+ {
+ }
+
+ key_value(key_value&& member) noexcept
+ : key_(std::move(member.key_)), value_(std::move(member.value_))
+ {
+ }
+ template <typename... U1, typename... U2>
+ JSONCONS_CONSTEXPR key_value(std::piecewise_construct_t /*unused*/, std::tuple<U1...> a,
+ std::tuple<U2...>
+ b) noexcept(noexcept(key_value(std::declval<std::tuple<U1...>&>(),
+ std::declval<std::tuple<U2...>&>(),
+ jsoncons::type_traits::index_sequence_for<U1...>(),
+ jsoncons::type_traits::index_sequence_for<U2...>())))
+ : key_value(a, b, jsoncons::type_traits::index_sequence_for<U1...>(),
+ jsoncons::type_traits::index_sequence_for<U2...>()) {
+ }
+
+ template <typename... U1, size_t... I1, typename... U2, size_t... I2>
+ key_value(std::tuple<U1...>& a, std::tuple<U2...>& b, jsoncons::type_traits::index_sequence<I1...> /*unused*/, jsoncons::type_traits::index_sequence<I2...> /*unused*/) noexcept(
+ noexcept(KeyT(std::forward<U1>(std::get<I1>(
+ std::declval<std::tuple<
+ U1...>&>()))...)) && noexcept(ValueT(std::
+ forward<U2>(std::get<I2>(
+ std::declval<std::tuple<U2...>&>()))...)))
+ : key_(std::forward<U1>(std::get<I1>(a))...)
+ , value_(std::forward<U2>(std::get<I2>(b))...) {
+ // make visual studio compiler happy about warning about unused a & b.
+ // Visual studio's key_value implementation disables warning 4100.
+ (void)a;
+ (void)b;
+ }
+
+ const key_type& key() const
+ {
+ return key_;
+ }
+
+ value_type& value()
+ {
+ return value_;
+ }
+
+ const value_type& value() const
+ {
+ return value_;
+ }
+
+ template <class T>
+ void value(T&& value)
+ {
+ value_ = std::forward<T>(value);
+ }
+
+ void swap(key_value& member) noexcept
+ {
+ key_.swap(member.key_);
+ value_.swap(member.value_);
+ }
+
+ key_value& operator=(const key_value& member)
+ {
+ if (this != & member)
+ {
+ key_ = member.key_;
+ value_ = member.value_;
+ }
+ return *this;
+ }
+
+ key_value& operator=(key_value&& member) noexcept
+ {
+ if (this != &member)
+ {
+ key_.swap(member.key_);
+ value_.swap(member.value_);
+ }
+ return *this;
+ }
+
+ void shrink_to_fit()
+ {
+ key_.shrink_to_fit();
+ value_.shrink_to_fit();
+ }
+ #if !defined(JSONCONS_NO_DEPRECATED)
+ JSONCONS_DEPRECATED_MSG("Instead, use key()")
+ const key_type& name() const
+ {
+ return key_;
+ }
+ #endif
+
+ friend bool operator==(const key_value& lhs, const key_value& rhs) noexcept
+ {
+ return lhs.key_ == rhs.key_ && lhs.value_ == rhs.value_;
+ }
+
+ friend bool operator!=(const key_value& lhs, const key_value& rhs) noexcept
+ {
+ return !(lhs == rhs);
+ }
+
+ friend bool operator<(const key_value& lhs, const key_value& rhs) noexcept
+ {
+ if (lhs.key_ < rhs.key_)
+ {
+ return true;
+ }
+ if (lhs.key_ == rhs.key_ && lhs.value_ < rhs.value_)
+ {
+ return true;
+ }
+ return false;
+ }
+
+ friend bool operator<=(const key_value& lhs, const key_value& rhs) noexcept
+ {
+ return !(rhs < lhs);
+ }
+
+ friend bool operator>(const key_value& lhs, const key_value& rhs) noexcept
+ {
+ return !(lhs <= rhs);
+ }
+
+ friend bool operator>=(const key_value& lhs, const key_value& rhs) noexcept
+ {
+ return !(lhs < rhs);
+ }
+
+ friend void swap(key_value& a, key_value& b) noexcept(
+ noexcept(std::declval<key_value&>().swap(std::declval<key_value&>())))
+ {
+ a.swap(b);
+ }
+ };
+
+ template <class KeyT, class ValueT>
+ struct get_key_value
+ {
+ using key_value_type = key_value<KeyT,ValueT>;
+
+ template <class T1,class T2>
+ key_value_type operator()(const std::pair<T1,T2>& p)
+ {
+ return key_value_type(p.first,p.second);
+ }
+ template <class T1,class T2>
+ key_value_type operator()(std::pair<T1,T2>&& p)
+ {
+ return key_value_type(std::forward<T1>(p.first),std::forward<T2>(p.second));
+ }
+ template <class T1,class T2>
+ const key_value_type& operator()(const key_value<T1,T2>& p)
+ {
+ return p;
+ }
+ template <class T1,class T2>
+ key_value_type operator()(key_value<T1,T2>&& p)
+ {
+ return std::move(p);
+ }
+ };
+
+ struct sort_key_order
+ {
+ explicit sort_key_order() = default;
+ };
+
+ struct preserve_key_order
+ {
+ explicit preserve_key_order() = default;
+ };
+
+
+ // Sort keys
+ template <class KeyT,class Json,template<typename,typename> class SequenceContainer = std::vector>
+ class sorted_json_object : public allocator_holder<typename Json::allocator_type>
+ {
+ public:
+ using allocator_type = typename Json::allocator_type;
+ using key_type = KeyT;
+ using key_value_type = key_value<KeyT,Json>;
+ using char_type = typename Json::char_type;
+ using string_view_type = typename Json::string_view_type;
+ private:
+ struct Comp
+ {
+ bool operator() (const key_value_type& kv, string_view_type k) const { return kv.key() < k; }
+ bool operator() (string_view_type k, const key_value_type& kv) const { return k < kv.key(); }
+ };
+
+ using key_value_allocator_type = typename std::allocator_traits<allocator_type>:: template rebind_alloc<key_value_type>;
+ using key_value_container_type = SequenceContainer<key_value_type,key_value_allocator_type>;
+
+ key_value_container_type members_;
+ public:
+ using iterator = typename key_value_container_type::iterator;
+ using const_iterator = typename key_value_container_type::const_iterator;
+
+ using allocator_holder<allocator_type>::get_allocator;
+
+ sorted_json_object()
+ {
+ }
+
+ explicit sorted_json_object(const allocator_type& alloc)
+ : allocator_holder<allocator_type>(alloc),
+ members_(key_value_allocator_type(alloc))
+ {
+ }
+
+ sorted_json_object(const sorted_json_object& val)
+ : allocator_holder<allocator_type>(val.get_allocator()),
+ members_(val.members_)
+ {
+ }
+
+ sorted_json_object(sorted_json_object&& val)
+ : allocator_holder<allocator_type>(val.get_allocator()),
+ members_(std::move(val.members_))
+ {
+ }
+
+ sorted_json_object& operator=(const sorted_json_object& val)
+ {
+ allocator_holder<allocator_type>::operator=(val.get_allocator());
+ members_ = val.members_;
+ return *this;
+ }
+
+ sorted_json_object& operator=(sorted_json_object&& val)
+ {
+ val.swap(*this);
+ return *this;
+ }
+
+ sorted_json_object(const sorted_json_object& val, const allocator_type& alloc)
+ : allocator_holder<allocator_type>(alloc),
+ members_(val.members_,key_value_allocator_type(alloc))
+ {
+ }
+
+ sorted_json_object(sorted_json_object&& val,const allocator_type& alloc)
+ : allocator_holder<allocator_type>(alloc), members_(std::move(val.members_),key_value_allocator_type(alloc))
+ {
+ }
+
+ template<class InputIt>
+ sorted_json_object(InputIt first, InputIt last)
+ {
+ std::size_t count = std::distance(first,last);
+ members_.reserve(count);
+ for (auto s = first; s != last; ++s)
+ {
+ members_.emplace_back(get_key_value<KeyT,Json>()(*s));
+ }
+ 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;});
+ auto it = std::unique(members_.begin(), members_.end(),
+ [](const key_value_type& a, const key_value_type& b) -> bool { return !(a.key().compare(b.key()));});
+ members_.erase(it, members_.end());
+ }
+
+ template<class InputIt>
+ sorted_json_object(InputIt first, InputIt last,
+ const allocator_type& alloc)
+ : allocator_holder<allocator_type>(alloc),
+ members_(key_value_allocator_type(alloc))
+ {
+ std::size_t count = std::distance(first,last);
+ members_.reserve(count);
+ for (auto s = first; s != last; ++s)
+ {
+ members_.emplace_back(get_key_value<KeyT,Json>()(*s));
+ }
+ 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;});
+ auto it = std::unique(members_.begin(), members_.end(),
+ [](const key_value_type& a, const key_value_type& b) -> bool { return !(a.key().compare(b.key()));});
+ members_.erase(it, members_.end());
+ }
+
+ sorted_json_object(const std::initializer_list<std::pair<std::basic_string<char_type>,Json>>& init,
+ const allocator_type& alloc = allocator_type())
+ : allocator_holder<allocator_type>(alloc),
+ members_(key_value_allocator_type(alloc))
+ {
+ members_.reserve(init.size());
+ for (auto& item : init)
+ {
+ insert_or_assign(item.first, item.second);
+ }
+ }
+
+ ~sorted_json_object() noexcept
+ {
+ flatten_and_destroy();
+ }
+
+ bool empty() const
+ {
+ return members_.empty();
+ }
+
+ void swap(sorted_json_object& val) noexcept
+ {
+ members_.swap(val.members_);
+ }
+
+ iterator begin()
+ {
+ return members_.begin();
+ }
+
+ iterator end()
+ {
+ return members_.end();
+ }
+
+ const_iterator begin() const
+ {
+ return members_.begin();
+ }
+
+ const_iterator end() const
+ {
+ return members_.end();
+ }
+
+ std::size_t size() const {return members_.size();}
+
+ std::size_t capacity() const {return members_.capacity();}
+
+ void clear() {members_.clear();}
+
+ void shrink_to_fit()
+ {
+ for (std::size_t i = 0; i < members_.size(); ++i)
+ {
+ members_[i].shrink_to_fit();
+ }
+ members_.shrink_to_fit();
+ }
+
+ void reserve(std::size_t n) {members_.reserve(n);}
+
+ Json& at(std::size_t i)
+ {
+ if (i >= members_.size())
+ {
+ JSONCONS_THROW(json_runtime_error<std::out_of_range>("Invalid array subscript"));
+ }
+ return members_[i].value();
+ }
+
+ const Json& at(std::size_t i) const
+ {
+ if (i >= members_.size())
+ {
+ JSONCONS_THROW(json_runtime_error<std::out_of_range>("Invalid array subscript"));
+ }
+ return members_[i].value();
+ }
+
+ iterator find(const string_view_type& name) noexcept
+ {
+ auto p = std::equal_range(members_.begin(),members_.end(), name,
+ Comp());
+ return p.first == p.second ? members_.end() : p.first;
+ }
+
+ const_iterator find(const string_view_type& name) const noexcept
+ {
+ auto p = std::equal_range(members_.begin(),members_.end(), name,
+ Comp());
+ return p.first == p.second ? members_.end() : p.first;
+ }
+
+ iterator erase(const_iterator pos)
+ {
+ #if defined(JSONCONS_NO_VECTOR_ERASE_TAKES_CONST_ITERATOR)
+ iterator it = members_.begin() + (pos - members_.begin());
+ return members_.erase(it);
+ #else
+ return members_.erase(pos);
+ #endif
+ }
+
+ iterator erase(const_iterator first, const_iterator last)
+ {
+ #if defined(JSONCONS_NO_VECTOR_ERASE_TAKES_CONST_ITERATOR)
+ iterator it1 = members_.begin() + (first - members_.begin());
+ iterator it2 = members_.begin() + (last - members_.begin());
+ return members_.erase(it1,it2);
+ #else
+ return members_.erase(first,last);
+ #endif
+ }
+
+ void erase(const string_view_type& name)
+ {
+ auto it = find(name);
+ if (it != members_.end())
+ {
+ members_.erase(it);
+ }
+ }
+
+ template<class InputIt, class Convert>
+ void insert(InputIt first, InputIt last, Convert convert)
+ {
+ std::size_t count = std::distance(first,last);
+ members_.reserve(members_.size() + count);
+ for (auto s = first; s != last; ++s)
+ {
+ members_.emplace_back(convert(*s));
+ }
+ 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;});
+ auto it = std::unique(members_.begin(), members_.end(),
+ [](const key_value_type& a, const key_value_type& b) -> bool { return !(a.key().compare(b.key()));});
+ members_.erase(it, members_.end());
+ }
+
+ template<class InputIt, class Convert>
+ void insert(sorted_unique_range_tag, InputIt first, InputIt last, Convert convert)
+ {
+ if (first != last)
+ {
+ std::size_t count = std::distance(first,last);
+ members_.reserve(members_.size() + count);
+
+ auto it = find(convert(*first).key());
+ if (it != members_.end())
+ {
+ for (auto s = first; s != last; ++s)
+ {
+ it = members_.emplace(it, convert(*s));
+ }
+ }
+ else
+ {
+ for (auto s = first; s != last; ++s)
+ {
+ members_.emplace_back(convert(*s));
+ }
+ }
+ }
+ }
+
+ // insert_or_assign
+
+ template <class T, class A=allocator_type>
+ typename std::enable_if<type_traits::is_stateless<A>::value,std::pair<iterator,bool>>::type
+ insert_or_assign(const string_view_type& name, T&& value)
+ {
+ bool inserted;
+ auto it = std::lower_bound(members_.begin(),members_.end(), name,
+ Comp());
+ if (it == members_.end())
+ {
+ members_.emplace_back(key_type(name.begin(),name.end()),
+ std::forward<T>(value));
+ inserted = true;
+ it = members_.begin() + members_.size() - 1;
+ }
+ else if (it->key() == name)
+ {
+ it->value(Json(std::forward<T>(value)));
+ inserted = false; // assigned
+ }
+ else
+ {
+ it = members_.emplace(it,
+ key_type(name.begin(),name.end()),
+ std::forward<T>(value));
+ inserted = true;
+ }
+ return std::make_pair(it,inserted);
+ }
+
+ template <class T, class A=allocator_type>
+ typename std::enable_if<!type_traits::is_stateless<A>::value,std::pair<iterator,bool>>::type
+ insert_or_assign(const string_view_type& name, T&& value)
+ {
+ bool inserted;
+ auto it = std::lower_bound(members_.begin(),members_.end(), name,
+ Comp());
+ if (it == members_.end())
+ {
+ members_.emplace_back(key_type(name.begin(),name.end(), get_allocator()),
+ std::forward<T>(value),get_allocator());
+ inserted = true;
+ it = members_.begin() + members_.size() - 1;
+ }
+ else if (it->key() == name)
+ {
+ it->value(Json(std::forward<T>(value), get_allocator()));
+ inserted = false; // assigned
+ }
+ else
+ {
+ it = members_.emplace(it,
+ key_type(name.begin(),name.end(), get_allocator()),
+ std::forward<T>(value),get_allocator());
+ inserted = true;
+ }
+ return std::make_pair(it,inserted);
+ }
+
+ // try_emplace
+
+ template <class A=allocator_type, class... Args>
+ typename std::enable_if<type_traits::is_stateless<A>::value,std::pair<iterator,bool>>::type
+ try_emplace(const string_view_type& name, Args&&... args)
+ {
+ bool inserted;
+ auto it = std::lower_bound(members_.begin(),members_.end(), name,
+ Comp());
+ if (it == members_.end())
+ {
+ members_.emplace_back(key_type(name.begin(),name.end()),
+ std::forward<Args>(args)...);
+ it = members_.begin() + members_.size() - 1;
+ inserted = true;
+ }
+ else if (it->key() == name)
+ {
+ inserted = false;
+ }
+ else
+ {
+ it = members_.emplace(it,
+ key_type(name.begin(),name.end()),
+ std::forward<Args>(args)...);
+ inserted = true;
+ }
+ return std::make_pair(it,inserted);
+ }
+
+ template <class A=allocator_type, class... Args>
+ typename std::enable_if<!type_traits::is_stateless<A>::value,std::pair<iterator,bool>>::type
+ try_emplace(const string_view_type& name, Args&&... args)
+ {
+ bool inserted;
+ auto it = std::lower_bound(members_.begin(),members_.end(), name,
+ Comp());
+ if (it == members_.end())
+ {
+ members_.emplace_back(key_type(name.begin(),name.end(), get_allocator()),
+ std::forward<Args>(args)...);
+ it = members_.begin() + members_.size() - 1;
+ inserted = true;
+ }
+ else if (it->key() == name)
+ {
+ inserted = false;
+ }
+ else
+ {
+ it = members_.emplace(it,
+ key_type(name.begin(),name.end(), get_allocator()),
+ std::forward<Args>(args)...);
+ inserted = true;
+ }
+ return std::make_pair(it,inserted);
+ }
+
+ template <class A=allocator_type, class ... Args>
+ typename std::enable_if<type_traits::is_stateless<A>::value,iterator>::type
+ try_emplace(iterator hint, const string_view_type& name, Args&&... args)
+ {
+ iterator it = hint;
+
+ if (hint != members_.end() && hint->key() <= name)
+ {
+ it = std::lower_bound(hint,members_.end(), name,
+ Comp());
+ }
+ else
+ {
+ it = std::lower_bound(members_.begin(),members_.end(), name,
+ Comp());
+ }
+
+ if (it == members_.end())
+ {
+ members_.emplace_back(key_type(name.begin(),name.end()),
+ std::forward<Args>(args)...);
+ it = members_.begin() + (members_.size() - 1);
+ }
+ else if (it->key() == name)
+ {
+ }
+ else
+ {
+ it = members_.emplace(it,
+ key_type(name.begin(),name.end()),
+ std::forward<Args>(args)...);
+ }
+
+ return it;
+ }
+
+ template <class A=allocator_type, class ... Args>
+ typename std::enable_if<!type_traits::is_stateless<A>::value,iterator>::type
+ try_emplace(iterator hint, const string_view_type& name, Args&&... args)
+ {
+ iterator it = hint;
+ if (hint != members_.end() && hint->key() <= name)
+ {
+ it = std::lower_bound(hint,members_.end(), name,
+ Comp());
+ }
+ else
+ {
+ it = std::lower_bound(members_.begin(),members_.end(), name,
+ Comp());
+ }
+
+ if (it == members_.end())
+ {
+ members_.emplace_back(key_type(name.begin(),name.end(), get_allocator()),
+ std::forward<Args>(args)...);
+ it = members_.begin() + (members_.size() - 1);
+ }
+ else if (it->key() == name)
+ {
+ }
+ else
+ {
+ it = members_.emplace(it,
+ key_type(name.begin(),name.end(), get_allocator()),
+ std::forward<Args>(args)...);
+ }
+ return it;
+ }
+
+ // insert_or_assign
+
+ template <class T, class A=allocator_type>
+ typename std::enable_if<type_traits::is_stateless<A>::value,iterator>::type
+ insert_or_assign(iterator hint, const string_view_type& name, T&& value)
+ {
+ iterator it;
+ if (hint != members_.end() && hint->key() <= name)
+ {
+ it = std::lower_bound(hint,members_.end(), name,
+ [](const key_value_type& a, const string_view_type& k) -> bool {return string_view_type(a.key()).compare(k) < 0;});
+ }
+ else
+ {
+ it = std::lower_bound(members_.begin(),members_.end(), name,
+ [](const key_value_type& a, const string_view_type& k) -> bool {return string_view_type(a.key()).compare(k) < 0;});
+ }
+
+ if (it == members_.end())
+ {
+ members_.emplace_back(key_type(name.begin(),name.end()),
+ std::forward<T>(value));
+ it = members_.begin() + (members_.size() - 1);
+ }
+ else if (it->key() == name)
+ {
+ it->value(Json(std::forward<T>(value)));
+ }
+ else
+ {
+ it = members_.emplace(it,
+ key_type(name.begin(),name.end()),
+ std::forward<T>(value));
+ }
+ return it;
+ }
+
+ template <class T, class A=allocator_type>
+ typename std::enable_if<!type_traits::is_stateless<A>::value,iterator>::type
+ insert_or_assign(iterator hint, const string_view_type& name, T&& value)
+ {
+ iterator it;
+ if (hint != members_.end() && hint->key() <= name)
+ {
+ it = std::lower_bound(hint,members_.end(), name,
+ Comp());
+ }
+ else
+ {
+ it = std::lower_bound(members_.begin(),members_.end(), name,
+ Comp());
+ }
+
+ if (it == members_.end())
+ {
+ members_.emplace_back(key_type(name.begin(),name.end(), get_allocator()),
+ std::forward<T>(value),get_allocator());
+ it = members_.begin() + (members_.size() - 1);
+ }
+ else if (it->key() == name)
+ {
+ it->value(Json(std::forward<T>(value),get_allocator()));
+ }
+ else
+ {
+ it = members_.emplace(it,
+ key_type(name.begin(),name.end(), get_allocator()),
+ std::forward<T>(value),get_allocator());
+ }
+ return it;
+ }
+
+ // merge
+
+ void merge(const sorted_json_object& source)
+ {
+ for (auto it = source.begin(); it != source.end(); ++it)
+ {
+ try_emplace(it->key(),it->value());
+ }
+ }
+
+ void merge(sorted_json_object&& source)
+ {
+ auto it = std::make_move_iterator(source.begin());
+ auto end = std::make_move_iterator(source.end());
+ for (; it != end; ++it)
+ {
+ auto pos = std::lower_bound(members_.begin(),members_.end(), (*it).key(),
+ Comp());
+ if (pos == members_.end() )
+ {
+ members_.emplace_back(*it);
+ }
+ else if ((*it).key() != pos->key())
+ {
+ members_.emplace(pos,*it);
+ }
+ }
+ }
+
+ void merge(iterator hint, const sorted_json_object& source)
+ {
+ for (auto it = source.begin(); it != source.end(); ++it)
+ {
+ hint = try_emplace(hint, (*it).key(),(*it).value());
+ }
+ }
+
+ void merge(iterator hint, sorted_json_object&& source)
+ {
+ auto it = std::make_move_iterator(source.begin());
+ auto end = std::make_move_iterator(source.end());
+ for (; it != end; ++it)
+ {
+ iterator pos;
+ if (hint != members_.end() && hint->key() <= (*it).key())
+ {
+ pos = std::lower_bound(hint,members_.end(), (*it).key(),
+ Comp());
+ }
+ else
+ {
+ pos = std::lower_bound(members_.begin(),members_.end(), (*it).key(),
+ Comp());
+ }
+ if (pos == members_.end() )
+ {
+ members_.emplace_back(*it);
+ hint = members_.begin() + (members_.size() - 1);
+ }
+ else if ((*it).key() != pos->key())
+ {
+ hint = members_.emplace(pos,*it);
+ }
+ }
+ }
+
+ // merge_or_update
+
+ void merge_or_update(const sorted_json_object& source)
+ {
+ for (auto it = source.begin(); it != source.end(); ++it)
+ {
+ insert_or_assign((*it).key(),(*it).value());
+ }
+ }
+
+ void merge_or_update(sorted_json_object&& source)
+ {
+ auto it = std::make_move_iterator(source.begin());
+ auto end = std::make_move_iterator(source.end());
+ for (; it != end; ++it)
+ {
+ auto pos = std::lower_bound(members_.begin(),members_.end(), (*it).key(),
+ Comp());
+ if (pos == members_.end() )
+ {
+ members_.emplace_back(*it);
+ }
+ else
+ {
+ pos->value((*it).value());
+ }
+ }
+ }
+
+ void merge_or_update(iterator hint, const sorted_json_object& source)
+ {
+ for (auto it = source.begin(); it != source.end(); ++it)
+ {
+ hint = insert_or_assign(hint, (*it).key(),(*it).value());
+ }
+ }
+
+ void merge_or_update(iterator hint, sorted_json_object&& source)
+ {
+ auto it = std::make_move_iterator(source.begin());
+ auto end = std::make_move_iterator(source.end());
+ for (; it != end; ++it)
+ {
+ iterator pos;
+ if (hint != members_.end() && hint->key() <= (*it).key())
+ {
+ pos = std::lower_bound(hint,members_.end(), (*it).key(),
+ Comp());
+ }
+ else
+ {
+ pos = std::lower_bound(members_.begin(),members_.end(), (*it).key(),
+ Comp());
+ }
+ if (pos == members_.end() )
+ {
+ members_.emplace_back(*it);
+ hint = members_.begin() + (members_.size() - 1);
+ }
+ else
+ {
+ pos->value((*it).value());
+ hint = pos;
+ }
+ }
+ }
+
+ bool operator==(const sorted_json_object& rhs) const
+ {
+ return members_ == rhs.members_;
+ }
+
+ bool operator<(const sorted_json_object& rhs) const
+ {
+ return members_ < rhs.members_;
+ }
+ private:
+
+ void flatten_and_destroy() noexcept
+ {
+ if (!members_.empty())
+ {
+ json_array<Json> temp(get_allocator());
+
+ for (auto& kv : members_)
+ {
+ switch (kv.value().storage_kind())
+ {
+ case json_storage_kind::array_value:
+ case json_storage_kind::object_value:
+ if (!kv.value().empty())
+ {
+ temp.emplace_back(std::move(kv.value()));
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+ };
+
+ // Preserve order
+ template <class KeyT,class Json,template<typename,typename> class SequenceContainer = std::vector>
+ class order_preserving_json_object : public allocator_holder<typename Json::allocator_type>
+ {
+ public:
+ using allocator_type = typename Json::allocator_type;
+ using char_type = typename Json::char_type;
+ using key_type = KeyT;
+ //using mapped_type = Json;
+ using string_view_type = typename Json::string_view_type;
+ using key_value_type = key_value<KeyT,Json>;
+ //using implementation_policy = typename Json::implementation_policy;
+ private:
+
+ using key_value_allocator_type = typename std::allocator_traits<allocator_type>:: template rebind_alloc<key_value_type>;
+ using key_value_container_type = SequenceContainer<key_value_type,key_value_allocator_type>;
+ typedef typename std::allocator_traits<allocator_type>:: template rebind_alloc<std::size_t> index_allocator_type;
+ //using index_container_type = typename implementation_policy::template sequence_container_type<std::size_t,index_allocator_type>;
+ using index_container_type = SequenceContainer<std::size_t,index_allocator_type>;
+
+ key_value_container_type members_;
+ index_container_type index_;
+
+ struct Comp
+ {
+ const key_value_container_type& members_;
+
+ Comp(const key_value_container_type& members_)
+ : members_(members_)
+ {
+ }
+
+ bool operator() (std::size_t i, string_view_type k) const { return members_.at(i).key() < k; }
+ bool operator() (string_view_type k, std::size_t i) const { return k < members_.at(i).key(); }
+ };
+ public:
+ using iterator = typename key_value_container_type::iterator;
+ using const_iterator = typename key_value_container_type::const_iterator;
+
+ using allocator_holder<allocator_type>::get_allocator;
+
+ order_preserving_json_object()
+ {
+ }
+ order_preserving_json_object(const allocator_type& alloc)
+ : allocator_holder<allocator_type>(alloc),
+ members_(key_value_allocator_type(alloc)),
+ index_(index_allocator_type(alloc))
+ {
+ }
+
+ order_preserving_json_object(const order_preserving_json_object& val)
+ : allocator_holder<allocator_type>(val.get_allocator()),
+ members_(val.members_),
+ index_(val.index_)
+ {
+ }
+
+ order_preserving_json_object(order_preserving_json_object&& val)
+ : allocator_holder<allocator_type>(val.get_allocator()),
+ members_(std::move(val.members_)),
+ index_(std::move(val.index_))
+ {
+ }
+
+ order_preserving_json_object(const order_preserving_json_object& val, const allocator_type& alloc)
+ : allocator_holder<allocator_type>(alloc),
+ members_(val.members_,key_value_allocator_type(alloc)),
+ index_(val.index_,index_allocator_type(alloc))
+ {
+ }
+
+ order_preserving_json_object(order_preserving_json_object&& val,const allocator_type& alloc)
+ : allocator_holder<allocator_type>(alloc),
+ members_(std::move(val.members_),key_value_allocator_type(alloc)),
+ index_(std::move(val.index_),index_allocator_type(alloc))
+ {
+ }
+
+ template<class InputIt>
+ order_preserving_json_object(InputIt first, InputIt last)
+ {
+ std::size_t count = std::distance(first,last);
+ members_.reserve(count);
+ for (auto s = first; s != last; ++s)
+ {
+ members_.emplace_back(get_key_value<KeyT,Json>()(*s));
+ }
+
+ build_index();
+ auto last_unique = std::unique(index_.begin(), index_.end(),
+ [&](std::size_t a, std::size_t b) { return !(members_.at(a).key().compare(members_.at(b).key())); });
+
+ if (last_unique != index_.end())
+ {
+ index_.erase(last_unique, index_.end());
+ std::sort(index_.begin(), index_.end());
+
+ auto result = index_.rbegin();
+ if (*result != members_.size())
+ {
+ members_.erase(members_.begin() + (*result + 1), members_.end());
+ }
+ for (auto it = index_.rbegin() + 1; it != index_.rend(); ++it, ++result)
+ {
+ if (*result - *it > 1)
+ {
+ members_.erase(members_.begin() + (*it + 1), members_.begin() + *result);
+ }
+ }
+ }
+ build_index();
+ }
+
+ template<class InputIt>
+ order_preserving_json_object(InputIt first, InputIt last,
+ const allocator_type& alloc)
+ : allocator_holder<allocator_type>(alloc),
+ members_(key_value_allocator_type(alloc)),
+ index_(index_allocator_type(alloc))
+ {
+ std::size_t count = std::distance(first,last);
+ members_.reserve(count);
+ for (auto s = first; s != last; ++s)
+ {
+ members_.emplace_back(get_key_value<KeyT,Json>()(*s));
+ }
+
+ build_index();
+ auto last_unique = std::unique(index_.begin(), index_.end(),
+ [&](std::size_t a, std::size_t b) { return !(members_.at(a).key().compare(members_.at(b).key())); });
+
+ if (last_unique != index_.end())
+ {
+ index_.erase(last_unique, index_.end());
+ std::sort(index_.begin(), index_.end());
+
+ auto result = index_.rbegin();
+ if (*result != members_.size())
+ {
+ members_.erase(members_.begin() + (*result + 1), members_.end());
+ }
+ for (auto it = index_.rbegin() + 1; it != index_.rend(); ++it, ++result)
+ {
+ if (*result - *it > 1)
+ {
+ members_.erase(members_.begin() + (*it + 1), members_.begin() + *result);
+ }
+ }
+ }
+ build_index();
+ }
+
+ order_preserving_json_object(std::initializer_list<std::pair<std::basic_string<char_type>,Json>> init,
+ const allocator_type& alloc = allocator_type())
+ : allocator_holder<allocator_type>(alloc),
+ members_(key_value_allocator_type(alloc)),
+ index_(index_allocator_type(alloc))
+ {
+ members_.reserve(init.size());
+ for (auto& item : init)
+ {
+ insert_or_assign(item.first, item.second);
+ }
+ }
+
+ ~order_preserving_json_object() noexcept
+ {
+ flatten_and_destroy();
+ }
+
+ order_preserving_json_object& operator=(order_preserving_json_object&& val)
+ {
+ val.swap(*this);
+ return *this;
+ }
+
+ order_preserving_json_object& operator=(const order_preserving_json_object& val)
+ {
+ allocator_holder<allocator_type>::operator=(val.get_allocator());
+ members_ = val.members_;
+ index_ = val.index_;
+ return *this;
+ }
+
+ void swap(order_preserving_json_object& val) noexcept
+ {
+ members_.swap(val.members_);
+ }
+
+ bool empty() const
+ {
+ return members_.empty();
+ }
+
+ iterator begin()
+ {
+ return members_.begin();
+ }
+
+ iterator end()
+ {
+ return members_.end();
+ }
+
+ const_iterator begin() const
+ {
+ return members_.begin();
+ }
+
+ const_iterator end() const
+ {
+ return members_.end();
+ }
+
+ std::size_t size() const {return members_.size();}
+
+ std::size_t capacity() const {return members_.capacity();}
+
+ void clear()
+ {
+ members_.clear();
+ index_.clear();
+ }
+
+ void shrink_to_fit()
+ {
+ for (std::size_t i = 0; i < members_.size(); ++i)
+ {
+ members_[i].shrink_to_fit();
+ }
+ members_.shrink_to_fit();
+ index_.shrink_to_fit();
+ }
+
+ void reserve(std::size_t n) {members_.reserve(n);}
+
+ Json& at(std::size_t i)
+ {
+ if (i >= members_.size())
+ {
+ JSONCONS_THROW(json_runtime_error<std::out_of_range>("Invalid array subscript"));
+ }
+ return members_[i].value();
+ }
+
+ const Json& at(std::size_t i) const
+ {
+ if (i >= members_.size())
+ {
+ JSONCONS_THROW(json_runtime_error<std::out_of_range>("Invalid array subscript"));
+ }
+ return members_[i].value();
+ }
+
+ iterator find(const string_view_type& name) noexcept
+ {
+ auto p = std::equal_range(index_.begin(),index_.end(), name,
+ Comp(members_));
+ return p.first == p.second ? members_.end() : members_.begin() + *p.first;
+ }
+
+ const_iterator find(const string_view_type& name) const noexcept
+ {
+ auto p = std::equal_range(index_.begin(),index_.end(), name,
+ Comp(members_));
+ return p.first == p.second ? members_.end() : members_.begin() + *p.first;
+ }
+
+ iterator erase(const_iterator pos)
+ {
+ if (pos != members_.end())
+ {
+ std::size_t pos1 = pos - members_.begin();
+ std::size_t pos2 = pos1 + 1;
+
+ erase_index_entries(pos1, pos2);
+ #if defined(JSONCONS_NO_VECTOR_ERASE_TAKES_CONST_ITERATOR)
+ iterator it = members_.begin() + (pos - members_.begin());
+ return members_.erase(it);
+ #else
+ return members_.erase(pos);
+ #endif
+ }
+ else
+ {
+ return members_.end();
+ }
+ }
+
+ iterator erase(const_iterator first, const_iterator last)
+ {
+ std::size_t pos1 = first == members_.end() ? members_.size() : first - members_.begin();
+ std::size_t pos2 = last == members_.end() ? members_.size() : last - members_.begin();
+
+ if (pos1 < members_.size() && pos2 <= members_.size())
+ {
+ erase_index_entries(pos1,pos2);
+
+ #if defined(JSONCONS_NO_VECTOR_ERASE_TAKES_CONST_ITERATOR)
+ iterator it1 = members_.begin() + (first - members_.begin());
+ iterator it2 = members_.begin() + (last - members_.begin());
+ return members_.erase(it1,it2);
+ #else
+ return members_.erase(first,last);
+ #endif
+ }
+ else
+ {
+ return members_.end();
+ }
+ }
+
+ void erase(const string_view_type& name)
+ {
+ auto pos = find(name);
+ if (pos != members_.end())
+ {
+ std::size_t pos1 = pos - members_.begin();
+ std::size_t pos2 = pos1 + 1;
+
+ erase_index_entries(pos1, pos2);
+ #if defined(JSONCONS_NO_VECTOR_ERASE_TAKES_CONST_ITERATOR)
+ iterator it = members_.begin() + (pos - members_.begin());
+ members_.erase(it);
+ #else
+ members_.erase(pos);
+ #endif
+ }
+ }
+
+ template<class InputIt, class Convert>
+ void insert(InputIt first, InputIt last, Convert convert)
+ {
+ std::size_t count = std::distance(first,last);
+ members_.reserve(members_.size() + count);
+ for (auto s = first; s != last; ++s)
+ {
+ members_.emplace_back(convert(*s));
+ }
+
+ build_index();
+ auto last_unique = std::unique(index_.begin(), index_.end(),
+ [&](std::size_t a, std::size_t b) { return !(members_.at(a).key().compare(members_.at(b).key())); });
+
+ if (last_unique != index_.end())
+ {
+ index_.erase(last_unique, index_.end());
+ std::sort(index_.begin(), index_.end());
+
+ auto result = index_.rbegin();
+ if (*result != members_.size())
+ {
+ members_.erase(members_.begin() + (*result + 1), members_.end());
+ }
+ for (auto it = index_.rbegin() + 1; it != index_.rend(); ++it, ++result)
+ {
+ if (*result - *it > 1)
+ {
+ members_.erase(members_.begin() + (*it + 1), members_.begin() + *result);
+ }
+ }
+ }
+ build_index();
+ }
+
+ template<class InputIt, class Convert>
+ void insert(sorted_unique_range_tag, InputIt first, InputIt last, Convert convert)
+ {
+ std::size_t count = std::distance(first,last);
+
+ members_.reserve(members_.size() + count);
+ for (auto s = first; s != last; ++s)
+ {
+ members_.emplace_back(convert(*s));
+ }
+
+ build_index();
+ }
+
+ template <class T, class A=allocator_type>
+ typename std::enable_if<type_traits::is_stateless<A>::value,std::pair<iterator,bool>>::type
+ insert_or_assign(const string_view_type& name, T&& value)
+ {
+ auto result = insert_index_entry(name,members_.size());
+ if (result.second)
+ {
+ members_.emplace_back(key_type(name.begin(), name.end()), std::forward<T>(value));
+ auto it = members_.begin() + result.first;
+ return std::make_pair(it,true);
+ }
+ else
+ {
+ auto it = members_.begin() + result.first;
+ it->value(Json(std::forward<T>(value)));
+ return std::make_pair(it,false);
+ }
+ }
+
+ template <class T, class A=allocator_type>
+ typename std::enable_if<!type_traits::is_stateless<A>::value,std::pair<iterator,bool>>::type
+ insert_or_assign(const string_view_type& name, T&& value)
+ {
+ auto result = insert_index_entry(name,members_.size());
+ if (result.second)
+ {
+ members_.emplace_back(key_type(name.begin(),name.end(),get_allocator()),
+ std::forward<T>(value),get_allocator());
+ auto it = members_.begin() + result.first;
+ return std::make_pair(it,true);
+ }
+ else
+ {
+ auto it = members_.begin() + result.first;
+ it->value(Json(std::forward<T>(value),get_allocator()));
+ return std::make_pair(it,false);
+ }
+ }
+
+ template <class A=allocator_type, class T>
+ typename std::enable_if<type_traits::is_stateless<A>::value,iterator>::type
+ insert_or_assign(iterator hint, const string_view_type& key, T&& value)
+ {
+ if (hint == members_.end())
+ {
+ auto result = insert_or_assign(key, std::forward<T>(value));
+ return result.first;
+ }
+ else
+ {
+ std::size_t pos = hint - members_.begin();
+ auto result = insert_index_entry(key,pos);
+
+ if (result.second)
+ {
+ auto it = members_.emplace(hint, key_type(key.begin(), key.end()), std::forward<T>(value));
+ return it;
+ }
+ else
+ {
+ auto it = members_.begin() + result.first;
+ it->value(Json(std::forward<T>(value)));
+ return it;
+ }
+ }
+ }
+
+ template <class A=allocator_type, class T>
+ typename std::enable_if<!type_traits::is_stateless<A>::value,iterator>::type
+ insert_or_assign(iterator hint, const string_view_type& key, T&& value)
+ {
+ if (hint == members_.end())
+ {
+ auto result = insert_or_assign(key, std::forward<T>(value));
+ return result.first;
+ }
+ else
+ {
+ std::size_t pos = hint - members_.begin();
+ auto result = insert_index_entry(key,pos);
+
+ if (result.second)
+ {
+ auto it = members_.emplace(hint,
+ key_type(key.begin(),key.end(),get_allocator()),
+ std::forward<T>(value),get_allocator());
+ return it;
+ }
+ else
+ {
+ auto it = members_.begin() + result.first;
+ it->value(Json(std::forward<T>(value),get_allocator()));
+ return it;
+ }
+ }
+ }
+
+ // merge
+
+ void merge(const order_preserving_json_object& source)
+ {
+ for (auto it = source.begin(); it != source.end(); ++it)
+ {
+ try_emplace((*it).key(),(*it).value());
+ }
+ }
+
+ void merge(order_preserving_json_object&& source)
+ {
+ auto it = std::make_move_iterator(source.begin());
+ auto end = std::make_move_iterator(source.end());
+ for (; it != end; ++it)
+ {
+ auto pos = find((*it).key());
+ if (pos == members_.end() )
+ {
+ try_emplace((*it).key(),std::move((*it).value()));
+ }
+ }
+ }
+
+ void merge(iterator hint, const order_preserving_json_object& source)
+ {
+ std::size_t pos = hint - members_.begin();
+ for (auto it = source.begin(); it != source.end(); ++it)
+ {
+ hint = try_emplace(hint, (*it).key(),(*it).value());
+ std::size_t newpos = hint - members_.begin();
+ if (newpos == pos)
+ {
+ ++hint;
+ pos = hint - members_.begin();
+ }
+ else
+ {
+ hint = members_.begin() + pos;
+ }
+ }
+ }
+
+ void merge(iterator hint, order_preserving_json_object&& source)
+ {
+ std::size_t pos = hint - members_.begin();
+
+ auto it = std::make_move_iterator(source.begin());
+ auto end = std::make_move_iterator(source.end());
+ for (; it != end; ++it)
+ {
+ hint = try_emplace(hint, (*it).key(), std::move((*it).value()));
+ std::size_t newpos = hint - members_.begin();
+ if (newpos == pos)
+ {
+ ++hint;
+ pos = hint - members_.begin();
+ }
+ else
+ {
+ hint = members_.begin() + pos;
+ }
+ }
+ }
+
+ // merge_or_update
+
+ void merge_or_update(const order_preserving_json_object& source)
+ {
+ for (auto it = source.begin(); it != source.end(); ++it)
+ {
+ insert_or_assign((*it).key(),(*it).value());
+ }
+ }
+
+ void merge_or_update(order_preserving_json_object&& source)
+ {
+ auto it = std::make_move_iterator(source.begin());
+ auto end = std::make_move_iterator(source.end());
+ for (; it != end; ++it)
+ {
+ auto pos = find((*it).key());
+ if (pos == members_.end() )
+ {
+ insert_or_assign((*it).key(),std::move((*it).value()));
+ }
+ else
+ {
+ pos->value(std::move((*it).value()));
+ }
+ }
+ }
+
+ void merge_or_update(iterator hint, const order_preserving_json_object& source)
+ {
+ std::size_t pos = hint - members_.begin();
+ for (auto it = source.begin(); it != source.end(); ++it)
+ {
+ hint = insert_or_assign(hint, (*it).key(),(*it).value());
+ std::size_t newpos = hint - members_.begin();
+ if (newpos == pos)
+ {
+ ++hint;
+ pos = hint - members_.begin();
+ }
+ else
+ {
+ hint = members_.begin() + pos;
+ }
+ }
+ }
+
+ void merge_or_update(iterator hint, order_preserving_json_object&& source)
+ {
+ std::size_t pos = hint - members_.begin();
+ auto it = std::make_move_iterator(source.begin());
+ auto end = std::make_move_iterator(source.end());
+ for (; it != end; ++it)
+ {
+ hint = insert_or_assign(hint, (*it).key(), std::move((*it).value()));
+ std::size_t newpos = hint - members_.begin();
+ if (newpos == pos)
+ {
+ ++hint;
+ pos = hint - members_.begin();
+ }
+ else
+ {
+ hint = members_.begin() + pos;
+ }
+ }
+ }
+
+ // try_emplace
+
+ template <class A=allocator_type, class... Args>
+ typename std::enable_if<type_traits::is_stateless<A>::value,std::pair<iterator,bool>>::type
+ try_emplace(const string_view_type& name, Args&&... args)
+ {
+ auto result = insert_index_entry(name,members_.size());
+ if (result.second)
+ {
+ members_.emplace_back(key_type(name.begin(), name.end()), std::forward<Args>(args)...);
+ auto it = members_.begin() + result.first;
+ return std::make_pair(it,true);
+ }
+ else
+ {
+ auto it = members_.begin() + result.first;
+ return std::make_pair(it,false);
+ }
+ }
+
+ template <class A=allocator_type, class... Args>
+ typename std::enable_if<!type_traits::is_stateless<A>::value,std::pair<iterator,bool>>::type
+ try_emplace(const string_view_type& key, Args&&... args)
+ {
+ auto result = insert_index_entry(key,members_.size());
+ if (result.second)
+ {
+ members_.emplace_back(key_type(key.begin(),key.end(), get_allocator()),
+ std::forward<Args>(args)...);
+ auto it = members_.begin() + result.first;
+ return std::make_pair(it,true);
+ }
+ else
+ {
+ auto it = members_.begin() + result.first;
+ return std::make_pair(it,false);
+ }
+ }
+
+ template <class A=allocator_type, class ... Args>
+ typename std::enable_if<type_traits::is_stateless<A>::value,iterator>::type
+ try_emplace(iterator hint, const string_view_type& key, Args&&... args)
+ {
+ if (hint == members_.end())
+ {
+ auto result = try_emplace(key, std::forward<Args>(args)...);
+ return result.first;
+ }
+ else
+ {
+ std::size_t pos = hint - members_.begin();
+ auto result = insert_index_entry(key, pos);
+
+ if (result.second)
+ {
+ auto it = members_.emplace(hint, key_type(key.begin(), key.end()), std::forward<Args>(args)...);
+ return it;
+ }
+ else
+ {
+ auto it = members_.begin() + result.first;
+ return it;
+ }
+ }
+ }
+
+ template <class A=allocator_type, class ... Args>
+ typename std::enable_if<!type_traits::is_stateless<A>::value,iterator>::type
+ try_emplace(iterator hint, const string_view_type& key, Args&&... args)
+ {
+ if (hint == members_.end())
+ {
+ auto result = try_emplace(key, std::forward<Args>(args)...);
+ return result.first;
+ }
+ else
+ {
+ std::size_t pos = hint - members_.begin();
+ auto result = insert_index_entry(key, pos);
+
+ if (result.second)
+ {
+ auto it = members_.emplace(hint,
+ key_type(key.begin(),key.end(), get_allocator()),
+ std::forward<Args>(args)...);
+ return it;
+ }
+ else
+ {
+ auto it = members_.begin() + result.first;
+ return it;
+ }
+ }
+ }
+
+ bool operator==(const order_preserving_json_object& rhs) const
+ {
+ return members_ == rhs.members_;
+ }
+
+ bool operator<(const order_preserving_json_object& rhs) const
+ {
+ return members_ < rhs.members_;
+ }
+ private:
+
+ void flatten_and_destroy() noexcept
+ {
+ if (!members_.empty())
+ {
+ json_array<Json> temp(get_allocator());
+
+ for (auto&& kv : members_)
+ {
+ if (kv.value().size() > 0)
+ {
+ temp.emplace_back(std::move(kv.value()));
+ }
+ }
+ }
+ }
+
+ std::pair<std::size_t,bool> insert_index_entry(const string_view_type& key, std::size_t pos)
+ {
+ JSONCONS_ASSERT(pos <= index_.size());
+
+ auto it = std::lower_bound(index_.begin(),index_.end(), key,
+ Comp(members_));
+
+ if (it == index_.end())
+ {
+ std::size_t count = index_.size() - pos;
+ for (std::size_t i = 0; count > 0 && i < index_.size(); ++i)
+ {
+ if (index_[i] >= pos)
+ {
+ ++index_[i];
+ --count;
+ }
+ }
+ index_.push_back(pos);
+ return std::make_pair(pos,true);
+ }
+ else if (members_.at(*it).key() != key)
+ {
+ std::size_t count = index_.size() - pos;
+ for (std::size_t i = 0; count > 0 && i < index_.size(); ++i)
+ {
+ if (index_[i] >= pos)
+ {
+ ++index_[i];
+ --count;
+ }
+ }
+ auto it2 = index_.insert(it, pos);
+ return std::make_pair(*it2,true);
+ }
+ else
+ {
+ return std::make_pair(*it,false);
+ }
+ }
+
+ void erase_index_entries(std::size_t pos1, std::size_t pos2)
+ {
+ JSONCONS_ASSERT(pos1 <= pos2);
+ JSONCONS_ASSERT(pos2 <= index_.size());
+
+ const size_t offset = pos2 - pos1;
+ const size_t n = index_.size() - offset;
+ for (std::size_t i = 0; i < index_.size(); ++i)
+ {
+ if (index_[i] >= pos1 && index_[i] < pos2)
+ {
+ index_.erase(index_.begin()+i);
+ --i;
+ }
+ }
+ for (std::size_t i = 0; i < index_.size(); ++i)
+ {
+ if (index_[i] >= pos2)
+ {
+ index_[i] -= offset;
+ }
+ }
+ JSONCONS_ASSERT(index_.size() == n);
+ }
+
+ void build_index()
+ {
+ index_.clear();
+ index_.reserve(members_.size());
+ for (std::size_t i = 0; i < members_.size(); ++i)
+ {
+ index_.push_back(i);
+ }
+ std::stable_sort(index_.begin(),index_.end(),
+ [&](std::size_t a, std::size_t b) -> bool {return members_.at(a).key().compare(members_.at(b).key()) < 0;});
+ }
+ };
+
+} // namespace jsoncons
+
+#endif
diff --git a/include/jsoncons/json_options.hpp b/include/jsoncons/json_options.hpp
new file mode 100644
index 0000000..e73f233
--- /dev/null
+++ b/include/jsoncons/json_options.hpp
@@ -0,0 +1,862 @@
+// Copyright 2013 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_OPTIONS_HPP
+#define JSONCONS_JSON_OPTIONS_HPP
+
+#include <string>
+#include <limits> // std::numeric_limits
+#include <cwchar>
+#include <jsoncons/json_exception.hpp>
+#include <jsoncons/more_type_traits.hpp>
+
+namespace jsoncons {
+
+enum class float_chars_format : uint8_t {general,fixed,scientific,hex};
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+JSONCONS_DEPRECATED_MSG("Instead, use float_chars_format") typedef float_chars_format chars_format;
+#endif
+
+enum class indenting : uint8_t {no_indent = 0, indent = 1};
+
+enum class line_split_kind : uint8_t {same_line,new_line,multi_line};
+
+enum class bigint_chars_format : uint8_t {number, base10, base64, base64url
+#if !defined(JSONCONS_NO_DEPRECATED)
+,integer = number
+#endif
+};
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+JSONCONS_DEPRECATED_MSG("Instead, use bigint_chars_format") typedef bigint_chars_format bignum_chars_format;
+JSONCONS_DEPRECATED_MSG("Instead, use bigint_chars_format") typedef bigint_chars_format big_integer_chars_format;
+#endif
+
+enum class byte_string_chars_format : uint8_t {none=0,base16,base64,base64url};
+
+enum class spaces_option : uint8_t {no_spaces=0,space_after,space_before,space_before_and_after};
+
+template <class CharT>
+class basic_json_options;
+
+template <class CharT>
+class basic_json_options_common
+{
+ friend class basic_json_options<CharT>;
+public:
+ using char_type = CharT;
+ using string_type = std::basic_string<CharT>;
+private:
+#if !defined(JSONCONS_NO_DEPRECATED)
+ bool can_read_nan_replacement_;
+ bool can_read_pos_inf_replacement_;
+ bool can_read_neg_inf_replacement_;
+ string_type nan_replacement_;
+ string_type pos_inf_replacement_;
+ string_type neg_inf_replacement_;
+#endif
+
+ bool enable_nan_to_num_:1;
+ bool enable_inf_to_num_:1;
+ bool enable_neginf_to_num_:1;
+ bool enable_nan_to_str_:1;
+ bool enable_inf_to_str_:1;
+ bool enable_neginf_to_str_:1;
+ bool enable_str_to_nan_:1;
+ bool enable_str_to_inf_:1;
+ bool enable_str_to_neginf_:1;
+
+ string_type nan_to_num_;
+ string_type inf_to_num_;
+ string_type neginf_to_num_;
+ string_type nan_to_str_;
+ string_type inf_to_str_;
+ string_type neginf_to_str_;
+ int max_nesting_depth_;
+
+protected:
+ basic_json_options_common()
+ :
+#if !defined(JSONCONS_NO_DEPRECATED)
+ can_read_nan_replacement_(false),
+ can_read_pos_inf_replacement_(false),
+ can_read_neg_inf_replacement_(false),
+#endif
+ enable_nan_to_num_(false),
+ enable_inf_to_num_(false),
+ enable_neginf_to_num_(false),
+ enable_nan_to_str_(false),
+ enable_inf_to_str_(false),
+ enable_neginf_to_str_(false),
+ enable_str_to_nan_(false),
+ enable_str_to_inf_(false),
+ enable_str_to_neginf_(false),
+ max_nesting_depth_(1024)
+ {}
+
+ virtual ~basic_json_options_common() noexcept = default;
+
+ basic_json_options_common(const basic_json_options_common&) = default;
+ basic_json_options_common& operator=(const basic_json_options_common&) = default;
+ basic_json_options_common(basic_json_options_common&&) = default;
+ basic_json_options_common& operator=(basic_json_options_common&&) = default;
+
+public:
+
+ bool enable_nan_to_num() const
+ {
+ return enable_nan_to_num_;
+ }
+
+ bool enable_inf_to_num() const
+ {
+ return enable_inf_to_num_;
+ }
+
+ bool enable_neginf_to_num() const
+ {
+ return enable_neginf_to_num_ || enable_inf_to_num_;
+ }
+
+ bool enable_nan_to_str() const
+ {
+ return enable_nan_to_str_;
+ }
+
+ bool enable_str_to_nan() const
+ {
+ return enable_str_to_nan_;
+ }
+
+ bool enable_inf_to_str() const
+ {
+ return enable_inf_to_str_;
+ }
+
+ bool enable_str_to_inf() const
+ {
+ return enable_str_to_inf_;
+ }
+
+ bool enable_neginf_to_str() const
+ {
+ return enable_neginf_to_str_ || enable_inf_to_str_;
+ }
+
+ bool enable_str_to_neginf() const
+ {
+ return enable_str_to_neginf_ || enable_str_to_inf_;
+ }
+
+ string_type nan_to_num() const
+ {
+ if (enable_nan_to_num_)
+ {
+ return nan_to_num_;
+ }
+#if !defined(JSONCONS_NO_DEPRECATED)
+ else if (!can_read_nan_replacement_) // not string
+ {
+ return nan_replacement_;
+ }
+#endif
+ else
+ {
+ return nan_to_num_; // empty string
+ }
+ }
+
+ string_type inf_to_num() const
+ {
+ if (enable_inf_to_num_)
+ {
+ return inf_to_num_;
+ }
+#if !defined(JSONCONS_NO_DEPRECATED)
+ else if (!can_read_pos_inf_replacement_) // not string
+ {
+ return pos_inf_replacement_;
+ }
+#endif
+ else
+ {
+ return inf_to_num_; // empty string
+ }
+ }
+
+ string_type neginf_to_num() const
+ {
+ if (enable_neginf_to_num_)
+ {
+ return neginf_to_num_;
+ }
+ else if (enable_inf_to_num_)
+ {
+ string_type s;
+ s.push_back('-');
+ s.append(inf_to_num_);
+ return s;
+ }
+#if !defined(JSONCONS_NO_DEPRECATED)
+ else if (!can_read_neg_inf_replacement_) // not string
+ {
+ return neg_inf_replacement_;
+ }
+#endif
+ else
+ {
+ return neginf_to_num_; // empty string
+ }
+ }
+
+ string_type nan_to_str() const
+ {
+ if (enable_nan_to_str_)
+ {
+ return nan_to_str_;
+ }
+#if !defined(JSONCONS_NO_DEPRECATED)
+ else if (can_read_nan_replacement_ && nan_replacement_.size() >= 2) // string
+ {
+ return nan_replacement_.substr(1, nan_replacement_.size() - 2); // Remove quotes
+ }
+#endif
+ else
+ {
+ return nan_to_str_; // empty string
+ }
+ }
+
+ string_type inf_to_str() const
+ {
+ if (enable_inf_to_str_)
+ {
+ return inf_to_str_;
+ }
+#if !defined(JSONCONS_NO_DEPRECATED)
+ else if (can_read_pos_inf_replacement_ && pos_inf_replacement_.size() >= 2) // string
+ {
+ return pos_inf_replacement_.substr(1, pos_inf_replacement_.size() - 2); // Strip quotes
+ }
+#endif
+ else
+ {
+ return inf_to_str_; // empty string
+ }
+ }
+
+ string_type neginf_to_str() const
+ {
+ if (enable_neginf_to_str_)
+ {
+ return neginf_to_str_;
+ }
+ else if (enable_inf_to_str_)
+ {
+ string_type s;
+ s.push_back('-');
+ s.append(inf_to_str_);
+ return s;
+ }
+#if !defined(JSONCONS_NO_DEPRECATED)
+ else if (can_read_neg_inf_replacement_ && neg_inf_replacement_.size() >= 2) // string
+ {
+ return neg_inf_replacement_.substr(1, neg_inf_replacement_.size() - 2); // Strip quotes
+ }
+#endif
+ else
+ {
+ return neginf_to_str_; // empty string
+ }
+ }
+
+ int max_nesting_depth() const
+ {
+ return max_nesting_depth_;
+ }
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+ JSONCONS_DEPRECATED_MSG("Instead, use enable_nan_to_num() or enable_nan_to_str()")
+ bool can_read_nan_replacement() const { return can_read_nan_replacement_; }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use enable_inf_to_num() or enable_inf_to_str()")
+ bool can_read_pos_inf_replacement() const { return can_read_pos_inf_replacement_; }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use enable_neginf_to_num() or enable_neginf_to_str()")
+ bool can_read_neg_inf_replacement() const { return can_read_neg_inf_replacement_; }
+
+ bool can_write_nan_replacement() const { return !nan_replacement_.empty(); }
+
+ bool can_write_pos_inf_replacement() const { return !pos_inf_replacement_.empty(); }
+
+ bool can_write_neg_inf_replacement() const { return !neg_inf_replacement_.empty(); }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use nan_to_num() or nan_to_str()")
+ const string_type& nan_replacement() const
+ {
+ return nan_replacement_;
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use inf_to_num() or inf_to_str()")
+ const string_type& pos_inf_replacement() const
+ {
+ return pos_inf_replacement_;
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use neginf_to_num() or neginf_to_str()")
+ const string_type& neg_inf_replacement() const
+ {
+ return neg_inf_replacement_;
+ }
+#endif
+};
+
+template <class CharT>
+class basic_json_decode_options : public virtual basic_json_options_common<CharT>
+{
+ friend class basic_json_options<CharT>;
+ using super_type = basic_json_options_common<CharT>;
+public:
+ using typename super_type::char_type;
+ using typename super_type::string_type;
+private:
+ bool lossless_number_:1;
+public:
+ basic_json_decode_options()
+ : lossless_number_(false)
+ {
+ }
+
+ basic_json_decode_options(const basic_json_decode_options&) = default;
+
+ basic_json_decode_options(basic_json_decode_options&& other)
+ : super_type(std::forward<basic_json_decode_options>(other)),
+ lossless_number_(other.lossless_number_)
+ {
+ }
+
+ basic_json_decode_options& operator=(const basic_json_decode_options&) = default;
+
+ bool lossless_number() const
+ {
+ return lossless_number_;
+ }
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+ JSONCONS_DEPRECATED_MSG("Instead, use lossless_number()")
+ bool dec_to_str() const
+ {
+ return lossless_number_;
+ }
+#endif
+};
+
+template <class CharT>
+class basic_json_encode_options : public virtual basic_json_options_common<CharT>
+{
+ friend class basic_json_options<CharT>;
+ using super_type = basic_json_options_common<CharT>;
+public:
+ using typename super_type::char_type;
+ using typename super_type::string_type;
+
+ static constexpr uint8_t indent_size_default = 4;
+ static constexpr size_t line_length_limit_default = 120;
+private:
+ bool escape_all_non_ascii_:1;
+ bool escape_solidus_:1;
+ bool pad_inside_object_braces_:1;
+ bool pad_inside_array_brackets_:1;
+ float_chars_format float_format_;
+ byte_string_chars_format byte_string_format_;
+ bigint_chars_format bigint_format_;
+ line_split_kind object_object_line_splits_;
+ line_split_kind object_array_line_splits_;
+ line_split_kind array_array_line_splits_;
+ line_split_kind array_object_line_splits_;
+ spaces_option spaces_around_colon_;
+ spaces_option spaces_around_comma_;
+ int8_t precision_;
+ uint8_t indent_size_;
+ std::size_t line_length_limit_;
+ string_type new_line_chars_;
+public:
+ basic_json_encode_options()
+ : escape_all_non_ascii_(false),
+ escape_solidus_(false),
+ pad_inside_object_braces_(false),
+ pad_inside_array_brackets_(false),
+ float_format_(float_chars_format::general),
+ byte_string_format_(byte_string_chars_format::none),
+ bigint_format_(bigint_chars_format::base10),
+ object_object_line_splits_(line_split_kind::multi_line),
+ object_array_line_splits_(line_split_kind::same_line),
+ array_array_line_splits_(line_split_kind::new_line),
+ array_object_line_splits_(line_split_kind::multi_line),
+ spaces_around_colon_(spaces_option::space_after),
+ spaces_around_comma_(spaces_option::space_after),
+ precision_(0),
+ indent_size_(indent_size_default),
+ line_length_limit_(line_length_limit_default)
+ {
+ new_line_chars_.push_back('\n');
+ }
+
+ basic_json_encode_options(const basic_json_encode_options&) = default;
+
+ basic_json_encode_options(basic_json_encode_options&& other)
+ : super_type(std::forward<basic_json_encode_options>(other)),
+ escape_all_non_ascii_(other.escape_all_non_ascii_),
+ escape_solidus_(other.escape_solidus_),
+ pad_inside_object_braces_(other.pad_inside_object_braces_),
+ pad_inside_array_brackets_(other.pad_inside_array_brackets_),
+ float_format_(other.float_format_),
+ byte_string_format_(other.byte_string_format_),
+ bigint_format_(other.bigint_format_),
+ object_object_line_splits_(other.object_object_line_splits_),
+ object_array_line_splits_(other.object_array_line_splits_),
+ array_array_line_splits_(other.array_array_line_splits_),
+ array_object_line_splits_(other.array_object_line_splits_),
+ spaces_around_colon_(other.spaces_around_colon_),
+ spaces_around_comma_(other.spaces_around_comma_),
+ precision_(other.precision_),
+ indent_size_(other.indent_size_),
+ line_length_limit_(other.line_length_limit_),
+ new_line_chars_(std::move(other.new_line_chars_))
+ {
+ }
+
+ basic_json_encode_options& operator=(const basic_json_encode_options&) = default;
+
+ byte_string_chars_format byte_string_format() const {return byte_string_format_;}
+
+ bigint_chars_format bigint_format() const {return bigint_format_;}
+
+ line_split_kind object_object_line_splits() const {return object_object_line_splits_;}
+
+ line_split_kind array_object_line_splits() const {return array_object_line_splits_;}
+
+ line_split_kind object_array_line_splits() const {return object_array_line_splits_;}
+
+ line_split_kind array_array_line_splits() const {return array_array_line_splits_;}
+
+ uint8_t indent_size() const
+ {
+ return indent_size_;
+ }
+
+ spaces_option spaces_around_colon() const
+ {
+ return spaces_around_colon_;
+ }
+
+ spaces_option spaces_around_comma() const
+ {
+ return spaces_around_comma_;
+ }
+
+ bool pad_inside_object_braces() const
+ {
+ return pad_inside_object_braces_;
+ }
+
+ bool pad_inside_array_brackets() const
+ {
+ return pad_inside_array_brackets_;
+ }
+
+ string_type new_line_chars() const
+ {
+ return new_line_chars_;
+ }
+
+ std::size_t line_length_limit() const
+ {
+ return line_length_limit_;
+ }
+
+ float_chars_format float_format() const
+ {
+ return float_format_;
+ }
+
+ int8_t precision() const
+ {
+ return precision_;
+ }
+
+ bool escape_all_non_ascii() const
+ {
+ return escape_all_non_ascii_;
+ }
+
+ bool escape_solidus() const
+ {
+ return escape_solidus_;
+ }
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+ JSONCONS_DEPRECATED_MSG("Instead, use bigint_format()")
+ bigint_chars_format bignum_format() const {return bigint_format_;}
+
+ JSONCONS_DEPRECATED_MSG("Instead, use indent_size()")
+ uint8_t indent() const
+ {
+ return indent_size();
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use object_object_line_splits()")
+ line_split_kind object_object_split_lines() const {return object_object_line_splits_;}
+
+ JSONCONS_DEPRECATED_MSG("Instead, use array_object_line_splits()")
+ line_split_kind array_object_split_lines() const {return array_object_line_splits_;}
+
+ JSONCONS_DEPRECATED_MSG("Instead, use object_array_line_splits()")
+ line_split_kind object_array_split_lines() const {return object_array_line_splits_;}
+
+ JSONCONS_DEPRECATED_MSG("Instead, use array_array_line_splits()")
+ line_split_kind array_array_split_lines() const {return array_array_line_splits_;}
+#endif
+};
+
+template <class CharT>
+class basic_json_options final: public basic_json_decode_options<CharT>,
+ public basic_json_encode_options<CharT>
+{
+public:
+ using char_type = CharT;
+ using string_type = std::basic_string<CharT>;
+
+ using basic_json_options_common<CharT>::max_nesting_depth;
+
+ using basic_json_decode_options<CharT>::enable_str_to_nan;
+ using basic_json_decode_options<CharT>::enable_str_to_inf;
+ using basic_json_decode_options<CharT>::enable_str_to_neginf;
+ using basic_json_decode_options<CharT>::nan_to_str;
+ using basic_json_decode_options<CharT>::inf_to_str;
+ using basic_json_decode_options<CharT>::neginf_to_str;
+ using basic_json_decode_options<CharT>::nan_to_num;
+ using basic_json_decode_options<CharT>::inf_to_num;
+ using basic_json_decode_options<CharT>::neginf_to_num;
+
+ using basic_json_decode_options<CharT>::lossless_number;
+
+ using basic_json_encode_options<CharT>::byte_string_format;
+ using basic_json_encode_options<CharT>::bigint_format;
+ using basic_json_encode_options<CharT>::object_object_line_splits;
+ using basic_json_encode_options<CharT>::array_object_line_splits;
+ using basic_json_encode_options<CharT>::object_array_line_splits;
+ using basic_json_encode_options<CharT>::array_array_line_splits;
+ using basic_json_encode_options<CharT>::indent_size;
+ using basic_json_encode_options<CharT>::spaces_around_colon;
+ using basic_json_encode_options<CharT>::spaces_around_comma;
+ using basic_json_encode_options<CharT>::pad_inside_object_braces;
+ using basic_json_encode_options<CharT>::pad_inside_array_brackets;
+ using basic_json_encode_options<CharT>::new_line_chars;
+ using basic_json_encode_options<CharT>::line_length_limit;
+ using basic_json_encode_options<CharT>::float_format;
+ using basic_json_encode_options<CharT>::precision;
+ using basic_json_encode_options<CharT>::escape_all_non_ascii;
+ using basic_json_encode_options<CharT>::escape_solidus;
+public:
+
+// Constructors
+
+ basic_json_options() = default;
+ basic_json_options(const basic_json_options&) = default;
+ basic_json_options(basic_json_options&&) = default;
+ basic_json_options& operator=(const basic_json_options&) = default;
+ basic_json_options& operator=(basic_json_options&&) = default;
+
+ basic_json_options& nan_to_num(const string_type& value)
+ {
+ this->enable_nan_to_num_ = true;
+ this->nan_to_str_.clear();
+ this->nan_to_num_ = value;
+ return *this;
+ }
+
+ basic_json_options& inf_to_num(const string_type& value)
+ {
+ this->enable_inf_to_num_ = true;
+ this->inf_to_str_.clear();
+ this->inf_to_num_ = value;
+ return *this;
+ }
+
+ basic_json_options& neginf_to_num(const string_type& value)
+ {
+ this->enable_neginf_to_num_ = true;
+ this->neginf_to_str_.clear();
+ this->neginf_to_num_ = value;
+ return *this;
+ }
+
+ basic_json_options& nan_to_str(const string_type& value, bool enable_inverse = true)
+ {
+ this->enable_nan_to_str_ = true;
+ this->enable_str_to_nan_ = enable_inverse;
+ this->nan_to_num_.clear();
+ this->nan_to_str_ = value;
+ return *this;
+ }
+
+ basic_json_options& inf_to_str(const string_type& value, bool enable_inverse = true)
+ {
+ this->enable_inf_to_str_ = true;
+ this->enable_inf_to_str_ = enable_inverse;
+ this->inf_to_num_.clear();
+ this->inf_to_str_ = value;
+ return *this;
+ }
+
+ basic_json_options& neginf_to_str(const string_type& value, bool enable_inverse = true)
+ {
+ this->enable_neginf_to_str_ = true;
+ this->enable_neginf_to_str_ = enable_inverse;
+ this->neginf_to_num_.clear();
+ this->neginf_to_str_ = value;
+ return *this;
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use inf_to_num(const string_type&) or inf_to_str(const string_type&)")
+ basic_json_options& replace_inf(bool replace)
+ {
+ this->can_read_pos_inf_replacement_ = replace;
+ this->can_read_neg_inf_replacement_ = replace;
+ return *this;
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use inf_to_num(const string_type&) or inf_to_str(const string_type&)")
+ basic_json_options& replace_pos_inf(bool replace)
+ {
+ this->can_read_pos_inf_replacement_ = replace;
+ return *this;
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use neginf_to_num(const string_type&) or neginf_to_str(const string_type&)")
+ basic_json_options& replace_neg_inf(bool replace)
+ {
+ this->can_read_neg_inf_replacement_ = replace;
+ return *this;
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use nan_to_num(const string_type&) or nan_to_str(const string_type&)")
+ basic_json_options& nan_replacement(const string_type& value)
+ {
+ this->nan_replacement_ = value;
+
+ this->can_read_nan_replacement_ = is_string(value);
+
+ return *this;
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use inf_to_num(const string_type&) or inf_to_str(const string_type&)")
+ basic_json_options& pos_inf_replacement(const string_type& value)
+ {
+ this->pos_inf_replacement_ = value;
+ this->can_read_pos_inf_replacement_ = is_string(value);
+ return *this;
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use neginf_to_num(const string_type&) or neginf_to_str(const string_type&)")
+ basic_json_options& neg_inf_replacement(const string_type& value)
+ {
+ this->neg_inf_replacement_ = value;
+ this->can_read_neg_inf_replacement_ = is_string(value);
+ return *this;
+ }
+
+ basic_json_options& byte_string_format(byte_string_chars_format value) {this->byte_string_format_ = value; return *this;}
+
+ basic_json_options& bigint_format(bigint_chars_format value) {this->bigint_format_ = value; return *this;}
+
+ basic_json_options& object_object_line_splits(line_split_kind value) {this->object_object_line_splits_ = value; return *this;}
+
+ basic_json_options& array_object_line_splits(line_split_kind value) {this->array_object_line_splits_ = value; return *this;}
+
+ basic_json_options& object_array_line_splits(line_split_kind value) {this->object_array_line_splits_ = value; return *this;}
+
+ basic_json_options& array_array_line_splits(line_split_kind value) {this->array_array_line_splits_ = value; return *this;}
+
+ basic_json_options& indent_size(uint8_t value)
+ {
+ this->indent_size_ = value;
+ return *this;
+ }
+
+ basic_json_options& spaces_around_colon(spaces_option value)
+ {
+ this->spaces_around_colon_ = value;
+ return *this;
+ }
+
+ basic_json_options& spaces_around_comma(spaces_option value)
+ {
+ this->spaces_around_comma_ = value;
+ return *this;
+ }
+
+ basic_json_options& pad_inside_object_braces(bool value)
+ {
+ this->pad_inside_object_braces_ = value;
+ return *this;
+ }
+
+ basic_json_options& pad_inside_array_brackets(bool value)
+ {
+ this->pad_inside_array_brackets_ = value;
+ return *this;
+ }
+
+ basic_json_options& new_line_chars(const string_type& value)
+ {
+ this->new_line_chars_ = value;
+ return *this;
+ }
+
+ basic_json_options& lossless_number(bool value)
+ {
+ this->lossless_number_ = value;
+ return *this;
+ }
+
+ basic_json_options& line_length_limit(std::size_t value)
+ {
+ this->line_length_limit_ = value;
+ return *this;
+ }
+
+ basic_json_options& float_format(float_chars_format value)
+ {
+ this->float_format_ = value;
+ return *this;
+ }
+
+ basic_json_options& precision(int8_t value)
+ {
+ this->precision_ = value;
+ return *this;
+ }
+
+ basic_json_options& escape_all_non_ascii(bool value)
+ {
+ this->escape_all_non_ascii_ = value;
+ return *this;
+ }
+
+ basic_json_options& escape_solidus(bool value)
+ {
+ this->escape_solidus_ = value;
+ return *this;
+ }
+
+ basic_json_options& max_nesting_depth(int value)
+ {
+ this->max_nesting_depth_ = value;
+ return *this;
+ }
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+ JSONCONS_DEPRECATED_MSG("Instead, use bigint_format(bigint_chars_format)")
+ basic_json_options& big_integer_format(bigint_chars_format value) {this->bigint_format_ = value; return *this;}
+
+ JSONCONS_DEPRECATED_MSG("Instead, use bigint_format(bigint_chars_format)")
+ basic_json_options& bignum_format(bigint_chars_format value) {this->bigint_format_ = value; return *this;}
+
+ JSONCONS_DEPRECATED_MSG("Instead, use float_format(float_chars_format)")
+ basic_json_options& floating_point_format(float_chars_format value)
+ {
+ this->float_format_ = value;
+ return *this;
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use lossless_number(bool)")
+ basic_json_options& dec_to_str(bool value)
+ {
+ this->lossless_number_ = value;
+ return *this;
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use indent_size(uint8_t_t)")
+ basic_json_options& indent(uint8_t value)
+ {
+ return indent_size(value);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use object_object_line_splits(line_split_kind)")
+ basic_json_options& object_object_split_lines(line_split_kind value) {this->object_object_line_splits_ = value; return *this;}
+
+ JSONCONS_DEPRECATED_MSG("Instead, use array_object_line_splits(line_split_kind)")
+ basic_json_options& array_object_split_lines(line_split_kind value) {this->array_object_line_splits_ = value; return *this;}
+
+ JSONCONS_DEPRECATED_MSG("Instead, use object_array_line_splits(line_split_kind)")
+ basic_json_options& object_array_split_lines(line_split_kind value) {this->object_array_line_splits_ = value; return *this;}
+
+ JSONCONS_DEPRECATED_MSG("Instead, use array_array_line_splits(line_split_kind)")
+ basic_json_options& array_array_split_lines(line_split_kind value) {this->array_array_line_splits_ = value; return *this;}
+#endif
+private:
+ enum class input_state {initial,begin_quote,character,end_quote,escape,error};
+ bool is_string(const string_type& s) const
+ {
+ input_state state = input_state::initial;
+ for (char_type c : s)
+ {
+ switch (c)
+ {
+ case '\t': case ' ': case '\n': case'\r':
+ break;
+ case '\\':
+ state = input_state::escape;
+ break;
+ case '\"':
+ switch (state)
+ {
+ case input_state::initial:
+ state = input_state::begin_quote;
+ break;
+ case input_state::begin_quote:
+ state = input_state::end_quote;
+ break;
+ case input_state::character:
+ state = input_state::end_quote;
+ break;
+ case input_state::end_quote:
+ state = input_state::error;
+ break;
+ case input_state::escape:
+ state = input_state::character;
+ break;
+ default:
+ state = input_state::character;
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ }
+ return state == input_state::end_quote;
+ }
+};
+
+using json_options = basic_json_options<char>;
+using wjson_options = basic_json_options<wchar_t>;
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+JSONCONS_DEPRECATED_MSG("json_options") typedef json_options output_format;
+JSONCONS_DEPRECATED_MSG("wjson_options") typedef wjson_options woutput_format;
+JSONCONS_DEPRECATED_MSG("json_options") typedef json_options serialization_options;
+JSONCONS_DEPRECATED_MSG("wjson_options") typedef wjson_options wserialization_options;
+JSONCONS_DEPRECATED_MSG("json_options") typedef json_options json_serializing_options;
+JSONCONS_DEPRECATED_MSG("wjson_options") typedef wjson_options wjson_serializing_options;
+#endif
+
+}
+#endif
diff --git a/include/jsoncons/json_parser.hpp b/include/jsoncons/json_parser.hpp
new file mode 100644
index 0000000..4ba9bf1
--- /dev/null
+++ b/include/jsoncons/json_parser.hpp
@@ -0,0 +1,2871 @@
+// Copyright 2015 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_PARSER_HPP
+#define JSONCONS_JSON_PARSER_HPP
+
+#include <memory> // std::allocator
+#include <string>
+#include <vector>
+#include <stdexcept>
+#include <system_error>
+#include <unordered_map>
+#include <limits> // std::numeric_limits
+#include <functional> // std::function
+#include <jsoncons/json_exception.hpp>
+#include <jsoncons/json_filter.hpp>
+#include <jsoncons/json_options.hpp>
+#include <jsoncons/json_visitor.hpp>
+#include <jsoncons/json_error.hpp>
+#include <jsoncons/detail/parse_number.hpp>
+
+#define JSONCONS_ILLEGAL_CONTROL_CHARACTER \
+ case 0x00:case 0x01:case 0x02:case 0x03:case 0x04:case 0x05:case 0x06:case 0x07:case 0x08:case 0x0b: \
+ case 0x0c:case 0x0e:case 0x0f:case 0x10:case 0x11:case 0x12:case 0x13:case 0x14:case 0x15:case 0x16: \
+ case 0x17:case 0x18:case 0x19:case 0x1a:case 0x1b:case 0x1c:case 0x1d:case 0x1e:case 0x1f
+
+namespace jsoncons {
+
+namespace detail {
+
+}
+
+enum class json_parse_state : uint8_t
+{
+ root,
+ start,
+ accept,
+ slash,
+ slash_slash,
+ slash_star,
+ slash_star_star,
+ expect_comma_or_end,
+ object,
+ expect_member_name_or_end,
+ expect_member_name,
+ expect_colon,
+ expect_value_or_end,
+ expect_value,
+ array,
+ string,
+ member_name,
+ escape,
+ escape_u1,
+ escape_u2,
+ escape_u3,
+ escape_u4,
+ escape_expect_surrogate_pair1,
+ escape_expect_surrogate_pair2,
+ escape_u5,
+ escape_u6,
+ escape_u7,
+ escape_u8,
+ minus,
+ zero,
+ integer,
+ fraction1,
+ fraction2,
+ exp1,
+ exp2,
+ exp3,
+ n,
+ nu,
+ nul,
+ t,
+ tr,
+ tru,
+ f,
+ fa,
+ fal,
+ fals,
+ cr,
+ done
+};
+
+struct default_json_parsing
+{
+ bool operator()(json_errc ec, const ser_context&) noexcept
+ {
+ if (ec == json_errc::illegal_comment)
+ {
+ return true; // Recover, allow comments
+ }
+ else
+ {
+ return false;
+ }
+ }
+};
+
+struct strict_json_parsing
+{
+ bool operator()(json_errc, const ser_context&) noexcept
+ {
+ return false;
+ }
+};
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+JSONCONS_DEPRECATED_MSG("Instead, use default_json_parsing") typedef default_json_parsing default_parse_error_handler;
+JSONCONS_DEPRECATED_MSG("Instead, use strict_json_parsing") typedef strict_json_parsing strict_parse_error_handler;
+#endif
+
+template <class CharT, class TempAllocator = std::allocator<char>>
+class basic_json_parser : public ser_context
+{
+public:
+ using char_type = CharT;
+ using string_view_type = typename basic_json_visitor<CharT>::string_view_type;
+private:
+ struct string_maps_to_double
+ {
+ string_view_type s;
+
+ bool operator()(const std::pair<string_view_type,double>& val) const
+ {
+ return val.first == s;
+ }
+ };
+
+ using temp_allocator_type = TempAllocator;
+ using char_allocator_type = typename std::allocator_traits<temp_allocator_type>:: template rebind_alloc<CharT>;
+ using parse_state_allocator_type = typename std::allocator_traits<temp_allocator_type>:: template rebind_alloc<json_parse_state>;
+
+ static constexpr std::size_t initial_string_buffer_capacity_ = 1024;
+ static constexpr std::size_t default_initial_stack_capacity_ = 100;
+
+ basic_json_decode_options<char_type> options_;
+
+ std::function<bool(json_errc,const ser_context&)> err_handler_;
+ int initial_stack_capacity_;
+ int nesting_depth_;
+ uint32_t cp_;
+ uint32_t cp2_;
+ std::size_t line_;
+ std::size_t position_;
+ std::size_t mark_position_;
+ std::size_t saved_position_;
+ const char_type* begin_input_;
+ const char_type* end_input_;
+ const char_type* input_ptr_;
+ json_parse_state state_;
+ bool more_;
+ bool done_;
+
+ std::basic_string<char_type,std::char_traits<char_type>,char_allocator_type> string_buffer_;
+ jsoncons::detail::chars_to to_double_;
+
+ std::vector<json_parse_state,parse_state_allocator_type> state_stack_;
+ std::vector<std::pair<string_view_type,double>> string_double_map_;
+
+ // Noncopyable and nonmoveable
+ basic_json_parser(const basic_json_parser&) = delete;
+ basic_json_parser& operator=(const basic_json_parser&) = delete;
+
+public:
+ basic_json_parser(const TempAllocator& alloc = TempAllocator())
+ : basic_json_parser(basic_json_decode_options<char_type>(), default_json_parsing(), alloc)
+ {
+ }
+
+ basic_json_parser(std::function<bool(json_errc,const ser_context&)> err_handler,
+ const TempAllocator& alloc = TempAllocator())
+ : basic_json_parser(basic_json_decode_options<char_type>(), err_handler, alloc)
+ {
+ }
+
+ basic_json_parser(const basic_json_decode_options<char_type>& options,
+ const TempAllocator& alloc = TempAllocator())
+ : basic_json_parser(options, default_json_parsing(), alloc)
+ {
+ }
+
+ basic_json_parser(const basic_json_decode_options<char_type>& options,
+ std::function<bool(json_errc,const ser_context&)> err_handler,
+ const TempAllocator& alloc = TempAllocator())
+ : options_(options),
+ err_handler_(err_handler),
+ initial_stack_capacity_(default_initial_stack_capacity_),
+ nesting_depth_(0),
+ cp_(0),
+ cp2_(0),
+ line_(1),
+ position_(0),
+ mark_position_(0),
+ saved_position_(0),
+ begin_input_(nullptr),
+ end_input_(nullptr),
+ input_ptr_(nullptr),
+ state_(json_parse_state::start),
+ more_(true),
+ done_(false),
+ string_buffer_(alloc),
+ state_stack_(alloc)
+ {
+ string_buffer_.reserve(initial_string_buffer_capacity_);
+
+ state_stack_.reserve(initial_stack_capacity_);
+ push_state(json_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<double>::infinity());
+ }
+ if (options_.enable_str_to_neginf())
+ {
+ string_double_map_.emplace_back(options_.neginf_to_str(),-std::numeric_limits<double>::infinity());
+ }
+ }
+
+ bool source_exhausted() const
+ {
+ return input_ptr_ == end_input_;
+ }
+
+ ~basic_json_parser() noexcept
+ {
+ }
+
+ json_parse_state parent() const
+ {
+ JSONCONS_ASSERT(state_stack_.size() >= 1);
+ return state_stack_.back();
+ }
+
+ bool done() const
+ {
+ return done_;
+ }
+
+ bool enter() const
+ {
+ return state_ == json_parse_state::start;
+ }
+
+ bool accept() const
+ {
+ return state_ == json_parse_state::accept || done_;
+ }
+
+ bool stopped() const
+ {
+ return !more_;
+ }
+
+ json_parse_state state() const
+ {
+ return state_;
+ }
+
+ bool finished() const
+ {
+ return !more_ && state_ != json_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_space()
+ {
+ const char_type* local_input_end = end_input_;
+ while (input_ptr_ != local_input_end)
+ {
+ switch (*input_ptr_)
+ {
+ case ' ':
+ case '\t':
+ ++input_ptr_;
+ ++position_;
+ break;
+ case '\r':
+ push_state(state_);
+ ++input_ptr_;
+ ++position_;
+ state_ = json_parse_state::cr;
+ return;
+ case '\n':
+ ++input_ptr_;
+ ++line_;
+ ++position_;
+ mark_position_ = position_;
+ return;
+ default:
+ return;
+ }
+ }
+ }
+
+ void skip_whitespace()
+ {
+ const char_type* local_input_end = end_input_;
+
+ while (input_ptr_ != local_input_end)
+ {
+ switch (state_)
+ {
+ case json_parse_state::cr:
+ ++line_;
+ ++position_;
+ mark_position_ = position_;
+ switch (*input_ptr_)
+ {
+ case '\n':
+ ++input_ptr_;
+ ++position_;
+ state_ = pop_state();
+ break;
+ default:
+ state_ = pop_state();
+ break;
+ }
+ break;
+
+ default:
+ switch (*input_ptr_)
+ {
+ case ' ':
+ case '\t':
+ case '\n':
+ case '\r':
+ skip_space();
+ break;
+ default:
+ return;
+ }
+ break;
+ }
+ }
+ }
+
+ void begin_object(basic_json_visitor<char_type>& visitor, std::error_code& ec)
+ {
+ if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth()))
+ {
+ more_ = err_handler_(json_errc::max_nesting_depth_exceeded, *this);
+ if (!more_)
+ {
+ ec = json_errc::max_nesting_depth_exceeded;
+ return;
+ }
+ }
+
+ push_state(json_parse_state::object);
+ state_ = json_parse_state::expect_member_name_or_end;
+ more_ = visitor.begin_object(semantic_tag::none, *this, ec);
+ }
+
+ void end_object(basic_json_visitor<char_type>& visitor, std::error_code& ec)
+ {
+ if (JSONCONS_UNLIKELY(nesting_depth_ < 1))
+ {
+ err_handler_(json_errc::unexpected_rbrace, *this);
+ ec = json_errc::unexpected_rbrace;
+ more_ = false;
+ return;
+ }
+ --nesting_depth_;
+ state_ = pop_state();
+ if (state_ == json_parse_state::object)
+ {
+ more_ = visitor.end_object(*this, ec);
+ }
+ else if (state_ == json_parse_state::array)
+ {
+ err_handler_(json_errc::expected_comma_or_rbracket, *this);
+ ec = json_errc::expected_comma_or_rbracket;
+ more_ = false;
+ return;
+ }
+ else
+ {
+ err_handler_(json_errc::unexpected_rbrace, *this);
+ ec = json_errc::unexpected_rbrace;
+ more_ = false;
+ return;
+ }
+
+ if (parent() == json_parse_state::root)
+ {
+ state_ = json_parse_state::accept;
+ }
+ else
+ {
+ state_ = json_parse_state::expect_comma_or_end;
+ }
+ }
+
+ void begin_array(basic_json_visitor<char_type>& visitor, std::error_code& ec)
+ {
+ if (++nesting_depth_ > options_.max_nesting_depth())
+ {
+ more_ = err_handler_(json_errc::max_nesting_depth_exceeded, *this);
+ if (!more_)
+ {
+ ec = json_errc::max_nesting_depth_exceeded;
+ return;
+ }
+ }
+
+ push_state(json_parse_state::array);
+ state_ = json_parse_state::expect_value_or_end;
+ more_ = visitor.begin_array(semantic_tag::none, *this, ec);
+ }
+
+ void end_array(basic_json_visitor<char_type>& visitor, std::error_code& ec)
+ {
+ if (nesting_depth_ < 1)
+ {
+ err_handler_(json_errc::unexpected_rbracket, *this);
+ ec = json_errc::unexpected_rbracket;
+ more_ = false;
+ return;
+ }
+ --nesting_depth_;
+ state_ = pop_state();
+ if (state_ == json_parse_state::array)
+ {
+ more_ = visitor.end_array(*this, ec);
+ }
+ else if (state_ == json_parse_state::object)
+ {
+ err_handler_(json_errc::expected_comma_or_rbrace, *this);
+ ec = json_errc::expected_comma_or_rbrace;
+ more_ = false;
+ return;
+ }
+ else
+ {
+ err_handler_(json_errc::unexpected_rbracket, *this);
+ ec = json_errc::unexpected_rbracket;
+ more_ = false;
+ return;
+ }
+ if (parent() == json_parse_state::root)
+ {
+ state_ = json_parse_state::accept;
+ }
+ else
+ {
+ state_ = json_parse_state::expect_comma_or_end;
+ }
+ }
+
+ void reinitialize()
+ {
+ reset();
+ cp_ = 0;
+ cp2_ = 0;
+ saved_position_ = 0;
+ begin_input_ = nullptr;
+ end_input_ = nullptr;
+ input_ptr_ = nullptr;
+ string_buffer_.clear();
+ }
+
+ void reset()
+ {
+ state_stack_.clear();
+ state_stack_.reserve(initial_stack_capacity_);
+ push_state(json_parse_state::root);
+ state_ = json_parse_state::start;
+ more_ = true;
+ done_ = false;
+ line_ = 1;
+ position_ = 0;
+ mark_position_ = 0;
+ nesting_depth_ = 0;
+ }
+
+ void restart()
+ {
+ more_ = true;
+ }
+
+ void check_done()
+ {
+ std::error_code ec;
+ check_done(ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec,line_,column()));
+ }
+ }
+
+ void check_done(std::error_code& ec)
+ {
+ for (; input_ptr_ != end_input_; ++input_ptr_)
+ {
+ char_type curr_char_ = *input_ptr_;
+ switch (curr_char_)
+ {
+ case '\n':
+ case '\r':
+ case '\t':
+ case ' ':
+ break;
+ default:
+ more_ = err_handler_(json_errc::extra_character, *this);
+ if (!more_)
+ {
+ ec = json_errc::extra_character;
+ return;
+ }
+ break;
+ }
+ }
+ }
+
+ void update(const string_view_type sv)
+ {
+ update(sv.data(),sv.length());
+ }
+
+ void update(const char_type* data, std::size_t length)
+ {
+ begin_input_ = data;
+ end_input_ = data + length;
+ input_ptr_ = begin_input_;
+ }
+
+ void parse_some(basic_json_visitor<char_type>& visitor)
+ {
+ std::error_code ec;
+ parse_some(visitor, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec,line_,column()));
+ }
+ }
+
+ void parse_some(basic_json_visitor<char_type>& visitor, std::error_code& ec)
+ {
+ parse_some_(visitor, ec);
+ }
+
+ void finish_parse(basic_json_visitor<char_type>& visitor)
+ {
+ std::error_code ec;
+ finish_parse(visitor, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec,line_,column()));
+ }
+ }
+
+ void finish_parse(basic_json_visitor<char_type>& visitor, std::error_code& ec)
+ {
+ while (!finished())
+ {
+ parse_some(visitor, ec);
+ }
+ }
+
+ void parse_some_(basic_json_visitor<char_type>& visitor, std::error_code& ec)
+ {
+ if (state_ == json_parse_state::accept)
+ {
+ visitor.flush();
+ done_ = true;
+ state_ = json_parse_state::done;
+ more_ = false;
+ return;
+ }
+ const char_type* local_input_end = end_input_;
+
+ if (input_ptr_ == local_input_end && more_)
+ {
+ switch (state_)
+ {
+ case json_parse_state::zero:
+ case json_parse_state::integer:
+ end_integer_value(visitor, ec);
+ if (ec) return;
+ break;
+ case json_parse_state::fraction2:
+ end_fraction_value(visitor, ec);
+ if (ec) return;
+ break;
+ case json_parse_state::exp3:
+ end_fraction_value(visitor, ec);
+ if (ec) return;
+ break;
+ case json_parse_state::accept:
+ visitor.flush();
+ done_ = true;
+ state_ = json_parse_state::done;
+ more_ = false;
+ break;
+ case json_parse_state::start:
+ case json_parse_state::done:
+ more_ = false;
+ break;
+ case json_parse_state::cr:
+ state_ = pop_state();
+ break;
+ default:
+ err_handler_(json_errc::unexpected_eof, *this);
+ ec = json_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ }
+
+ while ((input_ptr_ < local_input_end) && more_)
+ {
+ switch (state_)
+ {
+ case json_parse_state::accept:
+ visitor.flush();
+ done_ = true;
+ state_ = json_parse_state::done;
+ more_ = false;
+ break;
+ case json_parse_state::cr:
+ ++line_;
+ mark_position_ = position_;
+ switch (*input_ptr_)
+ {
+ case '\n':
+ ++input_ptr_;
+ ++position_;
+ state_ = pop_state();
+ break;
+ default:
+ state_ = pop_state();
+ break;
+ }
+ break;
+ case json_parse_state::start:
+ {
+ switch (*input_ptr_)
+ {
+ JSONCONS_ILLEGAL_CONTROL_CHARACTER:
+ more_ = err_handler_(json_errc::illegal_control_character, *this);
+ if (!more_)
+ {
+ ec = json_errc::illegal_control_character;
+ return;
+ }
+ break;
+ case '\r':
+ push_state(state_);
+ ++input_ptr_;
+ ++position_;
+ state_ = json_parse_state::cr;
+ break;
+ case '\n':
+ ++input_ptr_;
+ ++line_;
+ ++position_;
+ mark_position_ = position_;
+ break;
+ case ' ':case '\t':
+ skip_space();
+ break;
+ case '/':
+ ++input_ptr_;
+ ++position_;
+ push_state(state_);
+ state_ = json_parse_state::slash;
+ break;
+ case '{':
+ begin_object(visitor, ec);
+ if (ec) return;
+ ++input_ptr_;
+ ++position_;
+ break;
+ case '[':
+ begin_array(visitor, ec);
+ if (ec) return;
+ ++input_ptr_;
+ ++position_;
+ break;
+ case '\"':
+ state_ = json_parse_state::string;
+ ++input_ptr_;
+ ++position_;
+ string_buffer_.clear();
+ parse_string(visitor, ec);
+ if (ec) return;
+ break;
+ case '-':
+ string_buffer_.clear();
+ string_buffer_.push_back('-');
+ ++input_ptr_;
+ ++position_;
+ state_ = json_parse_state::minus;
+ parse_number(visitor, ec);
+ if (ec) {return;}
+ break;
+ case '0':
+ string_buffer_.clear();
+ string_buffer_.push_back(static_cast<char>(*input_ptr_));
+ state_ = json_parse_state::zero;
+ ++input_ptr_;
+ ++position_;
+ parse_number(visitor, ec);
+ if (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<char>(*input_ptr_));
+ ++input_ptr_;
+ ++position_;
+ state_ = json_parse_state::integer;
+ parse_number(visitor, ec);
+ if (ec) {return;}
+ break;
+ case 'n':
+ parse_null(visitor, ec);
+ if (ec) {return;}
+ break;
+ case 't':
+ parse_true(visitor, ec);
+ if (ec) {return;}
+ break;
+ case 'f':
+ parse_false(visitor, ec);
+ if (ec) {return;}
+ break;
+ case '}':
+ err_handler_(json_errc::unexpected_rbrace, *this);
+ ec = json_errc::unexpected_rbrace;
+ more_ = false;
+ return;
+ case ']':
+ err_handler_(json_errc::unexpected_rbracket, *this);
+ ec = json_errc::unexpected_rbracket;
+ more_ = false;
+ return;
+ default:
+ err_handler_(json_errc::syntax_error, *this);
+ ec = json_errc::syntax_error;
+ more_ = false;
+ return;
+ }
+ }
+ break;
+
+ case json_parse_state::expect_comma_or_end:
+ {
+ switch (*input_ptr_)
+ {
+ JSONCONS_ILLEGAL_CONTROL_CHARACTER:
+ more_ = err_handler_(json_errc::illegal_control_character, *this);
+ if (!more_)
+ {
+ ec = json_errc::illegal_control_character;
+ return;
+ }
+ ++input_ptr_;
+ ++position_;
+ break;
+ case '\r':
+ ++input_ptr_;
+ ++position_;
+ push_state(state_);
+ state_ = json_parse_state::cr;
+ break;
+ case '\n':
+ ++input_ptr_;
+ ++line_;
+ ++position_;
+ mark_position_ = position_;
+ break;
+ case ' ':case '\t':
+ skip_space();
+ break;
+ case '/':
+ ++input_ptr_;
+ ++position_;
+ push_state(state_);
+ state_ = json_parse_state::slash;
+ break;
+ case '}':
+ end_object(visitor, ec);
+ if (ec) return;
+ ++input_ptr_;
+ ++position_;
+ break;
+ case ']':
+ end_array(visitor, ec);
+ if (ec) return;
+ ++input_ptr_;
+ ++position_;
+ break;
+ case ',':
+ begin_member_or_element(ec);
+ if (ec) return;
+ ++input_ptr_;
+ ++position_;
+ break;
+ default:
+ if (parent() == json_parse_state::array)
+ {
+ more_ = err_handler_(json_errc::expected_comma_or_rbracket, *this);
+ if (!more_)
+ {
+ ec = json_errc::expected_comma_or_rbracket;
+ return;
+ }
+ }
+ else if (parent() == json_parse_state::object)
+ {
+ more_ = err_handler_(json_errc::expected_comma_or_rbrace, *this);
+ if (!more_)
+ {
+ ec = json_errc::expected_comma_or_rbrace;
+ return;
+ }
+ }
+ ++input_ptr_;
+ ++position_;
+ break;
+ }
+ }
+ break;
+ case json_parse_state::expect_member_name_or_end:
+ {
+ switch (*input_ptr_)
+ {
+ JSONCONS_ILLEGAL_CONTROL_CHARACTER:
+ more_ = err_handler_(json_errc::illegal_control_character, *this);
+ if (!more_)
+ {
+ ec = json_errc::illegal_control_character;
+ return;
+ }
+ ++input_ptr_;
+ ++position_;
+ break;
+ case '\r':
+ ++input_ptr_;
+ ++position_;
+ push_state(state_);
+ state_ = json_parse_state::cr;
+ break;
+ case '\n':
+ ++input_ptr_;
+ ++line_;
+ ++position_;
+ mark_position_ = position_;
+ break;
+ case ' ':case '\t':
+ skip_space();
+ break;
+ case '/':
+ ++input_ptr_;
+ ++position_;
+ push_state(state_);
+ state_ = json_parse_state::slash;
+ break;
+ case '}':
+ end_object(visitor, ec);
+ if (ec) return;
+ ++input_ptr_;
+ ++position_;
+ break;
+ case '\"':
+ ++input_ptr_;
+ ++position_;
+ push_state(json_parse_state::member_name);
+ state_ = json_parse_state::string;
+ string_buffer_.clear();
+ parse_string(visitor, ec);
+ if (ec) return;
+ break;
+ case '\'':
+ more_ = err_handler_(json_errc::single_quote, *this);
+ if (!more_)
+ {
+ ec = json_errc::single_quote;
+ return;
+ }
+ ++input_ptr_;
+ ++position_;
+ break;
+ default:
+ more_ = err_handler_(json_errc::expected_key, *this);
+ if (!more_)
+ {
+ ec = json_errc::expected_key;
+ return;
+ }
+ ++input_ptr_;
+ ++position_;
+ break;
+ }
+ }
+ break;
+ case json_parse_state::expect_member_name:
+ {
+ switch (*input_ptr_)
+ {
+ JSONCONS_ILLEGAL_CONTROL_CHARACTER:
+ more_ = err_handler_(json_errc::illegal_control_character, *this);
+ if (!more_)
+ {
+ ec = json_errc::illegal_control_character;
+ return;
+ }
+ ++input_ptr_;
+ ++position_;
+ break;
+ case '\r':
+ ++input_ptr_;
+ ++position_;
+ push_state(state_);
+ state_ = json_parse_state::cr;
+ break;
+ case '\n':
+ ++input_ptr_;
+ ++line_;
+ ++position_;
+ mark_position_ = position_;
+ break;
+ case ' ':case '\t':
+ skip_space();
+ break;
+ case '/':
+ ++input_ptr_;
+ ++position_;
+ push_state(state_);
+ state_ = json_parse_state::slash;
+ break;
+ case '\"':
+ ++input_ptr_;
+ ++position_;
+ push_state(json_parse_state::member_name);
+ state_ = json_parse_state::string;
+ string_buffer_.clear();
+ parse_string(visitor, ec);
+ if (ec) return;
+ break;
+ case '}':
+ more_ = err_handler_(json_errc::extra_comma, *this);
+ if (!more_)
+ {
+ ec = json_errc::extra_comma;
+ return;
+ }
+ end_object(visitor, ec); // Recover
+ if (ec) return;
+ ++input_ptr_;
+ ++position_;
+ break;
+ case '\'':
+ more_ = err_handler_(json_errc::single_quote, *this);
+ if (!more_)
+ {
+ ec = json_errc::single_quote;
+ return;
+ }
+ ++input_ptr_;
+ ++position_;
+ break;
+ default:
+ more_ = err_handler_(json_errc::expected_key, *this);
+ if (!more_)
+ {
+ ec = json_errc::expected_key;
+ return;
+ }
+ ++input_ptr_;
+ ++position_;
+ break;
+ }
+ }
+ break;
+ case json_parse_state::expect_colon:
+ {
+ switch (*input_ptr_)
+ {
+ JSONCONS_ILLEGAL_CONTROL_CHARACTER:
+ more_ = err_handler_(json_errc::illegal_control_character, *this);
+ if (!more_)
+ {
+ ec = json_errc::illegal_control_character;
+ return;
+ }
+ ++input_ptr_;
+ ++position_;
+ break;
+ case '\r':
+ push_state(state_);
+ state_ = json_parse_state::cr;
+ ++input_ptr_;
+ ++position_;
+ break;
+ case '\n':
+ ++input_ptr_;
+ ++line_;
+ ++position_;
+ mark_position_ = position_;
+ break;
+ case ' ':case '\t':
+ skip_space();
+ break;
+ case '/':
+ push_state(state_);
+ state_ = json_parse_state::slash;
+ ++input_ptr_;
+ ++position_;
+ break;
+ case ':':
+ state_ = json_parse_state::expect_value;
+ ++input_ptr_;
+ ++position_;
+ break;
+ default:
+ more_ = err_handler_(json_errc::expected_colon, *this);
+ if (!more_)
+ {
+ ec = json_errc::expected_colon;
+ return;
+ }
+ ++input_ptr_;
+ ++position_;
+ break;
+ }
+ }
+ break;
+
+ case json_parse_state::expect_value:
+ {
+ switch (*input_ptr_)
+ {
+ JSONCONS_ILLEGAL_CONTROL_CHARACTER:
+ more_ = err_handler_(json_errc::illegal_control_character, *this);
+ if (!more_)
+ {
+ ec = json_errc::illegal_control_character;
+ return;
+ }
+ ++input_ptr_;
+ ++position_;
+ break;
+ case '\r':
+ push_state(state_);
+ ++input_ptr_;
+ ++position_;
+ state_ = json_parse_state::cr;
+ break;
+ case '\n':
+ ++input_ptr_;
+ ++line_;
+ ++position_;
+ mark_position_ = position_;
+ break;
+ case ' ':case '\t':
+ skip_space();
+ break;
+ case '/':
+ push_state(state_);
+ ++input_ptr_;
+ ++position_;
+ state_ = json_parse_state::slash;
+ break;
+ case '{':
+ begin_object(visitor, ec);
+ if (ec) return;
+ ++input_ptr_;
+ ++position_;
+ break;
+ case '[':
+ begin_array(visitor, ec);
+ if (ec) return;
+ ++input_ptr_;
+ ++position_;
+ break;
+ case '\"':
+ ++input_ptr_;
+ ++position_;
+ state_ = json_parse_state::string;
+ string_buffer_.clear();
+ parse_string(visitor, ec);
+ if (ec) return;
+ break;
+ case '-':
+ string_buffer_.clear();
+ string_buffer_.push_back('-');
+ ++input_ptr_;
+ ++position_;
+ state_ = json_parse_state::minus;
+ parse_number(visitor, ec);
+ if (ec) {return;}
+ break;
+ case '0':
+ string_buffer_.clear();
+ string_buffer_.push_back(static_cast<char>(*input_ptr_));
+ ++input_ptr_;
+ ++position_;
+ state_ = json_parse_state::zero;
+ parse_number(visitor, ec);
+ if (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<char>(*input_ptr_));
+ ++input_ptr_;
+ ++position_;
+ state_ = json_parse_state::integer;
+ parse_number(visitor, ec);
+ if (ec) {return;}
+ break;
+ case 'n':
+ parse_null(visitor, ec);
+ if (ec) {return;}
+ break;
+ case 't':
+ parse_true(visitor, ec);
+ if (ec) {return;}
+ break;
+ case 'f':
+ parse_false(visitor, ec);
+ if (ec) {return;}
+ break;
+ case ']':
+ if (parent() == json_parse_state::array)
+ {
+ more_ = err_handler_(json_errc::extra_comma, *this);
+ if (!more_)
+ {
+ ec = json_errc::extra_comma;
+ return;
+ }
+ end_array(visitor, ec); // Recover
+ if (ec) return;
+ }
+ else
+ {
+ more_ = err_handler_(json_errc::expected_value, *this);
+ if (!more_)
+ {
+ ec = json_errc::expected_value;
+ return;
+ }
+ }
+ ++input_ptr_;
+ ++position_;
+ break;
+ case '\'':
+ more_ = err_handler_(json_errc::single_quote, *this);
+ if (!more_)
+ {
+ ec = json_errc::single_quote;
+ return;
+ }
+ ++input_ptr_;
+ ++position_;
+ break;
+ default:
+ more_ = err_handler_(json_errc::expected_value, *this);
+ if (!more_)
+ {
+ ec = json_errc::expected_value;
+ return;
+ }
+ ++input_ptr_;
+ ++position_;
+ break;
+ }
+ }
+ break;
+ case json_parse_state::expect_value_or_end:
+ {
+ switch (*input_ptr_)
+ {
+ JSONCONS_ILLEGAL_CONTROL_CHARACTER:
+ more_ = err_handler_(json_errc::illegal_control_character, *this);
+ if (!more_)
+ {
+ ec = json_errc::illegal_control_character;
+ return;
+ }
+ ++input_ptr_;
+ ++position_;
+ break;
+ case '\r':
+ ++input_ptr_;
+ ++position_;
+ push_state(state_);
+ state_ = json_parse_state::cr;
+ break;
+ case '\n':
+ ++input_ptr_;
+ ++line_;
+ ++position_;
+ mark_position_ = position_;
+ break;
+ case ' ':case '\t':
+ skip_space();
+ break;
+ case '/':
+ ++input_ptr_;
+ ++position_;
+ push_state(state_);
+ state_ = json_parse_state::slash;
+ break;
+ case '{':
+ begin_object(visitor, ec);
+ if (ec) return;
+ ++input_ptr_;
+ ++position_;
+ break;
+ case '[':
+ begin_array(visitor, ec);
+ if (ec) return;
+ ++input_ptr_;
+ ++position_;
+ break;
+ case ']':
+ end_array(visitor, ec);
+ if (ec) return;
+ ++input_ptr_;
+ ++position_;
+ break;
+ case '\"':
+ ++input_ptr_;
+ ++position_;
+ state_ = json_parse_state::string;
+ string_buffer_.clear();
+ parse_string(visitor, ec);
+ if (ec) return;
+ break;
+ case '-':
+ string_buffer_.clear();
+ string_buffer_.push_back('-');
+ ++input_ptr_;
+ ++position_;
+ state_ = json_parse_state::minus;
+ parse_number(visitor, ec);
+ if (ec) {return;}
+ break;
+ case '0':
+ string_buffer_.clear();
+ string_buffer_.push_back(static_cast<char>(*input_ptr_));
+ ++input_ptr_;
+ ++position_;
+ state_ = json_parse_state::zero;
+ parse_number(visitor, ec);
+ if (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<char>(*input_ptr_));
+ ++input_ptr_;
+ ++position_;
+ state_ = json_parse_state::integer;
+ parse_number(visitor, ec);
+ if (ec) {return;}
+ break;
+ case 'n':
+ parse_null(visitor, ec);
+ if (ec) {return;}
+ break;
+ case 't':
+ parse_true(visitor, ec);
+ if (ec) {return;}
+ break;
+ case 'f':
+ parse_false(visitor, ec);
+ if (ec) {return;}
+ break;
+ case '\'':
+ more_ = err_handler_(json_errc::single_quote, *this);
+ if (!more_)
+ {
+ ec = json_errc::single_quote;
+ return;
+ }
+ ++input_ptr_;
+ ++position_;
+ break;
+ default:
+ more_ = err_handler_(json_errc::expected_value, *this);
+ if (!more_)
+ {
+ ec = json_errc::expected_value;
+ return;
+ }
+ ++input_ptr_;
+ ++position_;
+ break;
+ }
+ }
+ break;
+ case json_parse_state::string:
+ case json_parse_state::escape:
+ case json_parse_state::escape_u1:
+ case json_parse_state::escape_u2:
+ case json_parse_state::escape_u3:
+ case json_parse_state::escape_u4:
+ case json_parse_state::escape_expect_surrogate_pair1:
+ case json_parse_state::escape_expect_surrogate_pair2:
+ case json_parse_state::escape_u5:
+ case json_parse_state::escape_u6:
+ case json_parse_state::escape_u7:
+ case json_parse_state::escape_u8:
+ parse_string(visitor, ec);
+ if (ec) return;
+ break;
+ case json_parse_state::minus:
+ case json_parse_state::zero:
+ case json_parse_state::integer:
+ case json_parse_state::fraction1:
+ case json_parse_state::fraction2:
+ case json_parse_state::exp1:
+ case json_parse_state::exp2:
+ case json_parse_state::exp3:
+ parse_number(visitor, ec);
+ if (ec) return;
+ break;
+ case json_parse_state::t:
+ switch (*input_ptr_)
+ {
+ case 'r':
+ ++input_ptr_;
+ ++position_;
+ state_ = json_parse_state::tr;
+ break;
+ default:
+ err_handler_(json_errc::invalid_value, *this);
+ ec = json_errc::invalid_value;
+ more_ = false;
+ return;
+ }
+ break;
+ case json_parse_state::tr:
+ switch (*input_ptr_)
+ {
+ case 'u':
+ state_ = json_parse_state::tru;
+ break;
+ default:
+ err_handler_(json_errc::invalid_value, *this);
+ ec = json_errc::invalid_value;
+ more_ = false;
+ return;
+ }
+ ++input_ptr_;
+ ++position_;
+ break;
+ case json_parse_state::tru:
+ switch (*input_ptr_)
+ {
+ case 'e':
+ more_ = visitor.bool_value(true, semantic_tag::none, *this, ec);
+ if (parent() == json_parse_state::root)
+ {
+ state_ = json_parse_state::accept;
+ }
+ else
+ {
+ state_ = json_parse_state::expect_comma_or_end;
+ }
+ break;
+ default:
+ err_handler_(json_errc::invalid_value, *this);
+ ec = json_errc::invalid_value;
+ more_ = false;
+ return;
+ }
+ ++input_ptr_;
+ ++position_;
+ break;
+ case json_parse_state::f:
+ switch (*input_ptr_)
+ {
+ case 'a':
+ ++input_ptr_;
+ ++position_;
+ state_ = json_parse_state::fa;
+ break;
+ default:
+ err_handler_(json_errc::invalid_value, *this);
+ ec = json_errc::invalid_value;
+ more_ = false;
+ return;
+ }
+ break;
+ case json_parse_state::fa:
+ switch (*input_ptr_)
+ {
+ case 'l':
+ state_ = json_parse_state::fal;
+ break;
+ default:
+ err_handler_(json_errc::invalid_value, *this);
+ ec = json_errc::invalid_value;
+ more_ = false;
+ return;
+ }
+ ++input_ptr_;
+ ++position_;
+ break;
+ case json_parse_state::fal:
+ switch (*input_ptr_)
+ {
+ case 's':
+ state_ = json_parse_state::fals;
+ break;
+ default:
+ err_handler_(json_errc::invalid_value, *this);
+ ec = json_errc::invalid_value;
+ more_ = false;
+ return;
+ }
+ ++input_ptr_;
+ ++position_;
+ break;
+ case json_parse_state::fals:
+ switch (*input_ptr_)
+ {
+ case 'e':
+ more_ = visitor.bool_value(false, semantic_tag::none, *this, ec);
+ if (parent() == json_parse_state::root)
+ {
+ state_ = json_parse_state::accept;
+ }
+ else
+ {
+ state_ = json_parse_state::expect_comma_or_end;
+ }
+ break;
+ default:
+ err_handler_(json_errc::invalid_value, *this);
+ ec = json_errc::invalid_value;
+ more_ = false;
+ return;
+ }
+ ++input_ptr_;
+ ++position_;
+ break;
+ case json_parse_state::n:
+ switch (*input_ptr_)
+ {
+ case 'u':
+ ++input_ptr_;
+ ++position_;
+ state_ = json_parse_state::nu;
+ break;
+ default:
+ err_handler_(json_errc::invalid_value, *this);
+ ec = json_errc::invalid_value;
+ more_ = false;
+ return;
+ }
+ break;
+ case json_parse_state::nu:
+ switch (*input_ptr_)
+ {
+ case 'l':
+ state_ = json_parse_state::nul;
+ break;
+ default:
+ err_handler_(json_errc::invalid_value, *this);
+ ec = json_errc::invalid_value;
+ more_ = false;
+ return;
+ }
+ ++input_ptr_;
+ ++position_;
+ break;
+ case json_parse_state::nul:
+ switch (*input_ptr_)
+ {
+ case 'l':
+ more_ = visitor.null_value(semantic_tag::none, *this, ec);
+ if (parent() == json_parse_state::root)
+ {
+ state_ = json_parse_state::accept;
+ }
+ else
+ {
+ state_ = json_parse_state::expect_comma_or_end;
+ }
+ break;
+ default:
+ err_handler_(json_errc::invalid_value, *this);
+ ec = json_errc::invalid_value;
+ more_ = false;
+ return;
+ }
+ ++input_ptr_;
+ ++position_;
+ break;
+ case json_parse_state::slash:
+ {
+ switch (*input_ptr_)
+ {
+ case '*':
+ state_ = json_parse_state::slash_star;
+ more_ = err_handler_(json_errc::illegal_comment, *this);
+ if (!more_)
+ {
+ ec = json_errc::illegal_comment;
+ return;
+ }
+ break;
+ case '/':
+ state_ = json_parse_state::slash_slash;
+ more_ = err_handler_(json_errc::illegal_comment, *this);
+ if (!more_)
+ {
+ ec = json_errc::illegal_comment;
+ return;
+ }
+ break;
+ default:
+ more_ = err_handler_(json_errc::syntax_error, *this);
+ if (!more_)
+ {
+ ec = json_errc::syntax_error;
+ return;
+ }
+ break;
+ }
+ ++input_ptr_;
+ ++position_;
+ break;
+ }
+ case json_parse_state::slash_star:
+ {
+ switch (*input_ptr_)
+ {
+ case '\r':
+ push_state(state_);
+ ++input_ptr_;
+ ++position_;
+ state_ = json_parse_state::cr;
+ break;
+ case '\n':
+ ++input_ptr_;
+ ++line_;
+ ++position_;
+ mark_position_ = position_;
+ break;
+ case '*':
+ ++input_ptr_;
+ ++position_;
+ state_ = json_parse_state::slash_star_star;
+ break;
+ default:
+ ++input_ptr_;
+ ++position_;
+ break;
+ }
+ break;
+ }
+ case json_parse_state::slash_slash:
+ {
+ switch (*input_ptr_)
+ {
+ case '\r':
+ state_ = pop_state();
+ break;
+ case '\n':
+ state_ = pop_state();
+ break;
+ default:
+ ++input_ptr_;
+ ++position_;
+ }
+ break;
+ }
+ case json_parse_state::slash_star_star:
+ {
+ switch (*input_ptr_)
+ {
+ case '/':
+ state_ = pop_state();
+ break;
+ default:
+ state_ = json_parse_state::slash_star;
+ break;
+ }
+ ++input_ptr_;
+ ++position_;
+ break;
+ }
+ default:
+ JSONCONS_ASSERT(false);
+ break;
+ }
+ }
+ }
+
+ void parse_true(basic_json_visitor<char_type>& visitor, std::error_code& ec)
+ {
+ saved_position_ = position_;
+ if (JSONCONS_LIKELY(end_input_ - input_ptr_ >= 4))
+ {
+ if (*(input_ptr_+1) == 'r' && *(input_ptr_+2) == 'u' && *(input_ptr_+3) == 'e')
+ {
+ more_ = visitor.bool_value(true, semantic_tag::none, *this, ec);
+ input_ptr_ += 4;
+ position_ += 4;
+ if (parent() == json_parse_state::root)
+ {
+ state_ = json_parse_state::accept;
+ }
+ else
+ {
+ state_ = json_parse_state::expect_comma_or_end;
+ }
+ }
+ else
+ {
+ err_handler_(json_errc::invalid_value, *this);
+ ec = json_errc::invalid_value;
+ more_ = false;
+ return;
+ }
+ }
+ else
+ {
+ ++input_ptr_;
+ ++position_;
+ state_ = json_parse_state::t;
+ }
+ }
+
+ void parse_null(basic_json_visitor<char_type>& visitor, std::error_code& ec)
+ {
+ saved_position_ = position_;
+ if (JSONCONS_LIKELY(end_input_ - input_ptr_ >= 4))
+ {
+ if (*(input_ptr_+1) == 'u' && *(input_ptr_+2) == 'l' && *(input_ptr_+3) == 'l')
+ {
+ more_ = visitor.null_value(semantic_tag::none, *this, ec);
+ input_ptr_ += 4;
+ position_ += 4;
+ if (parent() == json_parse_state::root)
+ {
+ state_ = json_parse_state::accept;
+ }
+ else
+ {
+ state_ = json_parse_state::expect_comma_or_end;
+ }
+ }
+ else
+ {
+ err_handler_(json_errc::invalid_value, *this);
+ ec = json_errc::invalid_value;
+ more_ = false;
+ return;
+ }
+ }
+ else
+ {
+ ++input_ptr_;
+ ++position_;
+ state_ = json_parse_state::n;
+ }
+ }
+
+ void parse_false(basic_json_visitor<char_type>& visitor, std::error_code& ec)
+ {
+ saved_position_ = position_;
+ if (JSONCONS_LIKELY(end_input_ - input_ptr_ >= 5))
+ {
+ if (*(input_ptr_+1) == 'a' && *(input_ptr_+2) == 'l' && *(input_ptr_+3) == 's' && *(input_ptr_+4) == 'e')
+ {
+ more_ = visitor.bool_value(false, semantic_tag::none, *this, ec);
+ input_ptr_ += 5;
+ position_ += 5;
+ if (parent() == json_parse_state::root)
+ {
+ state_ = json_parse_state::accept;
+ }
+ else
+ {
+ state_ = json_parse_state::expect_comma_or_end;
+ }
+ }
+ else
+ {
+ err_handler_(json_errc::invalid_value, *this);
+ ec = json_errc::invalid_value;
+ more_ = false;
+ return;
+ }
+ }
+ else
+ {
+ ++input_ptr_;
+ ++position_;
+ state_ = json_parse_state::f;
+ }
+ }
+
+ void parse_number(basic_json_visitor<char_type>& visitor, std::error_code& ec)
+ {
+ saved_position_ = position_ - 1;
+ const char_type* local_input_end = end_input_;
+
+ switch (state_)
+ {
+ case json_parse_state::minus:
+ goto minus_sign;
+ case json_parse_state::zero:
+ goto zero;
+ case json_parse_state::integer:
+ goto integer;
+ case json_parse_state::fraction1:
+ goto fraction1;
+ case json_parse_state::fraction2:
+ goto fraction2;
+ case json_parse_state::exp1:
+ goto exp1;
+ case json_parse_state::exp2:
+ goto exp2;
+ case json_parse_state::exp3:
+ goto exp3;
+ default:
+ JSONCONS_UNREACHABLE();
+ }
+minus_sign:
+ if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted
+ {
+ state_ = json_parse_state::minus;
+ return;
+ }
+ switch (*input_ptr_)
+ {
+ case '0':
+ string_buffer_.push_back(static_cast<char>(*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<char>(*input_ptr_));
+ ++input_ptr_;
+ ++position_;
+ goto integer;
+ default:
+ err_handler_(json_errc::invalid_number, *this);
+ ec = json_errc::expected_value;
+ more_ = false;
+ return;
+ }
+zero:
+ if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted
+ {
+ state_ = json_parse_state::zero;
+ return;
+ }
+ switch (*input_ptr_)
+ {
+ case '\r':
+ end_integer_value(visitor, ec);
+ if (ec) return;
+ ++input_ptr_;
+ ++position_;
+ push_state(state_);
+ state_ = json_parse_state::cr;
+ return;
+ case '\n':
+ end_integer_value(visitor, ec);
+ if (ec) return;
+ ++input_ptr_;
+ ++line_;
+ ++position_;
+ mark_position_ = position_;
+ return;
+ case ' ':case '\t':
+ end_integer_value(visitor, ec);
+ if (ec) return;
+ skip_space();
+ return;
+ case '/':
+ end_integer_value(visitor, ec);
+ if (ec) return;
+ ++input_ptr_;
+ ++position_;
+ push_state(state_);
+ state_ = json_parse_state::slash;
+ return;
+ case '}':
+ end_integer_value(visitor, ec);
+ if (ec) return;
+ state_ = json_parse_state::expect_comma_or_end;
+ return;
+ case ']':
+ end_integer_value(visitor, ec);
+ if (ec) return;
+ state_ = json_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<char>(*input_ptr_));
+ ++input_ptr_;
+ ++position_;
+ goto exp1;
+ case ',':
+ end_integer_value(visitor, ec);
+ if (ec) return;
+ begin_member_or_element(ec);
+ if (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;
+ state_ = json_parse_state::zero;
+ return;
+ default:
+ err_handler_(json_errc::invalid_number, *this);
+ ec = json_errc::invalid_number;
+ more_ = false;
+ state_ = json_parse_state::zero;
+ return;
+ }
+integer:
+ if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted
+ {
+ state_ = json_parse_state::integer;
+ return;
+ }
+ switch (*input_ptr_)
+ {
+ case '\r':
+ end_integer_value(visitor, ec);
+ if (ec) return;
+ push_state(state_);
+ ++input_ptr_;
+ ++position_;
+ state_ = json_parse_state::cr;
+ return;
+ case '\n':
+ end_integer_value(visitor, ec);
+ if (ec) return;
+ ++input_ptr_;
+ ++line_;
+ ++position_;
+ mark_position_ = position_;
+ return;
+ case ' ':case '\t':
+ end_integer_value(visitor, ec);
+ if (ec) return;
+ skip_space();
+ return;
+ case '/':
+ end_integer_value(visitor, ec);
+ if (ec) return;
+ push_state(state_);
+ ++input_ptr_;
+ ++position_;
+ state_ = json_parse_state::slash;
+ return;
+ case '}':
+ end_integer_value(visitor, ec);
+ if (ec) return;
+ state_ = json_parse_state::expect_comma_or_end;
+ return;
+ case ']':
+ end_integer_value(visitor, ec);
+ if (ec) return;
+ state_ = json_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<char>(*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<char>(*input_ptr_));
+ ++input_ptr_;
+ ++position_;
+ goto exp1;
+ case ',':
+ end_integer_value(visitor, ec);
+ if (ec) return;
+ begin_member_or_element(ec);
+ if (ec) return;
+ ++input_ptr_;
+ ++position_;
+ return;
+ default:
+ err_handler_(json_errc::invalid_number, *this);
+ ec = json_errc::invalid_number;
+ more_ = false;
+ state_ = json_parse_state::integer;
+ return;
+ }
+fraction1:
+ if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted
+ {
+ state_ = json_parse_state::fraction1;
+ return;
+ }
+ switch (*input_ptr_)
+ {
+ 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<char>(*input_ptr_));
+ ++input_ptr_;
+ ++position_;
+ goto fraction2;
+ default:
+ err_handler_(json_errc::invalid_number, *this);
+ ec = json_errc::invalid_number;
+ more_ = false;
+ state_ = json_parse_state::fraction1;
+ return;
+ }
+fraction2:
+ if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted
+ {
+ state_ = json_parse_state::fraction2;
+ return;
+ }
+ switch (*input_ptr_)
+ {
+ case '\r':
+ end_fraction_value(visitor, ec);
+ if (ec) return;
+ push_state(state_);
+ ++input_ptr_;
+ ++position_;
+ state_ = json_parse_state::cr;
+ return;
+ case '\n':
+ end_fraction_value(visitor, ec);
+ if (ec) return;
+ ++input_ptr_;
+ ++line_;
+ ++position_;
+ mark_position_ = position_;
+ return;
+ case ' ':case '\t':
+ end_fraction_value(visitor, ec);
+ if (ec) return;
+ skip_space();
+ return;
+ case '/':
+ end_fraction_value(visitor, ec);
+ if (ec) return;
+ push_state(state_);
+ ++input_ptr_;
+ ++position_;
+ state_ = json_parse_state::slash;
+ return;
+ case '}':
+ end_fraction_value(visitor, ec);
+ if (ec) return;
+ state_ = json_parse_state::expect_comma_or_end;
+ return;
+ case ']':
+ end_fraction_value(visitor, ec);
+ if (ec) return;
+ state_ = json_parse_state::expect_comma_or_end;
+ return;
+ case ',':
+ end_fraction_value(visitor, ec);
+ if (ec) return;
+ begin_member_or_element(ec);
+ if (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<char>(*input_ptr_));
+ ++input_ptr_;
+ ++position_;
+ goto fraction2;
+ case 'e':case 'E':
+ string_buffer_.push_back(static_cast<char>(*input_ptr_));
+ ++input_ptr_;
+ ++position_;
+ goto exp1;
+ default:
+ err_handler_(json_errc::invalid_number, *this);
+ ec = json_errc::invalid_number;
+ more_ = false;
+ state_ = json_parse_state::fraction2;
+ return;
+ }
+exp1:
+ if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted
+ {
+ state_ = json_parse_state::exp1;
+ return;
+ }
+ switch (*input_ptr_)
+ {
+ case '+':
+ ++input_ptr_;
+ ++position_;
+ goto exp2;
+ case '-':
+ string_buffer_.push_back(static_cast<char>(*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<char>(*input_ptr_));
+ ++input_ptr_;
+ ++position_;
+ goto exp3;
+ default:
+ err_handler_(json_errc::invalid_number, *this);
+ ec = json_errc::expected_value;
+ more_ = false;
+ state_ = json_parse_state::exp1;
+ return;
+ }
+exp2:
+ if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted
+ {
+ state_ = json_parse_state::exp2;
+ return;
+ }
+ switch (*input_ptr_)
+ {
+ 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<char>(*input_ptr_));
+ ++input_ptr_;
+ ++position_;
+ goto exp3;
+ default:
+ err_handler_(json_errc::invalid_number, *this);
+ ec = json_errc::expected_value;
+ more_ = false;
+ state_ = json_parse_state::exp2;
+ return;
+ }
+
+exp3:
+ if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted
+ {
+ state_ = json_parse_state::exp3;
+ return;
+ }
+ switch (*input_ptr_)
+ {
+ case '\r':
+ end_fraction_value(visitor, ec);
+ if (ec) return;
+ ++input_ptr_;
+ ++position_;
+ push_state(state_);
+ state_ = json_parse_state::cr;
+ return;
+ case '\n':
+ end_fraction_value(visitor, ec);
+ if (ec) return;
+ ++input_ptr_;
+ ++line_;
+ ++position_;
+ mark_position_ = position_;
+ return;
+ case ' ':case '\t':
+ end_fraction_value(visitor, ec);
+ if (ec) return;
+ skip_space();
+ return;
+ case '/':
+ end_fraction_value(visitor, ec);
+ if (ec) return;
+ push_state(state_);
+ ++input_ptr_;
+ ++position_;
+ state_ = json_parse_state::slash;
+ return;
+ case '}':
+ end_fraction_value(visitor, ec);
+ if (ec) return;
+ state_ = json_parse_state::expect_comma_or_end;
+ return;
+ case ']':
+ end_fraction_value(visitor, ec);
+ if (ec) return;
+ state_ = json_parse_state::expect_comma_or_end;
+ return;
+ case ',':
+ end_fraction_value(visitor, ec);
+ if (ec) return;
+ begin_member_or_element(ec);
+ if (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<char>(*input_ptr_));
+ ++input_ptr_;
+ ++position_;
+ goto exp3;
+ default:
+ err_handler_(json_errc::invalid_number, *this);
+ ec = json_errc::invalid_number;
+ more_ = false;
+ state_ = json_parse_state::exp3;
+ return;
+ }
+
+ JSONCONS_UNREACHABLE();
+ }
+
+ void parse_string(basic_json_visitor<char_type>& visitor, std::error_code& ec)
+ {
+ saved_position_ = position_ - 1;
+ const char_type* local_input_end = end_input_;
+ const char_type* sb = input_ptr_;
+
+ switch (state_)
+ {
+ case json_parse_state::string:
+ goto string_u1;
+ case json_parse_state::escape:
+ goto escape;
+ case json_parse_state::escape_u1:
+ goto escape_u1;
+ case json_parse_state::escape_u2:
+ goto escape_u2;
+ case json_parse_state::escape_u3:
+ goto escape_u3;
+ case json_parse_state::escape_u4:
+ goto escape_u4;
+ case json_parse_state::escape_expect_surrogate_pair1:
+ goto escape_expect_surrogate_pair1;
+ case json_parse_state::escape_expect_surrogate_pair2:
+ goto escape_expect_surrogate_pair2;
+ case json_parse_state::escape_u5:
+ goto escape_u5;
+ case json_parse_state::escape_u6:
+ goto escape_u6;
+ case json_parse_state::escape_u7:
+ goto escape_u7;
+ case json_parse_state::escape_u8:
+ goto escape_u8;
+ default:
+ JSONCONS_UNREACHABLE();
+ }
+
+string_u1:
+ while (input_ptr_ < local_input_end)
+ {
+ switch (*input_ptr_)
+ {
+ JSONCONS_ILLEGAL_CONTROL_CHARACTER:
+ {
+ position_ += (input_ptr_ - sb + 1);
+ more_ = err_handler_(json_errc::illegal_control_character, *this);
+ if (!more_)
+ {
+ ec = json_errc::illegal_control_character;
+ state_ = json_parse_state::string;
+ return;
+ }
+ // recovery - skip
+ string_buffer_.append(sb,input_ptr_-sb);
+ ++input_ptr_;
+ state_ = json_parse_state::string;
+ return;
+ }
+ case '\r':
+ {
+ position_ += (input_ptr_ - sb + 1);
+ more_ = err_handler_(json_errc::illegal_character_in_string, *this);
+ if (!more_)
+ {
+ ec = json_errc::illegal_character_in_string;
+ state_ = json_parse_state::string;
+ return;
+ }
+ // recovery - keep
+ string_buffer_.append(sb, input_ptr_ - sb + 1);
+ ++input_ptr_;
+ push_state(state_);
+ state_ = json_parse_state::cr;
+ return;
+ }
+ case '\n':
+ {
+ ++line_;
+ ++position_;
+ mark_position_ = position_;
+ more_ = err_handler_(json_errc::illegal_character_in_string, *this);
+ if (!more_)
+ {
+ ec = json_errc::illegal_character_in_string;
+ state_ = json_parse_state::string;
+ return;
+ }
+ // recovery - keep
+ string_buffer_.append(sb, input_ptr_ - sb + 1);
+ ++input_ptr_;
+ return;
+ }
+ case '\t':
+ {
+ position_ += (input_ptr_ - sb + 1);
+ more_ = err_handler_(json_errc::illegal_character_in_string, *this);
+ if (!more_)
+ {
+ ec = json_errc::illegal_character_in_string;
+ state_ = json_parse_state::string;
+ return;
+ }
+ // recovery - keep
+ string_buffer_.append(sb, input_ptr_ - sb + 1);
+ ++input_ptr_;
+ state_ = json_parse_state::string;
+ return;
+ }
+ case '\\':
+ {
+ string_buffer_.append(sb,input_ptr_-sb);
+ position_ += (input_ptr_ - sb + 1);
+ ++input_ptr_;
+ goto escape;
+ }
+ case '\"':
+ {
+ if (string_buffer_.length() == 0)
+ {
+ end_string_value(sb,input_ptr_-sb, visitor, ec);
+ if (ec) {return;}
+ }
+ else
+ {
+ string_buffer_.append(sb,input_ptr_-sb);
+ end_string_value(string_buffer_.data(),string_buffer_.length(), visitor, ec);
+ if (ec) {return;}
+ }
+ position_ += (input_ptr_ - sb + 1);
+ ++input_ptr_;
+ return;
+ }
+ default:
+ break;
+ }
+ ++input_ptr_;
+ }
+
+ // Buffer exhausted
+ {
+ string_buffer_.append(sb,input_ptr_-sb);
+ position_ += (input_ptr_ - sb + 1);
+ state_ = json_parse_state::string;
+ return;
+ }
+
+escape:
+ if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted
+ {
+ state_ = json_parse_state::escape;
+ return;
+ }
+ switch (*input_ptr_)
+ {
+ case '\"':
+ string_buffer_.push_back('\"');
+ sb = ++input_ptr_;
+ ++position_;
+ goto string_u1;
+ case '\\':
+ string_buffer_.push_back('\\');
+ sb = ++input_ptr_;
+ ++position_;
+ goto string_u1;
+ case '/':
+ string_buffer_.push_back('/');
+ sb = ++input_ptr_;
+ ++position_;
+ goto string_u1;
+ case 'b':
+ string_buffer_.push_back('\b');
+ sb = ++input_ptr_;
+ ++position_;
+ goto string_u1;
+ case 'f':
+ string_buffer_.push_back('\f');
+ sb = ++input_ptr_;
+ ++position_;
+ goto string_u1;
+ case 'n':
+ string_buffer_.push_back('\n');
+ sb = ++input_ptr_;
+ ++position_;
+ goto string_u1;
+ case 'r':
+ string_buffer_.push_back('\r');
+ sb = ++input_ptr_;
+ ++position_;
+ goto string_u1;
+ case 't':
+ string_buffer_.push_back('\t');
+ sb = ++input_ptr_;
+ ++position_;
+ goto string_u1;
+ case 'u':
+ cp_ = 0;
+ ++input_ptr_;
+ ++position_;
+ goto escape_u1;
+ default:
+ err_handler_(json_errc::illegal_escaped_character, *this);
+ ec = json_errc::illegal_escaped_character;
+ more_ = false;
+ state_ = json_parse_state::escape;
+ return;
+ }
+
+escape_u1:
+ if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted
+ {
+ state_ = json_parse_state::escape_u1;
+ return;
+ }
+ {
+ cp_ = append_to_codepoint(0, *input_ptr_, ec);
+ if (ec)
+ {
+ state_ = json_parse_state::escape_u1;
+ return;
+ }
+ ++input_ptr_;
+ ++position_;
+ goto escape_u2;
+ }
+
+escape_u2:
+ if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted
+ {
+ state_ = json_parse_state::escape_u2;
+ return;
+ }
+ {
+ cp_ = append_to_codepoint(cp_, *input_ptr_, ec);
+ if (ec)
+ {
+ state_ = json_parse_state::escape_u2;
+ return;
+ }
+ ++input_ptr_;
+ ++position_;
+ goto escape_u3;
+ }
+
+escape_u3:
+ if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted
+ {
+ state_ = json_parse_state::escape_u3;
+ return;
+ }
+ {
+ cp_ = append_to_codepoint(cp_, *input_ptr_, ec);
+ if (ec)
+ {
+ state_ = json_parse_state::escape_u3;
+ return;
+ }
+ ++input_ptr_;
+ ++position_;
+ goto escape_u4;
+ }
+
+escape_u4:
+ if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted
+ {
+ state_ = json_parse_state::escape_u4;
+ return;
+ }
+ {
+ cp_ = append_to_codepoint(cp_, *input_ptr_, ec);
+ if (ec)
+ {
+ state_ = json_parse_state::escape_u4;
+ return;
+ }
+ if (unicode_traits::is_high_surrogate(cp_))
+ {
+ ++input_ptr_;
+ ++position_;
+ goto escape_expect_surrogate_pair1;
+ }
+ else
+ {
+ unicode_traits::convert(&cp_, 1, string_buffer_);
+ sb = ++input_ptr_;
+ ++position_;
+ state_ = json_parse_state::string;
+ return;
+ }
+ }
+
+escape_expect_surrogate_pair1:
+ if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted
+ {
+ state_ = json_parse_state::escape_expect_surrogate_pair1;
+ return;
+ }
+ {
+ switch (*input_ptr_)
+ {
+ case '\\':
+ cp2_ = 0;
+ ++input_ptr_;
+ ++position_;
+ goto escape_expect_surrogate_pair2;
+ default:
+ err_handler_(json_errc::expected_codepoint_surrogate_pair, *this);
+ ec = json_errc::expected_codepoint_surrogate_pair;
+ more_ = false;
+ state_ = json_parse_state::escape_expect_surrogate_pair1;
+ return;
+ }
+ }
+
+escape_expect_surrogate_pair2:
+ if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted
+ {
+ state_ = json_parse_state::escape_expect_surrogate_pair2;
+ return;
+ }
+ {
+ switch (*input_ptr_)
+ {
+ case 'u':
+ ++input_ptr_;
+ ++position_;
+ goto escape_u5;
+ default:
+ err_handler_(json_errc::expected_codepoint_surrogate_pair, *this);
+ ec = json_errc::expected_codepoint_surrogate_pair;
+ more_ = false;
+ state_ = json_parse_state::escape_expect_surrogate_pair2;
+ return;
+ }
+ }
+
+escape_u5:
+ if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted
+ {
+ state_ = json_parse_state::escape_u5;
+ return;
+ }
+ {
+ cp2_ = append_to_codepoint(0, *input_ptr_, ec);
+ if (ec)
+ {
+ state_ = json_parse_state::escape_u5;
+ return;
+ }
+ }
+ ++input_ptr_;
+ ++position_;
+ goto escape_u6;
+
+escape_u6:
+ if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted
+ {
+ state_ = json_parse_state::escape_u6;
+ return;
+ }
+ {
+ cp2_ = append_to_codepoint(cp2_, *input_ptr_, ec);
+ if (ec)
+ {
+ state_ = json_parse_state::escape_u6;
+ return;
+ }
+ ++input_ptr_;
+ ++position_;
+ goto escape_u7;
+ }
+
+escape_u7:
+ if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted
+ {
+ state_ = json_parse_state::escape_u7;
+ return;
+ }
+ {
+ cp2_ = append_to_codepoint(cp2_, *input_ptr_, ec);
+ if (ec)
+ {
+ state_ = json_parse_state::escape_u7;
+ return;
+ }
+ ++input_ptr_;
+ ++position_;
+ goto escape_u8;
+ }
+
+escape_u8:
+ if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted
+ {
+ state_ = json_parse_state::escape_u8;
+ return;
+ }
+ {
+ cp2_ = append_to_codepoint(cp2_, *input_ptr_, ec);
+ if (ec)
+ {
+ state_ = json_parse_state::escape_u8;
+ return;
+ }
+ uint32_t cp = 0x10000 + ((cp_ & 0x3FF) << 10) + (cp2_ & 0x3FF);
+ unicode_traits::convert(&cp, 1, string_buffer_);
+ sb = ++input_ptr_;
+ ++position_;
+ goto string_u1;
+ }
+
+ JSONCONS_UNREACHABLE();
+ }
+
+ void translate_conv_errc(unicode_traits::conv_errc result, std::error_code& ec)
+ {
+ switch (result)
+ {
+ case unicode_traits::conv_errc():
+ break;
+ case unicode_traits::conv_errc::over_long_utf8_sequence:
+ more_ = err_handler_(json_errc::over_long_utf8_sequence, *this);
+ if (!more_)
+ {
+ ec = json_errc::over_long_utf8_sequence;
+ return;
+ }
+ break;
+ case unicode_traits::conv_errc::unpaired_high_surrogate:
+ more_ = err_handler_(json_errc::unpaired_high_surrogate, *this);
+ if (!more_)
+ {
+ ec = json_errc::unpaired_high_surrogate;
+ return;
+ }
+ break;
+ case unicode_traits::conv_errc::expected_continuation_byte:
+ more_ = err_handler_(json_errc::expected_continuation_byte, *this);
+ if (!more_)
+ {
+ ec = json_errc::expected_continuation_byte;
+ return;
+ }
+ break;
+ case unicode_traits::conv_errc::illegal_surrogate_value:
+ more_ = err_handler_(json_errc::illegal_surrogate_value, *this);
+ if (!more_)
+ {
+ ec = json_errc::illegal_surrogate_value;
+ return;
+ }
+ break;
+ default:
+ more_ = err_handler_(json_errc::illegal_codepoint, *this);
+ if (!more_)
+ {
+ ec = json_errc::illegal_codepoint;
+ return;
+ }
+ break;
+ }
+ }
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+
+ JSONCONS_DEPRECATED_MSG("Instead, use finish_parse(basic_json_visitor<char_type>&)")
+ void end_parse(basic_json_visitor<char_type>& visitor)
+ {
+ std::error_code ec;
+ finish_parse(visitor, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec,line_,column()));
+ }
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use finish_parse(basic_json_visitor<char_type>&, std::error_code&)")
+ void end_parse(basic_json_visitor<char_type>& visitor, std::error_code& ec)
+ {
+ while (!finished())
+ {
+ parse_some(visitor, ec);
+ }
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use update(const char_type*, std::size_t)")
+ void set_source(const char_type* data, std::size_t length)
+ {
+ begin_input_ = data;
+ end_input_ = data + length;
+ input_ptr_ = begin_input_;
+ }
+#endif
+
+ std::size_t line() const override
+ {
+ return line_;
+ }
+
+ std::size_t column() const override
+ {
+ return (position_ - mark_position_) + 1;
+ }
+
+ std::size_t position() const override
+ {
+ return saved_position_;
+ }
+
+ std::size_t offset() const
+ {
+ return input_ptr_ - begin_input_;
+ }
+private:
+
+ void end_integer_value(basic_json_visitor<char_type>& visitor, std::error_code& ec)
+ {
+ if (string_buffer_[0] == '-')
+ {
+ end_negative_value(visitor, ec);
+ }
+ else
+ {
+ end_positive_value(visitor, ec);
+ }
+ }
+
+ void end_negative_value(basic_json_visitor<char_type>& visitor, std::error_code& ec)
+ {
+ int64_t val;
+ auto result = jsoncons::detail::to_integer_unchecked(string_buffer_.data(), string_buffer_.length(), val);
+ if (result)
+ {
+ more_ = visitor.int64_value(val, semantic_tag::none, *this, ec);
+ }
+ else // Must be overflow
+ {
+ more_ = visitor.string_value(string_buffer_, semantic_tag::bigint, *this, ec);
+ }
+ after_value(ec);
+ }
+
+ void end_positive_value(basic_json_visitor<char_type>& visitor, std::error_code& ec)
+ {
+ uint64_t val;
+ auto result = jsoncons::detail::to_integer_unchecked(string_buffer_.data(), string_buffer_.length(), val);
+ if (result)
+ {
+ more_ = visitor.uint64_value(val, semantic_tag::none, *this, ec);
+ }
+ else // Must be overflow
+ {
+ more_ = visitor.string_value(string_buffer_, semantic_tag::bigint, *this, ec);
+ }
+ after_value(ec);
+ }
+
+ void end_fraction_value(basic_json_visitor<char_type>& visitor, std::error_code& ec)
+ {
+ JSONCONS_TRY
+ {
+ if (options_.lossless_number())
+ {
+ more_ = visitor.string_value(string_buffer_, semantic_tag::bigdec, *this, ec);
+ }
+ else
+ {
+ double d = to_double_(string_buffer_.c_str(), string_buffer_.length());
+ more_ = visitor.double_value(d, semantic_tag::none, *this, ec);
+ }
+ }
+ JSONCONS_CATCH(...)
+ {
+ more_ = err_handler_(json_errc::invalid_number, *this);
+ if (!more_)
+ {
+ ec = json_errc::invalid_number;
+ return;
+ }
+ more_ = visitor.null_value(semantic_tag::none, *this, ec); // recovery
+ }
+
+ after_value(ec);
+ }
+
+ void end_string_value(const char_type* s, std::size_t length, basic_json_visitor<char_type>& visitor, std::error_code& ec)
+ {
+ string_view_type sv(s, length);
+ auto result = unicode_traits::validate(s, length);
+ if (result.ec != unicode_traits::conv_errc())
+ {
+ translate_conv_errc(result.ec,ec);
+ position_ += (result.ptr - s);
+ return;
+ }
+ switch (parent())
+ {
+ case json_parse_state::member_name:
+ more_ = visitor.key(sv, *this, ec);
+ pop_state();
+ state_ = json_parse_state::expect_colon;
+ break;
+ case json_parse_state::object:
+ case json_parse_state::array:
+ {
+ auto it = std::find_if(string_double_map_.begin(), string_double_map_.end(), string_maps_to_double{ sv });
+ if (it != string_double_map_.end())
+ {
+ more_ = visitor.double_value(it->second, semantic_tag::none, *this, ec);
+ }
+ else
+ {
+ more_ = visitor.string_value(sv, semantic_tag::none, *this, ec);
+ }
+ state_ = json_parse_state::expect_comma_or_end;
+ break;
+ }
+ case json_parse_state::root:
+ {
+ auto it = std::find_if(string_double_map_.begin(),string_double_map_.end(),string_maps_to_double{sv});
+ if (it != string_double_map_.end())
+ {
+ more_ = visitor.double_value(it->second, semantic_tag::none, *this, ec);
+ }
+ else
+ {
+ more_ = visitor.string_value(sv, semantic_tag::none, *this, ec);
+ }
+ state_ = json_parse_state::accept;
+ break;
+ }
+ default:
+ more_ = err_handler_(json_errc::syntax_error, *this);
+ if (!more_)
+ {
+ ec = json_errc::syntax_error;
+ return;
+ }
+ break;
+ }
+ }
+
+ void begin_member_or_element(std::error_code& ec)
+ {
+ switch (parent())
+ {
+ case json_parse_state::object:
+ state_ = json_parse_state::expect_member_name;
+ break;
+ case json_parse_state::array:
+ state_ = json_parse_state::expect_value;
+ break;
+ case json_parse_state::root:
+ break;
+ default:
+ more_ = err_handler_(json_errc::syntax_error, *this);
+ if (!more_)
+ {
+ ec = json_errc::syntax_error;
+ return;
+ }
+ break;
+ }
+ }
+
+ void after_value(std::error_code& ec)
+ {
+ switch (parent())
+ {
+ case json_parse_state::array:
+ case json_parse_state::object:
+ state_ = json_parse_state::expect_comma_or_end;
+ break;
+ case json_parse_state::root:
+ state_ = json_parse_state::accept;
+ break;
+ default:
+ more_ = err_handler_(json_errc::syntax_error, *this);
+ if (!more_)
+ {
+ ec = json_errc::syntax_error;
+ return;
+ }
+ break;
+ }
+ }
+
+ void push_state(json_parse_state state)
+ {
+ state_stack_.push_back(state);
+ }
+
+ json_parse_state pop_state()
+ {
+ JSONCONS_ASSERT(!state_stack_.empty())
+ json_parse_state state = state_stack_.back();
+ state_stack_.pop_back();
+ return state;
+ }
+
+ uint32_t append_to_codepoint(uint32_t cp, int c, std::error_code& ec)
+ {
+ cp *= 16;
+ if (c >= '0' && c <= '9')
+ {
+ cp += c - '0';
+ }
+ else if (c >= 'a' && c <= 'f')
+ {
+ cp += c - 'a' + 10;
+ }
+ else if (c >= 'A' && c <= 'F')
+ {
+ cp += c - 'A' + 10;
+ }
+ else
+ {
+ more_ = err_handler_(json_errc::invalid_unicode_escape_sequence, *this);
+ if (!more_)
+ {
+ ec = json_errc::invalid_unicode_escape_sequence;
+ return cp;
+ }
+ }
+ return cp;
+ }
+};
+
+using json_parser = basic_json_parser<char>;
+using wjson_parser = basic_json_parser<wchar_t>;
+
+}
+
+#endif
+
diff --git a/include/jsoncons/json_reader.hpp b/include/jsoncons/json_reader.hpp
new file mode 100644
index 0000000..eeb351f
--- /dev/null
+++ b/include/jsoncons/json_reader.hpp
@@ -0,0 +1,731 @@
+// Copyright 2015 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_READER_HPP
+#define JSONCONS_JSON_READER_HPP
+
+#include <memory> // std::allocator
+#include <string>
+#include <vector>
+#include <stdexcept>
+#include <system_error>
+#include <ios>
+#include <utility> // std::move
+#include <jsoncons/source.hpp>
+#include <jsoncons/json_exception.hpp>
+#include <jsoncons/json_visitor.hpp>
+#include <jsoncons/json_parser.hpp>
+#include <jsoncons/source_adaptor.hpp>
+
+namespace jsoncons {
+
+ // utf8_other_json_input_adapter
+
+ template <class CharT>
+ class json_utf8_to_other_visitor_adaptor : public json_visitor
+ {
+ public:
+ using json_visitor::string_view_type;
+ private:
+ basic_default_json_visitor<CharT> default_visitor_;
+ basic_json_visitor<CharT>& other_visitor_;
+ //std::function<bool(json_errc,const ser_context&)> err_handler_;
+
+ // noncopyable and nonmoveable
+ json_utf8_to_other_visitor_adaptor(const json_utf8_to_other_visitor_adaptor<CharT>&) = delete;
+ json_utf8_to_other_visitor_adaptor<CharT>& operator=(const json_utf8_to_other_visitor_adaptor<CharT>&) = delete;
+
+ public:
+ json_utf8_to_other_visitor_adaptor()
+ : other_visitor_(default_visitor_)
+ {
+ }
+
+ json_utf8_to_other_visitor_adaptor(basic_json_visitor<CharT>& other_visitor/*,
+ std::function<bool(json_errc,const ser_context&)> err_handler*/)
+ : other_visitor_(other_visitor)/*,
+ err_handler_(err_handler)*/
+ {
+ }
+
+ private:
+
+ void visit_flush() override
+ {
+ other_visitor_.flush();
+ }
+
+ bool visit_begin_object(semantic_tag tag, const ser_context& context, std::error_code& ec) override
+ {
+ return other_visitor_.begin_object(tag, context, ec);
+ }
+
+ bool visit_end_object(const ser_context& context, std::error_code& ec) override
+ {
+ return other_visitor_.end_object(context, ec);
+ }
+
+ bool visit_begin_array(semantic_tag tag, const ser_context& context, std::error_code& ec) override
+ {
+ return other_visitor_.begin_array(tag, context, ec);
+ }
+
+ bool visit_end_array(const ser_context& context, std::error_code& ec) override
+ {
+ return other_visitor_.end_array(context, ec);
+ }
+
+ bool visit_key(const string_view_type& name, const ser_context& context, std::error_code& ec) override
+ {
+ std::basic_string<CharT> target;
+ auto result = unicode_traits::convert(
+ name.data(), name.size(), target,
+ unicode_traits::conv_flags::strict);
+ if (result.ec != unicode_traits::conv_errc())
+ {
+ JSONCONS_THROW(ser_error(result.ec,context.line(),context.column()));
+ }
+ return other_visitor_.key(target, context, ec);
+ }
+
+ bool visit_string(const string_view_type& value, semantic_tag tag, const ser_context& context, std::error_code& ec) override
+ {
+ std::basic_string<CharT> target;
+ auto result = unicode_traits::convert(
+ value.data(), value.size(), target,
+ unicode_traits::conv_flags::strict);
+ if (result.ec != unicode_traits::conv_errc())
+ {
+ ec = result.ec;
+ return false;
+ }
+ return other_visitor_.string_value(target, tag, context, ec);
+ }
+
+ bool visit_int64(int64_t value,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ return other_visitor_.int64_value(value, tag, context, ec);
+ }
+
+ bool visit_uint64(uint64_t value,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ return other_visitor_.uint64_value(value, tag, context, ec);
+ }
+
+ bool visit_half(uint16_t value,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ return other_visitor_.half_value(value, tag, context, ec);
+ }
+
+ bool visit_double(double value,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ return other_visitor_.double_value(value, tag, context, ec);
+ }
+
+ bool visit_bool(bool value, semantic_tag tag, const ser_context& context, std::error_code& ec) override
+ {
+ return other_visitor_.bool_value(value, tag, context, ec);
+ }
+
+ bool visit_null(semantic_tag tag, const ser_context& context, std::error_code& ec) override
+ {
+ return other_visitor_.null_value(tag, context, ec);
+ }
+ };
+
+ template<class CharT,class Source=jsoncons::stream_source<CharT>,class Allocator=std::allocator<char>>
+ class basic_json_reader
+ {
+ public:
+ using char_type = CharT;
+ using source_type = Source;
+ using string_view_type = jsoncons::basic_string_view<CharT>;
+ private:
+ typedef typename std::allocator_traits<Allocator>:: template rebind_alloc<CharT> char_allocator_type;
+
+ static constexpr size_t default_max_buffer_size = 16384;
+
+ json_source_adaptor<Source> source_;
+ basic_default_json_visitor<CharT> default_visitor_;
+ basic_json_visitor<CharT>& visitor_;
+ basic_json_parser<CharT,Allocator> parser_;
+
+ // Noncopyable and nonmoveable
+ basic_json_reader(const basic_json_reader&) = delete;
+ basic_json_reader& operator=(const basic_json_reader&) = delete;
+
+ public:
+ template <class Sourceable>
+ explicit basic_json_reader(Sourceable&& source, const Allocator& alloc = Allocator())
+ : basic_json_reader(std::forward<Sourceable>(source),
+ default_visitor_,
+ basic_json_decode_options<CharT>(),
+ default_json_parsing(),
+ alloc)
+ {
+ }
+
+ template <class Sourceable>
+ basic_json_reader(Sourceable&& source,
+ const basic_json_decode_options<CharT>& options,
+ const Allocator& alloc = Allocator())
+ : basic_json_reader(std::forward<Sourceable>(source),
+ default_visitor_,
+ options,
+ default_json_parsing(),
+ alloc)
+ {
+ }
+
+ template <class Sourceable>
+ basic_json_reader(Sourceable&& source,
+ std::function<bool(json_errc,const ser_context&)> err_handler,
+ const Allocator& alloc = Allocator())
+ : basic_json_reader(std::forward<Sourceable>(source),
+ default_visitor_,
+ basic_json_decode_options<CharT>(),
+ err_handler,
+ alloc)
+ {
+ }
+
+ template <class Sourceable>
+ basic_json_reader(Sourceable&& source,
+ const basic_json_decode_options<CharT>& options,
+ std::function<bool(json_errc,const ser_context&)> err_handler,
+ const Allocator& alloc = Allocator())
+ : basic_json_reader(std::forward<Sourceable>(source),
+ default_visitor_,
+ options,
+ err_handler,
+ alloc)
+ {
+ }
+
+ template <class Sourceable>
+ basic_json_reader(Sourceable&& source,
+ basic_json_visitor<CharT>& visitor,
+ const Allocator& alloc = Allocator())
+ : basic_json_reader(std::forward<Sourceable>(source),
+ visitor,
+ basic_json_decode_options<CharT>(),
+ default_json_parsing(),
+ alloc)
+ {
+ }
+
+ template <class Sourceable>
+ basic_json_reader(Sourceable&& source,
+ basic_json_visitor<CharT>& visitor,
+ const basic_json_decode_options<CharT>& options,
+ const Allocator& alloc = Allocator())
+ : basic_json_reader(std::forward<Sourceable>(source),
+ visitor,
+ options,
+ default_json_parsing(),
+ alloc)
+ {
+ }
+
+ template <class Sourceable>
+ basic_json_reader(Sourceable&& source,
+ basic_json_visitor<CharT>& visitor,
+ std::function<bool(json_errc,const ser_context&)> err_handler,
+ const Allocator& alloc = Allocator())
+ : basic_json_reader(std::forward<Sourceable>(source),
+ visitor,
+ basic_json_decode_options<CharT>(),
+ err_handler,
+ alloc)
+ {
+ }
+
+ template <class Sourceable>
+ basic_json_reader(Sourceable&& source,
+ basic_json_visitor<CharT>& visitor,
+ const basic_json_decode_options<CharT>& options,
+ std::function<bool(json_errc,const ser_context&)> err_handler,
+ const Allocator& alloc = Allocator())
+ : source_(std::forward<Sourceable>(source)),
+ visitor_(visitor),
+ parser_(options,err_handler,alloc)
+ {
+ }
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+ JSONCONS_DEPRECATED_MSG("Instead, use max_nesting_depth() on options")
+ int max_nesting_depth() const
+ {
+ return parser_.max_nesting_depth();
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use max_nesting_depth(int) on options")
+ void max_nesting_depth(int depth)
+ {
+ parser_.max_nesting_depth(depth);
+ }
+#endif
+ void read_next()
+ {
+ std::error_code ec;
+ read_next(ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column()));
+ }
+ }
+
+ void read_next(std::error_code& ec)
+ {
+ if (source_.is_error())
+ {
+ ec = json_errc::source_error;
+ return;
+ }
+ parser_.reset();
+ while (!parser_.stopped())
+ {
+ if (parser_.source_exhausted())
+ {
+ auto s = source_.read_buffer(ec);
+ if (ec) return;
+ if (s.size() > 0)
+ {
+ parser_.update(s.data(),s.size());
+ }
+ }
+ bool eof = parser_.source_exhausted();
+ parser_.parse_some(visitor_, ec);
+ if (ec) return;
+ if (eof)
+ {
+ if (parser_.enter())
+ {
+ break;
+ }
+ else if (!parser_.accept())
+ {
+ ec = json_errc::unexpected_eof;
+ return;
+ }
+ }
+ }
+
+ while (!source_.eof())
+ {
+ parser_.skip_whitespace();
+ if (parser_.source_exhausted())
+ {
+ auto s = source_.read_buffer(ec);
+ if (ec) return;
+ if (s.size() > 0)
+ {
+ parser_.update(s.data(),s.size());
+ }
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+
+ void check_done()
+ {
+ std::error_code ec;
+ check_done(ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column()));
+ }
+ }
+
+ std::size_t line() const
+ {
+ return parser_.line();
+ }
+
+ std::size_t column() const
+ {
+ return parser_.column();
+ }
+
+ void check_done(std::error_code& ec)
+ {
+ if (source_.is_error())
+ {
+ ec = json_errc::source_error;
+ return;
+ }
+ if (source_.eof())
+ {
+ parser_.check_done(ec);
+ if (ec) return;
+ }
+ else
+ {
+ do
+ {
+ if (parser_.source_exhausted())
+ {
+ auto s = source_.read_buffer(ec);
+ if (ec) return;
+ if (s.size() > 0)
+ {
+ parser_.update(s.data(),s.size());
+ }
+ }
+ if (!parser_.source_exhausted())
+ {
+ parser_.check_done(ec);
+ if (ec) return;
+ }
+ }
+ while (!eof());
+ }
+ }
+
+ bool eof() const
+ {
+ return parser_.source_exhausted() && source_.eof();
+ }
+
+ void read()
+ {
+ read_next();
+ check_done();
+ }
+
+ void read(std::error_code& ec)
+ {
+ read_next(ec);
+ if (!ec)
+ {
+ check_done(ec);
+ }
+ }
+ };
+
+ template<class CharT,class Source=jsoncons::stream_source<CharT>,class Allocator=std::allocator<char>>
+ class legacy_basic_json_reader
+ {
+ public:
+ using char_type = CharT;
+ using source_type = Source;
+ using string_view_type = jsoncons::basic_string_view<CharT>;
+ private:
+ typedef typename std::allocator_traits<Allocator>:: template rebind_alloc<CharT> char_allocator_type;
+
+ static constexpr size_t default_max_buffer_size = 16384;
+
+ json_source_adaptor<Source> source_;
+ basic_default_json_visitor<CharT> default_visitor_;
+ basic_json_visitor<CharT>& visitor_;
+ basic_json_parser<CharT,Allocator> parser_;
+
+ // Noncopyable and nonmoveable
+ legacy_basic_json_reader(const legacy_basic_json_reader&) = delete;
+ legacy_basic_json_reader& operator=(const legacy_basic_json_reader&) = delete;
+
+ public:
+ template <class Sourceable>
+ explicit legacy_basic_json_reader(Sourceable&& source, const Allocator& alloc = Allocator())
+ : legacy_basic_json_reader(std::forward<Sourceable>(source),
+ default_visitor_,
+ basic_json_decode_options<CharT>(),
+ default_json_parsing(),
+ alloc)
+ {
+ }
+
+ template <class Sourceable>
+ legacy_basic_json_reader(Sourceable&& source,
+ const basic_json_decode_options<CharT>& options,
+ const Allocator& alloc = Allocator())
+ : legacy_basic_json_reader(std::forward<Sourceable>(source),
+ default_visitor_,
+ options,
+ default_json_parsing(),
+ alloc)
+ {
+ }
+
+ template <class Sourceable>
+ legacy_basic_json_reader(Sourceable&& source,
+ std::function<bool(json_errc,const ser_context&)> err_handler,
+ const Allocator& alloc = Allocator())
+ : legacy_basic_json_reader(std::forward<Sourceable>(source),
+ default_visitor_,
+ basic_json_decode_options<CharT>(),
+ err_handler,
+ alloc)
+ {
+ }
+
+ template <class Sourceable>
+ legacy_basic_json_reader(Sourceable&& source,
+ const basic_json_decode_options<CharT>& options,
+ std::function<bool(json_errc,const ser_context&)> err_handler,
+ const Allocator& alloc = Allocator())
+ : legacy_basic_json_reader(std::forward<Sourceable>(source),
+ default_visitor_,
+ options,
+ err_handler,
+ alloc)
+ {
+ }
+
+ template <class Sourceable>
+ legacy_basic_json_reader(Sourceable&& source,
+ basic_json_visitor<CharT>& visitor,
+ const Allocator& alloc = Allocator())
+ : legacy_basic_json_reader(std::forward<Sourceable>(source),
+ visitor,
+ basic_json_decode_options<CharT>(),
+ default_json_parsing(),
+ alloc)
+ {
+ }
+
+ template <class Sourceable>
+ legacy_basic_json_reader(Sourceable&& source,
+ basic_json_visitor<CharT>& visitor,
+ const basic_json_decode_options<CharT>& options,
+ const Allocator& alloc = Allocator())
+ : legacy_basic_json_reader(std::forward<Sourceable>(source),
+ visitor,
+ options,
+ default_json_parsing(),
+ alloc)
+ {
+ }
+
+ template <class Sourceable>
+ legacy_basic_json_reader(Sourceable&& source,
+ basic_json_visitor<CharT>& visitor,
+ std::function<bool(json_errc,const ser_context&)> err_handler,
+ const Allocator& alloc = Allocator())
+ : legacy_basic_json_reader(std::forward<Sourceable>(source),
+ visitor,
+ basic_json_decode_options<CharT>(),
+ err_handler,
+ alloc)
+ {
+ }
+
+ template <class Sourceable>
+ legacy_basic_json_reader(Sourceable&& source,
+ basic_json_visitor<CharT>& visitor,
+ const basic_json_decode_options<CharT>& options,
+ std::function<bool(json_errc,const ser_context&)> err_handler,
+ const Allocator& alloc = Allocator(),
+ typename std::enable_if<!std::is_constructible<jsoncons::basic_string_view<CharT>,Sourceable>::value>::type* = 0)
+ : source_(std::forward<Sourceable>(source)),
+ visitor_(visitor),
+ parser_(options,err_handler,alloc)
+ {
+ }
+
+ template <class Sourceable>
+ legacy_basic_json_reader(Sourceable&& source,
+ basic_json_visitor<CharT>& visitor,
+ const basic_json_decode_options<CharT>& options,
+ std::function<bool(json_errc,const ser_context&)> err_handler,
+ const Allocator& alloc = Allocator(),
+ typename std::enable_if<std::is_constructible<jsoncons::basic_string_view<CharT>,Sourceable>::value>::type* = 0)
+ : source_(),
+ visitor_(visitor),
+ parser_(options,err_handler,alloc)
+ {
+ jsoncons::basic_string_view<CharT> sv(std::forward<Sourceable>(source));
+
+ auto r = unicode_traits::detect_json_encoding(sv.data(), sv.size());
+ if (!(r.encoding == unicode_traits::encoding_kind::utf8 || r.encoding == unicode_traits::encoding_kind::undetected))
+ {
+ JSONCONS_THROW(ser_error(json_errc::illegal_unicode_character,parser_.line(),parser_.column()));
+ }
+ std::size_t offset = (r.ptr - sv.data());
+ parser_.update(sv.data()+offset,sv.size()-offset);
+ }
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+ JSONCONS_DEPRECATED_MSG("Instead, use max_nesting_depth() on options")
+ int max_nesting_depth() const
+ {
+ return parser_.max_nesting_depth();
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use max_nesting_depth(int) on options")
+ void max_nesting_depth(int depth)
+ {
+ parser_.max_nesting_depth(depth);
+ }
+#endif
+ void read_next()
+ {
+ std::error_code ec;
+ read_next(ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column()));
+ }
+ }
+
+ void read_next(std::error_code& ec)
+ {
+ if (source_.is_error())
+ {
+ ec = json_errc::source_error;
+ return;
+ }
+ parser_.reset();
+ while (!parser_.stopped())
+ {
+ if (parser_.source_exhausted())
+ {
+ auto s = source_.read_buffer(ec);
+ if (ec) return;
+ if (s.size() > 0)
+ {
+ parser_.update(s.data(),s.size());
+ }
+ }
+ bool eof = parser_.source_exhausted();
+ parser_.parse_some(visitor_, ec);
+ if (ec) return;
+ if (eof)
+ {
+ if (parser_.enter())
+ {
+ break;
+ }
+ else if (!parser_.accept())
+ {
+ ec = json_errc::unexpected_eof;
+ return;
+ }
+ }
+ }
+
+ while (!source_.eof())
+ {
+ parser_.skip_whitespace();
+ if (parser_.source_exhausted())
+ {
+ auto s = source_.read_buffer(ec);
+ if (ec) return;
+ if (s.size() > 0)
+ {
+ parser_.update(s.data(),s.size());
+ }
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+
+ void check_done()
+ {
+ std::error_code ec;
+ check_done(ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column()));
+ }
+ }
+
+ std::size_t line() const
+ {
+ return parser_.line();
+ }
+
+ std::size_t column() const
+ {
+ return parser_.column();
+ }
+
+ void check_done(std::error_code& ec)
+ {
+ if (source_.is_error())
+ {
+ ec = json_errc::source_error;
+ return;
+ }
+ if (source_.eof())
+ {
+ parser_.check_done(ec);
+ if (ec) return;
+ }
+ else
+ {
+ do
+ {
+ if (parser_.source_exhausted())
+ {
+ auto s = source_.read_buffer(ec);
+ if (ec) return;
+ if (s.size() > 0)
+ {
+ parser_.update(s.data(),s.size());
+ }
+ }
+ if (!parser_.source_exhausted())
+ {
+ parser_.check_done(ec);
+ if (ec) return;
+ }
+ }
+ while (!eof());
+ }
+ }
+
+ bool eof() const
+ {
+ return parser_.source_exhausted() && source_.eof();
+ }
+
+ void read()
+ {
+ read_next();
+ check_done();
+ }
+
+ void read(std::error_code& ec)
+ {
+ read_next(ec);
+ if (!ec)
+ {
+ check_done(ec);
+ }
+ }
+ };
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+ using json_reader = legacy_basic_json_reader<char>;
+ using wjson_reader = legacy_basic_json_reader<wchar_t>;
+#endif
+ using json_string_reader = basic_json_reader<char,string_source<char>>;
+ using wjson_string_reader = basic_json_reader<wchar_t,string_source<wchar_t>>;
+ using json_stream_reader = basic_json_reader<char,stream_source<char>>;
+ using wjson_stream_reader = basic_json_reader<wchar_t,stream_source<wchar_t>>;
+}
+
+#endif
+
diff --git a/include/jsoncons/json_traits_macros.hpp b/include/jsoncons/json_traits_macros.hpp
new file mode 100644
index 0000000..321d889
--- /dev/null
+++ b/include/jsoncons/json_traits_macros.hpp
@@ -0,0 +1,1072 @@
+// Copyright 2019 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_TRAITS_MACROS_HPP
+#define JSONCONS_JSON_TRAITS_MACROS_HPP
+
+#include <algorithm> // std::swap
+#include <iterator> // std::iterator_traits, std::input_iterator_tag
+#include <jsoncons/config/jsoncons_config.hpp> // JSONCONS_EXPAND, JSONCONS_QUOTE
+#include <jsoncons/more_type_traits.hpp>
+#include <jsoncons/json_visitor.hpp>
+#include <limits> // std::numeric_limits
+#include <string>
+#include <type_traits> // std::enable_if
+#include <utility>
+#include <jsoncons/json_type_traits.hpp>
+
+namespace jsoncons
+{
+ #define JSONCONS_RDONLY(X)
+
+ #define JSONCONS_RDWR(X) X
+
+ struct always_true
+ {
+ template< class T>
+ constexpr bool operator()(const T&) const noexcept
+ {
+ return true;
+ }
+ };
+
+ struct identity
+ {
+ template< class T>
+ constexpr T&& operator()(T&& val) const noexcept
+ {
+ return std::forward<T>(val);
+ }
+ };
+
+ template <class ChT,class T>
+ struct json_traits_macro_names
+ {};
+
+ template <class Json>
+ struct json_traits_helper
+ {
+ using string_view_type = typename Json::string_view_type;
+
+ template <class OutputType>
+ static void set_udt_member(const Json&, const string_view_type&, const OutputType&)
+ {
+ }
+ template <class OutputType>
+ static void set_udt_member(const Json& j, const string_view_type& key, OutputType& val)
+ {
+ val = j.at(key).template as<OutputType>();
+ }
+
+ template <class T, class From, class OutputType>
+ static void set_udt_member(const Json&, const string_view_type&, From, const OutputType&)
+ {
+ }
+ template <class T, class From, class OutputType>
+ static void set_udt_member(const Json& j, const string_view_type& key, From from, OutputType& val)
+ {
+ val = from(j.at(key).template as<T>());
+ }
+ template <class U>
+ static void set_optional_json_member(const string_view_type& key, const std::shared_ptr<U>& val, Json& j)
+ {
+ if (val) j.try_emplace(key, val);
+ }
+ template <class U>
+ static void set_optional_json_member(const string_view_type& key, const std::unique_ptr<U>& val, Json& j)
+ {
+ if (val) j.try_emplace(key, val);
+ }
+ template <class U>
+ static void set_optional_json_member(const string_view_type& key, const jsoncons::optional<U>& val, Json& j)
+ {
+ if (val) j.try_emplace(key, val);
+ }
+ template <class U>
+ static void set_optional_json_member(const string_view_type& key, const U& val, Json& j)
+ {
+ j.try_emplace(key, val);
+ }
+ };
+}
+
+#if defined(_MSC_VER)
+#pragma warning( disable : 4127)
+#endif
+
+#define JSONCONS_CONCAT_RAW(a, b) a ## b
+#define JSONCONS_CONCAT(a, b) JSONCONS_CONCAT_RAW(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_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_REP_N(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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_EXPAND(JSONCONS_VARIADIC_REP_OF_1(Call, P1, P2, P3, __VA_ARGS__))
+#define JSONCONS_VARIADIC_REP_OF_1(Call, P1, P2, P3, P4) JSONCONS_EXPAND(Call ## _LAST(P1, P2, P3, P4, 1))
+
+#define JSONCONS_TYPE_TRAITS_FRIEND \
+ template <class JSON,class T,class Enable> \
+ friend struct jsoncons::json_type_traits;
+
+#define JSONCONS_EXPAND_CALL2(Call, Expr, Id) JSONCONS_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 char* Member ## _str(char) {return JSONCONS_QUOTE(,Member);} \
+ static inline const wchar_t* Member ## _str(wchar_t) {return JSONCONS_QUOTE(L,Member);} \
+ /**/
+
+#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_traits_macro_names<char_type,value_type>::Member##_str(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) \
+ if ((num_params-Count) < num_mandatory_params2 || ajson.contains(json_traits_macro_names<char_type,value_type>::Member##_str(char_type{}))) \
+ {json_traits_helper<Json>::set_udt_member(ajson,json_traits_macro_names<char_type,value_type>::Member##_str(char_type{}),aval.Member);}
+
+#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) \
+ json_traits_helper<Json>::set_udt_member(ajson,json_traits_macro_names<char_type,value_type>::Member##_str(char_type{}),aval.Member);
+
+#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_traits_macro_names<char_type,value_type>::Member##_str(char_type{}), aval.Member);} \
+ else {json_traits_helper<Json>::set_optional_json_member(json_traits_macro_names<char_type,value_type>::Member##_str(char_type{}), aval.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_traits_macro_names<char_type,value_type>::Member##_str(char_type{}), aval.Member);
+
+#define JSONCONS_MEMBER_TRAITS_BASE(AsT,ToJ,NumTemplateParams,ValueType,NumMandatoryParams1,NumMandatoryParams2, ...) \
+namespace jsoncons \
+{ \
+ template <class ChT JSONCONS_GENERATE_TPL_PARAMS(JSONCONS_GENERATE_MORE_TPL_PARAM, NumTemplateParams)> \
+ struct json_traits_macro_names<ChT,ValueType JSONCONS_GENERATE_TPL_ARGS(JSONCONS_GENERATE_TPL_ARG, NumTemplateParams)> \
+ { \
+ JSONCONS_VARIADIC_REP_N(JSONCONS_GENERATE_NAME_STR, ,,, __VA_ARGS__)\
+ }; \
+ template<typename Json JSONCONS_GENERATE_TPL_PARAMS(JSONCONS_GENERATE_MORE_TPL_PARAM, NumTemplateParams)> \
+ struct json_type_traits<Json, ValueType JSONCONS_GENERATE_TPL_ARGS(JSONCONS_GENERATE_TPL_ARG, NumTemplateParams)> \
+ { \
+ using value_type = ValueType JSONCONS_GENERATE_TPL_ARGS(JSONCONS_GENERATE_TPL_ARG, NumTemplateParams); \
+ using allocator_type = typename Json::allocator_type; \
+ 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_REP_N(JSONCONS_N_MEMBER_IS, ,,, __VA_ARGS__)\
+ return true; \
+ } \
+ static value_type as(const Json& ajson) \
+ { \
+ if (!is(ajson)) JSONCONS_THROW(conv_error(conv_errc::conversion_failed, "Not a " # ValueType)); \
+ value_type aval{}; \
+ JSONCONS_VARIADIC_REP_N(AsT, ,,, __VA_ARGS__) \
+ return aval; \
+ } \
+ static Json to_json(const value_type& aval, allocator_type alloc=allocator_type()) \
+ { \
+ Json ajson(json_object_arg, semantic_tag::none, alloc); \
+ JSONCONS_VARIADIC_REP_N(ToJ, ,,, __VA_ARGS__) \
+ return ajson; \
+ } \
+ }; \
+} \
+ /**/
+
+#define JSONCONS_N_MEMBER_TRAITS(ValueType,NumMandatoryParams,...) \
+ JSONCONS_MEMBER_TRAITS_BASE(JSONCONS_N_MEMBER_AS, JSONCONS_TO_JSON,0, ValueType,NumMandatoryParams,NumMandatoryParams, __VA_ARGS__) \
+ namespace jsoncons { template <> struct is_json_type_traits_declared<ValueType> : public std::true_type {}; } \
+ /**/
+
+#define JSONCONS_TPL_N_MEMBER_TRAITS(NumTemplateParams, ValueType,NumMandatoryParams, ...) \
+ JSONCONS_MEMBER_TRAITS_BASE(JSONCONS_N_MEMBER_AS, JSONCONS_TO_JSON,NumTemplateParams, ValueType,NumMandatoryParams,NumMandatoryParams, __VA_ARGS__) \
+ namespace jsoncons { template <JSONCONS_GENERATE_TPL_PARAMS(JSONCONS_GENERATE_TPL_PARAM, NumTemplateParams)> struct is_json_type_traits_declared<ValueType JSONCONS_GENERATE_TPL_ARGS(JSONCONS_GENERATE_TPL_ARG, NumTemplateParams)> : public std::true_type {}; } \
+ /**/
+
+#define JSONCONS_ALL_MEMBER_TRAITS(ValueType, ...) \
+ JSONCONS_MEMBER_TRAITS_BASE(JSONCONS_ALL_MEMBER_AS,JSONCONS_ALL_TO_JSON,0,ValueType, JSONCONS_NARGS(__VA_ARGS__), JSONCONS_NARGS(__VA_ARGS__),__VA_ARGS__) \
+ namespace jsoncons { template <> struct is_json_type_traits_declared<ValueType> : public std::true_type {}; } \
+ /**/
+
+#define JSONCONS_TPL_ALL_MEMBER_TRAITS(NumTemplateParams, ValueType, ...) \
+ JSONCONS_MEMBER_TRAITS_BASE(JSONCONS_ALL_MEMBER_AS,JSONCONS_ALL_TO_JSON,NumTemplateParams,ValueType, JSONCONS_NARGS(__VA_ARGS__), JSONCONS_NARGS(__VA_ARGS__),__VA_ARGS__) \
+ namespace jsoncons { template <JSONCONS_GENERATE_TPL_PARAMS(JSONCONS_GENERATE_TPL_PARAM, NumTemplateParams)> struct is_json_type_traits_declared<ValueType JSONCONS_GENERATE_TPL_ARGS(JSONCONS_GENERATE_TPL_ARG, NumTemplateParams)> : 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_EXPAND(JSONCONS_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<typename std::decay<decltype(Into((std::declval<value_type*>())->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) JSONCONS_EXPAND(JSONCONS_CONCAT(JSONCONS_N_MEMBER_NAME_AS_,JSONCONS_NARGS Seq) Seq)
+#define JSONCONS_N_MEMBER_NAME_AS_2(Member, Name) \
+ if (ajson.contains(Name)) {json_traits_helper<Json>::set_udt_member(ajson,Name,aval.Member);}
+#define JSONCONS_N_MEMBER_NAME_AS_3(Member, Name, Mode) Mode(JSONCONS_N_MEMBER_NAME_AS_2(Member, Name))
+#define JSONCONS_N_MEMBER_NAME_AS_4(Member, Name, Mode, Match) \
+ Mode(if (ajson.contains(Name)) {json_traits_helper<Json>::set_udt_member(ajson,Name,aval.Member);})
+#define JSONCONS_N_MEMBER_NAME_AS_5(Member, Name, Mode, Match, Into) \
+ Mode(if (ajson.contains(Name)) {json_traits_helper<Json>::template set_udt_member<typename std::decay<decltype(Into((std::declval<value_type*>())->Member))>::type>(ajson,Name,aval.Member);})
+#define JSONCONS_N_MEMBER_NAME_AS_6(Member, Name, Mode, Match, Into, From) \
+ Mode(if (ajson.contains(Name)) {json_traits_helper<Json>::template set_udt_member<typename std::decay<decltype(Into((std::declval<value_type*>())->Member))>::type>(ajson,Name,From,aval.Member);})
+
+#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_EXPAND(JSONCONS_CONCAT(JSONCONS_ALL_MEMBER_NAME_AS_,JSONCONS_NARGS Seq) Seq)
+#define JSONCONS_ALL_MEMBER_NAME_AS_2(Member, Name) \
+ json_traits_helper<Json>::set_udt_member(ajson,Name,aval.Member);
+#define JSONCONS_ALL_MEMBER_NAME_AS_3(Member, Name, Mode) Mode(JSONCONS_ALL_MEMBER_NAME_AS_2(Member, Name))
+#define JSONCONS_ALL_MEMBER_NAME_AS_4(Member, Name, Mode, Match) \
+ Mode(json_traits_helper<Json>::set_udt_member(ajson,Name,aval.Member);)
+#define JSONCONS_ALL_MEMBER_NAME_AS_5(Member, Name, Mode, Match, Into) \
+ Mode(json_traits_helper<Json>::template set_udt_member<typename std::decay<decltype(Into((std::declval<value_type*>())->Member))>::type>(ajson,Name,aval.Member);)
+#define JSONCONS_ALL_MEMBER_NAME_AS_6(Member, Name, Mode, Match, Into, From) \
+ Mode(json_traits_helper<Json>::template set_udt_member<typename std::decay<decltype(Into((std::declval<value_type*>())->Member))>::type>(ajson,Name,From,aval.Member);)
+
+#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_EXPAND(JSONCONS_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, aval.Member);} \
+else \
+ {json_traits_helper<Json>::set_optional_json_member(Name, aval.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(aval.Member));} \
+else \
+ {json_traits_helper<Json>::set_optional_json_member(Name, Into(aval.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_EXPAND(JSONCONS_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, aval.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(aval.Member));
+
+#define JSONCONS_MEMBER_NAME_TRAITS_BASE(AsT,ToJ, NumTemplateParams, ValueType,NumMandatoryParams1,NumMandatoryParams2, ...) \
+namespace jsoncons \
+{ \
+ template<typename Json JSONCONS_GENERATE_TPL_PARAMS(JSONCONS_GENERATE_MORE_TPL_PARAM, NumTemplateParams)> \
+ struct json_type_traits<Json, ValueType JSONCONS_GENERATE_TPL_ARGS(JSONCONS_GENERATE_TPL_ARG, NumTemplateParams)> \
+ { \
+ using value_type = ValueType JSONCONS_GENERATE_TPL_ARGS(JSONCONS_GENERATE_TPL_ARG, NumTemplateParams); \
+ using allocator_type = typename Json::allocator_type; \
+ 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_REP_N(JSONCONS_MEMBER_NAME_IS,,,, __VA_ARGS__)\
+ return true; \
+ } \
+ static value_type as(const Json& ajson) \
+ { \
+ if (!is(ajson)) JSONCONS_THROW(conv_error(conv_errc::conversion_failed, "Not a " # ValueType)); \
+ value_type aval{}; \
+ JSONCONS_VARIADIC_REP_N(AsT,,,, __VA_ARGS__) \
+ return aval; \
+ } \
+ static Json to_json(const value_type& aval, allocator_type alloc=allocator_type()) \
+ { \
+ Json ajson(json_object_arg, semantic_tag::none, alloc); \
+ JSONCONS_VARIADIC_REP_N(ToJ,,,, __VA_ARGS__) \
+ return ajson; \
+ } \
+ }; \
+} \
+ /**/
+
+
+#define JSONCONS_N_MEMBER_NAME_TRAITS(ValueType,NumMandatoryParams, ...) \
+ JSONCONS_MEMBER_NAME_TRAITS_BASE(JSONCONS_N_MEMBER_NAME_AS, JSONCONS_N_MEMBER_NAME_TO_JSON, 0, ValueType,NumMandatoryParams,NumMandatoryParams, __VA_ARGS__) \
+ namespace jsoncons { template <> struct is_json_type_traits_declared<ValueType> : public std::true_type {}; } \
+ /**/
+
+#define JSONCONS_TPL_N_MEMBER_NAME_TRAITS(NumTemplateParams, ValueType,NumMandatoryParams, ...) \
+ JSONCONS_MEMBER_NAME_TRAITS_BASE(JSONCONS_N_MEMBER_NAME_AS, JSONCONS_N_MEMBER_NAME_TO_JSON, NumTemplateParams, ValueType,NumMandatoryParams,NumMandatoryParams, __VA_ARGS__) \
+ namespace jsoncons { template <JSONCONS_GENERATE_TPL_PARAMS(JSONCONS_GENERATE_TPL_PARAM, NumTemplateParams)> struct is_json_type_traits_declared<ValueType JSONCONS_GENERATE_TPL_ARGS(JSONCONS_GENERATE_TPL_ARG, NumTemplateParams)> : public std::true_type {}; } \
+ /**/
+
+#define JSONCONS_ALL_MEMBER_NAME_TRAITS(ValueType, ...) \
+ JSONCONS_MEMBER_NAME_TRAITS_BASE(JSONCONS_ALL_MEMBER_NAME_AS, JSONCONS_ALL_MEMBER_NAME_TO_JSON, 0, ValueType, JSONCONS_NARGS(__VA_ARGS__), JSONCONS_NARGS(__VA_ARGS__), __VA_ARGS__) \
+ namespace jsoncons { template <> struct is_json_type_traits_declared<ValueType> : public std::true_type {}; } \
+ /**/
+
+#define JSONCONS_TPL_ALL_MEMBER_NAME_TRAITS(NumTemplateParams, ValueType, ...) \
+ JSONCONS_MEMBER_NAME_TRAITS_BASE(JSONCONS_ALL_MEMBER_NAME_AS, JSONCONS_ALL_MEMBER_NAME_TO_JSON, NumTemplateParams, ValueType, JSONCONS_NARGS(__VA_ARGS__), JSONCONS_NARGS(__VA_ARGS__), __VA_ARGS__) \
+ namespace jsoncons { template <JSONCONS_GENERATE_TPL_PARAMS(JSONCONS_GENERATE_TPL_PARAM, NumTemplateParams)> struct is_json_type_traits_declared<ValueType JSONCONS_GENERATE_TPL_ARGS(JSONCONS_GENERATE_TPL_ARG, NumTemplateParams)> : 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_traits_macro_names<char_type,value_type>::Getter##_str(char_type{}))) return false;
+
+#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) ((num_params-Count) < num_mandatory_params2) ? (ajson.at(json_traits_macro_names<char_type,value_type>::Getter##_str(char_type{}))).template as<typename std::decay<decltype((std::declval<value_type*>())->Getter())>::type>() : (ajson.contains(json_traits_macro_names<char_type,value_type>::Getter##_str(char_type{})) ? (ajson.at(json_traits_macro_names<char_type,value_type>::Getter##_str(char_type{}))).template as<typename std::decay<decltype((std::declval<value_type*>())->Getter())>::type>() : typename std::decay<decltype((std::declval<value_type*>())->Getter())>::type())
+
+#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_traits_macro_names<char_type,value_type>::Getter##_str(char_type{}), aval.Getter() ); \
+ } \
+else { \
+ json_traits_helper<Json>::set_optional_json_member(json_traits_macro_names<char_type,value_type>::Getter##_str(char_type{}), aval.Getter(), ajson); \
+}
+
+#define JSONCONS_CTOR_GETTER_TRAITS_BASE(NumTemplateParams, ValueType,NumMandatoryParams1,NumMandatoryParams2, ...) \
+namespace jsoncons \
+{ \
+ template <class ChT JSONCONS_GENERATE_TPL_PARAMS(JSONCONS_GENERATE_MORE_TPL_PARAM, NumTemplateParams)> \
+ struct json_traits_macro_names<ChT,ValueType JSONCONS_GENERATE_TPL_ARGS(JSONCONS_GENERATE_TPL_ARG, NumTemplateParams)> \
+ { \
+ JSONCONS_VARIADIC_REP_N(JSONCONS_GENERATE_NAME_STR, ,,, __VA_ARGS__)\
+ }; \
+ template<typename Json JSONCONS_GENERATE_TPL_PARAMS(JSONCONS_GENERATE_MORE_TPL_PARAM, NumTemplateParams)> \
+ struct json_type_traits<Json, ValueType JSONCONS_GENERATE_TPL_ARGS(JSONCONS_GENERATE_TPL_ARG, NumTemplateParams)> \
+ { \
+ using value_type = ValueType JSONCONS_GENERATE_TPL_ARGS(JSONCONS_GENERATE_TPL_ARG, NumTemplateParams); \
+ using allocator_type = typename Json::allocator_type; \
+ 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_REP_N(JSONCONS_CTOR_GETTER_IS, ,,, __VA_ARGS__)\
+ return true; \
+ } \
+ static value_type as(const Json& ajson) \
+ { \
+ if (!is(ajson)) JSONCONS_THROW(conv_error(conv_errc::conversion_failed, "Not a " # ValueType)); \
+ return value_type ( JSONCONS_VARIADIC_REP_N(JSONCONS_CTOR_GETTER_AS, ,,, __VA_ARGS__) ); \
+ } \
+ static Json to_json(const value_type& aval, allocator_type alloc=allocator_type()) \
+ { \
+ Json ajson(json_object_arg, semantic_tag::none, alloc); \
+ JSONCONS_VARIADIC_REP_N(JSONCONS_CTOR_GETTER_TO_JSON, ,,, __VA_ARGS__) \
+ return ajson; \
+ } \
+ }; \
+} \
+ /**/
+
+#define JSONCONS_ALL_CTOR_GETTER_TRAITS(ValueType, ...) \
+ JSONCONS_CTOR_GETTER_TRAITS_BASE(0, ValueType, JSONCONS_NARGS(__VA_ARGS__), JSONCONS_NARGS(__VA_ARGS__), __VA_ARGS__) \
+ namespace jsoncons { template <> struct is_json_type_traits_declared<ValueType> : public std::true_type {}; } \
+ /**/
+
+#define JSONCONS_TPL_ALL_CTOR_GETTER_TRAITS(NumTemplateParams, ValueType, ...) \
+ JSONCONS_CTOR_GETTER_TRAITS_BASE(NumTemplateParams, ValueType, JSONCONS_NARGS(__VA_ARGS__), JSONCONS_NARGS(__VA_ARGS__), __VA_ARGS__) \
+ namespace jsoncons { template <JSONCONS_GENERATE_TPL_PARAMS(JSONCONS_GENERATE_TPL_PARAM, NumTemplateParams)> struct is_json_type_traits_declared<ValueType JSONCONS_GENERATE_TPL_ARGS(JSONCONS_GENERATE_TPL_ARG, NumTemplateParams)> : public std::true_type {}; } \
+ /**/
+
+#define JSONCONS_N_CTOR_GETTER_TRAITS(ValueType,NumMandatoryParams, ...) \
+ JSONCONS_CTOR_GETTER_TRAITS_BASE(0, ValueType,NumMandatoryParams,NumMandatoryParams, __VA_ARGS__) \
+ namespace jsoncons { template <> struct is_json_type_traits_declared<ValueType> : public std::true_type {}; } \
+ /**/
+
+#define JSONCONS_N_ALL_CTOR_GETTER_TRAITS(NumTemplateParams, ValueType,NumMandatoryParams, ...) \
+ JSONCONS_CTOR_GETTER_TRAITS_BASE(NumTemplateParams, ValueType,NumMandatoryParams,NumMandatoryParams, __VA_ARGS__) \
+ namespace jsoncons { template <> struct is_json_type_traits_declared<ValueType> : 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_EXPAND(JSONCONS_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<typename std::decay<decltype(Into((std::declval<value_type*>())->Getter()))>::type>())) return false;} \
+ JSONCONS_CATCH(...) {return false;}
+
+#define JSONCONS_CTOR_GETTER_NAME_AS(P1, P2, P3, Seq, Count) JSONCONS_EXPAND(JSONCONS_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_2(Getter, Name) JSONCONS_COMMA
+#define JSONCONS_CTOR_GETTER_NAME_AS_3(Getter, Name, Mode) Mode(JSONCONS_CTOR_GETTER_NAME_AS_LAST_2(Getter, Name)) Mode(JSONCONS_COMMA)
+#define JSONCONS_CTOR_GETTER_NAME_AS_4(Getter, Name, Mode, Match) JSONCONS_CTOR_GETTER_NAME_AS_6(Getter, Name, Mode, Match,,)
+#define JSONCONS_CTOR_GETTER_NAME_AS_5(Getter, Name, Mode, Match, Into) JSONCONS_CTOR_GETTER_NAME_AS_6(Getter, Name, Mode, Match, Into, )
+#define JSONCONS_CTOR_GETTER_NAME_AS_6(Getter, Name, Mode, Match, Into, From) JSONCONS_CTOR_GETTER_NAME_AS_LAST_6(Getter,Name,Mode,Match,Into,From) Mode(JSONCONS_COMMA)
+#define JSONCONS_COMMA ,
+
+#define JSONCONS_CTOR_GETTER_NAME_AS_LAST(P1, P2, P3, Seq, Count) JSONCONS_EXPAND(JSONCONS_CONCAT(JSONCONS_CTOR_GETTER_NAME_AS_LAST_,JSONCONS_NARGS Seq) Seq)
+#define JSONCONS_CTOR_GETTER_NAME_AS_LAST_2(Getter, Name) (ajson.contains(Name)) ? (ajson.at(Name)).template as<typename std::decay<decltype((std::declval<value_type*>())->Getter())>::type>() : typename std::decay<decltype((std::declval<value_type*>())->Getter())>::type()
+#define JSONCONS_CTOR_GETTER_NAME_AS_LAST_3(Getter, Name, Mode) Mode(JSONCONS_CTOR_GETTER_NAME_AS_LAST_2(Getter, Name))
+#define JSONCONS_CTOR_GETTER_NAME_AS_LAST_4(Getter, Name, Mode, Match) JSONCONS_CTOR_GETTER_NAME_AS_LAST_6(Getter, Name, Mode, Match,,)
+#define JSONCONS_CTOR_GETTER_NAME_AS_LAST_5(Getter, Name, Mode, Match, Into) 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(ajson.contains(Name) ? From(ajson.at(Name).template as<typename std::decay<decltype(Into((std::declval<value_type*>())->Getter()))>::type>()) : From(typename std::decay<decltype(Into((std::declval<value_type*>())->Getter()))>::type()))
+
+#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_EXPAND(JSONCONS_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, aval.Getter() ); \
+} \
+else { \
+ json_traits_helper<Json>::set_optional_json_member(Name, aval.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(aval.Getter()) ); \
+} \
+else { \
+ json_traits_helper<Json>::set_optional_json_member(Name, Into(aval.Getter()), ajson); \
+}
+
+#define JSONCONS_CTOR_GETTER_NAME_TRAITS_BASE(NumTemplateParams, ValueType,NumMandatoryParams1,NumMandatoryParams2, ...) \
+namespace jsoncons \
+{ \
+ template<typename Json JSONCONS_GENERATE_TPL_PARAMS(JSONCONS_GENERATE_MORE_TPL_PARAM, NumTemplateParams)> \
+ struct json_type_traits<Json, ValueType JSONCONS_GENERATE_TPL_ARGS(JSONCONS_GENERATE_TPL_ARG, NumTemplateParams)> \
+ { \
+ using value_type = ValueType JSONCONS_GENERATE_TPL_ARGS(JSONCONS_GENERATE_TPL_ARG, NumTemplateParams); \
+ using allocator_type = typename Json::allocator_type; \
+ 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_REP_N(JSONCONS_CTOR_GETTER_NAME_IS,,,, __VA_ARGS__)\
+ return true; \
+ } \
+ static value_type as(const Json& ajson) \
+ { \
+ if (!is(ajson)) JSONCONS_THROW(conv_error(conv_errc::conversion_failed, "Not a " # ValueType)); \
+ return value_type ( JSONCONS_VARIADIC_REP_N(JSONCONS_CTOR_GETTER_NAME_AS,,,, __VA_ARGS__) ); \
+ } \
+ static Json to_json(const value_type& aval, allocator_type alloc=allocator_type()) \
+ { \
+ Json ajson(json_object_arg, semantic_tag::none, alloc); \
+ JSONCONS_VARIADIC_REP_N(JSONCONS_CTOR_GETTER_NAME_TO_JSON,,,, __VA_ARGS__) \
+ return ajson; \
+ } \
+ }; \
+} \
+ /**/
+
+#define JSONCONS_ALL_CTOR_GETTER_NAME_TRAITS(ValueType, ...) \
+ JSONCONS_CTOR_GETTER_NAME_TRAITS_BASE(0, ValueType, JSONCONS_NARGS(__VA_ARGS__), JSONCONS_NARGS(__VA_ARGS__), __VA_ARGS__) \
+ namespace jsoncons { template <> struct is_json_type_traits_declared<ValueType> : public std::true_type {}; } \
+ /**/
+
+#define JSONCONS_TPL_ALL_CTOR_GETTER_NAME_TRAITS(NumTemplateParams, ValueType, ...) \
+ JSONCONS_CTOR_GETTER_NAME_TRAITS_BASE(NumTemplateParams, ValueType, JSONCONS_NARGS(__VA_ARGS__), JSONCONS_NARGS(__VA_ARGS__), __VA_ARGS__) \
+ namespace jsoncons { template <JSONCONS_GENERATE_TPL_PARAMS(JSONCONS_GENERATE_TPL_PARAM, NumTemplateParams)> struct is_json_type_traits_declared<ValueType JSONCONS_GENERATE_TPL_ARGS(JSONCONS_GENERATE_TPL_ARG, NumTemplateParams)> : public std::true_type {}; } \
+ /**/
+
+#define JSONCONS_N_CTOR_GETTER_NAME_TRAITS(ValueType,NumMandatoryParams, ...) \
+ JSONCONS_CTOR_GETTER_NAME_TRAITS_BASE(0, ValueType,NumMandatoryParams,NumMandatoryParams, __VA_ARGS__) \
+ namespace jsoncons { template <> struct is_json_type_traits_declared<ValueType> : public std::true_type {}; } \
+ /**/
+
+#define JSONCONS_TPL_N_CTOR_GETTER_NAME_TRAITS(NumTemplateParams, ValueType,NumMandatoryParams, ...) \
+JSONCONS_CTOR_GETTER_NAME_TRAITS_BASE(NumTemplateParams, ValueType,NumMandatoryParams,NumMandatoryParams, __VA_ARGS__) \
+ namespace jsoncons { template <JSONCONS_GENERATE_TPL_PARAMS(JSONCONS_GENERATE_TPL_PARAM, NumTemplateParams)> struct is_json_type_traits_declared<ValueType JSONCONS_GENERATE_TPL_ARGS(JSONCONS_GENERATE_TPL_ARG, NumTemplateParams)> : 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_traits_macro_names<char_type,value_type>::Member##_str(char_type{})}
+
+#define JSONCONS_ENUM_TRAITS_BASE(EnumType, ...) \
+namespace jsoncons \
+{ \
+ template <class ChT> \
+ struct json_traits_macro_names<ChT,EnumType> \
+ { \
+ JSONCONS_VARIADIC_REP_N(JSONCONS_GENERATE_NAME_STR, ,,, __VA_ARGS__)\
+ }; \
+ template<typename Json> \
+ struct json_type_traits<Json, EnumType> \
+ { \
+ static_assert(std::is_enum<EnumType>::value, # EnumType " must be an enum"); \
+ using value_type = EnumType; \
+ using char_type = typename Json::char_type; \
+ using string_type = std::basic_string<char_type>; \
+ using string_view_type = jsoncons::basic_string_view<char_type>; \
+ using allocator_type = typename Json::allocator_type; \
+ using mapped_type = std::pair<EnumType,string_type>; \
+ \
+ static std::pair<const mapped_type*,const mapped_type*> get_values() \
+ { \
+ static const mapped_type v[] = { \
+ JSONCONS_VARIADIC_REP_N(JSONCONS_ENUM_PAIR, ,,, __VA_ARGS__)\
+ };\
+ return std::make_pair(v,v+JSONCONS_NARGS(__VA_ARGS__)); \
+ } \
+ \
+ static bool is(const Json& ajson) noexcept \
+ { \
+ if (!ajson.is_string()) return false; \
+ auto first = get_values().first; \
+ auto last = get_values().second; \
+ const string_view_type s = ajson.template as<string_view_type>(); \
+ 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; \
+ } \
+ static value_type as(const Json& ajson) \
+ { \
+ if (!is(ajson)) JSONCONS_THROW(conv_error(conv_errc::conversion_failed, "Not a " # EnumType)); \
+ const string_view_type s = ajson.template as<string_view_type>(); \
+ auto first = get_values().first; \
+ auto last = get_values().second; \
+ 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 value_type(); \
+ } \
+ else \
+ { \
+ JSONCONS_THROW(conv_error(conv_errc::conversion_failed, "Not an enum")); \
+ } \
+ } \
+ return it->first; \
+ } \
+ static Json to_json(value_type aval, allocator_type alloc=allocator_type()) \
+ { \
+ static constexpr char_type empty_string[] = {0}; \
+ auto first = get_values().first; \
+ auto last = get_values().second; \
+ auto it = std::find_if(first, last, \
+ [aval](const mapped_type& item) -> bool \
+ { return item.first == aval; }); \
+ if (it == last) \
+ { \
+ if (aval == value_type()) \
+ { \
+ return Json(empty_string); \
+ } \
+ else \
+ { \
+ JSONCONS_THROW(conv_error(conv_errc::conversion_failed, "Not an enum")); \
+ } \
+ } \
+ return Json(it->second,alloc); \
+ } \
+ }; \
+} \
+ /**/
+
+#define JSONCONS_ENUM_TRAITS(EnumType, ...) \
+ JSONCONS_ENUM_TRAITS_BASE(EnumType,__VA_ARGS__) \
+ namespace jsoncons { template <> struct is_json_type_traits_declared<EnumType> : public std::true_type {}; } \
+ /**/
+
+#define JSONCONS_NAME_ENUM_PAIR(P1, P2, P3, Seq, Count) JSONCONS_EXPAND(JSONCONS_NAME_ENUM_PAIR_ Seq),
+#define JSONCONS_NAME_ENUM_PAIR_LAST(P1, P2, P3, Seq, Count) JSONCONS_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 \
+{ \
+ template<typename Json> \
+ struct json_type_traits<Json, EnumType> \
+ { \
+ static_assert(std::is_enum<EnumType>::value, # EnumType " must be an enum"); \
+ using value_type = EnumType; \
+ using char_type = typename Json::char_type; \
+ using string_type = std::basic_string<char_type>; \
+ using string_view_type = jsoncons::basic_string_view<char_type>; \
+ using allocator_type = typename Json::allocator_type; \
+ using mapped_type = std::pair<EnumType,string_type>; \
+ \
+ static std::pair<const mapped_type*,const mapped_type*> get_values() \
+ { \
+ static const mapped_type v[] = { \
+ JSONCONS_VARIADIC_REP_N(JSONCONS_NAME_ENUM_PAIR,,,, __VA_ARGS__)\
+ };\
+ return std::make_pair(v,v+JSONCONS_NARGS(__VA_ARGS__)); \
+ } \
+ \
+ static bool is(const Json& ajson) noexcept \
+ { \
+ if (!ajson.is_string()) return false; \
+ auto first = get_values().first; \
+ auto last = get_values().second; \
+ const string_view_type s = ajson.template as<string_view_type>(); \
+ 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; \
+ } \
+ static value_type as(const Json& ajson) \
+ { \
+ if (!is(ajson)) JSONCONS_THROW(conv_error(conv_errc::conversion_failed, "Not a " # EnumType)); \
+ const string_view_type s = ajson.template as<string_view_type>(); \
+ auto first = get_values().first; \
+ auto last = get_values().second; \
+ 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 value_type(); \
+ } \
+ else \
+ { \
+ JSONCONS_THROW(conv_error(conv_errc::conversion_failed, "Not an enum")); \
+ } \
+ } \
+ return it->first; \
+ } \
+ static Json to_json(value_type aval, allocator_type alloc=allocator_type()) \
+ { \
+ static constexpr char_type empty_string[] = {0}; \
+ auto first = get_values().first; \
+ auto last = get_values().second; \
+ auto it = std::find_if(first, last, \
+ [aval](const mapped_type& item) -> bool \
+ { return item.first == aval; }); \
+ if (it == last) \
+ { \
+ if (aval == value_type()) \
+ { \
+ return Json(empty_string); \
+ } \
+ else \
+ { \
+ JSONCONS_THROW(conv_error(conv_errc::conversion_failed, "Not an enum")); \
+ } \
+ } \
+ return Json(it->second,alloc); \
+ } \
+ }; \
+ template <> struct is_json_type_traits_declared<EnumType> : public std::true_type {}; \
+} \
+ /**/
+
+#define JSONCONS_GETTER_SETTER_AS(Prefix, GetPrefix, SetPrefix, Property, Count) JSONCONS_GETTER_SETTER_AS_(Prefix, GetPrefix ## Property, SetPrefix ## Property, Property, Count)
+#define JSONCONS_GETTER_SETTER_AS_LAST(Prefix, GetPrefix, SetPrefix, Property, Count) JSONCONS_GETTER_SETTER_AS_(Prefix, GetPrefix ## Property, SetPrefix ## Property, Property, Count)
+#define JSONCONS_GETTER_SETTER_AS_(Prefix, Getter, Setter, Property, Count) if ((num_params-Count) < num_mandatory_params2 || ajson.contains(json_traits_macro_names<char_type,value_type>::Property##_str(char_type{}))) {aval.Setter(ajson.at(json_traits_macro_names<char_type,value_type>::Property##_str(char_type{})).template as<typename std::decay<decltype(aval.Getter())>::type>());}
+
+#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) aval.Setter(ajson.at(json_traits_macro_names<char_type,value_type>::Property##_str(char_type{})).template as<typename std::decay<decltype(aval.Getter())>::type>());
+
+#define JSONCONS_GETTER_SETTER_TO_JSON(Prefix, GetPrefix, SetPrefix, Property, Count) JSONCONS_GETTER_SETTER_TO_JSON_(Prefix, GetPrefix ## Property, SetPrefix ## Property, Property, Count)
+#define JSONCONS_GETTER_SETTER_TO_JSON_LAST(Prefix, GetPrefix, SetPrefix, Property, Count) JSONCONS_GETTER_SETTER_TO_JSON_(Prefix, GetPrefix ## Property, SetPrefix ## Property, Property, Count)
+#define JSONCONS_GETTER_SETTER_TO_JSON_(Prefix, Getter, Setter, Property, Count) \
+if ((num_params-Count) < num_mandatory_params2) \
+ {ajson.try_emplace(json_traits_macro_names<char_type,value_type>::Property##_str(char_type{}), aval.Getter());} \
+else \
+ {json_traits_helper<Json>::set_optional_json_member(json_traits_macro_names<char_type,value_type>::Property##_str(char_type{}), aval.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_traits_macro_names<char_type,value_type>::Property##_str(char_type{}), aval.Getter() );
+
+#define JSONCONS_GETTER_SETTER_TRAITS_BASE(AsT,ToJ,NumTemplateParams, ValueType,GetPrefix,SetPrefix,NumMandatoryParams1,NumMandatoryParams2, ...) \
+namespace jsoncons \
+{ \
+ template <class ChT JSONCONS_GENERATE_TPL_PARAMS(JSONCONS_GENERATE_MORE_TPL_PARAM, NumTemplateParams)> \
+ struct json_traits_macro_names<ChT,ValueType JSONCONS_GENERATE_TPL_ARGS(JSONCONS_GENERATE_TPL_ARG, NumTemplateParams)> \
+ { \
+ JSONCONS_VARIADIC_REP_N(JSONCONS_GENERATE_NAME_STR, ,,, __VA_ARGS__)\
+ }; \
+ template<typename Json JSONCONS_GENERATE_TPL_PARAMS(JSONCONS_GENERATE_MORE_TPL_PARAM, NumTemplateParams)> \
+ struct json_type_traits<Json, ValueType JSONCONS_GENERATE_TPL_ARGS(JSONCONS_GENERATE_TPL_ARG, NumTemplateParams)> \
+ { \
+ using value_type = ValueType JSONCONS_GENERATE_TPL_ARGS(JSONCONS_GENERATE_TPL_ARG, NumTemplateParams); \
+ using allocator_type = typename Json::allocator_type; \
+ 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_REP_N(JSONCONS_N_MEMBER_IS, ,GetPrefix,SetPrefix, __VA_ARGS__)\
+ return true; \
+ } \
+ static value_type as(const Json& ajson) \
+ { \
+ if (!is(ajson)) JSONCONS_THROW(conv_error(conv_errc::conversion_failed, "Not a " # ValueType)); \
+ value_type aval{}; \
+ JSONCONS_VARIADIC_REP_N(AsT, ,GetPrefix,SetPrefix, __VA_ARGS__) \
+ return aval; \
+ } \
+ static Json to_json(const value_type& aval, allocator_type alloc=allocator_type()) \
+ { \
+ Json ajson(json_object_arg, semantic_tag::none, alloc); \
+ JSONCONS_VARIADIC_REP_N(ToJ, ,GetPrefix,SetPrefix, __VA_ARGS__) \
+ return ajson; \
+ } \
+ }; \
+} \
+ /**/
+
+#define JSONCONS_N_GETTER_SETTER_TRAITS(ValueType,GetPrefix,SetPrefix,NumMandatoryParams, ...) \
+ JSONCONS_GETTER_SETTER_TRAITS_BASE(JSONCONS_GETTER_SETTER_AS, JSONCONS_GETTER_SETTER_TO_JSON,0, ValueType,GetPrefix,SetPrefix,NumMandatoryParams,NumMandatoryParams, __VA_ARGS__) \
+ namespace jsoncons { template <> struct is_json_type_traits_declared<ValueType> : public std::true_type {}; } \
+ /**/
+
+#define JSONCONS_TPL_N_GETTER_SETTER_TRAITS(NumTemplateParams, ValueType,GetPrefix,SetPrefix,NumMandatoryParams, ...) \
+ JSONCONS_GETTER_SETTER_TRAITS_BASE(JSONCONS_GETTER_SETTER_AS, JSONCONS_GETTER_SETTER_TO_JSON,NumTemplateParams, ValueType,GetPrefix,SetPrefix,NumMandatoryParams,NumMandatoryParams, __VA_ARGS__) \
+ namespace jsoncons { template <JSONCONS_GENERATE_TPL_PARAMS(JSONCONS_GENERATE_TPL_PARAM, NumTemplateParams)> struct is_json_type_traits_declared<ValueType JSONCONS_GENERATE_TPL_ARGS(JSONCONS_GENERATE_TPL_ARG, NumTemplateParams)> : public std::true_type {}; } \
+ /**/
+
+#define JSONCONS_ALL_GETTER_SETTER_TRAITS(ValueType,GetPrefix,SetPrefix, ...) \
+ JSONCONS_GETTER_SETTER_TRAITS_BASE(JSONCONS_ALL_GETTER_SETTER_AS, JSONCONS_ALL_GETTER_SETTER_TO_JSON,0,ValueType,GetPrefix,SetPrefix, JSONCONS_NARGS(__VA_ARGS__), JSONCONS_NARGS(__VA_ARGS__),__VA_ARGS__) \
+ namespace jsoncons { template <> struct is_json_type_traits_declared<ValueType> : public std::true_type {}; } \
+ /**/
+
+#define JSONCONS_TPL_ALL_GETTER_SETTER_TRAITS(NumTemplateParams, ValueType,GetPrefix,SetPrefix, ...) \
+ JSONCONS_GETTER_SETTER_TRAITS_BASE(JSONCONS_ALL_GETTER_SETTER_AS, JSONCONS_ALL_GETTER_SETTER_TO_JSON,NumTemplateParams,ValueType,GetPrefix,SetPrefix, JSONCONS_NARGS(__VA_ARGS__), JSONCONS_NARGS(__VA_ARGS__),__VA_ARGS__) \
+ namespace jsoncons { template <JSONCONS_GENERATE_TPL_PARAMS(JSONCONS_GENERATE_TPL_PARAM, NumTemplateParams)> struct is_json_type_traits_declared<ValueType JSONCONS_GENERATE_TPL_ARGS(JSONCONS_GENERATE_TPL_ARG, NumTemplateParams)> : 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_EXPAND(JSONCONS_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<typename std::decay<decltype(Into((std::declval<value_type*>())->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) JSONCONS_EXPAND(JSONCONS_CONCAT(JSONCONS_N_GETTER_SETTER_NAME_AS_,JSONCONS_NARGS Seq) Seq)
+#define JSONCONS_N_GETTER_SETTER_NAME_AS_3(Getter, Setter, Name) if (ajson.contains(Name)) aval.Setter(ajson.at(Name).template as<typename std::decay<decltype(aval.Getter())>::type>());
+#define JSONCONS_N_GETTER_SETTER_NAME_AS_4(Getter, Setter, Name, Mode) Mode(JSONCONS_N_GETTER_SETTER_NAME_AS_3(Getter, Setter, Name))
+#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) Mode(if (ajson.contains(Name)) aval.Setter(From(ajson.at(Name).template as<typename std::decay<decltype(Into(aval.Getter()))>::type>()));)
+
+#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_EXPAND(JSONCONS_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, aval.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(aval.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_EXPAND(JSONCONS_CONCAT(JSONCONS_ALL_GETTER_SETTER_NAME_AS_,JSONCONS_NARGS Seq) Seq)
+#define JSONCONS_ALL_GETTER_SETTER_NAME_AS_3(Getter, Setter, Name) aval.Setter(ajson.at(Name).template as<typename std::decay<decltype(aval.Getter())>::type>());
+#define JSONCONS_ALL_GETTER_SETTER_NAME_AS_4(Getter, Setter, Name, Mode) Mode(JSONCONS_ALL_GETTER_SETTER_NAME_AS_3(Getter, Setter, Name))
+#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) Mode(aval.Setter(From(ajson.at(Name).template as<typename std::decay<decltype(Into(aval.Getter()))>::type>()));)
+
+#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_EXPAND(JSONCONS_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, aval.Getter()); \
+else \
+ {json_traits_helper<Json>::set_optional_json_member(Name, aval.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(aval.Getter())); \
+else \
+ {json_traits_helper<Json>::set_optional_json_member(Name, Into(aval.Getter()), ajson);}
+
+#define JSONCONS_GETTER_SETTER_NAME_TRAITS_BASE(AsT,ToJ, NumTemplateParams, ValueType,NumMandatoryParams1,NumMandatoryParams2, ...) \
+namespace jsoncons \
+{ \
+ template<typename Json JSONCONS_GENERATE_TPL_PARAMS(JSONCONS_GENERATE_MORE_TPL_PARAM, NumTemplateParams)> \
+ struct json_type_traits<Json, ValueType JSONCONS_GENERATE_TPL_ARGS(JSONCONS_GENERATE_TPL_ARG, NumTemplateParams)> \
+ { \
+ using value_type = ValueType JSONCONS_GENERATE_TPL_ARGS(JSONCONS_GENERATE_TPL_ARG, NumTemplateParams); \
+ using allocator_type = typename Json::allocator_type; \
+ 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_REP_N(JSONCONS_GETTER_SETTER_NAME_IS,,,, __VA_ARGS__)\
+ return true; \
+ } \
+ static value_type as(const Json& ajson) \
+ { \
+ if (!is(ajson)) JSONCONS_THROW(conv_error(conv_errc::conversion_failed, "Not a " # ValueType)); \
+ value_type aval{}; \
+ JSONCONS_VARIADIC_REP_N(AsT,,,, __VA_ARGS__) \
+ return aval; \
+ } \
+ static Json to_json(const value_type& aval, allocator_type alloc=allocator_type()) \
+ { \
+ Json ajson(json_object_arg, semantic_tag::none, alloc); \
+ JSONCONS_VARIADIC_REP_N(ToJ,,,, __VA_ARGS__) \
+ return ajson; \
+ } \
+ }; \
+} \
+ /**/
+
+#define JSONCONS_N_GETTER_SETTER_NAME_TRAITS(ValueType,NumMandatoryParams, ...) \
+ JSONCONS_GETTER_SETTER_NAME_TRAITS_BASE(JSONCONS_N_GETTER_SETTER_NAME_AS,JSONCONS_N_GETTER_SETTER_NAME_TO_JSON, 0, ValueType,NumMandatoryParams,NumMandatoryParams, __VA_ARGS__) \
+ namespace jsoncons { template <> struct is_json_type_traits_declared<ValueType> : public std::true_type {}; } \
+ /**/
+
+#define JSONCONS_TPL_N_GETTER_SETTER_NAME_TRAITS(NumTemplateParams, ValueType,NumMandatoryParams, ...) \
+ JSONCONS_GETTER_SETTER_NAME_TRAITS_BASE(JSONCONS_N_GETTER_SETTER_NAME_AS,JSONCONS_N_GETTER_SETTER_NAME_TO_JSON, NumTemplateParams, ValueType,NumMandatoryParams,NumMandatoryParams, __VA_ARGS__) \
+ namespace jsoncons { template <JSONCONS_GENERATE_TPL_PARAMS(JSONCONS_GENERATE_TPL_PARAM, NumTemplateParams)> struct is_json_type_traits_declared<ValueType JSONCONS_GENERATE_TPL_ARGS(JSONCONS_GENERATE_TPL_ARG, NumTemplateParams)> : public std::true_type {}; } \
+ /**/
+
+#define JSONCONS_ALL_GETTER_SETTER_NAME_TRAITS(ValueType, ...) \
+ JSONCONS_GETTER_SETTER_NAME_TRAITS_BASE(JSONCONS_ALL_GETTER_SETTER_NAME_AS,JSONCONS_ALL_GETTER_SETTER_NAME_TO_JSON, 0, ValueType, JSONCONS_NARGS(__VA_ARGS__), JSONCONS_NARGS(__VA_ARGS__), __VA_ARGS__) \
+ namespace jsoncons { template <> struct is_json_type_traits_declared<ValueType> : public std::true_type {}; } \
+ /**/
+
+#define JSONCONS_TPL_ALL_GETTER_SETTER_NAME_TRAITS(NumTemplateParams, ValueType, ...) \
+ JSONCONS_GETTER_SETTER_NAME_TRAITS_BASE(JSONCONS_ALL_GETTER_SETTER_NAME_AS,JSONCONS_ALL_GETTER_SETTER_NAME_TO_JSON, NumTemplateParams, ValueType, JSONCONS_NARGS(__VA_ARGS__), JSONCONS_NARGS(__VA_ARGS__), __VA_ARGS__) \
+ namespace jsoncons { template <JSONCONS_GENERATE_TPL_PARAMS(JSONCONS_GENERATE_TPL_PARAM, NumTemplateParams)> struct is_json_type_traits_declared<ValueType JSONCONS_GENERATE_TPL_ARGS(JSONCONS_GENERATE_TPL_ARG, NumTemplateParams)> : public std::true_type {}; } \
+ /**/
+
+#define JSONCONS_POLYMORPHIC_IS(BaseClass, P2, P3, DerivedClass, Count) if (ajson.template is<DerivedClass>()) return true;
+#define JSONCONS_POLYMORPHIC_IS_LAST(BaseClass, P2, P3, DerivedClass, Count) if (ajson.template is<DerivedClass>()) return true;
+
+#define JSONCONS_POLYMORPHIC_AS(BaseClass, P2, P3, DerivedClass, Count) if (ajson.template is<DerivedClass>()) return std::make_shared<DerivedClass>(ajson.template as<DerivedClass>());
+#define JSONCONS_POLYMORPHIC_AS_LAST(BaseClass, P2, P3, DerivedClass, Count) if (ajson.template is<DerivedClass>()) return std::make_shared<DerivedClass>(ajson.template as<DerivedClass>());
+
+#define JSONCONS_POLYMORPHIC_AS_UNIQUE_PTR(BaseClass, P2, P3, DerivedClass, Count) if (ajson.template is<DerivedClass>()) return jsoncons::make_unique<DerivedClass>(ajson.template as<DerivedClass>());
+#define JSONCONS_POLYMORPHIC_AS_UNIQUE_PTR_LAST(BaseClass, P2, P3, DerivedClass, Count) if (ajson.template is<DerivedClass>()) return jsoncons::make_unique<DerivedClass>(ajson.template as<DerivedClass>());
+
+#define JSONCONS_POLYMORPHIC_AS_SHARED_PTR(BaseClass, P2, P3, DerivedClass, Count) if (ajson.template is<DerivedClass>()) return std::make_shared<DerivedClass>(ajson.template as<DerivedClass>());
+#define JSONCONS_POLYMORPHIC_AS_SHARED_PTR_LAST(BaseClass, P2, P3, DerivedClass, Count) if (ajson.template is<DerivedClass>()) return std::make_shared<DerivedClass>(ajson.template as<DerivedClass>());
+
+#define JSONCONS_POLYMORPHIC_TO_JSON(BaseClass, P2, P3, DerivedClass, Count) if (DerivedClass* p = dynamic_cast<DerivedClass*>(ptr.get())) {return Json(*p);}
+#define JSONCONS_POLYMORPHIC_TO_JSON_LAST(BaseClass, P2, P3, DerivedClass, Count) if (DerivedClass* p = dynamic_cast<DerivedClass*>(ptr.get())) {return Json(*p);}
+
+#define JSONCONS_POLYMORPHIC_TRAITS(BaseClass, ...) \
+namespace jsoncons { \
+ template<class Json> \
+ struct json_type_traits<Json, std::shared_ptr<BaseClass>> { \
+ static bool is(const Json& ajson) noexcept { \
+ if (!ajson.is_object()) return false; \
+ JSONCONS_VARIADIC_REP_N(JSONCONS_POLYMORPHIC_IS, BaseClass,,, __VA_ARGS__)\
+ return false; \
+ } \
+\
+ static std::shared_ptr<BaseClass> as(const Json& ajson) { \
+ if (!ajson.is_object()) return std::shared_ptr<BaseClass>(); \
+ JSONCONS_VARIADIC_REP_N(JSONCONS_POLYMORPHIC_AS_SHARED_PTR, BaseClass,,, __VA_ARGS__)\
+ return std::shared_ptr<BaseClass>(); \
+ } \
+\
+ static Json to_json(const std::shared_ptr<BaseClass>& ptr) { \
+ if (ptr.get() == nullptr) {return Json::null();} \
+ JSONCONS_VARIADIC_REP_N(JSONCONS_POLYMORPHIC_TO_JSON, BaseClass,,, __VA_ARGS__)\
+ return Json::null(); \
+ } \
+ }; \
+ template<class Json> \
+ struct json_type_traits<Json, std::unique_ptr<BaseClass>> { \
+ static bool is(const Json& ajson) noexcept { \
+ if (!ajson.is_object()) return false; \
+ JSONCONS_VARIADIC_REP_N(JSONCONS_POLYMORPHIC_IS, BaseClass,,, __VA_ARGS__)\
+ return false; \
+ } \
+ static std::unique_ptr<BaseClass> as(const Json& ajson) { \
+ if (!ajson.is_object()) return std::unique_ptr<BaseClass>(); \
+ JSONCONS_VARIADIC_REP_N(JSONCONS_POLYMORPHIC_AS_UNIQUE_PTR, BaseClass,,, __VA_ARGS__)\
+ return std::unique_ptr<BaseClass>(); \
+ } \
+ static Json to_json(const std::unique_ptr<BaseClass>& ptr) { \
+ if (ptr.get() == nullptr) {return Json::null();} \
+ JSONCONS_VARIADIC_REP_N(JSONCONS_POLYMORPHIC_TO_JSON, BaseClass,,, __VA_ARGS__)\
+ return Json::null(); \
+ } \
+ }; \
+} \
+ /**/
+
+#endif
diff --git a/include/jsoncons/json_traits_macros_deprecated.hpp b/include/jsoncons/json_traits_macros_deprecated.hpp
new file mode 100644
index 0000000..0d44e38
--- /dev/null
+++ b/include/jsoncons/json_traits_macros_deprecated.hpp
@@ -0,0 +1,144 @@
+// Copyright 2019 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_TRAITS_MACROS_DEPRECATED_HPP
+#define JSONCONS_JSON_TRAITS_MACROS_DEPRECATED_HPP
+
+#include <jsoncons/json_traits_macros.hpp>
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+
+#define JSONCONS_MEMBER_TRAITS_DECL(ValueType, ...) \
+ JSONCONS_MEMBER_TRAITS_BASE(JSONCONS_AS,JSONCONS_TO_JSON,0, ValueType, JSONCONS_NARGS(__VA_ARGS__), 0, __VA_ARGS__) \
+ /**/
+
+#define JSONCONS_TPL_MEMBER_TRAITS_DECL(NumTemplateParams, ValueType, ...) \
+ JSONCONS_MEMBER_TRAITS_BASE(JSONCONS_AS,JSONCONS_TO_JSON,NumTemplateParams, ValueType, JSONCONS_NARGS(__VA_ARGS__), 0, __VA_ARGS__) \
+ /**/
+
+#define JSONCONS_MEMBER_NAMED_TRAITS_DECL(ValueType, ...) \
+ JSONCONS_MEMBER_NAME_TRAITS_BASE(JSONCONS_NAME_AS, JSONCONS_NAME_TO_JSON, 0, ValueType, JSONCONS_NARGS(__VA_ARGS__), 0, __VA_ARGS__) \
+ /**/
+
+#define JSONCONS_TPL_MEMBER_NAMED_TRAITS_DECL(NumTemplateParams, ValueType, ...) \
+ JSONCONS_MEMBER_NAME_TRAITS_BASE(JSONCONS_NAME_AS, JSONCONS_NAME_TO_JSON, NumTemplateParams, ValueType, JSONCONS_NARGS(__VA_ARGS__), 0, __VA_ARGS__) \
+ /**/
+
+#define JSONCONS_GETTER_SETTER_TRAITS_DECL(ValueType,GetPrefix,SetPrefix, ...) \
+ JSONCONS_GETTER_SETTER_TRAITS_BASE(JSONCONS_GETTER_SETTER_AS, JSONCONS_GETTER_SETTER_TO_JSON,0, ValueType,GetPrefix,SetPrefix, JSONCONS_NARGS(__VA_ARGS__), 0, __VA_ARGS__) \
+ /**/
+
+#define JSONCONS_TPL_GETTER_SETTER_TRAITS_DECL(NumTemplateParams, ValueType,GetPrefix,SetPrefix, ...) \
+ JSONCONS_GETTER_SETTER_TRAITS_BASE(JSONCONS_GETTER_SETTER_AS, JSONCONS_GETTER_SETTER_TO_JSON,NumTemplateParams, ValueType,GetPrefix,SetPrefix, JSONCONS_NARGS(__VA_ARGS__), 0, __VA_ARGS__) \
+ /**/
+
+#define JSONCONS_GETTER_SETTER_NAMED_TRAITS_DECL(ValueType, ...) \
+JSONCONS_GETTER_SETTER_NAME_TRAITS_BASE(JSONCONS_GETTER_SETTER_NAME_AS,JSONCONS_GETTER_SETTER_NAME_TO_JSON, 0, ValueType, JSONCONS_NARGS(__VA_ARGS__), 0, __VA_ARGS__) \
+ /**/
+
+#define JSONCONS_TPL_GETTER_SETTER_NAMED_TRAITS_DECL(NumTemplateParams, ValueType, ...) \
+JSONCONS_GETTER_SETTER_NAME_TRAITS_BASE(JSONCONS_GETTER_SETTER_NAME_AS,JSONCONS_GETTER_SETTER_NAME_TO_JSON, NumTemplateParams, ValueType, JSONCONS_NARGS(__VA_ARGS__), 0, __VA_ARGS__) \
+ /**/
+
+#define JSONCONS_ALL_GETTER_CTOR_TRAITS JSONCONS_ALL_CTOR_GETTER_TRAITS
+#define JSONCONS_N_GETTER_CTOR_TRAITS JSONCONS_N_CTOR_GETTER_TRAITS
+#define JSONCONS_TPL_ALL_GETTER_CTOR_TRAITS JSONCONS_TPL_ALL_CTOR_GETTER_TRAITS
+#define JSONCONS_TPL_N_GETTER_CTOR_TRAITS JSONCONS_TPL_N_CTOR_GETTER_TRAITS
+
+#define JSONCONS_ALL_GETTER_CTOR_NAME_TRAITS JSONCONS_ALL_CTOR_GETTER_NAME_TRAITS
+#define JSONCONS_N_GETTER_CTOR_NAME_TRAITS JSONCONS_N_CTOR_GETTER_NAME_TRAITS
+#define JSONCONS_TPL_ALL_GETTER_CTOR_NAME_TRAITS JSONCONS_TPL_ALL_CTOR_GETTER_NAME_TRAITS
+#define JSONCONS_TPL_N_GETTER_CTOR_NAME_TRAITS JSONCONS_TPL_N_CTOR_GETTER_NAME_TRAITS
+
+#define JSONCONS_PROPERTY_TRAITS_DECL JSONCONS_GETTER_SETTER_TRAITS_DECL
+#define JSONCONS_TPL_PROPERTY_TRAITS_DECL JSONCONS_TPL_GETTER_SETTER_TRAITS_DECL
+#define JSONCONS_TYPE_TRAITS_DECL JSONCONS_MEMBER_TRAITS_DECL
+#define JSONCONS_MEMBER_TRAITS_NAMED_DECL JSONCONS_MEMBER_NAMED_TRAITS_DECL
+#define JSONCONS_TEMPLATE_MEMBER_TRAITS_NAMED_DECL JSONCONS_TPL_MEMBER_NAMED_TRAITS_DECL
+#define JSONCONS_TEMPLATE_GETTER_SETTER_TRAITS_NAMED_DECL JSONCONS_TPL_GETTER_SETTER_NAMED_TRAITS_DECL
+#define JSONCONS_TEMPLATE_MEMBER_TRAITS_DECL JSONCONS_TPL_MEMBER_TRAITS_DECL
+
+#define JSONCONS_N_MEMBER_NAMED_TRAITS JSONCONS_N_MEMBER_NAME_TRAITS
+#define JSONCONS_TPL_N_MEMBER_NAMED_TRAITS JSONCONS_TPL_N_MEMBER_NAME_TRAITS
+#define JSONCONS_ALL_MEMBER_NAMED_TRAITS JSONCONS_ALL_MEMBER_NAME_TRAITS
+#define JSONCONS_TPL_ALL_MEMBER_NAMED_TRAITS JSONCONS_TPL_ALL_MEMBER_NAME_TRAITS
+
+#define JSONCONS_ALL_GETTER_CTOR_NAMED_TRAITS JSONCONS_ALL_GETTER_CTOR_NAME_TRAITS
+#define JSONCONS_TPL_ALL_GETTER_CTOR_NAMED_TRAITS JSONCONS_TPL_ALL_GETTER_CTOR_NAME_TRAITS
+#define JSONCONS_N_GETTER_CTOR_NAMED_TRAITS JSONCONS_N_GETTER_CTOR_NAME_TRAITS
+#define JSONCONS_TPL_N_GETTER_CTOR_NAMED_TRAITS JSONCONS_TPL_N_GETTER_CTOR_NAME_TRAITS
+
+#define JSONCONS_ENUM_NAMED_TRAITS JSONCONS_ENUM_NAME_TRAITS
+
+#define JSONCONS_N_GETTER_SETTER_NAMED_TRAITS JSONCONS_N_GETTER_SETTER_NAME_TRAITS
+#define JSONCONS_TPL_N_GETTER_SETTER_NAMED_TRAITS JSONCONS_TPL_N_GETTER_SETTER_NAME_TRAITS
+#define JSONCONS_ALL_GETTER_SETTER_NAMED_TRAITS JSONCONS_ALL_GETTER_SETTER_NAME_TRAITS
+#define JSONCONS_TPL_ALL_GETTER_SETTER_NAMED_TRAITS JSONCONS_TPL_ALL_GETTER_SETTER_NAME_TRAITS
+
+#define JSONCONS_N_MEMBER_TRAITS_DECL JSONCONS_N_MEMBER_TRAITS
+#define JSONCONS_TPL_N_MEMBER_TRAITS_DECL JSONCONS_TPL_N_MEMBER_TRAITS
+#define JSONCONS_ALL_MEMBER_TRAITS_DECL JSONCONS_ALL_MEMBER_TRAITS
+#define JSONCONS_TPL_ALL_MEMBER_TRAITS_DECL JSONCONS_TPL_ALL_MEMBER_TRAITS
+#define JSONCONS_N_MEMBER_NAMED_TRAITS_DECL JSONCONS_N_MEMBER_NAMED_TRAITS
+#define JSONCONS_TPL_N_MEMBER_NAMED_TRAITS_DECL JSONCONS_TPL_N_MEMBER_NAMED_TRAITS
+#define JSONCONS_ALL_MEMBER_NAMED_TRAITS_DECL JSONCONS_ALL_MEMBER_NAMED_TRAITS
+#define JSONCONS_TPL_ALL_MEMBER_NAMED_TRAITS_DECL JSONCONS_TPL_ALL_MEMBER_NAMED_TRAITS
+#define JSONCONS_ALL_GETTER_CTOR_TRAITS_DECL JSONCONS_ALL_GETTER_CTOR_TRAITS
+#define JSONCONS_TPL_ALL_GETTER_CTOR_TRAITS_DECL JSONCONS_TPL_ALL_GETTER_CTOR_TRAITS
+#define JSONCONS_N_GETTER_CTOR_TRAITS_DECL JSONCONS_N_GETTER_CTOR_TRAITS
+#define JSONCONS_N_ALL_GETTER_CTOR_TRAITS_DECL JSONCONS_N_ALL_GETTER_CTOR_TRAITS
+#define JSONCONS_ALL_GETTER_CTOR_NAMED_TRAITS_DECL JSONCONS_ALL_GETTER_CTOR_NAMED_TRAITS
+#define JSONCONS_TPL_ALL_GETTER_CTOR_NAMED_TRAITS_DECL JSONCONS_TPL_ALL_GETTER_CTOR_NAMED_TRAITS
+#define JSONCONS_N_GETTER_CTOR_NAMED_TRAITS_DECL JSONCONS_N_GETTER_CTOR_NAMED_TRAITS
+#define JSONCONS_TPL_N_GETTER_CTOR_NAMED_TRAITS_DECL JSONCONS_TPL_N_GETTER_CTOR_NAMED_TRAITS
+#define JSONCONS_ENUM_TRAITS_DECL JSONCONS_ENUM_TRAITS
+#define JSONCONS_ENUM_NAMED_TRAITS_DECL JSONCONS_ENUM_NAMED_TRAITS
+#define JSONCONS_N_GETTER_SETTER_TRAITS_DECL JSONCONS_N_GETTER_SETTER_TRAITS
+#define JSONCONS_TPL_N_GETTER_SETTER_TRAITS_DECL JSONCONS_TPL_N_GETTER_SETTER_TRAITS
+#define JSONCONS_ALL_GETTER_SETTER_TRAITS_DECL JSONCONS_ALL_GETTER_SETTER_TRAITS
+#define JSONCONS_TPL_ALL_GETTER_SETTER_TRAITS_DECL JSONCONS_TPL_ALL_GETTER_SETTER_TRAITS
+#define JSONCONS_N_GETTER_SETTER_NAMED_TRAITS_DECL JSONCONS_N_GETTER_SETTER_NAMED_TRAITS
+#define JSONCONS_TPL_N_GETTER_SETTER_NAMED_TRAITS_DECL JSONCONS_TPL_N_GETTER_SETTER_NAMED_TRAITS
+#define JSONCONS_ALL_GETTER_SETTER_NAMED_TRAITS_DECL JSONCONS_ALL_GETTER_SETTER_NAMED_TRAITS
+#define JSONCONS_TPL_ALL_GETTER_SETTER_NAMED_TRAITS_DECL JSONCONS_TPL_ALL_GETTER_SETTER_NAMED_TRAITS
+#define JSONCONS_POLYMORPHIC_TRAITS_DECL JSONCONS_POLYMORPHIC_TRAITS
+#define JSONCONS_NONDEFAULT_MEMBER_TRAITS_DECL JSONCONS_ALL_MEMBER_TRAITS
+#define JSONCONS_TEMPLATE_STRICT_MEMBER_TRAITS_DECL JSONCONS_TPL_ALL_MEMBER_TRAITS
+
+#define JSONCONS_STRICT_MEMBER_TRAITS_NAMED_DECL JSONCONS_ALL_MEMBER_NAME_TRAITS
+#define JSONCONS_STRICT_TEMPLATE_MEMBER_TRAITS_DECL JSONCONS_TPL_ALL_MEMBER_TRAITS
+#define JSONCONS_STRICT_TEMPLATE_MEMBER_TRAITS_NAMED_DECL JSONCONS_TPL_ALL_MEMBER_NAME_TRAITS
+#define JSONCONS_ENUM_TRAITS_NAMED_DECL JSONCONS_ENUM_NAME_TRAITS
+#define JSONCONS_GETTER_CTOR_TRAITS_NAMED_DECL JSONCONS_ALL_GETTER_CTOR_NAME_TRAITS
+#define JSONCONS_TEMPLATE_GETTER_CTOR_TRAITS_DECL JSONCONS_TPL_ALL_GETTER_CTOR_TRAITS
+#define JSONCONS_TEMPLATE_GETTER_CTOR_TRAITS_NAMED_DECL JSONCONS_TPL_ALL_GETTER_CTOR_NAME_TRAITS
+#define JSONCONS_GETTER_SETTER_TRAITS_NAMED_DECL JSONCONS_ALL_GETTER_SETTER_NAME_TRAITS
+#define JSONCONS_STRICT_GETTER_SETTER_TRAITS_NAMED_DECL JSONCONS_ALL_GETTER_SETTER_NAME_TRAITS
+#define JSONCONS_STRICT_TEMPLATE_GETTER_SETTER_TRAITS_NAMED_DECL JSONCONS_TPL_ALL_GETTER_SETTER_NAME_TRAITS
+#define JSONCONS_STRICT_TPL_MEMBER_TRAITS_DECL JSONCONS_TPL_ALL_MEMBER_TRAITS
+#define JSONCONS_STRICT_TPL_MEMBER_NAMED_TRAITS_DECL JSONCONS_TPL_ALL_MEMBER_NAME_TRAITS
+#define JSONCONS_STRICT_TPL_GETTER_SETTER_NAMED_TRAITS_DECL JSONCONS_TPL_ALL_GETTER_SETTER_NAME_TRAITS
+
+#define JSONCONS_STRICT_MEMBER_TRAITS_DECL JSONCONS_ALL_MEMBER_TRAITS
+#define JSONCONS_TPL_STRICT_MEMBER_TRAITS_DECL JSONCONS_TPL_ALL_MEMBER_TRAITS
+#define JSONCONS_STRICT_MEMBER_NAMED_TRAITS_DECL JSONCONS_ALL_MEMBER_NAME_TRAITS
+#define JSONCONS_TPL_STRICT_MEMBER_NAMED_TRAITS_DECL JSONCONS_ALL_STRICT_MEMBER_NAME_TRAITS
+#define JSONCONS_STRICT_PROPERTY_TRAITS_DECL JSONCONS_ALL_GETTER_SETTER_TRAITS
+#define JSONCONS_TPL_STRICT_PROPERTY_TRAITS_DECL JSONCONS_TPL_ALL_GETTER_SETTER_TRAITS
+#define JSONCONS_STRICT_GETTER_SETTER_NAMED_TRAITS_DECL JSONCONS_ALL_GETTER_SETTER_NAME_TRAITS
+#define JSONCONS_TPL_STRICT_GETTER_SETTER_NAMED_TRAITS_DECL JSONCONS_TPL_ALL_GETTER_SETTER_NAME_TRAITS
+#define JSONCONS_GETTER_CTOR_TRAITS_DECL JSONCONS_ALL_GETTER_CTOR_TRAITS
+#define JSONCONS_TPL_GETTER_CTOR_TRAITS_DECL JSONCONS_TPL_ALL_GETTER_CTOR_TRAITS
+#define JSONCONS_GETTER_CTOR_NAMED_TRAITS_DECL JSONCONS_ALL_GETTER_CTOR_NAME_TRAITS
+#define JSONCONS_TPL_GETTER_CTOR_NAMED_TRAITS_DECL JSONCONS_TPL_ALL_GETTER_CTOR_NAME_TRAITS
+#define JSONCONS_N_PROPERTY_TRAITS_DECL JSONCONS_N_GETTER_SETTER_TRAITS
+#define JSONCONS_ALL_PROPERTY_TRAITS_DECL JSONCONS_ALL_GETTER_SETTER_TRAITS
+#define JSONCONS_TPL_N_PROPERTY_TRAITS_DECL JSONCONS_TPL_N_GETTER_SETTER_TRAITS
+#define JSONCONS_TPL_ALL_PROPERTY_TRAITS_DECL JSONCONS_TPL_ALL_GETTER_SETTER_TRAITS
+
+#endif
+
+#endif
diff --git a/include/jsoncons/json_type.hpp b/include/jsoncons/json_type.hpp
new file mode 100644
index 0000000..f642d6b
--- /dev/null
+++ b/include/jsoncons/json_type.hpp
@@ -0,0 +1,206 @@
+// Copyright 2013 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_TYPE_HPP
+#define JSONCONS_JSON_TYPE_HPP
+
+#include <ostream>
+#include <jsoncons/config/jsoncons_config.hpp>
+
+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
+ };
+
+ template <class CharT>
+ std::basic_ostream<CharT>& operator<<(std::basic_ostream<CharT>& 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* 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* 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");
+ static constexpr const CharT* object_value = JSONCONS_CSTRING_CONSTANT(CharT, "object");
+
+ switch (type)
+ {
+ case json_type::null_value:
+ {
+ os << null_value;
+ break;
+ }
+ case json_type::bool_value:
+ {
+ os << bool_value;
+ break;
+ }
+ case json_type::int64_value:
+ {
+ os << int64_value;
+ break;
+ }
+ case json_type::uint64_value:
+ {
+ os << uint64_value;
+ break;
+ }
+ case json_type::half_value:
+ {
+ os << half_value;
+ break;
+ }
+ case json_type::double_value:
+ {
+ os << double_value;
+ break;
+ }
+ case json_type::string_value:
+ {
+ os << string_value;
+ break;
+ }
+ case json_type::byte_string_value:
+ {
+ os << byte_string_value;
+ break;
+ }
+ case json_type::array_value:
+ {
+ os << array_value;
+ break;
+ }
+ case json_type::object_value:
+ {
+ os << object_value;
+ break;
+ }
+ }
+ return os;
+ }
+
+ enum class json_storage_kind : uint8_t
+ {
+ null_value = 0x00,
+ bool_value = 0x01,
+ int64_value = 0x02,
+ uint64_value = 0x03,
+ half_value = 0x04,
+ double_value = 0x05,
+ short_string_value = 0x06,
+ long_string_value = 0x07,
+ byte_string_value = 0x08,
+ array_value = 0x09,
+ empty_object_value = 0x0a,
+ object_value = 0x0b,
+ json_const_pointer = 0x0c
+ };
+
+ template <class CharT>
+ std::basic_ostream<CharT>& operator<<(std::basic_ostream<CharT>& os, json_storage_kind storage)
+ {
+ 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* 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* short_string_value = JSONCONS_CSTRING_CONSTANT(CharT, "short_string");
+ static constexpr const CharT* long_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");
+ 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_pointer = JSONCONS_CSTRING_CONSTANT(CharT, "json_const_pointer");
+
+ switch (storage)
+ {
+ case json_storage_kind::null_value:
+ {
+ os << null_value;
+ break;
+ }
+ case json_storage_kind::bool_value:
+ {
+ os << bool_value;
+ break;
+ }
+ case json_storage_kind::int64_value:
+ {
+ os << int64_value;
+ break;
+ }
+ case json_storage_kind::uint64_value:
+ {
+ os << uint64_value;
+ break;
+ }
+ case json_storage_kind::half_value:
+ {
+ os << half_value;
+ break;
+ }
+ case json_storage_kind::double_value:
+ {
+ os << double_value;
+ break;
+ }
+ case json_storage_kind::short_string_value:
+ {
+ os << short_string_value;
+ break;
+ }
+ case json_storage_kind::long_string_value:
+ {
+ os << long_string_value;
+ break;
+ }
+ case json_storage_kind::byte_string_value:
+ {
+ os << byte_string_value;
+ break;
+ }
+ case json_storage_kind::array_value:
+ {
+ os << array_value;
+ break;
+ }
+ case json_storage_kind::empty_object_value:
+ {
+ os << empty_object_value;
+ break;
+ }
+ case json_storage_kind::object_value:
+ {
+ os << object_value;
+ break;
+ }
+ case json_storage_kind::json_const_pointer:
+ {
+ os << json_const_pointer;
+ break;
+ }
+ }
+ return os;
+ }
+
+} // jsoncons
+
+#endif
diff --git a/include/jsoncons/json_type_traits.hpp b/include/jsoncons/json_type_traits.hpp
new file mode 100644
index 0000000..7ee26b2
--- /dev/null
+++ b/include/jsoncons/json_type_traits.hpp
@@ -0,0 +1,1829 @@
+// Copyright 2013 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_TYPE_TRAITS_HPP
+#define JSONCONS_JSON_TYPE_TRAITS_HPP
+
+#include <chrono>
+#include <array>
+#include <string>
+#include <vector>
+#include <valarray>
+#include <exception>
+#include <cstring>
+#include <utility>
+#include <algorithm> // std::swap
+#include <limits> // std::numeric_limits
+#include <type_traits> // std::enable_if
+#include <iterator> // std::iterator_traits, std::input_iterator_tag
+#include <jsoncons/json_type.hpp>
+#include <jsoncons/bigint.hpp>
+#include <jsoncons/json_visitor.hpp>
+#include <jsoncons/more_type_traits.hpp>
+#include <string>
+#include <tuple>
+#include <map>
+#include <functional>
+#include <memory>
+#include <bitset> // std::bitset
+#include <jsoncons/conv_error.hpp>
+#include <jsoncons/converter.hpp>
+
+#if defined(JSONCONS_HAS_STD_VARIANT)
+ #include <variant>
+#endif
+
+namespace jsoncons {
+
+ template <class T>
+ struct is_json_type_traits_declared : public std::false_type
+ {};
+
+ #if !defined(JSONCONS_NO_DEPRECATED)
+ template <class T>
+ using is_json_type_traits_impl = is_json_type_traits_declared<T>;
+ #endif
+
+ // json_type_traits
+
+ template<typename T>
+ struct unimplemented : std::false_type
+ {};
+
+ template <class Json, class T, class Enable=void>
+ 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<T>::value, "as not implemented");
+ }
+
+ static Json to_json(const T&, const allocator_type& = allocator_type())
+ {
+ static_assert(unimplemented<T>::value, "to_json not implemented");
+ }
+ };
+
+namespace detail {
+
+template<class Json, class T>
+using
+traits_can_convert_t = decltype(json_type_traits<Json,T>::can_convert(Json()));
+
+template<class Json, class T>
+using
+has_can_convert = type_traits::is_detected<traits_can_convert_t, Json, T>;
+
+ template <class T>
+ struct invoke_can_convert
+ {
+ template <class Json>
+ static
+ typename std::enable_if<has_can_convert<Json,T>::value,bool>::type
+ can_convert(const Json& j) noexcept
+ {
+ return json_type_traits<Json,T>::can_convert(j);
+ }
+ template <class Json>
+ static
+ typename std::enable_if<!has_can_convert<Json,T>::value,bool>::type
+ can_convert(const Json& j) noexcept
+ {
+ return json_type_traits<Json,T>::is(j);
+ }
+ };
+
+ // is_json_type_traits_unspecialized
+ template<class Json, class T, class Enable = void>
+ struct is_json_type_traits_unspecialized : std::false_type {};
+
+ // is_json_type_traits_unspecialized
+ template<class Json, class T>
+ struct is_json_type_traits_unspecialized<Json,T,
+ typename std::enable_if<!std::integral_constant<bool, json_type_traits<Json, T>::is_compatible>::value>::type
+ > : std::true_type {};
+
+ // is_compatible_array_type
+ template<class Json, class T, class Enable=void>
+ struct is_compatible_array_type : std::false_type {};
+
+ template<class Json, class T>
+ struct is_compatible_array_type<Json,T,
+ typename std::enable_if<!std::is_same<T,typename Json::array>::value &&
+ type_traits::is_list_like<T>::value &&
+ !is_json_type_traits_unspecialized<Json,typename std::iterator_traits<typename T::iterator>::value_type>::value
+ >::type> : std::true_type {};
+
+} // namespace detail
+
+ // is_json_type_traits_specialized
+ template<class Json, class T, class Enable=void>
+ struct is_json_type_traits_specialized : std::false_type {};
+
+ template<class Json, class T>
+ struct is_json_type_traits_specialized<Json,T,
+ typename std::enable_if<!jsoncons::detail::is_json_type_traits_unspecialized<Json,T>::value
+ >::type> : std::true_type {};
+
+ template<class Json>
+ struct json_type_traits<Json, const typename std::decay<typename Json::char_type>::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 <class ... Args>
+ static Json to_json(const char_type* s, Args&&... args)
+ {
+ return Json(s, semantic_tag::none, std::forward<Args>(args)...);
+ }
+ };
+
+ template<class Json>
+ struct json_type_traits<Json, typename std::decay<typename Json::char_type>::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 <class ... Args>
+ static Json to_json(const char_type* s, Args&&... args)
+ {
+ return Json(s, semantic_tag::none, std::forward<Args>(args)...);
+ }
+ };
+
+ // integer
+
+ template<class Json, class T>
+ struct json_type_traits<Json, T,
+ typename std::enable_if<type_traits::is_integer<T>::value
+ >::type>
+ {
+ using allocator_type = typename Json::allocator_type;
+
+ static bool is(const Json& j) noexcept
+ {
+ return j.template is_integer<T>();
+ }
+ static T as(const Json& j)
+ {
+ return j.template as_integer<T>();
+ }
+
+ static Json to_json(T val, allocator_type alloc = allocator_type())
+ {
+ return Json(val, semantic_tag::none, alloc);
+ }
+ };
+
+ template<class Json, class T>
+ struct json_type_traits<Json, T,
+ typename std::enable_if<std::is_floating_point<T>::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<T>(j.as_double());
+ }
+ static Json to_json(T val, allocator_type = allocator_type())
+ {
+ return Json(val, semantic_tag::none);
+ }
+ };
+
+ template<class Json>
+ struct json_type_traits<Json, typename Json::object>
+ {
+ 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, const allocator_type& = allocator_type())
+ {
+ return Json(o,semantic_tag::none);
+ }
+ };
+
+ template<class Json>
+ struct json_type_traits<Json, typename Json::array>
+ {
+ 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, const allocator_type& = allocator_type())
+ {
+ return Json(a, semantic_tag::none);
+ }
+ };
+
+ template<class Json>
+ struct json_type_traits<Json, Json>
+ {
+ 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, allocator_type = allocator_type())
+ {
+ return val;
+ }
+ };
+
+ template<class Json>
+ struct json_type_traits<Json, jsoncons::null_type>
+ {
+ 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, allocator_type = allocator_type())
+ {
+ return Json::null();
+ }
+ };
+
+ template<class Json>
+ struct json_type_traits<Json, bool>
+ {
+ 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, allocator_type = allocator_type())
+ {
+ return Json(val, semantic_tag::none);
+ }
+ };
+
+ template<class Json, class T>
+ struct json_type_traits<Json, T, typename std::enable_if<std::is_same<T,
+ std::conditional<!std::is_same<bool,std::vector<bool>::const_reference>::value,
+ std::vector<bool>::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, allocator_type = allocator_type())
+ {
+ return Json(val, semantic_tag::none);
+ }
+ };
+
+ template<class Json>
+ struct json_type_traits<Json, std::vector<bool>::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, allocator_type = allocator_type())
+ {
+ return Json(val, semantic_tag::none);
+ }
+ };
+
+ template<class Json, typename T>
+ struct json_type_traits<Json, T,
+ typename std::enable_if<!is_json_type_traits_declared<T>::value &&
+ type_traits::is_basic_string<T>::value &&
+ std::is_same<typename Json::char_type,typename T::value_type>::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<class Json, typename T>
+ struct json_type_traits<Json, T,
+ typename std::enable_if<!is_json_type_traits_declared<T>::value &&
+ type_traits::is_basic_string<T>::value &&
+ !std::is_same<typename Json::char_type,typename T::value_type>::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<char_type> 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<char_type> s;
+ unicode_traits::convert(val.data(), val.size(), s);
+ return Json(s, semantic_tag::none, alloc);
+ }
+ };
+
+ template<class Json, typename T>
+ struct json_type_traits<Json, T,
+ typename std::enable_if<!is_json_type_traits_declared<T>::value &&
+ type_traits::is_basic_string_view<T>::value &&
+ std::is_same<typename Json::char_type,typename T::value_type>::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<class Json, typename T>
+ struct json_type_traits<Json, T,
+ typename std::enable_if<!is_json_type_traits_declared<T>::value &&
+ jsoncons::detail::is_compatible_array_type<Json,T>::value &&
+ type_traits::is_back_insertable<T>::value
+ >::type>
+ {
+ typedef typename std::iterator_traits<typename T::iterator>::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<value_type>())
+ {
+ result = false;
+ break;
+ }
+ }
+ }
+ return result;
+ }
+
+ // array back insertable non-byte container
+
+ template <class Container = T>
+ static typename std::enable_if<!type_traits::is_byte<typename Container::value_type>::value,Container>::type
+ as(const Json& j)
+ {
+ if (j.is_array())
+ {
+ T result;
+ visit_reserve_(typename std::integral_constant<bool, type_traits::has_reserve<T>::value>::type(),result,j.size());
+ for (const auto& item : j.array_range())
+ {
+ result.push_back(item.template as<value_type>());
+ }
+
+ return result;
+ }
+ else
+ {
+ JSONCONS_THROW(conv_error(conv_errc::not_vector));
+ }
+ }
+
+ // array back insertable byte container
+
+ template <class Container = T>
+ static typename std::enable_if<type_traits::is_byte<typename Container::value_type>::value,Container>::type
+ as(const Json& j)
+ {
+ converter<T> convert;
+ std::error_code ec;
+ if (j.is_array())
+ {
+ T result;
+ visit_reserve_(typename std::integral_constant<bool, type_traits::has_reserve<T>::value>::type(),result,j.size());
+ for (const auto& item : j.array_range())
+ {
+ result.push_back(item.template as<value_type>());
+ }
+
+ return result;
+ }
+ else if (j.is_byte_string_view())
+ {
+ auto v = convert.from(j.as_byte_string_view(),j.tag(), ec);
+ if (ec)
+ {
+ JSONCONS_THROW(conv_error(ec));
+ }
+ return v;
+ }
+ else if (j.is_string())
+ {
+ auto v = convert.from(j.as_string_view(),j.tag(), ec);
+ if (ec)
+ {
+ JSONCONS_THROW(conv_error(ec));
+ }
+ return v;
+ }
+ else
+ {
+ JSONCONS_THROW(conv_error(conv_errc::not_vector));
+ }
+ }
+
+ template <class Container = T>
+ static typename std::enable_if<!type_traits::is_std_byte<typename Container::value_type>::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 <class Container = T>
+ static typename std::enable_if<!type_traits::is_std_byte<typename Container::value_type>::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 <class Container = T>
+ static typename std::enable_if<type_traits::is_std_byte<typename Container::value_type>::value,Json>::type
+ to_json(const T& val)
+ {
+ Json j(byte_string_arg, val);
+ return j;
+ }
+
+ template <class Container = T>
+ static typename std::enable_if<type_traits::is_std_byte<typename Container::value_type>::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<class Json, typename T>
+ struct json_type_traits<Json, T,
+ typename std::enable_if<!is_json_type_traits_declared<T>::value &&
+ jsoncons::detail::is_compatible_array_type<Json,T>::value &&
+ !type_traits::is_back_insertable<T>::value &&
+ type_traits::is_insertable<T>::value>::type>
+ {
+ typedef typename std::iterator_traits<typename T::iterator>::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<value_type>())
+ {
+ 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<value_type>());
+ }
+
+ 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<class Json, typename T>
+ struct json_type_traits<Json, T,
+ typename std::enable_if<!is_json_type_traits_declared<T>::value &&
+ jsoncons::detail::is_compatible_array_type<Json,T>::value &&
+ !type_traits::is_back_insertable<T>::value &&
+ !type_traits::is_insertable<T>::value &&
+ type_traits::is_front_insertable<T>::value>::type>
+ {
+ typedef typename std::iterator_traits<typename T::iterator>::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<value_type>())
+ {
+ 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<value_type>());
+ }
+
+ 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<class Json, class E, std::size_t N>
+ struct json_type_traits<Json, std::array<E, N>>
+ {
+ 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<value_type>())
+ {
+ result = false;
+ break;
+ }
+ }
+ }
+ return result;
+ }
+
+ static std::array<E, N> as(const Json& j)
+ {
+ std::array<E, N> 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<E>();
+ }
+ return buff;
+ }
+
+ static Json to_json(const std::array<E, N>& 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<E, N>& 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<class Json, typename T>
+ struct json_type_traits<Json, T,
+ typename std::enable_if<!is_json_type_traits_declared<T>::value &&
+ type_traits::is_map_like<T>::value &&
+ type_traits::is_constructible_from_const_pointer_and_size<typename T::key_type>::value &&
+ is_json_type_traits_specialized<Json,typename T::mapped_type>::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<mapped_type>())
+ {
+ 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<mapped_type>());
+ }
+
+ 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 <class Json, typename T>
+ struct json_type_traits<Json, T,
+ typename std::enable_if<!is_json_type_traits_declared<T>::value &&
+ type_traits::is_map_like<T>::value &&
+ !type_traits::is_constructible_from_const_pointer_and_size<typename T::key_type>::value &&
+ is_json_type_traits_specialized<Json,typename T::key_type>::value &&
+ is_json_type_traits_specialized<Json,typename T::mapped_type>::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<key_type>())
+ {
+ return false;
+ }
+ if (!item.value().template is<mapped_type>())
+ {
+ 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<Json,key_type>::as(j);
+ result.emplace(std::move(key), item.value().template as<mapped_type>());
+ }
+
+ 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<Json,key_type>::to_json(item.first);
+ 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<Json,key_type>::to_json(item.first, alloc);
+ typename Json::key_type key(alloc);
+ temp.dump(key);
+ j.try_emplace(std::move(key), item.second, alloc);
+ }
+ return j;
+ }
+ };
+
+ namespace tuple_detail
+ {
+ template<size_t Pos, std::size_t Size, class Json, class Tuple>
+ struct json_tuple_helper
+ {
+ using element_type = typename std::tuple_element<Size-Pos, Tuple>::type;
+ using next = json_tuple_helper<Pos-1, Size, Json, Tuple>;
+
+ static bool is(const Json& j) noexcept
+ {
+ if (j[Size-Pos].template is<element_type>())
+ {
+ return next::is(j);
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ static void as(Tuple& tuple, const Json& j)
+ {
+ std::get<Size-Pos>(tuple) = j[Size-Pos].template as<element_type>();
+ next::as(tuple, j);
+ }
+
+ static void to_json(const Tuple& tuple, Json& j)
+ {
+ j.push_back(json_type_traits<Json, element_type>::to_json(std::get<Size-Pos>(tuple)));
+ next::to_json(tuple, j);
+ }
+ };
+
+ template<size_t Size, class Json, class Tuple>
+ 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 detail
+
+ template<class Json, typename... E>
+ struct json_type_traits<Json, std::tuple<E...>>
+ {
+ private:
+ using helper = tuple_detail::json_tuple_helper<sizeof...(E), sizeof...(E), Json, std::tuple<E...>>;
+
+ public:
+ using allocator_type = typename Json::allocator_type;
+
+ static bool is(const Json& j) noexcept
+ {
+ return helper::is(j);
+ }
+
+ static std::tuple<E...> as(const Json& j)
+ {
+ std::tuple<E...> buff;
+ helper::as(buff, j);
+ return buff;
+ }
+
+ static Json to_json(const std::tuple<E...>& val)
+ {
+ Json j(json_array_arg);
+ j.reserve(sizeof...(E));
+ helper::to_json(val, j);
+ return j;
+ }
+
+ static Json to_json(const std::tuple<E...>& val,
+ const allocator_type& alloc)
+ {
+ Json j(json_array_arg, alloc);
+ j.reserve(sizeof...(E));
+ helper::to_json(val, j);
+ return j;
+ }
+ };
+
+ template<class Json, class T1, class T2>
+ struct json_type_traits<Json, std::pair<T1,T2>>
+ {
+ 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<T1,T2> as(const Json& j)
+ {
+ return std::make_pair<T1,T2>(j[0].template as<T1>(),j[1].template as<T2>());
+ }
+
+ static Json to_json(const std::pair<T1,T2>& 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<T1, T2>& 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<class Json, class T>
+ struct json_type_traits<Json, T,
+ typename std::enable_if<type_traits::is_basic_byte_string<T>::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<typename T::allocator_type>();
+ }
+
+ static Json to_json(const T& val,
+ const allocator_type& alloc = allocator_type())
+ {
+ return Json(byte_string_arg, val, semantic_tag::none, alloc);
+ }
+ };
+
+ template<class Json, class ValueType>
+ struct json_type_traits<Json, std::shared_ptr<ValueType>,
+ typename std::enable_if<!is_json_type_traits_declared<std::shared_ptr<ValueType>>::value &&
+ !std::is_polymorphic<ValueType>::value
+ >::type>
+ {
+ static bool is(const Json& j) noexcept
+ {
+ return j.is_null() || j.template is<ValueType>();
+ }
+
+ static std::shared_ptr<ValueType> as(const Json& j)
+ {
+ return j.is_null() ? std::shared_ptr<ValueType>(nullptr) : std::make_shared<ValueType>(j.template as<ValueType>());
+ }
+
+ static Json to_json(const std::shared_ptr<ValueType>& ptr)
+ {
+ if (ptr.get() != nullptr)
+ {
+ Json j(*ptr);
+ return j;
+ }
+ else
+ {
+ return Json::null();
+ }
+ }
+ };
+
+ template<class Json, class ValueType>
+ struct json_type_traits<Json, std::unique_ptr<ValueType>,
+ typename std::enable_if<!is_json_type_traits_declared<std::unique_ptr<ValueType>>::value &&
+ !std::is_polymorphic<ValueType>::value
+ >::type>
+ {
+ static bool is(const Json& j) noexcept
+ {
+ return j.is_null() || j.template is<ValueType>();
+ }
+
+ static std::unique_ptr<ValueType> as(const Json& j)
+ {
+ return j.is_null() ? std::unique_ptr<ValueType>(nullptr) : jsoncons::make_unique<ValueType>(j.template as<ValueType>());
+ }
+
+ static Json to_json(const std::unique_ptr<ValueType>& ptr)
+ {
+ if (ptr.get() != nullptr)
+ {
+ Json j(*ptr);
+ return j;
+ }
+ else
+ {
+ return Json::null();
+ }
+ }
+ };
+
+ template<class Json, class T>
+ struct json_type_traits<Json, jsoncons::optional<T>,
+ typename std::enable_if<!is_json_type_traits_declared<jsoncons::optional<T>>::value>::type>
+ {
+ public:
+ static bool is(const Json& j) noexcept
+ {
+ return j.is_null() || j.template is<T>();
+ }
+
+ static jsoncons::optional<T> as(const Json& j)
+ {
+ return j.is_null() ? jsoncons::optional<T>() : jsoncons::optional<T>(j.template as<T>());
+ }
+
+ static Json to_json(const jsoncons::optional<T>& val)
+ {
+ return val.has_value() ? Json(*val) : Json::null();
+ }
+ };
+
+ template<class Json>
+ struct json_type_traits<Json, byte_string_view>
+ {
+ 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<class Json, class Allocator>
+ struct json_type_traits<Json, basic_bigint<Allocator>>
+ {
+ public:
+ 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<Allocator> 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<Allocator>::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<Allocator>(j.template as<int64_t>());
+ case json_type::int64_value:
+ return basic_bigint<Allocator>(j.template as<int64_t>());
+ case json_type::uint64_value:
+ return basic_bigint<Allocator>(j.template as<uint64_t>());
+ default:
+ JSONCONS_THROW(conv_error(conv_errc::not_bigint));
+ }
+ }
+
+ static Json to_json(const basic_bigint<Allocator>& val)
+ {
+ std::basic_string<char_type> s;
+ val.write_string(s);
+ return Json(s,semantic_tag::bigint);
+ }
+ };
+
+ // std::valarray
+
+ template<class Json, class T>
+ struct json_type_traits<Json, std::valarray<T>>
+ {
+ 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<T>())
+ {
+ result = false;
+ break;
+ }
+ }
+ }
+ return result;
+ }
+
+ static std::valarray<T> as(const Json& j)
+ {
+ if (j.is_array())
+ {
+ std::valarray<T> v(j.size());
+ for (std::size_t i = 0; i < j.size(); ++i)
+ {
+ v[i] = j[i].template as<T>();
+ }
+ return v;
+ }
+ else
+ {
+ JSONCONS_THROW(conv_error(conv_errc::not_array));
+ }
+ }
+
+ static Json to_json(const std::valarray<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 std::valarray<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;
+ }
+ };
+
+#if defined(JSONCONS_HAS_STD_VARIANT)
+
+namespace variant_detail
+{
+ template<int N, class Json, class Variant, class ... Args>
+ typename std::enable_if<N == std::variant_size_v<Variant>, bool>::type
+ is_variant(const Json& /*j*/)
+ {
+ return false;
+ }
+
+ template<std::size_t N, class Json, class Variant, class T, class ... U>
+ typename std::enable_if<N < std::variant_size_v<Variant>, bool>::type
+ is_variant(const Json& j)
+ {
+ if (j.template is<T>())
+ {
+ return true;
+ }
+ else
+ {
+ return is_variant<N+1, Json, Variant, U...>(j);
+ }
+ }
+
+ template<int N, class Json, class Variant, class ... Args>
+ typename std::enable_if<N == std::variant_size_v<Variant>, Variant>::type
+ as_variant(const Json& /*j*/)
+ {
+ JSONCONS_THROW(conv_error(conv_errc::not_variant));
+ }
+
+ template<std::size_t N, class Json, class Variant, class T, class ... U>
+ typename std::enable_if<N < std::variant_size_v<Variant>, Variant>::type
+ as_variant(const Json& j)
+ {
+ if (j.template is<T>())
+ {
+ Variant var(j.template as<T>());
+ return var;
+ }
+ else
+ {
+ return as_variant<N+1, Json, Variant, U...>(j);
+ }
+ }
+
+ template <class Json>
+ struct variant_to_json_visitor
+ {
+ Json& j_;
+
+ variant_to_json_visitor(Json& j) : j_(j) {}
+
+ template<class T>
+ void operator()(const T& value) const
+ {
+ j_ = value;
+ }
+ };
+
+} // namespace variant_detail
+
+ template<class Json, typename... VariantTypes>
+ struct json_type_traits<Json, std::variant<VariantTypes...>>
+ {
+ public:
+ using variant_type = typename std::variant<VariantTypes...>;
+ 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<VariantTypes...> as(const Json& j)
+ {
+ return variant_detail::as_variant<0,Json,variant_type, VariantTypes...>(j);
+ }
+
+ static Json to_json(const std::variant<VariantTypes...>& var)
+ {
+ Json j(json_array_arg);
+ variant_detail::variant_to_json_visitor<Json> visitor(j);
+ std::visit(visitor, var);
+ return j;
+ }
+
+ static Json to_json(const std::variant<VariantTypes...>& var,
+ const allocator_type& alloc)
+ {
+ Json j(json_array_arg, alloc);
+ variant_detail::variant_to_json_visitor<Json> visitor(j);
+ std::visit(visitor, var);
+ return j;
+ }
+ };
+#endif
+
+ // std::chrono::duration
+ template<class Json,class Rep,class Period>
+ struct json_type_traits<Json,std::chrono::duration<Rep,Period>>
+ {
+ using duration_type = std::chrono::duration<Rep,Period>;
+
+ 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, allocator_type = allocator_type())
+ {
+ return to_json_(val);
+ }
+
+ template <class PeriodT=Period>
+ static
+ typename std::enable_if<std::is_same<PeriodT,std::ratio<1>>::value, duration_type>::type
+ from_json_(const Json& j)
+ {
+ if (j.is_int64() || j.is_uint64() || j.is_double())
+ {
+ auto count = j.template as<Rep>();
+ 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<Rep>();
+ 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<Rep>(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<Rep>(n));
+ }
+ default:
+ {
+ auto count = j.template as<Rep>();
+ return duration_type(count);
+ }
+ }
+ }
+ else
+ {
+ return duration_type();
+ }
+ }
+
+ template <class PeriodT=Period>
+ static
+ typename std::enable_if<std::is_same<PeriodT,std::milli>::value, duration_type>::type
+ from_json_(const Json& j)
+ {
+ if (j.is_int64() || j.is_uint64())
+ {
+ auto count = j.template as<Rep>();
+ 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<double>();
+ switch (j.tag())
+ {
+ case semantic_tag::epoch_second:
+ return duration_type(static_cast<Rep>(count * millis_in_second));
+ case semantic_tag::epoch_milli:
+ return duration_type(static_cast<Rep>(count));
+ case semantic_tag::epoch_nano:
+ return duration_type(count == 0 ? 0 : static_cast<Rep>(count / nanos_in_milli));
+ default:
+ return duration_type(static_cast<Rep>(count));
+ }
+ }
+ else if (j.is_string())
+ {
+ switch (j.tag())
+ {
+ case semantic_tag::epoch_second:
+ {
+ auto count = j.template as<Rep>();
+ 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::to_integer_decimal(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<Rep>(n));
+ }
+ default:
+ {
+ auto count = j.template as<Rep>();
+ return duration_type(count);
+ }
+ }
+ }
+ else
+ {
+ return duration_type();
+ }
+ }
+
+ template <class PeriodT=Period>
+ static
+ typename std::enable_if<std::is_same<PeriodT,std::nano>::value, duration_type>::type
+ from_json_(const Json& j)
+ {
+ if (j.is_int64() || j.is_uint64() || j.is_double())
+ {
+ auto count = j.template as<Rep>();
+ 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<double>();
+ switch (j.tag())
+ {
+ case semantic_tag::epoch_second:
+ return duration_type(static_cast<Rep>(count * nanos_in_second));
+ case semantic_tag::epoch_milli:
+ return duration_type(static_cast<Rep>(count * nanos_in_milli));
+ case semantic_tag::epoch_nano:
+ return duration_type(static_cast<Rep>(count));
+ default:
+ return duration_type(static_cast<Rep>(count));
+ }
+ }
+ else if (j.is_string())
+ {
+ auto count = j.template as<Rep>();
+ 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 <class PeriodT=Period>
+ static
+ typename std::enable_if<std::is_same<PeriodT,std::ratio<1>>::value,Json>::type
+ to_json_(const duration_type& val)
+ {
+ return Json(val.count(), semantic_tag::epoch_second);
+ }
+
+ template <class PeriodT=Period>
+ static
+ typename std::enable_if<std::is_same<PeriodT,std::milli>::value,Json>::type
+ to_json_(const duration_type& val)
+ {
+ return Json(val.count(), semantic_tag::epoch_milli);
+ }
+
+ template <class PeriodT=Period>
+ static
+ typename std::enable_if<std::is_same<PeriodT,std::nano>::value,Json>::type
+ to_json_(const duration_type& val)
+ {
+ return Json(val.count(), semantic_tag::epoch_nano);
+ }
+ };
+
+ // std::nullptr_t
+ template <class Json>
+ struct json_type_traits<Json,std::nullptr_t>
+ {
+ 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&, 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<class Json, std::size_t N>
+ struct json_type_traits<Json, std::bitset<N>>
+ {
+ 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<N> as(const Json& j)
+ {
+ if (j.template is<uint64_t>())
+ {
+ auto bits = j.template as<uint64_t>();
+ std::bitset<N> bs = static_cast<unsigned long long>(bits);
+ return bs;
+ }
+ else if (j.is_byte_string() || j.is_string())
+ {
+ std::bitset<N> bs;
+ std::vector<uint8_t> bits;
+ if (j.is_byte_string())
+ {
+ bits = j.template as<std::vector<uint8_t>>();
+ }
+ 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<std::uint8_t>(mask >> 1);
+ }
+ return bs;
+ }
+ else
+ {
+ JSONCONS_THROW(conv_error(conv_errc::not_bitset));
+ }
+ }
+
+ static Json to_json(const std::bitset<N>& val,
+ const allocator_type& alloc = allocator_type())
+ {
+ std::vector<uint8_t> bits;
+
+ 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<uint8_t>(mask >> 1);
+
+ if (mask == 0)
+ {
+ bits.push_back(byte);
+ byte = 0;
+ mask = 0x80;
+ }
+ }
+
+ // Encode remainder
+ if (mask != 0x80)
+ {
+ bits.push_back(byte);
+ }
+
+ Json j(byte_string_arg, bits, semantic_tag::base16, alloc);
+ return j;
+ }
+ };
+
+} // jsoncons
+
+#endif
diff --git a/include/jsoncons/json_visitor.hpp b/include/jsoncons/json_visitor.hpp
new file mode 100644
index 0000000..8be5900
--- /dev/null
+++ b/include/jsoncons/json_visitor.hpp
@@ -0,0 +1,1560 @@
+// 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)
+
+// See https://github.com/danielaparker/jsoncons for latest version
+
+#ifndef JSONCONS_JSON_VISITOR_HPP
+#define JSONCONS_JSON_VISITOR_HPP
+
+#include <iostream>
+#include <string>
+#include <utility>
+#include <jsoncons/json_exception.hpp>
+#include <jsoncons/bigint.hpp>
+#include <jsoncons/ser_context.hpp>
+#include <jsoncons/json_options.hpp>
+#include <jsoncons/config/jsoncons_config.hpp>
+#include <jsoncons/tag_type.hpp>
+#include <jsoncons/byte_string.hpp>
+
+namespace jsoncons {
+
+ template <class CharT>
+ class basic_json_visitor
+ {
+ public:
+ using char_type = CharT;
+ using char_traits_type = std::char_traits<char_type>;
+
+ using string_view_type = jsoncons::basic_string_view<char_type,char_traits_type>;
+
+ basic_json_visitor(basic_json_visitor&&) = default;
+
+ basic_json_visitor& operator=(basic_json_visitor&&) = default;
+
+ basic_json_visitor() = default;
+
+ virtual ~basic_json_visitor() noexcept = default;
+
+ void flush()
+ {
+ visit_flush();
+ }
+
+ bool begin_object(semantic_tag tag=semantic_tag::none,
+ const ser_context& context=ser_context())
+ {
+ std::error_code ec;
+ bool more = visit_begin_object(tag, context, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, context.line(), context.column()));
+ }
+ return more;
+ }
+
+ bool begin_object(std::size_t length,
+ semantic_tag tag=semantic_tag::none,
+ const ser_context& context = ser_context())
+ {
+ std::error_code ec;
+ bool more = visit_begin_object(length, tag, context, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, context.line(), context.column()));
+ }
+ return more;
+ }
+
+ bool end_object(const ser_context& context = ser_context())
+ {
+ std::error_code ec;
+ bool more = visit_end_object(context, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, context.line(), context.column()));
+ }
+ return more;
+ }
+
+ bool begin_array(semantic_tag tag=semantic_tag::none,
+ const ser_context& context=ser_context())
+ {
+ std::error_code ec;
+ bool more = visit_begin_array(tag, context, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, context.line(), context.column()));
+ }
+ return more;
+ }
+
+ bool begin_array(std::size_t length,
+ semantic_tag tag=semantic_tag::none,
+ const ser_context& context=ser_context())
+ {
+ std::error_code ec;
+ bool more = visit_begin_array(length, tag, context, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, context.line(), context.column()));
+ }
+ return more;
+ }
+
+ bool end_array(const ser_context& context=ser_context())
+ {
+ std::error_code ec;
+ bool more = visit_end_array(context, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, context.line(), context.column()));
+ }
+ return more;
+ }
+
+ bool key(const string_view_type& name, const ser_context& context=ser_context())
+ {
+ std::error_code ec;
+ bool more = visit_key(name, context, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, context.line(), context.column()));
+ }
+ return more;
+ }
+
+ bool null_value(semantic_tag tag = semantic_tag::none,
+ const ser_context& context=ser_context())
+ {
+ std::error_code ec;
+ bool more = visit_null(tag, context, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, context.line(), context.column()));
+ }
+ return more;
+ }
+
+ bool bool_value(bool value,
+ semantic_tag tag = semantic_tag::none,
+ const ser_context& context=ser_context())
+ {
+ std::error_code ec;
+ bool more = visit_bool(value, tag, context, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, context.line(), context.column()));
+ }
+ return more;
+ }
+
+ bool string_value(const string_view_type& value,
+ semantic_tag tag = semantic_tag::none,
+ const ser_context& context=ser_context())
+ {
+ std::error_code ec;
+ bool more = visit_string(value, tag, context, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, context.line(), context.column()));
+ }
+ return more;
+ }
+
+ template <class Source>
+ bool byte_string_value(const Source& b,
+ semantic_tag tag=semantic_tag::none,
+ const ser_context& context=ser_context(),
+ typename std::enable_if<type_traits::is_byte_sequence<Source>::value,int>::type = 0)
+ {
+ std::error_code ec;
+ bool more = visit_byte_string(byte_string_view(reinterpret_cast<const uint8_t*>(b.data()),b.size()), tag, context, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, context.line(), context.column()));
+ }
+ return more;
+ }
+
+ template <class Source>
+ bool byte_string_value(const Source& b,
+ uint64_t ext_tag,
+ const ser_context& context=ser_context(),
+ typename std::enable_if<type_traits::is_byte_sequence<Source>::value,int>::type = 0)
+ {
+ std::error_code ec;
+ bool more = visit_byte_string(byte_string_view(reinterpret_cast<const uint8_t*>(b.data()),b.size()), ext_tag, context, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, context.line(), context.column()));
+ }
+ return more;
+ }
+
+ bool uint64_value(uint64_t value,
+ semantic_tag tag = semantic_tag::none,
+ const ser_context& context=ser_context())
+ {
+ std::error_code ec;
+ bool more = visit_uint64(value, tag, context, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, context.line(), context.column()));
+ }
+ return more;
+ }
+
+ bool int64_value(int64_t value,
+ semantic_tag tag = semantic_tag::none,
+ const ser_context& context=ser_context())
+ {
+ std::error_code ec;
+ bool more = visit_int64(value, tag, context, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, context.line(), context.column()));
+ }
+ return more;
+ }
+
+ bool half_value(uint16_t value,
+ semantic_tag tag = semantic_tag::none,
+ const ser_context& context=ser_context())
+ {
+ std::error_code ec;
+ bool more = visit_half(value, tag, context, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, context.line(), context.column()));
+ }
+ return more;
+ }
+
+ bool double_value(double value,
+ semantic_tag tag = semantic_tag::none,
+ const ser_context& context=ser_context())
+ {
+ std::error_code ec;
+ bool more = visit_double(value, tag, context, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, context.line(), context.column()));
+ }
+ return more;
+ }
+
+ bool begin_object(semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ return visit_begin_object(tag, context, ec);
+ }
+
+ bool begin_object(std::size_t length,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ return visit_begin_object(length, tag, context, ec);
+ }
+
+ bool end_object(const ser_context& context, std::error_code& ec)
+ {
+ return visit_end_object(context, ec);
+ }
+
+ bool begin_array(semantic_tag tag, const ser_context& context, std::error_code& ec)
+ {
+ return visit_begin_array(tag, context, ec);
+ }
+
+ bool begin_array(std::size_t length, semantic_tag tag, const ser_context& context, std::error_code& ec)
+ {
+ return visit_begin_array(length, tag, context, ec);
+ }
+
+ bool end_array(const ser_context& context, std::error_code& ec)
+ {
+ return visit_end_array(context, ec);
+ }
+
+ bool key(const string_view_type& name, const ser_context& context, std::error_code& ec)
+ {
+ return visit_key(name, context, ec);
+ }
+
+ bool null_value(semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ return visit_null(tag, context, ec);
+ }
+
+ bool bool_value(bool value,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ return visit_bool(value, tag, context, ec);
+ }
+
+ bool string_value(const string_view_type& value,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ return visit_string(value, tag, context, ec);
+ }
+
+ template <class Source>
+ bool byte_string_value(const Source& b,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec,
+ typename std::enable_if<type_traits::is_byte_sequence<Source>::value,int>::type = 0)
+ {
+ return visit_byte_string(byte_string_view(reinterpret_cast<const uint8_t*>(b.data()),b.size()), tag, context, ec);
+ }
+
+ template <class Source>
+ bool byte_string_value(const Source& b,
+ uint64_t ext_tag,
+ const ser_context& context,
+ std::error_code& ec,
+ typename std::enable_if<type_traits::is_byte_sequence<Source>::value,int>::type = 0)
+ {
+ return visit_byte_string(byte_string_view(reinterpret_cast<const uint8_t*>(b.data()),b.size()), ext_tag, context, ec);
+ }
+
+ bool uint64_value(uint64_t value,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ return visit_uint64(value, tag, context, ec);
+ }
+
+ bool int64_value(int64_t value,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ return visit_int64(value, tag, context, ec);
+ }
+
+ bool half_value(uint16_t value,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ return visit_half(value, tag, context, ec);
+ }
+
+ bool double_value(double value,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ return visit_double(value, tag, context, ec);
+ }
+
+ template <class T>
+ bool typed_array(const jsoncons::span<T>& data,
+ semantic_tag tag=semantic_tag::none,
+ const ser_context& context=ser_context())
+ {
+ std::error_code ec;
+ bool more = visit_typed_array(data, tag, context, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, context.line(), context.column()));
+ }
+ return more;
+ }
+
+ template <class T>
+ bool typed_array(const jsoncons::span<T>& data,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ return visit_typed_array(data, tag, context, ec);
+ }
+
+ bool typed_array(half_arg_t, const jsoncons::span<const uint16_t>& s,
+ semantic_tag tag = semantic_tag::none,
+ const ser_context& context = ser_context())
+ {
+ std::error_code ec;
+ bool more = visit_typed_array(half_arg, s, tag, context, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, context.line(), context.column()));
+ }
+ return more;
+ }
+
+ bool typed_array(half_arg_t, const jsoncons::span<const uint16_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ return visit_typed_array(half_arg, s, tag, context, ec);
+ }
+
+ bool begin_multi_dim(const jsoncons::span<const size_t>& shape,
+ semantic_tag tag = semantic_tag::multi_dim_row_major,
+ const ser_context& context=ser_context())
+ {
+ std::error_code ec;
+ bool more = visit_begin_multi_dim(shape, tag, context, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, context.line(), context.column()));
+ }
+ return more;
+ }
+
+ bool begin_multi_dim(const jsoncons::span<const size_t>& shape,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ return visit_begin_multi_dim(shape, tag, context, ec);
+ }
+
+ bool end_multi_dim(const ser_context& context=ser_context())
+ {
+ std::error_code ec;
+ bool more = visit_end_multi_dim(context, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, context.line(), context.column()));
+ }
+ return more;
+ }
+
+ bool end_multi_dim(const ser_context& context,
+ std::error_code& ec)
+ {
+ return visit_end_multi_dim(context, ec);
+ }
+
+ #if !defined(JSONCONS_NO_DEPRECATED)
+
+ JSONCONS_DEPRECATED_MSG("Instead, use byte_string_value(const Source&,semantic_tag=semantic_tag::none, const ser_context&=ser_context()")
+ bool byte_string_value(const uint8_t* p, std::size_t size,
+ semantic_tag tag=semantic_tag::none,
+ const ser_context& context=ser_context())
+ {
+ return byte_string_value(byte_string(p, size), tag, context);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use byte_string_value(const Source&, semantic_tag, const ser_context&, std::error_code&")
+ bool byte_string_value(const uint8_t* p, std::size_t size,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ return byte_string_value(byte_string(p, size), tag, context, ec);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use key(const string_view_type&, const ser_context&=ser_context())")
+ bool name(const string_view_type& name, const ser_context& context=ser_context())
+ {
+ return key(name, context);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use key(const string_view_type&, const ser_context&, std::error_code&)")
+ bool name(const string_view_type& name, const ser_context& context, std::error_code& ec)
+ {
+ return key(name, context, ec);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use byte_string_value(const byte_string_view&, semantic_tag=semantic_tag::none, const ser_context&=ser_context()")
+ bool byte_string_value(const byte_string_view& b,
+ byte_string_chars_format encoding_hint,
+ semantic_tag tag=semantic_tag::none,
+ const ser_context& context=ser_context())
+ {
+ switch (encoding_hint)
+ {
+ case byte_string_chars_format::base16:
+ tag = semantic_tag::base16;
+ break;
+ case byte_string_chars_format::base64:
+ tag = semantic_tag::base64;
+ break;
+ case byte_string_chars_format::base64url:
+ tag = semantic_tag::base64url;
+ break;
+ default:
+ break;
+ }
+ return byte_string_value(b, tag, context);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use byte_string_value(const byte_string_view&, semantic_tag=semantic_tag::none, const ser_context&=ser_context()")
+ bool byte_string_value(const uint8_t* p, std::size_t size,
+ byte_string_chars_format encoding_hint,
+ semantic_tag tag=semantic_tag::none,
+ const ser_context& context=ser_context())
+ {
+ switch (encoding_hint)
+ {
+ case byte_string_chars_format::base16:
+ tag = semantic_tag::base16;
+ break;
+ case byte_string_chars_format::base64:
+ tag = semantic_tag::base64;
+ break;
+ case byte_string_chars_format::base64url:
+ tag = semantic_tag::base64url;
+ break;
+ default:
+ break;
+ }
+ return byte_string_value(byte_string(p, size), tag, context);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use string_value with semantic_tag::bigint")
+ bool big_integer_value(const string_view_type& value, const ser_context& context=ser_context())
+ {
+ return string_value(value, semantic_tag::bigint, context);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use string_value with semantic_tag::bigdec")
+ bool big_decimal_value(const string_view_type& value, const ser_context& context=ser_context())
+ {
+ return string_value(value, semantic_tag::bigdec, context);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use string_value with semantic_tag::datetime")
+ bool date_time_value(const string_view_type& value, const ser_context& context=ser_context())
+ {
+ return string_value(value, semantic_tag::datetime, context);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use int64_value with semantic_tag::epoch_second")
+ bool timestamp_value(int64_t val, const ser_context& context=ser_context())
+ {
+ return int64_value(val, semantic_tag::epoch_second, context);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Remove calls to this method, it doesn't do anything")
+ bool begin_document()
+ {
+ return true;
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use flush() when serializing")
+ bool end_document()
+ {
+ flush();
+ return true;
+ }
+
+ JSONCONS_DEPRECATED_MSG("Remove calls to this method, it doesn't do anything")
+ void begin_json()
+ {
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use flush() when serializing")
+ void end_json()
+ {
+ end_document();
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use key(const string_view_type&, const ser_context&=ser_context())")
+ void name(const char_type* p, std::size_t length, const ser_context& context)
+ {
+ name(string_view_type(p, length), context);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use uint64_value(uint64_t, semantic_tag = semantic_tag::none, const ser_context&=ser_context())")
+ void integer_value(int64_t value)
+ {
+ int64_value(value);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use int64_value(int64_t, semantic_tag = semantic_tag::none, const ser_context&=ser_context())")
+ void integer_value(int64_t value, const ser_context& context)
+ {
+ int64_value(value,context);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use uint64_value(uint64_t, semantic_tag = semantic_tag::none, const ser_context&=ser_context())")
+ void uinteger_value(uint64_t value)
+ {
+ uint64_value(value);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use uint64_value(uint64_t, semantic_tag = semantic_tag::none, const ser_context&=ser_context())")
+ void uinteger_value(uint64_t value, const ser_context& context)
+ {
+ uint64_value(value,context);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use string_value with semantic_tag::bigint")
+ bool bignum_value(const string_view_type& value, const ser_context& context=ser_context())
+ {
+ return string_value(value, semantic_tag::bigint, context);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use string_value with semantic_tag::bigdec")
+ bool decimal_value(const string_view_type& value, const ser_context& context=ser_context())
+ {
+ return string_value(value, semantic_tag::bigdec, context);
+ }
+
+ #endif
+ private:
+
+ virtual void visit_flush() = 0;
+
+ virtual bool visit_begin_object(semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) = 0;
+
+ virtual bool visit_begin_object(std::size_t /*length*/,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ return visit_begin_object(tag, context, ec);
+ }
+
+ virtual bool visit_end_object(const ser_context& context,
+ std::error_code& ec) = 0;
+
+ virtual bool visit_begin_array(semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) = 0;
+
+ virtual bool visit_begin_array(std::size_t /*length*/,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ return visit_begin_array(tag, context, ec);
+ }
+
+ virtual bool visit_end_array(const ser_context& context,
+ std::error_code& ec) = 0;
+
+ virtual bool visit_key(const string_view_type& name,
+ const ser_context& context,
+ std::error_code&) = 0;
+
+ virtual bool visit_null(semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) = 0;
+
+ virtual bool visit_bool(bool value,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code&) = 0;
+
+ virtual bool visit_string(const string_view_type& value,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) = 0;
+
+ virtual bool visit_byte_string(const byte_string_view& value,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) = 0;
+
+ virtual bool visit_byte_string(const byte_string_view& value,
+ uint64_t /* ext_tag */,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ return visit_byte_string(value, semantic_tag::none, context, ec);
+ }
+
+ virtual bool visit_uint64(uint64_t value,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) = 0;
+
+ virtual bool visit_int64(int64_t value,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) = 0;
+
+ virtual bool visit_half(uint16_t value,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ return visit_double(binary::decode_half(value),
+ tag,
+ context,
+ ec);
+ }
+
+ virtual bool visit_double(double value,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) = 0;
+
+ virtual bool visit_typed_array(const jsoncons::span<const uint8_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ bool more = begin_array(s.size(), tag, context, ec);
+ for (auto p = s.begin(); more && p != s.end(); ++p)
+ {
+ more = uint64_value(*p, semantic_tag::none, context, ec);
+ }
+ if (more)
+ {
+ more = end_array(context, ec);
+ }
+ return more;
+ }
+
+ virtual bool visit_typed_array(const jsoncons::span<const uint16_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ bool more = begin_array(s.size(), tag, context, ec);
+ for (auto p = s.begin(); more && p != s.end(); ++p)
+ {
+ more = uint64_value(*p, semantic_tag::none, context, ec);
+ }
+ if (more)
+ {
+ more = end_array(context, ec);
+ }
+ return more;
+ }
+
+ virtual bool visit_typed_array(const jsoncons::span<const uint32_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ bool more = begin_array(s.size(), tag, context, ec);
+ for (auto p = s.begin(); more && p != s.end(); ++p)
+ {
+ more = uint64_value(*p, semantic_tag::none, context, ec);
+ }
+ if (more)
+ {
+ more = end_array(context, ec);
+ }
+ return more;
+ }
+
+ virtual bool visit_typed_array(const jsoncons::span<const uint64_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ bool more = begin_array(s.size(), tag, context, ec);
+ for (auto p = s.begin(); more && p != s.end(); ++p)
+ {
+ more = uint64_value(*p,semantic_tag::none,context, ec);
+ }
+ if (more)
+ {
+ more = end_array(context, ec);
+ }
+ return more;
+ }
+
+ virtual bool visit_typed_array(const jsoncons::span<const int8_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ bool more = begin_array(s.size(), tag,context, ec);
+ for (auto p = s.begin(); more && p != s.end(); ++p)
+ {
+ more = int64_value(*p,semantic_tag::none,context, ec);
+ }
+ if (more)
+ {
+ more = end_array(context, ec);
+ }
+ return more;
+ }
+
+ virtual bool visit_typed_array(const jsoncons::span<const int16_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ bool more = begin_array(s.size(), tag,context, ec);
+ for (auto p = s.begin(); more && p != s.end(); ++p)
+ {
+ more = int64_value(*p,semantic_tag::none,context, ec);
+ }
+ if (more)
+ {
+ more = end_array(context, ec);
+ }
+ return more;
+ }
+
+ virtual bool visit_typed_array(const jsoncons::span<const int32_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ bool more = begin_array(s.size(), tag,context, ec);
+ for (auto p = s.begin(); more && p != s.end(); ++p)
+ {
+ more = int64_value(*p,semantic_tag::none,context, ec);
+ }
+ if (more)
+ {
+ more = end_array(context, ec);
+ }
+ return more;
+ }
+
+ virtual bool visit_typed_array(const jsoncons::span<const int64_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ bool more = begin_array(s.size(), tag,context, ec);
+ for (auto p = s.begin(); more && p != s.end(); ++p)
+ {
+ more = int64_value(*p,semantic_tag::none,context, ec);
+ }
+ if (more)
+ {
+ more = end_array(context, ec);
+ }
+ return more;
+ }
+
+ virtual bool visit_typed_array(half_arg_t,
+ const jsoncons::span<const uint16_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ bool more = begin_array(s.size(), tag, context, ec);
+ for (auto p = s.begin(); more && p != s.end(); ++p)
+ {
+ more = half_value(*p, semantic_tag::none, context, ec);
+ }
+ if (more)
+ {
+ more = end_array(context, ec);
+ }
+ return more;
+ }
+
+ virtual bool visit_typed_array(const jsoncons::span<const float>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ bool more = begin_array(s.size(), tag,context, ec);
+ for (auto p = s.begin(); more && p != s.end(); ++p)
+ {
+ more = double_value(*p,semantic_tag::none,context, ec);
+ }
+ if (more)
+ {
+ more = end_array(context, ec);
+ }
+ return more;
+ }
+
+ virtual bool visit_typed_array(const jsoncons::span<const double>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ bool more = begin_array(s.size(), tag,context, ec);
+ for (auto p = s.begin(); more && p != s.end(); ++p)
+ {
+ more = double_value(*p,semantic_tag::none,context, ec);
+ }
+ if (more)
+ {
+ more = end_array(context, ec);
+ }
+ return more;
+ }
+
+ virtual bool visit_begin_multi_dim(const jsoncons::span<const size_t>& shape,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ bool more = visit_begin_array(2, tag, context, ec);
+ if (more)
+ {
+ more = visit_begin_array(shape.size(), tag, context, ec);
+ for (auto it = shape.begin(); more && it != shape.end(); ++it)
+ {
+ visit_uint64(*it, semantic_tag::none, context, ec);
+ }
+ if (more)
+ {
+ more = visit_end_array(context, ec);
+ }
+ }
+ return more;
+ }
+
+ virtual bool visit_end_multi_dim(const ser_context& context,
+ std::error_code& ec)
+ {
+ return visit_end_array(context, ec);
+ }
+ };
+
+ template <class CharT>
+ class basic_default_json_visitor : public basic_json_visitor<CharT>
+ {
+ bool parse_more_;
+ std::error_code ec_;
+ public:
+ using typename basic_json_visitor<CharT>::string_view_type;
+
+ basic_default_json_visitor(bool accept_more = true,
+ std::error_code ec = std::error_code())
+ : parse_more_(accept_more), ec_(ec)
+ {
+ }
+ private:
+ void visit_flush() override
+ {
+ }
+
+ bool visit_begin_object(semantic_tag, const ser_context&, std::error_code& ec) override
+ {
+ if (ec_)
+ {
+ ec = ec_;
+ }
+ return parse_more_;
+ }
+
+ bool visit_end_object(const ser_context&, std::error_code& ec) override
+ {
+ if (ec_)
+ {
+ ec = ec_;
+ }
+ return parse_more_;
+ }
+
+ bool visit_begin_array(semantic_tag, const ser_context&, std::error_code& ec) override
+ {
+ if (ec_)
+ {
+ ec = ec_;
+ }
+ return parse_more_;
+ }
+
+ bool visit_end_array(const ser_context&, std::error_code& ec) override
+ {
+ if (ec_)
+ {
+ ec = ec_;
+ }
+ return parse_more_;
+ }
+
+ bool visit_key(const string_view_type&, const ser_context&, std::error_code& ec) override
+ {
+ if (ec_)
+ {
+ ec = ec_;
+ }
+ return parse_more_;
+ }
+
+ bool visit_null(semantic_tag, const ser_context&, std::error_code& ec) override
+ {
+ if (ec_)
+ {
+ ec = ec_;
+ }
+ return parse_more_;
+ }
+
+ bool visit_string(const string_view_type&, semantic_tag, const ser_context&, std::error_code& ec) override
+ {
+ if (ec_)
+ {
+ ec = ec_;
+ }
+ return parse_more_;
+ }
+
+ bool visit_byte_string(const byte_string_view&, semantic_tag, const ser_context&, std::error_code& ec) override
+ {
+ if (ec_)
+ {
+ ec = ec_;
+ }
+ return parse_more_;
+ }
+
+ bool visit_uint64(uint64_t, semantic_tag, const ser_context&, std::error_code& ec) override
+ {
+ if (ec_)
+ {
+ ec = ec_;
+ }
+ return parse_more_;
+ }
+
+ bool visit_int64(int64_t, semantic_tag, const ser_context&, std::error_code& ec) override
+ {
+ if (ec_)
+ {
+ ec = ec_;
+ }
+ return parse_more_;
+ }
+
+ bool visit_half(uint16_t, semantic_tag, const ser_context&, std::error_code& ec) override
+ {
+ if (ec_)
+ {
+ ec = ec_;
+ }
+ return parse_more_;
+ }
+
+ bool visit_double(double, semantic_tag, const ser_context&, std::error_code& ec) override
+ {
+ if (ec_)
+ {
+ ec = ec_;
+ }
+ return parse_more_;
+ }
+
+ bool visit_bool(bool, semantic_tag, const ser_context&, std::error_code& ec) override
+ {
+ if (ec_)
+ {
+ ec = ec_;
+ }
+ return parse_more_;
+ }
+ };
+
+ template <class CharT>
+ class basic_json_tee_visitor : public basic_json_visitor<CharT>
+ {
+ public:
+ using typename basic_json_visitor<CharT>::char_type;
+ using typename basic_json_visitor<CharT>::string_view_type;
+ private:
+ basic_json_visitor<char_type>& destination0_;
+ basic_json_visitor<char_type>& destination1_;
+
+ // noncopyable and nonmoveable
+ basic_json_tee_visitor(const basic_json_tee_visitor&) = delete;
+ basic_json_tee_visitor& operator=(const basic_json_tee_visitor&) = delete;
+ public:
+ basic_json_tee_visitor(basic_json_visitor<char_type>& destination0,
+ basic_json_visitor<char_type>& destination1)
+ : destination0_(destination0), destination1_(destination1)
+ {
+ }
+
+ basic_json_visitor<char_type>& destination1()
+ {
+ return destination0_;
+ }
+
+ basic_json_visitor<char_type>& destination2()
+ {
+ return destination1_;
+ }
+
+ private:
+ void visit_flush() override
+ {
+ destination0_.flush();
+ }
+
+ bool visit_begin_object(semantic_tag tag, const ser_context& context, std::error_code& ec) override
+ {
+ bool more0 = destination0_.begin_object(tag, context, ec);
+ bool more1 = destination1_.begin_object(tag, context, ec);
+
+ return more0 && more1;
+ }
+
+ bool visit_begin_object(std::size_t length, semantic_tag tag, const ser_context& context, std::error_code& ec) override
+ {
+ bool more0 = destination0_.begin_object(length, tag, context, ec);
+ bool more1 = destination1_.begin_object(length, tag, context, ec);
+
+ return more0 && more1;
+ }
+
+ bool visit_end_object(const ser_context& context, std::error_code& ec) override
+ {
+ bool more0 = destination0_.end_object(context, ec);
+ bool more1 = destination1_.end_object(context, ec);
+
+ return more0 && more1;
+ }
+
+ bool visit_begin_array(semantic_tag tag, const ser_context& context, std::error_code& ec) override
+ {
+ bool more0 = destination0_.begin_array(tag, context, ec);
+ bool more1 = destination1_.begin_array(tag, context, ec);
+
+ return more0 && more1;
+ }
+
+ bool visit_begin_array(std::size_t length, semantic_tag tag, const ser_context& context, std::error_code& ec) override
+ {
+ bool more0 = destination0_.begin_array(length, tag, context, ec);
+ bool more1 = destination1_.begin_array(length, tag, context, ec);
+
+ return more0 && more1;
+ }
+
+ bool visit_end_array(const ser_context& context, std::error_code& ec) override
+ {
+ bool more0 = destination0_.end_array(context, ec);
+ bool more1 = destination1_.end_array(context, ec);
+
+ return more0 && more1;
+ }
+
+ bool visit_key(const string_view_type& name,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ bool more0 = destination0_.key(name, context, ec);
+ bool more1 = destination1_.key(name, context, ec);
+
+ return more0 && more1;
+ }
+
+ bool visit_string(const string_view_type& value,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ bool more0 = destination0_.string_value(value, tag, context, ec);
+ bool more1 = destination1_.string_value(value, tag, context, ec);
+
+ return more0 && more1;
+ }
+
+ bool visit_byte_string(const byte_string_view& b,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ bool more0 = destination0_.byte_string_value(b, tag, context, ec);
+ bool more1 = destination1_.byte_string_value(b, tag, context, ec);
+
+ return more0 && more1;
+ }
+
+ bool visit_uint64(uint64_t value, semantic_tag tag, const ser_context& context, std::error_code& ec) override
+ {
+ bool more0 = destination0_.uint64_value(value, tag, context, ec);
+ bool more1 = destination1_.uint64_value(value, tag, context, ec);
+
+ return more0 && more1;
+ }
+
+ bool visit_int64(int64_t value, semantic_tag tag, const ser_context& context, std::error_code& ec) override
+ {
+ bool more0 = destination0_.int64_value(value, tag, context, ec);
+ bool more1 = destination1_.int64_value(value, tag, context, ec);
+
+ return more0 && more1;
+ }
+
+ bool visit_half(uint16_t value, semantic_tag tag, const ser_context& context, std::error_code& ec) override
+ {
+ bool more0 = destination0_.half_value(value, tag, context, ec);
+ bool more1 = destination1_.half_value(value, tag, context, ec);
+
+ return more0 && more1;
+ }
+
+ bool visit_double(double value, semantic_tag tag, const ser_context& context, std::error_code& ec) override
+ {
+ bool more0 = destination0_.double_value(value, tag, context, ec);
+ bool more1 = destination1_.double_value(value, tag, context, ec);
+
+ return more0 && more1;
+ }
+
+ bool visit_bool(bool value, semantic_tag tag, const ser_context& context, std::error_code& ec) override
+ {
+ bool more0 = destination0_.bool_value(value, tag, context, ec);
+ bool more1 = destination1_.bool_value(value, tag, context, ec);
+
+ return more0 && more1;
+ }
+
+ bool visit_null(semantic_tag tag, const ser_context& context, std::error_code& ec) override
+ {
+ bool more0 = destination0_.null_value(tag, context, ec);
+ bool more1 = destination1_.null_value(tag, context, ec);
+
+ return more0 && more1;
+ }
+
+ bool visit_typed_array(const jsoncons::span<const uint8_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ bool more0 = destination0_.typed_array(s, tag, context, ec);
+ bool more1 = destination1_.typed_array(s, tag, context, ec);
+
+ return more0 && more1;
+ }
+
+ bool visit_typed_array(const jsoncons::span<const uint16_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ bool more0 = destination0_.typed_array(s, tag, context, ec);
+ bool more1 = destination1_.typed_array(s, tag, context, ec);
+
+ return more0 && more1;
+ }
+
+ bool visit_typed_array(const jsoncons::span<const uint32_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ bool more0 = destination0_.typed_array(s, tag, context, ec);
+ bool more1 = destination1_.typed_array(s, tag, context, ec);
+
+ return more0 && more1;
+ }
+
+ bool visit_typed_array(const jsoncons::span<const uint64_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ bool more0 = destination0_.typed_array(s, tag, context, ec);
+ bool more1 = destination1_.typed_array(s, tag, context, ec);
+
+ return more0 && more1;
+ }
+
+ bool visit_typed_array(const jsoncons::span<const int8_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ bool more0 = destination0_.typed_array(s, tag, context, ec);
+ bool more1 = destination1_.typed_array(s, tag, context, ec);
+
+ return more0 && more1;
+ }
+
+ bool visit_typed_array(const jsoncons::span<const int16_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ bool more0 = destination0_.typed_array(s, tag, context, ec);
+ bool more1 = destination1_.typed_array(s, tag, context, ec);
+
+ return more0 && more1;
+ }
+
+ bool visit_typed_array(const jsoncons::span<const int32_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ bool more0 = destination0_.typed_array(s, tag, context, ec);
+ bool more1 = destination1_.typed_array(s, tag, context, ec);
+
+ return more0 && more1;
+ }
+
+ bool visit_typed_array(const jsoncons::span<const int64_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ bool more0 = destination0_.typed_array(s, tag, context, ec);
+ bool more1 = destination1_.typed_array(s, tag, context, ec);
+
+ return more0 && more1;
+ }
+
+ bool visit_typed_array(half_arg_t,
+ const jsoncons::span<const uint16_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ bool more0 = destination0_.typed_array(half_arg, s, tag, context, ec);
+ bool more1 = destination1_.typed_array(half_arg, s, tag, context, ec);
+
+ return more0 && more1;
+ }
+
+ bool visit_typed_array(const jsoncons::span<const float>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ bool more0 = destination0_.typed_array(s, tag, context, ec);
+ bool more1 = destination1_.typed_array(s, tag, context, ec);
+
+ return more0 && more1;
+ }
+
+ bool visit_typed_array(const jsoncons::span<const double>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ bool more0 = destination0_.typed_array(s, tag, context, ec);
+ bool more1 = destination1_.typed_array(s, tag, context, ec);
+
+ return more0 && more1;
+ }
+
+ bool visit_begin_multi_dim(const jsoncons::span<const size_t>& shape,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ bool more0 = destination0_.begin_multi_dim(shape, tag, context, ec);
+ bool more1 = destination1_.begin_multi_dim(shape, tag, context, ec);
+
+ return more0 && more1;
+ }
+
+ bool visit_end_multi_dim(const ser_context& context,
+ std::error_code& ec) override
+ {
+ bool more0 = destination0_.end_multi_dim(context, ec);
+ bool more1 = destination1_.end_multi_dim(context, ec);
+
+ return more0 && more1;
+ }
+
+ };
+
+ template <class CharT>
+ class basic_json_diagnostics_visitor : public basic_default_json_visitor<CharT>
+ {
+ public:
+ using stream_type = std::basic_ostream<CharT>;
+ using string_type = std::basic_string<CharT>;
+
+ private:
+ using supertype = basic_default_json_visitor<CharT>;
+ 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_;
+
+ public:
+ // If CharT is char, then enable the default constructor which binds to
+ // std::cout.
+ template <class U = enabler>
+ basic_json_diagnostics_visitor(
+ typename std::enable_if<std::is_same<CharT, char>::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 <class U = enabler>
+ basic_json_diagnostics_visitor(
+ typename std::enable_if<std::is_same<CharT, wchar_t>::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)),
+ level_(0)
+ {
+ }
+
+ private:
+ void indent()
+ {
+ for (long i=0; i<level_; ++i)
+ output_ << indentation_;
+ }
+
+ bool visit_begin_object(semantic_tag, const ser_context&, std::error_code&) override
+ {
+ indent();
+ output_ << visit_begin_object_name << std::endl;
+ ++level_;
+ return true;
+ }
+
+ bool visit_begin_object(std::size_t length, semantic_tag, const ser_context&, std::error_code&) override
+ {
+ indent();
+ output_ << visit_begin_object_name << separator_ << length << std::endl;
+ ++level_;
+ return true;
+ }
+
+ bool visit_end_object(const ser_context&, std::error_code&) override
+ {
+ --level_;
+ indent();
+ output_ << visit_end_object_name << std::endl;
+ return true;
+ }
+
+ bool visit_begin_array(semantic_tag, const ser_context&, std::error_code&) override
+ {
+ indent();
+ output_ << visit_begin_array_name << std::endl;
+ ++level_;
+ return true;
+ }
+
+ bool visit_begin_array(std::size_t length, semantic_tag, const ser_context&, std::error_code&) override
+ {
+ indent();
+ output_ << visit_begin_array_name << separator_ << length << std::endl;
+ ++level_;
+ return true;
+ }
+
+ bool visit_end_array(const ser_context&, std::error_code&) override
+ {
+ --level_;
+ indent();
+ output_ << visit_end_array_name << std::endl;
+ return true;
+ }
+
+ bool visit_key(const string_view_type& s, const ser_context&, std::error_code&) override
+ {
+ indent();
+ output_ << visit_key_name << separator_ << s << std::endl;
+ return true;
+ }
+ bool visit_string(const string_view_type& s, semantic_tag, const ser_context&, std::error_code&) override
+ {
+ indent();
+ output_ << visit_string_name << separator_ << s << std::endl;
+ return true;
+ }
+ bool visit_int64(int64_t val, semantic_tag, const ser_context&, std::error_code&) override
+ {
+ indent();
+ output_ << visit_int64_name << separator_ << val << std::endl;
+ return true;
+ }
+ bool visit_uint64(uint64_t val, semantic_tag, const ser_context&, std::error_code&) override
+ {
+ indent();
+ output_ << visit_uint64_name << separator_ << val << std::endl;
+ return true;
+ }
+ bool visit_bool(bool val, semantic_tag, const ser_context&, std::error_code&) override
+ {
+ indent();
+ output_ << visit_bool_name << separator_ << val << std::endl;
+ return true;
+ }
+ bool visit_null(semantic_tag, const ser_context&, std::error_code&) override
+ {
+ indent();
+ output_ << visit_null_name << std::endl;
+ return true;
+ }
+ };
+
+ template <class C> constexpr C basic_json_diagnostics_visitor<C>::visit_begin_array_name[];
+ template <class C> constexpr C basic_json_diagnostics_visitor<C>::visit_end_array_name[];
+ template <class C> constexpr C basic_json_diagnostics_visitor<C>::visit_begin_object_name[];
+ template <class C> constexpr C basic_json_diagnostics_visitor<C>::visit_end_object_name[];
+ template <class C> constexpr C basic_json_diagnostics_visitor<C>::visit_key_name[];
+ template <class C> constexpr C basic_json_diagnostics_visitor<C>::visit_string_name[];
+ template <class C> constexpr C basic_json_diagnostics_visitor<C>::visit_byte_string_name[];
+ template <class C> constexpr C basic_json_diagnostics_visitor<C>::visit_null_name[];
+ template <class C> constexpr C basic_json_diagnostics_visitor<C>::visit_bool_name[];
+ template <class C> constexpr C basic_json_diagnostics_visitor<C>::visit_uint64_name[];
+ template <class C> constexpr C basic_json_diagnostics_visitor<C>::visit_int64_name[];
+ template <class C> constexpr C basic_json_diagnostics_visitor<C>::visit_half_name[];
+ template <class C> constexpr C basic_json_diagnostics_visitor<C>::visit_double_name[];
+
+ using json_visitor = basic_json_visitor<char>;
+ using wjson_visitor = basic_json_visitor<wchar_t>;
+
+ using json_tee_visitor = basic_json_tee_visitor<char>;
+ using wjson_tee_visitor = basic_json_tee_visitor<wchar_t>;
+
+ using default_json_visitor = basic_default_json_visitor<char>;
+ using wdefault_json_visitor = basic_default_json_visitor<wchar_t>;
+
+ using json_diagnostics_visitor = basic_json_diagnostics_visitor<char>;
+ using wjson_diagnostics_visitor = basic_json_diagnostics_visitor<wchar_t>;
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+template<class CharT>
+using basic_json_content_handler = basic_json_visitor<CharT>;
+
+JSONCONS_DEPRECATED_MSG("Instead, use json_visitor") typedef json_visitor json_content_handler;
+JSONCONS_DEPRECATED_MSG("Instead, use wjson_visitor") typedef wjson_visitor wjson_content_handler;
+
+template<class CharT>
+using basic_default_json_content_handler = basic_default_json_visitor<CharT>;
+
+JSONCONS_DEPRECATED_MSG("Instead, use default_json_visitor") typedef default_json_visitor default_json_content_handler;
+JSONCONS_DEPRECATED_MSG("Instead, use default_wjson_visitor") typedef wdefault_json_visitor default_wjson_content_handler;
+#endif
+
+} // namespace jsoncons
+
+#endif
diff --git a/include/jsoncons/json_visitor2.hpp b/include/jsoncons/json_visitor2.hpp
new file mode 100644
index 0000000..26096aa
--- /dev/null
+++ b/include/jsoncons/json_visitor2.hpp
@@ -0,0 +1,2079 @@
+// 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)
+
+// See https://github.com/danielaparker/jsoncons for latest version
+
+#ifndef JSONCONS_JSON_VISITOR2_HPP
+#define JSONCONS_JSON_VISITOR2_HPP
+
+#include <jsoncons/json_visitor.hpp>
+#include <jsoncons/json_encoder.hpp>
+
+namespace jsoncons {
+
+ template <class CharT, class Allocator = std::allocator<char>>
+ class basic_json_visitor2_to_visitor_adaptor;
+
+ template <class CharT>
+ class basic_json_visitor2
+ {
+ template <class Ch, class Allocator>
+ friend class basic_json_visitor2_to_visitor_adaptor;
+ public:
+ using char_type = CharT;
+ using char_traits_type = std::char_traits<char_type>;
+
+ using string_view_type = jsoncons::basic_string_view<char_type,char_traits_type>;
+
+ basic_json_visitor2(basic_json_visitor2&&) = default;
+
+ basic_json_visitor2& operator=(basic_json_visitor2&&) = default;
+
+ basic_json_visitor2() = default;
+
+ virtual ~basic_json_visitor2() noexcept = default;
+
+ void flush()
+ {
+ visit_flush();
+ }
+
+ bool begin_object(semantic_tag tag=semantic_tag::none,
+ const ser_context& context=ser_context())
+ {
+ std::error_code ec;
+ bool more = visit_begin_object(tag, context, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, context.line(), context.column()));
+ }
+ return more;
+ }
+
+ bool begin_object(std::size_t length,
+ semantic_tag tag=semantic_tag::none,
+ const ser_context& context = ser_context())
+ {
+ std::error_code ec;
+ bool more = visit_begin_object(length, tag, context, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, context.line(), context.column()));
+ }
+ return more;
+ }
+
+ bool end_object(const ser_context& context = ser_context())
+ {
+ std::error_code ec;
+ bool more = visit_end_object(context, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, context.line(), context.column()));
+ }
+ return more;
+ }
+
+ bool begin_array(semantic_tag tag=semantic_tag::none,
+ const ser_context& context=ser_context())
+ {
+ std::error_code ec;
+ bool more = visit_begin_array(tag, context, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, context.line(), context.column()));
+ }
+ return more;
+ }
+
+ bool begin_array(std::size_t length,
+ semantic_tag tag=semantic_tag::none,
+ const ser_context& context=ser_context())
+ {
+ std::error_code ec;
+ bool more = visit_begin_array(length, tag, context, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, context.line(), context.column()));
+ }
+ return more;
+ }
+
+ bool end_array(const ser_context& context=ser_context())
+ {
+ std::error_code ec;
+ bool more = visit_end_array(context, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, context.line(), context.column()));
+ }
+ return more;
+ }
+
+ bool key(const string_view_type& name, const ser_context& context=ser_context())
+ {
+ std::error_code ec;
+ bool more = visit_string(name, semantic_tag::none, context, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, context.line(), context.column()));
+ }
+ return more;
+ }
+
+ bool null_value(semantic_tag tag = semantic_tag::none,
+ const ser_context& context=ser_context())
+ {
+ std::error_code ec;
+ bool more = visit_null(tag, context, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, context.line(), context.column()));
+ }
+ return more;
+ }
+
+ bool bool_value(bool value,
+ semantic_tag tag = semantic_tag::none,
+ const ser_context& context=ser_context())
+ {
+ std::error_code ec;
+ bool more = visit_bool(value, tag, context, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, context.line(), context.column()));
+ }
+ return more;
+ }
+
+ bool string_value(const string_view_type& value,
+ semantic_tag tag = semantic_tag::none,
+ const ser_context& context=ser_context())
+ {
+ std::error_code ec;
+ bool more = visit_string(value, tag, context, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, context.line(), context.column()));
+ }
+ return more;
+ }
+
+ template <class Source>
+ bool byte_string_value(const Source& b,
+ semantic_tag tag=semantic_tag::none,
+ const ser_context& context=ser_context(),
+ typename std::enable_if<type_traits::is_byte_sequence<Source>::value,int>::type = 0)
+ {
+ std::error_code ec;
+ bool more = visit_byte_string(byte_string_view(reinterpret_cast<const uint8_t*>(b.data()),b.size()), tag, context, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, context.line(), context.column()));
+ }
+ return more;
+ }
+
+ template <class Source>
+ bool byte_string_value(const Source& b,
+ uint64_t ext_tag,
+ const ser_context& context=ser_context(),
+ typename std::enable_if<type_traits::is_byte_sequence<Source>::value,int>::type = 0)
+ {
+ std::error_code ec;
+ bool more = visit_byte_string(byte_string_view(reinterpret_cast<const uint8_t*>(b.data()),b.size()), ext_tag, context, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, context.line(), context.column()));
+ }
+ return more;
+ }
+
+ bool uint64_value(uint64_t value,
+ semantic_tag tag = semantic_tag::none,
+ const ser_context& context=ser_context())
+ {
+ std::error_code ec;
+ bool more = visit_uint64(value, tag, context, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, context.line(), context.column()));
+ }
+ return more;
+ }
+
+ bool int64_value(int64_t value,
+ semantic_tag tag = semantic_tag::none,
+ const ser_context& context=ser_context())
+ {
+ std::error_code ec;
+ bool more = visit_int64(value, tag, context, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, context.line(), context.column()));
+ }
+ return more;
+ }
+
+ bool half_value(uint16_t value,
+ semantic_tag tag = semantic_tag::none,
+ const ser_context& context=ser_context())
+ {
+ std::error_code ec;
+ bool more = visit_half(value, tag, context, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, context.line(), context.column()));
+ }
+ return more;
+ }
+
+ bool double_value(double value,
+ semantic_tag tag = semantic_tag::none,
+ const ser_context& context=ser_context())
+ {
+ std::error_code ec;
+ bool more = visit_double(value, tag, context, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, context.line(), context.column()));
+ }
+ return more;
+ }
+
+ bool begin_object(semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ return visit_begin_object(tag, context, ec);
+ }
+
+ bool begin_object(std::size_t length,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ return visit_begin_object(length, tag, context, ec);
+ }
+
+ bool end_object(const ser_context& context, std::error_code& ec)
+ {
+ return visit_end_object(context, ec);
+ }
+
+ bool begin_array(semantic_tag tag, const ser_context& context, std::error_code& ec)
+ {
+ return visit_begin_array(tag, context, ec);
+ }
+
+ bool begin_array(std::size_t length, semantic_tag tag, const ser_context& context, std::error_code& ec)
+ {
+ return visit_begin_array(length, tag, context, ec);
+ }
+
+ bool end_array(const ser_context& context, std::error_code& ec)
+ {
+ return visit_end_array(context, ec);
+ }
+
+ bool key(const string_view_type& name, const ser_context& context, std::error_code& ec)
+ {
+ return visit_string(name, semantic_tag::none, context, ec);
+ }
+
+ bool null_value(semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ return visit_null(tag, context, ec);
+ }
+
+ bool bool_value(bool value,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ return visit_bool(value, tag, context, ec);
+ }
+
+ bool string_value(const string_view_type& value,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ return visit_string(value, tag, context, ec);
+ }
+
+ template <class Source>
+ bool byte_string_value(const Source& b,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec,
+ typename std::enable_if<type_traits::is_byte_sequence<Source>::value,int>::type = 0)
+ {
+ return visit_byte_string(byte_string_view(reinterpret_cast<const uint8_t*>(b.data()),b.size()), tag, context, ec);
+ }
+
+ template <class Source>
+ bool byte_string_value(const Source& b,
+ uint64_t ext_tag,
+ const ser_context& context,
+ std::error_code& ec,
+ typename std::enable_if<type_traits::is_byte_sequence<Source>::value,int>::type = 0)
+ {
+ return visit_byte_string(byte_string_view(reinterpret_cast<const uint8_t*>(b.data()),b.size()), ext_tag, context, ec);
+ }
+
+ bool uint64_value(uint64_t value,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ return visit_uint64(value, tag, context, ec);
+ }
+
+ bool int64_value(int64_t value,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ return visit_int64(value, tag, context, ec);
+ }
+
+ bool half_value(uint16_t value,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ return visit_half(value, tag, context, ec);
+ }
+
+ bool double_value(double value,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ return visit_double(value, tag, context, ec);
+ }
+
+ template <class T>
+ bool typed_array(const jsoncons::span<T>& data,
+ semantic_tag tag=semantic_tag::none,
+ const ser_context& context=ser_context())
+ {
+ std::error_code ec;
+ bool more = visit_typed_array(data, tag, context, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, context.line(), context.column()));
+ }
+ return more;
+ }
+
+ template <class T>
+ bool typed_array(const jsoncons::span<T>& data,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ return visit_typed_array(data, tag, context, ec);
+ }
+
+ bool typed_array(half_arg_t, const jsoncons::span<const uint16_t>& s,
+ semantic_tag tag = semantic_tag::none,
+ const ser_context& context = ser_context())
+ {
+ std::error_code ec;
+ bool more = visit_typed_array(half_arg, s, tag, context, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, context.line(), context.column()));
+ }
+ return more;
+ }
+
+ bool typed_array(half_arg_t, const jsoncons::span<const uint16_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ return visit_typed_array(half_arg, s, tag, context, ec);
+ }
+
+ bool begin_multi_dim(const jsoncons::span<const size_t>& shape,
+ semantic_tag tag = semantic_tag::multi_dim_row_major,
+ const ser_context& context=ser_context())
+ {
+ std::error_code ec;
+ bool more = visit_begin_multi_dim(shape, tag, context, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, context.line(), context.column()));
+ }
+ return more;
+ }
+
+ bool begin_multi_dim(const jsoncons::span<const size_t>& shape,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ return visit_begin_multi_dim(shape, tag, context, ec);
+ }
+
+ bool end_multi_dim(const ser_context& context=ser_context())
+ {
+ std::error_code ec;
+ bool more = visit_end_multi_dim(context, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, context.line(), context.column()));
+ }
+ return more;
+ }
+
+ bool end_multi_dim(const ser_context& context,
+ std::error_code& ec)
+ {
+ return visit_end_multi_dim(context, ec);
+ }
+
+ private:
+
+ virtual void visit_flush() = 0;
+
+ virtual bool visit_begin_object(semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) = 0;
+
+ virtual bool visit_begin_object(std::size_t /*length*/,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ return visit_begin_object(tag, context, ec);
+ }
+
+ virtual bool visit_end_object(const ser_context& context,
+ std::error_code& ec) = 0;
+
+ virtual bool visit_begin_array(semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) = 0;
+
+ virtual bool visit_begin_array(std::size_t /*length*/,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ return visit_begin_array(tag, context, ec);
+ }
+
+ virtual bool visit_end_array(const ser_context& context,
+ std::error_code& ec) = 0;
+
+ virtual bool visit_null(semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) = 0;
+
+ virtual bool visit_bool(bool value,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code&) = 0;
+
+ virtual bool visit_string(const string_view_type& value,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) = 0;
+
+ virtual bool visit_byte_string(const byte_string_view& value,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) = 0;
+
+ virtual bool visit_byte_string(const byte_string_view& value,
+ uint64_t /*ext_tag*/,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ return visit_byte_string(value, semantic_tag::none, context, ec);
+ }
+
+ virtual bool visit_uint64(uint64_t value,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) = 0;
+
+ virtual bool visit_int64(int64_t value,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) = 0;
+
+ virtual bool visit_half(uint16_t value,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ return visit_double(binary::decode_half(value),
+ tag,
+ context,
+ ec);
+ }
+
+ virtual bool visit_double(double value,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) = 0;
+
+ virtual bool visit_typed_array(const jsoncons::span<const uint8_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ bool more = begin_array(s.size(), tag, context, ec);
+ for (auto p = s.begin(); more && p != s.end(); ++p)
+ {
+ more = uint64_value(*p, semantic_tag::none, context, ec);
+ }
+ if (more)
+ {
+ more = end_array(context, ec);
+ }
+ return more;
+ }
+
+ virtual bool visit_typed_array(const jsoncons::span<const uint16_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ bool more = begin_array(s.size(), tag, context, ec);
+ for (auto p = s.begin(); more && p != s.end(); ++p)
+ {
+ more = uint64_value(*p, semantic_tag::none, context, ec);
+ }
+ if (more)
+ {
+ more = end_array(context, ec);
+ }
+ return more;
+ }
+
+ virtual bool visit_typed_array(const jsoncons::span<const uint32_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ bool more = begin_array(s.size(), tag, context, ec);
+ for (auto p = s.begin(); more && p != s.end(); ++p)
+ {
+ more = uint64_value(*p, semantic_tag::none, context, ec);
+ }
+ if (more)
+ {
+ more = end_array(context, ec);
+ }
+ return more;
+ }
+
+ virtual bool visit_typed_array(const jsoncons::span<const uint64_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ bool more = begin_array(s.size(), tag, context, ec);
+ for (auto p = s.begin(); more && p != s.end(); ++p)
+ {
+ more = uint64_value(*p,semantic_tag::none,context, ec);
+ }
+ if (more)
+ {
+ more = end_array(context, ec);
+ }
+ return more;
+ }
+
+ virtual bool visit_typed_array(const jsoncons::span<const int8_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ bool more = begin_array(s.size(), tag,context, ec);
+ for (auto p = s.begin(); more && p != s.end(); ++p)
+ {
+ more = int64_value(*p,semantic_tag::none,context, ec);
+ }
+ if (more)
+ {
+ more = end_array(context, ec);
+ }
+ return more;
+ }
+
+ virtual bool visit_typed_array(const jsoncons::span<const int16_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ bool more = begin_array(s.size(), tag,context, ec);
+ for (auto p = s.begin(); more && p != s.end(); ++p)
+ {
+ more = int64_value(*p,semantic_tag::none,context, ec);
+ }
+ if (more)
+ {
+ more = end_array(context, ec);
+ }
+ return more;
+ }
+
+ virtual bool visit_typed_array(const jsoncons::span<const int32_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ bool more = begin_array(s.size(), tag,context, ec);
+ for (auto p = s.begin(); more && p != s.end(); ++p)
+ {
+ more = int64_value(*p,semantic_tag::none,context, ec);
+ }
+ if (more)
+ {
+ more = end_array(context, ec);
+ }
+ return more;
+ }
+
+ virtual bool visit_typed_array(const jsoncons::span<const int64_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ bool more = begin_array(s.size(), tag,context, ec);
+ for (auto p = s.begin(); more && p != s.end(); ++p)
+ {
+ more = int64_value(*p,semantic_tag::none,context, ec);
+ }
+ if (more)
+ {
+ more = end_array(context, ec);
+ }
+ return more;
+ }
+
+ virtual bool visit_typed_array(half_arg_t,
+ const jsoncons::span<const uint16_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ bool more = begin_array(s.size(), tag, context, ec);
+ for (auto p = s.begin(); more && p != s.end(); ++p)
+ {
+ more = half_value(*p, semantic_tag::none, context, ec);
+ }
+ if (more)
+ {
+ more = end_array(context, ec);
+ }
+ return more;
+ }
+
+ virtual bool visit_typed_array(const jsoncons::span<const float>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ bool more = begin_array(s.size(), tag,context, ec);
+ for (auto p = s.begin(); more && p != s.end(); ++p)
+ {
+ more = double_value(*p,semantic_tag::none,context, ec);
+ }
+ if (more)
+ {
+ more = end_array(context, ec);
+ }
+ return more;
+ }
+
+ virtual bool visit_typed_array(const jsoncons::span<const double>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ bool more = begin_array(s.size(), tag,context, ec);
+ for (auto p = s.begin(); more && p != s.end(); ++p)
+ {
+ more = double_value(*p,semantic_tag::none,context, ec);
+ }
+ if (more)
+ {
+ more = end_array(context, ec);
+ }
+ return more;
+ }
+
+ virtual bool visit_begin_multi_dim(const jsoncons::span<const size_t>& shape,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ bool more = visit_begin_array(2, tag, context, ec);
+ if (more)
+ {
+ more = visit_begin_array(shape.size(), tag, context, ec);
+ for (auto it = shape.begin(); more && it != shape.end(); ++it)
+ {
+ visit_uint64(*it, semantic_tag::none, context, ec);
+ }
+ if (more)
+ {
+ more = visit_end_array(context, ec);
+ }
+ }
+ return more;
+ }
+
+ virtual bool visit_end_multi_dim(const ser_context& context,
+ std::error_code& ec)
+ {
+ return visit_end_array(context, ec);
+ }
+ };
+
+ template <class CharT, class Allocator>
+ class basic_json_visitor2_to_visitor_adaptor : public basic_json_visitor2<CharT>
+ {
+ public:
+ using typename basic_json_visitor2<CharT>::char_type;
+ using typename basic_json_visitor2<CharT>::string_view_type;
+ private:
+ using char_allocator_type = typename std::allocator_traits<Allocator>:: template rebind_alloc<char_type>;
+
+ using string_type = std::basic_string<char_type,std::char_traits<char_type>,char_allocator_type>;
+
+ enum class container_t {root, array, object};
+ enum class target_t {destination, buffer};
+
+ struct level
+ {
+ private:
+ target_t state_;
+ container_t type_;
+ int even_odd_;
+ std::size_t count_;
+ public:
+
+ level(target_t state, container_t type) noexcept
+ : state_(state), type_(type), even_odd_(type == container_t::object ? 0 : 1), count_(0)
+ {
+ }
+
+ void advance()
+ {
+ if (!is_key())
+ {
+ ++count_;
+ }
+ if (is_object())
+ {
+ even_odd_ = !even_odd_;
+ }
+ }
+
+ bool is_key() const
+ {
+ return even_odd_ == 0;
+ }
+
+ bool is_object() const
+ {
+ return type_ == container_t::object;
+ }
+
+ target_t target() const
+ {
+ return state_;
+ }
+
+ std::size_t count() const
+ {
+ return count_;
+ }
+ };
+ using level_allocator_type = typename std::allocator_traits<Allocator>:: template rebind_alloc<level>;
+
+ basic_default_json_visitor<char_type> default_visitor_;
+ basic_json_visitor<char_type>* destination_;
+ string_type key_;
+ string_type key_buffer_;
+ std::vector<level,level_allocator_type> level_stack_;
+
+ const std::basic_string<char> null_constant = {'n','u','l','l'};
+ const std::basic_string<char> true_constant = { 't','r','u','e' };
+ const std::basic_string<char> false_constant = { 'f', 'a', 'l', 's', 'e' };
+
+ // noncopyable and nonmoveable
+ basic_json_visitor2_to_visitor_adaptor(const basic_json_visitor2_to_visitor_adaptor&) = delete;
+ basic_json_visitor2_to_visitor_adaptor& operator=(const basic_json_visitor2_to_visitor_adaptor&) = delete;
+ public:
+ explicit basic_json_visitor2_to_visitor_adaptor(const Allocator& alloc = Allocator())
+ : default_visitor_(), destination_(std::addressof(default_visitor_)),
+ key_(alloc), key_buffer_(alloc), level_stack_(alloc)
+ {
+ level_stack_.emplace_back(target_t::destination,container_t::root); // root
+ }
+
+ explicit basic_json_visitor2_to_visitor_adaptor(basic_json_visitor<char_type>& visitor,
+ const Allocator& alloc = Allocator())
+ : destination_(std::addressof(visitor)),
+ key_(alloc), key_buffer_(alloc), level_stack_(alloc)
+ {
+ level_stack_.emplace_back(target_t::destination,container_t::root); // root
+ }
+
+ void reset()
+ {
+ key_.clear();
+ key_buffer_.clear();
+ level_stack_.clear();
+ level_stack_.emplace_back(target_t::destination,container_t::root); // root
+ }
+
+ basic_json_visitor<char_type>& destination()
+ {
+ return *destination_;
+ }
+
+ void destination(basic_json_visitor<char_type>& dest)
+ {
+ destination_ = std::addressof(dest);
+ }
+
+ private:
+ void visit_flush() override
+ {
+ destination_->flush();
+ }
+
+ bool visit_begin_object(semantic_tag tag, const ser_context& context, std::error_code& ec) override
+ {
+ if (level_stack_.back().is_key())
+ {
+ if (level_stack_.back().target() == target_t::buffer && level_stack_.back().count() > 0)
+ {
+ key_buffer_.push_back(',');
+ }
+ level_stack_.emplace_back(target_t::buffer, container_t::object);
+ key_buffer_.push_back('{');
+ return true;
+ }
+ else
+ {
+ switch (level_stack_.back().target())
+ {
+ case target_t::buffer:
+ level_stack_.emplace_back(target_t::buffer, container_t::object);
+ key_buffer_.push_back('{');
+ return true;
+ default:
+ level_stack_.emplace_back(target_t::destination, container_t::object);
+ return destination_->begin_object(tag, context, ec);
+ }
+ }
+ }
+
+ bool visit_begin_object(std::size_t length, semantic_tag tag, const ser_context& context, std::error_code& ec) override
+ {
+ if (level_stack_.back().is_key())
+ {
+ if (level_stack_.back().target() == target_t::buffer && level_stack_.back().count() > 0)
+ {
+ key_buffer_.push_back(',');
+ }
+ level_stack_.emplace_back(target_t::buffer, container_t::object);
+ key_buffer_.push_back('{');
+ return true;
+ }
+ else
+ {
+ switch (level_stack_.back().target())
+ {
+ case target_t::buffer:
+ if (!level_stack_.back().is_object() && level_stack_.back().count() > 0)
+ {
+ key_buffer_.push_back(',');
+ }
+ level_stack_.emplace_back(target_t::buffer, container_t::object);
+ key_buffer_.push_back('{');
+ return true;
+ default:
+ level_stack_.emplace_back(target_t::destination, container_t::object);
+ return destination_->begin_object(length, tag, context, ec);
+ }
+ }
+ }
+
+ bool visit_end_object(const ser_context& context, std::error_code& ec) override
+ {
+ bool retval = true;
+ switch (level_stack_.back().target())
+ {
+ case target_t::buffer:
+ key_buffer_.push_back('}');
+ JSONCONS_ASSERT(level_stack_.size() > 1);
+ level_stack_.pop_back();
+
+ if (level_stack_.back().target() == target_t::destination)
+ {
+ retval = destination_->key(key_buffer_,context, ec);
+ key_buffer_.clear();
+ }
+ else if (level_stack_.back().is_key())
+ {
+ key_buffer_.push_back(':');
+ }
+ level_stack_.back().advance();
+ break;
+ default:
+ JSONCONS_ASSERT(level_stack_.size() > 1);
+ level_stack_.pop_back();
+ level_stack_.back().advance();
+ retval = destination_->end_object(context, ec);
+ break;
+ }
+ return retval;
+ }
+
+ bool visit_begin_array(semantic_tag tag, const ser_context& context, std::error_code& ec) override
+ {
+ if (level_stack_.back().is_key())
+ {
+ if (level_stack_.back().target() == target_t::buffer && level_stack_.back().count() > 0)
+ {
+ key_buffer_.push_back(',');
+ }
+ level_stack_.emplace_back(target_t::buffer, container_t::array);
+ key_buffer_.push_back('[');
+ return true;
+ }
+ else
+ {
+ switch (level_stack_.back().target())
+ {
+ case target_t::buffer:
+ if (level_stack_.back().is_object() && level_stack_.back().count() > 0)
+ {
+ key_buffer_.push_back(',');
+ }
+ level_stack_.emplace_back(target_t::buffer, container_t::array);
+ key_buffer_.push_back('[');
+ return true;
+ default:
+ level_stack_.emplace_back(target_t::destination, container_t::array);
+ return destination_->begin_array(tag, context, ec);
+ }
+ }
+ }
+
+ bool visit_begin_array(std::size_t length, semantic_tag tag, const ser_context& context, std::error_code& ec) override
+ {
+ if (level_stack_.back().is_key())
+ {
+ if (level_stack_.back().target() == target_t::buffer && level_stack_.back().count() > 0)
+ {
+ key_buffer_.push_back(',');
+ }
+ level_stack_.emplace_back(target_t::buffer, container_t::array);
+ key_buffer_.push_back('[');
+ return true;
+ }
+ else
+ {
+ switch (level_stack_.back().target())
+ {
+ case target_t::buffer:
+ if (!level_stack_.back().is_object() && level_stack_.back().count() > 0)
+ {
+ key_buffer_.push_back(',');
+ }
+ level_stack_.emplace_back(target_t::buffer, container_t::array);
+ key_buffer_.push_back('[');
+ return true;
+ default:
+ level_stack_.emplace_back(target_t::destination, container_t::array);
+ return destination_->begin_array(length, tag, context, ec);
+ }
+ }
+ }
+
+ bool visit_end_array(const ser_context& context, std::error_code& ec) override
+ {
+ bool retval = true;
+ switch (level_stack_.back().target())
+ {
+ case target_t::buffer:
+ key_buffer_.push_back(']');
+ JSONCONS_ASSERT(level_stack_.size() > 1);
+ level_stack_.pop_back();
+ if (level_stack_.back().target() == target_t::destination)
+ {
+ retval = destination_->key(key_buffer_, context, ec);
+ key_buffer_.clear();
+ }
+ else if (level_stack_.back().is_key())
+ {
+ key_buffer_.push_back(':');
+ }
+ level_stack_.back().advance();
+ break;
+ default:
+ JSONCONS_ASSERT(level_stack_.size() > 1);
+ level_stack_.pop_back();
+ level_stack_.back().advance();
+ retval = destination_->end_array(context, ec);
+ break;
+ }
+ return retval;
+ }
+
+ bool visit_string(const string_view_type& value,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ bool retval = true;
+
+ if (level_stack_.back().is_key())
+ {
+ switch (level_stack_.back().target())
+ {
+ case target_t::buffer:
+ if (level_stack_.back().count() > 0)
+ {
+ key_buffer_.push_back(',');
+ }
+ key_buffer_.push_back('\"');
+ key_buffer_.insert(key_buffer_.end(), value.begin(), value.end());
+ key_buffer_.push_back('\"');
+ key_buffer_.push_back(':');
+ retval = true;
+ break;
+ default:
+ retval = destination_->key(value, context, ec);
+ break;
+ }
+ }
+ else
+ {
+ switch (level_stack_.back().target())
+ {
+ case target_t::buffer:
+ if (!level_stack_.back().is_object() && level_stack_.back().count() > 0)
+ {
+ key_buffer_.push_back(',');
+ }
+ key_buffer_.push_back('\"');
+ key_buffer_.insert(key_buffer_.end(), value.begin(), value.end());
+ key_buffer_.push_back('\"');
+ retval = true;
+ break;
+ default:
+ retval = destination_->string_value(value, tag, context, ec);
+ break;
+ }
+ }
+
+ level_stack_.back().advance();
+ return retval;
+ }
+
+ bool visit_byte_string(const byte_string_view& value,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ bool retval = true;
+
+ if (level_stack_.back().is_key() || level_stack_.back().target() == target_t::buffer)
+ {
+ key_.clear();
+ switch (tag)
+ {
+ case semantic_tag::base64:
+ encode_base64(value.begin(), value.end(), key_);
+ break;
+ case semantic_tag::base16:
+ encode_base16(value.begin(), value.end(),key_);
+ break;
+ default:
+ encode_base64url(value.begin(), value.end(),key_);
+ break;
+ }
+ }
+
+ if (level_stack_.back().is_key())
+ {
+ switch (level_stack_.back().target())
+ {
+ case target_t::buffer:
+ if (level_stack_.back().count() > 0)
+ {
+ key_buffer_.push_back(',');
+ }
+ key_buffer_.push_back('\"');
+ key_buffer_.insert(key_buffer_.end(), key_.begin(), key_.end());
+ key_buffer_.push_back('\"');
+ key_buffer_.push_back(':');
+ retval = true;
+ break;
+ default:
+ retval = destination_->key(key_, context, ec);
+ break;
+ }
+ }
+ else
+ {
+ switch (level_stack_.back().target())
+ {
+ case target_t::buffer:
+ if (!level_stack_.back().is_object() && level_stack_.back().count() > 0)
+ {
+ key_buffer_.push_back(',');
+ }
+ key_buffer_.push_back('\"');
+ key_buffer_.insert(key_buffer_.end(), key_.begin(), key_.end());
+ key_buffer_.push_back('\"');
+ retval = true;
+ break;
+ default:
+ retval = destination_->byte_string_value(value, tag, context, ec);
+ break;
+ }
+ }
+
+ level_stack_.back().advance();
+ return retval;
+ }
+
+ bool visit_byte_string(const byte_string_view& value,
+ uint64_t ext_tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ bool retval = true;
+
+ if (level_stack_.back().is_key() || level_stack_.back().target() == target_t::buffer)
+ {
+ key_.clear();
+ encode_base64url(value.begin(), value.end(),key_);
+ }
+
+ if (level_stack_.back().is_key())
+ {
+ switch (level_stack_.back().target())
+ {
+ case target_t::buffer:
+ if (level_stack_.back().count() > 0)
+ {
+ key_buffer_.push_back(',');
+ }
+ key_buffer_.push_back('\"');
+ key_buffer_.insert(key_buffer_.end(), key_.begin(), key_.end());
+ key_buffer_.push_back('\"');
+ key_buffer_.push_back(':');
+ retval = true;
+ break;
+ default:
+ retval = destination_->key(key_, context, ec);
+ break;
+ }
+ }
+ else
+ {
+ switch (level_stack_.back().target())
+ {
+ case target_t::buffer:
+ if (!level_stack_.back().is_object() && level_stack_.back().count() > 0)
+ {
+ key_buffer_.push_back(',');
+ }
+ key_buffer_.push_back('\"');
+ key_buffer_.insert(key_buffer_.end(), key_.begin(), key_.end());
+ key_buffer_.push_back('\"');
+ retval = true;
+ break;
+ default:
+ retval = destination_->byte_string_value(value, ext_tag, context, ec);
+ break;
+ }
+ }
+
+ level_stack_.back().advance();
+ return retval;
+ }
+
+ bool visit_uint64(uint64_t value, semantic_tag tag, const ser_context& context, std::error_code& ec) override
+ {
+ bool retval = true;
+
+ if (level_stack_.back().is_key() || level_stack_.back().target() == target_t::buffer)
+ {
+ key_.clear();
+ jsoncons::detail::from_integer(value,key_);
+ }
+
+ if (level_stack_.back().is_key())
+ {
+ switch (level_stack_.back().target())
+ {
+ case target_t::buffer:
+ if (level_stack_.back().count() > 0)
+ {
+ key_buffer_.push_back(',');
+ }
+ key_buffer_.insert(key_buffer_.end(), key_.begin(), key_.end());
+ key_buffer_.push_back(':');
+ retval = true;
+ break;
+ default:
+ retval = destination_->key(key_, context, ec);
+ break;
+ }
+ }
+ else
+ {
+ switch (level_stack_.back().target())
+ {
+ case target_t::buffer:
+ if (!level_stack_.back().is_object() && level_stack_.back().count() > 0)
+ {
+ key_buffer_.push_back(',');
+ }
+ key_buffer_.insert(key_buffer_.end(), key_.begin(), key_.end());
+ retval = true;
+ break;
+ default:
+ retval = destination_->uint64_value(value, tag, context, ec);
+ break;
+ }
+ }
+
+ level_stack_.back().advance();
+ return retval;
+ }
+
+ bool visit_int64(int64_t value, semantic_tag tag, const ser_context& context, std::error_code& ec) override
+ {
+ bool retval = true;
+
+ if (level_stack_.back().is_key() || level_stack_.back().target() == target_t::buffer)
+ {
+ key_.clear();
+ jsoncons::detail::from_integer(value,key_);
+ }
+
+ if (level_stack_.back().is_key())
+ {
+ switch (level_stack_.back().target())
+ {
+ case target_t::buffer:
+ if (level_stack_.back().count() > 0)
+ {
+ key_buffer_.push_back(',');
+ }
+ key_buffer_.insert(key_buffer_.end(), key_.begin(), key_.end());
+ key_buffer_.push_back(':');
+ retval = true;
+ break;
+ default:
+ retval = destination_->key(key_, context, ec);
+ break;
+ }
+ }
+ else
+ {
+ switch (level_stack_.back().target())
+ {
+ case target_t::buffer:
+ if (!level_stack_.back().is_object() && level_stack_.back().count() > 0)
+ {
+ key_buffer_.push_back(',');
+ }
+ key_buffer_.insert(key_buffer_.end(), key_.begin(), key_.end());
+ retval = true;
+ break;
+ default:
+ retval = destination_->int64_value(value, tag, context, ec);
+ break;
+ }
+ }
+
+ level_stack_.back().advance();
+ return retval;
+ }
+
+ bool visit_half(uint16_t value, semantic_tag tag, const ser_context& context, std::error_code& ec) override
+ {
+ bool retval = true;
+
+ if (level_stack_.back().is_key() || level_stack_.back().target() == target_t::buffer)
+ {
+ key_.clear();
+ jsoncons::string_sink<string_type> sink(key_);
+ jsoncons::detail::write_double f{float_chars_format::general,0};
+ double x = binary::decode_half(value);
+ f(x, sink);
+ }
+
+ if (level_stack_.back().is_key())
+ {
+ switch (level_stack_.back().target())
+ {
+ case target_t::buffer:
+ if (level_stack_.back().count() > 0)
+ {
+ key_buffer_.push_back(',');
+ }
+ key_buffer_.insert(key_buffer_.end(), key_.begin(), key_.end());
+ key_buffer_.push_back(':');
+ retval = true;
+ break;
+ default:
+ retval = destination_->key(key_, context, ec);
+ break;
+ }
+ }
+ else
+ {
+ switch (level_stack_.back().target())
+ {
+ case target_t::buffer:
+ if (!level_stack_.back().is_object() && level_stack_.back().count() > 0)
+ {
+ key_buffer_.push_back(',');
+ }
+ key_buffer_.insert(key_buffer_.end(), key_.begin(), key_.end());
+ retval = true;
+ break;
+ default:
+ retval = destination_->half_value(value, tag, context, ec);
+ break;
+ }
+ }
+
+ level_stack_.back().advance();
+ return retval;
+ }
+
+ bool visit_double(double value, semantic_tag tag, const ser_context& context, std::error_code& ec) override
+ {
+ bool retval = true;
+
+ if (level_stack_.back().is_key() || level_stack_.back().target() == target_t::buffer)
+ {
+ key_.clear();
+ string_sink<string_type> sink(key_);
+ jsoncons::detail::write_double f{float_chars_format::general,0};
+ f(value, sink);
+ }
+
+ if (level_stack_.back().is_key())
+ {
+ switch (level_stack_.back().target())
+ {
+ case target_t::buffer:
+ if (level_stack_.back().count() > 0)
+ {
+ key_buffer_.push_back(',');
+ }
+ key_buffer_.insert(key_buffer_.end(), key_.begin(), key_.end());
+ key_buffer_.push_back(':');
+ retval = true;
+ break;
+ default:
+ retval = destination_->key(key_, context, ec);
+ break;
+ }
+ }
+ else
+ {
+ switch (level_stack_.back().target())
+ {
+ case target_t::buffer:
+ if (!level_stack_.back().is_object() && level_stack_.back().count() > 0)
+ {
+ key_buffer_.push_back(',');
+ }
+ key_buffer_.insert(key_buffer_.end(), key_.begin(), key_.end());
+ retval = true;
+ break;
+ default:
+ retval = destination_->double_value(value, tag, context, ec);
+ break;
+ }
+ }
+
+ level_stack_.back().advance();
+ return retval;
+ }
+
+ bool visit_bool(bool value, semantic_tag tag, const ser_context& context, std::error_code& ec) override
+ {
+ bool retval = true;
+
+ if (level_stack_.back().is_key() || level_stack_.back().target() == target_t::buffer)
+ {
+ key_ = value ? true_constant : false_constant;
+ }
+
+ if (level_stack_.back().is_key())
+ {
+ switch (level_stack_.back().target())
+ {
+ case target_t::buffer:
+ if (level_stack_.back().count() > 0)
+ {
+ key_buffer_.push_back(',');
+ }
+ key_buffer_.insert(key_buffer_.end(), key_.begin(), key_.end());
+ key_buffer_.push_back(':');
+ retval = true;
+ break;
+ default:
+ retval = destination_->key(key_, context, ec);
+ break;
+ }
+ }
+ else
+ {
+ switch (level_stack_.back().target())
+ {
+ case target_t::buffer:
+ if (!level_stack_.back().is_object() && level_stack_.back().count() > 0)
+ {
+ key_buffer_.push_back(',');
+ }
+ key_buffer_.insert(key_buffer_.end(), key_.begin(), key_.end());
+ retval = true;
+ break;
+ default:
+ retval = destination_->bool_value(value, tag, context, ec);
+ break;
+ }
+ }
+
+ level_stack_.back().advance();
+ return retval;
+ }
+
+ bool visit_null(semantic_tag tag, const ser_context& context, std::error_code& ec) override
+ {
+ bool retval = true;
+
+ if (level_stack_.back().is_key() || level_stack_.back().target() == target_t::buffer)
+ {
+ key_ = null_constant;
+ }
+
+ if (level_stack_.back().is_key())
+ {
+ switch (level_stack_.back().target())
+ {
+ case target_t::buffer:
+ if (level_stack_.back().count() > 0)
+ {
+ key_buffer_.push_back(',');
+ }
+ key_buffer_.insert(key_buffer_.end(), key_.begin(), key_.end());
+ key_buffer_.push_back(':');
+ retval = true;
+ break;
+ default:
+ retval = destination_->key(key_, context, ec);
+ break;
+ }
+ }
+ else
+ {
+ switch (level_stack_.back().target())
+ {
+ case target_t::buffer:
+ if (!level_stack_.back().is_object() && level_stack_.back().count() > 0)
+ {
+ key_buffer_.push_back(',');
+ }
+ key_buffer_.insert(key_buffer_.end(), key_.begin(), key_.end());
+ retval = true;
+ break;
+ default:
+ retval = destination_->null_value(tag, context, ec);
+ break;
+ }
+ }
+
+ level_stack_.back().advance();
+ return retval;
+ }
+
+ bool visit_typed_array(const jsoncons::span<const uint8_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ bool is_key = level_stack_.back().is_key();
+ level_stack_.back().advance();
+
+ if (is_key || level_stack_.back().target() == target_t::buffer)
+ {
+ return basic_json_visitor2<CharT>::visit_typed_array(s,tag,context,ec);
+ }
+ else
+ {
+ return destination_->typed_array(s, tag, context, ec);
+ }
+ }
+
+ bool visit_typed_array(const jsoncons::span<const uint16_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ bool is_key = level_stack_.back().is_key();
+ level_stack_.back().advance();
+
+ if (is_key || level_stack_.back().target() == target_t::buffer)
+ {
+ return basic_json_visitor2<CharT>::visit_typed_array(s,tag,context,ec);
+ }
+ else
+ {
+ return destination_->typed_array(s, tag, context, ec);
+ }
+ }
+
+ bool visit_typed_array(const jsoncons::span<const uint32_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ bool is_key = level_stack_.back().is_key();
+ level_stack_.back().advance();
+
+ if (is_key || level_stack_.back().target() == target_t::buffer)
+ {
+ return basic_json_visitor2<CharT>::visit_typed_array(s,tag,context,ec);
+ }
+ else
+ {
+ return destination_->typed_array(s, tag, context, ec);
+ }
+ }
+
+ bool visit_typed_array(const jsoncons::span<const uint64_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ bool is_key = level_stack_.back().is_key();
+ level_stack_.back().advance();
+
+ if (is_key || level_stack_.back().target() == target_t::buffer)
+ {
+ return basic_json_visitor2<CharT>::visit_typed_array(s,tag,context,ec);
+ }
+ else
+ {
+ return destination_->typed_array(s, tag, context, ec);
+ }
+ }
+
+ bool visit_typed_array(const jsoncons::span<const int8_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ bool is_key = level_stack_.back().is_key();
+ level_stack_.back().advance();
+
+ if (is_key || level_stack_.back().target() == target_t::buffer)
+ {
+ return basic_json_visitor2<CharT>::visit_typed_array(s,tag,context,ec);
+ }
+ else
+ {
+ return destination_->typed_array(s, tag, context, ec);
+ }
+ }
+
+ bool visit_typed_array(const jsoncons::span<const int16_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ bool is_key = level_stack_.back().is_key();
+ level_stack_.back().advance();
+
+ if (is_key || level_stack_.back().target() == target_t::buffer)
+ {
+ return basic_json_visitor2<CharT>::visit_typed_array(s,tag,context,ec);
+ }
+ else
+ {
+ return destination_->typed_array(s, tag, context, ec);
+ }
+ }
+
+ bool visit_typed_array(const jsoncons::span<const int32_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ bool is_key = level_stack_.back().is_key();
+ level_stack_.back().advance();
+
+ if (is_key || level_stack_.back().target() == target_t::buffer)
+ {
+ return basic_json_visitor2<CharT>::visit_typed_array(s,tag,context,ec);
+ }
+ else
+ {
+ return destination_->typed_array(s, tag, context, ec);
+ }
+ }
+
+ bool visit_typed_array(const jsoncons::span<const int64_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ bool is_key = level_stack_.back().is_key();
+ level_stack_.back().advance();
+
+ if (is_key || level_stack_.back().target() == target_t::buffer)
+ {
+ return basic_json_visitor2<CharT>::visit_typed_array(s,tag,context,ec);
+ }
+ else
+ {
+ return destination_->typed_array(s, tag, context, ec);
+ }
+ }
+
+ bool visit_typed_array(half_arg_t,
+ const jsoncons::span<const uint16_t>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ bool is_key = level_stack_.back().is_key();
+ level_stack_.back().advance();
+
+ if (is_key || level_stack_.back().target() == target_t::buffer)
+ {
+ return basic_json_visitor2<CharT>::visit_typed_array(half_arg,s,tag,context,ec);
+ }
+ else
+ {
+ return destination_->typed_array(half_arg, s, tag, context, ec);
+ }
+ }
+
+ bool visit_typed_array(const jsoncons::span<const float>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ bool is_key = level_stack_.back().is_key();
+ level_stack_.back().advance();
+
+ if (is_key || level_stack_.back().target() == target_t::buffer)
+ {
+ return basic_json_visitor2<CharT>::visit_typed_array(s,tag,context,ec);
+ }
+ else
+ {
+ return destination_->typed_array(s, tag, context, ec);
+ }
+ }
+
+ bool visit_typed_array(const jsoncons::span<const double>& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ bool is_key = level_stack_.back().is_key();
+ level_stack_.back().advance();
+
+ if (is_key || level_stack_.back().target() == target_t::buffer)
+ {
+ return basic_json_visitor2<CharT>::visit_typed_array(s,tag,context,ec);
+ }
+ else
+ {
+ return destination_->typed_array(s, tag, context, ec);
+ }
+ }
+ };
+
+ template <class CharT>
+ class basic_default_json_visitor2 : public basic_json_visitor2<CharT>
+ {
+ bool parse_more_;
+ std::error_code ec_;
+ public:
+ using typename basic_json_visitor2<CharT>::string_view_type;
+
+ basic_default_json_visitor2(bool accept_more = true,
+ std::error_code ec = std::error_code())
+ : parse_more_(accept_more), ec_(ec)
+ {
+ }
+ private:
+ void visit_flush() override
+ {
+ }
+
+ bool visit_begin_object(semantic_tag, const ser_context&, std::error_code& ec) override
+ {
+ if (ec_)
+ {
+ ec = ec_;
+ }
+ return parse_more_;
+ }
+
+ bool visit_begin_object(std::size_t, semantic_tag, const ser_context&, std::error_code& ec) override
+ {
+ if (ec_)
+ {
+ ec = ec_;
+ }
+ return parse_more_;
+ }
+
+ bool visit_end_object(const ser_context&, std::error_code& ec) override
+ {
+ if (ec_)
+ {
+ ec = ec_;
+ }
+ return parse_more_;
+ }
+
+ bool visit_begin_array(semantic_tag, const ser_context&, std::error_code& ec) override
+ {
+ if (ec_)
+ {
+ ec = ec_;
+ }
+ return parse_more_;
+ }
+
+ bool visit_begin_array(std::size_t, semantic_tag, const ser_context&, std::error_code& ec) override
+ {
+ if (ec_)
+ {
+ ec = ec_;
+ }
+ return parse_more_;
+ }
+
+ bool visit_end_array(const ser_context&, std::error_code& ec) override
+ {
+ if (ec_)
+ {
+ ec = ec_;
+ }
+ return parse_more_;
+ }
+
+ bool visit_null(semantic_tag, const ser_context&, std::error_code& ec) override
+ {
+ if (ec_)
+ {
+ ec = ec_;
+ }
+ return parse_more_;
+ }
+
+ bool visit_string(const string_view_type&, semantic_tag, const ser_context&, std::error_code& ec) override
+ {
+ if (ec_)
+ {
+ ec = ec_;
+ }
+ return parse_more_;
+ }
+
+ bool visit_byte_string(const byte_string_view&, semantic_tag, const ser_context&, std::error_code& ec) override
+ {
+ if (ec_)
+ {
+ ec = ec_;
+ }
+ return parse_more_;
+ }
+
+ bool visit_uint64(uint64_t, semantic_tag, const ser_context&, std::error_code& ec) override
+ {
+ if (ec_)
+ {
+ ec = ec_;
+ }
+ return parse_more_;
+ }
+
+ bool visit_int64(int64_t, semantic_tag, const ser_context&, std::error_code& ec) override
+ {
+ if (ec_)
+ {
+ ec = ec_;
+ }
+ return parse_more_;
+ }
+
+ bool visit_half(uint16_t, semantic_tag, const ser_context&, std::error_code& ec) override
+ {
+ if (ec_)
+ {
+ ec = ec_;
+ }
+ return parse_more_;
+ }
+
+ bool visit_double(double, semantic_tag, const ser_context&, std::error_code& ec) override
+ {
+ if (ec_)
+ {
+ ec = ec_;
+ }
+ return parse_more_;
+ }
+
+ bool visit_bool(bool, semantic_tag, const ser_context&, std::error_code& ec) override
+ {
+ if (ec_)
+ {
+ ec = ec_;
+ }
+ return parse_more_;
+ }
+ };
+
+ // basic_json_visitor_to_visitor2_adaptor
+
+ template <class CharT>
+ class basic_json_visitor_to_visitor2_adaptor : public basic_json_visitor<CharT>
+ {
+ public:
+ using typename basic_json_visitor<CharT>::char_type;
+ using typename basic_json_visitor<CharT>::string_view_type;
+ private:
+ basic_json_visitor2<char_type>& destination_;
+
+ // noncopyable and nonmoveable
+ basic_json_visitor_to_visitor2_adaptor(const basic_json_visitor_to_visitor2_adaptor&) = delete;
+ basic_json_visitor_to_visitor2_adaptor& operator=(const basic_json_visitor_to_visitor2_adaptor&) = delete;
+ public:
+ basic_json_visitor_to_visitor2_adaptor(basic_json_visitor2<char_type>& visitor)
+ : destination_(visitor)
+ {
+ }
+
+ basic_json_visitor2<char_type>& destination()
+ {
+ return destination_;
+ }
+
+ private:
+ void visit_flush() override
+ {
+ destination_.flush();
+ }
+
+ bool visit_begin_object(semantic_tag tag, const ser_context& context, std::error_code& ec) override
+ {
+ return destination_.begin_object(tag, context, ec);
+ }
+
+ bool visit_begin_object(std::size_t length, semantic_tag tag, const ser_context& context, std::error_code& ec) override
+ {
+ return destination_.begin_object(length, tag, context, ec);
+ }
+
+ bool visit_end_object(const ser_context& context, std::error_code& ec) override
+ {
+ return destination_.end_object(context, ec);
+ }
+
+ bool visit_begin_array(semantic_tag tag, const ser_context& context, std::error_code& ec) override
+ {
+ return destination_.begin_array(tag, context, ec);
+ }
+
+ bool visit_begin_array(std::size_t length, semantic_tag tag, const ser_context& context, std::error_code& ec) override
+ {
+ return destination_.begin_array(length, tag, context, ec);
+ }
+
+ bool visit_end_array(const ser_context& context, std::error_code& ec) override
+ {
+ return destination_.end_array(context, ec);
+ }
+
+ bool visit_key(const string_view_type& name,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ return destination_.visit_string(name, context, ec);
+ }
+
+ bool visit_string(const string_view_type& value,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ return destination_.string_value(value, tag, context, ec);
+ }
+
+ bool visit_byte_string(const byte_string_view& b,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ return destination_.byte_string_value(b, tag, context, ec);
+ }
+
+ bool visit_uint64(uint64_t value, semantic_tag tag, const ser_context& context, std::error_code& ec) override
+ {
+ return destination_.uint64_value(value, tag, context, ec);
+ }
+
+ bool visit_int64(int64_t value, semantic_tag tag, const ser_context& context, std::error_code& ec) override
+ {
+ return destination_.int64_value(value, tag, context, ec);
+ }
+
+ bool visit_half(uint16_t value, semantic_tag tag, const ser_context& context, std::error_code& ec) override
+ {
+ return destination_.half_value(value, tag, context, ec);
+ }
+
+ bool visit_double(double value, semantic_tag tag, const ser_context& context, std::error_code& ec) override
+ {
+ return destination_.double_value(value, tag, context, ec);
+ }
+
+ bool visit_bool(bool value, semantic_tag tag, const ser_context& context, std::error_code& ec) override
+ {
+ return destination_.bool_value(value, tag, context, ec);
+ }
+
+ bool visit_null(semantic_tag tag, const ser_context& context, std::error_code& ec) override
+ {
+ return destination_.null_value(tag, context, ec);
+ }
+ };
+
+ class diagnostics_visitor2 : public basic_default_json_visitor2<char>
+ {
+ bool visit_begin_object(semantic_tag, const ser_context&, std::error_code&) override
+ {
+ std::cout << "visit_begin_object" << std::endl;
+ return true;
+ }
+
+ bool visit_begin_object(std::size_t length, semantic_tag, const ser_context&, std::error_code&) override
+ {
+ std::cout << "visit_begin_object " << length << std::endl;
+ return true;
+ }
+
+ bool visit_end_object(const ser_context&, std::error_code&) override
+ {
+ std::cout << "visit_end_object" << std::endl;
+ return true;
+ }
+
+ bool visit_begin_array(semantic_tag, const ser_context&, std::error_code&) override
+ {
+ std::cout << "visit_begin_array" << std::endl;
+ return true;
+ }
+
+ bool visit_begin_array(std::size_t length, semantic_tag, const ser_context&, std::error_code&) override
+ {
+ std::cout << "visit_begin_array " << length << std::endl;
+ return true;
+ }
+
+ bool visit_end_array(const ser_context&, std::error_code&) override
+ {
+ std::cout << "visit_end_array" << std::endl;
+ return true;
+ }
+
+ bool visit_string(const string_view_type& s, semantic_tag, const ser_context&, std::error_code&) override
+ {
+ std::cout << "visit_string " << s << std::endl;
+ return true;
+ }
+ bool visit_int64(int64_t val, semantic_tag, const ser_context&, std::error_code&) override
+ {
+ std::cout << "visit_int64 " << val << std::endl;
+ return true;
+ }
+ bool visit_uint64(uint64_t val, semantic_tag, const ser_context&, std::error_code&) override
+ {
+ std::cout << "visit_uint64 " << val << std::endl;
+ return true;
+ }
+ bool visit_bool(bool val, semantic_tag, const ser_context&, std::error_code&) override
+ {
+ std::cout << "visit_bool " << val << std::endl;
+ return true;
+ }
+ bool visit_null(semantic_tag, const ser_context&, std::error_code&) override
+ {
+ std::cout << "visit_null " << std::endl;
+ return true;
+ }
+
+ bool visit_typed_array(const jsoncons::span<const uint16_t>& s,
+ semantic_tag tag,
+ const ser_context&,
+ std::error_code&) override
+ {
+ std::cout << "visit_typed_array uint16_t " << tag << std::endl;
+ for (auto val : s)
+ {
+ std::cout << val << "" << std::endl;
+ }
+ std::cout << "" << std::endl;
+ return true;
+ }
+
+ bool visit_typed_array(half_arg_t, const jsoncons::span<const uint16_t>& s,
+ semantic_tag tag,
+ const ser_context&,
+ std::error_code&) override
+ {
+ std::cout << "visit_typed_array half_arg_t uint16_t " << tag << "" << std::endl;
+ for (auto val : s)
+ {
+ std::cout << val << "" << std::endl;
+ }
+ std::cout << "" << std::endl;
+ return true;
+ }
+ };
+
+ using json_visitor2 = basic_json_visitor2<char>;
+ using default_json_visitor2 = basic_default_json_visitor2<char>;
+ using json_visitor2_to_visitor_adaptor = basic_json_visitor2_to_visitor_adaptor<char>;
+
+} // namespace jsoncons
+
+#endif
diff --git a/include/jsoncons/more_type_traits.hpp b/include/jsoncons/more_type_traits.hpp
new file mode 100644
index 0000000..8e6b6c2
--- /dev/null
+++ b/include/jsoncons/more_type_traits.hpp
@@ -0,0 +1,874 @@
+// Copyright 2013 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_MORE_TYPE_TRAITS_HPP
+#define JSONCONS_MORE_TYPE_TRAITS_HPP
+
+#include <stdexcept>
+#include <string>
+#include <cmath>
+#include <type_traits> // std::enable_if, std::true_type
+#include <memory>
+#include <iterator> // std::iterator_traits
+#include <exception>
+#include <array> // std::array
+#include <cstddef> // std::byte
+#include <utility> // std::declval
+#include <climits> // CHAR_BIT
+#include <jsoncons/config/compiler_support.hpp>
+
+namespace jsoncons {
+namespace type_traits {
+
+ // is_char8
+ template <typename CharT, typename Enable=void>
+ struct is_char8 : std::false_type {};
+
+ template <typename CharT>
+ struct is_char8<CharT, typename std::enable_if<std::is_integral<CharT>::value &&
+ !std::is_same<CharT,bool>::value &&
+ sizeof(uint8_t) == sizeof(CharT)>::type> : std::true_type {};
+
+ // is_char16
+ template <typename CharT, typename Enable=void>
+ struct is_char16 : std::false_type {};
+
+ template <typename CharT>
+ struct is_char16<CharT, typename std::enable_if<std::is_integral<CharT>::value &&
+ !std::is_same<CharT,bool>::value &&
+ (std::is_same<CharT,char16_t>::value || sizeof(uint16_t) == sizeof(CharT))>::type> : std::true_type {};
+
+ // is_char32
+ template <typename CharT, typename Enable=void>
+ struct is_char32 : std::false_type {};
+
+ template <typename CharT>
+ struct is_char32<CharT, typename std::enable_if<std::is_integral<CharT>::value &&
+ !std::is_same<CharT,bool>::value &&
+ (std::is_same<CharT,char32_t>::value || (!std::is_same<CharT,char16_t>::value && sizeof(uint32_t) == sizeof(CharT)))>::type> : std::true_type {};
+
+ // is_int128
+
+ template <class T, class Enable=void>
+ struct is_int128_type : std::false_type {};
+
+#if defined(JSONCONS_HAS_INT128)
+ template <class T>
+ struct is_int128_type<T,typename std::enable_if<std::is_same<T,int128_type>::value>::type> : std::true_type {};
+#endif
+
+ // is_unsigned_integer
+
+ template <class T, class Enable=void>
+ struct is_uint128_type : std::false_type {};
+
+#if defined (JSONCONS_HAS_INT128)
+ template <class T>
+ struct is_uint128_type<T,typename std::enable_if<std::is_same<T,uint128_type>::value>::type> : std::true_type {};
+#endif
+
+ template <class T, class Enable = void>
+ class integer_limits
+ {
+ public:
+ static constexpr bool is_specialized = false;
+ };
+
+ template <class T>
+ class integer_limits<T,typename std::enable_if<std::is_integral<T>::value && !std::is_same<T,bool>::value>::type>
+ {
+ public:
+ static constexpr bool is_specialized = true;
+ static constexpr bool is_signed = std::numeric_limits<T>::is_signed;
+ static constexpr int digits = std::numeric_limits<T>::digits;
+ static constexpr std::size_t buffer_size = static_cast<std::size_t>(sizeof(T)*CHAR_BIT*0.302) + 3;
+
+ static constexpr T(max)() noexcept
+ {
+ return (std::numeric_limits<T>::max)();
+ }
+ static constexpr T(min)() noexcept
+ {
+ return (std::numeric_limits<T>::min)();
+ }
+ static constexpr T lowest() noexcept
+ {
+ return std::numeric_limits<T>::lowest();
+ }
+ };
+
+ template <class T>
+ class integer_limits<T,typename std::enable_if<!std::is_integral<T>::value && is_int128_type<T>::value>::type>
+ {
+ public:
+ static constexpr bool is_specialized = true;
+ 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 T(max)() noexcept
+ {
+ return (((((T)1 << (digits - 1)) - 1) << 1) + 1);
+ }
+ static constexpr T(min)() noexcept
+ {
+ return -(max)() - 1;
+ }
+ static constexpr T lowest() noexcept
+ {
+ return (min)();
+ }
+ };
+
+ template <class T>
+ class integer_limits<T,typename std::enable_if<!std::is_integral<T>::value && is_uint128_type<T>::value>::type>
+ {
+ public:
+ static constexpr bool is_specialized = true;
+ static constexpr bool is_signed = false;
+ static constexpr int digits = sizeof(T)*CHAR_BIT;
+
+ static constexpr T(max)() noexcept
+ {
+ return T(T(~0));
+ }
+ static constexpr T(min)() noexcept
+ {
+ return 0;
+ }
+ static constexpr T lowest() noexcept
+ {
+ return std::numeric_limits<T>::lowest();
+ }
+ };
+
+ #ifndef JSONCONS_HAS_VOID_T
+ // follows https://en.cppreference.com/w/cpp/types/void_t
+ template<typename... Ts> struct make_void { typedef void type;};
+ template<typename... Ts> using void_t = typename make_void<Ts...>::type;
+ #else
+ using void_t = std::void_t;
+ #endif
+
+ // follows http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4436.pdf
+
+ // detector
+
+ // primary template handles all types not supporting the archetypal Op
+ template<
+ class Default,
+ class, // always void; supplied externally
+ template<class...> class Op,
+ class... Args
+ >
+ struct detector
+ {
+ constexpr static auto value = false;
+ using type = Default;
+ };
+
+ // specialization recognizes and handles only types supporting Op
+ template<
+ class Default,
+ template<class...> class Op,
+ class... Args
+ >
+ struct detector<Default, void_t<Op<Args...>>, Op, Args...>
+ {
+ constexpr static auto value = true;
+ using type = Op<Args...>;
+ };
+
+ // is_detected, is_detected_t
+
+ template< template<class...> class Op, class... Args >
+ using
+ is_detected = detector<void, void, Op, Args...>;
+
+ template< template<class...> class Op, class... Args >
+ using
+ is_detected_t = typename is_detected<Op, Args...>::type;
+
+ // detected_or, detected_or_t
+
+ template< class Default, template<class...> class Op, class... Args >
+ using
+ detected_or = detector<Default, void, Op, Args...>;
+
+ template< class Default, template<class...> class Op, class... Args >
+ using
+ detected_or_t = typename detected_or<Default, Op, Args...>::type;
+
+ // is_detected_exact
+
+ template< class Expected, template<class...> class Op, class... Args >
+ using
+ is_detected_exact = std::is_same< Expected, is_detected_t<Op, Args...> >;
+
+ // is_detected_convertible
+
+ template< class To, template<class...> class Op, class... Args >
+ using
+ is_detected_convertible = std::is_convertible< is_detected_t<Op, Args...>, To >;
+
+ template <typename T>
+ struct is_stateless
+ : public std::integral_constant<bool,
+ (std::is_default_constructible<T>::value &&
+ std::is_empty<T>::value)>
+ {};
+
+ // to_plain_pointer
+
+ template<class Pointer> inline
+ typename std::pointer_traits<Pointer>::element_type* to_plain_pointer(Pointer ptr)
+ {
+ return (std::addressof(*ptr));
+ }
+
+ template<class T> inline
+ T * to_plain_pointer(T * ptr)
+ {
+ return (ptr);
+ }
+
+ // is_std_byte
+
+ template <class T, class Enable=void>
+ struct is_std_byte : std::false_type {};
+#if defined(JSONCONS_HAS_STD_BYTE)
+ template <class T>
+ struct is_std_byte<T,
+ typename std::enable_if<std::is_same<T,std::byte>::value
+ >::type> : std::true_type {};
+#endif
+ // is_byte
+
+ template <class T, class Enable=void>
+ struct is_byte : std::false_type {};
+
+ template <class T>
+ struct is_byte<T,
+ typename std::enable_if<std::is_same<T,char>::value ||
+ std::is_same<T,signed char>::value ||
+ std::is_same<T,unsigned char>::value ||
+ is_std_byte<T>::value
+ >::type> : std::true_type {};
+
+ // is_character
+
+ template <class T, class Enable=void>
+ struct is_character : std::false_type {};
+
+ template <class T>
+ struct is_character<T,
+ typename std::enable_if<std::is_same<T,char>::value ||
+ std::is_same<T,wchar_t>::value
+ >::type> : std::true_type {};
+
+ // is_narrow_character
+
+ template <class T, class Enable=void>
+ struct is_narrow_character : std::false_type {};
+
+ template <class T>
+ struct is_narrow_character<T,
+ typename std::enable_if<is_character<T>::value && (sizeof(T) == sizeof(char))
+ >::type> : std::true_type {};
+
+ // is_wide_character
+
+ template <class T, class Enable=void>
+ struct is_wide_character : std::false_type {};
+
+ template <class T>
+ struct is_wide_character<T,
+ typename std::enable_if<is_character<T>::value && (sizeof(T) != sizeof(char))
+ >::type> : std::true_type {};
+
+ // From boost
+ namespace ut_detail {
+
+ template<typename T>
+ struct is_cstring_impl : public std::false_type {};
+
+ template<typename T>
+ struct is_cstring_impl<T const*> : public is_cstring_impl<T*> {};
+
+ template<typename T>
+ struct is_cstring_impl<T const* const> : public is_cstring_impl<T*> {};
+
+ template<>
+ struct is_cstring_impl<char*> : public std::true_type {};
+
+ template<>
+ struct is_cstring_impl<wchar_t*> : public std::true_type {};
+
+ } // namespace ut_detail
+
+ template<typename T>
+ struct is_cstring : public ut_detail::is_cstring_impl<typename std::decay<T>::type> {};
+
+ // is_bool
+
+ template <class T, class Enable=void>
+ struct is_bool : std::false_type {};
+
+ template <class T>
+ struct is_bool<T,
+ typename std::enable_if<std::is_same<T,bool>::value
+ >::type> : std::true_type {};
+
+ // is_u8_u16_u32_or_u64
+
+ template <class T, class Enable=void>
+ struct is_u8_u16_u32_or_u64 : std::false_type {};
+
+ template <class T>
+ struct is_u8_u16_u32_or_u64<T,
+ typename std::enable_if<std::is_same<T,uint8_t>::value ||
+ std::is_same<T,uint16_t>::value ||
+ std::is_same<T,uint32_t>::value ||
+ std::is_same<T,uint64_t>::value
+ >::type> : std::true_type {};
+
+ // is_int
+
+ template <class T, class Enable=void>
+ struct is_i8_i16_i32_or_i64 : std::false_type {};
+
+ template <class T>
+ struct is_i8_i16_i32_or_i64<T,
+ typename std::enable_if<std::is_same<T,int8_t>::value ||
+ std::is_same<T,int16_t>::value ||
+ std::is_same<T,int32_t>::value ||
+ std::is_same<T,int64_t>::value
+ >::type> : std::true_type {};
+
+ // is_float_or_double
+
+ template <class T, class Enable=void>
+ struct is_float_or_double : std::false_type {};
+
+ template <class T>
+ struct is_float_or_double<T,
+ typename std::enable_if<std::is_same<T,float>::value ||
+ std::is_same<T,double>::value
+ >::type> : std::true_type {};
+
+ // make_unsigned
+ template <class T>
+ struct make_unsigned_impl {using type = typename std::make_unsigned<T>::type;};
+
+ #if defined(JSONCONS_HAS_INT128)
+ template <>
+ struct make_unsigned_impl<int128_type> {using type = uint128_type;};
+ template <>
+ struct make_unsigned_impl<uint128_type> {using type = uint128_type;};
+ #endif
+
+ template <class T>
+ struct make_unsigned
+ : make_unsigned_impl<typename std::remove_cv<T>::type>
+ {};
+
+ // is_integer
+
+ template <class T, class Enable=void>
+ struct is_integer : std::false_type {};
+
+ template <class T>
+ struct is_integer<T,typename std::enable_if<integer_limits<T>::is_specialized>::type> : std::true_type {};
+
+ // is_signed_integer
+
+ template <class T, class Enable=void>
+ struct is_signed_integer : std::false_type {};
+
+ template <class T>
+ struct is_signed_integer<T, typename std::enable_if<integer_limits<T>::is_specialized &&
+ integer_limits<T>::is_signed>::type> : std::true_type {};
+
+ // is_unsigned_integer
+
+ template <class T, class Enable=void>
+ struct is_unsigned_integer : std::false_type {};
+
+ template <class T>
+ struct is_unsigned_integer<T,
+ typename std::enable_if<integer_limits<T>::is_specialized &&
+ !integer_limits<T>::is_signed>::type> : std::true_type {};
+
+ // is_primitive
+
+ template <class T, class Enable=void>
+ struct is_primitive : std::false_type {};
+
+ template <class T>
+ struct is_primitive<T,
+ typename std::enable_if<is_integer<T>::value ||
+ is_bool<T>::value ||
+ std::is_floating_point<T>::value
+ >::type> : std::true_type {};
+
+ // Containers
+
+ template <class Container>
+ using
+ container_npos_t = decltype(Container::npos);
+
+ template <class Container>
+ using
+ container_allocator_type_t = typename Container::allocator_type;
+
+ template <class Container>
+ using
+ container_mapped_type_t = typename Container::mapped_type;
+
+ template <class Container>
+ using
+ container_key_type_t = typename Container::key_type;
+
+ template <class Container>
+ using
+ container_value_type_t = typename std::iterator_traits<typename Container::iterator>::value_type;
+
+ template <class Container>
+ using
+ container_char_traits_t = typename Container::traits_type::char_type;
+
+ template<class Container>
+ using
+ container_push_back_t = decltype(std::declval<Container>().push_back(std::declval<typename Container::value_type>()));
+
+ template<class Container>
+ using
+ container_push_front_t = decltype(std::declval<Container>().push_front(std::declval<typename Container::value_type>()));
+
+ template<class Container>
+ using
+ container_insert_t = decltype(std::declval<Container>().insert(std::declval<typename Container::value_type>()));
+
+ template<class Container>
+ using
+ container_reserve_t = decltype(std::declval<Container>().reserve(typename Container::size_type()));
+
+ template<class Container>
+ using
+ container_data_t = decltype(std::declval<Container>().data());
+
+ template<class Container>
+ using
+ container_size_t = decltype(std::declval<Container>().size());
+
+ // is_string_or_string_view
+
+ template <class T, class Enable=void>
+ struct is_string_or_string_view : std::false_type {};
+
+ template <class T>
+ struct is_string_or_string_view<T,
+ typename std::enable_if<is_character<typename T::value_type>::value &&
+ is_detected_exact<typename T::value_type,container_char_traits_t,T>::value &&
+ is_detected<container_npos_t,T>::value
+ >::type> : std::true_type {};
+
+ // is_basic_string
+
+ template <class T, class Enable=void>
+ struct is_basic_string : std::false_type {};
+
+ template <class T>
+ struct is_basic_string<T,
+ typename std::enable_if<is_string_or_string_view<T>::value &&
+ is_detected<container_allocator_type_t,T>::value
+ >::type> : std::true_type {};
+
+ // is_basic_string_view
+
+ template <class T, class Enable=void>
+ struct is_basic_string_view : std::false_type {};
+
+ template <class T>
+ struct is_basic_string_view<T,
+ typename std::enable_if<is_string_or_string_view<T>::value &&
+ !is_detected<container_allocator_type_t,T>::value
+ >::type> : std::true_type {};
+
+ // is_map_like
+
+ template <class T, class Enable=void>
+ struct is_map_like : std::false_type {};
+
+ template <class T>
+ struct is_map_like<T,
+ typename std::enable_if<is_detected<container_mapped_type_t,T>::value &&
+ is_detected<container_allocator_type_t,T>::value &&
+ is_detected<container_key_type_t,T>::value &&
+ is_detected<container_value_type_t,T>::value
+ >::type>
+ : std::true_type {};
+
+ // is_std_array
+ template<class T>
+ struct is_std_array : std::false_type {};
+
+ template<class E, std::size_t N>
+ struct is_std_array<std::array<E, N>> : std::true_type {};
+
+ // is_list_like
+
+ template <class T, class Enable=void>
+ struct is_list_like : std::false_type {};
+
+ template <class T>
+ struct is_list_like<T,
+ typename std::enable_if<is_detected<container_value_type_t,T>::value &&
+ is_detected<container_allocator_type_t,T>::value &&
+ !is_std_array<T>::value &&
+ !is_detected_exact<typename T::value_type,container_char_traits_t,T>::value &&
+ !is_map_like<T>::value
+ >::type>
+ : std::true_type {};
+
+ // is_constructible_from_const_pointer_and_size
+
+ template <class T, class Enable=void>
+ struct is_constructible_from_const_pointer_and_size : std::false_type {};
+
+ template <class T>
+ struct is_constructible_from_const_pointer_and_size<T,
+ typename std::enable_if<std::is_constructible<T,typename T::const_pointer,typename T::size_type>::value
+ >::type>
+ : std::true_type {};
+
+ // has_reserve
+
+ template<class Container>
+ using
+ has_reserve = is_detected<container_reserve_t, Container>;
+
+ // is_back_insertable
+
+ template<class Container>
+ using
+ is_back_insertable = is_detected<container_push_back_t, Container>;
+
+ // is_front_insertable
+
+ template<class Container>
+ using
+ is_front_insertable = is_detected<container_push_front_t, Container>;
+
+ // is_insertable
+
+ template<class Container>
+ using
+ is_insertable = is_detected<container_insert_t, Container>;
+
+ // has_data, has_data_exact
+
+ template<class Container>
+ using
+ has_data = is_detected<container_data_t, Container>;
+
+ template<class Ret, class Container>
+ using
+ has_data_exact = is_detected_exact<Ret, container_data_t, Container>;
+
+ // has_size
+
+ template<class Container>
+ using
+ has_size = is_detected<container_size_t, Container>;
+
+ // has_data_and_size
+
+ template<class Container>
+ struct has_data_and_size
+ {
+ static constexpr bool value = has_data<Container>::value && has_size<Container>::value;
+ };
+
+ // is_byte_sequence
+
+ template <class Container, class Enable=void>
+ struct is_byte_sequence : std::false_type {};
+
+ template <class Container>
+ struct is_byte_sequence<Container,
+ typename std::enable_if<has_data_exact<const typename Container::value_type*,const Container>::value &&
+ has_size<Container>::value &&
+ is_byte<typename Container::value_type>::value
+ >::type> : std::true_type {};
+
+ // is_char_sequence
+
+ template <class Container, class Enable=void>
+ struct is_char_sequence : std::false_type {};
+
+ template <class Container>
+ struct is_char_sequence<Container,
+ typename std::enable_if<has_data_exact<const typename Container::value_type*,const Container>::value &&
+ has_size<Container>::value &&
+ is_character<typename Container::value_type>::value
+ >::type> : std::true_type {};
+
+ // is_sequence_of
+
+ template <class Container, class ValueT, class Enable=void>
+ struct is_sequence_of : std::false_type {};
+
+ template <class Container, class ValueT>
+ struct is_sequence_of<Container,ValueT,
+ typename std::enable_if<has_data_exact<const typename Container::value_type*,const Container>::value &&
+ has_size<Container>::value &&
+ std::is_same<typename Container::value_type,ValueT>::value
+ >::type> : std::true_type {};
+
+ // is_back_insertable_byte_container
+
+ template <class Container, class Enable=void>
+ struct is_back_insertable_byte_container : std::false_type {};
+
+ template <class Container>
+ struct is_back_insertable_byte_container<Container,
+ typename std::enable_if<is_back_insertable<Container>::value &&
+ is_byte<typename Container::value_type>::value
+ >::type> : std::true_type {};
+
+ // is_back_insertable_char_container
+
+ template <class Container, class Enable=void>
+ struct is_back_insertable_char_container : std::false_type {};
+
+ template <class Container>
+ struct is_back_insertable_char_container<Container,
+ typename std::enable_if<is_back_insertable<Container>::value &&
+ is_character<typename Container::value_type>::value
+ >::type> : std::true_type {};
+
+ // is_back_insertable_container_of
+
+ template <class Container, class ValueT, class Enable=void>
+ struct is_back_insertable_container_of : std::false_type {};
+
+ template <class Container, class ValueT>
+ struct is_back_insertable_container_of<Container, ValueT,
+ typename std::enable_if<is_back_insertable<Container>::value &&
+ std::is_same<typename Container::value_type,ValueT>::value
+ >::type> : std::true_type {};
+
+ // is_c_array
+
+ template<class T>
+ struct is_c_array : std::false_type {};
+
+ template<class T>
+ struct is_c_array<T[]> : std::true_type {};
+
+ template<class T, std::size_t N>
+ struct is_c_array<T[N]> : std::true_type {};
+
+namespace impl {
+
+ template<class C, class Enable=void>
+ struct is_typed_array : std::false_type {};
+
+ template<class T>
+ struct is_typed_array
+ <
+ T,
+ typename std::enable_if<is_list_like<T>::value &&
+ (std::is_same<typename std::decay<typename T::value_type>::type,uint8_t>::value ||
+ std::is_same<typename std::decay<typename T::value_type>::type,uint16_t>::value ||
+ std::is_same<typename std::decay<typename T::value_type>::type,uint32_t>::value ||
+ std::is_same<typename std::decay<typename T::value_type>::type,uint64_t>::value ||
+ std::is_same<typename std::decay<typename T::value_type>::type,int8_t>::value ||
+ std::is_same<typename std::decay<typename T::value_type>::type,int16_t>::value ||
+ std::is_same<typename std::decay<typename T::value_type>::type,int32_t>::value ||
+ std::is_same<typename std::decay<typename T::value_type>::type,int64_t>::value ||
+ std::is_same<typename std::decay<typename T::value_type>::type,float_t>::value ||
+ std::is_same<typename std::decay<typename T::value_type>::type,double_t>::value)>::type
+ > : std::true_type{};
+
+} // namespace impl
+
+ template <typename T>
+ using is_typed_array = impl::is_typed_array<typename std::decay<T>::type>;
+
+ // is_compatible_element
+
+ template<class Container, class Element, class Enable=void>
+ struct is_compatible_element : std::false_type {};
+
+ template<class Container, class Element>
+ struct is_compatible_element
+ <
+ Container, Element,
+ typename std::enable_if<has_data<Container>::value>::type>
+ : std::is_convertible< typename std::remove_pointer<decltype(std::declval<Container>().data() )>::type(*)[], Element(*)[]>
+ {};
+
+ template<typename T>
+ using
+ construct_from_string_t = decltype(T(std::string{}));
+
+
+ template<class T>
+ using
+ is_constructible_from_string = is_detected<construct_from_string_t,T>;
+
+ template<typename T, typename Data, typename Size>
+ using
+ construct_from_data_size_t = decltype(T(static_cast<Data>(nullptr),Size{}));
+
+
+ template<class T, typename Data, typename Size>
+ using
+ is_constructible_from_data_size = is_detected<construct_from_data_size_t,T,Data,Size>;
+
+ // is_unary_function_object
+ // is_unary_function_object_exact
+
+ template<class FunctionObject, class Arg>
+ using
+ unary_function_object_t = decltype(std::declval<FunctionObject>()(std::declval<Arg>()));
+
+ template<class FunctionObject, class Arg>
+ using
+ is_unary_function_object = is_detected<unary_function_object_t, FunctionObject, Arg>;
+
+ template<class FunctionObject, class T, class Arg>
+ using
+ is_unary_function_object_exact = is_detected_exact<T,unary_function_object_t, FunctionObject, Arg>;
+
+ // is_binary_function_object
+ // is_binary_function_object_exact
+
+ template<class FunctionObject, class Arg1, class Arg2>
+ using
+ binary_function_object_t = decltype(std::declval<FunctionObject>()(std::declval<Arg1>(),std::declval<Arg2>()));
+
+ template<class FunctionObject, class Arg1, class Arg2>
+ using
+ is_binary_function_object = is_detected<binary_function_object_t, FunctionObject, Arg1, Arg2>;
+
+ template<class FunctionObject, class T, class Arg1, class Arg2>
+ using
+ is_binary_function_object_exact = is_detected_exact<T,binary_function_object_t, FunctionObject, Arg1, Arg2>;
+
+ template <class Source, class Enable=void>
+ struct is_convertible_to_string_view : std::false_type {};
+
+ template <class Source>
+ struct is_convertible_to_string_view<Source,typename std::enable_if<is_string_or_string_view<Source>::value ||
+ is_cstring<Source>::value
+ >::type> : std::true_type {};
+
+ #if defined(JSONCONS_HAS_2017)
+ template <typename T>
+ using is_nothrow_swappable = std::is_nothrow_swappable<T>;
+ #else
+ template <typename T>
+ struct is_nothrow_swappable {
+ static const bool value = noexcept(swap(std::declval<T&>(), std::declval<T&>()));
+ };
+ #endif
+
+ #if defined(JSONCONS_HAS_2014)
+ template <class T>
+ using alignment_of = std::alignment_of<T>;
+
+ template< class T, T... Ints >
+ using integer_sequence = std::integer_sequence<T,Ints...>;
+
+ template <T ... Inds>
+ using index_sequence = std::index_sequence<Inds...>;
+
+ template <class T, T N>
+ using make_integer_sequence = std::make_integer_sequence<T,N>;
+
+ template <std::size_t N>
+ using make_index_sequence = std::make_index_sequence<N>;
+
+ template<class... T>
+ using index_sequence_for = std::index_sequence_for<T...>;
+
+ #else
+ template <class T>
+ struct alignment_of
+ : std::integral_constant<std::size_t, alignof(typename std::remove_all_extents<T>::type)> {};
+
+ template <class T, T... Ints>
+ class integer_sequence
+ {
+ public:
+ using value_type = T;
+ static_assert(std::is_integral<value_type>::value, "not integral type");
+ static constexpr std::size_t size() noexcept
+ {
+ return sizeof...(Ints);
+ }
+ };
+
+ template <std::size_t... Inds>
+ using index_sequence = integer_sequence<std::size_t, Inds...>;
+ namespace detail_ {
+ template <class T, T Begin, T End, bool>
+ struct IntSeqImpl {
+ using TValue = T;
+ static_assert(std::is_integral<TValue>::value, "not integral type");
+ static_assert(Begin >= 0 && Begin < End, "unexpected argument (Begin<0 || Begin<=End)");
+
+ template <class, class>
+ struct IntSeqCombiner;
+
+ template <TValue... Inds0, TValue... Inds1>
+ struct IntSeqCombiner<integer_sequence<TValue, Inds0...>, integer_sequence<TValue, Inds1...>> {
+ using TResult = integer_sequence<TValue, Inds0..., Inds1...>;
+ };
+
+ using TResult =
+ typename IntSeqCombiner<typename IntSeqImpl<TValue, Begin, Begin + (End - Begin) / 2,
+ (End - Begin) / 2 == 1>::TResult,
+ typename IntSeqImpl<TValue, Begin + (End - Begin) / 2, End,
+ (End - Begin + 1) / 2 == 1>::TResult>::TResult;
+ };
+
+ template <class T, T Begin>
+ struct IntSeqImpl<T, Begin, Begin, false> {
+ using TValue = T;
+ static_assert(std::is_integral<TValue>::value, "not integral type");
+ static_assert(Begin >= 0, "unexpected argument (Begin<0)");
+ using TResult = integer_sequence<TValue>;
+ };
+
+ template <class T, T Begin, T End>
+ struct IntSeqImpl<T, Begin, End, true> {
+ using TValue = T;
+ static_assert(std::is_integral<TValue>::value, "not integral type");
+ static_assert(Begin >= 0, "unexpected argument (Begin<0)");
+ using TResult = integer_sequence<TValue, Begin>;
+ };
+ } // namespace detail_
+
+ template <class T, T N>
+ using make_integer_sequence = typename detail_::IntSeqImpl<T, 0, N, (N - 0) == 1>::TResult;
+
+ template <std::size_t N>
+ using make_index_sequence = make_integer_sequence<std::size_t, N>;
+
+ template <class... T>
+ using index_sequence_for = make_index_sequence<sizeof...(T)>;
+
+
+ #endif
+
+} // type_traits
+} // jsoncons
+
+#endif
diff --git a/include/jsoncons/pretty_print.hpp b/include/jsoncons/pretty_print.hpp
new file mode 100644
index 0000000..599c1ce
--- /dev/null
+++ b/include/jsoncons/pretty_print.hpp
@@ -0,0 +1,89 @@
+// Copyright 2013 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_PRETTY_PRINT_HPP
+#define JSONCONS_PRETTY_PRINT_HPP
+
+#include <string>
+#include <exception>
+#include <cstring>
+#include <ostream>
+#include <memory>
+#include <typeinfo>
+#include <cstring>
+#include <jsoncons/json_exception.hpp>
+#include <jsoncons/json_options.hpp>
+#include <jsoncons/json_encoder.hpp>
+#include <jsoncons/json_type_traits.hpp>
+#include <jsoncons/json_error.hpp>
+
+namespace jsoncons {
+
+template<class Json>
+class json_printable
+{
+public:
+ using char_type = typename Json::char_type;
+
+ json_printable(const Json& j, indenting line_indent)
+ : j_(&j), indenting_(line_indent)
+ {
+ }
+
+ json_printable(const Json& j,
+ const basic_json_encode_options<char_type>& options,
+ indenting line_indent)
+ : j_(&j), options_(options), indenting_(line_indent)
+ {
+ }
+
+ void dump(std::basic_ostream<char_type>& os) const
+ {
+ j_->dump(os, options_, indenting_);
+ }
+
+ friend std::basic_ostream<char_type>& operator<<(std::basic_ostream<char_type>& os, const json_printable<Json>& pr)
+ {
+ pr.dump(os);
+ return os;
+ }
+
+ const Json *j_;
+ basic_json_encode_options<char_type> options_;
+ indenting indenting_;
+private:
+ json_printable();
+};
+
+template<class Json>
+json_printable<Json> print(const Json& j)
+{
+ return json_printable<Json>(j, indenting::no_indent);
+}
+
+template<class Json>
+json_printable<Json> print(const Json& j,
+ const basic_json_encode_options<typename Json::char_type>& options)
+{
+ return json_printable<Json>(j, options, indenting::no_indent);
+}
+
+template<class Json>
+json_printable<Json> pretty_print(const Json& j)
+{
+ return json_printable<Json>(j, indenting::indent);
+}
+
+template<class Json>
+json_printable<Json> pretty_print(const Json& j,
+ const basic_json_encode_options<typename Json::char_type>& options)
+{
+ return json_printable<Json>(j, options, indenting::indent);
+}
+
+}
+
+#endif
diff --git a/include/jsoncons/ser_context.hpp b/include/jsoncons/ser_context.hpp
new file mode 100644
index 0000000..f5b9d14
--- /dev/null
+++ b/include/jsoncons/ser_context.hpp
@@ -0,0 +1,57 @@
+/// Copyright 2013-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)
+
+// See https://github.com/danielaparker/jsoncons for latest version
+
+#ifndef JSONCONS_SER_CONTEXT_HPP
+#define JSONCONS_SER_CONTEXT_HPP
+
+namespace jsoncons {
+
+class ser_context
+{
+public:
+ virtual ~ser_context() noexcept = default;
+
+ virtual size_t line() const
+ {
+ return 0;
+ }
+
+ virtual size_t column() const
+ {
+ return 0;
+ }
+
+ virtual size_t position() const
+ {
+ return 0;
+ }
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+ JSONCONS_DEPRECATED_MSG("Instead, use line()")
+ std::size_t line_number() const
+ {
+ return line();
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use column()")
+ std::size_t column_number() const
+ {
+ return column();
+ }
+#endif
+};
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+JSONCONS_DEPRECATED_MSG("Instead, use ser_context") typedef ser_context null_ser_context;
+
+JSONCONS_DEPRECATED_MSG("Instead, use ser_context") typedef ser_context parsing_context;
+JSONCONS_DEPRECATED_MSG("Instead, use ser_context") typedef ser_context serializing_context;
+JSONCONS_DEPRECATED_MSG("Instead, use ser_context") typedef ser_context null_parsing_context;
+JSONCONS_DEPRECATED_MSG("Instead, use ser_context") typedef ser_context null_serializing_context;
+#endif
+
+}
+#endif
diff --git a/include/jsoncons/sink.hpp b/include/jsoncons/sink.hpp
new file mode 100644
index 0000000..84771a3
--- /dev/null
+++ b/include/jsoncons/sink.hpp
@@ -0,0 +1,289 @@
+// 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)
+
+// See https://github.com/danielaparker/jsoncons for latest version
+
+#ifndef JSONCONS_SINK_HPP
+#define JSONCONS_SINK_HPP
+
+#include <stdexcept>
+#include <string>
+#include <vector>
+#include <ostream>
+#include <cmath>
+#include <exception>
+#include <memory> // std::addressof
+#include <cstring> // std::memcpy
+#include <jsoncons/config/jsoncons_config.hpp>
+#include <jsoncons/more_type_traits.hpp>
+
+namespace jsoncons {
+
+ // stream_sink
+
+ template <class CharT>
+ class stream_sink
+ {
+ public:
+ using value_type = CharT;
+ using container_type = std::basic_ostream<CharT>;
+
+ private:
+ static constexpr size_t default_buffer_length = 16384;
+
+ std::basic_ostream<CharT>* stream_ptr_;
+ std::vector<CharT> buffer_;
+ CharT * begin_buffer_;
+ const CharT* end_buffer_;
+ CharT* p_;
+
+ // Noncopyable
+ stream_sink(const stream_sink&) = delete;
+ stream_sink& operator=(const stream_sink&) = delete;
+
+ public:
+ stream_sink(stream_sink&&) = default;
+
+ stream_sink(std::basic_ostream<CharT>& os)
+ : stream_ptr_(std::addressof(os)), buffer_(default_buffer_length), begin_buffer_(buffer_.data()), end_buffer_(begin_buffer_+buffer_.size()), p_(begin_buffer_)
+ {
+ }
+ stream_sink(std::basic_ostream<CharT>& os, std::size_t buflen)
+ : stream_ptr_(std::addressof(os)), buffer_(buflen), begin_buffer_(buffer_.data()), end_buffer_(begin_buffer_+buffer_.size()), p_(begin_buffer_)
+ {
+ }
+ ~stream_sink() noexcept
+ {
+ stream_ptr_->write(begin_buffer_, buffer_length());
+ stream_ptr_->flush();
+ }
+
+ // Movable
+ stream_sink& operator=(stream_sink&&) = default;
+
+ void flush()
+ {
+ stream_ptr_->write(begin_buffer_, buffer_length());
+ stream_ptr_->flush();
+ p_ = buffer_.data();
+ }
+
+ void append(const CharT* s, std::size_t length)
+ {
+ std::size_t diff = end_buffer_ - p_;
+ if (diff >= length)
+ {
+ std::memcpy(p_, s, length*sizeof(CharT));
+ p_ += length;
+ }
+ else
+ {
+ stream_ptr_->write(begin_buffer_, buffer_length());
+ stream_ptr_->write(s,length);
+ p_ = begin_buffer_;
+ }
+ }
+
+ void push_back(CharT ch)
+ {
+ if (p_ < end_buffer_)
+ {
+ *p_++ = ch;
+ }
+ else
+ {
+ stream_ptr_->write(begin_buffer_, buffer_length());
+ p_ = begin_buffer_;
+ push_back(ch);
+ }
+ }
+ private:
+
+ std::size_t buffer_length() const
+ {
+ return p_ - begin_buffer_;
+ }
+ };
+
+ // binary_stream_sink
+
+ class binary_stream_sink
+ {
+ public:
+ typedef uint8_t value_type;
+ using container_type = std::basic_ostream<char>;
+ private:
+ static constexpr size_t default_buffer_length = 16384;
+
+ std::basic_ostream<char>* stream_ptr_;
+ std::vector<uint8_t> buffer_;
+ uint8_t * begin_buffer_;
+ const uint8_t* end_buffer_;
+ uint8_t* p_;
+
+ // Noncopyable
+ binary_stream_sink(const binary_stream_sink&) = delete;
+ binary_stream_sink& operator=(const binary_stream_sink&) = delete;
+
+ public:
+ binary_stream_sink(binary_stream_sink&&) = default;
+
+ binary_stream_sink(std::basic_ostream<char>& os)
+ : stream_ptr_(std::addressof(os)),
+ buffer_(default_buffer_length),
+ begin_buffer_(buffer_.data()),
+ end_buffer_(begin_buffer_+buffer_.size()),
+ p_(begin_buffer_)
+ {
+ }
+ binary_stream_sink(std::basic_ostream<char>& os, std::size_t buflen)
+ : stream_ptr_(std::addressof(os)),
+ buffer_(buflen),
+ begin_buffer_(buffer_.data()),
+ end_buffer_(begin_buffer_+buffer_.size()),
+ p_(begin_buffer_)
+ {
+ }
+ ~binary_stream_sink() noexcept
+ {
+ stream_ptr_->write((char*)begin_buffer_, buffer_length());
+ stream_ptr_->flush();
+ }
+
+ binary_stream_sink& operator=(binary_stream_sink&&) = default;
+
+ void flush()
+ {
+ stream_ptr_->write((char*)begin_buffer_, buffer_length());
+ p_ = buffer_.data();
+ }
+
+ void append(const uint8_t* s, std::size_t length)
+ {
+ std::size_t diff = end_buffer_ - p_;
+ if (diff >= length)
+ {
+ std::memcpy(p_, s, length*sizeof(uint8_t));
+ p_ += length;
+ }
+ else
+ {
+ stream_ptr_->write((char*)begin_buffer_, buffer_length());
+ stream_ptr_->write((const char*)s,length);
+ p_ = begin_buffer_;
+ }
+ }
+
+ void push_back(uint8_t ch)
+ {
+ if (p_ < end_buffer_)
+ {
+ *p_++ = ch;
+ }
+ else
+ {
+ stream_ptr_->write((char*)begin_buffer_, buffer_length());
+ p_ = begin_buffer_;
+ push_back(ch);
+ }
+ }
+ private:
+
+ std::size_t buffer_length() const
+ {
+ return p_ - begin_buffer_;
+ }
+ };
+
+ // string_sink
+
+ template <class StringT>
+ class string_sink
+ {
+ public:
+ using value_type = typename StringT::value_type;
+ using container_type = StringT;
+ private:
+ container_type* buf_ptr;
+
+ // Noncopyable
+ string_sink(const string_sink&) = delete;
+ string_sink& operator=(const string_sink&) = delete;
+ public:
+ string_sink(string_sink&& val) noexcept
+ : buf_ptr(nullptr)
+ {
+ std::swap(buf_ptr,val.buf_ptr);
+ }
+
+ string_sink(container_type& buf)
+ : buf_ptr(std::addressof(buf))
+ {
+ }
+
+ string_sink& operator=(string_sink&& val) noexcept
+ {
+ // TODO: Shouldn't val.buf_ptr be nullified?
+ // Also see move constructor above.
+ std::swap(buf_ptr,val.buf_ptr);
+ return *this;
+ }
+
+ void flush()
+ {
+ }
+
+ void append(const value_type* s, std::size_t length)
+ {
+ buf_ptr->insert(buf_ptr->end(), s, s+length);
+ }
+
+ void push_back(value_type ch)
+ {
+ buf_ptr->push_back(ch);
+ }
+ };
+
+ // bytes_sink
+
+ template <class Container, class = void>
+ class bytes_sink
+ {
+ };
+
+ template <class Container>
+ class bytes_sink<Container,typename std::enable_if<type_traits::is_back_insertable_byte_container<Container>::value>::type>
+ {
+ public:
+ using container_type = Container;
+ using value_type = typename Container::value_type;
+ private:
+ container_type* buf_ptr;
+
+ // Noncopyable
+ bytes_sink(const bytes_sink&) = delete;
+ bytes_sink& operator=(const bytes_sink&) = delete;
+ public:
+ bytes_sink(bytes_sink&&) = default;
+
+ bytes_sink(container_type& buf)
+ : buf_ptr(std::addressof(buf))
+ {
+ }
+
+ bytes_sink& operator=(bytes_sink&&) = default;
+
+ void flush()
+ {
+ }
+
+ void push_back(uint8_t ch)
+ {
+ buf_ptr->push_back(static_cast<value_type>(ch));
+ }
+ };
+
+} // namespace jsoncons
+
+#endif
diff --git a/include/jsoncons/source.hpp b/include/jsoncons/source.hpp
new file mode 100644
index 0000000..e9c0735
--- /dev/null
+++ b/include/jsoncons/source.hpp
@@ -0,0 +1,777 @@
+// 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)
+
+// See https://github.com/danielaparker/jsoncons for latest version
+
+#ifndef JSONCONS_SOURCE_HPP
+#define JSONCONS_SOURCE_HPP
+
+#include <stdexcept>
+#include <string>
+#include <vector>
+#include <istream>
+#include <memory> // std::addressof
+#include <cstring> // std::memcpy
+#include <exception>
+#include <iterator>
+#include <type_traits> // std::enable_if
+#include <jsoncons/config/jsoncons_config.hpp>
+#include <jsoncons/byte_string.hpp> // jsoncons::byte_traits
+#include <jsoncons/more_type_traits.hpp>
+
+namespace jsoncons {
+
+ template <class CharT>
+ class basic_null_istream : public std::basic_istream<CharT>
+ {
+ class null_buffer : public std::basic_streambuf<CharT>
+ {
+ null_buffer(const null_buffer&) = delete;
+ null_buffer& operator=(const null_buffer&) = delete;
+ public:
+ using typename std::basic_streambuf<CharT>::int_type;
+ using typename std::basic_streambuf<CharT>::traits_type;
+
+ null_buffer() = default;
+ null_buffer(null_buffer&&) = default;
+ null_buffer& operator=(null_buffer&&) = default;
+
+ int_type overflow( int_type ch = typename std::basic_streambuf<CharT>::traits_type::eof() ) override
+ {
+ return ch;
+ }
+ } nb_;
+ public:
+ basic_null_istream()
+ : std::basic_istream<CharT>(&nb_)
+ {
+ }
+
+ basic_null_istream(const null_buffer&) = delete;
+ basic_null_istream& operator=(const null_buffer&) = delete;
+ basic_null_istream(basic_null_istream&&) noexcept
+ : std::basic_istream<CharT>(&nb_)
+ {
+ }
+ basic_null_istream& operator=(basic_null_istream&&) noexcept
+ {
+ return *this;
+ }
+ };
+
+ template <class CharT>
+ struct char_result
+ {
+ CharT value;
+ bool eof;
+ };
+
+ // text sources
+
+ template <class CharT>
+ class stream_source
+ {
+ static constexpr std::size_t default_max_buffer_size = 16384;
+ public:
+ using value_type = CharT;
+ private:
+ using char_type = typename std::conditional<sizeof(CharT) == sizeof(char),char,CharT>::type;
+ basic_null_istream<char_type> null_is_;
+ std::basic_istream<char_type>* stream_ptr_;
+ std::basic_streambuf<char_type>* sbuf_;
+ std::size_t position_;
+ std::vector<value_type> buffer_;
+ const value_type* buffer_data_;
+ std::size_t buffer_length_;
+
+ // Noncopyable
+ stream_source(const stream_source&) = delete;
+ stream_source& operator=(const stream_source&) = delete;
+ public:
+ stream_source()
+ : stream_ptr_(&null_is_), sbuf_(null_is_.rdbuf()), position_(0),
+ buffer_(1), buffer_data_(buffer_.data()), buffer_length_(0)
+ {
+ }
+
+ stream_source(std::basic_istream<char_type>& is, std::size_t buf_size = default_max_buffer_size)
+ : stream_ptr_(std::addressof(is)), sbuf_(is.rdbuf()), position_(0),
+ buffer_(buf_size), buffer_data_(buffer_.data()), buffer_length_(0)
+ {
+ }
+
+ stream_source(stream_source&& other) noexcept
+ : stream_ptr_(&null_is_), sbuf_(null_is_.rdbuf()), position_(0),
+ buffer_(), buffer_data_(buffer_.data()), buffer_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();
+ }
+ }
+
+ ~stream_source()
+ {
+ }
+
+ stream_source& operator=(stream_source&& other) noexcept
+ {
+ 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;
+ }
+ return *this;
+ }
+
+ bool eof() const
+ {
+ return buffer_length_ == 0 && stream_ptr_->eof();
+ }
+
+ bool is_error() const
+ {
+ return stream_ptr_->bad();
+ }
+
+ std::size_t position() const
+ {
+ return position_;
+ }
+
+ void ignore(std::size_t length)
+ {
+ std::size_t len = 0;
+ if (buffer_length_ > 0)
+ {
+ len = (std::min)(buffer_length_, length);
+ position_ += len;
+ buffer_data_ += len;
+ buffer_length_ -= len;
+ }
+ while (length - len > 0)
+ {
+ fill_buffer();
+ if (buffer_length_ == 0)
+ {
+ break;
+ }
+ std::size_t len2 = (std::min)(buffer_length_, length-len);
+ position_ += len2;
+ buffer_data_ += len2;
+ buffer_length_ -= len2;
+ len += len2;
+ }
+ }
+
+ char_result<value_type> peek()
+ {
+ if (buffer_length_ == 0)
+ {
+ fill_buffer();
+ }
+ if (buffer_length_ > 0)
+ {
+ value_type c = *buffer_data_;
+ return char_result<value_type>{c, false};
+ }
+ else
+ {
+ return char_result<value_type>{0, true};
+ }
+ }
+
+ span<const value_type> read_buffer()
+ {
+ if (buffer_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;
+
+ return span<const value_type>(data, length);
+ }
+
+ std::size_t read(value_type* p, std::size_t length)
+ {
+ std::size_t len = 0;
+ if (buffer_length_ > 0)
+ {
+ len = (std::min)(buffer_length_, length);
+ std::memcpy(p, buffer_data_, len*sizeof(value_type));
+ buffer_data_ += len;
+ buffer_length_ -= len;
+ position_ += len;
+ }
+ if (length - len == 0)
+ {
+ return len;
+ }
+ else if (length - len < buffer_.size())
+ {
+ fill_buffer();
+ if (buffer_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;
+ position_ += len2;
+ len += len2;
+ }
+ return len;
+ }
+ else
+ {
+ if (stream_ptr_->eof())
+ {
+ buffer_length_ = 0;
+ return 0;
+ }
+ JSONCONS_TRY
+ {
+ std::streamsize count = sbuf_->sgetn(reinterpret_cast<char_type*>(p+len), length-len);
+ std::size_t len2 = static_cast<std::size_t>(count);
+ if (len2 < length-len)
+ {
+ stream_ptr_->clear(stream_ptr_->rdstate() | std::ios::eofbit);
+ }
+ len += len2;
+ position_ += len2;
+ return len;
+ }
+ JSONCONS_CATCH(const std::exception&)
+ {
+ stream_ptr_->clear(stream_ptr_->rdstate() | std::ios::badbit | std::ios::eofbit);
+ return 0;
+ }
+ }
+ }
+ private:
+ void fill_buffer()
+ {
+ if (stream_ptr_->eof())
+ {
+ buffer_length_ = 0;
+ return;
+ }
+
+ buffer_data_ = buffer_.data();
+ JSONCONS_TRY
+ {
+ std::streamsize count = sbuf_->sgetn(reinterpret_cast<char_type*>(buffer_.data()), buffer_.size());
+ buffer_length_ = static_cast<std::size_t>(count);
+
+ if (buffer_length_ < buffer_.size())
+ {
+ stream_ptr_->clear(stream_ptr_->rdstate() | std::ios::eofbit);
+ }
+ }
+ JSONCONS_CATCH(const std::exception&)
+ {
+ stream_ptr_->clear(stream_ptr_->rdstate() | std::ios::badbit | std::ios::eofbit);
+ buffer_length_ = 0;
+ }
+ }
+ };
+
+ // string_source
+
+ template <class CharT>
+ class string_source
+ {
+ public:
+ using value_type = CharT;
+ using string_view_type = jsoncons::basic_string_view<value_type>;
+ private:
+ const value_type* data_;
+ const value_type* current_;
+ const value_type* end_;
+
+ // Noncopyable
+ string_source(const string_source&) = delete;
+ string_source& operator=(const string_source&) = delete;
+ public:
+ string_source()
+ : data_(nullptr), current_(nullptr), end_(nullptr)
+ {
+ }
+
+ template <class Sourceable>
+ string_source(const Sourceable& s,
+ typename std::enable_if<type_traits::is_sequence_of<Sourceable,value_type>::value>::type* = 0)
+ : data_(s.data()), current_(s.data()), end_(s.data()+s.size())
+ {
+ }
+
+ string_source(const value_type* data)
+ : data_(data), current_(data), end_(data+std::char_traits<value_type>::length(data))
+ {
+ }
+
+ string_source(string_source&& val) = default;
+
+ string_source& operator=(string_source&& val) = default;
+
+ bool eof() const
+ {
+ return current_ == end_;
+ }
+
+ bool is_error() const
+ {
+ return false;
+ }
+
+ std::size_t position() const
+ {
+ return (current_ - data_)/sizeof(value_type);
+ }
+
+ void ignore(std::size_t count)
+ {
+ std::size_t len;
+ if ((std::size_t)(end_ - current_) < count)
+ {
+ len = end_ - current_;
+ }
+ else
+ {
+ len = count;
+ }
+ current_ += len;
+ }
+
+ char_result<value_type> peek()
+ {
+ return current_ < end_ ? char_result<value_type>{*current_, false} : char_result<value_type>{0, true};
+ }
+
+ span<const value_type> read_buffer()
+ {
+ const value_type* data = current_;
+ std::size_t length = end_ - current_;
+ current_ = end_;
+
+ return span<const value_type>(data, length);
+ }
+
+ std::size_t read(value_type* p, std::size_t length)
+ {
+ std::size_t len;
+ if ((std::size_t)(end_ - current_) < length)
+ {
+ len = end_ - current_;
+ }
+ else
+ {
+ len = length;
+ }
+ std::memcpy(p, current_, len*sizeof(value_type));
+ current_ += len;
+ return len;
+ }
+ };
+
+ // iterator source
+
+ template <class IteratorT>
+ class iterator_source
+ {
+ public:
+ using value_type = typename std::iterator_traits<IteratorT>::value_type;
+ private:
+ static constexpr std::size_t default_max_buffer_size = 16384;
+
+ IteratorT current_;
+ IteratorT end_;
+ std::size_t position_;
+ std::vector<value_type> buffer_;
+ std::size_t buffer_length_;
+
+ using difference_type = typename std::iterator_traits<IteratorT>::difference_type;
+ using iterator_category = typename std::iterator_traits<IteratorT>::iterator_category;
+
+ // Noncopyable
+ iterator_source(const iterator_source&) = delete;
+ iterator_source& operator=(const iterator_source&) = delete;
+ public:
+
+ iterator_source(const IteratorT& first, const IteratorT& last, std::size_t buf_size = default_max_buffer_size)
+ : current_(first), end_(last), position_(0), buffer_(buf_size), buffer_length_(0)
+ {
+ }
+
+ iterator_source(iterator_source&& other) = default;
+
+ iterator_source& operator=(iterator_source&& other) = default;
+
+ bool eof() const
+ {
+ return !(current_ != end_);
+ }
+
+ bool is_error() const
+ {
+ return false;
+ }
+
+ std::size_t position() const
+ {
+ return position_;
+ }
+
+ void ignore(std::size_t count)
+ {
+ while (count-- > 0 && current_ != end_)
+ {
+ ++position_;
+ ++current_;
+ }
+ }
+
+ char_result<value_type> peek()
+ {
+ return current_ != end_ ? char_result<value_type>{*current_, false} : char_result<value_type>{0, true};
+ }
+
+ span<const value_type> read_buffer()
+ {
+ if (buffer_length_ == 0)
+ {
+ buffer_length_ = read(buffer_.data(), buffer_.size());
+ }
+ std::size_t length = buffer_length_;
+ buffer_length_ = 0;
+
+ return span<const value_type>(buffer_.data(), length);
+ }
+
+ template <class Category = iterator_category>
+ typename std::enable_if<std::is_same<Category,std::random_access_iterator_tag>::value, std::size_t>::type
+ read(value_type* data, std::size_t length)
+ {
+ std::size_t count = (std::min)(length, static_cast<std::size_t>(std::distance(current_, end_)));
+
+ JSONCONS_COPY(current_, current_ + count, data);
+ current_ += count;
+ position_ += count;
+
+ return count;
+ }
+
+ template <class Category = iterator_category>
+ typename std::enable_if<!std::is_same<Category,std::random_access_iterator_tag>::value, std::size_t>::type
+ read(value_type* data, std::size_t length)
+ {
+ value_type* p = data;
+ value_type* pend = data + length;
+
+ while (p < pend && current_ != end_)
+ {
+ *p = static_cast<value_type>(*current_);
+ ++p;
+ ++current_;
+ }
+
+ position_ += (p - data);
+
+ return p - data;
+ }
+ };
+
+ // binary sources
+
+ using binary_stream_source = stream_source<uint8_t>;
+
+ class bytes_source
+ {
+ public:
+ typedef uint8_t value_type;
+ private:
+ const value_type* data_;
+ const value_type* current_;
+ const value_type* end_;
+
+ // Noncopyable
+ bytes_source(const bytes_source&) = delete;
+ bytes_source& operator=(const bytes_source&) = delete;
+ public:
+ bytes_source()
+ : data_(nullptr), current_(nullptr), end_(nullptr)
+ {
+ }
+
+ template <class Sourceable>
+ bytes_source(const Sourceable& source,
+ typename std::enable_if<type_traits::is_byte_sequence<Sourceable>::value,int>::type = 0)
+ : data_(reinterpret_cast<const value_type*>(source.data())),
+ current_(data_),
+ end_(data_+source.size())
+ {
+ }
+
+ bytes_source(bytes_source&&) = default;
+
+ bytes_source& operator=(bytes_source&&) = default;
+
+ bool eof() const
+ {
+ return current_ == end_;
+ }
+
+ bool is_error() const
+ {
+ return false;
+ }
+
+ std::size_t position() const
+ {
+ return current_ - data_;
+ }
+
+ void ignore(std::size_t count)
+ {
+ std::size_t len;
+ if ((std::size_t)(end_ - current_) < count)
+ {
+ len = end_ - current_;
+ }
+ else
+ {
+ len = count;
+ }
+ current_ += len;
+ }
+
+ char_result<value_type> peek()
+ {
+ return current_ < end_ ? char_result<value_type>{*current_, false} : char_result<value_type>{0, true};
+ }
+
+ span<const value_type> read_buffer()
+ {
+ const value_type* data = current_;
+ std::size_t length = end_ - current_;
+ current_ = end_;
+
+ return span<const value_type>(data, length);
+ }
+
+ std::size_t read(value_type* p, std::size_t length)
+ {
+ std::size_t len;
+ if ((std::size_t)(end_ - current_) < length)
+ {
+ len = end_ - current_;
+ }
+ else
+ {
+ len = length;
+ }
+ std::memcpy(p, current_, len*sizeof(value_type));
+ current_ += len;
+ return len;
+ }
+ };
+
+ // binary_iterator source
+
+ template <class IteratorT>
+ class binary_iterator_source
+ {
+ public:
+ using value_type = uint8_t;
+ private:
+ static constexpr std::size_t default_max_buffer_size = 16384;
+
+ IteratorT current_;
+ IteratorT end_;
+ std::size_t position_;
+ std::vector<value_type> buffer_;
+ std::size_t buffer_length_;
+
+ using difference_type = typename std::iterator_traits<IteratorT>::difference_type;
+ using iterator_category = typename std::iterator_traits<IteratorT>::iterator_category;
+
+ // Noncopyable
+ binary_iterator_source(const binary_iterator_source&) = delete;
+ binary_iterator_source& operator=(const binary_iterator_source&) = delete;
+ public:
+ binary_iterator_source(const IteratorT& first, const IteratorT& last, std::size_t buf_size = default_max_buffer_size)
+ : current_(first), end_(last), position_(0), buffer_(buf_size), buffer_length_(0)
+ {
+ }
+
+ binary_iterator_source(binary_iterator_source&& other) = default;
+
+ binary_iterator_source& operator=(binary_iterator_source&& other) = default;
+
+ bool eof() const
+ {
+ return !(current_ != end_);
+ }
+
+ bool is_error() const
+ {
+ return false;
+ }
+
+ std::size_t position() const
+ {
+ return position_;
+ }
+
+ void ignore(std::size_t count)
+ {
+ while (count-- > 0 && current_ != end_)
+ {
+ ++position_;
+ ++current_;
+ }
+ }
+
+ char_result<value_type> peek()
+ {
+ return current_ != end_ ? char_result<value_type>{static_cast<value_type>(*current_), false} : char_result<value_type>{0, true};
+ }
+
+ span<const value_type> read_buffer()
+ {
+ if (buffer_length_ == 0)
+ {
+ buffer_length_ = read(buffer_.data(), buffer_.size());
+ }
+ std::size_t length = buffer_length_;
+ buffer_length_ = 0;
+
+ return span<const value_type>(buffer_.data(), length);
+ }
+
+ template <class Category = iterator_category>
+ typename std::enable_if<std::is_same<Category,std::random_access_iterator_tag>::value, std::size_t>::type
+ read(value_type* data, std::size_t length)
+ {
+ std::size_t count = (std::min)(length, static_cast<std::size_t>(std::distance(current_, end_)));
+ JSONCONS_COPY(current_, current_ + count, data);
+ current_ += count;
+ position_ += count;
+
+ return count;
+ }
+
+ template <class Category = iterator_category>
+ typename std::enable_if<!std::is_same<Category,std::random_access_iterator_tag>::value, std::size_t>::type
+ read(value_type* data, std::size_t length)
+ {
+ value_type* p = data;
+ value_type* pend = data + length;
+
+ while (p < pend && current_ != end_)
+ {
+ *p = static_cast<value_type>(*current_);
+ ++p;
+ ++current_;
+ }
+
+ position_ += (p - data);
+
+ return p - data;
+ }
+ };
+
+ template <class Source>
+ struct source_reader
+ {
+ using value_type = typename Source::value_type;
+ static constexpr std::size_t max_buffer_length = 16384;
+
+ template <class Container>
+ static
+ typename std::enable_if<std::is_convertible<value_type,typename Container::value_type>::value &&
+ type_traits::has_reserve<Container>::value &&
+ type_traits::has_data_exact<value_type*,Container>::value
+ , std::size_t>::type
+ read(Source& source, Container& v, std::size_t length)
+ {
+ std::size_t unread = length;
+
+ std::size_t n = (std::min)(max_buffer_length, unread);
+ while (n > 0 && !source.eof())
+ {
+ std::size_t offset = v.size();
+ v.resize(v.size()+n);
+ std::size_t actual = source.read(v.data()+offset, n);
+ unread -= actual;
+ n = (std::min)(max_buffer_length, unread);
+ }
+
+ return length - unread;
+ }
+
+ template <class Container>
+ static
+ typename std::enable_if<std::is_convertible<value_type,typename Container::value_type>::value &&
+ type_traits::has_reserve<Container>::value &&
+ !type_traits::has_data_exact<value_type*, Container>::value
+ , std::size_t>::type
+ read(Source& source, Container& v, std::size_t length)
+ {
+ std::size_t unread = length;
+
+ std::size_t n = (std::min)(max_buffer_length, unread);
+ while (n > 0 && !source.eof())
+ {
+ v.reserve(v.size()+n);
+ std::size_t actual = 0;
+ while (actual < n)
+ {
+ typename Source::value_type c;
+ if (source.read(&c,1) != 1)
+ {
+ break;
+ }
+ v.push_back(c);
+ ++actual;
+ }
+ unread -= actual;
+ n = (std::min)(max_buffer_length, unread);
+ }
+
+ return length - unread;
+ }
+ };
+ template <class Source>
+ constexpr std::size_t source_reader<Source>::max_buffer_length;
+
+ #if !defined(JSONCONS_NO_DEPRECATED)
+ using bin_stream_source = binary_stream_source;
+ #endif
+
+} // namespace jsoncons
+
+#endif
diff --git a/include/jsoncons/source_adaptor.hpp b/include/jsoncons/source_adaptor.hpp
new file mode 100644
index 0000000..51c74a3
--- /dev/null
+++ b/include/jsoncons/source_adaptor.hpp
@@ -0,0 +1,148 @@
+// Copyright 2021 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_BUFFER_READER_HPP
+#define JSONCONS_BUFFER_READER_HPP
+
+#include <cstddef>
+#include <string>
+#include <vector>
+#include <stdexcept>
+#include <system_error>
+#include <memory> // std::allocator_traits
+#include <vector> // std::vector
+#include <jsoncons/unicode_traits.hpp>
+#include <jsoncons/json_error.hpp> // json_errc
+#include <jsoncons/source.hpp>
+#include <jsoncons/json_exception.hpp>
+
+namespace jsoncons {
+
+ // text_source_adaptor
+
+ template<class Source>
+ class text_source_adaptor
+ {
+ public:
+ using value_type = typename Source::value_type;
+ private:
+ Source source_;
+ bool bof_;
+
+ public:
+ text_source_adaptor()
+ : bof_(true)
+ {
+ }
+
+ template <class Sourceable>
+ text_source_adaptor(Sourceable&& source)
+ : source_(std::forward<Sourceable>(source)), bof_(true)
+ {
+ }
+
+ bool eof() const
+ {
+ return source_.eof();
+ }
+
+ bool is_error() const
+ {
+ return source_.is_error();
+ }
+
+ span<const value_type> read_buffer(std::error_code& ec)
+ {
+ if (source_.eof())
+ {
+ return span<const value_type>();
+ }
+
+ auto s = source_.read_buffer();
+ const value_type* data = s.data();
+ std::size_t length = s.size();
+
+ if (bof_ && length > 0)
+ {
+ auto r = unicode_traits::detect_encoding_from_bom(data, length);
+ if (!(r.encoding == unicode_traits::encoding_kind::utf8 || r.encoding == unicode_traits::encoding_kind::undetected))
+ {
+ ec = json_errc::illegal_unicode_character;
+ return span<const value_type>();
+ }
+ length -= (r.ptr - data);
+ data = r.ptr;
+ bof_ = false;
+ }
+ return span<const value_type>(data, length);
+ }
+ };
+
+ // json_source_adaptor
+
+ template<class Source>
+ class json_source_adaptor
+ {
+ public:
+ using value_type = typename Source::value_type;
+ private:
+ Source source_;
+ bool bof_;
+
+ public:
+ json_source_adaptor()
+ : bof_(true)
+ {
+ }
+
+ template <class Sourceable>
+ json_source_adaptor(Sourceable&& source)
+ : source_(std::forward<Sourceable>(source)), bof_(true)
+ {
+ }
+
+ bool eof() const
+ {
+ return source_.eof();
+ }
+
+ bool is_error() const
+ {
+ return source_.is_error();
+ }
+
+ span<const value_type> read_buffer(std::error_code& ec)
+ {
+ if (source_.eof())
+ {
+ return span<const value_type>();
+ }
+
+ auto s = source_.read_buffer();
+ const value_type* data = s.data();
+ std::size_t length = s.size();
+
+ if (bof_ && length > 0)
+ {
+ auto r = unicode_traits::detect_json_encoding(data, length);
+ if (!(r.encoding == unicode_traits::encoding_kind::utf8 || r.encoding == unicode_traits::encoding_kind::undetected))
+ {
+ ec = json_errc::illegal_unicode_character;
+ return span<const value_type>();
+ }
+ length -= (r.ptr - data);
+ data = r.ptr;
+ bof_ = false;
+ }
+
+ return span<const value_type>(data, length);
+ }
+ };
+
+} // namespace jsoncons
+
+#endif
+
diff --git a/include/jsoncons/staj2_cursor.hpp b/include/jsoncons/staj2_cursor.hpp
new file mode 100644
index 0000000..4cbbc2f
--- /dev/null
+++ b/include/jsoncons/staj2_cursor.hpp
@@ -0,0 +1,1178 @@
+// 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)
+
+// See https://github.com/danielaparker/jsoncons for latest version
+
+#ifndef JSONCONS_STAJ2_CURSOR_HPP
+#define JSONCONS_STAJ2_CURSOR_HPP
+
+#include <memory> // std::allocator
+#include <string>
+#include <stdexcept>
+#include <system_error>
+#include <ios>
+#include <type_traits> // std::enable_if
+#include <array> // std::array
+#include <functional> // std::function
+#include <jsoncons/json_exception.hpp>
+#include <jsoncons/json_visitor2.hpp>
+#include <jsoncons/bigint.hpp>
+#include <jsoncons/json_parser.hpp>
+#include <jsoncons/ser_context.hpp>
+#include <jsoncons/sink.hpp>
+#include <jsoncons/detail/write_number.hpp>
+#include <jsoncons/json_type_traits.hpp>
+#include <jsoncons/typed_array_view.hpp>
+#include <jsoncons/converter.hpp>
+
+namespace jsoncons {
+
+ enum class staj2_event_type
+ {
+ begin_array,
+ end_array,
+ begin_object,
+ end_object,
+ string_value,
+ byte_string_value,
+ null_value,
+ bool_value,
+ int64_value,
+ uint64_value,
+ half_value,
+ double_value
+ };
+
+ template <class CharT>
+ std::basic_ostream<CharT>& operator<<(std::basic_ostream<CharT>& os, staj2_event_type tag)
+ {
+ static constexpr const CharT* begin_array_name = JSONCONS_CSTRING_CONSTANT(CharT, "begin_array");
+ static constexpr const CharT* end_array_name = JSONCONS_CSTRING_CONSTANT(CharT, "end_array");
+ static constexpr const CharT* begin_object_name = JSONCONS_CSTRING_CONSTANT(CharT, "begin_object");
+ static constexpr const CharT* end_object_name = JSONCONS_CSTRING_CONSTANT(CharT, "end_object");
+ static constexpr const CharT* string_value_name = JSONCONS_CSTRING_CONSTANT(CharT, "string_value");
+ static constexpr const CharT* byte_string_value_name = JSONCONS_CSTRING_CONSTANT(CharT, "byte_string_value");
+ static constexpr const CharT* null_value_name = JSONCONS_CSTRING_CONSTANT(CharT, "null_value");
+ static constexpr const CharT* bool_value_name = JSONCONS_CSTRING_CONSTANT(CharT, "bool_value");
+ static constexpr const CharT* uint64_value_name = JSONCONS_CSTRING_CONSTANT(CharT, "uint64_value");
+ static constexpr const CharT* int64_value_name = JSONCONS_CSTRING_CONSTANT(CharT, "int64_value");
+ static constexpr const CharT* half_value_name = JSONCONS_CSTRING_CONSTANT(CharT, "half_value");
+ static constexpr const CharT* double_value_name = JSONCONS_CSTRING_CONSTANT(CharT, "double_value");
+
+ switch (tag)
+ {
+ case staj2_event_type::begin_array:
+ {
+ os << begin_array_name;
+ break;
+ }
+ case staj2_event_type::end_array:
+ {
+ os << end_array_name;
+ break;
+ }
+ case staj2_event_type::begin_object:
+ {
+ os << begin_object_name;
+ break;
+ }
+ case staj2_event_type::end_object:
+ {
+ os << end_object_name;
+ break;
+ }
+ case staj2_event_type::string_value:
+ {
+ os << string_value_name;
+ break;
+ }
+ case staj2_event_type::byte_string_value:
+ {
+ os << byte_string_value_name;
+ break;
+ }
+ case staj2_event_type::null_value:
+ {
+ os << null_value_name;
+ break;
+ }
+ case staj2_event_type::bool_value:
+ {
+ os << bool_value_name;
+ break;
+ }
+ case staj2_event_type::int64_value:
+ {
+ os << int64_value_name;
+ break;
+ }
+ case staj2_event_type::uint64_value:
+ {
+ os << uint64_value_name;
+ break;
+ }
+ case staj2_event_type::half_value:
+ {
+ os << half_value_name;
+ break;
+ }
+ case staj2_event_type::double_value:
+ {
+ os << double_value_name;
+ break;
+ }
+ }
+ return os;
+ }
+
+ template<class CharT>
+ class basic_staj2_event
+ {
+ staj2_event_type event_type_;
+ semantic_tag tag_;
+ uint64_t ext_tag_;
+ union
+ {
+ bool bool_value_;
+ int64_t int64_value_;
+ uint64_t uint64_value_;
+ uint16_t half_value_;
+ double double_value_;
+ const CharT* string_data_;
+ const uint8_t* byte_string_data_;
+ } value_;
+ std::size_t length_;
+ public:
+ using string_view_type = jsoncons::basic_string_view<CharT>;
+
+ basic_staj2_event(staj2_event_type event_type, semantic_tag tag = semantic_tag::none)
+ : event_type_(event_type), tag_(tag), ext_tag_(0), value_(), length_(0)
+ {
+ }
+
+ basic_staj2_event(staj2_event_type event_type, std::size_t length, semantic_tag tag = semantic_tag::none)
+ : event_type_(event_type), tag_(tag), ext_tag_(0), value_(), length_(length)
+ {
+ }
+
+ basic_staj2_event(null_type, semantic_tag tag)
+ : event_type_(staj2_event_type::null_value), tag_(tag), ext_tag_(0), value_(), length_(0)
+ {
+ }
+
+ basic_staj2_event(bool value, semantic_tag tag)
+ : event_type_(staj2_event_type::bool_value), tag_(tag), ext_tag_(0), length_(0)
+ {
+ value_.bool_value_ = value;
+ }
+
+ basic_staj2_event(int64_t value, semantic_tag tag)
+ : event_type_(staj2_event_type::int64_value), tag_(tag), ext_tag_(0), length_(0)
+ {
+ value_.int64_value_ = value;
+ }
+
+ basic_staj2_event(uint64_t value, semantic_tag tag)
+ : event_type_(staj2_event_type::uint64_value), tag_(tag), ext_tag_(0), length_(0)
+ {
+ value_.uint64_value_ = value;
+ }
+
+ basic_staj2_event(half_arg_t, uint16_t value, semantic_tag tag)
+ : event_type_(staj2_event_type::half_value), tag_(tag), ext_tag_(0), length_(0)
+ {
+ value_.half_value_ = value;
+ }
+
+ basic_staj2_event(double value, semantic_tag tag)
+ : event_type_(staj2_event_type::double_value), tag_(tag), ext_tag_(0), length_(0)
+ {
+ value_.double_value_ = value;
+ }
+
+ basic_staj2_event(const string_view_type& s,
+ staj2_event_type event_type,
+ semantic_tag tag = semantic_tag::none)
+ : event_type_(event_type), tag_(tag), ext_tag_(0), length_(s.length())
+ {
+ value_.string_data_ = s.data();
+ }
+
+ basic_staj2_event(const byte_string_view& s,
+ staj2_event_type event_type,
+ semantic_tag tag = semantic_tag::none)
+ : event_type_(event_type), tag_(tag), ext_tag_(0), length_(s.size())
+ {
+ value_.byte_string_data_ = s.data();
+ }
+
+ basic_staj2_event(const byte_string_view& s,
+ staj2_event_type event_type,
+ uint64_t ext_tag)
+ : event_type_(event_type), tag_(semantic_tag::ext), ext_tag_(ext_tag), length_(s.size())
+ {
+ value_.byte_string_data_ = s.data();
+ }
+
+ std::size_t size() const
+ {
+ return length_;
+ }
+
+ template <class T>
+ T get() const
+ {
+ std::error_code ec;
+ T val = get<T>(ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec));
+ }
+ return val;
+ }
+
+ template<class T, class CharT_ = CharT>
+ typename std::enable_if<type_traits::is_basic_string<T>::value && std::is_same<typename T::value_type, CharT_>::value, T>::type
+ get(std::error_code& ec) const
+ {
+ converter<T> conv;
+ switch (event_type_)
+ {
+ case staj2_event_type::string_value:
+ return conv.from(jsoncons::basic_string_view<CharT>(value_.string_data_, length_), tag(), ec);
+ case staj2_event_type::byte_string_value:
+ {
+ return conv.from(byte_string_view(value_.byte_string_data_,length_),
+ tag(),
+ ec);
+ }
+ case staj2_event_type::uint64_value:
+ {
+ return conv.from(value_.uint64_value_, tag(), ec);
+ }
+ case staj2_event_type::int64_value:
+ {
+ return conv.from(value_.int64_value_, tag(), ec);
+ }
+ case staj2_event_type::half_value:
+ {
+ return conv.from(half_arg, value_.half_value_, tag(), ec);
+ }
+ case staj2_event_type::double_value:
+ {
+ return conv.from(value_.double_value_, tag(), ec);
+ }
+ case staj2_event_type::bool_value:
+ {
+ return conv.from(value_.bool_value_,tag(),ec);
+ }
+ case staj2_event_type::null_value:
+ {
+ return conv.from(null_type(),tag(),ec);
+ }
+ default:
+ {
+ ec = conv_errc::not_string;
+ return T{};
+ }
+ }
+ }
+
+ template<class T, class CharT_ = CharT>
+ typename std::enable_if<type_traits::is_basic_string_view<T>::value && std::is_same<typename T::value_type, CharT_>::value, T>::type
+ get(std::error_code& ec) const
+ {
+ T s;
+ switch (event_type_)
+ {
+ case staj2_event_type::string_value:
+ s = T(value_.string_data_, length_);
+ break;
+ default:
+ ec = conv_errc::not_string_view;
+ break;
+ }
+ return s;
+ }
+
+ template<class T>
+ typename std::enable_if<std::is_same<T, byte_string_view>::value, T>::type
+ get(std::error_code& ec) const
+ {
+ T s;
+ switch (event_type_)
+ {
+ case staj2_event_type::byte_string_value:
+ s = T(value_.byte_string_data_, length_);
+ break;
+ default:
+ ec = conv_errc::not_byte_string_view;
+ break;
+ }
+ return s;
+ }
+
+ template<class T>
+ typename std::enable_if<type_traits::is_list_like<T>::value &&
+ std::is_same<typename T::value_type,uint8_t>::value,T>::type
+ get(std::error_code& ec) const
+ {
+ converter<T> conv;
+ switch (event_type_)
+ {
+ case staj2_event_type::byte_string_value:
+ return conv.from(byte_string_view(value_.byte_string_data_, length_), tag(), ec);
+ case staj2_event_type::string_value:
+ return conv.from(jsoncons::basic_string_view<CharT>(value_.string_data_, length_), tag(), ec);
+ default:
+ ec = conv_errc::not_byte_string;
+ return T{};
+ }
+ }
+
+ template <class IntegerType>
+ typename std::enable_if<type_traits::is_integer<IntegerType>::value, IntegerType>::type
+ get(std::error_code& ec) const
+ {
+ switch (event_type_)
+ {
+ case staj2_event_type::string_value:
+ {
+ IntegerType val;
+ auto result = jsoncons::detail::to_integer(value_.string_data_, length_, val);
+ if (!result)
+ {
+ ec = conv_errc::not_integer;
+ return IntegerType();
+ }
+ return val;
+ }
+ case staj2_event_type::half_value:
+ return static_cast<IntegerType>(value_.half_value_);
+ case staj2_event_type::double_value:
+ return static_cast<IntegerType>(value_.double_value_);
+ case staj2_event_type::int64_value:
+ return static_cast<IntegerType>(value_.int64_value_);
+ case staj2_event_type::uint64_value:
+ return static_cast<IntegerType>(value_.uint64_value_);
+ case staj2_event_type::bool_value:
+ return static_cast<IntegerType>(value_.bool_value_ ? 1 : 0);
+ default:
+ ec = conv_errc::not_integer;
+ return IntegerType();
+ }
+ }
+
+ template<class T>
+ typename std::enable_if<std::is_floating_point<T>::value, T>::type
+ get(std::error_code& ec) const
+ {
+ return static_cast<T>(as_double(ec));
+ }
+
+ template<class T>
+ typename std::enable_if<type_traits::is_bool<T>::value, T>::type
+ get(std::error_code& ec) const
+ {
+ return as_bool(ec);
+ }
+
+ staj2_event_type event_type() const noexcept { return event_type_; }
+
+ semantic_tag tag() const noexcept { return tag_; }
+
+ uint64_t ext_tag() const noexcept { return ext_tag_; }
+
+ private:
+
+ double as_double(std::error_code& ec) const
+ {
+ switch (event_type_)
+ {
+ case staj2_event_type::string_value:
+ {
+ jsoncons::detail::chars_to f;
+ return f(value_.string_data_, length_);
+ }
+ case staj2_event_type::double_value:
+ return value_.double_value_;
+ case staj2_event_type::int64_value:
+ return static_cast<double>(value_.int64_value_);
+ case staj2_event_type::uint64_value:
+ return static_cast<double>(value_.uint64_value_);
+ case staj2_event_type::half_value:
+ {
+ double x = binary::decode_half(value_.half_value_);
+ return static_cast<double>(x);
+ }
+ default:
+ ec = conv_errc::not_double;
+ return double();
+ }
+ }
+
+ bool as_bool(std::error_code& ec) const
+ {
+ switch (event_type_)
+ {
+ case staj2_event_type::bool_value:
+ return value_.bool_value_;
+ case staj2_event_type::double_value:
+ return value_.double_value_ != 0.0;
+ case staj2_event_type::int64_value:
+ return value_.int64_value_ != 0;
+ case staj2_event_type::uint64_value:
+ return value_.uint64_value_ != 0;
+ default:
+ ec = conv_errc::not_bool;
+ return bool();
+ }
+ }
+ };
+
+ // basic_staj2_visitor
+
+ enum class staj2_cursor_state
+ {
+ typed_array = 1,
+ multi_dim,
+ shape
+ };
+
+ template <class CharT>
+ class basic_staj2_visitor : public basic_json_visitor2<CharT>
+ {
+ using super_type = basic_json_visitor2<CharT>;
+ public:
+ using char_type = CharT;
+ using typename super_type::string_view_type;
+ private:
+ std::function<bool(const basic_staj2_event<CharT>&, const ser_context&)> pred_;
+ basic_staj2_event<CharT> event_;
+
+ staj2_cursor_state state_;
+ typed_array_view data_;
+ jsoncons::span<const size_t> shape_;
+ std::size_t index_;
+ public:
+ basic_staj2_visitor()
+ : pred_(accept), event_(staj2_event_type::null_value),
+ state_(), data_(), shape_(), index_(0)
+ {
+ }
+
+ basic_staj2_visitor(std::function<bool(const basic_staj2_event<CharT>&, const ser_context&)> pred)
+ : pred_(pred), event_(staj2_event_type::null_value),
+ state_(), data_(), shape_(), index_(0)
+ {
+ }
+
+ void reset()
+ {
+ event_ = staj2_event_type::null_value;
+ state_ = {};
+ data_ = {};
+ shape_ = {};
+ index_ = 0;
+ }
+
+ const basic_staj2_event<CharT>& event() const
+ {
+ return event_;
+ }
+
+ bool in_available() const
+ {
+ return state_ != staj2_cursor_state();
+ }
+
+ void send_available(std::error_code& ec)
+ {
+ switch (state_)
+ {
+ case staj2_cursor_state::typed_array:
+ advance_typed_array(ec);
+ break;
+ case staj2_cursor_state::multi_dim:
+ case staj2_cursor_state::shape:
+ advance_multi_dim(ec);
+ break;
+ default:
+ break;
+ }
+ }
+
+ bool is_typed_array() const
+ {
+ return data_.type() != typed_array_type();
+ }
+
+ staj2_cursor_state state() const
+ {
+ return state_;
+ }
+
+ void advance_typed_array(std::error_code& ec)
+ {
+ if (is_typed_array())
+ {
+ if (index_ < data_.size())
+ {
+ switch (data_.type())
+ {
+ case typed_array_type::uint8_value:
+ {
+ this->uint64_value(data_.data(uint8_array_arg)[index_], semantic_tag::none, ser_context(), ec);
+ break;
+ }
+ case typed_array_type::uint16_value:
+ {
+ this->uint64_value(data_.data(uint16_array_arg)[index_], semantic_tag::none, ser_context(), ec);
+ break;
+ }
+ case typed_array_type::uint32_value:
+ {
+ this->uint64_value(data_.data(uint32_array_arg)[index_], semantic_tag::none, ser_context(), ec);
+ break;
+ }
+ case typed_array_type::uint64_value:
+ {
+ this->uint64_value(data_.data(uint64_array_arg)[index_], semantic_tag::none, ser_context(), ec);
+ break;
+ }
+ case typed_array_type::int8_value:
+ {
+ this->int64_value(data_.data(int8_array_arg)[index_], semantic_tag::none, ser_context(), ec);
+ break;
+ }
+ case typed_array_type::int16_value:
+ {
+ this->int64_value(data_.data(int16_array_arg)[index_], semantic_tag::none, ser_context(), ec);
+ break;
+ }
+ case typed_array_type::int32_value:
+ {
+ this->int64_value(data_.data(int32_array_arg)[index_], semantic_tag::none, ser_context(), ec);
+ break;
+ }
+ case typed_array_type::int64_value:
+ {
+ this->int64_value(data_.data(int64_array_arg)[index_], semantic_tag::none, ser_context(), ec);
+ break;
+ }
+ case typed_array_type::half_value:
+ {
+ this->half_value(data_.data(half_array_arg)[index_], semantic_tag::none, ser_context(), ec);
+ break;
+ }
+ case typed_array_type::float_value:
+ {
+ this->double_value(data_.data(float_array_arg)[index_], semantic_tag::none, ser_context(), ec);
+ break;
+ }
+ case typed_array_type::double_value:
+ {
+ this->double_value(data_.data(double_array_arg)[index_], semantic_tag::none, ser_context(), ec);
+ break;
+ }
+ default:
+ break;
+ }
+ ++index_;
+ }
+ else
+ {
+ this->end_array();
+ state_ = staj2_cursor_state();
+ data_ = typed_array_view();
+ index_ = 0;
+ }
+ }
+ }
+
+ void advance_multi_dim(std::error_code& ec)
+ {
+ if (shape_.size() != 0)
+ {
+ if (state_ == staj2_cursor_state::multi_dim)
+ {
+ this->begin_array(shape_.size(), semantic_tag::none, ser_context(), ec);
+ state_ = staj2_cursor_state::shape;
+ }
+ else if (index_ < shape_.size())
+ {
+ this->uint64_value(shape_[index_], semantic_tag::none, ser_context(), ec);
+ ++index_;
+ }
+ else
+ {
+ state_ = staj2_cursor_state();
+ this->end_array(ser_context(), ec);
+ shape_ = jsoncons::span<const size_t>();
+ index_ = 0;
+ }
+ }
+ }
+
+ bool dump(basic_json_visitor2<CharT>& visitor, const ser_context& context, std::error_code& ec)
+ {
+ bool more = true;
+ if (is_typed_array())
+ {
+ if (index_ != 0)
+ {
+ more = staj2_to_saj_event(event(), visitor, context, ec);
+ while (more && is_typed_array())
+ {
+ if (index_ < data_.size())
+ {
+ switch (data_.type())
+ {
+ case typed_array_type::uint8_value:
+ {
+ more = visitor.uint64_value(data_.data(uint8_array_arg)[index_]);
+ break;
+ }
+ case typed_array_type::uint16_value:
+ {
+ more = visitor.uint64_value(data_.data(uint16_array_arg)[index_]);
+ break;
+ }
+ case typed_array_type::uint32_value:
+ {
+ more = visitor.uint64_value(data_.data(uint32_array_arg)[index_]);
+ break;
+ }
+ case typed_array_type::uint64_value:
+ {
+ more = visitor.uint64_value(data_.data(uint64_array_arg)[index_]);
+ break;
+ }
+ case typed_array_type::int8_value:
+ {
+ more = visitor.int64_value(data_.data(int8_array_arg)[index_]);
+ break;
+ }
+ case typed_array_type::int16_value:
+ {
+ more = visitor.int64_value(data_.data(int16_array_arg)[index_]);
+ break;
+ }
+ case typed_array_type::int32_value:
+ {
+ more = visitor.int64_value(data_.data(int32_array_arg)[index_]);
+ break;
+ }
+ case typed_array_type::int64_value:
+ {
+ more = visitor.int64_value(data_.data(int64_array_arg)[index_]);
+ break;
+ }
+ case typed_array_type::float_value:
+ {
+ more = visitor.double_value(data_.data(float_array_arg)[index_]);
+ break;
+ }
+ case typed_array_type::double_value:
+ {
+ more = visitor.double_value(data_.data(double_array_arg)[index_]);
+ break;
+ }
+ default:
+ break;
+ }
+ ++index_;
+ }
+ else
+ {
+ more = visitor.end_array();
+ state_ = staj2_cursor_state();
+ data_ = typed_array_view();
+ index_ = 0;
+ }
+ }
+ }
+ else
+ {
+ switch (data_.type())
+ {
+ case typed_array_type::uint8_value:
+ {
+ more = visitor.typed_array(data_.data(uint8_array_arg));
+ break;
+ }
+ case typed_array_type::uint16_value:
+ {
+ more = visitor.typed_array(data_.data(uint16_array_arg));
+ break;
+ }
+ case typed_array_type::uint32_value:
+ {
+ more = visitor.typed_array(data_.data(uint32_array_arg));
+ break;
+ }
+ case typed_array_type::uint64_value:
+ {
+ more = visitor.typed_array(data_.data(uint64_array_arg));
+ break;
+ }
+ case typed_array_type::int8_value:
+ {
+ more = visitor.typed_array(data_.data(int8_array_arg));
+ break;
+ }
+ case typed_array_type::int16_value:
+ {
+ more = visitor.typed_array(data_.data(int16_array_arg));
+ break;
+ }
+ case typed_array_type::int32_value:
+ {
+ more = visitor.typed_array(data_.data(int32_array_arg));
+ break;
+ }
+ case typed_array_type::int64_value:
+ {
+ more = visitor.typed_array(data_.data(int64_array_arg));
+ break;
+ }
+ case typed_array_type::float_value:
+ {
+ more = visitor.typed_array(data_.data(float_array_arg));
+ break;
+ }
+ case typed_array_type::double_value:
+ {
+ more = visitor.typed_array(data_.data(double_array_arg));
+ break;
+ }
+ default:
+ break;
+ }
+
+ state_ = staj2_cursor_state();
+ data_ = typed_array_view();
+ }
+ }
+ else
+ {
+ more = staj2_to_saj_event(event(), visitor, context, ec);
+ }
+ return more;
+ }
+
+ private:
+ static constexpr bool accept(const basic_staj2_event<CharT>&, const ser_context&)
+ {
+ return true;
+ }
+
+ bool visit_begin_object(semantic_tag tag, const ser_context& context, std::error_code&) override
+ {
+ event_ = basic_staj2_event<CharT>(staj2_event_type::begin_object, tag);
+ return !pred_(event_, context);
+ }
+
+ bool visit_begin_object(std::size_t length, semantic_tag tag, const ser_context& context, std::error_code&) override
+ {
+ event_ = basic_staj2_event<CharT>(staj2_event_type::begin_object, length, tag);
+ return !pred_(event_, context);
+ }
+
+ bool visit_end_object(const ser_context& context, std::error_code&) override
+ {
+ event_ = basic_staj2_event<CharT>(staj2_event_type::end_object);
+ return !pred_(event_, context);
+ }
+
+ bool visit_begin_array(semantic_tag tag, const ser_context& context, std::error_code&) override
+ {
+ event_ = basic_staj2_event<CharT>(staj2_event_type::begin_array, tag);
+ return !pred_(event_, context);
+ }
+
+ bool visit_begin_array(std::size_t length, semantic_tag tag, const ser_context& context, std::error_code&) override
+ {
+ event_ = basic_staj2_event<CharT>(staj2_event_type::begin_array, length, tag);
+ return !pred_(event_, context);
+ }
+
+ bool visit_end_array(const ser_context& context, std::error_code&) override
+ {
+ event_ = basic_staj2_event<CharT>(staj2_event_type::end_array);
+ return !pred_(event_, context);
+ }
+
+ bool visit_null(semantic_tag tag, const ser_context& context, std::error_code&) override
+ {
+ event_ = basic_staj2_event<CharT>(staj2_event_type::null_value, tag);
+ return !pred_(event_, context);
+ }
+
+ bool visit_bool(bool value, semantic_tag tag, const ser_context& context, std::error_code&) override
+ {
+ event_ = basic_staj2_event<CharT>(value, tag);
+ return !pred_(event_, context);
+ }
+
+ bool visit_string(const string_view_type& s, semantic_tag tag, const ser_context& context, std::error_code&) override
+ {
+ event_ = basic_staj2_event<CharT>(s, staj2_event_type::string_value, tag);
+ return !pred_(event_, context);
+ }
+
+ bool visit_byte_string(const byte_string_view& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code&) override
+ {
+ event_ = basic_staj2_event<CharT>(s, staj2_event_type::byte_string_value, tag);
+ return !pred_(event_, context);
+ }
+
+ bool visit_byte_string(const byte_string_view& s,
+ uint64_t ext_tag,
+ const ser_context& context,
+ std::error_code&) override
+ {
+ event_ = basic_staj2_event<CharT>(s, staj2_event_type::byte_string_value, ext_tag);
+ return !pred_(event_, context);
+ }
+
+ bool visit_uint64(uint64_t value,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code&) override
+ {
+ event_ = basic_staj2_event<CharT>(value, tag);
+ return !pred_(event_, context);
+ }
+
+ bool visit_int64(int64_t value,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code&) override
+ {
+ event_ = basic_staj2_event<CharT>(value, tag);
+ return !pred_(event_, context);
+ }
+
+ bool visit_half(uint16_t value,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code&) override
+ {
+ event_ = basic_staj2_event<CharT>(half_arg, value, tag);
+ return !pred_(event_, context);
+ }
+
+ bool visit_double(double value,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code&) override
+ {
+ event_ = basic_staj2_event<CharT>(value, tag);
+ return !pred_(event_, context);
+ }
+
+ bool visit_typed_array(const jsoncons::span<const uint8_t>& v,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ state_ = staj2_cursor_state::typed_array;
+ data_ = typed_array_view(v.data(), v.size());
+ index_ = 0;
+ return this->begin_array(tag, context, ec);
+ }
+
+ bool visit_typed_array(const jsoncons::span<const uint16_t>& data,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ state_ = staj2_cursor_state::typed_array;
+ data_ = typed_array_view(data.data(), data.size());
+ index_ = 0;
+ return this->begin_array(tag, context, ec);
+ }
+
+ bool visit_typed_array(const jsoncons::span<const uint32_t>& data,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ state_ = staj2_cursor_state::typed_array;
+ data_ = typed_array_view(data.data(), data.size());
+ index_ = 0;
+ return this->begin_array(tag, context, ec);
+ }
+
+ bool visit_typed_array(const jsoncons::span<const uint64_t>& data,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ state_ = staj2_cursor_state::typed_array;
+ data_ = typed_array_view(data.data(), data.size());
+ index_ = 0;
+ return this->begin_array(tag, context, ec);
+ }
+
+ bool visit_typed_array(const jsoncons::span<const int8_t>& data,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ state_ = staj2_cursor_state::typed_array;
+ data_ = typed_array_view(data.data(), data.size());
+ index_ = 0;
+ return this->begin_array(tag, context, ec);
+ }
+
+ bool visit_typed_array(const jsoncons::span<const int16_t>& data,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ state_ = staj2_cursor_state::typed_array;
+ data_ = typed_array_view(data.data(), data.size());
+ index_ = 0;
+ return this->begin_array(tag, context, ec);
+ }
+
+ bool visit_typed_array(const jsoncons::span<const int32_t>& data,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ state_ = staj2_cursor_state::typed_array;
+ data_ = typed_array_view(data.data(), data.size());
+ index_ = 0;
+ return this->begin_array(tag, context, ec);
+ }
+
+ bool visit_typed_array(const jsoncons::span<const int64_t>& data,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ state_ = staj2_cursor_state::typed_array;
+ data_ = typed_array_view(data.data(), data.size());
+ index_ = 0;
+ return this->begin_array(tag, context, ec);
+ }
+
+ bool visit_typed_array(half_arg_t, const jsoncons::span<const uint16_t>& data,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ state_ = staj2_cursor_state::typed_array;
+ data_ = typed_array_view(data.data(), data.size());
+ index_ = 0;
+ return this->begin_array(tag, context, ec);
+ }
+
+ bool visit_typed_array(const jsoncons::span<const float>& data,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ state_ = staj2_cursor_state::typed_array;
+ data_ = typed_array_view(data.data(), data.size());
+ index_ = 0;
+ return this->begin_array(tag, context, ec);
+ }
+
+ bool visit_typed_array(const jsoncons::span<const double>& data,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ state_ = staj2_cursor_state::typed_array;
+ data_ = typed_array_view(data.data(), data.size());
+ index_ = 0;
+ return this->begin_array(tag, context, ec);
+ }
+ /*
+ bool visit_typed_array(const jsoncons::span<const float128_type>&,
+ semantic_tag,
+ const ser_context&,
+ std::error_code&) override
+ {
+ return true;
+ }
+ */
+ bool visit_begin_multi_dim(const jsoncons::span<const size_t>& shape,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ state_ = staj2_cursor_state::multi_dim;
+ shape_ = shape;
+ return this->begin_array(2, tag, context, ec);
+ }
+
+ bool visit_end_multi_dim(const ser_context& context,
+ std::error_code& ec) override
+ {
+ return this->end_array(context, ec);
+ }
+
+ void visit_flush() override
+ {
+ }
+ };
+
+ template<class CharT>
+ bool staj2_to_saj_event(const basic_staj2_event<CharT>& ev,
+ basic_json_visitor2<CharT>& visitor,
+ const ser_context& context,
+ std::error_code& ec)
+ {
+ switch (ev.event_type())
+ {
+ case staj2_event_type::begin_array:
+ return visitor.begin_array(ev.tag(), context);
+ case staj2_event_type::end_array:
+ return visitor.end_array(context);
+ case staj2_event_type::begin_object:
+ return visitor.begin_object(ev.tag(), context, ec);
+ case staj2_event_type::end_object:
+ return visitor.end_object(context, ec);
+ case staj2_event_type::string_value:
+ return visitor.string_value(ev.template get<jsoncons::basic_string_view<CharT>>(), ev.tag(), context);
+ case staj2_event_type::byte_string_value:
+ return visitor.byte_string_value(ev.template get<byte_string_view>(), ev.tag(), context);
+ case staj2_event_type::null_value:
+ return visitor.null_value(ev.tag(), context);
+ case staj2_event_type::bool_value:
+ return visitor.bool_value(ev.template get<bool>(), ev.tag(), context);
+ case staj2_event_type::int64_value:
+ return visitor.int64_value(ev.template get<int64_t>(), ev.tag(), context);
+ case staj2_event_type::uint64_value:
+ return visitor.uint64_value(ev.template get<uint64_t>(), ev.tag(), context);
+ case staj2_event_type::half_value:
+ return visitor.half_value(ev.template get<uint16_t>(), ev.tag(), context);
+ case staj2_event_type::double_value:
+ return visitor.double_value(ev.template get<double>(), ev.tag(), context);
+ default:
+ return false;
+ }
+ }
+
+ // basic_staj2_cursor
+
+ template<class CharT>
+ class basic_staj2_cursor
+ {
+ public:
+ virtual ~basic_staj2_cursor() noexcept = default;
+
+ virtual void array_expected(std::error_code& ec)
+ {
+ if (!(current().event_type() == staj2_event_type::begin_array || current().event_type() == staj2_event_type::byte_string_value))
+ {
+ ec = conv_errc::not_vector;
+ }
+ }
+
+ virtual bool done() const = 0;
+
+ virtual const basic_staj2_event<CharT>& current() const = 0;
+
+ virtual void read_to(basic_json_visitor2<CharT>& visitor) = 0;
+
+ virtual void read_to(basic_json_visitor2<CharT>& visitor,
+ std::error_code& ec) = 0;
+
+ virtual void next() = 0;
+
+ virtual void next(std::error_code& ec) = 0;
+
+ virtual const ser_context& context() const = 0;
+ };
+
+ template<class CharT>
+ class basic_staj2_filter_view : basic_staj2_cursor<CharT>
+ {
+ basic_staj2_cursor<CharT>* cursor_;
+ std::function<bool(const basic_staj2_event<CharT>&, const ser_context&)> pred_;
+ public:
+ basic_staj2_filter_view(basic_staj2_cursor<CharT>& cursor,
+ std::function<bool(const basic_staj2_event<CharT>&, const ser_context&)> pred)
+ : cursor_(std::addressof(cursor)), pred_(pred)
+ {
+ while (!done() && !pred_(current(),context()))
+ {
+ cursor_->next();
+ }
+ }
+
+ bool done() const override
+ {
+ return cursor_->done();
+ }
+
+ const basic_staj2_event<CharT>& current() const override
+ {
+ return cursor_->current();
+ }
+
+ void read_to(basic_json_visitor2<CharT>& visitor) override
+ {
+ cursor_->read_to(visitor);
+ }
+
+ void read_to(basic_json_visitor2<CharT>& visitor,
+ std::error_code& ec) override
+ {
+ cursor_->read_to(visitor, ec);
+ }
+
+ void next() override
+ {
+ cursor_->next();
+ while (!done() && !pred_(current(),context()))
+ {
+ cursor_->next();
+ }
+ }
+
+ void next(std::error_code& ec) override
+ {
+ cursor_->next(ec);
+ while (!done() && !pred_(current(),context()) && !ec)
+ {
+ cursor_->next(ec);
+ }
+ }
+
+ const ser_context& context() const override
+ {
+ return cursor_->context();
+ }
+
+ friend
+ basic_staj2_filter_view<CharT> operator|(basic_staj2_filter_view& cursor,
+ std::function<bool(const basic_staj2_event<CharT>&, const ser_context&)> pred)
+ {
+ return basic_staj2_filter_view<CharT>(cursor, pred);
+ }
+ };
+
+ using staj2_event = basic_staj2_event<char>;
+ using wstaj2_event = basic_staj2_event<wchar_t>;
+
+ using staj2_cursor = basic_staj2_cursor<char>;
+ using wstaj2_cursor = basic_staj2_cursor<wchar_t>;
+
+ using staj2_filter_view = basic_staj2_filter_view<char>;
+ using wstaj2_filter_view = basic_staj2_filter_view<wchar_t>;
+
+} // namespace jsoncons
+
+#endif
+
diff --git a/include/jsoncons/staj_cursor.hpp b/include/jsoncons/staj_cursor.hpp
new file mode 100644
index 0000000..4594ba1
--- /dev/null
+++ b/include/jsoncons/staj_cursor.hpp
@@ -0,0 +1,1233 @@
+// 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)
+
+// See https://github.com/danielaparker/jsoncons for latest version
+
+#ifndef JSONCONS_STAJ_CURSOR_HPP
+#define JSONCONS_STAJ_CURSOR_HPP
+
+#include <memory> // std::allocator
+#include <string>
+#include <stdexcept>
+#include <system_error>
+#include <ios>
+#include <type_traits> // std::enable_if
+#include <array> // std::array
+#include <functional> // std::function
+#include <jsoncons/json_exception.hpp>
+#include <jsoncons/json_visitor.hpp>
+#include <jsoncons/bigint.hpp>
+#include <jsoncons/json_parser.hpp>
+#include <jsoncons/ser_context.hpp>
+#include <jsoncons/sink.hpp>
+#include <jsoncons/detail/write_number.hpp>
+#include <jsoncons/json_type_traits.hpp>
+#include <jsoncons/typed_array_view.hpp>
+#include <jsoncons/converter.hpp>
+
+namespace jsoncons {
+
+enum class staj_event_type
+{
+ begin_array,
+ end_array,
+ begin_object,
+ end_object,
+ key,
+ string_value,
+ byte_string_value,
+ null_value,
+ bool_value,
+ int64_value,
+ uint64_value,
+ half_value,
+ double_value
+#if !defined(JSONCONS_NO_DEPRECATED)
+ ,name = key
+#endif
+};
+
+template <class CharT>
+std::basic_ostream<CharT>& operator<<(std::basic_ostream<CharT>& os, staj_event_type tag)
+{
+ static constexpr const CharT* begin_array_name = JSONCONS_CSTRING_CONSTANT(CharT, "begin_array");
+ static constexpr const CharT* end_array_name = JSONCONS_CSTRING_CONSTANT(CharT, "end_array");
+ static constexpr const CharT* begin_object_name = JSONCONS_CSTRING_CONSTANT(CharT, "begin_object");
+ static constexpr const CharT* end_object_name = JSONCONS_CSTRING_CONSTANT(CharT, "end_object");
+ static constexpr const CharT* key_name = JSONCONS_CSTRING_CONSTANT(CharT, "key");
+ static constexpr const CharT* string_value_name = JSONCONS_CSTRING_CONSTANT(CharT, "string_value");
+ static constexpr const CharT* byte_string_value_name = JSONCONS_CSTRING_CONSTANT(CharT, "byte_string_value");
+ static constexpr const CharT* null_value_name = JSONCONS_CSTRING_CONSTANT(CharT, "null_value");
+ static constexpr const CharT* bool_value_name = JSONCONS_CSTRING_CONSTANT(CharT, "bool_value");
+ static constexpr const CharT* uint64_value_name = JSONCONS_CSTRING_CONSTANT(CharT, "uint64_value");
+ static constexpr const CharT* int64_value_name = JSONCONS_CSTRING_CONSTANT(CharT, "int64_value");
+ static constexpr const CharT* half_value_name = JSONCONS_CSTRING_CONSTANT(CharT, "half_value");
+ static constexpr const CharT* double_value_name = JSONCONS_CSTRING_CONSTANT(CharT, "double_value");
+
+ switch (tag)
+ {
+ case staj_event_type::begin_array:
+ {
+ os << begin_array_name;
+ break;
+ }
+ case staj_event_type::end_array:
+ {
+ os << end_array_name;
+ break;
+ }
+ case staj_event_type::begin_object:
+ {
+ os << begin_object_name;
+ break;
+ }
+ case staj_event_type::end_object:
+ {
+ os << end_object_name;
+ break;
+ }
+ case staj_event_type::key:
+ {
+ os << key_name;
+ break;
+ }
+ case staj_event_type::string_value:
+ {
+ os << string_value_name;
+ break;
+ }
+ case staj_event_type::byte_string_value:
+ {
+ os << byte_string_value_name;
+ break;
+ }
+ case staj_event_type::null_value:
+ {
+ os << null_value_name;
+ break;
+ }
+ case staj_event_type::bool_value:
+ {
+ os << bool_value_name;
+ break;
+ }
+ case staj_event_type::int64_value:
+ {
+ os << int64_value_name;
+ break;
+ }
+ case staj_event_type::uint64_value:
+ {
+ os << uint64_value_name;
+ break;
+ }
+ case staj_event_type::half_value:
+ {
+ os << half_value_name;
+ break;
+ }
+ case staj_event_type::double_value:
+ {
+ os << double_value_name;
+ break;
+ }
+ }
+ return os;
+}
+
+template<class CharT>
+class basic_staj_event
+{
+ staj_event_type event_type_;
+ semantic_tag tag_;
+ uint64_t ext_tag_;
+ union
+ {
+ bool bool_value_;
+ int64_t int64_value_;
+ uint64_t uint64_value_;
+ uint16_t half_value_;
+ double double_value_;
+ const CharT* string_data_;
+ const uint8_t* byte_string_data_;
+ } value_;
+ std::size_t length_;
+public:
+ using string_view_type = jsoncons::basic_string_view<CharT>;
+
+ basic_staj_event(staj_event_type event_type, semantic_tag tag = semantic_tag::none)
+ : event_type_(event_type), tag_(tag), ext_tag_(0), value_(), length_(0)
+ {
+ }
+
+ basic_staj_event(staj_event_type event_type, std::size_t length, semantic_tag tag = semantic_tag::none)
+ : event_type_(event_type), tag_(tag), ext_tag_(0), value_(), length_(length)
+ {
+ }
+
+ basic_staj_event(null_type, semantic_tag tag)
+ : event_type_(staj_event_type::null_value), tag_(tag), ext_tag_(0), value_(), length_(0)
+ {
+ }
+
+ basic_staj_event(bool value, semantic_tag tag)
+ : event_type_(staj_event_type::bool_value), tag_(tag), ext_tag_(0), length_(0)
+ {
+ value_.bool_value_ = value;
+ }
+
+ basic_staj_event(int64_t value, semantic_tag tag)
+ : event_type_(staj_event_type::int64_value), tag_(tag), ext_tag_(0), length_(0)
+ {
+ value_.int64_value_ = value;
+ }
+
+ basic_staj_event(uint64_t value, semantic_tag tag)
+ : event_type_(staj_event_type::uint64_value), tag_(tag), ext_tag_(0), length_(0)
+ {
+ value_.uint64_value_ = value;
+ }
+
+ basic_staj_event(half_arg_t, uint16_t value, semantic_tag tag)
+ : event_type_(staj_event_type::half_value), tag_(tag), ext_tag_(0), length_(0)
+ {
+ value_.half_value_ = value;
+ }
+
+ basic_staj_event(double value, semantic_tag tag)
+ : event_type_(staj_event_type::double_value), tag_(tag), ext_tag_(0), length_(0)
+ {
+ value_.double_value_ = value;
+ }
+
+ basic_staj_event(const string_view_type& s,
+ staj_event_type event_type,
+ semantic_tag tag = semantic_tag::none)
+ : event_type_(event_type), tag_(tag), ext_tag_(0), length_(s.length())
+ {
+ value_.string_data_ = s.data();
+ }
+
+ basic_staj_event(const byte_string_view& s,
+ staj_event_type event_type,
+ semantic_tag tag = semantic_tag::none)
+ : event_type_(event_type), tag_(tag), ext_tag_(0), length_(s.size())
+ {
+ value_.byte_string_data_ = s.data();
+ }
+
+ basic_staj_event(const byte_string_view& s,
+ staj_event_type event_type,
+ uint64_t ext_tag)
+ : event_type_(event_type), tag_(semantic_tag::ext), ext_tag_(ext_tag), length_(s.size())
+ {
+ value_.byte_string_data_ = s.data();
+ }
+
+ std::size_t size() const
+ {
+ return length_;
+ }
+
+ template <class T>
+ T get() const
+ {
+ std::error_code ec;
+ T val = get<T>(ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec));
+ }
+ return val;
+ }
+
+ template<class T, class CharT_ = CharT>
+ typename std::enable_if<type_traits::is_basic_string<T>::value && std::is_same<typename T::value_type, CharT_>::value, T>::type
+ get(std::error_code& ec) const
+ {
+ converter<T> conv;
+ switch (event_type_)
+ {
+ case staj_event_type::key:
+ case staj_event_type::string_value:
+ return conv.from(jsoncons::basic_string_view<CharT>(value_.string_data_, length_), tag(), ec);
+ case staj_event_type::byte_string_value:
+ {
+ return conv.from(byte_string_view(value_.byte_string_data_,length_),
+ tag(),
+ ec);
+ }
+ case staj_event_type::uint64_value:
+ {
+ return conv.from(value_.uint64_value_, tag(), ec);
+ }
+ case staj_event_type::int64_value:
+ {
+ return conv.from(value_.int64_value_, tag(), ec);
+ }
+ case staj_event_type::half_value:
+ {
+ return conv.from(half_arg, value_.half_value_, tag(), ec);
+ }
+ case staj_event_type::double_value:
+ {
+ return conv.from(value_.double_value_, tag(), ec);
+ }
+ case staj_event_type::bool_value:
+ {
+ return conv.from(value_.bool_value_,tag(),ec);
+ }
+ case staj_event_type::null_value:
+ {
+ return conv.from(null_type(),tag(),ec);
+ }
+ default:
+ {
+ ec = conv_errc::not_string;
+ return T{};
+ }
+ }
+ }
+
+ template<class T, class CharT_ = CharT>
+ typename std::enable_if<type_traits::is_basic_string_view<T>::value && std::is_same<typename T::value_type, CharT_>::value, T>::type
+ get(std::error_code& ec) const
+ {
+ T s;
+ switch (event_type_)
+ {
+ case staj_event_type::key:
+ case staj_event_type::string_value:
+ s = T(value_.string_data_, length_);
+ break;
+ default:
+ ec = conv_errc::not_string_view;
+ break;
+ }
+ return s;
+ }
+
+ template<class T>
+ typename std::enable_if<std::is_same<T, byte_string_view>::value, T>::type
+ get(std::error_code& ec) const
+ {
+ T s;
+ switch (event_type_)
+ {
+ case staj_event_type::byte_string_value:
+ s = T(value_.byte_string_data_, length_);
+ break;
+ default:
+ ec = conv_errc::not_byte_string_view;
+ break;
+ }
+ return s;
+ }
+
+ template<class T>
+ typename std::enable_if<type_traits::is_list_like<T>::value &&
+ std::is_same<typename T::value_type,uint8_t>::value,T>::type
+ get(std::error_code& ec) const
+ {
+ converter<T> conv;
+ switch (event_type_)
+ {
+ case staj_event_type::byte_string_value:
+ return conv.from(byte_string_view(value_.byte_string_data_, length_), tag(), ec);
+ case staj_event_type::string_value:
+ return conv.from(jsoncons::basic_string_view<CharT>(value_.string_data_, length_), tag(), ec);
+ default:
+ ec = conv_errc::not_byte_string;
+ return T{};
+ }
+ }
+
+ template <class IntegerType>
+ typename std::enable_if<type_traits::is_integer<IntegerType>::value, IntegerType>::type
+ get(std::error_code& ec) const
+ {
+ switch (event_type_)
+ {
+ case staj_event_type::string_value:
+ {
+ IntegerType val;
+ auto result = jsoncons::detail::to_integer(value_.string_data_, length_, val);
+ if (!result)
+ {
+ ec = conv_errc::not_integer;
+ return IntegerType();
+ }
+ return val;
+ }
+ case staj_event_type::half_value:
+ return static_cast<IntegerType>(value_.half_value_);
+ case staj_event_type::double_value:
+ return static_cast<IntegerType>(value_.double_value_);
+ case staj_event_type::int64_value:
+ return static_cast<IntegerType>(value_.int64_value_);
+ case staj_event_type::uint64_value:
+ return static_cast<IntegerType>(value_.uint64_value_);
+ case staj_event_type::bool_value:
+ return static_cast<IntegerType>(value_.bool_value_ ? 1 : 0);
+ default:
+ ec = conv_errc::not_integer;
+ return IntegerType();
+ }
+ }
+
+ template<class T>
+ typename std::enable_if<std::is_floating_point<T>::value, T>::type
+ get(std::error_code& ec) const
+ {
+ return static_cast<T>(as_double(ec));
+ }
+
+ template<class T>
+ typename std::enable_if<type_traits::is_bool<T>::value, T>::type
+ get(std::error_code& ec) const
+ {
+ return as_bool(ec);
+ }
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+ template<class T>
+ JSONCONS_DEPRECATED_MSG("Instead, use get<T>()")
+ T as() const
+ {
+ return get<T>();
+ }
+ semantic_tag get_semantic_tag() const noexcept { return tag_; }
+#endif
+
+ staj_event_type event_type() const noexcept { return event_type_; }
+
+ semantic_tag tag() const noexcept { return tag_; }
+
+ uint64_t ext_tag() const noexcept { return ext_tag_; }
+
+private:
+
+ double as_double(std::error_code& ec) const
+ {
+ switch (event_type_)
+ {
+ case staj_event_type::key:
+ case staj_event_type::string_value:
+ {
+ jsoncons::detail::chars_to f;
+ return f(value_.string_data_, length_);
+ }
+ case staj_event_type::double_value:
+ return value_.double_value_;
+ case staj_event_type::int64_value:
+ return static_cast<double>(value_.int64_value_);
+ case staj_event_type::uint64_value:
+ return static_cast<double>(value_.uint64_value_);
+ case staj_event_type::half_value:
+ {
+ double x = binary::decode_half(value_.half_value_);
+ return static_cast<double>(x);
+ }
+ default:
+ ec = conv_errc::not_double;
+ return double();
+ }
+ }
+
+ bool as_bool(std::error_code& ec) const
+ {
+ switch (event_type_)
+ {
+ case staj_event_type::bool_value:
+ return value_.bool_value_;
+ case staj_event_type::double_value:
+ return value_.double_value_ != 0.0;
+ case staj_event_type::int64_value:
+ return value_.int64_value_ != 0;
+ case staj_event_type::uint64_value:
+ return value_.uint64_value_ != 0;
+ default:
+ ec = conv_errc::not_bool;
+ return bool();
+ }
+ }
+};
+
+// basic_staj_visitor
+
+enum class staj_cursor_state
+{
+ typed_array = 1,
+ multi_dim,
+ shape
+};
+
+template <class CharT>
+class basic_staj_visitor : public basic_json_visitor<CharT>
+{
+ using super_type = basic_json_visitor<CharT>;
+public:
+ using char_type = CharT;
+ using typename super_type::string_view_type;
+private:
+ std::function<bool(const basic_staj_event<CharT>&, const ser_context&)> pred_;
+ basic_staj_event<CharT> event_;
+
+ staj_cursor_state state_;
+ typed_array_view data_;
+ jsoncons::span<const size_t> shape_;
+ std::size_t index_;
+public:
+ basic_staj_visitor()
+ : pred_(accept), event_(staj_event_type::null_value),
+ state_(), data_(), shape_(), index_(0)
+ {
+ }
+
+ basic_staj_visitor(std::function<bool(const basic_staj_event<CharT>&, const ser_context&)> pred)
+ : pred_(pred), event_(staj_event_type::null_value),
+ state_(), data_(), shape_(), index_(0)
+ {
+ }
+
+ void reset()
+ {
+ event_ = staj_event_type::null_value;
+ state_ = {};
+ data_ = {};
+ shape_ = {};
+ index_ = 0;
+ }
+
+ const basic_staj_event<CharT>& event() const
+ {
+ return event_;
+ }
+
+ bool in_available() const
+ {
+ return state_ != staj_cursor_state();
+ }
+
+ void send_available(std::error_code& ec)
+ {
+ switch (state_)
+ {
+ case staj_cursor_state::typed_array:
+ advance_typed_array(ec);
+ break;
+ case staj_cursor_state::multi_dim:
+ case staj_cursor_state::shape:
+ advance_multi_dim(ec);
+ break;
+ default:
+ break;
+ }
+ }
+
+ bool is_typed_array() const
+ {
+ return data_.type() != typed_array_type();
+ }
+
+ staj_cursor_state state() const
+ {
+ return state_;
+ }
+
+ void advance_typed_array(std::error_code& ec)
+ {
+ if (is_typed_array())
+ {
+ if (index_ < data_.size())
+ {
+ switch (data_.type())
+ {
+ case typed_array_type::uint8_value:
+ {
+ this->uint64_value(data_.data(uint8_array_arg)[index_], semantic_tag::none, ser_context(), ec);
+ break;
+ }
+ case typed_array_type::uint16_value:
+ {
+ this->uint64_value(data_.data(uint16_array_arg)[index_], semantic_tag::none, ser_context(), ec);
+ break;
+ }
+ case typed_array_type::uint32_value:
+ {
+ this->uint64_value(data_.data(uint32_array_arg)[index_], semantic_tag::none, ser_context(), ec);
+ break;
+ }
+ case typed_array_type::uint64_value:
+ {
+ this->uint64_value(data_.data(uint64_array_arg)[index_], semantic_tag::none, ser_context(), ec);
+ break;
+ }
+ case typed_array_type::int8_value:
+ {
+ this->int64_value(data_.data(int8_array_arg)[index_], semantic_tag::none, ser_context(), ec);
+ break;
+ }
+ case typed_array_type::int16_value:
+ {
+ this->int64_value(data_.data(int16_array_arg)[index_], semantic_tag::none, ser_context(), ec);
+ break;
+ }
+ case typed_array_type::int32_value:
+ {
+ this->int64_value(data_.data(int32_array_arg)[index_], semantic_tag::none, ser_context(), ec);
+ break;
+ }
+ case typed_array_type::int64_value:
+ {
+ this->int64_value(data_.data(int64_array_arg)[index_], semantic_tag::none, ser_context(), ec);
+ break;
+ }
+ case typed_array_type::half_value:
+ {
+ this->half_value(data_.data(half_array_arg)[index_], semantic_tag::none, ser_context(), ec);
+ break;
+ }
+ case typed_array_type::float_value:
+ {
+ this->double_value(data_.data(float_array_arg)[index_], semantic_tag::none, ser_context(), ec);
+ break;
+ }
+ case typed_array_type::double_value:
+ {
+ this->double_value(data_.data(double_array_arg)[index_], semantic_tag::none, ser_context(), ec);
+ break;
+ }
+ default:
+ break;
+ }
+ ++index_;
+ }
+ else
+ {
+ this->end_array();
+ state_ = staj_cursor_state();
+ data_ = typed_array_view();
+ index_ = 0;
+ }
+ }
+ }
+
+ void advance_multi_dim(std::error_code& ec)
+ {
+ if (shape_.size() != 0)
+ {
+ if (state_ == staj_cursor_state::multi_dim)
+ {
+ this->begin_array(shape_.size(), semantic_tag::none, ser_context(), ec);
+ state_ = staj_cursor_state::shape;
+ }
+ else if (index_ < shape_.size())
+ {
+ this->uint64_value(shape_[index_], semantic_tag::none, ser_context(), ec);
+ ++index_;
+ }
+ else
+ {
+ state_ = staj_cursor_state();
+ this->end_array(ser_context(), ec);
+ shape_ = jsoncons::span<const size_t>();
+ index_ = 0;
+ }
+ }
+ }
+
+ bool dump(basic_json_visitor<CharT>& visitor, const ser_context& context, std::error_code& ec)
+ {
+ bool more = true;
+ if (is_typed_array())
+ {
+ if (index_ != 0)
+ {
+ more = staj_to_saj_event(event(), visitor, context, ec);
+ while (more && is_typed_array())
+ {
+ if (index_ < data_.size())
+ {
+ switch (data_.type())
+ {
+ case typed_array_type::uint8_value:
+ {
+ more = visitor.uint64_value(data_.data(uint8_array_arg)[index_]);
+ break;
+ }
+ case typed_array_type::uint16_value:
+ {
+ more = visitor.uint64_value(data_.data(uint16_array_arg)[index_]);
+ break;
+ }
+ case typed_array_type::uint32_value:
+ {
+ more = visitor.uint64_value(data_.data(uint32_array_arg)[index_]);
+ break;
+ }
+ case typed_array_type::uint64_value:
+ {
+ more = visitor.uint64_value(data_.data(uint64_array_arg)[index_]);
+ break;
+ }
+ case typed_array_type::int8_value:
+ {
+ more = visitor.int64_value(data_.data(int8_array_arg)[index_]);
+ break;
+ }
+ case typed_array_type::int16_value:
+ {
+ more = visitor.int64_value(data_.data(int16_array_arg)[index_]);
+ break;
+ }
+ case typed_array_type::int32_value:
+ {
+ more = visitor.int64_value(data_.data(int32_array_arg)[index_]);
+ break;
+ }
+ case typed_array_type::int64_value:
+ {
+ more = visitor.int64_value(data_.data(int64_array_arg)[index_]);
+ break;
+ }
+ case typed_array_type::float_value:
+ {
+ more = visitor.double_value(data_.data(float_array_arg)[index_]);
+ break;
+ }
+ case typed_array_type::double_value:
+ {
+ more = visitor.double_value(data_.data(double_array_arg)[index_]);
+ break;
+ }
+ default:
+ break;
+ }
+ ++index_;
+ }
+ else
+ {
+ more = visitor.end_array();
+ state_ = staj_cursor_state();
+ data_ = typed_array_view();
+ index_ = 0;
+ }
+ }
+ }
+ else
+ {
+ switch (data_.type())
+ {
+ case typed_array_type::uint8_value:
+ {
+ more = visitor.typed_array(data_.data(uint8_array_arg));
+ break;
+ }
+ case typed_array_type::uint16_value:
+ {
+ more = visitor.typed_array(data_.data(uint16_array_arg));
+ break;
+ }
+ case typed_array_type::uint32_value:
+ {
+ more = visitor.typed_array(data_.data(uint32_array_arg));
+ break;
+ }
+ case typed_array_type::uint64_value:
+ {
+ more = visitor.typed_array(data_.data(uint64_array_arg));
+ break;
+ }
+ case typed_array_type::int8_value:
+ {
+ more = visitor.typed_array(data_.data(int8_array_arg));
+ break;
+ }
+ case typed_array_type::int16_value:
+ {
+ more = visitor.typed_array(data_.data(int16_array_arg));
+ break;
+ }
+ case typed_array_type::int32_value:
+ {
+ more = visitor.typed_array(data_.data(int32_array_arg));
+ break;
+ }
+ case typed_array_type::int64_value:
+ {
+ more = visitor.typed_array(data_.data(int64_array_arg));
+ break;
+ }
+ case typed_array_type::float_value:
+ {
+ more = visitor.typed_array(data_.data(float_array_arg));
+ break;
+ }
+ case typed_array_type::double_value:
+ {
+ more = visitor.typed_array(data_.data(double_array_arg));
+ break;
+ }
+ default:
+ break;
+ }
+
+ state_ = staj_cursor_state();
+ data_ = typed_array_view();
+ }
+ }
+ else
+ {
+ more = staj_to_saj_event(event(), visitor, context, ec);
+ }
+ return more;
+ }
+
+private:
+ static constexpr bool accept(const basic_staj_event<CharT>&, const ser_context&)
+ {
+ return true;
+ }
+
+ bool visit_begin_object(semantic_tag tag, const ser_context& context, std::error_code&) override
+ {
+ event_ = basic_staj_event<CharT>(staj_event_type::begin_object, tag);
+ return !pred_(event_, context);
+ }
+
+ bool visit_begin_object(std::size_t length, semantic_tag tag, const ser_context& context, std::error_code&) override
+ {
+ event_ = basic_staj_event<CharT>(staj_event_type::begin_object, length, tag);
+ return !pred_(event_, context);
+ }
+
+ bool visit_end_object(const ser_context& context, std::error_code&) override
+ {
+ event_ = basic_staj_event<CharT>(staj_event_type::end_object);
+ return !pred_(event_, context);
+ }
+
+ bool visit_begin_array(semantic_tag tag, const ser_context& context, std::error_code&) override
+ {
+ event_ = basic_staj_event<CharT>(staj_event_type::begin_array, tag);
+ return !pred_(event_, context);
+ }
+
+ bool visit_begin_array(std::size_t length, semantic_tag tag, const ser_context& context, std::error_code&) override
+ {
+ event_ = basic_staj_event<CharT>(staj_event_type::begin_array, length, tag);
+ return !pred_(event_, context);
+ }
+
+ bool visit_end_array(const ser_context& context, std::error_code&) override
+ {
+ event_ = basic_staj_event<CharT>(staj_event_type::end_array);
+ return !pred_(event_, context);
+ }
+
+ bool visit_key(const string_view_type& name, const ser_context& context, std::error_code&) override
+ {
+ event_ = basic_staj_event<CharT>(name, staj_event_type::key);
+ return !pred_(event_, context);
+ }
+
+ bool visit_null(semantic_tag tag, const ser_context& context, std::error_code&) override
+ {
+ event_ = basic_staj_event<CharT>(staj_event_type::null_value, tag);
+ return !pred_(event_, context);
+ }
+
+ bool visit_bool(bool value, semantic_tag tag, const ser_context& context, std::error_code&) override
+ {
+ event_ = basic_staj_event<CharT>(value, tag);
+ return !pred_(event_, context);
+ }
+
+ bool visit_string(const string_view_type& s, semantic_tag tag, const ser_context& context, std::error_code&) override
+ {
+ event_ = basic_staj_event<CharT>(s, staj_event_type::string_value, tag);
+ return !pred_(event_, context);
+ }
+
+ bool visit_byte_string(const byte_string_view& s,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code&) override
+ {
+ event_ = basic_staj_event<CharT>(s, staj_event_type::byte_string_value, tag);
+ return !pred_(event_, context);
+ }
+
+ bool visit_byte_string(const byte_string_view& s,
+ uint64_t ext_tag,
+ const ser_context& context,
+ std::error_code&) override
+ {
+ event_ = basic_staj_event<CharT>(s, staj_event_type::byte_string_value, ext_tag);
+ return !pred_(event_, context);
+ }
+
+ bool visit_uint64(uint64_t value,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code&) override
+ {
+ event_ = basic_staj_event<CharT>(value, tag);
+ return !pred_(event_, context);
+ }
+
+ bool visit_int64(int64_t value,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code&) override
+ {
+ event_ = basic_staj_event<CharT>(value, tag);
+ return !pred_(event_, context);
+ }
+
+ bool visit_half(uint16_t value,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code&) override
+ {
+ event_ = basic_staj_event<CharT>(half_arg, value, tag);
+ return !pred_(event_, context);
+ }
+
+ bool visit_double(double value,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code&) override
+ {
+ event_ = basic_staj_event<CharT>(value, tag);
+ return !pred_(event_, context);
+ }
+
+ bool visit_typed_array(const jsoncons::span<const uint8_t>& v,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ state_ = staj_cursor_state::typed_array;
+ data_ = typed_array_view(v.data(), v.size());
+ index_ = 0;
+ return this->begin_array(tag, context, ec);
+ }
+
+ bool visit_typed_array(const jsoncons::span<const uint16_t>& data,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ state_ = staj_cursor_state::typed_array;
+ data_ = typed_array_view(data.data(), data.size());
+ index_ = 0;
+ return this->begin_array(tag, context, ec);
+ }
+
+ bool visit_typed_array(const jsoncons::span<const uint32_t>& data,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ state_ = staj_cursor_state::typed_array;
+ data_ = typed_array_view(data.data(), data.size());
+ index_ = 0;
+ return this->begin_array(tag, context, ec);
+ }
+
+ bool visit_typed_array(const jsoncons::span<const uint64_t>& data,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ state_ = staj_cursor_state::typed_array;
+ data_ = typed_array_view(data.data(), data.size());
+ index_ = 0;
+ return this->begin_array(tag, context, ec);
+ }
+
+ bool visit_typed_array(const jsoncons::span<const int8_t>& data,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ state_ = staj_cursor_state::typed_array;
+ data_ = typed_array_view(data.data(), data.size());
+ index_ = 0;
+ return this->begin_array(tag, context, ec);
+ }
+
+ bool visit_typed_array(const jsoncons::span<const int16_t>& data,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ state_ = staj_cursor_state::typed_array;
+ data_ = typed_array_view(data.data(), data.size());
+ index_ = 0;
+ return this->begin_array(tag, context, ec);
+ }
+
+ bool visit_typed_array(const jsoncons::span<const int32_t>& data,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ state_ = staj_cursor_state::typed_array;
+ data_ = typed_array_view(data.data(), data.size());
+ index_ = 0;
+ return this->begin_array(tag, context, ec);
+ }
+
+ bool visit_typed_array(const jsoncons::span<const int64_t>& data,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ state_ = staj_cursor_state::typed_array;
+ data_ = typed_array_view(data.data(), data.size());
+ index_ = 0;
+ return this->begin_array(tag, context, ec);
+ }
+
+ bool visit_typed_array(half_arg_t, const jsoncons::span<const uint16_t>& data,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ state_ = staj_cursor_state::typed_array;
+ data_ = typed_array_view(data.data(), data.size());
+ index_ = 0;
+ return this->begin_array(tag, context, ec);
+ }
+
+ bool visit_typed_array(const jsoncons::span<const float>& data,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ state_ = staj_cursor_state::typed_array;
+ data_ = typed_array_view(data.data(), data.size());
+ index_ = 0;
+ return this->begin_array(tag, context, ec);
+ }
+
+ bool visit_typed_array(const jsoncons::span<const double>& data,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ state_ = staj_cursor_state::typed_array;
+ data_ = typed_array_view(data.data(), data.size());
+ index_ = 0;
+ return this->begin_array(tag, context, ec);
+ }
+/*
+ bool visit_typed_array(const jsoncons::span<const float128_type>&,
+ semantic_tag,
+ const ser_context&,
+ std::error_code&) override
+ {
+ return true;
+ }
+*/
+ bool visit_begin_multi_dim(const jsoncons::span<const size_t>& shape,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ state_ = staj_cursor_state::multi_dim;
+ shape_ = shape;
+ return this->begin_array(2, tag, context, ec);
+ }
+
+ bool visit_end_multi_dim(const ser_context& context,
+ std::error_code& ec) override
+ {
+ return this->end_array(context, ec);
+ }
+
+ void visit_flush() override
+ {
+ }
+};
+
+template<class CharT>
+bool staj_to_saj_event(const basic_staj_event<CharT>& ev,
+ basic_json_visitor<CharT>& visitor,
+ const ser_context& context,
+ std::error_code& ec)
+{
+ switch (ev.event_type())
+ {
+ case staj_event_type::begin_array:
+ return visitor.begin_array(ev.tag(), context);
+ case staj_event_type::end_array:
+ return visitor.end_array(context);
+ case staj_event_type::begin_object:
+ return visitor.begin_object(ev.tag(), context, ec);
+ case staj_event_type::end_object:
+ return visitor.end_object(context, ec);
+ case staj_event_type::key:
+ return visitor.key(ev.template get<jsoncons::basic_string_view<CharT>>(), context);
+ case staj_event_type::string_value:
+ return visitor.string_value(ev.template get<jsoncons::basic_string_view<CharT>>(), ev.tag(), context);
+ case staj_event_type::byte_string_value:
+ return visitor.byte_string_value(ev.template get<byte_string_view>(), ev.tag(), context);
+ case staj_event_type::null_value:
+ return visitor.null_value(ev.tag(), context);
+ case staj_event_type::bool_value:
+ return visitor.bool_value(ev.template get<bool>(), ev.tag(), context);
+ case staj_event_type::int64_value:
+ return visitor.int64_value(ev.template get<int64_t>(), ev.tag(), context);
+ case staj_event_type::uint64_value:
+ return visitor.uint64_value(ev.template get<uint64_t>(), ev.tag(), context);
+ case staj_event_type::half_value:
+ return visitor.half_value(ev.template get<uint16_t>(), ev.tag(), context);
+ case staj_event_type::double_value:
+ return visitor.double_value(ev.template get<double>(), ev.tag(), context);
+ default:
+ return false;
+ }
+}
+
+// basic_staj_cursor
+
+template<class CharT>
+class basic_staj_cursor
+{
+public:
+ virtual ~basic_staj_cursor() noexcept = default;
+
+ virtual void array_expected(std::error_code& ec)
+ {
+ if (!(current().event_type() == staj_event_type::begin_array || current().event_type() == staj_event_type::byte_string_value))
+ {
+ ec = conv_errc::not_vector;
+ }
+ }
+
+ virtual bool done() const = 0;
+
+ virtual const basic_staj_event<CharT>& current() const = 0;
+
+ virtual void read_to(basic_json_visitor<CharT>& visitor) = 0;
+
+ virtual void read_to(basic_json_visitor<CharT>& visitor,
+ std::error_code& ec) = 0;
+
+ virtual void next() = 0;
+
+ virtual void next(std::error_code& ec) = 0;
+
+ virtual const ser_context& context() const = 0;
+};
+
+template<class CharT>
+class basic_staj_filter_view : basic_staj_cursor<CharT>
+{
+ basic_staj_cursor<CharT>* cursor_;
+ std::function<bool(const basic_staj_event<CharT>&, const ser_context&)> pred_;
+public:
+ basic_staj_filter_view(basic_staj_cursor<CharT>& cursor,
+ std::function<bool(const basic_staj_event<CharT>&, const ser_context&)> pred)
+ : cursor_(std::addressof(cursor)), pred_(pred)
+ {
+ while (!done() && !pred_(current(),context()))
+ {
+ cursor_->next();
+ }
+ }
+
+ bool done() const override
+ {
+ return cursor_->done();
+ }
+
+ const basic_staj_event<CharT>& current() const override
+ {
+ return cursor_->current();
+ }
+
+ void read_to(basic_json_visitor<CharT>& visitor) override
+ {
+ cursor_->read_to(visitor);
+ }
+
+ void read_to(basic_json_visitor<CharT>& visitor,
+ std::error_code& ec) override
+ {
+ cursor_->read_to(visitor, ec);
+ }
+
+ void next() override
+ {
+ cursor_->next();
+ while (!done() && !pred_(current(),context()))
+ {
+ cursor_->next();
+ }
+ }
+
+ void next(std::error_code& ec) override
+ {
+ cursor_->next(ec);
+ while (!done() && !pred_(current(),context()) && !ec)
+ {
+ cursor_->next(ec);
+ }
+ }
+
+ const ser_context& context() const override
+ {
+ return cursor_->context();
+ }
+
+ friend
+ basic_staj_filter_view<CharT> operator|(basic_staj_filter_view& cursor,
+ std::function<bool(const basic_staj_event<CharT>&, const ser_context&)> pred)
+ {
+ return basic_staj_filter_view<CharT>(cursor, pred);
+ }
+};
+
+using staj_event = basic_staj_event<char>;
+using wstaj_event = basic_staj_event<wchar_t>;
+
+using staj_cursor = basic_staj_cursor<char>;
+using wstaj_cursor = basic_staj_cursor<wchar_t>;
+
+using staj_filter_view = basic_staj_filter_view<char>;
+using wstaj_filter_view = basic_staj_filter_view<wchar_t>;
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+
+JSONCONS_DEPRECATED_MSG("Instead, use staj_event_type") typedef staj_event_type stream_event_type;
+
+template<class CharT>
+using basic_stream_event = basic_staj_event<CharT>;
+
+template<class CharT>
+using basic_stream_reader = basic_staj_cursor<CharT>;
+
+template<class CharT>
+using basic_staj_reader = basic_staj_cursor<CharT>;
+
+JSONCONS_DEPRECATED_MSG("Instead, use staj_event") typedef basic_staj_event<char> stream_event;
+JSONCONS_DEPRECATED_MSG("Instead, use wstaj_event") typedef basic_staj_event<wchar_t> wstream_event;
+
+JSONCONS_DEPRECATED_MSG("Instead, use staj_cursor") typedef basic_staj_cursor<char> stream_reader;
+JSONCONS_DEPRECATED_MSG("Instead, use wstaj_cursor") typedef basic_staj_cursor<wchar_t> wstream_reader;
+
+JSONCONS_DEPRECATED_MSG("Instead, use staj_cursor") typedef basic_staj_cursor<char> staj_reader;
+JSONCONS_DEPRECATED_MSG("Instead, use wstaj_cursor") typedef basic_staj_cursor<wchar_t> wstaj_reader;
+
+#endif
+
+}
+
+#endif
+
diff --git a/include/jsoncons/staj_iterator.hpp b/include/jsoncons/staj_iterator.hpp
new file mode 100644
index 0000000..a6897f7
--- /dev/null
+++ b/include/jsoncons/staj_iterator.hpp
@@ -0,0 +1,449 @@
+// 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)
+
+// See https://github.com/danielaparker/jsoncons for latest version
+
+#ifndef JSONCONS_STAJ_ITERATOR_HPP
+#define JSONCONS_STAJ_ITERATOR_HPP
+
+#include <new> // placement new
+#include <memory>
+#include <string>
+#include <stdexcept>
+#include <system_error>
+#include <ios>
+#include <iterator> // std::input_iterator_tag
+#include <jsoncons/json_exception.hpp>
+#include <jsoncons/staj_cursor.hpp>
+#include <jsoncons/basic_json.hpp>
+#include <jsoncons/decode_traits.hpp>
+
+namespace jsoncons {
+
+ template <class T, class Json>
+ class staj_array_view;
+
+ template<class T, class Json>
+ class staj_array_iterator
+ {
+ using char_type = typename Json::char_type;
+
+ staj_array_view<T, Json>* view_;
+ std::exception_ptr eptr_;
+
+ public:
+ using value_type = T;
+ using difference_type = std::ptrdiff_t;
+ using pointer = T*;
+ using reference = T&;
+ using iterator_category = std::input_iterator_tag;
+
+ staj_array_iterator() noexcept
+ : view_(nullptr)
+ {
+ }
+
+ staj_array_iterator(staj_array_view<T, Json>& view)
+ : view_(std::addressof(view))
+ {
+ if (view_->cursor_->current().event_type() == staj_event_type::begin_array)
+ {
+ next();
+ }
+ else
+ {
+ view_->cursor_ = nullptr;
+ }
+ }
+
+ staj_array_iterator(staj_array_view<T, Json>& view,
+ std::error_code& ec)
+ : view_(std::addressof(view))
+ {
+ if (view_->cursor_->current().event_type() == staj_event_type::begin_array)
+ {
+ next(ec);
+ if (ec) {view_ = nullptr;}
+ }
+ else
+ {
+ view_ = nullptr;
+ }
+ }
+
+ ~staj_array_iterator() noexcept
+ {
+ }
+
+ bool has_value() const
+ {
+ return !eptr_;
+ }
+
+ const T& operator*() const
+ {
+ if (eptr_)
+ {
+ std::rethrow_exception(eptr_);
+ }
+ else
+ {
+ return *view_->value_;
+ }
+ }
+
+ const T* operator->() const
+ {
+ if (eptr_)
+ {
+ std::rethrow_exception(eptr_);
+ }
+ else
+ {
+ return view_->value_.operator->();
+ }
+ }
+
+ staj_array_iterator& operator++()
+ {
+ next();
+ return *this;
+ }
+
+ staj_array_iterator& increment(std::error_code& ec)
+ {
+ next(ec);
+ if (ec) {view_ = nullptr;}
+ return *this;
+ }
+
+ staj_array_iterator operator++(int) // postfix increment
+ {
+ staj_array_iterator temp(*this);
+ next();
+ return temp;
+ }
+
+ friend bool operator==(const staj_array_iterator& a, const staj_array_iterator& b)
+ {
+ return (!a.view_ && !b.view_)
+ || (!a.view_ && b.done())
+ || (!b.view_ && a.done());
+ }
+
+ friend bool operator!=(const staj_array_iterator& a, const staj_array_iterator& b)
+ {
+ return !(a == b);
+ }
+
+ private:
+
+ bool done() const
+ {
+ return view_->cursor_->done() || view_->cursor_->current().event_type() == staj_event_type::end_array;
+ }
+
+ void next()
+ {
+ std::error_code ec;
+ next(ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, view_->cursor_->context().line(), view_->cursor_->context().column()));
+ }
+ }
+
+ void next(std::error_code& ec)
+ {
+ using char_type = typename Json::char_type;
+
+ if (!done())
+ {
+ view_->cursor_->next(ec);
+ if (ec)
+ {
+ return;
+ }
+ if (!done())
+ {
+ eptr_ = std::exception_ptr();
+ JSONCONS_TRY
+ {
+ view_->value_ = decode_traits<T,char_type>::decode(*view_->cursor_, view_->decoder_, ec);
+ }
+ JSONCONS_CATCH(const conv_error&)
+ {
+ eptr_ = std::current_exception();
+ }
+ }
+ }
+ }
+ };
+
+ template <class Key,class Json,class T=Json>
+ class staj_object_view;
+
+ template <class Key, class T, class Json>
+ class staj_object_iterator
+ {
+ using char_type = typename Json::char_type;
+
+ staj_object_view<Key, T, Json>* view_;
+ std::exception_ptr eptr_;
+ public:
+ using key_type = std::basic_string<char_type>;
+ using value_type = std::pair<key_type,T>;
+ using difference_type = std::ptrdiff_t;
+ using pointer = value_type*;
+ using reference = value_type&;
+ using iterator_category = std::input_iterator_tag;
+
+ public:
+
+ staj_object_iterator() noexcept
+ : view_(nullptr)
+ {
+ }
+
+ staj_object_iterator(staj_object_view<Key, T, Json>& view)
+ : view_(std::addressof(view))
+ {
+ if (view_->cursor_->current().event_type() == staj_event_type::begin_object)
+ {
+ next();
+ }
+ else
+ {
+ view_ = nullptr;
+ }
+ }
+
+ staj_object_iterator(staj_object_view<Key, T, Json>& view,
+ std::error_code& ec)
+ : view_(std::addressof(view))
+ {
+ if (view_->cursor_->current().event_type() == staj_event_type::begin_object)
+ {
+ next(ec);
+ if (ec) {view_ = nullptr;}
+ }
+ else
+ {
+ view_ = nullptr;
+ }
+ }
+
+ ~staj_object_iterator() noexcept
+ {
+ }
+
+ bool has_value() const
+ {
+ return !eptr_;
+ }
+
+ const value_type& operator*() const
+ {
+ if (eptr_)
+ {
+ std::rethrow_exception(eptr_);
+ }
+ else
+ {
+ return *view_->key_value_;
+ }
+ }
+
+ const value_type* operator->() const
+ {
+ if (eptr_)
+ {
+ std::rethrow_exception(eptr_);
+ }
+ else
+ {
+ return view_->key_value_.operator->();
+ }
+ }
+
+ staj_object_iterator& operator++()
+ {
+ next();
+ return *this;
+ }
+
+ staj_object_iterator& increment(std::error_code& ec)
+ {
+ next(ec);
+ if (ec)
+ {
+ view_ = nullptr;
+ }
+ return *this;
+ }
+
+ staj_object_iterator operator++(int) // postfix increment
+ {
+ staj_object_iterator temp(*this);
+ next();
+ return temp;
+ }
+
+ friend bool operator==(const staj_object_iterator& a, const staj_object_iterator& b)
+ {
+ return (!a.view_ && !b.view_)
+ || (!a.view_ && b.done())
+ || (!b.view_ && a.done());
+ }
+
+ friend bool operator!=(const staj_object_iterator& a, const staj_object_iterator& b)
+ {
+ return !(a == b);
+ }
+
+ private:
+
+ bool done() const
+ {
+ return view_->cursor_->done() || view_->cursor_->current().event_type() == staj_event_type::end_object;
+ }
+
+ void next()
+ {
+ std::error_code ec;
+ next(ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, view_->cursor_->context().line(), view_->cursor_->context().column()));
+ }
+ }
+
+ void next(std::error_code& ec)
+ {
+ using char_type = typename Json::char_type;
+
+ view_->cursor_->next(ec);
+ if (ec)
+ {
+ return;
+ }
+ if (!done())
+ {
+ JSONCONS_ASSERT(view_->cursor_->current().event_type() == staj_event_type::key);
+ auto key = view_->cursor_->current(). template get<key_type>();
+ view_->cursor_->next(ec);
+ if (ec)
+ {
+ return;
+ }
+ if (!done())
+ {
+ eptr_ = std::exception_ptr();
+ JSONCONS_TRY
+ {
+ view_->key_value_ = value_type(std::move(key),decode_traits<T,char_type>::decode(*view_->cursor_, view_->decoder_, ec));
+ }
+ JSONCONS_CATCH(const conv_error&)
+ {
+ eptr_ = std::current_exception();
+ }
+ }
+ }
+ }
+ };
+
+ // staj_array_view
+
+ template <class T, class Json>
+ class staj_array_view
+ {
+ friend class staj_array_iterator<T, Json>;
+ public:
+ using char_type = typename Json::char_type;
+ using iterator = staj_array_iterator<T, Json>;
+ private:
+ basic_staj_cursor<char_type>* cursor_;
+ json_decoder<Json> decoder_;
+ jsoncons::optional<T> value_;
+ public:
+ staj_array_view(basic_staj_cursor<char_type>& cursor)
+ : cursor_(std::addressof(cursor))
+ {
+ }
+
+ iterator begin()
+ {
+ return staj_array_iterator<T, Json>(*this);
+ }
+
+ iterator end()
+ {
+ return staj_array_iterator<T, Json>();
+ }
+ };
+
+ // staj_object_view
+
+ template <class Key, class T, class Json>
+ class staj_object_view
+ {
+ friend class staj_object_iterator<Key,T,Json>;
+ public:
+ using char_type = typename Json::char_type;
+ using iterator = staj_object_iterator<Key,T,Json>;
+ using key_type = std::basic_string<char_type>;
+ using value_type = std::pair<key_type,T>;
+ private:
+ basic_staj_cursor<char_type>* cursor_;
+ json_decoder<Json> decoder_;
+ jsoncons::optional<value_type> key_value_;
+ public:
+ staj_object_view(basic_staj_cursor<char_type>& cursor)
+ : cursor_(std::addressof(cursor))
+ {
+ }
+
+ iterator begin()
+ {
+ return staj_object_iterator<Key,T,Json>(*this);
+ }
+
+ iterator end()
+ {
+ return staj_object_iterator<Key,T,Json>();
+ }
+ };
+
+ template <class T, class CharT, class Json=typename std::conditional<type_traits::is_basic_json<T>::value,T,basic_json<CharT>>::type>
+ staj_array_view<T, Json> staj_array(basic_staj_cursor<CharT>& cursor)
+ {
+ return staj_array_view<T, Json>(cursor);
+ }
+
+ template <class Key, class T, class CharT, class Json=typename std::conditional<type_traits::is_basic_json<T>::value,T,basic_json<CharT>>::type>
+ staj_object_view<Key, T, Json> staj_object(basic_staj_cursor<CharT>& cursor)
+ {
+ return staj_object_view<Key, T, Json>(cursor);
+ }
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+ template <class T, class CharT, class Json=typename std::conditional<type_traits::is_basic_json<T>::value,T,basic_json<CharT>>::type>
+ JSONCONS_DEPRECATED_MSG("Instead, use staj_array()")
+ staj_array_view<T, Json> make_array_iterator(basic_staj_cursor<CharT>& cursor)
+ {
+ return staj_array_view<T, Json>(cursor);
+ }
+
+ template <class T, class CharT, class Json=typename std::conditional<type_traits::is_basic_json<T>::value,T,basic_json<CharT>>::type>
+ JSONCONS_DEPRECATED_MSG("Instead, use staj_object()")
+ staj_object_view<std::basic_string<CharT>, T, Json> make_object_iterator(basic_staj_cursor<CharT>& cursor)
+ {
+ return staj_object_view<std::basic_string<CharT>, T, Json>(cursor);
+ }
+#endif
+
+} // namespace jsoncons
+
+#endif
+
diff --git a/include/jsoncons/tag_type.hpp b/include/jsoncons/tag_type.hpp
new file mode 100644
index 0000000..ffefd8a
--- /dev/null
+++ b/include/jsoncons/tag_type.hpp
@@ -0,0 +1,245 @@
+// Copyright 2019 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_TAG_TYPE_HPP
+#define JSONCONS_TAG_TYPE_HPP
+
+#include <ostream>
+#include <jsoncons/config/jsoncons_config.hpp>
+
+namespace jsoncons {
+
+struct null_type
+{
+ explicit null_type() = default;
+};
+
+struct temp_allocator_arg_t
+{
+ explicit temp_allocator_arg_t() = default;
+};
+
+constexpr temp_allocator_arg_t temp_allocator_arg{};
+
+struct result_allocator_arg_t
+{
+ explicit result_allocator_arg_t() = default;
+};
+
+constexpr result_allocator_arg_t result_allocator_arg{};
+
+struct half_arg_t
+{
+ explicit half_arg_t() = default;
+};
+
+constexpr half_arg_t half_arg{};
+
+struct json_array_arg_t
+{
+ explicit json_array_arg_t() = default;
+};
+
+constexpr json_array_arg_t json_array_arg{};
+
+struct json_object_arg_t
+{
+ explicit json_object_arg_t() = default;
+};
+
+constexpr json_object_arg_t json_object_arg{};
+
+struct byte_string_arg_t
+{
+ explicit byte_string_arg_t() = default;
+};
+
+constexpr byte_string_arg_t byte_string_arg{};
+
+struct json_const_pointer_arg_t
+{
+ explicit json_const_pointer_arg_t() = default;
+};
+
+constexpr json_const_pointer_arg_t json_const_pointer_arg{};
+
+enum class semantic_tag : uint8_t
+{
+ none = 0,
+ undefined = 0x01,
+ datetime = 0x02,
+ epoch_second = 0x03,
+ epoch_milli = 0x04,
+ epoch_nano = 0x05,
+ bigint = 0x06,
+ bigdec = 0x07,
+ bigfloat = 0x08,
+ float128 = 0x09,
+ base16 = 0x1a,
+ base64 = 0x1b,
+ base64url = 0x1c,
+ uri = 0x0d,
+ clamped = 0x0e,
+ multi_dim_row_major = 0x0f,
+ multi_dim_column_major = 0x10,
+ ext = 0x11,
+ id = 0x12,
+ regex = 0x13,
+ code = 0x14
+#if !defined(JSONCONS_NO_DEPRECATED)
+ , big_integer = bigint
+ , big_decimal = bigdec
+ , big_float = bigfloat
+ , date_time = datetime
+ , timestamp = epoch_second
+#endif
+};
+
+template <class CharT>
+std::basic_ostream<CharT>& operator<<(std::basic_ostream<CharT>& os, semantic_tag tag)
+{
+ static constexpr const CharT* na_name = JSONCONS_CSTRING_CONSTANT(CharT, "n/a");
+ static constexpr const CharT* undefined_name = JSONCONS_CSTRING_CONSTANT(CharT, "undefined");
+ static constexpr const CharT* datetime_name = JSONCONS_CSTRING_CONSTANT(CharT, "datetime");
+ static constexpr const CharT* epoch_second_name = JSONCONS_CSTRING_CONSTANT(CharT, "epoch-second");
+ static constexpr const CharT* epoch_milli_name = JSONCONS_CSTRING_CONSTANT(CharT, "epoch-milli");
+ static constexpr const CharT* epoch_nano_name = JSONCONS_CSTRING_CONSTANT(CharT, "epoch-nano");
+ static constexpr const CharT* bigint_name = JSONCONS_CSTRING_CONSTANT(CharT, "bigint");
+ static constexpr const CharT* bigdec_name = JSONCONS_CSTRING_CONSTANT(CharT, "bigdec");
+ static constexpr const CharT* bigfloat_name = JSONCONS_CSTRING_CONSTANT(CharT, "bigfloat");
+ static constexpr const CharT* base16_name = JSONCONS_CSTRING_CONSTANT(CharT, "base16");
+ static constexpr const CharT* base64_name = JSONCONS_CSTRING_CONSTANT(CharT, "base64");
+ static constexpr const CharT* base64url_name = JSONCONS_CSTRING_CONSTANT(CharT, "base64url");
+ static constexpr const CharT* uri_name = JSONCONS_CSTRING_CONSTANT(CharT, "uri");
+ static constexpr const CharT* clamped_name = JSONCONS_CSTRING_CONSTANT(CharT, "clamped");
+ static constexpr const CharT* multi_dim_row_major_name = JSONCONS_CSTRING_CONSTANT(CharT, "multi-dim-row-major");
+ static constexpr const CharT* multi_dim_column_major_name = JSONCONS_CSTRING_CONSTANT(CharT, "multi-dim-column-major");
+ static constexpr const CharT* ext_name = JSONCONS_CSTRING_CONSTANT(CharT, "ext");
+ static constexpr const CharT* id_name = JSONCONS_CSTRING_CONSTANT(CharT, "id");
+ static constexpr const CharT* float128_name = JSONCONS_CSTRING_CONSTANT(CharT, "float128");
+ static constexpr const CharT* regex_name = JSONCONS_CSTRING_CONSTANT(CharT, "regex");
+ static constexpr const CharT* code_name = JSONCONS_CSTRING_CONSTANT(CharT, "code");
+
+ switch (tag)
+ {
+ case semantic_tag::none:
+ {
+ os << na_name;
+ break;
+ }
+ case semantic_tag::undefined:
+ {
+ os << undefined_name;
+ break;
+ }
+ case semantic_tag::datetime:
+ {
+ os << datetime_name;
+ break;
+ }
+ case semantic_tag::epoch_second:
+ {
+ os << epoch_second_name;
+ break;
+ }
+ case semantic_tag::epoch_milli:
+ {
+ os << epoch_milli_name;
+ break;
+ }
+ case semantic_tag::epoch_nano:
+ {
+ os << epoch_nano_name;
+ break;
+ }
+ case semantic_tag::bigint:
+ {
+ os << bigint_name;
+ break;
+ }
+ case semantic_tag::bigdec:
+ {
+ os << bigdec_name;
+ break;
+ }
+ case semantic_tag::bigfloat:
+ {
+ os << bigfloat_name;
+ break;
+ }
+ case semantic_tag::float128:
+ {
+ os << float128_name;
+ break;
+ }
+ case semantic_tag::base16:
+ {
+ os << base16_name;
+ break;
+ }
+ case semantic_tag::base64:
+ {
+ os << base64_name;
+ break;
+ }
+ case semantic_tag::base64url:
+ {
+ os << base64url_name;
+ break;
+ }
+ case semantic_tag::uri:
+ {
+ os << uri_name;
+ break;
+ }
+ case semantic_tag::clamped:
+ {
+ os << clamped_name;
+ break;
+ }
+ case semantic_tag::multi_dim_row_major:
+ {
+ os << multi_dim_row_major_name;
+ break;
+ }
+ case semantic_tag::multi_dim_column_major:
+ {
+ os << multi_dim_column_major_name;
+ break;
+ }
+ case semantic_tag::ext:
+ {
+ os << ext_name;
+ break;
+ }
+ case semantic_tag::id:
+ {
+ os << id_name;
+ break;
+ }
+ case semantic_tag::regex:
+ {
+ os << regex_name;
+ break;
+ }
+ case semantic_tag::code:
+ {
+ os << code_name;
+ break;
+ }
+ }
+ return os;
+}
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+ JSONCONS_DEPRECATED_MSG("Instead, use semantic_tag") typedef semantic_tag semantic_tag_type;
+ JSONCONS_DEPRECATED_MSG("Instead, use byte_string_arg_t") typedef byte_string_arg_t bstr_arg_t;
+ constexpr byte_string_arg_t bstr_arg{};
+#endif
+
+}
+
+#endif
diff --git a/include/jsoncons/text_source_adaptor.hpp b/include/jsoncons/text_source_adaptor.hpp
new file mode 100644
index 0000000..491e8a6
--- /dev/null
+++ b/include/jsoncons/text_source_adaptor.hpp
@@ -0,0 +1,144 @@
+// Copyright 2021 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_TEXT_SOURCE_ADAPTOR_HPP
+#define JSONCONS_TEXT_SOURCE_ADAPTOR_HPP
+
+#include <cstddef>
+#include <string>
+#include <vector>
+#include <stdexcept>
+#include <system_error>
+#include <memory> // std::allocator_traits
+#include <vector> // std::vector
+#include <jsoncons/unicode_traits.hpp>
+#include <jsoncons/json_error.hpp> // json_errc
+#include <jsoncons/source.hpp>
+#include <jsoncons/json_exception.hpp>
+
+namespace jsoncons {
+
+ // unicode_source_adaptor
+
+ template<class Source,class Allocator>
+ class unicode_source_adaptor
+ {
+ public:
+ using value_type = typename Source::value_type;
+ using source_type = Source;
+ private:
+ source_type source_;
+ bool bof_;
+
+
+ public:
+ template <class Sourceable>
+ unicode_source_adaptor(Sourceable&& source)
+ : source_(std::forward<Sourceable>(source)),
+ bof_(true)
+ {
+ }
+
+ bool is_error() const
+ {
+ return source_.is_error();
+ }
+
+ bool eof() const
+ {
+ return source_.eof();
+ }
+
+ span<const value_type> read_buffer(std::error_code& ec)
+ {
+ if (source_.eof())
+ {
+ return span<const value_type>();
+ }
+
+ auto s = source_.read_buffer();
+ const value_type* data = s.data();
+ std::size_t length = s.size();
+
+ if (bof_ && length > 0)
+ {
+ auto r = unicode_traits::detect_encoding_from_bom(data, length);
+ if (!(r.encoding == unicode_traits::encoding_kind::utf8 || r.encoding == unicode_traits::encoding_kind::undetected))
+ {
+ ec = json_errc::illegal_unicode_character;
+ return;
+ }
+ length -= (r.ptr - data);
+ data = r.ptr;
+ bof_ = false;
+ }
+ return span<const value_type>(data, length);
+ }
+ };
+
+ // json_source_adaptor
+
+ template<class Source,class Allocator>
+ class json_source_adaptor
+ {
+ public:
+ using value_type = typename Source::value_type;
+ using value_type = typename Source::value_type;
+ using source_type = Source;
+ private:
+ source_type source_;
+ bool bof_;
+
+ public:
+
+ template <class Sourceable>
+ json_source_adaptor(Sourceable&& source)
+ : source_(std::forward<Sourceable>(source)),
+ bof_(true)
+ {
+ }
+
+ bool is_error() const
+ {
+ return source_.is_error();
+ }
+
+ bool eof() const
+ {
+ return source_.eof();
+ }
+
+ span<const value_type> read_buffer(std::error_code& ec)
+ {
+ if (source_.eof())
+ {
+ return span<const value_type>();
+ }
+
+ auto s = source_.read_buffer();
+ const value_type* data = s.data();
+ std::size_t length = s.size();
+
+ if (bof_ && length > 0)
+ {
+ auto r = unicode_traits::detect_json_encoding(data, length);
+ if (!(r.encoding == unicode_traits::encoding_kind::utf8 || r.encoding == unicode_traits::encoding_kind::undetected))
+ {
+ ec = json_errc::illegal_unicode_character;
+ return span<const value_type>();
+ }
+ length -= (r.ptr - data);
+ data = r.ptr;
+ bof_ = false;
+ }
+ return span<const value_type>(data, length);
+ }
+ };
+
+} // namespace jsoncons
+
+#endif
+
diff --git a/include/jsoncons/typed_array_view.hpp b/include/jsoncons/typed_array_view.hpp
new file mode 100644
index 0000000..d1b4906
--- /dev/null
+++ b/include/jsoncons/typed_array_view.hpp
@@ -0,0 +1,250 @@
+// 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)
+
+// See https://github.com/danielaparker/jsoncons for latest version
+
+#ifndef JSONCONS_TYPED_ARRAY_VIEW_HPP
+#define JSONCONS_TYPED_ARRAY_VIEW_HPP
+
+#include <memory> // std::allocator
+#include <string>
+#include <stdexcept>
+#include <system_error>
+#include <ios>
+#include <type_traits> // std::enable_if
+#include <array> // std::array
+#include <functional> // std::function
+#include <jsoncons/json_exception.hpp>
+#include <jsoncons/json_visitor.hpp>
+#include <jsoncons/bigint.hpp>
+#include <jsoncons/json_parser.hpp>
+#include <jsoncons/ser_context.hpp>
+#include <jsoncons/sink.hpp>
+#include <jsoncons/detail/write_number.hpp>
+#include <jsoncons/json_type_traits.hpp>
+#include <jsoncons/converter.hpp>
+
+namespace jsoncons {
+
+ struct uint8_array_arg_t {explicit uint8_array_arg_t() = default; };
+ constexpr uint8_array_arg_t uint8_array_arg = uint8_array_arg_t();
+ struct uint16_array_arg_t {explicit uint16_array_arg_t() = default; };
+ struct uint32_array_arg_t {explicit uint32_array_arg_t() = default; };
+ constexpr uint32_array_arg_t uint32_array_arg = uint32_array_arg_t();
+ struct uint64_array_arg_t {explicit uint64_array_arg_t() = default; };
+ constexpr uint64_array_arg_t uint64_array_arg = uint64_array_arg_t();
+ struct int8_array_arg_t {explicit int8_array_arg_t() = default; };
+ constexpr int8_array_arg_t int8_array_arg = int8_array_arg_t();
+ struct int16_array_arg_t {explicit int16_array_arg_t() = default; };
+ constexpr int16_array_arg_t int16_array_arg = int16_array_arg_t();
+ struct int32_array_arg_t {explicit int32_array_arg_t() = default; };
+ constexpr int32_array_arg_t int32_array_arg = int32_array_arg_t();
+ struct int64_array_arg_t {explicit int64_array_arg_t() = default; };
+ constexpr int64_array_arg_t int64_array_arg = int64_array_arg_t();
+ constexpr uint16_array_arg_t uint16_array_arg = uint16_array_arg_t();
+ struct half_array_arg_t {explicit half_array_arg_t() = default; };
+ constexpr half_array_arg_t half_array_arg = half_array_arg_t();
+ struct float_array_arg_t {explicit float_array_arg_t() = default; };
+ constexpr float_array_arg_t float_array_arg = float_array_arg_t();
+ struct double_array_arg_t {explicit double_array_arg_t() = default; };
+ constexpr double_array_arg_t double_array_arg = double_array_arg_t();
+ struct float128_array_arg_t {explicit float128_array_arg_t() = default; };
+ constexpr float128_array_arg_t float128_array_arg = float128_array_arg_t();
+
+ enum class typed_array_type{uint8_value=1,uint16_value,uint32_value,uint64_value,
+ int8_value,int16_value,int32_value,int64_value,
+ half_value, float_value,double_value};
+
+ class typed_array_view
+ {
+ typed_array_type type_;
+ union
+ {
+ const uint8_t* uint8_data_;
+ const uint16_t* uint16_data_;
+ const uint32_t* uint32_data_;
+ const uint64_t* uint64_data_;
+ const int8_t* int8_data_;
+ const int16_t* int16_data_;
+ const int32_t* int32_data_;
+ const int64_t* int64_data_;
+ const float* float_data_;
+ const double* double_data_;
+ } data_;
+ std::size_t size_;
+ public:
+
+ typed_array_view()
+ : type_(), data_(), size_(0)
+ {
+ }
+
+ typed_array_view(const typed_array_view& other)
+ : type_(other.type_), data_(other.data_), size_(other.size())
+ {
+ }
+
+ typed_array_view(typed_array_view&& other) noexcept
+ {
+ swap(*this,other);
+ }
+
+ typed_array_view(const uint8_t* data, std::size_t size)
+ : type_(typed_array_type::uint8_value), size_(size)
+ {
+ data_.uint8_data_ = data;
+ }
+
+ typed_array_view(const uint16_t* data, std::size_t size)
+ : type_(typed_array_type::uint16_value), size_(size)
+ {
+ data_.uint16_data_ = data;
+ }
+
+ typed_array_view(const uint32_t* data, std::size_t size)
+ : type_(typed_array_type::uint32_value), size_(size)
+ {
+ data_.uint32_data_ = data;
+ }
+
+ typed_array_view(const uint64_t* data, std::size_t size)
+ : type_(typed_array_type::uint64_value), size_(size)
+ {
+ data_.uint64_data_ = data;
+ }
+
+ typed_array_view(const int8_t* data, std::size_t size)
+ : type_(typed_array_type::int8_value), size_(size)
+ {
+ data_.int8_data_ = data;
+ }
+
+ typed_array_view(const int16_t* data, std::size_t size)
+ : type_(typed_array_type::int16_value), size_(size)
+ {
+ data_.int16_data_ = data;
+ }
+
+ typed_array_view(const int32_t* data, std::size_t size)
+ : type_(typed_array_type::int32_value), size_(size)
+ {
+ data_.int32_data_ = data;
+ }
+
+ typed_array_view(const int64_t* data, std::size_t size)
+ : type_(typed_array_type::int64_value), size_(size)
+ {
+ data_.int64_data_ = data;
+ }
+
+ typed_array_view(half_array_arg_t, const uint16_t* data, std::size_t size)
+ : type_(typed_array_type::half_value), size_(size)
+ {
+ data_.uint16_data_ = data;
+ }
+
+ typed_array_view(const float* data, std::size_t size)
+ : type_(typed_array_type::float_value), size_(size)
+ {
+ data_.float_data_ = data;
+ }
+
+ typed_array_view(const double* data, std::size_t size)
+ : type_(typed_array_type::double_value), size_(size)
+ {
+ data_.double_data_ = data;
+ }
+
+ typed_array_view& operator=(const typed_array_view& other)
+ {
+ typed_array_view temp(other);
+ swap(*this,temp);
+ return *this;
+ }
+
+ typed_array_type type() const {return type_;}
+
+ std::size_t size() const
+ {
+ return size_;
+ }
+
+ jsoncons::span<const uint8_t> data(uint8_array_arg_t) const
+ {
+ JSONCONS_ASSERT(type_ == typed_array_type::uint8_value);
+ return jsoncons::span<const uint8_t>(data_.uint8_data_, size_);
+ }
+
+ jsoncons::span<const uint16_t> data(uint16_array_arg_t) const
+ {
+ JSONCONS_ASSERT(type_ == typed_array_type::uint16_value);
+ return jsoncons::span<const uint16_t>(data_.uint16_data_, size_);
+ }
+
+ jsoncons::span<const uint32_t> data(uint32_array_arg_t) const
+ {
+ JSONCONS_ASSERT(type_ == typed_array_type::uint32_value);
+ return jsoncons::span<const uint32_t>(data_.uint32_data_, size_);
+ }
+
+ jsoncons::span<const uint64_t> data(uint64_array_arg_t) const
+ {
+ JSONCONS_ASSERT(type_ == typed_array_type::uint64_value);
+ return jsoncons::span<const uint64_t>(data_.uint64_data_, size_);
+ }
+
+ jsoncons::span<const int8_t> data(int8_array_arg_t) const
+ {
+ JSONCONS_ASSERT(type_ == typed_array_type::int8_value);
+ return jsoncons::span<const int8_t>(data_.int8_data_, size_);
+ }
+
+ jsoncons::span<const int16_t> data(int16_array_arg_t) const
+ {
+ JSONCONS_ASSERT(type_ == typed_array_type::int16_value);
+ return jsoncons::span<const int16_t>(data_.int16_data_, size_);
+ }
+
+ jsoncons::span<const int32_t> data(int32_array_arg_t) const
+ {
+ JSONCONS_ASSERT(type_ == typed_array_type::int32_value);
+ return jsoncons::span<const int32_t>(data_.int32_data_, size_);
+ }
+
+ jsoncons::span<const int64_t> data(int64_array_arg_t) const
+ {
+ JSONCONS_ASSERT(type_ == typed_array_type::int64_value);
+ return jsoncons::span<const int64_t>(data_.int64_data_, size_);
+ }
+
+ jsoncons::span<const uint16_t> data(half_array_arg_t) const
+ {
+ JSONCONS_ASSERT(type_ == typed_array_type::half_value);
+ return jsoncons::span<const uint16_t>(data_.uint16_data_, size_);
+ }
+
+ jsoncons::span<const float> data(float_array_arg_t) const
+ {
+ JSONCONS_ASSERT(type_ == typed_array_type::float_value);
+ return jsoncons::span<const float>(data_.float_data_, size_);
+ }
+
+ jsoncons::span<const double> data(double_array_arg_t) const
+ {
+ JSONCONS_ASSERT(type_ == typed_array_type::double_value);
+ return jsoncons::span<const double>(data_.double_data_, size_);
+ }
+
+ friend void swap(typed_array_view& a, typed_array_view& b) noexcept
+ {
+ std::swap(a.data_,b.data_);
+ std::swap(a.type_,b.type_);
+ std::swap(a.size_,b.size_);
+ }
+ };
+
+} // namespace jsoncons
+
+#endif
+
diff --git a/include/jsoncons/unicode_traits.hpp b/include/jsoncons/unicode_traits.hpp
new file mode 100644
index 0000000..f45bafe
--- /dev/null
+++ b/include/jsoncons/unicode_traits.hpp
@@ -0,0 +1,1330 @@
+// Copyright 2016 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/unicode_traits for latest version
+
+/*
+ * Includes code derived from Unicode, Inc decomposition code in ConvertUTF.h and ConvertUTF.c
+ * http://www.unicode.org/
+ *
+ * "Unicode, Inc. hereby grants the right to freely use the information
+ * supplied in this file in the creation of products supporting the
+ * Unicode Standard."
+*/
+
+#ifndef JSONCONS_UNICODE_TRAITS_HPP
+#define JSONCONS_UNICODE_TRAITS_HPP
+
+#include <cstring>
+#include <string>
+#include <iterator>
+#include <type_traits>
+#include <system_error>
+#include <limits>
+#include <jsoncons/config/compiler_support.hpp>
+#include <jsoncons/more_type_traits.hpp>
+
+namespace jsoncons { namespace unicode_traits {
+
+ enum class encoding_kind {undetected,utf8,utf16le,utf16be,utf32le,utf32be};
+
+ inline
+ std::string to_string(encoding_kind encoding)
+ {
+ switch (encoding)
+ {
+ case encoding_kind::utf8:
+ return "utf8";
+ case encoding_kind::utf16le:
+ return "utf16le";
+ case encoding_kind::utf16be:
+ return "utf16be";
+ case encoding_kind::utf32le:
+ return "utf32le";
+ case encoding_kind::utf32be:
+ return "utf32be";
+ default:
+ return "undetected";
+ }
+ }
+
+ template <class Byte>
+ struct detect_encoding_result
+ {
+ const Byte* ptr;
+ encoding_kind encoding;
+ };
+
+ template <class CharT>
+ typename std::enable_if<type_traits::is_char8<CharT>::value,detect_encoding_result<CharT>>::type
+ detect_encoding_from_bom(const CharT* data, std::size_t length)
+ {
+ const uint8_t bom_utf8[] = {0xef,0xbb,0xbf};
+ const uint8_t bom_utf16le[] = {0xff,0xfe};
+ const uint8_t bom_utf16be[] = {0xfe,0xff};
+ const uint8_t bom_utf32le[] = {0xff,0xfe,0x00,0x00};
+ const uint8_t bom_utf32be[] = {0x00,0x00,0xfe,0xff};
+
+ if (length >= 4 && !memcmp(data,bom_utf32le,4))
+ {
+ return detect_encoding_result<CharT>{data+4,encoding_kind::utf32le};
+ }
+ else if (length >= 4 && !memcmp(data,bom_utf32be,4))
+ {
+ return detect_encoding_result<CharT>{data+4,encoding_kind::utf32be};
+ }
+ else if (length >= 2 && !memcmp(data,bom_utf16le,2))
+ {
+ return detect_encoding_result<CharT>{data+2,encoding_kind::utf16le};
+ }
+ else if (length >= 2 && !memcmp(data,bom_utf16be,2))
+ {
+ return detect_encoding_result<CharT>{data+2,encoding_kind::utf16be};
+ }
+ else if (length >= 3 && !memcmp(data,bom_utf8,3))
+ {
+ return detect_encoding_result<CharT>{data+3,encoding_kind::utf8};
+ }
+ else
+ {
+ return detect_encoding_result<CharT>{data,encoding_kind::undetected};
+ }
+ }
+
+ template <class CharT>
+ typename std::enable_if<type_traits::is_char16<CharT>::value || type_traits::is_char32<CharT>::value,detect_encoding_result<CharT>>::type
+ detect_encoding_from_bom(const CharT* data, std::size_t)
+ {
+ return detect_encoding_result<CharT>{data,encoding_kind::undetected};
+ }
+
+ template <class CharT>
+ typename std::enable_if<type_traits::is_char8<CharT>::value,detect_encoding_result<CharT>>::type
+ detect_json_encoding(const CharT* data, std::size_t length)
+ {
+ detect_encoding_result<CharT> r = detect_encoding_from_bom(data,length);
+ if (r.encoding != encoding_kind::undetected)
+ {
+ return r;
+ }
+ else if (length < 4)
+ {
+ return detect_encoding_result<CharT>{data,encoding_kind::utf8};
+ }
+ else if (*data == 0 && *(data+1) == 0 && *(data+2) == 0)
+ {
+ return detect_encoding_result<CharT>{data,encoding_kind::utf32be};
+ }
+ else if (*data == 0 && *(data+2) == 0)
+ {
+ return detect_encoding_result<CharT>{data,encoding_kind::utf16be};
+ }
+ else if (*(data+1) == 0 && *(data+2) == 0 && *(data+3) == 0)
+ {
+ return detect_encoding_result<CharT>{data,encoding_kind::utf32le};
+ }
+ else if (*(data+1) == 0 && *(data+3) == 0)
+ {
+ return detect_encoding_result<CharT>{data,encoding_kind::utf16le};
+ }
+ else
+ {
+ return detect_encoding_result<CharT>{data,encoding_kind::utf8};
+ }
+ }
+
+ template <class CharT>
+ typename std::enable_if<type_traits::is_char16<CharT>::value || type_traits::is_char32<CharT>::value,detect_encoding_result<CharT>>::type
+ detect_json_encoding(const CharT* data, std::size_t)
+ {
+ return detect_encoding_result<CharT>{data,encoding_kind::undetected};
+ }
+
+ /*
+ * Magic values subtracted from a buffer value during UTF8 conversion.
+ * This table contains as many values as there might be trailing bytes
+ * in a UTF-8 sequence. Source: ConvertUTF.c
+ */
+ const uint32_t offsets_from_utf8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL,
+ 0x03C82080UL, 0xFA082080UL, 0x82082080UL };
+
+ /*
+ * Once the bits are split out into bytes of UTF-8, this is a mask OR-ed
+ * into the first byte, depending on how many bytes follow. There are
+ * as many entries in this table as there are UTF-8 sequence types.
+ * (I.e., one byte sequence, two byte... etc.). Remember that sequencs
+ * for *legal* UTF-8 will be 4 or fewer bytes total. Source: ConvertUTF.c
+ */
+ const uint8_t first_byte_mark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
+
+ /*
+ * Index into the table below with the first byte of a UTF-8 sequence to
+ * get the number of trailing bytes that are supposed to follow it.
+ * Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is
+ * left as-is for anyone who may want to do such conversion, which was
+ * allowed in earlier algorithms. Source: ConvertUTF.c
+ */
+ const uint8_t trailing_bytes_for_utf8[256] = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
+ };
+
+ // Some fundamental constants. Source: ConvertUTF.h
+ const uint32_t replacement_char = 0x0000FFFD;
+ const uint32_t max_bmp = 0x0000FFFF;
+ const uint32_t max_utf16 = 0x0010FFFF;
+ const uint32_t max_utf32 = 0x7FFFFFFF;
+ const uint32_t max_legal_utf32 = 0x0010FFFF;
+
+ const int half_shift = 10; // used for shifting by 10 bits
+ const uint32_t half_base = 0x0010000UL;
+ const uint32_t half_mask = 0x3FFUL;
+
+ const uint16_t sur_high_start = 0xD800;
+ const uint16_t sur_high_end = 0xDBFF;
+ const uint16_t sur_low_start = 0xDC00;
+ const uint16_t sur_low_end = 0xDFFF;
+
+ inline
+ static bool is_continuation_byte(unsigned char ch)
+ {
+ return (ch & 0xC0) == 0x80;
+ }
+
+ inline
+ bool is_high_surrogate(uint32_t ch) noexcept
+ {
+ return (ch >= sur_high_start && ch <= sur_high_end);
+ }
+
+ inline
+ bool is_low_surrogate(uint32_t ch) noexcept
+ {
+ return (ch >= sur_low_start && ch <= sur_low_end);
+ }
+
+ inline
+ bool is_surrogate(uint32_t ch) noexcept
+ {
+ return (ch >= sur_high_start && ch <= sur_low_end);
+ }
+
+ enum class conv_flags
+ {
+ strict = 0,
+ lenient
+ };
+
+ // conv_errc
+
+ enum class conv_errc
+ {
+ success = 0,
+ over_long_utf8_sequence = 1, // over long utf8 sequence
+ expected_continuation_byte, // expected continuation byte
+ unpaired_high_surrogate, // unpaired high surrogate UTF-16
+ illegal_surrogate_value, // UTF-16 surrogate values are illegal in UTF-32
+ source_exhausted, // partial character in source, but hit end
+ source_illegal // source sequence is illegal/malformed
+ };
+
+ class Unicode_traits_error_category_impl_
+ : public std::error_category
+ {
+ public:
+ virtual const char* name() const noexcept
+ {
+ return "unicode_traits conversion error";
+ }
+ virtual std::string message(int ev) const
+ {
+ switch (static_cast<conv_errc>(ev))
+ {
+ case conv_errc::over_long_utf8_sequence:
+ return "Over long utf8 sequence";
+ case conv_errc::expected_continuation_byte:
+ return "Expected continuation byte";
+ case conv_errc::unpaired_high_surrogate:
+ return "Unpaired high surrogate UTF-16";
+ case conv_errc::illegal_surrogate_value:
+ return "UTF-16 surrogate values are illegal in UTF-32";
+ case conv_errc::source_exhausted:
+ return "Partial character in source, but hit end";
+ case conv_errc::source_illegal:
+ return "Source sequence is illegal/malformed";
+ default:
+ return "";
+ break;
+ }
+ }
+ };
+
+ inline
+ const std::error_category& unicode_traits_error_category()
+ {
+ static Unicode_traits_error_category_impl_ instance;
+ return instance;
+ }
+
+ inline
+ std::error_code make_error_code(conv_errc result)
+ {
+ return std::error_code(static_cast<int>(result),unicode_traits_error_category());
+ }
+
+} // unicode_traits
+} // jsoncons
+
+namespace std {
+ template<>
+ struct is_error_code_enum<jsoncons::unicode_traits::conv_errc> : public true_type
+ {
+ };
+}
+
+namespace jsoncons { namespace unicode_traits {
+
+ // utf8
+
+ template <class CharT>
+ typename std::enable_if<type_traits::is_char8<CharT>::value, conv_errc>::type
+ is_legal_utf8(const CharT* first, std::size_t length)
+ {
+ uint8_t a;
+ const CharT* srcptr = first+length;
+ switch (length) {
+ default:
+ return conv_errc::over_long_utf8_sequence;
+ case 4:
+ if (((a = (*--srcptr))& 0xC0) != 0x80)
+ return conv_errc::expected_continuation_byte;
+ JSONCONS_FALLTHROUGH;
+ case 3:
+ if (((a = (*--srcptr))& 0xC0) != 0x80)
+ return conv_errc::expected_continuation_byte;
+ JSONCONS_FALLTHROUGH;
+ case 2:
+ if (((a = (*--srcptr))& 0xC0) != 0x80)
+ return conv_errc::expected_continuation_byte;
+
+ switch (static_cast<uint8_t>(*first))
+ {
+ // no fall-through in this inner switch
+ case 0xE0: if (a < 0xA0) return conv_errc::source_illegal; break;
+ case 0xED: if (a > 0x9F) return conv_errc::source_illegal; break;
+ case 0xF0: if (a < 0x90) return conv_errc::source_illegal; break;
+ case 0xF4: if (a > 0x8F) return conv_errc::source_illegal; break;
+ default: if (a < 0x80) return conv_errc::source_illegal;
+ }
+
+ JSONCONS_FALLTHROUGH;
+ case 1:
+ if (static_cast<uint8_t>(*first) >= 0x80 && static_cast<uint8_t>(*first) < 0xC2)
+ return conv_errc::source_illegal;
+ break;
+ }
+ if (static_cast<uint8_t>(*first) > 0xF4)
+ return conv_errc::source_illegal;
+
+ return conv_errc();
+ }
+
+ template <class...> using void_t = void;
+
+ template <class, class, class = void>
+ struct is_output_iterator : std::false_type {};
+
+ template <class I, class E>
+ struct is_output_iterator<I, E, void_t<
+ typename std::iterator_traits<I>::iterator_category,
+ decltype(*std::declval<I>() = std::declval<E>())>> : std::true_type {};
+
+ // is_same_size fixes issue with vs2013
+
+ // primary template
+ template<class T1, class T2, class Enable = void>
+ struct is_same_size : std::false_type
+ {
+ };
+
+ // specialization for non void types
+ template<class T1, class T2>
+ struct is_same_size<T1, T2, typename std::enable_if<!std::is_void<T1>::value && !std::is_void<T2>::value>::type>
+ {
+ static constexpr bool value = (sizeof(T1) == sizeof(T2));
+ };
+
+ // convert
+
+ template <class CharT>
+ struct convert_result
+ {
+ const CharT* ptr;
+ conv_errc ec;
+ };
+
+ // to_codepoint
+
+ template <class CharT,class CodepointT>
+ typename std::enable_if<type_traits::is_char8<CharT>::value && type_traits::is_char32<CodepointT>::value,
+ convert_result<CharT>>::type
+ to_codepoint(const CharT* first, const CharT* last,
+ CodepointT& ch,
+ conv_flags flags = conv_flags::strict) noexcept
+ {
+ ch = 0;
+ if (first >= last)
+ {
+ return convert_result<CharT>{first, conv_errc::source_exhausted};
+ }
+ conv_errc result = conv_errc();
+
+ unsigned short extra_bytes_to_read = trailing_bytes_for_utf8[static_cast<uint8_t>(*first)];
+ if (extra_bytes_to_read >= last - first)
+ {
+ result = conv_errc::source_exhausted;
+ return convert_result<CharT>{first, result};
+ }
+ // Do this check whether lenient or strict
+ if ((result=is_legal_utf8(first, extra_bytes_to_read+1)) != conv_errc())
+ {
+ return convert_result<CharT>{first, result};
+ }
+ // The cases all fall through. See "Note A" below.
+ switch (extra_bytes_to_read)
+ {
+ case 5:
+ ch += static_cast<uint8_t>(*first++);
+ ch <<= 6;
+ JSONCONS_FALLTHROUGH;
+ case 4:
+ ch += static_cast<uint8_t>(*first++);
+ ch <<= 6;
+ JSONCONS_FALLTHROUGH;
+ case 3:
+ ch += static_cast<uint8_t>(*first++);
+ ch <<= 6;
+ JSONCONS_FALLTHROUGH;
+ case 2:
+ ch += static_cast<uint8_t>(*first++);
+ ch <<= 6;
+ JSONCONS_FALLTHROUGH;
+ case 1:
+ ch += static_cast<uint8_t>(*first++);
+ ch <<= 6;
+ JSONCONS_FALLTHROUGH;
+ case 0:
+ ch += static_cast<uint8_t>(*first++);
+ break;
+ }
+ ch -= offsets_from_utf8[extra_bytes_to_read];
+
+ if (ch <= max_legal_utf32) {
+ /*
+ * UTF-16 surrogate values are illegal in UTF-32, and anything
+ * over Plane 17 (> 0x10FFFF) is illegal.
+ */
+ if (is_surrogate(ch) )
+ {
+ if (flags == conv_flags::strict)
+ {
+ first -= (extra_bytes_to_read+1); // return to the illegal value itself
+ result = conv_errc::source_illegal;
+ return convert_result<CharT>{first, result};
+ }
+ else
+ {
+ ch = replacement_char;
+ }
+ }
+ }
+ else // i.e., ch > max_legal_utf32
+ {
+ result = conv_errc::source_illegal;
+ ch = replacement_char;
+ }
+
+ return convert_result<CharT>{first,result} ;
+ }
+
+ template <class CharT,class CodepointT>
+ typename std::enable_if<type_traits::is_char16<CharT>::value && type_traits::is_char32<CodepointT>::value,
+ convert_result<CharT>>::type
+ to_codepoint(const CharT* first, const CharT* last,
+ CodepointT& ch,
+ conv_flags flags = conv_flags::strict) noexcept
+ {
+ ch = 0;
+ if (first >= last)
+ {
+ return convert_result<CharT>{first, conv_errc::source_exhausted};
+ }
+ conv_errc result = conv_errc();
+
+ ch = *first++;
+ // If we have a surrogate pair, convert to UTF32 first.
+ if (is_high_surrogate(ch))
+ {
+ // If the 16 bits following the high surrogate are in the first buffer...
+ if (first < last)
+ {
+ uint32_t ch2 = *first;
+ // If ptr's a low surrogate, convert to UTF32.
+ if (ch2 >= sur_low_start && ch2 <= sur_low_end )
+ {
+ ch = ((ch - sur_high_start) << half_shift)
+ + (ch2 - sur_low_start) + half_base;
+ ++first;
+ }
+ else if (flags == conv_flags::strict) // ptr's an unpaired high surrogate
+ {
+ --first; /* return to the illegal value itself */
+ result = conv_errc::source_illegal;
+ return convert_result<CharT>{first, result};
+ }
+ }
+ else
+ { /* We don't have the 16 bits following the high surrogate. */
+ --first; /* return to the high surrogate */
+ result = conv_errc::source_exhausted;
+ return convert_result<CharT>{first, result};
+ }
+ } else if (flags == conv_flags::strict) {
+ /* UTF-16 surrogate values are illegal in UTF-32 */
+ if (is_low_surrogate(ch) )
+ {
+ --first; /* return to the illegal value itself */
+ result = conv_errc::source_illegal;
+ return convert_result<CharT>{first, result};
+ }
+ }
+
+ return convert_result<CharT>{first,result} ;
+ }
+
+ template <class CharT,class CodepointT>
+ typename std::enable_if<type_traits::is_char32<CharT>::value && type_traits::is_char32<CodepointT>::value,
+ convert_result<CharT>>::type
+ to_codepoint(const CharT* first, const CharT* last,
+ CodepointT& ch,
+ conv_flags flags = conv_flags::strict) noexcept
+ {
+ ch = 0;
+ if (first >= last)
+ {
+ return convert_result<CharT>{first, conv_errc::source_exhausted};
+ }
+ conv_errc result = conv_errc();
+
+ ch = *first++;
+ if (flags == conv_flags::strict )
+ {
+ /* UTF-16 surrogate values are illegal in UTF-32 */
+ if (is_surrogate(ch))
+ {
+ --first; /* return to the illegal value itself */
+ result = conv_errc::illegal_surrogate_value;
+ return convert_result<CharT>{first,result} ;
+ }
+ }
+ if (!(ch <= max_legal_utf32))
+ {
+ ch = replacement_char;
+ result = conv_errc::source_illegal;
+ }
+
+ return convert_result<CharT>{first,result} ;
+ }
+
+ // convert
+
+ template <class CharT,class Container>
+ typename std::enable_if<type_traits::is_char8<CharT>::value
+ && type_traits::is_back_insertable<Container>::value
+ && type_traits::is_char8<typename Container::value_type>::value,
+ convert_result<CharT>>::type
+ convert(const CharT* data, std::size_t length, Container& target, conv_flags flags=conv_flags::strict)
+ {
+ (void)flags;
+
+ conv_errc result = conv_errc();
+ const CharT* last = data + length;
+ while (data != last)
+ {
+ std::size_t len = trailing_bytes_for_utf8[static_cast<uint8_t>(*data)] + 1;
+ if (len > (std::size_t)(last - data))
+ {
+ return convert_result<CharT>{data, conv_errc::source_exhausted};
+ }
+ if ((result=is_legal_utf8(data, len)) != conv_errc())
+ {
+ return convert_result<CharT>{data,result};
+ }
+
+ switch (len) {
+ case 4: target.push_back(static_cast<uint8_t>(*data++));
+ JSONCONS_FALLTHROUGH;
+ case 3: target.push_back(static_cast<uint8_t>(*data++));
+ JSONCONS_FALLTHROUGH;
+ case 2: target.push_back(static_cast<uint8_t>(*data++));
+ JSONCONS_FALLTHROUGH;
+ case 1: target.push_back(static_cast<uint8_t>(*data++));
+ }
+ }
+ return convert_result<CharT>{data,result} ;
+ }
+
+ template <class CharT,class Container>
+ typename std::enable_if<type_traits::is_char8<CharT>::value
+ && type_traits::is_back_insertable<Container>::value
+ && type_traits::is_char16<typename Container::value_type>::value,
+ convert_result<CharT>>::type
+ convert(const CharT* data, std::size_t length,
+ Container& target,
+ conv_flags flags = conv_flags::strict)
+ {
+ conv_errc result = conv_errc();
+
+ const CharT* last = data + length;
+ while (data != last)
+ {
+ unsigned short extra_bytes_to_read = trailing_bytes_for_utf8[static_cast<uint8_t>(*data)];
+ if (extra_bytes_to_read >= last - data)
+ {
+ result = conv_errc::source_exhausted;
+ break;
+ }
+ /* Do this check whether lenient or strict */
+ if ((result=is_legal_utf8(data, extra_bytes_to_read+1)) != conv_errc())
+ {
+ break;
+ }
+ /*
+ * The cases all fall through. See "Note A" below.
+ */
+ uint32_t ch = 0;
+ switch (extra_bytes_to_read) {
+ case 5: ch += static_cast<uint8_t>(*data++); ch <<= 6; /* remember, illegal UTF-8 */
+ JSONCONS_FALLTHROUGH;
+ case 4: ch += static_cast<uint8_t>(*data++); ch <<= 6; /* remember, illegal UTF-8 */
+ JSONCONS_FALLTHROUGH;
+ case 3: ch += static_cast<uint8_t>(*data++); ch <<= 6;
+ JSONCONS_FALLTHROUGH;
+ case 2: ch += static_cast<uint8_t>(*data++); ch <<= 6;
+ JSONCONS_FALLTHROUGH;
+ case 1: ch += static_cast<uint8_t>(*data++); ch <<= 6;
+ JSONCONS_FALLTHROUGH;
+ case 0: ch += static_cast<uint8_t>(*data++);
+ break;
+ }
+ ch -= offsets_from_utf8[extra_bytes_to_read];
+
+ if (ch <= max_bmp) { /* Target is a character <= 0xFFFF */
+ /* UTF-16 surrogate values are illegal in UTF-32 */
+ if (is_surrogate(ch) )
+ {
+ if (flags == conv_flags::strict) {
+ data -= (extra_bytes_to_read+1); /* return to the illegal value itself */
+ result = conv_errc::source_illegal;
+ break;
+ } else {
+ target.push_back(replacement_char);
+ }
+ } else {
+ target.push_back((uint16_t)ch); /* normal case */
+ }
+ } else if (ch > max_utf16) {
+ if (flags == conv_flags::strict) {
+ result = conv_errc::source_illegal;
+ data -= (extra_bytes_to_read+1); /* return to the start */
+ break; /* Bail out; shouldn't continue */
+ } else {
+ target.push_back(replacement_char);
+ }
+ } else {
+ /* target is a character in range 0xFFFF - 0x10FFFF. */
+ ch -= half_base;
+ target.push_back((uint16_t)((ch >> half_shift) + sur_high_start));
+ target.push_back((uint16_t)((ch & half_mask) + sur_low_start));
+ }
+ }
+ return convert_result<CharT>{data,result} ;
+ }
+
+ template <class CharT,class Container>
+ typename std::enable_if<type_traits::is_char8<CharT>::value
+ && type_traits::is_back_insertable<Container>::value
+ && type_traits::is_char32<typename Container::value_type>::value,
+ convert_result<CharT>>::type
+ convert(const CharT* data, std::size_t length,
+ Container& target,
+ conv_flags flags = conv_flags::strict)
+ {
+ conv_errc result = conv_errc();
+
+ const CharT* last = data + length;
+ while (data < last)
+ {
+ uint32_t ch = 0;
+ unsigned short extra_bytes_to_read = trailing_bytes_for_utf8[static_cast<uint8_t>(*data)];
+ if (extra_bytes_to_read >= last - data)
+ {
+ result = conv_errc::source_exhausted;
+ break;
+ }
+ /* Do this check whether lenient or strict */
+ if ((result=is_legal_utf8(data, extra_bytes_to_read+1)) != conv_errc())
+ {
+ break;
+ }
+ /*
+ * The cases all fall through. See "Note A" below.
+ */
+ switch (extra_bytes_to_read)
+ {
+ case 5:
+ ch += static_cast<uint8_t>(*data++);
+ ch <<= 6;
+ JSONCONS_FALLTHROUGH;
+ case 4:
+ ch += static_cast<uint8_t>(*data++);
+ ch <<= 6;
+ JSONCONS_FALLTHROUGH;
+ case 3:
+ ch += static_cast<uint8_t>(*data++);
+ ch <<= 6;
+ JSONCONS_FALLTHROUGH;
+ case 2:
+ ch += static_cast<uint8_t>(*data++);
+ ch <<= 6;
+ JSONCONS_FALLTHROUGH;
+ case 1:
+ ch += static_cast<uint8_t>(*data++);
+ ch <<= 6;
+ JSONCONS_FALLTHROUGH;
+ case 0:
+ ch += static_cast<uint8_t>(*data++);
+ break;
+ }
+ ch -= offsets_from_utf8[extra_bytes_to_read];
+
+ if (ch <= max_legal_utf32) {
+ /*
+ * UTF-16 surrogate values are illegal in UTF-32, and anything
+ * over Plane 17 (> 0x10FFFF) is illegal.
+ */
+ if (is_surrogate(ch) )
+ {
+ if (flags == conv_flags::strict) {
+ data -= (extra_bytes_to_read+1); /* return to the illegal value itself */
+ result = conv_errc::source_illegal;
+ break;
+ } else {
+ target.push_back(replacement_char);
+ }
+ } else {
+ target.push_back(ch);
+ }
+ } else { /* i.e., ch > max_legal_utf32 */
+ result = conv_errc::source_illegal;
+ target.push_back(replacement_char);
+ }
+ }
+ return convert_result<CharT>{data,result} ;
+ }
+
+ // utf16
+
+ template <class CharT,class Container>
+ typename std::enable_if<type_traits::is_char16<CharT>::value
+ && type_traits::is_back_insertable<Container>::value
+ && type_traits::is_char8<typename Container::value_type>::value,
+ convert_result<CharT>>::type
+ convert(const CharT* data, std::size_t length,
+ Container& target,
+ conv_flags flags = conv_flags::strict) {
+ conv_errc result = conv_errc();
+
+ const CharT* last = data + length;
+ while (data < last) {
+ unsigned short bytes_to_write = 0;
+ const uint32_t byteMask = 0xBF;
+ const uint32_t byteMark = 0x80;
+ uint32_t ch = *data++;
+ /* If we have a surrogate pair, convert to uint32_t data. */
+ if (is_high_surrogate(ch))
+ {
+ /* If the 16 bits following the high surrogate are in the data buffer... */
+ if (data < last) {
+ uint32_t ch2 = *data;
+ /* If ptr's a low surrogate, convert to uint32_t. */
+ if (ch2 >= sur_low_start && ch2 <= sur_low_end) {
+ ch = ((ch - sur_high_start) << half_shift)
+ + (ch2 - sur_low_start) + half_base;
+ ++data;
+ } else if (flags == conv_flags::strict) { /* ptr's an unpaired high surrogate */
+ --data; /* return to the illegal value itself */
+ result = conv_errc::unpaired_high_surrogate;
+ break;
+ }
+ } else { /* We don't have the 16 bits following the high surrogate. */
+ --data; /* return to the high surrogate */
+ result = conv_errc::source_exhausted;
+ break;
+ }
+ } else if (flags == conv_flags::strict) {
+ /* UTF-16 surrogate values are illegal in UTF-32 */
+ if (is_low_surrogate(ch))
+ {
+ --data; /* return to the illegal value itself */
+ result = conv_errc::source_illegal;
+ break;
+ }
+ }
+ /* Figure out how many bytes the result will require */
+ if (ch < (uint32_t)0x80) {
+ bytes_to_write = 1;
+ } else if (ch < (uint32_t)0x800) {
+ bytes_to_write = 2;
+ } else if (ch < (uint32_t)0x10000) {
+ bytes_to_write = 3;
+ } else if (ch < (uint32_t)0x110000) {
+ bytes_to_write = 4;
+ } else {
+ bytes_to_write = 3;
+ ch = replacement_char;
+ }
+
+ uint8_t byte1 = 0;
+ uint8_t byte2 = 0;
+ uint8_t byte3 = 0;
+ uint8_t byte4 = 0;
+
+ switch (bytes_to_write) { // note: everything falls through
+ case 4: byte4 = (uint8_t)((ch | byteMark) & byteMask); ch >>= 6;
+ JSONCONS_FALLTHROUGH;
+ case 3: byte3 = (uint8_t)((ch | byteMark) & byteMask); ch >>= 6;
+ JSONCONS_FALLTHROUGH;
+ case 2: byte2 = (uint8_t)((ch | byteMark) & byteMask); ch >>= 6;
+ JSONCONS_FALLTHROUGH;
+ case 1: byte1 = (uint8_t)(ch | first_byte_mark[bytes_to_write]);
+ break;
+ }
+ switch (bytes_to_write)
+ {
+ case 4:
+ target.push_back(byte1);
+ target.push_back(byte2);
+ target.push_back(byte3);
+ target.push_back(byte4);
+ break;
+ case 3:
+ target.push_back(byte1);
+ target.push_back(byte2);
+ target.push_back(byte3);
+ break;
+ case 2:
+ target.push_back(byte1);
+ target.push_back(byte2);
+ break;
+ case 1:
+ target.push_back(byte1);
+ break;
+ }
+ }
+ return convert_result<CharT>{data,result} ;
+ }
+
+ template <class CharT,class Container>
+ typename std::enable_if<type_traits::is_char16<CharT>::value
+ && type_traits::is_back_insertable<Container>::value
+ && type_traits::is_char16<typename Container::value_type>::value,
+ convert_result<CharT>>::type
+ convert(const CharT* data, std::size_t length,
+ Container& target,
+ conv_flags flags = conv_flags::strict)
+ {
+ conv_errc result = conv_errc();
+
+ const CharT* last = data + length;
+ while (data != last)
+ {
+ uint32_t ch = *data++;
+ /* If we have a surrogate pair, convert to uint32_t data. */
+ if (is_high_surrogate(ch))
+ {
+ /* If the 16 bits following the high surrogate are in the data buffer... */
+ if (data < last) {
+ uint32_t ch2 = *data;
+ /* If ptr's a low surrogate, */
+ if (ch2 >= sur_low_start && ch2 <= sur_low_end) {
+ target.push_back((uint16_t)ch);
+ target.push_back((uint16_t)ch2);
+ ++data;
+ } else if (flags == conv_flags::strict) { /* ptr's an unpaired high surrogate */
+ --data; /* return to the illegal value itself */
+ result = conv_errc::unpaired_high_surrogate;
+ break;
+ }
+ } else { /* We don't have the 16 bits following the high surrogate. */
+ --data; /* return to the high surrogate */
+ result = conv_errc::source_exhausted;
+ break;
+ }
+ } else if (is_low_surrogate(ch))
+ {
+ // illegal leading low surrogate
+ if (flags == conv_flags::strict) {
+ --data; /* return to the illegal value itself */
+ result = conv_errc::source_illegal;
+ break;
+ }
+ else
+ {
+ target.push_back((uint16_t)ch);
+ }
+ }
+ else
+ {
+ target.push_back((uint16_t)ch);
+ }
+ }
+ return convert_result<CharT>{data,result} ;
+ }
+
+ template <class CharT,class Container>
+ typename std::enable_if<type_traits::is_char16<CharT>::value
+ && type_traits::is_back_insertable<Container>::value
+ && type_traits::is_char32<typename Container::value_type>::value,
+ convert_result<CharT>>::type
+ convert(const CharT* data, std::size_t length,
+ Container& target,
+ conv_flags flags = conv_flags::strict)
+ {
+ conv_errc result = conv_errc();
+
+ const CharT* last = data + length;
+ while (data != last)
+ {
+ uint32_t ch = *data++;
+ /* If we have a surrogate pair, convert to UTF32 data. */
+ if (is_high_surrogate(ch))
+ {
+ /* If the 16 bits following the high surrogate are in the data buffer... */
+ if (data < last) {
+ uint32_t ch2 = *data;
+ /* If ptr's a low surrogate, convert to UTF32. */
+ if (ch2 >= sur_low_start && ch2 <= sur_low_end )
+ {
+ ch = ((ch - sur_high_start) << half_shift)
+ + (ch2 - sur_low_start) + half_base;
+ ++data;
+ } else if (flags == conv_flags::strict) { /* ptr's an unpaired high surrogate */
+ --data; /* return to the illegal value itself */
+ result = conv_errc::source_illegal;
+ break;
+ }
+ } else { /* We don't have the 16 bits following the high surrogate. */
+ --data; /* return to the high surrogate */
+ result = conv_errc::source_exhausted;
+ break;
+ }
+ } else if (flags == conv_flags::strict) {
+ /* UTF-16 surrogate values are illegal in UTF-32 */
+ if (is_low_surrogate(ch) )
+ {
+ --data; /* return to the illegal value itself */
+ result = conv_errc::source_illegal;
+ break;
+ }
+ }
+ target.push_back(ch);
+ }
+ return convert_result<CharT>{data,result} ;
+ }
+
+ // utf32
+
+ template <class CharT,class Container>
+ typename std::enable_if<type_traits::is_char32<CharT>::value
+ && type_traits::is_back_insertable<Container>::value
+ && type_traits::is_char8<typename Container::value_type>::value,
+ convert_result<CharT>>::type
+ convert(const CharT* data, std::size_t length,
+ Container& target,
+ conv_flags flags = conv_flags::strict)
+ {
+ conv_errc result = conv_errc();
+ const CharT* last = data + length;
+ while (data < last)
+ {
+ unsigned short bytes_to_write = 0;
+ const uint32_t byteMask = 0xBF;
+ const uint32_t byteMark = 0x80;
+ uint32_t ch = *data++;
+ if (flags == conv_flags::strict )
+ {
+ /* UTF-16 surrogate values are illegal in UTF-32 */
+ if (is_surrogate(ch))
+ {
+ --data; /* return to the illegal value itself */
+ result = conv_errc::illegal_surrogate_value;
+ break;
+ }
+ }
+ /*
+ * Figure out how many bytes the result will require. Turn any
+ * illegally large UTF32 things (> Plane 17) into replacement chars.
+ */
+ if (ch < (uint32_t)0x80) { bytes_to_write = 1;
+ } else if (ch < (uint32_t)0x800) { bytes_to_write = 2;
+ } else if (ch < (uint32_t)0x10000) { bytes_to_write = 3;
+ } else if (ch <= max_legal_utf32) { bytes_to_write = 4;
+ } else {
+ bytes_to_write = 3;
+ ch = replacement_char;
+ result = conv_errc::source_illegal;
+ }
+
+ uint8_t byte1 = 0;
+ uint8_t byte2 = 0;
+ uint8_t byte3 = 0;
+ uint8_t byte4 = 0;
+
+ switch (bytes_to_write) {
+ case 4:
+ byte4 = (uint8_t)((ch | byteMark) & byteMask); ch >>= 6;
+ JSONCONS_FALLTHROUGH;
+ case 3:
+ byte3 = (uint8_t)((ch | byteMark) & byteMask); ch >>= 6;
+ JSONCONS_FALLTHROUGH;
+ case 2:
+ byte2 = (uint8_t)((ch | byteMark) & byteMask); ch >>= 6;
+ JSONCONS_FALLTHROUGH;
+ case 1:
+ byte1 = (uint8_t) (ch | first_byte_mark[bytes_to_write]);
+ break;
+ }
+
+ switch (bytes_to_write)
+ {
+ case 4:
+ target.push_back(byte1);
+ target.push_back(byte2);
+ target.push_back(byte3);
+ target.push_back(byte4);
+ break;
+ case 3:
+ target.push_back(byte1);
+ target.push_back(byte2);
+ target.push_back(byte3);
+ break;
+ case 2:
+ target.push_back(byte1);
+ target.push_back(byte2);
+ break;
+ case 1:
+ target.push_back(byte1);
+ break;
+ }
+ }
+ return convert_result<CharT>{data,result} ;
+ }
+
+ template <class CharT,class Container>
+ typename std::enable_if<type_traits::is_char32<CharT>::value
+ && type_traits::is_back_insertable<Container>::value
+ && type_traits::is_char16<typename Container::value_type>::value,
+ convert_result<CharT>>::type
+ convert(const CharT* data, std::size_t length,
+ Container& target,
+ conv_flags flags = conv_flags::strict)
+ {
+ conv_errc result = conv_errc();
+
+ const CharT* last = data + length;
+ while (data != last)
+ {
+ uint32_t ch = *data++;
+ if (ch <= max_bmp) { /* Target is a character <= 0xFFFF */
+ /* UTF-16 surrogate values are illegal in UTF-32; 0xffff or 0xfffe are both reserved values */
+ if (is_surrogate(ch) )
+ {
+ if (flags == conv_flags::strict) {
+ --data; /* return to the illegal value itself */
+ result = conv_errc::source_illegal;
+ break;
+ } else {
+ target.push_back(replacement_char);
+ }
+ } else {
+ target.push_back((uint16_t)ch); /* normal case */
+ }
+ } else if (ch > max_legal_utf32) {
+ if (flags == conv_flags::strict) {
+ result = conv_errc::source_illegal;
+ } else {
+ target.push_back(replacement_char);
+ }
+ } else {
+ /* target is a character in range 0xFFFF - 0x10FFFF. */
+ ch -= half_base;
+ target.push_back((uint16_t)((ch >> half_shift) + sur_high_start));
+ target.push_back((uint16_t)((ch & half_mask) + sur_low_start));
+ }
+ }
+ return convert_result<CharT>{data,result} ;
+ }
+
+ template <class CharT,class Container>
+ typename std::enable_if<type_traits::is_char32<CharT>::value
+ && type_traits::is_back_insertable<Container>::value
+ && type_traits::is_char32<typename Container::value_type>::value,
+ convert_result<CharT>>::type
+ convert(const CharT* data, std::size_t length,
+ Container& target,
+ conv_flags flags = conv_flags::strict)
+ {
+ conv_errc result = conv_errc();
+
+ const CharT* last = data + length;
+ while (data != last)
+ {
+ uint32_t ch = *data++;
+ if (flags == conv_flags::strict )
+ {
+ /* UTF-16 surrogate values are illegal in UTF-32 */
+ if (is_surrogate(ch))
+ {
+ --data; /* return to the illegal value itself */
+ result = conv_errc::illegal_surrogate_value;
+ break;
+ }
+ }
+ if (ch <= max_legal_utf32)
+ {
+ target.push_back(ch);
+ }
+ else
+ {
+ target.push_back(replacement_char);
+ result = conv_errc::source_illegal;
+ }
+ }
+ return convert_result<CharT>{data,result} ;
+ }
+
+ // validate
+
+ template <class CharT>
+ typename std::enable_if<type_traits::is_char8<CharT>::value,
+ convert_result<CharT>>::type
+ validate(const CharT* data, std::size_t length) noexcept
+ {
+ conv_errc result = conv_errc();
+ const CharT* last = data + length;
+ while (data != last)
+ {
+ std::size_t len = static_cast<std::size_t>(trailing_bytes_for_utf8[static_cast<uint8_t>(*data)]) + 1;
+ if (len > (std::size_t)(last - data))
+ {
+ return convert_result<CharT>{data, conv_errc::source_exhausted};
+ }
+ if ((result=is_legal_utf8(data, len)) != conv_errc())
+ {
+ return convert_result<CharT>{data,result} ;
+ }
+ data += len;
+ }
+ return convert_result<CharT>{data,result} ;
+ }
+
+ // utf16
+
+ template <class CharT>
+ typename std::enable_if<type_traits::is_char16<CharT>::value,
+ convert_result<CharT>>::type
+ validate(const CharT* data, std::size_t length) noexcept
+ {
+ conv_errc result = conv_errc();
+
+ const CharT* last = data + length;
+ while (data != last)
+ {
+ uint32_t ch = *data++;
+ /* If we have a surrogate pair, validate to uint32_t data. */
+ if (is_high_surrogate(ch))
+ {
+ /* If the 16 bits following the high surrogate are in the data buffer... */
+ if (data < last) {
+ uint32_t ch2 = *data;
+ /* If ptr's a low surrogate, */
+ if (ch2 >= sur_low_start && ch2 <= sur_low_end) {
+ ++data;
+ } else {
+ --data; /* return to the illegal value itself */
+ result = conv_errc::unpaired_high_surrogate;
+ break;
+ }
+ }
+ else // We don't have the 16 bits following the high surrogate.
+ {
+ --data; /* return to the high surrogate */
+ result = conv_errc::source_exhausted;
+ break;
+ }
+ }
+ else if (is_low_surrogate(ch))
+ {
+ /* UTF-16 surrogate values are illegal in UTF-32 */
+ --data; /* return to the illegal value itself */
+ result = conv_errc::source_illegal;
+ break;
+ }
+ }
+ return convert_result<CharT>{data,result} ;
+ }
+
+ // utf32
+
+ template <class CharT>
+ typename std::enable_if<type_traits::is_char32<CharT>::value,
+ convert_result<CharT>>::type
+ validate(const CharT* data, std::size_t length) noexcept
+ {
+ conv_errc result = conv_errc();
+
+ const CharT* last = data + length;
+ while (data != last)
+ {
+ uint32_t ch = *data++;
+ /* UTF-16 surrogate values are illegal in UTF-32 */
+ if (is_surrogate(ch))
+ {
+ --data; /* return to the illegal value itself */
+ result = conv_errc::illegal_surrogate_value;
+ break;
+ }
+ if (!(ch <= max_legal_utf32))
+ {
+ result = conv_errc::source_illegal;
+ }
+ }
+ return convert_result<CharT>{data, result} ;
+ }
+
+ enum class encoding {u8,u16le,u16be,u32le,u32be,undetected};
+
+ template <class Iterator>
+ struct determine_encoding_result
+ {
+ Iterator it;
+ encoding ec;
+ };
+
+ template <class Iterator>
+ typename std::enable_if<std::is_integral<typename std::iterator_traits<Iterator>::value_type>::value && sizeof(typename std::iterator_traits<Iterator>::value_type) == sizeof(uint8_t),
+ determine_encoding_result<Iterator>>::type
+ detect_encoding(Iterator first, Iterator last) noexcept
+ {
+ Iterator it1 = first;
+ if (std::distance(first,last) < 4)
+ {
+ if (std::distance(first,last) == 3)
+ {
+ Iterator it2 = ++first;
+ Iterator it3 = ++first;
+ if (static_cast<uint8_t>(*it1) == 0xEF && static_cast<uint8_t>(*it2) == 0xBB && static_cast<uint8_t>(*it3) == 0xBF)
+ {
+ return determine_encoding_result<Iterator>{last,encoding::u8};
+ }
+ }
+ return determine_encoding_result<Iterator>{it1,encoding::undetected};
+ }
+ else
+ {
+ Iterator it2 = ++first;
+ Iterator it3 = ++first;
+ Iterator it4 = ++first;
+
+ uint32_t bom = static_cast<uint8_t>(*it1) | (static_cast<uint8_t>(*it2) << 8) | (static_cast<uint8_t>(*it3) << 16) | (static_cast<uint8_t>(*it4) << 24);
+ if (bom == 0xFFFE0000)
+ {
+ return determine_encoding_result<Iterator>{it4++,encoding::u32be};
+ }
+ else if (bom == 0x0000FEFF)
+ {
+ return determine_encoding_result<Iterator>{first,encoding::u32le};
+ }
+ else if ((bom & 0xFFFF) == 0xFFFE)
+ {
+ return determine_encoding_result<Iterator>{it3,encoding::u16be};
+ }
+ else if ((bom & 0xFFFF) == 0xFEFF)
+ {
+ return determine_encoding_result<Iterator>{it3,encoding::u16le};
+ }
+ else if ((bom & 0xFFFFFF) == 0xBFBBEF)
+ {
+ return determine_encoding_result<Iterator>{it4,encoding::u8};
+ }
+ else
+ {
+ uint32_t pattern = (static_cast<uint8_t>(*it1) ? 1 : 0) | (static_cast<uint8_t>(*it2) ? 2 : 0) | (static_cast<uint8_t>(*it3) ? 4 : 0) | (static_cast<uint8_t>(*it4) ? 8 : 0);
+ switch (pattern) {
+ case 0x08:
+ return determine_encoding_result<Iterator>{it1,encoding::u32be};
+ case 0x0A:
+ return determine_encoding_result<Iterator>{it1,encoding::u16be};
+ case 0x01:
+ return determine_encoding_result<Iterator>{it1,encoding::u32le};
+ case 0x05:
+ return determine_encoding_result<Iterator>{it1,encoding::u16le};
+ case 0x0F:
+ return determine_encoding_result<Iterator>{it1,encoding::u8};
+ default:
+ return determine_encoding_result<Iterator>{it1,encoding::undetected};
+ }
+ }
+ }
+ }
+
+ // count_codepoints
+
+ template <class CharT>
+ typename std::enable_if<type_traits::is_char8<CharT>::value || type_traits::is_char16<CharT>::value || type_traits::is_char32<CharT>::value, std::size_t>::type
+ count_codepoints(const CharT* data, std::size_t length,
+ conv_flags flags = conv_flags::strict) noexcept
+ {
+ conv_errc ec = conv_errc();
+
+ std::size_t count = 0;
+ const CharT* ptr = data;
+ const CharT* last = data + length;
+
+ for (; ptr < last; ++count)
+ {
+ uint32_t cp = 0;
+ auto r = to_codepoint(ptr, last, cp, flags);
+ if (r.ec != conv_errc())
+ {
+ ec = r.ec;
+ break;
+ }
+ ptr = r.ptr;
+ }
+ return ec == conv_errc() && ptr == last ? count : 0;
+ }
+
+} // unicode_traits
+} // jsoncons
+
+#endif
+
diff --git a/include/jsoncons/uri.hpp b/include/jsoncons/uri.hpp
new file mode 100644
index 0000000..51249ef
--- /dev/null
+++ b/include/jsoncons/uri.hpp
@@ -0,0 +1,635 @@
+// Copyright 2020 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_URI_HPP
+#define JSONCONS_URI_HPP
+
+#include <string> // std::string
+#include <algorithm>
+#include <sstream>
+#include <jsoncons/config/jsoncons_config.hpp>
+#include <jsoncons/json_exception.hpp>
+
+namespace jsoncons {
+
+ class uri
+ {
+ using part_type = std::pair<std::size_t,std::size_t>;
+
+ std::string uri_;
+ part_type scheme_;
+ part_type userinfo_;
+ part_type host_;
+ part_type port_;
+ part_type path_;
+ part_type query_;
+ part_type fragment_;
+ public:
+
+ uri() = default;
+
+ uri(const std::string& uri)
+ {
+ *this = parse(uri);
+ }
+
+ uri(jsoncons::string_view scheme,
+ jsoncons::string_view userinfo,
+ jsoncons::string_view host,
+ jsoncons::string_view port,
+ jsoncons::string_view path,
+ jsoncons::string_view query,
+ jsoncons::string_view fragment)
+ {
+ if (!scheme.empty())
+ {
+ uri_.append(std::string(scheme));
+ scheme_.second = uri_.length();
+ }
+ if (!userinfo.empty() || !host.empty() || !port.empty())
+ {
+ if (!scheme.empty())
+ {
+ uri_.append("://");
+ }
+
+ if (!userinfo.empty())
+ {
+ userinfo_.first = uri_.length();
+ uri_.append(std::string(userinfo));
+ userinfo_.second = uri_.length();
+ uri_.append("@");
+ }
+ else
+ {
+ userinfo_.first = userinfo_.second = uri_.length();
+ }
+
+ if (!host.empty())
+ {
+ host_.first = uri_.length();
+ uri_.append(std::string(host));
+ host_.second = uri_.length();
+ }
+ else
+ {
+ JSONCONS_THROW(json_runtime_error<std::invalid_argument>("uri error."));
+ }
+
+ if (!port.empty())
+ {
+ uri_.append(":");
+ port_.first = uri_.length();
+ uri_.append(std::string(port));
+ port_.second = uri_.length();
+ }
+ else
+ {
+ port_.first = port_.second = uri_.length();
+ }
+ }
+ else
+ {
+ userinfo_.first = userinfo_.second = uri_.length();
+ host_.first = host_.second = uri_.length();
+ port_.first = port_.second = uri_.length();
+ if (!scheme.empty())
+ {
+ if (!path.empty() || !query.empty() || !fragment.empty())
+ {
+ uri_.append(":");
+ }
+ else
+ {
+ JSONCONS_THROW(json_runtime_error<std::invalid_argument>("uri error."));
+ }
+ }
+ }
+
+ if (!path.empty())
+ {
+ // if the URI is not opaque and the path is not already prefixed
+ // with a '/', add one.
+ path_.first = uri_.length();
+ if (!host.empty() && (path.front() != '/'))
+ {
+ uri_.push_back('/');
+ }
+ uri_.append(std::string(path));
+ path_.second = uri_.length();
+ }
+ else
+ {
+ path_.first = path_.second = uri_.length();
+ }
+
+ if (!query.empty())
+ {
+ uri_.append("?");
+ query_.first = uri_.length();
+ uri_.append(std::string(query));
+ query_.second = uri_.length();
+ }
+ else
+ {
+ query_.first = query_.second = uri_.length();
+ }
+
+ if (!fragment.empty())
+ {
+ uri_.append("#");
+ fragment_.first = uri_.length();
+ uri_.append(std::string(fragment));
+ fragment_.second = uri_.length();
+ }
+ else
+ {
+ fragment_.first = fragment_.second = uri_.length();
+ }
+ }
+
+ const std::string& string() const
+ {
+ return uri_;
+ }
+
+ bool is_absolute() const noexcept
+ {
+ return scheme_.first != scheme_.second;
+ }
+
+ bool is_opaque() const noexcept
+ {
+ return is_absolute() && !authority().empty();
+ }
+
+ string_view base() const noexcept { return string_view(uri_.data()+scheme_.first,(path_.second-scheme_.first)); }
+
+ string_view scheme() const noexcept { return string_view(uri_.data()+scheme_.first,(scheme_.second-scheme_.first)); }
+
+ string_view userinfo() const noexcept { return string_view(uri_.data()+userinfo_.first,(userinfo_.second-userinfo_.first)); }
+
+ string_view host() const noexcept { return string_view(uri_.data()+host_.first,(host_.second-host_.first)); }
+
+ string_view port() const noexcept { return string_view(uri_.data()+port_.first,(port_.second-port_.first)); }
+
+ string_view path() const noexcept { return string_view(uri_.data()+path_.first,(path_.second-path_.first)); }
+
+ string_view query() const noexcept { return string_view(uri_.data()+query_.first,(query_.second-query_.first)); }
+
+ string_view fragment() const noexcept { return string_view(uri_.data()+fragment_.first,(fragment_.second-fragment_.first)); }
+
+ string_view authority() const noexcept { return string_view(uri_.data()+userinfo_.first,(port_.second-userinfo_.first)); }
+
+ uri resolve(const uri& base) const
+ {
+ // This implementation uses the psuedo-code given in
+ // http://tools.ietf.org/html/rfc3986#section-5.2.2
+
+ if (is_absolute() && !is_opaque())
+ {
+ return *this;
+ }
+
+ if (is_opaque())
+ {
+ return *this;
+ }
+
+ std::string userinfo, host, port, path, query, fragment;
+
+ if (!authority().empty())
+ {
+ // g -> http://g
+ if (!this->userinfo().empty())
+ {
+ userinfo = std::string(this->userinfo());
+ }
+
+ if (!this->host().empty())
+ {
+ host = std::string(this->host());
+ }
+
+ if (!this->port().empty())
+ {
+ port = std::string(this->port());
+ }
+
+ if (!this->path().empty())
+ {
+ path = remove_dot_segments(this->path());
+ }
+
+ if (!this->query().empty())
+ {
+ query = std::string(this->query());
+ }
+ }
+ else
+ {
+ if (this->path().empty())
+ {
+ if (!base.path().empty())
+ {
+ path = std::string(base.path());
+ }
+
+ if (!this->query().empty())
+ {
+ query = std::string(this->query());
+ }
+ else if (!base.query().empty())
+ {
+ query = std::string(base.query());
+ }
+ }
+ else
+ {
+ if (this->path().front() == '/')
+ {
+ path = remove_dot_segments(this->path());
+ }
+ else
+ {
+ path = merge_paths(base, *this);
+ }
+
+ if (!this->query().empty())
+ {
+ query = std::string(this->query());
+ }
+ }
+
+ if (!base.userinfo().empty())
+ {
+ userinfo = std::string(base.userinfo());
+ }
+
+ if (!base.host().empty())
+ {
+ host = std::string(base.host());
+ }
+
+ if (!base.port().empty())
+ {
+ port = std::string(base.port());
+ }
+ }
+
+ if (!this->fragment().empty())
+ {
+ fragment = std::string(this->fragment());
+ }
+
+ return uri(std::string(base.scheme()), userinfo, host, port, path, query, fragment);
+ }
+
+ int compare(const uri& other) const
+ {
+ int result = scheme().compare(other.scheme());
+ if (result != 0) return result;
+ result = userinfo().compare(other.userinfo());
+ if (result != 0) return result;
+ result = host().compare(other.host());
+ if (result != 0) return result;
+ result = port().compare(other.port());
+ if (result != 0) return result;
+ result = path().compare(other.path());
+ if (result != 0) return result;
+ result = query().compare(other.query());
+ if (result != 0) return result;
+ result = fragment().compare(other.fragment());
+
+ return result;
+ }
+
+ friend bool operator==(const uri& lhs, const uri& rhs)
+ {
+ return lhs.compare(rhs) == 0;
+ }
+
+ friend bool operator!=(const uri& lhs, const uri& rhs)
+ {
+ return lhs.compare(rhs) != 0;
+ }
+
+ friend bool operator<(const uri& lhs, const uri& rhs)
+ {
+ return lhs.compare(rhs) < 0;
+ }
+
+ friend bool operator<=(const uri& lhs, const uri& rhs)
+ {
+ return lhs.compare(rhs) <= 0;
+ }
+
+ friend bool operator>(const uri& lhs, const uri& rhs)
+ {
+ return lhs.compare(rhs) > 0;
+ }
+
+ friend bool operator>=(const uri& lhs, const uri& rhs)
+ {
+ return lhs.compare(rhs) >= 0;
+ }
+
+ private:
+ enum class parse_state {expect_scheme,
+ expect_first_slash,
+ expect_second_slash,
+ expect_authority,
+ expect_host_ipv6,
+ expect_userinfo,
+ expect_host,
+ expect_port,
+ expect_path,
+ expect_query,
+ expect_fragment};
+
+ uri(const std::string& uri, part_type scheme, part_type userinfo,
+ part_type host, part_type port, part_type path,
+ part_type query, part_type fragment)
+ : uri_(uri), scheme_(scheme), userinfo_(userinfo),
+ host_(host), port_(port), path_(path),
+ query_(query), fragment_(fragment)
+ {
+ }
+
+ static uri parse(const std::string& s)
+ {
+ part_type scheme;
+ part_type userinfo;
+ part_type host;
+ part_type port;
+ part_type path;
+ part_type query;
+ part_type fragment;
+
+ std::size_t start = 0;
+
+ parse_state state = parse_state::expect_scheme;
+ for (std::size_t i = 0; i < s.size(); ++i)
+ {
+ char c = s[i];
+ switch (state)
+ {
+ case parse_state::expect_scheme:
+ switch (c)
+ {
+ case ':':
+ scheme = std::make_pair(start,i);
+ state = parse_state::expect_first_slash;
+ start = i;
+ break;
+ case '#':
+ userinfo = std::make_pair(start,start);
+ host = std::make_pair(start,start);
+ port = std::make_pair(start,start);
+ path = std::make_pair(start,i);
+ query = std::make_pair(i,i);
+ state = parse_state::expect_fragment;
+ start = i+1;
+ break;
+ default:
+ break;
+ }
+ break;
+ case parse_state::expect_first_slash:
+ switch (c)
+ {
+ case '/':
+ state = parse_state::expect_second_slash;
+ break;
+ default:
+ start = i;
+ state = parse_state::expect_path;
+ break;
+ }
+ break;
+ case parse_state::expect_second_slash:
+ switch (c)
+ {
+ case '/':
+ state = parse_state::expect_authority;
+ start = i+1;
+ break;
+ default:
+ break;
+ }
+ break;
+ case parse_state::expect_authority:
+ switch (c)
+ {
+ case '[':
+ state = parse_state::expect_host_ipv6;
+ start = i+1;
+ break;
+ default:
+ state = parse_state::expect_userinfo;
+ start = i;
+ --i;
+ break;
+ }
+ break;
+ case parse_state::expect_host_ipv6:
+ switch (c)
+ {
+ case ']':
+ userinfo = std::make_pair(start,start);
+ host = std::make_pair(start,i);
+ port = std::make_pair(i,i);
+ state = parse_state::expect_path;
+ start = i+1;
+ break;
+ default:
+ break;
+ }
+ break;
+ case parse_state::expect_userinfo:
+ switch (c)
+ {
+ case '@':
+ userinfo = std::make_pair(start,i);
+ state = parse_state::expect_host;
+ start = i+1;
+ break;
+ case ':':
+ userinfo = std::make_pair(start,start);
+ host = std::make_pair(start,i);
+ state = parse_state::expect_port;
+ start = i+1;
+ break;
+ case '/':
+ userinfo = std::make_pair(start,start);
+ host = std::make_pair(start,i);
+ port = std::make_pair(i,i);
+ state = parse_state::expect_path;
+ start = i;
+ break;
+ default:
+ break;
+ }
+ break;
+ case parse_state::expect_host:
+ switch (c)
+ {
+ case ':':
+ host = std::make_pair(start,i);
+ state = parse_state::expect_port;
+ start = i+1;
+ break;
+ default:
+ break;
+ }
+ break;
+ case parse_state::expect_port:
+ switch (c)
+ {
+ case '/':
+ port = std::make_pair(start,i);
+ state = parse_state::expect_path;
+ start = i;
+ break;
+ default:
+ break;
+ }
+ break;
+ case parse_state::expect_path:
+ switch (c)
+ {
+ case '?':
+ path = std::make_pair(start,i);
+ state = parse_state::expect_query;
+ start = i+1;
+ break;
+ case '#':
+ path = std::make_pair(start,i);
+ query = std::make_pair(start,start);
+ state = parse_state::expect_fragment;
+ start = i+1;
+ break;
+ default:
+ break;
+ }
+ break;
+ case parse_state::expect_query:
+ switch (c)
+ {
+ case '#':
+ query = std::make_pair(start,i);
+ state = parse_state::expect_fragment;
+ start = i+1;
+ break;
+ default:
+ break;
+ }
+ break;
+ case parse_state::expect_fragment:
+ break;
+ }
+ }
+ switch (state)
+ {
+ case parse_state::expect_scheme:
+ userinfo = std::make_pair(start,start);
+ host = std::make_pair(start,start);
+ port = std::make_pair(start,start);
+ path = std::make_pair(start,s.size());
+ break;
+ case parse_state::expect_userinfo:
+ userinfo = std::make_pair(start,start);
+ host = std::make_pair(start,start);
+ port = std::make_pair(start,start);
+ path = std::make_pair(start,s.size());
+ break;
+ case parse_state::expect_path:
+ path = std::make_pair(start,s.size());
+ break;
+ case parse_state::expect_query:
+ query = std::make_pair(start,s.size());
+ break;
+ case parse_state::expect_fragment:
+ fragment = std::make_pair(start,s.size());
+ break;
+ default:
+ JSONCONS_THROW(std::invalid_argument("Invalid uri"));
+ break;
+ }
+
+ return uri(s, scheme, userinfo, host, port, path, query, fragment);
+ }
+
+ static std::string remove_dot_segments(const jsoncons::string_view& input)
+ {
+ std::string result = std::string(input);
+/*
+ std::size_t pos = 0;
+ while (pos < input.size())
+ {
+ if (input.compare(0, 3, "../"))
+ {
+ network_boost::erase_head(input, 3);
+ } else if (network_boost::starts_with(input, "./")) {
+ network_boost::erase_head(input, 2);
+ } else if (network_boost::starts_with(input, "/./")) {
+ network_boost::replace_head(input, 3, "/");
+ } else if (input == "/.") {
+ network_boost::replace_head(input, 2, "/");
+ } else if (network_boost::starts_with(input, "/../")) {
+ network_boost::erase_head(input, 3);
+ remove_last_segment(result);
+ } else if (network_boost::starts_with(input, "/..")) {
+ network_boost::replace_head(input, 3, "/");
+ remove_last_segment(result);
+ } else if (network_boost::algorithm::all(input, [](char ch) { return ch == '.'; })) {
+ input.clear();
+ }
+ else {
+ int n = (input.front() == '/')? 1 : 0;
+ auto slash = network_boost::find_nth(input, "/", n);
+ result.append(std::begin(input), std::begin(slash));
+ input.erase(std::begin(input), std::begin(slash));
+ }
+ }
+*/
+ return result;
+ }
+
+ static std::string merge_paths(const uri& base, const uri& relative)
+ {
+ std::string result;
+
+ if (base.path().empty())
+ {
+ result = "/";
+ }
+ else
+ {
+ const auto& base_path = base.path();
+ auto last_slash = base_path.rfind('/');
+ result.append(std::string(base_path.substr(0,last_slash+1)));
+ }
+ if (!relative.path().empty())
+ {
+ result.append(relative.path().begin(), relative.path().end());
+ }
+ return remove_dot_segments(jsoncons::string_view(result));
+ }
+
+ static void remove_last_segment(std::string& path)
+ {
+ auto last_slash = path.rfind('/');
+ if (last_slash != std::string::npos)
+ {
+ path.erase(last_slash);
+ }
+ }
+ };
+
+} // namespace jsoncons
+
+#endif
diff --git a/include/jsoncons_ext/LICENSE b/include/jsoncons_ext/LICENSE
new file mode 100644
index 0000000..ecf46ab
--- /dev/null
+++ b/include/jsoncons_ext/LICENSE
@@ -0,0 +1,28 @@
+// Copyright Daniel Parker 2013 - 2020.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/include/jsoncons_ext/bson/bson.hpp b/include/jsoncons_ext/bson/bson.hpp
new file mode 100644
index 0000000..ec3192d
--- /dev/null
+++ b/include/jsoncons_ext/bson/bson.hpp
@@ -0,0 +1,23 @@
+// Copyright 2013 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_BSON_BSON_HPP
+#define JSONCONS_BSON_BSON_HPP
+
+#include <string>
+#include <vector>
+#include <memory>
+#include <type_traits> // std::enable_if
+#include <istream> // std::basic_istream
+#include <jsoncons/json.hpp>
+#include <jsoncons/config/jsoncons_config.hpp>
+#include <jsoncons_ext/bson/bson_encoder.hpp>
+#include <jsoncons_ext/bson/bson_reader.hpp>
+#include <jsoncons_ext/bson/bson_cursor.hpp>
+#include <jsoncons_ext/bson/encode_bson.hpp>
+#include <jsoncons_ext/bson/decode_bson.hpp>
+
+#endif
diff --git a/include/jsoncons_ext/bson/bson_cursor.hpp b/include/jsoncons_ext/bson/bson_cursor.hpp
new file mode 100644
index 0000000..8baee53
--- /dev/null
+++ b/include/jsoncons_ext/bson/bson_cursor.hpp
@@ -0,0 +1,320 @@
+// 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)
+
+// See https://github.com/danielaparker/jsoncons for latest version
+
+#ifndef JSONCONS_BSON_BSON_CURSOR_HPP
+#define JSONCONS_BSON_BSON_CURSOR_HPP
+
+#include <memory> // std::allocator
+#include <string>
+#include <vector>
+#include <stdexcept>
+#include <system_error>
+#include <ios>
+#include <istream> // std::basic_istream
+#include <jsoncons/byte_string.hpp>
+#include <jsoncons/config/jsoncons_config.hpp>
+#include <jsoncons/json_visitor.hpp>
+#include <jsoncons/json_exception.hpp>
+#include <jsoncons/staj_cursor.hpp>
+#include <jsoncons/source.hpp>
+#include <jsoncons_ext/bson/bson_parser.hpp>
+
+namespace jsoncons {
+namespace bson {
+
+template<class Source=jsoncons::binary_stream_source,class Allocator=std::allocator<char>>
+class basic_bson_cursor : public basic_staj_cursor<char>, private virtual ser_context
+{
+ using super_type = basic_staj_cursor<char>;
+public:
+ using source_type = Source;
+ using char_type = char;
+ using allocator_type = Allocator;
+private:
+ basic_bson_parser<Source,Allocator> parser_;
+ basic_staj_visitor<char_type> cursor_visitor_;
+ bool eof_;
+
+ // Noncopyable and nonmoveable
+ basic_bson_cursor(const basic_bson_cursor&) = delete;
+ basic_bson_cursor& operator=(const basic_bson_cursor&) = delete;
+
+public:
+ using string_view_type = string_view;
+
+ template <class Sourceable>
+ basic_bson_cursor(Sourceable&& source,
+ const bson_decode_options& options = bson_decode_options(),
+ const Allocator& alloc = Allocator())
+ : parser_(std::forward<Sourceable>(source), options, alloc),
+ cursor_visitor_(accept_all),
+ eof_(false)
+ {
+ if (!done())
+ {
+ next();
+ }
+ }
+
+ // Constructors that set parse error codes
+
+ template <class Sourceable>
+ basic_bson_cursor(Sourceable&& source,
+ std::error_code& ec)
+ : basic_bson_cursor(std::allocator_arg, Allocator(),
+ std::forward<Sourceable>(source),
+ bson_decode_options(),
+ ec)
+ {
+ }
+
+ template <class Sourceable>
+ basic_bson_cursor(Sourceable&& source,
+ const bson_decode_options& options,
+ std::error_code& ec)
+ : basic_bson_cursor(std::allocator_arg, Allocator(),
+ std::forward<Sourceable>(source),
+ options,
+ ec)
+ {
+ }
+
+ template <class Sourceable>
+ basic_bson_cursor(std::allocator_arg_t, const Allocator& alloc,
+ Sourceable&& source,
+ const bson_decode_options& options,
+ std::error_code& ec)
+ : parser_(std::forward<Sourceable>(source), options, alloc),
+ cursor_visitor_(accept_all),
+ eof_(false)
+ {
+ if (!done())
+ {
+ next(ec);
+ }
+ }
+
+ void reset()
+ {
+ parser_.reset();
+ cursor_visitor_.reset();
+ eof_ = false;
+ if (!done())
+ {
+ next();
+ }
+ }
+
+ template <class Sourceable>
+ void reset(Sourceable&& source)
+ {
+ parser_.reset(std::forward<Sourceable>(source));
+ cursor_visitor_.reset();
+ eof_ = false;
+ if (!done())
+ {
+ next();
+ }
+ }
+
+ void reset(std::error_code& ec)
+ {
+ parser_.reset();
+ cursor_visitor_.reset();
+ eof_ = false;
+ if (!done())
+ {
+ next(ec);
+ }
+ }
+
+ template <class Sourceable>
+ void reset(Sourceable&& source, std::error_code& ec)
+ {
+ parser_.reset(std::forward<Sourceable>(source));
+ cursor_visitor_.reset();
+ eof_ = false;
+ if (!done())
+ {
+ next(ec);
+ }
+ }
+
+ bool done() const override
+ {
+ return parser_.done();
+ }
+
+ void array_expected(std::error_code& ec) override
+ {
+ if (cursor_visitor_.event().event_type() == staj_event_type::begin_object)
+ {
+ parser_.array_expected(cursor_visitor_, ec);
+ }
+ else
+ {
+ super_type::array_expected(ec);
+ }
+ }
+
+ const staj_event& current() const override
+ {
+ return cursor_visitor_.event();
+ }
+
+ void read_to(basic_json_visitor<char_type>& visitor) override
+ {
+ std::error_code ec;
+ read_to(visitor, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column()));
+ }
+ }
+
+ void read_to(basic_json_visitor<char_type>& visitor,
+ std::error_code& ec) override
+ {
+ if (staj_to_saj_event(cursor_visitor_.event(), visitor, *this, ec))
+ {
+ read_next(visitor, ec);
+ }
+ }
+
+ void next() override
+ {
+ std::error_code ec;
+ next(ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column()));
+ }
+ }
+
+ void next(std::error_code& ec) override
+ {
+ read_next(ec);
+ }
+
+ const ser_context& context() const override
+ {
+ return *this;
+ }
+
+ bool eof() const
+ {
+ return eof_;
+ }
+
+ std::size_t line() const override
+ {
+ return parser_.line();
+ }
+
+ std::size_t column() const override
+ {
+ return parser_.column();
+ }
+
+ friend
+ staj_filter_view operator|(basic_bson_cursor& cursor,
+ std::function<bool(const staj_event&, const ser_context&)> pred)
+ {
+ return staj_filter_view(cursor, pred);
+ }
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+
+ template <class Sourceable>
+ JSONCONS_DEPRECATED_MSG("Instead, use pipe syntax for filter")
+ basic_bson_cursor(Sourceable&& source,
+ std::function<bool(const staj_event&, const ser_context&)> filter,
+ std::error_code& ec)
+ : basic_bson_cursor(std::allocator_arg, Allocator(),
+ std::forward<Sourceable>(source), filter, ec)
+ {
+ }
+
+ template <class Sourceable>
+ JSONCONS_DEPRECATED_MSG("Instead, use pipe syntax for filter")
+ basic_bson_cursor(Sourceable&& source,
+ std::function<bool(const staj_event&, const ser_context&)> filter,
+ const bson_decode_options& options = bson_decode_options(),
+ const Allocator& alloc = Allocator())
+ : parser_(std::forward<Sourceable>(source), options, alloc),
+ cursor_visitor_(filter),
+ eof_(false)
+ {
+ if (!done())
+ {
+ next();
+ }
+ }
+
+ template <class Sourceable>
+ JSONCONS_DEPRECATED_MSG("Instead, use pipe syntax for filter")
+ basic_bson_cursor(std::allocator_arg_t, const Allocator& alloc,
+ Sourceable&& source,
+ std::function<bool(const staj_event&, const ser_context&)> filter,
+ std::error_code& ec)
+ : parser_(std::forward<Sourceable>(source),alloc),
+ cursor_visitor_(filter),
+ eof_(false)
+ {
+ if (!done())
+ {
+ next(ec);
+ }
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use read_to(basic_json_visitor<char_type>&)")
+ void read(basic_json_visitor<char_type>& visitor)
+ {
+ read_to(visitor);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use read_to(basic_json_visitor<char_type>&, std::error_code&)")
+ void read(basic_json_visitor<char_type>& visitor,
+ std::error_code& ec)
+ {
+ read_to(visitor, ec);
+ }
+#endif
+private:
+ static bool accept_all(const staj_event&, const ser_context&)
+ {
+ return true;
+ }
+
+ void read_next(std::error_code& ec)
+ {
+ parser_.restart();
+ while (!parser_.stopped())
+ {
+ parser_.parse(cursor_visitor_, ec);
+ if (ec) return;
+ }
+ }
+
+ void read_next(basic_json_visitor<char_type>& visitor, std::error_code& ec)
+ {
+ parser_.restart();
+ while (!parser_.stopped())
+ {
+ parser_.parse(visitor, ec);
+ if (ec) return;
+ }
+ }
+};
+
+using bson_stream_cursor = basic_bson_cursor<jsoncons::binary_stream_source>;
+using bson_bytes_cursor = basic_bson_cursor<jsoncons::bytes_source>;
+
+} // namespace bson
+} // namespace jsoncons
+
+#endif
+
diff --git a/include/jsoncons_ext/bson/bson_decimal128.hpp b/include/jsoncons_ext/bson/bson_decimal128.hpp
new file mode 100644
index 0000000..b487a04
--- /dev/null
+++ b/include/jsoncons_ext/bson/bson_decimal128.hpp
@@ -0,0 +1,865 @@
+#ifndef JSONCONS_BSON_BSON_DECIMAL128_HPP
+#define JSONCONS_BSON_BSON_DECIMAL128_HPP
+
+/*
+ * Implements decimal128_to_chars and decimal128_from_chars
+ *
+ * Based on the libjson functions bson_decimal128_to_string
+ * and bson_decimal128_from_string_w_len, available at
+ * https://github.com/mongodb/mongo-c-driver/blob/master/src/libbson/src/bson/bson-decimal128.h
+ * and https://github.com/mongodb/mongo-c-driver/blob/master/src/libbson/src/bson/bson-decimal128.c
+ *
+*/
+
+/*
+ * Copyright 2015 MongoDB, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <cstring>
+#include <ctype.h>
+#include <system_error>
+#include <algorithm>
+#include <jsoncons/config/jsoncons_config.hpp>
+
+namespace jsoncons { namespace bson {
+
+ struct decimal128_to_chars_result
+ {
+ char* ptr;
+ std::errc ec;
+ };
+
+ struct decimal128_from_chars_result
+ {
+ const char* ptr;
+ std::errc ec;
+ };
+
+/**
+ * BSON_DECIMAL128_STRING:
+ *
+ * The length of a decimal128 string (with null terminator).
+ *
+ * 1 for the sign
+ * 35 for digits and radix
+ * 2 for exponent indicator and sign
+ * 4 for exponent digits
+ */
+#define BSON_DECIMAL128_STRING 43
+
+ struct TP1
+ {
+ uint64_t low;
+ uint64_t high;
+
+ constexpr TP1() : low(0), high(0) {}
+ constexpr TP1(uint64_t hi, uint64_t lo) : low(lo), high(hi) {}
+ };
+ struct TP2
+ {
+ uint64_t high;
+ uint64_t low;
+
+ constexpr TP2() : high(0), low(0) {}
+ constexpr TP2(uint64_t hi, uint64_t lo) : high(hi), low(lo) {}
+ };
+
+ typedef std::conditional<
+ jsoncons::endian::native == jsoncons::endian::little,
+ TP1,
+ TP2
+ >::type decimal128_t;
+
+ inline
+ bool operator==(const decimal128_t& lhs, const decimal128_t& rhs)
+ {
+ return lhs.high == rhs.high && lhs.low == rhs.low;
+ }
+
+ inline
+ bool operator!=(const decimal128_t& lhs, const decimal128_t& rhs)
+ {
+ return !(lhs == rhs);
+ }
+
+ struct decimal128_limits
+ {
+ // The length of a decimal128 string (without null terminator).
+ //
+ // 1 for the sign
+ // 35 for digits and radix
+ // 2 for exponent indicator and sign
+ // 4 for exponent digits
+ static constexpr int buf_size = 42;
+ static constexpr int exponent_max = 6111;
+ static constexpr int exponent_min = -6176;
+ static constexpr int exponent_bias = 6176;
+ static constexpr int max_digits = 34;
+
+ static constexpr decimal128_t nan() {return decimal128_t(0x7c00000000000000ull, 0);}
+ static constexpr decimal128_t infinity() {return decimal128_t(0x7800000000000000ull, 0);}
+ static constexpr decimal128_t neg_infinity() {return decimal128_t(0x7800000000000000ull + 0x8000000000000000ull, 0);}
+ };
+
+ inline
+ bool is_nan(decimal128_t dec) { return dec == decimal128_limits::nan(); }
+
+ inline
+ bool is_inf(decimal128_t dec) { return dec == decimal128_limits::infinity(); }
+
+ inline
+ bool is_neg_inf(decimal128_t dec) { return dec == decimal128_limits::neg_infinity(); }
+
+ /**
+ * bson_uint128_t:
+ *
+ * This struct represents a 128 bit integer.
+ */
+ typedef struct {
+ uint32_t parts[4]; /* 32-bit words stored high to low. */
+ } bson_uint128_t;
+
+ typedef struct {
+ uint64_t high, low;
+ } bson_uint128_6464_t;
+
+ namespace detail {
+
+ /**
+ *------------------------------------------------------------------------------
+ *
+ * bson_uint128_divide1B --
+ *
+ * This function divides a #bson_uint128_t by 1000000000 (1 billion) and
+ * computes the quotient and remainder.
+ *
+ * The remainder will contain 9 decimal digits for conversion to string.
+ *
+ * @value The #bson_uint128_t operand.
+ * @quotient A pointer to store the #bson_uint128_t quotient.
+ * @rem A pointer to store the #uint64_t remainder.
+ *
+ * Returns:
+ * The quotient at @quotient and the remainder at @rem.
+ *
+ * Side effects:
+ * None.
+ *
+ *------------------------------------------------------------------------------
+ */
+
+ inline
+ void bson_uint128_divide1B (bson_uint128_t value, /* IN */
+ bson_uint128_t *quotient, /* OUT */
+ uint32_t *rem) /* OUT */
+ {
+ const uint32_t DIVISOR = 1000 * 1000 * 1000;
+ uint64_t _rem = 0;
+ int i = 0;
+
+ if (!value.parts[0] && !value.parts[1] && !value.parts[2] &&
+ !value.parts[3]) {
+ *quotient = value;
+ *rem = 0;
+ return;
+ }
+
+ for (i = 0; i <= 3; i++) {
+ _rem <<= 32; /* Adjust remainder to match value of next dividend */
+ _rem += value.parts[i]; /* Add the divided to _rem */
+ value.parts[i] = (uint32_t) (_rem / DIVISOR);
+ _rem %= DIVISOR; /* Store the remainder */
+ }
+
+ *quotient = value;
+ *rem = (uint32_t) _rem;
+ }
+
+ /**
+ *-------------------------------------------------------------------------
+ *
+ * mul64x64 --
+ *
+ * This function multiplies two &uint64_t into a &bson_uint128_6464_t.
+ *
+ * Returns:
+ * The product of @left and @right.
+ *
+ * Side Effects:
+ * None.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+ inline
+ void mul_64x64 (uint64_t left, /* IN */
+ uint64_t right, /* IN */
+ bson_uint128_6464_t *product) /* OUT */
+ {
+ uint64_t left_high, left_low, right_high, right_low, product_high,
+ product_mid, product_mid2, product_low;
+ bson_uint128_6464_t rt = {0,0};
+
+ if (!left && !right) {
+ *product = rt;
+ return;
+ }
+
+ left_high = left >> 32;
+ left_low = (uint32_t) left;
+ right_high = right >> 32;
+ right_low = (uint32_t) right;
+
+ product_high = left_high * right_high;
+ product_mid = left_high * right_low;
+ product_mid2 = left_low * right_high;
+ product_low = left_low * right_low;
+
+ product_high += product_mid >> 32;
+ product_mid = (uint32_t) product_mid + product_mid2 + (product_low >> 32);
+
+ product_high = product_high + (product_mid >> 32);
+ product_low = (product_mid << 32) + (uint32_t) product_low;
+
+ rt.high = product_high;
+ rt.low = product_low;
+ *product = rt;
+ }
+
+ /**
+ *------------------------------------------------------------------------------
+ *
+ * dec128_tolower --
+ *
+ * This function converts the ASCII character @c to lowercase. It is locale
+ * insensitive (unlike the stdlib tolower).
+ *
+ * Returns:
+ * The lowercased character.
+ */
+
+ inline
+ char dec128_tolower (char c)
+ {
+ if (isupper (c)) {
+ c += 32;
+ }
+
+ return c;
+ }
+
+ /**
+ *------------------------------------------------------------------------------
+ *
+ * dec128_istreq --
+ *
+ * This function compares the null-terminated *ASCII* strings @a and @b
+ * for case-insensitive equality.
+ *
+ * Returns:
+ * true if the strings are equal, false otherwise.
+ */
+
+ inline
+ bool dec128_istreq (const char* a, const char* lasta,
+ const char* b, const char* lastb)
+ {
+ while (!(a == lasta && b == lastb))
+ {
+ // strings are different lengths
+ if (a == lasta || b == lastb)
+ {
+ return false;
+ }
+
+ if (dec128_tolower (*a) != dec128_tolower (*b)) {
+ return false;
+ }
+
+ a++;
+ b++;
+ }
+
+ return true;
+ }
+
+ } // namespace detail
+
+
+ /**
+ *------------------------------------------------------------------------------
+ *
+ * decimal128_to_chars --
+ *
+ * This function converts a BID formatted decimal128 value to string,
+ * accepting a &decimal128_t as @dec. The string is stored at @str.
+ *
+ * @dec : The BID formatted decimal to convert.
+ * @str : The output decimal128 string. At least %BSON_DECIMAL128_STRING
+ *characters.
+ *
+ * Returns:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *------------------------------------------------------------------------------
+ */
+
+ inline
+ decimal128_to_chars_result decimal128_to_chars(char* first, char* last, const decimal128_t& dec)
+ {
+ const std::string bson_decimal128_inf = "Infinity";
+ const std::string bson_decimal128_nan = "NaN";
+
+ const uint32_t combination_mask = 0x1f; /* Extract least significant 5 bits */
+ const uint32_t exponent_mask = 0x3fff; /* Extract least significant 14 bits */
+ const uint32_t combination_infinity = 30; /* Value of combination field for Inf */
+ const uint32_t combination_nan = 31; /* Value of combination field for NaN */
+ const uint32_t exponent_bias = 6176; /* decimal128 exponent bias */
+
+ char* str_out = first; /* output pointer in string */
+ char significand_str[35]; /* decoded significand digits */
+
+ /* Note: bits in this routine are referred to starting at 0, */
+ /* from the sign bit, towards the coefficient. */
+ uint32_t high; /* bits 0 - 31 */
+ uint32_t midh; /* bits 32 - 63 */
+ uint32_t midl; /* bits 64 - 95 */
+ uint32_t low; /* bits 96 - 127 */
+ uint32_t combination; /* bits 1 - 5 */
+ uint32_t biased_exponent; /* decoded biased exponent (14 bits) */
+ uint32_t significand_digits = 0; /* the number of significand digits */
+ uint32_t significand[36] = {0}; /* the base-10 digits in the significand */
+ uint32_t *significand_read = significand; /* read pointer into significand */
+ int32_t exponent; /* unbiased exponent */
+ int32_t scientific_exponent; /* the exponent if scientific notation is
+ * used */
+ bool is_zero = false; /* true if the number is zero */
+
+ uint8_t significand_msb; /* the most signifcant significand bits (50-46) */
+ bson_uint128_t
+ significand128; /* temporary storage for significand decoding */
+
+ memset (significand_str, 0, sizeof (significand_str));
+
+ if ((int64_t) dec.high < 0) { /* negative */
+ *(str_out++) = '-';
+ }
+
+ low = (uint32_t) dec.low, midl = (uint32_t) (dec.low >> 32),
+ midh = (uint32_t) dec.high, high = (uint32_t) (dec.high >> 32);
+
+ /* Decode combination field and exponent */
+ combination = (high >> 26) & combination_mask;
+
+ if (JSONCONS_UNLIKELY ((combination >> 3) == 3)) {
+ /* Check for 'special' values */
+ if (combination == combination_infinity) { /* Infinity */
+ if (last-str_out >= static_cast<ptrdiff_t >(bson_decimal128_inf.size()))
+ {
+ std::memcpy(str_out, bson_decimal128_inf.data(), bson_decimal128_inf.size());
+ str_out += bson_decimal128_inf.size();
+ }
+ *str_out = 0;
+ //strcpy_s (str_out, last-str_out, bson_decimal128_inf.c_str());
+ 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;
+ if (last-str_out >= static_cast<ptrdiff_t >(bson_decimal128_nan.size()))
+ {
+ std::memcpy(str_out, bson_decimal128_nan.data(), bson_decimal128_nan.size());
+ str_out += bson_decimal128_nan.size();
+ }
+ *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()};
+ } else {
+ biased_exponent = (high >> 15) & exponent_mask;
+ significand_msb = 0x8 + ((high >> 14) & 0x1);
+ }
+ } else {
+ significand_msb = (high >> 14) & 0x7;
+ biased_exponent = (high >> 17) & exponent_mask;
+ }
+
+ exponent = biased_exponent - exponent_bias;
+ /* Create string of significand digits */
+
+ /* Convert the 114-bit binary number represented by */
+ /* (high, midh, midl, low) to at most 34 decimal */
+ /* digits through modulo and division. */
+ significand128.parts[0] = (high & 0x3fff) + ((significand_msb & 0xf) << 14);
+ significand128.parts[1] = midh;
+ significand128.parts[2] = midl;
+ significand128.parts[3] = low;
+
+ if (significand128.parts[0] == 0 && significand128.parts[1] == 0 &&
+ significand128.parts[2] == 0 && significand128.parts[3] == 0) {
+ is_zero = true;
+ } else if (significand128.parts[0] >= (1 << 17)) {
+ /* The significand is non-canonical or zero.
+ * In order to preserve compatibility with the densely packed decimal
+ * format, the maximum value for the significand of decimal128 is
+ * 1e34 - 1. If the value is greater than 1e34 - 1, the IEEE 754
+ * standard dictates that the significand is interpreted as zero.
+ */
+ is_zero = true;
+ } else {
+ for (int k = 3; k >= 0; k--) {
+ uint32_t least_digits = 0;
+ detail::bson_uint128_divide1B (
+ significand128, &significand128, &least_digits);
+
+ /* We now have the 9 least significant digits (in base 2). */
+ /* Convert and output to string. */
+ if (!least_digits) {
+ continue;
+ }
+
+ for (int j = 8; j >= 0; j--) {
+ significand[k * 9 + j] = least_digits % 10;
+ least_digits /= 10;
+ }
+ }
+ }
+
+ /* Output format options: */
+ /* Scientific - [-]d.dddE(+/-)dd or [-]dE(+/-)dd */
+ /* Regular - ddd.ddd */
+
+ if (is_zero) {
+ significand_digits = 1;
+ *significand_read = 0;
+ } else {
+ significand_digits = 36;
+ while (!(*significand_read)) {
+ significand_digits--;
+ significand_read++;
+ }
+ }
+
+ scientific_exponent = significand_digits - 1 + exponent;
+
+ /* The scientific exponent checks are dictated by the string conversion
+ * specification and are somewhat arbitrary cutoffs.
+ *
+ * We must check exponent > 0, because if this is the case, the number
+ * has trailing zeros. However, we *cannot* output these trailing zeros,
+ * because doing so would change the precision of the value, and would
+ * change stored data if the string converted number is round tripped.
+ */
+ if (scientific_exponent < -6 || exponent > 0) {
+ /* Scientific format */
+ *(str_out++) = char(*(significand_read++)) + '0';
+ significand_digits--;
+
+ if (significand_digits) {
+ *(str_out++) = '.';
+ }
+
+ for (std::size_t i = 0; i < significand_digits && (str_out - first) < 36; i++) {
+ *(str_out++) = char(*(significand_read++)) + '0';
+ }
+ /* Exponent */
+ *(str_out++) = 'E';
+
+ std::string s;
+ if (scientific_exponent >= 0) {
+ s.push_back('+');
+ }
+ jsoncons::detail::from_integer(scientific_exponent, s);
+ if (str_out + s.size() < last)
+ {
+ std::memcpy(str_out, s.data(), s.size());
+ }
+ else
+ {
+ return decimal128_to_chars_result{str_out, std::errc::value_too_large};
+ }
+ str_out += s.size();
+ } else {
+ /* Regular format with no decimal place */
+ if (exponent >= 0) {
+ for (std::size_t i = 0; i < significand_digits && (str_out - first) < 36; i++) {
+ *(str_out++) = char(*(significand_read++)) + '0';
+ }
+ } else {
+ int32_t radix_position = significand_digits + exponent;
+
+ if (radix_position > 0) { /* non-zero digits before radix */
+ for (int32_t i = 0;
+ i < radix_position && (str_out < last);
+ i++) {
+ *(str_out++) = char(*(significand_read++)) + '0';
+ }
+ } else { /* leading zero before radix point */
+ *(str_out++) = '0';
+ }
+
+ *(str_out++) = '.';
+ while (radix_position++ < 0) { /* add leading zeros after radix */
+ *(str_out++) = '0';
+ }
+
+ for (std::size_t i = 0;
+ (i < significand_digits - (std::max) (radix_position - 1, 0)) &&
+ (str_out < last);
+ i++) {
+ *(str_out++) = char(*(significand_read++)) + '0';
+ }
+ }
+ }
+ return decimal128_to_chars_result{str_out, std::errc()};
+ }
+
+
+
+ /**
+ *------------------------------------------------------------------------------
+ *
+ * bson_decimal128_from_string_w_len --
+ *
+ * This function converts @string in the format [+-]ddd[.]ddd[E][+-]dddd to
+ * decimal128. Out of range values are converted to +/-Infinity. Invalid
+ * strings are converted to NaN. @len is the length of the string, or -1
+ * meaning the string is null-terminated.
+ *
+ * If more digits are provided than the available precision allows,
+ * round to the nearest expressable decimal128 with ties going to even will
+ * occur.
+ *
+ * Note: @string must be ASCII only!
+ *
+ * Returns:
+ * true on success, or false on failure. @dec will be NaN if @str was invalid
+ * The &decimal128_t converted from @string at @dec.
+ *
+ * Side effects:
+ * None.
+ *
+ *------------------------------------------------------------------------------
+ */
+
+ inline
+ decimal128_from_chars_result decimal128_from_chars(const char* first, const char* last, decimal128_t& dec)
+ {
+ const string_view inf_str = "inf";
+ const string_view infinity_str = "infinity";
+ const string_view nan_str = "nan";
+
+ ptrdiff_t len = last - first;
+
+ bson_uint128_6464_t significand = {0,0};
+
+ const char* str_read = first; /* Read pointer for consuming str. */
+
+ /* Parsing state tracking */
+ bool is_negative = false;
+ bool saw_radix = false;
+ bool includes_sign = false; /* True if the input first contains a sign. */
+ bool found_nonzero = false;
+
+ size_t significant_digits = 0; /* Total number of significant digits
+ * (no leading or trailing zero) */
+ size_t ndigits_read = 0; /* Total number of significand digits read */
+ size_t ndigits = 0; /* Total number of digits (no leading zeros) */
+ size_t radix_position = 0; /* The number of the digits after radix */
+ size_t first_nonzero = 0; /* The index of the first non-zero in *str* */
+
+ uint16_t digits[decimal128_limits::max_digits] = {0};
+ uint16_t ndigits_stored = 0; /* The number of digits in digits */
+ uint16_t *digits_insert = digits; /* Insertion pointer for digits */
+ size_t first_digit = 0; /* The index of the first non-zero digit */
+ size_t last_digit = 0; /* The index of the last digit */
+
+ int32_t exponent = 0;
+ uint64_t significand_high = 0; /* The high 17 digits of the significand */
+ uint64_t significand_low = 0; /* The low 17 digits of the significand */
+ uint16_t biased_exponent = 0; /* The biased exponent */
+
+ dec.high = 0;
+ dec.low = 0;
+
+ if (*str_read == '+' || *str_read == '-') {
+ is_negative = *(str_read++) == '-';
+ includes_sign = true;
+ }
+
+ /* Check for Infinity or NaN */
+ if (!isdigit (*str_read) && *str_read != '.') {
+ if (detail::dec128_istreq (str_read, last, inf_str.data(), inf_str.data()+inf_str.length()) ||
+ 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()};
+ } 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()};
+ }
+
+ dec = decimal128_limits::nan();
+ return decimal128_from_chars_result{str_read,std::errc::invalid_argument};
+ }
+
+ /* Read digits */
+ while (((isdigit (*str_read) || *str_read == '.')) &&
+ (len == -1 || str_read < first + len)) {
+ if (*str_read == '.') {
+ if (saw_radix) {
+ dec = decimal128_limits::nan();
+ return decimal128_from_chars_result{str_read,std::errc::invalid_argument};
+ }
+
+ saw_radix = true;
+ str_read++;
+ continue;
+ }
+
+ if (ndigits_stored < 34) {
+ if (*str_read != '0' || found_nonzero) {
+ if (!found_nonzero) {
+ first_nonzero = ndigits_read;
+ }
+
+ found_nonzero = true;
+ *(digits_insert++) = *(str_read) - '0'; /* Only store 34 digits */
+ ndigits_stored++;
+ }
+ }
+
+ if (found_nonzero) {
+ ndigits++;
+ }
+
+ if (saw_radix) {
+ radix_position++;
+ }
+
+ ndigits_read++;
+ str_read++;
+ }
+
+ if (saw_radix && !ndigits_read) {
+ dec = decimal128_limits::nan();
+ return decimal128_from_chars_result{str_read,std::errc::invalid_argument};
+ }
+
+ /* Read exponent if exists */
+ if (*str_read == 'e' || *str_read == 'E') {
+ ++str_read;
+ 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())
+ {
+ dec = decimal128_limits::nan();
+ return decimal128_from_chars_result{str_read,std::errc::invalid_argument};
+ }
+ str_read = result.ptr;
+ }
+
+ if ((len == -1 || str_read < first + len) && *str_read) {
+ dec = decimal128_limits::nan();
+ return decimal128_from_chars_result{str_read,std::errc::invalid_argument};
+ }
+
+ /* Done reading input. */
+ /* Find first non-zero digit in digits */
+ first_digit = 0;
+
+ if (!ndigits_stored) { /* value is zero */
+ first_digit = 0;
+ last_digit = 0;
+ digits[0] = 0;
+ ndigits = 1;
+ ndigits_stored = 1;
+ significant_digits = 0;
+ } else {
+ last_digit = ndigits_stored - 1;
+ significant_digits = ndigits;
+ /* Mark trailing zeros as non-significant */
+ while (first[first_nonzero + significant_digits - 1 + includes_sign +
+ saw_radix] == '0') {
+ significant_digits--;
+ }
+ }
+
+
+ /* Normalization of exponent */
+ /* Correct exponent based on radix position, and shift significand as needed
+ */
+ /* to represent user input */
+
+ /* Overflow prevention */
+ if (exponent <= static_cast<int32_t>(radix_position) && static_cast<int32_t>(radix_position) - exponent > (1 << 14)) {
+ exponent = decimal128_limits::exponent_min;
+ } else {
+ exponent -= static_cast<int32_t>(radix_position);
+ }
+
+ /* Attempt to normalize the exponent */
+ while (exponent > decimal128_limits::exponent_max) {
+ /* Shift exponent to significand and decrease */
+ last_digit++;
+
+ if (last_digit - first_digit > decimal128_limits::max_digits) {
+ /* The exponent is too great to shift into the significand. */
+ if (significant_digits == 0) {
+ /* Value is zero, we are allowed to clamp the exponent. */
+ exponent = decimal128_limits::exponent_max;
+ break;
+ }
+
+ /* Overflow is not permitted, error. */
+ dec = decimal128_limits::nan();
+ return decimal128_from_chars_result{str_read,std::errc::invalid_argument};
+ }
+
+ exponent--;
+ }
+
+ while (exponent < decimal128_limits::exponent_min || ndigits_stored < ndigits) {
+ /* Shift last digit */
+ if (last_digit == 0) {
+ /* underflow is not allowed, but zero clamping is */
+ if (significant_digits == 0) {
+ exponent = decimal128_limits::exponent_min;
+ break;
+ }
+
+ dec = decimal128_limits::nan();
+ return decimal128_from_chars_result{str_read,std::errc::invalid_argument};
+ }
+
+ if (ndigits_stored < ndigits) {
+ if (first[ndigits - 1 + includes_sign + saw_radix] - '0' != 0 &&
+ significant_digits != 0) {
+ dec = decimal128_limits::nan();
+ return decimal128_from_chars_result{str_read,std::errc::invalid_argument};
+ }
+
+ ndigits--; /* adjust to match digits not stored */
+ } else {
+ if (digits[last_digit] != 0) {
+ /* Inexact rounding is not allowed. */
+ dec = decimal128_limits::nan();
+ return decimal128_from_chars_result{str_read,std::errc::invalid_argument};
+ }
+
+
+ last_digit--; /* adjust to round */
+ }
+
+ if (exponent < decimal128_limits::exponent_max) {
+ exponent++;
+ } else {
+ dec = decimal128_limits::nan();
+ return decimal128_from_chars_result{str_read,std::errc::invalid_argument};
+ }
+ }
+
+ /* Round */
+ /* We've normalized the exponent, but might still need to round. */
+ if (last_digit - first_digit + 1 < significant_digits) {
+ uint8_t round_digit;
+
+ /* There are non-zero digits after last_digit that need rounding. */
+ /* We round to nearest, ties to even */
+ round_digit =
+ first[first_nonzero + last_digit + includes_sign + saw_radix + 1] -
+ '0';
+
+ if (round_digit != 0) {
+ /* Inexact (non-zero) rounding is not allowed */
+ dec = decimal128_limits::nan();
+ return decimal128_from_chars_result{str_read,std::errc::invalid_argument};
+ }
+ }
+
+ /* Encode significand */
+ significand_high = 0, /* The high 17 digits of the significand */
+ significand_low = 0; /* The low 17 digits of the significand */
+
+ if (significant_digits == 0) { /* read a zero */
+ significand_high = 0;
+ significand_low = 0;
+ } else if (last_digit - first_digit < 17) {
+ size_t d_idx = first_digit;
+ significand_low = digits[d_idx++];
+
+ for (; d_idx <= last_digit; d_idx++) {
+ significand_low *= 10;
+ significand_low += digits[d_idx];
+ significand_high = 0;
+ }
+ } else {
+ size_t d_idx = first_digit;
+ significand_high = digits[d_idx++];
+
+ for (; d_idx <= last_digit - 17; d_idx++) {
+ significand_high *= 10;
+ significand_high += digits[d_idx];
+ }
+
+ significand_low = digits[d_idx++];
+
+ for (; d_idx <= last_digit; d_idx++) {
+ significand_low *= 10;
+ significand_low += digits[d_idx];
+ }
+ }
+
+ detail::mul_64x64 (significand_high, 100000000000000000ull, &significand);
+ significand.low += significand_low;
+
+ if (significand.low < significand_low) {
+ significand.high += 1;
+ }
+
+
+ biased_exponent = static_cast<uint16_t>(exponent + static_cast<int32_t>(decimal128_limits::exponent_bias));
+
+ /* Encode combination, exponent, and significand. */
+ if ((significand.high >> 49) & 1) {
+ /* Encode '11' into bits 1 to 3 */
+ dec.high |= (0x3ull << 61);
+ dec.high |= (biased_exponent & 0x3fffull) << 47;
+ dec.high |= significand.high & 0x7fffffffffffull;
+ } else {
+ dec.high |= (biased_exponent & 0x3fffull) << 49;
+ dec.high |= significand.high & 0x1ffffffffffffull;
+ }
+
+ dec.low = significand.low;
+
+ /* Encode sign */
+ if (is_negative) {
+ dec.high |= 0x8000000000000000ull;
+ }
+
+ return decimal128_from_chars_result{str_read,std::errc()};
+ }
+
+} // namespace bson
+} // namespace jsoncons
+
+#endif
diff --git a/include/jsoncons_ext/bson/bson_decimal128.hpp.bak b/include/jsoncons_ext/bson/bson_decimal128.hpp.bak
new file mode 100644
index 0000000..8e27ee1
--- /dev/null
+++ b/include/jsoncons_ext/bson/bson_decimal128.hpp.bak
@@ -0,0 +1,816 @@
+#ifndef JSONCONS_BSON_BSON_DECIMAL128_HPP
+#define JSONCONS_BSON_BSON_DECIMAL128_HPP
+
+/*
+ * Copyright 2015 MongoDB, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <system_error>
+#include <jsoncons/config/jsoncons_config.hpp>
+
+namespace jsoncons { namespace bson {
+
+ struct decimal128_to_chars_result
+ {
+ char* ptr;
+ std::errc ec;
+ };
+
+ struct decimal128_from_chars_result
+ {
+ const char* ptr;
+ std::errc ec;
+ };
+
+/**
+ * BSON_DECIMAL128_STRING:
+ *
+ * The length of a decimal128 string (with null terminator).
+ *
+ * 1 for the sign
+ * 35 for digits and radix
+ * 2 for exponent indicator and sign
+ * 4 for exponent digits
+ */
+#define BSON_DECIMAL128_STRING 43
+#define BSON_DECIMAL128_INF "Infinity"
+#define BSON_DECIMAL128_NAN "NaN"
+
+ struct TP1
+ {
+ uint64_t low;
+ uint64_t high;
+
+ constexpr TP1() : low(0), high(0) {}
+ constexpr TP1(uint64_t hi, uint64_t lo) : low(lo), high(hi) {}
+ };
+ struct TP2
+ {
+ uint64_t high;
+ uint64_t low;
+
+ constexpr TP2() : high(0), low(0) {}
+ constexpr TP2(uint64_t hi, uint64_t lo) : high(hi), low(lo) {}
+ };
+
+ typedef typename std::conditional<
+ jsoncons::endian::native == jsoncons::endian::little,
+ TP1,
+ TP2
+ >::type decimal128_t;
+
+ inline
+ bool operator==(const decimal128_t& lhs, const decimal128_t& rhs)
+ {
+ return lhs.high == rhs.high && lhs.low == rhs.low;
+ }
+
+ inline
+ bool operator!=(const decimal128_t& lhs, const decimal128_t& rhs)
+ {
+ return !(lhs == rhs);
+ }
+
+ struct decimal128_limits
+ {
+ // The length of a decimal128 string (without null terminator).
+ //
+ // 1 for the sign
+ // 35 for digits and radix
+ // 2 for exponent indicator and sign
+ // 4 for exponent digits
+ static constexpr int recommended_buffer_size = 42;
+ static constexpr decimal128_t nan = decimal128_t(0x7c00000000000000ull, 0);
+ static constexpr decimal128_t infinity = decimal128_t(0x7800000000000000ull, 0);
+ static constexpr decimal128_t neg_infinity = decimal128_t(0x7800000000000000ull + 0x8000000000000000ull, 0);
+ static constexpr int exponent_max = 6111;
+ static constexpr int exponent_min = -6176;
+ static constexpr int exponent_bias = 6176;
+ static constexpr int max_digits = 34;
+ };
+
+ /**
+ * bson_uint128_t:
+ *
+ * This struct represents a 128 bit integer.
+ */
+ typedef struct {
+ uint32_t parts[4]; /* 32-bit words stored high to low. */
+ } bson_uint128_t;
+
+ typedef struct {
+ uint64_t high, low;
+ } bson_uint128_6464_t;
+
+ namespace detail {
+
+ /**
+ *------------------------------------------------------------------------------
+ *
+ * bson_uint128_divide1B --
+ *
+ * This function divides a #bson_uint128_t by 1000000000 (1 billion) and
+ * computes the quotient and remainder.
+ *
+ * The remainder will contain 9 decimal digits for conversion to string.
+ *
+ * @value The #bson_uint128_t operand.
+ * @quotient A pointer to store the #bson_uint128_t quotient.
+ * @rem A pointer to store the #uint64_t remainder.
+ *
+ * Returns:
+ * The quotient at @quotient and the remainder at @rem.
+ *
+ * Side effects:
+ * None.
+ *
+ *------------------------------------------------------------------------------
+ */
+
+ inline
+ void bson_uint128_divide1B (bson_uint128_t value, /* IN */
+ bson_uint128_t *quotient, /* OUT */
+ uint32_t *rem) /* OUT */
+ {
+ const uint32_t DIVISOR = 1000 * 1000 * 1000;
+ uint64_t _rem = 0;
+ int i = 0;
+
+ if (!value.parts[0] && !value.parts[1] && !value.parts[2] &&
+ !value.parts[3]) {
+ *quotient = value;
+ *rem = 0;
+ return;
+ }
+
+ for (i = 0; i <= 3; i++) {
+ _rem <<= 32; /* Adjust remainder to match value of next dividend */
+ _rem += value.parts[i]; /* Add the divided to _rem */
+ value.parts[i] = (uint32_t) (_rem / DIVISOR);
+ _rem %= DIVISOR; /* Store the remainder */
+ }
+
+ *quotient = value;
+ *rem = (uint32_t) _rem;
+ }
+
+ /**
+ *-------------------------------------------------------------------------
+ *
+ * mul64x64 --
+ *
+ * This function multiplies two &uint64_t into a &bson_uint128_6464_t.
+ *
+ * Returns:
+ * The product of @left and @right.
+ *
+ * Side Effects:
+ * None.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+ inline
+ void mul_64x64 (uint64_t left, /* IN */
+ uint64_t right, /* IN */
+ bson_uint128_6464_t *product) /* OUT */
+ {
+ uint64_t left_high, left_low, right_high, right_low, product_high,
+ product_mid, product_mid2, product_low;
+ bson_uint128_6464_t rt = {0};
+
+ if (!left && !right) {
+ *product = rt;
+ return;
+ }
+
+ left_high = left >> 32;
+ left_low = (uint32_t) left;
+ right_high = right >> 32;
+ right_low = (uint32_t) right;
+
+ product_high = left_high * right_high;
+ product_mid = left_high * right_low;
+ product_mid2 = left_low * right_high;
+ product_low = left_low * right_low;
+
+ product_high += product_mid >> 32;
+ product_mid = (uint32_t) product_mid + product_mid2 + (product_low >> 32);
+
+ product_high = product_high + (product_mid >> 32);
+ product_low = (product_mid << 32) + (uint32_t) product_low;
+
+ rt.high = product_high;
+ rt.low = product_low;
+ *product = rt;
+ }
+
+ /**
+ *------------------------------------------------------------------------------
+ *
+ * dec128_tolower --
+ *
+ * This function converts the ASCII character @c to lowercase. It is locale
+ * insensitive (unlike the stdlib tolower).
+ *
+ * Returns:
+ * The lowercased character.
+ */
+
+ inline
+ char dec128_tolower (char c)
+ {
+ if (isupper (c)) {
+ c += 32;
+ }
+
+ return c;
+ }
+
+ /**
+ *------------------------------------------------------------------------------
+ *
+ * dec128_istreq --
+ *
+ * This function compares the null-terminated *ASCII* strings @a and @b
+ * for case-insensitive equality.
+ *
+ * Returns:
+ * true if the strings are equal, false otherwise.
+ */
+
+ inline
+ bool dec128_istreq (const char* a, /* IN */
+ const char* b /* IN */)
+ {
+ while (*a != '\0' || *b != '\0') {
+ /* strings are different lengths. */
+ if (*a == '\0' || *b == '\0') {
+ return false;
+ }
+
+ if (dec128_tolower (*a) != dec128_tolower (*b)) {
+ return false;
+ }
+
+ a++;
+ b++;
+ }
+
+ return true;
+ }
+
+ } // namespace detail
+
+
+ /**
+ *------------------------------------------------------------------------------
+ *
+ * decimal128_to_chars --
+ *
+ * This function converts a BID formatted decimal128 value to string,
+ * accepting a &decimal128_t as @dec. The string is stored at @str.
+ *
+ * @dec : The BID formatted decimal to convert.
+ * @str : The output decimal128 string. At least %BSON_DECIMAL128_STRING
+ *characters.
+ *
+ * Returns:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *------------------------------------------------------------------------------
+ */
+
+ inline
+ void decimal128_to_chars(char* first, char* last, const decimal128_t& dec)
+ {
+ uint32_t COMBINATION_MASK = 0x1f; /* Extract least significant 5 bits */
+ uint32_t EXPONENT_MASK = 0x3fff; /* Extract least significant 14 bits */
+ uint32_t COMBINATION_INFINITY = 30; /* Value of combination field for Inf */
+ uint32_t COMBINATION_NAN = 31; /* Value of combination field for NaN */
+ uint32_t EXPONENT_BIAS = 6176; /* decimal128 exponent bias */
+
+ char* str_out = first; /* output pointer in string */
+ char significand_str[35]; /* decoded significand digits */
+
+
+ /* Note: bits in this routine are referred to starting at 0, */
+ /* from the sign bit, towards the coefficient. */
+ uint32_t high; /* bits 0 - 31 */
+ uint32_t midh; /* bits 32 - 63 */
+ uint32_t midl; /* bits 64 - 95 */
+ uint32_t low; /* bits 96 - 127 */
+ uint32_t combination; /* bits 1 - 5 */
+ uint32_t biased_exponent; /* decoded biased exponent (14 bits) */
+ uint32_t significand_digits = 0; /* the number of significand digits */
+ uint32_t significand[36] = {0}; /* the base-10 digits in the significand */
+ uint32_t *significand_read = significand; /* read pointer into significand */
+ int32_t exponent; /* unbiased exponent */
+ int32_t scientific_exponent; /* the exponent if scientific notation is
+ * used */
+ bool is_zero = false; /* true if the number is zero */
+
+ uint8_t significand_msb; /* the most signifcant significand bits (50-46) */
+ bson_uint128_t
+ significand128; /* temporary storage for significand decoding */
+ size_t i; /* indexing variables */
+ int j, k;
+
+ memset (significand_str, 0, sizeof (significand_str));
+
+ if ((int64_t) dec.high < 0) { /* negative */
+ *(str_out++) = '-';
+ }
+
+ low = (uint32_t) dec.low, midl = (uint32_t) (dec.low >> 32),
+ midh = (uint32_t) dec.high, high = (uint32_t) (dec.high >> 32);
+
+ /* Decode combination field and exponent */
+ combination = (high >> 26) & COMBINATION_MASK;
+
+ if (JSONCONS_UNLIKELY ((combination >> 3) == 3)) {
+ /* Check for 'special' values */
+ if (combination == COMBINATION_INFINITY) { /* Infinity */
+ strcpy (str_out, BSON_DECIMAL128_INF);
+ return;
+ } else if (combination == COMBINATION_NAN) { /* NaN */
+ /* first, not str_out, to erase the sign */
+ strcpy (first, BSON_DECIMAL128_NAN);
+ /* we don't care about the NaN payload. */
+ return;
+ } else {
+ biased_exponent = (high >> 15) & EXPONENT_MASK;
+ significand_msb = 0x8 + ((high >> 14) & 0x1);
+ }
+ } else {
+ significand_msb = (high >> 14) & 0x7;
+ biased_exponent = (high >> 17) & EXPONENT_MASK;
+ }
+
+ exponent = biased_exponent - EXPONENT_BIAS;
+ /* Create string of significand digits */
+
+ /* Convert the 114-bit binary number represented by */
+ /* (high, midh, midl, low) to at most 34 decimal */
+ /* digits through modulo and division. */
+ significand128.parts[0] = (high & 0x3fff) + ((significand_msb & 0xf) << 14);
+ significand128.parts[1] = midh;
+ significand128.parts[2] = midl;
+ significand128.parts[3] = low;
+
+ if (significand128.parts[0] == 0 && significand128.parts[1] == 0 &&
+ significand128.parts[2] == 0 && significand128.parts[3] == 0) {
+ is_zero = true;
+ } else if (significand128.parts[0] >= (1 << 17)) {
+ /* The significand is non-canonical or zero.
+ * In order to preserve compatibility with the densely packed decimal
+ * format, the maximum value for the significand of decimal128 is
+ * 1e34 - 1. If the value is greater than 1e34 - 1, the IEEE 754
+ * standard dictates that the significand is interpreted as zero.
+ */
+ is_zero = true;
+ } else {
+ for (k = 3; k >= 0; k--) {
+ uint32_t least_digits = 0;
+ detail::bson_uint128_divide1B (
+ significand128, &significand128, &least_digits);
+
+ /* We now have the 9 least significant digits (in base 2). */
+ /* Convert and output to string. */
+ if (!least_digits) {
+ continue;
+ }
+
+ for (j = 8; j >= 0; j--) {
+ significand[k * 9 + j] = least_digits % 10;
+ least_digits /= 10;
+ }
+ }
+ }
+
+ /* Output format options: */
+ /* Scientific - [-]d.dddE(+/-)dd or [-]dE(+/-)dd */
+ /* Regular - ddd.ddd */
+
+ if (is_zero) {
+ significand_digits = 1;
+ *significand_read = 0;
+ } else {
+ significand_digits = 36;
+ while (!(*significand_read)) {
+ significand_digits--;
+ significand_read++;
+ }
+ }
+
+ scientific_exponent = significand_digits - 1 + exponent;
+
+ /* The scientific exponent checks are dictated by the string conversion
+ * specification and are somewhat arbitrary cutoffs.
+ *
+ * We must check exponent > 0, because if this is the case, the number
+ * has trailing zeros. However, we *cannot* output these trailing zeros,
+ * because doing so would change the precision of the value, and would
+ * change stored data if the string converted number is round tripped.
+ */
+ if (scientific_exponent < -6 || exponent > 0) {
+ /* Scientific format */
+ *(str_out++) = *(significand_read++) + '0';
+ significand_digits--;
+
+ if (significand_digits) {
+ *(str_out++) = '.';
+ }
+
+ for (i = 0; i < significand_digits && (str_out - first) < 36; i++) {
+ *(str_out++) = *(significand_read++) + '0';
+ }
+ /* Exponent */
+ *(str_out++) = 'E';
+ snprintf (str_out, 6, "%+d", scientific_exponent);
+ } else {
+ /* Regular format with no decimal place */
+ if (exponent >= 0) {
+ for (i = 0; i < significand_digits && (str_out - first) < 36; i++) {
+ *(str_out++) = *(significand_read++) + '0';
+ }
+ *str_out = '\0';
+ } else {
+ int32_t radix_position = significand_digits + exponent;
+
+ if (radix_position > 0) { /* non-zero digits before radix */
+ for (i = 0;
+ i < radix_position && (str_out < last);
+ i++) {
+ *(str_out++) = *(significand_read++) + '0';
+ }
+ } else { /* leading zero before radix point */
+ *(str_out++) = '0';
+ }
+
+ *(str_out++) = '.';
+ while (radix_position++ < 0) { /* add leading zeros after radix */
+ *(str_out++) = '0';
+ }
+
+ for (i = 0;
+ (i < significand_digits - (std::max) (radix_position - 1, 0)) &&
+ (str_out < last);
+ i++) {
+ *(str_out++) = *(significand_read++) + '0';
+ }
+ *str_out = '\0';
+ }
+ }
+ }
+
+
+
+ /**
+ *------------------------------------------------------------------------------
+ *
+ * bson_decimal128_from_string_w_len --
+ *
+ * This function converts @string in the format [+-]ddd[.]ddd[E][+-]dddd to
+ * decimal128. Out of range values are converted to +/-Infinity. Invalid
+ * strings are converted to NaN. @len is the length of the string, or -1
+ * meaning the string is null-terminated.
+ *
+ * If more digits are provided than the available precision allows,
+ * round to the nearest expressable decimal128 with ties going to even will
+ * occur.
+ *
+ * Note: @string must be ASCII only!
+ *
+ * Returns:
+ * true on success, or false on failure. @dec will be NaN if @str was invalid
+ * The &decimal128_t converted from @string at @dec.
+ *
+ * Side effects:
+ * None.
+ *
+ *------------------------------------------------------------------------------
+ */
+
+ inline
+ bool decimal128_from_chars(const char* first, const char* last, decimal128_t& dec)
+ {
+ int len = last - first;
+
+ bson_uint128_6464_t significand = {0};
+
+ const char* str_read = first; /* Read pointer for consuming str. */
+
+ /* Parsing state tracking */
+ bool is_negative = false;
+ bool saw_radix = false;
+ bool includes_sign = false; /* True if the input first contains a sign. */
+ bool found_nonzero = false;
+
+ size_t significant_digits = 0; /* Total number of significant digits
+ * (no leading or trailing zero) */
+ size_t ndigits_read = 0; /* Total number of significand digits read */
+ size_t ndigits = 0; /* Total number of digits (no leading zeros) */
+ size_t radix_position = 0; /* The number of the digits after radix */
+ size_t first_nonzero = 0; /* The index of the first non-zero in *str* */
+
+ uint16_t digits[decimal128_limits::max_digits] = {0};
+ uint16_t ndigits_stored = 0; /* The number of digits in digits */
+ uint16_t *digits_insert = digits; /* Insertion pointer for digits */
+ size_t first_digit = 0; /* The index of the first non-zero digit */
+ size_t last_digit = 0; /* The index of the last digit */
+
+ int32_t exponent = 0;
+ uint64_t significand_high = 0; /* The high 17 digits of the significand */
+ uint64_t significand_low = 0; /* The low 17 digits of the significand */
+ uint16_t biased_exponent = 0; /* The biased exponent */
+
+ dec.high = 0;
+ dec.low = 0;
+
+ if (*str_read == '+' || *str_read == '-') {
+ is_negative = *(str_read++) == '-';
+ includes_sign = true;
+ }
+
+ /* Check for Infinity or NaN */
+ if (!isdigit (*str_read) && *str_read != '.') {
+ if (detail::dec128_istreq (str_read, "inf") ||
+ detail::dec128_istreq (str_read, "infinity")) {
+ dec = is_negative ? decimal128_limits::neg_infinity : decimal128_limits::infinity;
+ return true;
+ } else if (detail::dec128_istreq (str_read, "nan")) {
+ dec = decimal128_limits::nan;
+ return true;
+ }
+
+ dec = decimal128_limits::nan;
+ return false;
+ }
+
+ /* Read digits */
+ while (((isdigit (*str_read) || *str_read == '.')) &&
+ (len == -1 || str_read < first + len)) {
+ if (*str_read == '.') {
+ if (saw_radix) {
+ dec = decimal128_limits::nan;
+ return false;
+ }
+
+ saw_radix = true;
+ str_read++;
+ continue;
+ }
+
+ if (ndigits_stored < 34) {
+ if (*str_read != '0' || found_nonzero) {
+ if (!found_nonzero) {
+ first_nonzero = ndigits_read;
+ }
+
+ found_nonzero = true;
+ *(digits_insert++) = *(str_read) - '0'; /* Only store 34 digits */
+ ndigits_stored++;
+ }
+ }
+
+ if (found_nonzero) {
+ ndigits++;
+ }
+
+ if (saw_radix) {
+ radix_position++;
+ }
+
+ ndigits_read++;
+ str_read++;
+ }
+
+ if (saw_radix && !ndigits_read) {
+ dec = decimal128_limits::nan;
+ return false;
+ }
+
+ /* Read exponent if exists */
+ if (*str_read == 'e' || *str_read == 'E') {
+ int nread = 0;
+ #ifdef _MSC_VER
+ #define SSCANF sscanf_s
+ #else
+ #define SSCANF sscanf
+ #endif
+ int read_exponent = SSCANF (++str_read, "%d%n", &exponent, &nread);
+ str_read += nread;
+
+ if (!read_exponent || nread == 0) {
+ dec = decimal128_limits::nan;
+ return false;
+ }
+
+ #undef SSCANF
+ }
+
+ if ((len == -1 || str_read < first + len) && *str_read) {
+ dec = decimal128_limits::nan;
+ return false;
+ }
+
+ /* Done reading input. */
+ /* Find first non-zero digit in digits */
+ first_digit = 0;
+
+ if (!ndigits_stored) { /* value is zero */
+ first_digit = 0;
+ last_digit = 0;
+ digits[0] = 0;
+ ndigits = 1;
+ ndigits_stored = 1;
+ significant_digits = 0;
+ } else {
+ last_digit = ndigits_stored - 1;
+ significant_digits = ndigits;
+ /* Mark trailing zeros as non-significant */
+ while (first[first_nonzero + significant_digits - 1 + includes_sign +
+ saw_radix] == '0') {
+ significant_digits--;
+ }
+ }
+
+
+ /* Normalization of exponent */
+ /* Correct exponent based on radix position, and shift significand as needed
+ */
+ /* to represent user input */
+
+ /* Overflow prevention */
+ if (exponent <= radix_position && radix_position - exponent > (1 << 14)) {
+ exponent = decimal128_limits::exponent_min;
+ } else {
+ exponent -= radix_position;
+ }
+
+ /* Attempt to normalize the exponent */
+ while (exponent > decimal128_limits::exponent_max) {
+ /* Shift exponent to significand and decrease */
+ last_digit++;
+
+ if (last_digit - first_digit > decimal128_limits::max_digits) {
+ /* The exponent is too great to shift into the significand. */
+ if (significant_digits == 0) {
+ /* Value is zero, we are allowed to clamp the exponent. */
+ exponent = decimal128_limits::exponent_max;
+ break;
+ }
+
+ /* Overflow is not permitted, error. */
+ dec = decimal128_limits::nan;
+ return false;
+ }
+
+ exponent--;
+ }
+
+ while (exponent < decimal128_limits::exponent_min || ndigits_stored < ndigits) {
+ /* Shift last digit */
+ if (last_digit == 0) {
+ /* underflow is not allowed, but zero clamping is */
+ if (significant_digits == 0) {
+ exponent = decimal128_limits::exponent_min;
+ break;
+ }
+
+ dec = decimal128_limits::nan;
+ return false;
+ }
+
+ if (ndigits_stored < ndigits) {
+ if (first[ndigits - 1 + includes_sign + saw_radix] - '0' != 0 &&
+ significant_digits != 0) {
+ dec = decimal128_limits::nan;
+ return false;
+ }
+
+ ndigits--; /* adjust to match digits not stored */
+ } else {
+ if (digits[last_digit] != 0) {
+ /* Inexact rounding is not allowed. */
+ dec = decimal128_limits::nan;
+ return false;
+ }
+
+
+ last_digit--; /* adjust to round */
+ }
+
+ if (exponent < decimal128_limits::exponent_max) {
+ exponent++;
+ } else {
+ dec = decimal128_limits::nan;
+ return false;
+ }
+ }
+
+ /* Round */
+ /* We've normalized the exponent, but might still need to round. */
+ if (last_digit - first_digit + 1 < significant_digits) {
+ uint8_t round_digit;
+
+ /* There are non-zero digits after last_digit that need rounding. */
+ /* We round to nearest, ties to even */
+ round_digit =
+ first[first_nonzero + last_digit + includes_sign + saw_radix + 1] -
+ '0';
+
+ if (round_digit != 0) {
+ /* Inexact (non-zero) rounding is not allowed */
+ dec = decimal128_limits::nan;
+ return false;
+ }
+ }
+
+ /* Encode significand */
+ significand_high = 0, /* The high 17 digits of the significand */
+ significand_low = 0; /* The low 17 digits of the significand */
+
+ if (significant_digits == 0) { /* read a zero */
+ significand_high = 0;
+ significand_low = 0;
+ } else if (last_digit - first_digit < 17) {
+ size_t d_idx = first_digit;
+ significand_low = digits[d_idx++];
+
+ for (; d_idx <= last_digit; d_idx++) {
+ significand_low *= 10;
+ significand_low += digits[d_idx];
+ significand_high = 0;
+ }
+ } else {
+ size_t d_idx = first_digit;
+ significand_high = digits[d_idx++];
+
+ for (; d_idx <= last_digit - 17; d_idx++) {
+ significand_high *= 10;
+ significand_high += digits[d_idx];
+ }
+
+ significand_low = digits[d_idx++];
+
+ for (; d_idx <= last_digit; d_idx++) {
+ significand_low *= 10;
+ significand_low += digits[d_idx];
+ }
+ }
+
+ detail::mul_64x64 (significand_high, 100000000000000000ull, &significand);
+ significand.low += significand_low;
+
+ if (significand.low < significand_low) {
+ significand.high += 1;
+ }
+
+
+ biased_exponent = (exponent + (int16_t) decimal128_limits::exponent_bias);
+
+ /* Encode combination, exponent, and significand. */
+ if ((significand.high >> 49) & 1) {
+ /* Encode '11' into bits 1 to 3 */
+ dec.high |= (0x3ull << 61);
+ dec.high |= (biased_exponent & 0x3fffull) << 47;
+ dec.high |= significand.high & 0x7fffffffffffull;
+ } else {
+ dec.high |= (biased_exponent & 0x3fffull) << 49;
+ dec.high |= significand.high & 0x1ffffffffffffull;
+ }
+
+ dec.low = significand.low;
+
+ /* Encode sign */
+ if (is_negative) {
+ dec.high |= 0x8000000000000000ull;
+ }
+
+ return true;
+ }
+
+} // namespace bson
+} // namespace jsoncons
+
+#endif
diff --git a/include/jsoncons_ext/bson/bson_encoder.hpp b/include/jsoncons_ext/bson/bson_encoder.hpp
new file mode 100644
index 0000000..4569f06
--- /dev/null
+++ b/include/jsoncons_ext/bson/bson_encoder.hpp
@@ -0,0 +1,585 @@
+// 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)
+
+// See https://github.com/danielaparker/jsoncons for latest version
+
+#ifndef JSONCONS_BSON_BSON_ENCODER_HPP
+#define JSONCONS_BSON_BSON_ENCODER_HPP
+
+#include <string>
+#include <vector>
+#include <limits> // std::numeric_limits
+#include <memory>
+#include <utility> // std::move
+#include <jsoncons/json_exception.hpp>
+#include <jsoncons/json_visitor.hpp>
+#include <jsoncons/config/jsoncons_config.hpp>
+#include <jsoncons/sink.hpp>
+#include <jsoncons/detail/parse_number.hpp>
+#include <jsoncons_ext/bson/bson_type.hpp>
+#include <jsoncons_ext/bson/bson_error.hpp>
+#include <jsoncons_ext/bson/bson_options.hpp>
+#include <jsoncons_ext/bson/bson_decimal128.hpp>
+#include <jsoncons_ext/bson/bson_oid.hpp>
+
+namespace jsoncons { namespace bson {
+
+template<class Sink=jsoncons::binary_stream_sink,class Allocator=std::allocator<char>>
+class basic_bson_encoder final : public basic_json_visitor<char>
+{
+ enum class decimal_parse_state { start, integer, exp1, exp2, fraction1 };
+ static constexpr int64_t nanos_in_milli = 1000000;
+ static constexpr int64_t nanos_in_second = 1000000000;
+ static constexpr int64_t millis_in_second = 1000;
+public:
+ using allocator_type = Allocator;
+ using char_type = char;
+ using typename basic_json_visitor<char>::string_view_type;
+ using sink_type = Sink;
+
+private:
+ struct stack_item
+ {
+ jsoncons::bson::bson_container_type type_;
+ std::size_t offset_;
+ std::size_t name_offset_;
+ std::size_t index_;
+
+ stack_item(jsoncons::bson::bson_container_type type, std::size_t offset) noexcept
+ : type_(type), offset_(offset), name_offset_(0), index_(0)
+ {
+ }
+
+ std::size_t offset() const
+ {
+ return offset_;
+ }
+
+ std::size_t member_offset() const
+ {
+ return name_offset_;
+ }
+
+ void member_offset(std::size_t offset)
+ {
+ name_offset_ = offset;
+ }
+
+ std::size_t next_index()
+ {
+ return index_++;
+ }
+
+ bool is_object() const
+ {
+ return type_ == jsoncons::bson::bson_container_type::document;
+ }
+
+
+ };
+
+ sink_type sink_;
+ const bson_encode_options options_;
+ allocator_type alloc_;
+
+ std::vector<stack_item> stack_;
+ std::vector<uint8_t> buffer_;
+ int nesting_depth_;
+
+ // Noncopyable and nonmoveable
+ basic_bson_encoder(const basic_bson_encoder&) = delete;
+ basic_bson_encoder& operator=(const basic_bson_encoder&) = delete;
+public:
+ explicit basic_bson_encoder(Sink&& sink,
+ const Allocator& alloc = Allocator())
+ : basic_bson_encoder(std::forward<Sink>(sink),
+ bson_encode_options(),
+ alloc)
+ {
+ }
+
+ explicit basic_bson_encoder(Sink&& sink,
+ const bson_encode_options& options,
+ const Allocator& alloc = Allocator())
+ : sink_(std::forward<Sink>(sink)),
+ options_(options),
+ alloc_(alloc),
+ nesting_depth_(0)
+ {
+ }
+
+ ~basic_bson_encoder() noexcept
+ {
+ sink_.flush();
+ }
+
+ void reset()
+ {
+ stack_.clear();
+ buffer_.clear();
+ nesting_depth_ = 0;
+ }
+
+ void reset(Sink&& sink)
+ {
+ sink_ = std::move(sink);
+ reset();
+ }
+
+private:
+ // Implementing methods
+
+ void visit_flush() override
+ {
+ sink_.flush();
+ }
+
+ bool visit_begin_object(semantic_tag, const ser_context&, std::error_code& ec) override
+ {
+ if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth()))
+ {
+ ec = bson_errc::max_nesting_depth_exceeded;
+ return false;
+ }
+ if (buffer_.size() > 0)
+ {
+ if (stack_.empty())
+ {
+ ec = bson_errc::expected_bson_document;
+ return false;
+ }
+ before_value(jsoncons::bson::bson_type::document_type);
+ }
+
+ stack_.emplace_back(jsoncons::bson::bson_container_type::document, buffer_.size());
+ buffer_.insert(buffer_.end(), sizeof(int32_t), 0);
+
+ return true;
+ }
+
+ bool visit_end_object(const ser_context&, std::error_code&) override
+ {
+ JSONCONS_ASSERT(!stack_.empty());
+ --nesting_depth_;
+
+ buffer_.push_back(0x00);
+
+ std::size_t length = buffer_.size() - stack_.back().offset();
+ binary::native_to_little(static_cast<uint32_t>(length), buffer_.begin()+stack_.back().offset());
+
+ stack_.pop_back();
+ if (stack_.empty())
+ {
+ for (auto c : buffer_)
+ {
+ sink_.push_back(c);
+ }
+ }
+ return true;
+ }
+
+ bool visit_begin_array(semantic_tag, const ser_context&, std::error_code& ec) override
+ {
+ if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth()))
+ {
+ ec = bson_errc::max_nesting_depth_exceeded;
+ return false;
+ }
+ if (buffer_.size() > 0)
+ {
+ if (stack_.empty())
+ {
+ ec = bson_errc::expected_bson_document;
+ return false;
+ }
+ before_value(jsoncons::bson::bson_type::array_type);
+ }
+ stack_.emplace_back(jsoncons::bson::bson_container_type::array, buffer_.size());
+ buffer_.insert(buffer_.end(), sizeof(int32_t), 0);
+ return true;
+ }
+
+ bool visit_end_array(const ser_context&, std::error_code&) override
+ {
+ JSONCONS_ASSERT(!stack_.empty());
+ --nesting_depth_;
+
+ buffer_.push_back(0x00);
+
+ std::size_t length = buffer_.size() - stack_.back().offset();
+ binary::native_to_little(static_cast<uint32_t>(length), buffer_.begin()+stack_.back().offset());
+
+ stack_.pop_back();
+ if (stack_.empty())
+ {
+ for (auto c : buffer_)
+ {
+ sink_.push_back(c);
+ }
+ }
+ return true;
+ }
+
+ bool visit_key(const string_view_type& name, const ser_context&, std::error_code&) override
+ {
+ stack_.back().member_offset(buffer_.size());
+ buffer_.push_back(0x00); // reserve space for code
+ for (auto c : name)
+ {
+ buffer_.push_back(c);
+ }
+ buffer_.push_back(0x00);
+ return true;
+ }
+
+ bool visit_null(semantic_tag tag, const ser_context&, std::error_code& ec) override
+ {
+ if (stack_.empty())
+ {
+ ec = bson_errc::expected_bson_document;
+ return false;
+ }
+ switch (tag)
+ {
+ case semantic_tag::undefined:
+ before_value(jsoncons::bson::bson_type::undefined_type);
+ break;
+ default:
+ before_value(jsoncons::bson::bson_type::null_type);
+ break;
+ }
+ return true;
+ }
+
+ bool visit_bool(bool val, semantic_tag, const ser_context&, std::error_code& ec) override
+ {
+ if (stack_.empty())
+ {
+ ec = bson_errc::expected_bson_document;
+ return false;
+ }
+ before_value(jsoncons::bson::bson_type::bool_type);
+ if (val)
+ {
+ buffer_.push_back(0x01);
+ }
+ else
+ {
+ buffer_.push_back(0x00);
+ }
+
+ return true;
+ }
+
+ bool visit_string(const string_view_type& sv, semantic_tag tag, const ser_context&, std::error_code& ec) override
+ {
+ if (stack_.empty())
+ {
+ ec = bson_errc::expected_bson_document;
+ return false;
+ }
+
+ switch (tag)
+ {
+ case semantic_tag::float128:
+ {
+ 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())
+ {
+ ec = bson_errc::invalid_decimal128_string;
+ return false;
+ }
+ binary::native_to_little(dec.low,std::back_inserter(buffer_));
+ binary::native_to_little(dec.high,std::back_inserter(buffer_));
+ break;
+ }
+ case semantic_tag::id:
+ {
+ before_value(jsoncons::bson::bson_type::object_id_type);
+ oid_t oid(sv);
+ for (auto b : oid)
+ {
+ buffer_.push_back(b);
+ }
+ break;
+ }
+ case semantic_tag::regex:
+ {
+ before_value(jsoncons::bson::bson_type::regex_type);
+ std::size_t first = sv.find_first_of('/');
+ std::size_t last = sv.find_last_of('/');
+ if (first == string_view::npos || last == string_view::npos || first == last)
+ {
+ ec = bson_errc::invalid_regex_string;
+ return false;
+ }
+ string_view regex = sv.substr(first+1,last-1);
+ for (auto c : regex)
+ {
+ buffer_.push_back(c);
+ }
+ buffer_.push_back(0x00);
+ string_view options = sv.substr(last+1);
+ for (auto c : options)
+ {
+ buffer_.push_back(c);
+ }
+ buffer_.push_back(0x00);
+ break;
+ }
+ default:
+ switch (tag)
+ {
+ case semantic_tag::code:
+ before_value(jsoncons::bson::bson_type::javascript_type);
+ break;
+ default:
+ before_value(jsoncons::bson::bson_type::string_type);
+ break;
+ }
+ std::size_t offset = buffer_.size();
+ buffer_.insert(buffer_.end(), sizeof(int32_t), 0);
+ std::size_t string_offset = buffer_.size();
+ auto sink = unicode_traits::validate(sv.data(), sv.size());
+ if (sink.ec != unicode_traits::conv_errc())
+ {
+ ec = bson_errc::invalid_utf8_text_string;
+ return false;
+ }
+ for (auto c : sv)
+ {
+ buffer_.push_back(c);
+ }
+ buffer_.push_back(0x00);
+ std::size_t length = buffer_.size() - string_offset;
+ binary::native_to_little(static_cast<uint32_t>(length), buffer_.begin()+offset);
+ break;
+ }
+
+ return true;
+ }
+
+ bool visit_byte_string(const byte_string_view& b,
+ semantic_tag,
+ const ser_context&,
+ std::error_code& ec) override
+ {
+ if (stack_.empty())
+ {
+ ec = bson_errc::expected_bson_document;
+ return false;
+ }
+ before_value(jsoncons::bson::bson_type::binary_type);
+
+ std::size_t offset = buffer_.size();
+ buffer_.insert(buffer_.end(), sizeof(int32_t), 0);
+ std::size_t string_offset = buffer_.size();
+
+ buffer_.push_back(0x80); // default subtype
+
+ for (auto c : b)
+ {
+ buffer_.push_back(c);
+ }
+ std::size_t length = buffer_.size() - string_offset - 1;
+ binary::native_to_little(static_cast<uint32_t>(length), buffer_.begin()+offset);
+
+ return true;
+ }
+
+ bool visit_byte_string(const byte_string_view& b,
+ uint64_t ext_tag,
+ const ser_context&,
+ std::error_code& ec) override
+ {
+ if (stack_.empty())
+ {
+ ec = bson_errc::expected_bson_document;
+ return false;
+ }
+ before_value(jsoncons::bson::bson_type::binary_type);
+
+ std::size_t offset = buffer_.size();
+ buffer_.insert(buffer_.end(), sizeof(int32_t), 0);
+ std::size_t string_offset = buffer_.size();
+
+ buffer_.push_back(static_cast<uint8_t>(ext_tag)); // default subtype
+
+ for (auto c : b)
+ {
+ buffer_.push_back(c);
+ }
+ std::size_t length = buffer_.size() - string_offset - 1;
+ binary::native_to_little(static_cast<uint32_t>(length), buffer_.begin()+offset);
+
+ return true;
+ }
+
+ bool visit_int64(int64_t val,
+ semantic_tag tag,
+ const ser_context&,
+ std::error_code& ec) override
+ {
+ static constexpr int64_t min_value_div_1000 = (std::numeric_limits<int64_t>::min)() / 1000;
+ static constexpr int64_t max_value_div_1000 = (std::numeric_limits<int64_t>::max)() / 1000;
+ if (stack_.empty())
+ {
+ ec = bson_errc::expected_bson_document;
+ return false;
+ }
+
+ switch (tag)
+ {
+ case semantic_tag::epoch_second:
+ if (val < min_value_div_1000)
+ {
+ ec = bson_errc::datetime_too_small;
+ return false;
+ }
+ if (val > max_value_div_1000)
+ {
+ ec = bson_errc::datetime_too_large;
+ return false;
+ }
+ before_value(jsoncons::bson::bson_type::datetime_type);
+ binary::native_to_little(val*millis_in_second,std::back_inserter(buffer_));
+ return true;
+ case semantic_tag::epoch_milli:
+ before_value(jsoncons::bson::bson_type::datetime_type);
+ binary::native_to_little(val,std::back_inserter(buffer_));
+ return true;
+ case semantic_tag::epoch_nano:
+ before_value(jsoncons::bson::bson_type::datetime_type);
+ if (val != 0)
+ {
+ val /= nanos_in_milli;
+ }
+ binary::native_to_little(static_cast<int64_t>(val),std::back_inserter(buffer_));
+ return true;
+ default:
+ {
+ if (val >= (std::numeric_limits<int32_t>::lowest)() && val <= (std::numeric_limits<int32_t>::max)())
+ {
+ before_value(jsoncons::bson::bson_type::int32_type);
+ binary::native_to_little(static_cast<uint32_t>(val),std::back_inserter(buffer_));
+ }
+ else
+ {
+ before_value(jsoncons::bson::bson_type::int64_type);
+ binary::native_to_little(static_cast<int64_t>(val),std::back_inserter(buffer_));
+ }
+ return true;
+ }
+ }
+ }
+
+ bool visit_uint64(uint64_t val,
+ semantic_tag tag,
+ const ser_context&,
+ std::error_code& ec) override
+ {
+ static constexpr uint64_t max_value_div_1000 = (std::numeric_limits<uint64_t>::max)() / 1000;
+ if (stack_.empty())
+ {
+ ec = bson_errc::expected_bson_document;
+ return false;
+ }
+
+ switch (tag)
+ {
+ case semantic_tag::epoch_second:
+ if (val > max_value_div_1000)
+ {
+ ec = bson_errc::datetime_too_large;
+ return false;
+ }
+ before_value(jsoncons::bson::bson_type::datetime_type);
+ binary::native_to_little(static_cast<int64_t>(val*millis_in_second),std::back_inserter(buffer_));
+ return true;
+ case semantic_tag::epoch_milli:
+ before_value(jsoncons::bson::bson_type::datetime_type);
+ binary::native_to_little(static_cast<int64_t>(val),std::back_inserter(buffer_));
+ return true;
+ case semantic_tag::epoch_nano:
+ before_value(jsoncons::bson::bson_type::datetime_type);
+ if (val != 0)
+ {
+ val /= nanos_in_second;
+ }
+ binary::native_to_little(static_cast<int64_t>(val),std::back_inserter(buffer_));
+ return true;
+ default:
+ {
+ bool more;
+ if (val <= static_cast<uint64_t>((std::numeric_limits<int32_t>::max)()))
+ {
+ before_value(jsoncons::bson::bson_type::int32_type);
+ binary::native_to_little(static_cast<uint32_t>(val),std::back_inserter(buffer_));
+ more = true;
+ }
+ else if (val <= static_cast<uint64_t>((std::numeric_limits<int64_t>::max)()))
+ {
+ before_value(jsoncons::bson::bson_type::int64_type);
+ binary::native_to_little(static_cast<uint64_t>(val),std::back_inserter(buffer_));
+ more = true;
+ }
+ else
+ {
+ ec = bson_errc::number_too_large;
+ more = false;
+ }
+ return more;
+ }
+ }
+ }
+
+ bool visit_double(double val,
+ semantic_tag,
+ const ser_context&,
+ std::error_code& ec) override
+ {
+ if (stack_.empty())
+ {
+ ec = bson_errc::expected_bson_document;
+ return false;
+ }
+ before_value(jsoncons::bson::bson_type::double_type);
+ binary::native_to_little(val,std::back_inserter(buffer_));
+ return true;
+ }
+
+ void before_value(uint8_t code)
+ {
+ JSONCONS_ASSERT(!stack_.empty());
+ if (stack_.back().is_object())
+ {
+ buffer_[stack_.back().member_offset()] = code;
+ }
+ else
+ {
+ buffer_.push_back(code);
+ std::string name = std::to_string(stack_.back().next_index());
+ buffer_.insert(buffer_.end(), name.begin(), name.end());
+ buffer_.push_back(0x00);
+ }
+ }
+};
+
+using bson_stream_encoder = basic_bson_encoder<jsoncons::binary_stream_sink>;
+using bson_bytes_encoder = basic_bson_encoder<jsoncons::bytes_sink<std::vector<uint8_t>>>;
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+template<class Sink=jsoncons::binary_stream_sink>
+using basic_bson_serializer = basic_bson_encoder<Sink>;
+
+JSONCONS_DEPRECATED_MSG("Instead, use bson_stream_encoder") typedef bson_stream_encoder bson_encoder;
+JSONCONS_DEPRECATED_MSG("Instead, use bson_stream_encoder") typedef bson_stream_encoder bson_serializer;
+JSONCONS_DEPRECATED_MSG("Instead, use bson_bytes_encoder") typedef bson_bytes_encoder bson_buffer_serializer;
+
+#endif
+
+}}
+#endif
diff --git a/include/jsoncons_ext/bson/bson_error.hpp b/include/jsoncons_ext/bson/bson_error.hpp
new file mode 100644
index 0000000..85cf24d
--- /dev/null
+++ b/include/jsoncons_ext/bson/bson_error.hpp
@@ -0,0 +1,103 @@
+/// 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)
+
+// See https://github.com/danielaparker/jsoncons for latest version
+
+#ifndef JSONCONS_BSON_BSON_ERROR_HPP
+#define JSONCONS_BSON_BSON_ERROR_HPP
+
+#include <system_error>
+#include <jsoncons/config/jsoncons_config.hpp>
+
+namespace jsoncons { namespace bson {
+
+enum class bson_errc
+{
+ success = 0,
+ unexpected_eof = 1,
+ source_error,
+ invalid_utf8_text_string,
+ max_nesting_depth_exceeded,
+ string_length_is_non_positive,
+ length_is_negative,
+ number_too_large,
+ invalid_decimal128_string,
+ datetime_too_small,
+ datetime_too_large,
+ expected_bson_document,
+ invalid_regex_string,
+ size_mismatch,
+ unknown_type
+};
+
+class bson_error_category_impl
+ : public std::error_category
+{
+public:
+ const char* name() const noexcept override
+ {
+ return "jsoncons/bson";
+ }
+ std::string message(int ev) const override
+ {
+ switch (static_cast<bson_errc>(ev))
+ {
+ case bson_errc::unexpected_eof:
+ return "Unexpected end of file";
+ case bson_errc::source_error:
+ return "Source error";
+ case bson_errc::invalid_utf8_text_string:
+ return "Illegal UTF-8 encoding in text string";
+ case bson_errc::max_nesting_depth_exceeded:
+ return "Data item nesting exceeds limit in options";
+ case bson_errc::string_length_is_non_positive:
+ return "Request for the length of a string returned a non-positive result";
+ case bson_errc::length_is_negative:
+ return "Request for the length of a binary returned a negative result";
+ case bson_errc::unknown_type:
+ return "An unknown type was found in the stream";
+ case bson_errc::number_too_large:
+ return "Number too large";
+ case bson_errc::invalid_decimal128_string:
+ return "Invalid decimal128 string";
+ case bson_errc::datetime_too_large:
+ return "datetime too large";
+ case bson_errc::datetime_too_small:
+ return "datetime too small";
+ case bson_errc::expected_bson_document:
+ return "Expected BSON document";
+ case bson_errc::invalid_regex_string:
+ return "Invalid regex string";
+ case bson_errc::size_mismatch:
+ return "Document or array size doesn't match bytes read";
+ default:
+ return "Unknown BSON parser error";
+ }
+ }
+};
+
+inline
+const std::error_category& bson_error_category()
+{
+ static bson_error_category_impl instance;
+ return instance;
+}
+
+inline
+std::error_code make_error_code(bson_errc result)
+{
+ return std::error_code(static_cast<int>(result),bson_error_category());
+}
+
+
+}}
+
+namespace std {
+ template<>
+ struct is_error_code_enum<jsoncons::bson::bson_errc> : public true_type
+ {
+ };
+}
+
+#endif
diff --git a/include/jsoncons_ext/bson/bson_oid.hpp b/include/jsoncons_ext/bson/bson_oid.hpp
new file mode 100644
index 0000000..065d8da
--- /dev/null
+++ b/include/jsoncons_ext/bson/bson_oid.hpp
@@ -0,0 +1,245 @@
+#ifndef JSONCONS_BSON_BSON_OID_HPP
+#define JSONCONS_BSON_BSON_OID_HPP
+
+/*
+ * Implements class oid_t and non member function bson_oid_to_string
+ *
+ * Based on the libjson functions bson_oid_to_string
+ * and bson_oid_init_from_string_unsafe , available at
+ * https://github.com/mongodb/mongo-c-driver/blob/master/src/libbson/src/bson/bson-oid.h
+ * and https://github.com/mongodb/mongo-c-driver/blob/master/src/libbson/src/bson/bson-oid.c
+ *
+*/
+
+/*
+ * Copyright 2015 MongoDB, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cstdlib>
+#include <cstring>
+#include <ctype.h>
+#include <system_error>
+#include <algorithm>
+#include <string>
+#include <type_traits>
+#include <array>
+#include <jsoncons/config/jsoncons_config.hpp>
+
+namespace jsoncons { namespace bson {
+
+ class oid_t
+ {
+ std::array<uint8_t,12> bytes_;
+ public:
+ using iterator = std::array<uint8_t,12>::iterator;
+ using const_iterator = std::array<uint8_t,12>::const_iterator;
+
+ oid_t(const std::array<uint8_t,12>& bytes)
+ : bytes_(bytes)
+ {
+ }
+ oid_t(uint8_t data[12])
+ {
+ std::memcpy(bytes_.data(),data,12);
+ }
+
+ oid_t(const string_view& str)
+ {
+ for (std::size_t i = 0; i < bytes_.size(); i++)
+ {
+ bytes_[i] = ((parse_hex_char (str[2 * i]) << 4) |
+ (parse_hex_char (str[2 * i + 1])));
+ }
+ }
+
+ const uint8_t* data() const
+ {
+ return bytes_.data();
+ }
+
+ std::size_t size() const
+ {
+ return bytes_.size();
+ }
+
+ iterator begin()
+ {
+ return bytes_.begin();
+ }
+
+ iterator end()
+ {
+ return bytes_.end();
+ }
+
+ const_iterator begin() const
+ {
+ return bytes_.begin();
+ }
+
+ const_iterator end() const
+ {
+ return bytes_.end();
+ }
+
+ private:
+
+ static uint8_t parse_hex_char (char hex)
+ {
+ switch (hex) {
+ case '0':
+ return 0;
+ case '1':
+ return 1;
+ case '2':
+ return 2;
+ case '3':
+ return 3;
+ case '4':
+ return 4;
+ case '5':
+ return 5;
+ case '6':
+ return 6;
+ case '7':
+ return 7;
+ case '8':
+ return 8;
+ case '9':
+ return 9;
+ case 'a':
+ case 'A':
+ return 0xa;
+ case 'b':
+ case 'B':
+ return 0xb;
+ case 'c':
+ case 'C':
+ return 0xc;
+ case 'd':
+ case 'D':
+ return 0xd;
+ case 'e':
+ case 'E':
+ return 0xe;
+ case 'f':
+ case 'F':
+ return 0xf;
+ default:
+ return 0;
+ }
+ }
+ };
+
+ namespace detail {
+
+ inline
+ const uint16_t* get_hex_char_pairs(std::true_type) // big endian
+ {
+ static const uint16_t hex_char_pairs[] = {
+ 12336, 12337, 12338, 12339, 12340, 12341, 12342, 12343, 12344, 12345, 12385,
+ 12386, 12387, 12388, 12389, 12390, 12592, 12593, 12594, 12595, 12596, 12597,
+ 12598, 12599, 12600, 12601, 12641, 12642, 12643, 12644, 12645, 12646, 12848,
+ 12849, 12850, 12851, 12852, 12853, 12854, 12855, 12856, 12857, 12897, 12898,
+ 12899, 12900, 12901, 12902, 13104, 13105, 13106, 13107, 13108, 13109, 13110,
+ 13111, 13112, 13113, 13153, 13154, 13155, 13156, 13157, 13158, 13360, 13361,
+ 13362, 13363, 13364, 13365, 13366, 13367, 13368, 13369, 13409, 13410, 13411,
+ 13412, 13413, 13414, 13616, 13617, 13618, 13619, 13620, 13621, 13622, 13623,
+ 13624, 13625, 13665, 13666, 13667, 13668, 13669, 13670, 13872, 13873, 13874,
+ 13875, 13876, 13877, 13878, 13879, 13880, 13881, 13921, 13922, 13923, 13924,
+ 13925, 13926, 14128, 14129, 14130, 14131, 14132, 14133, 14134, 14135, 14136,
+ 14137, 14177, 14178, 14179, 14180, 14181, 14182, 14384, 14385, 14386, 14387,
+ 14388, 14389, 14390, 14391, 14392, 14393, 14433, 14434, 14435, 14436, 14437,
+ 14438, 14640, 14641, 14642, 14643, 14644, 14645, 14646, 14647, 14648, 14649,
+ 14689, 14690, 14691, 14692, 14693, 14694, 24880, 24881, 24882, 24883, 24884,
+ 24885, 24886, 24887, 24888, 24889, 24929, 24930, 24931, 24932, 24933, 24934,
+ 25136, 25137, 25138, 25139, 25140, 25141, 25142, 25143, 25144, 25145, 25185,
+ 25186, 25187, 25188, 25189, 25190, 25392, 25393, 25394, 25395, 25396, 25397,
+ 25398, 25399, 25400, 25401, 25441, 25442, 25443, 25444, 25445, 25446, 25648,
+ 25649, 25650, 25651, 25652, 25653, 25654, 25655, 25656, 25657, 25697, 25698,
+ 25699, 25700, 25701, 25702, 25904, 25905, 25906, 25907, 25908, 25909, 25910,
+ 25911, 25912, 25913, 25953, 25954, 25955, 25956, 25957, 25958, 26160, 26161,
+ 26162, 26163, 26164, 26165, 26166, 26167, 26168, 26169, 26209, 26210, 26211,
+ 26212, 26213, 26214};
+
+ return hex_char_pairs;
+ }
+
+ inline
+ const uint16_t* get_hex_char_pairs(std::false_type) // little endian
+ {
+ static const uint16_t hex_char_pairs[] = {
+ 12336, 12592, 12848, 13104, 13360, 13616, 13872, 14128, 14384, 14640, 24880,
+ 25136, 25392, 25648, 25904, 26160, 12337, 12593, 12849, 13105, 13361, 13617,
+ 13873, 14129, 14385, 14641, 24881, 25137, 25393, 25649, 25905, 26161, 12338,
+ 12594, 12850, 13106, 13362, 13618, 13874, 14130, 14386, 14642, 24882, 25138,
+ 25394, 25650, 25906, 26162, 12339, 12595, 12851, 13107, 13363, 13619, 13875,
+ 14131, 14387, 14643, 24883, 25139, 25395, 25651, 25907, 26163, 12340, 12596,
+ 12852, 13108, 13364, 13620, 13876, 14132, 14388, 14644, 24884, 25140, 25396,
+ 25652, 25908, 26164, 12341, 12597, 12853, 13109, 13365, 13621, 13877, 14133,
+ 14389, 14645, 24885, 25141, 25397, 25653, 25909, 26165, 12342, 12598, 12854,
+ 13110, 13366, 13622, 13878, 14134, 14390, 14646, 24886, 25142, 25398, 25654,
+ 25910, 26166, 12343, 12599, 12855, 13111, 13367, 13623, 13879, 14135, 14391,
+ 14647, 24887, 25143, 25399, 25655, 25911, 26167, 12344, 12600, 12856, 13112,
+ 13368, 13624, 13880, 14136, 14392, 14648, 24888, 25144, 25400, 25656, 25912,
+ 26168, 12345, 12601, 12857, 13113, 13369, 13625, 13881, 14137, 14393, 14649,
+ 24889, 25145, 25401, 25657, 25913, 26169, 12385, 12641, 12897, 13153, 13409,
+ 13665, 13921, 14177, 14433, 14689, 24929, 25185, 25441, 25697, 25953, 26209,
+ 12386, 12642, 12898, 13154, 13410, 13666, 13922, 14178, 14434, 14690, 24930,
+ 25186, 25442, 25698, 25954, 26210, 12387, 12643, 12899, 13155, 13411, 13667,
+ 13923, 14179, 14435, 14691, 24931, 25187, 25443, 25699, 25955, 26211, 12388,
+ 12644, 12900, 13156, 13412, 13668, 13924, 14180, 14436, 14692, 24932, 25188,
+ 25444, 25700, 25956, 26212, 12389, 12645, 12901, 13157, 13413, 13669, 13925,
+ 14181, 14437, 14693, 24933, 25189, 25445, 25701, 25957, 26213, 12390, 12646,
+ 12902, 13158, 13414, 13670, 13926, 14182, 14438, 14694, 24934, 25190, 25446,
+ 25702, 25958, 26214};
+
+ return hex_char_pairs;
+ }
+
+ inline
+ void init_hex_char_pairs(const oid_t& oid, uint16_t* data)
+ {
+ const uint8_t* bytes = oid.data();
+ const uint16_t* gHexCharPairs = get_hex_char_pairs(std::integral_constant<bool, jsoncons::endian::native == jsoncons::endian::big>());
+
+ data[0] = gHexCharPairs[bytes[0]];
+ data[1] = gHexCharPairs[bytes[1]];
+ data[2] = gHexCharPairs[bytes[2]];
+ data[3] = gHexCharPairs[bytes[3]];
+ data[4] = gHexCharPairs[bytes[4]];
+ data[5] = gHexCharPairs[bytes[5]];
+ data[6] = gHexCharPairs[bytes[6]];
+ data[7] = gHexCharPairs[bytes[7]];
+ data[8] = gHexCharPairs[bytes[8]];
+ data[9] = gHexCharPairs[bytes[9]];
+ data[10] = gHexCharPairs[bytes[10]];
+ data[11] = gHexCharPairs[bytes[11]];
+ }
+
+ } // namsepace detail
+
+ template <typename StringT>
+ inline
+ void to_string(const oid_t& oid, StringT& s)
+ {
+ s.resize(24);
+ detail::init_hex_char_pairs(oid, reinterpret_cast<uint16_t*>(&s[0]));
+ }
+
+} // namespace bson
+} // namespace jsoncons
+
+#endif
diff --git a/include/jsoncons_ext/bson/bson_options.hpp b/include/jsoncons_ext/bson/bson_options.hpp
new file mode 100644
index 0000000..0e4620e
--- /dev/null
+++ b/include/jsoncons_ext/bson/bson_options.hpp
@@ -0,0 +1,75 @@
+// Copyright 2019 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_BSON_BSON_OPTIONS_HPP
+#define JSONCONS_BSON_BSON_OPTIONS_HPP
+
+#include <string>
+#include <limits> // std::numeric_limits
+#include <cwchar>
+#include <jsoncons/json_exception.hpp>
+#include <jsoncons_ext/bson/bson_type.hpp>
+
+namespace jsoncons { namespace bson {
+
+class bson_options;
+
+class bson_options_common
+{
+ friend class bson_options;
+
+ int max_nesting_depth_;
+protected:
+ 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
+ {
+ return max_nesting_depth_;
+ }
+};
+
+class bson_decode_options : public virtual bson_options_common
+{
+ friend class bson_options;
+public:
+ bson_decode_options()
+ {
+ }
+};
+
+class bson_encode_options : public virtual bson_options_common
+{
+ friend class bson_options;
+public:
+ bson_encode_options()
+ {
+ }
+};
+
+class bson_options final : public bson_decode_options, public bson_encode_options
+{
+public:
+ using bson_options_common::max_nesting_depth;
+
+ bson_options& max_nesting_depth(int value)
+ {
+ this->max_nesting_depth_ = value;
+ return *this;
+ }
+};
+
+}}
+#endif
diff --git a/include/jsoncons_ext/bson/bson_parser.hpp b/include/jsoncons_ext/bson/bson_parser.hpp
new file mode 100644
index 0000000..2dc6e75
--- /dev/null
+++ b/include/jsoncons_ext/bson/bson_parser.hpp
@@ -0,0 +1,645 @@
+// Copyright 2017 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_BSON_BSON_PARSER_HPP
+#define JSONCONS_BSON_BSON_PARSER_HPP
+
+#include <string>
+#include <vector>
+#include <memory>
+#include <utility> // std::move
+#include <jsoncons/json.hpp>
+#include <jsoncons/source.hpp>
+#include <jsoncons/json_visitor.hpp>
+#include <jsoncons/config/jsoncons_config.hpp>
+#include <jsoncons_ext/bson/bson_type.hpp>
+#include <jsoncons_ext/bson/bson_decimal128.hpp>
+#include <jsoncons_ext/bson/bson_error.hpp>
+#include <jsoncons_ext/bson/bson_options.hpp>
+#include <jsoncons_ext/bson/bson_oid.hpp>
+
+namespace jsoncons { namespace bson {
+
+enum class parse_mode {root,accept,document,array,value};
+
+struct parse_state
+{
+ parse_mode mode;
+ std::size_t length;
+ std::size_t pos;
+ uint8_t type;
+ std::size_t index;
+
+ parse_state(parse_mode mode_, std::size_t length_, std::size_t pos_, uint8_t type_ = 0) noexcept
+ : mode(mode_), length(length_), pos(pos_), type(type_), index(0)
+ {
+ }
+
+ parse_state(const parse_state&) = default;
+ parse_state(parse_state&&) = default;
+ parse_state& operator=(const parse_state&) = default;
+ parse_state& operator=(parse_state&&) = default;
+};
+
+template <class Source,class Allocator=std::allocator<char>>
+class basic_bson_parser : public ser_context
+{
+ using char_type = char;
+ using char_traits_type = std::char_traits<char>;
+ using temp_allocator_type = Allocator;
+ using char_allocator_type = typename std::allocator_traits<temp_allocator_type>:: template rebind_alloc<char_type>;
+ using byte_allocator_type = typename std::allocator_traits<temp_allocator_type>:: template rebind_alloc<uint8_t>;
+ using parse_state_allocator_type = typename std::allocator_traits<temp_allocator_type>:: template rebind_alloc<parse_state>;
+
+ Source source_;
+ bson_decode_options options_;
+ bool more_;
+ bool done_;
+ std::vector<uint8_t,byte_allocator_type> bytes_buffer_;
+ std::basic_string<char,std::char_traits<char>,char_allocator_type> text_buffer_;
+ std::vector<parse_state,parse_state_allocator_type> state_stack_;
+public:
+ template <class Sourceable>
+ basic_bson_parser(Sourceable&& source,
+ const bson_decode_options& options = bson_decode_options(),
+ const Allocator alloc = Allocator())
+ : source_(std::forward<Sourceable>(source)),
+ options_(options),
+ more_(true),
+ done_(false),
+ text_buffer_(alloc),
+ state_stack_(alloc)
+ {
+ state_stack_.emplace_back(parse_mode::root,0,0);
+ }
+
+ void restart()
+ {
+ more_ = true;
+ }
+
+ void reset()
+ {
+ more_ = true;
+ done_ = false;
+ bytes_buffer_.clear();
+ text_buffer_.clear();
+ state_stack_.clear();
+ state_stack_.emplace_back(parse_mode::root,0,0);
+ }
+
+ template <class Sourceable>
+ void reset(Sourceable&& source)
+ {
+ source_ = std::forward<Sourceable>(source);
+ reset();
+ }
+
+ bool done() const
+ {
+ return done_;
+ }
+
+ bool stopped() const
+ {
+ return !more_;
+ }
+
+ std::size_t line() const override
+ {
+ return 0;
+ }
+
+ std::size_t column() const override
+ {
+ return source_.position();
+ }
+
+ void array_expected(json_visitor& visitor, std::error_code& ec)
+ {
+ if (state_stack_.size() == 2 && state_stack_.back().mode == parse_mode::document)
+ {
+ state_stack_.back().mode = parse_mode::array;
+ more_ = visitor.begin_array(semantic_tag::none, *this, ec);
+ }
+ }
+
+ void parse(json_visitor& visitor, std::error_code& ec)
+ {
+ if (JSONCONS_UNLIKELY(source_.is_error()))
+ {
+ ec = bson_errc::source_error;
+ more_ = false;
+ return;
+ }
+
+ while (!done_ && more_)
+ {
+ switch (state_stack_.back().mode)
+ {
+ case parse_mode::root:
+ state_stack_.back().mode = parse_mode::accept;
+ begin_document(visitor, ec);
+ break;
+ case parse_mode::document:
+ {
+ uint8_t type;
+ std::size_t n = source_.read(&type, 1);
+ state_stack_.back().pos += n;
+ if (JSONCONS_UNLIKELY(n != 1))
+ {
+ ec = bson_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ if (type != 0x00)
+ {
+ read_e_name(visitor,jsoncons::bson::bson_container_type::document,ec);
+ state_stack_.back().mode = parse_mode::value;
+ state_stack_.back().type = type;
+ }
+ else
+ {
+ end_document(visitor,ec);
+ }
+ break;
+ }
+ case parse_mode::array:
+ {
+ uint8_t type;
+ std::size_t n = source_.read(&type, 1);
+ state_stack_.back().pos += n;
+ if (JSONCONS_UNLIKELY(n != 1))
+ {
+ ec = bson_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ if (type != 0x00)
+ {
+ read_e_name(visitor,jsoncons::bson::bson_container_type::array,ec);
+ read_value(visitor, type, ec);
+ }
+ else
+ {
+ end_array(visitor,ec);
+ }
+ break;
+ }
+ case parse_mode::value:
+ state_stack_.back().mode = parse_mode::document;
+ read_value(visitor,state_stack_.back().type,ec);
+ break;
+ case parse_mode::accept:
+ {
+ JSONCONS_ASSERT(state_stack_.size() == 1);
+ state_stack_.clear();
+ more_ = false;
+ done_ = true;
+ visitor.flush();
+ break;
+ }
+ }
+ }
+ }
+
+private:
+
+ void begin_document(json_visitor& visitor, std::error_code& ec)
+ {
+ if (JSONCONS_UNLIKELY(static_cast<int>(state_stack_.size()) > options_.max_nesting_depth()))
+ {
+ ec = bson_errc::max_nesting_depth_exceeded;
+ more_ = false;
+ return;
+ }
+
+ uint8_t buf[sizeof(int32_t)];
+ size_t n = source_.read(buf, sizeof(int32_t));
+ if (JSONCONS_UNLIKELY(n != sizeof(int32_t)))
+ {
+ ec = bson_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+
+ auto length = binary::little_to_native<int32_t>(buf, sizeof(buf));
+
+ more_ = visitor.begin_object(semantic_tag::none, *this, ec);
+ state_stack_.emplace_back(parse_mode::document,length,n);
+ }
+
+ void end_document(json_visitor& visitor, std::error_code& ec)
+ {
+ JSONCONS_ASSERT(state_stack_.size() >= 2);
+
+ more_ = visitor.end_object(*this,ec);
+ if (JSONCONS_UNLIKELY(state_stack_.back().pos != state_stack_.back().length))
+ {
+ ec = bson_errc::size_mismatch;
+ more_ = false;
+ return;
+ }
+ std::size_t pos = state_stack_.back().pos;
+ state_stack_.pop_back();
+ state_stack_.back().pos += pos;
+ }
+
+ void begin_array(json_visitor& visitor, std::error_code& ec)
+ {
+ if (JSONCONS_UNLIKELY(static_cast<int>(state_stack_.size()) > options_.max_nesting_depth()))
+ {
+ ec = bson_errc::max_nesting_depth_exceeded;
+ more_ = false;
+ return;
+ }
+ uint8_t buf[sizeof(int32_t)];
+ std::size_t n = source_.read(buf, sizeof(int32_t));
+ if (JSONCONS_UNLIKELY(n != sizeof(int32_t)))
+ {
+ ec = bson_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ auto length = binary::little_to_native<int32_t>(buf, sizeof(buf));
+
+ more_ = visitor.begin_array(semantic_tag::none, *this, ec);
+ if (ec)
+ {
+ return;
+ }
+ state_stack_.emplace_back(parse_mode::array, length, n);
+ }
+
+ void end_array(json_visitor& visitor, std::error_code& ec)
+ {
+ JSONCONS_ASSERT(state_stack_.size() >= 2);
+
+ more_ = visitor.end_array(*this, ec);
+ if (JSONCONS_UNLIKELY(state_stack_.back().pos != state_stack_.back().length))
+ {
+ ec = bson_errc::size_mismatch;
+ more_ = false;
+ return;
+ }
+ std::size_t pos = state_stack_.back().pos;
+ state_stack_.pop_back();
+ state_stack_.back().pos += pos;
+ }
+
+ void read_e_name(json_visitor& visitor, jsoncons::bson::bson_container_type type, std::error_code& ec)
+ {
+ text_buffer_.clear();
+ read_cstring(ec);
+ if (ec)
+ {
+ return;
+ }
+ if (type == jsoncons::bson::bson_container_type::document)
+ {
+ auto result = unicode_traits::validate(text_buffer_.data(),text_buffer_.size());
+ if (JSONCONS_UNLIKELY(result.ec != unicode_traits::conv_errc()))
+ {
+ ec = bson_errc::invalid_utf8_text_string;
+ more_ = false;
+ return;
+ }
+ more_ = visitor.key(jsoncons::basic_string_view<char>(text_buffer_.data(),text_buffer_.length()), *this, ec);
+ }
+ }
+
+ void read_value(json_visitor& visitor, uint8_t type, std::error_code& ec)
+ {
+ switch (type)
+ {
+ case jsoncons::bson::bson_type::double_type:
+ {
+ uint8_t buf[sizeof(double)];
+ std::size_t n = source_.read(buf, sizeof(double));
+ state_stack_.back().pos += n;
+ if (JSONCONS_UNLIKELY(n != sizeof(double)))
+ {
+ ec = bson_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ double res = binary::little_to_native<double>(buf, sizeof(buf));
+ more_ = visitor.double_value(res, semantic_tag::none, *this, ec);
+ break;
+ }
+ case jsoncons::bson::bson_type::symbol_type:
+ case jsoncons::bson::bson_type::min_key_type:
+ case jsoncons::bson::bson_type::max_key_type:
+ case jsoncons::bson::bson_type::string_type:
+ {
+ text_buffer_.clear();
+ read_string(ec);
+ if (ec)
+ {
+ return;
+ }
+ auto result = unicode_traits::validate(text_buffer_.data(), text_buffer_.size());
+ if (JSONCONS_UNLIKELY(result.ec != unicode_traits::conv_errc()))
+ {
+ ec = bson_errc::invalid_utf8_text_string;
+ more_ = false;
+ return;
+ }
+ more_ = visitor.string_value(text_buffer_, semantic_tag::none, *this, ec);
+ break;
+ }
+ case jsoncons::bson::bson_type::javascript_type:
+ {
+ text_buffer_.clear();
+ read_string(ec);
+ if (ec)
+ {
+ return;
+ }
+ auto result = unicode_traits::validate(text_buffer_.data(), text_buffer_.size());
+ if (JSONCONS_UNLIKELY(result.ec != unicode_traits::conv_errc()))
+ {
+ ec = bson_errc::invalid_utf8_text_string;
+ more_ = false;
+ return;
+ }
+ more_ = visitor.string_value(text_buffer_, semantic_tag::code, *this, ec);
+ break;
+ }
+ case jsoncons::bson::bson_type::regex_type:
+ {
+ text_buffer_.clear();
+ text_buffer_.push_back('/');
+ read_cstring(ec);
+ if (ec)
+ {
+ return;
+ }
+ text_buffer_.push_back('/');
+ read_cstring(ec);
+ if (ec)
+ {
+ return;
+ }
+ more_ = visitor.string_value(text_buffer_, semantic_tag::regex, *this, ec);
+ break;
+ }
+ case jsoncons::bson::bson_type::document_type:
+ {
+ begin_document(visitor,ec);
+ break;
+ }
+
+ case jsoncons::bson::bson_type::array_type:
+ {
+ begin_array(visitor,ec);
+ break;
+ }
+ case jsoncons::bson::bson_type::undefined_type:
+ {
+ more_ = visitor.null_value(semantic_tag::undefined, *this, ec);
+ break;
+ }
+ case jsoncons::bson::bson_type::null_type:
+ {
+ more_ = visitor.null_value(semantic_tag::none, *this, ec);
+ break;
+ }
+ case jsoncons::bson::bson_type::bool_type:
+ {
+ uint8_t c;
+ std::size_t n = source_.read(&c, 1);
+ state_stack_.back().pos += n;
+ if (JSONCONS_UNLIKELY(n != 1))
+ {
+ ec = bson_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ more_ = visitor.bool_value(c != 0, semantic_tag::none, *this, ec);
+ break;
+ }
+ case jsoncons::bson::bson_type::int32_type:
+ {
+ uint8_t buf[sizeof(int32_t)];
+ std::size_t n = source_.read(buf, sizeof(int32_t));
+ state_stack_.back().pos += n;
+ if (JSONCONS_UNLIKELY(n != sizeof(int32_t)))
+ {
+ ec = bson_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ auto val = binary::little_to_native<int32_t>(buf, sizeof(buf));
+ more_ = visitor.int64_value(val, semantic_tag::none, *this, ec);
+ break;
+ }
+
+ case jsoncons::bson::bson_type::timestamp_type:
+ {
+ uint8_t buf[sizeof(uint64_t)];
+ std::size_t n = source_.read(buf, sizeof(uint64_t));
+ state_stack_.back().pos += n;
+ if (JSONCONS_UNLIKELY(n != sizeof(uint64_t)))
+ {
+ ec = bson_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ auto val = binary::little_to_native<uint64_t>(buf, sizeof(buf));
+ more_ = visitor.uint64_value(val, semantic_tag::none, *this, ec);
+ break;
+ }
+
+ case jsoncons::bson::bson_type::int64_type:
+ {
+ uint8_t buf[sizeof(int64_t)];
+ std::size_t n = source_.read(buf, sizeof(int64_t));
+ state_stack_.back().pos += n;
+ if (JSONCONS_UNLIKELY(n != sizeof(int64_t)))
+ {
+ ec = bson_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ auto val = binary::little_to_native<int64_t>(buf, sizeof(buf));
+ more_ = visitor.int64_value(val, semantic_tag::none, *this, ec);
+ break;
+ }
+
+ case jsoncons::bson::bson_type::datetime_type:
+ {
+ uint8_t buf[sizeof(int64_t)];
+ std::size_t n = source_.read(buf, sizeof(int64_t));
+ state_stack_.back().pos += n;
+ if (JSONCONS_UNLIKELY(n != sizeof(int64_t)))
+ {
+ ec = bson_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ auto val = binary::little_to_native<int64_t>(buf, sizeof(buf));
+ more_ = visitor.int64_value(val, semantic_tag::epoch_milli, *this, ec);
+ break;
+ }
+ case jsoncons::bson::bson_type::binary_type:
+ {
+ uint8_t buf[sizeof(int32_t)];
+ std::size_t n = source_.read(buf, sizeof(int32_t));
+ state_stack_.back().pos += n;
+ if (JSONCONS_UNLIKELY(n != sizeof(int32_t)))
+ {
+ ec = bson_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ const auto len = binary::little_to_native<int32_t>(buf, sizeof(buf));
+ if (JSONCONS_UNLIKELY(len < 0))
+ {
+ ec = bson_errc::length_is_negative;
+ more_ = false;
+ return;
+ }
+ uint8_t subtype;
+ n = source_.read(&subtype, 1);
+ state_stack_.back().pos += n;
+ if (JSONCONS_UNLIKELY(n != 1))
+ {
+ ec = bson_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+
+ bytes_buffer_.clear();
+ n = source_reader<Source>::read(source_, bytes_buffer_, len);
+ state_stack_.back().pos += n;
+ if (JSONCONS_UNLIKELY(n != static_cast<std::size_t>(len)))
+ {
+ ec = bson_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+
+ more_ = visitor.byte_string_value(bytes_buffer_,
+ subtype,
+ *this,
+ ec);
+ break;
+ }
+ case jsoncons::bson::bson_type::decimal128_type:
+ {
+ uint8_t buf[sizeof(uint64_t)*2];
+ std::size_t n = source_.read(buf, sizeof(buf));
+ state_stack_.back().pos += n;
+ if (JSONCONS_UNLIKELY(n != sizeof(buf)))
+ {
+ ec = bson_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+
+ decimal128_t dec;
+ dec.low = binary::little_to_native<uint64_t>(buf, sizeof(uint64_t));
+ dec.high = binary::little_to_native<uint64_t>(buf+sizeof(uint64_t), sizeof(uint64_t));
+
+ text_buffer_.clear();
+ text_buffer_.resize(bson::decimal128_limits::buf_size);
+ auto r = bson::decimal128_to_chars(&text_buffer_[0], &text_buffer_[0]+text_buffer_.size(), dec);
+ more_ = visitor.string_value(string_view(text_buffer_.data(),static_cast<std::size_t>(r.ptr-text_buffer_.data())), semantic_tag::float128, *this, ec);
+ break;
+ }
+ case jsoncons::bson::bson_type::object_id_type:
+ {
+ uint8_t buf[12];
+ std::size_t n = source_.read(buf, sizeof(buf));
+ state_stack_.back().pos += n;
+ if (JSONCONS_UNLIKELY(n != sizeof(buf)))
+ {
+ ec = bson_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+
+ oid_t oid(buf);
+ to_string(oid, text_buffer_);
+
+ more_ = visitor.string_value(text_buffer_, semantic_tag::id, *this, ec);
+ break;
+ }
+ default:
+ {
+ ec = bson_errc::unknown_type;
+ more_ = false;
+ return;
+ }
+ }
+ }
+
+ void read_cstring(std::error_code& ec)
+ {
+ uint8_t c = 0xff;
+ while (true)
+ {
+ std::size_t n = source_.read(&c, 1);
+ state_stack_.back().pos += n;
+ if (JSONCONS_UNLIKELY(n != 1))
+ {
+ ec = bson_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ if (c == 0)
+ {
+ break;
+ }
+ text_buffer_.push_back(c);
+ }
+ }
+
+ void read_string(std::error_code& ec)
+ {
+ uint8_t buf[sizeof(int32_t)];
+ std::size_t n = source_.read(buf, sizeof(int32_t));
+ state_stack_.back().pos += n;
+ if (JSONCONS_UNLIKELY(n != sizeof(int32_t)))
+ {
+ ec = bson_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ auto len = binary::little_to_native<int32_t>(buf, sizeof(buf));
+ if (JSONCONS_UNLIKELY(len < 1))
+ {
+ ec = bson_errc::string_length_is_non_positive;
+ more_ = false;
+ return;
+ }
+
+ std::size_t size = static_cast<std::size_t>(len) - static_cast<std::size_t>(1);
+ n = source_reader<Source>::read(source_, text_buffer_, size);
+ state_stack_.back().pos += n;
+
+ if (JSONCONS_UNLIKELY(n != size))
+ {
+ ec = bson_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ uint8_t c;
+ n = source_.read(&c, 1);
+ state_stack_.back().pos += n;
+ if (JSONCONS_UNLIKELY(n != 1))
+ {
+ ec = bson_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ }
+};
+
+}}
+
+#endif
diff --git a/include/jsoncons_ext/bson/bson_reader.hpp b/include/jsoncons_ext/bson/bson_reader.hpp
new file mode 100644
index 0000000..0079cc9
--- /dev/null
+++ b/include/jsoncons_ext/bson/bson_reader.hpp
@@ -0,0 +1,92 @@
+// Copyright 2017 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_BSON_BSON_READER_HPP
+#define JSONCONS_BSON_BSON_READER_HPP
+
+#include <string>
+#include <vector>
+#include <memory>
+#include <utility> // std::move
+#include <jsoncons/json.hpp>
+#include <jsoncons/source.hpp>
+#include <jsoncons/json_visitor.hpp>
+#include <jsoncons/config/jsoncons_config.hpp>
+#include <jsoncons_ext/bson/bson_type.hpp>
+#include <jsoncons_ext/bson/bson_error.hpp>
+#include <jsoncons_ext/bson/bson_parser.hpp>
+
+namespace jsoncons { namespace bson {
+
+template <class Source,class Allocator=std::allocator<char>>
+class basic_bson_reader
+{
+ basic_bson_parser<Source,Allocator> parser_;
+ json_visitor& visitor_;
+public:
+ template <class Sourceable>
+ basic_bson_reader(Sourceable&& source,
+ json_visitor& visitor,
+ const Allocator alloc)
+ : basic_bson_reader(std::forward<Sourceable>(source),
+ visitor,
+ bson_decode_options(),
+ alloc)
+ {
+ }
+
+ template <class Sourceable>
+ basic_bson_reader(Sourceable&& source,
+ json_visitor& visitor,
+ const bson_decode_options& options = bson_decode_options(),
+ const Allocator alloc=Allocator())
+ : parser_(std::forward<Sourceable>(source), options, alloc),
+ visitor_(visitor)
+ {
+ }
+
+ void read()
+ {
+ std::error_code ec;
+ read(ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec,line(),column()));
+ }
+ }
+
+ void read(std::error_code& ec)
+ {
+ parser_.reset();
+ parser_.parse(visitor_, ec);
+ if (ec)
+ {
+ return;
+ }
+ }
+
+ std::size_t line() const
+ {
+ return parser_.line();
+ }
+
+ std::size_t column() const
+ {
+ return parser_.column();
+ }
+};
+
+using bson_stream_reader = basic_bson_reader<jsoncons::binary_stream_source>;
+using bson_bytes_reader = basic_bson_reader<jsoncons::bytes_source>;
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+JSONCONS_DEPRECATED_MSG("Instead, use bson_stream_reader") typedef bson_stream_reader bson_reader;
+JSONCONS_DEPRECATED_MSG("Instead, use bson_bytes_reader") typedef bson_bytes_reader bson_buffer_reader;
+#endif
+
+}}
+
+#endif
diff --git a/include/jsoncons_ext/bson/bson_type.hpp b/include/jsoncons_ext/bson/bson_type.hpp
new file mode 100644
index 0000000..cf12344
--- /dev/null
+++ b/include/jsoncons_ext/bson/bson_type.hpp
@@ -0,0 +1,44 @@
+// Copyright 2013 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_BSON_BSON_TYPE_HPP
+#define JSONCONS_BSON_BSON_TYPE_HPP
+
+#include <string>
+#include <memory>
+#include <jsoncons/config/jsoncons_config.hpp>
+
+namespace jsoncons { namespace bson {
+
+ namespace bson_type
+ {
+ const uint8_t double_type = 0x01;
+ const uint8_t string_type = 0x02; // UTF-8 string
+ const uint8_t document_type = 0x03;
+ const uint8_t array_type = 0x04;
+ const uint8_t binary_type = 0x05;
+ const uint8_t undefined_type = 0x06; // map to null
+ const uint8_t object_id_type = 0x07;
+ const uint8_t bool_type = 0x08;
+ const uint8_t datetime_type = 0x09;
+ const uint8_t null_type = 0x0a;
+ const uint8_t regex_type = 0x0b;
+ const uint8_t javascript_type = 0x0d;
+ const uint8_t symbol_type = 0x0e; // deprecated, mapped to string
+ const uint8_t javascript_with_scope_type = 0x0f; // unsupported
+ const uint8_t int32_type = 0x10;
+ const uint8_t timestamp_type = 0x11; // MongoDB internal Timestamp, uint64
+ const uint8_t int64_type = 0x12;
+ const uint8_t decimal128_type = 0x13;
+ const uint8_t min_key_type = 0xff;
+ const uint8_t max_key_type = 0x7f;
+ }
+
+ enum class bson_container_type {document, array};
+
+}}
+
+#endif
diff --git a/include/jsoncons_ext/bson/decode_bson.hpp b/include/jsoncons_ext/bson/decode_bson.hpp
new file mode 100644
index 0000000..ad352f6
--- /dev/null
+++ b/include/jsoncons_ext/bson/decode_bson.hpp
@@ -0,0 +1,201 @@
+// Copyright 2013 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_BSON_DECODE_BSON_HPP
+#define JSONCONS_BSON_DECODE_BSON_HPP
+
+#include <string>
+#include <vector>
+#include <memory>
+#include <type_traits> // std::enable_if
+#include <istream> // std::basic_istream
+#include <jsoncons/json.hpp>
+#include <jsoncons/config/jsoncons_config.hpp>
+#include <jsoncons_ext/bson/bson_reader.hpp>
+#include <jsoncons_ext/bson/bson_cursor.hpp>
+
+namespace jsoncons {
+namespace bson {
+
+ template<class T, class Source>
+ typename std::enable_if<type_traits::is_basic_json<T>::value &&
+ type_traits::is_byte_sequence<Source>::value,T>::type
+ decode_bson(const Source& v,
+ const bson_decode_options& options = bson_decode_options())
+ {
+ jsoncons::json_decoder<T> decoder;
+ auto adaptor = make_json_visitor_adaptor<json_visitor>(decoder);
+ basic_bson_reader<jsoncons::bytes_source> 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<class T, class Source>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value &&
+ type_traits::is_byte_sequence<Source>::value,T>::type
+ decode_bson(const Source& v,
+ const bson_decode_options& options = bson_decode_options())
+ {
+ basic_bson_cursor<bytes_source> cursor(v, options);
+ json_decoder<basic_json<char,sorted_policy>> decoder{};
+
+ std::error_code ec;
+ T val = decode_traits<T,char>::decode(cursor, decoder, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column()));
+ }
+ return val;
+ }
+
+ template<class T>
+ typename std::enable_if<type_traits::is_basic_json<T>::value,T>::type
+ decode_bson(std::istream& is,
+ const bson_decode_options& options = bson_decode_options())
+ {
+ jsoncons::json_decoder<T> decoder;
+ auto adaptor = make_json_visitor_adaptor<json_visitor>(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<class T>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value,T>::type
+ decode_bson(std::istream& is,
+ const bson_decode_options& options = bson_decode_options())
+ {
+ basic_bson_cursor<binary_stream_source> cursor(is, options);
+ json_decoder<basic_json<char,sorted_policy>> decoder{};
+
+ std::error_code ec;
+ T val = decode_traits<T,char>::decode(cursor, decoder, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column()));
+ }
+ return val;
+ }
+
+ template<class T, class InputIt>
+ typename std::enable_if<type_traits::is_basic_json<T>::value,T>::type
+ decode_bson(InputIt first, InputIt last,
+ const bson_decode_options& options = bson_decode_options())
+ {
+ jsoncons::json_decoder<T> decoder;
+ auto adaptor = make_json_visitor_adaptor<json_visitor>(decoder);
+ basic_bson_reader<binary_iterator_source<InputIt>> reader(binary_iterator_source<InputIt>(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<class T, class InputIt>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value,T>::type
+ decode_bson(InputIt first, InputIt last,
+ const bson_decode_options& options = bson_decode_options())
+ {
+ basic_bson_cursor<binary_iterator_source<InputIt>> cursor(binary_iterator_source<InputIt>(first, last), options);
+ json_decoder<basic_json<char,sorted_policy>> decoder{};
+
+ std::error_code ec;
+ T val = decode_traits<T,char>::decode(cursor, decoder, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column()));
+ }
+ return val;
+ }
+
+ // With leading allocator parameter
+
+ template<class T, class Source, class TempAllocator>
+ typename std::enable_if<type_traits::is_basic_json<T>::value &&
+ type_traits::is_byte_sequence<Source>::value,T>::type
+ decode_bson(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ const Source& v,
+ const bson_decode_options& options = bson_decode_options())
+ {
+ json_decoder<T,TempAllocator> decoder(temp_alloc);
+ auto adaptor = make_json_visitor_adaptor<json_visitor>(decoder);
+ basic_bson_reader<jsoncons::bytes_source,TempAllocator> reader(v, adaptor, options, temp_alloc);
+ reader.read();
+ if (!decoder.is_valid())
+ {
+ JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column()));
+ }
+ return decoder.get_result();
+ }
+
+ template<class T, class Source, class TempAllocator>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value &&
+ type_traits::is_byte_sequence<Source>::value,T>::type
+ decode_bson(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ const Source& v,
+ const bson_decode_options& options = bson_decode_options())
+ {
+ basic_bson_cursor<bytes_source,TempAllocator> cursor(v, options, temp_alloc);
+ json_decoder<basic_json<char,sorted_policy,TempAllocator>,TempAllocator> decoder(temp_alloc, temp_alloc);
+
+ std::error_code ec;
+ T val = decode_traits<T,char>::decode(cursor, decoder, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column()));
+ }
+ return val;
+ }
+
+ template<class T,class TempAllocator>
+ typename std::enable_if<type_traits::is_basic_json<T>::value,T>::type
+ decode_bson(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ std::istream& is,
+ const bson_decode_options& options = bson_decode_options())
+ {
+ json_decoder<T,TempAllocator> decoder(temp_alloc);
+ auto adaptor = make_json_visitor_adaptor<json_visitor>(decoder);
+ basic_bson_reader<jsoncons::binary_stream_source,TempAllocator> reader(is, adaptor, options, temp_alloc);
+ reader.read();
+ if (!decoder.is_valid())
+ {
+ JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column()));
+ }
+ return decoder.get_result();
+ }
+
+ template<class T,class TempAllocator>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value,T>::type
+ decode_bson(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ std::istream& is,
+ const bson_decode_options& options = bson_decode_options())
+ {
+ basic_bson_cursor<binary_stream_source,TempAllocator> cursor(is, options, temp_alloc);
+ json_decoder<basic_json<char,sorted_policy,TempAllocator>,TempAllocator> decoder(temp_alloc, temp_alloc);
+
+ std::error_code ec;
+ T val = decode_traits<T,char>::decode(cursor, decoder, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column()));
+ }
+ return val;
+ }
+
+} // bson
+} // jsoncons
+
+#endif
diff --git a/include/jsoncons_ext/bson/encode_bson.hpp b/include/jsoncons_ext/bson/encode_bson.hpp
new file mode 100644
index 0000000..55f8cf5
--- /dev/null
+++ b/include/jsoncons_ext/bson/encode_bson.hpp
@@ -0,0 +1,144 @@
+// Copyright 2013 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_BSON_ENCODE_BSON_HPP
+#define JSONCONS_BSON_ENCODE_BSON_HPP
+
+#include <string>
+#include <vector>
+#include <memory>
+#include <type_traits> // std::enable_if
+#include <istream> // std::basic_istream
+#include <jsoncons/json.hpp>
+#include <jsoncons/config/jsoncons_config.hpp>
+#include <jsoncons_ext/bson/bson_encoder.hpp>
+#include <jsoncons_ext/bson/bson_reader.hpp>
+
+namespace jsoncons {
+namespace bson {
+
+ template<class T, class Container>
+ typename std::enable_if<type_traits::is_basic_json<T>::value &&
+ type_traits::is_back_insertable_byte_container<Container>::value,void>::type
+ encode_bson(const T& j,
+ Container& v,
+ const bson_encode_options& options = bson_encode_options())
+ {
+ using char_type = typename T::char_type;
+ basic_bson_encoder<jsoncons::bytes_sink<Container>> encoder(v, options);
+ auto adaptor = make_json_visitor_adaptor<basic_json_visitor<char_type>>(encoder);
+ j.dump(adaptor);
+ }
+
+ template<class T, class Container>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value &&
+ type_traits::is_back_insertable_byte_container<Container>::value,void>::type
+ encode_bson(const T& val,
+ Container& v,
+ const bson_encode_options& options = bson_encode_options())
+ {
+ basic_bson_encoder<jsoncons::bytes_sink<Container>> encoder(v, options);
+ std::error_code ec;
+ encode_traits<T,char>::encode(val, encoder, json(), ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec));
+ }
+ }
+
+ template<class T>
+ typename std::enable_if<type_traits::is_basic_json<T>::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<basic_json_visitor<char_type>>(encoder);
+ j.dump(adaptor);
+ }
+
+ template<class T>
+ typename std::enable_if<!type_traits::is_basic_json<T>::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<T,char>::encode(val, encoder, json(), ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec));
+ }
+ }
+
+ // with temp_allocator_rag
+
+ template<class T, class Container, class TempAllocator>
+ typename std::enable_if<type_traits::is_basic_json<T>::value &&
+ type_traits::is_back_insertable_byte_container<Container>::value,void>::type
+ encode_bson(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ const T& j,
+ Container& v,
+ const bson_encode_options& options = bson_encode_options())
+ {
+ using char_type = typename T::char_type;
+ basic_bson_encoder<jsoncons::bytes_sink<Container>,TempAllocator> encoder(v, options, temp_alloc);
+ auto adaptor = make_json_visitor_adaptor<basic_json_visitor<char_type>>(encoder);
+ j.dump(adaptor);
+ }
+
+ template<class T, class Container, class TempAllocator>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value &&
+ type_traits::is_back_insertable_byte_container<Container>::value,void>::type
+ encode_bson(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ const T& val,
+ Container& v,
+ const bson_encode_options& options = bson_encode_options())
+ {
+ basic_bson_encoder<jsoncons::bytes_sink<Container>,TempAllocator> encoder(v, options, temp_alloc);
+ std::error_code ec;
+ encode_traits<T,char>::encode(val, encoder, json(), ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec));
+ }
+ }
+
+ template<class T,class TempAllocator>
+ typename std::enable_if<type_traits::is_basic_json<T>::value,void>::type
+ encode_bson(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ const T& j,
+ std::ostream& os,
+ const bson_encode_options& options = bson_encode_options())
+ {
+ using char_type = typename T::char_type;
+ basic_bson_encoder<jsoncons::binary_stream_sink,TempAllocator> encoder(os, options, temp_alloc);
+ auto adaptor = make_json_visitor_adaptor<basic_json_visitor<char_type>>(encoder);
+ j.dump(adaptor);
+ }
+
+ template<class T,class TempAllocator>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value,void>::type
+ encode_bson(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ const T& val,
+ std::ostream& os,
+ const bson_encode_options& options = bson_encode_options())
+ {
+ basic_bson_encoder<jsoncons::binary_stream_sink,TempAllocator> encoder(os, options, temp_alloc);
+ std::error_code ec;
+ encode_traits<T,char>::encode(val, encoder, json(), ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec));
+ }
+ }
+
+} // bson
+} // jsoncons
+
+#endif
diff --git a/include/jsoncons_ext/cbor/cbor.hpp b/include/jsoncons_ext/cbor/cbor.hpp
new file mode 100644
index 0000000..3f7329f
--- /dev/null
+++ b/include/jsoncons_ext/cbor/cbor.hpp
@@ -0,0 +1,26 @@
+// Copyright 2017 Daniel Parkerstd
+// 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_CBOR_CBOR_HPP
+#define JSONCONS_CBOR_CBOR_HPP
+
+#include <string>
+#include <vector>
+#include <memory>
+#include <type_traits> // std::enable_if
+#include <istream> // std::basic_istream
+#include <jsoncons/json.hpp>
+#include <jsoncons/json_filter.hpp>
+#include <jsoncons/config/jsoncons_config.hpp>
+#include <jsoncons_ext/cbor/cbor_reader.hpp>
+#include <jsoncons_ext/cbor/cbor_cursor.hpp>
+#include <jsoncons_ext/cbor/cbor_encoder.hpp>
+#include <jsoncons_ext/cbor/encode_cbor.hpp>
+#include <jsoncons_ext/cbor/decode_cbor.hpp>
+
+#endif
+
diff --git a/include/jsoncons_ext/cbor/cbor_cursor.hpp b/include/jsoncons_ext/cbor/cbor_cursor.hpp
new file mode 100644
index 0000000..af0d1a8
--- /dev/null
+++ b/include/jsoncons_ext/cbor/cbor_cursor.hpp
@@ -0,0 +1,351 @@
+// 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)
+
+// See https://github.com/danielaparker/jsoncons for latest version
+
+#ifndef JSONCONS_CBOR_CBOR_CURSOR_HPP
+#define JSONCONS_CBOR_CBOR_CURSOR_HPP
+
+#include <memory> // std::allocator
+#include <string>
+#include <vector>
+#include <stdexcept>
+#include <system_error>
+#include <ios>
+#include <istream> // std::basic_istream
+#include <jsoncons/byte_string.hpp>
+#include <jsoncons/config/jsoncons_config.hpp>
+#include <jsoncons/json_visitor.hpp>
+#include <jsoncons/json_exception.hpp>
+#include <jsoncons/staj_cursor.hpp>
+#include <jsoncons/source.hpp>
+#include <jsoncons_ext/cbor/cbor_parser.hpp>
+
+namespace jsoncons {
+namespace cbor {
+
+template<class Source=jsoncons::binary_stream_source,class Allocator=std::allocator<char>>
+class basic_cbor_cursor : public basic_staj_cursor<char>, private virtual ser_context
+{
+public:
+ using source_type = Source;
+ using char_type = char;
+ using allocator_type = Allocator;
+private:
+ basic_cbor_parser<Source,Allocator> parser_;
+ basic_staj_visitor<char_type> cursor_visitor_;
+ basic_json_visitor2_to_visitor_adaptor<char_type,Allocator> cursor_handler_adaptor_;
+ bool eof_;
+
+ // Noncopyable and nonmoveable
+ basic_cbor_cursor(const basic_cbor_cursor&) = delete;
+ basic_cbor_cursor& operator=(const basic_cbor_cursor&) = delete;
+
+public:
+ using string_view_type = string_view;
+
+ template <class Sourceable>
+ basic_cbor_cursor(Sourceable&& source,
+ const cbor_decode_options& options = cbor_decode_options(),
+ const Allocator& alloc = Allocator())
+ : parser_(std::forward<Sourceable>(source), options, alloc),
+ cursor_visitor_(accept_all),
+ cursor_handler_adaptor_(cursor_visitor_, alloc),
+ eof_(false)
+ {
+ if (!done())
+ {
+ next();
+ }
+ }
+
+ // Constructors that set parse error codes
+
+ template <class Sourceable>
+ basic_cbor_cursor(Sourceable&& source,
+ std::error_code& ec)
+ : basic_cbor_cursor(std::allocator_arg, Allocator(),
+ std::forward<Sourceable>(source),
+ cbor_decode_options(),
+ ec)
+ {
+ }
+
+ template <class Sourceable>
+ basic_cbor_cursor(Sourceable&& source,
+ const cbor_decode_options& options,
+ std::error_code& ec)
+ : basic_cbor_cursor(std::allocator_arg, Allocator(),
+ std::forward<Sourceable>(source),
+ options,
+ ec)
+ {
+ }
+
+ template <class Sourceable>
+ basic_cbor_cursor(std::allocator_arg_t, const Allocator& alloc,
+ Sourceable&& source,
+ const cbor_decode_options& options,
+ std::error_code& ec)
+ : parser_(std::forward<Sourceable>(source), options, alloc),
+ cursor_visitor_(accept_all),
+ cursor_handler_adaptor_(cursor_visitor_, alloc),
+ eof_(false)
+ {
+ if (!done())
+ {
+ next(ec);
+ }
+ }
+
+ void reset()
+ {
+ parser_.reset();
+ cursor_visitor_.reset();
+ cursor_handler_adaptor_.reset();
+ eof_ = false;
+ if (!done())
+ {
+ next();
+ }
+ }
+
+ template <class Sourceable>
+ void reset(Sourceable&& source)
+ {
+ parser_.reset(std::forward<Sourceable>(source));
+ cursor_visitor_.reset();
+ cursor_handler_adaptor_.reset();
+ eof_ = false;
+ if (!done())
+ {
+ next();
+ }
+ }
+
+ void reset(std::error_code& ec)
+ {
+ parser_.reset();
+ cursor_visitor_.reset();
+ cursor_handler_adaptor_.reset();
+ eof_ = false;
+ if (!done())
+ {
+ next(ec);
+ }
+ }
+
+ template <class Sourceable>
+ void reset(Sourceable&& source, std::error_code& ec)
+ {
+ parser_.reset(std::forward<Sourceable>(source));
+ cursor_visitor_.reset();
+ cursor_handler_adaptor_.reset();
+ eof_ = false;
+ if (!done())
+ {
+ next(ec);
+ }
+ }
+
+ bool done() const override
+ {
+ return parser_.done();
+ }
+
+ bool is_typed_array() const
+ {
+ return cursor_visitor_.is_typed_array();
+ }
+
+ const staj_event& current() const override
+ {
+ return cursor_visitor_.event();
+ }
+
+ void read_to(basic_json_visitor<char_type>& visitor) override
+ {
+ std::error_code ec;
+ read_to(visitor, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column()));
+ }
+ }
+
+ void read_to(basic_json_visitor<char_type>& visitor,
+ std::error_code& ec) override
+ {
+ if (cursor_visitor_.dump(visitor, *this, ec))
+ {
+ read_next(visitor, ec);
+ }
+ }
+
+ void next() override
+ {
+ std::error_code ec;
+ next(ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column()));
+ }
+ }
+
+ void next(std::error_code& ec) override
+ {
+ read_next(ec);
+ }
+
+ const ser_context& context() const override
+ {
+ return *this;
+ }
+
+ bool eof() const
+ {
+ return eof_;
+ }
+
+ std::size_t line() const override
+ {
+ return parser_.line();
+ }
+
+ std::size_t column() const override
+ {
+ return parser_.column();
+ }
+
+ friend
+ staj_filter_view operator|(basic_cbor_cursor& cursor,
+ std::function<bool(const staj_event&, const ser_context&)> pred)
+ {
+ return staj_filter_view(cursor, pred);
+ }
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+
+ template <class Sourceable>
+ JSONCONS_DEPRECATED_MSG("Instead, use pipe syntax for filter")
+ basic_cbor_cursor(Sourceable&& source,
+ std::function<bool(const staj_event&, const ser_context&)> filter,
+ const cbor_decode_options& options = cbor_decode_options(),
+ const Allocator& alloc = Allocator())
+ : parser_(std::forward<Sourceable>(source), options, alloc),
+ cursor_visitor_(filter),
+ cursor_handler_adaptor_(cursor_visitor_, alloc),
+ eof_(false)
+ {
+ if (!done())
+ {
+ next();
+ }
+ }
+
+ template <class Sourceable>
+ JSONCONS_DEPRECATED_MSG("Instead, use pipe syntax for filter")
+ basic_cbor_cursor(Sourceable&& source,
+ std::function<bool(const staj_event&, const ser_context&)> filter,
+ std::error_code& ec)
+ : basic_cbor_cursor(std::allocator_arg, Allocator(),
+ std::forward<Sourceable>(source), filter, ec)
+ {
+ }
+
+ template <class Sourceable>
+ JSONCONS_DEPRECATED_MSG("Instead, use pipe syntax for filter")
+ basic_cbor_cursor(std::allocator_arg_t, const Allocator& alloc,
+ Sourceable&& source,
+ std::function<bool(const staj_event&, const ser_context&)> filter,
+ std::error_code& ec)
+ : parser_(std::forward<Sourceable>(source), alloc),
+ cursor_visitor_(filter),
+ cursor_handler_adaptor_(cursor_visitor_, alloc),
+ eof_(false)
+ {
+ if (!done())
+ {
+ next(ec);
+ }
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use read_to(basic_json_visitor<char_type>&)")
+ void read(basic_json_visitor<char_type>& visitor)
+ {
+ read_to(visitor);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use read_to(basic_json_visitor<char_type>&, std::error_code&)")
+ void read(basic_json_visitor<char_type>& visitor,
+ std::error_code& ec)
+ {
+ read_to(visitor, ec);
+ }
+#endif
+private:
+ static bool accept_all(const staj_event&, const ser_context&)
+ {
+ return true;
+ }
+
+ void read_next(std::error_code& ec)
+ {
+ if (cursor_visitor_.in_available())
+ {
+ cursor_visitor_.send_available(ec);
+ }
+ else
+ {
+ parser_.restart();
+ while (!parser_.stopped())
+ {
+ parser_.parse(cursor_handler_adaptor_, ec);
+ if (ec) return;
+ }
+ }
+ }
+
+ void read_next(basic_json_visitor<char_type>& visitor, std::error_code& ec)
+ {
+ {
+ struct resource_wrapper
+ {
+ basic_json_visitor2_to_visitor_adaptor<char_type,Allocator>& adaptor;
+ basic_json_visitor<char_type>& original;
+
+ resource_wrapper(basic_json_visitor2_to_visitor_adaptor<char_type,Allocator>& adaptor,
+ basic_json_visitor<char_type>& visitor)
+ : adaptor(adaptor), original(adaptor.destination())
+ {
+ adaptor.destination(visitor);
+ }
+
+ ~resource_wrapper()
+ {
+ adaptor.destination(original);
+ }
+ } wrapper(cursor_handler_adaptor_, visitor);
+
+ parser_.restart();
+ while (!parser_.stopped())
+ {
+ parser_.parse(cursor_handler_adaptor_, ec);
+ if (ec)
+ {
+ return;
+ }
+ }
+ }
+ }
+};
+
+using cbor_stream_cursor = basic_cbor_cursor<jsoncons::binary_stream_source>;
+using cbor_bytes_cursor = basic_cbor_cursor<jsoncons::bytes_source>;
+
+} // namespace cbor
+} // namespace jsoncons
+
+#endif
+
diff --git a/include/jsoncons_ext/cbor/cbor_cursor2.hpp b/include/jsoncons_ext/cbor/cbor_cursor2.hpp
new file mode 100644
index 0000000..eee7445
--- /dev/null
+++ b/include/jsoncons_ext/cbor/cbor_cursor2.hpp
@@ -0,0 +1,265 @@
+// 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)
+
+// See https://github.com/danielaparker/jsoncons for latest version
+
+#ifndef JSONCONS_CBOR_CBOR_CURSOR2_HPP
+#define JSONCONS_CBOR_CBOR_CURSOR2_HPP
+
+#include <memory> // std::allocator
+#include <string>
+#include <vector>
+#include <stdexcept>
+#include <system_error>
+#include <ios>
+#include <istream> // std::basic_istream
+#include <jsoncons/byte_string.hpp>
+#include <jsoncons/config/jsoncons_config.hpp>
+#include <jsoncons/json_visitor2.hpp>
+#include <jsoncons/json_exception.hpp>
+#include <jsoncons/staj2_cursor.hpp>
+#include <jsoncons/source.hpp>
+#include <jsoncons_ext/cbor/cbor_parser.hpp>
+
+namespace jsoncons {
+namespace cbor {
+
+ template<class Source=jsoncons::binary_stream_source,class Allocator=std::allocator<char>>
+ class basic_cbor_cursor2 : public basic_staj2_cursor<char>, private virtual ser_context
+ {
+ public:
+ using source_type = Source;
+ using char_type = char;
+ using allocator_type = Allocator;
+ private:
+ basic_cbor_parser<Source,Allocator> parser_;
+ basic_staj2_visitor<char_type> cursor_visitor_;
+ bool eof_;
+
+ // Noncopyable and nonmoveable
+ basic_cbor_cursor2(const basic_cbor_cursor2&) = delete;
+ basic_cbor_cursor2& operator=(const basic_cbor_cursor2&) = delete;
+
+ public:
+ using string_view_type = string_view;
+
+ template <class Sourceable>
+ basic_cbor_cursor2(Sourceable&& source,
+ const cbor_decode_options& options = cbor_decode_options(),
+ const Allocator& alloc = Allocator())
+ : parser_(std::forward<Sourceable>(source), options, alloc),
+ cursor_visitor_(accept_all),
+ eof_(false)
+ {
+ if (!done())
+ {
+ next();
+ }
+ }
+
+ // Constructors that set parse error codes
+
+ template <class Sourceable>
+ basic_cbor_cursor2(Sourceable&& source,
+ std::error_code& ec)
+ : basic_cbor_cursor2(std::allocator_arg, Allocator(),
+ std::forward<Sourceable>(source),
+ cbor_decode_options(),
+ ec)
+ {
+ }
+
+ template <class Sourceable>
+ basic_cbor_cursor2(Sourceable&& source,
+ const cbor_decode_options& options,
+ std::error_code& ec)
+ : basic_cbor_cursor2(std::allocator_arg, Allocator(),
+ std::forward<Sourceable>(source),
+ options,
+ ec)
+ {
+ }
+
+ template <class Sourceable>
+ basic_cbor_cursor2(std::allocator_arg_t, const Allocator& alloc,
+ Sourceable&& source,
+ const cbor_decode_options& options,
+ std::error_code& ec)
+ : parser_(std::forward<Sourceable>(source), options, alloc),
+ cursor_visitor_(accept_all),
+ eof_(false)
+ {
+ if (!done())
+ {
+ next(ec);
+ }
+ }
+
+ void reset()
+ {
+ parser_.reset();
+ cursor_visitor_.reset();
+ eof_ = false;
+ if (!done())
+ {
+ next();
+ }
+ }
+
+ template <class Sourceable>
+ void reset(Sourceable&& source)
+ {
+ parser_.reset(std::forward<Sourceable>(source));
+ cursor_visitor_.reset();
+ eof_ = false;
+ if (!done())
+ {
+ next();
+ }
+ }
+
+ void reset(std::error_code& ec)
+ {
+ parser_.reset();
+ cursor_visitor_.reset();
+ eof_ = false;
+ if (!done())
+ {
+ next(ec);
+ }
+ }
+
+ template <class Sourceable>
+ void reset(Sourceable&& source, std::error_code& ec)
+ {
+ parser_.reset(std::forward<Sourceable>(source));
+ cursor_visitor_.reset();
+ eof_ = false;
+ if (!done())
+ {
+ next(ec);
+ }
+ }
+
+ bool done() const override
+ {
+ return parser_.done();
+ }
+
+ bool is_typed_array() const
+ {
+ return cursor_visitor_.is_typed_array();
+ }
+
+ const staj2_event& current() const override
+ {
+ return cursor_visitor_.event();
+ }
+
+ void read_to(basic_json_visitor2<char_type>& visitor) override
+ {
+ std::error_code ec;
+ read_to(visitor, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column()));
+ }
+ }
+
+ void read_to(basic_json_visitor2<char_type>& visitor,
+ std::error_code& ec) override
+ {
+ if (cursor_visitor_.dump(visitor, *this, ec))
+ {
+ read_next(visitor, ec);
+ }
+ }
+
+ void next() override
+ {
+ std::error_code ec;
+ next(ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column()));
+ }
+ }
+
+ void next(std::error_code& ec) override
+ {
+ read_next(ec);
+ }
+
+ const ser_context& context() const override
+ {
+ return *this;
+ }
+
+ bool eof() const
+ {
+ return eof_;
+ }
+
+ std::size_t line() const override
+ {
+ return parser_.line();
+ }
+
+ std::size_t column() const override
+ {
+ return parser_.column();
+ }
+
+ friend
+ staj2_filter_view operator|(basic_cbor_cursor2& cursor,
+ std::function<bool(const staj2_event&, const ser_context&)> pred)
+ {
+ return staj2_filter_view(cursor, pred);
+ }
+
+ private:
+ static bool accept_all(const staj2_event&, const ser_context&)
+ {
+ return true;
+ }
+
+ void read_next(std::error_code& ec)
+ {
+ if (cursor_visitor_.in_available())
+ {
+ cursor_visitor_.send_available(ec);
+ }
+ else
+ {
+ parser_.restart();
+ while (!parser_.stopped())
+ {
+ parser_.parse(cursor_visitor_, ec);
+ if (ec) return;
+ }
+ }
+ }
+
+ void read_next(basic_json_visitor2<char_type>& visitor, std::error_code& ec)
+ {
+ parser_.restart();
+ while (!parser_.stopped())
+ {
+ parser_.parse(visitor, ec);
+ if (ec)
+ {
+ return;
+ }
+ }
+ }
+ };
+
+ using cbor_stream_cursor2 = basic_cbor_cursor2<jsoncons::binary_stream_source>;
+ using cbor_bytes_cursor2 = basic_cbor_cursor2<jsoncons::bytes_source>;
+
+} // namespace cbor
+} // namespace jsoncons
+
+#endif
+
diff --git a/include/jsoncons_ext/cbor/cbor_detail.hpp b/include/jsoncons_ext/cbor/cbor_detail.hpp
new file mode 100644
index 0000000..9acfc3c
--- /dev/null
+++ b/include/jsoncons_ext/cbor/cbor_detail.hpp
@@ -0,0 +1,93 @@
+// Copyright 2017 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_CBOR_CBOR_DETAIL_HPP
+#define JSONCONS_CBOR_CBOR_DETAIL_HPP
+
+#include <string>
+#include <vector>
+#include <memory>
+#include <iterator> // std::forward_iterator_tag
+#include <limits> // std::numeric_limits
+#include <utility> // std::move
+#include <jsoncons/json.hpp>
+#include <jsoncons/json_visitor.hpp>
+#include <jsoncons/config/jsoncons_config.hpp>
+
+// 0x00..0x17 (0..23)
+#define JSONCONS_CBOR_0x00_0x17 \
+ 0x00:case 0x01:case 0x02:case 0x03:case 0x04:case 0x05:case 0x06:case 0x07:case 0x08:case 0x09:case 0x0a:case 0x0b:case 0x0c:case 0x0d:case 0x0e:case 0x0f:case 0x10:case 0x11:case 0x12:case 0x13:case 0x14:case 0x15:case 0x16:case 0x17
+
+#define JSONCONS_CBOR_ARRAY_TAGS \
+ 0x40:case 0x41:case 0x42:case 0x43:case 0x44:case 0x45:case 0x46:case 0x47:case 0x48:case 0x49:case 0x4a:case 0x4b:case 0x4c:case 0x4d:case 0x4e:case 0x4f:case 0x50:case 0x51:case 0x52:case 0x53:case 0x54:case 0x55:case 0x56:case 0x57
+
+namespace jsoncons { namespace cbor { namespace detail {
+
+//const uint8_t cbor_array_tags_010_mask = 0b11100000;
+//const uint8_t cbor_array_tags_f_mask = 0b00010000;
+//const uint8_t cbor_array_tags_s_mask = 0b00001000;
+//const uint8_t cbor_array_tags_e_mask = 0b00000100;
+//const uint8_t cbor_array_tags_ll_mask = 0b00000011;
+
+const uint8_t cbor_array_tags_010_mask = 0xE0;
+const uint8_t cbor_array_tags_f_mask = 0x10;
+const uint8_t cbor_array_tags_s_mask = 0x08;
+const uint8_t cbor_array_tags_e_mask = 0x04;
+const uint8_t cbor_array_tags_ll_mask = 0x03;
+
+const uint8_t cbor_array_tags_010_shift = 5;
+const uint8_t cbor_array_tags_f_shift = 4;
+const uint8_t cbor_array_tags_s_shift = 3;
+const uint8_t cbor_array_tags_e_shift = 2;
+const uint8_t cbor_array_tags_ll_shift = 0;
+
+enum class cbor_major_type : uint8_t
+{
+ unsigned_integer = 0x00,
+ negative_integer = 0x01,
+ byte_string = 0x02,
+ text_string = 0x03,
+ array = 0x04,
+ map = 0x05,
+ semantic_tag = 0x06,
+ simple = 0x7
+};
+
+namespace additional_info
+{
+ const uint8_t indefinite_length = 0x1f;
+}
+
+inline
+size_t min_length_for_stringref(uint64_t index)
+{
+ std::size_t n;
+ if (index <= 23)
+ {
+ n = 3;
+ }
+ else if (index <= 255)
+ {
+ n = 4;
+ }
+ else if (index <= 65535)
+ {
+ n = 5;
+ }
+ else if (index <= 4294967295)
+ {
+ n = 7;
+ }
+ else
+ {
+ n = 11;
+ }
+ return n;
+}
+
+}}}
+
+#endif
diff --git a/include/jsoncons_ext/cbor/cbor_encoder.hpp b/include/jsoncons_ext/cbor/cbor_encoder.hpp
new file mode 100644
index 0000000..f4699ee
--- /dev/null
+++ b/include/jsoncons_ext/cbor/cbor_encoder.hpp
@@ -0,0 +1,1766 @@
+// 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)
+
+// See https://github.com/danielaparker/jsoncons for latest version
+
+#ifndef JSONCONS_CBOR_CBOR_ENCODER_HPP
+#define JSONCONS_CBOR_CBOR_ENCODER_HPP
+
+#include <string>
+#include <vector>
+#include <limits> // std::numeric_limits
+#include <memory>
+#include <utility> // std::move
+#include <jsoncons/json_exception.hpp> // jsoncons::ser_error
+#include <jsoncons/json_visitor.hpp>
+#include <jsoncons/config/jsoncons_config.hpp>
+#include <jsoncons/sink.hpp>
+#include <jsoncons/detail/parse_number.hpp>
+#include <jsoncons_ext/cbor/cbor_error.hpp>
+#include <jsoncons_ext/cbor/cbor_options.hpp>
+
+namespace jsoncons { namespace cbor {
+
+enum class cbor_container_type {object, indefinite_length_object, array, indefinite_length_array};
+
+template<class Sink=jsoncons::binary_stream_sink,class Allocator=std::allocator<char>>
+class basic_cbor_encoder final : public basic_json_visitor<char>
+{
+ using super_type = basic_json_visitor<char>;
+
+ enum class decimal_parse_state { start, integer, exp1, exp2, fraction1 };
+ enum class hexfloat_parse_state { start, expect_0, expect_x, integer, exp1, exp2, fraction1 };
+
+ static constexpr int64_t nanos_in_second = 1000000000;
+ static constexpr int64_t millis_in_second = 1000;
+
+public:
+ using allocator_type = Allocator;
+ using sink_type = Sink;
+ using typename super_type::char_type;
+ using typename super_type::string_view_type;
+
+private:
+ using char_allocator_type = typename std::allocator_traits<allocator_type>:: template rebind_alloc<char_type>;
+ using byte_allocator_type = typename std::allocator_traits<allocator_type>:: template rebind_alloc<uint8_t>;
+
+ using string_type = std::basic_string<char_type,std::char_traits<char_type>,char_allocator_type>;
+ using byte_string_type = basic_byte_string<byte_allocator_type>;
+
+ struct stack_item
+ {
+ cbor_container_type type_;
+ std::size_t length_;
+ std::size_t count_;
+
+ stack_item(cbor_container_type type, std::size_t length = 0) noexcept
+ : type_(type), length_(length), count_(0)
+ {
+ }
+
+ std::size_t length() const
+ {
+ return length_;
+ }
+
+ std::size_t count() const
+ {
+ return count_;
+ }
+
+ bool is_object() const
+ {
+ return type_ == cbor_container_type::object || type_ == cbor_container_type::indefinite_length_object;
+ }
+
+ bool is_indefinite_length() const
+ {
+ return type_ == cbor_container_type::indefinite_length_array || type_ == cbor_container_type::indefinite_length_object;
+ }
+
+ };
+
+ typedef typename std::allocator_traits<allocator_type>:: template rebind_alloc<std::pair<const string_type,size_t>> string_size_allocator_type;
+ typedef typename std::allocator_traits<allocator_type>:: template rebind_alloc<std::pair<const byte_string_type,size_t>> byte_string_size_allocator_type;
+ typedef typename std::allocator_traits<allocator_type>:: template rebind_alloc<stack_item> stack_item_allocator_type;
+
+ Sink sink_;
+ const cbor_encode_options options_;
+ allocator_type alloc_;
+
+ std::vector<stack_item,stack_item_allocator_type> stack_;
+ std::map<string_type,size_t,std::less<string_type>,string_size_allocator_type> stringref_map_;
+ std::map<byte_string_type,size_t,std::less<byte_string_type>,byte_string_size_allocator_type> bytestringref_map_;
+ std::size_t next_stringref_ = 0;
+ int nesting_depth_;
+
+ // Noncopyable and nonmoveable
+ basic_cbor_encoder(const basic_cbor_encoder&) = delete;
+ basic_cbor_encoder& operator=(const basic_cbor_encoder&) = delete;
+public:
+ explicit basic_cbor_encoder(Sink&& sink,
+ const Allocator& alloc = Allocator())
+ : basic_cbor_encoder(std::forward<Sink>(sink), cbor_encode_options(), alloc)
+ {
+ }
+ basic_cbor_encoder(Sink&& sink,
+ const cbor_encode_options& options,
+ const Allocator& alloc = Allocator())
+ : sink_(std::forward<Sink>(sink)),
+ options_(options),
+ alloc_(alloc),
+ stack_(alloc),
+#if !defined(JSONCONS_NO_MAP_CONS_TAKES_ALLOCATOR)
+ stringref_map_(alloc),
+ bytestringref_map_(alloc),
+#endif
+ nesting_depth_(0)
+ {
+ if (options.pack_strings())
+ {
+ write_tag(256);
+ }
+ }
+
+ ~basic_cbor_encoder() noexcept
+ {
+ JSONCONS_TRY
+ {
+ sink_.flush();
+ }
+ JSONCONS_CATCH(...)
+ {
+ }
+ }
+
+ void reset()
+ {
+ stack_.clear();
+ stringref_map_.clear();
+ bytestringref_map_.clear();
+ next_stringref_ = 0;
+ nesting_depth_ = 0;
+ }
+
+ void reset(Sink&& sink)
+ {
+ sink_ = std::move(sink);
+ reset();
+ }
+
+private:
+ // Implementing methods
+
+ void visit_flush() override
+ {
+ sink_.flush();
+ }
+
+ bool visit_begin_object(semantic_tag, const ser_context&, std::error_code& ec) override
+ {
+ if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth()))
+ {
+ ec = cbor_errc::max_nesting_depth_exceeded;
+ return false;
+ }
+ stack_.emplace_back(cbor_container_type::indefinite_length_object);
+
+ sink_.push_back(0xbf);
+ return true;
+ }
+
+ bool 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()))
+ {
+ ec = cbor_errc::max_nesting_depth_exceeded;
+ return false;
+ }
+ stack_.emplace_back(cbor_container_type::object, length);
+
+ if (length <= 0x17)
+ {
+ binary::native_to_big(static_cast<uint8_t>(0xa0 + length),
+ std::back_inserter(sink_));
+ }
+ else if (length <= 0xff)
+ {
+ binary::native_to_big(static_cast<uint8_t>(0xb8),
+ std::back_inserter(sink_));
+ binary::native_to_big(static_cast<uint8_t>(length),
+ std::back_inserter(sink_));
+ }
+ else if (length <= 0xffff)
+ {
+ binary::native_to_big(static_cast<uint8_t>(0xb9),
+ std::back_inserter(sink_));
+ binary::native_to_big(static_cast<uint16_t>(length),
+ std::back_inserter(sink_));
+ }
+ else if (length <= 0xffffffff)
+ {
+ binary::native_to_big(static_cast<uint8_t>(0xba),
+ std::back_inserter(sink_));
+ binary::native_to_big(static_cast<uint32_t>(length),
+ std::back_inserter(sink_));
+ }
+ else if (length <= 0xffffffffffffffff)
+ {
+ binary::native_to_big(static_cast<uint8_t>(0xbb),
+ std::back_inserter(sink_));
+ binary::native_to_big(static_cast<uint64_t>(length),
+ std::back_inserter(sink_));
+ }
+
+ return true;
+ }
+
+ bool visit_end_object(const ser_context&, std::error_code& ec) override
+ {
+ JSONCONS_ASSERT(!stack_.empty());
+ --nesting_depth_;
+
+ if (stack_.back().is_indefinite_length())
+ {
+ sink_.push_back(0xff);
+ }
+ else
+ {
+ if (stack_.back().count() < stack_.back().length())
+ {
+ ec = cbor_errc::too_few_items;
+ return false;
+ }
+ if (stack_.back().count() > stack_.back().length())
+ {
+ ec = cbor_errc::too_many_items;
+ return false;
+ }
+ }
+
+ stack_.pop_back();
+ end_value();
+
+ return true;
+ }
+
+ bool visit_begin_array(semantic_tag, const ser_context&, std::error_code& ec) override
+ {
+ if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth()))
+ {
+ ec = cbor_errc::max_nesting_depth_exceeded;
+ return false;
+ }
+ stack_.emplace_back(cbor_container_type::indefinite_length_array);
+ sink_.push_back(0x9f);
+ return true;
+ }
+
+ bool 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()))
+ {
+ ec = cbor_errc::max_nesting_depth_exceeded;
+ return false;
+ }
+ stack_.emplace_back(cbor_container_type::array, length);
+ if (length <= 0x17)
+ {
+ binary::native_to_big(static_cast<uint8_t>(0x80 + length),
+ std::back_inserter(sink_));
+ }
+ else if (length <= 0xff)
+ {
+ binary::native_to_big(static_cast<uint8_t>(0x98),
+ std::back_inserter(sink_));
+ binary::native_to_big(static_cast<uint8_t>(length),
+ std::back_inserter(sink_));
+ }
+ else if (length <= 0xffff)
+ {
+ binary::native_to_big(static_cast<uint8_t>(0x99),
+ std::back_inserter(sink_));
+ binary::native_to_big(static_cast<uint16_t>(length),
+ std::back_inserter(sink_));
+ }
+ else if (length <= 0xffffffff)
+ {
+ binary::native_to_big(static_cast<uint8_t>(0x9a),
+ std::back_inserter(sink_));
+ binary::native_to_big(static_cast<uint32_t>(length),
+ std::back_inserter(sink_));
+ }
+ else if (length <= 0xffffffffffffffff)
+ {
+ binary::native_to_big(static_cast<uint8_t>(0x9b),
+ std::back_inserter(sink_));
+ binary::native_to_big(static_cast<uint64_t>(length),
+ std::back_inserter(sink_));
+ }
+ return true;
+ }
+
+ bool visit_end_array(const ser_context&, std::error_code& ec) override
+ {
+ JSONCONS_ASSERT(!stack_.empty());
+ --nesting_depth_;
+
+ if (stack_.back().is_indefinite_length())
+ {
+ sink_.push_back(0xff);
+ }
+ else
+ {
+ if (stack_.back().count() < stack_.back().length())
+ {
+ ec = cbor_errc::too_few_items;
+ return false;
+ }
+ if (stack_.back().count() > stack_.back().length())
+ {
+ ec = cbor_errc::too_many_items;
+ return false;
+ }
+ }
+
+ stack_.pop_back();
+ end_value();
+
+ return true;
+ }
+
+ bool visit_key(const string_view_type& name, const ser_context&, std::error_code&) override
+ {
+ write_string(name);
+ return true;
+ }
+
+ bool visit_null(semantic_tag tag, const ser_context&, std::error_code&) override
+ {
+ if (tag == semantic_tag::undefined)
+ {
+ sink_.push_back(0xf7);
+ }
+ else
+ {
+ sink_.push_back(0xf6);
+ }
+
+ end_value();
+ return true;
+ }
+
+ void write_string(const string_view& sv)
+ {
+ auto sink = unicode_traits::validate(sv.data(), sv.size());
+ if (sink.ec != unicode_traits::conv_errc())
+ {
+ 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_))
+ {
+ string_type s(sv.data(), sv.size(), alloc_);
+ auto it = stringref_map_.find(s);
+ if (it == stringref_map_.end())
+ {
+ stringref_map_.emplace(std::make_pair(std::move(s), next_stringref_++));
+ write_utf8_string(sv);
+ }
+ else
+ {
+ write_tag(25);
+ write_uint64_value(it->second);
+ }
+ }
+ else
+ {
+ write_utf8_string(sv);
+ }
+ }
+
+ void write_utf8_string(const string_view& sv)
+ {
+ const size_t length = sv.size();
+
+ if (length <= 0x17)
+ {
+ // fixstr stores a byte array whose length is upto 31 bytes
+ binary::native_to_big(static_cast<uint8_t>(0x60 + length),
+ std::back_inserter(sink_));
+ }
+ else if (length <= 0xff)
+ {
+ binary::native_to_big(static_cast<uint8_t>(0x78),
+ std::back_inserter(sink_));
+ binary::native_to_big(static_cast<uint8_t>(length),
+ std::back_inserter(sink_));
+ }
+ else if (length <= 0xffff)
+ {
+ binary::native_to_big(static_cast<uint8_t>(0x79),
+ std::back_inserter(sink_));
+ binary::native_to_big(static_cast<uint16_t>(length),
+ std::back_inserter(sink_));
+ }
+ else if (length <= 0xffffffff)
+ {
+ binary::native_to_big(static_cast<uint8_t>(0x7a),
+ std::back_inserter(sink_));
+ binary::native_to_big(static_cast<uint32_t>(length),
+ std::back_inserter(sink_));
+ }
+ else if (length <= 0xffffffffffffffff)
+ {
+ binary::native_to_big(static_cast<uint8_t>(0x7b),
+ std::back_inserter(sink_));
+ binary::native_to_big(static_cast<uint64_t>(length),
+ std::back_inserter(sink_));
+ }
+
+ for (auto c : sv)
+ {
+ sink_.push_back(c);
+ }
+ }
+
+ void write_bignum(bigint& n)
+ {
+ bool is_neg = n < 0;
+ if (is_neg)
+ {
+ n = - n -1;
+ }
+
+ int signum;
+ std::vector<uint8_t> data;
+ n.write_bytes_be(signum, data);
+ std::size_t length = data.size();
+
+ if (is_neg)
+ {
+ write_tag(3);
+ }
+ else
+ {
+ write_tag(2);
+ }
+
+ if (length <= 0x17)
+ {
+ // fixstr stores a byte array whose length is upto 31 bytes
+ binary::native_to_big(static_cast<uint8_t>(0x40 + length),
+ std::back_inserter(sink_));
+ }
+ else if (length <= 0xff)
+ {
+ binary::native_to_big(static_cast<uint8_t>(0x58),
+ std::back_inserter(sink_));
+ binary::native_to_big(static_cast<uint8_t>(length),
+ std::back_inserter(sink_));
+ }
+ else if (length <= 0xffff)
+ {
+ binary::native_to_big(static_cast<uint8_t>(0x59),
+ std::back_inserter(sink_));
+ binary::native_to_big(static_cast<uint16_t>(length),
+ std::back_inserter(sink_));
+ }
+ else if (length <= 0xffffffff)
+ {
+ binary::native_to_big(static_cast<uint8_t>(0x5a),
+ std::back_inserter(sink_));
+ binary::native_to_big(static_cast<uint32_t>(length),
+ std::back_inserter(sink_));
+ }
+ else if (length <= 0xffffffffffffffff)
+ {
+ binary::native_to_big(static_cast<uint8_t>(0x5b),
+ std::back_inserter(sink_));
+ binary::native_to_big(static_cast<uint64_t>(length),
+ std::back_inserter(sink_));
+ }
+
+ for (auto c : data)
+ {
+ sink_.push_back(c);
+ }
+ }
+
+ bool write_decimal_value(const string_view_type& sv, const ser_context& context, std::error_code& ec)
+ {
+ bool more = true;
+
+ decimal_parse_state state = decimal_parse_state::start;
+ std::basic_string<char> s;
+ std::basic_string<char> exponent;
+ int64_t scale = 0;
+ for (auto c : sv)
+ {
+ switch (state)
+ {
+ case decimal_parse_state::start:
+ {
+ switch (c)
+ {
+ case '-':
+ s.push_back(c);
+ state = decimal_parse_state::integer;
+ break;
+ case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9':
+ s.push_back(c);
+ state = decimal_parse_state::integer;
+ break;
+ default:
+ {
+ ec = cbor_errc::invalid_decimal_fraction;
+ return false;
+ }
+ }
+ break;
+ }
+ case decimal_parse_state::integer:
+ {
+ switch (c)
+ {
+ case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9':
+ s.push_back(c);
+ break;
+ case 'e': case 'E':
+ state = decimal_parse_state::exp1;
+ break;
+ case '.':
+ state = decimal_parse_state::fraction1;
+ break;
+ default:
+ {
+ ec = cbor_errc::invalid_decimal_fraction;
+ return false;
+ }
+ }
+ break;
+ }
+ case decimal_parse_state::exp1:
+ {
+ switch (c)
+ {
+ case '+':
+ state = decimal_parse_state::exp2;
+ break;
+ case '-':
+ exponent.push_back(c);
+ state = decimal_parse_state::exp2;
+ break;
+ case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9':
+ exponent.push_back(c);
+ state = decimal_parse_state::exp2;
+ break;
+ default:
+ {
+ ec = cbor_errc::invalid_decimal_fraction;
+ return false;
+ }
+ }
+ break;
+ }
+ case decimal_parse_state::exp2:
+ {
+ switch (c)
+ {
+ case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9':
+ exponent.push_back(c);
+ break;
+ default:
+ {
+ ec = cbor_errc::invalid_decimal_fraction;
+ return false;
+ }
+ }
+ break;
+ }
+ case decimal_parse_state::fraction1:
+ {
+ switch (c)
+ {
+ case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9':
+ s.push_back(c);
+ --scale;
+ break;
+ default:
+ {
+ ec = cbor_errc::invalid_decimal_fraction;
+ return false;
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ write_tag(4);
+ more = visit_begin_array((std::size_t)2, semantic_tag::none, context, ec);
+ if (!more) {return more;}
+ if (exponent.length() > 0)
+ {
+ int64_t val;
+ auto r = jsoncons::detail::to_integer(exponent.data(), exponent.length(), val);
+ if (!r)
+ {
+ ec = r.error_code();
+ return false;
+ }
+ scale += val;
+ }
+ more = visit_int64(scale, semantic_tag::none, context, ec);
+ if (!more) {return more;}
+
+ int64_t val{ 0 };
+ auto r = jsoncons::detail::to_integer(s.data(),s.length(), val);
+ if (r)
+ {
+ more = visit_int64(val, semantic_tag::none, context, ec);
+ if (!more) {return more;}
+ }
+ else if (r.error_code() == jsoncons::detail::to_integer_errc::overflow)
+ {
+ bigint n = bigint::from_string(s.data(), s.length());
+ write_bignum(n);
+ end_value();
+ }
+ else
+ {
+ ec = r.error_code();
+ return false;
+ }
+ more = visit_end_array(context, ec);
+
+ return more;
+ }
+
+ bool write_hexfloat_value(const string_view_type& sv, const ser_context& context, std::error_code& ec)
+ {
+ bool more = true;
+
+ hexfloat_parse_state state = hexfloat_parse_state::start;
+ std::basic_string<char> s;
+ std::basic_string<char> exponent;
+ int64_t scale = 0;
+
+ for (auto c : sv)
+ {
+ switch (state)
+ {
+ case hexfloat_parse_state::start:
+ {
+ switch (c)
+ {
+ case '-':
+ s.push_back(c);
+ state = hexfloat_parse_state::expect_0;
+ break;
+ case '0':
+ state = hexfloat_parse_state::expect_x;
+ break;
+ default:
+ {
+ ec = cbor_errc::invalid_bigfloat;
+ return false;
+ }
+ }
+ break;
+ }
+ case hexfloat_parse_state::expect_0:
+ {
+ switch (c)
+ {
+ case '0':
+ state = hexfloat_parse_state::expect_x;
+ break;
+ default:
+ {
+ ec = cbor_errc::invalid_bigfloat;
+ return false;
+ }
+ }
+ break;
+ }
+ case hexfloat_parse_state::expect_x:
+ {
+ switch (c)
+ {
+ case 'x':
+ case 'X':
+ state = hexfloat_parse_state::integer;
+ break;
+ default:
+ {
+ ec = cbor_errc::invalid_bigfloat;
+ return false;
+ }
+ }
+ break;
+ }
+ case hexfloat_parse_state::integer:
+ {
+ switch (c)
+ {
+ case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9':case 'a':case 'b':case 'c':case 'd':case 'e':case 'f':case 'A':case 'B':case 'C':case 'D':case 'E':case 'F':
+ s.push_back(c);
+ break;
+ case 'p': case 'P':
+ state = hexfloat_parse_state::exp1;
+ break;
+ case '.':
+ state = hexfloat_parse_state::fraction1;
+ break;
+ default:
+ {
+ ec = cbor_errc::invalid_bigfloat;
+ return false;
+ }
+ }
+ break;
+ }
+ case hexfloat_parse_state::exp1:
+ {
+ switch (c)
+ {
+ case '+':
+ state = hexfloat_parse_state::exp2;
+ break;
+ case '-':
+ exponent.push_back(c);
+ state = hexfloat_parse_state::exp2;
+ break;
+ case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9':case 'a':case 'b':case 'c':case 'd':case 'e':case 'f':case 'A':case 'B':case 'C':case 'D':case 'E':case 'F':
+ exponent.push_back(c);
+ state = hexfloat_parse_state::exp2;
+ break;
+ default:
+ {
+ ec = cbor_errc::invalid_bigfloat;
+ return false;
+ }
+ }
+ break;
+ }
+ case hexfloat_parse_state::exp2:
+ {
+ switch (c)
+ {
+ case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9':case 'a':case 'b':case 'c':case 'd':case 'e':case 'f':case 'A':case 'B':case 'C':case 'D':case 'E':case 'F':
+ exponent.push_back(c);
+ break;
+ default:
+ {
+ ec = cbor_errc::invalid_bigfloat;
+ return false;
+ }
+ }
+ break;
+ }
+ case hexfloat_parse_state::fraction1:
+ {
+ switch (c)
+ {
+ case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9':case 'a':case 'b':case 'c':case 'd':case 'e':case 'f':case 'A':case 'B':case 'C':case 'D':case 'E':case 'F':
+ s.push_back(c);
+ scale -= 4;
+ break;
+ default:
+ {
+ ec = cbor_errc::invalid_bigfloat;
+ return false;
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ write_tag(5);
+ more = visit_begin_array((std::size_t)2, semantic_tag::none, context, ec);
+ if (!more) return more;
+
+ if (exponent.length() > 0)
+ {
+ int64_t val{ 0 };
+ auto r = jsoncons::detail::base16_to_integer(exponent.data(), exponent.length(), val);
+ if (!r)
+ {
+ ec = r.error_code();
+ return false;
+ }
+ scale += val;
+ }
+ more = visit_int64(scale, semantic_tag::none, context, ec);
+ if (!more) return more;
+
+ int64_t val{ 0 };
+ auto r = jsoncons::detail::base16_to_integer(s.data(),s.length(), val);
+ if (r)
+ {
+ more = visit_int64(val, semantic_tag::none, context, ec);
+ if (!more) return more;
+ }
+ else if (r.error_code() == jsoncons::detail::to_integer_errc::overflow)
+ {
+ bigint n = bigint::from_string_radix(s.data(), s.length(), 16);
+ write_bignum(n);
+ end_value();
+ }
+ else
+ {
+ JSONCONS_THROW(json_runtime_error<std::invalid_argument>(r.error_code().message()));
+ }
+ return visit_end_array(context, ec);
+ }
+
+ bool visit_string(const string_view_type& sv, semantic_tag tag, const ser_context& context, std::error_code& ec) override
+ {
+ switch (tag)
+ {
+ case semantic_tag::bigint:
+ {
+ bigint n = bigint::from_string(sv.data(), sv.length());
+ write_bignum(n);
+ end_value();
+ break;
+ }
+ case semantic_tag::bigdec:
+ {
+ return write_decimal_value(sv, context, ec);
+ }
+ case semantic_tag::bigfloat:
+ {
+ return write_hexfloat_value(sv, context, ec);
+ }
+ case semantic_tag::datetime:
+ {
+ write_tag(0);
+
+ write_string(sv);
+ end_value();
+ break;
+ }
+ case semantic_tag::uri:
+ {
+ write_tag(32);
+ write_string(sv);
+ end_value();
+ break;
+ }
+ case semantic_tag::base64url:
+ {
+ write_tag(33);
+ write_string(sv);
+ end_value();
+ break;
+ }
+ case semantic_tag::base64:
+ {
+ write_tag(34);
+ write_string(sv);
+ end_value();
+ break;
+ }
+ default:
+ {
+ write_string(sv);
+ end_value();
+ break;
+ }
+ }
+ return true;
+ }
+
+ bool visit_byte_string(const byte_string_view& b,
+ semantic_tag tag,
+ const ser_context&,
+ std::error_code&) override
+ {
+ byte_string_chars_format encoding_hint;
+ switch (tag)
+ {
+ case semantic_tag::base16:
+ encoding_hint = byte_string_chars_format::base16;
+ break;
+ case semantic_tag::base64:
+ encoding_hint = byte_string_chars_format::base64;
+ break;
+ case semantic_tag::base64url:
+ encoding_hint = byte_string_chars_format::base64url;
+ break;
+ default:
+ encoding_hint = byte_string_chars_format::none;
+ break;
+ }
+ switch (encoding_hint)
+ {
+ case byte_string_chars_format::base64url:
+ write_tag(21);
+ break;
+ case byte_string_chars_format::base64:
+ write_tag(22);
+ break;
+ case byte_string_chars_format::base16:
+ write_tag(23);
+ break;
+ default:
+ break;
+ }
+ if (options_.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);
+ if (it == bytestringref_map_.end())
+ {
+ bytestringref_map_.emplace(std::make_pair(bs, next_stringref_++));
+ write_byte_string_value(bs);
+ }
+ else
+ {
+ write_tag(25);
+ write_uint64_value(it->second);
+ }
+ }
+ else
+ {
+ write_byte_string_value(b);
+ }
+
+ end_value();
+ return true;
+ }
+
+ bool visit_byte_string(const byte_string_view& b,
+ uint64_t ext_tag,
+ const ser_context&,
+ std::error_code&) override
+ {
+ if (options_.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);
+ if (it == bytestringref_map_.end())
+ {
+ bytestringref_map_.emplace(std::make_pair(bs, next_stringref_++));
+ write_tag(ext_tag);
+ write_byte_string_value(bs);
+ }
+ else
+ {
+ write_tag(25);
+ write_uint64_value(it->second);
+ }
+ }
+ else
+ {
+ write_tag(ext_tag);
+ write_byte_string_value(b);
+ }
+
+ end_value();
+ return true;
+ }
+
+ void write_byte_string_value(const byte_string_view& b)
+ {
+ if (b.size() <= 0x17)
+ {
+ // fixstr stores a byte array whose length is upto 31 bytes
+ binary::native_to_big(static_cast<uint8_t>(0x40 + b.size()),
+ std::back_inserter(sink_));
+ }
+ else if (b.size() <= 0xff)
+ {
+ binary::native_to_big(static_cast<uint8_t>(0x58),
+ std::back_inserter(sink_));
+ binary::native_to_big(static_cast<uint8_t>(b.size()),
+ std::back_inserter(sink_));
+ }
+ else if (b.size() <= 0xffff)
+ {
+ binary::native_to_big(static_cast<uint8_t>(0x59),
+ std::back_inserter(sink_));
+ binary::native_to_big(static_cast<uint16_t>(b.size()),
+ std::back_inserter(sink_));
+ }
+ else if (b.size() <= 0xffffffff)
+ {
+ binary::native_to_big(static_cast<uint8_t>(0x5a),
+ std::back_inserter(sink_));
+ binary::native_to_big(static_cast<uint32_t>(b.size()),
+ std::back_inserter(sink_));
+ }
+ else // if (b.size() <= 0xffffffffffffffff)
+ {
+ binary::native_to_big(static_cast<uint8_t>(0x5b),
+ std::back_inserter(sink_));
+ binary::native_to_big(static_cast<uint64_t>(b.size()),
+ std::back_inserter(sink_));
+ }
+
+ for (auto c : b)
+ {
+ sink_.push_back(c);
+ }
+ }
+
+ bool visit_double(double val,
+ semantic_tag tag,
+ const ser_context&,
+ std::error_code&) override
+ {
+ switch (tag)
+ {
+ case semantic_tag::epoch_second:
+ write_tag(1);
+ break;
+ case semantic_tag::epoch_milli:
+ write_tag(1);
+ if (val != 0)
+ {
+ val /= millis_in_second;
+ }
+ break;
+ case semantic_tag::epoch_nano:
+ write_tag(1);
+ if (val != 0)
+ {
+ val /= nanos_in_second;
+ }
+ break;
+ default:
+ break;
+ }
+
+ float valf = (float)val;
+ if ((double)valf == val)
+ {
+ binary::native_to_big(static_cast<uint8_t>(0xfa),
+ std::back_inserter(sink_));
+ binary::native_to_big(valf, std::back_inserter(sink_));
+ }
+ else
+ {
+ binary::native_to_big(static_cast<uint8_t>(0xfb),
+ std::back_inserter(sink_));
+ binary::native_to_big(val, std::back_inserter(sink_));
+ }
+
+ // write double
+
+ end_value();
+ return true;
+ }
+
+ bool visit_int64(int64_t value,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ switch (tag)
+ {
+ case semantic_tag::epoch_milli:
+ case semantic_tag::epoch_nano:
+ return visit_double(static_cast<double>(value), tag, context, ec);
+ case semantic_tag::epoch_second:
+ write_tag(1);
+ break;
+ default:
+ break;
+ }
+ if (value >= 0)
+ {
+ if (value <= 0x17)
+ {
+ binary::native_to_big(static_cast<uint8_t>(value),
+ std::back_inserter(sink_));
+ }
+ else if (value <= (std::numeric_limits<uint8_t>::max)())
+ {
+ binary::native_to_big(static_cast<uint8_t>(0x18),
+ std::back_inserter(sink_));
+ binary::native_to_big(static_cast<uint8_t>(value),
+ std::back_inserter(sink_));
+ }
+ else if (value <= (std::numeric_limits<uint16_t>::max)())
+ {
+ binary::native_to_big(static_cast<uint8_t>(0x19),
+ std::back_inserter(sink_));
+ binary::native_to_big(static_cast<uint16_t>(value),
+ std::back_inserter(sink_));
+ }
+ else if (value <= (std::numeric_limits<uint32_t>::max)())
+ {
+ binary::native_to_big(static_cast<uint8_t>(0x1a),
+ std::back_inserter(sink_));
+ binary::native_to_big(static_cast<uint32_t>(value),
+ std::back_inserter(sink_));
+ }
+ else if (value <= (std::numeric_limits<int64_t>::max)())
+ {
+ binary::native_to_big(static_cast<uint8_t>(0x1b),
+ std::back_inserter(sink_));
+ binary::native_to_big(static_cast<int64_t>(value),
+ std::back_inserter(sink_));
+ }
+ } else
+ {
+ const auto posnum = -1 - value;
+ if (value >= -24)
+ {
+ binary::native_to_big(static_cast<uint8_t>(0x20 + posnum),
+ std::back_inserter(sink_));
+ }
+ else if (posnum <= (std::numeric_limits<uint8_t>::max)())
+ {
+ binary::native_to_big(static_cast<uint8_t>(0x38),
+ std::back_inserter(sink_));
+ binary::native_to_big(static_cast<uint8_t>(posnum),
+ std::back_inserter(sink_));
+ }
+ else if (posnum <= (std::numeric_limits<uint16_t>::max)())
+ {
+ binary::native_to_big(static_cast<uint8_t>(0x39),
+ std::back_inserter(sink_));
+ binary::native_to_big(static_cast<uint16_t>(posnum),
+ std::back_inserter(sink_));
+ }
+ else if (posnum <= (std::numeric_limits<uint32_t>::max)())
+ {
+ binary::native_to_big(static_cast<uint8_t>(0x3a),
+ std::back_inserter(sink_));
+ binary::native_to_big(static_cast<uint32_t>(posnum),
+ std::back_inserter(sink_));
+ }
+ else if (posnum <= (std::numeric_limits<int64_t>::max)())
+ {
+ binary::native_to_big(static_cast<uint8_t>(0x3b),
+ std::back_inserter(sink_));
+ binary::native_to_big(static_cast<int64_t>(posnum),
+ std::back_inserter(sink_));
+ }
+ }
+ end_value();
+ return true;
+ }
+
+ bool visit_uint64(uint64_t value,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ switch (tag)
+ {
+ case semantic_tag::epoch_milli:
+ case semantic_tag::epoch_nano:
+ return visit_double(static_cast<double>(value), tag, context, ec);
+ case semantic_tag::epoch_second:
+ write_tag(1);
+ break;
+ default:
+ break;
+ }
+
+ write_uint64_value(value);
+ end_value();
+ return true;
+ }
+
+ void write_tag(uint64_t value)
+ {
+ if (value <= 0x17)
+ {
+ sink_.push_back(0xc0 | static_cast<uint8_t>(value));
+ }
+ else if (value <=(std::numeric_limits<uint8_t>::max)())
+ {
+ sink_.push_back(0xd8);
+ sink_.push_back(static_cast<uint8_t>(value));
+ }
+ else if (value <=(std::numeric_limits<uint16_t>::max)())
+ {
+ sink_.push_back(0xd9);
+ binary::native_to_big(static_cast<uint16_t>(value),
+ std::back_inserter(sink_));
+ }
+ else if (value <=(std::numeric_limits<uint32_t>::max)())
+ {
+ sink_.push_back(0xda);
+ binary::native_to_big(static_cast<uint32_t>(value),
+ std::back_inserter(sink_));
+ }
+ else
+ {
+ sink_.push_back(0xdb);
+ binary::native_to_big(static_cast<uint64_t>(value),
+ std::back_inserter(sink_));
+ }
+ }
+
+ void write_uint64_value(uint64_t value)
+ {
+ if (value <= 0x17)
+ {
+ sink_.push_back(static_cast<uint8_t>(value));
+ }
+ else if (value <=(std::numeric_limits<uint8_t>::max)())
+ {
+ sink_.push_back(static_cast<uint8_t>(0x18));
+ sink_.push_back(static_cast<uint8_t>(value));
+ }
+ else if (value <=(std::numeric_limits<uint16_t>::max)())
+ {
+ sink_.push_back(static_cast<uint8_t>(0x19));
+ binary::native_to_big(static_cast<uint16_t>(value),
+ std::back_inserter(sink_));
+ }
+ else if (value <=(std::numeric_limits<uint32_t>::max)())
+ {
+ sink_.push_back(static_cast<uint8_t>(0x1a));
+ binary::native_to_big(static_cast<uint32_t>(value),
+ std::back_inserter(sink_));
+ }
+ else if (value <=(std::numeric_limits<uint64_t>::max)())
+ {
+ sink_.push_back(static_cast<uint8_t>(0x1b));
+ binary::native_to_big(static_cast<uint64_t>(value),
+ std::back_inserter(sink_));
+ }
+ }
+
+ bool visit_bool(bool value, semantic_tag, const ser_context&, std::error_code&) override
+ {
+ if (value)
+ {
+ sink_.push_back(0xf5);
+ }
+ else
+ {
+ sink_.push_back(0xf4);
+ }
+
+ end_value();
+ return true;
+ }
+
+ bool visit_typed_array(const jsoncons::span<const uint8_t>& v,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ if (options_.use_typed_arrays())
+ {
+ switch (tag)
+ {
+ case semantic_tag::clamped:
+ write_tag(0x44);
+ break;
+ default:
+ write_tag(0x40);
+ break;
+ }
+ write_byte_string_value(byte_string_view(v));
+ return true;
+ }
+ else
+ {
+ bool more = this->begin_array(v.size(), semantic_tag::none, context, ec);
+ for (auto p = v.begin(); more && p != v.end(); ++p)
+ {
+ more = this->uint64_value(*p, tag, context, ec);
+ }
+ if (more)
+ {
+ more = this->end_array(context, ec);
+ }
+ return more;
+ }
+ }
+
+ bool visit_typed_array(const jsoncons::span<const uint16_t>& data,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ if (options_.use_typed_arrays())
+ {
+ write_typed_array_tag(std::integral_constant<bool, jsoncons::endian::native == jsoncons::endian::big>(),
+ uint16_t(),
+ tag);
+ std::vector<uint8_t> v(data.size()*sizeof(uint16_t));
+ std::memcpy(v.data(),data.data(),data.size()*sizeof(uint16_t));
+ write_byte_string_value(byte_string_view(v));
+ return true;
+ }
+ else
+ {
+ bool more = this->begin_array(data.size(), semantic_tag::none, context, ec);
+ for (auto p = data.begin(); more && p != data.end(); ++p)
+ {
+ more = this->uint64_value(*p, tag, context, ec);
+ }
+ if (more)
+ {
+ more = this->end_array(context, ec);
+ }
+ return more;
+ }
+ }
+
+ bool visit_typed_array(const jsoncons::span<const uint32_t>& data,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ if (options_.use_typed_arrays())
+ {
+ write_typed_array_tag(std::integral_constant<bool, jsoncons::endian::native == jsoncons::endian::big>(),
+ uint32_t(),
+ tag);
+ std::vector<uint8_t> v(data.size()*sizeof(uint32_t));
+ std::memcpy(v.data(), data.data(), data.size()*sizeof(uint32_t));
+ write_byte_string_value(byte_string_view(v));
+ return true;
+ }
+ else
+ {
+ bool more = this->begin_array(data.size(), semantic_tag::none, context, ec);
+ for (auto p = data.begin(); more && p != data.end(); ++p)
+ {
+ more = this->uint64_value(*p, semantic_tag::none, context, ec);
+ }
+ if (more)
+ {
+ more = this->end_array(context, ec);
+ }
+ return more;
+ }
+ }
+
+ bool visit_typed_array(const jsoncons::span<const uint64_t>& data,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ if (options_.use_typed_arrays())
+ {
+ write_typed_array_tag(std::integral_constant<bool, jsoncons::endian::native == jsoncons::endian::big>(),
+ uint64_t(),
+ tag);
+ std::vector<uint8_t> v(data.size()*sizeof(uint64_t));
+ std::memcpy(v.data(), data.data(), data.size()*sizeof(uint64_t));
+ write_byte_string_value(byte_string_view(v));
+ return true;
+ }
+ else
+ {
+ bool more = this->begin_array(data.size(), semantic_tag::none, context, ec);
+ for (auto p = data.begin(); more && p != data.end(); ++p)
+ {
+ more = this->uint64_value(*p,semantic_tag::none,context, ec);
+ }
+ if (more)
+ {
+ more = this->end_array(context, ec);
+ }
+ return more;
+ }
+ }
+
+ bool visit_typed_array(const jsoncons::span<const int8_t>& data,
+ semantic_tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ if (options_.use_typed_arrays())
+ {
+ write_tag(0x48);
+ std::vector<uint8_t> v(data.size()*sizeof(int8_t));
+ std::memcpy(v.data(), data.data(), data.size()*sizeof(int8_t));
+ write_byte_string_value(byte_string_view(v));
+ return true;
+ }
+ else
+ {
+ bool more = this->begin_array(data.size(), semantic_tag::none,context, ec);
+ for (auto p = data.begin(); more && p != data.end(); ++p)
+ {
+ more = this->int64_value(*p,semantic_tag::none,context, ec);
+ }
+ if (more)
+ {
+ more = this->end_array(context, ec);
+ }
+ return more;
+ }
+ }
+
+ bool visit_typed_array(const jsoncons::span<const int16_t>& data,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ if (options_.use_typed_arrays())
+ {
+ write_typed_array_tag(std::integral_constant<bool, jsoncons::endian::native == jsoncons::endian::big>(),
+ int16_t(),
+ tag);
+ std::vector<uint8_t> v(data.size()*sizeof(int16_t));
+ std::memcpy(v.data(), data.data(), data.size()*sizeof(int16_t));
+ write_byte_string_value(byte_string_view(v));
+ return true;
+ }
+ else
+ {
+ bool more = this->begin_array(data.size(), semantic_tag::none,context, ec);
+ for (auto p = data.begin(); more && p != data.end(); ++p)
+ {
+ more = this->int64_value(*p,semantic_tag::none,context, ec);
+ }
+ if (more)
+ {
+ more = this->end_array(context, ec);
+ }
+ return more;
+ }
+ }
+
+ bool visit_typed_array(const jsoncons::span<const int32_t>& data,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ if (options_.use_typed_arrays())
+ {
+ write_typed_array_tag(std::integral_constant<bool, jsoncons::endian::native == jsoncons::endian::big>(),
+ int32_t(),
+ tag);
+ std::vector<uint8_t> v(data.size()*sizeof(int32_t));
+ std::memcpy(v.data(), data.data(), data.size()*sizeof(int32_t));
+ write_byte_string_value(byte_string_view(v));
+ return true;
+ }
+ else
+ {
+ bool more = this->begin_array(data.size(), semantic_tag::none,context, ec);
+ for (auto p = data.begin(); more && p != data.end(); ++p)
+ {
+ more = this->int64_value(*p,semantic_tag::none,context, ec);
+ }
+ if (more)
+ {
+ more = this->end_array(context, ec);
+ }
+ return more;
+ }
+ }
+
+ bool visit_typed_array(const jsoncons::span<const int64_t>& data,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ if (options_.use_typed_arrays())
+ {
+ write_typed_array_tag(std::integral_constant<bool, jsoncons::endian::native == jsoncons::endian::big>(),
+ int64_t(),
+ tag);
+ std::vector<uint8_t> v(data.size()*sizeof(int64_t));
+ std::memcpy(v.data(), data.data(), data.size()*sizeof(int64_t));
+ write_byte_string_value(byte_string_view(v));
+ return true;
+ }
+ else
+ {
+ bool more = this->begin_array(data.size(), semantic_tag::none,context, ec);
+ for (auto p = data.begin(); more && p != data.end(); ++p)
+ {
+ more = this->int64_value(*p,semantic_tag::none,context, ec);
+ }
+ if (more)
+ {
+ more = this->end_array(context, ec);
+ }
+ return more;
+ }
+ }
+
+ bool visit_typed_array(half_arg_t, const jsoncons::span<const uint16_t>& data,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ if (options_.use_typed_arrays())
+ {
+ write_typed_array_tag(std::integral_constant<bool, jsoncons::endian::native == jsoncons::endian::big>(),
+ half_arg,
+ tag);
+ std::vector<uint8_t> v(data.size()*sizeof(uint16_t));
+ std::memcpy(v.data(),data.data(),data.size()*sizeof(uint16_t));
+ write_byte_string_value(byte_string_view(v));
+ return true;
+ }
+ else
+ {
+ bool more = this->begin_array(data.size(), semantic_tag::none, context, ec);
+ for (auto p = data.begin(); more && p != data.end(); ++p)
+ {
+ more = this->half_value(*p, tag, context, ec);
+ }
+ if (more)
+ {
+ more = this->end_array(context, ec);
+ }
+ return more;
+ }
+ }
+
+ bool visit_typed_array(const jsoncons::span<const float>& data,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ if (options_.use_typed_arrays())
+ {
+ write_typed_array_tag(std::integral_constant<bool, jsoncons::endian::native == jsoncons::endian::big>(),
+ float(),
+ tag);
+ std::vector<uint8_t> v(data.size()*sizeof(float));
+ std::memcpy(v.data(), data.data(), data.size()*sizeof(float));
+ write_byte_string_value(byte_string_view(v));
+ return true;
+ }
+ else
+ {
+ bool more = this->begin_array(data.size(), semantic_tag::none,context, ec);
+ for (auto p = data.begin(); more && p != data.end(); ++p)
+ {
+ more = this->double_value(*p,semantic_tag::none,context, ec);
+ }
+ if (more)
+ {
+ more = this->end_array(context, ec);
+ }
+ return more;
+ }
+ }
+
+ bool visit_typed_array(const jsoncons::span<const double>& data,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ if (options_.use_typed_arrays())
+ {
+ write_typed_array_tag(std::integral_constant<bool, jsoncons::endian::native == jsoncons::endian::big>(),
+ double(),
+ tag);
+ std::vector<uint8_t> v(data.size()*sizeof(double));
+ std::memcpy(v.data(), data.data(), data.size()*sizeof(double));
+ write_byte_string_value(byte_string_view(v));
+ return true;
+ }
+ else
+ {
+ bool more = this->begin_array(data.size(), semantic_tag::none,context, ec);
+ for (auto p = data.begin(); more && p != data.end(); ++p)
+ {
+ more = this->double_value(*p,semantic_tag::none,context, ec);
+ }
+ if (more)
+ {
+ more = this->end_array(context, ec);
+ }
+ return more;
+ }
+ }
+/*
+ bool visit_typed_array(const jsoncons::span<const float128_type>&,
+ semantic_tag,
+ const ser_context&,
+ std::error_code&) override
+ {
+ return true;
+ }
+*/
+ bool visit_begin_multi_dim(const jsoncons::span<const size_t>& shape,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ switch (tag)
+ {
+ case semantic_tag::multi_dim_column_major:
+ write_tag(1040);
+ break;
+ default:
+ write_tag(40);
+ break;
+ }
+ bool more = visit_begin_array(2, semantic_tag::none, context, ec);
+ if (more)
+ more = visit_begin_array(shape.size(), semantic_tag::none, context, ec);
+ for (auto it = shape.begin(); more && it != shape.end(); ++it)
+ {
+ more = visit_uint64(*it, semantic_tag::none, context, ec);
+ }
+ if (more)
+ {
+ more = visit_end_array(context, ec);
+ }
+ return more;
+ }
+
+ bool visit_end_multi_dim(const ser_context& context,
+ std::error_code& ec) override
+ {
+ bool more = visit_end_array(context, ec);
+ return more;
+ }
+
+ void write_typed_array_tag(std::true_type,
+ uint16_t,
+ semantic_tag)
+ {
+ write_tag(0x41); // big endian
+ }
+ void write_typed_array_tag(std::false_type,
+ uint16_t,
+ semantic_tag)
+ {
+ write_tag(0x45);
+ }
+
+ void write_typed_array_tag(std::true_type,
+ uint32_t,
+ semantic_tag)
+ {
+ write_tag(0x42); // big endian
+ }
+ void write_typed_array_tag(std::false_type,
+ uint32_t,
+ semantic_tag)
+ {
+ write_tag(0x46); // little endian
+ }
+
+ void write_typed_array_tag(std::true_type,
+ uint64_t,
+ semantic_tag)
+ {
+ write_tag(0x43); // big endian
+ }
+ void write_typed_array_tag(std::false_type,
+ uint64_t,
+ semantic_tag)
+ {
+ write_tag(0x47); // little endian
+ }
+
+ void write_typed_array_tag(std::true_type,
+ int16_t,
+ semantic_tag)
+ {
+ write_tag(0x49); // big endian
+ }
+ void write_typed_array_tag(std::false_type,
+ int16_t,
+ semantic_tag)
+ {
+ write_tag(0x4d); // little endian
+ }
+
+ void write_typed_array_tag(std::true_type,
+ int32_t,
+ semantic_tag)
+ {
+ write_tag(0x4a); // big endian
+ }
+ void write_typed_array_tag(std::false_type,
+ int32_t,
+ semantic_tag)
+ {
+ write_tag(0x4e); // little endian
+ }
+
+ void write_typed_array_tag(std::true_type,
+ int64_t,
+ semantic_tag)
+ {
+ write_tag(0x4b); // big endian
+ }
+ void write_typed_array_tag(std::false_type,
+ int64_t,
+ semantic_tag)
+ {
+ write_tag(0x4f); // little endian
+ }
+
+ void write_typed_array_tag(std::true_type,
+ half_arg_t,
+ semantic_tag)
+ {
+ write_tag(0x50);
+ }
+ void write_typed_array_tag(std::false_type,
+ half_arg_t,
+ semantic_tag)
+ {
+ write_tag(0x54);
+ }
+
+ void write_typed_array_tag(std::true_type,
+ float,
+ semantic_tag)
+ {
+ write_tag(0x51); // big endian
+ }
+ void write_typed_array_tag(std::false_type,
+ float,
+ semantic_tag)
+ {
+ write_tag(0x55); // little endian
+ }
+
+ void write_typed_array_tag(std::true_type,
+ double,
+ semantic_tag)
+ {
+ write_tag(0x52); // big endian
+ }
+ void write_typed_array_tag(std::false_type,
+ double,
+ semantic_tag)
+ {
+ write_tag(0x56); // little endian
+ }
+
+ void end_value()
+ {
+ if (!stack_.empty())
+ {
+ ++stack_.back().count_;
+ }
+ }
+};
+
+using cbor_stream_encoder = basic_cbor_encoder<jsoncons::binary_stream_sink>;
+using cbor_bytes_encoder = basic_cbor_encoder<jsoncons::bytes_sink<std::vector<uint8_t>>>;
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+JSONCONS_DEPRECATED_MSG("Instead, use cbor_bytes_encoder") typedef cbor_bytes_encoder cbor_bytes_serializer;
+
+template<class Sink=jsoncons::binary_stream_sink>
+using basic_cbor_serializer = basic_cbor_encoder<Sink>;
+
+JSONCONS_DEPRECATED_MSG("Instead, use cbor_stream_encoder") typedef cbor_stream_encoder cbor_encoder;
+JSONCONS_DEPRECATED_MSG("Instead, use cbor_stream_encoder") typedef cbor_stream_encoder cbor_serializer;
+JSONCONS_DEPRECATED_MSG("Instead, use cbor_bytes_encoder") typedef cbor_bytes_encoder cbor_buffer_serializer;
+#endif
+
+}}
+#endif
diff --git a/include/jsoncons_ext/cbor/cbor_error.hpp b/include/jsoncons_ext/cbor/cbor_error.hpp
new file mode 100644
index 0000000..a7a6626
--- /dev/null
+++ b/include/jsoncons_ext/cbor/cbor_error.hpp
@@ -0,0 +1,105 @@
+/// 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)
+
+// See https://github.com/danielaparker/jsoncons for latest version
+
+#ifndef JSONCONS_CBOR_CBOR_ERROR_HPP
+#define JSONCONS_CBOR_CBOR_ERROR_HPP
+
+#include <system_error>
+#include <jsoncons/config/jsoncons_config.hpp>
+#include <jsoncons/json_exception.hpp> // jsoncons::ser_error
+
+namespace jsoncons { namespace cbor {
+
+enum class cbor_errc
+{
+ success = 0,
+ unexpected_eof,
+ source_error,
+ invalid_decimal_fraction,
+ invalid_bigfloat,
+ invalid_utf8_text_string,
+ too_many_items,
+ too_few_items,
+ number_too_large,
+ stringref_too_large,
+ max_nesting_depth_exceeded,
+ unknown_type,
+ illegal_chunked_string
+};
+
+class cbor_error_category_impl
+ : public std::error_category
+{
+public:
+ const char* name() const noexcept override
+ {
+ return "jsoncons/cbor";
+ }
+ std::string message(int ev) const override
+ {
+ switch (static_cast<cbor_errc>(ev))
+ {
+ case cbor_errc::unexpected_eof:
+ return "Unexpected end of file";
+ case cbor_errc::source_error:
+ return "Source error";
+ case cbor_errc::invalid_decimal_fraction:
+ return "Invalid decimal fraction";
+ case cbor_errc::invalid_bigfloat:
+ return "Invalid bigfloat";
+ case cbor_errc::invalid_utf8_text_string:
+ return "Illegal UTF-8 encoding in text string";
+ case cbor_errc::too_many_items:
+ return "Too many items were added to a CBOR map or array of known length";
+ case cbor_errc::too_few_items:
+ return "Too few items were added to a CBOR map or array of known length";
+ case cbor_errc::number_too_large:
+ return "Number exceeds implementation limits";
+ case cbor_errc::stringref_too_large:
+ return "stringref exceeds stringref map size";
+ case cbor_errc::max_nesting_depth_exceeded:
+ return "Data item nesting exceeds limit in options";
+ case cbor_errc::unknown_type:
+ return "An unknown type was found in the stream";
+ case cbor_errc::illegal_chunked_string:
+ return "An illegal type was found while parsing an indefinite length string";
+ default:
+ return "Unknown CBOR parser error";
+ }
+ }
+};
+
+inline
+const std::error_category& cbor_error_category()
+{
+ static cbor_error_category_impl instance;
+ return instance;
+}
+
+inline
+std::error_code make_error_code(cbor_errc e)
+{
+ return std::error_code(static_cast<int>(e),cbor_error_category());
+}
+
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+
+JSONCONS_DEPRECATED_MSG("Instead, use ser_error") typedef ser_error cbor_error;
+JSONCONS_DEPRECATED_MSG("Instead, use ser_error") typedef ser_error cbor_decode_error;
+JSONCONS_DEPRECATED_MSG("Instead, use ser_error") typedef ser_error cbor_reader_errc;
+#endif
+
+}}
+
+namespace std {
+ template<>
+ struct is_error_code_enum<jsoncons::cbor::cbor_errc> : public true_type
+ {
+ };
+}
+
+#endif
diff --git a/include/jsoncons_ext/cbor/cbor_options.hpp b/include/jsoncons_ext/cbor/cbor_options.hpp
new file mode 100644
index 0000000..1de4a4e
--- /dev/null
+++ b/include/jsoncons_ext/cbor/cbor_options.hpp
@@ -0,0 +1,113 @@
+// Copyright 2019 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_CBOR_CBOR_OPTIONS_HPP
+#define JSONCONS_CBOR_CBOR_OPTIONS_HPP
+
+#include <string>
+#include <limits> // std::numeric_limits
+#include <cwchar>
+#include <jsoncons/json_exception.hpp>
+#include <jsoncons_ext/cbor/cbor_detail.hpp>
+
+namespace jsoncons { namespace cbor {
+
+class cbor_options;
+
+class cbor_options_common
+{
+ friend class cbor_options;
+
+ int max_nesting_depth_;
+protected:
+ 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
+ {
+ return max_nesting_depth_;
+ }
+};
+
+class cbor_decode_options : public virtual cbor_options_common
+{
+ friend class cbor_options;
+public:
+ cbor_decode_options()
+ {
+ }
+};
+
+class cbor_encode_options : public virtual cbor_options_common
+{
+ friend class cbor_options;
+
+ bool use_stringref_;
+ bool use_typed_arrays_;
+public:
+ cbor_encode_options()
+ : use_stringref_(false),
+ use_typed_arrays_(false)
+ {
+ }
+
+ bool pack_strings() const
+ {
+ return use_stringref_;
+ }
+
+ bool use_typed_arrays() const
+ {
+ return use_typed_arrays_;
+ }
+};
+
+class cbor_options final : public cbor_decode_options, public cbor_encode_options
+{
+public:
+ using cbor_options_common::max_nesting_depth;
+ using cbor_encode_options::pack_strings;
+ using cbor_encode_options::use_typed_arrays;
+
+ cbor_options& max_nesting_depth(int value)
+ {
+ this->max_nesting_depth_ = value;
+ return *this;
+ }
+
+ cbor_options& pack_strings(bool value)
+ {
+ this->use_stringref_ = value;
+ return *this;
+ }
+
+ cbor_options& use_typed_arrays(bool value)
+ {
+ this->use_typed_arrays_ = value;
+ return *this;
+ }
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+ JSONCONS_DEPRECATED_MSG("Instead, use use_typed_arrays(bool)")
+ cbor_options& enable_typed_arrays(bool value)
+ {
+ this->use_typed_arrays_ = value;
+ return *this;
+ }
+#endif
+};
+
+}}
+#endif
diff --git a/include/jsoncons_ext/cbor/cbor_parser.hpp b/include/jsoncons_ext/cbor/cbor_parser.hpp
new file mode 100644
index 0000000..f3d03bb
--- /dev/null
+++ b/include/jsoncons_ext/cbor/cbor_parser.hpp
@@ -0,0 +1,1942 @@
+// Copyright 2017 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_CBOR_CBOR_PARSER_HPP
+#define JSONCONS_CBOR_CBOR_PARSER_HPP
+
+#include <string>
+#include <vector>
+#include <memory>
+#include <utility> // std::move
+#include <bitset> // std::bitset
+#include <jsoncons/json.hpp>
+#include <jsoncons/source.hpp>
+#include <jsoncons/json_visitor.hpp>
+#include <jsoncons/config/jsoncons_config.hpp>
+#include <jsoncons_ext/cbor/cbor_error.hpp>
+#include <jsoncons_ext/cbor/cbor_detail.hpp>
+#include <jsoncons_ext/cbor/cbor_options.hpp>
+#include <jsoncons/json_visitor2.hpp>
+
+namespace jsoncons { namespace cbor {
+
+enum class parse_mode {root,accept,array,indefinite_array,map_key,map_value,indefinite_map_key,indefinite_map_value,multi_dim};
+
+struct mapped_string
+{
+ jsoncons::cbor::detail::cbor_major_type type;
+ std::string s;
+ std::vector<uint8_t> bytes;
+
+ mapped_string(const std::string& s)
+ : type(jsoncons::cbor::detail::cbor_major_type::text_string), s(s)
+ {
+ }
+
+ mapped_string(std::string&& s)
+ : type(jsoncons::cbor::detail::cbor_major_type::text_string), s(std::move(s))
+ {
+ }
+
+ mapped_string(const std::vector<uint8_t>& bytes)
+ : type(jsoncons::cbor::detail::cbor_major_type::byte_string), bytes(bytes)
+ {
+ }
+
+ mapped_string(std::vector<uint8_t>&& bytes)
+ : type(jsoncons::cbor::detail::cbor_major_type::byte_string), bytes(std::move(bytes))
+ {
+ }
+
+ mapped_string(const mapped_string&) = default;
+
+ mapped_string(mapped_string&&) = default;
+
+ mapped_string& operator=(const mapped_string&) = default;
+
+ mapped_string& operator=(mapped_string&&) = default;
+};
+
+struct parse_state
+{
+ parse_mode mode;
+ std::size_t length;
+ std::size_t index;
+ bool pop_stringref_map_stack;
+
+ parse_state(parse_mode mode, std::size_t length, bool pop_stringref_map_stack = false) noexcept
+ : mode(mode), length(length), index(0), pop_stringref_map_stack(pop_stringref_map_stack)
+ {
+ }
+
+ parse_state(const parse_state&) = default;
+ parse_state(parse_state&&) = default;
+};
+
+template <class Source,class Allocator=std::allocator<char>>
+class basic_cbor_parser : public ser_context
+{
+ using char_type = char;
+ using char_traits_type = std::char_traits<char>;
+ using allocator_type = Allocator;
+ using char_allocator_type = typename std::allocator_traits<allocator_type>:: template rebind_alloc<char_type>;
+ using byte_allocator_type = typename std::allocator_traits<allocator_type>:: template rebind_alloc<uint8_t>;
+ using tag_allocator_type = typename std::allocator_traits<allocator_type>:: template rebind_alloc<uint64_t>;
+ using parse_state_allocator_type = typename std::allocator_traits<allocator_type>:: template rebind_alloc<parse_state>;
+ using stringref_map = std::vector<mapped_string>;
+ using stringref_map_allocator_type = typename std::allocator_traits<allocator_type>:: template rebind_alloc<stringref_map>;
+
+ using string_type = std::basic_string<char_type,char_traits_type,char_allocator_type>;
+
+ enum {stringref_tag, // 25
+ stringref_namespace_tag, // 256
+ item_tag,
+ num_of_tags};
+
+ std::bitset<num_of_tags> other_tags_;
+
+ allocator_type alloc_;
+ Source source_;
+ cbor_decode_options options_;
+
+ bool more_;
+ bool done_;
+ string_type text_buffer_;
+ std::vector<uint8_t,byte_allocator_type> bytes_buffer_;
+ uint64_t item_tag_;
+ std::vector<parse_state,parse_state_allocator_type> state_stack_;
+ std::vector<uint8_t,byte_allocator_type> typed_array_;
+ std::vector<std::size_t> shape_;
+ std::size_t index_; // TODO: Never used!
+ std::vector<stringref_map,stringref_map_allocator_type> stringref_map_stack_;
+ int nesting_depth_;
+
+ struct read_byte_string_from_buffer
+ {
+ byte_string_view bytes;
+
+ read_byte_string_from_buffer(const byte_string_view& b)
+ : bytes(b)
+ {
+ }
+ template <class Container>
+ void operator()(Container& c, std::error_code&)
+ {
+ c.clear();
+ c.reserve(bytes.size());
+ for (auto b : bytes)
+ {
+ c.push_back(b);
+ }
+ }
+ };
+
+ struct read_byte_string_from_source
+ {
+ basic_cbor_parser<Source,Allocator>* source;
+
+ read_byte_string_from_source(basic_cbor_parser<Source,Allocator>* source)
+ : source(source)
+ {
+ }
+ template <class Container>
+ void operator()(Container& c, std::error_code& ec)
+ {
+ source->read_byte_string(c,ec);
+ }
+ };
+
+public:
+ template <class Sourceable>
+ basic_cbor_parser(Sourceable&& source,
+ const cbor_decode_options& options = cbor_decode_options(),
+ const Allocator alloc = Allocator())
+ : alloc_(alloc),
+ source_(std::forward<Sourceable>(source)),
+ options_(options),
+ more_(true),
+ done_(false),
+ text_buffer_(alloc),
+ bytes_buffer_(alloc),
+ item_tag_(0),
+ state_stack_(alloc),
+ typed_array_(alloc),
+ index_(0),
+ stringref_map_stack_(alloc),
+ nesting_depth_(0)
+ {
+ state_stack_.emplace_back(parse_mode::root,0);
+ }
+
+ void restart()
+ {
+ more_ = true;
+ }
+
+ void reset()
+ {
+ more_ = true;
+ done_ = false;
+ text_buffer_.clear();
+ bytes_buffer_.clear();
+ item_tag_ = 0;
+ state_stack_.clear();
+ state_stack_.emplace_back(parse_mode::root,0);
+ typed_array_.clear();
+ stringref_map_stack_.clear();
+ nesting_depth_ = 0;
+ }
+
+ template <class Sourceable>
+ void reset(Sourceable&& source)
+ {
+ source_ = std::forward<Sourceable>(source);
+ reset();
+ }
+
+ bool done() const
+ {
+ return done_;
+ }
+
+ bool stopped() const
+ {
+ return !more_;
+ }
+
+ std::size_t line() const override
+ {
+ return 0;
+ }
+
+ std::size_t column() const override
+ {
+ return source_.position();
+ }
+
+ void parse(json_visitor2& visitor, std::error_code& ec)
+ {
+ while (!done_ && more_)
+ {
+ switch (state_stack_.back().mode)
+ {
+ case parse_mode::multi_dim:
+ {
+ if (state_stack_.back().index == 0)
+ {
+ ++state_stack_.back().index;
+ read_item(visitor, ec);
+ }
+ else
+ {
+ produce_end_multi_dim(visitor, ec);
+ }
+ break;
+ }
+ case parse_mode::array:
+ {
+ if (state_stack_.back().index < state_stack_.back().length)
+ {
+ ++state_stack_.back().index;
+ read_item(visitor, ec);
+ }
+ else
+ {
+ end_array(visitor, ec);
+ }
+ break;
+ }
+ case parse_mode::indefinite_array:
+ {
+ auto c = source_.peek();
+ if (c.eof)
+ {
+ ec = cbor_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ if (c.value == 0xff)
+ {
+ source_.ignore(1);
+ end_array(visitor, ec);
+ }
+ else
+ {
+ read_item(visitor, ec);
+ }
+ break;
+ }
+ case parse_mode::map_key:
+ {
+ if (state_stack_.back().index < state_stack_.back().length)
+ {
+ ++state_stack_.back().index;
+ state_stack_.back().mode = parse_mode::map_value;
+ read_item(visitor, ec);
+ }
+ else
+ {
+ end_object(visitor, ec);
+ }
+ break;
+ }
+ case parse_mode::map_value:
+ {
+ state_stack_.back().mode = parse_mode::map_key;
+ read_item(visitor, ec);
+ break;
+ }
+ case parse_mode::indefinite_map_key:
+ {
+ auto c = source_.peek();
+ if (c.eof)
+ {
+ ec = cbor_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ if (c.value == 0xff)
+ {
+ source_.ignore(1);
+ end_object(visitor, ec);
+ }
+ else
+ {
+ state_stack_.back().mode = parse_mode::indefinite_map_value;
+ read_item(visitor, ec);
+ }
+ break;
+ }
+ case parse_mode::indefinite_map_value:
+ {
+ state_stack_.back().mode = parse_mode::indefinite_map_key;
+ read_item(visitor, ec);
+ break;
+ }
+ case parse_mode::root:
+ {
+ state_stack_.back().mode = parse_mode::accept;
+ read_item(visitor, ec);
+ break;
+ }
+ case parse_mode::accept:
+ {
+ JSONCONS_ASSERT(state_stack_.size() == 1);
+ state_stack_.clear();
+ more_ = false;
+ done_ = true;
+ visitor.flush();
+ break;
+ }
+ }
+ }
+ }
+private:
+ void read_item(json_visitor2& visitor, std::error_code& ec)
+ {
+ read_tags(ec);
+ if (!more_)
+ {
+ return;
+ }
+ auto c = source_.peek();
+ if (c.eof)
+ {
+ ec = cbor_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ jsoncons::cbor::detail::cbor_major_type major_type = get_major_type(c.value);
+ uint8_t info = get_additional_information_value(c.value);
+
+ switch (major_type)
+ {
+ case jsoncons::cbor::detail::cbor_major_type::unsigned_integer:
+ {
+ uint64_t val = get_uint64_value(ec);
+ if (ec)
+ {
+ return;
+ }
+ if (!stringref_map_stack_.empty() && other_tags_[stringref_tag])
+ {
+ other_tags_[stringref_tag] = false;
+ if (val >= stringref_map_stack_.back().size())
+ {
+ ec = cbor_errc::stringref_too_large;
+ more_ = false;
+ return;
+ }
+ stringref_map::size_type index = (stringref_map::size_type)val;
+ if (index != val)
+ {
+ ec = cbor_errc::number_too_large;
+ more_ = false;
+ return;
+ }
+ auto& str = stringref_map_stack_.back().at(index);
+ switch (str.type)
+ {
+ case jsoncons::cbor::detail::cbor_major_type::text_string:
+ {
+ handle_string(visitor, jsoncons::basic_string_view<char>(str.s.data(),str.s.length()),ec);
+ if (ec)
+ {
+ return;
+ }
+ break;
+ }
+ case jsoncons::cbor::detail::cbor_major_type::byte_string:
+ {
+ read_byte_string_from_buffer read(byte_string_view(str.bytes));
+ write_byte_string(read, visitor, ec);
+ if (ec)
+ {
+ return;
+ }
+ break;
+ }
+ default:
+ JSONCONS_UNREACHABLE();
+ break;
+ }
+ }
+ else
+ {
+ semantic_tag tag = semantic_tag::none;
+ if (other_tags_[item_tag])
+ {
+ if (item_tag_ == 1)
+ {
+ tag = semantic_tag::epoch_second;
+ }
+ other_tags_[item_tag] = false;
+ }
+ more_ = visitor.uint64_value(val, tag, *this, ec);
+ }
+ break;
+ }
+ case jsoncons::cbor::detail::cbor_major_type::negative_integer:
+ {
+ int64_t val = get_int64_value(ec);
+ if (ec)
+ {
+ return;
+ }
+ semantic_tag tag = semantic_tag::none;
+ if (other_tags_[item_tag])
+ {
+ if (item_tag_ == 1)
+ {
+ tag = semantic_tag::epoch_second;
+ }
+ other_tags_[item_tag] = false;
+ }
+ more_ = visitor.int64_value(val, tag, *this, ec);
+ break;
+ }
+ case jsoncons::cbor::detail::cbor_major_type::byte_string:
+ {
+ read_byte_string_from_source read(this);
+ write_byte_string(read, visitor, ec);
+ if (ec)
+ {
+ return;
+ }
+ break;
+ }
+ case jsoncons::cbor::detail::cbor_major_type::text_string:
+ {
+ text_buffer_.clear();
+ read_text_string(text_buffer_, ec);
+ if (ec)
+ {
+ return;
+ }
+ auto result = unicode_traits::validate(text_buffer_.data(),text_buffer_.size());
+ if (result.ec != unicode_traits::conv_errc())
+ {
+ ec = cbor_errc::invalid_utf8_text_string;
+ more_ = false;
+ return;
+ }
+ handle_string(visitor, jsoncons::basic_string_view<char>(text_buffer_.data(),text_buffer_.length()),ec);
+ if (ec)
+ {
+ return;
+ }
+ break;
+ }
+ case jsoncons::cbor::detail::cbor_major_type::semantic_tag:
+ {
+ JSONCONS_UNREACHABLE();
+ break;
+ }
+ case jsoncons::cbor::detail::cbor_major_type::simple:
+ {
+ switch (info)
+ {
+ case 0x14:
+ more_ = visitor.bool_value(false, semantic_tag::none, *this, ec);
+ source_.ignore(1);
+ break;
+ case 0x15:
+ more_ = visitor.bool_value(true, semantic_tag::none, *this, ec);
+ source_.ignore(1);
+ break;
+ case 0x16:
+ more_ = visitor.null_value(semantic_tag::none, *this, ec);
+ source_.ignore(1);
+ break;
+ case 0x17:
+ more_ = visitor.null_value(semantic_tag::undefined, *this, ec);
+ source_.ignore(1);
+ break;
+ case 0x19: // Half-Precision Float (two-byte IEEE 754)
+ {
+ uint64_t val = get_uint64_value(ec);
+ if (ec)
+ {
+ return;
+ }
+ more_ = visitor.half_value(static_cast<uint16_t>(val), semantic_tag::none, *this, ec);
+ break;
+ }
+ case 0x1a: // Single-Precision Float (four-byte IEEE 754)
+ case 0x1b: // Double-Precision Float (eight-byte IEEE 754)
+ {
+ double val = get_double(ec);
+ if (ec)
+ {
+ return;
+ }
+ semantic_tag tag = semantic_tag::none;
+ if (other_tags_[item_tag])
+ {
+ if (item_tag_ == 1)
+ {
+ tag = semantic_tag::epoch_second;
+ }
+ other_tags_[item_tag] = false;
+ }
+ more_ = visitor.double_value(val, tag, *this, ec);
+ break;
+ }
+ default:
+ {
+ ec = cbor_errc::unknown_type;
+ more_ = false;
+ return;
+ }
+ }
+ break;
+ }
+ case jsoncons::cbor::detail::cbor_major_type::array:
+ {
+ if (other_tags_[item_tag])
+ {
+ switch (item_tag_)
+ {
+ case 0x04:
+ text_buffer_.clear();
+ read_decimal_fraction(text_buffer_, ec);
+ if (ec)
+ {
+ return;
+ }
+ more_ = visitor.string_value(text_buffer_, semantic_tag::bigdec, *this, ec);
+ break;
+ case 0x05:
+ text_buffer_.clear();
+ read_bigfloat(text_buffer_, ec);
+ if (ec)
+ {
+ return;
+ }
+ more_ = visitor.string_value(text_buffer_, semantic_tag::bigfloat, *this, ec);
+ break;
+ case 40: // row major storage
+ produce_begin_multi_dim(visitor, semantic_tag::multi_dim_row_major, ec);
+ break;
+ case 1040: // column major storage
+ produce_begin_multi_dim(visitor, semantic_tag::multi_dim_column_major, ec);
+ break;
+ default:
+ begin_array(visitor, info, ec);
+ break;
+ }
+ other_tags_[item_tag] = false;
+ }
+ else
+ {
+ begin_array(visitor, info, ec);
+ }
+ break;
+ }
+ case jsoncons::cbor::detail::cbor_major_type::map:
+ {
+ begin_object(visitor, info, ec);
+ break;
+ }
+ default:
+ break;
+ }
+ other_tags_[item_tag] = false;
+ }
+
+ void begin_array(json_visitor2& visitor, uint8_t info, std::error_code& ec)
+ {
+ if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth()))
+ {
+ ec = cbor_errc::max_nesting_depth_exceeded;
+ more_ = false;
+ return;
+ }
+ semantic_tag tag = semantic_tag::none;
+ bool pop_stringref_map_stack = false;
+ if (other_tags_[stringref_namespace_tag])
+ {
+ stringref_map_stack_.emplace_back(alloc_);
+ other_tags_[stringref_namespace_tag] = false;
+ pop_stringref_map_stack = true;
+ }
+ switch (info)
+ {
+ case jsoncons::cbor::detail::additional_info::indefinite_length:
+ {
+ state_stack_.emplace_back(parse_mode::indefinite_array,0,pop_stringref_map_stack);
+ more_ = visitor.begin_array(tag, *this, ec);
+ source_.ignore(1);
+ break;
+ }
+ default: // definite length
+ {
+ std::size_t len = get_size(ec);
+ if (!more_)
+ {
+ return;
+ }
+ state_stack_.emplace_back(parse_mode::array,len,pop_stringref_map_stack);
+ more_ = visitor.begin_array(len, tag, *this, ec);
+ break;
+ }
+ }
+ }
+
+ void end_array(json_visitor2& visitor, std::error_code& ec)
+ {
+ --nesting_depth_;
+
+ more_ = visitor.end_array(*this, ec);
+ if (state_stack_.back().pop_stringref_map_stack)
+ {
+ stringref_map_stack_.pop_back();
+ }
+ state_stack_.pop_back();
+ }
+
+ void begin_object(json_visitor2& visitor, uint8_t info, std::error_code& ec)
+ {
+ if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth()))
+ {
+ ec = cbor_errc::max_nesting_depth_exceeded;
+ more_ = false;
+ return;
+ }
+ bool pop_stringref_map_stack = false;
+ if (other_tags_[stringref_namespace_tag])
+ {
+ stringref_map_stack_.emplace_back(alloc_);
+ other_tags_[stringref_namespace_tag] = false;
+ pop_stringref_map_stack = true;
+ }
+ switch (info)
+ {
+ case jsoncons::cbor::detail::additional_info::indefinite_length:
+ {
+ state_stack_.emplace_back(parse_mode::indefinite_map_key,0,pop_stringref_map_stack);
+ more_ = visitor.begin_object(semantic_tag::none, *this, ec);
+ source_.ignore(1);
+ break;
+ }
+ default: // definite_length
+ {
+ std::size_t len = get_size(ec);
+ if (!more_)
+ {
+ return;
+ }
+ state_stack_.emplace_back(parse_mode::map_key,len,pop_stringref_map_stack);
+ more_ = visitor.begin_object(len, semantic_tag::none, *this, ec);
+ break;
+ }
+ }
+ }
+
+ void end_object(json_visitor2& visitor, std::error_code& ec)
+ {
+ --nesting_depth_;
+ more_ = visitor.end_object(*this, ec);
+ if (state_stack_.back().pop_stringref_map_stack)
+ {
+ stringref_map_stack_.pop_back();
+ }
+ state_stack_.pop_back();
+ }
+
+ void read_text_string(string_type& s, std::error_code& ec)
+ {
+ auto c = source_.peek();
+ if (c.eof)
+ {
+ ec = cbor_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ jsoncons::cbor::detail::cbor_major_type major_type = get_major_type(c.value);
+ uint8_t info = get_additional_information_value(c.value);
+
+ JSONCONS_ASSERT(major_type == jsoncons::cbor::detail::cbor_major_type::text_string);
+ auto func = [&s](Source& source, std::size_t length, std::error_code& ec) -> bool
+ {
+ if (source_reader<Source>::read(source, s, length) != length)
+ {
+ ec = cbor_errc::unexpected_eof;
+ return false;
+ }
+ return true;
+ };
+ iterate_string_chunks(func, major_type, ec);
+ if (!stringref_map_stack_.empty() &&
+ info != jsoncons::cbor::detail::additional_info::indefinite_length &&
+ s.length() >= jsoncons::cbor::detail::min_length_for_stringref(stringref_map_stack_.back().size()))
+ {
+ stringref_map_stack_.back().emplace_back(s);
+ }
+ }
+
+ std::size_t get_size(std::error_code& ec)
+ {
+ uint64_t u = get_uint64_value(ec);
+ if (!more_)
+ {
+ return 0;
+ }
+ std::size_t len = static_cast<std::size_t>(u);
+ if (len != u)
+ {
+ ec = cbor_errc::number_too_large;
+ more_ = false;
+ }
+ return len;
+ }
+
+ bool read_byte_string(std::vector<uint8_t,byte_allocator_type>& v, std::error_code& ec)
+ {
+ bool more = true;
+ v.clear();
+ auto c = source_.peek();
+ if (c.eof)
+ {
+ ec = cbor_errc::unexpected_eof;
+ more = false;
+ return more;
+ }
+ jsoncons::cbor::detail::cbor_major_type major_type = get_major_type(c.value);
+ uint8_t info = get_additional_information_value(c.value);
+
+ JSONCONS_ASSERT(major_type == jsoncons::cbor::detail::cbor_major_type::byte_string);
+
+ switch(info)
+ {
+ case jsoncons::cbor::detail::additional_info::indefinite_length:
+ {
+ auto func = [&v,&more](Source& source, std::size_t length, std::error_code& ec) -> bool
+ {
+ if (source_reader<Source>::read(source, v, length) != length)
+ {
+ ec = cbor_errc::unexpected_eof;
+ more = false;
+ return more;
+ }
+ return true;
+ };
+ iterate_string_chunks(func, major_type, ec);
+ break;
+ }
+ default:
+ {
+ std::size_t length = get_size(ec);
+ if (ec)
+ {
+ more = false;
+ return more;
+ }
+ if (source_reader<Source>::read(source_, v, length) != length)
+ {
+ ec = cbor_errc::unexpected_eof;
+ more = false;
+ return more;
+ }
+ if (!stringref_map_stack_.empty() &&
+ v.size() >= jsoncons::cbor::detail::min_length_for_stringref(stringref_map_stack_.back().size()))
+ {
+ stringref_map_stack_.back().emplace_back(v);
+ }
+ break;
+ }
+ }
+ return more;
+ }
+
+ template <class Function>
+ void iterate_string_chunks(Function& func, jsoncons::cbor::detail::cbor_major_type type, std::error_code& ec)
+ {
+ int nesting_level = 0;
+
+ bool done = false;
+ while (!done)
+ {
+ auto c = source_.peek();
+ if (c.eof)
+ {
+ ec = cbor_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ if (nesting_level > 0 && c.value == 0xff)
+ {
+ --nesting_level;
+ if (nesting_level == 0)
+ {
+ done = true;
+ }
+ source_.ignore(1);
+ continue;
+ }
+
+ jsoncons::cbor::detail::cbor_major_type major_type = get_major_type(c.value);
+ if (major_type != type)
+ {
+ ec = cbor_errc::illegal_chunked_string;
+ more_ = false;
+ return;
+ }
+ uint8_t info = get_additional_information_value(c.value);
+
+ switch (info)
+ {
+ case jsoncons::cbor::detail::additional_info::indefinite_length:
+ {
+ ++nesting_level;
+ source_.ignore(1);
+ break;
+ }
+ default: // definite length
+ {
+ std::size_t length = get_size(ec);
+ if (!more_)
+ {
+ return;
+ }
+ more_ = func(source_, length, ec);
+ if (!more_)
+ {
+ return;
+ }
+ if (nesting_level == 0)
+ {
+ done = true;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ uint64_t get_uint64_value(std::error_code& ec)
+ {
+ uint64_t val = 0;
+
+ uint8_t initial_b;
+ if (source_.read(&initial_b, 1) == 0)
+ {
+ ec = cbor_errc::unexpected_eof;
+ more_ = false;
+ return 0;
+ }
+ uint8_t info = get_additional_information_value(initial_b);
+ switch (info)
+ {
+ case JSONCONS_CBOR_0x00_0x17: // Integer 0x00..0x17 (0..23)
+ {
+ val = info;
+ break;
+ }
+
+ case 0x18: // Unsigned integer (one-byte uint8_t follows)
+ {
+ uint8_t b;
+ if (source_.read(&b, 1) == 0)
+ {
+ ec = cbor_errc::unexpected_eof;
+ more_ = false;
+ return val;
+ }
+ val = b;
+ break;
+ }
+
+ case 0x19: // Unsigned integer (two-byte uint16_t follows)
+ {
+ uint8_t buf[sizeof(uint16_t)];
+ source_.read(buf, sizeof(uint16_t));
+ val = binary::big_to_native<uint16_t>(buf, sizeof(buf));
+ break;
+ }
+
+ case 0x1a: // Unsigned integer (four-byte uint32_t follows)
+ {
+ uint8_t buf[sizeof(uint32_t)];
+ source_.read(buf, sizeof(uint32_t));
+ val = binary::big_to_native<uint32_t>(buf, sizeof(buf));
+ break;
+ }
+
+ case 0x1b: // Unsigned integer (eight-byte uint64_t follows)
+ {
+ uint8_t buf[sizeof(uint64_t)];
+ source_.read(buf, sizeof(uint64_t));
+ val = binary::big_to_native<uint64_t>(buf, sizeof(buf));
+ break;
+ }
+ default:
+ break;
+ }
+ return val;
+ }
+
+ int64_t get_int64_value(std::error_code& ec)
+ {
+ int64_t val = 0;
+
+ auto ch = source_.peek();
+ if (ch.eof)
+ {
+ ec = cbor_errc::unexpected_eof;
+ more_ = false;
+ return val;
+ }
+
+ jsoncons::cbor::detail::cbor_major_type major_type = get_major_type(ch.value);
+ uint8_t info = get_additional_information_value(ch.value);
+ switch (major_type)
+ {
+ case jsoncons::cbor::detail::cbor_major_type::negative_integer:
+ source_.ignore(1);
+ switch (info)
+ {
+ case JSONCONS_CBOR_0x00_0x17: // 0x00..0x17 (0..23)
+ {
+ val = static_cast<int8_t>(- 1 - info);
+ break;
+ }
+ case 0x18: // Negative integer (one-byte uint8_t follows)
+ {
+ uint8_t b;
+ if (source_.read(&b, 1) == 0)
+ {
+ ec = cbor_errc::unexpected_eof;
+ more_ = false;
+ return val;
+ }
+ val = static_cast<int64_t>(-1) - static_cast<int64_t>(b);
+ break;
+ }
+
+ case 0x19: // Negative integer -1-n (two-byte uint16_t follows)
+ {
+ uint8_t buf[sizeof(uint16_t)];
+ if (source_.read(buf, sizeof(uint16_t)) != sizeof(uint16_t))
+ {
+ ec = cbor_errc::unexpected_eof;
+ more_ = false;
+ return val;
+ }
+ auto x = binary::big_to_native<uint16_t>(buf, sizeof(buf));
+ val = static_cast<int64_t>(-1)- x;
+ break;
+ }
+
+ case 0x1a: // Negative integer -1-n (four-byte uint32_t follows)
+ {
+ uint8_t buf[sizeof(uint32_t)];
+ if (source_.read(buf, sizeof(uint32_t)) != sizeof(uint32_t))
+ {
+ ec = cbor_errc::unexpected_eof;
+ more_ = false;
+ return val;
+ }
+ auto x = binary::big_to_native<uint32_t>(buf, sizeof(buf));
+ val = static_cast<int64_t>(-1)- x;
+ break;
+ }
+
+ case 0x1b: // Negative integer -1-n (eight-byte uint64_t follows)
+ {
+ uint8_t buf[sizeof(uint64_t)];
+ if (source_.read(buf, sizeof(uint64_t)) != sizeof(uint64_t))
+ {
+ ec = cbor_errc::unexpected_eof;
+ more_ = false;
+ return val;
+ }
+ auto x = binary::big_to_native<uint64_t>(buf, sizeof(buf));
+ val = static_cast<int64_t>(-1)- static_cast<int64_t>(x);
+ break;
+ }
+ }
+ break;
+
+ case jsoncons::cbor::detail::cbor_major_type::unsigned_integer:
+ {
+ uint64_t x = get_uint64_value(ec);
+ if (ec)
+ {
+ return 0;
+ }
+ if (x <= static_cast<uint64_t>((std::numeric_limits<int64_t>::max)()))
+ {
+ val = x;
+ }
+ else
+ {
+ // error;
+ }
+
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return val;
+ }
+
+ double get_double(std::error_code& ec)
+ {
+ double val = 0;
+
+ uint8_t b;
+ if (source_.read(&b, 1) == 0)
+ {
+ ec = cbor_errc::unexpected_eof;
+ more_ = false;
+ return 0;
+ }
+ uint8_t info = get_additional_information_value(b);
+ switch (info)
+ {
+ case 0x1a: // Single-Precision Float (four-byte IEEE 754)
+ {
+ uint8_t buf[sizeof(float)];
+ if (source_.read(buf, sizeof(float)) !=sizeof(float))
+ {
+ ec = cbor_errc::unexpected_eof;
+ more_ = false;
+ return 0;
+ }
+ val = binary::big_to_native<float>(buf, sizeof(buf));
+ break;
+ }
+
+ case 0x1b: // Double-Precision Float (eight-byte IEEE 754)
+ {
+ uint8_t buf[sizeof(double)];
+ if (source_.read(buf, sizeof(double)) != sizeof(double))
+ {
+ ec = cbor_errc::unexpected_eof;
+ more_ = false;
+ return 0;
+ }
+ val = binary::big_to_native<double>(buf, sizeof(buf));
+ break;
+ }
+ default:
+ break;
+ }
+
+ return val;
+ }
+
+ void read_decimal_fraction(string_type& result, std::error_code& ec)
+ {
+ std::size_t size = get_size(ec);
+ if (!more_)
+ {
+ return;
+ }
+ if (size != 2)
+ {
+ ec = cbor_errc::invalid_decimal_fraction;
+ more_ = false;
+ return;
+ }
+
+ auto c = source_.peek();
+ if (c.eof)
+ {
+ ec = cbor_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ int64_t exponent = 0;
+ switch (get_major_type(c.value))
+ {
+ case jsoncons::cbor::detail::cbor_major_type::unsigned_integer:
+ {
+ exponent = get_uint64_value(ec);
+ if (ec)
+ {
+ return;
+ }
+ break;
+ }
+ case jsoncons::cbor::detail::cbor_major_type::negative_integer:
+ {
+ exponent = get_int64_value(ec);
+ if (ec)
+ {
+ return;
+ }
+ break;
+ }
+ default:
+ {
+ ec = cbor_errc::invalid_decimal_fraction;
+ more_ = false;
+ return;
+ }
+ }
+
+ string_type s;
+
+ c = source_.peek();
+ if (c.eof)
+ {
+ ec = cbor_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+
+ switch (get_major_type(c.value))
+ {
+ case jsoncons::cbor::detail::cbor_major_type::unsigned_integer:
+ {
+ uint64_t val = get_uint64_value(ec);
+ if (ec)
+ {
+ return;
+ }
+ jsoncons::detail::from_integer(val, s);
+ break;
+ }
+ case jsoncons::cbor::detail::cbor_major_type::negative_integer:
+ {
+ int64_t val = get_int64_value(ec);
+ if (ec)
+ {
+ return;
+ }
+ jsoncons::detail::from_integer(val, s);
+ break;
+ }
+ case jsoncons::cbor::detail::cbor_major_type::semantic_tag:
+ {
+ uint8_t b;
+ if (source_.read(&b, 1) == 0)
+ {
+ ec = cbor_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ uint8_t tag = get_additional_information_value(b);
+ c = source_.peek();
+ if (c.eof)
+ {
+ ec = cbor_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+
+ if (get_major_type(c.value) == jsoncons::cbor::detail::cbor_major_type::byte_string)
+ {
+ bytes_buffer_.clear();
+ read_byte_string(bytes_buffer_, ec);
+ if (ec)
+ {
+ more_ = false;
+ return;
+ }
+ if (tag == 2)
+ {
+ bigint n = bigint::from_bytes_be(1, bytes_buffer_.data(), bytes_buffer_.size());
+ n.write_string(s);
+ }
+ else if (tag == 3)
+ {
+ bigint n = bigint::from_bytes_be(1, bytes_buffer_.data(), bytes_buffer_.size());
+ n = -1 - n;
+ n.write_string(s);
+ }
+ }
+ break;
+ }
+ default:
+ {
+ ec = cbor_errc::invalid_decimal_fraction;
+ more_ = false;
+ return;
+ }
+ }
+
+ if (s.size() >= static_cast<std::size_t>((std::numeric_limits<int32_t>::max)()) ||
+ exponent >= (std::numeric_limits<int32_t>::max)() ||
+ exponent <= (std::numeric_limits<int32_t>::min)())
+ {
+ ec = cbor_errc::invalid_decimal_fraction;
+ more_ = false;
+ return;
+ }
+ else if (s.size() > 0)
+ {
+ if (s[0] == '-')
+ {
+ result.push_back('-');
+ jsoncons::detail::prettify_string(s.c_str()+1, s.size()-1, (int)exponent, -4, 17, result);
+ }
+ else
+ {
+ jsoncons::detail::prettify_string(s.c_str(), s.size(), (int)exponent, -4, 17, result);
+ }
+ }
+ else
+ {
+ ec = cbor_errc::invalid_decimal_fraction;
+ more_ = false;
+ return;
+ }
+ }
+
+ void read_bigfloat(string_type& s, std::error_code& ec)
+ {
+ std::size_t size = get_size(ec);
+ if (!more_)
+ {
+ return;
+ }
+ if (size != 2)
+ {
+ ec = cbor_errc::invalid_bigfloat;
+ more_ = false;
+ return;
+ }
+
+ auto c = source_.peek();
+ if (c.eof)
+ {
+ ec = cbor_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ int64_t exponent = 0;
+ switch (get_major_type(c.value))
+ {
+ case jsoncons::cbor::detail::cbor_major_type::unsigned_integer:
+ {
+ exponent = get_uint64_value(ec);
+ if (ec)
+ {
+ return;
+ }
+ break;
+ }
+ case jsoncons::cbor::detail::cbor_major_type::negative_integer:
+ {
+ exponent = get_int64_value(ec);
+ if (ec)
+ {
+ return;
+ }
+ break;
+ }
+ default:
+ {
+ ec = cbor_errc::invalid_bigfloat;
+ more_ = false;
+ return;
+ }
+ }
+
+ c = source_.peek();
+ if (c.eof)
+ {
+ ec = cbor_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ switch (get_major_type(c.value))
+ {
+ case jsoncons::cbor::detail::cbor_major_type::unsigned_integer:
+ {
+ uint64_t val = get_uint64_value(ec);
+ if (ec)
+ {
+ return;
+ }
+ s.push_back('0');
+ s.push_back('x');
+ jsoncons::detail::integer_to_string_hex(val, s);
+ break;
+ }
+ case jsoncons::cbor::detail::cbor_major_type::negative_integer:
+ {
+ int64_t val = get_int64_value(ec);
+ if (ec)
+ {
+ return;
+ }
+ s.push_back('-');
+ s.push_back('0');
+ s.push_back('x');
+ jsoncons::detail::integer_to_string_hex(static_cast<uint64_t>(-val), s);
+ break;
+ }
+ case jsoncons::cbor::detail::cbor_major_type::semantic_tag:
+ {
+ uint8_t b;
+ if (source_.read(&b, 1) == 0)
+ {
+ ec = cbor_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ uint8_t tag = get_additional_information_value(b);
+
+ c = source_.peek();
+ if (c.eof)
+ {
+ ec = cbor_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+
+ if (get_major_type(c.value) == jsoncons::cbor::detail::cbor_major_type::byte_string)
+ {
+ bytes_buffer_.clear();
+ more_ = read_byte_string(bytes_buffer_, ec);
+ if (!more_)
+ {
+ return;
+ }
+ if (tag == 2)
+ {
+ s.push_back('0');
+ s.push_back('x');
+ bigint n = bigint::from_bytes_be(1, bytes_buffer_.data(), bytes_buffer_.size());
+ n.write_string_hex(s);
+ }
+ else if (tag == 3)
+ {
+ s.push_back('-');
+ s.push_back('0');
+ bigint n = bigint::from_bytes_be(1, bytes_buffer_.data(), bytes_buffer_.size());
+ n = -1 - n;
+ n.write_string_hex(s);
+ s[2] = 'x'; // overwrite minus
+ }
+ }
+ break;
+ }
+ default:
+ {
+ ec = cbor_errc::invalid_bigfloat;
+ more_ = false;
+ return;
+ }
+ }
+
+ s.push_back('p');
+ if (exponent >=0)
+ {
+ jsoncons::detail::integer_to_string_hex(static_cast<uint64_t>(exponent), s);
+ }
+ else
+ {
+ s.push_back('-');
+ jsoncons::detail::integer_to_string_hex(static_cast<uint64_t>(-exponent), s);
+ }
+ }
+
+ static jsoncons::cbor::detail::cbor_major_type get_major_type(uint8_t type)
+ {
+ static constexpr uint8_t major_type_shift = 0x05;
+ uint8_t value = type >> major_type_shift;
+ return static_cast<jsoncons::cbor::detail::cbor_major_type>(value);
+ }
+
+ static uint8_t get_additional_information_value(uint8_t type)
+ {
+ static constexpr uint8_t additional_information_mask = (1U << 5) - 1;
+ uint8_t value = type & additional_information_mask;
+ return value;
+ }
+
+ void read_tags(std::error_code& ec)
+ {
+ auto c = source_.peek();
+ if (c.eof)
+ {
+ ec = cbor_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ jsoncons::cbor::detail::cbor_major_type major_type = get_major_type(c.value);
+
+ while (major_type == jsoncons::cbor::detail::cbor_major_type::semantic_tag)
+ {
+ uint64_t val = get_uint64_value(ec);
+ if (!more_)
+ {
+ return;
+ }
+ switch(val)
+ {
+ case 25: // stringref
+ other_tags_[stringref_tag] = true;
+ break;
+ case 256: // stringref-namespace
+ other_tags_[stringref_namespace_tag] = true;
+ break;
+ default:
+ other_tags_[item_tag] = true;
+ item_tag_ = val;
+ break;
+ }
+ c = source_.peek();
+ if (c.eof)
+ {
+ ec = cbor_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ major_type = get_major_type(c.value);
+ }
+ }
+
+ void handle_string(json_visitor2& visitor, const jsoncons::basic_string_view<char>& v, std::error_code& ec)
+ {
+ semantic_tag tag = semantic_tag::none;
+ if (other_tags_[item_tag])
+ {
+ switch (item_tag_)
+ {
+ case 0:
+ tag = semantic_tag::datetime;
+ break;
+ case 32:
+ tag = semantic_tag::uri;
+ break;
+ case 33:
+ tag = semantic_tag::base64url;
+ break;
+ case 34:
+ tag = semantic_tag::base64;
+ break;
+ default:
+ break;
+ }
+ other_tags_[item_tag] = false;
+ }
+ more_ = visitor.string_value(v, tag, *this, ec);
+ }
+
+ static jsoncons::endian get_typed_array_endianness(const uint8_t tag)
+ {
+ return ((tag & detail::cbor_array_tags_e_mask) >> detail::cbor_array_tags_e_shift) == 0 ? jsoncons::endian::big : jsoncons::endian::little;
+ }
+
+ static std::size_t get_typed_array_bytes_per_element(const uint8_t tag)
+ {
+ const uint8_t f = (tag & detail::cbor_array_tags_f_mask) >> detail::cbor_array_tags_f_shift;
+ const uint8_t ll = (tag & detail::cbor_array_tags_ll_mask) >> detail::cbor_array_tags_ll_shift;
+
+ return std::size_t(1) << (f + ll);
+ }
+
+ template <typename Read>
+ void write_byte_string(Read read, json_visitor2& visitor, std::error_code& ec)
+ {
+ if (other_tags_[item_tag])
+ {
+ switch (item_tag_)
+ {
+ case 0x2:
+ {
+ bytes_buffer_.clear();
+ read(bytes_buffer_,ec);
+ if (ec)
+ {
+ more_ = false;
+ return;
+ }
+ bigint n = bigint::from_bytes_be(1, bytes_buffer_.data(), bytes_buffer_.size());
+ text_buffer_.clear();
+ n.write_string(text_buffer_);
+ more_ = visitor.string_value(text_buffer_, semantic_tag::bigint, *this, ec);
+ break;
+ }
+ case 0x3:
+ {
+ bytes_buffer_.clear();
+ read(bytes_buffer_,ec);
+ if (ec)
+ {
+ more_ = false;
+ return;
+ }
+ bigint n = bigint::from_bytes_be(1, bytes_buffer_.data(), bytes_buffer_.size());
+ n = -1 - n;
+ text_buffer_.clear();
+ n.write_string(text_buffer_);
+ more_ = visitor.string_value(text_buffer_, semantic_tag::bigint, *this, ec);
+ break;
+ }
+ case 0x15:
+ {
+ read(bytes_buffer_,ec);
+ if (ec)
+ {
+ more_ = false;
+ return;
+ }
+ more_ = visitor.byte_string_value(bytes_buffer_, semantic_tag::base64url, *this, ec);
+ break;
+ }
+ case 0x16:
+ {
+ read(bytes_buffer_,ec);
+ if (ec)
+ {
+ more_ = false;
+ return;
+ }
+ more_ = visitor.byte_string_value(bytes_buffer_, semantic_tag::base64, *this, ec);
+ break;
+ }
+ case 0x17:
+ {
+ read(bytes_buffer_,ec);
+ if (ec)
+ {
+ more_ = false;
+ return;
+ }
+ more_ = visitor.byte_string_value(bytes_buffer_, semantic_tag::base16, *this, ec);
+ break;
+ }
+ case 0x40:
+ {
+ typed_array_.clear();
+ read(typed_array_,ec);
+ if (ec)
+ {
+ more_ = false;
+ return;
+ }
+ uint8_t* data = reinterpret_cast<uint8_t*>(typed_array_.data());
+ std::size_t size = typed_array_.size();
+ more_ = visitor.typed_array(jsoncons::span<const uint8_t>(data,size), semantic_tag::none, *this, ec);
+ break;
+ }
+ case 0x44:
+ {
+ typed_array_.clear();
+ read(typed_array_,ec);
+ if (ec)
+ {
+ more_ = false;
+ return;
+ }
+ uint8_t* data = reinterpret_cast<uint8_t*>(typed_array_.data());
+ std::size_t size = typed_array_.size();
+ more_ = visitor.typed_array(jsoncons::span<const uint8_t>(data,size), semantic_tag::clamped, *this, ec);
+ break;
+ }
+ case 0x41:
+ case 0x45:
+ {
+ typed_array_.clear();
+ read(typed_array_,ec);
+ if (ec)
+ {
+ more_ = false;
+ return;
+ }
+ const uint8_t tag = (uint8_t)item_tag_;
+ jsoncons::endian e = get_typed_array_endianness(tag);
+ const size_t bytes_per_elem = get_typed_array_bytes_per_element(tag);
+
+ uint16_t* data = reinterpret_cast<uint16_t*>(typed_array_.data());
+ std::size_t size = typed_array_.size()/bytes_per_elem;
+
+ if (e != jsoncons::endian::native)
+ {
+ for (std::size_t i = 0; i < size; ++i)
+ {
+ data[i] = binary::byte_swap<uint16_t>(data[i]);
+ }
+ }
+ more_ = visitor.typed_array(jsoncons::span<const uint16_t>(data,size), semantic_tag::none, *this, ec);
+ break;
+ }
+ case 0x42:
+ case 0x46:
+ {
+ typed_array_.clear();
+ read(typed_array_,ec);
+ if (ec)
+ {
+ more_ = false;
+ return;
+ }
+ const uint8_t tag = (uint8_t)item_tag_;
+ jsoncons::endian e = get_typed_array_endianness(tag);
+ const size_t bytes_per_elem = get_typed_array_bytes_per_element(tag);
+
+ uint32_t* data = reinterpret_cast<uint32_t*>(typed_array_.data());
+ std::size_t size = typed_array_.size()/bytes_per_elem;
+ if (e != jsoncons::endian::native)
+ {
+ for (std::size_t i = 0; i < size; ++i)
+ {
+ data[i] = binary::byte_swap<uint32_t>(data[i]);
+ }
+ }
+ more_ = visitor.typed_array(jsoncons::span<const uint32_t>(data,size), semantic_tag::none, *this, ec);
+ break;
+ }
+ case 0x43:
+ case 0x47:
+ {
+ typed_array_.clear();
+ read(typed_array_,ec);
+ if (ec)
+ {
+ more_ = false;
+ return;
+ }
+ const uint8_t tag = (uint8_t)item_tag_;
+ jsoncons::endian e = get_typed_array_endianness(tag);
+ const size_t bytes_per_elem = get_typed_array_bytes_per_element(tag);
+
+ uint64_t* data = reinterpret_cast<uint64_t*>(typed_array_.data());
+ std::size_t size = typed_array_.size()/bytes_per_elem;
+ if (e != jsoncons::endian::native)
+ {
+ for (std::size_t i = 0; i < size; ++i)
+ {
+ data[i] = binary::byte_swap<uint64_t>(data[i]);
+ }
+ }
+ more_ = visitor.typed_array(jsoncons::span<const uint64_t>(data,size), semantic_tag::none, *this, ec);
+ break;
+ }
+ case 0x48:
+ {
+ typed_array_.clear();
+ read(typed_array_,ec);
+ if (ec)
+ {
+ more_ = false;
+ return;
+ }
+ int8_t* data = reinterpret_cast<int8_t*>(typed_array_.data());
+ std::size_t size = typed_array_.size();
+ more_ = visitor.typed_array(jsoncons::span<const int8_t>(data,size), semantic_tag::none, *this, ec);
+ break;
+ }
+ case 0x49:
+ case 0x4d:
+ {
+ typed_array_.clear();
+ read(typed_array_,ec);
+ if (ec)
+ {
+ more_ = false;
+ return;
+ }
+ const uint8_t tag = (uint8_t)item_tag_;
+ jsoncons::endian e = get_typed_array_endianness(tag);
+ const size_t bytes_per_elem = get_typed_array_bytes_per_element(tag);
+
+ int16_t* data = reinterpret_cast<int16_t*>(typed_array_.data());
+ std::size_t size = typed_array_.size()/bytes_per_elem;
+ if (e != jsoncons::endian::native)
+ {
+ for (std::size_t i = 0; i < size; ++i)
+ {
+ data[i] = binary::byte_swap<int16_t>(data[i]);
+ }
+ }
+ more_ = visitor.typed_array(jsoncons::span<const int16_t>(data,size), semantic_tag::none, *this, ec);
+ break;
+ }
+ case 0x4a:
+ case 0x4e:
+ {
+ typed_array_.clear();
+ read(typed_array_,ec);
+ if (ec)
+ {
+ more_ = false;
+ return;
+ }
+ const uint8_t tag = (uint8_t)item_tag_;
+ jsoncons::endian e = get_typed_array_endianness(tag);
+ const size_t bytes_per_elem = get_typed_array_bytes_per_element(tag);
+
+ int32_t* data = reinterpret_cast<int32_t*>(typed_array_.data());
+ std::size_t size = typed_array_.size()/bytes_per_elem;
+ if (e != jsoncons::endian::native)
+ {
+ for (std::size_t i = 0; i < size; ++i)
+ {
+ data[i] = binary::byte_swap<int32_t>(data[i]);
+ }
+ }
+ more_ = visitor.typed_array(jsoncons::span<const int32_t>(data,size), semantic_tag::none, *this, ec);
+ break;
+ }
+ case 0x4b:
+ case 0x4f:
+ {
+ typed_array_.clear();
+ read(typed_array_,ec);
+ if (ec)
+ {
+ more_ = false;
+ return;
+ }
+ const uint8_t tag = (uint8_t)item_tag_;
+ jsoncons::endian e = get_typed_array_endianness(tag);
+ const size_t bytes_per_elem = get_typed_array_bytes_per_element(tag);
+
+ int64_t* data = reinterpret_cast<int64_t*>(typed_array_.data());
+ std::size_t size = typed_array_.size()/bytes_per_elem;
+ if (e != jsoncons::endian::native)
+ {
+ for (std::size_t i = 0; i < size; ++i)
+ {
+ data[i] = binary::byte_swap<int64_t>(data[i]);
+ }
+ }
+ more_ = visitor.typed_array(jsoncons::span<const int64_t>(data,size), semantic_tag::none, *this, ec);
+ break;
+ }
+ case 0x50:
+ case 0x54:
+ {
+ typed_array_.clear();
+ read(typed_array_,ec);
+ if (ec)
+ {
+ more_ = false;
+ return;
+ }
+ const uint8_t tag = (uint8_t)item_tag_;
+ jsoncons::endian e = get_typed_array_endianness(tag);
+ const size_t bytes_per_elem = get_typed_array_bytes_per_element(tag);
+
+ uint16_t* data = reinterpret_cast<uint16_t*>(typed_array_.data());
+ std::size_t size = typed_array_.size()/bytes_per_elem;
+ if (e != jsoncons::endian::native)
+ {
+ for (std::size_t i = 0; i < size; ++i)
+ {
+ data[i] = binary::byte_swap<uint16_t>(data[i]);
+ }
+ }
+ more_ = visitor.typed_array(half_arg, jsoncons::span<const uint16_t>(data,size), semantic_tag::none, *this, ec);
+ break;
+ }
+ case 0x51:
+ case 0x55:
+ {
+ typed_array_.clear();
+ read(typed_array_,ec);
+ if (ec)
+ {
+ more_ = false;
+ return;
+ }
+ const uint8_t tag = (uint8_t)item_tag_;
+ jsoncons::endian e = get_typed_array_endianness(tag);
+ const size_t bytes_per_elem = get_typed_array_bytes_per_element(tag);
+
+ float* data = reinterpret_cast<float*>(typed_array_.data());
+ std::size_t size = typed_array_.size()/bytes_per_elem;
+ if (e != jsoncons::endian::native)
+ {
+ for (std::size_t i = 0; i < size; ++i)
+ {
+ data[i] = binary::byte_swap<float>(data[i]);
+ }
+ }
+ more_ = visitor.typed_array(jsoncons::span<const float>(data,size), semantic_tag::none, *this, ec);
+ break;
+ }
+ case 0x52:
+ case 0x56:
+ {
+ typed_array_.clear();
+ read(typed_array_,ec);
+ if (ec)
+ {
+ more_ = false;
+ return;
+ }
+ const uint8_t tag = (uint8_t)item_tag_;
+ jsoncons::endian e = get_typed_array_endianness(tag);
+ const size_t bytes_per_elem = get_typed_array_bytes_per_element(tag);
+
+ double* data = reinterpret_cast<double*>(typed_array_.data());
+ std::size_t size = typed_array_.size()/bytes_per_elem;
+
+ if (e != jsoncons::endian::native)
+ {
+ for (std::size_t i = 0; i < size; ++i)
+ {
+ data[i] = binary::byte_swap<double>(data[i]);
+ }
+ }
+ more_ = visitor.typed_array(jsoncons::span<const double>(data,size), semantic_tag::none, *this, ec);
+ break;
+ }
+ default:
+ {
+ read(bytes_buffer_,ec);
+ if (ec)
+ {
+ more_ = false;
+ return;
+ }
+ more_ = visitor.byte_string_value(bytes_buffer_, item_tag_, *this, ec);
+ break;
+ }
+ }
+ other_tags_[item_tag] = false;
+ }
+ else
+ {
+ read(bytes_buffer_,ec);
+ if (ec)
+ {
+ return;
+ }
+ more_ = visitor.byte_string_value(bytes_buffer_, semantic_tag::none, *this, ec);
+ }
+ }
+
+ void produce_begin_multi_dim(json_visitor2& visitor,
+ semantic_tag tag,
+ std::error_code& ec)
+ {
+ uint8_t b;
+ if (source_.read(&b, 1) == 0)
+ {
+ ec = cbor_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ jsoncons::cbor::detail::cbor_major_type major_type = get_major_type(b);
+ JSONCONS_ASSERT(major_type == jsoncons::cbor::detail::cbor_major_type::array);
+ uint8_t info = get_additional_information_value(b);
+
+ read_shape(info, ec);
+ if (ec)
+ {
+ return;
+ }
+
+ state_stack_.emplace_back(parse_mode::multi_dim, 0);
+ more_ = visitor.begin_multi_dim(shape_, tag, *this, ec);
+ }
+
+ void produce_end_multi_dim(json_visitor2& visitor, std::error_code& ec)
+ {
+ more_ = visitor.end_multi_dim(*this, ec);
+ state_stack_.pop_back();
+ }
+
+ void read_shape(uint8_t info, std::error_code& ec)
+ {
+ shape_.clear();
+ switch (info)
+ {
+ case jsoncons::cbor::detail::additional_info::indefinite_length:
+ {
+ while (true)
+ {
+ auto c = source_.peek();
+ if (c.eof)
+ {
+ ec = cbor_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ if (c.value == 0xff)
+ {
+ source_.ignore(1);
+ }
+ else
+ {
+ std::size_t dim = get_size(ec);
+ if (!more_)
+ {
+ return;
+ }
+ shape_.push_back(dim);
+ }
+ }
+ break;
+ }
+ default:
+ {
+ std::size_t size = get_size(ec);
+ if (!more_)
+ {
+ return;
+ }
+ for (std::size_t i = 0; more_ && i < size; ++i)
+ {
+ std::size_t dim = get_size(ec);
+ if (!more_)
+ {
+ return;
+ }
+ shape_.push_back(dim);
+ }
+ break;
+ }
+ }
+ }
+};
+
+}}
+
+#endif
diff --git a/include/jsoncons_ext/cbor/cbor_reader.hpp b/include/jsoncons_ext/cbor/cbor_reader.hpp
new file mode 100644
index 0000000..a46a52e
--- /dev/null
+++ b/include/jsoncons_ext/cbor/cbor_reader.hpp
@@ -0,0 +1,116 @@
+// Copyright 2017 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_CBOR_CBOR_READER_HPP
+#define JSONCONS_CBOR_CBOR_READER_HPP
+
+#include <string>
+#include <vector>
+#include <memory>
+#include <utility> // std::move
+#include <jsoncons/json.hpp>
+#include <jsoncons/source.hpp>
+#include <jsoncons/config/jsoncons_config.hpp>
+#include <jsoncons_ext/cbor/cbor_encoder.hpp>
+#include <jsoncons_ext/cbor/cbor_error.hpp>
+#include <jsoncons_ext/cbor/cbor_detail.hpp>
+#include <jsoncons_ext/cbor/cbor_parser.hpp>
+
+namespace jsoncons { namespace cbor {
+
+template <class Source,class Allocator=std::allocator<char>>
+class basic_cbor_reader
+{
+ using char_type = char;
+
+ basic_cbor_parser<Source,Allocator> parser_;
+ basic_json_visitor2_to_visitor_adaptor<char_type,Allocator> adaptor_;
+ json_visitor2& visitor_;
+public:
+ template <class Sourceable>
+ basic_cbor_reader(Sourceable&& source,
+ json_visitor& visitor,
+ const Allocator alloc)
+ : basic_cbor_reader(std::forward<Sourceable>(source),
+ visitor,
+ cbor_decode_options(),
+ alloc)
+ {
+ }
+
+ template <class Sourceable>
+ basic_cbor_reader(Sourceable&& source,
+ json_visitor& visitor,
+ const cbor_decode_options& options = cbor_decode_options(),
+ const Allocator alloc=Allocator())
+ : parser_(std::forward<Sourceable>(source), options, alloc),
+ adaptor_(visitor, alloc), visitor_(adaptor_)
+ {
+ }
+ template <class Sourceable>
+ basic_cbor_reader(Sourceable&& source,
+ json_visitor2& visitor,
+ const Allocator alloc)
+ : basic_cbor_reader(std::forward<Sourceable>(source),
+ visitor,
+ cbor_decode_options(),
+ alloc)
+ {
+ }
+
+ template <class Sourceable>
+ basic_cbor_reader(Sourceable&& source,
+ json_visitor2& visitor,
+ const cbor_decode_options& options = cbor_decode_options(),
+ const Allocator alloc=Allocator())
+ : parser_(std::forward<Sourceable>(source), options, alloc),
+ visitor_(visitor)
+ {
+ }
+
+ void read()
+ {
+ std::error_code ec;
+ read(ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec,line(),column()));
+ }
+ }
+
+ void read(std::error_code& ec)
+ {
+ parser_.reset();
+ parser_.parse(visitor_, ec);
+ if (ec)
+ {
+ return;
+ }
+ }
+
+ std::size_t line() const
+ {
+ return parser_.line();
+ }
+
+ std::size_t column() const
+ {
+ return parser_.column();
+ }
+};
+
+using cbor_stream_reader = basic_cbor_reader<jsoncons::binary_stream_source>;
+
+using cbor_bytes_reader = basic_cbor_reader<jsoncons::bytes_source>;
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+JSONCONS_DEPRECATED_MSG("Instead, use cbor_stream_reader") typedef cbor_stream_reader cbor_reader;
+JSONCONS_DEPRECATED_MSG("Instead, use cbor_bytes_reader") typedef cbor_bytes_reader cbor_buffer_reader;
+#endif
+
+}}
+
+#endif
diff --git a/include/jsoncons_ext/cbor/decode_cbor.hpp b/include/jsoncons_ext/cbor/decode_cbor.hpp
new file mode 100644
index 0000000..ab5c913
--- /dev/null
+++ b/include/jsoncons_ext/cbor/decode_cbor.hpp
@@ -0,0 +1,203 @@
+// Copyright 2017 Daniel Parkerstd
+// 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_CBOR_DECODE_CBOR_HPP
+#define JSONCONS_CBOR_DECODE_CBOR_HPP
+
+#include <string>
+#include <vector>
+#include <memory>
+#include <type_traits> // std::enable_if
+#include <istream> // std::basic_istream
+#include <jsoncons/json.hpp>
+#include <jsoncons/json_filter.hpp>
+#include <jsoncons/decode_traits.hpp>
+#include <jsoncons/config/jsoncons_config.hpp>
+#include <jsoncons_ext/cbor/cbor_reader.hpp>
+#include <jsoncons_ext/cbor/cbor_cursor.hpp>
+
+namespace jsoncons {
+namespace cbor {
+
+ template<class T, class Source>
+ typename std::enable_if<type_traits::is_basic_json<T>::value &&
+ type_traits::is_byte_sequence<Source>::value,T>::type
+ decode_cbor(const Source& v,
+ const cbor_decode_options& options = cbor_decode_options())
+ {
+ jsoncons::json_decoder<T> decoder;
+ auto adaptor = make_json_visitor_adaptor<json_visitor>(decoder);
+ basic_cbor_reader<jsoncons::bytes_source> 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<class T, class Source>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value &&
+ type_traits::is_byte_sequence<Source>::value,T>::type
+ decode_cbor(const Source& v,
+ const cbor_decode_options& options = cbor_decode_options())
+ {
+ basic_cbor_cursor<bytes_source> cursor(v, options);
+ json_decoder<basic_json<char,sorted_policy>> decoder{};
+
+ std::error_code ec;
+ T val = decode_traits<T,char>::decode(cursor, decoder, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column()));
+ }
+ return val;
+ }
+
+ template<class T>
+ typename std::enable_if<type_traits::is_basic_json<T>::value,T>::type
+ decode_cbor(std::istream& is,
+ const cbor_decode_options& options = cbor_decode_options())
+ {
+ jsoncons::json_decoder<T> decoder;
+ auto adaptor = make_json_visitor_adaptor<json_visitor>(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<class T>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value,T>::type
+ decode_cbor(std::istream& is,
+ const cbor_decode_options& options = cbor_decode_options())
+ {
+ basic_cbor_cursor<binary_stream_source> cursor(is, options);
+ json_decoder<basic_json<char,sorted_policy>> decoder{};
+
+ std::error_code ec;
+ T val = decode_traits<T,char>::decode(cursor, decoder, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column()));
+ }
+ return val;
+ }
+
+ template<class T, class InputIt>
+ typename std::enable_if<type_traits::is_basic_json<T>::value,T>::type
+ decode_cbor(InputIt first, InputIt last,
+ const cbor_decode_options& options = cbor_decode_options())
+ {
+ jsoncons::json_decoder<T> decoder;
+ auto adaptor = make_json_visitor_adaptor<json_visitor>(decoder);
+ basic_cbor_reader<binary_iterator_source<InputIt>> reader(binary_iterator_source<InputIt>(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<class T, class InputIt>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value,T>::type
+ decode_cbor(InputIt first, InputIt last,
+ const cbor_decode_options& options = cbor_decode_options())
+ {
+ basic_cbor_cursor<binary_iterator_source<InputIt>> cursor(binary_iterator_source<InputIt>(first, last), options);
+ json_decoder<basic_json<char,sorted_policy>> decoder{};
+
+ std::error_code ec;
+ T val = decode_traits<T,char>::decode(cursor, decoder, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column()));
+ }
+ return val;
+ }
+
+ // With leading allocator parameter
+
+ template<class T, class Source, class TempAllocator>
+ typename std::enable_if<type_traits::is_basic_json<T>::value &&
+ type_traits::is_byte_sequence<Source>::value,T>::type
+ decode_cbor(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ const Source& v,
+ const cbor_decode_options& options = cbor_decode_options())
+ {
+ json_decoder<T,TempAllocator> decoder(temp_alloc);
+ auto adaptor = make_json_visitor_adaptor<json_visitor>(decoder);
+ basic_cbor_reader<jsoncons::bytes_source,TempAllocator> reader(v, adaptor, options, temp_alloc);
+ reader.read();
+ if (!decoder.is_valid())
+ {
+ JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column()));
+ }
+ return decoder.get_result();
+ }
+
+ template<class T, class Source, class TempAllocator>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value &&
+ type_traits::is_byte_sequence<Source>::value,T>::type
+ decode_cbor(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ const Source& v,
+ const cbor_decode_options& options = cbor_decode_options())
+ {
+ basic_cbor_cursor<bytes_source,TempAllocator> cursor(v, options, temp_alloc);
+ json_decoder<basic_json<char,sorted_policy,TempAllocator>,TempAllocator> decoder(temp_alloc, temp_alloc);
+
+ std::error_code ec;
+ T val = decode_traits<T,char>::decode(cursor, decoder, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column()));
+ }
+ return val;
+ }
+
+ template<class T,class TempAllocator>
+ typename std::enable_if<type_traits::is_basic_json<T>::value,T>::type
+ decode_cbor(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ std::istream& is,
+ const cbor_decode_options& options = cbor_decode_options())
+ {
+ json_decoder<T,TempAllocator> decoder(temp_alloc);
+ auto adaptor = make_json_visitor_adaptor<json_visitor>(decoder);
+ basic_cbor_reader<jsoncons::binary_stream_source,TempAllocator> reader(is, adaptor, options, temp_alloc);
+ reader.read();
+ if (!decoder.is_valid())
+ {
+ JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column()));
+ }
+ return decoder.get_result();
+ }
+
+ template<class T,class TempAllocator>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value,T>::type
+ decode_cbor(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ std::istream& is,
+ const cbor_decode_options& options = cbor_decode_options())
+ {
+ basic_cbor_cursor<binary_stream_source,TempAllocator> cursor(is, options, temp_alloc);
+ json_decoder<basic_json<char,sorted_policy,TempAllocator>,TempAllocator> decoder(temp_alloc, temp_alloc);
+
+ std::error_code ec;
+ T val = decode_traits<T,char>::decode(cursor, decoder, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column()));
+ }
+ return val;
+ }
+
+} // namespace cbor
+} // namespace jsoncons
+
+#endif
diff --git a/include/jsoncons_ext/cbor/encode_cbor.hpp b/include/jsoncons_ext/cbor/encode_cbor.hpp
new file mode 100644
index 0000000..8576f1c
--- /dev/null
+++ b/include/jsoncons_ext/cbor/encode_cbor.hpp
@@ -0,0 +1,151 @@
+// Copyright 2017 Daniel Parkerstd
+// 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_CBOR_ENCODE_CBOR_HPP
+#define JSONCONS_CBOR_ENCODE_CBOR_HPP
+
+#include <string>
+#include <vector>
+#include <memory>
+#include <type_traits> // std::enable_if
+#include <istream> // std::basic_istream
+#include <jsoncons/json.hpp>
+#include <jsoncons/json_filter.hpp>
+#include <jsoncons/config/jsoncons_config.hpp>
+#include <jsoncons/encode_traits.hpp>
+#include <jsoncons_ext/cbor/cbor_encoder.hpp>
+
+namespace jsoncons {
+namespace cbor {
+
+ // to bytes
+
+ template<class T, class Container>
+ typename std::enable_if<type_traits::is_basic_json<T>::value &&
+ type_traits::is_back_insertable_byte_container<Container>::value,void>::type
+ encode_cbor(const T& j,
+ Container& v,
+ const cbor_encode_options& options = cbor_encode_options())
+ {
+ using char_type = typename T::char_type;
+ basic_cbor_encoder<jsoncons::bytes_sink<Container>> encoder(v, options);
+ auto adaptor = make_json_visitor_adaptor<basic_json_visitor<char_type>>(encoder);
+ j.dump(adaptor);
+ }
+
+ template<class T, class Container>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value &&
+ type_traits::is_back_insertable_byte_container<Container>::value,void>::type
+ encode_cbor(const T& val, Container& v,
+ const cbor_encode_options& options = cbor_encode_options())
+ {
+ basic_cbor_encoder<jsoncons::bytes_sink<Container>> encoder(v, options);
+ std::error_code ec;
+ encode_traits<T,char>::encode(val, encoder, json(), ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec));
+ }
+ }
+
+ // stream
+
+ template<class T>
+ typename std::enable_if<type_traits::is_basic_json<T>::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<basic_json_visitor<char_type>>(encoder);
+ j.dump(adaptor);
+ }
+
+ template<class T>
+ typename std::enable_if<!type_traits::is_basic_json<T>::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<T,char>::encode(val, encoder, json(), ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec));
+ }
+ }
+
+ // temp_allocator_arg
+
+ // to bytes
+
+ template<class T, class Container, class TempAllocator>
+ typename std::enable_if<type_traits::is_basic_json<T>::value &&
+ type_traits::is_back_insertable_byte_container<Container>::value,void>::type
+ encode_cbor(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ const T& j,
+ Container& v,
+ const cbor_encode_options& options = cbor_encode_options())
+ {
+ using char_type = typename T::char_type;
+ basic_cbor_encoder<bytes_sink<Container>,TempAllocator> encoder(v, options, temp_alloc);
+ auto adaptor = make_json_visitor_adaptor<basic_json_visitor<char_type>>(encoder);
+ j.dump(adaptor);
+ }
+
+ template<class T, class Container, class TempAllocator>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value &&
+ type_traits::is_back_insertable_byte_container<Container>::value,void>::type
+ encode_cbor(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ const T& val,
+ Container& v,
+ const cbor_encode_options& options = cbor_encode_options())
+ {
+ basic_cbor_encoder<jsoncons::bytes_sink<Container>,TempAllocator> encoder(v, options, temp_alloc);
+ std::error_code ec;
+ encode_traits<T,char>::encode(val, encoder, json(), ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec));
+ }
+ }
+
+ // stream
+
+ template<class T,class TempAllocator>
+ typename std::enable_if<type_traits::is_basic_json<T>::value,void>::type
+ encode_cbor(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ const T& j,
+ std::ostream& os,
+ const cbor_encode_options& options = cbor_encode_options())
+ {
+ using char_type = typename T::char_type;
+ basic_cbor_encoder<binary_stream_sink,TempAllocator> encoder(os, options, temp_alloc);
+ auto adaptor = make_json_visitor_adaptor<basic_json_visitor<char_type>>(encoder);
+ j.dump(adaptor);
+ }
+
+ template<class T,class TempAllocator>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value,void>::type
+ encode_cbor(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ const T& val,
+ std::ostream& os,
+ const cbor_encode_options& options = cbor_encode_options())
+ {
+ std::error_code ec;
+ encode_cbor(temp_allocator_arg, temp_alloc, val, os, options, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec));
+ }
+ }
+
+} // namespace cbor
+} // namespace jsoncons
+
+#endif
diff --git a/include/jsoncons_ext/csv/csv.hpp b/include/jsoncons_ext/csv/csv.hpp
new file mode 100644
index 0000000..9f8a9c5
--- /dev/null
+++ b/include/jsoncons_ext/csv/csv.hpp
@@ -0,0 +1,17 @@
+/// Copyright 2013 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_CSV_CSV_HPP
+#define JSONCONS_CSV_CSV_HPP
+
+#include <jsoncons_ext/csv/csv_options.hpp>
+#include <jsoncons_ext/csv/csv_reader.hpp>
+#include <jsoncons_ext/csv/csv_encoder.hpp>
+#include <jsoncons_ext/csv/csv_cursor.hpp>
+#include <jsoncons_ext/csv/decode_csv.hpp>
+#include <jsoncons_ext/csv/encode_csv.hpp>
+
+#endif
diff --git a/include/jsoncons_ext/csv/csv_cursor.hpp b/include/jsoncons_ext/csv/csv_cursor.hpp
new file mode 100644
index 0000000..67d55a6
--- /dev/null
+++ b/include/jsoncons_ext/csv/csv_cursor.hpp
@@ -0,0 +1,358 @@
+// 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)
+
+// See https://github.com/danielaparker/jsoncons for latest version
+
+#ifndef JSONCONS_CSV_CSV_CURSOR_HPP
+#define JSONCONS_CSV_CSV_CURSOR_HPP
+
+#include <memory> // std::allocator
+#include <string>
+#include <vector>
+#include <stdexcept>
+#include <system_error>
+#include <ios>
+#include <istream> // std::basic_istream
+#include <jsoncons/byte_string.hpp>
+#include <jsoncons/config/jsoncons_config.hpp>
+#include <jsoncons/json_visitor.hpp>
+#include <jsoncons/json_exception.hpp>
+#include <jsoncons_ext/csv/csv_parser.hpp>
+#include <jsoncons/staj_cursor.hpp>
+#include <jsoncons/source.hpp>
+#include <jsoncons/source_adaptor.hpp>
+
+namespace jsoncons { namespace csv {
+
+template<class CharT,class Source=jsoncons::stream_source<CharT>,class Allocator=std::allocator<char>>
+class basic_csv_cursor : public basic_staj_cursor<CharT>, private virtual ser_context
+{
+public:
+ using source_type = Source;
+ using char_type = CharT;
+ using allocator_type = Allocator;
+private:
+ static constexpr size_t default_max_buffer_size = 16384;
+
+ typedef typename std::allocator_traits<allocator_type>:: template rebind_alloc<CharT> char_allocator_type;
+
+ text_source_adaptor<Source> source_;
+ basic_csv_parser<CharT,Allocator> parser_;
+ basic_staj_visitor<CharT> cursor_visitor_;
+
+ // Noncopyable and nonmoveable
+ basic_csv_cursor(const basic_csv_cursor&) = delete;
+ basic_csv_cursor& operator=(const basic_csv_cursor&) = delete;
+
+public:
+ using string_view_type = jsoncons::basic_string_view<CharT>;
+
+ // Constructors that throw parse exceptions
+
+ template <class Sourceable>
+ basic_csv_cursor(Sourceable&& source,
+ const basic_csv_decode_options<CharT>& options = basic_csv_decode_options<CharT>(),
+ std::function<bool(csv_errc,const ser_context&)> err_handler = default_csv_parsing(),
+ const Allocator& alloc = Allocator(),
+ typename std::enable_if<!std::is_constructible<jsoncons::basic_string_view<CharT>,Sourceable>::value>::type* = 0)
+ : source_(std::forward<Sourceable>(source)),
+ parser_(options,err_handler,alloc),
+ cursor_visitor_(accept_all)
+ {
+ if (!done())
+ {
+ next();
+ }
+ }
+
+ template <class Sourceable>
+ basic_csv_cursor(Sourceable&& source,
+ const basic_csv_decode_options<CharT>& options = basic_csv_decode_options<CharT>(),
+ std::function<bool(csv_errc,const ser_context&)> err_handler = default_csv_parsing(),
+ const Allocator& alloc = Allocator(),
+ typename std::enable_if<std::is_constructible<jsoncons::basic_string_view<CharT>,Sourceable>::value>::type* = 0)
+ : source_(),
+ parser_(options,err_handler,alloc),
+ cursor_visitor_(accept_all)
+ {
+ jsoncons::basic_string_view<CharT> sv(std::forward<Sourceable>(source));
+ initialize_with_string_view(sv);
+ }
+
+
+ // Constructors that set parse error codes
+ template <class Sourceable>
+ basic_csv_cursor(Sourceable&& source,
+ std::error_code& ec)
+ : basic_csv_cursor(std::allocator_arg, Allocator(),
+ std::forward<Sourceable>(source),
+ basic_csv_decode_options<CharT>(),
+ default_csv_parsing(),
+ ec)
+ {
+ }
+
+ template <class Sourceable>
+ basic_csv_cursor(Sourceable&& source,
+ const basic_csv_decode_options<CharT>& options,
+ std::error_code& ec)
+ : basic_csv_cursor(std::allocator_arg, Allocator(),
+ std::forward<Sourceable>(source),
+ options,
+ default_csv_parsing(),
+ ec)
+ {
+ }
+
+ template <class Sourceable>
+ basic_csv_cursor(Sourceable&& source,
+ const basic_csv_decode_options<CharT>& options,
+ std::function<bool(csv_errc,const ser_context&)> err_handler,
+ std::error_code& ec)
+ : basic_csv_cursor(std::allocator_arg, Allocator(),
+ std::forward<Sourceable>(source),
+ options,
+ err_handler,
+ ec)
+ {
+ }
+
+ template <class Sourceable>
+ basic_csv_cursor(std::allocator_arg_t, const Allocator& alloc,
+ Sourceable&& source,
+ const basic_csv_decode_options<CharT>& options,
+ std::function<bool(csv_errc,const ser_context&)> err_handler,
+ std::error_code& ec,
+ typename std::enable_if<!std::is_constructible<jsoncons::basic_string_view<CharT>,Sourceable>::value>::type* = 0)
+ : source_(std::forward<Sourceable>(source)),
+ parser_(options,err_handler,alloc),
+ cursor_visitor_(accept_all)
+ {
+ if (!done())
+ {
+ next(ec);
+ }
+ }
+
+ template <class Sourceable>
+ basic_csv_cursor(std::allocator_arg_t, const Allocator& alloc,
+ Sourceable&& source,
+ const basic_csv_decode_options<CharT>& options,
+ std::function<bool(csv_errc,const ser_context&)> err_handler,
+ std::error_code& ec,
+ typename std::enable_if<std::is_constructible<jsoncons::basic_string_view<CharT>,Sourceable>::value>::type* = 0)
+ : source_(),
+ parser_(options,err_handler,alloc),
+ cursor_visitor_(accept_all)
+ {
+ jsoncons::basic_string_view<CharT> sv(std::forward<Sourceable>(source));
+ initialize_with_string_view(sv, ec);
+ }
+
+ template <class Sourceable>
+ typename std::enable_if<!std::is_constructible<jsoncons::basic_string_view<CharT>,Sourceable>::value>::type
+ reset(Sourceable&& source)
+ {
+ source_ = std::forward<Sourceable>(source);
+ parser_.reinitialize();
+ cursor_visitor_.reset();
+ if (!done())
+ {
+ next();
+ }
+ }
+
+ template <class Sourceable>
+ typename std::enable_if<std::is_constructible<jsoncons::basic_string_view<CharT>,Sourceable>::value>::type
+ reset(Sourceable&& source)
+ {
+ source_ = {};
+ parser_.reinitialize();
+ cursor_visitor_.reset();
+ initialize_with_string_view(std::forward<Sourceable>(source));
+ }
+
+ template <class Sourceable>
+ typename std::enable_if<!std::is_constructible<jsoncons::basic_string_view<CharT>,Sourceable>::value>::type
+ reset(Sourceable&& source, std::error_code& ec)
+ {
+ source_ = std::forward<Sourceable>(source);
+ parser_.reinitialize();
+ cursor_visitor_.reset();
+ if (!done())
+ {
+ next(ec);
+ }
+ }
+
+ template <class Sourceable>
+ typename std::enable_if<std::is_constructible<jsoncons::basic_string_view<CharT>,Sourceable>::value>::type
+ reset(Sourceable&& source, std::error_code& ec)
+ {
+ source_ = {};
+ parser_.reinitialize();
+ initialize_with_string_view(std::forward<Sourceable>(source), ec);
+ }
+
+ bool done() const override
+ {
+ return parser_.done();
+ }
+
+ const basic_staj_event<CharT>& current() const override
+ {
+ return cursor_visitor_.event();
+ }
+
+ void read_to(basic_json_visitor<CharT>& visitor) override
+ {
+ std::error_code ec;
+ read_to(visitor, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column()));
+ }
+ }
+
+ void read_to(basic_json_visitor<CharT>& visitor,
+ std::error_code& ec) override
+ {
+ if (staj_to_saj_event(cursor_visitor_.event(), visitor, *this, ec))
+ {
+ read_next(visitor, ec);
+ }
+ }
+
+ void next() override
+ {
+ std::error_code ec;
+ next(ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column()));
+ }
+ }
+
+ void next(std::error_code& ec) override
+ {
+ read_next(ec);
+ }
+
+ static bool accept_all(const basic_staj_event<CharT>&, const ser_context&)
+ {
+ return true;
+ }
+
+ const ser_context& context() const override
+ {
+ return *this;
+ }
+
+ bool eof() const
+ {
+ return parser_.source_exhausted() && source_.eof();
+ }
+
+ std::size_t line() const override
+ {
+ return parser_.line();
+ }
+
+ std::size_t column() const override
+ {
+ return parser_.column();
+ }
+
+ friend
+ basic_staj_filter_view<CharT> operator|(basic_csv_cursor& cursor,
+ std::function<bool(const basic_staj_event<CharT>&, const ser_context&)> pred)
+ {
+ return basic_staj_filter_view<CharT>(cursor, pred);
+ }
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+
+ JSONCONS_DEPRECATED_MSG("Instead, use read_to(basic_json_visitor<CharT>&)")
+ void read(basic_json_visitor<CharT>& visitor)
+ {
+ read_to(visitor);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use read_to(basic_json_visitor<CharT>&, std::error_code&)")
+ void read(basic_json_visitor<CharT>& visitor,
+ std::error_code& ec)
+ {
+ read_to(visitor, ec);
+ }
+#endif
+private:
+
+ void initialize_with_string_view(string_view_type sv)
+ {
+ auto r = unicode_traits::detect_json_encoding(sv.data(), sv.size());
+ if (!(r.encoding == unicode_traits::encoding_kind::utf8 || r.encoding == unicode_traits::encoding_kind::undetected))
+ {
+ JSONCONS_THROW(ser_error(json_errc::illegal_unicode_character,parser_.line(),parser_.column()));
+ }
+ std::size_t offset = (r.ptr - sv.data());
+ parser_.update(sv.data()+offset,sv.size()-offset);
+ if (!done())
+ {
+ next();
+ }
+ }
+
+ void initialize_with_string_view(string_view_type sv, std::error_code& ec)
+ {
+ auto r = unicode_traits::detect_encoding_from_bom(sv.data(), sv.size());
+ if (!(r.encoding == unicode_traits::encoding_kind::utf8 || r.encoding == unicode_traits::encoding_kind::undetected))
+ {
+ ec = json_errc::illegal_unicode_character;
+ return;
+ }
+ std::size_t offset = (r.ptr - sv.data());
+ parser_.update(sv.data()+offset,sv.size()-offset);
+ if (!done())
+ {
+ next(ec);
+ }
+ }
+
+ void read_next(std::error_code& ec)
+ {
+ read_next(cursor_visitor_, ec);
+ }
+
+ void read_next(basic_json_visitor<CharT>& visitor, std::error_code& ec)
+ {
+ parser_.restart();
+ while (!parser_.stopped())
+ {
+ if (parser_.source_exhausted())
+ {
+ auto s = source_.read_buffer(ec);
+ if (ec) return;
+ if (s.size() > 0)
+ {
+ parser_.update(s.data(),s.size());
+ }
+ }
+ parser_.parse_some(visitor, ec);
+ if (ec) return;
+ }
+ }
+};
+
+using csv_stream_cursor = basic_csv_cursor<char,jsoncons::stream_source<char>>;
+using csv_string_cursor = basic_csv_cursor<char,jsoncons::string_source<char>>;
+using wcsv_stream_cursor = basic_csv_cursor<wchar_t,jsoncons::stream_source<wchar_t>>;
+using wcsv_string_cursor = basic_csv_cursor<wchar_t,jsoncons::string_source<wchar_t>>;
+
+using csv_cursor = basic_csv_cursor<char>;
+using wcsv_cursor = basic_csv_cursor<wchar_t>;
+
+}}
+
+#endif
+
diff --git a/include/jsoncons_ext/csv/csv_encoder.hpp b/include/jsoncons_ext/csv/csv_encoder.hpp
new file mode 100644
index 0000000..49c1a3d
--- /dev/null
+++ b/include/jsoncons_ext/csv/csv_encoder.hpp
@@ -0,0 +1,954 @@
+// Copyright 2013 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_CSV_CSV_ENCODER_HPP
+#define JSONCONS_CSV_CSV_ENCODER_HPP
+
+#include <array> // std::array
+#include <string>
+#include <vector>
+#include <ostream>
+#include <utility> // std::move
+#include <unordered_map> // std::unordered_map
+#include <memory> // std::allocator
+#include <limits> // std::numeric_limits
+#include <jsoncons/json_exception.hpp>
+#include <jsoncons/json_visitor.hpp>
+#include <jsoncons/detail/write_number.hpp>
+#include <jsoncons_ext/csv/csv_options.hpp>
+#include <jsoncons/sink.hpp>
+
+namespace jsoncons { namespace csv {
+
+template<class CharT,class Sink=jsoncons::stream_sink<CharT>,class Allocator=std::allocator<char>>
+class basic_csv_encoder final : public basic_json_visitor<CharT>
+{
+public:
+ using char_type = CharT;
+ using typename basic_json_visitor<CharT>::string_view_type;
+ using sink_type = Sink;
+
+ using allocator_type = Allocator;
+ using char_allocator_type = typename std::allocator_traits<allocator_type>:: template rebind_alloc<CharT>;
+ using string_type = std::basic_string<CharT, std::char_traits<CharT>, char_allocator_type>;
+ using string_allocator_type = typename std::allocator_traits<allocator_type>:: template rebind_alloc<string_type>;
+ using string_string_allocator_type = typename std::allocator_traits<allocator_type>:: template rebind_alloc<std::pair<const string_type,string_type>>;
+
+private:
+ static jsoncons::basic_string_view<CharT> null_constant()
+ {
+ static jsoncons::basic_string_view<CharT> k = JSONCONS_STRING_VIEW_CONSTANT(CharT,"null");
+ return k;
+ }
+ static jsoncons::basic_string_view<CharT> true_constant()
+ {
+ static jsoncons::basic_string_view<CharT> k = JSONCONS_STRING_VIEW_CONSTANT(CharT,"true");
+ return k;
+ }
+ static jsoncons::basic_string_view<CharT> false_constant()
+ {
+ static jsoncons::basic_string_view<CharT> k = JSONCONS_STRING_VIEW_CONSTANT(CharT,"false");
+ return k;
+ }
+
+ enum class stack_item_kind
+ {
+ row_mapping,
+ column_mapping,
+ object,
+ row,
+ column,
+ object_multi_valued_field,
+ row_multi_valued_field,
+ column_multi_valued_field
+ };
+
+ struct stack_item
+ {
+ stack_item_kind item_kind_;
+ std::size_t count_;
+
+ stack_item(stack_item_kind item_kind) noexcept
+ : item_kind_(item_kind), count_(0)
+ {
+ }
+
+ bool is_object() const
+ {
+ return item_kind_ == stack_item_kind::object;
+ }
+
+ stack_item_kind item_kind() const
+ {
+ return item_kind_;
+ }
+ };
+
+ Sink sink_;
+ const basic_csv_encode_options<CharT> options_;
+ allocator_type alloc_;
+
+ std::vector<stack_item> stack_;
+ jsoncons::detail::write_double fp_;
+ std::vector<string_type,string_allocator_type> strings_buffer_;
+
+ std::unordered_map<string_type,string_type, std::hash<string_type>,std::equal_to<string_type>,string_string_allocator_type> buffered_line_;
+ string_type name_;
+ std::size_t column_index_;
+ std::vector<std::size_t> row_counts_;
+
+ // Noncopyable and nonmoveable
+ basic_csv_encoder(const basic_csv_encoder&) = delete;
+ basic_csv_encoder& operator=(const basic_csv_encoder&) = delete;
+public:
+ basic_csv_encoder(Sink&& sink,
+ const Allocator& alloc = Allocator())
+ : basic_csv_encoder(std::forward<Sink>(sink), basic_csv_encode_options<CharT>(), alloc)
+ {
+ }
+
+ basic_csv_encoder(Sink&& sink,
+ const basic_csv_encode_options<CharT>& options,
+ const Allocator& alloc = Allocator())
+ : sink_(std::forward<Sink>(sink)),
+ options_(options),
+ alloc_(alloc),
+ stack_(),
+ fp_(options.float_format(), options.precision()),
+ column_index_(0)
+ {
+ jsoncons::csv::detail::parse_column_names(options.column_names(), strings_buffer_);
+ }
+
+ ~basic_csv_encoder() noexcept
+ {
+ JSONCONS_TRY
+ {
+ sink_.flush();
+ }
+ JSONCONS_CATCH(...)
+ {
+ }
+ }
+
+ void reset()
+ {
+ stack_.clear();
+ strings_buffer_.clear();
+ buffered_line_.clear();
+ name_.clear();
+ column_index_ = 0;
+ row_counts_.clear();
+ }
+
+ void reset(Sink&& sink)
+ {
+ sink_ = std::move(sink);
+ reset();
+ }
+
+private:
+
+ template<class AnyWriter>
+ void escape_string(const CharT* s,
+ std::size_t length,
+ CharT quote_char, CharT quote_escape_char,
+ AnyWriter& sink)
+ {
+ const CharT* begin = s;
+ const CharT* end = s + length;
+ for (const CharT* it = begin; it != end; ++it)
+ {
+ CharT c = *it;
+ if (c == quote_char)
+ {
+ sink.push_back(quote_escape_char);
+ sink.push_back(quote_char);
+ }
+ else
+ {
+ sink.push_back(c);
+ }
+ }
+ }
+
+ void visit_flush() override
+ {
+ sink_.flush();
+ }
+
+ bool visit_begin_object(semantic_tag, const ser_context&, std::error_code& ec) override
+ {
+ if (stack_.empty())
+ {
+ stack_.emplace_back(stack_item_kind::column_mapping);
+ return true;
+ }
+ switch (stack_.back().item_kind_)
+ {
+ case stack_item_kind::row_mapping:
+ stack_.emplace_back(stack_item_kind::object);
+ return true;
+ default: // error
+ ec = csv_errc::source_error;
+ return false;
+ }
+ }
+
+ bool visit_end_object(const ser_context&, std::error_code&) override
+ {
+ JSONCONS_ASSERT(!stack_.empty());
+
+ switch (stack_.back().item_kind_)
+ {
+ case stack_item_kind::object:
+ if (stack_[0].count_ == 0)
+ {
+ for (std::size_t i = 0; i < strings_buffer_.size(); ++i)
+ {
+ if (i > 0)
+ {
+ sink_.push_back(options_.field_delimiter());
+ }
+ sink_.append(strings_buffer_[i].data(),
+ strings_buffer_[i].length());
+ }
+ sink_.append(options_.line_delimiter().data(),
+ options_.line_delimiter().length());
+ }
+ for (std::size_t i = 0; i < strings_buffer_.size(); ++i)
+ {
+ if (i > 0)
+ {
+ sink_.push_back(options_.field_delimiter());
+ }
+ auto it = buffered_line_.find(strings_buffer_[i]);
+ if (it != buffered_line_.end())
+ {
+ sink_.append(it->second.data(),it->second.length());
+ it->second.clear();
+ }
+ }
+ sink_.append(options_.line_delimiter().data(), options_.line_delimiter().length());
+ break;
+ case stack_item_kind::column_mapping:
+ {
+ for (const auto& item : strings_buffer_)
+ {
+ sink_.append(item.data(), item.size());
+ sink_.append(options_.line_delimiter().data(), options_.line_delimiter().length());
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ stack_.pop_back();
+ if (!stack_.empty())
+ {
+ end_value();
+ }
+ return true;
+ }
+
+ bool visit_begin_array(semantic_tag, const ser_context&, std::error_code& ec) override
+ {
+ if (stack_.empty())
+ {
+ stack_.emplace_back(stack_item_kind::row_mapping);
+ return true;
+ }
+ switch (stack_.back().item_kind_)
+ {
+ case stack_item_kind::row_mapping:
+ stack_.emplace_back(stack_item_kind::row);
+ if (stack_[0].count_ == 0)
+ {
+ for (std::size_t i = 0; i < strings_buffer_.size(); ++i)
+ {
+ if (i > 0)
+ {
+ sink_.push_back(options_.field_delimiter());
+ }
+ sink_.append(strings_buffer_[i].data(),strings_buffer_[i].length());
+ }
+ if (strings_buffer_.size() > 0)
+ {
+ sink_.append(options_.line_delimiter().data(),
+ options_.line_delimiter().length());
+ }
+ }
+ return true;
+ case stack_item_kind::object:
+ stack_.emplace_back(stack_item_kind::object_multi_valued_field);
+ return true;
+ case stack_item_kind::column_mapping:
+ stack_.emplace_back(stack_item_kind::column);
+ row_counts_.push_back(1);
+ if (strings_buffer_.size() <= row_counts_.back())
+ {
+ strings_buffer_.emplace_back();
+ }
+ return true;
+ case stack_item_kind::column:
+ {
+ if (strings_buffer_.size() <= row_counts_.back())
+ {
+ strings_buffer_.emplace_back();
+ }
+ jsoncons::string_sink<std::basic_string<CharT>> bo(strings_buffer_[row_counts_.back()]);
+ begin_value(bo);
+ stack_.emplace_back(stack_item_kind::column_multi_valued_field);
+ return true;
+ }
+ case stack_item_kind::row:
+ begin_value(sink_);
+ stack_.emplace_back(stack_item_kind::row_multi_valued_field);
+ return true;
+ default: // error
+ ec = csv_errc::source_error;
+ return false;
+ }
+ }
+
+ bool visit_end_array(const ser_context&, std::error_code&) override
+ {
+ JSONCONS_ASSERT(!stack_.empty());
+ switch (stack_.back().item_kind_)
+ {
+ case stack_item_kind::row:
+ sink_.append(options_.line_delimiter().data(),
+ options_.line_delimiter().length());
+ break;
+ case stack_item_kind::column:
+ ++column_index_;
+ break;
+ default:
+ break;
+ }
+ stack_.pop_back();
+
+ if (!stack_.empty())
+ {
+ end_value();
+ }
+ return true;
+ }
+
+ bool visit_key(const string_view_type& name, const ser_context&, std::error_code&) override
+ {
+ JSONCONS_ASSERT(!stack_.empty());
+ switch (stack_.back().item_kind_)
+ {
+ case stack_item_kind::object:
+ {
+ name_ = string_type(name);
+ buffered_line_[string_type(name)] = std::basic_string<CharT>();
+ if (stack_[0].count_ == 0 && options_.column_names().size() == 0)
+ {
+ strings_buffer_.emplace_back(name);
+ }
+ break;
+ }
+ case stack_item_kind::column_mapping:
+ {
+ if (strings_buffer_.empty())
+ {
+ strings_buffer_.emplace_back(name);
+ }
+ else
+ {
+ strings_buffer_[0].push_back(options_.field_delimiter());
+ strings_buffer_[0].append(string_type(name));
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ return true;
+ }
+
+ bool visit_null(semantic_tag, const ser_context&, std::error_code&) override
+ {
+ JSONCONS_ASSERT(!stack_.empty());
+ switch (stack_.back().item_kind_)
+ {
+ case stack_item_kind::object:
+ case stack_item_kind::object_multi_valued_field:
+ {
+ auto it = buffered_line_.find(name_);
+ if (it != buffered_line_.end())
+ {
+ std::basic_string<CharT> s;
+ jsoncons::string_sink<std::basic_string<CharT>> bo(s);
+ write_null_value(bo);
+ bo.flush();
+ if (!it->second.empty() && options_.subfield_delimiter() != char_type())
+ {
+ it->second.push_back(options_.subfield_delimiter());
+ }
+ it->second.append(s);
+ }
+ break;
+ }
+ case stack_item_kind::row:
+ case stack_item_kind::row_multi_valued_field:
+ write_null_value(sink_);
+ break;
+ case stack_item_kind::column:
+ {
+ if (strings_buffer_.size() <= row_counts_.back())
+ {
+ strings_buffer_.emplace_back();
+ }
+ jsoncons::string_sink<std::basic_string<CharT>> bo(strings_buffer_[row_counts_.back()]);
+ write_null_value(bo);
+ break;
+ }
+ case stack_item_kind::column_multi_valued_field:
+ {
+ jsoncons::string_sink<std::basic_string<CharT>> bo(strings_buffer_[row_counts_.back()]);
+ write_null_value(bo);
+ break;
+ }
+ default:
+ break;
+ }
+ return true;
+ }
+
+ bool visit_string(const string_view_type& sv, semantic_tag, const ser_context&, std::error_code&) override
+ {
+ JSONCONS_ASSERT(!stack_.empty());
+ switch (stack_.back().item_kind_)
+ {
+ case stack_item_kind::object:
+ case stack_item_kind::object_multi_valued_field:
+ {
+ auto it = buffered_line_.find(name_);
+ if (it != buffered_line_.end())
+ {
+ std::basic_string<CharT> s;
+ jsoncons::string_sink<std::basic_string<CharT>> bo(s);
+ write_string_value(sv,bo);
+ bo.flush();
+ if (!it->second.empty() && options_.subfield_delimiter() != char_type())
+ {
+ it->second.push_back(options_.subfield_delimiter());
+ }
+ it->second.append(s);
+ }
+ break;
+ }
+ case stack_item_kind::row:
+ case stack_item_kind::row_multi_valued_field:
+ write_string_value(sv,sink_);
+ break;
+ case stack_item_kind::column:
+ {
+ if (strings_buffer_.size() <= row_counts_.back())
+ {
+ strings_buffer_.emplace_back();
+ }
+ jsoncons::string_sink<std::basic_string<CharT>> bo(strings_buffer_[row_counts_.back()]);
+ write_string_value(sv,bo);
+ break;
+ }
+ case stack_item_kind::column_multi_valued_field:
+ {
+ jsoncons::string_sink<std::basic_string<CharT>> bo(strings_buffer_[row_counts_.back()]);
+ write_string_value(sv,bo);
+ break;
+ }
+ default:
+ break;
+ }
+ return true;
+ }
+
+ bool visit_byte_string(const byte_string_view& b,
+ semantic_tag tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ byte_string_chars_format encoding_hint;
+ switch (tag)
+ {
+ case semantic_tag::base16:
+ encoding_hint = byte_string_chars_format::base16;
+ break;
+ case semantic_tag::base64:
+ encoding_hint = byte_string_chars_format::base64;
+ break;
+ case semantic_tag::base64url:
+ encoding_hint = byte_string_chars_format::base64url;
+ break;
+ default:
+ encoding_hint = byte_string_chars_format::none;
+ break;
+ }
+ byte_string_chars_format format = jsoncons::detail::resolve_byte_string_chars_format(encoding_hint,byte_string_chars_format::none,byte_string_chars_format::base64url);
+
+ std::basic_string<CharT> s;
+ switch (format)
+ {
+ case byte_string_chars_format::base16:
+ {
+ encode_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);
+ visit_string(s, semantic_tag::none, context, ec);
+ break;
+ }
+ case byte_string_chars_format::base64url:
+ {
+ encode_base64url(b.begin(),b.end(),s);
+ visit_string(s, semantic_tag::none, context, ec);
+ break;
+ }
+ default:
+ {
+ JSONCONS_UNREACHABLE();
+ }
+ }
+
+ return true;
+ }
+
+ bool visit_double(double val,
+ semantic_tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ JSONCONS_ASSERT(!stack_.empty());
+ switch (stack_.back().item_kind_)
+ {
+ case stack_item_kind::object:
+ case stack_item_kind::object_multi_valued_field:
+ {
+ auto it = buffered_line_.find(name_);
+ if (it != buffered_line_.end())
+ {
+ std::basic_string<CharT> s;
+ jsoncons::string_sink<std::basic_string<CharT>> bo(s);
+ write_double_value(val, context, bo, ec);
+ bo.flush();
+ if (!it->second.empty() && options_.subfield_delimiter() != char_type())
+ {
+ it->second.push_back(options_.subfield_delimiter());
+ }
+ it->second.append(s);
+ }
+ break;
+ }
+ case stack_item_kind::row:
+ case stack_item_kind::row_multi_valued_field:
+ write_double_value(val, context, sink_, ec);
+ break;
+ case stack_item_kind::column:
+ {
+ if (strings_buffer_.size() <= row_counts_.back())
+ {
+ strings_buffer_.emplace_back();
+ }
+ jsoncons::string_sink<std::basic_string<CharT>> bo(strings_buffer_[row_counts_.back()]);
+ write_double_value(val, context, bo, ec);
+ break;
+ }
+ case stack_item_kind::column_multi_valued_field:
+ {
+ jsoncons::string_sink<std::basic_string<CharT>> bo(strings_buffer_[row_counts_.back()]);
+ write_double_value(val, context, bo, ec);
+ break;
+ }
+ default:
+ break;
+ }
+ return true;
+ }
+
+ bool visit_int64(int64_t val,
+ semantic_tag,
+ const ser_context&,
+ std::error_code&) override
+ {
+ JSONCONS_ASSERT(!stack_.empty());
+ switch (stack_.back().item_kind_)
+ {
+ case stack_item_kind::object:
+ case stack_item_kind::object_multi_valued_field:
+ {
+ auto it = buffered_line_.find(name_);
+ if (it != buffered_line_.end())
+ {
+ std::basic_string<CharT> s;
+ jsoncons::string_sink<std::basic_string<CharT>> bo(s);
+ write_int64_value(val,bo);
+ bo.flush();
+ if (!it->second.empty() && options_.subfield_delimiter() != char_type())
+ {
+ it->second.push_back(options_.subfield_delimiter());
+ }
+ it->second.append(s);
+ }
+ break;
+ }
+ case stack_item_kind::row:
+ case stack_item_kind::row_multi_valued_field:
+ write_int64_value(val,sink_);
+ break;
+ case stack_item_kind::column:
+ {
+ if (strings_buffer_.size() <= row_counts_.back())
+ {
+ strings_buffer_.emplace_back();
+ }
+ jsoncons::string_sink<std::basic_string<CharT>> bo(strings_buffer_[row_counts_.back()]);
+ write_int64_value(val, bo);
+ break;
+ }
+ case stack_item_kind::column_multi_valued_field:
+ {
+ jsoncons::string_sink<std::basic_string<CharT>> bo(strings_buffer_[row_counts_.back()]);
+ write_int64_value(val, bo);
+ break;
+ }
+ default:
+ break;
+ }
+ return true;
+ }
+
+ bool visit_uint64(uint64_t val,
+ semantic_tag,
+ const ser_context&,
+ std::error_code&) override
+ {
+ JSONCONS_ASSERT(!stack_.empty());
+ switch (stack_.back().item_kind_)
+ {
+ case stack_item_kind::object:
+ case stack_item_kind::object_multi_valued_field:
+ {
+ auto it = buffered_line_.find(name_);
+ if (it != buffered_line_.end())
+ {
+ std::basic_string<CharT> s;
+ jsoncons::string_sink<std::basic_string<CharT>> bo(s);
+ write_uint64_value(val, bo);
+ bo.flush();
+ if (!it->second.empty() && options_.subfield_delimiter() != char_type())
+ {
+ it->second.push_back(options_.subfield_delimiter());
+ }
+ it->second.append(s);
+ }
+ break;
+ }
+ case stack_item_kind::row:
+ case stack_item_kind::row_multi_valued_field:
+ write_uint64_value(val,sink_);
+ break;
+ case stack_item_kind::column:
+ {
+ if (strings_buffer_.size() <= row_counts_.back())
+ {
+ strings_buffer_.emplace_back();
+ }
+ jsoncons::string_sink<std::basic_string<CharT>> bo(strings_buffer_[row_counts_.back()]);
+ write_uint64_value(val, bo);
+ break;
+ }
+ case stack_item_kind::column_multi_valued_field:
+ {
+ jsoncons::string_sink<std::basic_string<CharT>> bo(strings_buffer_[row_counts_.back()]);
+ write_uint64_value(val, bo);
+ break;
+ }
+ default:
+ break;
+ }
+ return true;
+ }
+
+ bool visit_bool(bool val, semantic_tag, const ser_context&, std::error_code&) override
+ {
+ JSONCONS_ASSERT(!stack_.empty());
+ switch (stack_.back().item_kind_)
+ {
+ case stack_item_kind::object:
+ case stack_item_kind::object_multi_valued_field:
+ {
+ auto it = buffered_line_.find(name_);
+ if (it != buffered_line_.end())
+ {
+ std::basic_string<CharT> s;
+ jsoncons::string_sink<std::basic_string<CharT>> bo(s);
+ write_bool_value(val,bo);
+ bo.flush();
+ if (!it->second.empty() && options_.subfield_delimiter() != char_type())
+ {
+ it->second.push_back(options_.subfield_delimiter());
+ }
+ it->second.append(s);
+ }
+ break;
+ }
+ case stack_item_kind::row:
+ case stack_item_kind::row_multi_valued_field:
+ write_bool_value(val,sink_);
+ break;
+ case stack_item_kind::column:
+ {
+ if (strings_buffer_.size() <= row_counts_.back())
+ {
+ strings_buffer_.emplace_back();
+ }
+ jsoncons::string_sink<std::basic_string<CharT>> bo(strings_buffer_[row_counts_.back()]);
+ write_bool_value(val, bo);
+ break;
+ }
+ case stack_item_kind::column_multi_valued_field:
+ {
+ jsoncons::string_sink<std::basic_string<CharT>> bo(strings_buffer_[row_counts_.back()]);
+ write_bool_value(val, bo);
+ break;
+ }
+ default:
+ break;
+ }
+ return true;
+ }
+
+ template <class AnyWriter>
+ bool do_string_value(const CharT* s, std::size_t length, AnyWriter& sink)
+ {
+ bool quote = false;
+ if (options_.quote_style() == quote_style_kind::all || options_.quote_style() == quote_style_kind::nonnumeric ||
+ (options_.quote_style() == quote_style_kind::minimal &&
+ (std::char_traits<CharT>::find(s, length, options_.field_delimiter()) != nullptr || std::char_traits<CharT>::find(s, length, options_.quote_char()) != nullptr)))
+ {
+ quote = true;
+ sink.push_back(options_.quote_char());
+ }
+ escape_string(s, length, options_.quote_char(), options_.quote_escape_char(), sink);
+ if (quote)
+ {
+ sink.push_back(options_.quote_char());
+ }
+
+ return true;
+ }
+
+ template <class AnyWriter>
+ void write_string_value(const string_view_type& value, AnyWriter& sink)
+ {
+ begin_value(sink);
+ do_string_value(value.data(),value.length(),sink);
+ end_value();
+ }
+
+ template <class AnyWriter>
+ void write_double_value(double val, const ser_context& context, AnyWriter& sink, std::error_code& ec)
+ {
+ begin_value(sink);
+
+ if (!std::isfinite(val))
+ {
+ if ((std::isnan)(val))
+ {
+ if (options_.enable_nan_to_num())
+ {
+ sink.append(options_.nan_to_num().data(), options_.nan_to_num().length());
+ }
+ else if (options_.enable_nan_to_str())
+ {
+ visit_string(options_.nan_to_str(), semantic_tag::none, context, ec);
+ }
+ else
+ {
+ sink.append(null_constant().data(), null_constant().size());
+ }
+ }
+ else if (val == std::numeric_limits<double>::infinity())
+ {
+ if (options_.enable_inf_to_num())
+ {
+ sink.append(options_.inf_to_num().data(), options_.inf_to_num().length());
+ }
+ else if (options_.enable_inf_to_str())
+ {
+ visit_string(options_.inf_to_str(), semantic_tag::none, context, ec);
+ }
+ else
+ {
+ sink.append(null_constant().data(), null_constant().size());
+ }
+ }
+ else
+ {
+ if (options_.enable_neginf_to_num())
+ {
+ sink.append(options_.neginf_to_num().data(), options_.neginf_to_num().length());
+ }
+ else if (options_.enable_neginf_to_str())
+ {
+ visit_string(options_.neginf_to_str(), semantic_tag::none, context, ec);
+ }
+ else
+ {
+ sink.append(null_constant().data(), null_constant().size());
+ }
+ }
+ }
+ else
+ {
+ fp_(val, sink);
+ }
+
+ end_value();
+
+ }
+
+ template <class AnyWriter>
+ void write_int64_value(int64_t val, AnyWriter& sink)
+ {
+ begin_value(sink);
+
+ jsoncons::detail::from_integer(val,sink);
+
+ end_value();
+ }
+
+ template <class AnyWriter>
+ void write_uint64_value(uint64_t val, AnyWriter& sink)
+ {
+ begin_value(sink);
+
+ jsoncons::detail::from_integer(val,sink);
+
+ end_value();
+ }
+
+ template <class AnyWriter>
+ void write_bool_value(bool val, AnyWriter& sink)
+ {
+ begin_value(sink);
+
+ if (val)
+ {
+ sink.append(true_constant().data(), true_constant().size());
+ }
+ else
+ {
+ sink.append(false_constant().data(), false_constant().size());
+ }
+
+ end_value();
+ }
+
+ template <class AnyWriter>
+ bool write_null_value(AnyWriter& sink)
+ {
+ begin_value(sink);
+ sink.append(null_constant().data(), null_constant().size());
+ end_value();
+ return true;
+ }
+
+ template <class AnyWriter>
+ void begin_value(AnyWriter& sink)
+ {
+ JSONCONS_ASSERT(!stack_.empty());
+ switch (stack_.back().item_kind_)
+ {
+ case stack_item_kind::row:
+ if (stack_.back().count_ > 0)
+ {
+ sink.push_back(options_.field_delimiter());
+ }
+ break;
+ case stack_item_kind::column:
+ {
+ if (row_counts_.size() >= 3)
+ {
+ for (std::size_t i = row_counts_.size()-2; i-- > 0;)
+ {
+ if (row_counts_[i] <= row_counts_.back())
+ {
+ sink.push_back(options_.field_delimiter());
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+ if (column_index_ > 0)
+ {
+ sink.push_back(options_.field_delimiter());
+ }
+ break;
+ }
+ case stack_item_kind::row_multi_valued_field:
+ case stack_item_kind::column_multi_valued_field:
+ if (stack_.back().count_ > 0 && options_.subfield_delimiter() != char_type())
+ {
+ sink.push_back(options_.subfield_delimiter());
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ void end_value()
+ {
+ JSONCONS_ASSERT(!stack_.empty());
+ switch(stack_.back().item_kind_)
+ {
+ case stack_item_kind::row:
+ {
+ ++stack_.back().count_;
+ break;
+ }
+ case stack_item_kind::column:
+ {
+ ++row_counts_.back();
+ break;
+ }
+ default:
+ ++stack_.back().count_;
+ break;
+ }
+ }
+};
+
+using csv_stream_encoder = basic_csv_encoder<char>;
+using csv_string_encoder = basic_csv_encoder<char,jsoncons::string_sink<std::string>>;
+using csv_wstream_encoder = basic_csv_encoder<wchar_t>;
+using wcsv_string_encoder = basic_csv_encoder<wchar_t,jsoncons::string_sink<std::wstring>>;
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+template<class CharT, class Sink = jsoncons::stream_sink<CharT>, class Allocator = std::allocator<CharT>>
+using basic_csv_serializer = basic_csv_encoder<CharT,Sink,Allocator>;
+
+JSONCONS_DEPRECATED_MSG("Instead, use csv_stream_encoder") typedef csv_stream_encoder csv_serializer;
+JSONCONS_DEPRECATED_MSG("Instead, use csv_string_encoder") typedef csv_string_encoder csv_string_serializer;
+JSONCONS_DEPRECATED_MSG("Instead, use csv_stream_encoder") typedef csv_stream_encoder csv_serializer;
+JSONCONS_DEPRECATED_MSG("Instead, use csv_string_encoder") typedef csv_string_encoder csv_string_serializer;
+JSONCONS_DEPRECATED_MSG("Instead, use csv_stream_encoder") typedef csv_stream_encoder csv_encoder;
+JSONCONS_DEPRECATED_MSG("Instead, use wcsv_stream_encoder") typedef csv_stream_encoder wcsv_encoder;
+#endif
+
+}}
+
+#endif
diff --git a/include/jsoncons_ext/csv/csv_error.hpp b/include/jsoncons_ext/csv/csv_error.hpp
new file mode 100644
index 0000000..30255dd
--- /dev/null
+++ b/include/jsoncons_ext/csv/csv_error.hpp
@@ -0,0 +1,85 @@
+/// Copyright 2013 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_CSV_CSV_ERROR_HPP
+#define JSONCONS_CSV_CSV_ERROR_HPP
+
+#include <system_error>
+#include <jsoncons/json_exception.hpp>
+
+namespace jsoncons { namespace csv {
+
+ enum class csv_errc : int
+ {
+ success = 0,
+ unexpected_eof = 1,
+ source_error,
+ expected_quote,
+ syntax_error,
+ invalid_parse_state,
+ invalid_escaped_char,
+ unexpected_char_between_fields
+ };
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+JSONCONS_DEPRECATED_MSG("Instead, use csv_errc") typedef csv_errc csv_parser_errc;
+#endif
+
+class csv_error_category_impl
+ : public std::error_category
+{
+public:
+ const char* name() const noexcept override
+ {
+ return "jsoncons/csv";
+ }
+ std::string message(int ev) const override
+ {
+ switch (static_cast<csv_errc>(ev))
+ {
+ case csv_errc::unexpected_eof:
+ return "Unexpected end of file";
+ case csv_errc::source_error:
+ return "Source error";
+ case csv_errc::expected_quote:
+ return "Expected quote character";
+ case csv_errc::syntax_error:
+ return "CSV syntax error";
+ case csv_errc::invalid_parse_state:
+ return "Invalid CSV parser state";
+ case csv_errc::invalid_escaped_char:
+ return "Invalid character following quote escape character";
+ case csv_errc::unexpected_char_between_fields:
+ return "Unexpected character between fields";
+ default:
+ return "Unknown CSV parser error";
+ }
+ }
+};
+
+inline
+const std::error_category& csv_error_category()
+{
+ static csv_error_category_impl instance;
+ return instance;
+}
+
+inline
+std::error_code make_error_code(csv_errc result)
+{
+ return std::error_code(static_cast<int>(result),csv_error_category());
+}
+
+}}
+
+namespace std {
+ template<>
+ struct is_error_code_enum<jsoncons::csv::csv_errc> : public true_type
+ {
+ };
+}
+
+#endif
diff --git a/include/jsoncons_ext/csv/csv_options.hpp b/include/jsoncons_ext/csv/csv_options.hpp
new file mode 100644
index 0000000..8bd2e22
--- /dev/null
+++ b/include/jsoncons_ext/csv/csv_options.hpp
@@ -0,0 +1,973 @@
+// Copyright 2013 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_CSV_CSV_OPTIONS_HPP
+#define JSONCONS_CSV_CSV_OPTIONS_HPP
+
+#include <string>
+#include <vector>
+#include <utility> // std::pair
+#include <unordered_map> // std::unordered_map
+#include <map>
+#include <limits> // std::numeric_limits
+#include <cwchar>
+#include <jsoncons/json_options.hpp>
+
+namespace jsoncons { namespace csv {
+
+enum class csv_column_type : uint8_t
+{
+ string_t,integer_t,float_t,boolean_t,repeat_t
+};
+
+enum class quote_style_kind : uint8_t
+{
+ minimal,all,nonnumeric,none
+};
+
+enum class csv_mapping_kind : uint8_t
+{
+ n_rows = 1,
+ n_objects,
+ m_columns
+};
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+using mapping_kind = csv_mapping_kind;
+JSONCONS_DEPRECATED_MSG("Instead, use quote_style_kind") typedef quote_style_kind quote_styles;
+JSONCONS_DEPRECATED_MSG("Instead, use quote_style_kind") typedef quote_style_kind quote_style_type;
+JSONCONS_DEPRECATED_MSG("Instead, use csv_mapping_kind") typedef csv_mapping_kind mapping_type;
+#endif
+
+enum class column_state {sequence,label};
+
+struct csv_type_info
+{
+ csv_type_info() = default;
+ csv_type_info(const csv_type_info&) = default;
+ csv_type_info(csv_type_info&&) = default;
+
+ csv_type_info(csv_column_type ctype, std::size_t lev, std::size_t repcount = 0) noexcept
+ {
+ col_type = ctype;
+ level = lev;
+ rep_count = repcount;
+ }
+
+ csv_column_type col_type;
+ std::size_t level;
+ std::size_t rep_count;
+};
+
+namespace detail {
+
+template <class CharT,class Container>
+void parse_column_names(const std::basic_string<CharT>& names,
+ Container& cont)
+{
+ column_state state = column_state::sequence;
+ typename Container::value_type buffer(cont.get_allocator());
+
+ auto p = names.begin();
+ while (p != names.end())
+ {
+ switch (state)
+ {
+ case column_state::sequence:
+ {
+ switch (*p)
+ {
+ case ' ': case '\t':case '\r': case '\n':
+ ++p;
+ break;
+ default:
+ buffer.clear();
+ state = column_state::label;
+ break;
+ }
+ break;
+ }
+ case column_state::label:
+ {
+ switch (*p)
+ {
+ case ',':
+ cont.push_back(buffer);
+ buffer.clear();
+ ++p;
+ state = column_state::sequence;
+ break;
+ default:
+ buffer.push_back(*p);
+ ++p;
+ break;
+ }
+ break;
+ }
+ }
+ }
+ if (state == column_state::label)
+ {
+ cont.push_back(buffer);
+ buffer.clear();
+ }
+}
+
+template <class CharT,class Container>
+void parse_column_types(const std::basic_string<CharT>& types,
+ Container& column_types)
+{
+ const std::map<jsoncons::basic_string_view<CharT>,csv_column_type> type_dictionary =
+ {
+
+ {JSONCONS_STRING_VIEW_CONSTANT(CharT,"string"),csv_column_type::string_t},
+ {JSONCONS_STRING_VIEW_CONSTANT(CharT,"integer"),csv_column_type::integer_t},
+ {JSONCONS_STRING_VIEW_CONSTANT(CharT,"float"),csv_column_type::float_t},
+ {JSONCONS_STRING_VIEW_CONSTANT(CharT,"boolean"),csv_column_type::boolean_t}
+ };
+
+ column_state state = column_state::sequence;
+ int depth = 0;
+ std::basic_string<CharT> buffer;
+
+ auto p = types.begin();
+ while (p != types.end())
+ {
+ switch (state)
+ {
+ case column_state::sequence:
+ {
+ switch (*p)
+ {
+ case ' ': case '\t':case '\r': case '\n':
+ ++p;
+ break;
+ case '[':
+ ++depth;
+ ++p;
+ break;
+ case ']':
+ JSONCONS_ASSERT(depth > 0);
+ --depth;
+ ++p;
+ break;
+ case '*':
+ {
+ JSONCONS_ASSERT(column_types.size() != 0);
+ std::size_t offset = 0;
+ std::size_t level = column_types.size() > 0 ? column_types.back().level: 0;
+ if (level > 0)
+ {
+ for (auto it = column_types.rbegin();
+ it != column_types.rend() && level == it->level;
+ ++it)
+ {
+ ++offset;
+ }
+ }
+ else
+ {
+ offset = 1;
+ }
+ column_types.emplace_back(csv_column_type::repeat_t,depth,offset);
+ ++p;
+ break;
+ }
+ default:
+ buffer.clear();
+ state = column_state::label;
+ break;
+ }
+ break;
+ }
+ case column_state::label:
+ {
+ switch (*p)
+ {
+ case '*':
+ {
+ auto it = type_dictionary.find(buffer);
+ if (it != type_dictionary.end())
+ {
+ column_types.emplace_back(it->second,depth);
+ buffer.clear();
+ }
+ else
+ {
+ JSONCONS_ASSERT(false);
+ }
+ state = column_state::sequence;
+ break;
+ }
+ case ',':
+ {
+ auto it = type_dictionary.find(buffer);
+ if (it != type_dictionary.end())
+ {
+ column_types.emplace_back(it->second,depth);
+ buffer.clear();
+ }
+ else
+ {
+ JSONCONS_ASSERT(false);
+ }
+ ++p;
+ state = column_state::sequence;
+ break;
+ }
+ case ']':
+ {
+ JSONCONS_ASSERT(depth > 0);
+ auto it = type_dictionary.find(buffer);
+ if (it != type_dictionary.end())
+ {
+ column_types.emplace_back(it->second,depth);
+ buffer.clear();
+ }
+ else
+ {
+ JSONCONS_ASSERT(false);
+ }
+ --depth;
+ ++p;
+ state = column_state::sequence;
+ break;
+ }
+ default:
+ {
+ buffer.push_back(*p);
+ ++p;
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+ if (state == column_state::label)
+ {
+ auto it = type_dictionary.find(buffer);
+ if (it != type_dictionary.end())
+ {
+ column_types.emplace_back(it->second,depth);
+ buffer.clear();
+ }
+ else
+ {
+ JSONCONS_ASSERT(false);
+ }
+ }
+}
+
+} // detail
+
+template <class CharT>
+class basic_csv_options;
+
+template <class CharT>
+class basic_csv_options_common
+{
+ friend class basic_csv_options<CharT>;
+public:
+ using char_type = CharT;
+ using string_type = std::basic_string<CharT>;
+private:
+ char_type field_delimiter_;
+ char_type quote_char_;
+ char_type quote_escape_char_;
+ char_type subfield_delimiter_;
+
+ bool enable_nan_to_num_:1;
+ bool enable_inf_to_num_:1;
+ bool enable_neginf_to_num_:1;
+ bool enable_nan_to_str_:1;
+ bool enable_inf_to_str_:1;
+ bool enable_neginf_to_str_:1;
+ bool enable_str_to_nan_:1;
+ bool enable_str_to_inf_:1;
+ bool enable_str_to_neginf_:1;
+
+ string_type nan_to_num_;
+ string_type inf_to_num_;
+ string_type neginf_to_num_;
+ string_type nan_to_str_;
+ string_type inf_to_str_;
+ string_type neginf_to_str_;
+ string_type column_names_;
+
+protected:
+ basic_csv_options_common()
+ : field_delimiter_(','),
+ quote_char_('\"'),
+ quote_escape_char_('\"'),
+ subfield_delimiter_(char_type()),
+ enable_nan_to_num_(false),
+ enable_inf_to_num_(false),
+ enable_neginf_to_num_(false),
+ enable_nan_to_str_(false),
+ enable_inf_to_str_(false),
+ enable_neginf_to_str_(false),
+ enable_str_to_nan_(false),
+ enable_str_to_inf_(false),
+ enable_str_to_neginf_(false)
+ {
+ }
+
+ basic_csv_options_common(const basic_csv_options_common&) = default;
+ basic_csv_options_common& operator=(const basic_csv_options_common&) = default;
+
+ virtual ~basic_csv_options_common() noexcept = default;
+public:
+
+ char_type field_delimiter() const
+ {
+ return field_delimiter_;
+ }
+
+ const char_type subfield_delimiter() const
+ {
+ return subfield_delimiter_;
+ }
+
+ char_type quote_char() const
+ {
+ return quote_char_;
+ }
+
+ char_type quote_escape_char() const
+ {
+ return quote_escape_char_;
+ }
+
+ string_type column_names() const
+ {
+ return column_names_;
+ }
+
+ bool enable_nan_to_num() const
+ {
+ return enable_nan_to_num_;
+ }
+
+ bool enable_inf_to_num() const
+ {
+ return enable_inf_to_num_;
+ }
+
+ bool enable_neginf_to_num() const
+ {
+ return enable_neginf_to_num_ || enable_inf_to_num_;
+ }
+
+ bool enable_nan_to_str() const
+ {
+ return enable_nan_to_str_;
+ }
+
+ bool enable_str_to_nan() const
+ {
+ return enable_str_to_nan_;
+ }
+
+ bool enable_inf_to_str() const
+ {
+ return enable_inf_to_str_;
+ }
+
+ bool enable_str_to_inf() const
+ {
+ return enable_str_to_inf_;
+ }
+
+ bool enable_neginf_to_str() const
+ {
+ return enable_neginf_to_str_ || enable_inf_to_str_;
+ }
+
+ bool enable_str_to_neginf() const
+ {
+ return enable_str_to_neginf_ || enable_str_to_inf_;
+ }
+
+ string_type nan_to_num() const
+ {
+ return nan_to_num_;
+ }
+
+ string_type inf_to_num() const
+ {
+ return inf_to_num_;
+ }
+
+ string_type neginf_to_num() const
+ {
+ if (enable_neginf_to_num_)
+ {
+ return neginf_to_num_;
+ }
+ else if (enable_inf_to_num_)
+ {
+ string_type s;
+ s.push_back('-');
+ s.append(inf_to_num_);
+ return s;
+ }
+ else
+ {
+ return neginf_to_num_;
+ }
+ }
+
+ string_type nan_to_str() const
+ {
+ return nan_to_str_;
+ }
+
+ string_type inf_to_str() const
+ {
+ return inf_to_str_;
+ }
+
+ string_type neginf_to_str() const
+ {
+ if (enable_neginf_to_str_)
+ {
+ return neginf_to_str_;
+ }
+ else if (enable_inf_to_str_)
+ {
+ string_type s;
+ s.push_back('-');
+ s.append(inf_to_str_);
+ return s;
+ }
+ else
+ {
+ return neginf_to_str_; // empty string
+ }
+ }
+};
+
+template <class CharT>
+class basic_csv_decode_options : public virtual basic_csv_options_common<CharT>
+{
+ friend class basic_csv_options<CharT>;
+ using super_type = basic_csv_options_common<CharT>;
+public:
+ using typename super_type::char_type;
+ using typename super_type::string_type;
+
+private:
+ bool assume_header_:1;
+ bool ignore_empty_values_:1;
+ bool ignore_empty_lines_:1;
+ bool trim_leading_:1;
+ bool trim_trailing_:1;
+ bool trim_leading_inside_quotes_:1;
+ bool trim_trailing_inside_quotes_:1;
+ bool unquoted_empty_value_is_null_:1;
+ bool infer_types_:1;
+ bool lossless_number_:1;
+ char_type comment_starter_;
+ csv_mapping_kind mapping_;
+ std::size_t header_lines_;
+ std::size_t max_lines_;
+ string_type column_types_;
+ string_type column_defaults_;
+public:
+ basic_csv_decode_options()
+ : assume_header_(false),
+ ignore_empty_values_(false),
+ ignore_empty_lines_(true),
+ trim_leading_(false),
+ trim_trailing_(false),
+ trim_leading_inside_quotes_(false),
+ trim_trailing_inside_quotes_(false),
+ unquoted_empty_value_is_null_(false),
+ infer_types_(true),
+ lossless_number_(false),
+ comment_starter_('\0'),
+ mapping_(),
+ header_lines_(0),
+ max_lines_((std::numeric_limits<std::size_t>::max)())
+ {}
+
+ basic_csv_decode_options(const basic_csv_decode_options& other) = default;
+
+ basic_csv_decode_options(basic_csv_decode_options&& other)
+ : super_type(std::forward<basic_csv_decode_options>(other)),
+ assume_header_(other.assume_header_),
+ ignore_empty_values_(other.ignore_empty_values_),
+ ignore_empty_lines_(other.ignore_empty_lines_),
+ trim_leading_(other.trim_leading_),
+ trim_trailing_(other.trim_trailing_),
+ trim_leading_inside_quotes_(other.trim_leading_inside_quotes_),
+ trim_trailing_inside_quotes_(other.trim_trailing_inside_quotes_),
+ unquoted_empty_value_is_null_(other.unquoted_empty_value_is_null_),
+ infer_types_(other.infer_types_),
+ lossless_number_(other.lossless_number_),
+ comment_starter_(other.comment_starter_),
+ mapping_(other.mapping_),
+ header_lines_(other.header_lines_),
+ max_lines_(other.max_lines_),
+ column_types_(std::move(other.column_types_)),
+ column_defaults_(std::move(other.column_defaults_))
+ {}
+
+ std::size_t header_lines() const
+ {
+ return (assume_header_ && header_lines_ <= 1) ? 1 : header_lines_;
+ }
+
+ bool assume_header() const
+ {
+ return assume_header_;
+ }
+
+ bool ignore_empty_values() const
+ {
+ return ignore_empty_values_;
+ }
+
+ bool ignore_empty_lines() const
+ {
+ return ignore_empty_lines_;
+ }
+
+ bool trim_leading() const
+ {
+ return trim_leading_;
+ }
+
+ bool trim_trailing() const
+ {
+ return trim_trailing_;
+ }
+
+ bool trim_leading_inside_quotes() const
+ {
+ return trim_leading_inside_quotes_;
+ }
+
+ bool trim_trailing_inside_quotes() const
+ {
+ return trim_trailing_inside_quotes_;
+ }
+
+ bool trim() const
+ {
+ return trim_leading_ && trim_trailing_;
+ }
+
+ bool trim_inside_quotes() const
+ {
+ return trim_leading_inside_quotes_ && trim_trailing_inside_quotes_;
+ }
+
+ bool unquoted_empty_value_is_null() const
+ {
+ return unquoted_empty_value_is_null_;
+ }
+
+ bool infer_types() const
+ {
+ return infer_types_;
+ }
+
+ bool lossless_number() const
+ {
+ return lossless_number_;
+ }
+
+ char_type comment_starter() const
+ {
+ return comment_starter_;
+ }
+
+ csv_mapping_kind mapping_kind() const
+ {
+ return mapping_ != csv_mapping_kind() ? mapping_ : (assume_header() || this->column_names().size() > 0 ? csv_mapping_kind::n_objects : csv_mapping_kind::n_rows);
+ }
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+ csv_mapping_kind mapping() const
+ {
+ return mapping_kind();
+ }
+#endif
+
+ std::size_t max_lines() const
+ {
+ return max_lines_;
+ }
+
+ string_type column_types() const
+ {
+ return column_types_;
+ }
+
+ string_type column_defaults() const
+ {
+ return column_defaults_;
+ }
+};
+
+template <class CharT>
+class basic_csv_encode_options : public virtual basic_csv_options_common<CharT>
+{
+ friend class basic_csv_options<CharT>;
+ using super_type = basic_csv_options_common<CharT>;
+public:
+ using typename super_type::char_type;
+ using typename super_type::string_type;
+private:
+ quote_style_kind quote_style_;
+ float_chars_format float_format_;
+ int8_t precision_;
+ string_type line_delimiter_;
+public:
+ basic_csv_encode_options()
+ : quote_style_(quote_style_kind::minimal),
+ float_format_(float_chars_format::general),
+ precision_(0)
+ {
+ line_delimiter_.push_back('\n');
+ }
+
+ basic_csv_encode_options(const basic_csv_encode_options& other) = default;
+
+ basic_csv_encode_options(basic_csv_encode_options&& other)
+ : super_type(std::forward<basic_csv_encode_options>(other)),
+ quote_style_(other.quote_style_),
+ float_format_(other.float_format_),
+ precision_(other.precision_),
+ line_delimiter_(std::move(other.line_delimiter_))
+ {
+ }
+
+ quote_style_kind quote_style() const
+ {
+ return quote_style_;
+ }
+
+ float_chars_format float_format() const
+ {
+ return float_format_;
+ }
+
+ int8_t precision() const
+ {
+ return precision_;
+ }
+
+ string_type line_delimiter() const
+ {
+ return line_delimiter_;
+ }
+};
+
+template <class CharT>
+class basic_csv_options final : public basic_csv_decode_options<CharT>, public basic_csv_encode_options<CharT>
+{
+ using char_type = CharT;
+ using string_type = std::basic_string<CharT>;
+
+public:
+ using basic_csv_decode_options<CharT>::enable_str_to_nan;
+ using basic_csv_decode_options<CharT>::enable_str_to_inf;
+ using basic_csv_decode_options<CharT>::enable_str_to_neginf;
+ using basic_csv_decode_options<CharT>::nan_to_str;
+ using basic_csv_decode_options<CharT>::inf_to_str;
+ using basic_csv_decode_options<CharT>::neginf_to_str;
+ using basic_csv_decode_options<CharT>::nan_to_num;
+ using basic_csv_decode_options<CharT>::inf_to_num;
+ using basic_csv_decode_options<CharT>::neginf_to_num;
+ using basic_csv_decode_options<CharT>::field_delimiter;
+ using basic_csv_decode_options<CharT>::subfield_delimiter;
+ using basic_csv_decode_options<CharT>::quote_char;
+ using basic_csv_decode_options<CharT>::quote_escape_char;
+ using basic_csv_decode_options<CharT>::column_names;
+ using basic_csv_decode_options<CharT>::header_lines;
+ using basic_csv_decode_options<CharT>::assume_header;
+ using basic_csv_decode_options<CharT>::ignore_empty_values;
+ using basic_csv_decode_options<CharT>::ignore_empty_lines;
+ using basic_csv_decode_options<CharT>::trim_leading;
+ using basic_csv_decode_options<CharT>::trim_trailing;
+ using basic_csv_decode_options<CharT>::trim_leading_inside_quotes;
+ using basic_csv_decode_options<CharT>::trim_trailing_inside_quotes;
+ using basic_csv_decode_options<CharT>::trim;
+ using basic_csv_decode_options<CharT>::trim_inside_quotes;
+ using basic_csv_decode_options<CharT>::unquoted_empty_value_is_null;
+ using basic_csv_decode_options<CharT>::infer_types;
+ using basic_csv_decode_options<CharT>::lossless_number;
+ using basic_csv_decode_options<CharT>::comment_starter;
+ using basic_csv_decode_options<CharT>::mapping;
+ using basic_csv_decode_options<CharT>::max_lines;
+ using basic_csv_decode_options<CharT>::column_types;
+ using basic_csv_decode_options<CharT>::column_defaults;
+ using basic_csv_encode_options<CharT>::float_format;
+ using basic_csv_encode_options<CharT>::precision;
+ using basic_csv_encode_options<CharT>::line_delimiter;
+ using basic_csv_encode_options<CharT>::quote_style;
+
+ static constexpr size_t default_indent = 4;
+
+// Constructors
+
+ basic_csv_options() = default;
+ basic_csv_options(const basic_csv_options&) = default;
+ basic_csv_options(basic_csv_options&&) = default;
+ basic_csv_options& operator=(const basic_csv_options&) = default;
+ basic_csv_options& operator=(basic_csv_options&&) = default;
+
+ basic_csv_options& float_format(float_chars_format value)
+ {
+ this->float_format_ = value;
+ return *this;
+ }
+
+ basic_csv_options& precision(int8_t value)
+ {
+ this->precision_ = value;
+ return *this;
+ }
+
+ basic_csv_options& header_lines(std::size_t value)
+ {
+ this->header_lines_ = value;
+ return *this;
+ }
+
+ basic_csv_options& assume_header(bool value)
+ {
+ this->assume_header_ = value;
+ return *this;
+ }
+
+ basic_csv_options& ignore_empty_values(bool value)
+ {
+ this->ignore_empty_values_ = value;
+ return *this;
+ }
+
+ basic_csv_options& ignore_empty_lines(bool value)
+ {
+ this->ignore_empty_lines_ = value;
+ return *this;
+ }
+
+ basic_csv_options& trim_leading(bool value)
+ {
+ this->trim_leading_ = value;
+ return *this;
+ }
+
+ basic_csv_options& trim_trailing(bool value)
+ {
+ this->trim_trailing_ = value;
+ return *this;
+ }
+
+ basic_csv_options& trim_leading_inside_quotes(bool value)
+ {
+ this->trim_leading_inside_quotes_ = value;
+ return *this;
+ }
+
+ basic_csv_options& trim_trailing_inside_quotes(bool value)
+ {
+ this->trim_trailing_inside_quotes_ = value;
+ return *this;
+ }
+
+ basic_csv_options& trim(bool value)
+ {
+ this->trim_leading_ = value;
+ this->trim_trailing_ = value;
+ return *this;
+ }
+
+ basic_csv_options& trim_inside_quotes(bool value)
+ {
+ this->trim_leading_inside_quotes_ = value;
+ this->trim_trailing_inside_quotes_ = value;
+ return *this;
+ }
+
+ basic_csv_options& unquoted_empty_value_is_null(bool value)
+ {
+ this->unquoted_empty_value_is_null_ = value;
+ return *this;
+ }
+
+ basic_csv_options& column_names(const string_type& value)
+ {
+ this->column_names_ = value;
+ return *this;
+ }
+
+ basic_csv_options& column_types(const string_type& value)
+ {
+ this->column_types_ = value;
+ return *this;
+ }
+
+ basic_csv_options& column_defaults(const string_type& value)
+ {
+ this->column_defaults_ = value;
+ return *this;
+ }
+
+ basic_csv_options& field_delimiter(char_type value)
+ {
+ this->field_delimiter_ = value;
+ return *this;
+ }
+
+ basic_csv_options& subfield_delimiter(char_type value)
+ {
+ this->subfield_delimiter_ = value;
+ return *this;
+ }
+
+ basic_csv_options& line_delimiter(const string_type& value)
+ {
+ this->line_delimiter_ = value;
+ return *this;
+ }
+
+ basic_csv_options& quote_char(char_type value)
+ {
+ this->quote_char_ = value;
+ return *this;
+ }
+
+ basic_csv_options& infer_types(bool value)
+ {
+ this->infer_types_ = value;
+ return *this;
+ }
+
+ basic_csv_options& lossless_number(bool value)
+ {
+ this->lossless_number_ = value;
+ return *this;
+ }
+
+ basic_csv_options& quote_escape_char(char_type value)
+ {
+ this->quote_escape_char_ = value;
+ return *this;
+ }
+
+ basic_csv_options& comment_starter(char_type value)
+ {
+ this->comment_starter_ = value;
+ return *this;
+ }
+
+ basic_csv_options& quote_style(quote_style_kind value)
+ {
+ this->quote_style_ = value;
+ return *this;
+ }
+
+//#if !defined(JSONCONS_NO_DEPRECATED)
+ basic_csv_options& mapping(csv_mapping_kind value)
+ {
+ this->mapping_ = value;
+ return *this;
+ }
+//#endif
+
+ basic_csv_options& mapping_kind(csv_mapping_kind value)
+ {
+ this->mapping_ = value;
+ return *this;
+ }
+
+ basic_csv_options& max_lines(std::size_t value)
+ {
+ this->max_lines_ = value;
+ return *this;
+ }
+
+ basic_csv_options& nan_to_num(const string_type& value)
+ {
+ this->enable_nan_to_num_ = true;
+ this->nan_to_str_.clear();
+ this->nan_to_num_ = value;
+ return *this;
+ }
+
+ basic_csv_options& inf_to_num(const string_type& value)
+ {
+ this->enable_inf_to_num_ = true;
+ this->inf_to_str_.clear();
+ this->inf_to_num_ = value;
+ return *this;
+ }
+
+ basic_csv_options& neginf_to_num(const string_type& value)
+ {
+ this->enable_neginf_to_num_ = true;
+ this->neginf_to_str_.clear();
+ this->neginf_to_num_ = value;
+ return *this;
+ }
+
+ basic_csv_options& nan_to_str(const string_type& value, bool enable_inverse = true)
+ {
+ this->enable_nan_to_str_ = true;
+ this->enable_str_to_nan_ = enable_inverse;
+ this->nan_to_num_.clear();
+ this->nan_to_str_ = value;
+ return *this;
+ }
+
+ basic_csv_options& inf_to_str(const string_type& value, bool enable_inverse = true)
+ {
+ this->enable_inf_to_str_ = true;
+ this->enable_inf_to_str_ = enable_inverse;
+ this->inf_to_num_.clear();
+ this->inf_to_str_ = value;
+ return *this;
+ }
+
+ basic_csv_options& neginf_to_str(const string_type& value, bool enable_inverse = true)
+ {
+ this->enable_neginf_to_str_ = true;
+ this->enable_neginf_to_str_ = enable_inverse;
+ this->neginf_to_num_.clear();
+ this->neginf_to_str_ = value;
+ return *this;
+ }
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+
+ JSONCONS_DEPRECATED_MSG("Instead, use float_format(float_chars_format)")
+ basic_csv_options& floating_point_format(float_chars_format value)
+ {
+ this->float_format_ = value;
+ return *this;
+ }
+#endif
+
+};
+
+using csv_options = basic_csv_options<char>;
+using wcsv_options = basic_csv_options<wchar_t>;
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+JSONCONS_DEPRECATED_MSG("Instead, use csv_options") typedef csv_options csv_parameters;
+JSONCONS_DEPRECATED_MSG("Instead, use wcsv_options") typedef wcsv_options wcsv_parameters;
+JSONCONS_DEPRECATED_MSG("Instead, use csv_options") typedef csv_options csv_serializing_options;
+JSONCONS_DEPRECATED_MSG("Instead, use wcsv_options") typedef wcsv_options wcsv_serializing_options;
+#endif
+
+
+}}
+#endif
diff --git a/include/jsoncons_ext/csv/csv_parser.hpp b/include/jsoncons_ext/csv/csv_parser.hpp
new file mode 100644
index 0000000..37887e2
--- /dev/null
+++ b/include/jsoncons_ext/csv/csv_parser.hpp
@@ -0,0 +1,2097 @@
+// Copyright 2015 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_CSV_CSV_PARSER_HPP
+#define JSONCONS_CSV_CSV_PARSER_HPP
+
+#include <memory> // std::allocator
+#include <string>
+#include <sstream>
+#include <vector>
+#include <stdexcept>
+#include <system_error>
+#include <cctype>
+#include <jsoncons/json_exception.hpp>
+#include <jsoncons/json_visitor.hpp>
+#include <jsoncons/json_reader.hpp>
+#include <jsoncons/json_filter.hpp>
+#include <jsoncons/json.hpp>
+#include <jsoncons/detail/parse_number.hpp>
+#include <jsoncons_ext/csv/csv_error.hpp>
+#include <jsoncons_ext/csv/csv_options.hpp>
+
+namespace jsoncons { namespace csv {
+
+enum class csv_mode
+{
+ initial,
+ header,
+ data,
+ subfields
+};
+
+enum class csv_parse_state
+{
+ start,
+ cr,
+ column_labels,
+ expect_comment_or_record,
+ expect_record,
+ end_record,
+ no_more_records,
+ comment,
+ between_values,
+ quoted_string,
+ unquoted_string,
+ before_unquoted_string,
+ escaped_value,
+ minus,
+ zero,
+ integer,
+ fraction,
+ exp1,
+ exp2,
+ exp3,
+ accept,
+ before_unquoted_field,
+ before_unquoted_field_tail,
+ before_unquoted_field_tail1,
+ before_last_unquoted_field,
+ before_last_unquoted_field_tail,
+ before_unquoted_subfield,
+ before_unquoted_subfield_tail,
+ before_quoted_subfield,
+ before_quoted_subfield_tail,
+ before_quoted_field,
+ before_quoted_field_tail,
+ before_last_quoted_field,
+ before_last_quoted_field_tail,
+ done
+};
+
+enum class cached_state
+{
+ begin_object,
+ end_object,
+ begin_array,
+ end_array,
+ name,
+ item,
+ done
+};
+
+struct default_csv_parsing
+{
+ bool operator()(csv_errc, const ser_context&) noexcept
+ {
+ return false;
+ }
+};
+
+namespace detail {
+
+ template <class CharT,class TempAllocator>
+ class parse_event
+ {
+ using temp_allocator_type = TempAllocator;
+ using string_view_type = typename basic_json_visitor<CharT>::string_view_type;
+ using char_allocator_type = typename std::allocator_traits<temp_allocator_type>:: template rebind_alloc<CharT>;
+ using byte_allocator_type = typename std::allocator_traits<temp_allocator_type>:: template rebind_alloc<uint8_t>;
+ using string_type = std::basic_string<CharT,std::char_traits<CharT>,char_allocator_type>;
+ using byte_string_type = basic_byte_string<byte_allocator_type>;
+
+ staj_event_type event_type;
+ string_type string_value;
+ byte_string_type byte_string_value;
+ union
+ {
+ bool bool_value;
+ int64_t int64_value;
+ uint64_t uint64_value;
+ double double_value;
+ };
+ semantic_tag tag;
+ public:
+ parse_event(staj_event_type event_type, semantic_tag tag, const TempAllocator& alloc)
+ : event_type(event_type),
+ string_value(alloc),
+ byte_string_value(alloc),
+ tag(tag)
+ {
+ }
+
+ parse_event(const string_view_type& value, semantic_tag tag, const TempAllocator& alloc)
+ : event_type(staj_event_type::string_value),
+ string_value(value.data(),value.length(),alloc),
+ byte_string_value(alloc),
+ tag(tag)
+ {
+ }
+
+ parse_event(const byte_string_view& value, semantic_tag tag, const TempAllocator& alloc)
+ : event_type(staj_event_type::byte_string_value),
+ string_value(alloc),
+ byte_string_value(value.data(),value.size(),alloc),
+ tag(tag)
+ {
+ }
+
+ parse_event(bool value, semantic_tag tag, const TempAllocator& alloc)
+ : event_type(staj_event_type::bool_value),
+ string_value(alloc),
+ byte_string_value(alloc),
+ bool_value(value),
+ tag(tag)
+ {
+ }
+
+ parse_event(int64_t value, semantic_tag tag, const TempAllocator& alloc)
+ : event_type(staj_event_type::int64_value),
+ string_value(alloc),
+ byte_string_value(alloc),
+ int64_value(value),
+ tag(tag)
+ {
+ }
+
+ parse_event(uint64_t value, semantic_tag tag, const TempAllocator& alloc)
+ : event_type(staj_event_type::uint64_value),
+ string_value(alloc),
+ byte_string_value(alloc),
+ uint64_value(value),
+ tag(tag)
+ {
+ }
+
+ parse_event(double value, semantic_tag tag, const TempAllocator& alloc)
+ : event_type(staj_event_type::double_value),
+ string_value(alloc),
+ byte_string_value(alloc),
+ double_value(value),
+ tag(tag)
+ {
+ }
+
+ parse_event(const parse_event&) = default;
+ parse_event(parse_event&&) = default;
+ parse_event& operator=(const parse_event&) = default;
+ parse_event& operator=(parse_event&&) = default;
+
+ bool replay(basic_json_visitor<CharT>& visitor) const
+ {
+ switch (event_type)
+ {
+ case staj_event_type::begin_array:
+ return visitor.begin_array(tag, ser_context());
+ case staj_event_type::end_array:
+ return visitor.end_array(ser_context());
+ case staj_event_type::string_value:
+ return visitor.string_value(string_value, tag, ser_context());
+ case staj_event_type::byte_string_value:
+ case staj_event_type::null_value:
+ return visitor.null_value(tag, ser_context());
+ case staj_event_type::bool_value:
+ return visitor.bool_value(bool_value, tag, ser_context());
+ case staj_event_type::int64_value:
+ return visitor.int64_value(int64_value, tag, ser_context());
+ case staj_event_type::uint64_value:
+ return visitor.uint64_value(uint64_value, tag, ser_context());
+ case staj_event_type::double_value:
+ return visitor.double_value(double_value, tag, ser_context());
+ default:
+ return false;
+ }
+ }
+ };
+
+ template <class CharT, class TempAllocator>
+ class m_columns_filter : public basic_json_visitor<CharT>
+ {
+ public:
+ using string_view_type = typename basic_json_visitor<CharT>::string_view_type;
+ using char_type = CharT;
+ using temp_allocator_type = TempAllocator;
+
+ using char_allocator_type = typename std::allocator_traits<temp_allocator_type>:: template rebind_alloc<CharT>;
+ using string_type = std::basic_string<CharT,std::char_traits<CharT>,char_allocator_type>;
+
+ using string_allocator_type = typename std::allocator_traits<temp_allocator_type>:: template rebind_alloc<string_type>;
+ using parse_event_allocator_type = typename std::allocator_traits<temp_allocator_type>:: template rebind_alloc<parse_event<CharT,TempAllocator>>;
+ using parse_event_vector_type = std::vector<parse_event<CharT,TempAllocator>, parse_event_allocator_type>;
+ using parse_event_vector_allocator_type = typename std::allocator_traits<temp_allocator_type>:: template rebind_alloc<parse_event_vector_type>;
+ private:
+ TempAllocator alloc_;
+ std::size_t name_index_;
+ int level_;
+ cached_state state_;
+ std::size_t column_index_;
+ std::size_t row_index_;
+
+ std::vector<string_type, string_allocator_type> column_names_;
+ std::vector<parse_event_vector_type,parse_event_vector_allocator_type> cached_events_;
+ public:
+
+ m_columns_filter(const TempAllocator& alloc)
+ : alloc_(alloc),
+ name_index_(0),
+ level_(0),
+ state_(cached_state::begin_object),
+ column_index_(0),
+ row_index_(0),
+ column_names_(alloc),
+ cached_events_(alloc)
+ {
+ }
+
+ void reset()
+ {
+ name_index_ = 0;
+ level_ = 0;
+ state_ = cached_state::begin_object;
+ column_index_ = 0;
+ row_index_ = 0;
+ column_names_.clear();
+ cached_events_.clear();
+ }
+
+ bool done() const
+ {
+ return state_ == cached_state::done;
+ }
+
+ void initialize(const std::vector<string_type, string_allocator_type>& column_names)
+ {
+ for (const auto& name : column_names)
+ {
+ column_names_.push_back(name);
+ cached_events_.emplace_back(alloc_);
+ }
+ name_index_ = 0;
+ level_ = 0;
+ column_index_ = 0;
+ row_index_ = 0;
+ state_ = cached_state::begin_object;
+ }
+
+ void skip_column()
+ {
+ ++name_index_;
+ }
+
+ bool replay_parse_events(basic_json_visitor<CharT>& visitor)
+ {
+ bool more = true;
+ while (more)
+ {
+ switch (state_)
+ {
+ case cached_state::begin_object:
+ more = visitor.begin_object(semantic_tag::none, ser_context());
+ column_index_ = 0;
+ state_ = cached_state::name;
+ break;
+ case cached_state::end_object:
+ more = visitor.end_object(ser_context());
+ state_ = cached_state::done;
+ break;
+ case cached_state::name:
+ if (column_index_ < column_names_.size())
+ {
+ more = visitor.key(column_names_[column_index_], ser_context());
+ state_ = cached_state::begin_array;
+ }
+ else
+ {
+ state_ = cached_state::end_object;
+ }
+ break;
+ case cached_state::begin_array:
+ more = visitor.begin_array(semantic_tag::none, ser_context());
+ row_index_ = 0;
+ state_ = cached_state::item;
+ break;
+ case cached_state::end_array:
+ more = visitor.end_array(ser_context());
+ ++column_index_;
+ state_ = cached_state::name;
+ break;
+ case cached_state::item:
+ if (row_index_ < cached_events_[column_index_].size())
+ {
+ more = cached_events_[column_index_][row_index_].replay(visitor);
+ ++row_index_;
+ }
+ else
+ {
+ state_ = cached_state::end_array;
+ }
+ break;
+ default:
+ more = false;
+ break;
+ }
+ }
+ return more;
+ }
+
+ void visit_flush() override
+ {
+ }
+
+ bool visit_begin_object(semantic_tag, const ser_context&, std::error_code& ec) override
+ {
+ ec = csv_errc::invalid_parse_state;
+ return false;
+ }
+
+ bool visit_end_object(const ser_context&, std::error_code& ec) override
+ {
+ ec = csv_errc::invalid_parse_state;
+ return false;
+ }
+
+ bool visit_begin_array(semantic_tag tag, const ser_context&, std::error_code&) override
+ {
+ if (name_index_ < column_names_.size())
+ {
+ cached_events_[name_index_].emplace_back(staj_event_type::begin_array, tag, alloc_);
+
+ ++level_;
+ }
+ return true;
+ }
+
+ bool visit_end_array(const ser_context&, std::error_code&) override
+ {
+ if (level_ > 0)
+ {
+ cached_events_[name_index_].emplace_back(staj_event_type::end_array, semantic_tag::none, alloc_);
+ ++name_index_;
+ --level_;
+ }
+ else
+ {
+ name_index_ = 0;
+ }
+ return true;
+ }
+
+ bool visit_key(const string_view_type&, const ser_context&, std::error_code& ec) override
+ {
+ ec = csv_errc::invalid_parse_state;
+ return false;
+ }
+
+ bool visit_null(semantic_tag tag, const ser_context&, std::error_code&) override
+ {
+ if (name_index_ < column_names_.size())
+ {
+ cached_events_[name_index_].emplace_back(staj_event_type::null_value, tag, alloc_);
+ if (level_ == 0)
+ {
+ ++name_index_;
+ }
+ }
+ return true;
+ }
+
+ bool visit_string(const string_view_type& value, semantic_tag tag, const ser_context&, std::error_code&) override
+ {
+ if (name_index_ < column_names_.size())
+ {
+ cached_events_[name_index_].emplace_back(value, tag, alloc_);
+
+ if (level_ == 0)
+ {
+ ++name_index_;
+ }
+ }
+ return true;
+ }
+
+ bool visit_byte_string(const byte_string_view& value,
+ semantic_tag tag,
+ const ser_context&,
+ std::error_code&) override
+ {
+ if (name_index_ < column_names_.size())
+ {
+ cached_events_[name_index_].emplace_back(value, tag, alloc_);
+ if (level_ == 0)
+ {
+ ++name_index_;
+ }
+ }
+ return true;
+ }
+
+ bool visit_double(double value,
+ semantic_tag tag,
+ const ser_context&,
+ std::error_code&) override
+ {
+ if (name_index_ < column_names_.size())
+ {
+ cached_events_[name_index_].emplace_back(value, tag, alloc_);
+ if (level_ == 0)
+ {
+ ++name_index_;
+ }
+ }
+ return true;
+ }
+
+ bool visit_int64(int64_t value,
+ semantic_tag tag,
+ const ser_context&,
+ std::error_code&) override
+ {
+ if (name_index_ < column_names_.size())
+ {
+ cached_events_[name_index_].emplace_back(value, tag, alloc_);
+ if (level_ == 0)
+ {
+ ++name_index_;
+ }
+ }
+ return true;
+ }
+
+ bool visit_uint64(uint64_t value,
+ semantic_tag tag,
+ const ser_context&,
+ std::error_code&) override
+ {
+ if (name_index_ < column_names_.size())
+ {
+ cached_events_[name_index_].emplace_back(value, tag, alloc_);
+ if (level_ == 0)
+ {
+ ++name_index_;
+ }
+ }
+ return true;
+ }
+
+ bool visit_bool(bool value, semantic_tag tag, const ser_context&, std::error_code&) override
+ {
+ if (name_index_ < column_names_.size())
+ {
+ cached_events_[name_index_].emplace_back(value, tag, alloc_);
+ if (level_ == 0)
+ {
+ ++name_index_;
+ }
+ }
+ return true;
+ }
+ };
+
+} // namespace detail
+
+template<class CharT,class TempAllocator=std::allocator<char>>
+class basic_csv_parser : public ser_context
+{
+public:
+ using string_view_type = jsoncons::basic_string_view<CharT>;
+ using char_type = CharT;
+private:
+ struct string_maps_to_double
+ {
+ string_view_type s;
+
+ bool operator()(const std::pair<string_view_type,double>& val) const
+ {
+ return val.first == s;
+ }
+ };
+
+ using temp_allocator_type = TempAllocator;
+ typedef typename std::allocator_traits<temp_allocator_type>:: template rebind_alloc<CharT> char_allocator_type;
+ using string_type = std::basic_string<CharT,std::char_traits<CharT>,char_allocator_type>;
+ typedef typename std::allocator_traits<temp_allocator_type>:: template rebind_alloc<string_type> string_allocator_type;
+ typedef typename std::allocator_traits<temp_allocator_type>:: template rebind_alloc<csv_mode> csv_mode_allocator_type;
+ typedef typename std::allocator_traits<temp_allocator_type>:: template rebind_alloc<csv_type_info> csv_type_info_allocator_type;
+ typedef typename std::allocator_traits<temp_allocator_type>:: template rebind_alloc<std::vector<string_type,string_allocator_type>> string_vector_allocator_type;
+ typedef typename std::allocator_traits<temp_allocator_type>:: template rebind_alloc<csv_parse_state> csv_parse_state_allocator_type;
+
+ static constexpr int default_depth = 3;
+
+ temp_allocator_type alloc_;
+ csv_parse_state state_;
+ basic_json_visitor<CharT>* visitor_;
+ std::function<bool(csv_errc,const ser_context&)> err_handler_;
+ std::size_t column_;
+ std::size_t line_;
+ int depth_;
+ const basic_csv_decode_options<CharT> options_;
+ std::size_t column_index_;
+ std::size_t level_;
+ std::size_t offset_;
+ jsoncons::detail::chars_to to_double_;
+ const CharT* begin_input_;
+ const CharT* input_end_;
+ const CharT* input_ptr_;
+ bool more_;
+ std::size_t header_line_;
+
+ detail::m_columns_filter<CharT,TempAllocator> m_columns_filter_;
+ std::vector<csv_mode,csv_mode_allocator_type> stack_;
+ std::vector<string_type,string_allocator_type> column_names_;
+ std::vector<csv_type_info,csv_type_info_allocator_type> column_types_;
+ std::vector<string_type,string_allocator_type> column_defaults_;
+ std::vector<csv_parse_state,csv_parse_state_allocator_type> state_stack_;
+ string_type buffer_;
+ std::vector<std::pair<string_view_type,double>> string_double_map_;
+
+public:
+ basic_csv_parser(const TempAllocator& alloc = TempAllocator())
+ : basic_csv_parser(basic_csv_decode_options<CharT>(),
+ default_csv_parsing(),
+ alloc)
+ {
+ }
+
+ basic_csv_parser(const basic_csv_decode_options<CharT>& options,
+ const TempAllocator& alloc = TempAllocator())
+ : basic_csv_parser(options,
+ default_csv_parsing(),
+ alloc)
+ {
+ }
+
+ basic_csv_parser(std::function<bool(csv_errc,const ser_context&)> err_handler,
+ const TempAllocator& alloc = TempAllocator())
+ : basic_csv_parser(basic_csv_decode_options<CharT>(),
+ err_handler,
+ alloc)
+ {
+ }
+
+ basic_csv_parser(const basic_csv_decode_options<CharT>& options,
+ std::function<bool(csv_errc,const ser_context&)> err_handler,
+ const TempAllocator& alloc = TempAllocator())
+ : alloc_(alloc),
+ state_(csv_parse_state::start),
+ visitor_(nullptr),
+ err_handler_(err_handler),
+ column_(1),
+ line_(1),
+ depth_(default_depth),
+ options_(options),
+ column_index_(0),
+ level_(0),
+ offset_(0),
+ begin_input_(nullptr),
+ input_end_(nullptr),
+ input_ptr_(nullptr),
+ more_(true),
+ header_line_(1),
+ 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<double>::infinity());
+ }
+ if (options_.enable_str_to_neginf())
+ {
+ string_double_map_.emplace_back(options_.neginf_to_str(),-std::numeric_limits<double>::infinity());
+ }
+
+ initialize();
+ }
+
+ ~basic_csv_parser() noexcept
+ {
+ }
+
+ bool done() const
+ {
+ return state_ == csv_parse_state::done;
+ }
+
+ bool accept() const
+ {
+ return state_ == csv_parse_state::accept || state_ == csv_parse_state::done;
+ }
+
+ bool stopped() const
+ {
+ return !more_;
+ }
+
+ bool source_exhausted() const
+ {
+ return input_ptr_ == input_end_;
+ }
+
+ const std::vector<string_type,string_allocator_type>& column_labels() const
+ {
+ return column_names_;
+ }
+
+ void reinitialize()
+ {
+ state_ = csv_parse_state::start;
+ visitor_ = nullptr;
+ column_ = 1;
+ line_ = 1;
+ depth_ = default_depth;
+ column_index_ = 0;
+ level_ = 0;
+ offset_ = 0;
+ begin_input_ = nullptr;
+ input_end_ = nullptr;
+ input_ptr_ = nullptr;
+ more_ = true;
+ header_line_ = 1;
+ m_columns_filter_.reset();
+ stack_.clear();
+ column_names_.clear();
+ column_types_.clear();
+ column_defaults_.clear();
+ state_stack_.clear();
+ buffer_.clear();
+
+ initialize();
+ }
+
+ void restart()
+ {
+ more_ = true;
+ }
+
+ void parse_some(basic_json_visitor<CharT>& visitor)
+ {
+ std::error_code ec;
+ parse_some(visitor,ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec,line_,column_));
+ }
+ }
+
+ void parse_some(basic_json_visitor<CharT>& visitor, std::error_code& ec)
+ {
+ switch (options_.mapping_kind())
+ {
+ case csv_mapping_kind::m_columns:
+ visitor_ = &m_columns_filter_;
+ break;
+ default:
+ visitor_ = std::addressof(visitor);
+ break;
+ }
+
+ const CharT* local_input_end = input_end_;
+
+ if (input_ptr_ == local_input_end && more_)
+ {
+ switch (state_)
+ {
+ case csv_parse_state::start:
+ ec = csv_errc::source_error;
+ more_ = false;
+ return;
+ case csv_parse_state::before_unquoted_field:
+ case csv_parse_state::before_last_unquoted_field:
+ end_unquoted_string_value(ec);
+ state_ = csv_parse_state::before_last_unquoted_field_tail;
+ break;
+ case csv_parse_state::before_last_unquoted_field_tail:
+ if (stack_.back() == csv_mode::subfields)
+ {
+ stack_.pop_back();
+ more_ = visitor_->end_array(*this, ec);
+ }
+ ++column_index_;
+ state_ = csv_parse_state::end_record;
+ break;
+ case csv_parse_state::before_unquoted_string:
+ buffer_.clear();
+ JSONCONS_FALLTHROUGH;
+ case csv_parse_state::unquoted_string:
+ if (options_.trim_leading() || options_.trim_trailing())
+ {
+ trim_string_buffer(options_.trim_leading(),options_.trim_trailing());
+ }
+ if (options_.ignore_empty_values() && buffer_.empty())
+ {
+ state_ = csv_parse_state::end_record;
+ }
+ else
+ {
+ before_value(ec);
+ state_ = csv_parse_state::before_unquoted_field;
+ }
+ break;
+ case csv_parse_state::before_last_quoted_field:
+ end_quoted_string_value(ec);
+ ++column_index_;
+ state_ = csv_parse_state::end_record;
+ break;
+ case csv_parse_state::escaped_value:
+ if (options_.quote_escape_char() == options_.quote_char())
+ {
+ if (!(options_.ignore_empty_values() && buffer_.empty()))
+ {
+ before_value(ec);
+ ++column_;
+ state_ = csv_parse_state::before_last_quoted_field;
+ }
+ else
+ {
+ state_ = csv_parse_state::end_record;
+ }
+ }
+ else
+ {
+ ec = csv_errc::invalid_escaped_char;
+ more_ = false;
+ return;
+ }
+ break;
+ case csv_parse_state::end_record:
+ if (column_index_ > 0)
+ {
+ after_record(ec);
+ }
+ state_ = csv_parse_state::no_more_records;
+ break;
+ case csv_parse_state::no_more_records:
+ switch (stack_.back())
+ {
+ case csv_mode::header:
+ stack_.pop_back();
+ break;
+ case csv_mode::data:
+ stack_.pop_back();
+ break;
+ default:
+ break;
+ }
+ more_ = visitor_->end_array(*this, ec);
+ if (options_.mapping_kind() == csv_mapping_kind::m_columns)
+ {
+ if (!m_columns_filter_.done())
+ {
+ more_ = m_columns_filter_.replay_parse_events(visitor);
+ }
+ else
+ {
+ state_ = csv_parse_state::accept;
+ }
+ }
+ else
+ {
+ state_ = csv_parse_state::accept;
+ }
+ break;
+ case csv_parse_state::accept:
+ if (!(stack_.size() == 1 && stack_.back() == csv_mode::initial))
+ {
+ err_handler_(csv_errc::unexpected_eof, *this);
+ ec = csv_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ stack_.pop_back();
+ visitor_->flush();
+ state_ = csv_parse_state::done;
+ more_ = false;
+ return;
+ default:
+ state_ = csv_parse_state::end_record;
+ break;
+ }
+ }
+
+ for (; (input_ptr_ < local_input_end) && more_;)
+ {
+ CharT curr_char = *input_ptr_;
+
+ switch (state_)
+ {
+ case csv_parse_state::cr:
+ ++line_;
+ column_ = 1;
+ switch (*input_ptr_)
+ {
+ case '\n':
+ ++input_ptr_;
+ state_ = pop_state();
+ break;
+ default:
+ state_ = pop_state();
+ break;
+ }
+ break;
+ case csv_parse_state::start:
+ if (options_.mapping_kind() != csv_mapping_kind::m_columns)
+ {
+ more_ = visitor_->begin_array(semantic_tag::none, *this, ec);
+ }
+ if (options_.assume_header() && options_.mapping_kind() == csv_mapping_kind::n_rows && options_.column_names().size() > 0)
+ {
+ column_index_ = 0;
+ state_ = csv_parse_state::column_labels;
+ more_ = visitor_->begin_array(semantic_tag::none, *this, ec);
+ state_ = csv_parse_state::expect_comment_or_record;
+ }
+ else
+ {
+ state_ = csv_parse_state::expect_comment_or_record;
+ }
+ break;
+ case csv_parse_state::column_labels:
+ if (column_index_ < column_names_.size())
+ {
+ more_ = visitor_->string_value(column_names_[column_index_], semantic_tag::none, *this, ec);
+ ++column_index_;
+ }
+ else
+ {
+ more_ = visitor_->end_array(*this, ec);
+ state_ = csv_parse_state::expect_comment_or_record;
+ //stack_.back() = csv_mode::data;
+ column_index_ = 0;
+ }
+ break;
+ case csv_parse_state::comment:
+ switch (curr_char)
+ {
+ case '\n':
+ {
+ ++line_;
+ if (stack_.back() == csv_mode::header)
+ {
+ ++header_line_;
+ }
+ column_ = 1;
+ state_ = csv_parse_state::expect_comment_or_record;
+ break;
+ }
+ case '\r':
+ ++line_;
+ if (stack_.back() == csv_mode::header)
+ {
+ ++header_line_;
+ }
+ column_ = 1;
+ state_ = csv_parse_state::expect_comment_or_record;
+ push_state(state_);
+ state_ = csv_parse_state::cr;
+ break;
+ default:
+ ++column_;
+ break;
+ }
+ ++input_ptr_;
+ break;
+
+ case csv_parse_state::expect_comment_or_record:
+ buffer_.clear();
+ if (curr_char == options_.comment_starter())
+ {
+ state_ = csv_parse_state::comment;
+ ++column_;
+ ++input_ptr_;
+ }
+ else
+ {
+ state_ = csv_parse_state::expect_record;
+ }
+ break;
+ case csv_parse_state::quoted_string:
+ {
+ if (curr_char == options_.quote_escape_char())
+ {
+ state_ = csv_parse_state::escaped_value;
+ }
+ else if (curr_char == options_.quote_char())
+ {
+ state_ = csv_parse_state::between_values;
+ }
+ else
+ {
+ buffer_.push_back(static_cast<CharT>(curr_char));
+ }
+ }
+ ++column_;
+ ++input_ptr_;
+ break;
+ case csv_parse_state::escaped_value:
+ {
+ if (curr_char == options_.quote_char())
+ {
+ buffer_.push_back(static_cast<CharT>(curr_char));
+ state_ = csv_parse_state::quoted_string;
+ ++column_;
+ ++input_ptr_;
+ }
+ else if (options_.quote_escape_char() == options_.quote_char())
+ {
+ state_ = csv_parse_state::between_values;
+ }
+ else
+ {
+ ec = csv_errc::invalid_escaped_char;
+ more_ = false;
+ return;
+ }
+ }
+ break;
+ case csv_parse_state::between_values:
+ switch (curr_char)
+ {
+ case '\r':
+ case '\n':
+ {
+ if (options_.trim_leading() || options_.trim_trailing())
+ {
+ trim_string_buffer(options_.trim_leading(),options_.trim_trailing());
+ }
+ if (!(options_.ignore_empty_values() && buffer_.empty()))
+ {
+ before_value(ec);
+ state_ = csv_parse_state::before_last_quoted_field;
+ }
+ else
+ {
+ state_ = csv_parse_state::end_record;
+ }
+ break;
+ }
+ default:
+ if (curr_char == options_.field_delimiter())
+ {
+ if (options_.trim_leading() || options_.trim_trailing())
+ {
+ trim_string_buffer(options_.trim_leading(),options_.trim_trailing());
+ }
+ before_value(ec);
+ state_ = csv_parse_state::before_quoted_field;
+ }
+ else if (options_.subfield_delimiter() != char_type() && curr_char == options_.subfield_delimiter())
+ {
+ if (options_.trim_leading() || options_.trim_trailing())
+ {
+ trim_string_buffer(options_.trim_leading(),options_.trim_trailing());
+ }
+ before_value(ec);
+ state_ = csv_parse_state::before_quoted_subfield;
+ }
+ else if (curr_char == ' ' || curr_char == '\t')
+ {
+ ++column_;
+ ++input_ptr_;
+ }
+ else
+ {
+ ec = csv_errc::unexpected_char_between_fields;
+ more_ = false;
+ return;
+ }
+ break;
+ }
+ break;
+ case csv_parse_state::before_unquoted_string:
+ {
+ buffer_.clear();
+ state_ = csv_parse_state::unquoted_string;
+ break;
+ }
+ case csv_parse_state::before_unquoted_field:
+ end_unquoted_string_value(ec);
+ state_ = csv_parse_state::before_unquoted_field_tail;
+ break;
+ case csv_parse_state::before_unquoted_field_tail:
+ {
+ if (stack_.back() == csv_mode::subfields)
+ {
+ stack_.pop_back();
+ more_ = visitor_->end_array(*this, ec);
+ }
+ ++column_index_;
+ state_ = csv_parse_state::before_unquoted_string;
+ ++column_;
+ ++input_ptr_;
+ break;
+ }
+ case csv_parse_state::before_unquoted_field_tail1:
+ {
+ if (stack_.back() == csv_mode::subfields)
+ {
+ stack_.pop_back();
+ more_ = visitor_->end_array(*this, ec);
+ }
+ state_ = csv_parse_state::end_record;
+ ++column_;
+ ++input_ptr_;
+ break;
+ }
+
+ case csv_parse_state::before_last_unquoted_field:
+ end_unquoted_string_value(ec);
+ state_ = csv_parse_state::before_last_unquoted_field_tail;
+ break;
+
+ case csv_parse_state::before_last_unquoted_field_tail:
+ if (stack_.back() == csv_mode::subfields)
+ {
+ stack_.pop_back();
+ more_ = visitor_->end_array(*this, ec);
+ }
+ ++column_index_;
+ state_ = csv_parse_state::end_record;
+ break;
+
+ case csv_parse_state::before_unquoted_subfield:
+ if (stack_.back() == csv_mode::data)
+ {
+ stack_.push_back(csv_mode::subfields);
+ more_ = visitor_->begin_array(semantic_tag::none, *this, ec);
+ }
+ state_ = csv_parse_state::before_unquoted_subfield_tail;
+ break;
+ case csv_parse_state::before_unquoted_subfield_tail:
+ end_unquoted_string_value(ec);
+ state_ = csv_parse_state::before_unquoted_string;
+ ++column_;
+ ++input_ptr_;
+ break;
+ case csv_parse_state::before_quoted_field:
+ end_quoted_string_value(ec);
+ state_ = csv_parse_state::before_unquoted_field_tail; // return to unquoted
+ break;
+ case csv_parse_state::before_quoted_subfield:
+ if (stack_.back() == csv_mode::data)
+ {
+ stack_.push_back(csv_mode::subfields);
+ more_ = visitor_->begin_array(semantic_tag::none, *this, ec);
+ }
+ state_ = csv_parse_state::before_quoted_subfield_tail;
+ break;
+ case csv_parse_state::before_quoted_subfield_tail:
+ end_quoted_string_value(ec);
+ state_ = csv_parse_state::before_unquoted_string;
+ ++column_;
+ ++input_ptr_;
+ break;
+ case csv_parse_state::before_last_quoted_field:
+ end_quoted_string_value(ec);
+ state_ = csv_parse_state::before_last_quoted_field_tail;
+ break;
+ case csv_parse_state::before_last_quoted_field_tail:
+ if (stack_.back() == csv_mode::subfields)
+ {
+ stack_.pop_back();
+ more_ = visitor_->end_array(*this, ec);
+ }
+ ++column_index_;
+ state_ = csv_parse_state::end_record;
+ break;
+ case csv_parse_state::unquoted_string:
+ {
+ switch (curr_char)
+ {
+ case '\n':
+ case '\r':
+ {
+ if (options_.trim_leading() || options_.trim_trailing())
+ {
+ trim_string_buffer(options_.trim_leading(),options_.trim_trailing());
+ }
+ if (!(options_.ignore_empty_values() && buffer_.empty()))
+ {
+ before_value(ec);
+ state_ = csv_parse_state::before_last_unquoted_field;
+ }
+ else
+ {
+ state_ = csv_parse_state::end_record;
+ }
+ break;
+ }
+ default:
+ if (curr_char == options_.field_delimiter())
+ {
+ if (options_.trim_leading() || options_.trim_trailing())
+ {
+ trim_string_buffer(options_.trim_leading(),options_.trim_trailing());
+ }
+ before_value(ec);
+ state_ = csv_parse_state::before_unquoted_field;
+ }
+ else if (options_.subfield_delimiter() != char_type() && curr_char == options_.subfield_delimiter())
+ {
+ if (options_.trim_leading() || options_.trim_trailing())
+ {
+ trim_string_buffer(options_.trim_leading(),options_.trim_trailing());
+ }
+ before_value(ec);
+ state_ = csv_parse_state::before_unquoted_subfield;
+ }
+ else if (curr_char == options_.quote_char())
+ {
+ buffer_.clear();
+ state_ = csv_parse_state::quoted_string;
+ ++column_;
+ ++input_ptr_;
+ }
+ else
+ {
+ buffer_.push_back(static_cast<CharT>(curr_char));
+ ++column_;
+ ++input_ptr_;
+ }
+ break;
+ }
+ break;
+ }
+ case csv_parse_state::expect_record:
+ {
+ switch (curr_char)
+ {
+ case '\n':
+ {
+ if (!options_.ignore_empty_lines())
+ {
+ before_record(ec);
+ state_ = csv_parse_state::end_record;
+ }
+ else
+ {
+ ++line_;
+ column_ = 1;
+ state_ = csv_parse_state::expect_comment_or_record;
+ ++input_ptr_;
+ }
+ break;
+ }
+ case '\r':
+ if (!options_.ignore_empty_lines())
+ {
+ before_record(ec);
+ state_ = csv_parse_state::end_record;
+ }
+ else
+ {
+ ++line_;
+ column_ = 1;
+ state_ = csv_parse_state::expect_comment_or_record;
+ ++input_ptr_;
+ push_state(state_);
+ state_ = csv_parse_state::cr;
+ }
+ break;
+ case ' ':
+ case '\t':
+ if (!options_.trim_leading())
+ {
+ buffer_.push_back(static_cast<CharT>(curr_char));
+ before_record(ec);
+ state_ = csv_parse_state::unquoted_string;
+ }
+ ++column_;
+ ++input_ptr_;
+ break;
+ default:
+ before_record(ec);
+ if (curr_char == options_.quote_char())
+ {
+ buffer_.clear();
+ state_ = csv_parse_state::quoted_string;
+ ++column_;
+ ++input_ptr_;
+ }
+ else
+ {
+ state_ = csv_parse_state::unquoted_string;
+ }
+ break;
+ }
+ break;
+ }
+ case csv_parse_state::end_record:
+ {
+ switch (curr_char)
+ {
+ case '\n':
+ {
+ ++line_;
+ column_ = 1;
+ state_ = csv_parse_state::expect_comment_or_record;
+ after_record(ec);
+ ++input_ptr_;
+ break;
+ }
+ case '\r':
+ ++line_;
+ column_ = 1;
+ state_ = csv_parse_state::expect_comment_or_record;
+ after_record(ec);
+ push_state(state_);
+ state_ = csv_parse_state::cr;
+ ++input_ptr_;
+ break;
+ case ' ':
+ case '\t':
+ ++column_;
+ ++input_ptr_;
+ break;
+ default:
+ err_handler_(csv_errc::syntax_error, *this);
+ ec = csv_errc::syntax_error;
+ more_ = false;
+ return;
+ }
+ break;
+ }
+ default:
+ err_handler_(csv_errc::invalid_parse_state, *this);
+ ec = csv_errc::invalid_parse_state;
+ more_ = false;
+ return;
+ }
+ if (line_ > options_.max_lines())
+ {
+ state_ = csv_parse_state::done;
+ more_ = false;
+ }
+ }
+ }
+
+ void finish_parse()
+ {
+ std::error_code ec;
+ finish_parse(ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec,line_,column_));
+ }
+ }
+
+ void finish_parse(std::error_code& ec)
+ {
+ while (more_)
+ {
+ parse_some(ec);
+ }
+ }
+
+ csv_parse_state state() const
+ {
+ return state_;
+ }
+
+ void update(const string_view_type sv)
+ {
+ update(sv.data(),sv.length());
+ }
+
+ void update(const CharT* data, std::size_t length)
+ {
+ begin_input_ = data;
+ input_end_ = data + length;
+ input_ptr_ = begin_input_;
+ }
+
+ std::size_t line() const override
+ {
+ return line_;
+ }
+
+ std::size_t column() const override
+ {
+ return column_;
+ }
+
+private:
+ void initialize()
+ {
+ jsoncons::csv::detail::parse_column_names(options_.column_names(), column_names_);
+ jsoncons::csv::detail::parse_column_types(options_.column_types(), column_types_);
+ jsoncons::csv::detail::parse_column_names(options_.column_defaults(), column_defaults_);
+
+ stack_.reserve(default_depth);
+ stack_.push_back(csv_mode::initial);
+ stack_.push_back((options_.header_lines() > 0) ? csv_mode::header
+ : csv_mode::data);
+ }
+
+ // name
+ void before_value(std::error_code& ec)
+ {
+ switch (stack_.back())
+ {
+ case csv_mode::header:
+ if (options_.trim_leading_inside_quotes() || options_.trim_trailing_inside_quotes())
+ {
+ trim_string_buffer(options_.trim_leading_inside_quotes(),options_.trim_trailing_inside_quotes());
+ }
+ if (line_ == header_line_)
+ {
+ column_names_.push_back(buffer_);
+ if (options_.assume_header() && options_.mapping_kind() == csv_mapping_kind::n_rows)
+ {
+ more_ = visitor_->string_value(buffer_, semantic_tag::none, *this, ec);
+ }
+ }
+ break;
+ case csv_mode::data:
+ if (options_.mapping_kind() == csv_mapping_kind::n_objects)
+ {
+ if (!(options_.ignore_empty_values() && buffer_.empty()))
+ {
+ if (column_index_ < column_names_.size() + offset_)
+ {
+ more_ = visitor_->key(column_names_[column_index_ - offset_], *this, ec);
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ // begin_array or begin_record
+ void before_record(std::error_code& ec)
+ {
+ offset_ = 0;
+
+ switch (stack_.back())
+ {
+ case csv_mode::header:
+ if (options_.assume_header() && line_ == header_line_)
+ {
+ if (options_.mapping_kind() == csv_mapping_kind::n_rows)
+ {
+ more_ = visitor_->begin_array(semantic_tag::none, *this, ec);
+ }
+ }
+ break;
+ case csv_mode::data:
+ switch (options_.mapping_kind())
+ {
+ case csv_mapping_kind::n_rows:
+ more_ = visitor_->begin_array(semantic_tag::none, *this, ec);
+ break;
+ case csv_mapping_kind::n_objects:
+ more_ = visitor_->begin_object(semantic_tag::none, *this, ec);
+ break;
+ case csv_mapping_kind::m_columns:
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ // end_array, begin_array, string_value (headers)
+ void after_record(std::error_code& ec)
+ {
+ if (column_types_.size() > 0)
+ {
+ if (level_ > 0)
+ {
+ more_ = visitor_->end_array(*this, ec);
+ level_ = 0;
+ }
+ }
+ switch (stack_.back())
+ {
+ case csv_mode::header:
+ if (line_ >= options_.header_lines())
+ {
+ stack_.back() = csv_mode::data;
+ }
+ switch (options_.mapping_kind())
+ {
+ case csv_mapping_kind::n_rows:
+ if (options_.assume_header())
+ {
+ more_ = visitor_->end_array(*this, ec);
+ }
+ break;
+ case csv_mapping_kind::m_columns:
+ m_columns_filter_.initialize(column_names_);
+ break;
+ default:
+ break;
+ }
+ break;
+ case csv_mode::data:
+ case csv_mode::subfields:
+ {
+ switch (options_.mapping_kind())
+ {
+ case csv_mapping_kind::n_rows:
+ more_ = visitor_->end_array(*this, ec);
+ break;
+ case csv_mapping_kind::n_objects:
+ more_ = visitor_->end_object(*this, ec);
+ break;
+ case csv_mapping_kind::m_columns:
+ more_ = visitor_->end_array(*this, ec);
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ column_index_ = 0;
+ }
+
+ void trim_string_buffer(bool trim_leading, bool trim_trailing)
+ {
+ std::size_t start = 0;
+ std::size_t length = buffer_.length();
+ if (trim_leading)
+ {
+ bool done = false;
+ while (!done && start < buffer_.length())
+ {
+ if ((buffer_[start] < 256) && std::isspace(buffer_[start]))
+ {
+ ++start;
+ }
+ else
+ {
+ done = true;
+ }
+ }
+ }
+ if (trim_trailing)
+ {
+ bool done = false;
+ while (!done && length > 0)
+ {
+ if ((buffer_[length-1] < 256) && std::isspace(buffer_[length-1]))
+ {
+ --length;
+ }
+ else
+ {
+ done = true;
+ }
+ }
+ }
+ if (start != 0 || length != buffer_.size())
+ {
+ buffer_ = buffer_.substr(start,length-start);
+ }
+ }
+
+ /*
+ end_array, begin_array, xxx_value (end_value)
+ */
+ void end_unquoted_string_value(std::error_code& ec)
+ {
+ switch (stack_.back())
+ {
+ case csv_mode::data:
+ case csv_mode::subfields:
+ switch (options_.mapping_kind())
+ {
+ case csv_mapping_kind::n_rows:
+ if (options_.unquoted_empty_value_is_null() && buffer_.length() == 0)
+ {
+ more_ = visitor_->null_value(semantic_tag::none, *this, ec);
+ }
+ else
+ {
+ end_value(options_.infer_types(), ec);
+ }
+ break;
+ case csv_mapping_kind::n_objects:
+ if (!(options_.ignore_empty_values() && buffer_.empty()))
+ {
+ if (column_index_ < column_names_.size() + offset_)
+ {
+ if (options_.unquoted_empty_value_is_null() && buffer_.length() == 0)
+ {
+ more_ = visitor_->null_value(semantic_tag::none, *this, ec);
+ }
+ else
+ {
+ end_value(options_.infer_types(), ec);
+ }
+ }
+ else if (level_ > 0)
+ {
+ if (options_.unquoted_empty_value_is_null() && buffer_.length() == 0)
+ {
+ more_ = visitor_->null_value(semantic_tag::none, *this, ec);
+ }
+ else
+ {
+ end_value(options_.infer_types(), ec);
+ }
+ }
+ }
+ break;
+ case csv_mapping_kind::m_columns:
+ if (!(options_.ignore_empty_values() && buffer_.empty()))
+ {
+ end_value(options_.infer_types(), ec);
+ }
+ else
+ {
+ m_columns_filter_.skip_column();
+ }
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ void end_quoted_string_value(std::error_code& ec)
+ {
+ switch (stack_.back())
+ {
+ case csv_mode::data:
+ case csv_mode::subfields:
+ if (options_.trim_leading_inside_quotes() || options_.trim_trailing_inside_quotes())
+ {
+ trim_string_buffer(options_.trim_leading_inside_quotes(),options_.trim_trailing_inside_quotes());
+ }
+ switch (options_.mapping_kind())
+ {
+ case csv_mapping_kind::n_rows:
+ end_value(false, ec);
+ break;
+ case csv_mapping_kind::n_objects:
+ if (!(options_.ignore_empty_values() && buffer_.empty()))
+ {
+ if (column_index_ < column_names_.size() + offset_)
+ {
+ if (options_.unquoted_empty_value_is_null() && buffer_.length() == 0)
+ {
+ more_ = visitor_->null_value(semantic_tag::none, *this, ec);
+ }
+ else
+ {
+ end_value(false, ec);
+ }
+ }
+ else if (level_ > 0)
+ {
+ if (options_.unquoted_empty_value_is_null() && buffer_.length() == 0)
+ {
+ more_ = visitor_->null_value(semantic_tag::none, *this, ec);
+ }
+ else
+ {
+ end_value(false, ec);
+ }
+ }
+ }
+ break;
+ case csv_mapping_kind::m_columns:
+ if (!(options_.ignore_empty_values() && buffer_.empty()))
+ {
+ end_value(false, ec);
+ }
+ else
+ {
+ m_columns_filter_.skip_column();
+ }
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ void end_value(bool infer_types, std::error_code& ec)
+ {
+ auto it = std::find_if(string_double_map_.begin(), string_double_map_.end(), string_maps_to_double{ buffer_ });
+ if (it != string_double_map_.end())
+ {
+ more_ = visitor_->double_value(it->second, semantic_tag::none, *this, ec);
+ }
+ else if (column_index_ < column_types_.size() + offset_)
+ {
+ if (column_types_[column_index_ - offset_].col_type == csv_column_type::repeat_t)
+ {
+ offset_ = offset_ + column_types_[column_index_ - offset_].rep_count;
+ if (column_index_ - offset_ + 1 < column_types_.size())
+ {
+ if (column_index_ == offset_ || level_ > column_types_[column_index_-offset_].level)
+ {
+ more_ = visitor_->end_array(*this, ec);
+ }
+ level_ = column_index_ == offset_ ? 0 : column_types_[column_index_ - offset_].level;
+ }
+ }
+ if (level_ < column_types_[column_index_ - offset_].level)
+ {
+ more_ = visitor_->begin_array(semantic_tag::none, *this, ec);
+ level_ = column_types_[column_index_ - offset_].level;
+ }
+ else if (level_ > column_types_[column_index_ - offset_].level)
+ {
+ more_ = visitor_->end_array(*this, ec);
+ level_ = column_types_[column_index_ - offset_].level;
+ }
+ switch (column_types_[column_index_ - offset_].col_type)
+ {
+ case csv_column_type::integer_t:
+ {
+ std::basic_istringstream<CharT,std::char_traits<CharT>,char_allocator_type> iss{buffer_};
+ int64_t val;
+ iss >> val;
+ if (!iss.fail())
+ {
+ more_ = visitor_->int64_value(val, semantic_tag::none, *this, ec);
+ }
+ else
+ {
+ if (column_index_ - offset_ < column_defaults_.size() && column_defaults_[column_index_ - offset_].length() > 0)
+ {
+ basic_json_parser<CharT,temp_allocator_type> parser(alloc_);
+ parser.update(column_defaults_[column_index_ - offset_].data(),column_defaults_[column_index_ - offset_].length());
+ parser.parse_some(*visitor_);
+ parser.finish_parse(*visitor_);
+ }
+ else
+ {
+ more_ = visitor_->null_value(semantic_tag::none, *this, ec);
+ }
+ }
+ }
+ break;
+ case csv_column_type::float_t:
+ {
+ if (options_.lossless_number())
+ {
+ more_ = visitor_->string_value(buffer_,semantic_tag::bigdec, *this, ec);
+ }
+ else
+ {
+ std::basic_istringstream<CharT, std::char_traits<CharT>, char_allocator_type> iss{ buffer_ };
+ double val;
+ iss >> val;
+ if (!iss.fail())
+ {
+ more_ = visitor_->double_value(val, semantic_tag::none, *this, ec);
+ }
+ else
+ {
+ if (column_index_ - offset_ < column_defaults_.size() && column_defaults_[column_index_ - offset_].length() > 0)
+ {
+ basic_json_parser<CharT,temp_allocator_type> parser(alloc_);
+ parser.update(column_defaults_[column_index_ - offset_].data(),column_defaults_[column_index_ - offset_].length());
+ parser.parse_some(*visitor_);
+ parser.finish_parse(*visitor_);
+ }
+ else
+ {
+ more_ = visitor_->null_value(semantic_tag::none, *this, ec);
+ }
+ }
+ }
+ }
+ break;
+ case csv_column_type::boolean_t:
+ {
+ if (buffer_.length() == 1 && buffer_[0] == '0')
+ {
+ more_ = visitor_->bool_value(false, semantic_tag::none, *this, ec);
+ }
+ else if (buffer_.length() == 1 && buffer_[0] == '1')
+ {
+ more_ = visitor_->bool_value(true, semantic_tag::none, *this, ec);
+ }
+ else if (buffer_.length() == 5 && ((buffer_[0] == 'f' || buffer_[0] == 'F') && (buffer_[1] == 'a' || buffer_[1] == 'A') && (buffer_[2] == 'l' || buffer_[2] == 'L') && (buffer_[3] == 's' || buffer_[3] == 'S') && (buffer_[4] == 'e' || buffer_[4] == 'E')))
+ {
+ more_ = visitor_->bool_value(false, semantic_tag::none, *this, ec);
+ }
+ else if (buffer_.length() == 4 && ((buffer_[0] == 't' || buffer_[0] == 'T') && (buffer_[1] == 'r' || buffer_[1] == 'R') && (buffer_[2] == 'u' || buffer_[2] == 'U') && (buffer_[3] == 'e' || buffer_[3] == 'E')))
+ {
+ more_ = visitor_->bool_value(true, semantic_tag::none, *this, ec);
+ }
+ else
+ {
+ if (column_index_ - offset_ < column_defaults_.size() && column_defaults_[column_index_ - offset_].length() > 0)
+ {
+ basic_json_parser<CharT,temp_allocator_type> parser(alloc_);
+ parser.update(column_defaults_[column_index_ - offset_].data(),column_defaults_[column_index_ - offset_].length());
+ parser.parse_some(*visitor_);
+ parser.finish_parse(*visitor_);
+ }
+ else
+ {
+ more_ = visitor_->null_value(semantic_tag::none, *this, ec);
+ }
+ }
+ }
+ break;
+ default:
+ if (buffer_.length() > 0)
+ {
+ more_ = visitor_->string_value(buffer_, semantic_tag::none, *this, ec);
+ }
+ else
+ {
+ if (column_index_ < column_defaults_.size() + offset_ && column_defaults_[column_index_ - offset_].length() > 0)
+ {
+ basic_json_parser<CharT,temp_allocator_type> parser(alloc_);
+ parser.update(column_defaults_[column_index_ - offset_].data(),column_defaults_[column_index_ - offset_].length());
+ parser.parse_some(*visitor_);
+ parser.finish_parse(*visitor_);
+ }
+ else
+ {
+ more_ = visitor_->string_value(string_view_type(), semantic_tag::none, *this, ec);
+ }
+ }
+ break;
+ }
+ }
+ else
+ {
+ if (infer_types)
+ {
+ end_value_with_numeric_check(ec);
+ }
+ else
+ {
+ more_ = visitor_->string_value(buffer_, semantic_tag::none, *this, ec);
+ }
+ }
+ }
+
+ enum class numeric_check_state
+ {
+ initial,
+ null,
+ boolean_true,
+ boolean_false,
+ minus,
+ zero,
+ integer,
+ fraction1,
+ fraction,
+ exp1,
+ exp,
+ not_a_number
+ };
+
+ /*
+ xxx_value
+ */
+ void end_value_with_numeric_check(std::error_code& ec)
+ {
+ numeric_check_state state = numeric_check_state::initial;
+ bool is_negative = false;
+ int precision = 0;
+ uint8_t decimal_places = 0;
+
+ auto last = buffer_.end();
+
+ std::string buffer;
+ for (auto p = buffer_.begin(); state != numeric_check_state::not_a_number && p != last; ++p)
+ {
+ switch (state)
+ {
+ case numeric_check_state::initial:
+ {
+ switch (*p)
+ {
+ case 'n':case 'N':
+ if ((last-p) == 4 && (p[1] == 'u' || p[1] == 'U') && (p[2] == 'l' || p[2] == 'L') && (p[3] == 'l' || p[3] == 'L'))
+ {
+ state = numeric_check_state::null;
+ }
+ else
+ {
+ state = numeric_check_state::not_a_number;
+ }
+ break;
+ case 't':case 'T':
+ if ((last-p) == 4 && (p[1] == 'r' || p[1] == 'R') && (p[2] == 'u' || p[2] == 'U') && (p[3] == 'e' || p[3] == 'U'))
+ {
+ state = numeric_check_state::boolean_true;
+ }
+ else
+ {
+ state = numeric_check_state::not_a_number;
+ }
+ break;
+ case 'f':case 'F':
+ if ((last-p) == 5 && (p[1] == 'a' || p[1] == 'A') && (p[2] == 'l' || p[2] == 'L') && (p[3] == 's' || p[3] == 'S') && (p[4] == 'e' || p[4] == 'E'))
+ {
+ state = numeric_check_state::boolean_false;
+ }
+ else
+ {
+ state = numeric_check_state::not_a_number;
+ }
+ break;
+ case '-':
+ is_negative = true;
+ buffer.push_back(*p);
+ state = numeric_check_state::minus;
+ break;
+ case '0':
+ ++precision;
+ buffer.push_back(*p);
+ state = numeric_check_state::zero;
+ break;
+ case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9':
+ ++precision;
+ buffer.push_back(*p);
+ state = numeric_check_state::integer;
+ break;
+ default:
+ state = numeric_check_state::not_a_number;
+ break;
+ }
+ break;
+ }
+ case numeric_check_state::zero:
+ {
+ switch (*p)
+ {
+ case '.':
+ buffer.push_back(to_double_.get_decimal_point());
+ state = numeric_check_state::fraction1;
+ break;
+ case 'e':case 'E':
+ buffer.push_back(*p);
+ state = numeric_check_state::exp1;
+ break;
+ default:
+ state = numeric_check_state::not_a_number;
+ break;
+ }
+ break;
+ }
+ case numeric_check_state::integer:
+ {
+ switch (*p)
+ {
+ case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9':
+ ++precision;
+ buffer.push_back(*p);
+ break;
+ case '.':
+ buffer.push_back(to_double_.get_decimal_point());
+ state = numeric_check_state::fraction1;
+ break;
+ case 'e':case 'E':
+ buffer.push_back(*p);
+ state = numeric_check_state::exp1;
+ break;
+ default:
+ state = numeric_check_state::not_a_number;
+ break;
+ }
+ break;
+ }
+ case numeric_check_state::minus:
+ {
+ switch (*p)
+ {
+ case '0':
+ ++precision;
+ buffer.push_back(*p);
+ state = numeric_check_state::zero;
+ break;
+ case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9':
+ ++precision;
+ buffer.push_back(*p);
+ state = numeric_check_state::integer;
+ break;
+ default:
+ state = numeric_check_state::not_a_number;
+ break;
+ }
+ break;
+ }
+ case numeric_check_state::fraction1:
+ {
+ switch (*p)
+ {
+ case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9':
+ ++precision;
+ ++decimal_places;
+ buffer.push_back(*p);
+ state = numeric_check_state::fraction;
+ break;
+ default:
+ state = numeric_check_state::not_a_number;
+ break;
+ }
+ break;
+ }
+ case numeric_check_state::fraction:
+ {
+ switch (*p)
+ {
+ case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9':
+ ++precision;
+ ++decimal_places;
+ buffer.push_back(*p);
+ break;
+ case 'e':case 'E':
+ buffer.push_back(*p);
+ state = numeric_check_state::exp1;
+ break;
+ default:
+ state = numeric_check_state::not_a_number;
+ break;
+ }
+ break;
+ }
+ case numeric_check_state::exp1:
+ {
+ switch (*p)
+ {
+ case '-':
+ buffer.push_back(*p);
+ break;
+ case '+':
+ break;
+ case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9':
+ state = numeric_check_state::exp;
+ buffer.push_back(*p);
+ break;
+ default:
+ state = numeric_check_state::not_a_number;
+ break;
+ }
+ break;
+ }
+ case numeric_check_state::exp:
+ {
+ switch (*p)
+ {
+ case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9':
+ buffer.push_back(*p);
+ break;
+ default:
+ state = numeric_check_state::not_a_number;
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ switch (state)
+ {
+ case numeric_check_state::null:
+ more_ = visitor_->null_value(semantic_tag::none, *this, ec);
+ break;
+ case numeric_check_state::boolean_true:
+ more_ = visitor_->bool_value(true, semantic_tag::none, *this, ec);
+ break;
+ case numeric_check_state::boolean_false:
+ more_ = visitor_->bool_value(false, semantic_tag::none, *this, ec);
+ break;
+ case numeric_check_state::zero:
+ case numeric_check_state::integer:
+ {
+ if (is_negative)
+ {
+ int64_t val{ 0 };
+ auto result = jsoncons::detail::to_integer_decimal(buffer_.data(), buffer_.length(), val);
+ if (result)
+ {
+ more_ = visitor_->int64_value(val, semantic_tag::none, *this, ec);
+ }
+ else // Must be overflow
+ {
+ more_ = visitor_->string_value(buffer_, semantic_tag::bigint, *this, ec);
+ }
+ }
+ else
+ {
+ uint64_t val{ 0 };
+ auto result = jsoncons::detail::to_integer_decimal(buffer_.data(), buffer_.length(), val);
+ if (result)
+ {
+ more_ = visitor_->uint64_value(val, semantic_tag::none, *this, ec);
+ }
+ else if (result.ec == jsoncons::detail::to_integer_errc::overflow)
+ {
+ more_ = visitor_->string_value(buffer_, semantic_tag::bigint, *this, ec);
+ }
+ else
+ {
+ ec = result.ec;
+ more_ = false;
+ return;
+ }
+ }
+ break;
+ }
+ case numeric_check_state::fraction:
+ case numeric_check_state::exp:
+ {
+ if (options_.lossless_number())
+ {
+ more_ = visitor_->string_value(buffer_,semantic_tag::bigdec, *this, ec);
+ }
+ else
+ {
+ double d = to_double_(buffer.c_str(), buffer.length());
+ more_ = visitor_->double_value(d, semantic_tag::none, *this, ec);
+ }
+ break;
+ }
+ default:
+ {
+ more_ = visitor_->string_value(buffer_, semantic_tag::none, *this, ec);
+ break;
+ }
+ }
+ }
+
+ void push_state(csv_parse_state state)
+ {
+ state_stack_.push_back(state);
+ }
+
+ csv_parse_state pop_state()
+ {
+ JSONCONS_ASSERT(!state_stack_.empty())
+ csv_parse_state state = state_stack_.back();
+ state_stack_.pop_back();
+ return state;
+ }
+};
+
+using csv_parser = basic_csv_parser<char>;
+using wcsv_parser = basic_csv_parser<wchar_t>;
+
+}}
+
+#endif
+
diff --git a/include/jsoncons_ext/csv/csv_reader.hpp b/include/jsoncons_ext/csv/csv_reader.hpp
new file mode 100644
index 0000000..f10211a
--- /dev/null
+++ b/include/jsoncons_ext/csv/csv_reader.hpp
@@ -0,0 +1,348 @@
+// Copyright 2013 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_CSV_CSV_READER_HPP
+#define JSONCONS_CSV_CSV_READER_HPP
+
+#include <string>
+#include <vector>
+#include <stdexcept>
+#include <memory> // std::allocator
+#include <utility> // std::move
+#include <istream> // std::basic_istream
+#include <jsoncons/source.hpp>
+#include <jsoncons/json_exception.hpp>
+#include <jsoncons/json_visitor.hpp>
+#include <jsoncons_ext/csv/csv_error.hpp>
+#include <jsoncons_ext/csv/csv_parser.hpp>
+#include <jsoncons/json.hpp>
+#include <jsoncons/json_reader.hpp>
+#include <jsoncons/json_decoder.hpp>
+#include <jsoncons/source_adaptor.hpp>
+#include <jsoncons_ext/csv/csv_options.hpp>
+
+namespace jsoncons { namespace csv {
+
+ template<class CharT,class Source=jsoncons::stream_source<CharT>,class Allocator=std::allocator<char>>
+ class basic_csv_reader
+ {
+ struct stack_item
+ {
+ stack_item() noexcept
+ : array_begun_(false)
+ {
+ }
+
+ bool array_begun_;
+ };
+ using char_type = CharT;
+ using temp_allocator_type = Allocator;
+ typedef typename std::allocator_traits<temp_allocator_type>:: template rebind_alloc<CharT> char_allocator_type;
+
+ basic_csv_reader(const basic_csv_reader&) = delete;
+ basic_csv_reader& operator = (const basic_csv_reader&) = delete;
+
+ basic_default_json_visitor<CharT> default_visitor_;
+ text_source_adaptor<Source> source_;
+ basic_json_visitor<CharT>& visitor_;
+ basic_csv_parser<CharT,Allocator> 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 <class Sourceable>
+ basic_csv_reader(Sourceable&& source,
+ basic_json_visitor<CharT>& visitor,
+ const Allocator& alloc = Allocator())
+
+ : basic_csv_reader(std::forward<Sourceable>(source),
+ visitor,
+ basic_csv_decode_options<CharT>(),
+ default_csv_parsing(),
+ alloc)
+ {
+ }
+
+ template <class Sourceable>
+ basic_csv_reader(Sourceable&& source,
+ basic_json_visitor<CharT>& visitor,
+ const basic_csv_decode_options<CharT>& options,
+ const Allocator& alloc = Allocator())
+
+ : basic_csv_reader(std::forward<Sourceable>(source),
+ visitor,
+ options,
+ default_csv_parsing(),
+ alloc)
+ {
+ }
+
+ template <class Sourceable>
+ basic_csv_reader(Sourceable&& source,
+ basic_json_visitor<CharT>& visitor,
+ std::function<bool(csv_errc,const ser_context&)> err_handler,
+ const Allocator& alloc = Allocator())
+ : basic_csv_reader(std::forward<Sourceable>(source),
+ visitor,
+ basic_csv_decode_options<CharT>(),
+ err_handler,
+ alloc)
+ {
+ }
+
+ template <class Sourceable>
+ basic_csv_reader(Sourceable&& source,
+ basic_json_visitor<CharT>& visitor,
+ const basic_csv_decode_options<CharT>& options,
+ std::function<bool(csv_errc,const ser_context&)> err_handler,
+ const Allocator& alloc = Allocator())
+ : source_(std::forward<Sourceable>(source)),
+ visitor_(visitor),
+ parser_(options, err_handler, alloc)
+
+ {
+ }
+
+ ~basic_csv_reader() noexcept = default;
+
+ void read()
+ {
+ std::error_code ec;
+ read(ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column()));
+ }
+ }
+
+ void read(std::error_code& ec)
+ {
+ read_internal(ec);
+ }
+
+ std::size_t line() const
+ {
+ return parser_.line();
+ }
+
+ std::size_t column() const
+ {
+ return parser_.column();
+ }
+
+ bool eof() const
+ {
+ return parser_.source_exhausted() && source_.eof();
+ }
+
+ private:
+
+ void read_internal(std::error_code& ec)
+ {
+ if (source_.is_error())
+ {
+ ec = csv_errc::source_error;
+ return;
+ }
+ while (!parser_.stopped())
+ {
+ if (parser_.source_exhausted())
+ {
+ auto s = source_.read_buffer(ec);
+ if (ec) return;
+ if (s.size() > 0)
+ {
+ parser_.update(s.data(),s.size());
+ }
+ }
+ parser_.parse_some(visitor_, ec);
+ if (ec) return;
+ }
+ }
+ };
+
+ template<class CharT,class Source=jsoncons::stream_source<CharT>,class Allocator=std::allocator<char>>
+ class legacy_basic_csv_reader
+ {
+ struct stack_item
+ {
+ stack_item() noexcept
+ : array_begun_(false)
+ {
+ }
+
+ bool array_begun_;
+ };
+ using char_type = CharT;
+ using temp_allocator_type = Allocator;
+ typedef typename std::allocator_traits<temp_allocator_type>:: template rebind_alloc<CharT> char_allocator_type;
+
+ legacy_basic_csv_reader(const legacy_basic_csv_reader&) = delete;
+ legacy_basic_csv_reader& operator = (const legacy_basic_csv_reader&) = delete;
+
+ basic_default_json_visitor<CharT> default_visitor_;
+ text_source_adaptor<Source> source_;
+ basic_json_visitor<CharT>& visitor_;
+ basic_csv_parser<CharT,Allocator> 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 <class Sourceable>
+ legacy_basic_csv_reader(Sourceable&& source,
+ basic_json_visitor<CharT>& visitor,
+ const Allocator& alloc = Allocator())
+
+ : legacy_basic_csv_reader(std::forward<Sourceable>(source),
+ visitor,
+ basic_csv_decode_options<CharT>(),
+ default_csv_parsing(),
+ alloc)
+ {
+ }
+
+ template <class Sourceable>
+ legacy_basic_csv_reader(Sourceable&& source,
+ basic_json_visitor<CharT>& visitor,
+ const basic_csv_decode_options<CharT>& options,
+ const Allocator& alloc = Allocator())
+
+ : legacy_basic_csv_reader(std::forward<Sourceable>(source),
+ visitor,
+ options,
+ default_csv_parsing(),
+ alloc)
+ {
+ }
+
+ template <class Sourceable>
+ legacy_basic_csv_reader(Sourceable&& source,
+ basic_json_visitor<CharT>& visitor,
+ std::function<bool(csv_errc,const ser_context&)> err_handler,
+ const Allocator& alloc = Allocator())
+ : legacy_basic_csv_reader(std::forward<Sourceable>(source),
+ visitor,
+ basic_csv_decode_options<CharT>(),
+ err_handler,
+ alloc)
+ {
+ }
+
+ template <class Sourceable>
+ legacy_basic_csv_reader(Sourceable&& source,
+ basic_json_visitor<CharT>& visitor,
+ const basic_csv_decode_options<CharT>& options,
+ std::function<bool(csv_errc,const ser_context&)> err_handler,
+ const Allocator& alloc = Allocator(),
+ typename std::enable_if<!std::is_constructible<jsoncons::basic_string_view<CharT>,Sourceable>::value>::type* = 0)
+ : source_(std::forward<Sourceable>(source)),
+ visitor_(visitor),
+ parser_(options, err_handler, alloc)
+ {
+ }
+
+ template <class Sourceable>
+ legacy_basic_csv_reader(Sourceable&& source,
+ basic_json_visitor<CharT>& visitor,
+ const basic_csv_decode_options<CharT>& options,
+ std::function<bool(csv_errc,const ser_context&)> err_handler,
+ const Allocator& alloc = Allocator(),
+ typename std::enable_if<std::is_constructible<jsoncons::basic_string_view<CharT>,Sourceable>::value>::type* = 0)
+ : source_(),
+ visitor_(visitor),
+ parser_(options, err_handler, alloc)
+ {
+ jsoncons::basic_string_view<CharT> sv(std::forward<Sourceable>(source));
+ auto r = unicode_traits::detect_encoding_from_bom(sv.data(), sv.size());
+ if (!(r.encoding == unicode_traits::encoding_kind::utf8 || r.encoding == unicode_traits::encoding_kind::undetected))
+ {
+ JSONCONS_THROW(ser_error(json_errc::illegal_unicode_character,parser_.line(),parser_.column()));
+ }
+ std::size_t offset = (r.ptr - sv.data());
+ parser_.update(sv.data()+offset,sv.size()-offset);
+ }
+
+ ~legacy_basic_csv_reader() noexcept = default;
+
+ void read()
+ {
+ std::error_code ec;
+ read(ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column()));
+ }
+ }
+
+ void read(std::error_code& ec)
+ {
+ read_internal(ec);
+ }
+
+ std::size_t line() const
+ {
+ return parser_.line();
+ }
+
+ std::size_t column() const
+ {
+ return parser_.column();
+ }
+
+ bool eof() const
+ {
+ return parser_.source_exhausted() && source_.eof();
+ }
+
+ private:
+
+ void read_internal(std::error_code& ec)
+ {
+ if (source_.is_error())
+ {
+ ec = csv_errc::source_error;
+ return;
+ }
+ while (!parser_.stopped())
+ {
+ if (parser_.source_exhausted())
+ {
+ auto s = source_.read_buffer(ec);
+ if (ec) return;
+ if (s.size() > 0)
+ {
+ parser_.update(s.data(),s.size());
+ }
+ }
+ parser_.parse_some(visitor_, ec);
+ if (ec) return;
+ }
+ }
+ };
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+ using csv_reader = legacy_basic_csv_reader<char>;
+ using wcsv_reader = legacy_basic_csv_reader<wchar_t>;
+#endif
+
+ using csv_string_reader = basic_csv_reader<char,string_source<char>>;
+ using wcsv_string_reader = basic_csv_reader<wchar_t,string_source<wchar_t>>;
+ using csv_stream_reader = basic_csv_reader<char,stream_source<char>>;
+ using wcsv_stream_reader = basic_csv_reader<wchar_t,stream_source<wchar_t>>;
+
+}}
+
+#endif
diff --git a/include/jsoncons_ext/csv/csv_serializer.hpp b/include/jsoncons_ext/csv/csv_serializer.hpp
new file mode 100644
index 0000000..ec73510
--- /dev/null
+++ b/include/jsoncons_ext/csv/csv_serializer.hpp
@@ -0,0 +1,12 @@
+// Copyright 2013 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_CSV_CSV_SERIALIZER_HPP
+#define JSONCONS_CSV_CSV_SERIALIZER_HPP
+
+#include <jsoncons_ext/csv/csv_encoder.hpp>
+
+#endif
diff --git a/include/jsoncons_ext/csv/decode_csv.hpp b/include/jsoncons_ext/csv/decode_csv.hpp
new file mode 100644
index 0000000..b91c58b
--- /dev/null
+++ b/include/jsoncons_ext/csv/decode_csv.hpp
@@ -0,0 +1,208 @@
+/// Copyright 2013 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_CSV_DECODE_CSV_HPP
+#define JSONCONS_CSV_DECODE_CSV_HPP
+
+#include <jsoncons_ext/csv/csv_options.hpp>
+#include <jsoncons_ext/csv/csv_reader.hpp>
+#include <jsoncons_ext/csv/csv_encoder.hpp>
+#include <jsoncons_ext/csv/csv_cursor.hpp>
+
+namespace jsoncons {
+namespace csv {
+
+ template <class T,class Source>
+ typename std::enable_if<type_traits::is_basic_json<T>::value &&
+ type_traits::is_sequence_of<Source,typename T::char_type>::value,T>::type
+ decode_csv(const Source& s, const basic_csv_decode_options<typename Source::value_type>& options = basic_csv_decode_options<typename Source::value_type>())
+ {
+ using char_type = typename Source::value_type;
+
+ json_decoder<T> decoder;
+
+ basic_csv_reader<char_type,jsoncons::string_source<char_type>> 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 <class T,class Source>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value &&
+ type_traits::is_char_sequence<Source>::value,T>::type
+ decode_csv(const Source& s, const basic_csv_decode_options<typename Source::value_type>& options = basic_csv_decode_options<typename Source::value_type>())
+ {
+ using char_type = typename Source::value_type;
+
+ basic_csv_cursor<char_type> cursor(s, options);
+ jsoncons::json_decoder<basic_json<char_type>> decoder;
+
+ std::error_code ec;
+ T val = decode_traits<T,char_type>::decode(cursor, decoder, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column()));
+ }
+ return val;
+ }
+
+ template <class T,class CharT>
+ typename std::enable_if<type_traits::is_basic_json<T>::value,T>::type
+ decode_csv(std::basic_istream<CharT>& is, const basic_csv_decode_options<CharT>& options = basic_csv_decode_options<CharT>())
+ {
+ using char_type = CharT;
+
+ json_decoder<T> decoder;
+
+ basic_csv_reader<char_type,jsoncons::stream_source<char_type>> 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 <class T,class CharT>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value,T>::type
+ decode_csv(std::basic_istream<CharT>& is, const basic_csv_decode_options<CharT>& options = basic_csv_decode_options<CharT>())
+ {
+ basic_csv_cursor<CharT> cursor(is, options);
+ jsoncons::json_decoder<basic_json<CharT>> decoder;
+
+ std::error_code ec;
+ T val = decode_traits<T,CharT>::decode(cursor, decoder, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column()));
+ }
+ return val;
+ }
+
+ template <class T, class InputIt>
+ typename std::enable_if<type_traits::is_basic_json<T>::value,T>::type
+ decode_csv(InputIt first, InputIt last,
+ const basic_csv_decode_options<typename std::iterator_traits<InputIt>::value_type>& options =
+ basic_csv_decode_options<typename std::iterator_traits<InputIt>::value_type>())
+ {
+ using char_type = typename std::iterator_traits<InputIt>::value_type;
+
+ jsoncons::json_decoder<T> decoder;
+ basic_csv_reader<char_type, iterator_source<InputIt>> reader(iterator_source<InputIt>(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 <class T, class InputIt>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value,T>::type
+ decode_csv(InputIt first, InputIt last,
+ const basic_csv_decode_options<typename std::iterator_traits<InputIt>::value_type>& options =
+ basic_csv_decode_options<typename std::iterator_traits<InputIt>::value_type>())
+ {
+ using char_type = typename std::iterator_traits<InputIt>::value_type;
+
+ basic_csv_cursor<char_type,iterator_source<InputIt>> cursor(iterator_source<InputIt>(first, last), options);
+ jsoncons::json_decoder<basic_json<char_type>> decoder;
+ std::error_code ec;
+ T val = decode_traits<T,char_type>::decode(cursor, decoder, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column()));
+ }
+ return val;
+ }
+
+ // With leading allocator parameter
+
+ template <class T,class Source,class TempAllocator>
+ typename std::enable_if<type_traits::is_basic_json<T>::value &&
+ type_traits::is_sequence_of<Source,typename T::char_type>::value,T>::type
+ decode_csv(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ const Source& s,
+ const basic_csv_decode_options<typename Source::value_type>& options = basic_csv_decode_options<typename Source::value_type>())
+ {
+ using char_type = typename Source::value_type;
+
+ json_decoder<T,TempAllocator> decoder(temp_alloc);
+
+ basic_csv_reader<char_type,jsoncons::string_source<char_type>,TempAllocator> reader(s,decoder,options,temp_alloc);
+ reader.read();
+ if (!decoder.is_valid())
+ {
+ JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column()));
+ }
+ return decoder.get_result();
+ }
+
+ template <class T,class Source,class TempAllocator>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value &&
+ type_traits::is_char_sequence<Source>::value,T>::type
+ decode_csv(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ const Source& s,
+ const basic_csv_decode_options<typename Source::value_type>& options = basic_csv_decode_options<typename Source::value_type>())
+ {
+ using char_type = typename Source::value_type;
+
+ basic_csv_cursor<char_type,stream_source<char_type>,TempAllocator> cursor(s, options, temp_alloc);
+ json_decoder<basic_json<char_type,sorted_policy,TempAllocator>,TempAllocator> decoder(temp_alloc, temp_alloc);
+
+ std::error_code ec;
+ T val = decode_traits<T,char_type>::decode(cursor, decoder, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column()));
+ }
+ return val;
+ }
+
+ template <class T,class CharT,class TempAllocator>
+ typename std::enable_if<type_traits::is_basic_json<T>::value,T>::type
+ decode_csv(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ std::basic_istream<CharT>& is,
+ const basic_csv_decode_options<CharT>& options = basic_csv_decode_options<CharT>())
+ {
+ using char_type = CharT;
+
+ json_decoder<T,TempAllocator> decoder(temp_alloc);
+
+ basic_csv_reader<char_type,jsoncons::string_source<char_type>,TempAllocator> reader(is,decoder,options,temp_alloc);
+ reader.read();
+ if (!decoder.is_valid())
+ {
+ JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column()));
+ }
+ return decoder.get_result();
+ }
+
+ template <class T,class CharT,class TempAllocator>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value,T>::type
+ decode_csv(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ std::basic_istream<CharT>& is,
+ const basic_csv_decode_options<CharT>& options = basic_csv_decode_options<CharT>())
+ {
+ basic_csv_cursor<CharT,stream_source<CharT>,TempAllocator> cursor(is, options, temp_alloc);
+ json_decoder<basic_json<CharT,sorted_policy,TempAllocator>,TempAllocator> decoder(temp_alloc, temp_alloc);
+
+ std::error_code ec;
+ T val = decode_traits<T,CharT>::decode(cursor, decoder, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column()));
+ }
+ return val;
+ }
+
+} // namespace csv
+} // namespace jsoncons
+
+#endif
diff --git a/include/jsoncons_ext/csv/encode_csv.hpp b/include/jsoncons_ext/csv/encode_csv.hpp
new file mode 100644
index 0000000..d919253
--- /dev/null
+++ b/include/jsoncons_ext/csv/encode_csv.hpp
@@ -0,0 +1,122 @@
+/// Copyright 2013 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_CSV_ENCODE_CSV_HPP
+#define JSONCONS_CSV_ENCODE_CSV_HPP
+
+#include <jsoncons_ext/csv/csv_options.hpp>
+#include <jsoncons_ext/csv/csv_reader.hpp>
+#include <jsoncons_ext/csv/csv_encoder.hpp>
+
+namespace jsoncons {
+namespace csv {
+
+ template <class T,class Container>
+ typename std::enable_if<type_traits::is_basic_json<T>::value &&
+ type_traits::is_back_insertable_char_container<Container>::value>::type
+ encode_csv(const T& j, Container& s, const basic_csv_encode_options<typename Container::value_type>& options = basic_csv_encode_options<typename Container::value_type>())
+ {
+ using char_type = typename Container::value_type;
+ basic_csv_encoder<char_type,jsoncons::string_sink<std::basic_string<char_type>>> encoder(s,options);
+ j.dump(encoder);
+ }
+
+ template <class T,class Container>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value &&
+ type_traits::is_back_insertable_char_container<Container>::value>::type
+ encode_csv(const T& val, Container& s, const basic_csv_encode_options<typename Container::value_type>& options = basic_csv_encode_options<typename Container::value_type>())
+ {
+ using char_type = typename Container::value_type;
+ basic_csv_encoder<char_type,jsoncons::string_sink<std::basic_string<char_type>>> encoder(s,options);
+ std::error_code ec;
+ encode_traits<T,char_type>::encode(val, encoder, json(), ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec));
+ }
+ }
+
+ template <class T, class CharT>
+ typename std::enable_if<type_traits::is_basic_json<T>::value,void>::type
+ encode_csv(const T& j, std::basic_ostream<CharT>& os, const basic_csv_encode_options<CharT>& options = basic_csv_encode_options<CharT>())
+ {
+ using char_type = CharT;
+ basic_csv_encoder<char_type,jsoncons::stream_sink<char_type>> encoder(os,options);
+ j.dump(encoder);
+ }
+
+ template <class T, class CharT>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value,void>::type
+ encode_csv(const T& val, std::basic_ostream<CharT>& os, const basic_csv_encode_options<CharT>& options = basic_csv_encode_options<CharT>())
+ {
+ using char_type = CharT;
+ basic_csv_encoder<char_type,jsoncons::stream_sink<char_type>> encoder(os,options);
+ std::error_code ec;
+ encode_traits<T,CharT>::encode(val, encoder, json(), ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec));
+ }
+ }
+
+ // with temp_allocator_arg_t
+
+ template <class T, class Container, class TempAllocator>
+ typename std::enable_if<type_traits::is_basic_json<T>::value &&
+ type_traits::is_back_insertable_char_container<Container>::value>::type
+ encode_csv(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ const T& j, Container& s, const basic_csv_encode_options<typename Container::value_type>& options = basic_csv_encode_options<typename Container::value_type>())
+ {
+ using char_type = typename Container::value_type;
+ basic_csv_encoder<char_type,jsoncons::string_sink<std::basic_string<char_type>>,TempAllocator> encoder(s, options, temp_alloc);
+ j.dump(encoder);
+ }
+
+ template <class T, class Container, class TempAllocator>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value &&
+ type_traits::is_back_insertable_char_container<Container>::value>::type
+ encode_csv(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ const T& val, Container& s, const basic_csv_encode_options<typename Container::value_type>& options = basic_csv_encode_options<typename Container::value_type>())
+ {
+ using char_type = typename Container::value_type;
+ basic_csv_encoder<char_type,jsoncons::string_sink<std::basic_string<char_type>>,TempAllocator> encoder(s, options, temp_alloc);
+ std::error_code ec;
+ encode_traits<T,char_type>::encode(val, encoder, json(), ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec));
+ }
+ }
+
+ template <class T, class CharT, class TempAllocator>
+ typename std::enable_if<type_traits::is_basic_json<T>::value,void>::type
+ encode_csv(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ const T& j, std::basic_ostream<CharT>& os, const basic_csv_encode_options<CharT>& options = basic_csv_encode_options<CharT>())
+ {
+ using char_type = CharT;
+ basic_csv_encoder<char_type,jsoncons::stream_sink<char_type>,TempAllocator> encoder(os, options, temp_alloc);
+ j.dump(encoder);
+ }
+
+ template <class T, class CharT, class TempAllocator>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value,void>::type
+ encode_csv(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ const T& val, std::basic_ostream<CharT>& os, const basic_csv_encode_options<CharT>& options = basic_csv_encode_options<CharT>())
+ {
+ using char_type = CharT;
+ basic_csv_encoder<char_type,jsoncons::stream_sink<char_type>,TempAllocator> encoder(os, options, temp_alloc);
+ std::error_code ec;
+ encode_traits<T,CharT>::encode(val, encoder, json(), ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec));
+ }
+ }
+
+} // namespace csv
+} // namespace jsoncons
+
+#endif
diff --git a/include/jsoncons_ext/jmespath/jmespath.hpp b/include/jsoncons_ext/jmespath/jmespath.hpp
new file mode 100644
index 0000000..69458cd
--- /dev/null
+++ b/include/jsoncons_ext/jmespath/jmespath.hpp
@@ -0,0 +1,5215 @@
+// Copyright 2020 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_JMESPATH_JMESPATH_HPP
+#define JSONCONS_JMESPATH_JMESPATH_HPP
+
+#include <string>
+#include <vector>
+#include <unordered_map> // std::unordered_map
+#include <memory>
+#include <type_traits> // std::is_const
+#include <limits> // std::numeric_limits
+#include <utility> // std::move
+#include <functional> //
+#include <algorithm> // std::stable_sort, std::reverse
+#include <cmath> // std::abs
+#include <jsoncons/json.hpp>
+#include <jsoncons_ext/jmespath/jmespath_error.hpp>
+
+namespace jsoncons {
+namespace jmespath {
+
+ enum class operator_kind
+ {
+ default_op, // Identifier, CurrentNode, Index, MultiSelectList, MultiSelectHash, FunctionExpression
+ projection_op,
+ flatten_projection_op, // FlattenProjection
+ or_op,
+ and_op,
+ eq_op,
+ ne_op,
+ lt_op,
+ lte_op,
+ gt_op,
+ gte_op,
+ not_op
+ };
+
+ struct operator_table final
+ {
+ static int precedence_level(operator_kind oper)
+ {
+ switch (oper)
+ {
+ case operator_kind::projection_op:
+ return 11;
+ case operator_kind::flatten_projection_op:
+ return 11;
+ case operator_kind::or_op:
+ return 9;
+ case operator_kind::and_op:
+ return 8;
+ case operator_kind::eq_op:
+ case operator_kind::ne_op:
+ return 6;
+ case operator_kind::lt_op:
+ case operator_kind::lte_op:
+ case operator_kind::gt_op:
+ case operator_kind::gte_op:
+ return 5;
+ case operator_kind::not_op:
+ return 1;
+ default:
+ return 1;
+ }
+ }
+
+ static bool is_right_associative(operator_kind oper)
+ {
+ switch (oper)
+ {
+ case operator_kind::not_op:
+ return true;
+ case operator_kind::projection_op:
+ return true;
+ case operator_kind::flatten_projection_op:
+ return false;
+ case operator_kind::or_op:
+ case operator_kind::and_op:
+ case operator_kind::eq_op:
+ case operator_kind::ne_op:
+ case operator_kind::lt_op:
+ case operator_kind::lte_op:
+ case operator_kind::gt_op:
+ case operator_kind::gte_op:
+ return false;
+ default:
+ return false;
+ }
+ }
+ };
+
+ enum class token_kind
+ {
+ current_node,
+ lparen,
+ rparen,
+ begin_multi_select_hash,
+ end_multi_select_hash,
+ begin_multi_select_list,
+ end_multi_select_list,
+ begin_filter,
+ end_filter,
+ pipe,
+ separator,
+ key,
+ literal,
+ expression,
+ binary_operator,
+ unary_operator,
+ function,
+ end_function,
+ argument,
+ begin_expression_type,
+ end_expression_type,
+ end_of_expression
+ };
+
+ struct literal_arg_t
+ {
+ explicit literal_arg_t() = default;
+ };
+ constexpr literal_arg_t literal_arg{};
+
+ struct begin_expression_type_arg_t
+ {
+ explicit begin_expression_type_arg_t() = default;
+ };
+ constexpr begin_expression_type_arg_t begin_expression_type_arg{};
+
+ struct end_expression_type_arg_t
+ {
+ explicit end_expression_type_arg_t() = default;
+ };
+ constexpr end_expression_type_arg_t end_expression_type_arg{};
+
+ struct end_of_expression_arg_t
+ {
+ explicit end_of_expression_arg_t() = default;
+ };
+ constexpr end_of_expression_arg_t end_of_expression_arg{};
+
+ struct separator_arg_t
+ {
+ explicit separator_arg_t() = default;
+ };
+ constexpr separator_arg_t separator_arg{};
+
+ struct key_arg_t
+ {
+ explicit key_arg_t() = default;
+ };
+ constexpr key_arg_t key_arg{};
+
+ struct lparen_arg_t
+ {
+ explicit lparen_arg_t() = default;
+ };
+ constexpr lparen_arg_t lparen_arg{};
+
+ struct rparen_arg_t
+ {
+ explicit rparen_arg_t() = default;
+ };
+ constexpr rparen_arg_t rparen_arg{};
+
+ struct begin_multi_select_hash_arg_t
+ {
+ explicit begin_multi_select_hash_arg_t() = default;
+ };
+ constexpr begin_multi_select_hash_arg_t begin_multi_select_hash_arg{};
+
+ struct end_multi_select_hash_arg_t
+ {
+ explicit end_multi_select_hash_arg_t() = default;
+ };
+ constexpr end_multi_select_hash_arg_t end_multi_select_hash_arg{};
+
+ struct begin_multi_select_list_arg_t
+ {
+ explicit begin_multi_select_list_arg_t() = default;
+ };
+ constexpr begin_multi_select_list_arg_t begin_multi_select_list_arg{};
+
+ struct end_multi_select_list_arg_t
+ {
+ explicit end_multi_select_list_arg_t() = default;
+ };
+ constexpr end_multi_select_list_arg_t end_multi_select_list_arg{};
+
+ struct begin_filter_arg_t
+ {
+ explicit begin_filter_arg_t() = default;
+ };
+ constexpr begin_filter_arg_t begin_filter_arg{};
+
+ struct end_filter_arg_t
+ {
+ explicit end_filter_arg_t() = default;
+ };
+ constexpr end_filter_arg_t end_filter_arg{};
+
+ struct pipe_arg_t
+ {
+ explicit pipe_arg_t() = default;
+ };
+ constexpr pipe_arg_t pipe_arg{};
+
+ struct current_node_arg_t
+ {
+ explicit current_node_arg_t() = default;
+ };
+ constexpr current_node_arg_t current_node_arg{};
+
+ struct end_function_arg_t
+ {
+ explicit end_function_arg_t() = default;
+ };
+ constexpr end_function_arg_t end_function_arg{};
+
+ struct argument_arg_t
+ {
+ explicit argument_arg_t() = default;
+ };
+ constexpr argument_arg_t argument_arg{};
+
+ struct slice
+ {
+ jsoncons::optional<int64_t> start_;
+ jsoncons::optional<int64_t> stop_;
+ int64_t step_;
+
+ slice()
+ : start_(), stop_(), step_(1)
+ {
+ }
+
+ slice(const jsoncons::optional<int64_t>& start, const jsoncons::optional<int64_t>& end, int64_t step)
+ : start_(start), stop_(end), step_(step)
+ {
+ }
+
+ slice(const slice& other)
+ : start_(other.start_), stop_(other.stop_), step_(other.step_)
+ {
+ }
+
+ slice& operator=(const slice& rhs)
+ {
+ if (this != &rhs)
+ {
+ if (rhs.start_)
+ {
+ start_ = rhs.start_;
+ }
+ else
+ {
+ start_.reset();
+ }
+ if (rhs.stop_)
+ {
+ stop_ = rhs.stop_;
+ }
+ else
+ {
+ stop_.reset();
+ }
+ step_ = rhs.step_;
+ }
+ return *this;
+ }
+
+ int64_t get_start(std::size_t size) const
+ {
+ if (start_)
+ {
+ auto len = *start_ >= 0 ? *start_ : (static_cast<int64_t>(size) + *start_);
+ return len <= static_cast<int64_t>(size) ? len : static_cast<int64_t>(size);
+ }
+ else
+ {
+ if (step_ >= 0)
+ {
+ return 0;
+ }
+ else
+ {
+ return static_cast<int64_t>(size);
+ }
+ }
+ }
+
+ int64_t get_stop(std::size_t size) const
+ {
+ if (stop_)
+ {
+ auto len = *stop_ >= 0 ? *stop_ : (static_cast<int64_t>(size) + *stop_);
+ return len <= static_cast<int64_t>(size) ? len : static_cast<int64_t>(size);
+ }
+ else
+ {
+ return step_ >= 0 ? static_cast<int64_t>(size) : -1;
+ }
+ }
+
+ int64_t step() const
+ {
+ return step_; // Allow negative
+ }
+ };
+
+ namespace detail {
+
+ enum class path_state
+ {
+ start,
+ lhs_expression,
+ rhs_expression,
+ sub_expression,
+ expression_type,
+ comparator_expression,
+ function_expression,
+ argument,
+ expression_or_expression_type,
+ quoted_string,
+ raw_string,
+ raw_string_escape_char,
+ quoted_string_escape_char,
+ escape_u1,
+ escape_u2,
+ escape_u3,
+ escape_u4,
+ escape_expect_surrogate_pair1,
+ escape_expect_surrogate_pair2,
+ escape_u5,
+ escape_u6,
+ escape_u7,
+ escape_u8,
+ literal,
+ key_expr,
+ val_expr,
+ identifier_or_function_expr,
+ unquoted_string,
+ key_val_expr,
+ number,
+ digit,
+ index_or_slice_expression,
+ bracket_specifier,
+ bracket_specifier_or_multi_select_list,
+ filter,
+ multi_select_list,
+ multi_select_hash,
+ rhs_slice_expression_stop,
+ rhs_slice_expression_step,
+ expect_rbracket,
+ expect_rparen,
+ expect_dot,
+ expect_rbrace,
+ expect_colon,
+ expect_multi_select_list,
+ cmp_lt_or_lte,
+ cmp_eq,
+ cmp_gt_or_gte,
+ cmp_ne,
+ expect_pipe_or_or,
+ expect_and
+ };
+
+ // dynamic_resources
+
+ template<class Json, class JsonReference>
+ class dynamic_resources
+ {
+ typedef typename Json::char_type char_type;
+ typedef typename Json::char_traits_type char_traits_type;
+ typedef std::basic_string<char_type,char_traits_type> string_type;
+ typedef typename Json::string_view_type string_view_type;
+ typedef JsonReference reference;
+ using pointer = typename std::conditional<std::is_const<typename std::remove_reference<JsonReference>::type>::value,typename Json::const_pointer,typename Json::pointer>::type;
+ typedef typename Json::const_pointer const_pointer;
+
+ std::vector<std::unique_ptr<Json>> temp_storage_;
+
+ public:
+ ~dynamic_resources()
+ {
+ }
+
+ reference number_type_name()
+ {
+ static Json number_type_name(JSONCONS_STRING_CONSTANT(char_type, "number"));
+
+ return number_type_name;
+ }
+
+ reference boolean_type_name()
+ {
+ static Json boolean_type_name(JSONCONS_STRING_CONSTANT(char_type, "boolean"));
+
+ return boolean_type_name;
+ }
+
+ reference string_type_name()
+ {
+ static Json string_type_name(JSONCONS_STRING_CONSTANT(char_type, "string"));
+
+ return string_type_name;
+ }
+
+ reference object_type_name()
+ {
+ static Json object_type_name(JSONCONS_STRING_CONSTANT(char_type, "object"));
+
+ return object_type_name;
+ }
+
+ reference array_type_name()
+ {
+ static Json array_type_name(JSONCONS_STRING_CONSTANT(char_type, "array"));
+
+ return array_type_name;
+ }
+
+ reference null_type_name()
+ {
+ static Json null_type_name(JSONCONS_STRING_CONSTANT(char_type, "null"));
+
+ return null_type_name;
+ }
+
+ reference true_value() const
+ {
+ static const Json true_value(true, semantic_tag::none);
+ return true_value;
+ }
+
+ reference false_value() const
+ {
+ static const Json false_value(false, semantic_tag::none);
+ return false_value;
+ }
+
+ reference null_value() const
+ {
+ static const Json null_value(null_type(), semantic_tag::none);
+ return null_value;
+ }
+
+ template <typename... Args>
+ Json* create_json(Args&& ... args)
+ {
+ auto temp = jsoncons::make_unique<Json>(std::forward<Args>(args)...);
+ Json* ptr = temp.get();
+ temp_storage_.emplace_back(std::move(temp));
+ return ptr;
+ }
+ };
+
+ template<class Json, class JsonReference>
+ class jmespath_evaluator
+ {
+ public:
+ typedef typename Json::char_type char_type;
+ typedef typename Json::char_traits_type char_traits_type;
+ typedef std::basic_string<char_type,char_traits_type> string_type;
+ typedef typename Json::string_view_type string_view_type;
+ typedef JsonReference reference;
+ using pointer = typename std::conditional<std::is_const<typename std::remove_reference<JsonReference>::type>::value,typename Json::const_pointer,typename Json::pointer>::type;
+ typedef typename Json::const_pointer const_pointer;
+
+ static bool is_false(reference ref)
+ {
+ return (ref.is_array() && ref.empty()) ||
+ (ref.is_object() && ref.empty()) ||
+ (ref.is_string() && ref.as_string_view().size() == 0) ||
+ (ref.is_bool() && !ref.as_bool()) ||
+ ref.is_null();
+ }
+
+ static bool is_true(reference ref)
+ {
+ return !is_false(ref);
+ }
+
+ class unary_operator
+ {
+ std::size_t precedence_level_;
+ bool is_right_associative_;
+
+ protected:
+ ~unary_operator() = default; // virtual destructor not needed
+ public:
+ unary_operator(operator_kind oper)
+ : precedence_level_(operator_table::precedence_level(oper)),
+ is_right_associative_(operator_table::is_right_associative(oper))
+ {
+ }
+
+ std::size_t precedence_level() const
+ {
+ return precedence_level_;
+ }
+ bool is_right_associative() const
+ {
+ return is_right_associative_;
+ }
+
+ virtual reference evaluate(reference val, dynamic_resources<Json,JsonReference>&, std::error_code& ec) const = 0;
+ };
+
+ class not_expression final : public unary_operator
+ {
+ public:
+ not_expression()
+ : unary_operator(operator_kind::not_op)
+ {}
+
+ reference evaluate(reference val, dynamic_resources<Json,JsonReference>& resources, std::error_code&) const override
+ {
+ return is_false(val) ? resources.true_value() : resources.false_value();
+ }
+ };
+
+ class binary_operator
+ {
+ std::size_t precedence_level_;
+ bool is_right_associative_;
+ protected:
+ ~binary_operator() = default; // virtual destructor not needed
+ public:
+ binary_operator(operator_kind oper)
+ : precedence_level_(operator_table::precedence_level(oper)),
+ is_right_associative_(operator_table::is_right_associative(oper))
+ {
+ }
+
+
+ std::size_t precedence_level() const
+ {
+ return precedence_level_;
+ }
+ bool is_right_associative() const
+ {
+ return is_right_associative_;
+ }
+
+ virtual reference evaluate(reference lhs, reference rhs, dynamic_resources<Json,JsonReference>&, std::error_code& ec) const = 0;
+
+ virtual std::string to_string(std::size_t indent = 0) const
+ {
+ std::string s;
+ for (std::size_t i = 0; i <= indent; ++i)
+ {
+ s.push_back(' ');
+ }
+ s.append("to_string not implemented\n");
+ return s;
+ }
+ };
+
+ // expression_base
+ class expression_base
+ {
+ std::size_t precedence_level_;
+ bool is_right_associative_;
+ bool is_projection_;
+ public:
+ expression_base(operator_kind oper, bool is_projection)
+ : precedence_level_(operator_table::precedence_level(oper)),
+ is_right_associative_(operator_table::is_right_associative(oper)),
+ is_projection_(is_projection)
+ {
+ }
+
+ std::size_t precedence_level() const
+ {
+ return precedence_level_;
+ }
+
+ bool is_right_associative() const
+ {
+ return is_right_associative_;
+ }
+
+ bool is_projection() const
+ {
+ return is_projection_;
+ }
+
+ virtual ~expression_base() = default;
+
+ virtual reference evaluate(reference val, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const = 0;
+
+ virtual void add_expression(std::unique_ptr<expression_base>&& expressions) = 0;
+
+ virtual std::string to_string(std::size_t = 0) const
+ {
+ return std::string("to_string not implemented");
+ }
+ };
+
+ // parameter
+
+ enum class parameter_kind{value, expression};
+
+ class parameter
+ {
+ parameter_kind type_;
+
+ union
+ {
+ expression_base* expression_;
+ pointer value_;
+ };
+
+ public:
+
+ parameter(const parameter& other) noexcept
+ : type_(other.type_)
+ {
+ switch (type_)
+ {
+ case parameter_kind::expression:
+ expression_ = other.expression_;
+ break;
+ case parameter_kind::value:
+ value_ = other.value_;
+ break;
+ default:
+ break;
+ }
+ }
+
+ parameter(reference value) noexcept
+ : type_(parameter_kind::value), value_(std::addressof(value))
+ {
+ }
+
+ parameter(expression_base* expression) noexcept
+ : type_(parameter_kind::expression), expression_(expression)
+ {
+ }
+
+ parameter& operator=(const parameter& other)
+ {
+ if (&other != this)
+ {
+ type_ = other.type_;
+ switch (type_)
+ {
+ case parameter_kind::expression:
+ expression_ = other.expression_;
+ break;
+ case parameter_kind::value:
+ value_ = other.value_;
+ break;
+ default:
+ break;
+ }
+ }
+ return *this;
+ }
+
+ bool is_value() const
+ {
+ return type_ == parameter_kind::value;
+ }
+
+ bool is_expression() const
+ {
+ return type_ == parameter_kind::expression;
+ }
+
+ const Json& value() const
+ {
+ return *value_;
+ }
+
+ const expression_base& expression() const
+ {
+ return *expression_;
+ }
+ };
+
+ // function_base
+ class function_base
+ {
+ jsoncons::optional<std::size_t> arg_count_;
+ public:
+ function_base(jsoncons::optional<std::size_t> arg_count)
+ : arg_count_(arg_count)
+ {
+ }
+
+ jsoncons::optional<std::size_t> arity() const
+ {
+ return arg_count_;
+ }
+
+ virtual ~function_base() = default;
+
+ virtual reference evaluate(std::vector<parameter>& args, dynamic_resources<Json,JsonReference>&, std::error_code& ec) const = 0;
+
+ virtual std::string to_string(std::size_t = 0) const
+ {
+ return std::string("to_string not implemented");
+ }
+ };
+
+ class abs_function : public function_base
+ {
+ public:
+ abs_function()
+ : function_base(1)
+ {
+ }
+
+ reference evaluate(std::vector<parameter>& args, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override
+ {
+ JSONCONS_ASSERT(args.size() == *this->arity());
+
+ if (!args[0].is_value())
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+
+ reference arg0 = args[0].value();
+ switch (arg0.type())
+ {
+ case json_type::uint64_value:
+ return arg0;
+ case json_type::int64_value:
+ {
+ return arg0.template as<int64_t>() >= 0 ? arg0 : *resources.create_json(std::abs(arg0.template as<int64_t>()));
+ }
+ case json_type::double_value:
+ {
+ return arg0.template as<double>() >= 0 ? arg0 : *resources.create_json(std::abs(arg0.template as<double>()));
+ }
+ default:
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+ }
+ }
+ };
+
+ class avg_function : public function_base
+ {
+ public:
+ avg_function()
+ : function_base(1)
+ {
+ }
+
+ reference evaluate(std::vector<parameter>& args, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override
+ {
+ JSONCONS_ASSERT(args.size() == *this->arity());
+
+ if (!args[0].is_value())
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+
+ reference arg0 = args[0].value();
+ if (!arg0.is_array())
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+ if (arg0.empty())
+ {
+ return resources.null_value();
+ }
+
+ double sum = 0;
+ for (auto& j : arg0.array_range())
+ {
+ if (!j.is_number())
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+ sum += j.template as<double>();
+ }
+
+ return sum == 0 ? resources.null_value() : *resources.create_json(sum/arg0.size());
+ }
+ };
+
+ class ceil_function : public function_base
+ {
+ public:
+ ceil_function()
+ : function_base(1)
+ {
+ }
+
+ reference evaluate(std::vector<parameter>& args, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override
+ {
+ JSONCONS_ASSERT(args.size() == *this->arity());
+
+ if (!args[0].is_value())
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+
+ reference arg0 = args[0].value();
+ switch (arg0.type())
+ {
+ case json_type::uint64_value:
+ case json_type::int64_value:
+ {
+ return *resources.create_json(arg0.template as<double>());
+ }
+ case json_type::double_value:
+ {
+ return *resources.create_json(std::ceil(arg0.template as<double>()));
+ }
+ default:
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+ }
+ };
+
+ class contains_function : public function_base
+ {
+ public:
+ contains_function()
+ : function_base(2)
+ {
+ }
+
+ reference evaluate(std::vector<parameter>& args, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override
+ {
+ JSONCONS_ASSERT(args.size() == *this->arity());
+
+ if (!(args[0].is_value() && args[1].is_value()))
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+
+
+ reference arg0 = args[0].value();
+ reference arg1 = args[1].value();
+
+ switch (arg0.type())
+ {
+ case json_type::array_value:
+ for (auto& j : arg0.array_range())
+ {
+ if (j == arg1)
+ {
+ return resources.true_value();
+ }
+ }
+ return resources.false_value();
+ case json_type::string_value:
+ {
+ if (!arg1.is_string())
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+ auto sv0 = arg0.template as<string_view_type>();
+ auto sv1 = arg1.template as<string_view_type>();
+ return sv0.find(sv1) != string_view_type::npos ? resources.true_value() : resources.false_value();
+ }
+ default:
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+ }
+ }
+ };
+
+ class ends_with_function : public function_base
+ {
+ public:
+ ends_with_function()
+ : function_base(2)
+ {
+ }
+
+ reference evaluate(std::vector<parameter>& args, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override
+ {
+ JSONCONS_ASSERT(args.size() == *this->arity());
+
+ if (!(args[0].is_value() && args[1].is_value()))
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+
+ reference arg0 = args[0].value();
+ if (!arg0.is_string())
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+
+ reference arg1 = args[1].value();
+ if (!arg1.is_string())
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+
+ auto sv0 = arg0.template as<string_view_type>();
+ auto sv1 = arg1.template as<string_view_type>();
+
+ if (sv1.length() <= sv0.length() && sv1 == sv0.substr(sv0.length() - sv1.length()))
+ {
+ return resources.true_value();
+ }
+ else
+ {
+ return resources.false_value();
+ }
+ }
+ };
+
+ class floor_function : public function_base
+ {
+ public:
+ floor_function()
+ : function_base(1)
+ {
+ }
+
+ reference evaluate(std::vector<parameter>& args, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override
+ {
+ JSONCONS_ASSERT(args.size() == *this->arity());
+
+ if (!args[0].is_value())
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+
+ reference arg0 = args[0].value();
+ switch (arg0.type())
+ {
+ case json_type::uint64_value:
+ case json_type::int64_value:
+ {
+ return *resources.create_json(arg0.template as<double>());
+ }
+ case json_type::double_value:
+ {
+ return *resources.create_json(std::floor(arg0.template as<double>()));
+ }
+ default:
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+ }
+ };
+
+ class join_function : public function_base
+ {
+ public:
+ join_function()
+ : function_base(2)
+ {
+ }
+
+ reference evaluate(std::vector<parameter>& args, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override
+ {
+ JSONCONS_ASSERT(args.size() == *this->arity());
+
+ reference arg0 = args[0].value();
+ reference arg1 = args[1].value();
+
+ if (!(args[0].is_value() && args[1].is_value()))
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+
+ if (!arg0.is_string())
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+ if (!arg1.is_array())
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+
+ string_type sep = arg0.template as<string_type>();
+ string_type buf;
+ for (auto& j : arg1.array_range())
+ {
+ if (!j.is_string())
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+ if (!buf.empty())
+ {
+ buf.append(sep);
+ }
+ auto sv = j.template as<string_view_type>();
+ buf.append(sv.begin(), sv.end());
+ }
+ return *resources.create_json(buf);
+ }
+ };
+
+ class length_function : public function_base
+ {
+ public:
+ length_function()
+ : function_base(1)
+ {
+ }
+
+ reference evaluate(std::vector<parameter>& args, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override
+ {
+ JSONCONS_ASSERT(args.size() == *this->arity());
+
+ if (!args[0].is_value())
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+
+ reference arg0 = args[0].value();
+
+ switch (arg0.type())
+ {
+ case json_type::object_value:
+ case json_type::array_value:
+ return *resources.create_json(arg0.size());
+ case json_type::string_value:
+ {
+ auto sv0 = arg0.template as<string_view_type>();
+ auto length = unicode_traits::count_codepoints(sv0.data(), sv0.size());
+ return *resources.create_json(length);
+ }
+ default:
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+ }
+ }
+ };
+
+ class max_function : public function_base
+ {
+ public:
+ max_function()
+ : function_base(1)
+ {
+ }
+
+ reference evaluate(std::vector<parameter>& args, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override
+ {
+ JSONCONS_ASSERT(args.size() == *this->arity());
+
+ if (!args[0].is_value())
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+
+ reference arg0 = args[0].value();
+ if (!arg0.is_array())
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+ if (arg0.empty())
+ {
+ return resources.null_value();
+ }
+
+ bool is_number = arg0.at(0).is_number();
+ bool is_string = arg0.at(0).is_string();
+ if (!is_number && !is_string)
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+
+ std::size_t index = 0;
+ for (std::size_t i = 1; i < arg0.size(); ++i)
+ {
+ if (!(arg0.at(i).is_number() == is_number && arg0.at(i).is_string() == is_string))
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+ if (arg0.at(i) > arg0.at(index))
+ {
+ index = i;
+ }
+ }
+
+ return arg0.at(index);
+ }
+ };
+
+ class max_by_function : public function_base
+ {
+ public:
+ max_by_function()
+ : function_base(2)
+ {
+ }
+
+ reference evaluate(std::vector<parameter>& args, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override
+ {
+ JSONCONS_ASSERT(args.size() == *this->arity());
+
+ if (!(args[0].is_value() && args[1].is_expression()))
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+
+ reference arg0 = args[0].value();
+ if (!arg0.is_array())
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+ if (arg0.empty())
+ {
+ return resources.null_value();
+ }
+
+ const auto& expr = args[1].expression();
+
+ std::error_code ec2;
+ Json key1 = expr.evaluate(arg0.at(0), resources, ec2);
+
+ bool is_number = key1.is_number();
+ bool is_string = key1.is_string();
+ if (!(is_number || is_string))
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+
+ std::size_t index = 0;
+ for (std::size_t i = 1; i < arg0.size(); ++i)
+ {
+ reference key2 = expr.evaluate(arg0.at(i), resources, ec2);
+ if (!(key2.is_number() == is_number && key2.is_string() == is_string))
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+ if (key2 > key1)
+ {
+ key1 = key2;
+ index = i;
+ }
+ }
+
+ return arg0.at(index);
+ }
+ };
+
+ class map_function : public function_base
+ {
+ public:
+ map_function()
+ : function_base(2)
+ {
+ }
+
+ reference evaluate(std::vector<parameter>& args, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override
+ {
+ JSONCONS_ASSERT(args.size() == *this->arity());
+
+ if (!(args[0].is_expression() && args[1].is_value()))
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+ const auto& expr = args[0].expression();
+
+ reference arg0 = args[1].value();
+ if (!arg0.is_array())
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+
+ auto result = resources.create_json(json_array_arg);
+
+ for (auto& item : arg0.array_range())
+ {
+ auto& j = expr.evaluate(item, resources, ec);
+ if (ec)
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+ result->emplace_back(json_const_pointer_arg, std::addressof(j));
+ }
+
+ return *result;
+ }
+
+ std::string to_string(std::size_t = 0) const override
+ {
+ return std::string("map_function\n");
+ }
+ };
+
+ class min_function : public function_base
+ {
+ public:
+ min_function()
+ : function_base(1)
+ {
+ }
+
+ reference evaluate(std::vector<parameter>& args, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override
+ {
+ JSONCONS_ASSERT(args.size() == *this->arity());
+
+ if (!args[0].is_value())
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+
+ reference arg0 = args[0].value();
+ if (!arg0.is_array())
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+ if (arg0.empty())
+ {
+ return resources.null_value();
+ }
+
+ bool is_number = arg0.at(0).is_number();
+ bool is_string = arg0.at(0).is_string();
+ if (!is_number && !is_string)
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+
+ std::size_t index = 0;
+ for (std::size_t i = 1; i < arg0.size(); ++i)
+ {
+ if (!(arg0.at(i).is_number() == is_number && arg0.at(i).is_string() == is_string))
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+ if (arg0.at(i) < arg0.at(index))
+ {
+ index = i;
+ }
+ }
+
+ return arg0.at(index);
+ }
+ };
+
+ class min_by_function : public function_base
+ {
+ public:
+ min_by_function()
+ : function_base(2)
+ {
+ }
+
+ reference evaluate(std::vector<parameter>& args, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override
+ {
+ JSONCONS_ASSERT(args.size() == *this->arity());
+
+ if (!(args[0].is_value() && args[1].is_expression()))
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+
+ reference arg0 = args[0].value();
+ if (!arg0.is_array())
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+ if (arg0.empty())
+ {
+ return resources.null_value();
+ }
+
+ const auto& expr = args[1].expression();
+
+ std::error_code ec2;
+ Json key1 = expr.evaluate(arg0.at(0), resources, ec2);
+
+ bool is_number = key1.is_number();
+ bool is_string = key1.is_string();
+ if (!(is_number || is_string))
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+
+ std::size_t index = 0;
+ for (std::size_t i = 1; i < arg0.size(); ++i)
+ {
+ reference key2 = expr.evaluate(arg0.at(i), resources, ec2);
+ if (!(key2.is_number() == is_number && key2.is_string() == is_string))
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+ if (key2 < key1)
+ {
+ key1 = key2;
+ index = i;
+ }
+ }
+
+ return arg0.at(index);
+ }
+ };
+
+ class merge_function : public function_base
+ {
+ public:
+ merge_function()
+ : function_base(jsoncons::optional<std::size_t>())
+ {
+ }
+
+ reference evaluate(std::vector<parameter>& args, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override
+ {
+ if (args.empty())
+ {
+ ec = jmespath_errc::invalid_arity;
+ return resources.null_value();
+ }
+
+ for (auto& param : args)
+ {
+ if (!param.is_value())
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+ }
+
+ reference arg0 = args[0].value();
+ if (!arg0.is_object())
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+ if (args.size() == 1)
+ {
+ return arg0;
+ }
+
+ auto result = resources.create_json(arg0);
+ for (std::size_t i = 1; i < args.size(); ++i)
+ {
+ reference argi = args[i].value();
+ if (!argi.is_object())
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+ for (auto& item : argi.object_range())
+ {
+ result->insert_or_assign(item.key(),item.value());
+ }
+ }
+
+ return *result;
+ }
+ };
+
+ class type_function : public function_base
+ {
+ public:
+ type_function()
+ : function_base(1)
+ {
+ }
+
+ reference evaluate(std::vector<parameter>& args, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override
+ {
+ JSONCONS_ASSERT(args.size() == *this->arity());
+
+ if (!args[0].is_value())
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+
+ reference arg0 = args[0].value();
+
+ switch (arg0.type())
+ {
+ case json_type::int64_value:
+ case json_type::uint64_value:
+ case json_type::double_value:
+ return resources.number_type_name();
+ case json_type::bool_value:
+ return resources.boolean_type_name();
+ case json_type::string_value:
+ return resources.string_type_name();
+ case json_type::object_value:
+ return resources.object_type_name();
+ case json_type::array_value:
+ return resources.array_type_name();
+ default:
+ return resources.null_type_name();
+ break;
+
+ }
+ }
+ };
+
+ class sort_function : public function_base
+ {
+ public:
+ sort_function()
+ : function_base(1)
+ {
+ }
+
+ reference evaluate(std::vector<parameter>& args, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override
+ {
+ JSONCONS_ASSERT(args.size() == *this->arity());
+
+ if (!args[0].is_value())
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+
+ reference arg0 = args[0].value();
+ if (!arg0.is_array())
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+ if (arg0.size() <= 1)
+ {
+ return arg0;
+ }
+
+ bool is_number = arg0.at(0).is_number();
+ bool is_string = arg0.at(0).is_string();
+ if (!is_number && !is_string)
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+
+ for (std::size_t i = 1; i < arg0.size(); ++i)
+ {
+ if (arg0.at(i).is_number() != is_number || arg0.at(i).is_string() != is_string)
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+ }
+
+ auto v = resources.create_json(arg0);
+ std::stable_sort((v->array_range()).begin(), (v->array_range()).end());
+ return *v;
+ }
+ };
+
+ class sort_by_function : public function_base
+ {
+ public:
+ sort_by_function()
+ : function_base(2)
+ {
+ }
+
+ reference evaluate(std::vector<parameter>& args, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override
+ {
+ JSONCONS_ASSERT(args.size() == *this->arity());
+
+ if (!(args[0].is_value() && args[1].is_expression()))
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+
+ reference arg0 = args[0].value();
+ if (!arg0.is_array())
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+ if (arg0.size() <= 1)
+ {
+ return arg0;
+ }
+
+ const auto& expr = args[1].expression();
+
+ auto v = resources.create_json(arg0);
+ std::stable_sort((v->array_range()).begin(), (v->array_range()).end(),
+ [&expr,&resources,&ec](reference lhs, reference rhs) -> bool
+ {
+ std::error_code ec2;
+ reference key1 = expr.evaluate(lhs, resources, ec2);
+ bool is_number = key1.is_number();
+ bool is_string = key1.is_string();
+ if (!(is_number || is_string))
+ {
+ ec = jmespath_errc::invalid_type;
+ }
+
+ reference key2 = expr.evaluate(rhs, resources, ec2);
+ if (!(key2.is_number() == is_number && key2.is_string() == is_string))
+ {
+ ec = jmespath_errc::invalid_type;
+ }
+
+ return key1 < key2;
+ });
+ return ec ? resources.null_value() : *v;
+ }
+
+ std::string to_string(std::size_t = 0) const override
+ {
+ return std::string("sort_by_function\n");
+ }
+ };
+
+ class keys_function final : public function_base
+ {
+ public:
+ keys_function()
+ : function_base(1)
+ {
+ }
+
+ reference evaluate(std::vector<parameter>& args, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override
+ {
+ JSONCONS_ASSERT(args.size() == *this->arity());
+
+ if (!args[0].is_value())
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+
+ reference arg0 = args[0].value();
+ if (!arg0.is_object())
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+
+ auto result = resources.create_json(json_array_arg);
+ result->reserve(args.size());
+
+ for (auto& item : arg0.object_range())
+ {
+ result->emplace_back(item.key());
+ }
+ return *result;
+ }
+ };
+
+ class values_function final : public function_base
+ {
+ public:
+ values_function()
+ : function_base(1)
+ {
+ }
+
+ reference evaluate(std::vector<parameter>& args, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override
+ {
+ JSONCONS_ASSERT(args.size() == *this->arity());
+
+ if (!args[0].is_value())
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+
+ reference arg0 = args[0].value();
+ if (!arg0.is_object())
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+
+ auto result = resources.create_json(json_array_arg);
+ result->reserve(args.size());
+
+ for (auto& item : arg0.object_range())
+ {
+ result->emplace_back(item.value());
+ }
+ return *result;
+ }
+ };
+
+ class reverse_function final : public function_base
+ {
+ public:
+ reverse_function()
+ : function_base(1)
+ {
+ }
+
+ reference evaluate(std::vector<parameter>& args, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override
+ {
+ JSONCONS_ASSERT(args.size() == *this->arity());
+
+ if (!args[0].is_value())
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+
+ reference arg0 = args[0].value();
+ switch (arg0.type())
+ {
+ case json_type::string_value:
+ {
+ string_view_type sv = arg0.as_string_view();
+ std::basic_string<char32_t> buf;
+ unicode_traits::convert(sv.data(), sv.size(), buf);
+ std::reverse(buf.begin(), buf.end());
+ string_type s;
+ unicode_traits::convert(buf.data(), buf.size(), s);
+ return *resources.create_json(s);
+ }
+ case json_type::array_value:
+ {
+ auto result = resources.create_json(arg0);
+ std::reverse(result->array_range().begin(),result->array_range().end());
+ return *result;
+ }
+ default:
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+ }
+ };
+
+ class starts_with_function : public function_base
+ {
+ public:
+ starts_with_function()
+ : function_base(2)
+ {
+ }
+
+ reference evaluate(std::vector<parameter>& args, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override
+ {
+ JSONCONS_ASSERT(args.size() == *this->arity());
+
+ if (!(args[0].is_value() && args[1].is_value()))
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+
+ reference arg0 = args[0].value();
+ if (!arg0.is_string())
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+
+ reference arg1 = args[1].value();
+ if (!arg1.is_string())
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+
+ auto sv0 = arg0.template as<string_view_type>();
+ auto sv1 = arg1.template as<string_view_type>();
+
+ if (sv1.length() <= sv0.length() && sv1 == sv0.substr(0, sv1.length()))
+ {
+ return resources.true_value();
+ }
+ else
+ {
+ return resources.false_value();
+ }
+ }
+ };
+
+ class sum_function : public function_base
+ {
+ public:
+ sum_function()
+ : function_base(1)
+ {
+ }
+
+ reference evaluate(std::vector<parameter>& args, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override
+ {
+ JSONCONS_ASSERT(args.size() == *this->arity());
+
+ if (!args[0].is_value())
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+
+ reference arg0 = args[0].value();
+ if (!arg0.is_array())
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+ double sum = 0;
+ for (auto& j : arg0.array_range())
+ {
+ if (!j.is_number())
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+ sum += j.template as<double>();
+ }
+
+ return *resources.create_json(sum);
+ }
+ };
+
+ class to_array_function final : public function_base
+ {
+ public:
+ to_array_function()
+ : function_base(1)
+ {
+ }
+
+ reference evaluate(std::vector<parameter>& args, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override
+ {
+ JSONCONS_ASSERT(args.size() == *this->arity());
+
+ if (!args[0].is_value())
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+
+ reference arg0 = args[0].value();
+ if (arg0.is_array())
+ {
+ return arg0;
+ }
+ else
+ {
+ auto result = resources.create_json(json_array_arg);
+ result->push_back(arg0);
+ return *result;
+ }
+ }
+
+ std::string to_string(std::size_t = 0) const override
+ {
+ return std::string("to_array_function\n");
+ }
+ };
+
+ class to_number_function final : public function_base
+ {
+ public:
+ to_number_function()
+ : function_base(1)
+ {
+ }
+
+ reference evaluate(std::vector<parameter>& args, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override
+ {
+ JSONCONS_ASSERT(args.size() == *this->arity());
+
+ if (!args[0].is_value())
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+
+ reference arg0 = args[0].value();
+ switch (arg0.type())
+ {
+ case json_type::int64_value:
+ case json_type::uint64_value:
+ case json_type::double_value:
+ return arg0;
+ case json_type::string_value:
+ {
+ auto sv = arg0.as_string_view();
+ uint64_t uval{ 0 };
+ auto result1 = jsoncons::detail::to_integer(sv.data(), sv.length(), uval);
+ if (result1)
+ {
+ return *resources.create_json(uval);
+ }
+ int64_t sval{ 0 };
+ auto result2 = jsoncons::detail::to_integer(sv.data(), sv.length(), sval);
+ if (result2)
+ {
+ return *resources.create_json(sval);
+ }
+ jsoncons::detail::chars_to to_double;
+ try
+ {
+ auto s = arg0.as_string();
+ double d = to_double(s.c_str(), s.length());
+ return *resources.create_json(d);
+ }
+ catch (const std::exception&)
+ {
+ return resources.null_value();
+ }
+ }
+ default:
+ return resources.null_value();
+ }
+ }
+
+ std::string to_string(std::size_t = 0) const override
+ {
+ return std::string("to_number_function\n");
+ }
+ };
+
+ class to_string_function final : public function_base
+ {
+ public:
+ to_string_function()
+ : function_base(1)
+ {
+ }
+
+ reference evaluate(std::vector<parameter>& args, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override
+ {
+ JSONCONS_ASSERT(args.size() == *this->arity());
+
+ if (!args[0].is_value())
+ {
+ ec = jmespath_errc::invalid_type;
+ return resources.null_value();
+ }
+
+ reference arg0 = args[0].value();
+ return *resources.create_json(arg0.template as<string_type>());
+ }
+
+ std::string to_string(std::size_t = 0) const override
+ {
+ return std::string("to_string_function\n");
+ }
+ };
+
+ class not_null_function final : public function_base
+ {
+ public:
+ not_null_function()
+ : function_base(jsoncons::optional<std::size_t>())
+ {
+ }
+
+ reference evaluate(std::vector<parameter>& args, dynamic_resources<Json,JsonReference>& resources, std::error_code&) const override
+ {
+ for (auto& param : args)
+ {
+ if (param.is_value() && !param.value().is_null())
+ {
+ return param.value();
+ }
+ }
+ return resources.null_value();
+ }
+
+ std::string to_string(std::size_t = 0) const override
+ {
+ return std::string("to_string_function\n");
+ }
+ };
+
+ // token
+
+ class token
+ {
+ public:
+ token_kind type_;
+
+ union
+ {
+ std::unique_ptr<expression_base> expression_;
+ const unary_operator* unary_operator_;
+ const binary_operator* binary_operator_;
+ const function_base* function_;
+ Json value_;
+ string_type key_;
+ };
+ public:
+
+ token(current_node_arg_t) noexcept
+ : type_(token_kind::current_node)
+ {
+ }
+
+ token(end_function_arg_t) noexcept
+ : type_(token_kind::end_function)
+ {
+ }
+
+ token(separator_arg_t) noexcept
+ : type_(token_kind::separator)
+ {
+ }
+
+ token(lparen_arg_t) noexcept
+ : type_(token_kind::lparen)
+ {
+ }
+
+ token(rparen_arg_t) noexcept
+ : type_(token_kind::rparen)
+ {
+ }
+
+ token(end_of_expression_arg_t) noexcept
+ : type_(token_kind::end_of_expression)
+ {
+ }
+
+ token(begin_multi_select_hash_arg_t) noexcept
+ : type_(token_kind::begin_multi_select_hash)
+ {
+ }
+
+ token(end_multi_select_hash_arg_t) noexcept
+ : type_(token_kind::end_multi_select_hash)
+ {
+ }
+
+ token(begin_multi_select_list_arg_t) noexcept
+ : type_(token_kind::begin_multi_select_list)
+ {
+ }
+
+ token(end_multi_select_list_arg_t) noexcept
+ : type_(token_kind::end_multi_select_list)
+ {
+ }
+
+ token(begin_filter_arg_t) noexcept
+ : type_(token_kind::begin_filter)
+ {
+ }
+
+ token(end_filter_arg_t) noexcept
+ : type_(token_kind::end_filter)
+ {
+ }
+
+ token(pipe_arg_t) noexcept
+ : type_(token_kind::pipe)
+ {
+ }
+
+ token(key_arg_t, const string_type& key)
+ : type_(token_kind::key)
+ {
+ new (&key_) string_type(key);
+ }
+
+ token(std::unique_ptr<expression_base>&& expression)
+ : type_(token_kind::expression)
+ {
+ new (&expression_) std::unique_ptr<expression_base>(std::move(expression));
+ }
+
+ token(const unary_operator* expression) noexcept
+ : type_(token_kind::unary_operator),
+ unary_operator_(expression)
+ {
+ }
+
+ token(const binary_operator* expression) noexcept
+ : type_(token_kind::binary_operator),
+ binary_operator_(expression)
+ {
+ }
+
+ token(const function_base* function) noexcept
+ : type_(token_kind::function),
+ function_(function)
+ {
+ }
+
+ token(argument_arg_t) noexcept
+ : type_(token_kind::argument)
+ {
+ }
+
+ token(begin_expression_type_arg_t) noexcept
+ : type_(token_kind::begin_expression_type)
+ {
+ }
+
+ token(end_expression_type_arg_t) noexcept
+ : type_(token_kind::end_expression_type)
+ {
+ }
+
+ token(literal_arg_t, Json&& value) noexcept
+ : type_(token_kind::literal), value_(std::move(value))
+ {
+ }
+
+ token(token&& other) noexcept
+ {
+ construct(std::forward<token>(other));
+ }
+
+ token& operator=(token&& other)
+ {
+ if (&other != this)
+ {
+ if (type_ == other.type_)
+ {
+ switch (type_)
+ {
+ case token_kind::expression:
+ expression_ = std::move(other.expression_);
+ break;
+ case token_kind::key:
+ key_ = std::move(other.key_);
+ break;
+ case token_kind::unary_operator:
+ unary_operator_ = other.unary_operator_;
+ break;
+ case token_kind::binary_operator:
+ binary_operator_ = other.binary_operator_;
+ break;
+ case token_kind::function:
+ function_ = other.function_;
+ break;
+ case token_kind::literal:
+ value_ = std::move(other.value_);
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ destroy();
+ construct(std::forward<token>(other));
+ }
+ }
+ return *this;
+ }
+
+ ~token() noexcept
+ {
+ destroy();
+ }
+
+ token_kind type() const
+ {
+ return type_;
+ }
+
+ bool is_lparen() const
+ {
+ return type_ == token_kind::lparen;
+ }
+
+ bool is_lbrace() const
+ {
+ return type_ == token_kind::begin_multi_select_hash;
+ }
+
+ bool is_key() const
+ {
+ return type_ == token_kind::key;
+ }
+
+ bool is_rparen() const
+ {
+ return type_ == token_kind::rparen;
+ }
+
+ bool is_current_node() const
+ {
+ return type_ == token_kind::current_node;
+ }
+
+ bool is_projection() const
+ {
+ return type_ == token_kind::expression && expression_->is_projection();
+ }
+
+ bool is_expression() const
+ {
+ return type_ == token_kind::expression;
+ }
+
+ bool is_operator() const
+ {
+ return type_ == token_kind::unary_operator ||
+ type_ == token_kind::binary_operator;
+ }
+
+ std::size_t precedence_level() const
+ {
+ switch(type_)
+ {
+ case token_kind::unary_operator:
+ return unary_operator_->precedence_level();
+ case token_kind::binary_operator:
+ return binary_operator_->precedence_level();
+ case token_kind::expression:
+ return expression_->precedence_level();
+ default:
+ return 0;
+ }
+ }
+
+ jsoncons::optional<std::size_t> arity() const
+ {
+ return type_ == token_kind::function ? function_->arity() : jsoncons::optional<std::size_t>();
+ }
+
+ bool is_right_associative() const
+ {
+ switch(type_)
+ {
+ case token_kind::unary_operator:
+ return unary_operator_->is_right_associative();
+ case token_kind::binary_operator:
+ return binary_operator_->is_right_associative();
+ case token_kind::expression:
+ return expression_->is_right_associative();
+ default:
+ return false;
+ }
+ }
+
+ void construct(token&& other)
+ {
+ type_ = other.type_;
+ switch (type_)
+ {
+ case token_kind::expression:
+ new (&expression_) std::unique_ptr<expression_base>(std::move(other.expression_));
+ break;
+ case token_kind::key:
+ new (&key_) string_type(std::move(other.key_));
+ break;
+ case token_kind::unary_operator:
+ unary_operator_ = other.unary_operator_;
+ break;
+ case token_kind::binary_operator:
+ binary_operator_ = other.binary_operator_;
+ break;
+ case token_kind::function:
+ function_ = other.function_;
+ break;
+ case token_kind::literal:
+ new (&value_) Json(std::move(other.value_));
+ break;
+ default:
+ break;
+ }
+ }
+
+ void destroy() noexcept
+ {
+ switch(type_)
+ {
+ case token_kind::expression:
+ expression_.~unique_ptr();
+ break;
+ case token_kind::key:
+ key_.~basic_string();
+ break;
+ case token_kind::literal:
+ value_.~Json();
+ break;
+ default:
+ break;
+ }
+ }
+
+ std::string to_string(std::size_t indent = 0) const
+ {
+ switch(type_)
+ {
+ case token_kind::expression:
+ return expression_->to_string(indent);
+ break;
+ case token_kind::unary_operator:
+ return std::string("unary_operator");
+ break;
+ case token_kind::binary_operator:
+ return binary_operator_->to_string(indent);
+ break;
+ case token_kind::current_node:
+ return std::string("current_node");
+ break;
+ case token_kind::end_function:
+ return std::string("end_function");
+ break;
+ case token_kind::separator:
+ return std::string("separator");
+ break;
+ case token_kind::literal:
+ return std::string("literal");
+ break;
+ case token_kind::key:
+ return std::string("key") + key_;
+ break;
+ case token_kind::begin_multi_select_hash:
+ return std::string("begin_multi_select_hash");
+ break;
+ case token_kind::begin_multi_select_list:
+ return std::string("begin_multi_select_list");
+ break;
+ case token_kind::begin_filter:
+ return std::string("begin_filter");
+ break;
+ case token_kind::pipe:
+ return std::string("pipe");
+ break;
+ case token_kind::lparen:
+ return std::string("lparen");
+ break;
+ case token_kind::function:
+ return function_->to_string();
+ case token_kind::argument:
+ return std::string("argument");
+ break;
+ case token_kind::begin_expression_type:
+ return std::string("begin_expression_type");
+ break;
+ case token_kind::end_expression_type:
+ return std::string("end_expression_type");
+ break;
+ default:
+ return std::string("default");
+ break;
+ }
+ }
+ };
+
+ static pointer evaluate_tokens(reference doc, const std::vector<token>& output_stack, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec)
+ {
+ pointer root_ptr = std::addressof(doc);
+ std::vector<parameter> stack;
+ std::vector<parameter> arg_stack;
+ for (std::size_t i = 0; i < output_stack.size(); ++i)
+ {
+ auto& t = output_stack[i];
+ switch (t.type())
+ {
+ case token_kind::literal:
+ {
+ stack.emplace_back(t.value_);
+ break;
+ }
+ case token_kind::begin_expression_type:
+ {
+ JSONCONS_ASSERT(i+1 < output_stack.size());
+ ++i;
+ JSONCONS_ASSERT(output_stack[i].is_expression());
+ JSONCONS_ASSERT(!stack.empty());
+ stack.pop_back();
+ stack.emplace_back(output_stack[i].expression_.get());
+ break;
+ }
+ case token_kind::pipe:
+ {
+ JSONCONS_ASSERT(!stack.empty());
+ root_ptr = std::addressof(stack.back().value());
+ break;
+ }
+ case token_kind::current_node:
+ stack.emplace_back(*root_ptr);
+ break;
+ case token_kind::expression:
+ {
+ JSONCONS_ASSERT(!stack.empty());
+ pointer ptr = std::addressof(stack.back().value());
+ stack.pop_back();
+ auto& ref = t.expression_->evaluate(*ptr, resources, ec);
+ stack.emplace_back(ref);
+ break;
+ }
+ case token_kind::unary_operator:
+ {
+ JSONCONS_ASSERT(stack.size() >= 1);
+ pointer ptr = std::addressof(stack.back().value());
+ stack.pop_back();
+ reference r = t.unary_operator_->evaluate(*ptr, resources, ec);
+ stack.emplace_back(r);
+ break;
+ }
+ case token_kind::binary_operator:
+ {
+ JSONCONS_ASSERT(stack.size() >= 2);
+ pointer rhs = std::addressof(stack.back().value());
+ stack.pop_back();
+ pointer lhs = std::addressof(stack.back().value());
+ stack.pop_back();
+ reference r = t.binary_operator_->evaluate(*lhs,*rhs, resources, ec);
+ stack.emplace_back(r);
+ break;
+ }
+ case token_kind::argument:
+ {
+ JSONCONS_ASSERT(!stack.empty());
+ arg_stack.push_back(std::move(stack.back()));
+ stack.pop_back();
+ break;
+ }
+ case token_kind::function:
+ {
+ if (t.function_->arity() && *(t.function_->arity()) != arg_stack.size())
+ {
+ ec = jmespath_errc::invalid_arity;
+ return std::addressof(resources.null_value());
+ }
+
+ reference r = t.function_->evaluate(arg_stack, resources, ec);
+ if (ec)
+ {
+ return std::addressof(resources.null_value());
+ }
+ arg_stack.clear();
+ stack.emplace_back(r);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ JSONCONS_ASSERT(stack.size() == 1);
+ return std::addressof(stack.back().value());
+ }
+
+ // Implementations
+
+ class or_operator final : public binary_operator
+ {
+ public:
+ or_operator()
+ : binary_operator(operator_kind::or_op)
+ {
+ }
+
+ reference evaluate(reference lhs, reference rhs, dynamic_resources<Json,JsonReference>& resources, std::error_code&) const override
+ {
+ if (lhs.is_null() && rhs.is_null())
+ {
+ return resources.null_value();
+ }
+ if (!is_false(lhs))
+ {
+ return lhs;
+ }
+ else
+ {
+ return rhs;
+ }
+ }
+
+ std::string to_string(std::size_t indent = 0) const override
+ {
+ std::string s;
+ for (std::size_t i = 0; i <= indent; ++i)
+ {
+ s.push_back(' ');
+ }
+ s.append("or_operator\n");
+ return s;
+ }
+ };
+
+ class and_operator final : public binary_operator
+ {
+ public:
+ and_operator()
+ : binary_operator(operator_kind::and_op)
+ {
+ }
+
+ reference evaluate(reference lhs, reference rhs, dynamic_resources<Json,JsonReference>&, std::error_code&) const override
+ {
+ if (is_true(lhs))
+ {
+ return rhs;
+ }
+ else
+ {
+ return lhs;
+ }
+ }
+
+ std::string to_string(std::size_t indent = 0) const override
+ {
+ std::string s;
+ for (std::size_t i = 0; i <= indent; ++i)
+ {
+ s.push_back(' ');
+ }
+ s.append("and_operator\n");
+ return s;
+ }
+ };
+
+ class eq_operator final : public binary_operator
+ {
+ public:
+ eq_operator()
+ : binary_operator(operator_kind::eq_op)
+ {
+ }
+
+ reference evaluate(reference lhs, reference rhs, dynamic_resources<Json,JsonReference>& resources, std::error_code&) const override
+ {
+ return lhs == rhs ? resources.true_value() : resources.false_value();
+ }
+
+ std::string to_string(std::size_t indent = 0) const override
+ {
+ std::string s;
+ for (std::size_t i = 0; i <= indent; ++i)
+ {
+ s.push_back(' ');
+ }
+ s.append("eq_operator\n");
+ return s;
+ }
+ };
+
+ class ne_operator final : public binary_operator
+ {
+ public:
+ ne_operator()
+ : binary_operator(operator_kind::ne_op)
+ {
+ }
+
+ reference evaluate(reference lhs, reference rhs, dynamic_resources<Json,JsonReference>& resources, std::error_code&) const override
+ {
+ return lhs != rhs ? resources.true_value() : resources.false_value();
+ }
+
+ std::string to_string(std::size_t indent = 0) const override
+ {
+ std::string s;
+ for (std::size_t i = 0; i <= indent; ++i)
+ {
+ s.push_back(' ');
+ }
+ s.append("ne_operator\n");
+ return s;
+ }
+ };
+
+ class lt_operator final : public binary_operator
+ {
+ public:
+ lt_operator()
+ : binary_operator(operator_kind::lt_op)
+ {
+ }
+
+ reference evaluate(reference lhs, reference rhs, dynamic_resources<Json,JsonReference>& resources, std::error_code&) const override
+ {
+ if (!(lhs.is_number() && rhs.is_number()))
+ {
+ return resources.null_value();
+ }
+ return lhs < rhs ? resources.true_value() : resources.false_value();
+ }
+
+ std::string to_string(std::size_t indent = 0) const override
+ {
+ std::string s;
+ for (std::size_t i = 0; i <= indent; ++i)
+ {
+ s.push_back(' ');
+ }
+ s.append("lt_operator\n");
+ return s;
+ }
+ };
+
+ class lte_operator final : public binary_operator
+ {
+ public:
+ lte_operator()
+ : binary_operator(operator_kind::lte_op)
+ {
+ }
+
+ reference evaluate(reference lhs, reference rhs, dynamic_resources<Json,JsonReference>& resources, std::error_code&) const override
+ {
+ if (!(lhs.is_number() && rhs.is_number()))
+ {
+ return resources.null_value();
+ }
+ return lhs <= rhs ? resources.true_value() : resources.false_value();
+ }
+
+ std::string to_string(std::size_t indent = 0) const override
+ {
+ std::string s;
+ for (std::size_t i = 0; i <= indent; ++i)
+ {
+ s.push_back(' ');
+ }
+ s.append("lte_operator\n");
+ return s;
+ }
+ };
+
+ class gt_operator final : public binary_operator
+ {
+ public:
+ gt_operator()
+ : binary_operator(operator_kind::gt_op)
+ {
+ }
+
+ reference evaluate(reference lhs, reference rhs, dynamic_resources<Json,JsonReference>& resources, std::error_code&) const override
+ {
+ if (!(lhs.is_number() && rhs.is_number()))
+ {
+ return resources.null_value();
+ }
+ return lhs > rhs ? resources.true_value() : resources.false_value();
+ }
+
+ std::string to_string(std::size_t indent = 0) const override
+ {
+ std::string s;
+ for (std::size_t i = 0; i <= indent; ++i)
+ {
+ s.push_back(' ');
+ }
+ s.append("gt_operator\n");
+ return s;
+ }
+ };
+
+ class gte_operator final : public binary_operator
+ {
+ public:
+ gte_operator()
+ : binary_operator(operator_kind::gte_op)
+ {
+ }
+
+ reference evaluate(reference lhs, reference rhs, dynamic_resources<Json,JsonReference>& resources, std::error_code&) const override
+ {
+ if (!(lhs.is_number() && rhs.is_number()))
+ {
+ return resources.null_value();
+ }
+ return lhs >= rhs ? resources.true_value() : resources.false_value();
+ }
+
+ std::string to_string(std::size_t indent = 0) const override
+ {
+ std::string s;
+ for (std::size_t i = 0; i <= indent; ++i)
+ {
+ s.push_back(' ');
+ }
+ s.append("gte_operator\n");
+ return s;
+ }
+ };
+
+ // basic_expression
+ class basic_expression : public expression_base
+ {
+ public:
+ basic_expression()
+ : expression_base(operator_kind::default_op, false)
+ {
+ }
+
+ void add_expression(std::unique_ptr<expression_base>&&) override
+ {
+ }
+ };
+
+ class identifier_selector final : public basic_expression
+ {
+ private:
+ string_type identifier_;
+ public:
+ identifier_selector(const string_view_type& name)
+ : identifier_(name)
+ {
+ }
+
+ reference evaluate(reference val, dynamic_resources<Json,JsonReference>& resources, std::error_code&) const override
+ {
+ //std::cout << "(identifier_selector " << identifier_ << " ) " << pretty_print(val) << "\n";
+ if (val.is_object() && val.contains(identifier_))
+ {
+ return val.at(identifier_);
+ }
+ else
+ {
+ return resources.null_value();
+ }
+ }
+
+ std::string to_string(std::size_t indent = 0) const override
+ {
+ std::string s;
+ for (std::size_t i = 0; i <= indent; ++i)
+ {
+ s.push_back(' ');
+ }
+ s.append("identifier_selector ");
+ s.append(identifier_);
+ return s;
+ }
+ };
+
+ class current_node final : public basic_expression
+ {
+ public:
+ current_node()
+ {
+ }
+
+ reference evaluate(reference val, dynamic_resources<Json,JsonReference>&, std::error_code&) const override
+ {
+ return val;
+ }
+
+ std::string to_string(std::size_t indent = 0) const override
+ {
+ std::string s;
+ for (std::size_t i = 0; i <= indent; ++i)
+ {
+ s.push_back(' ');
+ }
+ s.append("current_node ");
+ return s;
+ }
+ };
+
+ class index_selector final : public basic_expression
+ {
+ int64_t index_;
+ public:
+ index_selector(int64_t index)
+ : index_(index)
+ {
+ }
+
+ reference evaluate(reference val, dynamic_resources<Json,JsonReference>& resources, std::error_code&) const override
+ {
+ if (!val.is_array())
+ {
+ return resources.null_value();
+ }
+ int64_t slen = static_cast<int64_t>(val.size());
+ if (index_ >= 0 && index_ < slen)
+ {
+ std::size_t index = static_cast<std::size_t>(index_);
+ return val.at(index);
+ }
+ else if ((slen + index_) >= 0 && (slen+index_) < slen)
+ {
+ std::size_t index = static_cast<std::size_t>(slen + index_);
+ return val.at(index);
+ }
+ else
+ {
+ return resources.null_value();
+ }
+ }
+
+ std::string to_string(std::size_t indent = 0) const override
+ {
+ std::string s;
+ for (std::size_t i = 0; i <= indent; ++i)
+ {
+ s.push_back(' ');
+ }
+ s.append("index_selector ");
+ s.append(std::to_string(index_));
+ return s;
+ }
+ };
+
+ // projection_base
+ class projection_base : public expression_base
+ {
+ protected:
+ std::vector<std::unique_ptr<expression_base>> expressions_;
+ public:
+ projection_base(operator_kind oper)
+ : expression_base(oper, true)
+ {
+ }
+
+ void add_expression(std::unique_ptr<expression_base>&& expr) override
+ {
+ if (!expressions_.empty() && expressions_.back()->is_projection() &&
+ (expr->precedence_level() < expressions_.back()->precedence_level() ||
+ (expr->precedence_level() == expressions_.back()->precedence_level() && expr->is_right_associative())))
+ {
+ expressions_.back()->add_expression(std::move(expr));
+ }
+ else
+ {
+ expressions_.emplace_back(std::move(expr));
+ }
+ }
+
+ reference apply_expressions(reference val, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const
+ {
+ pointer ptr = std::addressof(val);
+ for (auto& expression : expressions_)
+ {
+ ptr = std::addressof(expression->evaluate(*ptr, resources, ec));
+ }
+ return *ptr;
+ }
+ };
+
+ class object_projection final : public projection_base
+ {
+ public:
+ object_projection()
+ : projection_base(operator_kind::projection_op)
+ {
+ }
+
+ reference evaluate(reference val, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override
+ {
+ if (!val.is_object())
+ {
+ return resources.null_value();
+ }
+
+ auto result = resources.create_json(json_array_arg);
+ for (auto& item : val.object_range())
+ {
+ if (!item.value().is_null())
+ {
+ reference j = this->apply_expressions(item.value(), resources, ec);
+ if (!j.is_null())
+ {
+ result->emplace_back(json_const_pointer_arg, std::addressof(j));
+ }
+ }
+ }
+ return *result;
+ }
+
+ std::string to_string(std::size_t indent = 0) const override
+ {
+ std::string s;
+ for (std::size_t i = 0; i <= indent; ++i)
+ {
+ s.push_back(' ');
+ }
+ s.append("object_projection\n");
+ for (auto& expr : this->expressions_)
+ {
+ std::string sss = expr->to_string(indent+2);
+ s.insert(s.end(), sss.begin(), sss.end());
+ s.push_back('\n');
+ }
+ return s;
+ }
+ };
+
+ class list_projection final : public projection_base
+ {
+ public:
+ list_projection()
+ : projection_base(operator_kind::projection_op)
+ {
+ }
+
+ reference evaluate(reference val, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override
+ {
+ if (!val.is_array())
+ {
+ return resources.null_value();
+ }
+
+ auto result = resources.create_json(json_array_arg);
+ for (reference item : val.array_range())
+ {
+ if (!item.is_null())
+ {
+ reference j = this->apply_expressions(item, resources, ec);
+ if (!j.is_null())
+ {
+ result->emplace_back(json_const_pointer_arg, std::addressof(j));
+ }
+ }
+ }
+ return *result;
+ }
+
+ std::string to_string(std::size_t indent = 0) const override
+ {
+ std::string s;
+ for (std::size_t i = 0; i <= indent; ++i)
+ {
+ s.push_back(' ');
+ }
+ s.append("list_projection\n");
+ for (auto& expr : this->expressions_)
+ {
+ std::string sss = expr->to_string(indent+2);
+ s.insert(s.end(), sss.begin(), sss.end());
+ s.push_back('\n');
+ }
+ return s;
+ }
+ };
+
+ class slice_projection final : public projection_base
+ {
+ slice slice_;
+ public:
+ slice_projection(const slice& s)
+ : projection_base(operator_kind::projection_op), slice_(s)
+ {
+ }
+
+ reference evaluate(reference val, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override
+ {
+ if (!val.is_array())
+ {
+ return resources.null_value();
+ }
+
+ auto start = slice_.get_start(val.size());
+ auto end = slice_.get_stop(val.size());
+ auto step = slice_.step();
+
+ if (step == 0)
+ {
+ ec = jmespath_errc::step_cannot_be_zero;
+ return resources.null_value();
+ }
+
+ auto result = resources.create_json(json_array_arg);
+ if (step > 0)
+ {
+ if (start < 0)
+ {
+ start = 0;
+ }
+ if (end > static_cast<int64_t>(val.size()))
+ {
+ end = val.size();
+ }
+ for (int64_t i = start; i < end; i += step)
+ {
+ reference j = this->apply_expressions(val.at(static_cast<std::size_t>(i)), resources, ec);
+ if (!j.is_null())
+ {
+ result->emplace_back(json_const_pointer_arg, std::addressof(j));
+ }
+ }
+ }
+ else
+ {
+ if (start >= static_cast<int64_t>(val.size()))
+ {
+ start = static_cast<int64_t>(val.size()) - 1;
+ }
+ if (end < -1)
+ {
+ end = -1;
+ }
+ for (int64_t i = start; i > end; i += step)
+ {
+ reference j = this->apply_expressions(val.at(static_cast<std::size_t>(i)), resources, ec);
+ if (!j.is_null())
+ {
+ result->emplace_back(json_const_pointer_arg, std::addressof(j));
+ }
+ }
+ }
+
+ return *result;
+ }
+
+ std::string to_string(std::size_t indent = 0) const override
+ {
+ std::string s;
+ for (std::size_t i = 0; i <= indent; ++i)
+ {
+ s.push_back(' ');
+ }
+ s.append("slice_projection\n");
+ for (auto& expr : this->expressions_)
+ {
+ std::string sss = expr->to_string(indent+2);
+ s.insert(s.end(), sss.begin(), sss.end());
+ s.push_back('\n');
+ }
+ return s;
+ }
+ };
+
+ class filter_expression final : public projection_base
+ {
+ std::vector<token> token_list_;
+ public:
+ filter_expression(std::vector<token>&& token_list)
+ : projection_base(operator_kind::projection_op), token_list_(std::move(token_list))
+ {
+ }
+
+ reference evaluate(reference val, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override
+ {
+ if (!val.is_array())
+ {
+ return resources.null_value();
+ }
+ auto result = resources.create_json(json_array_arg);
+
+ for (auto& item : val.array_range())
+ {
+ Json j(json_const_pointer_arg, evaluate_tokens(item, token_list_, resources, ec));
+ if (is_true(j))
+ {
+ reference jj = this->apply_expressions(item, resources, ec);
+ if (!jj.is_null())
+ {
+ result->emplace_back(json_const_pointer_arg, std::addressof(jj));
+ }
+ }
+ }
+ return *result;
+ }
+
+ std::string to_string(std::size_t indent = 0) const override
+ {
+ std::string s;
+ for (std::size_t i = 0; i <= indent; ++i)
+ {
+ s.push_back(' ');
+ }
+ s.append("filter_expression\n");
+ for (auto& item : token_list_)
+ {
+ std::string sss = item.to_string(indent+2);
+ s.insert(s.end(), sss.begin(), sss.end());
+ s.push_back('\n');
+ }
+ return s;
+ }
+ };
+
+ class flatten_projection final : public projection_base
+ {
+ public:
+ flatten_projection()
+ : projection_base(operator_kind::flatten_projection_op)
+ {
+ }
+
+ reference evaluate(reference val, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override
+ {
+ if (!val.is_array())
+ {
+ return resources.null_value();
+ }
+
+ auto result = resources.create_json(json_array_arg);
+ for (reference current_elem : val.array_range())
+ {
+ if (current_elem.is_array())
+ {
+ for (reference elem : current_elem.array_range())
+ {
+ if (!elem.is_null())
+ {
+ reference j = this->apply_expressions(elem, resources, ec);
+ if (!j.is_null())
+ {
+ result->emplace_back(json_const_pointer_arg, std::addressof(j));
+ }
+ }
+ }
+ }
+ else
+ {
+ if (!current_elem.is_null())
+ {
+ reference j = this->apply_expressions(current_elem, resources, ec);
+ if (!j.is_null())
+ {
+ result->emplace_back(json_const_pointer_arg, std::addressof(j));
+ }
+ }
+ }
+ }
+ return *result;
+ }
+
+ std::string to_string(std::size_t indent = 0) const override
+ {
+ std::string s;
+ for (std::size_t i = 0; i <= indent; ++i)
+ {
+ s.push_back(' ');
+ }
+ s.append("flatten_projection\n");
+ for (auto& expr : this->expressions_)
+ {
+ std::string sss = expr->to_string(indent+2);
+ s.insert(s.end(), sss.begin(), sss.end());
+ s.push_back('\n');
+ }
+ return s;
+ }
+ };
+
+ class multi_select_list final : public basic_expression
+ {
+ std::vector<std::vector<token>> token_lists_;
+ public:
+ multi_select_list(std::vector<std::vector<token>>&& token_lists)
+ : token_lists_(std::move(token_lists))
+ {
+ }
+
+ reference evaluate(reference val, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override
+ {
+ if (val.is_null())
+ {
+ return val;
+ }
+ auto result = resources.create_json(json_array_arg);
+ result->reserve(token_lists_.size());
+
+ for (auto& list : token_lists_)
+ {
+ result->emplace_back(json_const_pointer_arg, evaluate_tokens(val, list, resources, ec));
+ }
+ return *result;
+ }
+
+ std::string to_string(std::size_t indent = 0) const override
+ {
+ std::string s;
+ for (std::size_t i = 0; i <= indent; ++i)
+ {
+ s.push_back(' ');
+ }
+ s.append("multi_select_list\n");
+ for (auto& list : token_lists_)
+ {
+ for (auto& item : list)
+ {
+ std::string sss = item.to_string(indent+2);
+ s.insert(s.end(), sss.begin(), sss.end());
+ s.push_back('\n');
+ }
+ s.append("---\n");
+ }
+ return s;
+ }
+ };
+
+ struct key_tokens
+ {
+ string_type key;
+ std::vector<token> tokens;
+
+ key_tokens(string_type&& key, std::vector<token>&& tokens) noexcept
+ : key(std::move(key)), tokens(std::move(tokens))
+ {
+ }
+ };
+
+ class multi_select_hash final : public basic_expression
+ {
+ public:
+ std::vector<key_tokens> key_toks_;
+
+ multi_select_hash(std::vector<key_tokens>&& key_toks)
+ : key_toks_(std::move(key_toks))
+ {
+ }
+
+ reference evaluate(reference val, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override
+ {
+ if (val.is_null())
+ {
+ return val;
+ }
+ auto resultp = resources.create_json(json_object_arg);
+ resultp->reserve(key_toks_.size());
+ for (auto& item : key_toks_)
+ {
+ resultp->try_emplace(item.key, json_const_pointer_arg, evaluate_tokens(val, item.tokens, resources, ec));
+ }
+
+ return *resultp;
+ }
+
+ std::string to_string(std::size_t indent = 0) const override
+ {
+ std::string s;
+ for (std::size_t i = 0; i <= indent; ++i)
+ {
+ s.push_back(' ');
+ }
+ s.append("multi_select_list\n");
+ return s;
+ }
+ };
+
+ class function_expression final : public basic_expression
+ {
+ public:
+ std::vector<token> toks_;
+
+ function_expression(std::vector<token>&& toks)
+ : toks_(std::move(toks))
+ {
+ }
+
+ reference evaluate(reference val, dynamic_resources<Json,JsonReference>& resources, std::error_code& ec) const override
+ {
+ return *evaluate_tokens(val, toks_, resources, ec);
+ }
+
+ std::string to_string(std::size_t indent = 0) const override
+ {
+ std::string s;
+ for (std::size_t i = 0; i <= indent; ++i)
+ {
+ s.push_back(' ');
+ }
+ s.append("function_expression\n");
+ for (auto& tok : toks_)
+ {
+ for (std::size_t i = 0; i <= indent+2; ++i)
+ {
+ s.push_back(' ');
+ }
+ std::string sss = tok.to_string(indent+2);
+ s.insert(s.end(), sss.begin(), sss.end());
+ s.push_back('\n');
+ }
+ return s;
+ }
+ };
+
+ class static_resources
+ {
+ std::vector<std::unique_ptr<Json>> temp_storage_;
+
+ public:
+
+ static_resources() = default;
+ static_resources(const static_resources& expr) = delete;
+ static_resources& operator=(const static_resources& expr) = delete;
+ static_resources(static_resources&& expr) = default;
+ static_resources& operator=(static_resources&& expr) = default;
+
+ const function_base* get_function(const string_type& name, std::error_code& ec) const
+ {
+ static abs_function abs_func;
+ static avg_function avg_func;
+ static ceil_function ceil_func;
+ static contains_function contains_func;
+ static ends_with_function ends_with_func;
+ static floor_function floor_func;
+ static join_function join_func;
+ static length_function length_func;
+ static max_function max_func;
+ static max_by_function max_by_func;
+ static map_function map_func;
+ static merge_function merge_func;
+ static min_function min_func;
+ static min_by_function min_by_func;
+ static type_function type_func;
+ static sort_function sort_func;
+ static sort_by_function sort_by_func;
+ static keys_function keys_func;
+ static values_function values_func;
+ static reverse_function reverse_func;
+ static starts_with_function starts_with_func;
+ static const sum_function sum_func;
+ static to_array_function to_array_func;
+ static to_number_function to_number_func;
+ static to_string_function to_string_func;
+ static not_null_function not_null_func;
+
+ using function_dictionary = std::unordered_map<string_type,const function_base*>;
+ static const function_dictionary functions_ =
+ {
+ {string_type{'a','b','s'}, &abs_func},
+ {string_type{'a','v','g'}, &avg_func},
+ {string_type{'c','e','i', 'l'}, &ceil_func},
+ {string_type{'c','o','n', 't', 'a', 'i', 'n', 's'}, &contains_func},
+ {string_type{'e','n','d', 's', '_', 'w', 'i', 't', 'h'}, &ends_with_func},
+ {string_type{'f','l','o', 'o', 'r'}, &floor_func},
+ {string_type{'j','o','i', 'n'}, &join_func},
+ {string_type{'l','e','n', 'g', 't', 'h'}, &length_func},
+ {string_type{'m','a','x'}, &max_func},
+ {string_type{'m','a','x','_','b','y'}, &max_by_func},
+ {string_type{'m','a','p'}, &map_func},
+ {string_type{'m','i','n'}, &min_func},
+ {string_type{'m','i','n','_','b','y'}, &min_by_func},
+ {string_type{'m','e','r', 'g', 'e'}, &merge_func},
+ {string_type{'t','y','p', 'e'}, &type_func},
+ {string_type{'s','o','r', 't'}, &sort_func},
+ {string_type{'s','o','r', 't','_','b','y'}, &sort_by_func},
+ {string_type{'k','e','y', 's'}, &keys_func},
+ {string_type{'v','a','l', 'u','e','s'}, &values_func},
+ {string_type{'r','e','v', 'e', 'r', 's','e'}, &reverse_func},
+ {string_type{'s','t','a', 'r','t','s','_','w','i','t','h'}, &starts_with_func},
+ {string_type{'s','u','m'}, &sum_func},
+ {string_type{'t','o','_','a','r','r','a','y',}, &to_array_func},
+ {string_type{'t','o','_', 'n', 'u', 'm','b','e','r'}, &to_number_func},
+ {string_type{'t','o','_', 's', 't', 'r','i','n','g'}, &to_string_func},
+ {string_type{'n','o','t', '_', 'n', 'u','l','l'}, &not_null_func}
+ };
+ auto it = functions_.find(name);
+ if (it == functions_.end())
+ {
+ ec = jmespath_errc::unknown_function;
+ return nullptr;
+ }
+ return it->second;
+ }
+
+ const unary_operator* get_not_operator() const
+ {
+ static const not_expression not_oper;
+
+ return &not_oper;
+ }
+
+ const binary_operator* get_or_operator() const
+ {
+ static const or_operator or_oper;
+
+ return &or_oper;
+ }
+
+ const binary_operator* get_and_operator() const
+ {
+ static const and_operator and_oper;
+
+ return &and_oper;
+ }
+
+ const binary_operator* get_eq_operator() const
+ {
+ static const eq_operator eq_oper;
+ return &eq_oper;
+ }
+
+ const binary_operator* get_ne_operator() const
+ {
+ static const ne_operator ne_oper;
+ return &ne_oper;
+ }
+
+ const binary_operator* get_lt_operator() const
+ {
+ static const lt_operator lt_oper;
+ return &lt_oper;
+ }
+
+ const binary_operator* get_lte_operator() const
+ {
+ static const lte_operator lte_oper;
+ return &lte_oper;
+ }
+
+ const binary_operator* get_gt_operator() const
+ {
+ static const gt_operator gt_oper;
+ return &gt_oper;
+ }
+
+ const binary_operator* get_gte_operator() const
+ {
+ static const gte_operator gte_oper;
+ return &gte_oper;
+ }
+ };
+
+ class jmespath_expression
+ {
+ static_resources resources_;
+ std::vector<token> output_stack_;
+ public:
+ jmespath_expression()
+ {
+ }
+
+ jmespath_expression(const jmespath_expression& expr) = delete;
+ jmespath_expression& operator=(const jmespath_expression& expr) = delete;
+
+ jmespath_expression(jmespath_expression&& expr)
+ : resources_(std::move(expr.resources_)),
+ output_stack_(std::move(expr.output_stack_))
+ {
+ }
+
+ jmespath_expression(static_resources&& resources,
+ std::vector<token>&& output_stack)
+ : resources_(std::move(resources)), output_stack_(std::move(output_stack))
+ {
+ }
+
+ Json evaluate(reference doc)
+ {
+ if (output_stack_.empty())
+ {
+ return Json::null();
+ }
+ std::error_code ec;
+ Json result = evaluate(doc, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(jmespath_error(ec));
+ }
+ return result;
+ }
+
+ Json evaluate(reference doc, std::error_code& ec)
+ {
+ if (output_stack_.empty())
+ {
+ return Json::null();
+ }
+ dynamic_resources<Json,JsonReference> dynamic_storage;
+ return deep_copy(*evaluate_tokens(doc, output_stack_, dynamic_storage, ec));
+ }
+
+ static jmespath_expression compile(const string_view_type& expr)
+ {
+ jsoncons::jmespath::detail::jmespath_evaluator<Json,const Json&> evaluator;
+ std::error_code ec;
+ jmespath_expression result = evaluator.compile(expr.data(), expr.size(), ec);
+ if (ec)
+ {
+ JSONCONS_THROW(jmespath_error(ec, evaluator.line(), evaluator.column()));
+ }
+ return result;
+ }
+
+ static jmespath_expression compile(const string_view_type& expr,
+ std::error_code& ec)
+ {
+ jsoncons::jmespath::detail::jmespath_evaluator<Json,const Json&> evaluator;
+ return evaluator.compile(expr.data(), expr.size(), ec);
+ }
+ };
+ private:
+ std::size_t line_;
+ std::size_t column_;
+ const char_type* begin_input_;
+ const char_type* end_input_;
+ const char_type* p_;
+
+ static_resources resources_;
+ std::vector<path_state> state_stack_;
+
+ std::vector<token> output_stack_;
+ std::vector<token> operator_stack_;
+
+ public:
+ jmespath_evaluator()
+ : line_(1), column_(1),
+ begin_input_(nullptr), end_input_(nullptr),
+ p_(nullptr)
+ {
+ }
+
+ std::size_t line() const
+ {
+ return line_;
+ }
+
+ std::size_t column() const
+ {
+ return column_;
+ }
+
+ jmespath_expression compile(const char_type* path,
+ std::size_t length,
+ std::error_code& ec)
+ {
+ push_token(current_node_arg, ec);
+ if (ec) {return jmespath_expression();}
+ state_stack_.emplace_back(path_state::start);
+
+ string_type buffer;
+ uint32_t cp = 0;
+ uint32_t cp2 = 0;
+
+ begin_input_ = path;
+ end_input_ = path + length;
+ p_ = begin_input_;
+
+ slice slic{};
+
+ while (p_ < end_input_)
+ {
+ switch (state_stack_.back())
+ {
+ case path_state::start:
+ {
+ state_stack_.back() = path_state::rhs_expression;
+ state_stack_.emplace_back(path_state::lhs_expression);
+ break;
+ }
+ case path_state::rhs_expression:
+ switch(*p_)
+ {
+ case ' ':case '\t':case '\r':case '\n':
+ advance_past_space_character(ec);
+ break;
+ case '.':
+ ++p_;
+ ++column_;
+ state_stack_.emplace_back(path_state::sub_expression);
+ break;
+ case '|':
+ ++p_;
+ ++column_;
+ state_stack_.emplace_back(path_state::lhs_expression);
+ state_stack_.emplace_back(path_state::expect_pipe_or_or);
+ break;
+ case '&':
+ ++p_;
+ ++column_;
+ state_stack_.emplace_back(path_state::lhs_expression);
+ state_stack_.emplace_back(path_state::expect_and);
+ break;
+ case '<':
+ case '>':
+ case '=':
+ {
+ state_stack_.emplace_back(path_state::comparator_expression);
+ break;
+ }
+ case '!':
+ {
+ ++p_;
+ ++column_;
+ state_stack_.emplace_back(path_state::lhs_expression);
+ state_stack_.emplace_back(path_state::cmp_ne);
+ break;
+ }
+ case ')':
+ {
+ state_stack_.pop_back();
+ break;
+ }
+ case '[':
+ state_stack_.emplace_back(path_state::bracket_specifier);
+ ++p_;
+ ++column_;
+ break;
+ default:
+ if (state_stack_.size() > 1)
+ {
+ state_stack_.pop_back();
+ }
+ else
+ {
+ ec = jmespath_errc::syntax_error;
+ return jmespath_expression();
+ }
+ break;
+ }
+ break;
+ case path_state::comparator_expression:
+ switch(*p_)
+ {
+ case ' ':case '\t':case '\r':case '\n':
+ advance_past_space_character(ec);
+ break;
+ case '<':
+ ++p_;
+ ++column_;
+ state_stack_.back() = path_state::lhs_expression;
+ state_stack_.emplace_back(path_state::cmp_lt_or_lte);
+ break;
+ case '>':
+ ++p_;
+ ++column_;
+ state_stack_.back() = path_state::lhs_expression;
+ state_stack_.emplace_back(path_state::cmp_gt_or_gte);
+ break;
+ case '=':
+ {
+ ++p_;
+ ++column_;
+ state_stack_.back() = path_state::lhs_expression;
+ state_stack_.emplace_back(path_state::cmp_eq);
+ break;
+ }
+ default:
+ if (state_stack_.size() > 1)
+ {
+ state_stack_.pop_back();
+ }
+ else
+ {
+ ec = jmespath_errc::syntax_error;
+ return jmespath_expression();
+ }
+ break;
+ }
+ break;
+ case path_state::lhs_expression:
+ {
+ switch (*p_)
+ {
+ case ' ':case '\t':case '\r':case '\n':
+ advance_past_space_character(ec);
+ break;
+ case '\"':
+ state_stack_.back() = path_state::val_expr;
+ state_stack_.emplace_back(path_state::quoted_string);
+ ++p_;
+ ++column_;
+ break;
+ case '\'':
+ state_stack_.back() = path_state::raw_string;
+ ++p_;
+ ++column_;
+ break;
+ case '`':
+ state_stack_.back() = path_state::literal;
+ ++p_;
+ ++column_;
+ break;
+ case '{':
+ push_token(begin_multi_select_hash_arg, ec);
+ if (ec) {return jmespath_expression();}
+ state_stack_.back() = path_state::multi_select_hash;
+ ++p_;
+ ++column_;
+ break;
+ case '*': // wildcard
+ push_token(token(jsoncons::make_unique<object_projection>()), ec);
+ if (ec) {return jmespath_expression();}
+ state_stack_.pop_back();
+ ++p_;
+ ++column_;
+ break;
+ case '(':
+ {
+ ++p_;
+ ++column_;
+ push_token(lparen_arg, ec);
+ if (ec) {return jmespath_expression();}
+ state_stack_.back() = path_state::expect_rparen;
+ state_stack_.emplace_back(path_state::rhs_expression);
+ state_stack_.emplace_back(path_state::lhs_expression);
+ break;
+ }
+ case '!':
+ {
+ ++p_;
+ ++column_;
+ push_token(token(resources_.get_not_operator()), ec);
+ if (ec) {return jmespath_expression();}
+ break;
+ }
+ case '@':
+ ++p_;
+ ++column_;
+ push_token(token(jsoncons::make_unique<current_node>()), ec);
+ if (ec) {return jmespath_expression();}
+ state_stack_.pop_back();
+ break;
+ case '[':
+ state_stack_.back() = path_state::bracket_specifier_or_multi_select_list;
+ ++p_;
+ ++column_;
+ break;
+ default:
+ if ((*p_ >= 'A' && *p_ <= 'Z') || (*p_ >= 'a' && *p_ <= 'z') || (*p_ == '_'))
+ {
+ state_stack_.back() = path_state::identifier_or_function_expr;
+ state_stack_.emplace_back(path_state::unquoted_string);
+ buffer.push_back(*p_);
+ ++p_;
+ ++column_;
+ }
+ else
+ {
+ ec = jmespath_errc::expected_identifier;
+ return jmespath_expression();
+ }
+ break;
+ };
+ break;
+ }
+ case path_state::sub_expression:
+ {
+ switch (*p_)
+ {
+ case ' ':case '\t':case '\r':case '\n':
+ advance_past_space_character(ec);
+ break;
+ case '\"':
+ state_stack_.back() = path_state::val_expr;
+ state_stack_.emplace_back(path_state::quoted_string);
+ ++p_;
+ ++column_;
+ break;
+ case '{':
+ push_token(begin_multi_select_hash_arg, ec);
+ if (ec) {return jmespath_expression();}
+ state_stack_.back() = path_state::multi_select_hash;
+ ++p_;
+ ++column_;
+ break;
+ case '*':
+ push_token(token(jsoncons::make_unique<object_projection>()), ec);
+ if (ec) {return jmespath_expression();}
+ state_stack_.pop_back();
+ ++p_;
+ ++column_;
+ break;
+ case '[':
+ state_stack_.back() = path_state::expect_multi_select_list;
+ ++p_;
+ ++column_;
+ break;
+ default:
+ if ((*p_ >= 'A' && *p_ <= 'Z') || (*p_ >= 'a' && *p_ <= 'z') || (*p_ == '_'))
+ {
+ state_stack_.back() = path_state::identifier_or_function_expr;
+ state_stack_.emplace_back(path_state::unquoted_string);
+ buffer.push_back(*p_);
+ ++p_;
+ ++column_;
+ }
+ else
+ {
+ ec = jmespath_errc::expected_identifier;
+ return jmespath_expression();
+ }
+ break;
+ };
+ break;
+ }
+ case path_state::key_expr:
+ push_token(token(key_arg, buffer), ec);
+ if (ec) {return jmespath_expression();}
+ buffer.clear();
+ state_stack_.pop_back();
+ break;
+ case path_state::val_expr:
+ push_token(token(jsoncons::make_unique<identifier_selector>(buffer)), ec);
+ if (ec) {return jmespath_expression();}
+ buffer.clear();
+ state_stack_.pop_back();
+ break;
+ case path_state::expression_or_expression_type:
+ switch (*p_)
+ {
+ case ' ':case '\t':case '\r':case '\n':
+ advance_past_space_character(ec);
+ break;
+ case '&':
+ push_token(token(begin_expression_type_arg), ec);
+ if (ec) {return jmespath_expression();}
+ state_stack_.back() = path_state::expression_type;
+ state_stack_.emplace_back(path_state::rhs_expression);
+ state_stack_.emplace_back(path_state::lhs_expression);
+ ++p_;
+ ++column_;
+ break;
+ default:
+ state_stack_.back() = path_state::argument;
+ state_stack_.emplace_back(path_state::rhs_expression);
+ state_stack_.emplace_back(path_state::lhs_expression);
+ break;
+ }
+ break;
+ case path_state::identifier_or_function_expr:
+ switch(*p_)
+ {
+ case '(':
+ {
+ auto f = resources_.get_function(buffer, ec);
+ if (ec)
+ {
+ return jmespath_expression();
+ }
+ buffer.clear();
+ push_token(token(f), ec);
+ if (ec) {return jmespath_expression();}
+ state_stack_.back() = path_state::function_expression;
+ state_stack_.emplace_back(path_state::expression_or_expression_type);
+ ++p_;
+ ++column_;
+ break;
+ }
+ default:
+ {
+ push_token(token(jsoncons::make_unique<identifier_selector>(buffer)), ec);
+ if (ec) {return jmespath_expression();}
+ buffer.clear();
+ state_stack_.pop_back();
+ break;
+ }
+ }
+ break;
+
+ case path_state::function_expression:
+ switch (*p_)
+ {
+ case ' ':case '\t':case '\r':case '\n':
+ advance_past_space_character(ec);
+ break;
+ case ',':
+ push_token(token(current_node_arg), ec);
+ if (ec) {return jmespath_expression();}
+ state_stack_.emplace_back(path_state::expression_or_expression_type);
+ ++p_;
+ ++column_;
+ break;
+ case ')':
+ {
+ push_token(token(end_function_arg), ec);
+ if (ec) {return jmespath_expression();}
+ state_stack_.pop_back();
+ ++p_;
+ ++column_;
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+
+ case path_state::argument:
+ push_token(argument_arg, ec);
+ if (ec) {return jmespath_expression();}
+ state_stack_.pop_back();
+ break;
+
+ case path_state::expression_type:
+ push_token(end_expression_type_arg, ec);
+ push_token(argument_arg, ec);
+ if (ec) {return jmespath_expression();}
+ state_stack_.pop_back();
+ break;
+
+ case path_state::quoted_string:
+ switch (*p_)
+ {
+ case '\"':
+ state_stack_.pop_back(); // quoted_string
+ ++p_;
+ ++column_;
+ break;
+ case '\\':
+ state_stack_.emplace_back(path_state::quoted_string_escape_char);
+ ++p_;
+ ++column_;
+ break;
+ default:
+ buffer.push_back(*p_);
+ ++p_;
+ ++column_;
+ break;
+ };
+ break;
+
+ case path_state::unquoted_string:
+ switch (*p_)
+ {
+ case ' ':case '\t':case '\r':case '\n':
+ state_stack_.pop_back(); // unquoted_string
+ advance_past_space_character(ec);
+ break;
+ default:
+ if ((*p_ >= '0' && *p_ <= '9') || (*p_ >= 'A' && *p_ <= 'Z') || (*p_ >= 'a' && *p_ <= 'z') || (*p_ == '_'))
+ {
+ buffer.push_back(*p_);
+ ++p_;
+ ++column_;
+ }
+ else
+ {
+ state_stack_.pop_back(); // unquoted_string
+ }
+ break;
+ };
+ break;
+ case path_state::raw_string_escape_char:
+ switch (*p_)
+ {
+ case '\'':
+ buffer.push_back(*p_);
+ state_stack_.pop_back();
+ ++p_;
+ ++column_;
+ break;
+ default:
+ buffer.push_back('\\');
+ buffer.push_back(*p_);
+ state_stack_.pop_back();
+ ++p_;
+ ++column_;
+ break;
+ }
+ break;
+ case path_state::quoted_string_escape_char:
+ switch (*p_)
+ {
+ case '\"':
+ buffer.push_back('\"');
+ ++p_;
+ ++column_;
+ state_stack_.pop_back();
+ break;
+ case '\\':
+ buffer.push_back('\\');
+ ++p_;
+ ++column_;
+ state_stack_.pop_back();
+ break;
+ case '/':
+ buffer.push_back('/');
+ ++p_;
+ ++column_;
+ state_stack_.pop_back();
+ break;
+ case 'b':
+ buffer.push_back('\b');
+ ++p_;
+ ++column_;
+ state_stack_.pop_back();
+ break;
+ case 'f':
+ buffer.push_back('\f');
+ ++p_;
+ ++column_;
+ state_stack_.pop_back();
+ break;
+ case 'n':
+ buffer.push_back('\n');
+ ++p_;
+ ++column_;
+ state_stack_.pop_back();
+ break;
+ case 'r':
+ buffer.push_back('\r');
+ ++p_;
+ ++column_;
+ state_stack_.pop_back();
+ break;
+ case 't':
+ buffer.push_back('\t');
+ ++p_;
+ ++column_;
+ state_stack_.pop_back();
+ break;
+ case 'u':
+ ++p_;
+ ++column_;
+ state_stack_.back() = path_state::escape_u1;
+ break;
+ default:
+ ec = jmespath_errc::illegal_escaped_character;
+ return jmespath_expression();
+ }
+ break;
+ case path_state::escape_u1:
+ cp = append_to_codepoint(0, *p_, ec);
+ if (ec)
+ {
+ return jmespath_expression();
+ }
+ ++p_;
+ ++column_;
+ state_stack_.back() = path_state::escape_u2;
+ break;
+ case path_state::escape_u2:
+ cp = append_to_codepoint(cp, *p_, ec);
+ if (ec)
+ {
+ return jmespath_expression();
+ }
+ ++p_;
+ ++column_;
+ state_stack_.back() = path_state::escape_u3;
+ break;
+ case path_state::escape_u3:
+ cp = append_to_codepoint(cp, *p_, ec);
+ if (ec)
+ {
+ return jmespath_expression();
+ }
+ ++p_;
+ ++column_;
+ state_stack_.back() = path_state::escape_u4;
+ break;
+ case path_state::escape_u4:
+ cp = append_to_codepoint(cp, *p_, ec);
+ if (ec)
+ {
+ return jmespath_expression();
+ }
+ if (unicode_traits::is_high_surrogate(cp))
+ {
+ ++p_;
+ ++column_;
+ state_stack_.back() = path_state::escape_expect_surrogate_pair1;
+ }
+ else
+ {
+ unicode_traits::convert(&cp, 1, buffer);
+ ++p_;
+ ++column_;
+ state_stack_.pop_back();
+ }
+ break;
+ case path_state::escape_expect_surrogate_pair1:
+ switch (*p_)
+ {
+ case '\\':
+ ++p_;
+ ++column_;
+ state_stack_.back() = path_state::escape_expect_surrogate_pair2;
+ break;
+ default:
+ ec = jmespath_errc::invalid_codepoint;
+ return jmespath_expression();
+ }
+ break;
+ case path_state::escape_expect_surrogate_pair2:
+ switch (*p_)
+ {
+ case 'u':
+ ++p_;
+ ++column_;
+ state_stack_.back() = path_state::escape_u5;
+ break;
+ default:
+ ec = jmespath_errc::invalid_codepoint;
+ return jmespath_expression();
+ }
+ break;
+ case path_state::escape_u5:
+ cp2 = append_to_codepoint(0, *p_, ec);
+ if (ec)
+ {
+ return jmespath_expression();
+ }
+ ++p_;
+ ++column_;
+ state_stack_.back() = path_state::escape_u6;
+ break;
+ case path_state::escape_u6:
+ cp2 = append_to_codepoint(cp2, *p_, ec);
+ if (ec)
+ {
+ return jmespath_expression();
+ }
+ ++p_;
+ ++column_;
+ state_stack_.back() = path_state::escape_u7;
+ break;
+ case path_state::escape_u7:
+ cp2 = append_to_codepoint(cp2, *p_, ec);
+ if (ec)
+ {
+ return jmespath_expression();
+ }
+ ++p_;
+ ++column_;
+ state_stack_.back() = path_state::escape_u8;
+ break;
+ case path_state::escape_u8:
+ {
+ cp2 = append_to_codepoint(cp2, *p_, ec);
+ if (ec)
+ {
+ return jmespath_expression();
+ }
+ uint32_t codepoint = 0x10000 + ((cp & 0x3FF) << 10) + (cp2 & 0x3FF);
+ unicode_traits::convert(&codepoint, 1, buffer);
+ state_stack_.pop_back();
+ ++p_;
+ ++column_;
+ break;
+ }
+ case path_state::raw_string:
+ switch (*p_)
+ {
+ case '\'':
+ {
+ push_token(token(literal_arg, Json(buffer)), ec);
+ if (ec) {return jmespath_expression();}
+ buffer.clear();
+ state_stack_.pop_back(); // raw_string
+ ++p_;
+ ++column_;
+ break;
+ }
+ case '\\':
+ state_stack_.emplace_back(path_state::raw_string_escape_char);
+ ++p_;
+ ++column_;
+ break;
+ default:
+ buffer.push_back(*p_);
+ ++p_;
+ ++column_;
+ break;
+ };
+ break;
+ case path_state::literal:
+ switch (*p_)
+ {
+ case '`':
+ {
+ json_decoder<Json> decoder;
+ basic_json_reader<char_type,string_source<char_type>> reader(buffer, decoder);
+ std::error_code parse_ec;
+ reader.read(parse_ec);
+ if (parse_ec)
+ {
+ ec = jmespath_errc::invalid_literal;
+ return jmespath_expression();
+ }
+ auto j = decoder.get_result();
+
+ push_token(token(literal_arg, std::move(j)), ec);
+ if (ec) {return jmespath_expression();}
+ buffer.clear();
+ state_stack_.pop_back(); // json_value
+ ++p_;
+ ++column_;
+ break;
+ }
+ case '\\':
+ if (p_+1 < end_input_)
+ {
+ ++p_;
+ ++column_;
+ if (*p_ != '`')
+ {
+ buffer.push_back('\\');
+ }
+ buffer.push_back(*p_);
+ }
+ else
+ {
+ ec = jmespath_errc::unexpected_end_of_input;
+ return jmespath_expression();
+ }
+ ++p_;
+ ++column_;
+ break;
+ default:
+ buffer.push_back(*p_);
+ ++p_;
+ ++column_;
+ break;
+ };
+ break;
+ case path_state::number:
+ switch(*p_)
+ {
+ case '-':
+ buffer.push_back(*p_);
+ state_stack_.back() = path_state::digit;
+ ++p_;
+ ++column_;
+ break;
+ default:
+ state_stack_.back() = path_state::digit;
+ break;
+ }
+ break;
+ case path_state::digit:
+ switch(*p_)
+ {
+ case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9':
+ buffer.push_back(*p_);
+ ++p_;
+ ++column_;
+ break;
+ default:
+ state_stack_.pop_back(); // digit
+ break;
+ }
+ break;
+
+ case path_state::bracket_specifier:
+ switch(*p_)
+ {
+ case '*':
+ push_token(token(jsoncons::make_unique<list_projection>()), ec);
+ if (ec) {return jmespath_expression();}
+ state_stack_.back() = path_state::expect_rbracket;
+ ++p_;
+ ++column_;
+ break;
+ case ']': // []
+ push_token(token(jsoncons::make_unique<flatten_projection>()), ec);
+ if (ec) {return jmespath_expression();}
+ state_stack_.pop_back(); // bracket_specifier
+ ++p_;
+ ++column_;
+ break;
+ case '?':
+ push_token(token(begin_filter_arg), ec);
+ if (ec) {return jmespath_expression();}
+ state_stack_.back() = path_state::filter;
+ state_stack_.emplace_back(path_state::rhs_expression);
+ state_stack_.emplace_back(path_state::lhs_expression);
+ ++p_;
+ ++column_;
+ break;
+ case ':': // slice_expression
+ state_stack_.back() = path_state::rhs_slice_expression_stop ;
+ state_stack_.emplace_back(path_state::number);
+ ++p_;
+ ++column_;
+ break;
+ // number
+ case '-':case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9':
+ state_stack_.back() = path_state::index_or_slice_expression;
+ state_stack_.emplace_back(path_state::number);
+ break;
+ default:
+ ec = jmespath_errc::expected_index_expression;
+ return jmespath_expression();
+ }
+ break;
+ case path_state::bracket_specifier_or_multi_select_list:
+ switch(*p_)
+ {
+ case '*':
+ if (p_+1 >= end_input_)
+ {
+ ec = jmespath_errc::unexpected_end_of_input;
+ return jmespath_expression();
+ }
+ if (*(p_+1) == ']')
+ {
+ state_stack_.back() = path_state::bracket_specifier;
+ }
+ else
+ {
+ push_token(token(begin_multi_select_list_arg), ec);
+ if (ec) {return jmespath_expression();}
+ state_stack_.back() = path_state::multi_select_list;
+ state_stack_.emplace_back(path_state::lhs_expression);
+ }
+ break;
+ case ']': // []
+ case '?':
+ case ':': // slice_expression
+ case '-':case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9':
+ state_stack_.back() = path_state::bracket_specifier;
+ break;
+ default:
+ push_token(token(begin_multi_select_list_arg), ec);
+ if (ec) {return jmespath_expression();}
+ state_stack_.back() = path_state::multi_select_list;
+ state_stack_.emplace_back(path_state::lhs_expression);
+ break;
+ }
+ break;
+
+ case path_state::expect_multi_select_list:
+ switch(*p_)
+ {
+ case ']':
+ case '?':
+ case ':':
+ case '-':case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9':
+ ec = jmespath_errc::expected_multi_select_list;
+ return jmespath_expression();
+ case '*':
+ push_token(token(jsoncons::make_unique<list_projection>()), ec);
+ if (ec) {return jmespath_expression();}
+ state_stack_.back() = path_state::expect_rbracket;
+ ++p_;
+ ++column_;
+ break;
+ default:
+ push_token(token(begin_multi_select_list_arg), ec);
+ if (ec) {return jmespath_expression();}
+ state_stack_.back() = path_state::multi_select_list;
+ state_stack_.emplace_back(path_state::lhs_expression);
+ break;
+ }
+ break;
+
+ case path_state::multi_select_hash:
+ switch(*p_)
+ {
+ case '*':
+ case ']':
+ case '?':
+ case ':':
+ case '-':case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9':
+ break;
+ default:
+ state_stack_.back() = path_state::key_val_expr;
+ break;
+ }
+ break;
+
+ case path_state::index_or_slice_expression:
+ switch(*p_)
+ {
+ case ']':
+ {
+ if (buffer.empty())
+ {
+ push_token(token(jsoncons::make_unique<flatten_projection>()), ec);
+ if (ec) {return jmespath_expression();}
+ }
+ else
+ {
+ int64_t val{ 0 };
+ auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), val);
+ if (!r)
+ {
+ ec = jmespath_errc::invalid_number;
+ return jmespath_expression();
+ }
+ push_token(token(jsoncons::make_unique<index_selector>(val)), ec);
+ if (ec) {return jmespath_expression();}
+
+ buffer.clear();
+ }
+ state_stack_.pop_back(); // bracket_specifier
+ ++p_;
+ ++column_;
+ break;
+ }
+ case ':':
+ {
+ if (!buffer.empty())
+ {
+ int64_t val;
+ auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), val);
+ if (!r)
+ {
+ ec = jmespath_errc::invalid_number;
+ return jmespath_expression();
+ }
+ slic.start_ = val;
+ buffer.clear();
+ }
+ state_stack_.back() = path_state::rhs_slice_expression_stop;
+ state_stack_.emplace_back(path_state::number);
+ ++p_;
+ ++column_;
+ break;
+ }
+ default:
+ ec = jmespath_errc::expected_rbracket;
+ return jmespath_expression();
+ }
+ break;
+ case path_state::rhs_slice_expression_stop :
+ {
+ if (!buffer.empty())
+ {
+ int64_t val{ 0 };
+ auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), val);
+ if (!r)
+ {
+ ec = jmespath_errc::invalid_number;
+ return jmespath_expression();
+ }
+ slic.stop_ = jsoncons::optional<int64_t>(val);
+ buffer.clear();
+ }
+ switch(*p_)
+ {
+ case ']':
+ push_token(token(jsoncons::make_unique<slice_projection>(slic)), ec);
+ if (ec) {return jmespath_expression();}
+ slic = slice{};
+ state_stack_.pop_back(); // bracket_specifier2
+ ++p_;
+ ++column_;
+ break;
+ case ':':
+ state_stack_.back() = path_state::rhs_slice_expression_step;
+ state_stack_.emplace_back(path_state::number);
+ ++p_;
+ ++column_;
+ break;
+ default:
+ ec = jmespath_errc::expected_rbracket;
+ return jmespath_expression();
+ }
+ break;
+ }
+ case path_state::rhs_slice_expression_step:
+ {
+ if (!buffer.empty())
+ {
+ int64_t val{ 0 };
+ auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), val);
+ if (!r)
+ {
+ ec = jmespath_errc::invalid_number;
+ return jmespath_expression();
+ }
+ if (val == 0)
+ {
+ ec = jmespath_errc::step_cannot_be_zero;
+ return jmespath_expression();
+ }
+ slic.step_ = val;
+ buffer.clear();
+ }
+ switch(*p_)
+ {
+ case ']':
+ push_token(token(jsoncons::make_unique<slice_projection>(slic)), ec);
+ if (ec) {return jmespath_expression();}
+ buffer.clear();
+ slic = slice{};
+ state_stack_.pop_back(); // rhs_slice_expression_step
+ ++p_;
+ ++column_;
+ break;
+ default:
+ ec = jmespath_errc::expected_rbracket;
+ return jmespath_expression();
+ }
+ break;
+ }
+ case path_state::expect_rbracket:
+ {
+ switch(*p_)
+ {
+ case ']':
+ state_stack_.pop_back(); // expect_rbracket
+ ++p_;
+ ++column_;
+ break;
+ default:
+ ec = jmespath_errc::expected_rbracket;
+ return jmespath_expression();
+ }
+ break;
+ }
+ case path_state::expect_rparen:
+ switch (*p_)
+ {
+ case ' ':case '\t':case '\r':case '\n':
+ advance_past_space_character(ec);
+ break;
+ case ')':
+ ++p_;
+ ++column_;
+ push_token(rparen_arg, ec);
+ if (ec) {return jmespath_expression();}
+ state_stack_.back() = path_state::rhs_expression;
+ break;
+ default:
+ ec = jmespath_errc::expected_rparen;
+ return jmespath_expression();
+ }
+ break;
+ case path_state::key_val_expr:
+ {
+ switch (*p_)
+ {
+ case ' ':case '\t':case '\r':case '\n':
+ advance_past_space_character(ec);
+ break;
+ case '\"':
+ state_stack_.back() = path_state::expect_colon;
+ state_stack_.emplace_back(path_state::key_expr);
+ state_stack_.emplace_back(path_state::quoted_string);
+ ++p_;
+ ++column_;
+ break;
+ case '\'':
+ state_stack_.back() = path_state::expect_colon;
+ state_stack_.emplace_back(path_state::raw_string);
+ ++p_;
+ ++column_;
+ break;
+ default:
+ if ((*p_ >= 'A' && *p_ <= 'Z') || (*p_ >= 'a' && *p_ <= 'z') || (*p_ == '_'))
+ {
+ state_stack_.back() = path_state::expect_colon;
+ state_stack_.emplace_back(path_state::key_expr);
+ state_stack_.emplace_back(path_state::unquoted_string);
+ buffer.push_back(*p_);
+ ++p_;
+ ++column_;
+ }
+ else
+ {
+ ec = jmespath_errc::expected_key;
+ return jmespath_expression();
+ }
+ break;
+ };
+ break;
+ }
+ case path_state::cmp_lt_or_lte:
+ {
+ switch(*p_)
+ {
+ case '=':
+ push_token(token(resources_.get_lte_operator()), ec);
+ push_token(token(current_node_arg), ec);
+ if (ec) {return jmespath_expression();}
+ state_stack_.pop_back();
+ ++p_;
+ ++column_;
+ break;
+ default:
+ push_token(token(resources_.get_lt_operator()), ec);
+ push_token(token(current_node_arg), ec);
+ if (ec) {return jmespath_expression();}
+ state_stack_.pop_back();
+ break;
+ }
+ break;
+ }
+ case path_state::cmp_gt_or_gte:
+ {
+ switch(*p_)
+ {
+ case '=':
+ push_token(token(resources_.get_gte_operator()), ec);
+ push_token(token(current_node_arg), ec);
+ if (ec) {return jmespath_expression();}
+ state_stack_.pop_back();
+ ++p_;
+ ++column_;
+ break;
+ default:
+ push_token(token(resources_.get_gt_operator()), ec);
+ push_token(token(current_node_arg), ec);
+ if (ec) {return jmespath_expression();}
+ state_stack_.pop_back();
+ break;
+ }
+ break;
+ }
+ case path_state::cmp_eq:
+ {
+ switch(*p_)
+ {
+ case '=':
+ push_token(token(resources_.get_eq_operator()), ec);
+ push_token(token(current_node_arg), ec);
+ if (ec) {return jmespath_expression();}
+ state_stack_.pop_back();
+ ++p_;
+ ++column_;
+ break;
+ default:
+ ec = jmespath_errc::expected_comparator;
+ return jmespath_expression();
+ }
+ break;
+ }
+ case path_state::cmp_ne:
+ {
+ switch(*p_)
+ {
+ case '=':
+ push_token(token(resources_.get_ne_operator()), ec);
+ push_token(token(current_node_arg), ec);
+ if (ec) {return jmespath_expression();}
+ state_stack_.pop_back();
+ ++p_;
+ ++column_;
+ break;
+ default:
+ ec = jmespath_errc::expected_comparator;
+ return jmespath_expression();
+ }
+ break;
+ }
+ case path_state::expect_dot:
+ {
+ switch(*p_)
+ {
+ case ' ':case '\t':case '\r':case '\n':
+ advance_past_space_character(ec);
+ break;
+ case '.':
+ state_stack_.pop_back(); // expect_dot
+ ++p_;
+ ++column_;
+ break;
+ default:
+ ec = jmespath_errc::expected_dot;
+ return jmespath_expression();
+ }
+ break;
+ }
+ case path_state::expect_pipe_or_or:
+ {
+ switch(*p_)
+ {
+ case '|':
+ push_token(token(resources_.get_or_operator()), ec);
+ push_token(token(current_node_arg), ec);
+ if (ec) {return jmespath_expression();}
+ state_stack_.pop_back();
+ ++p_;
+ ++column_;
+ break;
+ default:
+ push_token(token(pipe_arg), ec);
+ if (ec) {return jmespath_expression();}
+ state_stack_.pop_back();
+ break;
+ }
+ break;
+ }
+ case path_state::expect_and:
+ {
+ switch(*p_)
+ {
+ case '&':
+ push_token(token(resources_.get_and_operator()), ec);
+ push_token(token(current_node_arg), ec);
+ if (ec) {return jmespath_expression();}
+ state_stack_.pop_back(); // expect_and
+ ++p_;
+ ++column_;
+ break;
+ default:
+ ec = jmespath_errc::expected_and;
+ return jmespath_expression();
+ }
+ break;
+ }
+ case path_state::multi_select_list:
+ {
+ switch(*p_)
+ {
+ case ' ':case '\t':case '\r':case '\n':
+ advance_past_space_character(ec);
+ break;
+ case ',':
+ push_token(token(separator_arg), ec);
+ if (ec) {return jmespath_expression();}
+ state_stack_.emplace_back(path_state::lhs_expression);
+ ++p_;
+ ++column_;
+ break;
+ case '[':
+ state_stack_.emplace_back(path_state::lhs_expression);
+ break;
+ case '.':
+ state_stack_.emplace_back(path_state::sub_expression);
+ ++p_;
+ ++column_;
+ break;
+ case '|':
+ {
+ ++p_;
+ ++column_;
+ state_stack_.emplace_back(path_state::lhs_expression);
+ state_stack_.emplace_back(path_state::expect_pipe_or_or);
+ break;
+ }
+ case ']':
+ {
+ push_token(token(end_multi_select_list_arg), ec);
+ if (ec) {return jmespath_expression();}
+ state_stack_.pop_back();
+
+ ++p_;
+ ++column_;
+ break;
+ }
+ default:
+ ec = jmespath_errc::expected_rbracket;
+ return jmespath_expression();
+ }
+ break;
+ }
+ case path_state::filter:
+ {
+ switch(*p_)
+ {
+ case ' ':case '\t':case '\r':case '\n':
+ advance_past_space_character(ec);
+ break;
+ case ']':
+ {
+ push_token(token(end_filter_arg), ec);
+ if (ec) {return jmespath_expression();}
+ state_stack_.pop_back();
+ ++p_;
+ ++column_;
+ break;
+ }
+ default:
+ ec = jmespath_errc::expected_rbracket;
+ return jmespath_expression();
+ }
+ break;
+ }
+ case path_state::expect_rbrace:
+ {
+ switch(*p_)
+ {
+ case ' ':case '\t':case '\r':case '\n':
+ advance_past_space_character(ec);
+ break;
+ case ',':
+ push_token(token(separator_arg), ec);
+ if (ec) {return jmespath_expression();}
+ state_stack_.back() = path_state::key_val_expr;
+ ++p_;
+ ++column_;
+ break;
+ case '[':
+ case '{':
+ state_stack_.emplace_back(path_state::lhs_expression);
+ break;
+ case '.':
+ state_stack_.emplace_back(path_state::sub_expression);
+ ++p_;
+ ++column_;
+ break;
+ case '}':
+ {
+ state_stack_.pop_back();
+ push_token(end_multi_select_hash_arg, ec);
+ if (ec) {return jmespath_expression();}
+ ++p_;
+ ++column_;
+ break;
+ }
+ default:
+ ec = jmespath_errc::expected_rbrace;
+ return jmespath_expression();
+ }
+ break;
+ }
+ case path_state::expect_colon:
+ {
+ switch(*p_)
+ {
+ case ' ':case '\t':case '\r':case '\n':
+ advance_past_space_character(ec);
+ break;
+ case ':':
+ state_stack_.back() = path_state::expect_rbrace;
+ state_stack_.emplace_back(path_state::lhs_expression);
+ ++p_;
+ ++column_;
+ break;
+ default:
+ ec = jmespath_errc::expected_colon;
+ return jmespath_expression();
+ }
+ break;
+ }
+ }
+
+ }
+
+ if (state_stack_.empty())
+ {
+ ec = jmespath_errc::syntax_error;
+ return jmespath_expression();
+ }
+ while (state_stack_.size() > 1)
+ {
+ switch (state_stack_.back())
+ {
+ case path_state::rhs_expression:
+ if (state_stack_.size() > 1)
+ {
+ state_stack_.pop_back();
+ }
+ else
+ {
+ ec = jmespath_errc::syntax_error;
+ return jmespath_expression();
+ }
+ break;
+ case path_state::val_expr:
+ push_token(token(jsoncons::make_unique<identifier_selector>(buffer)), ec);
+ if (ec) {return jmespath_expression();}
+ state_stack_.pop_back();
+ break;
+ case path_state::identifier_or_function_expr:
+ push_token(token(jsoncons::make_unique<identifier_selector>(buffer)), ec);
+ if (ec) {return jmespath_expression();}
+ state_stack_.pop_back();
+ break;
+ case path_state::unquoted_string:
+ state_stack_.pop_back();
+ break;
+ default:
+ ec = jmespath_errc::syntax_error;
+ return jmespath_expression();
+ break;
+ }
+ }
+
+ if (!(state_stack_.size() == 1 && state_stack_.back() == path_state::rhs_expression))
+ {
+ ec = jmespath_errc::unexpected_end_of_input;
+ return jmespath_expression();
+ }
+
+ state_stack_.pop_back();
+
+ push_token(end_of_expression_arg, ec);
+ if (ec) {return jmespath_expression();}
+
+ //for (auto& t : output_stack_)
+ //{
+ // std::cout << t.to_string() << std::endl;
+ //}
+
+ return jmespath_expression(std::move(resources_), std::move(output_stack_));
+ }
+
+ void advance_past_space_character(std::error_code& ec)
+ {
+ switch (*p_)
+ {
+ case ' ':case '\t':
+ ++p_;
+ ++column_;
+ break;
+ case '\r':
+ if (p_+1 >= end_input_)
+ {
+ ec = jmespath_errc::unexpected_end_of_input;
+ return;
+ }
+ if (*(p_+1) == '\n')
+ ++p_;
+ ++line_;
+ column_ = 1;
+ ++p_;
+ break;
+ case '\n':
+ ++line_;
+ column_ = 1;
+ ++p_;
+ break;
+ default:
+ break;
+ }
+ }
+
+ void unwind_rparen(std::error_code& ec)
+ {
+ auto it = operator_stack_.rbegin();
+ while (it != operator_stack_.rend() && !it->is_lparen())
+ {
+ output_stack_.emplace_back(std::move(*it));
+ ++it;
+ }
+ if (it == operator_stack_.rend())
+ {
+ ec = jmespath_errc::unbalanced_parentheses;
+ return;
+ }
+ ++it;
+ operator_stack_.erase(it.base(),operator_stack_.end());
+ }
+
+ void push_token(token&& tok, std::error_code& ec)
+ {
+ switch (tok.type())
+ {
+ case token_kind::end_filter:
+ {
+ unwind_rparen(ec);
+ std::vector<token> toks;
+ auto it = output_stack_.rbegin();
+ while (it != output_stack_.rend() && it->type() != token_kind::begin_filter)
+ {
+ toks.emplace_back(std::move(*it));
+ ++it;
+ }
+ if (it == output_stack_.rend())
+ {
+ ec = jmespath_errc::unbalanced_braces;
+ return;
+ }
+ if (toks.back().type() != token_kind::literal)
+ {
+ toks.emplace_back(current_node_arg);
+ }
+ std::reverse(toks.begin(), toks.end());
+ ++it;
+ output_stack_.erase(it.base(),output_stack_.end());
+
+ if (!output_stack_.empty() && output_stack_.back().is_projection() &&
+ (tok.precedence_level() < output_stack_.back().precedence_level() ||
+ (tok.precedence_level() == output_stack_.back().precedence_level() && tok.is_right_associative())))
+ {
+ output_stack_.back().expression_->add_expression(jsoncons::make_unique<filter_expression>(std::move(toks)));
+ }
+ else
+ {
+ output_stack_.emplace_back(token(jsoncons::make_unique<filter_expression>(std::move(toks))));
+ }
+ break;
+ }
+ case token_kind::end_multi_select_list:
+ {
+ unwind_rparen(ec);
+ std::vector<std::vector<token>> vals;
+ auto it = output_stack_.rbegin();
+ while (it != output_stack_.rend() && it->type() != token_kind::begin_multi_select_list)
+ {
+ std::vector<token> toks;
+ do
+ {
+ toks.emplace_back(std::move(*it));
+ ++it;
+ } while (it != output_stack_.rend() && it->type() != token_kind::begin_multi_select_list && it->type() != token_kind::separator);
+ if (it->type() == token_kind::separator)
+ {
+ ++it;
+ }
+ if (toks.back().type() != token_kind::literal)
+ {
+ toks.emplace_back(current_node_arg);
+ }
+ std::reverse(toks.begin(), toks.end());
+ vals.emplace_back(std::move(toks));
+ }
+ if (it == output_stack_.rend())
+ {
+ ec = jmespath_errc::unbalanced_braces;
+ return;
+ }
+ ++it;
+ output_stack_.erase(it.base(),output_stack_.end());
+ std::reverse(vals.begin(), vals.end());
+ if (!output_stack_.empty() && output_stack_.back().is_projection() &&
+ (tok.precedence_level() < output_stack_.back().precedence_level() ||
+ (tok.precedence_level() == output_stack_.back().precedence_level() && tok.is_right_associative())))
+ {
+ output_stack_.back().expression_->add_expression(jsoncons::make_unique<multi_select_list>(std::move(vals)));
+ }
+ else
+ {
+ output_stack_.emplace_back(token(jsoncons::make_unique<multi_select_list>(std::move(vals))));
+ }
+ break;
+ }
+ case token_kind::end_multi_select_hash:
+ {
+ unwind_rparen(ec);
+ std::vector<key_tokens> key_toks;
+ auto it = output_stack_.rbegin();
+ while (it != output_stack_.rend() && it->type() != token_kind::begin_multi_select_hash)
+ {
+ std::vector<token> toks;
+ do
+ {
+ toks.emplace_back(std::move(*it));
+ ++it;
+ } while (it != output_stack_.rend() && it->type() != token_kind::key);
+ JSONCONS_ASSERT(it->is_key());
+ auto key = std::move(it->key_);
+ ++it;
+ if (it->type() == token_kind::separator)
+ {
+ ++it;
+ }
+ if (toks.back().type() != token_kind::literal)
+ {
+ toks.emplace_back(current_node_arg);
+ }
+ std::reverse(toks.begin(), toks.end());
+ key_toks.emplace_back(std::move(key), std::move(toks));
+ }
+ if (it == output_stack_.rend())
+ {
+ ec = jmespath_errc::unbalanced_braces;
+ return;
+ }
+ std::reverse(key_toks.begin(), key_toks.end());
+ ++it;
+ output_stack_.erase(it.base(),output_stack_.end());
+
+ if (!output_stack_.empty() && output_stack_.back().is_projection() &&
+ (tok.precedence_level() < output_stack_.back().precedence_level() ||
+ (tok.precedence_level() == output_stack_.back().precedence_level() && tok.is_right_associative())))
+ {
+ output_stack_.back().expression_->add_expression(jsoncons::make_unique<multi_select_hash>(std::move(key_toks)));
+ }
+ else
+ {
+ output_stack_.emplace_back(token(jsoncons::make_unique<multi_select_hash>(std::move(key_toks))));
+ }
+ break;
+ }
+ case token_kind::end_expression_type:
+ {
+ std::vector<token> toks;
+ auto it = output_stack_.rbegin();
+ while (it != output_stack_.rend() && it->type() != token_kind::begin_expression_type)
+ {
+ toks.emplace_back(std::move(*it));
+ ++it;
+ }
+ if (it == output_stack_.rend())
+ {
+ JSONCONS_THROW(json_runtime_error<std::runtime_error>("Unbalanced braces"));
+ }
+ if (toks.back().type() != token_kind::literal)
+ {
+ toks.emplace_back(current_node_arg);
+ }
+ std::reverse(toks.begin(), toks.end());
+ output_stack_.erase(it.base(),output_stack_.end());
+ output_stack_.emplace_back(token(jsoncons::make_unique<function_expression>(std::move(toks))));
+ break;
+ }
+ case token_kind::literal:
+ if (!output_stack_.empty() && output_stack_.back().type() == token_kind::current_node)
+ {
+ output_stack_.back() = std::move(tok);
+ }
+ else
+ {
+ output_stack_.emplace_back(std::move(tok));
+ }
+ break;
+ case token_kind::expression:
+ if (!output_stack_.empty() && output_stack_.back().is_projection() &&
+ (tok.precedence_level() < output_stack_.back().precedence_level() ||
+ (tok.precedence_level() == output_stack_.back().precedence_level() && tok.is_right_associative())))
+ {
+ output_stack_.back().expression_->add_expression(std::move(tok.expression_));
+ }
+ else
+ {
+ output_stack_.emplace_back(std::move(tok));
+ }
+ break;
+ case token_kind::rparen:
+ {
+ unwind_rparen(ec);
+ break;
+ }
+ case token_kind::end_function:
+ {
+ unwind_rparen(ec);
+ std::vector<token> toks;
+ auto it = output_stack_.rbegin();
+ std::size_t arg_count = 0;
+ while (it != output_stack_.rend() && it->type() != token_kind::function)
+ {
+ if (it->type() == token_kind::argument)
+ {
+ ++arg_count;
+ }
+ toks.emplace_back(std::move(*it));
+ ++it;
+ }
+ if (it == output_stack_.rend())
+ {
+ ec = jmespath_errc::unbalanced_parentheses;
+ return;
+ }
+ if (it->arity() && arg_count != *(it->arity()))
+ {
+ ec = jmespath_errc::invalid_arity;
+ return;
+ }
+ if (toks.back().type() != token_kind::literal)
+ {
+ toks.emplace_back(current_node_arg);
+ }
+ std::reverse(toks.begin(), toks.end());
+ toks.push_back(std::move(*it));
+ ++it;
+ output_stack_.erase(it.base(),output_stack_.end());
+
+ if (!output_stack_.empty() && output_stack_.back().is_projection() &&
+ (tok.precedence_level() < output_stack_.back().precedence_level() ||
+ (tok.precedence_level() == output_stack_.back().precedence_level() && tok.is_right_associative())))
+ {
+ output_stack_.back().expression_->add_expression(jsoncons::make_unique<function_expression>(std::move(toks)));
+ }
+ else
+ {
+ output_stack_.emplace_back(token(jsoncons::make_unique<function_expression>(std::move(toks))));
+ }
+ break;
+ }
+ case token_kind::end_of_expression:
+ {
+ auto it = operator_stack_.rbegin();
+ while (it != operator_stack_.rend())
+ {
+ output_stack_.emplace_back(std::move(*it));
+ ++it;
+ }
+ operator_stack_.clear();
+ break;
+ }
+ case token_kind::unary_operator:
+ case token_kind::binary_operator:
+ {
+ if (operator_stack_.empty() || operator_stack_.back().is_lparen())
+ {
+ operator_stack_.emplace_back(std::move(tok));
+ }
+ else if (tok.precedence_level() < operator_stack_.back().precedence_level()
+ || (tok.precedence_level() == operator_stack_.back().precedence_level() && tok.is_right_associative()))
+ {
+ operator_stack_.emplace_back(std::move(tok));
+ }
+ else
+ {
+ auto it = operator_stack_.rbegin();
+ while (it != operator_stack_.rend() && it->is_operator()
+ && (tok.precedence_level() > it->precedence_level()
+ || (tok.precedence_level() == it->precedence_level() && tok.is_right_associative())))
+ {
+ output_stack_.emplace_back(std::move(*it));
+ ++it;
+ }
+
+ operator_stack_.erase(it.base(),operator_stack_.end());
+ operator_stack_.emplace_back(std::move(tok));
+ }
+ break;
+ }
+ case token_kind::separator:
+ {
+ unwind_rparen(ec);
+ output_stack_.emplace_back(std::move(tok));
+ operator_stack_.emplace_back(token(lparen_arg));
+ break;
+ }
+ case token_kind::begin_filter:
+ output_stack_.emplace_back(std::move(tok));
+ operator_stack_.emplace_back(token(lparen_arg));
+ break;
+ case token_kind::begin_multi_select_list:
+ output_stack_.emplace_back(std::move(tok));
+ operator_stack_.emplace_back(token(lparen_arg));
+ break;
+ case token_kind::begin_multi_select_hash:
+ output_stack_.emplace_back(std::move(tok));
+ operator_stack_.emplace_back(token(lparen_arg));
+ break;
+ case token_kind::function:
+ output_stack_.emplace_back(std::move(tok));
+ operator_stack_.emplace_back(token(lparen_arg));
+ break;
+ case token_kind::current_node:
+ output_stack_.emplace_back(std::move(tok));
+ break;
+ case token_kind::key:
+ case token_kind::pipe:
+ case token_kind::argument:
+ case token_kind::begin_expression_type:
+ output_stack_.emplace_back(std::move(tok));
+ break;
+ case token_kind::lparen:
+ operator_stack_.emplace_back(std::move(tok));
+ break;
+ default:
+ break;
+ }
+ }
+
+ uint32_t append_to_codepoint(uint32_t cp, int c, std::error_code& ec)
+ {
+ cp *= 16;
+ if (c >= '0' && c <= '9')
+ {
+ cp += c - '0';
+ }
+ else if (c >= 'a' && c <= 'f')
+ {
+ cp += c - 'a' + 10;
+ }
+ else if (c >= 'A' && c <= 'F')
+ {
+ cp += c - 'A' + 10;
+ }
+ else
+ {
+ ec = jmespath_errc::invalid_codepoint;
+ }
+ return cp;
+ }
+ };
+
+ } // detail
+
+ template <class Json>
+ using jmespath_expression = typename jsoncons::jmespath::detail::jmespath_evaluator<Json,const Json&>::jmespath_expression;
+
+ template<class Json>
+ Json search(const Json& doc, const typename Json::string_view_type& path)
+ {
+ jsoncons::jmespath::detail::jmespath_evaluator<Json,const Json&> evaluator;
+ std::error_code ec;
+ auto expr = evaluator.compile(path.data(), path.size(), ec);
+ if (ec)
+ {
+ JSONCONS_THROW(jmespath_error(ec, evaluator.line(), evaluator.column()));
+ }
+ auto result = expr.evaluate(doc, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(jmespath_error(ec));
+ }
+ return result;
+ }
+
+ template<class Json>
+ Json search(const Json& doc, const typename Json::string_view_type& path, std::error_code& ec)
+ {
+ jsoncons::jmespath::detail::jmespath_evaluator<Json,const Json&> evaluator;
+ auto expr = evaluator.compile(path.data(), path.size(), ec);
+ if (ec)
+ {
+ return Json::null();
+ }
+ auto result = expr.evaluate(doc, ec);
+ if (ec)
+ {
+ return Json::null();
+ }
+ return result;
+ }
+
+ template <class Json>
+ jmespath_expression<Json> make_expression(const typename json::string_view_type& expr)
+ {
+ return jmespath_expression<Json>::compile(expr);
+ }
+
+ template <class Json>
+ jmespath_expression<Json> make_expression(const typename json::string_view_type& expr,
+ std::error_code& ec)
+ {
+ return jmespath_expression<Json>::compile(expr, ec);
+ }
+
+
+} // namespace jmespath
+} // namespace jsoncons
+
+#endif
diff --git a/include/jsoncons_ext/jmespath/jmespath_error.hpp b/include/jsoncons_ext/jmespath/jmespath_error.hpp
new file mode 100644
index 0000000..6422c65
--- /dev/null
+++ b/include/jsoncons_ext/jmespath/jmespath_error.hpp
@@ -0,0 +1,215 @@
+/// Copyright 2020 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_JMESPATH_JMESPATH_ERROR_HPP
+#define JSONCONS_JMESPATH_JMESPATH_ERROR_HPP
+
+#include <jsoncons/json_exception.hpp>
+#include <system_error>
+
+namespace jsoncons { namespace jmespath {
+
+ class jmespath_error : public std::system_error, public virtual json_exception
+ {
+ std::size_t line_number_;
+ std::size_t column_number_;
+ mutable std::string what_;
+ public:
+ jmespath_error(std::error_code ec)
+ : std::system_error(ec), line_number_(0), column_number_(0)
+ {
+ }
+ jmespath_error(std::error_code ec, const std::string& what_arg)
+ : std::system_error(ec, what_arg), line_number_(0), column_number_(0)
+ {
+ }
+ jmespath_error(std::error_code ec, std::size_t position)
+ : std::system_error(ec), line_number_(0), column_number_(position)
+ {
+ }
+ jmespath_error(std::error_code ec, std::size_t line, std::size_t column)
+ : std::system_error(ec), line_number_(line), column_number_(column)
+ {
+ }
+ jmespath_error(const jmespath_error& other) = default;
+
+ jmespath_error(jmespath_error&& other) = default;
+
+ const char* what() const noexcept override
+ {
+ 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_;
+ }
+ };
+
+enum class jmespath_errc
+{
+ success = 0,
+ expected_identifier,
+ expected_index,
+ expected_A_Za_Z_,
+ expected_rbracket,
+ expected_rparen,
+ expected_rbrace,
+ expected_colon,
+ expected_dot,
+ expected_or,
+ expected_and,
+ expected_multi_select_list,
+ invalid_number,
+ invalid_literal,
+ expected_comparator,
+ expected_key,
+ invalid_argument,
+ unknown_function,
+ invalid_type,
+ unexpected_end_of_input,
+ step_cannot_be_zero,
+ syntax_error,
+ invalid_codepoint,
+ illegal_escaped_character,
+ unbalanced_parentheses,
+ unbalanced_braces,
+ invalid_arity,
+ identifier_not_found,
+ expected_index_expression,
+ unknown_error
+};
+
+class jmespath_error_category_impl
+ : public std::error_category
+{
+public:
+ const char* name() const noexcept override
+ {
+ return "jsoncons/jmespath";
+ }
+ std::string message(int ev) const override
+ {
+ switch (static_cast<jmespath_errc>(ev))
+ {
+ case jmespath_errc::expected_identifier:
+ return "Expected identifier";
+ case jmespath_errc::expected_index:
+ return "Expected index";
+ case jmespath_errc::expected_A_Za_Z_:
+ return "Expected A-Z, a-z, or _";
+ case jmespath_errc::expected_rbracket:
+ return "Expected ]";
+ case jmespath_errc::expected_rparen:
+ return "Expected )";
+ case jmespath_errc::expected_rbrace:
+ return "Expected }";
+ case jmespath_errc::expected_colon:
+ return "Expected :";
+ case jmespath_errc::expected_dot:
+ return "Expected \".\"";
+ case jmespath_errc::expected_or:
+ return "Expected \"||\"";
+ case jmespath_errc::expected_and:
+ return "Expected \"&&\"";
+ case jmespath_errc::expected_multi_select_list:
+ return "Expected multi-select-list";
+ case jmespath_errc::invalid_number:
+ return "Invalid number";
+ case jmespath_errc::invalid_literal:
+ return "Invalid literal";
+ case jmespath_errc::expected_comparator:
+ return "Expected <, <=, ==, >=, > or !=";
+ case jmespath_errc::expected_key:
+ return "Expected key";
+ case jmespath_errc::invalid_argument:
+ return "Invalid argument type";
+ case jmespath_errc::unknown_function:
+ return "Unknown function";
+ case jmespath_errc::invalid_type:
+ return "Invalid type";
+ case jmespath_errc::unexpected_end_of_input:
+ return "Unexpected end of jmespath input";
+ case jmespath_errc::step_cannot_be_zero:
+ return "Slice step cannot be zero";
+ case jmespath_errc::syntax_error:
+ return "Syntax error";
+ case jmespath_errc::invalid_codepoint:
+ return "Invalid codepoint";
+ case jmespath_errc::illegal_escaped_character:
+ return "Illegal escaped character";
+ case jmespath_errc::unbalanced_parentheses:
+ return "Unbalanced parentheses";
+ case jmespath_errc::unbalanced_braces:
+ return "Unbalanced braces";
+ case jmespath_errc::invalid_arity:
+ return "Function called with wrong number of arguments";
+ case jmespath_errc::identifier_not_found:
+ return "Identifier not found";
+ case jmespath_errc::expected_index_expression:
+ return "Expected index expression";
+ case jmespath_errc::unknown_error:
+ default:
+ return "Unknown jmespath parser error";
+ }
+ }
+};
+
+inline
+const std::error_category& jmespath_error_category()
+{
+ static jmespath_error_category_impl instance;
+ return instance;
+}
+
+inline
+std::error_code make_error_code(jmespath_errc result)
+{
+ return std::error_code(static_cast<int>(result),jmespath_error_category());
+}
+
+}}
+
+namespace std {
+ template<>
+ struct is_error_code_enum<jsoncons::jmespath::jmespath_errc> : public true_type
+ {
+ };
+}
+
+#endif
diff --git a/include/jsoncons_ext/jsonpatch/jsonpatch.hpp b/include/jsoncons_ext/jsonpatch/jsonpatch.hpp
new file mode 100644
index 0000000..ab4ace7
--- /dev/null
+++ b/include/jsoncons_ext/jsonpatch/jsonpatch.hpp
@@ -0,0 +1,579 @@
+// Copyright 2017 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_JSONPATCH_JSONPATCH_HPP
+#define JSONCONS_JSONPATCH_JSONPATCH_HPP
+
+#include <string>
+#include <vector>
+#include <memory>
+#include <algorithm> // std::min
+#include <utility> // std::move
+#include <jsoncons/json.hpp>
+#include <jsoncons_ext/jsonpointer/jsonpointer.hpp>
+#include <jsoncons_ext/jsonpatch/jsonpatch_error.hpp>
+
+namespace jsoncons { namespace jsonpatch {
+
+namespace detail {
+
+ template <class CharT>
+ struct jsonpatch_names
+ {
+ static std::basic_string<CharT> test_name()
+ {
+ static std::basic_string<CharT> name{'t','e','s','t'};
+ return name;
+ }
+ static std::basic_string<CharT> add_name()
+ {
+ static std::basic_string<CharT> name{'a','d','d'};
+ return name;
+ }
+ static std::basic_string<CharT> remove_name()
+ {
+ static std::basic_string<CharT> name{'r','e','m','o','v','e'};
+ return name;
+ }
+ static std::basic_string<CharT> replace_name()
+ {
+ static std::basic_string<CharT> name{'r','e','p','l','a','c','e'};
+ return name;
+ }
+ static std::basic_string<CharT> move_name()
+ {
+ static std::basic_string<CharT> name{'m','o','v','e'};
+ return name;
+ }
+ static std::basic_string<CharT> copy_name()
+ {
+ static std::basic_string<CharT> name{'c','o','p','y'};
+ return name;
+ }
+ static std::basic_string<CharT> op_name()
+ {
+ static std::basic_string<CharT> name{'o','p'};
+ return name;
+ }
+ static std::basic_string<CharT> path_name()
+ {
+ static std::basic_string<CharT> name{'p','a','t','h'};
+ return name;
+ }
+ static std::basic_string<CharT> from_name()
+ {
+ static std::basic_string<CharT> name{'f','r','o','m'};
+ return name;
+ }
+ static std::basic_string<CharT> value_name()
+ {
+ static std::basic_string<CharT> name{'v','a','l','u','e'};
+ return name;
+ }
+ static std::basic_string<CharT> dash_name()
+ {
+ static std::basic_string<CharT> name{'-'};
+ return name;
+ }
+ };
+
+ template<class Json>
+ jsonpointer::basic_json_pointer<typename Json::char_type> definite_path(const Json& root, jsonpointer::basic_json_pointer<typename Json::char_type>& location)
+ {
+ using char_type = typename Json::char_type;
+ using string_type = std::basic_string<char_type>;
+
+ auto rit = location.rbegin();
+ if (rit == location.rend())
+ {
+ return location;
+ }
+
+ if (*rit != jsonpatch_names<char_type>::dash_name())
+ {
+ return location;
+ }
+
+ std::vector<string_type> tokens;
+ for (auto it = location.begin(); it != location.rbegin().base()-1; ++it)
+ {
+ tokens.push_back(*it);
+ }
+ jsonpointer::basic_json_pointer<char_type> pointer(tokens);
+
+ std::error_code ec;
+
+ Json val = jsonpointer::get(root, pointer, ec);
+ if (ec || !val.is_array())
+ {
+ return location;
+ }
+ string_type last_token;
+ jsoncons::detail::from_integer(val.size(), last_token);
+ tokens.emplace_back(std::move(last_token));
+
+ return jsonpointer::basic_json_pointer<char_type>(std::move(tokens));
+ }
+
+ enum class op_type {add,remove,replace};
+ enum class state_type {begin,abort,commit};
+
+ template <class Json>
+ struct operation_unwinder
+ {
+ using char_type = typename Json::char_type;
+ using string_type = std::basic_string<char_type>;
+ using json_pointer_type = jsonpointer::basic_json_pointer<char_type>;
+
+ struct entry
+ {
+ op_type op;
+ json_pointer_type path;
+ Json value;
+
+ entry(op_type op, const json_pointer_type& path, const Json& value)
+ : op(op), path(path), value(value)
+ {
+ }
+
+ entry(const entry&) = default;
+
+ entry(entry&&) = default;
+
+ entry& operator=(const entry&) = default;
+
+ entry& operator=(entry&&) = default;
+ };
+
+ Json& target;
+ state_type state;
+ std::vector<entry> stack;
+
+ operation_unwinder(Json& j)
+ : target(j), state(state_type::begin)
+ {
+ }
+
+ ~operation_unwinder() noexcept
+ {
+ std::error_code ec;
+ if (state != state_type::commit)
+ {
+ for (auto it = stack.rbegin(); it != stack.rend(); ++it)
+ {
+ if (it->op == op_type::add)
+ {
+ jsonpointer::add(target,it->path,it->value,ec);
+ if (ec)
+ {
+ //std::cout << "add: " << it->path << std::endl;
+ break;
+ }
+ }
+ else if (it->op == op_type::remove)
+ {
+ jsonpointer::remove(target,it->path,ec);
+ if (ec)
+ {
+ //std::cout << "remove: " << it->path << std::endl;
+ break;
+ }
+ }
+ else if (it->op == op_type::replace)
+ {
+ jsonpointer::replace(target,it->path,it->value,ec);
+ if (ec)
+ {
+ //std::cout << "replace: " << it->path << std::endl;
+ break;
+ }
+ }
+ }
+ }
+ }
+ };
+
+ template <class Json>
+ Json from_diff(const Json& source, const Json& target, const typename Json::string_view_type& path)
+ {
+ using char_type = typename Json::char_type;
+
+ Json result = typename Json::array();
+
+ if (source == target)
+ {
+ return result;
+ }
+
+ if (source.is_array() && target.is_array())
+ {
+ std::size_t common = (std::min)(source.size(),target.size());
+ for (std::size_t i = 0; i < common; ++i)
+ {
+ std::basic_string<char_type> ss(path);
+ ss.push_back('/');
+ jsoncons::detail::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());
+ }
+ // Element in source, not in target - remove
+ for (std::size_t i = source.size(); i-- > target.size();)
+ {
+ std::basic_string<char_type> ss(path);
+ ss.push_back('/');
+ jsoncons::detail::from_integer(i,ss);
+ Json val(json_object_arg);
+ val.insert_or_assign(jsonpatch_names<char_type>::op_name(), jsonpatch_names<char_type>::remove_name());
+ val.insert_or_assign(jsonpatch_names<char_type>::path_name(), ss);
+ result.push_back(std::move(val));
+ }
+ // Element in target, not in source - add,
+ // Fix contributed by Alexander rog13
+ for (std::size_t i = source.size(); i < target.size(); ++i)
+ {
+ const auto& a = target[i];
+ std::basic_string<char_type> ss(path);
+ ss.push_back('/');
+ jsoncons::detail::from_integer(i,ss);
+ Json val(json_object_arg);
+ val.insert_or_assign(jsonpatch_names<char_type>::op_name(), jsonpatch_names<char_type>::add_name());
+ val.insert_or_assign(jsonpatch_names<char_type>::path_name(), ss);
+ val.insert_or_assign(jsonpatch_names<char_type>::value_name(), a);
+ result.push_back(std::move(val));
+ }
+ }
+ else if (source.is_object() && target.is_object())
+ {
+ for (const auto& a : source.object_range())
+ {
+ std::basic_string<char_type> ss(path);
+ ss.push_back('/');
+ jsonpointer::escape(a.key(),ss);
+ auto it = target.find(a.key());
+ if (it != target.object_range().end())
+ {
+ auto temp_diff = from_diff(a.value(),it->value(),ss);
+ result.insert(result.array_range().end(),temp_diff.array_range().begin(),temp_diff.array_range().end());
+ }
+ else
+ {
+ Json val(json_object_arg);
+ val.insert_or_assign(jsonpatch_names<char_type>::op_name(), jsonpatch_names<char_type>::remove_name());
+ val.insert_or_assign(jsonpatch_names<char_type>::path_name(), ss);
+ result.push_back(std::move(val));
+ }
+ }
+ for (const auto& a : target.object_range())
+ {
+ auto it = source.find(a.key());
+ if (it == source.object_range().end())
+ {
+ std::basic_string<char_type> ss(path);
+ ss.push_back('/');
+ jsonpointer::escape(a.key(),ss);
+ Json val(json_object_arg);
+ val.insert_or_assign(jsonpatch_names<char_type>::op_name(), jsonpatch_names<char_type>::add_name());
+ val.insert_or_assign(jsonpatch_names<char_type>::path_name(), ss);
+ val.insert_or_assign(jsonpatch_names<char_type>::value_name(), a.value());
+ result.push_back(std::move(val));
+ }
+ }
+ }
+ else
+ {
+ Json val(json_object_arg);
+ val.insert_or_assign(jsonpatch_names<char_type>::op_name(), jsonpatch_names<char_type>::replace_name());
+ val.insert_or_assign(jsonpatch_names<char_type>::path_name(), path);
+ val.insert_or_assign(jsonpatch_names<char_type>::value_name(), target);
+ result.push_back(std::move(val));
+ }
+
+ return result;
+ }
+}
+
+template <class Json>
+void apply_patch(Json& target, const Json& patch, std::error_code& ec)
+{
+ using char_type = typename Json::char_type;
+ using string_type = std::basic_string<char_type>;
+ using json_pointer_type = jsonpointer::basic_json_pointer<char_type>;
+
+ jsoncons::jsonpatch::detail::operation_unwinder<Json> unwinder(target);
+ std::error_code local_ec;
+
+ // Validate
+
+ for (const auto& operation : patch.array_range())
+ {
+ unwinder.state =jsoncons::jsonpatch::detail::state_type::begin;
+
+ auto it_op = operation.find(detail::jsonpatch_names<char_type>::op_name());
+ if (it_op == operation.object_range().end())
+ {
+ ec = jsonpatch_errc::invalid_patch;
+ unwinder.state =jsoncons::jsonpatch::detail::state_type::abort;
+ return;
+ }
+ string_type op = it_op->value().template as<string_type>();
+
+ auto it_path = operation.find(detail::jsonpatch_names<char_type>::path_name());
+ if (it_path == operation.object_range().end())
+ {
+ ec = jsonpatch_errc::invalid_patch;
+ unwinder.state =jsoncons::jsonpatch::detail::state_type::abort;
+ return;
+ }
+ string_type path = it_path->value().template as<string_type>();
+ auto location = json_pointer_type::parse(path, local_ec);
+ if (local_ec)
+ {
+ ec = jsonpatch_errc::invalid_patch;
+ unwinder.state =jsoncons::jsonpatch::detail::state_type::abort;
+ return;
+ }
+
+ if (op ==jsoncons::jsonpatch::detail::jsonpatch_names<char_type>::test_name())
+ {
+ Json val = jsonpointer::get(target,location,local_ec);
+ if (local_ec)
+ {
+ ec = jsonpatch_errc::test_failed;
+ unwinder.state =jsoncons::jsonpatch::detail::state_type::abort;
+ return;
+ }
+ auto it_value = operation.find(detail::jsonpatch_names<char_type>::value_name());
+ if (it_value == operation.object_range().end())
+ {
+ ec = jsonpatch_errc::invalid_patch;
+ unwinder.state =jsoncons::jsonpatch::detail::state_type::abort;
+ return;
+ }
+ if (val != it_value->value())
+ {
+ ec = jsonpatch_errc::test_failed;
+ unwinder.state =jsoncons::jsonpatch::detail::state_type::abort;
+ return;
+ }
+ }
+ else if (op ==jsoncons::jsonpatch::detail::jsonpatch_names<char_type>::add_name())
+ {
+ auto it_value = operation.find(detail::jsonpatch_names<char_type>::value_name());
+ if (it_value == operation.object_range().end())
+ {
+ ec = jsonpatch_errc::invalid_patch;
+ unwinder.state =jsoncons::jsonpatch::detail::state_type::abort;
+ return;
+ }
+ Json val = it_value->value();
+ auto npath = jsonpatch::detail::definite_path(target,location);
+
+ std::error_code insert_ec;
+ jsonpointer::add_if_absent(target,npath,val,insert_ec); // try insert without replace
+ if (insert_ec) // try a replace
+ {
+ std::error_code select_ec;
+ Json orig_val = jsonpointer::get(target,npath,select_ec);
+ if (select_ec) // shouldn't happen
+ {
+ ec = jsonpatch_errc::add_failed;
+ unwinder.state =jsoncons::jsonpatch::detail::state_type::abort;
+ return;
+ }
+ std::error_code replace_ec;
+ jsonpointer::replace(target,npath,val,replace_ec);
+ if (replace_ec)
+ {
+ ec = jsonpatch_errc::add_failed;
+ unwinder.state =jsoncons::jsonpatch::detail::state_type::abort;
+ return;
+ }
+ unwinder.stack.emplace_back(detail::op_type::replace,npath,orig_val);
+ }
+ else // insert without replace succeeded
+ {
+ unwinder.stack.emplace_back(detail::op_type::remove,npath,Json::null());
+ }
+ }
+ else if (op ==jsoncons::jsonpatch::detail::jsonpatch_names<char_type>::remove_name())
+ {
+ Json val = jsonpointer::get(target,location,local_ec);
+ if (local_ec)
+ {
+ ec = jsonpatch_errc::remove_failed;
+ unwinder.state =jsoncons::jsonpatch::detail::state_type::abort;
+ return;
+ }
+ jsonpointer::remove(target,location,local_ec);
+ if (local_ec)
+ {
+ ec = jsonpatch_errc::remove_failed;
+ unwinder.state =jsoncons::jsonpatch::detail::state_type::abort;
+ return;
+ }
+ unwinder.stack.emplace_back(detail::op_type::add, location, val);
+ }
+ else if (op ==jsoncons::jsonpatch::detail::jsonpatch_names<char_type>::replace_name())
+ {
+ Json val = jsonpointer::get(target,location,local_ec);
+ if (local_ec)
+ {
+ ec = jsonpatch_errc::replace_failed;
+ unwinder.state =jsoncons::jsonpatch::detail::state_type::abort;
+ return;
+ }
+ auto it_value = operation.find(detail::jsonpatch_names<char_type>::value_name());
+ if (it_value == operation.object_range().end())
+ {
+ ec = jsonpatch_errc::invalid_patch;
+ unwinder.state =jsoncons::jsonpatch::detail::state_type::abort;
+ return;
+ }
+ jsonpointer::replace(target, location, it_value->value(), local_ec);
+ if (local_ec)
+ {
+ ec = jsonpatch_errc::replace_failed;
+ unwinder.state =jsoncons::jsonpatch::detail::state_type::abort;
+ return;
+ }
+ unwinder.stack.emplace_back(detail::op_type::replace,location,val);
+ }
+ else if (op ==jsoncons::jsonpatch::detail::jsonpatch_names<char_type>::move_name())
+ {
+ auto it_from = operation.find(detail::jsonpatch_names<char_type>::from_name());
+ if (it_from == operation.object_range().end())
+ {
+ ec = jsonpatch_errc::invalid_patch;
+ unwinder.state =jsoncons::jsonpatch::detail::state_type::abort;
+ return;
+ }
+ string_type from = it_from->value().as_string();
+ auto from_pointer = json_pointer_type::parse(from, local_ec);
+ if (local_ec)
+ {
+ ec = jsonpatch_errc::move_failed;
+ unwinder.state = jsoncons::jsonpatch::detail::state_type::abort;
+ return;
+ }
+
+ Json val = jsonpointer::get(target, from_pointer, local_ec);
+ if (local_ec)
+ {
+ ec = jsonpatch_errc::move_failed;
+ unwinder.state =jsoncons::jsonpatch::detail::state_type::abort;
+ return;
+ }
+ jsonpointer::remove(target, from_pointer, local_ec);
+ if (local_ec)
+ {
+ ec = jsonpatch_errc::move_failed;
+ unwinder.state =jsoncons::jsonpatch::detail::state_type::abort;
+ return;
+ }
+ unwinder.stack.emplace_back(detail::op_type::add, from_pointer, val);
+ // add
+ std::error_code insert_ec;
+ auto npath = jsonpatch::detail::definite_path(target,location);
+ jsonpointer::add_if_absent(target,npath,val,insert_ec); // try insert without replace
+ if (insert_ec) // try a replace
+ {
+ std::error_code select_ec;
+ Json orig_val = jsonpointer::get(target,npath,select_ec);
+ if (select_ec) // shouldn't happen
+ {
+ ec = jsonpatch_errc::copy_failed;
+ unwinder.state =jsoncons::jsonpatch::detail::state_type::abort;
+ return;
+ }
+ std::error_code replace_ec;
+ jsonpointer::replace(target, npath, val, replace_ec);
+ if (replace_ec)
+ {
+ ec = jsonpatch_errc::copy_failed;
+ unwinder.state =jsoncons::jsonpatch::detail::state_type::abort;
+ return;
+ }
+ unwinder.stack.emplace_back(jsoncons::jsonpatch::detail::op_type::replace,npath,orig_val);
+ }
+ else
+ {
+ unwinder.stack.emplace_back(detail::op_type::remove,npath,Json::null());
+ }
+ }
+ else if (op ==jsoncons::jsonpatch::detail::jsonpatch_names<char_type>::copy_name())
+ {
+ auto it_from = operation.find(detail::jsonpatch_names<char_type>::from_name());
+ if (it_from == operation.object_range().end())
+ {
+ ec = jsonpatch_errc::invalid_patch;
+ unwinder.state =jsoncons::jsonpatch::detail::state_type::abort;
+ return;
+ }
+ string_type from = it_from->value().as_string();
+ Json val = jsonpointer::get(target,from,local_ec);
+ if (local_ec)
+ {
+ ec = jsonpatch_errc::copy_failed;
+ unwinder.state =jsoncons::jsonpatch::detail::state_type::abort;
+ return;
+ }
+ // add
+ auto npath = jsonpatch::detail::definite_path(target,location);
+ std::error_code insert_ec;
+ jsonpointer::add_if_absent(target,npath,val,insert_ec); // try insert without replace
+ if (insert_ec) // Failed, try a replace
+ {
+ std::error_code select_ec;
+ Json orig_val = jsonpointer::get(target,npath, select_ec);
+ if (select_ec) // shouldn't happen
+ {
+ ec = jsonpatch_errc::copy_failed;
+ unwinder.state =jsoncons::jsonpatch::detail::state_type::abort;
+ return;
+ }
+ std::error_code replace_ec;
+ jsonpointer::replace(target, npath, val,replace_ec);
+ if (replace_ec)
+ {
+ ec = jsonpatch_errc::copy_failed;
+ unwinder.state =jsoncons::jsonpatch::detail::state_type::abort;
+ return;
+ }
+ unwinder.stack.emplace_back(jsoncons::jsonpatch::detail::op_type::replace,npath,orig_val);
+ }
+ else
+ {
+ unwinder.stack.emplace_back(detail::op_type::remove,npath,Json::null());
+ }
+ }
+ }
+ if (unwinder.state ==jsoncons::jsonpatch::detail::state_type::begin)
+ {
+ unwinder.state =jsoncons::jsonpatch::detail::state_type::commit;
+ }
+}
+
+template <class Json>
+Json from_diff(const Json& source, const Json& target)
+{
+ std::basic_string<typename Json::char_type> path;
+ return jsoncons::jsonpatch::detail::from_diff(source, target, path);
+}
+
+template <class Json>
+void apply_patch(Json& target, const Json& patch)
+{
+ std::error_code ec;
+ apply_patch(target, patch, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(jsonpatch_error(ec));
+ }
+}
+
+}}
+
+#endif
diff --git a/include/jsoncons_ext/jsonpatch/jsonpatch_error.hpp b/include/jsoncons_ext/jsonpatch/jsonpatch_error.hpp
new file mode 100644
index 0000000..33f8007
--- /dev/null
+++ b/include/jsoncons_ext/jsonpatch/jsonpatch_error.hpp
@@ -0,0 +1,121 @@
+/// Copyright 2017 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_JSONPATCH_JSONPATCH_ERROR_HPP
+#define JSONCONS_JSONPATCH_JSONPATCH_ERROR_HPP
+
+#include <jsoncons/json_exception.hpp>
+#include <system_error>
+
+namespace jsoncons { namespace jsonpatch {
+
+ enum class jsonpatch_errc
+ {
+ success = 0,
+ invalid_patch = 1,
+ test_failed,
+ add_failed,
+ remove_failed,
+ replace_failed,
+ move_failed,
+ copy_failed
+
+ };
+
+ class jsonpatch_error_category_impl
+ : public std::error_category
+ {
+ public:
+ const char* name() const noexcept override
+ {
+ return "jsoncons/jsonpatch";
+ }
+ std::string message(int ev) const override
+ {
+ switch (static_cast<jsonpatch_errc>(ev))
+ {
+ case jsonpatch_errc::invalid_patch:
+ return "Invalid JSON Patch document";
+ case jsonpatch_errc::test_failed:
+ return "JSON Patch test operation failed";
+ case jsonpatch_errc::add_failed:
+ return "JSON Patch add operation failed";
+ case jsonpatch_errc::remove_failed:
+ return "JSON Patch remove operation failed";
+ case jsonpatch_errc::replace_failed:
+ return "JSON Patch replace operation failed";
+ case jsonpatch_errc::move_failed:
+ return "JSON Patch move operation failed";
+ case jsonpatch_errc::copy_failed:
+ return "JSON Patch copy operation failed";
+ default:
+ return "Unknown JSON Patch error";
+ }
+ }
+ };
+
+ inline
+ const std::error_category& jsonpatch_error_category()
+ {
+ static jsonpatch_error_category_impl instance;
+ return instance;
+ }
+
+ inline
+ std::error_code make_error_code(jsonpatch_errc result)
+ {
+ return std::error_code(static_cast<int>(result),jsonpatch_error_category());
+ }
+
+} // jsonpatch
+} // jsoncons
+
+namespace std {
+ template<>
+ struct is_error_code_enum<jsoncons::jsonpatch::jsonpatch_errc> : public true_type
+ {
+ };
+}
+
+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:
+ jsonpatch_error(const std::error_code& ec)
+ : std::system_error(ec)
+ {
+ }
+
+ jsonpatch_error(const jsonpatch_error& other) = default;
+
+ jsonpatch_error(jsonpatch_error&& other) = default;
+
+ jsonpatch_error& operator=(const jsonpatch_error& e) = default;
+ jsonpatch_error& operator=(jsonpatch_error&& e) = default;
+
+ const char* what() const noexcept override
+ {
+ return std::system_error::what();
+ }
+ };
+} // jsonpatch
+} // jsoncons
+
+#endif
diff --git a/include/jsoncons_ext/jsonpath/expression.hpp b/include/jsoncons_ext/jsonpath/expression.hpp
new file mode 100644
index 0000000..f655f2d
--- /dev/null
+++ b/include/jsoncons_ext/jsonpath/expression.hpp
@@ -0,0 +1,3329 @@
+// Copyright 2021 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_JSONPATH_EXPRESSION_HPP
+#define JSONCONS_JSONPATH_EXPRESSION_HPP
+
+#include <string> // std::basic_string
+#include <vector> // std::vector
+#include <unordered_map> // std::unordered_map
+#include <unordered_set> // std::unordered_set
+#include <limits> // std::numeric_limits
+#include <set> // std::set
+#include <utility> // std::move
+#if defined(JSONCONS_HAS_STD_REGEX)
+#include <regex>
+#endif
+#include <jsoncons/json_type.hpp>
+#include <jsoncons_ext/jsonpath/json_location.hpp>
+#include <jsoncons_ext/jsonpath/jsonpath_error.hpp>
+
+namespace jsoncons {
+namespace jsonpath {
+
+ struct reference_arg_t
+ {
+ explicit reference_arg_t() = default;
+ };
+ constexpr reference_arg_t reference_arg{};
+
+ struct const_reference_arg_t
+ {
+ explicit const_reference_arg_t() = default;
+ };
+ constexpr const_reference_arg_t const_reference_arg{};
+
+ struct literal_arg_t
+ {
+ explicit literal_arg_t() = default;
+ };
+ constexpr literal_arg_t literal_arg{};
+
+ struct end_of_expression_arg_t
+ {
+ explicit end_of_expression_arg_t() = default;
+ };
+ constexpr end_of_expression_arg_t end_of_expression_arg{};
+
+ struct separator_arg_t
+ {
+ explicit separator_arg_t() = default;
+ };
+ constexpr separator_arg_t separator_arg{};
+
+ struct lparen_arg_t
+ {
+ explicit lparen_arg_t() = default;
+ };
+ constexpr lparen_arg_t lparen_arg{};
+
+ struct rparen_arg_t
+ {
+ explicit rparen_arg_t() = default;
+ };
+ constexpr rparen_arg_t rparen_arg{};
+
+ struct begin_union_arg_t
+ {
+ explicit begin_union_arg_t() = default;
+ };
+ constexpr begin_union_arg_t begin_union_arg{};
+
+ struct end_union_arg_t
+ {
+ explicit end_union_arg_t() = default;
+ };
+ constexpr end_union_arg_t end_union_arg{};
+
+ struct begin_filter_arg_t
+ {
+ explicit begin_filter_arg_t() = default;
+ };
+ constexpr begin_filter_arg_t begin_filter_arg{};
+
+ struct end_filter_arg_t
+ {
+ explicit end_filter_arg_t() = default;
+ };
+ constexpr end_filter_arg_t end_filter_arg{};
+
+ struct begin_expression_arg_t
+ {
+ explicit begin_expression_arg_t() = default;
+ };
+ constexpr begin_expression_arg_t begin_expression_arg{};
+
+ struct end_index_expression_arg_t
+ {
+ explicit end_index_expression_arg_t() = default;
+ };
+ constexpr end_index_expression_arg_t end_index_expression_arg{};
+
+ struct end_argument_expression_arg_t
+ {
+ explicit end_argument_expression_arg_t() = default;
+ };
+ constexpr end_argument_expression_arg_t end_argument_expression_arg{};
+
+ struct current_node_arg_t
+ {
+ explicit current_node_arg_t() = default;
+ };
+ constexpr current_node_arg_t current_node_arg{};
+
+ struct root_node_arg_t
+ {
+ explicit root_node_arg_t() = default;
+ };
+ constexpr root_node_arg_t root_node_arg{};
+
+ struct end_function_arg_t
+ {
+ explicit end_function_arg_t() = default;
+ };
+ constexpr end_function_arg_t end_function_arg{};
+
+ struct argument_arg_t
+ {
+ explicit argument_arg_t() = default;
+ };
+ constexpr argument_arg_t argument_arg{};
+
+ enum class result_options {value=0, nodups=1, sort=2, path=4};
+
+ using result_type = result_options;
+
+ inline result_options operator~(result_options a)
+ {
+ return static_cast<result_options>(~static_cast<unsigned int>(a));
+ }
+
+ inline result_options operator&(result_options a, result_options b)
+ {
+ return static_cast<result_options>(static_cast<unsigned int>(a) & static_cast<unsigned int>(b));
+ }
+
+ inline result_options operator^(result_options a, result_options b)
+ {
+ return static_cast<result_options>(static_cast<unsigned int>(a) ^ static_cast<unsigned int>(b));
+ }
+
+ inline result_options operator|(result_options a, result_options b)
+ {
+ return static_cast<result_options>(static_cast<unsigned int>(a) | static_cast<unsigned int>(b));
+ }
+
+ inline result_options operator&=(result_options& a, result_options b)
+ {
+ a = a & b;
+ return a;
+ }
+
+ inline result_options operator^=(result_options& a, result_options b)
+ {
+ a = a ^ b;
+ return a;
+ }
+
+ inline result_options operator|=(result_options& a, result_options b)
+ {
+ a = a | b;
+ return a;
+ }
+
+ template <class Json>
+ class parameter;
+
+ template <class Json,class JsonReference>
+ class value_or_pointer
+ {
+ public:
+ friend class parameter<Json>;
+ using value_type = Json;
+ using reference = JsonReference;
+ using pointer = typename std::conditional<std::is_const<typename std::remove_reference<reference>::type>::value,typename Json::const_pointer,typename Json::pointer>::type;
+ private:
+ bool is_value_;
+ union
+ {
+ value_type val_;
+ pointer ptr_;
+ };
+ public:
+ value_or_pointer(value_type&& val)
+ : is_value_(true), val_(std::move(val))
+ {
+ }
+
+ value_or_pointer(pointer ptr)
+ : is_value_(false), ptr_(std::move(ptr))
+ {
+ }
+
+ value_or_pointer(value_or_pointer&& other) noexcept
+ : is_value_(other.is_value_)
+ {
+ if (is_value_)
+ {
+ new(&val_)value_type(std::move(other.val_));
+ }
+ else
+ {
+ ptr_ = other.ptr_;
+ }
+ }
+
+ ~value_or_pointer() noexcept
+ {
+ if (is_value_)
+ {
+ val_.~value_type();
+ }
+ }
+
+ value_or_pointer& operator=(value_or_pointer&& other) noexcept
+ {
+ if (is_value_)
+ {
+ val_.~value_type();
+ }
+ is_value_ = other.is_value_;
+
+ if (is_value_)
+ {
+ new(&val_)value_type(std::move(other.val_));
+ }
+ else
+ {
+ ptr_ = other.ptr_;
+ }
+ return *this;
+ }
+
+ reference value()
+ {
+ return is_value_ ? val_ : *ptr_;
+ }
+
+ pointer ptr()
+ {
+ return is_value_ ? &val_ : ptr_;
+ }
+ };
+
+ template <class Json>
+ class parameter
+ {
+ using value_type = Json;
+ using reference = const Json&;
+ using pointer = const Json*;
+ private:
+ value_or_pointer<Json,reference> data_;
+ public:
+ template <class JsonReference>
+ parameter(value_or_pointer<Json,JsonReference>&& data) noexcept
+ : data_(nullptr)
+ {
+ data_.is_value_ = data.is_value_;
+ if (data.is_value_)
+ {
+ data_.val_ = std::move(data.val_);
+ }
+ else
+ {
+ data_.ptr_ = data.ptr_;
+ }
+ }
+
+ parameter(parameter&& other) noexcept = default;
+
+ parameter& operator=(parameter&& other) noexcept = default;
+
+ const Json& value() const
+ {
+ return data_.is_value_ ? data_.val_ : *data_.ptr_;
+ }
+ };
+
+ template <class Json>
+ class custom_function
+ {
+ public:
+ using value_type = Json;
+ using char_type = typename Json::char_type;
+ using parameter_type = parameter<Json>;
+ using function_type = std::function<value_type(jsoncons::span<const parameter_type>, std::error_code& ec)>;
+ using string_type = std::basic_string<char_type>;
+
+ string_type function_name_;
+ optional<std::size_t> arity_;
+ function_type f_;
+
+ custom_function(const string_type& function_name,
+ const optional<std::size_t>& arity,
+ const function_type& f)
+ : function_name_(function_name),
+ arity_(arity),
+ f_(f)
+ {
+ }
+
+ custom_function(string_type&& function_name,
+ optional<std::size_t>&& arity,
+ function_type&& f)
+ : function_name_(std::move(function_name)),
+ arity_(std::move(arity)),
+ f_(std::move(f))
+ {
+ }
+
+ custom_function(const custom_function&) = default;
+
+ custom_function(custom_function&&) = default;
+
+ const string_type& name() const
+ {
+ return function_name_;
+ }
+
+ optional<std::size_t> arity() const
+ {
+ return arity_;
+ }
+
+ const function_type& function() const
+ {
+ return f_;
+ }
+ };
+
+ template <class Json>
+ class custom_functions
+ {
+ using char_type = typename Json::char_type;
+ using string_type = std::basic_string<char_type>;
+ using value_type = Json;
+ using parameter_type = parameter<Json>;
+ using function_type = std::function<value_type(jsoncons::span<const parameter_type>, std::error_code& ec)>;
+ using const_iterator = typename std::vector<custom_function<Json>>::const_iterator;
+
+ std::vector<custom_function<Json>> functions_;
+ public:
+ void register_function(const string_type& name,
+ jsoncons::optional<std::size_t> arity,
+ const function_type& f)
+ {
+ functions_.emplace_back(name, arity, f);
+ }
+
+ const_iterator begin() const
+ {
+ return functions_.begin();
+ }
+
+ const_iterator end() const
+ {
+ return functions_.end();
+ }
+ };
+
+namespace detail {
+
+ template <class Json,class JsonReference>
+ class dynamic_resources;
+
+ template <class Json,class JsonReference>
+ struct unary_operator
+ {
+ std::size_t precedence_level_;
+ bool is_right_associative_;
+
+ unary_operator(std::size_t precedence_level,
+ bool is_right_associative)
+ : precedence_level_(precedence_level),
+ is_right_associative_(is_right_associative)
+ {
+ }
+
+ virtual ~unary_operator() = default;
+
+ std::size_t precedence_level() const
+ {
+ return precedence_level_;
+ }
+ bool is_right_associative() const
+ {
+ return is_right_associative_;
+ }
+
+ virtual Json evaluate(JsonReference,
+ std::error_code&) const = 0;
+ };
+
+ template <class Json>
+ bool is_false(const Json& val)
+ {
+ return ((val.is_array() && val.empty()) ||
+ (val.is_object() && val.empty()) ||
+ (val.is_string() && val.as_string_view().empty()) ||
+ (val.is_bool() && !val.as_bool()) ||
+ val.is_null());
+ }
+
+ template <class Json>
+ bool is_true(const Json& val)
+ {
+ return !is_false(val);
+ }
+
+ template <class Json,class JsonReference>
+ class unary_not_operator final : public unary_operator<Json,JsonReference>
+ {
+ public:
+ unary_not_operator()
+ : unary_operator<Json,JsonReference>(1, true)
+ {}
+
+ Json evaluate(JsonReference val,
+ std::error_code&) const override
+ {
+ return is_false(val) ? Json(true) : Json(false);
+ }
+ };
+
+ template <class Json,class JsonReference>
+ class unary_minus_operator final : public unary_operator<Json,JsonReference>
+ {
+ public:
+ unary_minus_operator()
+ : unary_operator<Json,JsonReference>(1, true)
+ {}
+
+ Json evaluate(JsonReference val,
+ std::error_code&) const override
+ {
+ if (val.is_int64())
+ {
+ return Json(-val.template as<int64_t>());
+ }
+ else if (val.is_double())
+ {
+ return Json(-val.as_double());
+ }
+ else
+ {
+ return Json::null();
+ }
+ }
+ };
+
+ template <class Json,class JsonReference>
+ class regex_operator final : public unary_operator<Json,JsonReference>
+ {
+ using char_type = typename Json::char_type;
+ using string_type = std::basic_string<char_type>;
+ std::basic_regex<char_type> pattern_;
+ public:
+ regex_operator(std::basic_regex<char_type>&& pattern)
+ : unary_operator<Json,JsonReference>(2, true),
+ pattern_(std::move(pattern))
+ {
+ }
+
+ regex_operator(regex_operator&&) = default;
+ regex_operator& operator=(regex_operator&&) = default;
+
+ Json evaluate(JsonReference val,
+ std::error_code&) const override
+ {
+ if (!val.is_string())
+ {
+ return Json::null();
+ }
+ return std::regex_search(val.as_string(), pattern_) ? Json(true) : Json(false);
+ }
+ };
+
+ template <class Json,class JsonReference>
+ struct binary_operator
+ {
+ std::size_t precedence_level_;
+ bool is_right_associative_;
+
+ binary_operator(std::size_t precedence_level,
+ bool is_right_associative = false)
+ : precedence_level_(precedence_level),
+ is_right_associative_(is_right_associative)
+ {
+ }
+
+ std::size_t precedence_level() const
+ {
+ return precedence_level_;
+ }
+ bool is_right_associative() const
+ {
+ return is_right_associative_;
+ }
+
+ virtual Json evaluate(JsonReference,
+ JsonReference,
+
+ std::error_code&) const = 0;
+
+ virtual std::string to_string(int = 0) const
+ {
+ return "binary operator";
+ }
+
+ protected:
+ ~binary_operator() = default;
+ };
+
+ // Implementations
+
+ template <class Json,class JsonReference>
+ class or_operator final : public binary_operator<Json,JsonReference>
+ {
+ public:
+ or_operator()
+ : binary_operator<Json,JsonReference>(9)
+ {
+ }
+
+ Json evaluate(JsonReference lhs, JsonReference rhs, std::error_code&) const override
+ {
+ if (lhs.is_null() && rhs.is_null())
+ {
+ return Json::null();
+ }
+ if (!is_false(lhs))
+ {
+ return lhs;
+ }
+ else
+ {
+ return rhs;
+ }
+ }
+ std::string to_string(int level = 0) const override
+ {
+ std::string s;
+ if (level > 0)
+ {
+ //s.append("\n");
+ s.append(level*2, ' ');
+ }
+ s.append("or operator");
+ return s;
+ }
+ };
+
+ template <class Json,class JsonReference>
+ class and_operator final : public binary_operator<Json,JsonReference>
+ {
+ public:
+ and_operator()
+ : binary_operator<Json,JsonReference>(8)
+ {
+ }
+
+ Json evaluate(JsonReference lhs, JsonReference rhs, std::error_code&) const override
+ {
+ if (is_true(lhs))
+ {
+ return rhs;
+ }
+ else
+ {
+ return lhs;
+ }
+ }
+
+ std::string to_string(int level = 0) const override
+ {
+ std::string s;
+ if (level > 0)
+ {
+ s.append("\n");
+ s.append(level*2, ' ');
+ }
+ s.append("and operator");
+ return s;
+ }
+ };
+
+ template <class Json,class JsonReference>
+ class eq_operator final : public binary_operator<Json,JsonReference>
+ {
+ public:
+ eq_operator()
+ : binary_operator<Json,JsonReference>(6)
+ {
+ }
+
+ Json evaluate(JsonReference lhs, JsonReference rhs, std::error_code&) const override
+ {
+ return lhs == rhs ? Json(true) : Json(false);
+ }
+
+ std::string to_string(int level = 0) const override
+ {
+ std::string s;
+ if (level > 0)
+ {
+ s.append("\n");
+ s.append(level*2, ' ');
+ }
+ s.append("equal operator");
+ return s;
+ }
+ };
+
+ template <class Json,class JsonReference>
+ class ne_operator final : public binary_operator<Json,JsonReference>
+ {
+ public:
+ ne_operator()
+ : binary_operator<Json,JsonReference>(6)
+ {
+ }
+
+ Json evaluate(JsonReference lhs, JsonReference rhs, std::error_code&) const override
+ {
+ return lhs != rhs ? Json(true) : Json(false);
+ }
+
+ std::string to_string(int level = 0) const override
+ {
+ std::string s;
+ if (level > 0)
+ {
+ s.append("\n");
+ s.append(level*2, ' ');
+ }
+ s.append("not equal operator");
+ return s;
+ }
+ };
+
+ template <class Json,class JsonReference>
+ class lt_operator final : public binary_operator<Json,JsonReference>
+ {
+ public:
+ lt_operator()
+ : binary_operator<Json,JsonReference>(5)
+ {
+ }
+
+ Json evaluate(JsonReference lhs, JsonReference rhs, std::error_code&) const override
+ {
+ if (lhs.is_number() && rhs.is_number())
+ {
+ return lhs < rhs ? Json(true) : Json(false);
+ }
+ else if (lhs.is_string() && rhs.is_string())
+ {
+ return lhs < rhs ? Json(true) : Json(false);
+ }
+ return Json::null();
+ }
+
+ std::string to_string(int level = 0) const override
+ {
+ std::string s;
+ if (level > 0)
+ {
+ s.append("\n");
+ s.append(level*2, ' ');
+ }
+ s.append("less than operator");
+ return s;
+ }
+ };
+
+ template <class Json,class JsonReference>
+ class lte_operator final : public binary_operator<Json,JsonReference>
+ {
+ public:
+ lte_operator()
+ : binary_operator<Json,JsonReference>(5)
+ {
+ }
+
+ Json evaluate(JsonReference lhs, JsonReference rhs, std::error_code&) const override
+ {
+ if (lhs.is_number() && rhs.is_number())
+ {
+ return lhs <= rhs ? Json(true) : Json(false);
+ }
+ else if (lhs.is_string() && rhs.is_string())
+ {
+ return lhs <= rhs ? Json(true) : Json(false);
+ }
+ return Json::null();
+ }
+
+ std::string to_string(int level = 0) const override
+ {
+ std::string s;
+ if (level > 0)
+ {
+ s.append("\n");
+ s.append(level*2, ' ');
+ }
+ s.append("less than or equal operator");
+ return s;
+ }
+ };
+
+ template <class Json,class JsonReference>
+ class gt_operator final : public binary_operator<Json,JsonReference>
+ {
+ public:
+ gt_operator()
+ : binary_operator<Json,JsonReference>(5)
+ {
+ }
+
+ Json evaluate(JsonReference lhs, JsonReference rhs, std::error_code&) const override
+ {
+ //std::cout << "operator> lhs: " << lhs << ", rhs: " << rhs << "\n";
+
+ if (lhs.is_number() && rhs.is_number())
+ {
+ return lhs > rhs ? Json(true) : Json(false);
+ }
+ else if (lhs.is_string() && rhs.is_string())
+ {
+ return lhs > rhs ? Json(true) : Json(false);
+ }
+ return Json::null();
+ }
+
+ std::string to_string(int level = 0) const override
+ {
+ std::string s;
+ if (level > 0)
+ {
+ s.append("\n");
+ s.append(level*2, ' ');
+ }
+ s.append("greater than operator");
+ return s;
+ }
+ };
+
+ template <class Json,class JsonReference>
+ class gte_operator final : public binary_operator<Json,JsonReference>
+ {
+ public:
+ gte_operator()
+ : binary_operator<Json,JsonReference>(5)
+ {
+ }
+
+ Json evaluate(JsonReference lhs, JsonReference rhs, std::error_code&) const override
+ {
+ if (lhs.is_number() && rhs.is_number())
+ {
+ return lhs >= rhs ? Json(true) : Json(false);
+ }
+ else if (lhs.is_string() && rhs.is_string())
+ {
+ return lhs >= rhs ? Json(true) : Json(false);
+ }
+ return Json::null();
+ }
+
+ std::string to_string(int level = 0) const override
+ {
+ std::string s;
+ if (level > 0)
+ {
+ s.append("\n");
+ s.append(level*2, ' ');
+ }
+ s.append("greater than or equal operator");
+ return s;
+ }
+ };
+
+ template <class Json,class JsonReference>
+ class plus_operator final : public binary_operator<Json,JsonReference>
+ {
+ public:
+ plus_operator()
+ : binary_operator<Json,JsonReference>(4)
+ {
+ }
+
+ Json evaluate(JsonReference lhs, JsonReference rhs, std::error_code&) const override
+ {
+ if (!(lhs.is_number() && rhs.is_number()))
+ {
+ return Json::null();
+ }
+ else if (lhs.is_int64() && rhs.is_int64())
+ {
+ return Json(((lhs.template as<int64_t>() + rhs.template as<int64_t>())));
+ }
+ else if (lhs.is_uint64() && rhs.is_uint64())
+ {
+ return Json((lhs.template as<uint64_t>() + rhs.template as<uint64_t>()));
+ }
+ else
+ {
+ return Json((lhs.as_double() + rhs.as_double()));
+ }
+ }
+
+ std::string to_string(int level = 0) const override
+ {
+ std::string s;
+ if (level > 0)
+ {
+ s.append("\n");
+ s.append(level*2, ' ');
+ }
+ s.append("plus operator");
+ return s;
+ }
+ };
+
+ template <class Json,class JsonReference>
+ class minus_operator final : public binary_operator<Json,JsonReference>
+ {
+ public:
+ minus_operator()
+ : binary_operator<Json,JsonReference>(4)
+ {
+ }
+
+ Json evaluate(JsonReference lhs, JsonReference rhs, std::error_code&) const override
+ {
+ if (!(lhs.is_number() && rhs.is_number()))
+ {
+ return Json::null();
+ }
+ else if (lhs.is_int64() && rhs.is_int64())
+ {
+ return Json(((lhs.template as<int64_t>() - rhs.template as<int64_t>())));
+ }
+ else if (lhs.is_uint64() && rhs.is_uint64())
+ {
+ return Json((lhs.template as<uint64_t>() - rhs.template as<uint64_t>()));
+ }
+ else
+ {
+ return Json((lhs.as_double() - rhs.as_double()));
+ }
+ }
+
+ std::string to_string(int level = 0) const override
+ {
+ std::string s;
+ if (level > 0)
+ {
+ s.append("\n");
+ s.append(level*2, ' ');
+ }
+ s.append("minus operator");
+ return s;
+ }
+ };
+
+ template <class Json,class JsonReference>
+ class mult_operator final : public binary_operator<Json,JsonReference>
+ {
+ public:
+ mult_operator()
+ : binary_operator<Json,JsonReference>(3)
+ {
+ }
+
+ Json evaluate(JsonReference lhs, JsonReference rhs, std::error_code&) const override
+ {
+ if (!(lhs.is_number() && rhs.is_number()))
+ {
+ return Json::null();
+ }
+ else if (lhs.is_int64() && rhs.is_int64())
+ {
+ return Json(((lhs.template as<int64_t>() * rhs.template as<int64_t>())));
+ }
+ else if (lhs.is_uint64() && rhs.is_uint64())
+ {
+ return Json((lhs.template as<uint64_t>() * rhs.template as<uint64_t>()));
+ }
+ else
+ {
+ return Json((lhs.as_double() * rhs.as_double()));
+ }
+ }
+
+ std::string to_string(int level = 0) const override
+ {
+ std::string s;
+ if (level > 0)
+ {
+ s.append("\n");
+ s.append(level*2, ' ');
+ }
+ s.append("multiply operator");
+ return s;
+ }
+ };
+
+ template <class Json,class JsonReference>
+ class div_operator final : public binary_operator<Json,JsonReference>
+ {
+ public:
+ div_operator()
+ : binary_operator<Json,JsonReference>(3)
+ {
+ }
+
+ Json evaluate(JsonReference lhs, JsonReference rhs, std::error_code&) const override
+ {
+ //std::cout << "operator/ lhs: " << lhs << ", rhs: " << rhs << "\n";
+
+ if (!(lhs.is_number() && rhs.is_number()))
+ {
+ return Json::null();
+ }
+ else if (lhs.is_int64() && rhs.is_int64())
+ {
+ return Json(((lhs.template as<int64_t>() / rhs.template as<int64_t>())));
+ }
+ else if (lhs.is_uint64() && rhs.is_uint64())
+ {
+ return Json((lhs.template as<uint64_t>() / rhs.template as<uint64_t>()));
+ }
+ else
+ {
+ return Json((lhs.as_double() / rhs.as_double()));
+ }
+ }
+
+ std::string to_string(int level = 0) const override
+ {
+ std::string s;
+ if (level > 0)
+ {
+ s.append("\n");
+ s.append(level*2, ' ');
+ }
+ s.append("divide operator");
+ return s;
+ }
+ };
+
+ template <class Json,class JsonReference>
+ class modulus_operator final : public binary_operator<Json,JsonReference>
+ {
+ public:
+ modulus_operator()
+ : binary_operator<Json,JsonReference>(3)
+ {
+ }
+
+ Json evaluate(JsonReference lhs, JsonReference rhs, std::error_code&) const override
+ {
+ //std::cout << "operator/ lhs: " << lhs << ", rhs: " << rhs << "\n";
+
+ if (!(lhs.is_number() && rhs.is_number()))
+ {
+ return Json::null();
+ }
+ else if (lhs.is_int64() && rhs.is_int64())
+ {
+ return Json(((lhs.template as<int64_t>() % rhs.template as<int64_t>())));
+ }
+ else if (lhs.is_uint64() && rhs.is_uint64())
+ {
+ return Json((lhs.template as<uint64_t>() % rhs.template as<uint64_t>()));
+ }
+ else
+ {
+ return Json(fmod(lhs.as_double(), rhs.as_double()));
+ }
+ }
+
+ std::string to_string(int level = 0) const override
+ {
+ std::string s;
+ if (level > 0)
+ {
+ s.append("\n");
+ s.append(level*2, ' ');
+ }
+ s.append("modulus operator");
+ return s;
+ }
+ };
+
+ // function_base
+ template <class Json>
+ class function_base
+ {
+ jsoncons::optional<std::size_t> arg_count_;
+ public:
+ using value_type = Json;
+ using parameter_type = parameter<Json>;
+
+ function_base(jsoncons::optional<std::size_t> arg_count)
+ : arg_count_(arg_count)
+ {
+ }
+
+ virtual ~function_base() noexcept = default;
+
+ jsoncons::optional<std::size_t> arity() const
+ {
+ return arg_count_;
+ }
+
+ virtual value_type evaluate(const std::vector<parameter_type>& args,
+ std::error_code& ec) const = 0;
+
+ virtual std::string to_string(int level = 0) const
+ {
+ std::string s;
+ if (level > 0)
+ {
+ s.append("\n");
+ s.append(level*2, ' ');
+ }
+ s.append("function");
+ return s;
+ }
+ };
+
+ template <class Json>
+ class decorator_function : public function_base<Json>
+ {
+ public:
+ using value_type = Json;
+ using parameter_type = parameter<Json>;
+ using string_view_type = typename Json::string_view_type;
+ using function_type = std::function<value_type(jsoncons::span<const parameter_type>, std::error_code& ec)>;
+ private:
+ function_type f_;
+ public:
+ decorator_function(jsoncons::optional<std::size_t> arity,
+ const function_type& f)
+ : function_base<Json>(arity), f_(f)
+ {
+ }
+
+ value_type evaluate(const std::vector<parameter_type>& args,
+ std::error_code& ec) const override
+ {
+ return f_(args, ec);
+ }
+ };
+
+ template <class Json>
+ class contains_function : public function_base<Json>
+ {
+ public:
+ using value_type = Json;
+ using parameter_type = parameter<Json>;
+ using string_view_type = typename Json::string_view_type;
+
+ contains_function()
+ : function_base<Json>(2)
+ {
+ }
+
+ value_type evaluate(const std::vector<parameter_type>& args,
+ std::error_code& ec) const override
+ {
+ if (args.size() != *this->arity())
+ {
+ ec = jsonpath_errc::invalid_arity;
+ return value_type::null();
+ }
+
+ auto arg0= args[0].value();
+ auto arg1= args[1].value();
+
+ switch (arg0.type())
+ {
+ case json_type::array_value:
+ for (auto& j : arg0.array_range())
+ {
+ if (j == arg1)
+ {
+ return value_type(true);
+ }
+ }
+ return value_type(false);
+ case json_type::string_value:
+ {
+ if (!arg1.is_string())
+ {
+ ec = jsonpath_errc::invalid_type;
+ return value_type::null();
+ }
+ auto sv0 = arg0.template as<string_view_type>();
+ auto sv1 = arg1.template as<string_view_type>();
+ return sv0.find(sv1) != string_view_type::npos ? value_type(true) : value_type(false);
+ }
+ default:
+ {
+ ec = jsonpath_errc::invalid_type;
+ return value_type::null();
+ }
+ }
+ }
+
+ std::string to_string(int level = 0) const override
+ {
+ std::string s;
+ if (level > 0)
+ {
+ s.append("\n");
+ s.append(level*2, ' ');
+ }
+ s.append("contains function");
+ return s;
+ }
+ };
+
+ template <class Json>
+ class ends_with_function : public function_base<Json>
+ {
+ public:
+ using value_type = Json;
+ using parameter_type = parameter<Json>;
+ using string_view_type = typename Json::string_view_type;
+
+ ends_with_function()
+ : function_base<Json>(2)
+ {
+ }
+
+ value_type evaluate(const std::vector<parameter_type>& args,
+ std::error_code& ec) const override
+ {
+ if (args.size() != *this->arity())
+ {
+ ec = jsonpath_errc::invalid_arity;
+ return value_type::null();
+ }
+
+ auto arg0= args[0].value();
+ if (!arg0.is_string())
+ {
+ ec = jsonpath_errc::invalid_type;
+ return value_type::null();
+ }
+
+ auto arg1= args[1].value();
+ if (!arg1.is_string())
+ {
+ ec = jsonpath_errc::invalid_type;
+ return value_type::null();
+ }
+
+ auto sv0 = arg0.template as<string_view_type>();
+ auto sv1 = arg1.template as<string_view_type>();
+
+ if (sv1.length() <= sv0.length() && sv1 == sv0.substr(sv0.length() - sv1.length()))
+ {
+ return value_type(true);
+ }
+ else
+ {
+ return value_type(false);
+ }
+ }
+
+ std::string to_string(int level = 0) const override
+ {
+ std::string s;
+ if (level > 0)
+ {
+ s.append("\n");
+ s.append(level*2, ' ');
+ }
+ s.append("ends_with function");
+ return s;
+ }
+ };
+
+ template <class Json>
+ class starts_with_function : public function_base<Json>
+ {
+ public:
+ using value_type = Json;
+ using parameter_type = parameter<Json>;
+ using string_view_type = typename Json::string_view_type;
+
+ starts_with_function()
+ : function_base<Json>(2)
+ {
+ }
+
+ value_type evaluate(const std::vector<parameter_type>& args,
+ std::error_code& ec) const override
+ {
+ if (args.size() != *this->arity())
+ {
+ ec = jsonpath_errc::invalid_arity;
+ return value_type::null();
+ }
+
+ auto arg0= args[0].value();
+ if (!arg0.is_string())
+ {
+ ec = jsonpath_errc::invalid_type;
+ return value_type::null();
+ }
+
+ auto arg1= args[1].value();
+ if (!arg1.is_string())
+ {
+ ec = jsonpath_errc::invalid_type;
+ return value_type::null();
+ }
+
+ auto sv0 = arg0.template as<string_view_type>();
+ auto sv1 = arg1.template as<string_view_type>();
+
+ if (sv1.length() <= sv0.length() && sv1 == sv0.substr(0, sv1.length()))
+ {
+ return value_type(true);
+ }
+ else
+ {
+ return value_type(false);
+ }
+ }
+
+ std::string to_string(int level = 0) const override
+ {
+ std::string s;
+ if (level > 0)
+ {
+ s.append("\n");
+ s.append(level*2, ' ');
+ }
+ s.append("starts_with function");
+ return s;
+ }
+ };
+
+ template <class Json>
+ class sum_function : public function_base<Json>
+ {
+ public:
+ using value_type = Json;
+ using parameter_type = parameter<Json>;
+
+ sum_function()
+ : function_base<Json>(1)
+ {
+ }
+
+ value_type evaluate(const std::vector<parameter_type>& args,
+ std::error_code& ec) const override
+ {
+ if (args.size() != *this->arity())
+ {
+ ec = jsonpath_errc::invalid_arity;
+ return value_type::null();
+ }
+
+ auto arg0= args[0].value();
+ if (!arg0.is_array())
+ {
+ //std::cout << "arg: " << arg0 << "\n";
+ ec = jsonpath_errc::invalid_type;
+ return value_type::null();
+ }
+ //std::cout << "sum function arg: " << arg0 << "\n";
+
+ double sum = 0;
+ for (auto& j : arg0.array_range())
+ {
+ if (!j.is_number())
+ {
+ ec = jsonpath_errc::invalid_type;
+ return value_type::null();
+ }
+ sum += j.template as<double>();
+ }
+
+ return value_type(sum);
+ }
+
+ std::string to_string(int level = 0) const override
+ {
+ std::string s;
+ if (level > 0)
+ {
+ s.append("\n");
+ s.append(level*2, ' ');
+ }
+ s.append("sum function");
+ return s;
+ }
+ };
+
+#if defined(JSONCONS_HAS_STD_REGEX)
+
+ template <class Json>
+ class tokenize_function : public function_base<Json>
+ {
+ public:
+ using value_type = Json;
+ using parameter_type = parameter<Json>;
+ using char_type = typename Json::char_type;
+ using string_type = std::basic_string<char_type>;
+
+ tokenize_function()
+ : function_base<Json>(2)
+ {
+ }
+
+ value_type evaluate(const std::vector<parameter_type>& args,
+ std::error_code& ec) const override
+ {
+ if (args.size() != *this->arity())
+ {
+ ec = jsonpath_errc::invalid_arity;
+ return value_type::null();
+ }
+
+ if (!args[0].value().is_string() || !args[1].value().is_string())
+ {
+ //std::cout << "arg: " << arg0 << "\n";
+ ec = jsonpath_errc::invalid_type;
+ return value_type::null();
+ }
+ auto arg0 = args[0].value().template as<string_type>();
+ auto arg1 = args[1].value().template as<string_type>();
+
+ std::regex::flag_type options = std::regex_constants::ECMAScript;
+ std::basic_regex<char_type> pieces_regex(arg1, options);
+
+ std::regex_token_iterator<typename string_type::const_iterator> rit ( arg0.begin(), arg0.end(), pieces_regex, -1);
+ std::regex_token_iterator<typename string_type::const_iterator> rend;
+
+ value_type j(json_array_arg);
+ while (rit != rend)
+ {
+ j.emplace_back(rit->str());
+ ++rit;
+ }
+ return j;
+ }
+
+ std::string to_string(int level = 0) const override
+ {
+ std::string s;
+ if (level > 0)
+ {
+ s.append("\n");
+ s.append(level*2, ' ');
+ }
+ s.append("tokenize function");
+ return s;
+ }
+ };
+
+#endif // defined(JSONCONS_HAS_STD_REGEX)
+
+ template <class Json>
+ class ceil_function : public function_base<Json>
+ {
+ public:
+ using value_type = Json;
+ using parameter_type = parameter<Json>;
+
+ ceil_function()
+ : function_base<Json>(1)
+ {
+ }
+
+ value_type evaluate(const std::vector<parameter_type>& args,
+ std::error_code& ec) const override
+ {
+ if (args.size() != *this->arity())
+ {
+ ec = jsonpath_errc::invalid_arity;
+ return value_type::null();
+ }
+
+ auto arg0= args[0].value();
+ switch (arg0.type())
+ {
+ case json_type::uint64_value:
+ case json_type::int64_value:
+ {
+ return value_type(arg0.template as<double>());
+ }
+ case json_type::double_value:
+ {
+ return value_type(std::ceil(arg0.template as<double>()));
+ }
+ default:
+ ec = jsonpath_errc::invalid_type;
+ return value_type::null();
+ }
+ }
+
+ std::string to_string(int level = 0) const override
+ {
+ std::string s;
+ if (level > 0)
+ {
+ s.append("\n");
+ s.append(level*2, ' ');
+ }
+ s.append("ceil function");
+ return s;
+ }
+ };
+
+ template <class Json>
+ class floor_function : public function_base<Json>
+ {
+ public:
+ using value_type = Json;
+ using parameter_type = parameter<Json>;
+
+ floor_function()
+ : function_base<Json>(1)
+ {
+ }
+
+ value_type evaluate(const std::vector<parameter_type>& args,
+ std::error_code& ec) const override
+ {
+ if (args.size() != *this->arity())
+ {
+ ec = jsonpath_errc::invalid_arity;
+ return value_type::null();
+ }
+
+ auto arg0= args[0].value();
+ switch (arg0.type())
+ {
+ case json_type::uint64_value:
+ case json_type::int64_value:
+ {
+ return value_type(arg0.template as<double>());
+ }
+ case json_type::double_value:
+ {
+ return value_type(std::floor(arg0.template as<double>()));
+ }
+ default:
+ ec = jsonpath_errc::invalid_type;
+ return value_type::null();
+ }
+ }
+
+ std::string to_string(int level = 0) const override
+ {
+ std::string s;
+ if (level > 0)
+ {
+ s.append("\n");
+ s.append(level*2, ' ');
+ }
+ s.append("floor function");
+ return s;
+ }
+ };
+
+ template <class Json>
+ class to_number_function : public function_base<Json>
+ {
+ public:
+ using value_type = Json;
+ using parameter_type = parameter<Json>;
+
+ to_number_function()
+ : function_base<Json>(1)
+ {
+ }
+
+ value_type evaluate(const std::vector<parameter_type>& args,
+ std::error_code& ec) const override
+ {
+ if (args.size() != *this->arity())
+ {
+ ec = jsonpath_errc::invalid_arity;
+ return value_type::null();
+ }
+
+ auto arg0= args[0].value();
+ switch (arg0.type())
+ {
+ case json_type::int64_value:
+ case json_type::uint64_value:
+ case json_type::double_value:
+ return arg0;
+ case json_type::string_value:
+ {
+ auto sv = arg0.as_string_view();
+ uint64_t un{0};
+ auto result1 = jsoncons::detail::to_integer(sv.data(), sv.length(), un);
+ if (result1)
+ {
+ return value_type(un);
+ }
+ int64_t sn{0};
+ auto result2 = jsoncons::detail::to_integer(sv.data(), sv.length(), sn);
+ if (result2)
+ {
+ return value_type(sn);
+ }
+ 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);
+ }
+ catch (const std::exception&)
+ {
+ return value_type::null();
+ }
+ }
+ default:
+ ec = jsonpath_errc::invalid_type;
+ return value_type::null();
+ }
+ }
+
+ std::string to_string(int level = 0) const override
+ {
+ std::string s;
+ if (level > 0)
+ {
+ s.append("\n");
+ s.append(level*2, ' ');
+ }
+ s.append("to_number function");
+ return s;
+ }
+ };
+
+ template <class Json>
+ class prod_function : public function_base<Json>
+ {
+ public:
+ using value_type = Json;
+ using parameter_type = parameter<Json>;
+
+ prod_function()
+ : function_base<Json>(1)
+ {
+ }
+
+ value_type evaluate(const std::vector<parameter_type>& args,
+ std::error_code& ec) const override
+ {
+ if (args.size() != *this->arity())
+ {
+ ec = jsonpath_errc::invalid_arity;
+ return value_type::null();
+ }
+
+ auto arg0= args[0].value();
+ if (!arg0.is_array() || arg0.empty())
+ {
+ //std::cout << "arg: " << arg0 << "\n";
+ ec = jsonpath_errc::invalid_type;
+ return value_type::null();
+ }
+ double prod = 1;
+ for (auto& j : arg0.array_range())
+ {
+ if (!j.is_number())
+ {
+ ec = jsonpath_errc::invalid_type;
+ return value_type::null();
+ }
+ prod *= j.template as<double>();
+ }
+
+ return value_type(prod);
+ }
+
+ std::string to_string(int level = 0) const override
+ {
+ std::string s;
+ if (level > 0)
+ {
+ s.append("\n");
+ s.append(level*2, ' ');
+ }
+ s.append("prod function");
+ return s;
+ }
+ };
+
+ template <class Json>
+ class avg_function : public function_base<Json>
+ {
+ public:
+ using value_type = Json;
+ using parameter_type = parameter<Json>;
+
+ avg_function()
+ : function_base<Json>(1)
+ {
+ }
+
+ value_type evaluate(const std::vector<parameter_type>& args,
+ std::error_code& ec) const override
+ {
+ if (args.size() != *this->arity())
+ {
+ ec = jsonpath_errc::invalid_arity;
+ return value_type::null();
+ }
+
+ auto arg0= args[0].value();
+ if (!arg0.is_array())
+ {
+ ec = jsonpath_errc::invalid_type;
+ return value_type::null();
+ }
+ if (arg0.empty())
+ {
+ return value_type::null();
+ }
+ double sum = 0;
+ for (auto& j : arg0.array_range())
+ {
+ if (!j.is_number())
+ {
+ ec = jsonpath_errc::invalid_type;
+ return value_type::null();
+ }
+ sum += j.template as<double>();
+ }
+
+ return value_type(sum / static_cast<double>(arg0.size()));
+ }
+
+ std::string to_string(int level = 0) const override
+ {
+ std::string s;
+ if (level > 0)
+ {
+ s.append("\n");
+ s.append(level*2, ' ');
+ }
+ s.append("to_string function");
+ return s;
+ }
+ };
+
+ template <class Json>
+ class min_function : public function_base<Json>
+ {
+ public:
+ using value_type = Json;
+ using parameter_type = parameter<Json>;
+
+ min_function()
+ : function_base<Json>(1)
+ {
+ }
+
+ value_type evaluate(const std::vector<parameter_type>& args,
+ std::error_code& ec) const override
+ {
+ if (args.size() != *this->arity())
+ {
+ ec = jsonpath_errc::invalid_arity;
+ return value_type::null();
+ }
+
+ auto arg0= args[0].value();
+ if (!arg0.is_array())
+ {
+ //std::cout << "arg: " << arg0 << "\n";
+ ec = jsonpath_errc::invalid_type;
+ return value_type::null();
+ }
+ if (arg0.empty())
+ {
+ return value_type::null();
+ }
+ bool is_number = arg0.at(0).is_number();
+ bool is_string = arg0.at(0).is_string();
+ if (!is_number && !is_string)
+ {
+ ec = jsonpath_errc::invalid_type;
+ return value_type::null();
+ }
+
+ std::size_t index = 0;
+ for (std::size_t i = 1; i < arg0.size(); ++i)
+ {
+ if (!(arg0.at(i).is_number() == is_number && arg0.at(i).is_string() == is_string))
+ {
+ ec = jsonpath_errc::invalid_type;
+ return value_type::null();
+ }
+ if (arg0.at(i) < arg0.at(index))
+ {
+ index = i;
+ }
+ }
+
+ return arg0.at(index);
+ }
+
+ std::string to_string(int level = 0) const override
+ {
+ std::string s;
+ if (level > 0)
+ {
+ s.append("\n");
+ s.append(level*2, ' ');
+ }
+ s.append("min function");
+ return s;
+ }
+ };
+
+ template <class Json>
+ class max_function : public function_base<Json>
+ {
+ public:
+ using value_type = Json;
+ using parameter_type = parameter<Json>;
+
+ max_function()
+ : function_base<Json>(1)
+ {
+ }
+
+ value_type evaluate(const std::vector<parameter_type>& args,
+ std::error_code& ec) const override
+ {
+ if (args.size() != *this->arity())
+ {
+ ec = jsonpath_errc::invalid_arity;
+ return value_type::null();
+ }
+
+ auto arg0= args[0].value();
+ if (!arg0.is_array())
+ {
+ //std::cout << "arg: " << arg0 << "\n";
+ ec = jsonpath_errc::invalid_type;
+ return value_type::null();
+ }
+ if (arg0.empty())
+ {
+ return value_type::null();
+ }
+
+ bool is_number = arg0.at(0).is_number();
+ bool is_string = arg0.at(0).is_string();
+ if (!is_number && !is_string)
+ {
+ ec = jsonpath_errc::invalid_type;
+ return value_type::null();
+ }
+
+ std::size_t index = 0;
+ for (std::size_t i = 1; i < arg0.size(); ++i)
+ {
+ if (!(arg0.at(i).is_number() == is_number && arg0.at(i).is_string() == is_string))
+ {
+ ec = jsonpath_errc::invalid_type;
+ return value_type::null();
+ }
+ if (arg0.at(i) > arg0.at(index))
+ {
+ index = i;
+ }
+ }
+
+ return arg0.at(index);
+ }
+
+ std::string to_string(int level = 0) const override
+ {
+ std::string s;
+ if (level > 0)
+ {
+ s.append("\n");
+ s.append(level*2, ' ');
+ }
+ s.append("max function");
+ return s;
+ }
+ };
+
+ template <class Json>
+ class abs_function : public function_base<Json>
+ {
+ public:
+ using value_type = Json;
+ using parameter_type = parameter<Json>;
+
+ abs_function()
+ : function_base<Json>(1)
+ {
+ }
+
+ value_type evaluate(const std::vector<parameter_type>& args,
+ std::error_code& ec) const override
+ {
+ if (args.size() != *this->arity())
+ {
+ ec = jsonpath_errc::invalid_arity;
+ return value_type::null();
+ }
+
+ auto arg0= args[0].value();
+ switch (arg0.type())
+ {
+ case json_type::uint64_value:
+ return arg0;
+ case json_type::int64_value:
+ {
+ return arg0.template as<int64_t>() >= 0 ? arg0 : value_type(std::abs(arg0.template as<int64_t>()));
+ }
+ case json_type::double_value:
+ {
+ return arg0.template as<double>() >= 0 ? arg0 : value_type(std::abs(arg0.template as<double>()));
+ }
+ default:
+ {
+ ec = jsonpath_errc::invalid_type;
+ return value_type::null();
+ }
+ }
+ }
+
+ std::string to_string(int level = 0) const override
+ {
+ std::string s;
+ if (level > 0)
+ {
+ s.append("\n");
+ s.append(level*2, ' ');
+ }
+ s.append("abs function");
+ return s;
+ }
+ };
+
+ template <class Json>
+ class length_function : public function_base<Json>
+ {
+ public:
+ using value_type = Json;
+ using string_view_type = typename Json::string_view_type;
+ using parameter_type = parameter<Json>;
+
+ length_function()
+ : function_base<Json>(1)
+ {
+ }
+
+ value_type evaluate(const std::vector<parameter_type>& args,
+ std::error_code& ec) const override
+ {
+ if (args.size() != *this->arity())
+ {
+ ec = jsonpath_errc::invalid_arity;
+ return value_type::null();
+ }
+
+ auto arg0= args[0].value();
+ //std::cout << "length function arg: " << arg0 << "\n";
+
+ switch (arg0.type())
+ {
+ case json_type::object_value:
+ case json_type::array_value:
+ return value_type(arg0.size());
+ case json_type::string_value:
+ {
+ auto sv0 = arg0.template as<string_view_type>();
+ auto length = unicode_traits::count_codepoints(sv0.data(), sv0.size());
+ return value_type(length);
+ }
+ default:
+ {
+ ec = jsonpath_errc::invalid_type;
+ return value_type::null();
+ }
+ }
+ }
+
+ std::string to_string(int level = 0) const override
+ {
+ std::string s;
+ if (level > 0)
+ {
+ s.append("\n");
+ s.append(level*2, ' ');
+ }
+ s.append("length function");
+ return s;
+ }
+ };
+
+ template <class Json>
+ class keys_function : public function_base<Json>
+ {
+ public:
+ using value_type = Json;
+ using parameter_type = parameter<Json>;
+ using string_view_type = typename Json::string_view_type;
+
+ keys_function()
+ : function_base<Json>(1)
+ {
+ }
+
+ value_type evaluate(const std::vector<parameter_type>& args,
+ std::error_code& ec) const override
+ {
+ if (args.size() != *this->arity())
+ {
+ ec = jsonpath_errc::invalid_arity;
+ return value_type::null();
+ }
+
+ auto arg0= args[0].value();
+ if (!arg0.is_object())
+ {
+ ec = jsonpath_errc::invalid_type;
+ return value_type::null();
+ }
+
+ value_type result(json_array_arg);
+ result.reserve(args.size());
+
+ for (auto& item : arg0.object_range())
+ {
+ result.emplace_back(item.key());
+ }
+ return result;
+ }
+
+ std::string to_string(int level = 0) const override
+ {
+ std::string s;
+ if (level > 0)
+ {
+ s.append("\n");
+ s.append(level*2, ' ');
+ }
+ s.append("keys function");
+ return s;
+ }
+ };
+
+ enum class jsonpath_token_kind
+ {
+ root_node,
+ current_node,
+ expression,
+ lparen,
+ rparen,
+ begin_union,
+ end_union,
+ begin_filter,
+ end_filter,
+ begin_expression,
+ end_index_expression,
+ end_argument_expression,
+ separator,
+ literal,
+ selector,
+ function,
+ end_function,
+ argument,
+ unary_operator,
+ binary_operator
+ };
+
+ inline
+ std::string to_string(jsonpath_token_kind kind)
+ {
+ switch (kind)
+ {
+ case jsonpath_token_kind::root_node:
+ return "root_node";
+ case jsonpath_token_kind::current_node:
+ return "current_node";
+ case jsonpath_token_kind::lparen:
+ return "lparen";
+ case jsonpath_token_kind::rparen:
+ return "rparen";
+ case jsonpath_token_kind::begin_union:
+ return "begin_union";
+ case jsonpath_token_kind::end_union:
+ return "end_union";
+ case jsonpath_token_kind::begin_filter:
+ return "begin_filter";
+ case jsonpath_token_kind::end_filter:
+ return "end_filter";
+ case jsonpath_token_kind::begin_expression:
+ return "begin_expression";
+ case jsonpath_token_kind::end_index_expression:
+ return "end_index_expression";
+ case jsonpath_token_kind::end_argument_expression:
+ return "end_argument_expression";
+ case jsonpath_token_kind::separator:
+ return "separator";
+ case jsonpath_token_kind::literal:
+ return "literal";
+ case jsonpath_token_kind::selector:
+ return "selector";
+ case jsonpath_token_kind::function:
+ return "function";
+ case jsonpath_token_kind::end_function:
+ return "end_function";
+ case jsonpath_token_kind::argument:
+ return "argument";
+ case jsonpath_token_kind::unary_operator:
+ return "unary_operator";
+ case jsonpath_token_kind::binary_operator:
+ return "binary_operator";
+ default:
+ return "";
+ }
+ }
+
+ template <class Json,class JsonReference>
+ struct path_value_pair
+ {
+ using char_type = typename Json::char_type;
+ using string_type = std::basic_string<char_type,std::char_traits<char_type>>;
+ using value_type = Json;
+ using reference = JsonReference;
+ using value_pointer = typename std::conditional<std::is_const<typename std::remove_reference<JsonReference>::type>::value,typename Json::const_pointer,typename Json::pointer>::type;
+ using json_location_node_type = json_location_node<char_type>;
+ using json_location_type = json_location<char_type>;
+ using path_pointer = const json_location_node_type*;
+
+ json_location_type path_;
+ value_pointer value_ptr_;
+
+ path_value_pair(const json_location_type& path, reference value) noexcept
+ : path_(path), value_ptr_(std::addressof(value))
+ {
+ }
+
+ path_value_pair(json_location_type&& path, value_pointer valp) noexcept
+ : path_(std::move(path)), value_ptr_(valp)
+ {
+ }
+
+ path_value_pair(const path_value_pair&) = default;
+ path_value_pair(path_value_pair&& other) = default;
+ path_value_pair& operator=(const path_value_pair&) = default;
+ path_value_pair& operator=(path_value_pair&& other) = default;
+
+ json_location_type path() const
+ {
+ return path_;
+ }
+
+ reference value()
+ {
+ return *value_ptr_;
+ }
+ };
+
+ template <class Json,class JsonReference>
+ struct path_value_pair_less
+ {
+ bool operator()(const path_value_pair<Json,JsonReference>& lhs,
+ const path_value_pair<Json,JsonReference>& rhs) const noexcept
+ {
+ return lhs.path() < rhs.path();
+ }
+ };
+
+ template <class Json,class JsonReference>
+ struct path_value_pair_equal
+ {
+ bool operator()(const path_value_pair<Json,JsonReference>& lhs,
+ const path_value_pair<Json,JsonReference>& rhs) const noexcept
+ {
+ return lhs.path() == rhs.path();
+ }
+ };
+
+ template <class Json,class JsonReference>
+ struct path_component_value_pair
+ {
+ using char_type = typename Json::char_type;
+ using string_type = std::basic_string<char_type,std::char_traits<char_type>>;
+ using value_type = Json;
+ using reference = JsonReference;
+ using value_pointer = typename std::conditional<std::is_const<typename std::remove_reference<JsonReference>::type>::value,typename Json::const_pointer,typename Json::pointer>::type;
+ using json_location_node_type = json_location_node<char_type>;
+ using json_location_type = json_location<char_type>;
+ using path_pointer = const json_location_node_type*;
+ private:
+ const json_location_node_type* last_ptr_;
+ value_pointer value_ptr_;
+ public:
+ path_component_value_pair(const json_location_node_type& last, reference value) noexcept
+ : last_ptr_(std::addressof(last)), value_ptr_(std::addressof(value))
+ {
+ }
+
+ const json_location_node_type& last() const
+ {
+ return *last_ptr_;
+ }
+
+ reference value() const
+ {
+ return *value_ptr_;
+ }
+ };
+
+ template <class Json,class JsonReference>
+ class node_receiver
+ {
+ public:
+ using char_type = typename Json::char_type;
+ using reference = JsonReference;
+ using json_location_node_type = json_location_node<char_type>;
+
+ virtual ~node_receiver() noexcept = default;
+
+ virtual void add(const json_location_node_type& path_tail,
+ reference value) = 0;
+ };
+
+ template <class Json,class JsonReference>
+ class path_value_receiver : public node_receiver<Json,JsonReference>
+ {
+ public:
+ using reference = JsonReference;
+ using char_type = typename Json::char_type;
+ using json_location_node_type = json_location_node<char_type>;
+ using json_location_type = json_location<char_type>;
+ using path_value_pair_type = path_value_pair<Json,JsonReference>;
+
+ std::vector<path_value_pair_type> nodes;
+
+ void add(const json_location_node_type& path_tail,
+ reference value) override
+ {
+ nodes.emplace_back(json_location_type(path_tail), std::addressof(value));
+ }
+ };
+
+ template <class Json,class JsonReference>
+ class path_stem_value_receiver : public node_receiver<Json,JsonReference>
+ {
+ public:
+ using reference = JsonReference;
+ using char_type = typename Json::char_type;
+ using json_location_node_type = json_location_node<char_type>;
+ using path_stem_value_pair_type = path_component_value_pair<Json,JsonReference>;
+
+ std::vector<path_stem_value_pair_type> nodes;
+
+ void add(const json_location_node_type& path_tail,
+ reference value) override
+ {
+ nodes.emplace_back(path_tail, value);
+ }
+ };
+
+ template <class Json, class JsonReference>
+ class dynamic_resources
+ {
+ using reference = JsonReference;
+ using pointer = typename std::conditional<std::is_const<typename std::remove_reference<reference>::type>::value,typename Json::const_pointer,typename Json::pointer>::type;
+ using json_location_node_type = json_location_node<typename Json::char_type>;
+ using path_stem_value_pair_type = path_component_value_pair<Json,JsonReference>;
+ std::vector<std::unique_ptr<Json>> temp_json_values_;
+ std::vector<std::unique_ptr<json_location_node_type>> temp_path_node_values_;
+ std::unordered_map<std::size_t,pointer> cache_;
+ public:
+ bool is_cached(std::size_t id) const
+ {
+ return cache_.find(id) != cache_.end();
+ }
+ void add_to_cache(std::size_t id, reference val)
+ {
+ cache_.emplace(id, std::addressof(val));
+ }
+ reference retrieve_from_cache(std::size_t id)
+ {
+ return *cache_[id];
+ }
+
+ reference null_value()
+ {
+ static Json j{ null_type{} };
+ return j;
+ }
+
+ template <typename... Args>
+ Json* create_json(Args&& ... args)
+ {
+ auto temp = jsoncons::make_unique<Json>(std::forward<Args>(args)...);
+ Json* ptr = temp.get();
+ temp_json_values_.emplace_back(std::move(temp));
+ return ptr;
+ }
+
+ const json_location_node_type& root_path_node() const
+ {
+ static json_location_node_type root('$');
+ return root;
+ }
+
+ const json_location_node_type& current_path_node() const
+ {
+ static json_location_node_type root('@');
+ return root;
+ }
+
+ template <typename... Args>
+ const json_location_node_type* create_path_node(Args&& ... args)
+ {
+ auto temp = jsoncons::make_unique<json_location_node_type>(std::forward<Args>(args)...);
+ json_location_node_type* ptr = temp.get();
+ temp_path_node_values_.emplace_back(std::move(temp));
+ return ptr;
+ }
+ };
+
+ template <class Json,class JsonReference>
+ struct node_less
+ {
+ bool operator()(const path_value_pair<Json,JsonReference>& a, const path_value_pair<Json,JsonReference>& b) const
+ {
+ return *(a.ptr) < *(b.ptr);
+ }
+ };
+
+ template <class Json,class JsonReference>
+ class jsonpath_selector
+ {
+ bool is_path_;
+ std::size_t precedence_level_;
+
+ public:
+ using char_type = typename Json::char_type;
+ using string_type = std::basic_string<char_type,std::char_traits<char_type>>;
+ using string_view_type = jsoncons::basic_string_view<char_type, std::char_traits<char_type>>;
+ using value_type = Json;
+ using reference = JsonReference;
+ using pointer = typename std::conditional<std::is_const<typename std::remove_reference<JsonReference>::type>::value,typename Json::const_pointer,typename Json::pointer>::type;
+ using path_value_pair_type = path_value_pair<Json,JsonReference>;
+ using json_location_node_type = json_location_node<char_type>;
+ using json_location_type = json_location<char_type>;
+ using node_receiver_type = node_receiver<Json,JsonReference>;
+ using selector_type = jsonpath_selector<Json,JsonReference>;
+
+ jsonpath_selector(bool is_path,
+ std::size_t precedence_level = 0)
+ : is_path_(is_path),
+ precedence_level_(precedence_level)
+ {
+ }
+
+ virtual ~jsonpath_selector() noexcept = default;
+
+ bool is_path() const
+ {
+ return is_path_;
+ }
+
+ std::size_t precedence_level() const
+ {
+ return precedence_level_;
+ }
+
+ bool is_right_associative() const
+ {
+ return true;
+ }
+
+ virtual void select(dynamic_resources<Json,JsonReference>& resources,
+ reference root,
+ const json_location_node_type& path_tail,
+ reference val,
+ node_receiver_type& receiver,
+ result_options options) const = 0;
+
+ virtual reference evaluate(dynamic_resources<Json,JsonReference>& resources,
+ reference root,
+ const json_location_node_type& path_tail,
+ reference current,
+ result_options options,
+ std::error_code& ec) const = 0;
+
+ virtual void append_selector(jsonpath_selector*)
+ {
+ }
+
+ virtual std::string to_string(int = 0) const
+ {
+ return std::string();
+ }
+ };
+
+ template <class Json, class JsonReference>
+ struct static_resources
+ {
+ using char_type = typename Json::char_type;
+ using string_type = std::basic_string<char_type>;
+ using value_type = Json;
+ using reference = JsonReference;
+ using function_base_type = function_base<Json>;
+ using selector_type = jsonpath_selector<Json,JsonReference>;
+
+ std::vector<std::unique_ptr<selector_type>> selectors_;
+ std::vector<std::unique_ptr<Json>> temp_json_values_;
+ std::vector<std::unique_ptr<unary_operator<Json,JsonReference>>> unary_operators_;
+ std::unordered_map<string_type,std::unique_ptr<function_base_type>> custom_functions_;
+
+ static_resources()
+ {
+ }
+
+ static_resources(const custom_functions<Json>& functions)
+ {
+ for (const auto& item : functions)
+ {
+ custom_functions_.emplace(item.name(),
+ jsoncons::make_unique<decorator_function<Json>>(item.arity(),item.function()));
+ }
+ }
+
+ static_resources(const static_resources&) = default;
+
+ static_resources(static_resources&& other) noexcept
+ : selectors_(std::move(other.selectors_)),
+ temp_json_values_(std::move(other.temp_json_values_)),
+ unary_operators_(std::move(other.unary_operators_)),
+ custom_functions_(std::move(other.custom_functions_))
+ {
+ }
+
+ const function_base_type* get_function(const string_type& name, std::error_code& ec) const
+ {
+ static abs_function<Json> abs_func;
+ static contains_function<Json> contains_func;
+ static starts_with_function<Json> starts_with_func;
+ static ends_with_function<Json> ends_with_func;
+ static ceil_function<Json> ceil_func;
+ static floor_function<Json> floor_func;
+ static to_number_function<Json> to_number_func;
+ static sum_function<Json> sum_func;
+ static prod_function<Json> prod_func;
+ static avg_function<Json> avg_func;
+ static min_function<Json> min_func;
+ static max_function<Json> max_func;
+ static length_function<Json> length_func;
+ static keys_function<Json> keys_func;
+#if defined(JSONCONS_HAS_STD_REGEX)
+ static tokenize_function<Json> tokenize_func;
+#endif
+
+ static std::unordered_map<string_type,const function_base_type*> functions =
+ {
+ {string_type{'a','b','s'}, &abs_func},
+ {string_type{'c','o','n','t','a','i','n','s'}, &contains_func},
+ {string_type{'s','t','a','r','t','s','_','w','i','t','h'}, &starts_with_func},
+ {string_type{'e','n','d','s','_','w','i','t','h'}, &ends_with_func},
+ {string_type{'c','e','i','l'}, &ceil_func},
+ {string_type{'f','l','o','o','r'}, &floor_func},
+ {string_type{'t','o','_','n','u','m','b','e','r'}, &to_number_func},
+ {string_type{'s','u','m'}, &sum_func},
+ {string_type{'p','r','o', 'd'}, &prod_func},
+ {string_type{'a','v','g'}, &avg_func},
+ {string_type{'m','i','n'}, &min_func},
+ {string_type{'m','a','x'}, &max_func},
+ {string_type{'l','e','n','g','t','h'}, &length_func},
+ {string_type{'k','e','y','s'}, &keys_func},
+#if defined(JSONCONS_HAS_STD_REGEX)
+ {string_type{'t','o','k','e','n','i','z','e'}, &tokenize_func},
+#endif
+ {string_type{'c','o','u','n','t'}, &length_func}
+ };
+
+ auto it = functions.find(name);
+ if (it == functions.end())
+ {
+ auto it2 = custom_functions_.find(name);
+ if (it2 == custom_functions_.end())
+ {
+ ec = jsonpath_errc::unknown_function;
+ return nullptr;
+ }
+ else
+ {
+ return it2->second.get();
+ }
+ }
+ else
+ {
+ return it->second;
+ }
+ }
+
+ const unary_operator<Json,JsonReference>* get_unary_not() const
+ {
+ static unary_not_operator<Json,JsonReference> oper;
+ return &oper;
+ }
+
+ const unary_operator<Json,JsonReference>* get_unary_minus() const
+ {
+ static unary_minus_operator<Json,JsonReference> oper;
+ return &oper;
+ }
+
+ const unary_operator<Json,JsonReference>* get_regex_operator(std::basic_regex<char_type>&& pattern)
+ {
+ unary_operators_.push_back(jsoncons::make_unique<regex_operator<Json,JsonReference>>(std::move(pattern)));
+ return unary_operators_.back().get();
+ }
+
+ const binary_operator<Json,JsonReference>* get_or_operator() const
+ {
+ static or_operator<Json,JsonReference> oper;
+
+ return &oper;
+ }
+
+ const binary_operator<Json,JsonReference>* get_and_operator() const
+ {
+ static and_operator<Json,JsonReference> oper;
+
+ return &oper;
+ }
+
+ const binary_operator<Json,JsonReference>* get_eq_operator() const
+ {
+ static eq_operator<Json,JsonReference> oper;
+ return &oper;
+ }
+
+ const binary_operator<Json,JsonReference>* get_ne_operator() const
+ {
+ static ne_operator<Json,JsonReference> oper;
+ return &oper;
+ }
+
+ const binary_operator<Json,JsonReference>* get_lt_operator() const
+ {
+ static lt_operator<Json,JsonReference> oper;
+ return &oper;
+ }
+
+ const binary_operator<Json,JsonReference>* get_lte_operator() const
+ {
+ static lte_operator<Json,JsonReference> oper;
+ return &oper;
+ }
+
+ const binary_operator<Json,JsonReference>* get_gt_operator() const
+ {
+ static gt_operator<Json,JsonReference> oper;
+ return &oper;
+ }
+
+ const binary_operator<Json,JsonReference>* get_gte_operator() const
+ {
+ static gte_operator<Json,JsonReference> oper;
+ return &oper;
+ }
+
+ const binary_operator<Json,JsonReference>* get_plus_operator() const
+ {
+ static plus_operator<Json,JsonReference> oper;
+ return &oper;
+ }
+
+ const binary_operator<Json,JsonReference>* get_minus_operator() const
+ {
+ static minus_operator<Json,JsonReference> oper;
+ return &oper;
+ }
+
+ const binary_operator<Json,JsonReference>* get_mult_operator() const
+ {
+ static mult_operator<Json,JsonReference> oper;
+ return &oper;
+ }
+
+ const binary_operator<Json,JsonReference>* get_div_operator() const
+ {
+ static div_operator<Json,JsonReference> oper;
+ return &oper;
+ }
+
+ const binary_operator<Json,JsonReference>* get_modulus_operator() const
+ {
+ static modulus_operator<Json,JsonReference> oper;
+ return &oper;
+ }
+
+ template <typename T>
+ selector_type* new_selector(T&& val)
+ {
+ selectors_.emplace_back(jsoncons::make_unique<T>(std::forward<T>(val)));
+ return selectors_.back().get();
+ }
+
+ template <typename... Args>
+ Json* create_json(Args&& ... args)
+ {
+ auto temp = jsoncons::make_unique<Json>(std::forward<Args>(args)...);
+ Json* ptr = temp.get();
+ temp_json_values_.emplace_back(std::move(temp));
+ return ptr;
+ }
+ };
+
+ template <class Json, class JsonReference>
+ class expression_base
+ {
+ public:
+ using char_type = typename Json::char_type;
+ using string_type = std::basic_string<char_type,std::char_traits<char_type>>;
+ using string_view_type = jsoncons::basic_string_view<char_type, std::char_traits<char_type>>;
+ using value_type = Json;
+ using reference = JsonReference;
+ using pointer = typename std::conditional<std::is_const<typename std::remove_reference<JsonReference>::type>::value,typename Json::const_pointer,typename Json::pointer>::type;
+ using path_value_pair_type = path_value_pair<Json,JsonReference>;
+ using json_location_node_type = json_location_node<char_type>;
+
+ virtual ~expression_base() noexcept = default;
+
+ virtual value_type evaluate(dynamic_resources<Json,JsonReference>& resources,
+ reference root,
+ //const json_location_node_type& path,
+ reference val,
+ result_options options,
+ std::error_code& ec) const = 0;
+
+ virtual std::string to_string(int level = 0) const = 0;
+ };
+
+ template <class Json,class JsonReference>
+ class token
+ {
+ public:
+ using selector_type = jsonpath_selector<Json,JsonReference>;
+ using expression_base_type = expression_base<Json,JsonReference>;
+
+ jsonpath_token_kind token_kind_;
+
+ union
+ {
+ selector_type* selector_;
+ std::unique_ptr<expression_base_type> expression_;
+ const unary_operator<Json,JsonReference>* unary_operator_;
+ const binary_operator<Json,JsonReference>* binary_operator_;
+ const function_base<Json>* function_;
+ Json value_;
+ };
+ public:
+
+ token(const unary_operator<Json,JsonReference>* expr) noexcept
+ : token_kind_(jsonpath_token_kind::unary_operator),
+ unary_operator_(expr)
+ {
+ }
+
+ token(const binary_operator<Json,JsonReference>* expr) noexcept
+ : token_kind_(jsonpath_token_kind::binary_operator),
+ binary_operator_(expr)
+ {
+ }
+
+ token(current_node_arg_t) noexcept
+ : token_kind_(jsonpath_token_kind::current_node)
+ {
+ }
+
+ token(root_node_arg_t) noexcept
+ : token_kind_(jsonpath_token_kind::root_node)
+ {
+ }
+
+ token(end_function_arg_t) noexcept
+ : token_kind_(jsonpath_token_kind::end_function)
+ {
+ }
+
+ token(separator_arg_t) noexcept
+ : token_kind_(jsonpath_token_kind::separator)
+ {
+ }
+
+ token(lparen_arg_t) noexcept
+ : token_kind_(jsonpath_token_kind::lparen)
+ {
+ }
+
+ token(rparen_arg_t) noexcept
+ : token_kind_(jsonpath_token_kind::rparen)
+ {
+ }
+
+ token(begin_union_arg_t) noexcept
+ : token_kind_(jsonpath_token_kind::begin_union)
+ {
+ }
+
+ token(end_union_arg_t) noexcept
+ : token_kind_(jsonpath_token_kind::end_union)
+ {
+ }
+
+ token(begin_filter_arg_t) noexcept
+ : token_kind_(jsonpath_token_kind::begin_filter)
+ {
+ }
+
+ token(end_filter_arg_t) noexcept
+ : token_kind_(jsonpath_token_kind::end_filter)
+ {
+ }
+
+ token(begin_expression_arg_t) noexcept
+ : token_kind_(jsonpath_token_kind::begin_expression)
+ {
+ }
+
+ token(end_index_expression_arg_t) noexcept
+ : token_kind_(jsonpath_token_kind::end_index_expression)
+ {
+ }
+
+ token(end_argument_expression_arg_t) noexcept
+ : token_kind_(jsonpath_token_kind::end_argument_expression)
+ {
+ }
+
+ token(selector_type* selector)
+ : token_kind_(jsonpath_token_kind::selector), selector_(selector)
+ {
+ }
+
+ token(std::unique_ptr<expression_base_type>&& expr)
+ : token_kind_(jsonpath_token_kind::expression)
+ {
+ new (&expression_) std::unique_ptr<expression_base_type>(std::move(expr));
+ }
+
+ token(const function_base<Json>* function) noexcept
+ : token_kind_(jsonpath_token_kind::function),
+ function_(function)
+ {
+ }
+
+ token(argument_arg_t) noexcept
+ : token_kind_(jsonpath_token_kind::argument)
+ {
+ }
+
+ token(literal_arg_t, Json&& value) noexcept
+ : token_kind_(jsonpath_token_kind::literal), value_(std::move(value))
+ {
+ }
+
+ token(token&& other) noexcept
+ {
+ construct(std::forward<token>(other));
+ }
+
+ const Json& get_value(const_reference_arg_t, dynamic_resources<Json,JsonReference>&) const
+ {
+ return value_;
+ }
+
+ Json& get_value(reference_arg_t, dynamic_resources<Json,JsonReference>& resources) const
+ {
+ return *resources.create_json(value_);
+ }
+
+ token& operator=(token&& other)
+ {
+ if (&other != this)
+ {
+ if (token_kind_ == other.token_kind_)
+ {
+ switch (token_kind_)
+ {
+ case jsonpath_token_kind::selector:
+ selector_ = other.selector_;
+ break;
+ case jsonpath_token_kind::expression:
+ expression_ = std::move(other.expression_);
+ break;
+ case jsonpath_token_kind::unary_operator:
+ unary_operator_ = other.unary_operator_;
+ break;
+ case jsonpath_token_kind::binary_operator:
+ binary_operator_ = other.binary_operator_;
+ break;
+ case jsonpath_token_kind::function:
+ function_ = other.function_;
+ break;
+ case jsonpath_token_kind::literal:
+ value_ = std::move(other.value_);
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ destroy();
+ construct(std::forward<token>(other));
+ }
+ }
+ return *this;
+ }
+
+ ~token() noexcept
+ {
+ destroy();
+ }
+
+ jsonpath_token_kind token_kind() const
+ {
+ return token_kind_;
+ }
+
+ bool is_lparen() const
+ {
+ return token_kind_ == jsonpath_token_kind::lparen;
+ }
+
+ bool is_rparen() const
+ {
+ return token_kind_ == jsonpath_token_kind::rparen;
+ }
+
+ bool is_current_node() const
+ {
+ return token_kind_ == jsonpath_token_kind::current_node;
+ }
+
+ bool is_path() const
+ {
+ return token_kind_ == jsonpath_token_kind::selector && selector_->is_path();
+ }
+
+ bool is_operator() const
+ {
+ return token_kind_ == jsonpath_token_kind::unary_operator ||
+ token_kind_ == jsonpath_token_kind::binary_operator;
+ }
+
+ std::size_t precedence_level() const
+ {
+ switch(token_kind_)
+ {
+ case jsonpath_token_kind::selector:
+ return selector_->precedence_level();
+ case jsonpath_token_kind::unary_operator:
+ return unary_operator_->precedence_level();
+ case jsonpath_token_kind::binary_operator:
+ return binary_operator_->precedence_level();
+ default:
+ return 0;
+ }
+ }
+
+ jsoncons::optional<std::size_t> arity() const
+ {
+ return token_kind_ == jsonpath_token_kind::function ? function_->arity() : jsoncons::optional<std::size_t>();
+ }
+
+ bool is_right_associative() const
+ {
+ switch(token_kind_)
+ {
+ case jsonpath_token_kind::selector:
+ return selector_->is_right_associative();
+ case jsonpath_token_kind::unary_operator:
+ return unary_operator_->is_right_associative();
+ case jsonpath_token_kind::binary_operator:
+ return binary_operator_->is_right_associative();
+ default:
+ return false;
+ }
+ }
+
+ void construct(token&& other)
+ {
+ token_kind_ = other.token_kind_;
+ switch (token_kind_)
+ {
+ case jsonpath_token_kind::selector:
+ selector_ = other.selector_;
+ break;
+ case jsonpath_token_kind::expression:
+ new (&expression_) std::unique_ptr<expression_base_type>(std::move(other.expression_));
+ break;
+ case jsonpath_token_kind::unary_operator:
+ unary_operator_ = other.unary_operator_;
+ break;
+ case jsonpath_token_kind::binary_operator:
+ binary_operator_ = other.binary_operator_;
+ break;
+ case jsonpath_token_kind::function:
+ function_ = other.function_;
+ break;
+ case jsonpath_token_kind::literal:
+ new (&value_) Json(std::move(other.value_));
+ break;
+ default:
+ break;
+ }
+ }
+
+ void destroy() noexcept
+ {
+ switch(token_kind_)
+ {
+ case jsonpath_token_kind::expression:
+ expression_.~unique_ptr();
+ break;
+ case jsonpath_token_kind::literal:
+ value_.~Json();
+ break;
+ default:
+ break;
+ }
+ }
+
+ std::string to_string(int level = 0) const
+ {
+ std::string s;
+ switch (token_kind_)
+ {
+ case jsonpath_token_kind::root_node:
+ if (level > 0)
+ {
+ s.append("\n");
+ s.append(level*2, ' ');
+ }
+ s.append("root node");
+ break;
+ case jsonpath_token_kind::current_node:
+ if (level > 0)
+ {
+ s.append("\n");
+ s.append(level*2, ' ');
+ }
+ s.append("current node");
+ break;
+ case jsonpath_token_kind::argument:
+ if (level > 0)
+ {
+ s.append("\n");
+ s.append(level*2, ' ');
+ }
+ s.append("argument");
+ break;
+ case jsonpath_token_kind::selector:
+ s.append(selector_->to_string(level));
+ break;
+ case jsonpath_token_kind::expression:
+ s.append(expression_->to_string(level));
+ break;
+ case jsonpath_token_kind::literal:
+ {
+ if (level > 0)
+ {
+ s.append("\n");
+ s.append(level*2, ' ');
+ }
+ auto sbuf = value_.to_string();
+ unicode_traits::convert(sbuf.data(), sbuf.size(), s);
+ break;
+ }
+ case jsonpath_token_kind::binary_operator:
+ s.append(binary_operator_->to_string(level));
+ break;
+ case jsonpath_token_kind::function:
+ s.append(function_->to_string(level));
+ break;
+ default:
+ if (level > 0)
+ {
+ s.append("\n");
+ s.append(level*2, ' ');
+ }
+ s.append("token kind: ");
+ s.append(jsoncons::jsonpath::detail::to_string(token_kind_));
+ break;
+ }
+ //s.append("\n");
+ return s;
+ }
+ };
+
+ template <class Callback, class Json,class JsonReference>
+ class callback_receiver : public node_receiver<Json,JsonReference>
+ {
+ Callback& callback_;
+ public:
+ using reference = JsonReference;
+ using char_type = typename Json::char_type;
+ using json_location_node_type = json_location_node<char_type>;
+ using json_location_type = json_location<char_type>;
+
+ callback_receiver(Callback& callback)
+ : callback_(callback)
+ {
+ }
+
+ void add(const json_location_node_type& path_tail,
+ reference value) override
+ {
+ callback_(json_location_type(path_tail), value);
+ }
+ };
+
+ template <class Json,class JsonReference>
+ class path_expression
+ {
+ public:
+ using char_type = typename Json::char_type;
+ using string_type = std::basic_string<char_type,std::char_traits<char_type>>;
+ using string_view_type = typename Json::string_view_type;
+ using path_value_pair_type = path_value_pair<Json,JsonReference>;
+ using path_value_pair_less_type = path_value_pair_less<Json,JsonReference>;
+ using path_value_pair_equal_type = path_value_pair_equal<Json,JsonReference>;
+ using value_type = Json;
+ using reference = typename path_value_pair_type::reference;
+ using pointer = typename path_value_pair_type::value_pointer;
+ using token_type = token<Json,JsonReference>;
+ using reference_arg_type = typename std::conditional<std::is_const<typename std::remove_reference<JsonReference>::type>::value,
+ const_reference_arg_t,reference_arg_t>::type;
+ using json_location_node_type = json_location_node<char_type>;
+ using json_location_type = json_location<char_type>;
+ using selector_type = jsonpath_selector<Json,JsonReference>;
+ private:
+ selector_type* selector_;
+ result_options required_options_;
+ public:
+
+ path_expression()
+ : required_options_()
+ {
+ }
+
+ path_expression(path_expression&& expr) = default;
+
+ path_expression(selector_type* selector, bool paths_required)
+ : selector_(selector), required_options_()
+ {
+ if (paths_required)
+ {
+ required_options_ |= result_options::path;
+ }
+ }
+
+ path_expression& operator=(path_expression&& expr) = default;
+
+ Json evaluate(dynamic_resources<Json,JsonReference>& resources,
+ reference root,
+ const json_location_node_type& path,
+ reference instance,
+ result_options options) const
+ {
+ Json result(json_array_arg);
+
+ if ((options & result_options::path) == result_options::path)
+ {
+ auto callback = [&result](const json_location_type& path, reference)
+ {
+ result.emplace_back(path.to_string());
+ };
+ evaluate(resources, root, path, instance, callback, options);
+ }
+ else
+ {
+ auto callback = [&result](const json_location_type&, reference val)
+ {
+ result.push_back(val);
+ };
+ evaluate(resources, root, path, instance, callback, options);
+ }
+
+ return result;
+ }
+
+ template <class Callback>
+ typename std::enable_if<type_traits::is_binary_function_object<Callback,const json_location_type&,reference>::value,void>::type
+ evaluate(dynamic_resources<Json,JsonReference>& resources,
+ reference root,
+ const json_location_node_type& path,
+ reference current,
+ Callback callback,
+ result_options options) const
+ {
+ std::error_code ec;
+
+ options |= required_options_;
+
+ const result_options require_more = result_options::nodups | result_options::sort;
+
+ if ((options & require_more) != result_options())
+ {
+ path_value_receiver<Json,JsonReference> receiver;
+ selector_->select(resources, root, path, current, receiver, options);
+
+ if (receiver.nodes.size() > 1 && (options & result_options::sort) == result_options::sort)
+ {
+ std::sort(receiver.nodes.begin(), receiver.nodes.end(), path_value_pair_less_type());
+ }
+
+ if (receiver.nodes.size() > 1 && (options & result_options::nodups) == result_options::nodups)
+ {
+ if ((options & result_options::sort) == result_options::sort)
+ {
+ auto last = std::unique(receiver.nodes.begin(),receiver.nodes.end(),path_value_pair_equal_type());
+ receiver.nodes.erase(last,receiver.nodes.end());
+ for (auto& node : receiver.nodes)
+ {
+ callback(node.path(), node.value());
+ }
+ }
+ else
+ {
+ std::vector<path_value_pair_type> index(receiver.nodes);
+ std::sort(index.begin(), index.end(), path_value_pair_less_type());
+ auto last = std::unique(index.begin(),index.end(),path_value_pair_equal_type());
+ index.erase(last,index.end());
+
+ std::vector<path_value_pair_type> temp2;
+ temp2.reserve(index.size());
+ for (auto&& node : receiver.nodes)
+ {
+ auto it = std::lower_bound(index.begin(),index.end(),node, path_value_pair_less_type());
+ if (it != index.end() && it->path() == node.path())
+ {
+ temp2.emplace_back(std::move(node));
+ index.erase(it);
+ }
+ }
+ for (auto& node : temp2)
+ {
+ callback(node.path(), node.value());
+ }
+ }
+ }
+ else
+ {
+ for (auto& node : receiver.nodes)
+ {
+ callback(node.path(), node.value());
+ }
+ }
+ }
+ else
+ {
+ callback_receiver<Callback,Json,JsonReference> receiver(callback);
+ selector_->select(resources, root, path, current, receiver, options);
+ }
+ }
+
+ std::string to_string(int level) const
+ {
+ std::string s;
+ if (level > 0)
+ {
+ s.append("\n");
+ s.append(level*2, ' ');
+ }
+ s.append("expression ");
+ s.append(selector_->to_string(level+1));
+
+ return s;
+
+ }
+ };
+
+ template <class Json,class JsonReference>
+ class expression : public expression_base<Json,JsonReference>
+ {
+ public:
+ using path_value_pair_type = path_value_pair<Json,JsonReference>;
+ using value_type = Json;
+ using reference = typename path_value_pair_type::reference;
+ using pointer = typename path_value_pair_type::value_pointer;
+ using const_pointer = const value_type*;
+ using char_type = typename Json::char_type;
+ using string_type = std::basic_string<char_type,std::char_traits<char_type>>;
+ using string_view_type = typename Json::string_view_type;
+ using path_value_pair_less_type = path_value_pair_less<Json,reference>;
+ using path_value_pair_equal_type = path_value_pair_equal<Json,reference>;
+ using parameter_type = parameter<Json>;
+ using token_type = token<Json,reference>;
+ using reference_arg_type = typename std::conditional<std::is_const<typename std::remove_reference<reference>::type>::value,
+ const_reference_arg_t,reference_arg_t>::type;
+ using json_location_node_type = json_location_node<char_type>;
+ using stack_item_type = value_or_pointer<Json,JsonReference>;
+ private:
+ std::vector<token_type> token_list_;
+ public:
+
+ expression()
+ {
+ }
+
+ expression(expression&& expr)
+ : token_list_(std::move(expr.token_list_))
+ {
+ }
+
+ expression(std::vector<token_type>&& token_stack)
+ : token_list_(std::move(token_stack))
+ {
+ }
+
+ expression& operator=(expression&& expr) = default;
+
+ value_type evaluate(dynamic_resources<Json,reference>& resources,
+ reference root,
+ reference current,
+ result_options options,
+ std::error_code& ec) const override
+ {
+ std::vector<stack_item_type> stack;
+ std::vector<parameter_type> arg_stack;
+
+ //std::cout << "EVALUATE TOKENS\n";
+ //for (auto& tok : token_list_)
+ //{
+ // std::cout << tok.to_string() << "\n";
+ //}
+ //std::cout << "\n";
+
+ if (!token_list_.empty())
+ {
+ for (auto& tok : token_list_)
+ {
+ //std::cout << "Token: " << tok.to_string() << "\n";
+ switch (tok.token_kind())
+ {
+ case jsonpath_token_kind::literal:
+ {
+ stack.emplace_back(std::addressof(tok.get_value(reference_arg_type(), resources)));
+ break;
+ }
+ case jsonpath_token_kind::unary_operator:
+ {
+ JSONCONS_ASSERT(stack.size() >= 1);
+ auto item = std::move(stack.back());
+ stack.pop_back();
+
+ auto val = tok.unary_operator_->evaluate(item.value(), ec);
+ stack.emplace_back(std::move(val));
+ break;
+ }
+ case jsonpath_token_kind::binary_operator:
+ {
+ //std::cout << "binary operator: " << stack.size() << "\n";
+ JSONCONS_ASSERT(stack.size() >= 2);
+ auto rhs = std::move(stack.back());
+ //std::cout << "rhs: " << *rhs << "\n";
+ stack.pop_back();
+ auto lhs = std::move(stack.back());
+ //std::cout << "lhs: " << *lhs << "\n";
+ stack.pop_back();
+
+ auto val = tok.binary_operator_->evaluate(lhs.value(), rhs.value(), ec);
+ //std::cout << "Evaluate binary expression: " << r << "\n";
+ stack.emplace_back(std::move(val));
+ break;
+ }
+ case jsonpath_token_kind::root_node:
+ //std::cout << "root: " << root << "\n";
+ stack.emplace_back(std::addressof(root));
+ break;
+ case jsonpath_token_kind::current_node:
+ //std::cout << "current: " << current << "\n";
+ stack.emplace_back(std::addressof(current));
+ break;
+ case jsonpath_token_kind::argument:
+ JSONCONS_ASSERT(!stack.empty());
+ //std::cout << "argument stack items " << stack.size() << "\n";
+ //for (auto& item : stack)
+ //{
+ // std::cout << *item.to_pointer(resources) << "\n";
+ //}
+ //std::cout << "\n";
+ arg_stack.emplace_back(std::move(stack.back()));
+ //for (auto& item : arg_stack)
+ //{
+ // std::cout << *item << "\n";
+ //}
+ //std::cout << "\n";
+ stack.pop_back();
+ break;
+ case jsonpath_token_kind::function:
+ {
+ if (tok.function_->arity() && *(tok.function_->arity()) != arg_stack.size())
+ {
+ ec = jsonpath_errc::invalid_arity;
+ return Json::null();
+ }
+ //std::cout << "function arg stack:\n";
+ //for (auto& item : arg_stack)
+ //{
+ // std::cout << *item << "\n";
+ //}
+ //std::cout << "\n";
+
+ value_type val = tok.function_->evaluate(arg_stack, ec);
+ if (ec)
+ {
+ return Json::null();
+ }
+ //std::cout << "function result: " << val << "\n";
+ arg_stack.clear();
+ stack.emplace_back(std::move(val));
+ break;
+ }
+ case jsonpath_token_kind::expression:
+ {
+ value_type val = tok.expression_->evaluate(resources, root, current, options, ec);
+ stack.emplace_back(std::move(val));
+ break;
+ }
+ case jsonpath_token_kind::selector:
+ {
+ JSONCONS_ASSERT(!stack.empty());
+ auto item = std::move(stack.back());
+ //for (auto& item : stack)
+ //{
+ //std::cout << "selector stack input:\n";
+ //switch (item.tag)
+ //{
+ // case node_set_tag::single:
+ // std::cout << "single: " << *(item.node.ptr) << "\n";
+ // break;
+ // case node_set_tag::multi:
+ // for (auto& node : stack.back().ptr().nodes)
+ // {
+ // std::cout << "multi: " << *node.ptr << "\n";
+ // }
+ // break;
+ // default:
+ // break;
+ //}
+ //std::cout << "\n";
+ //}
+ //std::cout << "selector item: " << *ptr << "\n";
+
+ reference val = tok.selector_->evaluate(resources, root, resources.current_path_node(), item.value(), options, ec);
+
+ stack.pop_back();
+ stack.emplace_back(stack_item_type(std::addressof(val)));
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ }
+
+ //if (stack.size() != 1)
+ //{
+ // std::cout << "Stack size: " << stack.size() << "\n";
+ //}
+ return stack.empty() ? Json::null() : stack.back().value();
+ }
+
+ std::string to_string(int level) const override
+ {
+ std::string s;
+ if (level > 0)
+ {
+ s.append("\n");
+ s.append(level*2, ' ');
+ }
+ s.append("expression ");
+ for (const auto& item : token_list_)
+ {
+ s.append(item.to_string(level+1));
+ }
+
+ return s;
+
+ }
+ private:
+ };
+
+} // namespace detail
+} // namespace jsonpath
+} // namespace jsoncons
+
+#endif // JSONCONS_JSONPATH_JSONPATH_EXPRESSION_HPP
diff --git a/include/jsoncons_ext/jsonpath/flatten.hpp b/include/jsoncons_ext/jsonpath/flatten.hpp
new file mode 100644
index 0000000..938391f
--- /dev/null
+++ b/include/jsoncons_ext/jsonpath/flatten.hpp
@@ -0,0 +1,432 @@
+// Copyright 2021 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_JSONPATH_FLATTEN_HPP
+#define JSONCONS_JSONPATH_FLATTEN_HPP
+
+#include <string>
+#include <vector>
+#include <memory>
+#include <type_traits> // std::is_const
+#include <limits> // std::numeric_limits
+#include <utility> // std::move
+#include <algorithm> // std::copy
+#include <iterator> // std::back_inserter
+#include <jsoncons_ext/jsonpath/jsonpath.hpp>
+
+namespace jsoncons { namespace jsonpath {
+
+ template <class CharT, class Sink>
+ std::size_t escape_string(const CharT* s, std::size_t length,
+ 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:
+ sink.push_back(c);
+ ++count;
+ break;
+ }
+ }
+ return count;
+ }
+
+ template<class Json>
+ void flatten_(const std::basic_string<typename Json::char_type>& parent_key,
+ const Json& parent_value,
+ Json& result)
+ {
+ using char_type = typename Json::char_type;
+ using string_type = std::basic_string<char_type>;
+
+ switch (parent_value.type())
+ {
+ case json_type::array_value:
+ {
+ if (parent_value.empty())
+ {
+ result.try_emplace(parent_key, parent_value);
+ }
+ else
+ {
+ for (std::size_t i = 0; i < parent_value.size(); ++i)
+ {
+ string_type key(parent_key);
+ key.push_back('[');
+ jsoncons::detail::from_integer(i,key);
+ key.push_back(']');
+ flatten_(key, parent_value.at(i), result);
+ }
+ }
+ break;
+ }
+
+ case json_type::object_value:
+ {
+ if (parent_value.empty())
+ {
+ result.try_emplace(parent_key, Json());
+ }
+ else
+ {
+ for (const auto& item : parent_value.object_range())
+ {
+ string_type key(parent_key);
+ key.push_back('[');
+ key.push_back('\'');
+ escape_string(item.key().data(), item.key().length(), key);
+ key.push_back('\'');
+ key.push_back(']');
+ flatten_(key, item.value(), result);
+ }
+ }
+ break;
+ }
+
+ default:
+ {
+ result[parent_key] = parent_value;
+ break;
+ }
+ }
+ }
+
+ template<class Json>
+ Json flatten(const Json& value)
+ {
+ Json result;
+ std::basic_string<typename Json::char_type> parent_key = {'$'};
+ flatten_(parent_key, value, result);
+ return result;
+ }
+
+ enum class unflatten_state
+ {
+ start,
+ expect_lbracket,
+ lbracket,
+ single_quoted_name_state,
+ double_quoted_name_state,
+ index_state,
+ expect_rbracket,
+ double_quoted_string_escape_char,
+ single_quoted_string_escape_char
+ };
+
+ template<class Json>
+ Json unflatten(const Json& value)
+ {
+ using char_type = typename Json::char_type;
+ using string_type = std::basic_string<char_type>;
+
+ if (JSONCONS_UNLIKELY(!value.is_object()))
+ {
+ JSONCONS_THROW(jsonpath_error(jsonpath_errc::argument_to_unflatten_invalid));
+ }
+
+ Json result;
+
+ for (const auto& item : value.object_range())
+ {
+ Json* part = &result;
+ string_type buffer;
+ unflatten_state state = unflatten_state::start;
+
+ auto it = item.key().begin();
+ auto last = item.key().end();
+
+ for (; it != last; ++it)
+ {
+ switch (state)
+ {
+ case unflatten_state::start:
+ {
+ switch (*it)
+ {
+ case '$':
+ state = unflatten_state::expect_lbracket;
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case unflatten_state::expect_lbracket:
+ {
+ switch (*it)
+ {
+ case '[':
+ state = unflatten_state::lbracket;
+ break;
+ default:
+ JSONCONS_THROW(jsonpath_error(jsonpath_errc::invalid_flattened_key));
+ break;
+ }
+ break;
+ }
+ case unflatten_state::lbracket:
+ {
+ switch (*it)
+ {
+ case '\'':
+ state = unflatten_state::single_quoted_name_state;
+ break;
+ case '\"':
+ state = unflatten_state::double_quoted_name_state;
+ break;
+ case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9':
+ buffer.push_back(*it);
+ state = unflatten_state::index_state;
+ break;
+ default:
+ JSONCONS_THROW(jsonpath_error(jsonpath_errc::invalid_flattened_key));
+ break;
+ }
+ break;
+ }
+ case unflatten_state::single_quoted_name_state:
+ {
+ switch (*it)
+ {
+ case '\'':
+ if (it != last-2)
+ {
+ auto res = part->try_emplace(buffer,Json());
+ part = &(res.first->value());
+ }
+ else
+ {
+ auto res = part->try_emplace(buffer,item.value());
+ part = &(res.first->value());
+ }
+ buffer.clear();
+ state = unflatten_state::expect_rbracket;
+ break;
+ case '\\':
+ state = unflatten_state::single_quoted_string_escape_char;
+ break;
+ default:
+ buffer.push_back(*it);
+ break;
+ }
+ break;
+ }
+ case unflatten_state::double_quoted_name_state:
+ {
+ switch (*it)
+ {
+ case '\"':
+ if (it != last-2)
+ {
+ auto res = part->try_emplace(buffer,Json());
+ part = &(res.first->value());
+ }
+ else
+ {
+ auto res = part->try_emplace(buffer,item.value());
+ part = &(res.first->value());
+ }
+ buffer.clear();
+ state = unflatten_state::expect_rbracket;
+ break;
+ case '\\':
+ state = unflatten_state::double_quoted_string_escape_char;
+ break;
+ default:
+ buffer.push_back(*it);
+ break;
+ }
+ break;
+ }
+ case unflatten_state::double_quoted_string_escape_char:
+ switch (*it)
+ {
+ case '\"':
+ buffer.push_back('\"');
+ state = unflatten_state::double_quoted_name_state;
+ break;
+ case '\\':
+ buffer.push_back('\\');
+ state = unflatten_state::double_quoted_name_state;
+ break;
+ case '/':
+ buffer.push_back('/');
+ state = unflatten_state::double_quoted_name_state;
+ break;
+ case 'b':
+ buffer.push_back('\b');
+ state = unflatten_state::double_quoted_name_state;
+ break;
+ case 'f':
+ buffer.push_back('\f');
+ state = unflatten_state::double_quoted_name_state;
+ break;
+ case 'n':
+ buffer.push_back('\n');
+ state = unflatten_state::double_quoted_name_state;
+ break;
+ case 'r':
+ buffer.push_back('\r');
+ state = unflatten_state::double_quoted_name_state;
+ break;
+ case 't':
+ buffer.push_back('\t');
+ state = unflatten_state::double_quoted_name_state;
+ break;
+ default:
+ break;
+ }
+ break;
+ case unflatten_state::single_quoted_string_escape_char:
+ switch (*it)
+ {
+ case '\'':
+ buffer.push_back('\'');
+ state = unflatten_state::single_quoted_name_state;
+ break;
+ case '\\':
+ buffer.push_back('\\');
+ state = unflatten_state::double_quoted_name_state;
+ break;
+ case '/':
+ buffer.push_back('/');
+ state = unflatten_state::double_quoted_name_state;
+ break;
+ case 'b':
+ buffer.push_back('\b');
+ state = unflatten_state::double_quoted_name_state;
+ break;
+ case 'f':
+ buffer.push_back('\f');
+ state = unflatten_state::double_quoted_name_state;
+ break;
+ case 'n':
+ buffer.push_back('\n');
+ state = unflatten_state::double_quoted_name_state;
+ break;
+ case 'r':
+ buffer.push_back('\r');
+ state = unflatten_state::double_quoted_name_state;
+ break;
+ case 't':
+ buffer.push_back('\t');
+ state = unflatten_state::double_quoted_name_state;
+ break;
+ default:
+ break;
+ }
+ break;
+ case unflatten_state::index_state:
+ {
+ switch (*it)
+ {
+ case ']':
+ {
+ std::size_t n{0};
+ auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), n);
+ if (r)
+ {
+ if (!part->is_array())
+ {
+ *part = Json(json_array_arg);
+ }
+ if (it != last-1)
+ {
+ if (n+1 > part->size())
+ {
+ Json& ref = part->emplace_back();
+ part = std::addressof(ref);
+ }
+ else
+ {
+ part = &part->at(n);
+ }
+ }
+ else
+ {
+ Json& ref = part->emplace_back(item.value());
+ part = std::addressof(ref);
+ }
+ }
+ buffer.clear();
+ state = unflatten_state::expect_lbracket;
+ break;
+ }
+ case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9':
+ buffer.push_back(*it);
+ break;
+ default:
+ JSONCONS_THROW(jsonpath_error(jsonpath_errc::invalid_flattened_key));
+ break;
+ }
+ break;
+ }
+ case unflatten_state::expect_rbracket:
+ {
+ switch (*it)
+ {
+ case ']':
+ state = unflatten_state::expect_lbracket;
+ break;
+ default:
+ JSONCONS_THROW(jsonpath_error(jsonpath_errc::invalid_flattened_key));
+ break;
+ }
+ break;
+ }
+ default:
+ JSONCONS_UNREACHABLE();
+ break;
+ }
+ }
+ }
+
+ return result;
+ }
+}}
+
+#endif
diff --git a/include/jsoncons_ext/jsonpath/json_location.hpp b/include/jsoncons_ext/jsonpath/json_location.hpp
new file mode 100644
index 0000000..9105608
--- /dev/null
+++ b/include/jsoncons_ext/jsonpath/json_location.hpp
@@ -0,0 +1,445 @@
+// Copyright 2021 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_JSONPATH_JSON_LOCATION_HPP
+#define JSONCONS_JSONPATH_JSON_LOCATION_HPP
+
+#include <string>
+#include <vector>
+#include <functional>
+#include <algorithm> // std::reverse
+#include <jsoncons/config/jsoncons_config.hpp>
+#include <jsoncons/detail/write_number.hpp>
+#include <jsoncons_ext/jsonpath/jsonpath_error.hpp>
+#include <jsoncons/json_type.hpp>
+
+namespace jsoncons {
+namespace jsonpath {
+
+ template <class CharT>
+ class json_location;
+
+ enum class json_location_node_kind { root, index, name };
+
+ template <class CharT>
+ class json_location_node
+ {
+ friend class json_location<CharT>;
+ public:
+ using char_type = CharT;
+ using string_type = std::basic_string<CharT>;
+ private:
+
+ const json_location_node* parent_;
+ json_location_node_kind node_kind_;
+ string_type name_;
+ std::size_t index_;
+ public:
+ json_location_node(char_type c)
+ : parent_(nullptr), node_kind_(json_location_node_kind::root), index_(0)
+ {
+ name_.push_back(c);
+ }
+
+ json_location_node(const json_location_node* parent, const string_type& name)
+ : parent_(parent), node_kind_(json_location_node_kind::name), name_(name), index_(0)
+ {
+ }
+
+ json_location_node(const json_location_node* parent, std::size_t index)
+ : parent_(parent), node_kind_(json_location_node_kind::index), index_(index)
+ {
+ }
+
+ const json_location_node* parent() const { return parent_;}
+
+ json_location_node_kind node_kind() const
+ {
+ return node_kind_;
+ }
+
+ const string_type& name() const
+ {
+ return name_;
+ }
+
+ std::size_t index() const
+ {
+ return index_;
+ }
+
+ void swap(json_location_node& node)
+ {
+ std::swap(parent_, node.parent_);
+ std::swap(node_kind_, node.node_kind_);
+ std::swap(name_, node.name_);
+ std::swap(index_, node.index_);
+ }
+
+ private:
+
+ std::size_t node_hash() const
+ {
+ std::size_t h = node_kind_ == json_location_node_kind::index ? std::hash<std::size_t>{}(index_) : std::hash<string_type>{}(name_);
+
+ return h;
+ }
+
+ int compare_node(const json_location_node& other) const
+ {
+ int diff = 0;
+ if (node_kind_ != other.node_kind_)
+ {
+ diff = static_cast<int>(node_kind_) - static_cast<int>(other.node_kind_);
+ }
+ else
+ {
+ switch (node_kind_)
+ {
+ case json_location_node_kind::root:
+ diff = name_.compare(other.name_);
+ break;
+ case json_location_node_kind::index:
+ diff = index_ < other.index_ ? -1 : index_ > other.index_ ? 1 : 0;
+ break;
+ case json_location_node_kind::name:
+ diff = name_.compare(other.name_);
+ break;
+ }
+ }
+ return diff;
+ }
+ };
+
+ namespace detail {
+
+ template <class Iterator>
+ class json_location_iterator
+ {
+ Iterator it_;
+
+ public:
+ using iterator_category = std::random_access_iterator_tag;
+
+ using value_type = typename std::remove_pointer<typename std::iterator_traits<Iterator>::value_type>::type;
+ using difference_type = typename std::iterator_traits<Iterator>::difference_type;
+ using pointer = const value_type*;
+ using reference = const value_type&;
+
+ json_location_iterator() : it_()
+ {
+ }
+
+ explicit json_location_iterator(Iterator ptr) : it_(ptr)
+ {
+ }
+
+ json_location_iterator(const json_location_iterator&) = default;
+ json_location_iterator(json_location_iterator&&) = default;
+ json_location_iterator& operator=(const json_location_iterator&) = default;
+ json_location_iterator& operator=(json_location_iterator&&) = default;
+
+ template <class Iter,
+ class=typename std::enable_if<!std::is_same<Iter,Iterator>::value && std::is_convertible<Iter,Iterator>::value>::type>
+ json_location_iterator(const json_location_iterator<Iter>& other)
+ : it_(other.it_)
+ {
+ }
+
+ operator Iterator() const
+ {
+ return it_;
+ }
+
+ reference operator*() const
+ {
+ return *(*it_);
+ }
+
+ pointer operator->() const
+ {
+ return (*it_);
+ }
+
+ json_location_iterator& operator++()
+ {
+ ++it_;
+ return *this;
+ }
+
+ json_location_iterator operator++(int)
+ {
+ json_location_iterator temp = *this;
+ ++*this;
+ return temp;
+ }
+
+ json_location_iterator& operator--()
+ {
+ --it_;
+ return *this;
+ }
+
+ json_location_iterator operator--(int)
+ {
+ json_location_iterator temp = *this;
+ --*this;
+ return temp;
+ }
+
+ json_location_iterator& operator+=(const difference_type offset)
+ {
+ it_ += offset;
+ return *this;
+ }
+
+ json_location_iterator operator+(const difference_type offset) const
+ {
+ json_location_iterator temp = *this;
+ return temp += offset;
+ }
+
+ json_location_iterator& operator-=(const difference_type offset)
+ {
+ return *this += -offset;
+ }
+
+ json_location_iterator operator-(const difference_type offset) const
+ {
+ json_location_iterator temp = *this;
+ return temp -= offset;
+ }
+
+ difference_type operator-(const json_location_iterator& rhs) const noexcept
+ {
+ return it_ - rhs.it_;
+ }
+
+ reference operator[](const difference_type offset) const noexcept
+ {
+ return *(*(*this + offset));
+ }
+
+ bool operator==(const json_location_iterator& rhs) const noexcept
+ {
+ return it_ == rhs.it_;
+ }
+
+ bool operator!=(const json_location_iterator& rhs) const noexcept
+ {
+ return !(*this == rhs);
+ }
+
+ bool operator<(const json_location_iterator& rhs) const noexcept
+ {
+ return it_ < rhs.it_;
+ }
+
+ bool operator>(const json_location_iterator& rhs) const noexcept
+ {
+ return rhs < *this;
+ }
+
+ bool operator<=(const json_location_iterator& rhs) const noexcept
+ {
+ return !(rhs < *this);
+ }
+
+ bool operator>=(const json_location_iterator& rhs) const noexcept
+ {
+ return !(*this < rhs);
+ }
+
+ inline
+ friend json_location_iterator<Iterator> operator+(
+ difference_type offset, json_location_iterator<Iterator> next)
+ {
+ return next += offset;
+ }
+ };
+
+ } // namespace detail
+
+ template <class CharT>
+ class json_location
+ {
+ public:
+ using char_type = CharT;
+ using string_type = std::basic_string<CharT>;
+ using json_location_node_type = json_location_node<CharT>;
+ private:
+ std::vector<const json_location_node_type*> nodes_;
+ public:
+ using iterator = typename detail::json_location_iterator<typename std::vector<const json_location_node_type*>::iterator>;
+ using const_iterator = typename detail::json_location_iterator<typename std::vector<const json_location_node_type*>::const_iterator>;
+
+ json_location(const json_location_node_type& node)
+ {
+ const json_location_node_type* p = std::addressof(node);
+ do
+ {
+ nodes_.push_back(p);
+ p = p->parent_;
+ }
+ while (p != nullptr);
+
+ std::reverse(nodes_.begin(), nodes_.end());
+ }
+
+ iterator begin()
+ {
+ return iterator(nodes_.begin());
+ }
+
+ iterator end()
+ {
+ return iterator(nodes_.end());
+ }
+
+ const_iterator begin() const
+ {
+ return const_iterator(nodes_.begin());
+ }
+
+ const_iterator end() const
+ {
+ return const_iterator(nodes_.end());
+ }
+
+ const json_location_node_type& last() const
+ {
+ return *nodes_.back();
+ }
+
+ string_type to_string() const
+ {
+ string_type buffer;
+
+ for (const auto& node : nodes_)
+ {
+ switch (node->node_kind())
+ {
+ case json_location_node_kind::root:
+ buffer.append(node->name());
+ break;
+ case json_location_node_kind::name:
+ buffer.push_back('[');
+ buffer.push_back('\'');
+ for (auto c : node->name())
+ {
+ if (c == '\'')
+ {
+ buffer.push_back('\\');
+ buffer.push_back('\'');
+ }
+ else
+ {
+ buffer.push_back(c);
+ }
+ }
+ buffer.push_back('\'');
+ buffer.push_back(']');
+ break;
+ case json_location_node_kind::index:
+ buffer.push_back('[');
+ jsoncons::detail::from_integer(node->index(), buffer);
+ buffer.push_back(']');
+ break;
+ }
+ }
+
+ return buffer;
+ }
+
+ int compare(const json_location& other) const
+ {
+ if (this == &other)
+ {
+ return 0;
+ }
+
+ auto it1 = nodes_.begin();
+ auto it2 = other.nodes_.begin();
+ while (it1 != nodes_.end() && it2 != other.nodes_.end())
+ {
+ int diff = (*it1)->compare_node(*(*it2));
+ if (diff != 0)
+ {
+ return diff;
+ }
+ ++it1;
+ ++it2;
+ }
+ return (nodes_.size() < other.nodes_.size()) ? -1 : (nodes_.size() == other.nodes_.size()) ? 0 : 1;
+ }
+
+ std::size_t hash() const
+ {
+
+ auto it = nodes_.begin();
+ std::size_t hash = (*it).hash();
+ ++it;
+
+ while (it != nodes_.end())
+ {
+ hash += 17*(*it)->node_hash();
+ ++it;
+ }
+
+ return hash;
+ }
+
+ friend bool operator==(const json_location& lhs, const json_location& rhs)
+ {
+ return lhs.compare(rhs) == 0;
+ }
+
+ friend bool operator!=(const json_location& lhs, const json_location& rhs)
+ {
+ return !(lhs == rhs);
+ }
+
+ friend bool operator<(const json_location& lhs, const json_location& rhs)
+ {
+ return lhs.compare(rhs) < 0;
+ }
+ };
+
+ template <class Json>
+ Json* select(Json& root, const json_location<typename Json::char_type>& path)
+ {
+ Json* current = std::addressof(root);
+ for (const auto& json_location_node : path)
+ {
+ if (json_location_node.node_kind() == json_location_node_kind::index)
+ {
+ if (current->type() != json_type::array_value || json_location_node.index() >= current->size())
+ {
+ return nullptr;
+ }
+ current = std::addressof(current->at(json_location_node.index()));
+ }
+ else if (json_location_node.node_kind() == json_location_node_kind::name)
+ {
+ if (current->type() != json_type::object_value)
+ {
+ return nullptr;
+ }
+ auto it = current->find(json_location_node.name());
+ if (it == current->object_range().end())
+ {
+ return nullptr;
+ }
+ current = std::addressof(it->value());
+ }
+ }
+ return current;
+ }
+
+} // namespace jsonpath
+} // namespace jsoncons
+
+#endif
diff --git a/include/jsoncons_ext/jsonpath/json_query.hpp b/include/jsoncons_ext/jsonpath/json_query.hpp
new file mode 100644
index 0000000..8facfa9
--- /dev/null
+++ b/include/jsoncons_ext/jsonpath/json_query.hpp
@@ -0,0 +1,115 @@
+// Copyright 2021 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_JSONPATH_JSON_QUERY_HPP
+#define JSONCONS_JSONPATH_JSON_QUERY_HPP
+
+#include <jsoncons/json.hpp>
+#include <jsoncons_ext/jsonpath/jsonpath_expression.hpp>
+
+namespace jsoncons {
+namespace jsonpath {
+
+ template<class Json>
+ Json json_query(const Json& instance,
+ const typename Json::string_view_type& path,
+ result_options options = result_options(),
+ const custom_functions<Json>& functions = custom_functions<Json>())
+ {
+ auto expr = make_expression<Json>(path, functions);
+ return expr.evaluate(instance, options);
+ }
+
+ template<class Json,class Callback>
+ typename std::enable_if<type_traits::is_binary_function_object<Callback,const std::basic_string<typename Json::char_type>&,const Json&>::value,void>::type
+ json_query(const Json& instance,
+ const typename Json::string_view_type& path,
+ Callback callback,
+ result_options options = result_options(),
+ const custom_functions<Json>& functions = custom_functions<Json>())
+ {
+ auto expr = make_expression<Json>(path, functions);
+ expr.evaluate(instance, callback, options);
+ }
+
+ template<class Json, class T>
+ typename std::enable_if<is_json_type_traits_specialized<Json,T>::value,void>::type
+ json_replace(Json& instance, const typename Json::string_view_type& path, T&& new_value,
+ result_options options = result_options::nodups,
+ const custom_functions<Json>& funcs = custom_functions<Json>())
+ {
+ using evaluator_t = typename jsoncons::jsonpath::detail::jsonpath_evaluator<Json, Json&>;
+ //using string_type = typename evaluator_t::string_type;
+ using value_type = typename evaluator_t::value_type;
+ using reference = typename evaluator_t::reference;
+ using json_selector_t = typename evaluator_t::path_expression_type;
+ using json_location_type = typename evaluator_t::json_location_type;
+
+ jsoncons::jsonpath::detail::static_resources<value_type,reference> static_resources(funcs);
+ evaluator_t e;
+ json_selector_t expr = e.compile(static_resources, path);
+
+ jsoncons::jsonpath::detail::dynamic_resources<Json,reference> resources;
+ auto callback = [&new_value](const json_location_type&, reference v)
+ {
+ v = std::forward<T>(new_value);
+ };
+ expr.evaluate(resources, instance, resources.root_path_node(), instance, callback, options);
+ }
+
+ template<class Json, class UnaryCallback>
+ typename std::enable_if<type_traits::is_unary_function_object<UnaryCallback,Json>::value,void>::type
+ json_replace(Json& instance, const typename Json::string_view_type& path , UnaryCallback callback)
+ {
+ using evaluator_t = typename jsoncons::jsonpath::detail::jsonpath_evaluator<Json, Json&>;
+ //using string_type = typename evaluator_t::string_type;
+ using value_type = typename evaluator_t::value_type;
+ using reference = typename evaluator_t::reference;
+ using json_selector_t = typename evaluator_t::path_expression_type;
+ using json_location_type = typename evaluator_t::json_location_type;
+
+ jsoncons::jsonpath::detail::static_resources<value_type,reference> static_resources;
+ evaluator_t e;
+ json_selector_t expr = e.compile(static_resources, path);
+
+ jsoncons::jsonpath::detail::dynamic_resources<Json,reference> resources;
+ auto f = [callback](const json_location_type&, reference v)
+ {
+ v = callback(v);
+ };
+ expr.evaluate(resources, instance, resources.root_path_node(), instance, f, result_options::nodups);
+ }
+
+ template<class Json, class BinaryCallback>
+ typename std::enable_if<type_traits::is_binary_function_object<BinaryCallback,const std::basic_string<typename Json::char_type>&,Json&>::value,void>::type
+ json_replace(Json& instance, const typename Json::string_view_type& path , BinaryCallback callback,
+ result_options options = result_options::nodups,
+ const custom_functions<Json>& funcs = custom_functions<Json>())
+ {
+ using evaluator_t = typename jsoncons::jsonpath::detail::jsonpath_evaluator<Json, Json&>;
+ //using string_type = typename evaluator_t::string_type;
+ using value_type = typename evaluator_t::value_type;
+ using reference = typename evaluator_t::reference;
+ using json_selector_t = typename evaluator_t::path_expression_type;
+ using json_location_type = typename evaluator_t::json_location_type;
+
+ jsoncons::jsonpath::detail::static_resources<value_type,reference> static_resources(funcs);
+ evaluator_t e;
+ json_selector_t expr = e.compile(static_resources, path);
+
+ jsoncons::jsonpath::detail::dynamic_resources<Json,reference> resources;
+
+ auto f = [&callback](const json_location_type& path, reference val)
+ {
+ callback(path.to_string(), val);
+ };
+ expr.evaluate(resources, instance, resources.root_path_node(), instance, f, options);
+ }
+
+} // namespace jsonpath
+} // namespace jsoncons
+
+#endif
diff --git a/include/jsoncons_ext/jsonpath/jsonpath.hpp b/include/jsoncons_ext/jsonpath/jsonpath.hpp
new file mode 100644
index 0000000..18500c5
--- /dev/null
+++ b/include/jsoncons_ext/jsonpath/jsonpath.hpp
@@ -0,0 +1,13 @@
+// Copyright 2013 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_JSONPATH_JSONPATH_HPP
+#define JSONCONS_JSONPATH_JSONPATH_HPP
+
+#include <jsoncons_ext/jsonpath/json_query.hpp>
+#include <jsoncons_ext/jsonpath/flatten.hpp>
+
+#endif
diff --git a/include/jsoncons_ext/jsonpath/jsonpath_error.hpp b/include/jsoncons_ext/jsonpath/jsonpath_error.hpp
new file mode 100644
index 0000000..8157bba
--- /dev/null
+++ b/include/jsoncons_ext/jsonpath/jsonpath_error.hpp
@@ -0,0 +1,240 @@
+/// Copyright 2021 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_JSONPATH_JSONPATH_ERROR_HPP
+#define JSONCONS_JSONPATH_JSONPATH_ERROR_HPP
+
+#include <jsoncons/json_exception.hpp>
+#include <system_error>
+
+namespace jsoncons { namespace jsonpath {
+
+ enum class jsonpath_errc
+ {
+ success = 0,
+ expected_root_or_function,
+ expected_current_node,
+ expected_rparen,
+ expected_rbracket,
+ expected_separator,
+ expected_forward_slash,
+ expected_slice_start,
+ expected_slice_end,
+ expected_slice_step,
+ expected_bracket_specifier_or_union,
+ unexpected_operator,
+ invalid_function_name,
+ invalid_argument,
+ invalid_arity,
+ function_name_not_found,
+ parse_error_in_filter,
+ argument_parse_error,
+ unidentified_error,
+ unexpected_eof,
+ expected_colon_dot_left_bracket_comma_or_rbracket,
+ argument_to_unflatten_invalid,
+ invalid_flattened_key,
+ step_cannot_be_zero,
+ invalid_number,
+ illegal_escaped_character,
+ invalid_codepoint,
+ unknown_function,
+ invalid_type,
+ unbalanced_parentheses,
+ syntax_error,
+ expected_comparator,
+ expected_or,
+ expected_and,
+ expected_comma_or_rparen,
+ expected_comma_or_rbracket,
+ expected_relative_path
+ };
+
+ class jsonpath_error_category_impl
+ : public std::error_category
+ {
+ public:
+ const char* name() const noexcept override
+ {
+ return "jsoncons/jsonpath";
+ }
+ std::string message(int ev) const override
+ {
+ switch (static_cast<jsonpath_errc>(ev))
+ {
+ case jsonpath_errc::expected_root_or_function:
+ return "Expected '$' or function expression";
+ case jsonpath_errc::expected_current_node:
+ return "Expected @";
+ case jsonpath_errc::expected_rbracket:
+ return "Expected ]";
+ case jsonpath_errc::expected_rparen:
+ return "Expected )";
+ case jsonpath_errc::expected_slice_start:
+ return "Expected slice start";
+ case jsonpath_errc::expected_slice_end:
+ return "Expected slice end";
+ case jsonpath_errc::expected_slice_step:
+ return "Expected slice step";
+ case jsonpath_errc::expected_separator:
+ return "Expected dot or left bracket separator";
+ case jsonpath_errc::expected_forward_slash:
+ return "Invalid path filter, expected '/'";
+ case jsonpath_errc::expected_bracket_specifier_or_union:
+ return "Expected index, single or double quoted name, expression, filter, absolute ('$') path or relative ('@') path";
+ case jsonpath_errc::invalid_function_name:
+ return "Invalid function name";
+ case jsonpath_errc::invalid_argument:
+ return "Invalid argument type";
+ case jsonpath_errc::invalid_arity:
+ return "Incorrect number of arguments";
+ case jsonpath_errc::function_name_not_found:
+ return "Function name not found";
+ case jsonpath_errc::parse_error_in_filter:
+ return "Could not parse JSON expression in a JSONPath filter";
+ case jsonpath_errc::argument_parse_error:
+ return "Could not parse JSON expression passed to JSONPath function";
+ case jsonpath_errc::unidentified_error:
+ return "Unidentified error";
+ case jsonpath_errc::unexpected_eof:
+ 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:
+ return "Argument to unflatten must be an object";
+ case jsonpath_errc::invalid_flattened_key:
+ return "Flattened key is invalid";
+ case jsonpath_errc::step_cannot_be_zero:
+ return "Slice step cannot be zero";
+ case jsonpath_errc::invalid_number:
+ return "Invalid number";
+ case jsonpath_errc::illegal_escaped_character:
+ return "Illegal escaped character";
+ case jsonpath_errc::invalid_codepoint:
+ return "Invalid codepoint";
+ case jsonpath_errc::unknown_function:
+ return "Unknown function";
+ case jsonpath_errc::invalid_type:
+ return "Invalid type";
+ case jsonpath_errc::unbalanced_parentheses:
+ return "Unbalanced parentheses";
+ case jsonpath_errc::syntax_error:
+ return "Syntax error";
+ case jsonpath_errc::expected_comparator:
+ return "Expected comparator";
+ case jsonpath_errc::expected_or:
+ return "Expected operator '||'";
+ case jsonpath_errc::expected_and:
+ return "Expected operator '&&'";
+ case jsonpath_errc::expected_comma_or_rparen:
+ return "Expected comma or right parenthesis";
+ case jsonpath_errc::expected_comma_or_rbracket:
+ return "Expected comma or right bracket";
+ case jsonpath_errc::expected_relative_path:
+ return "Expected unquoted string, or single or double quoted string, or index or '*'";
+ default:
+ return "Unknown jsonpath parser error";
+ }
+ }
+ };
+
+ inline
+ const std::error_category& jsonpath_error_category()
+ {
+ static jsonpath_error_category_impl instance;
+ return instance;
+ }
+
+ inline
+ std::error_code make_error_code(jsonpath_errc result)
+ {
+ return std::error_code(static_cast<int>(result),jsonpath_error_category());
+ }
+
+} // jsonpath
+} // jsoncons
+
+namespace std {
+ template<>
+ struct is_error_code_enum<jsoncons::jsonpath::jsonpath_errc> : public true_type
+ {
+ };
+}
+
+namespace jsoncons { namespace jsonpath {
+
+ class jsonpath_error : public std::system_error, public virtual json_exception
+ {
+ std::size_t line_number_;
+ std::size_t column_number_;
+ mutable std::string what_;
+ public:
+ jsonpath_error(std::error_code ec)
+ : std::system_error(ec), line_number_(0), column_number_(0)
+ {
+ }
+ jsonpath_error(std::error_code ec, const std::string& what_arg)
+ : std::system_error(ec, what_arg), line_number_(0), column_number_(0)
+ {
+ }
+ jsonpath_error(std::error_code ec, std::size_t position)
+ : std::system_error(ec), line_number_(0), column_number_(position)
+ {
+ }
+ jsonpath_error(std::error_code ec, std::size_t line, std::size_t column)
+ : std::system_error(ec), line_number_(line), column_number_(column)
+ {
+ }
+ jsonpath_error(const jsonpath_error& other) = default;
+
+ jsonpath_error(jsonpath_error&& other) = default;
+
+ const char* what() const noexcept override
+ {
+ 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_;
+ }
+ };
+
+}}
+
+#endif
diff --git a/include/jsoncons_ext/jsonpath/jsonpath_expression.hpp b/include/jsoncons_ext/jsonpath/jsonpath_expression.hpp
new file mode 100644
index 0000000..a3537b3
--- /dev/null
+++ b/include/jsoncons_ext/jsonpath/jsonpath_expression.hpp
@@ -0,0 +1,2612 @@
+// Copyright 2021 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_JSONPATH_JSONPATH_EXPRESSION_HPP
+#define JSONCONS_JSONPATH_JSONPATH_EXPRESSION_HPP
+
+#include <string>
+#include <vector>
+#include <memory>
+#include <type_traits> // std::is_const
+#include <limits> // std::numeric_limits
+#include <utility> // std::move
+#include <regex>
+#include <algorithm> // std::reverse
+#include <jsoncons/json.hpp>
+#include <jsoncons_ext/jsonpath/jsonpath_error.hpp>
+#include <jsoncons_ext/jsonpath/expression.hpp>
+#include <jsoncons_ext/jsonpath/jsonpath_selector.hpp>
+
+namespace jsoncons {
+namespace jsonpath {
+namespace detail {
+
+ enum class path_state
+ {
+ start,
+ root_or_current_node,
+ expect_function_expr,
+ relative_path,
+ relative_location,
+ parent_operator,
+ ancestor_depth,
+ filter_expression,
+ expression_rhs,
+ recursive_descent_or_expression_lhs,
+ path_or_literal_or_function,
+ json_text_or_function,
+ json_text_or_function_name,
+ json_text_string,
+ json_value,
+ json_string,
+ identifier_or_function_expr,
+ name_or_lbracket,
+ unquoted_string,
+ anything,
+ number,
+ function_expression,
+ argument,
+ zero_or_one_arguments,
+ one_or_more_arguments,
+ identifier,
+ single_quoted_string,
+ double_quoted_string,
+ bracketed_unquoted_name_or_union,
+ union_expression,
+ identifier_or_union,
+ bracket_specifier_or_union,
+ bracketed_wildcard,
+ index_or_slice,
+ wildcard_or_union,
+ union_element,
+ index_or_slice_or_union,
+ integer,
+ digit,
+ slice_expression_stop,
+ slice_expression_step,
+ comma_or_rbracket,
+ expect_rparen,
+ expect_rbracket,
+ quoted_string_escape_char,
+ escape_u1,
+ escape_u2,
+ escape_u3,
+ escape_u4,
+ escape_expect_surrogate_pair1,
+ escape_expect_surrogate_pair2,
+ escape_u5,
+ escape_u6,
+ escape_u7,
+ escape_u8,
+ expression,
+ comparator_expression,
+ eq_or_regex,
+ expect_regex,
+ regex,
+ regex_options,
+ regex_pattern,
+ cmp_lt_or_lte,
+ cmp_gt_or_gte,
+ cmp_ne,
+ expect_or,
+ expect_and
+ };
+
+ template<class Json,
+ class JsonReference>
+ class jsonpath_evaluator : public ser_context
+ {
+ public:
+ using char_type = typename Json::char_type;
+ using string_type = std::basic_string<char_type,std::char_traits<char_type>>;
+ using string_view_type = typename Json::string_view_type;
+ using path_value_pair_type = path_value_pair<Json,JsonReference>;
+ using value_type = Json;
+ using reference = JsonReference;
+ using pointer = typename path_value_pair_type::value_pointer;
+ using token_type = token<Json,JsonReference>;
+ using path_expression_type = path_expression<Json,JsonReference>;
+ using expression_type = expression<Json,JsonReference>;
+ using json_location_type = json_location<char_type>;
+ using json_location_node_type = json_location_node<char_type>;
+ using selector_type = jsonpath_selector<Json,JsonReference>;
+
+ private:
+
+ std::size_t line_;
+ std::size_t column_;
+ const char_type* begin_input_;
+ const char_type* end_input_;
+ const char_type* p_;
+
+ using argument_type = std::vector<pointer>;
+ std::vector<argument_type> function_stack_;
+ std::vector<path_state> state_stack_;
+ std::vector<token_type> output_stack_;
+ std::vector<token_type> operator_stack_;
+
+ public:
+ jsonpath_evaluator()
+ : line_(1), column_(1),
+ begin_input_(nullptr), end_input_(nullptr),
+ p_(nullptr)
+ {
+ }
+
+ jsonpath_evaluator(std::size_t line, std::size_t column)
+ : line_(line), column_(column),
+ begin_input_(nullptr), end_input_(nullptr),
+ p_(nullptr)
+ {
+ }
+
+ std::size_t line() const
+ {
+ return line_;
+ }
+
+ std::size_t column() const
+ {
+ return column_;
+ }
+
+ path_expression_type compile(static_resources<value_type,reference>& resources, const string_view_type& path)
+ {
+ std::error_code ec;
+ auto result = compile(resources, path, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(jsonpath_error(ec, line_, column_));
+ }
+ return result;
+ }
+
+ path_expression_type compile(static_resources<value_type,reference>& resources,
+ const string_view_type& path,
+ std::error_code& ec)
+ {
+ std::size_t selector_id = 0;
+
+ string_type buffer;
+ string_type buffer2;
+ uint32_t cp = 0;
+ uint32_t cp2 = 0;
+
+ begin_input_ = path.data();
+ end_input_ = path.data() + path.length();
+ p_ = begin_input_;
+
+ slice slic;
+ bool paths_required = false;
+ int ancestor_depth = 0;
+
+ state_stack_.emplace_back(path_state::start);
+ while (p_ < end_input_ && !state_stack_.empty())
+ {
+ switch (state_stack_.back())
+ {
+ case path_state::start:
+ {
+ switch (*p_)
+ {
+ case ' ':case '\t':case '\r':case '\n':
+ advance_past_space_character();
+ break;
+ case '$':
+ case '@':
+ {
+ push_token(resources, token_type(resources.new_selector(current_node_selector<Json,JsonReference>())), ec);
+ if (ec) {return path_expression_type();}
+ state_stack_.emplace_back(path_state::relative_location);
+ ++p_;
+ ++column_;
+ break;
+ }
+ default:
+ {
+ state_stack_.emplace_back(path_state::relative_location);
+ state_stack_.emplace_back(path_state::expect_function_expr);
+ state_stack_.emplace_back(path_state::unquoted_string);
+ break;
+ }
+ }
+ break;
+ }
+ case path_state::root_or_current_node:
+ switch (*p_)
+ {
+ case ' ':case '\t':case '\r':case '\n':
+ advance_past_space_character();
+ break;
+ case '$':
+ push_token(resources, token_type(root_node_arg), ec);
+ push_token(resources, token_type(resources.new_selector(root_selector<Json,JsonReference>(selector_id++))), ec);
+ if (ec) {return path_expression_type();}
+ state_stack_.pop_back();
+ ++p_;
+ ++column_;
+ break;
+ case '@':
+ push_token(resources, token_type(current_node_arg), ec); // ISSUE
+ push_token(resources, token_type(resources.new_selector(current_node_selector<Json,JsonReference>())), ec);
+ if (ec) {return path_expression_type();}
+ state_stack_.pop_back();
+ ++p_;
+ ++column_;
+ break;
+ default:
+ ec = jsonpath_errc::syntax_error;
+ return path_expression_type();
+ }
+ break;
+ case path_state::recursive_descent_or_expression_lhs:
+ switch (*p_)
+ {
+ case '.':
+ push_token(resources, token_type(resources.new_selector(recursive_selector<Json,JsonReference>())), ec);
+ if (ec) {return path_expression_type();}
+ ++p_;
+ ++column_;
+ state_stack_.back() = path_state::name_or_lbracket;
+ break;
+ default:
+ state_stack_.back() = path_state::relative_path;
+ break;
+ }
+ break;
+ case path_state::name_or_lbracket:
+ switch (*p_)
+ {
+ case ' ':case '\t':case '\r':case '\n':
+ advance_past_space_character();
+ break;
+ case '[': // [ can follow ..
+ state_stack_.back() = path_state::bracket_specifier_or_union;
+ ++p_;
+ ++column_;
+ break;
+ default:
+ buffer.clear();
+ state_stack_.back() = path_state::relative_path;
+ break;
+ }
+ break;
+ case path_state::json_string:
+ {
+ //std::cout << "literal: " << buffer << "\n";
+ push_token(resources, token_type(literal_arg, Json(buffer)), ec);
+ if (ec) {return path_expression_type();}
+ buffer.clear();
+ state_stack_.pop_back(); // json_value
+ break;
+ }
+ case path_state::path_or_literal_or_function:
+ {
+ switch (*p_)
+ {
+ case ' ':case '\t':case '\r':case '\n':
+ advance_past_space_character();
+ break;
+ case '$':
+ case '@':
+ state_stack_.back() = path_state::relative_location;
+ state_stack_.push_back(path_state::root_or_current_node);
+ break;
+ case '(':
+ {
+ ++p_;
+ ++column_;
+ push_token(resources, lparen_arg, ec);
+ if (ec) {return path_expression_type();}
+ state_stack_.back() = path_state::expect_rparen;
+ state_stack_.emplace_back(path_state::expression_rhs);
+ state_stack_.emplace_back(path_state::path_or_literal_or_function);
+ break;
+ }
+ case '\'':
+ state_stack_.back() = path_state::json_string;
+ state_stack_.emplace_back(path_state::single_quoted_string);
+ ++p_;
+ ++column_;
+ break;
+ case '\"':
+ state_stack_.back() = path_state::json_string;
+ state_stack_.emplace_back(path_state::double_quoted_string);
+ ++p_;
+ ++column_;
+ break;
+ case '!':
+ {
+ ++p_;
+ ++column_;
+ push_token(resources, token_type(resources.get_unary_not()), ec);
+ if (ec) {return path_expression_type();}
+ break;
+ }
+ case '-':
+ {
+ ++p_;
+ ++column_;
+ push_token(resources, token_type(resources.get_unary_minus()), ec);
+ if (ec) {return path_expression_type();}
+ break;
+ }
+ case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9':
+ {
+ state_stack_.back() = path_state::json_value;
+ state_stack_.emplace_back(path_state::number);
+ break;
+ }
+ default:
+ {
+ state_stack_.back() = path_state::json_text_or_function_name;
+ break;
+ }
+ }
+ break;
+ }
+ case path_state::json_text_or_function:
+ {
+ switch(*p_)
+ {
+ case '(':
+ {
+ auto f = resources.get_function(buffer, ec);
+ if (ec)
+ {
+ return path_expression_type();
+ }
+ buffer.clear();
+ push_token(resources, current_node_arg, ec);
+ if (ec) {return path_expression_type();}
+ push_token(resources, token_type(f), ec);
+ if (ec) {return path_expression_type();}
+ state_stack_.back() = path_state::function_expression;
+ state_stack_.emplace_back(path_state::zero_or_one_arguments);
+ ++p_;
+ ++column_;
+ break;
+ }
+ default:
+ {
+ json_decoder<Json> decoder;
+ basic_json_parser<char_type> parser;
+ parser.update(buffer.data(),buffer.size());
+ parser.parse_some(decoder, ec);
+ if (ec)
+ {
+ return path_expression_type();
+ }
+ parser.finish_parse(decoder, ec);
+ if (ec)
+ {
+ return path_expression_type();
+ }
+ push_token(resources, token_type(literal_arg, decoder.get_result()), ec);
+ if (ec) {return path_expression_type();}
+ buffer.clear();
+ state_stack_.pop_back();
+ break;
+ }
+ }
+ break;
+ }
+ case path_state::json_value:
+ {
+ json_decoder<Json> decoder;
+ basic_json_parser<char_type> parser;
+ parser.update(buffer.data(),buffer.size());
+ parser.parse_some(decoder, ec);
+ if (ec)
+ {
+ return path_expression_type();
+ }
+ parser.finish_parse(decoder, ec);
+ if (ec)
+ {
+ return path_expression_type();
+ }
+ push_token(resources, token_type(literal_arg, decoder.get_result()), ec);
+ if (ec) {return path_expression_type();}
+ buffer.clear();
+ state_stack_.pop_back();
+ break;
+ }
+ case path_state::json_text_or_function_name:
+ switch (*p_)
+ {
+ case ' ':case '\t':case '\r':case '\n':
+ advance_past_space_character();
+ break;
+ case '{':
+ case '[':
+ {
+ json_decoder<Json> decoder;
+ basic_json_parser<char_type> parser;
+ parser.update(p_,end_input_ - p_);
+ parser.parse_some(decoder, ec);
+ if (ec)
+ {
+ return path_expression_type();
+ }
+ parser.finish_parse(decoder, ec);
+ if (ec)
+ {
+ return path_expression_type();
+ }
+ push_token(resources, token_type(literal_arg, decoder.get_result()), ec);
+ if (ec) {return path_expression_type();}
+ buffer.clear();
+ state_stack_.pop_back();
+ p_ = parser.current();
+ column_ = column_ + parser.column() - 1;
+ break;
+ }
+ case '-':case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9':
+ state_stack_.back() = path_state::json_text_or_function;
+ state_stack_.emplace_back(path_state::number);
+ buffer.push_back(*p_);
+ ++p_;
+ ++column_;
+ break;
+ case '\"':
+ state_stack_.back() = path_state::json_text_or_function;
+ state_stack_.emplace_back(path_state::json_text_string);
+ buffer.push_back(*p_);
+ ++p_;
+ ++column_;
+ break;
+ default:
+ state_stack_.back() = path_state::json_text_or_function;
+ state_stack_.emplace_back(path_state::unquoted_string);
+ buffer.push_back(*p_);
+ ++p_;
+ ++column_;
+ break;
+ };
+ break;
+ case path_state::number:
+ switch (*p_)
+ {
+ case '-':case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9':
+ case 'e':case 'E':case '.':
+ buffer.push_back(*p_);
+ ++p_;
+ ++column_;
+ break;
+ default:
+ state_stack_.pop_back(); // number
+ break;
+ };
+ break;
+ case path_state::json_text_string:
+ switch (*p_)
+ {
+ case '\\':
+ buffer.push_back(*p_);
+ ++p_;
+ ++column_;
+ if (p_ == end_input_)
+ {
+ ec = jsonpath_errc::unexpected_eof;
+ return path_expression_type();
+ }
+ buffer.push_back(*p_);
+ ++p_;
+ ++column_;
+ break;
+ case '\"':
+ buffer.push_back(*p_);
+ state_stack_.pop_back();
+ ++p_;
+ ++column_;
+ break;
+ default:
+ buffer.push_back(*p_);
+ ++p_;
+ ++column_;
+ break;
+ };
+ break;
+ case path_state::relative_path:
+ switch (*p_)
+ {
+ case ' ':case '\t':case '\r':case '\n':
+ advance_past_space_character();
+ break;
+ case '*':
+ push_token(resources, token_type(resources.new_selector(wildcard_selector<Json,JsonReference>())), ec);
+ if (ec) {return path_expression_type();}
+ state_stack_.pop_back();
+ ++p_;
+ ++column_;
+ break;
+ case '\'':
+ state_stack_.back() = path_state::identifier;
+ state_stack_.emplace_back(path_state::single_quoted_string);
+ ++p_;
+ ++column_;
+ break;
+ case '\"':
+ state_stack_.back() = path_state::identifier;
+ state_stack_.emplace_back(path_state::double_quoted_string);
+ ++p_;
+ ++column_;
+ break;
+ case '[':
+ case '.':
+ ec = jsonpath_errc::expected_relative_path;
+ return path_expression_type();
+ default:
+ buffer.clear();
+ state_stack_.back() = path_state::identifier_or_function_expr;
+ state_stack_.emplace_back(path_state::unquoted_string);
+ break;
+ }
+ break;
+ case path_state::identifier_or_function_expr:
+ {
+ switch(*p_)
+ {
+ case ' ':case '\t':case '\r':case '\n':
+ advance_past_space_character();
+ break;
+ case '(':
+ {
+ auto f = resources.get_function(buffer, ec);
+ if (ec)
+ {
+ return path_expression_type();
+ }
+ buffer.clear();
+ push_token(resources, current_node_arg, ec);
+ push_token(resources, token_type(f), ec);
+ if (ec) {return path_expression_type();}
+ state_stack_.back() = path_state::function_expression;
+ state_stack_.emplace_back(path_state::zero_or_one_arguments);
+ ++p_;
+ ++column_;
+ break;
+ }
+ default:
+ {
+ push_token(resources, token_type(resources.new_selector(identifier_selector<Json,JsonReference>(buffer))), ec);
+ if (ec) {return path_expression_type();}
+ buffer.clear();
+ state_stack_.pop_back();
+ break;
+ }
+ }
+ break;
+ }
+ case path_state::expect_function_expr:
+ {
+ switch(*p_)
+ {
+ case ' ':case '\t':case '\r':case '\n':
+ advance_past_space_character();
+ break;
+ case '(':
+ {
+ auto f = resources.get_function(buffer, ec);
+ if (ec)
+ {
+ return path_expression_type();
+ }
+ buffer.clear();
+ push_token(resources, current_node_arg, ec);
+ push_token(resources, token_type(f), ec);
+ if (ec) {return path_expression_type();}
+ state_stack_.back() = path_state::function_expression;
+ state_stack_.emplace_back(path_state::zero_or_one_arguments);
+ ++p_;
+ ++column_;
+ break;
+ }
+ default:
+ {
+ ec = jsonpath_errc::expected_root_or_function;
+ return path_expression_type();
+ }
+ }
+ break;
+ }
+ case path_state::function_expression:
+ {
+
+ switch (*p_)
+ {
+ case ' ':case '\t':case '\r':case '\n':
+ advance_past_space_character();
+ break;
+ case ',':
+ push_token(resources, token_type(current_node_arg), ec);
+ if (ec) {return path_expression_type();}
+ push_token(resources, token_type(begin_expression_arg), ec);
+ if (ec) {return path_expression_type();}
+ if (ec) {return path_expression_type();}
+ state_stack_.emplace_back(path_state::argument);
+ state_stack_.emplace_back(path_state::expression_rhs);
+ state_stack_.emplace_back(path_state::path_or_literal_or_function);
+ ++p_;
+ ++column_;
+ break;
+ case ')':
+ {
+ push_token(resources, token_type(end_function_arg), ec);
+ if (ec) {return path_expression_type();}
+ state_stack_.pop_back();
+ ++p_;
+ ++column_;
+ break;
+ }
+ default:
+ ec = jsonpath_errc::syntax_error;
+ return path_expression_type();
+ }
+ break;
+ }
+ case path_state::zero_or_one_arguments:
+ {
+ switch (*p_)
+ {
+ case ' ':case '\t':case '\r':case '\n':
+ advance_past_space_character();
+ break;
+ case ')':
+ state_stack_.pop_back();
+ break;
+ default:
+ push_token(resources, token_type(begin_expression_arg), ec);
+ if (ec) {return path_expression_type();}
+ state_stack_.back() = path_state::one_or_more_arguments;
+ state_stack_.emplace_back(path_state::argument);
+ state_stack_.emplace_back(path_state::expression_rhs);
+ state_stack_.emplace_back(path_state::path_or_literal_or_function);
+ break;
+ }
+ break;
+ }
+ case path_state::one_or_more_arguments:
+ {
+ switch (*p_)
+ {
+ case ' ':case '\t':case '\r':case '\n':
+ advance_past_space_character();
+ break;
+ case ')':
+ state_stack_.pop_back();
+ break;
+ case ',':
+ push_token(resources, token_type(begin_expression_arg), ec);
+ if (ec) {return path_expression_type();}
+ state_stack_.emplace_back(path_state::argument);
+ state_stack_.emplace_back(path_state::expression_rhs);
+ state_stack_.emplace_back(path_state::path_or_literal_or_function);
+ ++p_;
+ ++column_;
+ break;
+ }
+ break;
+ }
+ case path_state::argument:
+ {
+ switch(*p_)
+ {
+ case ' ':case '\t':case '\r':case '\n':
+ advance_past_space_character();
+ break;
+ case ',':
+ case ')':
+ {
+ push_token(resources, token_type(end_argument_expression_arg), ec);
+ push_token(resources, argument_arg, ec);
+ //push_token(resources, argument_arg, ec);
+ if (ec) {return path_expression_type();}
+ state_stack_.pop_back();
+ break;
+ }
+ default:
+ ec = jsonpath_errc::expected_comma_or_rparen;
+ return path_expression_type();
+ }
+ break;
+ }
+ case path_state::unquoted_string:
+ switch (*p_)
+ {
+ case 'a':case 'b':case 'c':case 'd':case 'e':case 'f':case 'g':case 'h':case 'i':case 'j':case 'k':case 'l':case 'm':case 'n':case 'o':case 'p':case 'q':case 'r':case 's':case 't':case 'u':case 'v':case 'w':case 'x':case 'y':case 'z':
+ case 'A':case 'B':case 'C':case 'D':case 'E':case 'F':case 'G':case 'H':case 'I':case 'J':case 'K':case 'L':case 'M':case 'N':case 'O':case 'P':case 'Q':case 'R':case 'S':case 'T':case 'U':case 'V':case 'W':case 'X':case 'Y':case 'Z':
+ case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9':
+ case '_':
+ buffer.push_back(*p_);
+ ++p_;
+ ++column_;
+ break;
+ default:
+ if (typename std::make_unsigned<char_type>::type(*p_) > 127)
+ {
+ buffer.push_back(*p_);
+ ++p_;
+ ++column_;
+ }
+ else
+ {
+ state_stack_.pop_back(); // unquoted_string
+ }
+ break;
+ };
+ break;
+ case path_state::relative_location:
+ switch (*p_)
+ {
+ case ' ':case '\t':case '\r':case '\n':
+ advance_past_space_character();
+ break;
+ case '.':
+ state_stack_.emplace_back(path_state::recursive_descent_or_expression_lhs);
+ ++p_;
+ ++column_;
+ break;
+ case '[':
+ state_stack_.emplace_back(path_state::bracket_specifier_or_union);
+ ++p_;
+ ++column_;
+ break;
+ case '^':
+ ancestor_depth = 0;
+ state_stack_.emplace_back(path_state::parent_operator);
+ state_stack_.emplace_back(path_state::ancestor_depth);
+ break;
+ default:
+ state_stack_.pop_back();
+ break;
+ };
+ break;
+ case path_state::parent_operator:
+ {
+ push_token(resources, token_type(resources.new_selector(parent_node_selector<Json,JsonReference>(ancestor_depth))), ec);
+ paths_required = true;
+ ancestor_depth = 0;
+ ++p_;
+ ++column_;
+ state_stack_.pop_back();
+ break;
+ }
+ case path_state::ancestor_depth:
+ {
+ switch (*p_)
+ {
+ case ' ':case '\t':case '\r':case '\n':
+ advance_past_space_character();
+ break;
+ case '^':
+ {
+ ++ancestor_depth;
+ ++p_;
+ ++column_;
+ break;
+ }
+ default:
+ {
+ state_stack_.pop_back();
+ break;
+ }
+ }
+ break;
+ }
+ case path_state::expression_rhs:
+ switch (*p_)
+ {
+ case ' ':case '\t':case '\r':case '\n':
+ advance_past_space_character();
+ break;
+ case '.':
+ state_stack_.emplace_back(path_state::recursive_descent_or_expression_lhs);
+ ++p_;
+ ++column_;
+ break;
+ case '[':
+ state_stack_.emplace_back(path_state::bracket_specifier_or_union);
+ ++p_;
+ ++column_;
+ break;
+ case ')':
+ {
+ state_stack_.pop_back();
+ break;
+ }
+ case '|':
+ ++p_;
+ ++column_;
+ state_stack_.emplace_back(path_state::path_or_literal_or_function);
+ state_stack_.emplace_back(path_state::expect_or);
+ break;
+ case '&':
+ ++p_;
+ ++column_;
+ state_stack_.emplace_back(path_state::path_or_literal_or_function);
+ state_stack_.emplace_back(path_state::expect_and);
+ break;
+ case '<':
+ case '>':
+ {
+ state_stack_.emplace_back(path_state::comparator_expression);
+ break;
+ }
+ case '=':
+ {
+ state_stack_.emplace_back(path_state::eq_or_regex);
+ ++p_;
+ ++column_;
+ break;
+ }
+ case '!':
+ {
+ ++p_;
+ ++column_;
+ state_stack_.emplace_back(path_state::path_or_literal_or_function);
+ state_stack_.emplace_back(path_state::cmp_ne);
+ break;
+ }
+ case '+':
+ state_stack_.emplace_back(path_state::path_or_literal_or_function);
+ push_token(resources, token_type(resources.get_plus_operator()), ec);
+ if (ec) {return path_expression_type();}
+ ++p_;
+ ++column_;
+ break;
+ case '-':
+ state_stack_.emplace_back(path_state::path_or_literal_or_function);
+ push_token(resources, token_type(resources.get_minus_operator()), ec);
+ if (ec) {return path_expression_type();}
+ ++p_;
+ ++column_;
+ break;
+ case '*':
+ state_stack_.emplace_back(path_state::path_or_literal_or_function);
+ push_token(resources, token_type(resources.get_mult_operator()), ec);
+ if (ec) {return path_expression_type();}
+ ++p_;
+ ++column_;
+ break;
+ case '/':
+ state_stack_.emplace_back(path_state::path_or_literal_or_function);
+ push_token(resources, token_type(resources.get_div_operator()), ec);
+ if (ec) {return path_expression_type();}
+ ++p_;
+ ++column_;
+ break;
+ case '%':
+ state_stack_.emplace_back(path_state::path_or_literal_or_function);
+ push_token(resources, token_type(resources.get_modulus_operator()), ec);
+ if (ec) {return path_expression_type();}
+ ++p_;
+ ++column_;
+ break;
+ case ']':
+ case ',':
+ state_stack_.pop_back();
+ break;
+ default:
+ ec = jsonpath_errc::expected_separator;
+ return path_expression_type();
+ };
+ break;
+ case path_state::expect_or:
+ {
+ switch (*p_)
+ {
+ case '|':
+ push_token(resources, token_type(resources.get_or_operator()), ec);
+ if (ec) {return path_expression_type();}
+ state_stack_.pop_back();
+ ++p_;
+ ++column_;
+ break;
+ default:
+ ec = jsonpath_errc::expected_or;
+ return path_expression_type();
+ }
+ break;
+ }
+ case path_state::expect_and:
+ {
+ switch(*p_)
+ {
+ case '&':
+ push_token(resources, token_type(resources.get_and_operator()), ec);
+ if (ec) {return path_expression_type();}
+ state_stack_.pop_back(); // expect_and
+ ++p_;
+ ++column_;
+ break;
+ default:
+ ec = jsonpath_errc::expected_and;
+ return path_expression_type();
+ }
+ break;
+ }
+ case path_state::comparator_expression:
+ switch(*p_)
+ {
+ case ' ':case '\t':case '\r':case '\n':
+ advance_past_space_character();
+ break;
+ case '<':
+ ++p_;
+ ++column_;
+ state_stack_.back() = path_state::path_or_literal_or_function;
+ state_stack_.emplace_back(path_state::cmp_lt_or_lte);
+ break;
+ case '>':
+ ++p_;
+ ++column_;
+ state_stack_.back() = path_state::path_or_literal_or_function;
+ state_stack_.emplace_back(path_state::cmp_gt_or_gte);
+ break;
+ default:
+ if (state_stack_.size() > 1)
+ {
+ state_stack_.pop_back();
+ }
+ else
+ {
+ ec = jsonpath_errc::syntax_error;
+ return path_expression_type();
+ }
+ break;
+ }
+ break;
+ case path_state::eq_or_regex:
+ switch(*p_)
+ {
+ case ' ':case '\t':case '\r':case '\n':
+ advance_past_space_character();
+ break;
+ case '=':
+ {
+ push_token(resources, token_type(resources.get_eq_operator()), ec);
+ if (ec) {return path_expression_type();}
+ state_stack_.back() = path_state::path_or_literal_or_function;
+ ++p_;
+ ++column_;
+ break;
+ }
+ case '~':
+ {
+ ++p_;
+ ++column_;
+ state_stack_.emplace_back(path_state::expect_regex);
+ break;
+ }
+ default:
+ if (state_stack_.size() > 1)
+ {
+ state_stack_.pop_back();
+ }
+ else
+ {
+ ec = jsonpath_errc::syntax_error;
+ return path_expression_type();
+ }
+ break;
+ }
+ break;
+ case path_state::expect_regex:
+ switch (*p_)
+ {
+ case ' ':case '\t':case '\r':case '\n':
+ advance_past_space_character();
+ break;
+ case '/':
+ state_stack_.back() = path_state::regex;
+ state_stack_.emplace_back(path_state::regex_options);
+ state_stack_.emplace_back(path_state::regex_pattern);
+ ++p_;
+ ++column_;
+ break;
+ default:
+ ec = jsonpath_errc::expected_forward_slash;
+ return path_expression_type();
+ };
+ break;
+ case path_state::regex:
+ {
+ std::regex::flag_type options = std::regex_constants::ECMAScript;
+ if (buffer2.find('i') != string_type::npos)
+ {
+ options |= std::regex_constants::icase;
+ }
+ std::basic_regex<char_type> pattern(buffer, options);
+ push_token(resources, resources.get_regex_operator(std::move(pattern)), ec);
+ if (ec) {return path_expression_type();}
+ buffer.clear();
+ buffer2.clear();
+ state_stack_.pop_back();
+ break;
+ }
+ case path_state::regex_pattern:
+ {
+ switch (*p_)
+ {
+ case '/':
+ {
+ state_stack_.pop_back();
+ ++p_;
+ ++column_;
+ }
+ break;
+
+ default:
+ buffer.push_back(*p_);
+ ++p_;
+ ++column_;
+ break;
+ }
+ break;
+ }
+ case path_state::regex_options:
+ {
+ if (*p_ == 'i')
+ {
+ buffer2.push_back(*p_);
+ ++p_;
+ ++column_;
+ }
+ else
+ {
+ state_stack_.pop_back();
+ }
+ break;
+ }
+ case path_state::cmp_lt_or_lte:
+ {
+ switch(*p_)
+ {
+ case '=':
+ push_token(resources, token_type(resources.get_lte_operator()), ec);
+ if (ec) {return path_expression_type();}
+ state_stack_.pop_back();
+ ++p_;
+ ++column_;
+ break;
+ default:
+ push_token(resources, token_type(resources.get_lt_operator()), ec);
+ if (ec) {return path_expression_type();}
+ state_stack_.pop_back();
+ break;
+ }
+ break;
+ }
+ case path_state::cmp_gt_or_gte:
+ {
+ switch(*p_)
+ {
+ case '=':
+ push_token(resources, token_type(resources.get_gte_operator()), ec);
+ if (ec) {return path_expression_type();}
+ state_stack_.pop_back();
+ ++p_;
+ ++column_;
+ break;
+ default:
+ //std::cout << "Parse: gt_operator\n";
+ push_token(resources, token_type(resources.get_gt_operator()), ec);
+ if (ec) {return path_expression_type();}
+ state_stack_.pop_back();
+ break;
+ }
+ break;
+ }
+ case path_state::cmp_ne:
+ {
+ switch(*p_)
+ {
+ case '=':
+ push_token(resources, token_type(resources.get_ne_operator()), ec);
+ if (ec) {return path_expression_type();}
+ state_stack_.pop_back();
+ ++p_;
+ ++column_;
+ break;
+ default:
+ ec = jsonpath_errc::expected_comparator;
+ return path_expression_type();
+ }
+ break;
+ }
+ case path_state::identifier:
+ push_token(resources, token_type(resources.new_selector(identifier_selector<Json,JsonReference>(buffer))), ec);
+ if (ec) {return path_expression_type();}
+ buffer.clear();
+ state_stack_.pop_back();
+ break;
+ case path_state::single_quoted_string:
+ switch (*p_)
+ {
+ case '\'':
+ state_stack_.pop_back();
+ ++p_;
+ ++column_;
+ break;
+ case '\\':
+ state_stack_.emplace_back(path_state::quoted_string_escape_char);
+ ++p_;
+ ++column_;
+ break;
+ default:
+ buffer.push_back(*p_);
+ ++p_;
+ ++column_;
+ break;
+ };
+ break;
+ case path_state::double_quoted_string:
+ switch (*p_)
+ {
+ case '\"':
+ state_stack_.pop_back();
+ ++p_;
+ ++column_;
+ break;
+ case '\\':
+ state_stack_.emplace_back(path_state::quoted_string_escape_char);
+ ++p_;
+ ++column_;
+ break;
+ default:
+ buffer.push_back(*p_);
+ ++p_;
+ ++column_;
+ break;
+ };
+ break;
+ case path_state::comma_or_rbracket:
+ switch (*p_)
+ {
+ case ' ':case '\t':case '\r':case '\n':
+ advance_past_space_character();
+ break;
+ case ',':
+ state_stack_.back() = path_state::bracket_specifier_or_union;
+ ++p_;
+ ++column_;
+ break;
+ case ']':
+ state_stack_.pop_back();
+ ++p_;
+ ++column_;
+ break;
+ default:
+ ec = jsonpath_errc::expected_comma_or_rbracket;
+ return path_expression_type();
+ }
+ break;
+ case path_state::expect_rbracket:
+ switch (*p_)
+ {
+ case ' ':case '\t':case '\r':case '\n':
+ advance_past_space_character();
+ break;
+ case ']':
+ state_stack_.pop_back();
+ ++p_;
+ ++column_;
+ break;
+ default:
+ ec = jsonpath_errc::expected_rbracket;
+ return path_expression_type();
+ }
+ break;
+ case path_state::expect_rparen:
+ switch (*p_)
+ {
+ case ' ':case '\t':case '\r':case '\n':
+ advance_past_space_character();
+ break;
+ case ')':
+ ++p_;
+ ++column_;
+ push_token(resources, rparen_arg, ec);
+ if (ec) {return path_expression_type();}
+ state_stack_.back() = path_state::expression_rhs;
+ break;
+ default:
+ ec = jsonpath_errc::expected_rparen;
+ return path_expression_type();
+ }
+ break;
+ case path_state::bracket_specifier_or_union:
+ switch (*p_)
+ {
+ case ' ':case '\t':case '\r':case '\n':
+ advance_past_space_character();
+ break;
+ case '(':
+ {
+ push_token(resources, token_type(begin_union_arg), ec);
+ push_token(resources, token_type(begin_expression_arg), ec);
+ push_token(resources, lparen_arg, ec);
+ if (ec) {return path_expression_type();}
+ state_stack_.back() = path_state::union_expression; // union
+ state_stack_.emplace_back(path_state::expression);
+ state_stack_.emplace_back(path_state::expect_rparen);
+ state_stack_.emplace_back(path_state::expression_rhs);
+ state_stack_.emplace_back(path_state::path_or_literal_or_function);
+ ++p_;
+ ++column_;
+ break;
+ }
+ case '?':
+ {
+ push_token(resources, token_type(begin_union_arg), ec);
+ push_token(resources, token_type(begin_filter_arg), ec);
+ if (ec) {return path_expression_type();}
+ state_stack_.back() = path_state::union_expression; // union
+ state_stack_.emplace_back(path_state::filter_expression);
+ state_stack_.emplace_back(path_state::expression_rhs);
+ state_stack_.emplace_back(path_state::path_or_literal_or_function);
+ ++p_;
+ ++column_;
+ break;
+ }
+ case '*':
+ state_stack_.back() = path_state::wildcard_or_union;
+ ++p_;
+ ++column_;
+ break;
+ case '\'':
+ state_stack_.back() = path_state::identifier_or_union;
+ state_stack_.push_back(path_state::single_quoted_string);
+ ++p_;
+ ++column_;
+ break;
+ case '\"':
+ state_stack_.back() = path_state::identifier_or_union;
+ state_stack_.push_back(path_state::double_quoted_string);
+ ++p_;
+ ++column_;
+ break;
+ case ':': // slice_expression
+ state_stack_.back() = path_state::index_or_slice_or_union;
+ break;
+ case '-':case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9':
+ state_stack_.back() = path_state::index_or_slice_or_union;
+ state_stack_.emplace_back(path_state::integer);
+ break;
+ case '$':
+ push_token(resources, token_type(begin_union_arg), ec);
+ push_token(resources, root_node_arg, ec);
+ if (ec) {return path_expression_type();}
+ state_stack_.back() = path_state::union_expression; // union
+ state_stack_.emplace_back(path_state::relative_location);
+ ++p_;
+ ++column_;
+ break;
+ case '@':
+ push_token(resources, token_type(begin_union_arg), ec);
+ push_token(resources, token_type(current_node_arg), ec); // ISSUE
+ push_token(resources, token_type(resources.new_selector(current_node_selector<Json,JsonReference>())), ec);
+ if (ec) {return path_expression_type();}
+ state_stack_.back() = path_state::union_expression; // union
+ state_stack_.emplace_back(path_state::relative_location);
+ ++p_;
+ ++column_;
+ break;
+ default:
+ ec = jsonpath_errc::expected_bracket_specifier_or_union;
+ return path_expression_type();
+ }
+ break;
+ case path_state::union_element:
+ switch (*p_)
+ {
+ case ' ':case '\t':case '\r':case '\n':
+ advance_past_space_character();
+ break;
+ case ':': // slice_expression
+ state_stack_.back() = path_state::index_or_slice;
+ break;
+ case '-':case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9':
+ state_stack_.back() = path_state::index_or_slice;
+ state_stack_.emplace_back(path_state::integer);
+ break;
+ case '(':
+ {
+ push_token(resources, token_type(begin_expression_arg), ec);
+ push_token(resources, lparen_arg, ec);
+ if (ec) {return path_expression_type();}
+ state_stack_.back() = path_state::expression;
+ state_stack_.emplace_back(path_state::expect_rparen);
+ state_stack_.emplace_back(path_state::expression_rhs);
+ state_stack_.emplace_back(path_state::path_or_literal_or_function);
+ ++p_;
+ ++column_;
+ break;
+ }
+ case '?':
+ {
+ push_token(resources, token_type(begin_filter_arg), ec);
+ if (ec) {return path_expression_type();}
+ state_stack_.back() = path_state::filter_expression;
+ state_stack_.emplace_back(path_state::expression_rhs);
+ state_stack_.emplace_back(path_state::path_or_literal_or_function);
+ ++p_;
+ ++column_;
+ break;
+ }
+ case '*':
+ push_token(resources, token_type(resources.new_selector(wildcard_selector<Json,JsonReference>())), ec);
+ if (ec) {return path_expression_type();}
+ state_stack_.back() = path_state::relative_location;
+ ++p_;
+ ++column_;
+ break;
+ case '$':
+ push_token(resources, token_type(root_node_arg), ec);
+ push_token(resources, token_type(resources.new_selector(root_selector<Json,JsonReference>(selector_id++))), ec);
+ if (ec) {return path_expression_type();}
+ state_stack_.back() = path_state::relative_location;
+ ++p_;
+ ++column_;
+ break;
+ case '@':
+ push_token(resources, token_type(current_node_arg), ec); // ISSUE
+ push_token(resources, token_type(resources.new_selector(current_node_selector<Json,JsonReference>())), ec);
+ if (ec) {return path_expression_type();}
+ state_stack_.back() = path_state::relative_location;
+ ++p_;
+ ++column_;
+ break;
+ case '\'':
+ state_stack_.back() = path_state::identifier;
+ state_stack_.push_back(path_state::single_quoted_string);
+ ++p_;
+ ++column_;
+ break;
+ case '\"':
+ state_stack_.back() = path_state::identifier;
+ state_stack_.push_back(path_state::double_quoted_string);
+ ++p_;
+ ++column_;
+ break;
+ default:
+ ec = jsonpath_errc::expected_bracket_specifier_or_union;
+ return path_expression_type();
+ }
+ break;
+
+ case path_state::integer:
+ switch(*p_)
+ {
+ case '-':
+ buffer.push_back(*p_);
+ state_stack_.back() = path_state::digit;
+ ++p_;
+ ++column_;
+ break;
+ default:
+ state_stack_.back() = path_state::digit;
+ break;
+ }
+ break;
+ case path_state::digit:
+ switch(*p_)
+ {
+ case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9':
+ buffer.push_back(*p_);
+ ++p_;
+ ++column_;
+ break;
+ default:
+ state_stack_.pop_back(); // digit
+ break;
+ }
+ break;
+ case path_state::index_or_slice_or_union:
+ switch(*p_)
+ {
+ case ' ':case '\t':case '\r':case '\n':
+ advance_past_space_character();
+ break;
+ case ']':
+ {
+ if (buffer.empty())
+ {
+ ec = jsonpath_errc::invalid_number;
+ return path_expression_type();
+ }
+ int64_t n{0};
+ auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), n);
+ if (!r)
+ {
+ ec = jsonpath_errc::invalid_number;
+ return path_expression_type();
+ }
+ push_token(resources, token_type(resources.new_selector(index_selector<Json,JsonReference>(n))), ec);
+ if (ec) {return path_expression_type();}
+ buffer.clear();
+ state_stack_.pop_back(); // index_or_slice_or_union
+ ++p_;
+ ++column_;
+ break;
+ }
+ case ',':
+ {
+ push_token(resources, token_type(begin_union_arg), ec);
+ if (ec) {return path_expression_type();}
+ if (buffer.empty())
+ {
+ ec = jsonpath_errc::invalid_number;
+ return path_expression_type();
+ }
+ else
+ {
+ int64_t n{0};
+ auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), n);
+ if (!r)
+ {
+ ec = jsonpath_errc::invalid_number;
+ return path_expression_type();
+ }
+ push_token(resources, token_type(resources.new_selector(index_selector<Json,JsonReference>(n))), ec);
+ if (ec) {return path_expression_type();}
+
+ buffer.clear();
+ }
+ push_token(resources, token_type(separator_arg), ec);
+ if (ec) {return path_expression_type();}
+ buffer.clear();
+ state_stack_.back() = path_state::union_expression; // union
+ state_stack_.emplace_back(path_state::union_element);
+ ++p_;
+ ++column_;
+ break;
+ }
+ case ':':
+ {
+ if (!buffer.empty())
+ {
+ int64_t n{0};
+ auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), n);
+ if (!r)
+ {
+ ec = jsonpath_errc::invalid_number;
+ return path_expression_type();
+ }
+ slic.start_ = n;
+ buffer.clear();
+ }
+ push_token(resources, token_type(begin_union_arg), ec);
+ if (ec) {return path_expression_type();}
+ state_stack_.back() = path_state::union_expression; // union
+ state_stack_.emplace_back(path_state::slice_expression_stop);
+ state_stack_.emplace_back(path_state::integer);
+ ++p_;
+ ++column_;
+ break;
+ }
+ default:
+ ec = jsonpath_errc::expected_rbracket;
+ return path_expression_type();
+ }
+ break;
+ case path_state::slice_expression_stop:
+ {
+ if (!buffer.empty())
+ {
+ int64_t n{0};
+ auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), n);
+ if (!r)
+ {
+ ec = jsonpath_errc::invalid_number;
+ return path_expression_type();
+ }
+ slic.stop_ = jsoncons::optional<int64_t>(n);
+ buffer.clear();
+ }
+ switch(*p_)
+ {
+ case ' ':case '\t':case '\r':case '\n':
+ advance_past_space_character();
+ break;
+ case ']':
+ case ',':
+ push_token(resources, token_type(resources.new_selector(slice_selector<Json,JsonReference>(slic))), ec);
+ if (ec) {return path_expression_type();}
+ slic = slice{};
+ state_stack_.pop_back(); // bracket_specifier2
+ break;
+ case ':':
+ state_stack_.back() = path_state::slice_expression_step;
+ state_stack_.emplace_back(path_state::integer);
+ ++p_;
+ ++column_;
+ break;
+ default:
+ ec = jsonpath_errc::expected_rbracket;
+ return path_expression_type();
+ }
+ break;
+ }
+ case path_state::slice_expression_step:
+ {
+ if (!buffer.empty())
+ {
+ int64_t n{0};
+ auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), n);
+ if (!r)
+ {
+ ec = jsonpath_errc::invalid_number;
+ return path_expression_type();
+ }
+ if (n == 0)
+ {
+ ec = jsonpath_errc::step_cannot_be_zero;
+ return path_expression_type();
+ }
+ slic.step_ = n;
+ buffer.clear();
+ }
+ switch(*p_)
+ {
+ case ' ':case '\t':case '\r':case '\n':
+ advance_past_space_character();
+ break;
+ case ']':
+ case ',':
+ push_token(resources, token_type(resources.new_selector(slice_selector<Json,JsonReference>(slic))), ec);
+ if (ec) {return path_expression_type();}
+ buffer.clear();
+ slic = slice{};
+ state_stack_.pop_back(); // slice_expression_step
+ break;
+ default:
+ ec = jsonpath_errc::expected_rbracket;
+ return path_expression_type();
+ }
+ break;
+ }
+
+ case path_state::bracketed_unquoted_name_or_union:
+ switch (*p_)
+ {
+ case ' ':case '\t':case '\r':case '\n':
+ advance_past_space_character();
+ break;
+ case ']':
+ push_token(resources, token_type(resources.new_selector(identifier_selector<Json,JsonReference>(buffer))), ec);
+ if (ec) {return path_expression_type();}
+ buffer.clear();
+ state_stack_.pop_back();
+ ++p_;
+ ++column_;
+ break;
+ case '.':
+ push_token(resources, token_type(begin_union_arg), ec);
+ push_token(resources, token_type(resources.new_selector(identifier_selector<Json,JsonReference>(buffer))), ec);
+ if (ec) {return path_expression_type();}
+ buffer.clear();
+ state_stack_.back() = path_state::union_expression; // union
+ state_stack_.emplace_back(path_state::relative_path);
+ ++p_;
+ ++column_;
+ break;
+ case '[':
+ push_token(resources, token_type(begin_union_arg), ec);
+ push_token(resources, token_type(resources.new_selector(identifier_selector<Json,JsonReference>(buffer))), ec);
+ if (ec) {return path_expression_type();}
+ state_stack_.back() = path_state::union_expression; // union
+ state_stack_.emplace_back(path_state::relative_path);
+ ++p_;
+ ++column_;
+ break;
+ case ',':
+ push_token(resources, token_type(begin_union_arg), ec);
+ push_token(resources, token_type(resources.new_selector(identifier_selector<Json,JsonReference>(buffer))), ec);
+ push_token(resources, token_type(separator_arg), ec);
+ if (ec) {return path_expression_type();}
+ buffer.clear();
+ state_stack_.back() = path_state::union_expression; // union
+ state_stack_.emplace_back(path_state::relative_path);
+ ++p_;
+ ++column_;
+ break;
+ default:
+ buffer.push_back(*p_);
+ ++p_;
+ ++column_;
+ break;
+ }
+ break;
+ case path_state::union_expression:
+ switch (*p_)
+ {
+ case ' ':case '\t':case '\r':case '\n':
+ advance_past_space_character();
+ break;
+ case '.':
+ state_stack_.emplace_back(path_state::relative_path);
+ ++p_;
+ ++column_;
+ break;
+ case '[':
+ state_stack_.emplace_back(path_state::bracket_specifier_or_union);
+ ++p_;
+ ++column_;
+ break;
+ case ',':
+ push_token(resources, token_type(separator_arg), ec);
+ if (ec) {return path_expression_type();}
+ state_stack_.emplace_back(path_state::union_element);
+ ++p_;
+ ++column_;
+ break;
+ case ']':
+ push_token(resources, token_type(end_union_arg), ec);
+ if (ec) {return path_expression_type();}
+ state_stack_.pop_back();
+ ++p_;
+ ++column_;
+ break;
+ default:
+ ec = jsonpath_errc::expected_rbracket;
+ return path_expression_type();
+ }
+ break;
+ case path_state::identifier_or_union:
+ switch (*p_)
+ {
+ case ' ':case '\t':case '\r':case '\n':
+ advance_past_space_character();
+ break;
+ case ']':
+ push_token(resources, token_type(resources.new_selector(identifier_selector<Json,JsonReference>(buffer))), ec);
+ if (ec) {return path_expression_type();}
+ buffer.clear();
+ state_stack_.pop_back();
+ ++p_;
+ ++column_;
+ break;
+ case ',':
+ push_token(resources, token_type(begin_union_arg), ec);
+ push_token(resources, token_type(resources.new_selector(identifier_selector<Json,JsonReference>(buffer))), ec);
+ push_token(resources, token_type(separator_arg), ec);
+ if (ec) {return path_expression_type();}
+ buffer.clear();
+ state_stack_.back() = path_state::union_expression; // union
+ state_stack_.emplace_back(path_state::union_element);
+ ++p_;
+ ++column_;
+ break;
+ default:
+ ec = jsonpath_errc::expected_rbracket;
+ return path_expression_type();
+ }
+ break;
+ case path_state::bracketed_wildcard:
+ switch (*p_)
+ {
+ case ' ':case '\t':case '\r':case '\n':
+ advance_past_space_character();
+ break;
+ case '[':
+ case ']':
+ case ',':
+ case '.':
+ push_token(resources, token_type(resources.new_selector(wildcard_selector<Json,JsonReference>())), ec);
+ if (ec) {return path_expression_type();}
+ buffer.clear();
+ state_stack_.pop_back();
+ break;
+ default:
+ ec = jsonpath_errc::expected_rbracket;
+ return path_expression_type();
+ }
+ break;
+ case path_state::index_or_slice:
+ switch(*p_)
+ {
+ case ' ':case '\t':case '\r':case '\n':
+ advance_past_space_character();
+ break;
+ case ',':
+ case ']':
+ {
+ if (buffer.empty())
+ {
+ ec = jsonpath_errc::invalid_number;
+ return path_expression_type();
+ }
+ else
+ {
+ int64_t n{0};
+ auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), n);
+ if (!r)
+ {
+ ec = jsonpath_errc::invalid_number;
+ return path_expression_type();
+ }
+ push_token(resources, token_type(resources.new_selector(index_selector<Json,JsonReference>(n))), ec);
+ if (ec) {return path_expression_type();}
+
+ buffer.clear();
+ }
+ state_stack_.pop_back(); // bracket_specifier
+ break;
+ }
+ case ':':
+ {
+ if (!buffer.empty())
+ {
+ int64_t n{0};
+ auto r = jsoncons::detail::to_integer(buffer.data(), buffer.size(), n);
+ if (!r)
+ {
+ ec = jsonpath_errc::invalid_number;
+ return path_expression_type();
+ }
+ slic.start_ = n;
+ buffer.clear();
+ }
+ state_stack_.back() = path_state::slice_expression_stop;
+ state_stack_.emplace_back(path_state::integer);
+ ++p_;
+ ++column_;
+ break;
+ }
+ default:
+ ec = jsonpath_errc::expected_rbracket;
+ return path_expression_type();
+ }
+ break;
+ case path_state::wildcard_or_union:
+ switch (*p_)
+ {
+ case ' ':case '\t':case '\r':case '\n':
+ advance_past_space_character();
+ break;
+ case ']':
+ push_token(resources, token_type(resources.new_selector(wildcard_selector<Json,JsonReference>())), ec);
+ if (ec) {return path_expression_type();}
+ buffer.clear();
+ state_stack_.pop_back();
+ ++p_;
+ ++column_;
+ break;
+ case ',':
+ push_token(resources, token_type(begin_union_arg), ec);
+ push_token(resources, token_type(resources.new_selector(wildcard_selector<Json,JsonReference>())), ec);
+ push_token(resources, token_type(separator_arg), ec);
+ if (ec) {return path_expression_type();}
+ buffer.clear();
+ state_stack_.back() = path_state::union_expression; // union
+ state_stack_.emplace_back(path_state::union_element);
+ ++p_;
+ ++column_;
+ break;
+ default:
+ ec = jsonpath_errc::expected_rbracket;
+ return path_expression_type();
+ }
+ break;
+ case path_state::quoted_string_escape_char:
+ switch (*p_)
+ {
+ case '\"':
+ buffer.push_back('\"');
+ ++p_;
+ ++column_;
+ state_stack_.pop_back();
+ break;
+ case '\'':
+ buffer.push_back('\'');
+ ++p_;
+ ++column_;
+ state_stack_.pop_back();
+ break;
+ case '\\':
+ buffer.push_back('\\');
+ ++p_;
+ ++column_;
+ state_stack_.pop_back();
+ break;
+ case '/':
+ buffer.push_back('/');
+ ++p_;
+ ++column_;
+ state_stack_.pop_back();
+ break;
+ case 'b':
+ buffer.push_back('\b');
+ ++p_;
+ ++column_;
+ state_stack_.pop_back();
+ break;
+ case 'f':
+ buffer.push_back('\f');
+ ++p_;
+ ++column_;
+ state_stack_.pop_back();
+ break;
+ case 'n':
+ buffer.push_back('\n');
+ ++p_;
+ ++column_;
+ state_stack_.pop_back();
+ break;
+ case 'r':
+ buffer.push_back('\r');
+ ++p_;
+ ++column_;
+ state_stack_.pop_back();
+ break;
+ case 't':
+ buffer.push_back('\t');
+ ++p_;
+ ++column_;
+ state_stack_.pop_back();
+ break;
+ case 'u':
+ ++p_;
+ ++column_;
+ state_stack_.back() = path_state::escape_u1;
+ break;
+ default:
+ ec = jsonpath_errc::illegal_escaped_character;
+ return path_expression_type();
+ }
+ break;
+ case path_state::escape_u1:
+ cp = append_to_codepoint(0, *p_, ec);
+ if (ec)
+ {
+ return path_expression_type();
+ }
+ ++p_;
+ ++column_;
+ state_stack_.back() = path_state::escape_u2;
+ break;
+ case path_state::escape_u2:
+ cp = append_to_codepoint(cp, *p_, ec);
+ if (ec)
+ {
+ return path_expression_type();
+ }
+ ++p_;
+ ++column_;
+ state_stack_.back() = path_state::escape_u3;
+ break;
+ case path_state::escape_u3:
+ cp = append_to_codepoint(cp, *p_, ec);
+ if (ec)
+ {
+ return path_expression_type();
+ }
+ ++p_;
+ ++column_;
+ state_stack_.back() = path_state::escape_u4;
+ break;
+ case path_state::escape_u4:
+ cp = append_to_codepoint(cp, *p_, ec);
+ if (ec)
+ {
+ return path_expression_type();
+ }
+ if (unicode_traits::is_high_surrogate(cp))
+ {
+ ++p_;
+ ++column_;
+ state_stack_.back() = path_state::escape_expect_surrogate_pair1;
+ }
+ else
+ {
+ unicode_traits::convert(&cp, 1, buffer);
+ ++p_;
+ ++column_;
+ state_stack_.pop_back();
+ }
+ break;
+ case path_state::escape_expect_surrogate_pair1:
+ switch (*p_)
+ {
+ case '\\':
+ ++p_;
+ ++column_;
+ state_stack_.back() = path_state::escape_expect_surrogate_pair2;
+ break;
+ default:
+ ec = jsonpath_errc::invalid_codepoint;
+ return path_expression_type();
+ }
+ break;
+ case path_state::escape_expect_surrogate_pair2:
+ switch (*p_)
+ {
+ case 'u':
+ ++p_;
+ ++column_;
+ state_stack_.back() = path_state::escape_u5;
+ break;
+ default:
+ ec = jsonpath_errc::invalid_codepoint;
+ return path_expression_type();
+ }
+ break;
+ case path_state::escape_u5:
+ cp2 = append_to_codepoint(0, *p_, ec);
+ if (ec)
+ {
+ return path_expression_type();
+ }
+ ++p_;
+ ++column_;
+ state_stack_.back() = path_state::escape_u6;
+ break;
+ case path_state::escape_u6:
+ cp2 = append_to_codepoint(cp2, *p_, ec);
+ if (ec)
+ {
+ return path_expression_type();
+ }
+ ++p_;
+ ++column_;
+ state_stack_.back() = path_state::escape_u7;
+ break;
+ case path_state::escape_u7:
+ cp2 = append_to_codepoint(cp2, *p_, ec);
+ if (ec)
+ {
+ return path_expression_type();
+ }
+ ++p_;
+ ++column_;
+ state_stack_.back() = path_state::escape_u8;
+ break;
+ case path_state::escape_u8:
+ {
+ cp2 = append_to_codepoint(cp2, *p_, ec);
+ if (ec)
+ {
+ return path_expression_type();
+ }
+ uint32_t codepoint = 0x10000 + ((cp & 0x3FF) << 10) + (cp2 & 0x3FF);
+ unicode_traits::convert(&codepoint, 1, buffer);
+ state_stack_.pop_back();
+ ++p_;
+ ++column_;
+ break;
+ }
+ case path_state::filter_expression:
+ {
+ switch(*p_)
+ {
+ case ' ':case '\t':case '\r':case '\n':
+ advance_past_space_character();
+ break;
+ case ',':
+ case ']':
+ {
+ push_token(resources, token_type(end_filter_arg), ec);
+ if (ec) {return path_expression_type();}
+ state_stack_.pop_back();
+ break;
+ }
+ default:
+ ec = jsonpath_errc::expected_comma_or_rbracket;
+ return path_expression_type();
+ }
+ break;
+ }
+ case path_state::expression:
+ {
+ switch(*p_)
+ {
+ case ' ':case '\t':case '\r':case '\n':
+ advance_past_space_character();
+ break;
+ case ',':
+ case ']':
+ {
+ push_token(resources, token_type(end_index_expression_arg), ec);
+ if (ec) {return path_expression_type();}
+ state_stack_.pop_back();
+ break;
+ }
+ default:
+ ec = jsonpath_errc::expected_comma_or_rbracket;
+ return path_expression_type();
+ }
+ break;
+ }
+ default:
+ ++p_;
+ ++column_;
+ break;
+ }
+ }
+
+ if (state_stack_.empty())
+ {
+ ec = jsonpath_errc::syntax_error;
+ return path_expression_type();
+ }
+
+ while (state_stack_.size() > 1)
+ {
+ switch (state_stack_.back())
+ {
+ case path_state::name_or_lbracket:
+ state_stack_.back() = path_state::relative_path;
+ break;
+ case path_state::relative_path:
+ state_stack_.back() = path_state::identifier_or_function_expr;
+ state_stack_.emplace_back(path_state::unquoted_string);
+ break;
+ case path_state::identifier_or_function_expr:
+ if (!buffer.empty()) // Can't be quoted string
+ {
+ push_token(resources, token_type(resources.new_selector(identifier_selector<Json,JsonReference>(buffer))), ec);
+ if (ec) {return path_expression_type();}
+ }
+ state_stack_.pop_back();
+ break;
+ case path_state::unquoted_string:
+ state_stack_.pop_back(); // unquoted_string
+ break;
+ case path_state::relative_location:
+ state_stack_.pop_back();
+ break;
+ case path_state::identifier:
+ if (!buffer.empty()) // Can't be quoted string
+ {
+ push_token(resources, token_type(resources.new_selector(identifier_selector<Json,JsonReference>(buffer))), ec);
+ if (ec) {return path_expression_type();}
+ }
+ state_stack_.pop_back();
+ break;
+ case path_state::parent_operator:
+ {
+ push_token(resources, token_type(resources.new_selector(parent_node_selector<Json,JsonReference>(ancestor_depth))), ec);
+ if (ec) { return path_expression_type(); }
+ paths_required = true;
+ state_stack_.pop_back();
+ break;
+ }
+ case path_state::ancestor_depth:
+ state_stack_.pop_back();
+ break;
+ default:
+ ec = jsonpath_errc::syntax_error;
+ return path_expression_type();
+ }
+ }
+
+ if (state_stack_.size() > 2)
+ {
+ ec = jsonpath_errc::unexpected_eof;
+ return path_expression_type();
+ }
+
+ //std::cout << "\nTokens\n\n";
+ //for (const auto& tok : output_stack_)
+ //{
+ // std::cout << tok.to_string() << "\n";
+ //}
+ //std::cout << "\n";
+
+ if (output_stack_.empty() || !operator_stack_.empty())
+ {
+ ec = jsonpath_errc::unexpected_eof;
+ return path_expression_type();
+ }
+
+ return path_expression_type(output_stack_.back().selector_, paths_required);
+ }
+
+ void advance_past_space_character()
+ {
+ switch (*p_)
+ {
+ case ' ':case '\t':
+ ++p_;
+ ++column_;
+ break;
+ case '\r':
+ if (p_+1 < end_input_ && *(p_+1) == '\n')
+ ++p_;
+ ++line_;
+ column_ = 1;
+ ++p_;
+ break;
+ case '\n':
+ ++line_;
+ column_ = 1;
+ ++p_;
+ break;
+ default:
+ break;
+ }
+ }
+
+ void unwind_rparen(std::error_code& ec)
+ {
+ auto it = operator_stack_.rbegin();
+ while (it != operator_stack_.rend() && !it->is_lparen())
+ {
+ output_stack_.emplace_back(std::move(*it));
+ ++it;
+ }
+ if (it == operator_stack_.rend())
+ {
+ ec = jsonpath_errc::unbalanced_parentheses;
+ return;
+ }
+ ++it;
+ operator_stack_.erase(it.base(),operator_stack_.end());
+ }
+
+ void push_token(jsoncons::jsonpath::detail::static_resources<value_type,reference>& resources, token_type&& tok, std::error_code& ec)
+ {
+ //std::cout << tok.to_string() << "\n";
+ switch (tok.token_kind())
+ {
+ case jsonpath_token_kind::begin_filter:
+ output_stack_.emplace_back(std::move(tok));
+ operator_stack_.emplace_back(token_type(lparen_arg));
+ break;
+ case jsonpath_token_kind::end_filter:
+ {
+ //std::cout << "push_token end_filter 1\n";
+ //for (const auto& tok2 : output_stack_)
+ //{
+ // std::cout << tok2.to_string() << "\n";
+ //}
+ //std::cout << "\n\n";
+ unwind_rparen(ec);
+ if (ec)
+ {
+ return;
+ }
+ std::vector<token_type> toks;
+ auto it = output_stack_.rbegin();
+ while (it != output_stack_.rend() && it->token_kind() != jsonpath_token_kind::begin_filter)
+ {
+ toks.emplace_back(std::move(*it));
+ ++it;
+ }
+ if (it == output_stack_.rend())
+ {
+ ec = jsonpath_errc::unbalanced_parentheses;
+ return;
+ }
+ std::reverse(toks.begin(), toks.end());
+ ++it;
+ output_stack_.erase(it.base(),output_stack_.end());
+
+ if (!output_stack_.empty() && output_stack_.back().is_path())
+ {
+ output_stack_.back().selector_->append_selector(resources.new_selector(filter_selector<Json,JsonReference>(expression_type(std::move(toks)))));
+ }
+ else
+ {
+ output_stack_.emplace_back(token_type(resources.new_selector(filter_selector<Json,JsonReference>(expression_type(std::move(toks))))));
+ }
+ //std::cout << "push_token end_filter 2\n";
+ //for (const auto& tok2 : output_stack_)
+ //{
+ // std::cout << tok2.to_string() << "\n";
+ //}
+ //std::cout << "\n\n";
+ break;
+ }
+ case jsonpath_token_kind::begin_expression:
+ //std::cout << "begin_expression\n";
+ output_stack_.emplace_back(std::move(tok));
+ operator_stack_.emplace_back(token_type(lparen_arg));
+ break;
+ case jsonpath_token_kind::end_index_expression:
+ {
+ //std::cout << "jsonpath_token_kind::end_index_expression\n";
+ //for (const auto& t : output_stack_)
+ //{
+ // std::cout << t.to_string() << "\n";
+ //}
+ //std::cout << "/jsonpath_token_kind::end_index_expression\n";
+ unwind_rparen(ec);
+ if (ec)
+ {
+ return;
+ }
+ std::vector<token_type> toks;
+ auto it = output_stack_.rbegin();
+ while (it != output_stack_.rend() && it->token_kind() != jsonpath_token_kind::begin_expression)
+ {
+ toks.emplace_back(std::move(*it));
+ ++it;
+ }
+ if (it == output_stack_.rend())
+ {
+ ec = jsonpath_errc::unbalanced_parentheses;
+ return;
+ }
+ std::reverse(toks.begin(), toks.end());
+ ++it;
+ output_stack_.erase(it.base(),output_stack_.end());
+
+ if (!output_stack_.empty() && output_stack_.back().is_path())
+ {
+ output_stack_.back().selector_->append_selector(resources.new_selector(index_expression_selector<Json,JsonReference>(expression_type(std::move(toks)))));
+ }
+ else
+ {
+ output_stack_.emplace_back(token_type(resources.new_selector(index_expression_selector<Json,JsonReference>(expression_type(std::move(toks))))));
+ }
+ break;
+ }
+ case jsonpath_token_kind::end_argument_expression:
+ {
+ //std::cout << "jsonpath_token_kind::end_index_expression\n";
+ //for (const auto& t : output_stack_)
+ //{
+ // std::cout << t.to_string() << "\n";
+ //}
+ //std::cout << "/jsonpath_token_kind::end_index_expression\n";
+ unwind_rparen(ec);
+ if (ec)
+ {
+ return;
+ }
+ std::vector<token_type> toks;
+ auto it = output_stack_.rbegin();
+ while (it != output_stack_.rend() && it->token_kind() != jsonpath_token_kind::begin_expression)
+ {
+ toks.emplace_back(std::move(*it));
+ ++it;
+ }
+ if (it == output_stack_.rend())
+ {
+ ec = jsonpath_errc::unbalanced_parentheses;
+ return;
+ }
+ std::reverse(toks.begin(), toks.end());
+ ++it;
+ output_stack_.erase(it.base(),output_stack_.end());
+ output_stack_.emplace_back(token_type(jsoncons::make_unique<expression_type>(std::move(toks))));
+ break;
+ }
+ case jsonpath_token_kind::selector:
+ {
+ if (!output_stack_.empty() && output_stack_.back().is_path())
+ {
+ output_stack_.back().selector_->append_selector(std::move(tok.selector_));
+ }
+ else
+ {
+ output_stack_.emplace_back(std::move(tok));
+ }
+ break;
+ }
+ case jsonpath_token_kind::separator:
+ output_stack_.emplace_back(std::move(tok));
+ break;
+ case jsonpath_token_kind::begin_union:
+ output_stack_.emplace_back(std::move(tok));
+ break;
+ case jsonpath_token_kind::end_union:
+ {
+ std::vector<selector_type*> expressions;
+ auto it = output_stack_.rbegin();
+ while (it != output_stack_.rend() && it->token_kind() != jsonpath_token_kind::begin_union)
+ {
+ if (it->token_kind() == jsonpath_token_kind::selector)
+ {
+ expressions.emplace_back(std::move(it->selector_));
+ }
+ do
+ {
+ ++it;
+ }
+ while (it != output_stack_.rend() && it->token_kind() != jsonpath_token_kind::begin_union && it->token_kind() != jsonpath_token_kind::separator);
+ if (it->token_kind() == jsonpath_token_kind::separator)
+ {
+ ++it;
+ }
+ }
+ if (it == output_stack_.rend())
+ {
+ ec = jsonpath_errc::unbalanced_parentheses;
+ return;
+ }
+ std::reverse(expressions.begin(), expressions.end());
+ ++it;
+ output_stack_.erase(it.base(),output_stack_.end());
+
+ if (!output_stack_.empty() && output_stack_.back().is_path())
+ {
+ output_stack_.back().selector_->append_selector(resources.new_selector(union_selector<Json,JsonReference>(std::move(expressions))));
+ }
+ else
+ {
+ output_stack_.emplace_back(token_type(resources.new_selector(union_selector<Json,JsonReference>(std::move(expressions)))));
+ }
+ break;
+ }
+ case jsonpath_token_kind::lparen:
+ operator_stack_.emplace_back(std::move(tok));
+ break;
+ case jsonpath_token_kind::rparen:
+ {
+ unwind_rparen(ec);
+ break;
+ }
+ case jsonpath_token_kind::end_function:
+ {
+ //std::cout << "jsonpath_token_kind::end_function\n";
+ unwind_rparen(ec);
+ if (ec)
+ {
+ return;
+ }
+ std::vector<token_type> toks;
+ auto it = output_stack_.rbegin();
+ std::size_t arg_count = 0;
+ while (it != output_stack_.rend() && it->token_kind() != jsonpath_token_kind::function)
+ {
+ if (it->token_kind() == jsonpath_token_kind::argument)
+ {
+ ++arg_count;
+ }
+ toks.emplace_back(std::move(*it));
+ ++it;
+ }
+ if (it == output_stack_.rend())
+ {
+ ec = jsonpath_errc::unbalanced_parentheses;
+ return;
+ }
+ std::reverse(toks.begin(), toks.end());
+ if (it->arity() && arg_count != *(it->arity()))
+ {
+ ec = jsonpath_errc::invalid_arity;
+ return;
+ }
+ toks.push_back(std::move(*it));
+ ++it;
+ output_stack_.erase(it.base(),output_stack_.end());
+
+ if (!output_stack_.empty() && output_stack_.back().is_path())
+ {
+ output_stack_.back().selector_->append_selector(resources.new_selector(function_selector<Json,JsonReference>(expression_type(std::move(toks)))));
+ }
+ else
+ {
+ output_stack_.emplace_back(token_type(resources.new_selector(function_selector<Json,JsonReference>(std::move(toks)))));
+ }
+ break;
+ }
+ case jsonpath_token_kind::literal:
+ if (!output_stack_.empty() && (output_stack_.back().token_kind() == jsonpath_token_kind::current_node || output_stack_.back().token_kind() == jsonpath_token_kind::root_node))
+ {
+ output_stack_.back() = std::move(tok);
+ }
+ else
+ {
+ output_stack_.emplace_back(std::move(tok));
+ }
+ break;
+ case jsonpath_token_kind::function:
+ output_stack_.emplace_back(std::move(tok));
+ operator_stack_.emplace_back(token_type(lparen_arg));
+ break;
+ case jsonpath_token_kind::argument:
+ output_stack_.emplace_back(std::move(tok));
+ break;
+ case jsonpath_token_kind::root_node:
+ case jsonpath_token_kind::current_node:
+ output_stack_.emplace_back(std::move(tok));
+ break;
+ case jsonpath_token_kind::unary_operator:
+ case jsonpath_token_kind::binary_operator:
+ {
+ if (operator_stack_.empty() || operator_stack_.back().is_lparen())
+ {
+ operator_stack_.emplace_back(std::move(tok));
+ }
+ else if (tok.precedence_level() < operator_stack_.back().precedence_level()
+ || (tok.precedence_level() == operator_stack_.back().precedence_level() && tok.is_right_associative()))
+ {
+ operator_stack_.emplace_back(std::move(tok));
+ }
+ else
+ {
+ auto it = operator_stack_.rbegin();
+ while (it != operator_stack_.rend() && it->is_operator()
+ && (tok.precedence_level() > it->precedence_level()
+ || (tok.precedence_level() == it->precedence_level() && tok.is_right_associative())))
+ {
+ output_stack_.emplace_back(std::move(*it));
+ ++it;
+ }
+
+ operator_stack_.erase(it.base(),operator_stack_.end());
+ operator_stack_.emplace_back(std::move(tok));
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ //std::cout << " " << "Output Stack\n";
+ //for (auto&& t : output_stack_)
+ //{
+ // std::cout << t.to_string(2) << "\n";
+ //}
+ //if (!operator_stack_.empty())
+ //{
+ // std::cout << " " << "Operator Stack\n";
+ // for (auto&& t : operator_stack_)
+ // {
+ // std::cout << t.to_string(2) << "\n";
+ // }
+ //}
+ }
+
+ uint32_t append_to_codepoint(uint32_t cp, int c, std::error_code& ec)
+ {
+ cp *= 16;
+ if (c >= '0' && c <= '9')
+ {
+ cp += c - '0';
+ }
+ else if (c >= 'a' && c <= 'f')
+ {
+ cp += c - 'a' + 10;
+ }
+ else if (c >= 'A' && c <= 'F')
+ {
+ cp += c - 'A' + 10;
+ }
+ else
+ {
+ ec = jsonpath_errc::invalid_codepoint;
+ }
+ return cp;
+ }
+ };
+
+ } // namespace detail
+
+ template <class Json,class JsonReference = const Json&>
+ class jsonpath_expression
+ {
+ public:
+ using evaluator_t = typename jsoncons::jsonpath::detail::jsonpath_evaluator<Json, JsonReference>;
+ using char_type = typename evaluator_t::char_type;
+ using string_type = typename evaluator_t::string_type;
+ using string_view_type = typename evaluator_t::string_view_type;
+ using value_type = typename evaluator_t::value_type;
+ using reference = typename evaluator_t::reference;
+ using parameter_type = parameter<Json>;
+ using json_selector_t = typename evaluator_t::path_expression_type;
+ using path_value_pair_type = typename evaluator_t::path_value_pair_type;
+ using json_location_type = typename evaluator_t::json_location_type;
+ using function_type = std::function<value_type(jsoncons::span<const parameter_type>, std::error_code& ec)>;
+ private:
+ jsoncons::jsonpath::detail::static_resources<value_type,reference> static_resources_;
+ json_selector_t expr_;
+ public:
+ jsonpath_expression(jsoncons::jsonpath::detail::static_resources<value_type,reference>&& resources,
+ json_selector_t&& expr)
+ : static_resources_(std::move(resources)),
+ expr_(std::move(expr))
+ {
+ }
+
+ jsonpath_expression(jsoncons::jsonpath::detail::static_resources<value_type,reference>&& resources,
+ json_selector_t&& expr, std::vector<function_type>&& custom_functions)
+ : static_resources_(std::move(resources)),
+ expr_(std::move(expr), std::move(custom_functions))
+ {
+ }
+
+ template <class BinaryCallback>
+ typename std::enable_if<type_traits::is_binary_function_object<BinaryCallback,const string_type&,reference>::value,void>::type
+ evaluate(reference instance, BinaryCallback callback, result_options options = result_options())
+ {
+ jsoncons::jsonpath::detail::dynamic_resources<Json,reference> resources;
+ auto f = [&callback](const json_location_type& path, reference val)
+ {
+ callback(path.to_string(), val);
+ };
+ expr_.evaluate(resources, instance, resources.root_path_node(), instance, f, options);
+ }
+
+ Json evaluate(reference instance, result_options options = result_options())
+ {
+ if ((options & result_options::path) == result_options::path)
+ {
+ jsoncons::jsonpath::detail::dynamic_resources<Json,reference> resources;
+
+ Json result(json_array_arg);
+ auto callback = [&result](const json_location_type& p, reference)
+ {
+ result.emplace_back(p.to_string());
+ };
+ expr_.evaluate(resources, instance, resources.root_path_node(), instance, callback, options);
+ return result;
+ }
+ else
+ {
+ jsoncons::jsonpath::detail::dynamic_resources<Json,reference> resources;
+ return expr_.evaluate(resources, instance, resources.current_path_node(), instance, options);
+ }
+ }
+
+ static jsonpath_expression compile(const string_view_type& path)
+ {
+ jsoncons::jsonpath::detail::static_resources<value_type,reference> resources;
+
+ evaluator_t e;
+ json_selector_t expr = e.compile(resources, path);
+ return jsonpath_expression(std::move(resources), std::move(expr));
+ }
+
+ static jsonpath_expression compile(const string_view_type& path, std::error_code& ec)
+ {
+ jsoncons::jsonpath::detail::static_resources<value_type,reference> resources;
+ evaluator_t e;
+ json_selector_t expr = e.compile(resources, path, ec);
+ return jsonpath_expression(std::move(resources), std::move(expr));
+ }
+
+ static jsonpath_expression compile(const string_view_type& path,
+ const custom_functions<Json>& functions)
+ {
+ jsoncons::jsonpath::detail::static_resources<value_type,reference> resources(functions);
+
+ evaluator_t e;
+ json_selector_t expr = e.compile(resources, path);
+ return jsonpath_expression(std::move(resources), std::move(expr));
+ }
+
+ static jsonpath_expression compile(const string_view_type& path,
+ const custom_functions<Json>& functions,
+ std::error_code& ec)
+ {
+ jsoncons::jsonpath::detail::static_resources<value_type,reference> resources(functions);
+ evaluator_t e;
+ json_selector_t expr = e.compile(resources, path, ec);
+ return jsonpath_expression(std::move(resources), std::move(expr));
+ }
+ };
+
+ template <class Json>
+ jsonpath_expression<Json> make_expression(const typename Json::string_view_type& expr,
+ const custom_functions<Json>& functions = custom_functions<Json>())
+ {
+ return jsonpath_expression<Json>::compile(expr, functions);
+ }
+
+ template <class Json>
+ jsonpath_expression<Json> make_expression(const typename Json::string_view_type& expr, std::error_code& ec)
+ {
+ return jsonpath_expression<Json>::compile(expr, ec);
+ }
+
+ template <class Json>
+ jsonpath_expression<Json> make_expression(const typename Json::string_view_type& expr,
+ const custom_functions<Json>& functions,
+ std::error_code& ec)
+ {
+ return jsonpath_expression<Json>::compile(expr, functions, ec);
+ }
+
+} // namespace jsonpath
+} // namespace jsoncons
+
+#endif
diff --git a/include/jsoncons_ext/jsonpath/jsonpath_selector.hpp b/include/jsoncons_ext/jsonpath/jsonpath_selector.hpp
new file mode 100644
index 0000000..e06d37c
--- /dev/null
+++ b/include/jsoncons_ext/jsonpath/jsonpath_selector.hpp
@@ -0,0 +1,1322 @@
+// Copyright 2021 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_JSONPATH_JSONPATH_SELECTOR_HPP
+#define JSONCONS_JSONPATH_JSONPATH_SELECTOR_HPP
+
+#include <string>
+#include <vector>
+#include <memory>
+#include <type_traits> // std::is_const
+#include <limits> // std::numeric_limits
+#include <utility> // std::move
+#include <regex>
+#include <jsoncons/json.hpp>
+#include <jsoncons_ext/jsonpath/jsonpath_error.hpp>
+#include <jsoncons_ext/jsonpath/expression.hpp>
+
+namespace jsoncons {
+namespace jsonpath {
+namespace detail {
+
+ struct slice
+ {
+ jsoncons::optional<int64_t> start_;
+ jsoncons::optional<int64_t> stop_;
+ int64_t step_;
+
+ slice()
+ : start_(), stop_(), step_(1)
+ {
+ }
+
+ slice(const jsoncons::optional<int64_t>& start, const jsoncons::optional<int64_t>& end, int64_t step)
+ : start_(start), stop_(end), step_(step)
+ {
+ }
+
+ slice(const slice& other)
+ : start_(other.start_), stop_(other.stop_), step_(other.step_)
+ {
+ }
+
+ slice& operator=(const slice& rhs)
+ {
+ if (this != &rhs)
+ {
+ if (rhs.start_)
+ {
+ start_ = rhs.start_;
+ }
+ else
+ {
+ start_.reset();
+ }
+ if (rhs.stop_)
+ {
+ stop_ = rhs.stop_;
+ }
+ else
+ {
+ stop_.reset();
+ }
+ step_ = rhs.step_;
+ }
+ return *this;
+ }
+
+ int64_t get_start(std::size_t size) const
+ {
+ if (start_)
+ {
+ auto len = *start_ >= 0 ? *start_ : (static_cast<int64_t>(size) + *start_);
+ return len <= static_cast<int64_t>(size) ? len : static_cast<int64_t>(size);
+ }
+ else
+ {
+ if (step_ >= 0)
+ {
+ return 0;
+ }
+ else
+ {
+ return static_cast<int64_t>(size);
+ }
+ }
+ }
+
+ int64_t get_stop(std::size_t size) const
+ {
+ if (stop_)
+ {
+ auto len = *stop_ >= 0 ? *stop_ : (static_cast<int64_t>(size) + *stop_);
+ return len <= static_cast<int64_t>(size) ? len : static_cast<int64_t>(size);
+ }
+ else
+ {
+ return step_ >= 0 ? static_cast<int64_t>(size) : -1;
+ }
+ }
+
+ int64_t step() const
+ {
+ return step_; // Allow negative
+ }
+ };
+
+ template <class Json,class JsonReference>
+ class json_array_receiver : public node_receiver<Json,JsonReference>
+ {
+ public:
+ using reference = JsonReference;
+ using char_type = typename Json::char_type;
+ using json_location_node_type = json_location_node<char_type>;
+
+ Json* val;
+
+ json_array_receiver(Json* ptr)
+ : val(ptr)
+ {
+ }
+
+ void add(const json_location_node_type&, reference value) override
+ {
+ val->emplace_back(value);
+ }
+ };
+
+ template <class Json,class JsonReference>
+ struct path_generator
+ {
+ using char_type = typename Json::char_type;
+ using json_location_node_type = json_location_node<char_type>;
+ using string_type = std::basic_string<char_type>;
+
+ static const json_location_node_type& generate(dynamic_resources<Json,JsonReference>& resources,
+ const json_location_node_type& last,
+ std::size_t index,
+ result_options options)
+ {
+ const result_options require_path = result_options::path | result_options::nodups | result_options::sort;
+ if ((options & require_path) != result_options())
+ {
+ return *resources.create_path_node(&last, index);
+ }
+ else
+ {
+ return last;
+ }
+ }
+
+ static const json_location_node_type& generate(dynamic_resources<Json,JsonReference>& resources,
+ const json_location_node_type& last,
+ const string_type& identifier,
+ result_options options)
+ {
+ const result_options require_path = result_options::path | result_options::nodups | result_options::sort;
+ if ((options & require_path) != result_options())
+ {
+ return *resources.create_path_node(&last, identifier);
+ }
+ else
+ {
+ return last;
+ }
+ }
+ };
+
+ template <class Json,class JsonReference>
+ class base_selector : public jsonpath_selector<Json,JsonReference>
+ {
+ using supertype = jsonpath_selector<Json,JsonReference>;
+
+ supertype* tail_;
+ public:
+ using value_type = typename supertype::value_type;
+ using reference = typename supertype::reference;
+ using pointer = typename supertype::pointer;
+ using path_value_pair_type = typename supertype::path_value_pair_type;
+ using json_location_node_type = typename supertype::json_location_node_type;
+ using json_location_type = typename supertype::json_location_type;
+ using node_receiver_type = typename supertype::node_receiver_type;
+ using selector_type = typename supertype::selector_type;
+
+ base_selector()
+ : supertype(true, 11), tail_(nullptr)
+ {
+ }
+
+ base_selector(bool is_path, std::size_t precedence_level)
+ : supertype(is_path, precedence_level), tail_(nullptr)
+ {
+ }
+
+ void append_selector(selector_type* expr) override
+ {
+ if (!tail_)
+ {
+ tail_ = expr;
+ }
+ else
+ {
+ tail_->append_selector(expr);
+ }
+ }
+
+ void tail_select(dynamic_resources<Json,JsonReference>& resources,
+ reference root,
+ const json_location_node_type& last,
+ reference current,
+ node_receiver_type& receiver,
+ result_options options) const
+ {
+ if (!tail_)
+ {
+ receiver.add(last, current);
+ }
+ else
+ {
+ tail_->select(resources, root, last, current, receiver, options);
+ }
+ }
+
+ reference evaluate_tail(dynamic_resources<Json,JsonReference>& resources,
+ reference root,
+ const json_location_node_type& last,
+ reference current,
+ result_options options,
+ std::error_code& ec) const
+ {
+ if (!tail_)
+ {
+ return current;
+ }
+ else
+ {
+ return tail_->evaluate(resources, root, last, current, options, ec);
+ }
+ }
+
+ std::string to_string(int level = 0) const override
+ {
+ std::string s;
+ if (level > 0)
+ {
+ s.append("\n");
+ s.append(level*2, ' ');
+ }
+ if (tail_)
+ {
+ s.append(tail_->to_string(level));
+ }
+ return s;
+ }
+ };
+
+ template <class Json,class JsonReference>
+ class identifier_selector final : public base_selector<Json,JsonReference>
+ {
+ using supertype = base_selector<Json,JsonReference>;
+ using path_generator_type = path_generator<Json,JsonReference>;
+ public:
+ using value_type = typename supertype::value_type;
+ using reference = typename supertype::reference;
+ using pointer = typename supertype::pointer;
+ using path_value_pair_type = typename supertype::path_value_pair_type;
+ using json_location_node_type = typename supertype::json_location_node_type;
+ using char_type = typename Json::char_type;
+ using string_type = std::basic_string<char_type>;
+ using string_view_type = basic_string_view<char_type>;
+ using node_receiver_type = typename supertype::node_receiver_type;
+ private:
+ string_type identifier_;
+ public:
+
+ identifier_selector(const string_view_type& identifier)
+ : base_selector<Json,JsonReference>(), identifier_(identifier)
+ {
+ }
+
+ void select(dynamic_resources<Json,JsonReference>& resources,
+ reference root,
+ const json_location_node_type& last,
+ reference current,
+ node_receiver_type& receiver,
+ result_options options) const override
+ {
+ //std::string buf;
+ //buf.append("identifier selector: ");
+ //unicode_traits::convert(identifier_.data(),identifier_.size(),buf);
+
+ static const char_type length_name[] = {'l', 'e', 'n', 'g', 't', 'h', 0};
+
+ if (current.is_object())
+ {
+ auto it = current.find(identifier_);
+ if (it != current.object_range().end())
+ {
+ this->tail_select(resources, root,
+ path_generator_type::generate(resources, last, identifier_, options),
+ it->value(), receiver, options);
+ }
+ }
+ else if (current.is_array())
+ {
+ int64_t n{0};
+ auto r = jsoncons::detail::to_integer_decimal(identifier_.data(), identifier_.size(), n);
+ if (r)
+ {
+ std::size_t index = (n >= 0) ? static_cast<std::size_t>(n) : static_cast<std::size_t>(static_cast<int64_t>(current.size()) + n);
+ if (index < current.size())
+ {
+ this->tail_select(resources, root,
+ path_generator_type::generate(resources, last, index, options),
+ current[index], receiver, options);
+ }
+ }
+ else if (identifier_ == length_name && current.size() > 0)
+ {
+ pointer ptr = resources.create_json(current.size());
+ this->tail_select(resources, root,
+ path_generator_type::generate(resources, last, identifier_, options),
+ *ptr,
+ receiver, options);
+ }
+ }
+ else if (current.is_string() && identifier_ == length_name)
+ {
+ string_view_type sv = current.as_string_view();
+ std::size_t count = unicode_traits::count_codepoints(sv.data(), sv.size());
+ pointer ptr = resources.create_json(count);
+ this->tail_select(resources, root,
+ path_generator_type::generate(resources, last, identifier_, options),
+ *ptr, receiver, options);
+ }
+ //std::cout << "end identifier_selector\n";
+ }
+
+ reference evaluate(dynamic_resources<Json,JsonReference>& resources,
+ reference root,
+ const json_location_node_type& last,
+ reference current,
+ result_options options,
+ std::error_code& ec) const override
+ {
+ static const char_type length_name[] = {'l', 'e', 'n', 'g', 't', 'h', 0};
+
+ if (current.is_object())
+ {
+ auto it = current.find(identifier_);
+ if (it != current.object_range().end())
+ {
+ return this->evaluate_tail(resources, root,
+ path_generator_type::generate(resources, last, identifier_, options),
+ it->value(), options, ec);
+ }
+ else
+ {
+ return resources.null_value();
+ }
+ }
+ else if (current.is_array())
+ {
+ int64_t n{0};
+ auto r = jsoncons::detail::to_integer_decimal(identifier_.data(), identifier_.size(), n);
+ if (r)
+ {
+ std::size_t index = (n >= 0) ? static_cast<std::size_t>(n) : static_cast<std::size_t>(static_cast<int64_t>(current.size()) + n);
+ if (index < current.size())
+ {
+ return this->evaluate_tail(resources, root,
+ path_generator_type::generate(resources, last, index, options),
+ current[index], options, ec);
+ }
+ else
+ {
+ return resources.null_value();
+ }
+ }
+ else if (identifier_ == length_name && current.size() > 0)
+ {
+ pointer ptr = resources.create_json(current.size());
+ return this->evaluate_tail(resources, root,
+ path_generator_type::generate(resources, last, identifier_, options),
+ *ptr,
+ options, ec);
+ }
+ else
+ {
+ return resources.null_value();
+ }
+ }
+ else if (current.is_string() && identifier_ == length_name)
+ {
+ string_view_type sv = current.as_string_view();
+ std::size_t count = unicode_traits::count_codepoints(sv.data(), sv.size());
+ pointer ptr = resources.create_json(count);
+ return this->evaluate_tail(resources, root,
+ path_generator_type::generate(resources, last, identifier_, options),
+ *ptr, options, ec);
+ }
+ else
+ {
+ return resources.null_value();
+ }
+ }
+
+ std::string to_string(int level = 0) const override
+ {
+ std::string s;
+ if (level > 0)
+ {
+ s.append("\n");
+ s.append(level*2, ' ');
+ }
+ s.append("identifier selector ");
+ unicode_traits::convert(identifier_.data(),identifier_.size(),s);
+ s.append(base_selector<Json,JsonReference>::to_string(level+1));
+ //s.append("\n");
+
+ return s;
+ }
+ };
+
+ template <class Json,class JsonReference>
+ class root_selector final : public base_selector<Json,JsonReference>
+ {
+ using supertype = base_selector<Json,JsonReference>;
+ using path_generator_type = path_generator<Json,JsonReference>;
+
+ std::size_t id_;
+ public:
+ using value_type = typename supertype::value_type;
+ using reference = typename supertype::reference;
+ using pointer = typename supertype::pointer;
+ using path_value_pair_type = typename supertype::path_value_pair_type;
+ using json_location_node_type = typename supertype::json_location_node_type;
+ using node_receiver_type = typename supertype::node_receiver_type;
+
+ root_selector(std::size_t id)
+ : base_selector<Json,JsonReference>(), id_(id)
+ {
+ }
+
+ void select(dynamic_resources<Json,JsonReference>& resources,
+ reference root,
+ const json_location_node_type& last,
+ reference,
+ node_receiver_type& receiver,
+ result_options options) const override
+ {
+ this->tail_select(resources, root, last, root, receiver, options);
+ }
+
+ reference evaluate(dynamic_resources<Json,JsonReference>& resources,
+ reference root,
+ const json_location_node_type& last,
+ reference,
+ result_options options,
+ std::error_code& ec) const override
+ {
+ if (resources.is_cached(id_))
+ {
+ return resources.retrieve_from_cache(id_);
+ }
+ else
+ {
+ auto& ref = this->evaluate_tail(resources, root, last, root, options, ec);
+ if (!ec)
+ {
+ resources.add_to_cache(id_, ref);
+ }
+
+ return ref;
+ }
+ }
+
+ std::string to_string(int level = 0) const override
+ {
+ std::string s;
+ if (level > 0)
+ {
+ s.append("\n");
+ s.append(level*2, ' ');
+ }
+ s.append("root_selector ");
+ s.append(base_selector<Json,JsonReference>::to_string(level+1));
+
+ return s;
+ }
+ };
+
+ template <class Json,class JsonReference>
+ class current_node_selector final : public base_selector<Json,JsonReference>
+ {
+ using supertype = base_selector<Json,JsonReference>;
+
+ public:
+ using value_type = typename supertype::value_type;
+ using reference = typename supertype::reference;
+ using pointer = typename supertype::pointer;
+ using path_value_pair_type = typename supertype::path_value_pair_type;
+ using json_location_node_type = typename supertype::json_location_node_type;
+ using path_generator_type = path_generator<Json,JsonReference>;
+ using node_receiver_type = typename supertype::node_receiver_type;
+
+ current_node_selector()
+ {
+ }
+
+ void select(dynamic_resources<Json,JsonReference>& resources,
+ reference root,
+ const json_location_node_type& last,
+ reference current,
+ node_receiver_type& receiver,
+ result_options options) const override
+ {
+ this->tail_select(resources,
+ root, last, current, receiver, options);
+ }
+
+ reference evaluate(dynamic_resources<Json,JsonReference>& resources,
+ reference root,
+ const json_location_node_type& last,
+ reference current,
+ result_options options,
+ std::error_code& ec) const override
+ {
+ //std::cout << "current_node_selector: " << current << "\n";
+ return this->evaluate_tail(resources,
+ root, last, current, options, ec);
+ }
+
+ std::string to_string(int level = 0) const override
+ {
+ std::string s;
+ if (level > 0)
+ {
+ s.append("\n");
+ s.append(level*2, ' ');
+ }
+ s.append("current_node_selector");
+ s.append(base_selector<Json,JsonReference>::to_string(level+1));
+
+ return s;
+ }
+ };
+
+ template <class Json,class JsonReference>
+ class parent_node_selector final : public base_selector<Json,JsonReference>
+ {
+ using supertype = base_selector<Json,JsonReference>;
+
+ int ancestor_depth_;
+
+ public:
+ using value_type = typename supertype::value_type;
+ using reference = typename supertype::reference;
+ using pointer = typename supertype::pointer;
+ using path_value_pair_type = typename supertype::path_value_pair_type;
+ using json_location_node_type = typename supertype::json_location_node_type;
+ using json_location_type = typename supertype::json_location_type;
+ using path_generator_type = path_generator<Json,JsonReference>;
+ using node_receiver_type = typename supertype::node_receiver_type;
+
+ parent_node_selector(int ancestor_depth)
+ {
+ ancestor_depth_ = ancestor_depth;
+ }
+
+ void select(dynamic_resources<Json,JsonReference>& resources,
+ reference root,
+ const json_location_node_type& last,
+ reference,
+ node_receiver_type& receiver,
+ result_options options) const override
+ {
+ const json_location_node_type* ancestor = std::addressof(last);
+ int index = 0;
+ while (ancestor != nullptr && index < ancestor_depth_)
+ {
+ ancestor = ancestor->parent();
+ ++index;
+ }
+
+ if (ancestor != nullptr)
+ {
+ json_location_type path(*ancestor);
+ pointer ptr = jsoncons::jsonpath::select(root,path);
+ if (ptr != nullptr)
+ {
+ this->tail_select(resources, root, path.last(), *ptr, receiver, options);
+ }
+ }
+ }
+
+ reference evaluate(dynamic_resources<Json,JsonReference>& resources,
+ reference root,
+ const json_location_node_type& last,
+ reference,
+ result_options options,
+ std::error_code& ec) const override
+ {
+ const json_location_node_type* ancestor = std::addressof(last);
+ int index = 0;
+ while (ancestor != nullptr && index < ancestor_depth_)
+ {
+ ancestor = ancestor->parent();
+ ++index;
+ }
+
+ if (ancestor != nullptr)
+ {
+ json_location_type path(*ancestor);
+ pointer ptr = jsoncons::jsonpath::select(root,path);
+ if (ptr != nullptr)
+ {
+ return this->evaluate_tail(resources, root, path.last(), *ptr, options, ec);
+ }
+ else
+ {
+ return resources.null_value();
+ }
+ }
+ else
+ {
+ return resources.null_value();
+ }
+ }
+
+ std::string to_string(int level = 0) const override
+ {
+ std::string s;
+ if (level > 0)
+ {
+ s.append("\n");
+ s.append(level*2, ' ');
+ }
+ s.append("parent_node_selector");
+ s.append(base_selector<Json,JsonReference>::to_string(level+1));
+
+ return s;
+ }
+ };
+
+ template <class Json,class JsonReference>
+ class index_selector final : public base_selector<Json,JsonReference>
+ {
+ using supertype = base_selector<Json,JsonReference>;
+
+ int64_t index_;
+ public:
+ using value_type = typename supertype::value_type;
+ using reference = typename supertype::reference;
+ using pointer = typename supertype::pointer;
+ using path_value_pair_type = typename supertype::path_value_pair_type;
+ using json_location_node_type = typename supertype::json_location_node_type;
+ using path_generator_type = path_generator<Json,JsonReference>;
+ using node_receiver_type = typename supertype::node_receiver_type;
+
+ index_selector(int64_t index)
+ : base_selector<Json,JsonReference>(), index_(index)
+ {
+ }
+
+ void select(dynamic_resources<Json,JsonReference>& resources,
+ reference root,
+ const json_location_node_type& last,
+ reference current,
+ node_receiver_type& receiver,
+ result_options options) const override
+ {
+ if (current.is_array())
+ {
+ int64_t slen = static_cast<int64_t>(current.size());
+ if (index_ >= 0 && index_ < slen)
+ {
+ std::size_t i = static_cast<std::size_t>(index_);
+ this->tail_select(resources, root,
+ path_generator_type::generate(resources, last, i, options),
+ current.at(i), receiver, options);
+ }
+ else
+ {
+ int64_t index = slen + index_;
+ if (index >= 0 && index < slen)
+ {
+ std::size_t i = static_cast<std::size_t>(index);
+ this->tail_select(resources, root,
+ path_generator_type::generate(resources, last, i, options),
+ current.at(i), receiver, options);
+ }
+ }
+ }
+ }
+
+ reference evaluate(dynamic_resources<Json,JsonReference>& resources,
+ reference root,
+ const json_location_node_type& last,
+ reference current,
+ result_options options,
+ std::error_code& ec) const override
+ {
+ if (current.is_array())
+ {
+ int64_t slen = static_cast<int64_t>(current.size());
+ if (index_ >= 0 && index_ < slen)
+ {
+ std::size_t i = static_cast<std::size_t>(index_);
+ return this->evaluate_tail(resources, root,
+ path_generator_type::generate(resources, last, i, options),
+ current.at(i), options, ec);
+ }
+ else
+ {
+ int64_t index = slen + index_;
+ if (index >= 0 && index < slen)
+ {
+ std::size_t i = static_cast<std::size_t>(index);
+ return this->evaluate_tail(resources, root,
+ path_generator_type::generate(resources, last, i, options),
+ current.at(i), options, ec);
+ }
+ else
+ {
+ return resources.null_value();
+ }
+ }
+ }
+ else
+ {
+ return resources.null_value();
+ }
+ }
+ };
+
+ template <class Json,class JsonReference>
+ class wildcard_selector final : public base_selector<Json,JsonReference>
+ {
+ using supertype = base_selector<Json,JsonReference>;
+
+ public:
+ using value_type = typename supertype::value_type;
+ using reference = typename supertype::reference;
+ using pointer = typename supertype::pointer;
+ using path_value_pair_type = typename supertype::path_value_pair_type;
+ using json_location_node_type = typename supertype::json_location_node_type;
+ using path_generator_type = path_generator<Json,JsonReference>;
+ using node_receiver_type = typename supertype::node_receiver_type;
+
+ wildcard_selector()
+ : base_selector<Json,JsonReference>()
+ {
+ }
+
+ void select(dynamic_resources<Json,JsonReference>& resources,
+ reference root,
+ const json_location_node_type& last,
+ reference current,
+ node_receiver_type& receiver,
+ result_options options) const override
+ {
+ if (current.is_array())
+ {
+ for (std::size_t i = 0; i < current.size(); ++i)
+ {
+ this->tail_select(resources, root,
+ path_generator_type::generate(resources, last, i, options), current[i],
+ receiver, options);
+ }
+ }
+ else if (current.is_object())
+ {
+ for (auto& member : current.object_range())
+ {
+ this->tail_select(resources, root,
+ path_generator_type::generate(resources, last, member.key(), options),
+ member.value(), receiver, options);
+ }
+ }
+ //std::cout << "end wildcard_selector\n";
+ }
+
+ reference evaluate(dynamic_resources<Json,JsonReference>& resources,
+ reference root,
+ const json_location_node_type& last,
+ reference current,
+ result_options options,
+ std::error_code&) const override
+ {
+ auto jptr = resources.create_json(json_array_arg);
+ json_array_receiver<Json,JsonReference> receiver(jptr);
+ select(resources, root, last, current, receiver, options);
+ return *jptr;
+ }
+
+ std::string to_string(int level = 0) const override
+ {
+ std::string s;
+ if (level > 0)
+ {
+ s.append("\n");
+ s.append(level*2, ' ');
+ }
+ s.append("wildcard selector");
+ s.append(base_selector<Json,JsonReference>::to_string(level));
+
+ return s;
+ }
+ };
+
+ template <class Json,class JsonReference>
+ class recursive_selector final : public base_selector<Json,JsonReference>
+ {
+ using supertype = base_selector<Json,JsonReference>;
+
+ public:
+ using value_type = typename supertype::value_type;
+ using reference = typename supertype::reference;
+ using pointer = typename supertype::pointer;
+ using path_value_pair_type = typename supertype::path_value_pair_type;
+ using json_location_node_type = typename supertype::json_location_node_type;
+ using path_generator_type = path_generator<Json,JsonReference>;
+ using node_receiver_type = typename supertype::node_receiver_type;
+
+ recursive_selector()
+ : base_selector<Json,JsonReference>()
+ {
+ }
+
+ void select(dynamic_resources<Json,JsonReference>& resources,
+ reference root,
+ const json_location_node_type& last,
+ reference current,
+ node_receiver_type& receiver,
+ result_options options) const override
+ {
+ if (current.is_array())
+ {
+ this->tail_select(resources, root, last, current, receiver, options);
+ for (std::size_t i = 0; i < current.size(); ++i)
+ {
+ select(resources, root,
+ path_generator_type::generate(resources, last, i, options), current[i], receiver, options);
+ }
+ }
+ else if (current.is_object())
+ {
+ this->tail_select(resources, root, last, current, receiver, options);
+ for (auto& item : current.object_range())
+ {
+ select(resources, root,
+ path_generator_type::generate(resources, last, item.key(), options), item.value(), receiver, options);
+ }
+ }
+ //std::cout << "end wildcard_selector\n";
+ }
+
+ reference evaluate(dynamic_resources<Json,JsonReference>& resources,
+ reference root,
+ const json_location_node_type& last,
+ reference current,
+ result_options options,
+ std::error_code&) const override
+ {
+ auto jptr = resources.create_json(json_array_arg);
+ json_array_receiver<Json,JsonReference> receiver(jptr);
+ select(resources, root, last, current, receiver, options);
+ return *jptr;
+ }
+
+ std::string to_string(int level = 0) const override
+ {
+ std::string s;
+ if (level > 0)
+ {
+ s.append("\n");
+ s.append(level*2, ' ');
+ }
+ s.append("wildcard selector");
+ s.append(base_selector<Json,JsonReference>::to_string(level));
+
+ return s;
+ }
+ };
+
+ template <class Json,class JsonReference>
+ class union_selector final : public jsonpath_selector<Json,JsonReference>
+ {
+ using supertype = jsonpath_selector<Json,JsonReference>;
+ public:
+ using value_type = typename supertype::value_type;
+ using reference = typename supertype::reference;
+ using pointer = typename supertype::pointer;
+ using path_value_pair_type = typename supertype::path_value_pair_type;
+ using json_location_node_type = typename supertype::json_location_node_type;
+ using json_location_type = typename supertype::json_location_type;
+ using path_expression_type = path_expression<Json, JsonReference>;
+ using path_generator_type = path_generator<Json,JsonReference>;
+ using node_receiver_type = typename supertype::node_receiver_type;
+ using selector_type = typename supertype::selector_type;
+ private:
+ std::vector<selector_type*> selectors_;
+ selector_type* tail_;
+ public:
+ union_selector(std::vector<selector_type*>&& selectors)
+ : supertype(true, 11), selectors_(std::move(selectors)), tail_(nullptr)
+ {
+ }
+
+ void append_selector(selector_type* tail) override
+ {
+ if (tail_ == nullptr)
+ {
+ tail_ = tail;
+ for (auto& selector : selectors_)
+ {
+ selector->append_selector(tail);
+ }
+ }
+ else
+ {
+ tail_->append_selector(tail);
+ }
+ }
+
+ void select(dynamic_resources<Json,JsonReference>& resources,
+ reference root,
+ const json_location_node_type& last,
+ reference current,
+ node_receiver_type& receiver,
+ result_options options) const override
+ {
+ for (auto& selector : selectors_)
+ {
+ selector->select(resources, root, last, current, receiver, options);
+ }
+ }
+
+ reference evaluate(dynamic_resources<Json,JsonReference>& resources,
+ reference root,
+ const json_location_node_type& last,
+ reference current,
+ result_options options,
+ std::error_code&) const override
+ {
+ auto jptr = resources.create_json(json_array_arg);
+ json_array_receiver<Json,JsonReference> receiver(jptr);
+ select(resources,root,last,current,receiver,options);
+ return *jptr;
+ }
+
+ std::string to_string(int level = 0) const override
+ {
+ std::string s;
+ if (level > 0)
+ {
+ s.append("\n");
+ s.append(level*2, ' ');
+ }
+ s.append("union selector ");
+ for (auto& selector : selectors_)
+ {
+ s.append(selector->to_string(level+1));
+ //s.push_back('\n');
+ }
+
+ return s;
+ }
+ };
+
+ template <class Json,class JsonReference>
+ class filter_selector final : public base_selector<Json,JsonReference>
+ {
+ using supertype = base_selector<Json,JsonReference>;
+
+ expression<Json,JsonReference> expr_;
+
+ public:
+ using value_type = typename supertype::value_type;
+ using reference = typename supertype::reference;
+ using pointer = typename supertype::pointer;
+ using path_value_pair_type = typename supertype::path_value_pair_type;
+ using json_location_node_type = typename supertype::json_location_node_type;
+ using path_generator_type = path_generator<Json,JsonReference>;
+ using node_receiver_type = typename supertype::node_receiver_type;
+
+ filter_selector(expression<Json,JsonReference>&& expr)
+ : base_selector<Json,JsonReference>(), expr_(std::move(expr))
+ {
+ }
+
+ void select(dynamic_resources<Json,JsonReference>& resources,
+ reference root,
+ const json_location_node_type& last,
+ reference current,
+ node_receiver_type& receiver,
+ result_options options) const override
+ {
+ if (current.is_array())
+ {
+ for (std::size_t i = 0; i < current.size(); ++i)
+ {
+ std::error_code ec;
+ value_type r = expr_.evaluate(resources, root, current[i], options, ec);
+ bool t = ec ? false : detail::is_true(r);
+ if (t)
+ {
+ this->tail_select(resources, root,
+ path_generator_type::generate(resources, last, i, options),
+ current[i], receiver, options);
+ }
+ }
+ }
+ else if (current.is_object())
+ {
+ for (auto& member : current.object_range())
+ {
+ std::error_code ec;
+ value_type r = expr_.evaluate(resources, root, member.value(), options, ec);
+ bool t = ec ? false : detail::is_true(r);
+ if (t)
+ {
+ this->tail_select(resources, root,
+ path_generator_type::generate(resources, last, member.key(), options),
+ member.value(), receiver, options);
+ }
+ }
+ }
+ }
+
+ reference evaluate(dynamic_resources<Json,JsonReference>& resources,
+ reference root,
+ const json_location_node_type& last,
+ reference current,
+ result_options options,
+ std::error_code&) const override
+ {
+ auto jptr = resources.create_json(json_array_arg);
+ json_array_receiver<Json,JsonReference> receiver(jptr);
+ select(resources, root, last, current, receiver, options);
+ return *jptr;
+ }
+
+ std::string to_string(int level = 0) const override
+ {
+ std::string s;
+ if (level > 0)
+ {
+ s.append("\n");
+ s.append(level*2, ' ');
+ }
+ s.append("filter selector ");
+ s.append(expr_.to_string(level+1));
+
+ return s;
+ }
+ };
+
+ template <class Json,class JsonReference>
+ class index_expression_selector final : public base_selector<Json,JsonReference>
+ {
+ using supertype = base_selector<Json,JsonReference>;
+
+ expression<Json,JsonReference> expr_;
+
+ public:
+ using value_type = typename supertype::value_type;
+ using reference = typename supertype::reference;
+ using pointer = typename supertype::pointer;
+ using path_value_pair_type = typename supertype::path_value_pair_type;
+ using json_location_node_type = typename supertype::json_location_node_type;
+ using path_generator_type = path_generator<Json,JsonReference>;
+ using node_receiver_type = typename supertype::node_receiver_type;
+
+ index_expression_selector(expression<Json,JsonReference>&& expr)
+ : base_selector<Json,JsonReference>(), expr_(std::move(expr))
+ {
+ }
+
+ void select(dynamic_resources<Json,JsonReference>& resources,
+ reference root,
+ const json_location_node_type& last,
+ reference current,
+ node_receiver_type& receiver,
+ result_options options) const override
+ {
+ std::error_code ec;
+ value_type j = expr_.evaluate(resources, root, current, options, ec);
+
+ if (!ec)
+ {
+ if (j.template is<std::size_t>() && current.is_array())
+ {
+ std::size_t start = j.template as<std::size_t>();
+ this->tail_select(resources, root,
+ path_generator_type::generate(resources, last, start, options),
+ current.at(start), receiver, options);
+ }
+ else if (j.is_string() && current.is_object())
+ {
+ this->tail_select(resources, root,
+ path_generator_type::generate(resources, last, j.as_string(), options),
+ current.at(j.as_string_view()), receiver, options);
+ }
+ }
+ }
+
+ reference evaluate(dynamic_resources<Json,JsonReference>& resources,
+ reference root,
+ const json_location_node_type& last,
+ reference current,
+ result_options options,
+ std::error_code& ec) const override
+ {
+ //std::cout << "index_expression_selector current: " << current << "\n";
+
+ value_type j = expr_.evaluate(resources, root, current, options, ec);
+
+ if (!ec)
+ {
+ if (j.template is<std::size_t>() && current.is_array())
+ {
+ std::size_t start = j.template as<std::size_t>();
+ return this->evaluate_tail(resources, root, last, current.at(start), options, ec);
+ }
+ else if (j.is_string() && current.is_object())
+ {
+ return this->evaluate_tail(resources, root, last, current.at(j.as_string_view()), options, ec);
+ }
+ else
+ {
+ return resources.null_value();
+ }
+ }
+ else
+ {
+ return resources.null_value();
+ }
+ }
+
+ std::string to_string(int level = 0) const override
+ {
+ std::string s;
+ if (level > 0)
+ {
+ s.append("\n");
+ s.append(level*2, ' ');
+ }
+ s.append("bracket expression selector ");
+ s.append(expr_.to_string(level+1));
+ s.append(base_selector<Json,JsonReference>::to_string(level+1));
+
+ return s;
+ }
+ };
+
+ template <class Json,class JsonReference>
+ class slice_selector final : public base_selector<Json,JsonReference>
+ {
+ using supertype = base_selector<Json,JsonReference>;
+ using path_generator_type = path_generator<Json, JsonReference>;
+
+ slice slice_;
+ public:
+ using value_type = typename supertype::value_type;
+ using reference = typename supertype::reference;
+ using pointer = typename supertype::pointer;
+ using path_value_pair_type = typename supertype::path_value_pair_type;
+ using json_location_node_type = typename supertype::json_location_node_type;
+ using node_receiver_type = typename supertype::node_receiver_type;
+
+ slice_selector(const slice& slic)
+ : base_selector<Json,JsonReference>(), slice_(slic)
+ {
+ }
+
+ void select(dynamic_resources<Json,JsonReference>& resources,
+ reference root,
+ const json_location_node_type& last,
+ reference current,
+ node_receiver_type& receiver,
+ result_options options) const override
+ {
+ if (current.is_array())
+ {
+ auto start = slice_.get_start(current.size());
+ auto end = slice_.get_stop(current.size());
+ auto step = slice_.step();
+
+ if (step > 0)
+ {
+ if (start < 0)
+ {
+ start = 0;
+ }
+ if (end > static_cast<int64_t>(current.size()))
+ {
+ end = current.size();
+ }
+ for (int64_t i = start; i < end; i += step)
+ {
+ std::size_t j = static_cast<std::size_t>(i);
+ this->tail_select(resources, root,
+ path_generator_type::generate(resources, last, j, options),
+ current[j], receiver, options);
+ }
+ }
+ else if (step < 0)
+ {
+ if (start >= static_cast<int64_t>(current.size()))
+ {
+ start = static_cast<int64_t>(current.size()) - 1;
+ }
+ if (end < -1)
+ {
+ end = -1;
+ }
+ for (int64_t i = start; i > end; i += step)
+ {
+ std::size_t j = static_cast<std::size_t>(i);
+ if (j < current.size())
+ {
+ this->tail_select(resources, root,
+ path_generator_type::generate(resources, last,j,options), current[j], receiver, options);
+ }
+ }
+ }
+ }
+ }
+
+ reference evaluate(dynamic_resources<Json,JsonReference>& resources,
+ reference root,
+ const json_location_node_type& last,
+ reference current,
+ result_options options,
+ std::error_code&) const override
+ {
+ auto jptr = resources.create_json(json_array_arg);
+ json_array_receiver<Json,JsonReference> accum(jptr);
+ select(resources, root, last, current, accum, options);
+ return *jptr;
+ }
+ };
+
+ template <class Json,class JsonReference>
+ class function_selector final : public base_selector<Json,JsonReference>
+ {
+ using supertype = base_selector<Json,JsonReference>;
+
+ expression<Json,JsonReference> expr_;
+
+ public:
+ using value_type = typename supertype::value_type;
+ using reference = typename supertype::reference;
+ using pointer = typename supertype::pointer;
+ using path_value_pair_type = typename supertype::path_value_pair_type;
+ using json_location_node_type = typename supertype::json_location_node_type;
+ using path_generator_type = path_generator<Json,JsonReference>;
+ using node_receiver_type = typename supertype::node_receiver_type;
+
+ function_selector(expression<Json,JsonReference>&& expr)
+ : base_selector<Json,JsonReference>(), expr_(std::move(expr))
+ {
+ }
+
+ void select(dynamic_resources<Json,JsonReference>& resources,
+ reference root,
+ const json_location_node_type& last,
+ reference current,
+ node_receiver_type& receiver,
+ result_options options) const override
+ {
+ std::error_code ec;
+ value_type ref = expr_.evaluate(resources, root, current, options, ec);
+ if (!ec)
+ {
+ this->tail_select(resources, root, last, *resources.create_json(std::move(ref)), receiver, options);
+ }
+ }
+
+ reference evaluate(dynamic_resources<Json,JsonReference>& resources,
+ reference root,
+ const json_location_node_type& last,
+ reference current,
+ result_options options,
+ std::error_code& ec) const override
+ {
+ value_type ref = expr_.evaluate(resources, root, current, options, ec);
+ if (!ec)
+ {
+ return this->evaluate_tail(resources, root, last, *resources.create_json(std::move(ref)),
+ options, ec);
+ }
+ else
+ {
+ return resources.null_value();
+ }
+ }
+
+ std::string to_string(int level = 0) const override
+ {
+ std::string s;
+ if (level > 0)
+ {
+ s.append("\n");
+ s.append(level*2, ' ');
+ }
+ s.append("function_selector ");
+ s.append(expr_.to_string(level+1));
+
+ return s;
+ }
+ };
+
+} // namespace detail
+} // namespace jsonpath
+} // namespace jsoncons
+
+#endif
diff --git a/include/jsoncons_ext/jsonpointer/jsonpointer.hpp b/include/jsoncons_ext/jsonpointer/jsonpointer.hpp
new file mode 100644
index 0000000..41e41e2
--- /dev/null
+++ b/include/jsoncons_ext/jsonpointer/jsonpointer.hpp
@@ -0,0 +1,1577 @@
+// Copyright 2017 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_JSONPOINTER_JSONPOINTER_HPP
+#define JSONCONS_JSONPOINTER_JSONPOINTER_HPP
+
+#include <string>
+#include <vector>
+#include <memory>
+#include <iostream>
+#include <iterator>
+#include <utility> // std::move
+#include <system_error> // system_error
+#include <type_traits> // std::enable_if, std::true_type
+#include <jsoncons/json.hpp>
+#include <jsoncons_ext/jsonpointer/jsonpointer_error.hpp>
+#include <jsoncons/detail/write_number.hpp>
+
+namespace jsoncons { namespace jsonpointer {
+
+ namespace detail {
+
+ enum class pointer_state
+ {
+ start,
+ escaped,
+ delim
+ };
+
+ } // namespace detail
+
+ template <class CharT>
+ std::basic_string<CharT> escape_string(const std::basic_string<CharT>& s)
+ {
+ std::basic_string<CharT> result;
+ for (auto c : s)
+ {
+ switch (c)
+ {
+ case '~':
+ result.push_back('~');
+ result.push_back('0');
+ break;
+ case '/':
+ result.push_back('~');
+ result.push_back('1');
+ break;
+ default:
+ result.push_back(c);
+ break;
+ }
+ }
+ return result;
+ }
+
+ // basic_json_pointer
+
+ template <class CharT>
+ class basic_json_pointer
+ {
+ public:
+ // Member types
+ using char_type = CharT;
+ using string_type = std::basic_string<char_type>;
+ using string_view_type = jsoncons::basic_string_view<char_type>;
+ using const_iterator = typename std::vector<string_type>::const_iterator;
+ using iterator = const_iterator;
+ using const_reverse_iterator = typename std::vector<string_type>::const_reverse_iterator;
+ using reverse_iterator = const_reverse_iterator;
+ private:
+ std::vector<string_type> tokens_;
+ public:
+ // Constructors
+ basic_json_pointer()
+ {
+ }
+
+ basic_json_pointer(const std::vector<string_type>& tokens)
+ : tokens_(tokens)
+ {
+ }
+
+ basic_json_pointer(std::vector<string_type>&& tokens)
+ : tokens_(std::move(tokens))
+ {
+ }
+
+ explicit basic_json_pointer(const string_view_type& s)
+ {
+ std::error_code ec;
+ auto jp = parse(s, ec);
+ if (ec)
+ {
+ throw jsonpointer_error(ec);
+ }
+ tokens_ = std::move(jp.tokens_);
+ }
+
+ explicit basic_json_pointer(const string_view_type& s, std::error_code& ec)
+ {
+ auto jp = parse(s, ec);
+ if (!ec)
+ {
+ tokens_ = std::move(jp.tokens_);
+ }
+ }
+
+ basic_json_pointer(const basic_json_pointer&) = default;
+
+ basic_json_pointer(basic_json_pointer&&) = default;
+
+ static basic_json_pointer parse(const string_view_type& input, std::error_code& ec)
+ {
+ std::vector<string_type> tokens;
+ if (input.empty() || (input[0] == '#' && input.size() == 1))
+ {
+ return basic_json_pointer<CharT>();
+ }
+
+ const char_type* p;
+ const char_type* pend;
+ string_type unescaped;
+ if (input[0] == '#')
+ {
+ unescaped = unescape_uri_string(input, ec);
+ p = unescaped.data() + 1;
+ pend = unescaped.data() + unescaped.size();
+ }
+ else
+ {
+ p = input.data();
+ pend = input.data() + input.size();
+ }
+
+ auto state = jsonpointer::detail::pointer_state::start;
+ string_type buffer;
+
+ while (p < pend)
+ {
+ bool done = false;
+ while (p < pend && !done)
+ {
+ switch (state)
+ {
+ case jsonpointer::detail::pointer_state::start:
+ switch (*p)
+ {
+ case '/':
+ state = jsonpointer::detail::pointer_state::delim;
+ break;
+ default:
+ ec = jsonpointer_errc::expected_slash;
+ return basic_json_pointer();
+ };
+ break;
+ case jsonpointer::detail::pointer_state::delim:
+ switch (*p)
+ {
+ case '/':
+ done = true;
+ break;
+ case '~':
+ state = jsonpointer::detail::pointer_state::escaped;
+ break;
+ default:
+ buffer.push_back(*p);
+ break;
+ };
+ break;
+ case jsonpointer::detail::pointer_state::escaped:
+ switch (*p)
+ {
+ case '0':
+ buffer.push_back('~');
+ state = jsonpointer::detail::pointer_state::delim;
+ break;
+ case '1':
+ buffer.push_back('/');
+ state = jsonpointer::detail::pointer_state::delim;
+ break;
+ default:
+ ec = jsonpointer_errc::expected_0_or_1;
+ return basic_json_pointer();
+ };
+ break;
+ }
+ ++p;
+ }
+ tokens.push_back(buffer);
+ buffer.clear();
+ }
+ if (!buffer.empty())
+ {
+ tokens.push_back(buffer);
+ }
+ return basic_json_pointer(tokens);
+ }
+
+ static string_type escape_uri_string(const string_type& s)
+ {
+ string_type escaped;
+ for (auto ch : s)
+ {
+ switch (ch)
+ {
+ case '%':
+ escaped.append(string_type{'%','2','5'});
+ break;
+ case '^':
+ escaped.append(string_type{'%','5','E'});
+ break;
+ case '|':
+ escaped.append(string_type{'%','7','C'});
+ break;
+ case '\\':
+ escaped.append(string_type{'%','5','C'});
+ break;
+ case '\"':
+ escaped.append(string_type{'%','2','2'});
+ break;
+ case ' ':
+ escaped.append(string_type{'%','2','0'});
+ break;
+ default:
+ escaped.push_back(ch);
+ break;
+ }
+ }
+
+ return escaped;
+ }
+
+ static string_type unescape_uri_string(const string_view_type& s, std::error_code& ec)
+ {
+ if (s.size() < 3)
+ {
+ return string_type(s);
+ }
+ string_type unescaped;
+ std::size_t last = s.size() - 2;
+ std::size_t pos = 0;
+ while (pos < last)
+ {
+ if (s[pos] == '%')
+ {
+ uint8_t ch;
+ auto result = jsoncons::detail::to_integer_base16(s.data() + (pos+1), 2, ch);
+ if (!result)
+ {
+ ec = jsonpointer_errc::invalid_uri_escaped_data;
+ return string_type(s);
+ }
+ unescaped.push_back(ch);
+ pos += 3;
+ }
+ else
+ {
+ unescaped.push_back(s[pos]);
+ ++pos;
+ }
+ }
+ while (pos < s.size())
+ {
+ unescaped.push_back(s[pos]);
+ ++pos;
+ }
+ return unescaped;
+ }
+
+ // operator=
+ basic_json_pointer& operator=(const basic_json_pointer&) = default;
+
+ basic_json_pointer& operator=(basic_json_pointer&&) = default;
+
+ // Modifiers
+
+ void clear()
+ {
+ tokens_.clear();
+ }
+
+ basic_json_pointer& operator/=(const string_type& s)
+ {
+ tokens_.push_back(s);
+ return *this;
+ }
+
+ template <class IntegerType>
+ typename std::enable_if<type_traits::is_integer<IntegerType>::value, basic_json_pointer&>::type
+ operator/=(IntegerType val)
+ {
+ string_type s;
+ jsoncons::detail::from_integer(val, s);
+ tokens_.push_back(s);
+
+ return *this;
+ }
+
+ basic_json_pointer& operator+=(const basic_json_pointer& p)
+ {
+ for (const auto& s : p.tokens_)
+ {
+ tokens_.push_back(s);
+ }
+ return *this;
+ }
+
+ // Accessors
+ bool empty() const
+ {
+ return tokens_.empty();
+ }
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+
+ JSONCONS_DEPRECATED_MSG("Instead, use to_string()")
+ string_type string() const
+ {
+ return to_string();
+ }
+#endif
+ string_type to_string() const
+ {
+ string_type buffer;
+ for (const auto& token : tokens_)
+ {
+ buffer.push_back('/');
+ for (auto c : token)
+ {
+ switch (c)
+ {
+ case '~':
+ buffer.push_back('~');
+ buffer.push_back('0');
+ break;
+ case '/':
+ buffer.push_back('~');
+ buffer.push_back('1');
+ break;
+ default:
+ buffer.push_back(c);
+ break;
+ }
+ }
+ }
+ return buffer;
+ }
+
+ string_type to_uri_fragment() const
+ {
+ string_type buffer{'#'};
+ for (const auto& token : tokens_)
+ {
+ buffer.push_back('/');
+ string_type s = escape_uri_string(token);
+ for (auto c : s)
+ {
+ switch (c)
+ {
+ case '~':
+ buffer.push_back('~');
+ buffer.push_back('0');
+ break;
+ case '/':
+ buffer.push_back('~');
+ buffer.push_back('1');
+ break;
+ default:
+ buffer.push_back(c);
+ break;
+ }
+ }
+ }
+ return buffer;
+ }
+
+ // Iterators
+ iterator begin() const
+ {
+ return tokens_.begin();
+ }
+ iterator end() const
+ {
+ return tokens_.end();
+ }
+
+ reverse_iterator rbegin() const
+ {
+ return tokens_.rbegin();
+ }
+ reverse_iterator rend() const
+ {
+ return tokens_.rend();
+ }
+
+ // Non-member functions
+ friend basic_json_pointer<CharT> operator/(const basic_json_pointer<CharT>& lhs, const string_type& rhs)
+ {
+ basic_json_pointer<CharT> p(lhs);
+ p /= rhs;
+ return p;
+ }
+
+ friend basic_json_pointer<CharT> operator+( const basic_json_pointer<CharT>& lhs, const basic_json_pointer<CharT>& rhs )
+ {
+ basic_json_pointer<CharT> p(lhs);
+ p += rhs;
+ return p;
+ }
+
+ friend bool operator==( const basic_json_pointer& lhs, const basic_json_pointer& rhs )
+ {
+ return lhs.tokens_ == rhs.okens_;
+ }
+
+ friend bool operator!=( const basic_json_pointer& lhs, const basic_json_pointer& rhs )
+ {
+ return lhs.tokens_ != rhs.tokens_;
+ }
+
+ friend std::basic_ostream<CharT>&
+ operator<<( std::basic_ostream<CharT>& os, const basic_json_pointer<CharT>& p )
+ {
+ os << p.to_string();
+ return os;
+ }
+ };
+
+ template <class CharT,class IntegerType>
+ typename std::enable_if<type_traits::is_integer<IntegerType>::value, basic_json_pointer<CharT>>::type
+ operator/(const basic_json_pointer<CharT>& lhs, IntegerType rhs)
+ {
+ basic_json_pointer<CharT> p(lhs);
+ p /= rhs;
+ return p;
+ }
+
+ using json_pointer = basic_json_pointer<char>;
+ using wjson_pointer = basic_json_pointer<wchar_t>;
+
+ #if !defined(JSONCONS_NO_DEPRECATED)
+ template<class CharT>
+ using basic_address = basic_json_pointer<CharT>;
+ template<class CharT>
+ using basic_json_ptr = basic_json_pointer<CharT>;
+ JSONCONS_DEPRECATED_MSG("Instead, use json_pointer") typedef json_pointer address;
+ JSONCONS_DEPRECATED_MSG("Instead, use json_pointer") typedef json_pointer json_ptr;
+ JSONCONS_DEPRECATED_MSG("Instead, use wjson_pointer") typedef json_pointer wjson_ptr;
+ #endif
+
+ namespace detail {
+
+ template <class Json>
+ const Json* resolve(const Json* current, const typename Json::string_view_type& buffer, std::error_code& ec)
+ {
+ if (current->is_array())
+ {
+ if (buffer.size() == 1 && buffer[0] == '-')
+ {
+ ec = jsonpointer_errc::index_exceeds_array_size;
+ return current;
+ }
+ std::size_t index{0};
+ auto result = jsoncons::detail::to_integer_decimal(buffer.data(), buffer.length(), index);
+ if (!result)
+ {
+ ec = jsonpointer_errc::invalid_index;
+ return current;
+ }
+ if (index >= current->size())
+ {
+ ec = jsonpointer_errc::index_exceeds_array_size;
+ return current;
+ }
+ current = std::addressof(current->at(index));
+ }
+ else if (current->is_object())
+ {
+ if (!current->contains(buffer))
+ {
+ ec = jsonpointer_errc::key_not_found;
+ return current;
+ }
+ current = std::addressof(current->at(buffer));
+ }
+ else
+ {
+ ec = jsonpointer_errc::expected_object_or_array;
+ return current;
+ }
+ return current;
+ }
+
+ template <class Json>
+ Json* resolve(Json* current, const typename Json::string_view_type& buffer, bool create_if_missing, std::error_code& ec)
+ {
+ if (current->is_array())
+ {
+ if (buffer.size() == 1 && buffer[0] == '-')
+ {
+ ec = jsonpointer_errc::index_exceeds_array_size;
+ return current;
+ }
+ std::size_t index{0};
+ auto result = jsoncons::detail::to_integer_decimal(buffer.data(), buffer.length(), index);
+ if (!result)
+ {
+ ec = jsonpointer_errc::invalid_index;
+ return current;
+ }
+ if (index >= current->size())
+ {
+ ec = jsonpointer_errc::index_exceeds_array_size;
+ return current;
+ }
+ current = std::addressof(current->at(index));
+ }
+ else if (current->is_object())
+ {
+ if (!current->contains(buffer))
+ {
+ if (create_if_missing)
+ {
+ auto r = current->try_emplace(buffer, Json());
+ current = std::addressof(r.first->value());
+ }
+ else
+ {
+ ec = jsonpointer_errc::key_not_found;
+ return current;
+ }
+ }
+ else
+ {
+ current = std::addressof(current->at(buffer));
+ }
+ }
+ else
+ {
+ ec = jsonpointer_errc::expected_object_or_array;
+ return current;
+ }
+ return current;
+ }
+
+ } // namespace detail
+
+ // get
+
+ template<class Json>
+ Json& get(Json& root,
+ const basic_json_pointer<typename Json::char_type>& location,
+ bool create_if_missing,
+ std::error_code& ec)
+ {
+ if (location.empty())
+ {
+ return root;
+ }
+
+ Json* current = std::addressof(root);
+ auto it = location.begin();
+ auto end = location.end();
+ while (it != end)
+ {
+ current = jsoncons::jsonpointer::detail::resolve(current, *it, create_if_missing, ec);
+ if (ec)
+ return *current;
+ ++it;
+ }
+ return *current;
+ }
+
+ template<class Json, class StringSource>
+ typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,Json&>::type
+ get(Json& root,
+ const StringSource& location_str,
+ bool create_if_missing,
+ std::error_code& ec)
+ {
+ auto jsonptr = basic_json_pointer<typename Json::char_type>::parse(location_str, ec);
+ if (ec)
+ {
+ return root;
+ }
+ return get(root, jsonptr, create_if_missing, ec);
+ }
+
+ template<class Json>
+ const Json& get(const Json& root,
+ const basic_json_pointer<typename Json::char_type>& location,
+ std::error_code& ec)
+ {
+ if (location.empty())
+ {
+ return root;
+ }
+
+ const Json* current = std::addressof(root);
+ auto it = location.begin();
+ auto end = location.end();
+ while (it != end)
+ {
+ current = jsoncons::jsonpointer::detail::resolve(current, *it, ec);
+ if (ec)
+ return *current;
+ ++it;
+ }
+ return *current;
+ }
+
+ template<class Json, class StringSource>
+ typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,const Json&>::type
+ get(const Json& root,
+ const StringSource& location_str,
+ std::error_code& ec)
+ {
+ auto jsonptr = basic_json_pointer<typename Json::char_type>::parse(location_str, ec);
+ if (ec)
+ {
+ return root;
+ }
+ return get(root, jsonptr, ec);
+ }
+
+ template<class Json>
+ Json& get(Json& root,
+ const basic_json_pointer<typename Json::char_type>& location,
+ std::error_code& ec)
+ {
+ return get(root, location, false, ec);
+ }
+
+ template<class Json, class StringSource>
+ typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,Json&>::type
+ get(Json& root,
+ const StringSource& location_str,
+ std::error_code& ec)
+ {
+ return get(root, location_str, false, ec);
+ }
+
+ template<class Json>
+ Json& get(Json& root,
+ const basic_json_pointer<typename Json::char_type>& location,
+ bool create_if_missing = false)
+ {
+ std::error_code ec;
+ Json& j = get(root, location, create_if_missing, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(jsonpointer_error(ec));
+ }
+ return j;
+ }
+
+ template<class Json, class StringSource>
+ typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,Json&>::type
+ get(Json& root,
+ const StringSource& location_str,
+ bool create_if_missing = false)
+ {
+ std::error_code ec;
+ Json& result = get(root, location_str, create_if_missing, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(jsonpointer_error(ec));
+ }
+ return result;
+ }
+
+ template<class Json>
+ const Json& get(const Json& root, const basic_json_pointer<typename Json::char_type>& location)
+ {
+ std::error_code ec;
+ const Json& j = get(root, location, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(jsonpointer_error(ec));
+ }
+ return j;
+ }
+
+ template<class Json, class StringSource>
+ typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,const Json&>::type
+ get(const Json& root, const StringSource& location_str)
+ {
+ std::error_code ec;
+ const Json& j = get(root, location_str, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(jsonpointer_error(ec));
+ }
+ return j;
+ }
+
+ // contains
+
+ template<class Json>
+ bool contains(const Json& root, const basic_json_pointer<typename Json::char_type>& location)
+ {
+ std::error_code ec;
+ get(root, location, ec);
+ return !ec ? true : false;
+ }
+
+ template<class Json, class StringSource>
+ typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,bool>::type
+ contains(const Json& root, const StringSource& location_str)
+ {
+ std::error_code ec;
+ get(root, location_str, ec);
+ return !ec ? true : false;
+ }
+
+ template<class Json,class T>
+ void add(Json& root,
+ const basic_json_pointer<typename Json::char_type>& location,
+ T&& value,
+ bool create_if_missing,
+ std::error_code& ec)
+ {
+ Json* current = std::addressof(root);
+
+ std::basic_string<typename Json::char_type> buffer;
+ auto it = location.begin();
+ auto end = location.end();
+ while (it != end)
+ {
+ buffer = *it;
+ ++it;
+ if (it != end)
+ {
+ current = jsoncons::jsonpointer::detail::resolve(current, buffer, create_if_missing, ec);
+ if (ec)
+ return;
+ }
+ }
+ if (current->is_array())
+ {
+ if (buffer.size() == 1 && buffer[0] == '-')
+ {
+ current->emplace_back(std::forward<T>(value));
+ current = std::addressof(current->at(current->size()-1));
+ }
+ else
+ {
+ std::size_t index{0};
+ auto result = jsoncons::detail::to_integer_decimal(buffer.data(), buffer.length(), index);
+ if (!result)
+ {
+ ec = jsonpointer_errc::invalid_index;
+ return;
+ }
+ if (index > current->size())
+ {
+ ec = jsonpointer_errc::index_exceeds_array_size;
+ return;
+ }
+ if (index == current->size())
+ {
+ current->emplace_back(std::forward<T>(value));
+ current = std::addressof(current->at(current->size()-1));
+ }
+ else
+ {
+ auto it2 = current->insert(current->array_range().begin()+index,std::forward<T>(value));
+ current = std::addressof(*it2);
+ }
+ }
+ }
+ else if (current->is_object())
+ {
+ auto r = current->insert_or_assign(buffer,std::forward<T>(value));
+ current = std::addressof(r.first->value());
+ }
+ else
+ {
+ ec = jsonpointer_errc::expected_object_or_array;
+ return;
+ }
+ }
+
+ // add
+ template<class Json, class StringSource, class T>
+ typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type
+ add(Json& root,
+ const StringSource& location_str,
+ T&& value,
+ bool create_if_missing,
+ std::error_code& ec)
+ {
+ auto jsonptr = basic_json_pointer<typename Json::char_type>::parse(location_str, ec);
+ if (ec)
+ {
+ return;
+ }
+ add(root, jsonptr, std::forward<T>(value), create_if_missing, ec);
+ }
+
+ template<class Json,class T>
+ void add(Json& root,
+ const basic_json_pointer<typename Json::char_type>& location,
+ T&& value,
+ std::error_code& ec)
+ {
+ add(root, location, std::forward<T>(value), false, ec);
+ }
+
+ template<class Json, class StringSource, class T>
+ typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type
+ add(Json& root,
+ const StringSource& location_str,
+ T&& value,
+ std::error_code& ec)
+ {
+ add(root, location_str, std::forward<T>(value), false, ec);
+ }
+
+ template<class Json,class T>
+ void add(Json& root,
+ const basic_json_pointer<typename Json::char_type>& location,
+ T&& value,
+ bool create_if_missing = false)
+ {
+ std::error_code ec;
+ add(root, location, std::forward<T>(value), create_if_missing, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(jsonpointer_error(ec));
+ }
+ }
+
+ template<class Json, class StringSource, class T>
+ typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type
+ add(Json& root,
+ const StringSource& location_str,
+ T&& value,
+ bool create_if_missing = false)
+ {
+ std::error_code ec;
+ add(root, location_str, std::forward<T>(value), create_if_missing, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(jsonpointer_error(ec));
+ }
+ }
+
+ // add_if_absent
+
+ template<class Json, class T>
+ void add_if_absent(Json& root,
+ const basic_json_pointer<typename Json::char_type>& location,
+ T&& value,
+ bool create_if_missing,
+ std::error_code& ec)
+ {
+ Json* current = std::addressof(root);
+
+ std::basic_string<typename Json::char_type> buffer;
+ auto it = location.begin();
+ auto end = location.end();
+
+ while (it != end)
+ {
+ buffer = *it;
+ ++it;
+ if (it != end)
+ {
+ current = jsoncons::jsonpointer::detail::resolve(current, buffer, create_if_missing, ec);
+ if (ec)
+ return;
+ }
+ }
+ if (current->is_array())
+ {
+ if (buffer.size() == 1 && buffer[0] == '-')
+ {
+ current->emplace_back(std::forward<T>(value));
+ current = std::addressof(current->at(current->size()-1));
+ }
+ else
+ {
+ std::size_t index{0};
+ auto result = jsoncons::detail::to_integer_decimal(buffer.data(), buffer.length(), index);
+ if (!result)
+ {
+ ec = jsonpointer_errc::invalid_index;
+ return;
+ }
+ if (index > current->size())
+ {
+ ec = jsonpointer_errc::index_exceeds_array_size;
+ return;
+ }
+ if (index == current->size())
+ {
+ current->emplace_back(std::forward<T>(value));
+ current = std::addressof(current->at(current->size()-1));
+ }
+ else
+ {
+ auto it2 = current->insert(current->array_range().begin()+index,std::forward<T>(value));
+ current = std::addressof(*it2);
+ }
+ }
+ }
+ else if (current->is_object())
+ {
+ if (current->contains(buffer))
+ {
+ ec = jsonpointer_errc::key_already_exists;
+ return;
+ }
+ else
+ {
+ auto r = current->try_emplace(buffer,std::forward<T>(value));
+ current = std::addressof(r.first->value());
+ }
+ }
+ else
+ {
+ ec = jsonpointer_errc::expected_object_or_array;
+ return;
+ }
+ }
+
+ template<class Json, class StringSource, class T>
+ typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type
+ add_if_absent(Json& root,
+ const StringSource& location_str,
+ T&& value,
+ bool create_if_missing,
+ std::error_code& ec)
+ {
+ auto jsonptr = basic_json_pointer<typename Json::char_type>::parse(location_str, ec);
+ if (ec)
+ {
+ return;
+ }
+ add_if_absent(root, jsonptr, std::forward<T>(value), create_if_missing, ec);
+ }
+
+ template<class Json, class StringSource, class T>
+ typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type
+ add_if_absent(Json& root,
+ const StringSource& location,
+ T&& value,
+ std::error_code& ec)
+ {
+ add_if_absent(root, location, std::forward<T>(value), false, ec);
+ }
+
+ template<class Json, class StringSource, class T>
+ typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type
+ add_if_absent(Json& root,
+ const StringSource& location_str,
+ T&& value,
+ bool create_if_missing = false)
+ {
+ std::error_code ec;
+ add_if_absent(root, location_str, std::forward<T>(value), create_if_missing, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(jsonpointer_error(ec));
+ }
+ }
+
+ template<class Json, class T>
+ void add_if_absent(Json& root,
+ const basic_json_pointer<typename Json::char_type>& location,
+ T&& value,
+ std::error_code& ec)
+ {
+ add_if_absent(root, location, std::forward<T>(value), false, ec);
+ }
+
+ template<class Json, class T>
+ void add_if_absent(Json& root,
+ const basic_json_pointer<typename Json::char_type>& location,
+ T&& value,
+ bool create_if_missing = false)
+ {
+ std::error_code ec;
+ add_if_absent(root, location, std::forward<T>(value), create_if_missing, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(jsonpointer_error(ec));
+ }
+ }
+
+ // remove
+
+ template<class Json>
+ void remove(Json& root, const basic_json_pointer<typename Json::char_type>& location, std::error_code& ec)
+ {
+ Json* current = std::addressof(root);
+
+ std::basic_string<typename Json::char_type> buffer;
+ auto it = location.begin();
+ auto end = location.end();
+
+ while (it != end)
+ {
+ buffer = *it;
+ ++it;
+ if (it != end)
+ {
+ current = jsoncons::jsonpointer::detail::resolve(current, buffer, false, ec);
+ if (ec)
+ return;
+ }
+ }
+ if (current->is_array())
+ {
+ if (buffer.size() == 1 && buffer[0] == '-')
+ {
+ ec = jsonpointer_errc::index_exceeds_array_size;
+ return;
+ }
+ else
+ {
+ std::size_t index{0};
+ auto result = jsoncons::detail::to_integer_decimal(buffer.data(), buffer.length(), index);
+ if (!result)
+ {
+ ec = jsonpointer_errc::invalid_index;
+ return;
+ }
+ if (index >= current->size())
+ {
+ ec = jsonpointer_errc::index_exceeds_array_size;
+ return;
+ }
+ current->erase(current->array_range().begin()+index);
+ }
+ }
+ else if (current->is_object())
+ {
+ if (!current->contains(buffer))
+ {
+ ec = jsonpointer_errc::key_not_found;
+ return;
+ }
+ else
+ {
+ current->erase(buffer);
+ }
+ }
+ else
+ {
+ ec = jsonpointer_errc::expected_object_or_array;
+ return;
+ }
+ }
+
+ template<class Json, class StringSource>
+ typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type
+ remove(Json& root, const StringSource& location_str, std::error_code& ec)
+ {
+ auto jsonptr = basic_json_pointer<typename Json::char_type>::parse(location_str, ec);
+ if (ec)
+ {
+ return;
+ }
+ remove(root, jsonptr, ec);
+ }
+
+ template<class Json, class StringSource>
+ typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type
+ remove(Json& root, const StringSource& location_str)
+ {
+ std::error_code ec;
+ remove(root, location_str, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(jsonpointer_error(ec));
+ }
+ }
+
+ template<class Json>
+ void remove(Json& root, const basic_json_pointer<typename Json::char_type>& location)
+ {
+ std::error_code ec;
+ remove(root, location, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(jsonpointer_error(ec));
+ }
+ }
+
+ // replace
+
+ template<class Json, class T>
+ void replace(Json& root,
+ const basic_json_pointer<typename Json::char_type>& location,
+ T&& value,
+ bool create_if_missing,
+ std::error_code& ec)
+ {
+ Json* current = std::addressof(root);
+
+ std::basic_string<typename Json::char_type> buffer;
+ auto it = location.begin();
+ auto end = location.end();
+
+ while (it != end)
+ {
+ buffer = *it;
+ ++it;
+ if (it != end)
+ {
+ current = jsoncons::jsonpointer::detail::resolve(current, buffer, create_if_missing, ec);
+ if (ec)
+ return;
+ }
+ }
+ if (current->is_array())
+ {
+ if (buffer.size() == 1 && buffer[0] == '-')
+ {
+ ec = jsonpointer_errc::index_exceeds_array_size;
+ return;
+ }
+ else
+ {
+ std::size_t index{};
+ auto result = jsoncons::detail::to_integer_decimal(buffer.data(), buffer.length(), index);
+ if (!result)
+ {
+ ec = jsonpointer_errc::invalid_index;
+ return;
+ }
+ if (index >= current->size())
+ {
+ ec = jsonpointer_errc::index_exceeds_array_size;
+ return;
+ }
+ current->at(index) = std::forward<T>(value);
+ }
+ }
+ else if (current->is_object())
+ {
+ if (!current->contains(buffer))
+ {
+ if (create_if_missing)
+ {
+ current->try_emplace(buffer,std::forward<T>(value));
+ }
+ else
+ {
+ ec = jsonpointer_errc::key_not_found;
+ return;
+ }
+ }
+ else
+ {
+ auto r = current->insert_or_assign(buffer,std::forward<T>(value));
+ current = std::addressof(r.first->value());
+ }
+ }
+ else
+ {
+ ec = jsonpointer_errc::expected_object_or_array;
+ return;
+ }
+ }
+
+ template<class Json, class StringSource, class T>
+ typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type
+ replace(Json& root,
+ const StringSource& location_str,
+ T&& value,
+ bool create_if_missing,
+ std::error_code& ec)
+ {
+ auto jsonptr = basic_json_pointer<typename Json::char_type>::parse(location_str, ec);
+ if (ec)
+ {
+ return;
+ }
+ replace(root, jsonptr, std::forward<T>(value), create_if_missing, ec);
+ }
+
+ template<class Json, class StringSource, class T>
+ typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type
+ replace(Json& root,
+ const StringSource& location_str,
+ T&& value,
+ std::error_code& ec)
+ {
+ replace(root, location_str, std::forward<T>(value), false, ec);
+ }
+
+ template<class Json, class StringSource, class T>
+ typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type
+ replace(Json& root,
+ const StringSource& location_str,
+ T&& value,
+ bool create_if_missing = false)
+ {
+ std::error_code ec;
+ replace(root, location_str, std::forward<T>(value), create_if_missing, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(jsonpointer_error(ec));
+ }
+ }
+
+ template<class Json, class T>
+ void replace(Json& root,
+ const basic_json_pointer<typename Json::char_type>& location,
+ T&& value,
+ std::error_code& ec)
+ {
+ replace(root, location, std::forward<T>(value), false, ec);
+ }
+
+ template<class Json, class T>
+ void replace(Json& root,
+ const basic_json_pointer<typename Json::char_type>& location,
+ T&& value,
+ bool create_if_missing = false)
+ {
+ std::error_code ec;
+ replace(root, location, std::forward<T>(value), create_if_missing, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(jsonpointer_error(ec));
+ }
+ }
+
+ template <class String,class Result>
+ typename std::enable_if<std::is_convertible<typename String::value_type,typename Result::value_type>::value>::type
+ escape(const String& s, Result& result)
+ {
+ for (auto c : s)
+ {
+ if (c == '~')
+ {
+ result.push_back('~');
+ result.push_back('0');
+ }
+ else if (c == '/')
+ {
+ result.push_back('~');
+ result.push_back('1');
+ }
+ else
+ {
+ result.push_back(c);
+ }
+ }
+ }
+
+ template <class CharT>
+ std::basic_string<CharT> escape(const jsoncons::basic_string_view<CharT>& s)
+ {
+ std::basic_string<CharT> result;
+
+ for (auto c : s)
+ {
+ if (c == '~')
+ {
+ result.push_back('~');
+ result.push_back('0');
+ }
+ else if (c == '/')
+ {
+ result.push_back('~');
+ result.push_back('1');
+ }
+ else
+ {
+ result.push_back(c);
+ }
+ }
+ return result;
+ }
+
+ // flatten
+
+ template<class Json>
+ void flatten_(const std::basic_string<typename Json::char_type>& parent_key,
+ const Json& parent_value,
+ Json& result)
+ {
+ using char_type = typename Json::char_type;
+ using string_type = std::basic_string<char_type>;
+
+ switch (parent_value.type())
+ {
+ case json_type::array_value:
+ {
+ if (parent_value.empty())
+ {
+ // Flatten empty array to null
+ //result.try_emplace(parent_key, null_type{});
+ //result[parent_key] = parent_value;
+ result.try_emplace(parent_key, parent_value);
+ }
+ else
+ {
+ for (std::size_t i = 0; i < parent_value.size(); ++i)
+ {
+ string_type key(parent_key);
+ key.push_back('/');
+ jsoncons::detail::from_integer(i,key);
+ flatten_(key, parent_value.at(i), result);
+ }
+ }
+ break;
+ }
+
+ case json_type::object_value:
+ {
+ if (parent_value.empty())
+ {
+ // Flatten empty object to null
+ //result.try_emplace(parent_key, null_type{});
+ //result[parent_key] = parent_value;
+ result.try_emplace(parent_key, parent_value);
+ }
+ else
+ {
+ for (const auto& item : parent_value.object_range())
+ {
+ string_type key(parent_key);
+ key.push_back('/');
+ escape(jsoncons::basic_string_view<char_type>(item.key().data(),item.key().size()), key);
+ flatten_(key, item.value(), result);
+ }
+ }
+ break;
+ }
+
+ default:
+ {
+ // add primitive parent_value with its reference string
+ //result[parent_key] = parent_value;
+ result.try_emplace(parent_key, parent_value);
+ break;
+ }
+ }
+ }
+
+ template<class Json>
+ Json flatten(const Json& value)
+ {
+ Json result;
+ std::basic_string<typename Json::char_type> parent_key;
+ flatten_(parent_key, value, result);
+ return result;
+ }
+
+
+ // unflatten
+
+ enum class unflatten_options {none,assume_object = 1
+ #if !defined(JSONCONS_NO_DEPRECATED)
+,object = assume_object
+#endif
+};
+
+ template<class Json>
+ Json safe_unflatten (Json& value)
+ {
+ if (!value.is_object() || value.empty())
+ {
+ 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::to_integer_decimal(item.key().data(),item.key().size(), n);
+ if (!r || (index++ != n))
+ {
+ safe = false;
+ break;
+ }
+ }
+
+ if (safe)
+ {
+ Json j(json_array_arg);
+ j.reserve(value.size());
+ for (auto& item : value.object_range())
+ {
+ j.emplace_back(std::move(item.value()));
+ }
+ Json a(json_array_arg);
+ for (auto& item : j.array_range())
+ {
+ a.emplace_back(safe_unflatten (item));
+ }
+ return a;
+ }
+ else
+ {
+ Json o(json_object_arg);
+ for (auto& item : value.object_range())
+ {
+ o.try_emplace(item.key(), safe_unflatten (item.value()));
+ }
+ return o;
+ }
+ }
+
+ template<class Json>
+ jsoncons::optional<Json> 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<char_type> ptr(item.key());
+ std::size_t index = 0;
+ for (auto it = ptr.begin(); it != ptr.end(); )
+ {
+ auto s = *it;
+ size_t n{0};
+ auto r = jsoncons::detail::to_integer_decimal(s.data(), s.size(), n);
+ if (r.ec == jsoncons::detail::to_integer_errc() && (index++ == n))
+ {
+ if (!part->is_array())
+ {
+ *part = Json(json_array_arg);
+ }
+ if (++it != ptr.end())
+ {
+ if (n+1 > part->size())
+ {
+ Json& ref = part->emplace_back();
+ part = std::addressof(ref);
+ }
+ else
+ {
+ part = &part->at(n);
+ }
+ }
+ 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());
+ }
+ }
+ else
+ {
+ return jsoncons::optional<Json>();
+ }
+ }
+ }
+
+ return result;
+ }
+
+ template<class Json>
+ Json unflatten_to_object(const Json& value, unflatten_options options = unflatten_options::none)
+ {
+ 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<char_type> ptr(item.key());
+ for (auto it = ptr.begin(); it != ptr.end(); )
+ {
+ auto s = *it;
+ 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());
+ }
+ }
+ }
+
+ return options == unflatten_options::none ? safe_unflatten (result) : result;
+ }
+
+ template<class Json>
+ Json unflatten(const Json& value, unflatten_options options = unflatten_options::none)
+ {
+ if (options == unflatten_options::none)
+ {
+ jsoncons::optional<Json> j = try_unflatten_array(value);
+ return j ? *j : unflatten_to_object(value,options);
+ }
+ else
+ {
+ return unflatten_to_object(value,options);
+ }
+ }
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+
+ template<class Json>
+ JSONCONS_DEPRECATED_MSG("Instead, use add(Json&, const typename Json::string_view_type&, const Json&)")
+ void insert_or_assign(Json& root, const std::basic_string<typename Json::char_type>& location, const Json& value)
+ {
+ add(root, location, value);
+ }
+
+ template<class Json>
+ JSONCONS_DEPRECATED_MSG("Instead, use add(Json&, const typename Json::string_view_type&, const Json&, std::error_code&)")
+ void insert_or_assign(Json& root, const std::basic_string<typename Json::char_type>& location, const Json& value, std::error_code& ec)
+ {
+ add(root, location, value, ec);
+ }
+ template<class Json, class T>
+ void insert(Json& root,
+ const std::basic_string<typename Json::char_type>& location,
+ T&& value,
+ bool create_if_missing,
+ std::error_code& ec)
+ {
+ add_if_absent(root,location,std::forward<T>(value),create_if_missing,ec);
+ }
+
+ template<class Json, class T>
+ void insert(Json& root,
+ const std::basic_string<typename Json::char_type>& location,
+ T&& value,
+ std::error_code& ec)
+ {
+ add_if_absent(root, location, std::forward<T>(value), ec);
+ }
+
+ template<class Json, class T>
+ void insert(Json& root,
+ const std::basic_string<typename Json::char_type>& location,
+ T&& value,
+ bool create_if_missing = false)
+ {
+ add_if_absent(root, location, std::forward<T>(value), create_if_missing);
+ }
+#endif
+
+} // namespace jsonpointer
+} // namespace jsoncons
+
+#endif
diff --git a/include/jsoncons_ext/jsonpointer/jsonpointer_error.hpp b/include/jsoncons_ext/jsonpointer/jsonpointer_error.hpp
new file mode 100644
index 0000000..a0cfeff
--- /dev/null
+++ b/include/jsoncons_ext/jsonpointer/jsonpointer_error.hpp
@@ -0,0 +1,119 @@
+// Copyright 2013 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_JSONPOINTER_JSONPOINTER_ERROR_HPP
+#define JSONCONS_JSONPOINTER_JSONPOINTER_ERROR_HPP
+
+#include <jsoncons/json_exception.hpp>
+#include <system_error>
+
+namespace jsoncons { namespace jsonpointer {
+
+class jsonpointer_error : public std::system_error, public virtual json_exception
+{
+public:
+ jsonpointer_error(const std::error_code& ec)
+ : std::system_error(ec)
+ {
+ }
+ jsonpointer_error(const std::error_code& ec, const std::string& what_arg)
+ : std::system_error(ec, what_arg)
+ {
+ }
+ jsonpointer_error(const std::error_code& ec, const char* what_arg)
+ : std::system_error(ec, what_arg)
+ {
+ }
+ jsonpointer_error(const jsonpointer_error& other) = default;
+
+ jsonpointer_error(jsonpointer_error&& other) = default;
+
+ const char* what() const noexcept override
+ {
+ return std::system_error::what();
+ }
+};
+
+enum class jsonpointer_errc
+{
+ success = 0,
+ expected_slash = 1,
+ index_exceeds_array_size,
+ expected_0_or_1,
+ invalid_index,
+ key_not_found,
+ key_already_exists,
+ expected_object_or_array,
+ end_of_input,
+ unexpected_end_of_input,
+ argument_to_unflatten_invalid,
+ invalid_flattened_key,
+ invalid_uri_escaped_data
+};
+
+class jsonpointer_error_category_impl
+ : public std::error_category
+{
+public:
+ const char* name() const noexcept override
+ {
+ return "jsoncons/jsonpointer";
+ }
+ std::string message(int ev) const override
+ {
+ switch (static_cast<jsonpointer_errc>(ev))
+ {
+ case jsonpointer_errc::expected_slash:
+ return "Expected /";
+ case jsonpointer_errc::index_exceeds_array_size:
+ return "Index exceeds array size";
+ case jsonpointer_errc::expected_0_or_1:
+ return "Expected '0' or '1' after escape character '~'";
+ case jsonpointer_errc::key_not_found:
+ return "Key not found";
+ case jsonpointer_errc::invalid_index:
+ return "Invalid array index";
+ case jsonpointer_errc::key_already_exists:
+ return "Key already exists";
+ case jsonpointer_errc::expected_object_or_array:
+ return "Expected object or array";
+ case jsonpointer_errc::end_of_input:
+ 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_flattened_key:
+ return "Flattened key is invalid";
+ default:
+ return "Unknown jsonpointer error";
+ }
+ }
+};
+
+inline
+const std::error_category& jsonpointer_error_category()
+{
+ static jsonpointer_error_category_impl instance;
+ return instance;
+}
+
+inline
+std::error_code make_error_code(jsonpointer_errc result)
+{
+ return std::error_code(static_cast<int>(result),jsonpointer_error_category());
+}
+
+}}
+
+namespace std {
+ template<>
+ struct is_error_code_enum<jsoncons::jsonpointer::jsonpointer_errc> : public true_type
+ {
+ };
+}
+
+#endif
diff --git a/include/jsoncons_ext/jsonschema/format_validator.hpp b/include/jsoncons_ext/jsonschema/format_validator.hpp
new file mode 100644
index 0000000..312bf41
--- /dev/null
+++ b/include/jsoncons_ext/jsonschema/format_validator.hpp
@@ -0,0 +1,968 @@
+// Copyright 2020 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_JSONSCHEMA_FORMAT_VALIDATOR_HPP
+#define JSONCONS_JSONSCHEMA_FORMAT_VALIDATOR_HPP
+
+#include <jsoncons/config/jsoncons_config.hpp>
+#include <jsoncons/uri.hpp>
+#include <jsoncons/json.hpp>
+#include <jsoncons_ext/jsonpointer/jsonpointer.hpp>
+#include <jsoncons_ext/jsonschema/subschema.hpp>
+#include <cassert>
+#include <set>
+#include <sstream>
+#include <iostream>
+#include <cassert>
+#if defined(JSONCONS_HAS_STD_REGEX)
+#include <regex>
+#endif
+
+namespace jsoncons {
+namespace jsonschema {
+
+ inline
+ bool is_atext( char c)
+ {
+ switch (c)
+ {
+ case '!':
+ case '#':
+ case '$':
+ case '%':
+ case '&':
+ case '\'':
+ case '*':
+ case '+':
+ case '-':
+ case '/':
+ case '=':
+ case '?':
+ case '^':
+ case '_':
+ case '`':
+ case '{':
+ case '|':
+ case '}':
+ case '~':
+ return true;
+ default:
+ return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
+ }
+ }
+
+ inline
+ bool is_dtext( char c)
+ {
+ return (c >= 33 && c <= 90) || (c >= 94 && c <= 126);
+ }
+
+ // RFC 5322, section 3.4.1
+ inline
+ bool validate_email_rfc5322(const std::string& s)
+ {
+ enum class state_t {local_part,atom,dot_atom,quoted_string,amp,domain};
+
+ state_t state = state_t::local_part;
+ std::size_t part_length = 0;
+
+ for (char c : s)
+ {
+ switch (state)
+ {
+ case state_t::local_part:
+ {
+ if (is_atext(c))
+ {
+ state = state_t::atom;
+ }
+ else if (c == '"')
+ {
+ state = state_t::quoted_string;
+ }
+ else
+ {
+ return false;
+ }
+ break;
+ }
+ case state_t::dot_atom:
+ {
+ if (is_atext(c))
+ {
+ ++part_length;
+ state = state_t::atom;
+ }
+ else
+ return false;
+ break;
+ }
+ case state_t::atom:
+ {
+ switch (c)
+ {
+ case '@':
+ state = state_t::domain;
+ part_length = 0;
+ break;
+ case '.':
+ state = state_t::dot_atom;
+ ++part_length;
+ break;
+ default:
+ if (is_atext(c))
+ ++part_length;
+ else
+ return false;
+ break;
+ }
+ break;
+ }
+ case state_t::quoted_string:
+ {
+ if (c == '\"')
+ {
+ state = state_t::amp;
+ }
+ else
+ {
+ ++part_length;
+ }
+ break;
+ }
+ case state_t::amp:
+ {
+ if (c == '@')
+ {
+ state = state_t::domain;
+ part_length = 0;
+ }
+ else
+ {
+ return false;
+ }
+ break;
+ }
+ case state_t::domain:
+ {
+ if (is_dtext(c))
+ {
+ ++part_length;
+ }
+ else
+ {
+ return false;
+ }
+ break;
+ }
+ }
+ }
+
+ return state == state_t::domain && part_length > 0;
+ }
+
+ // RFC 2673, Section 3.2
+
+ inline
+ bool validate_ipv6_rfc2373(const std::string& s)
+ {
+ enum class state_t{start,expect_hexdig_or_unspecified,
+ hexdig, decdig,expect_unspecified, unspecified};
+
+ state_t state = state_t::start;
+
+ std::size_t digit_count = 0;
+ std::size_t piece_count = 0;
+ std::size_t piece_count2 = 0;
+ bool has_unspecified = false;
+ std::size_t dec_value = 0;
+
+ for (std::size_t i = 0; i < s.length(); ++i)
+ {
+ char c = s[i];
+ switch (state)
+ {
+ case state_t::start:
+ {
+ switch (c)
+ {
+ case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9':
+ 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 = state_t::hexdig;
+ ++digit_count;
+ piece_count = 0;
+ break;
+ case ':':
+ if (!has_unspecified)
+ {
+ state = state_t::expect_unspecified;
+ }
+ else
+ {
+ return false;
+ }
+ break;
+ default:
+ return false;
+ }
+ break;
+ }
+ case state_t::expect_hexdig_or_unspecified:
+ {
+ switch (c)
+ {
+ case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9':
+ dec_value = dec_value*10 + static_cast<std::size_t>(c - '0'); // just in case this piece is followed by a dot
+ state = state_t::hexdig;
+ ++digit_count;
+ break;
+ 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 = state_t::hexdig;
+ ++digit_count;
+ break;
+ case ':':
+ if (!has_unspecified)
+ {
+ has_unspecified = true;
+ state = state_t::unspecified;
+ }
+ else
+ {
+ return false;
+ }
+ break;
+ default:
+ return false;
+ }
+ break;
+ }
+ case state_t::expect_unspecified:
+ {
+ if (c == ':')
+ {
+ has_unspecified = true;
+ state = state_t::unspecified;
+ }
+ else
+ {
+ return false;
+ }
+ break;
+ }
+ case state_t::hexdig:
+ {
+ switch (c)
+ {
+ case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9':
+ case 'A':case 'B':case 'C':case 'D':case 'E':case 'F':
+ case 'a':case 'b':case 'c':case 'd':case 'e':case 'f':
+ ++digit_count;
+ break;
+ case ':':
+ if (digit_count <= 4)
+ {
+ ++piece_count;
+ digit_count = 0;
+ dec_value = 0;
+ state = state_t::expect_hexdig_or_unspecified;
+ }
+ else
+ {
+ return false;
+ }
+ break;
+ case '.':
+ if (piece_count == 6 || has_unspecified)
+ {
+ ++piece_count2;
+ state = state_t::decdig;
+ dec_value = 0;
+ }
+ else
+ {
+ return false;
+ }
+ break;
+ default:
+ return false;
+ }
+ break;
+ }
+ case state_t::decdig:
+ {
+ switch (c)
+ {
+ case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9':
+ dec_value = dec_value*10 + static_cast<std::size_t>(c - '0');
+ ++digit_count;
+ break;
+ case '.':
+ if (dec_value > 0xff)
+ {
+ return false;
+ }
+ digit_count = 0;
+ dec_value = 0;
+ ++piece_count2;
+ break;
+ default:
+ return false;
+ }
+ break;
+ }
+ case state_t::unspecified:
+ {
+ switch (c)
+ {
+ case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9':
+ 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 = state_t::hexdig;
+ ++digit_count;
+ break;
+ default:
+ return false;
+ }
+ break;
+ }
+ default:
+ return false;
+ }
+ }
+
+ switch (state)
+ {
+ case state_t::unspecified:
+ return piece_count <= 8;
+ case state_t::hexdig:
+ if (digit_count <= 4)
+ {
+ ++piece_count;
+ return digit_count > 0 && (piece_count == 8 || (has_unspecified && piece_count <= 8));
+ }
+ else
+ {
+ return false;
+ }
+ case state_t::decdig:
+ ++piece_count2;
+ if (dec_value > 0xff)
+ {
+ return false;
+ }
+ return digit_count > 0 && piece_count2 == 4;
+ default:
+ return false;
+ }
+ }
+
+ // RFC 2673, Section 3.2
+
+ inline
+ bool validate_ipv4_rfc2673(const std::string& s)
+ {
+ enum class state_t {expect_indicator_or_dotted_quad,decbyte,
+ bindig, octdig, hexdig};
+
+ state_t state = state_t::expect_indicator_or_dotted_quad;
+
+ std::size_t digit_count = 0;
+ std::size_t decbyte_count = 0;
+ std::size_t value = 0;
+
+ for (std::size_t i = 0; i < s.length(); ++i)
+ {
+ char c = s[i];
+ switch (state)
+ {
+ case state_t::expect_indicator_or_dotted_quad:
+ {
+ switch (c)
+ {
+ case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9':
+ state = state_t::decbyte;
+ decbyte_count = 0;
+ digit_count = 1;
+ value = 0;
+ break;
+ case 'b':
+ state = state_t::bindig;
+ digit_count = 0;
+ break;
+ case 'o':
+ state = state_t::octdig;
+ digit_count = 0;
+ break;
+ case 'x':
+ state = state_t::hexdig;
+ digit_count = 0;
+ break;
+ default:
+ return false;
+ }
+ break;
+ }
+ case state_t::bindig:
+ {
+ if (digit_count >= 256)
+ {
+ return false;
+ }
+ switch (c)
+ {
+ case '0':case '1':
+ ++digit_count;
+ break;
+ default:
+ return false;
+ }
+ break;
+ }
+ case state_t::octdig:
+ {
+ if (digit_count >= 86)
+ {
+ return false;
+ }
+ switch (c)
+ {
+ case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':
+ ++digit_count;
+ break;
+ default:
+ return false;
+ }
+ break;
+ }
+ case state_t::hexdig:
+ {
+ if (digit_count >= 64)
+ {
+ return false;
+ }
+ switch (c)
+ {
+ case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9':
+ case 'A':case 'B':case 'C':case 'D':case 'E':case 'F':
+ case 'a':case 'b':case 'c':case 'd':case 'e':case 'f':
+ ++digit_count;
+ break;
+ default:
+ return false;
+ }
+ break;
+ }
+ case state_t::decbyte:
+ {
+ if (decbyte_count >= 4)
+ {
+ return false;
+ }
+ switch (c)
+ {
+ case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9':
+ {
+ if (digit_count >= 3)
+ {
+ return false;
+ }
+ ++digit_count;
+ value = value*10 + static_cast<std::size_t>(c - '0');
+ if (value > 255)
+ {
+ return false;
+ }
+ break;
+ }
+ case '.':
+ if (decbyte_count > 3)
+ {
+ return false;
+ }
+ ++decbyte_count;
+ digit_count = 0;
+ value = 0;
+ break;
+ default:
+ return false;
+ }
+ break;
+ }
+ default:
+ return false;
+ }
+ }
+
+ switch (state)
+ {
+ case state_t::decbyte:
+ if (digit_count > 0)
+ {
+ ++decbyte_count;
+ }
+ else
+ {
+ return false;
+ }
+ return (decbyte_count == 4) ? true : false;
+ case state_t::bindig:
+ return digit_count > 0 ? true : false;
+ case state_t::octdig:
+ return digit_count > 0 ? true : false;
+ case state_t::hexdig:
+ return digit_count > 0 ? true : false;
+ default:
+ return false;
+ }
+ }
+
+ // RFC 1034, Section 3.1
+ inline
+ bool validate_hostname_rfc1034(const std::string& hostname)
+ {
+ enum class state_t {start_label,expect_letter_or_digit_or_hyphen_or_dot};
+
+ state_t state = state_t::start_label;
+ std::size_t length = hostname.length() - 1;
+ std::size_t label_length = 0;
+
+ for (std::size_t i = 0; i < length; ++i)
+ {
+ char c = hostname[i];
+ switch (state)
+ {
+ case state_t::start_label:
+ {
+ if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
+ {
+ ++label_length;
+ state = state_t::expect_letter_or_digit_or_hyphen_or_dot;
+ }
+ else
+ {
+ return false;
+ }
+ break;
+ }
+ case state_t::expect_letter_or_digit_or_hyphen_or_dot:
+ {
+ if (c == '.')
+ {
+ label_length = 0;
+ state = state_t::start_label;
+ }
+ else if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
+ (c >= '0' && c < '9') || c == '-'))
+ {
+ return false;
+ }
+ if (++label_length > 63)
+ {
+ return false;
+ }
+ break;
+ }
+ }
+ }
+
+ char last = hostname.back();
+ if (!((last >= 'a' && last <= 'z') || (last >= 'A' && last <= 'Z') || (last >= '0' && last < '9')))
+ {
+ return false;
+ }
+ return true;
+ }
+
+ inline
+ bool is_leap_year(std::size_t year)
+ {
+ return (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0));
+ }
+
+ inline
+ std::size_t days_in_month(std::size_t year, std::size_t month)
+ {
+ switch (month)
+ {
+ case 1: return 31;
+ case 2: return is_leap_year(year) ? 29 : 28;
+ case 3: return 31;
+ case 4: return 30;
+ case 5: return 31;
+ case 6: return 30;
+ case 7: return 31;
+ case 8: return 31;
+ case 9: return 30;
+ case 10: return 31;
+ case 11: return 30;
+ case 12: return 31;
+ default:
+ JSONCONS_UNREACHABLE();
+ break;
+ }
+ }
+
+ enum class date_time_type {date_time,date,time};
+ // RFC 3339, Section 5.6
+ inline
+ bool validate_date_time_rfc3339(const std::string& s, date_time_type type)
+ {
+ enum class state_t {fullyear,month,mday,hour,minute,second,secfrac,z,offset_hour,offset_minute};
+
+ std::size_t piece_length = 0;
+ std::size_t year = 0;
+ std::size_t month = 0;
+ std::size_t mday = 0;
+ std::size_t value = 0;
+ state_t state = (type == date_time_type::time) ? state_t::hour : state_t::fullyear;
+
+ for (char c : s)
+ {
+ switch (state)
+ {
+ case state_t::fullyear:
+ {
+ if (piece_length < 4 && (c >= '0' && c <= '9'))
+ {
+ piece_length++;
+ year = year*10 + static_cast<std::size_t>(c - '0');
+ }
+ else if (c == '-' && piece_length == 4)
+ {
+ state = state_t::month;
+ piece_length = 0;
+ }
+ else
+ {
+ return false;
+ }
+ break;
+ }
+ case state_t::month:
+ {
+ if (piece_length < 2 && (c >= '0' && c <= '9'))
+ {
+ piece_length++;
+ month = month*10 + static_cast<std::size_t>(c - '0');
+ }
+ else if (c == '-' && piece_length == 2 && (month >=1 && month <= 12))
+ {
+ state = state_t::mday;
+ piece_length = 0;
+ }
+ else
+ {
+ return false;
+ }
+ break;
+ }
+ case state_t::mday:
+ {
+ if (piece_length < 2 && (c >= '0' && c <= '9'))
+ {
+ piece_length++;
+ mday = mday *10 + static_cast<std::size_t>(c - '0');
+ }
+ else if ((c == 'T' || c == 't') && piece_length == 2 && (mday <= days_in_month(year, month)))
+ {
+ piece_length = 0;
+ state = state_t::hour;
+ }
+ else
+ {
+ return false;
+ }
+ break;
+ }
+ case state_t::hour:
+ {
+ if (piece_length < 2 && (c >= '0' && c <= '9'))
+ {
+ piece_length++;
+ value = value*10 + static_cast<std::size_t>(c - '0');
+ }
+ else if (c == ':' && piece_length == 2 && (/*value >=0 && */ value <= 23))
+ {
+ state = state_t::minute;
+ value = 0;
+ piece_length = 0;
+ }
+ else
+ {
+ return false;
+ }
+ break;
+ }
+ case state_t::minute:
+ {
+ if (piece_length < 2 && (c >= '0' && c <= '9'))
+ {
+ piece_length++;
+ value = value*10 + static_cast<std::size_t>(c - '0');
+ }
+ else if (c == ':' && piece_length == 2 && (/*value >=0 && */value <= 59))
+ {
+ state = state_t::second;
+ value = 0;
+ piece_length = 0;
+ }
+ else
+ {
+ return false;
+ }
+ break;
+ }
+ case state_t::second:
+ {
+ if (piece_length < 2 && (c >= '0' && c <= '9'))
+ {
+ piece_length++;
+ value = value*10 + static_cast<std::size_t>(c - '0');
+ }
+ else if (piece_length == 2 && (/*value >=0 && */value <= 60)) // 00-58, 00-59, 00-60 based on leap second rules
+ {
+ switch (c)
+ {
+ case '.':
+ value = 0;
+ state = state_t::secfrac;
+ break;
+ case '+':
+ case '-':
+ value = 0;
+ piece_length = 0;
+ state = state_t::offset_hour;
+ break;
+ case 'Z':
+ case 'z':
+ state = state_t::z;
+ break;
+ default:
+ return false;
+ }
+ }
+ else
+ {
+ return false;
+ }
+ break;
+ }
+ case state_t::secfrac:
+ {
+ if (c >= '0' && c <= '9')
+ {
+ value = value*10 + static_cast<std::size_t>(c - '0');
+ }
+ else
+ {
+ switch (c)
+ {
+ case '+':
+ case '-':
+ value = 0;
+ piece_length = 0;
+ state = state_t::offset_hour;
+ break;
+ case 'Z':
+ case 'z':
+ state = state_t::z;
+ break;
+ default:
+ return false;
+ }
+ }
+ break;
+ }
+ case state_t::offset_hour:
+ {
+ if (piece_length < 2 && (c >= '0' && c <= '9'))
+ {
+ piece_length++;
+ value = value*10 + static_cast<std::size_t>(c - '0');
+ }
+ else if (c == ':' && piece_length == 2 && (/*value >=0 && */value <= 23))
+ {
+ value = 0;
+ piece_length = 0;
+ state = state_t::offset_minute;
+ }
+ else
+ {
+ return false;
+ }
+ break;
+ }
+ case state_t::offset_minute:
+ {
+ if (piece_length < 2 && (c >= '0' && c <= '9'))
+ {
+ piece_length++;
+ value = value*10 + static_cast<std::size_t>(c - '0');
+ }
+ else if (c == ':' && piece_length == 2 && (/*value >=0 && */value <= 59))
+ {
+ value = 0;
+ piece_length = 0;
+ }
+ else
+ {
+ return false;
+ }
+ break;
+ }
+ case state_t::z:
+ return false;
+ }
+ }
+
+ if (type == date_time_type::date)
+ {
+ return state == state_t::mday && piece_length == 2 && (mday >= 1 && mday <= days_in_month(year, month));
+ }
+ else
+ {
+ return state == state_t::offset_minute || state == state_t::z || state == state_t::secfrac;
+ }
+ }
+
+ // format checkers
+ using format_checker = std::function<void(const std::string& absolute_keyword_location,
+ const jsonpointer::json_pointer& instance_location,
+ const std::string&,
+ error_reporter& reporter)>;
+
+ inline
+ void rfc3339_date_check(const std::string& absolute_keyword_location,
+ const jsonpointer::json_pointer& instance_location,
+ const std::string& value,
+ error_reporter& reporter)
+ {
+ if (!validate_date_time_rfc3339(value,date_time_type::date))
+ {
+ reporter.error(validation_output("date",
+ absolute_keyword_location,
+ instance_location.to_uri_fragment(),
+ "\"" + value + "\" is not a RFC 3339 date string"));
+ }
+ }
+
+ inline
+ void rfc3339_time_check(const std::string& absolute_keyword_location,
+ const jsonpointer::json_pointer& instance_location,
+ const std::string &value,
+ error_reporter& reporter)
+ {
+ if (!validate_date_time_rfc3339(value, date_time_type::time))
+ {
+ reporter.error(validation_output("time",
+ absolute_keyword_location,
+ instance_location.to_uri_fragment(),
+ "\"" + value + "\" is not a RFC 3339 time string"));
+ }
+ }
+
+ inline
+ void rfc3339_date_time_check(const std::string& absolute_keyword_location,
+ const jsonpointer::json_pointer& instance_location,
+ const std::string &value,
+ error_reporter& reporter)
+ {
+ if (!validate_date_time_rfc3339(value, date_time_type::date_time))
+ {
+ reporter.error(validation_output("date-time",
+ absolute_keyword_location,
+ instance_location.to_uri_fragment(),
+ "\"" + value + "\" is not a RFC 3339 date-time string"));
+ }
+ }
+
+ inline
+ void email_check(const std::string& absolute_keyword_location,
+ const jsonpointer::json_pointer& instance_location,
+ const std::string& value,
+ error_reporter& reporter)
+ {
+ if (!validate_email_rfc5322(value))
+ {
+ reporter.error(validation_output("email",
+ absolute_keyword_location,
+ instance_location.to_uri_fragment(),
+ "\"" + value + "\" is not a valid email address as defined by RFC 5322"));
+ }
+ }
+
+ inline
+ void hostname_check(const std::string& absolute_keyword_location,
+ const jsonpointer::json_pointer& instance_location,
+ const std::string& value,
+ error_reporter& reporter)
+ {
+ if (!validate_hostname_rfc1034(value))
+ {
+ reporter.error(validation_output("hostname",
+ absolute_keyword_location,
+ instance_location.to_uri_fragment(),
+ "\"" + value + "\" is not a valid hostname as defined by RFC 3986 Appendix A"));
+ }
+ }
+
+ inline
+ void ipv4_check(const std::string& absolute_keyword_location,
+ const jsonpointer::json_pointer& instance_location,
+ const std::string& value,
+ error_reporter& reporter)
+ {
+ if (!validate_ipv4_rfc2673(value))
+ {
+ reporter.error(validation_output("ipv4",
+ absolute_keyword_location,
+ instance_location.to_uri_fragment(),
+ "\"" + value + "\" is not a valid IPv4 address as defined by RFC 2673"));
+ }
+ }
+
+ inline
+ void ipv6_check(const std::string& absolute_keyword_location,
+ const jsonpointer::json_pointer& instance_location,
+ const std::string& value,
+ error_reporter& reporter)
+ {
+ if (!validate_ipv6_rfc2373(value))
+ {
+ reporter.error(validation_output("ipv6",
+ absolute_keyword_location,
+ instance_location.to_uri_fragment(),
+ "\"" + value + "\" is not a valid IPv6 address as defined by RFC 2373"));
+ }
+ }
+
+ inline
+ void regex_check(const std::string& absolute_keyword_location,
+ const jsonpointer::json_pointer& instance_location,
+ const std::string& value,
+ error_reporter& reporter)
+ {
+#if defined(JSONCONS_HAS_STD_REGEX)
+ try
+ {
+ std::regex re(value, std::regex::ECMAScript);
+ }
+ catch (const std::exception& e)
+ {
+ reporter.error(validation_output("pattern",
+ absolute_keyword_location,
+ instance_location.to_uri_fragment(),
+ "\"" + value + "\" is not a valid ECMAScript regular expression. " + e.what()));
+ }
+#endif
+ }
+
+} // namespace jsonschema
+} // namespace jsoncons
+
+#endif // JSONCONS_JSONSCHEMA_FORMAT_CHECKERS_HPP
diff --git a/include/jsoncons_ext/jsonschema/json_validator.hpp b/include/jsoncons_ext/jsonschema/json_validator.hpp
new file mode 100644
index 0000000..87bec58
--- /dev/null
+++ b/include/jsoncons_ext/jsonschema/json_validator.hpp
@@ -0,0 +1,120 @@
+// Copyright 2020 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_JSONSCHEMA_JSON_VALIDATOR_HPP
+#define JSONCONS_JSONSCHEMA_JSON_VALIDATOR_HPP
+
+#include <jsoncons/config/jsoncons_config.hpp>
+#include <jsoncons/uri.hpp>
+#include <jsoncons/json.hpp>
+#include <jsoncons_ext/jsonpointer/jsonpointer.hpp>
+#include <jsoncons_ext/jsonschema/keyword_validator_factory.hpp>
+#include <cassert>
+#include <set>
+#include <sstream>
+#include <iostream>
+#include <cassert>
+#include <functional>
+
+namespace jsoncons {
+namespace jsonschema {
+
+ class throwing_error_reporter : public error_reporter
+ {
+ void do_error(const validation_output& o) override
+ {
+ JSONCONS_THROW(validation_error(o.message()));
+ }
+ };
+
+ class fail_early_reporter : public error_reporter
+ {
+ void do_error(const validation_output&) override
+ {
+ }
+ public:
+ fail_early_reporter()
+ : error_reporter(true)
+ {
+ }
+ };
+
+ using error_reporter_t = std::function<void(const validation_output& o)>;
+
+ struct error_reporter_adaptor : public error_reporter
+ {
+ error_reporter_t reporter_;
+
+ error_reporter_adaptor(const error_reporter_t& reporter)
+ : reporter_(reporter)
+ {
+ }
+ private:
+ void do_error(const validation_output& e) override
+ {
+ reporter_(e);
+ }
+ };
+
+ template <class Json>
+ class json_validator
+ {
+ std::shared_ptr<json_schema<Json>> root_;
+
+ public:
+ json_validator(std::shared_ptr<json_schema<Json>> root)
+ : root_(root)
+ {
+ }
+
+ json_validator(json_validator &&) = default;
+ json_validator &operator=(json_validator &&) = default;
+
+ json_validator(json_validator const &) = delete;
+ json_validator &operator=(json_validator const &) = delete;
+
+ ~json_validator() = default;
+
+ // Validate input JSON against a JSON Schema with a default throwing error reporter
+ Json validate(const Json& instance) const
+ {
+ throwing_error_reporter reporter;
+ jsonpointer::json_pointer instance_location("#");
+ Json patch(json_array_arg);
+
+ root_->validate(instance, instance_location, reporter, patch);
+ return patch;
+ }
+
+ // Validate input JSON against a JSON Schema
+ bool is_valid(const Json& instance) const
+ {
+ fail_early_reporter reporter;
+ jsonpointer::json_pointer instance_location("#");
+ Json patch(json_array_arg);
+
+ root_->validate(instance, instance_location, reporter, patch);
+ return reporter.error_count() == 0;
+ }
+
+ // Validate input JSON against a JSON Schema with a provided error reporter
+ template <class Reporter>
+ typename std::enable_if<type_traits::is_unary_function_object_exact<Reporter,void,validation_output>::value,Json>::type
+ validate(const Json& instance, const Reporter& reporter) const
+ {
+ jsonpointer::json_pointer instance_location("#");
+ Json patch(json_array_arg);
+
+ error_reporter_adaptor adaptor(reporter);
+ root_->validate(instance, instance_location, adaptor, patch);
+ return patch;
+ }
+ };
+
+} // namespace jsonschema
+} // namespace jsoncons
+
+#endif // JSONCONS_JSONSCHEMA_JSON_VALIDATOR_HPP
diff --git a/include/jsoncons_ext/jsonschema/jsonschema.hpp b/include/jsoncons_ext/jsonschema/jsonschema.hpp
new file mode 100644
index 0000000..e2c4210
--- /dev/null
+++ b/include/jsoncons_ext/jsonschema/jsonschema.hpp
@@ -0,0 +1,13 @@
+// Copyright 2020 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_JSONSCHEMA_JSONSCHEMA_HPP
+#define JSONCONS_JSONSCHEMA_JSONSCHEMA_HPP
+
+#include <jsoncons_ext/jsonschema/keyword_validator.hpp>
+#include <jsoncons_ext/jsonschema/json_validator.hpp>
+
+#endif // JSONCONS_JSONSCHEMA_JSONSCHEMA_HPP
diff --git a/include/jsoncons_ext/jsonschema/jsonschema_error.hpp b/include/jsoncons_ext/jsonschema/jsonschema_error.hpp
new file mode 100644
index 0000000..7cb1061
--- /dev/null
+++ b/include/jsoncons_ext/jsonschema/jsonschema_error.hpp
@@ -0,0 +1,105 @@
+/// Copyright 2020 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_JSONSCHEMA_JSONSCHEMA_ERROR_HPP
+#define JSONCONS_JSONSCHEMA_JSONSCHEMA_ERROR_HPP
+
+#include <jsoncons/json_exception.hpp>
+#include <system_error>
+
+namespace jsoncons {
+namespace jsonschema {
+
+ class schema_error : public std::runtime_error, public virtual json_exception
+ {
+ public:
+ schema_error(const std::string& message)
+ : std::runtime_error(message)
+ {
+ }
+
+ const char* what() const noexcept override
+ {
+ return std::runtime_error::what();
+ }
+ };
+
+ class validation_error : public std::runtime_error, public virtual json_exception
+ {
+ public:
+ validation_error(const std::string& message)
+ : std::runtime_error(message)
+ {
+ }
+
+ const char* what() const noexcept override
+ {
+ return std::runtime_error::what();
+ }
+ };
+
+ class validation_output
+ {
+ std::string keyword_;
+ std::string absolute_keyword_location_;
+ std::string instance_location_;
+ std::string message_;
+ std::vector<validation_output> nested_errors_;
+ public:
+ validation_output(std::string keyword,
+ std::string absolute_keyword_location,
+ std::string instance_location,
+ std::string message)
+ : keyword_(std::move(keyword)),
+ absolute_keyword_location_(std::move(absolute_keyword_location)),
+ instance_location_(std::move(instance_location)),
+ message_(std::move(message))
+ {
+ }
+
+ validation_output(const std::string& keyword,
+ const std::string& absolute_keyword_location,
+ const std::string& instance_location,
+ const std::string& message,
+ const std::vector<validation_output>& nested_errors)
+ : keyword_(keyword),
+ absolute_keyword_location_(absolute_keyword_location),
+ instance_location_(instance_location),
+ message_(message),
+ nested_errors_(nested_errors)
+ {
+ }
+
+ const std::string& instance_location() const
+ {
+ return instance_location_;
+ }
+
+ const std::string& message() const
+ {
+ return message_;
+ }
+
+ const std::string& absolute_keyword_location() const
+ {
+ return absolute_keyword_location_;
+ }
+
+ const std::string& keyword() const
+ {
+ return keyword_;
+ }
+
+ const std::vector<validation_output>& nested_errors() const
+ {
+ return nested_errors_;
+ }
+ };
+
+} // namespace jsonschema
+} // namespace jsoncons
+
+#endif // JSONCONS_JSONSCHEMA_JSONSCHEMA_ERROR_HPP
diff --git a/include/jsoncons_ext/jsonschema/jsonschema_version.hpp b/include/jsoncons_ext/jsonschema/jsonschema_version.hpp
new file mode 100644
index 0000000..bf0afff
--- /dev/null
+++ b/include/jsoncons_ext/jsonschema/jsonschema_version.hpp
@@ -0,0 +1,18 @@
+// Copyright 2021 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_JSONSCHEMA_JSONSCHEMA_VERSION_HPP
+#define JSONCONS_JSONSCHEMA_JSONSCHEMA_VERSION_HPP
+
+#include <jsoncons/json.hpp>
+
+namespace jsoncons {
+namespace jsonschema {
+
+} // namespace jsonschema
+} // namespace jsoncons
+
+#endif // JSONCONS_JSONSCHEMA_JSONSCHEMA_VERSION_HPP
diff --git a/include/jsoncons_ext/jsonschema/keyword_validator.hpp b/include/jsoncons_ext/jsonschema/keyword_validator.hpp
new file mode 100644
index 0000000..249c7d0
--- /dev/null
+++ b/include/jsoncons_ext/jsonschema/keyword_validator.hpp
@@ -0,0 +1,1745 @@
+// Copyright 2020 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_JSONSCHEMA_KEYWORD_VALIDATOR_HPP
+#define JSONCONS_JSONSCHEMA_KEYWORD_VALIDATOR_HPP
+
+#include <jsoncons/config/jsoncons_config.hpp>
+#include <jsoncons/uri.hpp>
+#include <jsoncons/json.hpp>
+#include <jsoncons_ext/jsonpointer/jsonpointer.hpp>
+#include <jsoncons_ext/jsonschema/subschema.hpp>
+#include <jsoncons_ext/jsonschema/format_validator.hpp>
+#include <cassert>
+#include <set>
+#include <sstream>
+#include <iostream>
+#include <cassert>
+#if defined(JSONCONS_HAS_STD_REGEX)
+#include <regex>
+#endif
+
+namespace jsoncons {
+namespace jsonschema {
+
+ template <class Json>
+ class abstract_keyword_validator_factory
+ {
+ public:
+ using validator_pointer = typename keyword_validator<Json>::self_pointer;
+
+ virtual ~abstract_keyword_validator_factory() = default;
+
+ virtual validator_pointer make_keyword_validator(const Json& schema,
+ const std::vector<schema_location>& uris,
+ const std::vector<std::string>& keys) = 0;
+ virtual validator_pointer make_required_validator(const std::vector<schema_location>& uris,
+ const std::vector<std::string>& items) = 0;
+
+ virtual validator_pointer make_null_validator(const std::vector<schema_location>& uris) = 0;
+
+ virtual validator_pointer make_true_validator(const std::vector<schema_location>& uris) = 0;
+
+ virtual validator_pointer make_false_validator(const std::vector<schema_location>& uris) = 0;
+
+ virtual validator_pointer make_object_validator(const Json& sch,
+ const std::vector<schema_location>& uris) = 0;
+
+ virtual validator_pointer make_array_validator(const Json& sch,
+ const std::vector<schema_location>& uris) = 0;
+
+ virtual validator_pointer make_string_validator(const Json& sch,
+ const std::vector<schema_location>& uris) = 0;
+
+ virtual validator_pointer make_boolean_validator(const std::vector<schema_location>& uris) = 0;
+
+ virtual validator_pointer make_integer_validator(const Json& sch,
+ const std::vector<schema_location>& uris,
+ std::set<std::string>& keywords) = 0;
+
+ virtual validator_pointer make_number_validator(const Json& sch,
+ const std::vector<schema_location>& uris,
+ std::set<std::string>& keywords) = 0;
+
+ virtual validator_pointer make_not_validator(const Json& schema,
+ const std::vector<schema_location>& uris) = 0;
+
+ virtual validator_pointer make_all_of_validator(const Json& schema,
+ const std::vector<schema_location>& uris) = 0;
+
+ virtual validator_pointer make_any_of_validator(const Json& schema,
+ const std::vector<schema_location>& uris) = 0;
+
+ virtual validator_pointer make_one_of_validator(const Json& schema,
+ const std::vector<schema_location>& uris) = 0;
+
+ virtual validator_pointer make_type_validator(const Json& schema,
+ const std::vector<schema_location>& uris) = 0;
+ };
+
+ struct collecting_error_reporter : public error_reporter
+ {
+ std::vector<validation_output> errors;
+
+ private:
+ void do_error(const validation_output& o) override
+ {
+ errors.push_back(o);
+ }
+ };
+
+ // string keyword_validator
+
+ inline
+ std::string make_absolute_keyword_location(const std::vector<schema_location>& uris,
+ const std::string& keyword)
+ {
+ for (auto it = uris.rbegin(); it != uris.rend(); ++it)
+ {
+ if (!it->has_identifier() && it->is_absolute())
+ {
+ return it->append(keyword).string();
+ }
+ }
+ return "";
+ }
+
+ template <class Json>
+ class string_validator : public keyword_validator<Json>
+ {
+ jsoncons::optional<std::size_t> max_length_;
+ std::string max_length_location_;
+ jsoncons::optional<std::size_t> min_length_;
+ std::string min_length_location_;
+
+ #if defined(JSONCONS_HAS_STD_REGEX)
+ jsoncons::optional<std::regex> pattern_;
+ std::string pattern_string_;
+ std::string pattern_location_;
+ #endif
+
+ format_checker format_check_;
+ std::string format_location_;
+
+ jsoncons::optional<std::string> content_encoding_;
+ std::string content_encoding_location_;
+ jsoncons::optional<std::string> content_media_type_;
+ std::string content_media_type_location_;
+
+ public:
+ string_validator(const Json& sch, const std::vector<schema_location>& uris)
+ : keyword_validator<Json>((!uris.empty() && uris.back().is_absolute()) ? uris.back().string() : ""), max_length_(), min_length_(),
+ #if defined(JSONCONS_HAS_STD_REGEX)
+ pattern_(),
+ #endif
+ content_encoding_(), content_media_type_()
+ {
+ auto it = sch.find("maxLength");
+ if (it != sch.object_range().end())
+ {
+ max_length_ = it->value().template as<std::size_t>();
+ max_length_location_ = make_absolute_keyword_location(uris, "maxLength");
+ }
+
+ it = sch.find("minLength");
+ if (it != sch.object_range().end())
+ {
+ min_length_ = it->value().template as<std::size_t>();
+ min_length_location_ = make_absolute_keyword_location(uris, "minLength");
+ }
+
+ it = sch.find("contentEncoding");
+ if (it != sch.object_range().end())
+ {
+ content_encoding_ = it->value().template as<std::string>();
+ content_encoding_location_ = make_absolute_keyword_location(uris, "contentEncoding");
+ // If "contentEncoding" is set to "binary", a Json value
+ // of type json_type::byte_string_value is accepted.
+ }
+
+ it = sch.find("contentMediaType");
+ if (it != sch.object_range().end())
+ {
+ content_media_type_ = it->value().template as<std::string>();
+ content_media_type_location_ = make_absolute_keyword_location(uris, "contentMediaType");
+ }
+
+ #if defined(JSONCONS_HAS_STD_REGEX)
+ it = sch.find("pattern");
+ if (it != sch.object_range().end())
+ {
+ pattern_string_ = it->value().template as<std::string>();
+ pattern_ = std::regex(it->value().template as<std::string>(),std::regex::ECMAScript);
+ pattern_location_ = make_absolute_keyword_location(uris, "pattern");
+ }
+ #endif
+
+ it = sch.find("format");
+ if (it != sch.object_range().end())
+ {
+ format_location_ = make_absolute_keyword_location(uris, "format");
+ std::string format = it->value().template as<std::string>();
+ if (format == "date-time")
+ {
+ format_check_ = rfc3339_date_time_check;
+ }
+ else if (format == "date")
+ {
+ format_check_ = rfc3339_date_check;
+ }
+ else if (format == "time")
+ {
+ format_check_ = rfc3339_time_check;
+ }
+ else if (format == "email")
+ {
+ format_check_ = email_check;
+ }
+ else if (format == "hostname")
+ {
+ format_check_ = hostname_check;
+ }
+ else if (format == "ipv4")
+ {
+ format_check_ = ipv4_check;
+ }
+ else if (format == "ipv6")
+ {
+ format_check_ = ipv6_check;
+ }
+ else if (format == "regex")
+ {
+ format_check_ = regex_check;
+ }
+ else
+ {
+ // Not supported - ignore
+ }
+ }
+ }
+
+ private:
+
+ void do_validate(const Json& instance,
+ const jsonpointer::json_pointer& instance_location,
+ error_reporter& reporter,
+ Json&) const override
+ {
+ std::string content;
+ if (content_encoding_)
+ {
+ if (*content_encoding_ == "base64")
+ {
+ auto s = instance.template as<jsoncons::string_view>();
+ auto retval = jsoncons::decode_base64(s.begin(), s.end(), content);
+ if (retval.ec != jsoncons::conv_errc::success)
+ {
+ reporter.error(validation_output("contentEncoding",
+ content_encoding_location_,
+ instance_location.to_uri_fragment(),
+ "Content is not a base64 string"));
+ if (reporter.fail_early())
+ {
+ return;
+ }
+ }
+ }
+ else if (!content_encoding_->empty())
+ {
+ reporter.error(validation_output("contentEncoding",
+ content_encoding_location_,
+ instance_location.to_uri_fragment(),
+ "unable to check for contentEncoding '" + *content_encoding_ + "'"));
+ if (reporter.fail_early())
+ {
+ return;
+ }
+ }
+ }
+ else
+ {
+ content = instance.template as<std::string>();
+ }
+
+ if (content_media_type_)
+ {
+ if (content_media_type_ == "application/Json")
+ {
+ json_string_reader reader(content);
+ std::error_code ec;
+ reader.read(ec);
+
+ if (ec)
+ {
+ reporter.error(validation_output("contentMediaType",
+ content_media_type_location_,
+ instance_location.to_uri_fragment(),
+ std::string("Content is not JSON: ") + ec.message()));
+ }
+ }
+ }
+ else if (instance.type() == json_type::byte_string_value)
+ {
+ reporter.error(validation_output("contentMediaType",
+ content_media_type_location_,
+ instance_location.to_uri_fragment(),
+ "Expected string, but is byte string"));
+ if (reporter.fail_early())
+ {
+ return;
+ }
+ }
+
+ if (instance.type() != json_type::string_value)
+ {
+ return;
+ }
+
+ if (min_length_)
+ {
+ std::size_t length = unicode_traits::count_codepoints(content.data(), content.size());
+ if (length < *min_length_)
+ {
+ reporter.error(validation_output("minLength",
+ min_length_location_,
+ instance_location.to_uri_fragment(),
+ std::string("Expected minLength: ") + std::to_string(*min_length_)
+ + ", actual: " + std::to_string(length)));
+ if (reporter.fail_early())
+ {
+ return;
+ }
+ }
+ }
+
+ if (max_length_)
+ {
+ std::size_t length = unicode_traits::count_codepoints(content.data(), content.size());
+ if (length > *max_length_)
+ {
+ reporter.error(validation_output("maxLength",
+ max_length_location_,
+ instance_location.to_uri_fragment(),
+ std::string("Expected maxLength: ") + std::to_string(*max_length_)
+ + ", actual: " + std::to_string(length)));
+ if (reporter.fail_early())
+ {
+ return;
+ }
+ }
+ }
+
+ #if defined(JSONCONS_HAS_STD_REGEX)
+ if (pattern_)
+ {
+ if (!std::regex_search(content, *pattern_))
+ {
+ std::string message("String \"");
+ message.append(instance.template as<std::string>());
+ message.append("\" does not match pattern \"");
+ message.append(pattern_string_);
+ message.append("\"");
+ reporter.error(validation_output("pattern",
+ pattern_location_,
+ instance_location.to_uri_fragment(),
+ std::move(message)));
+ if (reporter.fail_early())
+ {
+ return;
+ }
+ }
+ }
+
+ #endif
+
+ if (format_check_ != nullptr)
+ {
+ format_check_(format_location_, instance_location, content, reporter);
+ if (reporter.error_count() > 0 && reporter.fail_early())
+ {
+ return;
+ }
+ }
+ }
+ };
+
+ // not_validator
+
+ template <class Json>
+ class not_validator : public keyword_validator<Json>
+ {
+ using validator_pointer = typename keyword_validator<Json>::self_pointer;
+
+ validator_pointer rule_;
+
+ public:
+ not_validator(abstract_keyword_validator_factory<Json>* builder,
+ const Json& sch,
+ const std::vector<schema_location>& uris)
+ : keyword_validator<Json>((!uris.empty() && uris.back().is_absolute()) ? uris.back().string() : "")
+ {
+ rule_ = builder->make_keyword_validator(sch, uris, {"not"});
+ }
+
+ private:
+
+ void do_validate(const Json& instance,
+ const jsonpointer::json_pointer& instance_location,
+ error_reporter& reporter,
+ Json& patch) const final
+ {
+ collecting_error_reporter local_reporter;
+ rule_->validate(instance, instance_location, local_reporter, patch);
+
+ if (local_reporter.errors.empty())
+ {
+ reporter.error(validation_output("not",
+ this->absolute_keyword_location(),
+ instance_location.to_uri_fragment(),
+ "Instance must not be valid against schema"));
+ }
+ }
+
+ jsoncons::optional<Json> get_default_value(const jsonpointer::json_pointer& instance_location,
+ const Json& instance,
+ error_reporter& reporter) const override
+ {
+ return rule_->get_default_value(instance_location, instance, reporter);
+ }
+ };
+
+ template <class Json>
+ struct all_of_criterion
+ {
+ static const std::string& key()
+ {
+ static const std::string k("allOf");
+ return k;
+ }
+
+ static bool is_complete(const Json&,
+ const jsonpointer::json_pointer& instance_location,
+ error_reporter& reporter,
+ const collecting_error_reporter& local_reporter,
+ std::size_t)
+ {
+ if (!local_reporter.errors.empty())
+ reporter.error(validation_output("allOf",
+ "",
+ instance_location.to_uri_fragment(),
+ "At least one schema failed to match, but all are required to match. ",
+ local_reporter.errors));
+ return !local_reporter.errors.empty();
+ }
+ };
+
+ template <class Json>
+ struct any_of_criterion
+ {
+ static const std::string& key()
+ {
+ static const std::string k("anyOf");
+ return k;
+ }
+
+ static bool is_complete(const Json&,
+ const jsonpointer::json_pointer&,
+ error_reporter&,
+ const collecting_error_reporter&,
+ std::size_t count)
+ {
+ return count == 1;
+ }
+ };
+
+ template <class Json>
+ struct one_of_criterion
+ {
+ static const std::string& key()
+ {
+ static const std::string k("oneOf");
+ return k;
+ }
+
+ static bool is_complete(const Json&,
+ const jsonpointer::json_pointer& instance_location,
+ error_reporter& reporter,
+ const collecting_error_reporter&,
+ std::size_t count)
+ {
+ if (count > 1)
+ {
+ std::string message(std::to_string(count));
+ message.append(" subschemas matched, but exactly one is required to match");
+ reporter.error(validation_output("oneOf",
+ "",
+ instance_location.to_uri_fragment(),
+ std::move(message)));
+ }
+ return count > 1;
+ }
+ };
+
+ template <class Json,class Criterion>
+ class combining_validator : public keyword_validator<Json>
+ {
+ using validator_pointer = typename keyword_validator<Json>::self_pointer;
+
+ std::vector<validator_pointer> subschemas_;
+
+ public:
+ combining_validator(abstract_keyword_validator_factory<Json>* builder,
+ const Json& sch,
+ const std::vector<schema_location>& uris)
+ : keyword_validator<Json>((!uris.empty() && uris.back().is_absolute()) ? uris.back().string() : "")
+ {
+ size_t c = 0;
+ for (const auto& subsch : sch.array_range())
+ {
+ subschemas_.push_back(builder->make_keyword_validator(subsch, uris, {Criterion::key(), std::to_string(c++)}));
+ }
+
+ // Validate value of allOf, anyOf, and oneOf "MUST be a non-empty array"
+ }
+
+ private:
+
+ void do_validate(const Json& instance,
+ const jsonpointer::json_pointer& instance_location,
+ error_reporter& reporter,
+ Json& patch) const final
+ {
+ size_t count = 0;
+
+ collecting_error_reporter local_reporter;
+ for (auto& s : subschemas_)
+ {
+ std::size_t mark = local_reporter.errors.size();
+ s->validate(instance, instance_location, local_reporter, patch);
+ if (mark == local_reporter.errors.size())
+ count++;
+
+ if (Criterion::is_complete(instance, instance_location, reporter, local_reporter, count))
+ return;
+ }
+
+ if (count == 0)
+ {
+ reporter.error(validation_output("combined",
+ this->absolute_keyword_location(),
+ instance_location.to_uri_fragment(),
+ "No schema matched, but one of them is required to match",
+ local_reporter.errors));
+ }
+ }
+ };
+
+ template <class T, class Json>
+ T get_number(const Json& val, const string_view& keyword)
+ {
+ if (!val.is_number())
+ {
+ std::string message(keyword);
+ message.append(" must be a number value");
+ JSONCONS_THROW(schema_error(message));
+ }
+ return val.template as<T>();
+ }
+
+ template <class Json,class T>
+ class numeric_validator_base : public keyword_validator<Json>
+ {
+ jsoncons::optional<T> maximum_;
+ std::string absolute_maximum_location_;
+ jsoncons::optional<T> minimum_;
+ std::string absolute_minimum_location_;
+ jsoncons::optional<T> exclusive_maximum_;
+ std::string absolute_exclusive_maximum_location_;
+ jsoncons::optional<T> exclusive_minimum_;
+ std::string absolute_exclusive_minimum_location_;
+ jsoncons::optional<double> multiple_of_;
+ std::string absolute_multiple_of_location_;
+
+ public:
+ numeric_validator_base(const Json& sch,
+ const std::vector<schema_location>& uris,
+ std::set<std::string>& keywords)
+ : keyword_validator<Json>((!uris.empty() && uris.back().is_absolute()) ? uris.back().string() : ""),
+ maximum_(), minimum_(),exclusive_maximum_(), exclusive_minimum_(), multiple_of_()
+ {
+ auto it = sch.find("maximum");
+ if (it != sch.object_range().end())
+ {
+ maximum_ = get_number<T>(it->value(), "maximum");
+ absolute_maximum_location_ = make_absolute_keyword_location(uris,"maximum");
+ keywords.insert("maximum");
+ }
+
+ it = sch.find("minimum");
+ if (it != sch.object_range().end())
+ {
+ minimum_ = get_number<T>(it->value(), "minimum");
+ absolute_minimum_location_ = make_absolute_keyword_location(uris,"minimum");
+ keywords.insert("minimum");
+ }
+
+ it = sch.find("exclusiveMaximum");
+ if (it != sch.object_range().end())
+ {
+ exclusive_maximum_ = get_number<T>(it->value(), "exclusiveMaximum");
+ absolute_exclusive_maximum_location_ = make_absolute_keyword_location(uris,"exclusiveMaximum");
+ keywords.insert("exclusiveMaximum");
+ }
+
+ it = sch.find("exclusiveMinimum");
+ if (it != sch.object_range().end())
+ {
+ exclusive_minimum_ = get_number<T>(it->value(), "exclusiveMinimum");
+ absolute_exclusive_minimum_location_ = make_absolute_keyword_location(uris,"exclusiveMinimum");
+ keywords.insert("exclusiveMinimum");
+ }
+
+ it = sch.find("multipleOf");
+ if (it != sch.object_range().end())
+ {
+ multiple_of_ = get_number<double>(it->value(), "multipleOf");
+ absolute_multiple_of_location_ = make_absolute_keyword_location(uris,"multipleOf");
+ keywords.insert("multipleOf");
+ }
+ }
+
+ protected:
+
+ void apply_kewords(T value,
+ const jsonpointer::json_pointer& instance_location,
+ const Json& instance,
+ error_reporter& reporter) const
+ {
+ if (multiple_of_ && value != 0) // exclude zero
+ {
+ if (!is_multiple_of(value, *multiple_of_))
+ {
+ reporter.error(validation_output("multipleOf",
+ absolute_multiple_of_location_,
+ instance_location.to_uri_fragment(),
+ instance.template as<std::string>() + " is not a multiple of " + std::to_string(*multiple_of_)));
+ if (reporter.fail_early())
+ {
+ return;
+ }
+ }
+ }
+
+ if (maximum_)
+ {
+ if (value > *maximum_)
+ {
+ reporter.error(validation_output("maximum",
+ absolute_maximum_location_,
+ instance_location.to_uri_fragment(),
+ instance.template as<std::string>() + " exceeds maximum of " + std::to_string(*maximum_)));
+ if (reporter.fail_early())
+ {
+ return;
+ }
+ }
+ }
+
+ if (minimum_)
+ {
+ if (value < *minimum_)
+ {
+ reporter.error(validation_output("minimum",
+ absolute_minimum_location_,
+ instance_location.to_uri_fragment(),
+ instance.template as<std::string>() + " is below minimum of " + std::to_string(*minimum_)));
+ if (reporter.fail_early())
+ {
+ return;
+ }
+ }
+ }
+
+ if (exclusive_maximum_)
+ {
+ if (value >= *exclusive_maximum_)
+ {
+ reporter.error(validation_output("exclusiveMaximum",
+ absolute_exclusive_maximum_location_,
+ instance_location.to_uri_fragment(),
+ instance.template as<std::string>() + " exceeds maximum of " + std::to_string(*exclusive_maximum_)));
+ if (reporter.fail_early())
+ {
+ return;
+ }
+ }
+ }
+
+ if (exclusive_minimum_)
+ {
+ if (value <= *exclusive_minimum_)
+ {
+ reporter.error(validation_output("exclusiveMinimum",
+ absolute_exclusive_minimum_location_,
+ instance_location.to_uri_fragment(),
+ instance.template as<std::string>() + " is below minimum of " + std::to_string(*exclusive_minimum_)));
+ if (reporter.fail_early())
+ {
+ return;
+ }
+ }
+ }
+ }
+ private:
+ static bool is_multiple_of(T x, double multiple_of)
+ {
+ double rem = std::remainder(x, multiple_of);
+ double eps = std::nextafter(x, 0) - x;
+ return std::fabs(rem) < std::fabs(eps);
+ }
+ };
+
+ template <class Json>
+ class integer_validator : public numeric_validator_base<Json,int64_t>
+ {
+ public:
+ integer_validator(const Json& sch,
+ const std::vector<schema_location>& uris,
+ std::set<std::string>& keywords)
+ : numeric_validator_base<Json, int64_t>(sch, uris, keywords)
+ {
+ }
+ private:
+ void do_validate(const Json& instance,
+ const jsonpointer::json_pointer& instance_location,
+ error_reporter& reporter,
+ Json&) const
+ {
+ if (!(instance.template is_integer<int64_t>() || (instance.is_double() && static_cast<double>(instance.template as<int64_t>()) == instance.template as<double>())))
+ {
+ reporter.error(validation_output("integer",
+ this->absolute_keyword_location(),
+ instance_location.to_uri_fragment(),
+ "Instance is not an integer"));
+ if (reporter.fail_early())
+ {
+ return;
+ }
+ }
+ int64_t value = instance.template as<int64_t>();
+ this->apply_kewords(value, instance_location, instance, reporter);
+ }
+ };
+
+ template <class Json>
+ class number_validator : public numeric_validator_base<Json,double>
+ {
+ public:
+ number_validator(const Json& sch,
+ const std::vector<schema_location>& uris,
+ std::set<std::string>& keywords)
+ : numeric_validator_base<Json, double>(sch, uris, keywords)
+ {
+ }
+ private:
+ void do_validate(const Json& instance,
+ const jsonpointer::json_pointer& instance_location,
+ error_reporter& reporter,
+ Json&) const
+ {
+ if (!(instance.template is_integer<int64_t>() || instance.is_double()))
+ {
+ reporter.error(validation_output("number",
+ this->absolute_keyword_location(),
+ instance_location.to_uri_fragment(),
+ "Instance is not a number"));
+ if (reporter.fail_early())
+ {
+ return;
+ }
+ }
+ double value = instance.template as<double>();
+ this->apply_kewords(value, instance_location, instance, reporter);
+ }
+ };
+
+ // null_validator
+
+ template <class Json>
+ class null_validator : public keyword_validator<Json>
+ {
+ public:
+ null_validator(const std::vector<schema_location>& uris)
+ : keyword_validator<Json>((!uris.empty() && uris.back().is_absolute()) ? uris.back().string() : "")
+ {
+ }
+ private:
+ void do_validate(const Json& instance,
+ const jsonpointer::json_pointer& instance_location,
+ error_reporter& reporter,
+ Json&) const override
+ {
+ if (!instance.is_null())
+ {
+ reporter.error(validation_output("null",
+ this->absolute_keyword_location(),
+ instance_location.to_uri_fragment(),
+ "Expected to be null"));
+ }
+ }
+ };
+
+ template <class Json>
+ class boolean_validator : public keyword_validator<Json>
+ {
+ public:
+ boolean_validator(const std::vector<schema_location>& uris)
+ : keyword_validator<Json>((!uris.empty() && uris.back().is_absolute()) ? uris.back().string() : "")
+ {
+ }
+ private:
+ void do_validate(const Json&,
+ const jsonpointer::json_pointer&,
+ error_reporter&,
+ Json&) const override
+ {
+ }
+
+ };
+
+ template <class Json>
+ class true_validator : public keyword_validator<Json>
+ {
+ public:
+ true_validator(const std::vector<schema_location>& uris)
+ : keyword_validator<Json>((!uris.empty() && uris.back().is_absolute()) ? uris.back().string() : "")
+ {
+ }
+ private:
+ void do_validate(const Json&,
+ const jsonpointer::json_pointer&,
+ error_reporter&,
+ Json&) const override
+ {
+ }
+ };
+
+ template <class Json>
+ class false_validator : public keyword_validator<Json>
+ {
+ public:
+ false_validator(const std::vector<schema_location>& uris)
+ : keyword_validator<Json>((!uris.empty() && uris.back().is_absolute()) ? uris.back().string() : "")
+ {
+ }
+ private:
+ void do_validate(const Json&,
+ const jsonpointer::json_pointer& instance_location,
+ error_reporter& reporter,
+ Json&) const override
+ {
+ reporter.error(validation_output("false",
+ this->absolute_keyword_location(),
+ instance_location.to_uri_fragment(),
+ "False schema always fails"));
+ }
+ };
+
+ template <class Json>
+ class required_validator : public keyword_validator<Json>
+ {
+ using validator_pointer = typename keyword_validator<Json>::self_pointer;
+
+ std::vector<std::string> items_;
+
+ public:
+ required_validator(const std::vector<schema_location>& uris,
+ const std::vector<std::string>& items)
+ : keyword_validator<Json>((!uris.empty() && uris.back().is_absolute()) ? uris.back().string() : ""), items_(items) {}
+ required_validator(const std::string& absolute_keyword_location, const std::vector<std::string>& items)
+ : keyword_validator<Json>(absolute_keyword_location), items_(items) {}
+
+ required_validator(const required_validator&) = delete;
+ required_validator(required_validator&&) = default;
+ required_validator& operator=(const required_validator&) = delete;
+ required_validator& operator=(required_validator&&) = default;
+ private:
+
+ void do_validate(const Json& instance,
+ const jsonpointer::json_pointer& instance_location,
+ error_reporter& reporter,
+ Json&) const override final
+ {
+ for (const auto& key : items_)
+ {
+ if (instance.find(key) == instance.object_range().end())
+ {
+ reporter.error(validation_output("required",
+ this->absolute_keyword_location(),
+ instance_location.to_uri_fragment(),
+ "Required property \"" + key + "\" not found"));
+ if (reporter.fail_early())
+ {
+ return;
+ }
+ }
+ }
+ }
+ };
+
+ template <class Json>
+ class object_validator : public keyword_validator<Json>
+ {
+ using validator_pointer = typename keyword_validator<Json>::self_pointer;
+
+ jsoncons::optional<std::size_t> max_properties_;
+ std::string absolute_max_properties_location_;
+ jsoncons::optional<std::size_t> min_properties_;
+ std::string absolute_min_properties_location_;
+ jsoncons::optional<required_validator<Json>> required_;
+
+ std::map<std::string, validator_pointer> properties_;
+ #if defined(JSONCONS_HAS_STD_REGEX)
+ std::vector<std::pair<std::regex, validator_pointer>> pattern_properties_;
+ #endif
+ validator_pointer additional_properties_;
+
+ std::map<std::string, validator_pointer> dependencies_;
+
+ validator_pointer property_name_validator_;
+
+ public:
+ object_validator(abstract_keyword_validator_factory<Json>* builder,
+ const Json& sch,
+ const std::vector<schema_location>& uris)
+ : keyword_validator<Json>((!uris.empty() && uris.back().is_absolute()) ? uris.back().string() : ""),
+ max_properties_(), min_properties_(),
+ additional_properties_(nullptr),
+ property_name_validator_(nullptr)
+ {
+ auto it = sch.find("maxProperties");
+ if (it != sch.object_range().end())
+ {
+ max_properties_ = it->value().template as<std::size_t>();
+ absolute_max_properties_location_ = make_absolute_keyword_location(uris, "maxProperties");
+ }
+
+ it = sch.find("minProperties");
+ if (it != sch.object_range().end())
+ {
+ min_properties_ = it->value().template as<std::size_t>();
+ absolute_min_properties_location_ = make_absolute_keyword_location(uris, "minProperties");
+ }
+
+ it = sch.find("required");
+ if (it != sch.object_range().end())
+ {
+ auto location = make_absolute_keyword_location(uris, "required");
+ required_ = required_validator<Json>(location,
+ it->value().template as<std::vector<std::string>>());
+ }
+
+ it = sch.find("properties");
+ if (it != sch.object_range().end())
+ {
+ for (const auto& prop : it->value().object_range())
+ properties_.emplace(
+ std::make_pair(
+ prop.key(),
+ builder->make_keyword_validator(prop.value(), uris, {"properties", prop.key()})));
+ }
+
+ #if defined(JSONCONS_HAS_STD_REGEX)
+ it = sch.find("patternProperties");
+ if (it != sch.object_range().end())
+ {
+ for (const auto& prop : it->value().object_range())
+ pattern_properties_.emplace_back(
+ std::make_pair(
+ std::regex(prop.key(), std::regex::ECMAScript),
+ builder->make_keyword_validator(prop.value(), uris, {prop.key()})));
+ }
+ #endif
+
+ it = sch.find("additionalProperties");
+ if (it != sch.object_range().end())
+ {
+ additional_properties_ = builder->make_keyword_validator(it->value(), uris, {"additionalProperties"});
+ }
+
+ it = sch.find("dependencies");
+ if (it != sch.object_range().end())
+ {
+ for (const auto& dep : it->value().object_range())
+ {
+ switch (dep.value().type())
+ {
+ case json_type::array_value:
+ {
+ auto location = make_absolute_keyword_location(uris, "dependencies");
+ dependencies_.emplace(dep.key(),
+ builder->make_required_validator({location},
+ dep.value().template as<std::vector<std::string>>()));
+ break;
+ }
+ default:
+ {
+ dependencies_.emplace(dep.key(),
+ builder->make_keyword_validator(dep.value(), uris, {"dependencies", dep.key()}));
+ break;
+ }
+ }
+ }
+ }
+
+ auto property_names_it = sch.find("propertyNames");
+ if (property_names_it != sch.object_range().end())
+ {
+ property_name_validator_ = builder->make_keyword_validator(property_names_it->value(), uris, {"propertyNames"});
+ }
+ }
+ private:
+
+ void do_validate(const Json& instance,
+ const jsonpointer::json_pointer& instance_location,
+ error_reporter& reporter,
+ Json& patch) const override
+ {
+ if (max_properties_ && instance.size() > *max_properties_)
+ {
+ std::string message("Maximum properties: " + std::to_string(*max_properties_));
+ message.append(", found: " + std::to_string(instance.size()));
+ reporter.error(validation_output("maxProperties",
+ absolute_max_properties_location_,
+ instance_location.to_uri_fragment(),
+ std::move(message)));
+ if (reporter.fail_early())
+ {
+ return;
+ }
+ }
+
+ if (min_properties_ && instance.size() < *min_properties_)
+ {
+ std::string message("Minimum properties: " + std::to_string(*min_properties_));
+ message.append(", found: " + std::to_string(instance.size()));
+ reporter.error(validation_output("minProperties",
+ absolute_min_properties_location_,
+ instance_location.to_uri_fragment(),
+ std::move(message)));
+ if (reporter.fail_early())
+ {
+ return;
+ }
+ }
+
+ if (required_)
+ required_->validate(instance, instance_location, reporter, patch);
+
+ for (const auto& property : instance.object_range())
+ {
+ if (property_name_validator_)
+ property_name_validator_->validate(property.key(), instance_location, reporter, patch);
+
+ bool a_prop_or_pattern_matched = false;
+ auto properties_it = properties_.find(property.key());
+
+ // check if it is in "properties"
+ if (properties_it != properties_.end())
+ {
+ a_prop_or_pattern_matched = true;
+ jsonpointer::json_pointer pointer(instance_location);
+ pointer /= property.key();
+ properties_it->second->validate(property.value(), pointer, reporter, patch);
+ }
+
+ #if defined(JSONCONS_HAS_STD_REGEX)
+
+ // check all matching "patternProperties"
+ for (auto& schema_pp : pattern_properties_)
+ if (std::regex_search(property.key(), schema_pp.first))
+ {
+ a_prop_or_pattern_matched = true;
+ jsonpointer::json_pointer pointer(instance_location);
+ pointer /= property.key();
+ schema_pp.second->validate(property.value(), pointer, reporter, patch);
+ }
+ #endif
+
+ // finally, check "additionalProperties"
+ if (!a_prop_or_pattern_matched && additional_properties_)
+ {
+ collecting_error_reporter local_reporter;
+
+ jsonpointer::json_pointer pointer(instance_location);
+ pointer /= property.key();
+ additional_properties_->validate(property.value(), pointer, local_reporter, patch);
+ if (!local_reporter.errors.empty())
+ {
+ reporter.error(validation_output("additionalProperties",
+ additional_properties_->absolute_keyword_location(),
+ instance_location.to_uri_fragment(),
+ "Additional property \"" + property.key() + "\" found but was invalid."));
+ if (reporter.fail_early())
+ {
+ return;
+ }
+ }
+ }
+ }
+
+ // reverse search
+ for (auto const& prop : properties_)
+ {
+ const auto finding = instance.find(prop.first);
+ if (finding == instance.object_range().end())
+ {
+ // If property is not in instance
+ auto default_value = prop.second->get_default_value(instance_location, instance, reporter);
+ if (default_value)
+ {
+ // If default value is available, update patch
+ jsonpointer::json_pointer pointer(instance_location);
+ pointer /= prop.first;
+
+ update_patch(patch, pointer, std::move(*default_value));
+ }
+ }
+ }
+
+ for (const auto& dep : dependencies_)
+ {
+ auto prop = instance.find(dep.first);
+ if (prop != instance.object_range().end())
+ {
+ // if dependency-property is present in instance
+ jsonpointer::json_pointer pointer(instance_location);
+ pointer /= dep.first;
+ dep.second->validate(instance, pointer, reporter, patch); // validate
+ }
+ }
+ }
+
+ void update_patch(Json& patch, const jsonpointer::json_pointer& instance_location, Json&& default_value) const
+ {
+ Json j;
+ j.try_emplace("op", "add");
+ j.try_emplace("path", instance_location.to_uri_fragment());
+ j.try_emplace("value", std::forward<Json>(default_value));
+ patch.push_back(std::move(j));
+ }
+ };
+
+ // array_validator
+
+ template <class Json>
+ class array_validator : public keyword_validator<Json>
+ {
+ using validator_pointer = typename keyword_validator<Json>::self_pointer;
+
+ jsoncons::optional<std::size_t> max_items_;
+ std::string absolute_max_items_location_;
+ jsoncons::optional<std::size_t> min_items_;
+ std::string absolute_min_items_location_;
+ bool unique_items_ = false;
+ validator_pointer items_validator_;
+ std::vector<validator_pointer> item_validators_;
+ validator_pointer additional_items_validator_;
+ validator_pointer contains_validator_;
+
+ public:
+ array_validator(abstract_keyword_validator_factory<Json>* builder,
+ const Json& sch,
+ const std::vector<schema_location>& uris)
+ : keyword_validator<Json>((!uris.empty() && uris.back().is_absolute()) ? uris.back().string() : ""),
+ max_items_(), min_items_(), items_validator_(nullptr), additional_items_validator_(nullptr), contains_validator_(nullptr)
+ {
+ {
+ auto it = sch.find("maxItems");
+ if (it != sch.object_range().end())
+ {
+ max_items_ = it->value().template as<std::size_t>();
+ absolute_max_items_location_ = make_absolute_keyword_location(uris, "maxItems");
+ }
+ }
+
+ {
+ auto it = sch.find("minItems");
+ if (it != sch.object_range().end())
+ {
+ min_items_ = it->value().template as<std::size_t>();
+ absolute_min_items_location_ = make_absolute_keyword_location(uris, "minItems");
+ }
+ }
+
+ {
+ auto it = sch.find("uniqueItems");
+ if (it != sch.object_range().end())
+ {
+ unique_items_ = it->value().template as<bool>();
+ }
+ }
+
+ {
+ auto it = sch.find("items");
+ if (it != sch.object_range().end())
+ {
+
+ if (it->value().type() == json_type::array_value)
+ {
+ size_t c = 0;
+ for (const auto& subsch : it->value().array_range())
+ item_validators_.push_back(builder->make_keyword_validator(subsch, uris, {"items", std::to_string(c++)}));
+
+ auto attr_add = sch.find("additionalItems");
+ if (attr_add != sch.object_range().end())
+ {
+ additional_items_validator_ = builder->make_keyword_validator(attr_add->value(), uris, {"additionalItems"});
+ }
+
+ }
+ else if (it->value().type() == json_type::object_value ||
+ it->value().type() == json_type::bool_value)
+ {
+ items_validator_ = builder->make_keyword_validator(it->value(), uris, {"items"});
+ }
+
+ }
+ }
+
+ {
+ auto it = sch.find("contains");
+ if (it != sch.object_range().end())
+ {
+ contains_validator_ = builder->make_keyword_validator(it->value(), uris, {"contains"});
+ }
+ }
+ }
+ private:
+
+ void do_validate(const Json& instance,
+ const jsonpointer::json_pointer& instance_location,
+ error_reporter& reporter,
+ Json& patch) const override
+ {
+ if (max_items_)
+ {
+ if (instance.size() > *max_items_)
+ {
+ std::string message("Expected maximum item count: " + std::to_string(*max_items_));
+ message.append(", found: " + std::to_string(instance.size()));
+ reporter.error(validation_output("maxItems",
+ absolute_max_items_location_,
+ instance_location.to_uri_fragment(),
+ std::move(message)));
+ if (reporter.fail_early())
+ {
+ return;
+ }
+ }
+ }
+
+ if (min_items_)
+ {
+ if (instance.size() < *min_items_)
+ {
+ std::string message("Expected minimum item count: " + std::to_string(*min_items_));
+ message.append(", found: " + std::to_string(instance.size()));
+ reporter.error(validation_output("minItems",
+ absolute_min_items_location_,
+ instance_location.to_uri_fragment(),
+ std::move(message)));
+ if (reporter.fail_early())
+ {
+ return;
+ }
+ }
+ }
+
+ if (unique_items_)
+ {
+ if (!array_has_unique_items(instance))
+ {
+ reporter.error(validation_output("uniqueItems",
+ this->absolute_keyword_location(),
+ instance_location.to_uri_fragment(),
+ "Array items are not unique"));
+ if (reporter.fail_early())
+ {
+ return;
+ }
+ }
+ }
+
+ size_t index = 0;
+ if (items_validator_)
+ {
+ for (const auto& i : instance.array_range())
+ {
+ jsonpointer::json_pointer pointer(instance_location);
+ pointer /= index;
+ items_validator_->validate(i, pointer, reporter, patch);
+ index++;
+ }
+ }
+ else
+ {
+ auto validator_it = item_validators_.cbegin();
+ for (const auto& item : instance.array_range())
+ {
+ validator_pointer item_validator = nullptr;
+ if (validator_it != item_validators_.cend())
+ {
+ item_validator = *validator_it;
+ ++validator_it;
+ }
+ else if (additional_items_validator_ != nullptr)
+ {
+ item_validator = additional_items_validator_;
+ }
+ else
+ break;
+
+ jsonpointer::json_pointer pointer(instance_location);
+ pointer /= index;
+ item_validator->validate(item, pointer, reporter, patch);
+ }
+ }
+
+ if (contains_validator_)
+ {
+ bool contained = false;
+ collecting_error_reporter local_reporter;
+ for (const auto& item : instance.array_range())
+ {
+ std::size_t mark = local_reporter.errors.size();
+ contains_validator_->validate(item, instance_location, local_reporter, patch);
+ if (mark == local_reporter.errors.size())
+ {
+ contained = true;
+ break;
+ }
+ }
+ if (!contained)
+ {
+ reporter.error(validation_output("contains",
+ this->absolute_keyword_location(),
+ instance_location.to_uri_fragment(),
+ "Expected at least one array item to match \"contains\" schema",
+ local_reporter.errors));
+ if (reporter.fail_early())
+ {
+ return;
+ }
+ }
+ }
+ }
+
+ static bool array_has_unique_items(const Json& a)
+ {
+ for (auto it = a.array_range().begin(); it != a.array_range().end(); ++it)
+ {
+ for (auto jt = it+1; jt != a.array_range().end(); ++jt)
+ {
+ if (*it == *jt)
+ {
+ return false; // contains duplicates
+ }
+ }
+ }
+ return true; // elements are unique
+ }
+ };
+
+ template <class Json>
+ class conditional_validator : public keyword_validator<Json>
+ {
+ using validator_pointer = typename keyword_validator<Json>::self_pointer;
+
+ validator_pointer if_validator_;
+ validator_pointer then_validator_;
+ validator_pointer else_validator_;
+
+ public:
+ conditional_validator(abstract_keyword_validator_factory<Json>* builder,
+ const Json& sch_if,
+ const Json& sch,
+ const std::vector<schema_location>& uris)
+ : keyword_validator<Json>((!uris.empty() && uris.back().is_absolute()) ? uris.back().string() : ""), if_validator_(nullptr), then_validator_(nullptr), else_validator_(nullptr)
+ {
+ auto then_it = sch.find("then");
+ auto else_it = sch.find("else");
+
+ if (then_it != sch.object_range().end() || else_it != sch.object_range().end())
+ {
+ if_validator_ = builder->make_keyword_validator(sch_if, uris, {"if"});
+
+ if (then_it != sch.object_range().end())
+ {
+ then_validator_ = builder->make_keyword_validator(then_it->value(), uris, {"then"});
+ }
+
+ if (else_it != sch.object_range().end())
+ {
+ else_validator_ = builder->make_keyword_validator(else_it->value(), uris, {"else"});
+ }
+ }
+ }
+ private:
+ void do_validate(const Json& instance,
+ const jsonpointer::json_pointer& instance_location,
+ error_reporter& reporter,
+ Json& patch) const final
+ {
+ if (if_validator_)
+ {
+ collecting_error_reporter local_reporter;
+
+ if_validator_->validate(instance, instance_location, local_reporter, patch);
+ if (local_reporter.errors.empty())
+ {
+ if (then_validator_)
+ then_validator_->validate(instance, instance_location, reporter, patch);
+ }
+ else
+ {
+ if (else_validator_)
+ else_validator_->validate(instance, instance_location, reporter, patch);
+ }
+ }
+ }
+ };
+
+ // enum_validator
+
+ template <class Json>
+ class enum_validator : public keyword_validator<Json>
+ {
+ Json enum_validator_;
+
+ public:
+ enum_validator(const Json& sch,
+ const std::vector<schema_location>& uris)
+ : keyword_validator<Json>((!uris.empty() && uris.back().is_absolute()) ? uris.back().string() : ""), enum_validator_(sch)
+ {
+ }
+ private:
+ void do_validate(const Json& instance,
+ const jsonpointer::json_pointer& instance_location,
+ error_reporter& reporter,
+ Json&) const final
+ {
+ bool in_range = false;
+ for (const auto& item : enum_validator_.array_range())
+ {
+ if (item == instance)
+ {
+ in_range = true;
+ break;
+ }
+ }
+
+ if (!in_range)
+ {
+ reporter.error(validation_output("enum",
+ this->absolute_keyword_location(),
+ instance_location.to_uri_fragment(),
+ instance.template as<std::string>() + " is not a valid enum value"));
+ if (reporter.fail_early())
+ {
+ return;
+ }
+ }
+ }
+ };
+
+ // const_keyword
+
+ template <class Json>
+ class const_keyword : public keyword_validator<Json>
+ {
+ Json const_validator_;
+
+ public:
+ const_keyword(const Json& sch, const std::vector<schema_location>& uris)
+ : keyword_validator<Json>((!uris.empty() && uris.back().is_absolute()) ? uris.back().string() : ""), const_validator_(sch)
+ {
+ }
+ private:
+ void do_validate(const Json& instance,
+ const jsonpointer::json_pointer& instance_location,
+ error_reporter& reporter,
+ Json&) const final
+ {
+ if (const_validator_ != instance)
+ reporter.error(validation_output("const",
+ this->absolute_keyword_location(),
+ instance_location.to_uri_fragment(),
+ "Instance is not const"));
+ }
+ };
+
+ template <class Json>
+ class type_validator : public keyword_validator<Json>
+ {
+ using validator_pointer = typename keyword_validator<Json>::self_pointer;
+
+ Json default_value_;
+ std::vector<validator_pointer> type_mapping_;
+ jsoncons::optional<enum_validator<Json>> enum_validator_;
+ jsoncons::optional<const_keyword<Json>> const_validator_;
+ std::vector<validator_pointer> combined_validators_;
+ jsoncons::optional<conditional_validator<Json>> conditional_validator_;
+ std::vector<std::string> expected_types_;
+
+ public:
+ type_validator(const type_validator&) = delete;
+ type_validator& operator=(const type_validator&) = delete;
+ type_validator(type_validator&&) = default;
+ type_validator& operator=(type_validator&&) = default;
+
+ type_validator(abstract_keyword_validator_factory<Json>* builder,
+ const Json& sch,
+ const std::vector<schema_location>& uris)
+ : keyword_validator<Json>((!uris.empty() && uris.back().is_absolute()) ? uris.back().string() : ""), default_value_(jsoncons::null_type()),
+ type_mapping_((uint8_t)(json_type::object_value)+1),
+ enum_validator_(), const_validator_()
+ {
+ //std::cout << uris.size() << " uris: ";
+ //for (const auto& uri : uris)
+ //{
+ // std::cout << uri.string() << ", ";
+ //}
+ //std::cout << "\n";
+ std::set<std::string> known_keywords;
+
+ auto it = sch.find("type");
+ if (it == sch.object_range().end())
+ {
+ initialize_type_mapping(builder, "", sch, uris, known_keywords);
+ }
+ else
+ {
+ switch (it->value().type())
+ {
+ case json_type::string_value:
+ {
+ auto type = it->value().template as<std::string>();
+ initialize_type_mapping(builder, type, sch, uris, known_keywords);
+ expected_types_.emplace_back(std::move(type));
+ break;
+ }
+
+ case json_type::array_value: // "type": ["type1", "type2"]
+ {
+ for (const auto& item : it->value().array_range())
+ {
+ auto type = item.template as<std::string>();
+ initialize_type_mapping(builder, type, sch, uris, known_keywords);
+ expected_types_.emplace_back(std::move(type));
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ const auto default_it = sch.find("default");
+ if (default_it != sch.object_range().end())
+ {
+ default_value_ = default_it->value();
+ }
+
+ it = sch.find("enum");
+ if (it != sch.object_range().end())
+ {
+ enum_validator_ = enum_validator<Json >(it->value(), uris);
+ }
+
+ it = sch.find("const");
+ if (it != sch.object_range().end())
+ {
+ const_validator_ = const_keyword<Json>(it->value(), uris);
+ }
+
+ it = sch.find("not");
+ if (it != sch.object_range().end())
+ {
+ combined_validators_.push_back(builder->make_not_validator(it->value(), uris));
+ }
+
+ it = sch.find("allOf");
+ if (it != sch.object_range().end())
+ {
+ combined_validators_.push_back(builder->make_all_of_validator(it->value(), uris));
+ }
+
+ it = sch.find("anyOf");
+ if (it != sch.object_range().end())
+ {
+ combined_validators_.push_back(builder->make_any_of_validator(it->value(), uris));
+ }
+
+ it = sch.find("oneOf");
+ if (it != sch.object_range().end())
+ {
+ combined_validators_.push_back(builder->make_one_of_validator(it->value(), uris));
+ }
+
+ it = sch.find("if");
+ if (it != sch.object_range().end())
+ {
+ conditional_validator_ = conditional_validator<Json>(builder, it->value(), sch, uris);
+ }
+ }
+ private:
+
+ void do_validate(const Json& instance,
+ const jsonpointer::json_pointer& instance_location,
+ error_reporter& reporter,
+ Json& patch) const override final
+ {
+ auto type = type_mapping_[(uint8_t) instance.type()];
+
+ if (type)
+ type->validate(instance, instance_location, reporter, patch);
+ else
+ {
+ std::ostringstream ss;
+ ss << "Expected ";
+ for (std::size_t i = 0; i < expected_types_.size(); ++i)
+ {
+ if (i > 0)
+ {
+ ss << ", ";
+ if (i+1 == expected_types_.size())
+ {
+ ss << "or ";
+ }
+ }
+ ss << expected_types_[i];
+ }
+ ss << ", found " << instance.type();
+
+ reporter.error(validation_output("type",
+ this->absolute_keyword_location(),
+ instance_location.to_uri_fragment(),
+ ss.str()));
+ if (reporter.fail_early())
+ {
+ return;
+ }
+ }
+
+ if (enum_validator_)
+ {
+ enum_validator_->validate(instance, instance_location, reporter, patch);
+ if (reporter.error_count() > 0 && reporter.fail_early())
+ {
+ return;
+ }
+ }
+
+ if (const_validator_)
+ {
+ const_validator_->validate(instance, instance_location, reporter, patch);
+ if (reporter.error_count() > 0 && reporter.fail_early())
+ {
+ return;
+ }
+ }
+
+ for (const auto& validator : combined_validators_)
+ {
+ validator->validate(instance, instance_location, reporter, patch);
+ if (reporter.error_count() > 0 && reporter.fail_early())
+ {
+ return;
+ }
+ }
+
+
+ if (conditional_validator_)
+ {
+ conditional_validator_->validate(instance, instance_location, reporter, patch);
+ if (reporter.error_count() > 0 && reporter.fail_early())
+ {
+ return;
+ }
+ }
+ }
+
+ jsoncons::optional<Json> get_default_value(const jsonpointer::json_pointer&,
+ const Json&,
+ error_reporter&) const override
+ {
+ return default_value_;
+ }
+
+ void initialize_type_mapping(abstract_keyword_validator_factory<Json>* builder,
+ const std::string& type,
+ const Json& sch,
+ const std::vector<schema_location>& uris,
+ std::set<std::string>& keywords)
+ {
+ if (type == "null")
+ {
+ type_mapping_[(uint8_t)json_type::null_value] = builder->make_null_validator(uris);
+ }
+ else if (type == "object")
+ {
+ type_mapping_[(uint8_t)json_type::object_value] = builder->make_object_validator(sch, uris);
+ }
+ else if (type == "array")
+ {
+ type_mapping_[(uint8_t)json_type::array_value] = builder->make_array_validator(sch, uris);
+ }
+ else if (type == "string")
+ {
+ type_mapping_[(uint8_t)json_type::string_value] = builder->make_string_validator(sch, uris);
+ // For binary types
+ type_mapping_[(uint8_t) json_type::byte_string_value] = type_mapping_[(uint8_t) json_type::string_value];
+ }
+ else if (type == "boolean")
+ {
+ type_mapping_[(uint8_t)json_type::bool_value] = builder->make_boolean_validator(uris);
+ }
+ else if (type == "integer")
+ {
+ type_mapping_[(uint8_t)json_type::int64_value] = builder->make_integer_validator(sch, uris, keywords);
+ type_mapping_[(uint8_t)json_type::uint64_value] = type_mapping_[(uint8_t)json_type::int64_value];
+ type_mapping_[(uint8_t)json_type::double_value] = type_mapping_[(uint8_t)json_type::int64_value];
+ }
+ else if (type == "number")
+ {
+ type_mapping_[(uint8_t)json_type::double_value] = builder->make_number_validator(sch, uris, keywords);
+ type_mapping_[(uint8_t)json_type::int64_value] = type_mapping_[(uint8_t)json_type::double_value];
+ type_mapping_[(uint8_t)json_type::uint64_value] = type_mapping_[(uint8_t)json_type::double_value];
+ }
+ else if (type.empty())
+ {
+ type_mapping_[(uint8_t)json_type::null_value] = builder->make_null_validator(uris);
+ type_mapping_[(uint8_t)json_type::object_value] = builder->make_object_validator(sch, uris);
+ type_mapping_[(uint8_t)json_type::array_value] = builder->make_array_validator(sch, uris);
+ type_mapping_[(uint8_t)json_type::string_value] = builder->make_string_validator(sch, uris);
+ // For binary types
+ type_mapping_[(uint8_t) json_type::byte_string_value] = type_mapping_[(uint8_t) json_type::string_value];
+ type_mapping_[(uint8_t)json_type::bool_value] = builder->make_boolean_validator(uris);
+ type_mapping_[(uint8_t)json_type::int64_value] = builder->make_integer_validator(sch, uris, keywords);
+ type_mapping_[(uint8_t)json_type::uint64_value] = type_mapping_[(uint8_t)json_type::int64_value];
+ type_mapping_[(uint8_t)json_type::double_value] = type_mapping_[(uint8_t)json_type::int64_value];
+ type_mapping_[(uint8_t)json_type::double_value] = builder->make_number_validator(sch, uris, keywords);
+ type_mapping_[(uint8_t)json_type::int64_value] = type_mapping_[(uint8_t)json_type::double_value];
+ type_mapping_[(uint8_t)json_type::uint64_value] = type_mapping_[(uint8_t)json_type::double_value];
+ }
+ }
+ };
+
+} // namespace jsonschema
+} // namespace jsoncons
+
+#endif // JSONCONS_JSONSCHEMA_VALUE_RULES_HPP
diff --git a/include/jsoncons_ext/jsonschema/keyword_validator_factory.hpp b/include/jsoncons_ext/jsonschema/keyword_validator_factory.hpp
new file mode 100644
index 0000000..f538105
--- /dev/null
+++ b/include/jsoncons_ext/jsonschema/keyword_validator_factory.hpp
@@ -0,0 +1,556 @@
+// Copyright 2020 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_JSONSCHEMA_KEYWORD_VALIDATOR_FACTORY_HPP
+#define JSONCONS_JSONSCHEMA_KEYWORD_VALIDATOR_FACTORY_HPP
+
+#include <jsoncons/config/jsoncons_config.hpp>
+#include <jsoncons/uri.hpp>
+#include <jsoncons/json.hpp>
+#include <jsoncons_ext/jsonpointer/jsonpointer.hpp>
+#include <jsoncons_ext/jsonschema/subschema.hpp>
+#include <jsoncons_ext/jsonschema/keyword_validator.hpp>
+#include <jsoncons_ext/jsonschema/schema_draft7.hpp>
+#include <jsoncons_ext/jsonschema/schema_version.hpp>
+#include <cassert>
+#include <set>
+#include <sstream>
+#include <iostream>
+#include <cassert>
+#if defined(JSONCONS_HAS_STD_REGEX)
+#include <regex>
+#endif
+
+namespace jsoncons {
+namespace jsonschema {
+
+ template <class Json>
+ using uri_resolver = std::function<Json(const jsoncons::uri & /*id*/)>;
+
+ template <class Json>
+ class reference_schema : public keyword_validator<Json>
+ {
+ using validator_pointer = typename keyword_validator<Json>::self_pointer;
+
+ validator_pointer referred_schema_;
+
+ public:
+ reference_schema(const std::string& id)
+ : keyword_validator<Json>(id), referred_schema_(nullptr) {}
+
+ void set_referred_schema(validator_pointer target) { referred_schema_ = target; }
+
+ private:
+
+ void do_validate(const Json& instance,
+ const jsonpointer::json_pointer& instance_location,
+ error_reporter& reporter,
+ Json& patch) const override
+ {
+ if (!referred_schema_)
+ {
+ reporter.error(validation_output("",
+ this->absolute_keyword_location(),
+ instance_location.to_uri_fragment(),
+ "Unresolved schema reference " + this->absolute_keyword_location()));
+ return;
+ }
+
+ referred_schema_->validate(instance, instance_location, reporter, patch);
+ }
+
+ jsoncons::optional<Json> get_default_value(const jsonpointer::json_pointer& instance_location,
+ const Json& instance,
+ error_reporter& reporter) const override
+ {
+ if (!referred_schema_)
+ {
+ reporter.error(validation_output("",
+ this->absolute_keyword_location(),
+ instance_location.to_uri_fragment(),
+ "Unresolved schema reference " + this->absolute_keyword_location()));
+ return jsoncons::optional<Json>();
+ }
+
+ return referred_schema_->get_default_value(instance_location, instance, reporter);
+ }
+ };
+
+ template <class Json>
+ class keyword_validator_factory;
+
+ template <class Json>
+ class json_schema
+ {
+ using validator_pointer = typename keyword_validator<Json>::self_pointer;
+
+ friend class keyword_validator_factory<Json>;
+
+ std::vector<std::unique_ptr<keyword_validator<Json>>> subschemas_;
+ validator_pointer root_;
+ public:
+ json_schema(std::vector<std::unique_ptr<keyword_validator<Json>>>&& subschemas,
+ validator_pointer root)
+ : subschemas_(std::move(subschemas)), root_(root)
+ {
+ if (root_ == nullptr)
+ JSONCONS_THROW(schema_error("There is no root schema to validate an instance against"));
+ }
+
+ json_schema(const json_schema&) = delete;
+ json_schema(json_schema&&) = default;
+ json_schema& operator=(const json_schema&) = delete;
+ json_schema& operator=(json_schema&&) = default;
+
+ void validate(const Json& instance,
+ const jsonpointer::json_pointer& instance_location,
+ error_reporter& reporter,
+ Json& patch) const
+ {
+ JSONCONS_ASSERT(root_ != nullptr);
+ root_->validate(instance, instance_location, reporter, patch);
+ }
+ };
+
+ template <class Json>
+ struct default_uri_resolver
+ {
+ Json operator()(const jsoncons::uri& uri)
+ {
+ if (uri.path() == "/draft-07/schema")
+ {
+ return jsoncons::jsonschema::schema_draft7<Json>::get_schema();
+ }
+
+ JSONCONS_THROW(jsonschema::schema_error("Don't know how to load JSON Schema " + std::string(uri.base())));
+ }
+ };
+
+ template <class Json>
+ class keyword_validator_factory : public abstract_keyword_validator_factory<Json>
+ {
+ using validator_pointer = typename keyword_validator<Json>::self_pointer;
+
+ struct subschema_registry
+ {
+ std::map<std::string, validator_pointer> schemas; // schemas
+ std::map<std::string, reference_schema<Json>*> unresolved; // unresolved references
+ std::map<std::string, Json> unprocessed_keywords;
+ };
+
+ uri_resolver<Json> resolver_;
+ validator_pointer root_;
+
+ // Owns all schemas
+ std::vector<std::unique_ptr<keyword_validator<Json>>> subschemas_;
+
+ // Map location to subschema_registry
+ std::map<std::string, subschema_registry> subschema_registries_;
+
+ public:
+ keyword_validator_factory(uri_resolver<Json>&& resolver) noexcept
+
+ : resolver_(std::move(resolver))
+ {
+ }
+
+ keyword_validator_factory(const keyword_validator_factory&) = delete;
+ keyword_validator_factory& operator=(const keyword_validator_factory&) = delete;
+ keyword_validator_factory(keyword_validator_factory&&) = default;
+ keyword_validator_factory& operator=(keyword_validator_factory&&) = default;
+
+ std::shared_ptr<json_schema<Json>> get_schema()
+ {
+ return std::make_shared<json_schema<Json>>(std::move(subschemas_), root_);
+ }
+
+ validator_pointer make_required_validator(const std::vector<schema_location>& uris,
+ const std::vector<std::string>& r) override
+ {
+ auto sch_orig = jsoncons::make_unique<required_validator<Json>>(uris, r);
+ auto sch = sch_orig.get();
+ subschemas_.emplace_back(std::move(sch_orig));
+ return sch;
+ }
+
+ validator_pointer make_null_validator(const std::vector<schema_location>& uris) override
+ {
+ auto sch_orig = jsoncons::make_unique<null_validator<Json>>(uris);
+ auto sch = sch_orig.get();
+ subschemas_.emplace_back(std::move(sch_orig));
+ return sch;
+ }
+
+ validator_pointer make_true_validator(const std::vector<schema_location>& uris) override
+ {
+ auto sch_orig = jsoncons::make_unique<true_validator<Json>>(uris);
+ auto sch = sch_orig.get();
+ subschemas_.emplace_back(std::move(sch_orig));
+ return sch;
+ }
+
+ validator_pointer make_false_validator(const std::vector<schema_location>& uris) override
+ {
+ auto sch_orig = jsoncons::make_unique<false_validator<Json>>(uris);
+ auto sch = sch_orig.get();
+ subschemas_.emplace_back(std::move(sch_orig));
+ return sch;
+ }
+
+ validator_pointer make_object_validator(const Json& schema,
+ const std::vector<schema_location>& uris) override
+ {
+ auto sch_orig = jsoncons::make_unique<object_validator<Json>>(this, schema, uris);
+ auto sch = sch_orig.get();
+ subschemas_.emplace_back(std::move(sch_orig));
+ return sch;
+ }
+
+ validator_pointer make_array_validator(const Json& schema,
+ const std::vector<schema_location>& uris) override
+ {
+ auto sch_orig = jsoncons::make_unique<array_validator<Json>>(this, schema, uris);
+ auto sch = sch_orig.get();
+ subschemas_.emplace_back(std::move(sch_orig));
+ return sch;
+ }
+
+ validator_pointer make_string_validator(const Json& schema,
+ const std::vector<schema_location>& uris) override
+ {
+ auto sch_orig = jsoncons::make_unique<string_validator<Json>>(schema, uris);
+ auto sch = sch_orig.get();
+ subschemas_.emplace_back(std::move(sch_orig));
+ return sch;
+ }
+
+ validator_pointer make_boolean_validator(const std::vector<schema_location>& uris) override
+ {
+ auto sch_orig = jsoncons::make_unique<boolean_validator<Json>>(uris);
+ auto sch = sch_orig.get();
+ subschemas_.emplace_back(std::move(sch_orig));
+ return sch;
+ }
+
+ validator_pointer make_integer_validator(const Json& schema,
+ const std::vector<schema_location>& uris,
+ std::set<std::string>& keywords) override
+ {
+ auto sch_orig = jsoncons::make_unique<integer_validator<Json>>(schema, uris, keywords);
+ auto sch = sch_orig.get();
+ subschemas_.emplace_back(std::move(sch_orig));
+ return sch;
+ }
+
+ validator_pointer make_number_validator(const Json& schema,
+ const std::vector<schema_location>& uris,
+ std::set<std::string>& keywords) override
+ {
+ auto sch_orig = jsoncons::make_unique<number_validator<Json>>(schema, uris, keywords);
+ auto sch = sch_orig.get();
+ subschemas_.emplace_back(std::move(sch_orig));
+ return sch;
+ }
+
+ validator_pointer make_not_validator(const Json& schema,
+ const std::vector<schema_location>& uris) override
+ {
+ auto sch_orig = jsoncons::make_unique<not_validator<Json>>(this, schema, uris);
+ auto sch = sch_orig.get();
+ subschemas_.emplace_back(std::move(sch_orig));
+ return sch;
+ }
+
+ validator_pointer make_all_of_validator(const Json& schema,
+ const std::vector<schema_location>& uris) override
+ {
+ auto sch_orig = jsoncons::make_unique<combining_validator<Json,all_of_criterion<Json>>>(this, schema, uris);
+ auto sch = sch_orig.get();
+ subschemas_.emplace_back(std::move(sch_orig));
+ return sch;
+ }
+
+ validator_pointer make_any_of_validator(const Json& schema,
+ const std::vector<schema_location>& uris) override
+ {
+ auto sch_orig = jsoncons::make_unique<combining_validator<Json,any_of_criterion<Json>>>(this, schema, uris);
+ auto sch = sch_orig.get();
+ subschemas_.emplace_back(std::move(sch_orig));
+ return sch;
+ }
+
+ validator_pointer make_one_of_validator(const Json& schema,
+ const std::vector<schema_location>& uris) override
+ {
+ auto sch_orig = jsoncons::make_unique<combining_validator<Json,one_of_criterion<Json>>>(this, schema, uris);
+ auto sch = sch_orig.get();
+ subschemas_.emplace_back(std::move(sch_orig));
+ return sch;
+ }
+
+ validator_pointer make_type_validator(const Json& schema,
+ const std::vector<schema_location>& uris) override
+ {
+ auto sch_orig = jsoncons::make_unique<type_validator<Json>>(this, schema, uris);
+ auto sch = sch_orig.get();
+ subschemas_.emplace_back(std::move(sch_orig));
+ return sch;
+ }
+
+ validator_pointer make_keyword_validator(const Json& schema,
+ const std::vector<schema_location>& uris,
+ const std::vector<std::string>& keys) override
+ {
+ std::vector<schema_location> new_uris = update_uris(schema, uris, keys);
+
+ validator_pointer sch = nullptr;
+
+ switch (schema.type())
+ {
+ case json_type::bool_value:
+ if (schema.template as<bool>())
+ {
+ sch = make_true_validator(new_uris);
+ }
+ else
+ {
+ sch = make_false_validator(new_uris);
+ }
+ break;
+ case json_type::object_value:
+ {
+ auto it = schema.find("definitions");
+ if (it != schema.object_range().end())
+ {
+ for (const auto& def : it->value().object_range())
+ make_keyword_validator(def.value(), new_uris, {"definitions", def.key()});
+ }
+
+ it = schema.find("$ref");
+ if (it != schema.object_range().end()) // this schema is a reference
+ {
+ schema_location relative(it->value().template as<std::string>());
+ schema_location id = relative.resolve(new_uris.back());
+ sch = get_or_create_reference(id);
+ }
+ else
+ {
+ sch = make_type_validator(schema, new_uris);
+ }
+ break;
+ }
+ default:
+ JSONCONS_THROW(schema_error("invalid JSON-type for a schema for " + new_uris[0].string() + ", expected: boolean or object"));
+ break;
+ }
+
+ for (const auto& uri : new_uris)
+ {
+ insert(uri, sch);
+
+ if (schema.type() == json_type::object_value)
+ {
+ for (const auto& item : schema.object_range())
+ insert_unknown_keyword(uri, item.key(), item.value()); // save unknown keywords for later reference
+ }
+ }
+ return sch;
+ }
+
+ void load_root(const Json& sch)
+ {
+ if (sch.is_object())
+ {
+ auto it = sch.find("$schema");
+ if (it != sch.object_range().end())
+ {
+ auto sv = it->value().as_string_view();
+ if (!schema_version::contains(sv))
+ {
+ std::string message("Unsupported schema version ");
+ message.append(sv.data(), sv.size());
+ JSONCONS_THROW(schema_error(message));
+ }
+ }
+ }
+ load(sch);
+ }
+
+ void load(const Json& sch)
+ {
+ subschema_registries_.clear();
+ root_ = make_keyword_validator(sch, {{"#"}}, {});
+
+ // load all external schemas that have not already been loaded
+
+ std::size_t loaded_count = 0;
+ do
+ {
+ loaded_count = 0;
+
+ std::vector<std::string> locations;
+ for (const auto& item : subschema_registries_)
+ locations.push_back(item.first);
+
+ for (const auto& loc : locations)
+ {
+ if (subschema_registries_[loc].schemas.empty()) // registry for this file is empty
+ {
+ if (resolver_)
+ {
+ Json external_schema = resolver_(loc);
+ make_keyword_validator(external_schema, {{loc}}, {});
+ ++loaded_count;
+ }
+ else
+ {
+ JSONCONS_THROW(schema_error("External schema reference '" + loc + "' needs to be loaded, but no resolver provided"));
+ }
+ }
+ }
+ }
+ while (loaded_count > 0);
+
+ for (const auto &file : subschema_registries_)
+ {
+ if (!file.second.unresolved.empty())
+ {
+ JSONCONS_THROW(schema_error("after all files have been parsed, '" +
+ (file.first == "" ? "<root>" : file.first) +
+ "' has still undefined references."));
+ }
+ }
+ }
+
+ private:
+
+ void insert(const schema_location& uri, validator_pointer s)
+ {
+ auto& file = get_or_create_file(std::string(uri.base()));
+ auto schemas_it = file.schemas.find(std::string(uri.fragment()));
+ if (schemas_it != file.schemas.end())
+ {
+ JSONCONS_THROW(schema_error("schema with " + uri.string() + " already inserted"));
+ return;
+ }
+
+ file.schemas.insert({std::string(uri.fragment()), s});
+
+ // is there an unresolved reference to this newly inserted schema?
+ auto unresolved_it = file.unresolved.find(std::string(uri.fragment()));
+ if (unresolved_it != file.unresolved.end())
+ {
+ unresolved_it->second->set_referred_schema(s);
+ file.unresolved.erase(unresolved_it);
+
+ }
+ }
+
+ void insert_unknown_keyword(const schema_location& uri,
+ const std::string& key,
+ const Json& value)
+ {
+ auto &file = get_or_create_file(std::string(uri.base()));
+ auto new_u = uri.append(key);
+ schema_location new_uri(new_u);
+
+ if (new_uri.has_fragment() && !new_uri.has_identifier())
+ {
+ auto fragment = std::string(new_uri.fragment());
+ // is there a reference looking for this unknown-keyword, which is thus no longer a unknown keyword but a schema
+ auto unresolved = file.unresolved.find(fragment);
+ if (unresolved != file.unresolved.end())
+ make_keyword_validator(value, {{new_uri}}, {});
+ else // no, nothing ref'd it, keep for later
+ file.unprocessed_keywords[fragment] = value;
+
+ // recursively add possible subschemas of unknown keywords
+ if (value.type() == json_type::object_value)
+ for (const auto& subsch : value.object_range())
+ {
+ insert_unknown_keyword(new_uri, subsch.key(), subsch.value());
+ }
+ }
+ }
+
+ validator_pointer get_or_create_reference(const schema_location& uri)
+ {
+ auto &file = get_or_create_file(std::string(uri.base()));
+
+ // a schema already exists
+ auto sch = file.schemas.find(std::string(uri.fragment()));
+ if (sch != file.schemas.end())
+ return sch->second;
+
+ // referencing an unknown keyword, turn it into schema
+ //
+ // an unknown keyword can only be referenced by a JSONPointer,
+ // not by a plain name identifier
+ if (uri.has_fragment() && !uri.has_identifier())
+ {
+ std::string fragment = std::string(uri.fragment());
+ auto unprocessed_keywords_it = file.unprocessed_keywords.find(fragment);
+ if (unprocessed_keywords_it != file.unprocessed_keywords.end())
+ {
+ auto &subsch = unprocessed_keywords_it->second;
+ auto s = make_keyword_validator(subsch, {{uri}}, {}); // A JSON Schema MUST be an object or a boolean.
+ file.unprocessed_keywords.erase(unprocessed_keywords_it);
+ return s;
+ }
+ }
+
+ // get or create a reference_schema
+ auto ref = file.unresolved.find(std::string(uri.fragment()));
+ if (ref != file.unresolved.end())
+ {
+ return ref->second; // unresolved, use existing reference
+ }
+ else
+ {
+ auto orig = jsoncons::make_unique<reference_schema<Json>>(uri.string());
+ auto p = file.unresolved.insert(ref,
+ {std::string(uri.fragment()), orig.get()})
+ ->second; // unresolved, create new reference
+
+ subschemas_.emplace_back(std::move(orig));
+ return p;
+ }
+ }
+
+ subschema_registry& get_or_create_file(const std::string& loc)
+ {
+ auto file = subschema_registries_.find(loc);
+ if (file != subschema_registries_.end())
+ return file->second;
+ else
+ return subschema_registries_.insert(file, {loc, {}})->second;
+ }
+
+ };
+
+ template <class Json>
+ std::shared_ptr<json_schema<Json>> make_schema(const Json& schema)
+ {
+ keyword_validator_factory<Json> loader{default_uri_resolver<Json>()};
+ loader.load_root(schema);
+
+ return loader.get_schema();
+ }
+
+ template <class Json,class URIResolver>
+ typename std::enable_if<type_traits::is_unary_function_object_exact<URIResolver,Json,std::string>::value,std::shared_ptr<json_schema<Json>>>::type
+ make_schema(const Json& schema, const URIResolver& resolver)
+ {
+ keyword_validator_factory<Json> loader(resolver);
+ loader.load_root(schema);
+
+ return loader.get_schema();
+ }
+
+} // namespace jsonschema
+} // namespace jsoncons
+
+#endif // JSONCONS_JSONSCHEMA_SCHEMA_LOADER_HPP
diff --git a/include/jsoncons_ext/jsonschema/schema_draft7.hpp b/include/jsoncons_ext/jsonschema/schema_draft7.hpp
new file mode 100644
index 0000000..c6f6fbc
--- /dev/null
+++ b/include/jsoncons_ext/jsonschema/schema_draft7.hpp
@@ -0,0 +1,198 @@
+// Copyright 2020 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_JSONSCHEMA_SCHEMA_DRAFT7_HPP
+#define JSONCONS_JSONSCHEMA_SCHEMA_DRAFT7_HPP
+
+#include <jsoncons/json.hpp>
+
+namespace jsoncons {
+namespace jsonschema {
+
+ template <class Json>
+ struct schema_draft7
+ {
+ static Json get_schema()
+ {
+ static Json schema = Json::parse(R"(
+ {
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$id": "http://json-schema.org/draft-07/schema#",
+ "title": "Core schema meta-schema",
+ "definitions": {
+ "schemaArray": {
+ "type": "array",
+ "minItems": 1,
+ "items": { "$ref": "#" }
+ },
+ "nonNegativeInteger": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "nonNegativeIntegerDefault0": {
+ "allOf": [
+ { "$ref": "#/definitions/nonNegativeInteger" },
+ { "default": 0 }
+ ]
+ },
+ "simpleTypes": {
+ "enum": [
+ "array",
+ "boolean",
+ "integer",
+ "null",
+ "number",
+ "object",
+ "string"
+ ]
+ },
+ "stringArray": {
+ "type": "array",
+ "items": { "type": "string" },
+ "uniqueItems": true,
+ "default": []
+ }
+ },
+ "type": ["object", "boolean"],
+ "properties": {
+ "$id": {
+ "type": "string",
+ "format": "uri-reference"
+ },
+ "$schema": {
+ "type": "string",
+ "format": "uri"
+ },
+ "$ref": {
+ "type": "string",
+ "format": "uri-reference"
+ },
+ "$comment": {
+ "type": "string"
+ },
+ "title": {
+ "type": "string"
+ },
+ "description": {
+ "type": "string"
+ },
+ "default": true,
+ "readOnly": {
+ "type": "boolean",
+ "default": false
+ },
+ "examples": {
+ "type": "array",
+ "items": true
+ },
+ "multipleOf": {
+ "type": "number",
+ "exclusiveMinimum": 0
+ },
+ "maximum": {
+ "type": "number"
+ },
+ "exclusiveMaximum": {
+ "type": "number"
+ },
+ "minimum": {
+ "type": "number"
+ },
+ "exclusiveMinimum": {
+ "type": "number"
+ },
+ "maxLength": { "$ref": "#/definitions/nonNegativeInteger" },
+ "minLength": { "$ref": "#/definitions/nonNegativeIntegerDefault0" },
+ "pattern": {
+ "type": "string",
+ "format": "regex"
+ },
+ "additionalItems": { "$ref": "#" },
+ "items": {
+ "anyOf": [
+ { "$ref": "#" },
+ { "$ref": "#/definitions/schemaArray" }
+ ],
+ "default": true
+ },
+ "maxItems": { "$ref": "#/definitions/nonNegativeInteger" },
+ "minItems": { "$ref": "#/definitions/nonNegativeIntegerDefault0" },
+ "uniqueItems": {
+ "type": "boolean",
+ "default": false
+ },
+ "contains": { "$ref": "#" },
+ "maxProperties": { "$ref": "#/definitions/nonNegativeInteger" },
+ "minProperties": { "$ref": "#/definitions/nonNegativeIntegerDefault0" },
+ "required": { "$ref": "#/definitions/stringArray" },
+ "additionalProperties": { "$ref": "#" },
+ "definitions": {
+ "type": "object",
+ "additionalProperties": { "$ref": "#" },
+ "default": {}
+ },
+ "properties": {
+ "type": "object",
+ "additionalProperties": { "$ref": "#" },
+ "default": {}
+ },
+ "patternProperties": {
+ "type": "object",
+ "additionalProperties": { "$ref": "#" },
+ "propertyNames": { "format": "regex" },
+ "default": {}
+ },
+ "dependencies": {
+ "type": "object",
+ "additionalProperties": {
+ "anyOf": [
+ { "$ref": "#" },
+ { "$ref": "#/definitions/stringArray" }
+ ]
+ }
+ },
+ "propertyNames": { "$ref": "#" },
+ "const": true,
+ "enum": {
+ "type": "array",
+ "items": true,
+ "minItems": 1,
+ "uniqueItems": true
+ },
+ "type": {
+ "anyOf": [
+ { "$ref": "#/definitions/simpleTypes" },
+ {
+ "type": "array",
+ "items": { "$ref": "#/definitions/simpleTypes" },
+ "minItems": 1,
+ "uniqueItems": true
+ }
+ ]
+ },
+ "format": { "type": "string" },
+ "contentMediaType": { "type": "string" },
+ "contentEncoding": { "type": "string" },
+ "if": { "$ref": "#" },
+ "then": { "$ref": "#" },
+ "else": { "$ref": "#" },
+ "allOf": { "$ref": "#/definitions/schemaArray" },
+ "anyOf": { "$ref": "#/definitions/schemaArray" },
+ "oneOf": { "$ref": "#/definitions/schemaArray" },
+ "not": { "$ref": "#" }
+ },
+ "default": true
+ }
+ )");
+
+ return schema;
+ }
+ };
+
+} // namespace jsonschema
+} // namespace jsoncons
+
+#endif // JSONCONS_JSONSCHEMA_SCHEMA_DRAFT7_HPP
diff --git a/include/jsoncons_ext/jsonschema/schema_location.hpp b/include/jsoncons_ext/jsonschema/schema_location.hpp
new file mode 100644
index 0000000..fd1a743
--- /dev/null
+++ b/include/jsoncons_ext/jsonschema/schema_location.hpp
@@ -0,0 +1,200 @@
+// Copyright 2020 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_JSONSCHEMA_SCHEMA_LOCATION_HPP
+#define JSONCONS_JSONSCHEMA_SCHEMA_LOCATION_HPP
+
+#include <jsoncons/config/jsoncons_config.hpp>
+#include <jsoncons/uri.hpp>
+#include <jsoncons/json.hpp>
+#include <jsoncons_ext/jsonpointer/jsonpointer.hpp>
+#include <jsoncons_ext/jsonschema/jsonschema_error.hpp>
+
+namespace jsoncons {
+namespace jsonschema {
+
+ class schema_location
+ {
+ jsoncons::uri uri_;
+ std::string identifier_;
+ public:
+ schema_location()
+ {
+ }
+
+ schema_location(const std::string& uri)
+ {
+ auto pos = uri.find('#');
+ if (pos != std::string::npos)
+ {
+ identifier_ = uri.substr(pos + 1);
+ unescape_percent(identifier_);
+ }
+ uri_ = jsoncons::uri(uri);
+ }
+
+ jsoncons::uri uri() const
+ {
+ return uri_;
+ }
+
+ bool has_fragment() const
+ {
+ return !identifier_.empty();
+ }
+
+ bool has_identifier() const
+ {
+ return !identifier_.empty() && identifier_.front() != '/';
+ }
+
+ jsoncons::string_view base() const
+ {
+ return uri_.base();
+ }
+
+ jsoncons::string_view path() const
+ {
+ return uri_.path();
+ }
+
+ bool is_absolute() const
+ {
+ return uri_.is_absolute();
+ }
+
+ std::string identifier() const
+ {
+ return identifier_;
+ }
+
+ std::string fragment() const
+ {
+ return identifier_;
+ }
+
+ schema_location resolve(const schema_location& uri) const
+ {
+ schema_location new_uri;
+ new_uri.identifier_ = identifier_;
+ new_uri.uri_ = uri_.resolve(uri.uri_);
+ return new_uri;
+ }
+
+ int compare(const schema_location& other) const
+ {
+ int result = uri_.compare(other.uri_);
+ if (result != 0)
+ {
+ return result;
+ }
+ return result;
+ }
+
+ schema_location append(const std::string& field) const
+ {
+ if (has_identifier())
+ return *this;
+
+ jsoncons::jsonpointer::json_pointer pointer(std::string(uri_.fragment()));
+ pointer /= field;
+
+ jsoncons::uri new_uri(uri_.scheme(),
+ uri_.userinfo(),
+ uri_.host(),
+ uri_.port(),
+ uri_.path(),
+ uri_.query(),
+ pointer.to_string());
+
+ schema_location wrapper;
+ wrapper.uri_ = new_uri;
+ wrapper.identifier_ = pointer.to_string();
+
+ return wrapper;
+ }
+
+ schema_location append(std::size_t index) const
+ {
+ if (has_identifier())
+ return *this;
+
+ jsoncons::jsonpointer::json_pointer pointer(std::string(uri_.fragment()));
+ pointer /= index;
+
+ jsoncons::uri new_uri(uri_.scheme(),
+ uri_.userinfo(),
+ uri_.host(),
+ uri_.port(),
+ uri_.path(),
+ uri_.query(),
+ pointer.to_string());
+
+ schema_location wrapper;
+ wrapper.uri_ = new_uri;
+ wrapper.identifier_ = pointer.to_string();
+
+ return wrapper;
+ }
+
+ std::string string() const
+ {
+ std::string s = uri_.string();
+ return s;
+ }
+
+ friend bool operator==(const schema_location& lhs, const schema_location& rhs)
+ {
+ return lhs.compare(rhs) == 0;
+ }
+
+ friend bool operator!=(const schema_location& lhs, const schema_location& rhs)
+ {
+ return lhs.compare(rhs) != 0;
+ }
+
+ friend bool operator<(const schema_location& lhs, const schema_location& rhs)
+ {
+ return lhs.compare(rhs) < 0;
+ }
+
+ friend bool operator<=(const schema_location& lhs, const schema_location& rhs)
+ {
+ return lhs.compare(rhs) <= 0;
+ }
+
+ friend bool operator>(const schema_location& lhs, const schema_location& rhs)
+ {
+ return lhs.compare(rhs) > 0;
+ }
+
+ friend bool operator>=(const schema_location& lhs, const schema_location& rhs)
+ {
+ return lhs.compare(rhs) >= 0;
+ }
+ private:
+ static void unescape_percent(std::string& s)
+ {
+ if (s.size() >= 3)
+ {
+ std::size_t pos = s.size() - 2;
+ while (pos-- >= 1)
+ {
+ if (s[pos] == '%')
+ {
+ std::string hex = s.substr(pos + 1, 2);
+ char ch = (char) std::strtoul(hex.c_str(), nullptr, 16);
+ s.replace(pos, 3, 1, ch);
+ }
+ }
+ }
+ }
+ };
+
+} // namespace jsonschema
+} // namespace jsoncons
+
+#endif // JSONCONS_JSONSCHEMA_RULE_HPP
diff --git a/include/jsoncons_ext/jsonschema/schema_version.hpp b/include/jsoncons_ext/jsonschema/schema_version.hpp
new file mode 100644
index 0000000..b804712
--- /dev/null
+++ b/include/jsoncons_ext/jsonschema/schema_version.hpp
@@ -0,0 +1,35 @@
+// Copyright 2021 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_JSONSCHEMA_SCHEMA_VERSION_HPP
+#define JSONCONS_JSONSCHEMA_SCHEMA_VERSION_HPP
+
+#include <jsoncons/json.hpp>
+
+namespace jsoncons {
+namespace jsonschema {
+
+ class schema_version
+ {
+ public:
+ static bool contains(const string_view& url)
+ {
+ if (url.find("json-schema.org/draft-07/schema#") != string_view::npos)
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ };
+
+
+} // namespace jsonschema
+} // namespace jsoncons
+
+#endif // JSONCONS_JSONSCHEMA_JSONSCHEMA_VERSION_HPP
diff --git a/include/jsoncons_ext/jsonschema/subschema.hpp b/include/jsoncons_ext/jsonschema/subschema.hpp
new file mode 100644
index 0000000..cbe0af4
--- /dev/null
+++ b/include/jsoncons_ext/jsonschema/subschema.hpp
@@ -0,0 +1,144 @@
+// Copyright 2020 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_JSONSCHEMA_SUBSCHEMA_HPP
+#define JSONCONS_JSONSCHEMA_SUBSCHEMA_HPP
+
+#include <jsoncons/config/jsoncons_config.hpp>
+#include <jsoncons/uri.hpp>
+#include <jsoncons/json.hpp>
+#include <jsoncons_ext/jsonpointer/jsonpointer.hpp>
+#include <jsoncons_ext/jsonschema/jsonschema_error.hpp>
+#include <jsoncons_ext/jsonschema/schema_location.hpp>
+
+namespace jsoncons {
+namespace jsonschema {
+
+ // Interface for validation error handlers
+ class error_reporter
+ {
+ bool fail_early_;
+ std::size_t error_count_;
+ public:
+ error_reporter(bool fail_early = false)
+ : fail_early_(fail_early), error_count_(0)
+ {
+ }
+
+ virtual ~error_reporter() = default;
+
+ void error(const validation_output& o)
+ {
+ ++error_count_;
+ do_error(o);
+ }
+
+ std::size_t error_count() const
+ {
+ return error_count_;
+ }
+
+ bool fail_early() const
+ {
+ return fail_early_;
+ }
+
+ private:
+ virtual void do_error(const validation_output& /* e */) = 0;
+ };
+
+ template <class Json>
+ class keyword_validator
+ {
+ std::string absolute_keyword_location_;
+ public:
+ using self_pointer = keyword_validator<Json>*;
+
+ keyword_validator(const std::string& absolute_keyword_location)
+ : absolute_keyword_location_(absolute_keyword_location)
+ {
+ }
+
+ keyword_validator(const keyword_validator&) = delete;
+ keyword_validator(keyword_validator&&) = default;
+ keyword_validator& operator=(const keyword_validator&) = delete;
+ keyword_validator& operator=(keyword_validator&&) = default;
+
+ virtual ~keyword_validator() = default;
+
+ const std::string& absolute_keyword_location() const
+ {
+ return absolute_keyword_location_;
+ }
+
+ void validate(const Json& instance,
+ const jsonpointer::json_pointer& instance_location,
+ error_reporter& reporter,
+ Json& patch) const
+ {
+ do_validate(instance,
+ instance_location,
+ reporter,
+ patch);
+ }
+
+ virtual jsoncons::optional<Json> get_default_value(const jsonpointer::json_pointer&, const Json&, error_reporter&) const
+ {
+ return jsoncons::optional<Json>();
+ }
+
+ private:
+ virtual void do_validate(const Json& instance,
+ const jsonpointer::json_pointer& instance_location,
+ error_reporter& reporter,
+ Json& patch) const = 0;
+ };
+
+ template <class Json>
+ std::vector<schema_location> update_uris(const Json& schema,
+ const std::vector<schema_location>& uris,
+ const std::vector<std::string>& keys)
+ {
+ // Exclude uri's that are not plain name identifiers
+ std::vector<schema_location> new_uris;
+ for (const auto& uri : uris)
+ {
+ if (!uri.has_identifier())
+ new_uris.push_back(uri);
+ }
+
+ // Append the keys for this sub-schema to the uri's
+ for (const auto& key : keys)
+ {
+ for (auto& uri : new_uris)
+ {
+ auto new_u = uri.append(key);
+ uri = schema_location(new_u);
+ }
+ }
+ if (schema.type() == json_type::object_value)
+ {
+ auto it = schema.find("$id"); // If $id is found, this schema can be referenced by the id
+ if (it != schema.object_range().end())
+ {
+ std::string id = it->value().template as<std::string>();
+ // Add it to the list if it is not already there
+ if (std::find(new_uris.begin(), new_uris.end(), id) == new_uris.end())
+ {
+ schema_location relative(id);
+ schema_location new_uri = relative.resolve(new_uris.back());
+ new_uris.emplace_back(new_uri);
+ }
+ }
+ }
+
+ return new_uris;
+ }
+
+} // namespace jsonschema
+} // namespace jsoncons
+
+#endif // JSONCONS_JSONSCHEMA_RULE_HPP
diff --git a/include/jsoncons_ext/mergepatch/mergepatch.hpp b/include/jsoncons_ext/mergepatch/mergepatch.hpp
new file mode 100644
index 0000000..c5fdefe
--- /dev/null
+++ b/include/jsoncons_ext/mergepatch/mergepatch.hpp
@@ -0,0 +1,103 @@
+// Copyright 2017 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_JSONMERGEPATCH_JSONMERGEPATCH_HPP
+#define JSONCONS_JSONMERGEPATCH_JSONMERGEPATCH_HPP
+
+#include <string>
+#include <vector>
+#include <memory>
+#include <algorithm> // std::min
+#include <utility> // std::move
+#include <jsoncons/json.hpp>
+
+namespace jsoncons {
+namespace mergepatch {
+
+ template <class Json>
+ Json from_diff(const Json& source, const Json& target)
+ {
+ if (!source.is_object() || !target.is_object())
+ {
+ return target;
+ }
+ Json result(json_object_arg);
+
+ for (const auto& member : source.object_range())
+ {
+ auto it = target.find(member.key());
+ if (it != target.object_range().end())
+ {
+ if (member.value() != it->value())
+ {
+ result.try_emplace(member.key(), from_diff(member.value(), it->value()));
+ }
+ }
+ else
+ {
+ result.try_emplace(member.key(), Json::null());
+ }
+ }
+
+ for (const auto& member : target.object_range())
+ {
+ auto it = source.find(member.key());
+ if (it == source.object_range().end())
+ {
+ result.try_emplace(member.key(), member.value());
+ }
+ }
+
+ return result;
+ }
+
+ namespace detail {
+ template <class Json>
+ Json apply_merge_patch_(Json& target, const Json& patch)
+ {
+ if (patch.is_object())
+ {
+ if (!target.is_object())
+ {
+ target = Json(json_object_arg);
+ }
+ for (auto& member : patch.object_range())
+ {
+ auto it = target.find(member.key());
+ if (it != target.object_range().end())
+ {
+ Json item = it->value();
+ target.erase(it);
+ if (!member.value().is_null())
+ {
+ target.try_emplace(member.key(), apply_merge_patch_(item, member.value()));
+ }
+ }
+ else if (!member.value().is_null())
+ {
+ Json item(json_object_arg);
+ target.try_emplace(member.key(), apply_merge_patch_(item, member.value()));
+ }
+ }
+ return target;
+ }
+ else
+ {
+ return patch;
+ }
+ }
+ } // namespace detail
+
+ template <class Json>
+ void apply_merge_patch(Json& target, const Json& patch)
+ {
+ target = detail::apply_merge_patch_(target, patch);
+ }
+
+} // namespace mergepatch
+} // namespace jsoncons
+
+#endif
diff --git a/include/jsoncons_ext/msgpack/decode_msgpack.hpp b/include/jsoncons_ext/msgpack/decode_msgpack.hpp
new file mode 100644
index 0000000..614af3d
--- /dev/null
+++ b/include/jsoncons_ext/msgpack/decode_msgpack.hpp
@@ -0,0 +1,202 @@
+// Copyright 2013 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_MSGPACK_DECODE_MSGPACK_HPP
+#define JSONCONS_MSGPACK_DECODE_MSGPACK_HPP
+
+#include <string>
+#include <vector>
+#include <memory>
+#include <type_traits> // std::enable_if
+#include <istream> // std::basic_istream
+#include <jsoncons/json.hpp>
+#include <jsoncons/config/jsoncons_config.hpp>
+#include <jsoncons_ext/msgpack/msgpack_encoder.hpp>
+#include <jsoncons_ext/msgpack/msgpack_reader.hpp>
+#include <jsoncons_ext/msgpack/msgpack_cursor.hpp>
+
+namespace jsoncons {
+namespace msgpack {
+
+ template<class T, class Source>
+ typename std::enable_if<type_traits::is_basic_json<T>::value &&
+ type_traits::is_byte_sequence<Source>::value,T>::type
+ decode_msgpack(const Source& v,
+ const msgpack_decode_options& options = msgpack_decode_options())
+ {
+ jsoncons::json_decoder<T> decoder;
+ auto adaptor = make_json_visitor_adaptor<json_visitor>(decoder);
+ basic_msgpack_reader<jsoncons::bytes_source> 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<class T, class Source>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value &&
+ type_traits::is_byte_sequence<Source>::value,T>::type
+ decode_msgpack(const Source& v,
+ const msgpack_decode_options& options = msgpack_decode_options())
+ {
+ basic_msgpack_cursor<bytes_source> cursor(v, options);
+ json_decoder<basic_json<char,sorted_policy>> decoder{};
+
+ std::error_code ec;
+ T val = decode_traits<T,char>::decode(cursor, decoder, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column()));
+ }
+ return val;
+ }
+
+ template<class T>
+ typename std::enable_if<type_traits::is_basic_json<T>::value,T>::type
+ decode_msgpack(std::istream& is,
+ const msgpack_decode_options& options = msgpack_decode_options())
+ {
+ jsoncons::json_decoder<T> decoder;
+ auto adaptor = make_json_visitor_adaptor<json_visitor>(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<class T>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value,T>::type
+ decode_msgpack(std::istream& is,
+ const msgpack_decode_options& options = msgpack_decode_options())
+ {
+ basic_msgpack_cursor<binary_stream_source> cursor(is, options);
+ json_decoder<basic_json<char,sorted_policy>> decoder{};
+
+ std::error_code ec;
+ T val = decode_traits<T,char>::decode(cursor, decoder, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column()));
+ }
+ return val;
+ }
+
+ template<class T, class InputIt>
+ typename std::enable_if<type_traits::is_basic_json<T>::value,T>::type
+ decode_msgpack(InputIt first, InputIt last,
+ const msgpack_decode_options& options = msgpack_decode_options())
+ {
+ jsoncons::json_decoder<T> decoder;
+ auto adaptor = make_json_visitor_adaptor<json_visitor>(decoder);
+ basic_msgpack_reader<binary_iterator_source<InputIt>> reader(binary_iterator_source<InputIt>(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<class T, class InputIt>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value,T>::type
+ decode_msgpack(InputIt first, InputIt last,
+ const msgpack_decode_options& options = msgpack_decode_options())
+ {
+ basic_msgpack_cursor<binary_iterator_source<InputIt>> cursor(binary_iterator_source<InputIt>(first, last), options);
+ json_decoder<basic_json<char,sorted_policy>> decoder{};
+
+ std::error_code ec;
+ T val = decode_traits<T,char>::decode(cursor, decoder, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column()));
+ }
+ return val;
+ }
+
+ // With leading allocator parameter
+
+ template<class T, class Source, class TempAllocator>
+ typename std::enable_if<type_traits::is_basic_json<T>::value &&
+ type_traits::is_byte_sequence<Source>::value,T>::type
+ decode_msgpack(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ const Source& v,
+ const msgpack_decode_options& options = msgpack_decode_options())
+ {
+ json_decoder<T,TempAllocator> decoder(temp_alloc);
+ auto adaptor = make_json_visitor_adaptor<json_visitor>(decoder);
+ basic_msgpack_reader<jsoncons::bytes_source,TempAllocator> reader(v, adaptor, options, temp_alloc);
+ reader.read();
+ if (!decoder.is_valid())
+ {
+ JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column()));
+ }
+ return decoder.get_result();
+ }
+
+ template<class T, class Source, class TempAllocator>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value &&
+ type_traits::is_byte_sequence<Source>::value,T>::type
+ decode_msgpack(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ const Source& v,
+ const msgpack_decode_options& options = msgpack_decode_options())
+ {
+ basic_msgpack_cursor<bytes_source,TempAllocator> cursor(v, options, temp_alloc);
+ json_decoder<basic_json<char,sorted_policy,TempAllocator>,TempAllocator> decoder(temp_alloc, temp_alloc);
+
+ std::error_code ec;
+ T val = decode_traits<T,char>::decode(cursor, decoder, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column()));
+ }
+ return val;
+ }
+
+ template<class T,class TempAllocator>
+ typename std::enable_if<type_traits::is_basic_json<T>::value,T>::type
+ decode_msgpack(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ std::istream& is,
+ const msgpack_decode_options& options = msgpack_decode_options())
+ {
+ json_decoder<T,TempAllocator> decoder(temp_alloc);
+ auto adaptor = make_json_visitor_adaptor<json_visitor>(decoder);
+ basic_msgpack_reader<jsoncons::binary_stream_source,TempAllocator> reader(is, adaptor, options, temp_alloc);
+ reader.read();
+ if (!decoder.is_valid())
+ {
+ JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column()));
+ }
+ return decoder.get_result();
+ }
+
+ template<class T,class TempAllocator>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value,T>::type
+ decode_msgpack(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ std::istream& is,
+ const msgpack_decode_options& options = msgpack_decode_options())
+ {
+ basic_msgpack_cursor<binary_stream_source,TempAllocator> cursor(is, options, temp_alloc);
+ json_decoder<basic_json<char,sorted_policy,TempAllocator>,TempAllocator> decoder(temp_alloc, temp_alloc);
+
+ std::error_code ec;
+ T val = decode_traits<T,char>::decode(cursor, decoder, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column()));
+ }
+ return val;
+ }
+
+} // msgpack
+} // jsoncons
+
+#endif
diff --git a/include/jsoncons_ext/msgpack/encode_msgpack.hpp b/include/jsoncons_ext/msgpack/encode_msgpack.hpp
new file mode 100644
index 0000000..10a61e0
--- /dev/null
+++ b/include/jsoncons_ext/msgpack/encode_msgpack.hpp
@@ -0,0 +1,142 @@
+// Copyright 2013 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_MSGPACK_ENCODE_MSGPACK_HPP
+#define JSONCONS_MSGPACK_ENCODE_MSGPACK_HPP
+
+#include <string>
+#include <vector>
+#include <memory>
+#include <type_traits> // std::enable_if
+#include <istream> // std::basic_istream
+#include <jsoncons/json.hpp>
+#include <jsoncons/config/jsoncons_config.hpp>
+#include <jsoncons_ext/msgpack/msgpack_encoder.hpp>
+#include <jsoncons_ext/msgpack/msgpack_reader.hpp>
+
+namespace jsoncons {
+namespace msgpack {
+
+ template<class T, class Container>
+ typename std::enable_if<type_traits::is_basic_json<T>::value &&
+ type_traits::is_back_insertable_byte_container<Container>::value,void>::type
+ encode_msgpack(const T& j,
+ Container& v,
+ const msgpack_encode_options& options = msgpack_encode_options())
+ {
+ using char_type = typename T::char_type;
+ basic_msgpack_encoder<jsoncons::bytes_sink<Container>> encoder(v, options);
+ auto adaptor = make_json_visitor_adaptor<basic_json_visitor<char_type>>(encoder);
+ j.dump(adaptor);
+ }
+
+ template<class T, class Container>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value &&
+ type_traits::is_back_insertable_byte_container<Container>::value,void>::type
+ encode_msgpack(const T& val,
+ Container& v,
+ const msgpack_encode_options& options = msgpack_encode_options())
+ {
+ basic_msgpack_encoder<jsoncons::bytes_sink<Container>> encoder(v, options);
+ std::error_code ec;
+ encode_traits<T,char>::encode(val, encoder, json(), ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec));
+ }
+ }
+
+ template<class T>
+ typename std::enable_if<type_traits::is_basic_json<T>::value,void>::type
+ 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<basic_json_visitor<char_type>>(encoder);
+ j.dump(adaptor);
+ }
+
+ template<class T>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value,void>::type
+ 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<T,char>::encode(val, encoder, json(), ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec));
+ }
+ }
+
+ // with temp_allocator_arg_t
+
+ template<class T, class Container, class TempAllocator>
+ typename std::enable_if<type_traits::is_basic_json<T>::value &&
+ type_traits::is_back_insertable_byte_container<Container>::value,void>::type
+ encode_msgpack(temp_allocator_arg_t, const TempAllocator& temp_alloc, const T& j,
+ Container& v,
+ const msgpack_encode_options& options = msgpack_encode_options())
+ {
+ using char_type = typename T::char_type;
+ basic_msgpack_encoder<jsoncons::bytes_sink<Container>,TempAllocator> encoder(v, options, temp_alloc);
+ auto adaptor = make_json_visitor_adaptor<basic_json_visitor<char_type>>(encoder);
+ j.dump(adaptor);
+ }
+
+ template<class T, class Container, class TempAllocator>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value &&
+ type_traits::is_back_insertable_byte_container<Container>::value,void>::type
+ encode_msgpack(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ const T& val, Container& v,
+ const msgpack_encode_options& options = msgpack_encode_options())
+ {
+ basic_msgpack_encoder<jsoncons::bytes_sink<Container>,TempAllocator> encoder(v, options, temp_alloc);
+ std::error_code ec;
+ encode_traits<T,char>::encode(val, encoder, json(), ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec));
+ }
+ }
+
+ template<class T,class TempAllocator>
+ typename std::enable_if<type_traits::is_basic_json<T>::value,void>::type
+ encode_msgpack(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ const T& j,
+ std::ostream& os,
+ const msgpack_encode_options& options = msgpack_encode_options())
+ {
+ using char_type = typename T::char_type;
+ basic_msgpack_encoder<jsoncons::binary_stream_sink,TempAllocator> encoder(os, options, temp_alloc);
+ auto adaptor = make_json_visitor_adaptor<basic_json_visitor<char_type>>(encoder);
+ j.dump(adaptor);
+ }
+
+ template<class T,class TempAllocator>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value,void>::type
+ encode_msgpack(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ const T& val,
+ std::ostream& os,
+ const msgpack_encode_options& options = msgpack_encode_options())
+ {
+ basic_msgpack_encoder<jsoncons::binary_stream_sink,TempAllocator> encoder(os, options, temp_alloc);
+ std::error_code ec;
+ encode_traits<T,char>::encode(val, encoder, json(), ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec));
+ }
+ }
+
+} // msgpack
+} // jsoncons
+
+#endif
diff --git a/include/jsoncons_ext/msgpack/msgpack.hpp b/include/jsoncons_ext/msgpack/msgpack.hpp
new file mode 100644
index 0000000..307aad2
--- /dev/null
+++ b/include/jsoncons_ext/msgpack/msgpack.hpp
@@ -0,0 +1,24 @@
+// Copyright 2013 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_MSGPACK_MSGPACK_HPP
+#define JSONCONS_MSGPACK_MSGPACK_HPP
+
+#include <string>
+#include <vector>
+#include <memory>
+#include <type_traits> // std::enable_if
+#include <istream> // std::basic_istream
+#include <jsoncons/json.hpp>
+#include <jsoncons/config/jsoncons_config.hpp>
+#include <jsoncons_ext/msgpack/msgpack_encoder.hpp>
+#include <jsoncons_ext/msgpack/msgpack_reader.hpp>
+#include <jsoncons_ext/msgpack/msgpack_cursor.hpp>
+#include <jsoncons_ext/msgpack/encode_msgpack.hpp>
+#include <jsoncons_ext/msgpack/decode_msgpack.hpp>
+
+#endif
+
diff --git a/include/jsoncons_ext/msgpack/msgpack_cursor.hpp b/include/jsoncons_ext/msgpack/msgpack_cursor.hpp
new file mode 100644
index 0000000..a813429
--- /dev/null
+++ b/include/jsoncons_ext/msgpack/msgpack_cursor.hpp
@@ -0,0 +1,343 @@
+// 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)
+
+// See https://github.com/danielaparker/jsoncons for latest version
+
+#ifndef JSONCONS_MSGPACK_MSGPACK_CURSOR_HPP
+#define JSONCONS_MSGPACK_MSGPACK_CURSOR_HPP
+
+#include <memory> // std::allocator
+#include <string>
+#include <vector>
+#include <stdexcept>
+#include <system_error>
+#include <ios>
+#include <istream> // std::basic_istream
+#include <jsoncons/byte_string.hpp>
+#include <jsoncons/config/jsoncons_config.hpp>
+#include <jsoncons/json_visitor.hpp>
+#include <jsoncons/json_exception.hpp>
+#include <jsoncons/staj_cursor.hpp>
+#include <jsoncons/source.hpp>
+#include <jsoncons_ext/msgpack/msgpack_parser.hpp>
+
+namespace jsoncons {
+namespace msgpack {
+
+template<class Source=jsoncons::binary_stream_source,class Allocator=std::allocator<char>>
+class basic_msgpack_cursor : public basic_staj_cursor<char>, private virtual ser_context
+{
+public:
+ using source_type = Source;
+ using char_type = char;
+ using allocator_type = Allocator;
+private:
+ basic_msgpack_parser<Source,Allocator> parser_;
+ basic_staj_visitor<char_type> cursor_visitor_;
+ basic_json_visitor2_to_visitor_adaptor<char_type,Allocator> cursor_handler_adaptor_;
+ bool eof_;
+
+ // Noncopyable and nonmoveable
+ basic_msgpack_cursor(const basic_msgpack_cursor&) = delete;
+ basic_msgpack_cursor& operator=(const basic_msgpack_cursor&) = delete;
+
+public:
+ using string_view_type = string_view;
+
+ template <class Sourceable>
+ basic_msgpack_cursor(Sourceable&& source,
+ const msgpack_decode_options& options = msgpack_decode_options(),
+ const Allocator& alloc = Allocator())
+ : parser_(std::forward<Sourceable>(source), options, alloc),
+ cursor_visitor_(accept_all),
+ cursor_handler_adaptor_(cursor_visitor_, alloc),
+ eof_(false)
+ {
+ if (!done())
+ {
+ next();
+ }
+ }
+
+ // Constructors that set parse error codes
+
+ template <class Sourceable>
+ basic_msgpack_cursor(Sourceable&& source,
+ std::error_code& ec)
+ : basic_msgpack_cursor(std::allocator_arg, Allocator(),
+ std::forward<Sourceable>(source),
+ msgpack_decode_options(),
+ ec)
+ {
+ }
+
+ template <class Sourceable>
+ basic_msgpack_cursor(Sourceable&& source,
+ const msgpack_decode_options& options,
+ std::error_code& ec)
+ : basic_msgpack_cursor(std::allocator_arg, Allocator(),
+ std::forward<Sourceable>(source),
+ options,
+ ec)
+ {
+ }
+
+ template <class Sourceable>
+ basic_msgpack_cursor(std::allocator_arg_t, const Allocator& alloc,
+ Sourceable&& source,
+ const msgpack_decode_options& options,
+ std::error_code& ec)
+ : parser_(std::forward<Sourceable>(source), options, alloc),
+ cursor_visitor_(accept_all),
+ cursor_handler_adaptor_(cursor_visitor_, alloc),
+ eof_(false)
+ {
+ if (!done())
+ {
+ next(ec);
+ }
+ }
+
+ void reset()
+ {
+ parser_.reset();
+ cursor_visitor_.reset();
+ cursor_handler_adaptor_.reset();
+ eof_ = false;
+ if (!done())
+ {
+ next();
+ }
+ }
+
+ template <class Sourceable>
+ void reset(Sourceable&& source)
+ {
+ parser_.reset(std::forward<Sourceable>(source));
+ cursor_visitor_.reset();
+ cursor_handler_adaptor_.reset();
+ eof_ = false;
+ if (!done())
+ {
+ next();
+ }
+ }
+
+ void reset(std::error_code& ec)
+ {
+ parser_.reset();
+ cursor_visitor_.reset();
+ cursor_handler_adaptor_.reset();
+ eof_ = false;
+ if (!done())
+ {
+ next(ec);
+ }
+ }
+
+ template <class Sourceable>
+ void reset(Sourceable&& source, std::error_code& ec)
+ {
+ parser_.reset(std::forward<Sourceable>(source));
+ cursor_visitor_.reset();
+ cursor_handler_adaptor_.reset();
+ eof_ = false;
+ if (!done())
+ {
+ next(ec);
+ }
+ }
+
+ bool done() const override
+ {
+ return parser_.done();
+ }
+
+ const staj_event& current() const override
+ {
+ return cursor_visitor_.event();
+ }
+
+ void read_to(basic_json_visitor<char_type>& visitor) override
+ {
+ std::error_code ec;
+ read_to(visitor, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column()));
+ }
+ }
+
+ void read_to(basic_json_visitor<char_type>& visitor,
+ std::error_code& ec) override
+ {
+ if (cursor_visitor_.dump(visitor, *this, ec))
+ {
+ read_next(visitor, ec);
+ }
+ }
+
+ void next() override
+ {
+ std::error_code ec;
+ next(ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column()));
+ }
+ }
+
+ void next(std::error_code& ec) override
+ {
+ read_next(ec);
+ }
+
+ const ser_context& context() const override
+ {
+ return *this;
+ }
+
+ bool eof() const
+ {
+ return eof_;
+ }
+
+ std::size_t line() const override
+ {
+ return parser_.line();
+ }
+
+ std::size_t column() const override
+ {
+ return parser_.column();
+ }
+
+ friend
+ staj_filter_view operator|(basic_msgpack_cursor& cursor,
+ std::function<bool(const staj_event&, const ser_context&)> pred)
+ {
+ return staj_filter_view(cursor, pred);
+ }
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+
+ template <class Sourceable>
+ JSONCONS_DEPRECATED_MSG("Instead, use pipe syntax for filter")
+ basic_msgpack_cursor(Sourceable&& source,
+ std::function<bool(const staj_event&, const ser_context&)> filter,
+ const msgpack_decode_options& options = msgpack_decode_options(),
+ const Allocator& alloc = Allocator())
+ : parser_(std::forward<Sourceable>(source), options, alloc),
+ cursor_visitor_(filter),
+ cursor_handler_adaptor_(cursor_visitor_, alloc),
+ eof_(false)
+ {
+ if (!done())
+ {
+ next();
+ }
+ }
+
+ template <class Sourceable>
+ JSONCONS_DEPRECATED_MSG("Instead, use pipe syntax for filter")
+ basic_msgpack_cursor(Sourceable&& source,
+ std::function<bool(const staj_event&, const ser_context&)> filter,
+ std::error_code& ec)
+ : basic_msgpack_cursor(std::allocator_arg, Allocator(),
+ std::forward<Sourceable>(source), filter, ec)
+ {
+ }
+
+ template <class Sourceable>
+ JSONCONS_DEPRECATED_MSG("Instead, use pipe syntax for filter")
+ basic_msgpack_cursor(std::allocator_arg_t, const Allocator& alloc,
+ Sourceable&& source,
+ std::function<bool(const staj_event&, const ser_context&)> filter,
+ std::error_code& ec)
+ : parser_(std::forward<Sourceable>(source), alloc),
+ cursor_visitor_(filter),
+ cursor_handler_adaptor_(cursor_visitor_, alloc),
+ eof_(false)
+ {
+ if (!done())
+ {
+ next(ec);
+ }
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use read_to(basic_json_visitor<char_type>&)")
+ void read(basic_json_visitor<char_type>& visitor)
+ {
+ read_to(visitor);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use read_to(basic_json_visitor<char_type>&, std::error_code&)")
+ void read(basic_json_visitor<char_type>& visitor,
+ std::error_code& ec)
+ {
+ read_to(visitor, ec);
+ }
+#endif
+private:
+ static bool accept_all(const staj_event&, const ser_context&)
+ {
+ return true;
+ }
+
+ void read_next(std::error_code& ec)
+ {
+ if (cursor_visitor_.in_available())
+ {
+ cursor_visitor_.send_available(ec);
+ }
+ else
+ {
+ parser_.restart();
+ while (!parser_.stopped())
+ {
+ parser_.parse(cursor_handler_adaptor_, ec);
+ if (ec) return;
+ }
+ }
+ }
+
+ void read_next(basic_json_visitor<char_type>& visitor, std::error_code& ec)
+ {
+ {
+ struct resource_wrapper
+ {
+ basic_json_visitor2_to_visitor_adaptor<char_type,Allocator>& adaptor;
+ basic_json_visitor<char_type>& original;
+
+ resource_wrapper(basic_json_visitor2_to_visitor_adaptor<char_type,Allocator>& adaptor,
+ basic_json_visitor<char_type>& visitor)
+ : adaptor(adaptor), original(adaptor.destination())
+ {
+ adaptor.destination(visitor);
+ }
+
+ ~resource_wrapper()
+ {
+ adaptor.destination(original);
+ }
+ } wrapper(cursor_handler_adaptor_, visitor);
+
+ parser_.restart();
+ while (!parser_.stopped())
+ {
+ parser_.parse(cursor_handler_adaptor_, ec);
+ if (ec) return;
+ }
+ }
+ }
+};
+
+using msgpack_stream_cursor = basic_msgpack_cursor<jsoncons::binary_stream_source>;
+using msgpack_bytes_cursor = basic_msgpack_cursor<jsoncons::bytes_source>;
+
+} // namespace msgpack
+} // namespace jsoncons
+
+#endif
+
diff --git a/include/jsoncons_ext/msgpack/msgpack_cursor2.hpp b/include/jsoncons_ext/msgpack/msgpack_cursor2.hpp
new file mode 100644
index 0000000..8fce1ca
--- /dev/null
+++ b/include/jsoncons_ext/msgpack/msgpack_cursor2.hpp
@@ -0,0 +1,259 @@
+// 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)
+
+// See https://github.com/danielaparker/jsoncons for latest version
+
+#ifndef JSONCONS_MSGPACK_MSGPACK_CURSOR2_HPP
+#define JSONCONS_MSGPACK_MSGPACK_CURSOR2_HPP
+
+#include <memory> // std::allocator
+#include <string>
+#include <vector>
+#include <stdexcept>
+#include <system_error>
+#include <ios>
+#include <istream> // std::basic_istream
+#include <jsoncons/byte_string.hpp>
+#include <jsoncons/config/jsoncons_config.hpp>
+#include <jsoncons/json_visitor2.hpp>
+#include <jsoncons/json_exception.hpp>
+#include <jsoncons/staj2_cursor.hpp>
+#include <jsoncons/source.hpp>
+#include <jsoncons_ext/msgpack/msgpack_parser.hpp>
+
+namespace jsoncons {
+namespace msgpack {
+
+ template<class Source=jsoncons::binary_stream_source,class Allocator=std::allocator<char>>
+ class basic_msgpack_cursor2 : public basic_staj2_cursor<char>, private virtual ser_context
+ {
+ public:
+ using source_type = Source;
+ using char_type = char;
+ using allocator_type = Allocator;
+ private:
+ basic_msgpack_parser<Source,Allocator> parser_;
+ basic_staj2_visitor<char_type> cursor_visitor_;
+ bool eof_;
+
+ // Noncopyable and nonmoveable
+ basic_msgpack_cursor2(const basic_msgpack_cursor2&) = delete;
+ basic_msgpack_cursor2& operator=(const basic_msgpack_cursor2&) = delete;
+
+ public:
+ using string_view_type = string_view;
+
+ template <class Sourceable>
+ basic_msgpack_cursor2(Sourceable&& source,
+ const msgpack_decode_options& options = msgpack_decode_options(),
+ const Allocator& alloc = Allocator())
+ : parser_(std::forward<Sourceable>(source), options, alloc),
+ cursor_visitor_(accept_all),
+ eof_(false)
+ {
+ if (!done())
+ {
+ next();
+ }
+ }
+
+ // Constructors that set parse error codes
+
+ template <class Sourceable>
+ basic_msgpack_cursor2(Sourceable&& source,
+ std::error_code& ec)
+ : basic_msgpack_cursor2(std::allocator_arg, Allocator(),
+ std::forward<Sourceable>(source),
+ msgpack_decode_options(),
+ ec)
+ {
+ }
+
+ template <class Sourceable>
+ basic_msgpack_cursor2(Sourceable&& source,
+ const msgpack_decode_options& options,
+ std::error_code& ec)
+ : basic_msgpack_cursor2(std::allocator_arg, Allocator(),
+ std::forward<Sourceable>(source),
+ options,
+ ec)
+ {
+ }
+
+ template <class Sourceable>
+ basic_msgpack_cursor2(std::allocator_arg_t, const Allocator& alloc,
+ Sourceable&& source,
+ const msgpack_decode_options& options,
+ std::error_code& ec)
+ : parser_(std::forward<Sourceable>(source), options, alloc),
+ cursor_visitor_(accept_all),
+ eof_(false)
+ {
+ if (!done())
+ {
+ next(ec);
+ }
+ }
+
+ void reset()
+ {
+ parser_.reset();
+ cursor_visitor_.reset();
+ eof_ = false;
+ if (!done())
+ {
+ next();
+ }
+ }
+
+ template <class Sourceable>
+ void reset(Sourceable&& source)
+ {
+ parser_.reset(std::forward<Sourceable>(source));
+ cursor_visitor_.reset();
+ eof_ = false;
+ if (!done())
+ {
+ next();
+ }
+ }
+
+ void reset(std::error_code& ec)
+ {
+ parser_.reset();
+ cursor_visitor_.reset();
+ eof_ = false;
+ if (!done())
+ {
+ next(ec);
+ }
+ }
+
+ template <class Sourceable>
+ void reset(Sourceable&& source, std::error_code& ec)
+ {
+ parser_.reset(std::forward<Sourceable>(source));
+ cursor_visitor_.reset();
+ eof_ = false;
+ if (!done())
+ {
+ next(ec);
+ }
+ }
+
+ bool done() const override
+ {
+ return parser_.done();
+ }
+
+ const staj2_event& current() const override
+ {
+ return cursor_visitor_.event();
+ }
+
+ void read_to(basic_json_visitor2<char_type>& visitor) override
+ {
+ std::error_code ec;
+ read_to(visitor, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column()));
+ }
+ }
+
+ void read_to(basic_json_visitor2<char_type>& visitor,
+ std::error_code& ec) override
+ {
+ if (cursor_visitor_.dump(visitor, *this, ec))
+ {
+ read_next(visitor, ec);
+ }
+ }
+
+ void next() override
+ {
+ std::error_code ec;
+ next(ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column()));
+ }
+ }
+
+ void next(std::error_code& ec) override
+ {
+ read_next(ec);
+ }
+
+ const ser_context& context() const override
+ {
+ return *this;
+ }
+
+ bool eof() const
+ {
+ return eof_;
+ }
+
+ std::size_t line() const override
+ {
+ return parser_.line();
+ }
+
+ std::size_t column() const override
+ {
+ return parser_.column();
+ }
+
+ friend
+ staj2_filter_view operator|(basic_msgpack_cursor2& cursor,
+ std::function<bool(const staj2_event&, const ser_context&)> pred)
+ {
+ return staj2_filter_view(cursor, pred);
+ }
+
+ private:
+ static bool accept_all(const staj2_event&, const ser_context&)
+ {
+ return true;
+ }
+
+ void read_next(std::error_code& ec)
+ {
+ if (cursor_visitor_.in_available())
+ {
+ cursor_visitor_.send_available(ec);
+ }
+ else
+ {
+ parser_.restart();
+ while (!parser_.stopped())
+ {
+ parser_.parse(cursor_visitor_, ec);
+ if (ec) return;
+ }
+ }
+ }
+
+ void read_next(basic_json_visitor2<char_type>& visitor, std::error_code& ec)
+ {
+ {
+ parser_.restart();
+ while (!parser_.stopped())
+ {
+ parser_.parse(visitor, ec);
+ if (ec) return;
+ }
+ }
+ }
+ };
+
+ using msgpack_stream_cursor2 = basic_msgpack_cursor2<jsoncons::binary_stream_source>;
+ using msgpack_bytes_cursor2 = basic_msgpack_cursor2<jsoncons::bytes_source>;
+
+} // namespace msgpack
+} // namespace jsoncons
+
+#endif
+
diff --git a/include/jsoncons_ext/msgpack/msgpack_encoder.hpp b/include/jsoncons_ext/msgpack/msgpack_encoder.hpp
new file mode 100644
index 0000000..34e882b
--- /dev/null
+++ b/include/jsoncons_ext/msgpack/msgpack_encoder.hpp
@@ -0,0 +1,753 @@
+// 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)
+
+// See https://github.com/danielaparker/jsoncons for latest version
+
+#ifndef JSONCONS_MSGPACK_MSGPACK_ENCODER_HPP
+#define JSONCONS_MSGPACK_MSGPACK_ENCODER_HPP
+
+#include <string>
+#include <vector>
+#include <limits> // std::numeric_limits
+#include <memory>
+#include <utility> // std::move
+#include <jsoncons/json_exception.hpp>
+#include <jsoncons/json_visitor.hpp>
+#include <jsoncons/config/jsoncons_config.hpp>
+#include <jsoncons/sink.hpp>
+#include <jsoncons/detail/parse_number.hpp>
+#include <jsoncons_ext/msgpack/msgpack_type.hpp>
+#include <jsoncons_ext/msgpack/msgpack_error.hpp>
+#include <jsoncons_ext/msgpack/msgpack_options.hpp>
+
+namespace jsoncons {
+namespace msgpack {
+
+ enum class msgpack_container_type {object, array};
+
+ template<class Sink=jsoncons::binary_stream_sink,class Allocator=std::allocator<char>>
+ class basic_msgpack_encoder final : public basic_json_visitor<char>
+ {
+ enum class decimal_parse_state { start, integer, exp1, exp2, fraction1 };
+
+ static constexpr int64_t nanos_in_milli = 1000000;
+ static constexpr int64_t nanos_in_second = 1000000000;
+ static constexpr int64_t millis_in_second = 1000;
+ public:
+ using allocator_type = Allocator;
+ using char_type = char;
+ using typename basic_json_visitor<char>::string_view_type;
+ using sink_type = Sink;
+
+ private:
+ struct stack_item
+ {
+ msgpack_container_type type_;
+ std::size_t length_;
+ std::size_t count_;
+
+ stack_item(msgpack_container_type type, std::size_t length = 0) noexcept
+ : type_(type), length_(length), count_(0)
+ {
+ }
+
+ std::size_t length() const
+ {
+ return length_;
+ }
+
+ std::size_t count() const
+ {
+ return count_;
+ }
+
+ bool is_object() const
+ {
+ return type_ == msgpack_container_type::object;
+ }
+ };
+
+ Sink sink_;
+ const msgpack_encode_options options_;
+ allocator_type alloc_;
+
+ std::vector<stack_item> stack_;
+ int nesting_depth_;
+
+ // Noncopyable and nonmoveable
+ basic_msgpack_encoder(const basic_msgpack_encoder&) = delete;
+ basic_msgpack_encoder& operator=(const basic_msgpack_encoder&) = delete;
+ public:
+ explicit basic_msgpack_encoder(Sink&& sink,
+ const Allocator& alloc = Allocator())
+ : basic_msgpack_encoder(std::forward<Sink>(sink), msgpack_encode_options(), alloc)
+ {
+ }
+
+ explicit basic_msgpack_encoder(Sink&& sink,
+ const msgpack_encode_options& options,
+ const Allocator& alloc = Allocator())
+ : sink_(std::forward<Sink>(sink)),
+ options_(options),
+ alloc_(alloc),
+ nesting_depth_(0)
+ {
+ }
+
+ ~basic_msgpack_encoder() noexcept
+ {
+ sink_.flush();
+ }
+
+ void reset()
+ {
+ stack_.clear();
+ nesting_depth_ = 0;
+ }
+
+ void reset(Sink&& sink)
+ {
+ sink_ = std::move(sink);
+ reset();
+ }
+
+ private:
+ // Implementing methods
+
+ void visit_flush() override
+ {
+ sink_.flush();
+ }
+
+ bool visit_begin_object(semantic_tag, const ser_context&, std::error_code& ec) override
+ {
+ ec = msgpack_errc::object_length_required;
+ return false;
+ }
+
+ bool 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()))
+ {
+ ec = msgpack_errc::max_nesting_depth_exceeded;
+ return false;
+ }
+ stack_.emplace_back(msgpack_container_type::object, length);
+
+ if (length <= 15)
+ {
+ // fixmap
+ sink_.push_back(jsoncons::msgpack::msgpack_type::fixmap_base_type | (length & 0xf));
+ }
+ else if (length <= 65535)
+ {
+ // map 16
+ sink_.push_back(jsoncons::msgpack::msgpack_type::map16_type);
+ binary::native_to_big(static_cast<uint16_t>(length),
+ std::back_inserter(sink_));
+ }
+ else if (length <= 4294967295)
+ {
+ // map 32
+ sink_.push_back(jsoncons::msgpack::msgpack_type::map32_type);
+ binary::native_to_big(static_cast<uint32_t>(length),
+ std::back_inserter(sink_));
+ }
+
+ return true;
+ }
+
+ bool visit_end_object(const ser_context&, std::error_code& ec) override
+ {
+ JSONCONS_ASSERT(!stack_.empty());
+ --nesting_depth_;
+
+ if (stack_.back().count() < stack_.back().length())
+ {
+ ec = msgpack_errc::too_few_items;
+ return false;
+ }
+ else if (stack_.back().count() > stack_.back().length())
+ {
+ ec = msgpack_errc::too_many_items;
+ return false;
+ }
+
+ stack_.pop_back();
+ end_value();
+ return true;
+ }
+
+ bool visit_begin_array(semantic_tag, const ser_context&, std::error_code& ec) override
+ {
+ ec = msgpack_errc::array_length_required;
+ return false;
+ }
+
+ bool 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()))
+ {
+ ec = msgpack_errc::max_nesting_depth_exceeded;
+ return false;
+ }
+ stack_.emplace_back(msgpack_container_type::array, length);
+ if (length <= 15)
+ {
+ // fixarray
+ sink_.push_back(jsoncons::msgpack::msgpack_type::fixarray_base_type | (length & 0xf));
+ }
+ else if (length <= (std::numeric_limits<uint16_t>::max)())
+ {
+ // array 16
+ sink_.push_back(jsoncons::msgpack::msgpack_type::array16_type);
+ binary::native_to_big(static_cast<uint16_t>(length),std::back_inserter(sink_));
+ }
+ else if (length <= (std::numeric_limits<uint32_t>::max)())
+ {
+ // array 32
+ sink_.push_back(jsoncons::msgpack::msgpack_type::array32_type);
+ binary::native_to_big(static_cast<uint32_t>(length),std::back_inserter(sink_));
+ }
+ return true;
+ }
+
+ bool visit_end_array(const ser_context&, std::error_code& ec) override
+ {
+ JSONCONS_ASSERT(!stack_.empty());
+
+ --nesting_depth_;
+
+ if (stack_.back().count() < stack_.back().length())
+ {
+ ec = msgpack_errc::too_few_items;
+ return false;
+ }
+ else if (stack_.back().count() > stack_.back().length())
+ {
+ ec = msgpack_errc::too_many_items;
+ return false;
+ }
+
+ stack_.pop_back();
+ end_value();
+ return true;
+ }
+
+ bool visit_key(const string_view_type& name, const ser_context&, std::error_code&) override
+ {
+ write_string_value(name);
+ return true;
+ }
+
+ bool visit_null(semantic_tag, const ser_context&, std::error_code&) override
+ {
+ // nil
+ sink_.push_back(jsoncons::msgpack::msgpack_type::nil_type);
+ end_value();
+ return true;
+ }
+
+ void write_timestamp(int64_t seconds, int64_t nanoseconds)
+ {
+ if ((seconds >> 34) == 0)
+ {
+ uint64_t data64 = (nanoseconds << 34) | seconds;
+ if ((data64 & 0xffffffff00000000L) == 0)
+ {
+ // timestamp 32
+ sink_.push_back(jsoncons::msgpack::msgpack_type::fixext4_type);
+ sink_.push_back(0xff);
+ binary::native_to_big(static_cast<uint32_t>(data64), std::back_inserter(sink_));
+ }
+ else
+ {
+ // timestamp 64
+ sink_.push_back(jsoncons::msgpack::msgpack_type::fixext8_type);
+ sink_.push_back(0xff);
+ binary::native_to_big(static_cast<uint64_t>(data64), std::back_inserter(sink_));
+ }
+ }
+ else
+ {
+ // timestamp 96
+ sink_.push_back(jsoncons::msgpack::msgpack_type::ext8_type);
+ sink_.push_back(0x0c); // 12
+ sink_.push_back(0xff);
+ binary::native_to_big(static_cast<uint32_t>(nanoseconds), std::back_inserter(sink_));
+ binary::native_to_big(static_cast<uint64_t>(seconds), std::back_inserter(sink_));
+ }
+ }
+
+ bool visit_string(const string_view_type& sv, semantic_tag tag, const ser_context&, std::error_code& ec) override
+ {
+ switch (tag)
+ {
+ case semantic_tag::epoch_second:
+ {
+ int64_t seconds;
+ auto result = jsoncons::detail::to_integer(sv.data(), sv.length(), seconds);
+ if (!result)
+ {
+ ec = msgpack_errc::invalid_timestamp;
+ return false;
+ }
+ write_timestamp(seconds, 0);
+ break;
+ }
+ case semantic_tag::epoch_milli:
+ {
+ bigint n = bigint::from_string(sv.data(), sv.length());
+ if (n != 0)
+ {
+ bigint q;
+ bigint rem;
+ n.divide(millis_in_second, q, rem, true);
+ int64_t seconds = static_cast<int64_t>(q);
+ int64_t nanoseconds = static_cast<int64_t>(rem) * nanos_in_milli;
+ if (nanoseconds < 0)
+ {
+ nanoseconds = -nanoseconds;
+ }
+ write_timestamp(seconds, nanoseconds);
+ }
+ else
+ {
+ write_timestamp(0, 0);
+ }
+ break;
+ }
+ case semantic_tag::epoch_nano:
+ {
+ bigint n = bigint::from_string(sv.data(), sv.length());
+ if (n != 0)
+ {
+ bigint q;
+ bigint rem;
+ n.divide(nanos_in_second, q, rem, true);
+ int64_t seconds = static_cast<int64_t>(q);
+ int64_t nanoseconds = static_cast<int64_t>(rem);
+ if (nanoseconds < 0)
+ {
+ nanoseconds = -nanoseconds;
+ }
+ write_timestamp(seconds, nanoseconds);
+ }
+ else
+ {
+ write_timestamp(0, 0);
+ }
+ break;
+ }
+ default:
+ {
+ write_string_value(sv);
+ end_value();
+ break;
+ }
+ }
+ return true;
+ }
+
+ void write_string_value(const string_view_type& sv)
+ {
+ auto sink = unicode_traits::validate(sv.data(), sv.size());
+ if (sink.ec != unicode_traits::conv_errc())
+ {
+ JSONCONS_THROW(ser_error(msgpack_errc::invalid_utf8_text_string));
+ }
+
+ const size_t length = sv.length();
+ if (length <= 31)
+ {
+ // fixstr stores a byte array whose length is upto 31 bytes
+ sink_.push_back(jsoncons::msgpack::msgpack_type::fixstr_base_type | static_cast<uint8_t>(length));
+ }
+ else if (length <= (std::numeric_limits<uint8_t>::max)())
+ {
+ // str 8 stores a byte array whose length is upto (2^8)-1 bytes
+ sink_.push_back(jsoncons::msgpack::msgpack_type::str8_type);
+ sink_.push_back(static_cast<uint8_t>(length));
+ }
+ else if (length <= (std::numeric_limits<uint16_t>::max)())
+ {
+ // str 16 stores a byte array whose length is upto (2^16)-1 bytes
+ sink_.push_back(jsoncons::msgpack::msgpack_type::str16_type);
+ binary::native_to_big(static_cast<uint16_t>(length), std::back_inserter(sink_));
+ }
+ else if (length <= (std::numeric_limits<uint32_t>::max)())
+ {
+ // str 32 stores a byte array whose length is upto (2^32)-1 bytes
+ sink_.push_back(jsoncons::msgpack::msgpack_type::str32_type);
+ binary::native_to_big(static_cast<uint32_t>(length),std::back_inserter(sink_));
+ }
+
+ for (auto c : sv)
+ {
+ sink_.push_back(c);
+ }
+ }
+
+ bool visit_byte_string(const byte_string_view& b,
+ semantic_tag,
+ const ser_context&,
+ std::error_code&) override
+ {
+
+ const std::size_t length = b.size();
+ if (length <= (std::numeric_limits<uint8_t>::max)())
+ {
+ // bin 8 stores a byte array whose length is upto (2^8)-1 bytes
+ sink_.push_back(jsoncons::msgpack::msgpack_type::bin8_type);
+ sink_.push_back(static_cast<uint8_t>(length));
+ }
+ else if (length <= (std::numeric_limits<uint16_t>::max)())
+ {
+ // bin 16 stores a byte array whose length is upto (2^16)-1 bytes
+ sink_.push_back(jsoncons::msgpack::msgpack_type::bin16_type);
+ binary::native_to_big(static_cast<uint16_t>(length), std::back_inserter(sink_));
+ }
+ else if (length <= (std::numeric_limits<uint32_t>::max)())
+ {
+ // bin 32 stores a byte array whose length is upto (2^32)-1 bytes
+ sink_.push_back(jsoncons::msgpack::msgpack_type::bin32_type);
+ binary::native_to_big(static_cast<uint32_t>(length),std::back_inserter(sink_));
+ }
+
+ for (auto c : b)
+ {
+ sink_.push_back(c);
+ }
+
+ end_value();
+ return true;
+ }
+
+ bool visit_byte_string(const byte_string_view& b,
+ uint64_t ext_tag,
+ const ser_context&,
+ std::error_code&) override
+ {
+ const std::size_t length = b.size();
+ switch (length)
+ {
+ case 1:
+ sink_.push_back(jsoncons::msgpack::msgpack_type::fixext1_type);
+ sink_.push_back(static_cast<uint8_t>(ext_tag));
+ break;
+ case 2:
+ sink_.push_back(jsoncons::msgpack::msgpack_type::fixext2_type);
+ sink_.push_back(static_cast<uint8_t>(ext_tag));
+ break;
+ case 4:
+ sink_.push_back(jsoncons::msgpack::msgpack_type::fixext4_type);
+ sink_.push_back(static_cast<uint8_t>(ext_tag));
+ break;
+ case 8:
+ sink_.push_back(jsoncons::msgpack::msgpack_type::fixext8_type);
+ sink_.push_back(static_cast<uint8_t>(ext_tag));
+ break;
+ case 16:
+ sink_.push_back(jsoncons::msgpack::msgpack_type::fixext16_type);
+ sink_.push_back(static_cast<uint8_t>(ext_tag));
+ break;
+ default:
+ if (length <= (std::numeric_limits<uint8_t>::max)())
+ {
+ sink_.push_back(jsoncons::msgpack::msgpack_type::ext8_type);
+ sink_.push_back(static_cast<uint8_t>(length));
+ sink_.push_back(static_cast<uint8_t>(ext_tag));
+ }
+ else if (length <= (std::numeric_limits<uint16_t>::max)())
+ {
+ sink_.push_back(jsoncons::msgpack::msgpack_type::ext16_type);
+ binary::native_to_big(static_cast<uint16_t>(length), std::back_inserter(sink_));
+ sink_.push_back(static_cast<uint8_t>(ext_tag));
+ }
+ else if (length <= (std::numeric_limits<uint32_t>::max)())
+ {
+ sink_.push_back(jsoncons::msgpack::msgpack_type::ext32_type);
+ binary::native_to_big(static_cast<uint32_t>(length),std::back_inserter(sink_));
+ sink_.push_back(static_cast<uint8_t>(ext_tag));
+ }
+ break;
+ }
+
+ for (auto c : b)
+ {
+ sink_.push_back(c);
+ }
+
+ end_value();
+ return true;
+ }
+
+ bool visit_double(double val,
+ semantic_tag,
+ const ser_context&,
+ std::error_code&) override
+ {
+ float valf = (float)val;
+ if ((double)valf == val)
+ {
+ // float 32
+ sink_.push_back(jsoncons::msgpack::msgpack_type::float32_type);
+ binary::native_to_big(valf,std::back_inserter(sink_));
+ }
+ else
+ {
+ // float 64
+ sink_.push_back(jsoncons::msgpack::msgpack_type::float64_type);
+ binary::native_to_big(val,std::back_inserter(sink_));
+ }
+
+ // write double
+
+ end_value();
+ return true;
+ }
+
+ bool visit_int64(int64_t val,
+ semantic_tag tag,
+ const ser_context&,
+ std::error_code&) override
+ {
+ switch (tag)
+ {
+ case semantic_tag::epoch_second:
+ write_timestamp(val, 0);
+ break;
+ case semantic_tag::epoch_milli:
+ {
+ if (val != 0)
+ {
+ auto dv = std::div(val,millis_in_second);
+ int64_t seconds = dv.quot;
+ int64_t nanoseconds = dv.rem*nanos_in_milli;
+ if (nanoseconds < 0)
+ {
+ nanoseconds = -nanoseconds;
+ }
+ write_timestamp(seconds, nanoseconds);
+ }
+ else
+ {
+ write_timestamp(0, 0);
+ }
+ break;
+ }
+ case semantic_tag::epoch_nano:
+ {
+ if (val != 0)
+ {
+ auto dv = std::div(val,static_cast<int64_t>(nanos_in_second));
+ int64_t seconds = dv.quot;
+ int64_t nanoseconds = dv.rem;
+ if (nanoseconds < 0)
+ {
+ nanoseconds = -nanoseconds;
+ }
+ write_timestamp(seconds, nanoseconds);
+ }
+ else
+ {
+ write_timestamp(0, 0);
+ }
+ break;
+ }
+ default:
+ {
+ if (val >= 0)
+ {
+ if (val <= 0x7f)
+ {
+ // positive fixnum stores 7-bit positive integer
+ sink_.push_back(static_cast<uint8_t>(val));
+ }
+ else if (val <= (std::numeric_limits<uint8_t>::max)())
+ {
+ // uint 8 stores a 8-bit unsigned integer
+ sink_.push_back(jsoncons::msgpack::msgpack_type::uint8_type);
+ sink_.push_back(static_cast<uint8_t>(val));
+ }
+ else if (val <= (std::numeric_limits<uint16_t>::max)())
+ {
+ // uint 16 stores a 16-bit big-endian unsigned integer
+ sink_.push_back(jsoncons::msgpack::msgpack_type::uint16_type);
+ binary::native_to_big(static_cast<uint16_t>(val),std::back_inserter(sink_));
+ }
+ else if (val <= (std::numeric_limits<uint32_t>::max)())
+ {
+ // uint 32 stores a 32-bit big-endian unsigned integer
+ sink_.push_back(jsoncons::msgpack::msgpack_type::uint32_type);
+ binary::native_to_big(static_cast<uint32_t>(val),std::back_inserter(sink_));
+ }
+ else if (val <= (std::numeric_limits<int64_t>::max)())
+ {
+ // int 64 stores a 64-bit big-endian signed integer
+ sink_.push_back(jsoncons::msgpack::msgpack_type::uint64_type);
+ binary::native_to_big(static_cast<uint64_t>(val),std::back_inserter(sink_));
+ }
+ }
+ else
+ {
+ if (val >= -32)
+ {
+ // negative fixnum stores 5-bit negative integer
+ binary::native_to_big(static_cast<int8_t>(val), std::back_inserter(sink_));
+ }
+ else if (val >= (std::numeric_limits<int8_t>::lowest)())
+ {
+ // int 8 stores a 8-bit signed integer
+ sink_.push_back(jsoncons::msgpack::msgpack_type::int8_type);
+ binary::native_to_big(static_cast<int8_t>(val),std::back_inserter(sink_));
+ }
+ else if (val >= (std::numeric_limits<int16_t>::lowest)())
+ {
+ // int 16 stores a 16-bit big-endian signed integer
+ sink_.push_back(jsoncons::msgpack::msgpack_type::int16_type);
+ binary::native_to_big(static_cast<int16_t>(val),std::back_inserter(sink_));
+ }
+ else if (val >= (std::numeric_limits<int32_t>::lowest)())
+ {
+ // int 32 stores a 32-bit big-endian signed integer
+ sink_.push_back(jsoncons::msgpack::msgpack_type::int32_type);
+ binary::native_to_big(static_cast<int32_t>(val),std::back_inserter(sink_));
+ }
+ else if (val >= (std::numeric_limits<int64_t>::lowest)())
+ {
+ // int 64 stores a 64-bit big-endian signed integer
+ sink_.push_back(jsoncons::msgpack::msgpack_type::int64_type);
+ binary::native_to_big(static_cast<int64_t>(val),std::back_inserter(sink_));
+ }
+ }
+ }
+ break;
+ }
+ end_value();
+ return true;
+ }
+
+ bool visit_uint64(uint64_t val,
+ semantic_tag tag,
+ const ser_context&,
+ std::error_code&) override
+ {
+ switch (tag)
+ {
+ case semantic_tag::epoch_second:
+ write_timestamp(static_cast<int64_t>(val), 0);
+ break;
+ case semantic_tag::epoch_milli:
+ {
+ if (val != 0)
+ {
+ auto dv = std::div(static_cast<int64_t>(val), static_cast<int64_t>(millis_in_second));
+ int64_t seconds = dv.quot;
+ int64_t nanoseconds = dv.rem*nanos_in_milli;
+ if (nanoseconds < 0)
+ {
+ nanoseconds = -nanoseconds;
+ }
+ write_timestamp(seconds, nanoseconds);
+ }
+ else
+ {
+ write_timestamp(0, 0);
+ }
+ break;
+ }
+ case semantic_tag::epoch_nano:
+ {
+ if (val != 0)
+ {
+ auto dv = std::div(static_cast<int64_t>(val), static_cast<int64_t>(nanos_in_second));
+ int64_t seconds = dv.quot;
+ int64_t nanoseconds = dv.rem;
+ if (nanoseconds < 0)
+ {
+ nanoseconds = -nanoseconds;
+ }
+ write_timestamp(seconds, nanoseconds);
+ }
+ else
+ {
+ write_timestamp(0, 0);
+ }
+ break;
+ }
+ default:
+ {
+ if (val <= static_cast<uint64_t>((std::numeric_limits<int8_t>::max)()))
+ {
+ // positive fixnum stores 7-bit positive integer
+ sink_.push_back(static_cast<uint8_t>(val));
+ }
+ else if (val <= (std::numeric_limits<uint8_t>::max)())
+ {
+ // uint 8 stores a 8-bit unsigned integer
+ sink_.push_back(jsoncons::msgpack::msgpack_type::uint8_type);
+ sink_.push_back(static_cast<uint8_t>(val));
+ }
+ else if (val <= (std::numeric_limits<uint16_t>::max)())
+ {
+ // uint 16 stores a 16-bit big-endian unsigned integer
+ sink_.push_back(jsoncons::msgpack::msgpack_type::uint16_type);
+ binary::native_to_big(static_cast<uint16_t>(val),std::back_inserter(sink_));
+ }
+ else if (val <= (std::numeric_limits<uint32_t>::max)())
+ {
+ // uint 32 stores a 32-bit big-endian unsigned integer
+ sink_.push_back(jsoncons::msgpack::msgpack_type::uint32_type);
+ binary::native_to_big(static_cast<uint32_t>(val),std::back_inserter(sink_));
+ }
+ else if (val <= (std::numeric_limits<uint64_t>::max)())
+ {
+ // uint 64 stores a 64-bit big-endian unsigned integer
+ sink_.push_back(jsoncons::msgpack::msgpack_type::uint64_type);
+ binary::native_to_big(static_cast<uint64_t>(val),std::back_inserter(sink_));
+ }
+ break;
+ }
+ }
+ end_value();
+ return true;
+ }
+
+ bool visit_bool(bool val, semantic_tag, const ser_context&, std::error_code&) override
+ {
+ // true and false
+ sink_.push_back(static_cast<uint8_t>(val ? jsoncons::msgpack::msgpack_type::true_type : jsoncons::msgpack::msgpack_type::false_type));
+
+ end_value();
+ return true;
+ }
+
+ void end_value()
+ {
+ if (!stack_.empty())
+ {
+ ++stack_.back().count_;
+ }
+ }
+ };
+
+ using msgpack_stream_encoder = basic_msgpack_encoder<jsoncons::binary_stream_sink>;
+ using msgpack_bytes_encoder = basic_msgpack_encoder<jsoncons::bytes_sink<std::vector<uint8_t>>>;
+
+ #if !defined(JSONCONS_NO_DEPRECATED)
+ JSONCONS_DEPRECATED_MSG("Instead, use msgpack_bytes_encoder") typedef msgpack_bytes_encoder msgpack_bytes_serializer;
+
+ template<class Sink=jsoncons::binary_stream_sink>
+ using basic_msgpack_serializer = basic_msgpack_encoder<Sink>;
+
+ JSONCONS_DEPRECATED_MSG("Instead, use msgpack_stream_encoder") typedef msgpack_stream_encoder msgpack_encoder;
+ JSONCONS_DEPRECATED_MSG("Instead, use msgpack_stream_encoder") typedef msgpack_stream_encoder msgpack_serializer;
+ JSONCONS_DEPRECATED_MSG("Instead, use msgpack_bytes_encoder") typedef msgpack_bytes_encoder msgpack_buffer_serializer;
+ #endif
+
+} // namespace msgpack
+} // namespace jsoncons
+
+#endif
diff --git a/include/jsoncons_ext/msgpack/msgpack_error.hpp b/include/jsoncons_ext/msgpack/msgpack_error.hpp
new file mode 100644
index 0000000..80c76b6
--- /dev/null
+++ b/include/jsoncons_ext/msgpack/msgpack_error.hpp
@@ -0,0 +1,94 @@
+/// 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)
+
+// See https://github.com/danielaparker/jsoncons for latest version
+
+#ifndef JSONCONS_MSGPACK_MSGPACK_ERROR_HPP
+#define JSONCONS_MSGPACK_MSGPACK_ERROR_HPP
+
+#include <system_error>
+#include <jsoncons/config/jsoncons_config.hpp>
+
+namespace jsoncons { namespace msgpack {
+
+enum class msgpack_errc
+{
+ success = 0,
+ unexpected_eof = 1,
+ source_error,
+ invalid_utf8_text_string,
+ array_length_required,
+ object_length_required,
+ too_many_items,
+ too_few_items,
+ max_nesting_depth_exceeded,
+ length_is_negative,
+ invalid_timestamp,
+ unknown_type
+};
+
+class msgpack_error_category_impl
+ : public std::error_category
+{
+public:
+ const char* name() const noexcept override
+ {
+ return "jsoncons/msgpack";
+ }
+ std::string message(int ev) const override
+ {
+ switch (static_cast<msgpack_errc>(ev))
+ {
+ case msgpack_errc::unexpected_eof:
+ return "Unexpected end of file";
+ case msgpack_errc::source_error:
+ return "Source error";
+ case msgpack_errc::invalid_utf8_text_string:
+ return "Illegal UTF-8 encoding in text string";
+ case msgpack_errc::array_length_required:
+ return "MessagePack encoder requires array length";
+ case msgpack_errc::object_length_required:
+ return "MessagePack encoder requires object length";
+ case msgpack_errc::too_many_items:
+ return "Too many items were added to a MessagePack object or array";
+ case msgpack_errc::too_few_items:
+ return "Too few items were added to a MessagePack object or array";
+ case msgpack_errc::max_nesting_depth_exceeded:
+ return "Data item nesting exceeds limit in options";
+ case msgpack_errc::length_is_negative:
+ return "Request for the length of an array, map or string returned a negative result";
+ case msgpack_errc::invalid_timestamp:
+ return "Invalid timestamp";
+ case msgpack_errc::unknown_type:
+ return "An unknown type was found in the stream";
+ default:
+ return "Unknown MessagePack parser error";
+ }
+ }
+};
+
+inline
+const std::error_category& msgpack_error_category()
+{
+ static msgpack_error_category_impl instance;
+ return instance;
+}
+
+inline
+std::error_code make_error_code(msgpack_errc e)
+{
+ return std::error_code(static_cast<int>(e),msgpack_error_category());
+}
+
+
+}}
+
+namespace std {
+ template<>
+ struct is_error_code_enum<jsoncons::msgpack::msgpack_errc> : public true_type
+ {
+ };
+}
+
+#endif
diff --git a/include/jsoncons_ext/msgpack/msgpack_options.hpp b/include/jsoncons_ext/msgpack/msgpack_options.hpp
new file mode 100644
index 0000000..17bddf1
--- /dev/null
+++ b/include/jsoncons_ext/msgpack/msgpack_options.hpp
@@ -0,0 +1,74 @@
+// Copyright 2019 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_MSGPACK_MSGPACK_OPTIONS_HPP
+#define JSONCONS_MSGPACK_MSGPACK_OPTIONS_HPP
+
+#include <string>
+#include <limits> // std::numeric_limits
+#include <cwchar>
+#include <jsoncons/json_exception.hpp>
+
+namespace jsoncons { namespace msgpack {
+
+class msgpack_options;
+
+class msgpack_options_common
+{
+ friend class msgpack_options;
+
+ int max_nesting_depth_;
+protected:
+ virtual ~msgpack_options_common() = default;
+
+ msgpack_options_common()
+ : max_nesting_depth_(1024)
+ {
+ }
+
+ 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
+ {
+ return max_nesting_depth_;
+ }
+};
+
+class msgpack_decode_options : public virtual msgpack_options_common
+{
+ friend class msgpack_options;
+public:
+ msgpack_decode_options()
+ {
+ }
+};
+
+class msgpack_encode_options : public virtual msgpack_options_common
+{
+ friend class msgpack_options;
+public:
+ msgpack_encode_options()
+ {
+ }
+};
+
+class msgpack_options final : public msgpack_decode_options, public msgpack_encode_options
+{
+public:
+ using msgpack_options_common::max_nesting_depth;
+
+ msgpack_options& max_nesting_depth(int value)
+ {
+ this->max_nesting_depth_ = value;
+ return *this;
+ }
+};
+
+}}
+#endif
diff --git a/include/jsoncons_ext/msgpack/msgpack_parser.hpp b/include/jsoncons_ext/msgpack/msgpack_parser.hpp
new file mode 100644
index 0000000..cf2d507
--- /dev/null
+++ b/include/jsoncons_ext/msgpack/msgpack_parser.hpp
@@ -0,0 +1,748 @@
+// Copyright 2017 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_MSGPACK_MSGPACK_PARSER_HPP
+#define JSONCONS_MSGPACK_MSGPACK_PARSER_HPP
+
+#include <string>
+#include <vector>
+#include <memory>
+#include <utility> // std::move
+#include <jsoncons/json.hpp>
+#include <jsoncons/source.hpp>
+#include <jsoncons/bigint.hpp>
+#include <jsoncons/json_visitor.hpp>
+#include <jsoncons/config/jsoncons_config.hpp>
+#include <jsoncons_ext/msgpack/msgpack_type.hpp>
+#include <jsoncons_ext/msgpack/msgpack_error.hpp>
+#include <jsoncons_ext/msgpack/msgpack_options.hpp>
+#include <jsoncons/json_visitor2.hpp>
+
+namespace jsoncons { namespace msgpack {
+
+enum class parse_mode {root,accept,array,map_key,map_value};
+
+struct parse_state
+{
+ parse_mode mode;
+ std::size_t length;
+ std::size_t index;
+
+ parse_state(parse_mode mode, std::size_t length) noexcept
+ : mode(mode), length(length), index(0)
+ {
+ }
+
+ parse_state(const parse_state&) = default;
+ parse_state(parse_state&&) = default;
+};
+
+template <class Source,class Allocator=std::allocator<char>>
+class basic_msgpack_parser : public ser_context
+{
+ using char_type = char;
+ using char_traits_type = std::char_traits<char>;
+ using temp_allocator_type = Allocator;
+ using char_allocator_type = typename std::allocator_traits<temp_allocator_type>:: template rebind_alloc<char_type>;
+ using byte_allocator_type = typename std::allocator_traits<temp_allocator_type>:: template rebind_alloc<uint8_t>;
+ using int64_allocator_type = typename std::allocator_traits<temp_allocator_type>:: template rebind_alloc<int64_t>;
+ using parse_state_allocator_type = typename std::allocator_traits<temp_allocator_type>:: template rebind_alloc<parse_state>;
+
+ static constexpr int64_t nanos_in_second = 1000000000;
+
+ Source source_;
+ msgpack_decode_options options_;
+ bool more_;
+ bool done_;
+ std::basic_string<char,std::char_traits<char>,char_allocator_type> text_buffer_;
+ std::vector<uint8_t,byte_allocator_type> bytes_buffer_;
+ std::vector<parse_state,parse_state_allocator_type> state_stack_;
+ int nesting_depth_;
+
+public:
+ template <class Sourceable>
+ basic_msgpack_parser(Sourceable&& source,
+ const msgpack_decode_options& options = msgpack_decode_options(),
+ const Allocator alloc = Allocator())
+ : source_(std::forward<Sourceable>(source)),
+ options_(options),
+ more_(true),
+ done_(false),
+ text_buffer_(alloc),
+ bytes_buffer_(alloc),
+ state_stack_(alloc),
+ nesting_depth_(0)
+ {
+ state_stack_.emplace_back(parse_mode::root,0);
+ }
+
+ void restart()
+ {
+ more_ = true;
+ }
+
+ void reset()
+ {
+ more_ = true;
+ done_ = false;
+ text_buffer_.clear();
+ bytes_buffer_.clear();
+ state_stack_.clear();
+ state_stack_.emplace_back(parse_mode::root,0);
+ nesting_depth_ = 0;
+ }
+
+ template <class Sourceable>
+ void reset(Sourceable&& source)
+ {
+ source_ = std::forward<Sourceable>(source);
+ reset();
+ }
+
+ bool done() const
+ {
+ return done_;
+ }
+
+ bool stopped() const
+ {
+ return !more_;
+ }
+
+ std::size_t line() const override
+ {
+ return 0;
+ }
+
+ std::size_t column() const override
+ {
+ return source_.position();
+ }
+
+ void parse(json_visitor2& visitor, std::error_code& ec)
+ {
+ while (!done_ && more_)
+ {
+ switch (state_stack_.back().mode)
+ {
+ case parse_mode::array:
+ {
+ if (state_stack_.back().index < state_stack_.back().length)
+ {
+ ++state_stack_.back().index;
+ read_item(visitor, ec);
+ if (ec)
+ {
+ return;
+ }
+ }
+ else
+ {
+ end_array(visitor, ec);
+ }
+ break;
+ }
+ case parse_mode::map_key:
+ {
+ if (state_stack_.back().index < state_stack_.back().length)
+ {
+ ++state_stack_.back().index;
+ state_stack_.back().mode = parse_mode::map_value;
+ read_item(visitor, ec);
+ if (ec)
+ {
+ return;
+ }
+ }
+ else
+ {
+ end_object(visitor, ec);
+ }
+ break;
+ }
+ case parse_mode::map_value:
+ {
+ state_stack_.back().mode = parse_mode::map_key;
+ read_item(visitor, ec);
+ if (ec)
+ {
+ return;
+ }
+ break;
+ }
+ case parse_mode::root:
+ {
+ state_stack_.back().mode = parse_mode::accept;
+ read_item(visitor, ec);
+ if (ec)
+ {
+ return;
+ }
+ break;
+ }
+ case parse_mode::accept:
+ {
+ JSONCONS_ASSERT(state_stack_.size() == 1);
+ state_stack_.clear();
+ more_ = false;
+ done_ = true;
+ visitor.flush();
+ break;
+ }
+ }
+ }
+ }
+private:
+
+ void read_item(json_visitor2& visitor, std::error_code& ec)
+ {
+ if (source_.is_error())
+ {
+ ec = msgpack_errc::source_error;
+ more_ = false;
+ return;
+ }
+
+ uint8_t type;
+ if (source_.read(&type, 1) == 0)
+ {
+ ec = msgpack_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+
+ if (type <= 0xbf)
+ {
+ if (type <= 0x7f)
+ {
+ // positive fixint
+ more_ = visitor.uint64_value(type, semantic_tag::none, *this, ec);
+ }
+ else if (type <= 0x8f)
+ {
+ begin_object(visitor,type,ec); // fixmap
+ }
+ else if (type <= 0x9f)
+ {
+ begin_array(visitor,type,ec); // fixarray
+ }
+ else
+ {
+ // fixstr
+ const size_t len = type & 0x1f;
+
+ text_buffer_.clear();
+
+ if (source_reader<Source>::read(source_,text_buffer_,len) != static_cast<std::size_t>(len))
+ {
+ ec = msgpack_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+
+ auto result = unicode_traits::validate(text_buffer_.data(),text_buffer_.size());
+ if (result.ec != unicode_traits::conv_errc())
+ {
+ ec = msgpack_errc::invalid_utf8_text_string;
+ more_ = false;
+ return;
+ }
+ more_ = visitor.string_value(jsoncons::basic_string_view<char>(text_buffer_.data(),text_buffer_.length()), semantic_tag::none, *this, ec);
+ }
+ }
+ else if (type >= 0xe0)
+ {
+ // negative fixint
+ more_ = visitor.int64_value(static_cast<int8_t>(type), semantic_tag::none, *this, ec);
+ }
+ else
+ {
+ switch (type)
+ {
+ case jsoncons::msgpack::msgpack_type::nil_type:
+ {
+ more_ = visitor.null_value(semantic_tag::none, *this, ec);
+ break;
+ }
+ case jsoncons::msgpack::msgpack_type::true_type:
+ {
+ more_ = visitor.bool_value(true, semantic_tag::none, *this, ec);
+ break;
+ }
+ case jsoncons::msgpack::msgpack_type::false_type:
+ {
+ more_ = visitor.bool_value(false, semantic_tag::none, *this, ec);
+ break;
+ }
+ case jsoncons::msgpack::msgpack_type::float32_type:
+ {
+ uint8_t buf[sizeof(float)];
+ if (source_.read(buf, sizeof(float)) != sizeof(float))
+ {
+ ec = msgpack_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ float val = binary::big_to_native<float>(buf, sizeof(buf));
+ more_ = visitor.double_value(val, semantic_tag::none, *this, ec);
+ break;
+ }
+
+ case jsoncons::msgpack::msgpack_type::float64_type:
+ {
+ uint8_t buf[sizeof(double)];
+ if (source_.read(buf, sizeof(double)) != sizeof(double))
+ {
+ ec = msgpack_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ double val = binary::big_to_native<double>(buf, sizeof(buf));
+ more_ = visitor.double_value(val, semantic_tag::none, *this, ec);
+ break;
+ }
+
+ case jsoncons::msgpack::msgpack_type::uint8_type:
+ {
+ uint8_t b;
+ if (source_.read(&b, 1) == 0)
+ {
+ ec = msgpack_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ more_ = visitor.uint64_value(b, semantic_tag::none, *this, ec);
+ break;
+ }
+
+ case jsoncons::msgpack::msgpack_type::uint16_type:
+ {
+ uint8_t buf[sizeof(uint16_t)];
+ if (source_.read(buf, sizeof(uint16_t)) !=sizeof(uint16_t))
+ {
+ ec = msgpack_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ uint16_t val = binary::big_to_native<uint16_t>(buf, sizeof(buf));
+ more_ = visitor.uint64_value(val, semantic_tag::none, *this, ec);
+ break;
+ }
+
+ case jsoncons::msgpack::msgpack_type::uint32_type:
+ {
+ uint8_t buf[sizeof(uint32_t)];
+ if (source_.read(buf, sizeof(uint32_t)) != sizeof(uint32_t))
+ {
+ ec = msgpack_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ uint32_t val = binary::big_to_native<uint32_t>(buf, sizeof(buf));
+ more_ = visitor.uint64_value(val, semantic_tag::none, *this, ec);
+ break;
+ }
+
+ case jsoncons::msgpack::msgpack_type::uint64_type:
+ {
+ uint8_t buf[sizeof(uint64_t)];
+ if (source_.read(buf, sizeof(uint64_t)) != sizeof(uint64_t))
+ {
+ ec = msgpack_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ uint64_t val = binary::big_to_native<uint64_t>(buf, sizeof(buf));
+ more_ = visitor.uint64_value(val, semantic_tag::none, *this, ec);
+ break;
+ }
+
+ case jsoncons::msgpack::msgpack_type::int8_type:
+ {
+ uint8_t buf[sizeof(int8_t)];
+ if (source_.read(buf, sizeof(int8_t)) != sizeof(int8_t))
+ {
+ ec = msgpack_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ int8_t val = binary::big_to_native<int8_t>(buf, sizeof(buf));
+ more_ = visitor.int64_value(val, semantic_tag::none, *this, ec);
+ break;
+ }
+
+ case jsoncons::msgpack::msgpack_type::int16_type:
+ {
+ uint8_t buf[sizeof(int16_t)];
+ if (source_.read(buf, sizeof(int16_t)) != sizeof(int16_t))
+ {
+ ec = msgpack_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ int16_t val = binary::big_to_native<int16_t>(buf, sizeof(buf));
+ more_ = visitor.int64_value(val, semantic_tag::none, *this, ec);
+ break;
+ }
+
+ case jsoncons::msgpack::msgpack_type::int32_type:
+ {
+ uint8_t buf[sizeof(int32_t)];
+ if (source_.read(buf, sizeof(int32_t)) != sizeof(int32_t))
+ {
+ ec = msgpack_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ int32_t val = binary::big_to_native<int32_t>(buf, sizeof(buf));
+ more_ = visitor.int64_value(val, semantic_tag::none, *this, ec);
+ break;
+ }
+
+ case jsoncons::msgpack::msgpack_type::int64_type:
+ {
+ uint8_t buf[sizeof(int64_t)];
+ if (source_.read(buf, sizeof(int64_t)) != sizeof(int64_t))
+ {
+ ec = msgpack_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ int64_t val = binary::big_to_native<int64_t>(buf, sizeof(buf));
+ more_ = visitor.int64_value(val, semantic_tag::none, *this, ec);
+ break;
+ }
+
+ case jsoncons::msgpack::msgpack_type::str8_type:
+ case jsoncons::msgpack::msgpack_type::str16_type:
+ case jsoncons::msgpack::msgpack_type::str32_type:
+ {
+ std::size_t len = get_size(type, ec);
+ if (!more_)
+ {
+ return;
+ }
+
+ text_buffer_.clear();
+ if (source_reader<Source>::read(source_,text_buffer_,len) != static_cast<std::size_t>(len))
+ {
+ ec = msgpack_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+
+ auto result = unicode_traits::validate(text_buffer_.data(),text_buffer_.size());
+ if (result.ec != unicode_traits::conv_errc())
+ {
+ ec = msgpack_errc::invalid_utf8_text_string;
+ more_ = false;
+ return;
+ }
+ more_ = visitor.string_value(jsoncons::basic_string_view<char>(text_buffer_.data(),text_buffer_.length()), semantic_tag::none, *this, ec);
+ break;
+ }
+
+ case jsoncons::msgpack::msgpack_type::bin8_type:
+ case jsoncons::msgpack::msgpack_type::bin16_type:
+ case jsoncons::msgpack::msgpack_type::bin32_type:
+ {
+ std::size_t len = get_size(type,ec);
+ if (!more_)
+ {
+ return;
+ }
+ bytes_buffer_.clear();
+ if (source_reader<Source>::read(source_,bytes_buffer_,len) != static_cast<std::size_t>(len))
+ {
+ ec = msgpack_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+
+ more_ = visitor.byte_string_value(byte_string_view(bytes_buffer_.data(),bytes_buffer_.size()),
+ semantic_tag::none,
+ *this,
+ ec);
+ break;
+ }
+ case jsoncons::msgpack::msgpack_type::fixext1_type:
+ case jsoncons::msgpack::msgpack_type::fixext2_type:
+ case jsoncons::msgpack::msgpack_type::fixext4_type:
+ case jsoncons::msgpack::msgpack_type::fixext8_type:
+ case jsoncons::msgpack::msgpack_type::fixext16_type:
+ case jsoncons::msgpack::msgpack_type::ext8_type:
+ case jsoncons::msgpack::msgpack_type::ext16_type:
+ case jsoncons::msgpack::msgpack_type::ext32_type:
+ {
+ std::size_t len = get_size(type,ec);
+ if (!more_)
+ {
+ return;
+ }
+
+ // type
+ uint8_t buf[sizeof(int8_t)];
+ if (source_.read(buf, sizeof(int8_t)) != sizeof(int8_t))
+ {
+ ec = msgpack_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+
+ int8_t ext_type = binary::big_to_native<int8_t>(buf, sizeof(buf));
+
+ bool is_timestamp = false;
+ if (ext_type == -1)
+ {
+ is_timestamp = true;;
+ }
+
+ // payload
+ if (is_timestamp && len == 4)
+ {
+ uint8_t buf32[sizeof(uint32_t)];
+ if (source_.read(buf32, sizeof(uint32_t)) != sizeof(uint32_t))
+ {
+ ec = msgpack_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ uint32_t val = binary::big_to_native<uint32_t>(buf32, sizeof(buf32));
+ more_ = visitor.uint64_value(val, semantic_tag::epoch_second, *this, ec);
+ }
+ else if (is_timestamp && len == 8)
+ {
+ uint8_t buf64[sizeof(uint64_t)];
+ if (source_.read(buf64, sizeof(uint64_t)) != sizeof(uint64_t))
+ {
+ ec = msgpack_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ uint64_t data64 = binary::big_to_native<uint64_t>(buf64, sizeof(buf64));
+ uint64_t sec = data64 & 0x00000003ffffffffL;
+ uint64_t nsec = data64 >> 34;
+
+ bigint nano(sec);
+ nano *= uint64_t(nanos_in_second);
+ nano += nsec;
+ text_buffer_.clear();
+ nano.write_string(text_buffer_);
+ more_ = visitor.string_value(text_buffer_, semantic_tag::epoch_nano, *this, ec);
+ if (!more_) return;
+ }
+ else if (is_timestamp && len == 12)
+ {
+ uint8_t buf1[sizeof(uint32_t)];
+ if (source_.read(buf1, sizeof(uint32_t)) != sizeof(uint32_t))
+ {
+ ec = msgpack_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ uint32_t nsec = binary::big_to_native<uint32_t>(buf1, sizeof(buf1));
+
+ uint8_t buf2[sizeof(int64_t)];
+ if (source_.read(buf2, sizeof(int64_t)) != sizeof(int64_t))
+ {
+ ec = msgpack_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ int64_t sec = binary::big_to_native<int64_t>(buf2, sizeof(buf2));
+
+ bigint nano(sec);
+
+ nano *= uint64_t(nanos_in_second);
+
+ if (nano < 0)
+ {
+ nano -= nsec;
+ }
+ else
+ {
+ nano += nsec;
+ }
+
+ text_buffer_.clear();
+ nano.write_string(text_buffer_);
+ more_ = visitor.string_value(text_buffer_, semantic_tag::epoch_nano, *this, ec);
+ if (!more_) return;
+ }
+ else
+ {
+ bytes_buffer_.clear();
+ if (source_reader<Source>::read(source_,bytes_buffer_,len) != static_cast<std::size_t>(len))
+ {
+ ec = msgpack_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+
+ more_ = visitor.byte_string_value(byte_string_view(bytes_buffer_.data(),bytes_buffer_.size()),
+ static_cast<uint8_t>(ext_type),
+ *this,
+ ec);
+ }
+ break;
+ }
+
+ case jsoncons::msgpack::msgpack_type::array16_type:
+ case jsoncons::msgpack::msgpack_type::array32_type:
+ {
+ begin_array(visitor,type,ec);
+ break;
+ }
+
+ case jsoncons::msgpack::msgpack_type::map16_type :
+ case jsoncons::msgpack::msgpack_type::map32_type :
+ {
+ begin_object(visitor, type, ec);
+ break;
+ }
+
+ default:
+ {
+ ec = msgpack_errc::unknown_type;
+ more_ = false;
+ return;
+ }
+ }
+ }
+ }
+
+ void begin_array(json_visitor2& visitor, uint8_t type, std::error_code& ec)
+ {
+ if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth()))
+ {
+ ec = msgpack_errc::max_nesting_depth_exceeded;
+ more_ = false;
+ return;
+ }
+ std::size_t length = get_size(type, ec);
+ if (!more_)
+ {
+ return;
+ }
+ state_stack_.emplace_back(parse_mode::array,length);
+ more_ = visitor.begin_array(length, semantic_tag::none, *this, ec);
+ }
+
+ void end_array(json_visitor2& visitor, std::error_code& ec)
+ {
+ --nesting_depth_;
+
+ more_ = visitor.end_array(*this, ec);
+ state_stack_.pop_back();
+ }
+
+ void begin_object(json_visitor2& visitor, uint8_t type, std::error_code& ec)
+ {
+ if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth()))
+ {
+ ec = msgpack_errc::max_nesting_depth_exceeded;
+ more_ = false;
+ return;
+ }
+ std::size_t length = get_size(type, ec);
+ if (!more_)
+ {
+ return;
+ }
+ state_stack_.emplace_back(parse_mode::map_key,length);
+ more_ = visitor.begin_object(length, semantic_tag::none, *this, ec);
+ }
+
+ void end_object(json_visitor2& visitor, std::error_code& ec)
+ {
+ --nesting_depth_;
+ more_ = visitor.end_object(*this, ec);
+ state_stack_.pop_back();
+ }
+
+ std::size_t get_size(uint8_t type, std::error_code& ec)
+ {
+ switch (type)
+ {
+ case jsoncons::msgpack::msgpack_type::str8_type:
+ case jsoncons::msgpack::msgpack_type::bin8_type:
+ case jsoncons::msgpack::msgpack_type::ext8_type:
+ {
+ uint8_t buf[sizeof(int8_t)];
+ if (source_.read(buf, sizeof(int8_t)) != sizeof(int8_t))
+ {
+ ec = msgpack_errc::unexpected_eof;
+ more_ = false;
+ return 0;
+ }
+ uint8_t len = binary::big_to_native<uint8_t>(buf, sizeof(buf));
+ return static_cast<std::size_t>(len);
+ }
+
+ case jsoncons::msgpack::msgpack_type::str16_type:
+ case jsoncons::msgpack::msgpack_type::bin16_type:
+ case jsoncons::msgpack::msgpack_type::ext16_type:
+ case jsoncons::msgpack::msgpack_type::array16_type:
+ case jsoncons::msgpack::msgpack_type::map16_type:
+ {
+ uint8_t buf[sizeof(int16_t)];
+ if (source_.read(buf, sizeof(int16_t)) != sizeof(int16_t))
+ {
+ ec = msgpack_errc::unexpected_eof;
+ more_ = false;
+ return 0;
+ }
+ uint16_t len = binary::big_to_native<uint16_t>(buf, sizeof(buf));
+ return static_cast<std::size_t>(len);
+ }
+
+ case jsoncons::msgpack::msgpack_type::str32_type:
+ case jsoncons::msgpack::msgpack_type::bin32_type:
+ case jsoncons::msgpack::msgpack_type::ext32_type:
+ case jsoncons::msgpack::msgpack_type::array32_type:
+ case jsoncons::msgpack::msgpack_type::map32_type :
+ {
+ uint8_t buf[sizeof(int32_t)];
+ if (source_.read(buf, sizeof(int32_t)) != sizeof(int32_t))
+ {
+ ec = msgpack_errc::unexpected_eof;
+ more_ = false;
+ return 0;
+ }
+ uint32_t len = binary::big_to_native<uint32_t>(buf, sizeof(buf));
+ return static_cast<std::size_t>(len);
+ }
+ case jsoncons::msgpack::msgpack_type::fixext1_type:
+ return 1;
+ case jsoncons::msgpack::msgpack_type::fixext2_type:
+ return 2;
+ case jsoncons::msgpack::msgpack_type::fixext4_type:
+ return 4;
+ case jsoncons::msgpack::msgpack_type::fixext8_type:
+ return 8;
+ case jsoncons::msgpack::msgpack_type::fixext16_type:
+ return 16;
+ default:
+ if ((type > 0x8f && type <= 0x9f) // fixarray
+ || (type > 0x7f && type <= 0x8f) // fixmap
+ )
+ {
+ return type & 0x0f;
+ }
+ else
+ {
+ ec = msgpack_errc::unknown_type;
+ more_ = false;
+ return 0;
+ }
+ break;
+ }
+ }
+};
+
+}}
+
+#endif
diff --git a/include/jsoncons_ext/msgpack/msgpack_reader.hpp b/include/jsoncons_ext/msgpack/msgpack_reader.hpp
new file mode 100644
index 0000000..c0d788a
--- /dev/null
+++ b/include/jsoncons_ext/msgpack/msgpack_reader.hpp
@@ -0,0 +1,116 @@
+// Copyright 2017 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_MSGPACK_MSGPACK_READER_HPP
+#define JSONCONS_MSGPACK_MSGPACK_READER_HPP
+
+#include <string>
+#include <vector>
+#include <memory>
+#include <utility> // std::move
+#include <jsoncons/json.hpp>
+#include <jsoncons/source.hpp>
+#include <jsoncons/json_visitor.hpp>
+#include <jsoncons/config/jsoncons_config.hpp>
+#include <jsoncons_ext/msgpack/msgpack_type.hpp>
+#include <jsoncons_ext/msgpack/msgpack_error.hpp>
+#include <jsoncons_ext/msgpack/msgpack_parser.hpp>
+
+namespace jsoncons { namespace msgpack {
+
+template <class Source,class Allocator=std::allocator<char>>
+class basic_msgpack_reader
+{
+ using char_type = char;
+
+ basic_msgpack_parser<Source,Allocator> parser_;
+ basic_json_visitor2_to_visitor_adaptor<char_type,Allocator> adaptor_;
+ json_visitor2& visitor_;
+public:
+ template <class Sourceable>
+ basic_msgpack_reader(Sourceable&& source,
+ json_visitor& visitor,
+ const Allocator alloc)
+ : basic_msgpack_reader(std::forward<Sourceable>(source),
+ visitor,
+ msgpack_decode_options(),
+ alloc)
+ {
+ }
+
+ template <class Sourceable>
+ basic_msgpack_reader(Sourceable&& source,
+ json_visitor& visitor,
+ const msgpack_decode_options& options = msgpack_decode_options(),
+ const Allocator alloc=Allocator())
+ : parser_(std::forward<Sourceable>(source), options, alloc),
+ adaptor_(visitor, alloc), visitor_(adaptor_)
+ {
+ }
+ template <class Sourceable>
+ basic_msgpack_reader(Sourceable&& source,
+ json_visitor2& visitor,
+ const Allocator alloc)
+ : basic_msgpack_reader(std::forward<Sourceable>(source),
+ visitor,
+ msgpack_decode_options(),
+ alloc)
+ {
+ }
+
+ template <class Sourceable>
+ basic_msgpack_reader(Sourceable&& source,
+ json_visitor2& visitor,
+ const msgpack_decode_options& options = msgpack_decode_options(),
+ const Allocator alloc=Allocator())
+ : parser_(std::forward<Sourceable>(source), options, alloc),
+ visitor_(visitor)
+ {
+ }
+
+ void read()
+ {
+ std::error_code ec;
+ read(ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec,line(),column()));
+ }
+ }
+
+ void read(std::error_code& ec)
+ {
+ parser_.reset();
+ parser_.parse(visitor_, ec);
+ if (ec)
+ {
+ return;
+ }
+ }
+
+ std::size_t line() const
+ {
+ return parser_.line();
+ }
+
+ std::size_t column() const
+ {
+ return parser_.column();
+ }
+};
+
+using msgpack_stream_reader = basic_msgpack_reader<jsoncons::binary_stream_source>;
+
+using msgpack_bytes_reader = basic_msgpack_reader<jsoncons::bytes_source>;
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+JSONCONS_DEPRECATED_MSG("Instead, use msgpack_stream_reader") typedef msgpack_stream_reader msgpack_reader;
+JSONCONS_DEPRECATED_MSG("Instead, use msgpack_bytes_reader") typedef msgpack_bytes_reader msgpack_buffer_reader;
+#endif
+
+}}
+
+#endif
diff --git a/include/jsoncons_ext/msgpack/msgpack_type.hpp b/include/jsoncons_ext/msgpack/msgpack_type.hpp
new file mode 100644
index 0000000..aa9f8fd
--- /dev/null
+++ b/include/jsoncons_ext/msgpack/msgpack_type.hpp
@@ -0,0 +1,63 @@
+// Copyright 2013 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_MSGPACK_MSGPACK_TYPE_HPP
+#define JSONCONS_MSGPACK_MSGPACK_TYPE_HPP
+
+#include <string>
+#include <memory>
+#include <jsoncons/config/jsoncons_config.hpp>
+
+namespace jsoncons { namespace msgpack {
+
+ namespace msgpack_type
+ {
+ const uint8_t positive_fixint_base_type = 0x00;
+ const uint8_t nil_type = 0xc0;
+ const uint8_t false_type = 0xc2;
+ const uint8_t true_type = 0xc3;
+ const uint8_t float32_type = 0xca;
+ const uint8_t float64_type = 0xcb;
+ const uint8_t uint8_type = 0xcc;
+ const uint8_t uint16_type = 0xcd;
+ const uint8_t uint32_type = 0xce;
+ const uint8_t uint64_type = 0xcf;
+ const uint8_t int8_type = 0xd0;
+ const uint8_t int16_type = 0xd1;
+ const uint8_t int32_type = 0xd2;
+ const uint8_t int64_type = 0xd3;
+
+ const uint8_t fixmap_base_type = 0x80;
+ const uint8_t fixarray_base_type = 0x90;
+ const uint8_t fixstr_base_type = 0xa0;
+ const uint8_t str8_type = 0xd9;
+ const uint8_t str16_type = 0xda;
+ const uint8_t str32_type = 0xdb;
+
+ const uint8_t bin8_type = 0xc4; // 0xC4
+ const uint8_t bin16_type = 0xc5;
+ const uint8_t bin32_type = 0xc6;
+
+ const uint8_t fixext1_type = 0xd4;
+ const uint8_t fixext2_type = 0xd5;
+ const uint8_t fixext4_type = 0xd6;
+ const uint8_t fixext8_type = 0xd7;
+ const uint8_t fixext16_type = 0xd8;
+ const uint8_t ext8_type = 0xc7; // 0xC4
+ const uint8_t ext16_type = 0xc8;
+ const uint8_t ext32_type = 0xc9;
+
+ const uint8_t array16_type = 0xdc;
+ const uint8_t array32_type = 0xdd;
+ const uint8_t map16_type = 0xde;
+ const uint8_t map32_type = 0xdf;
+ const uint8_t negative_fixint_base_type = 0xe0;
+ }
+
+} // namespace msgpack
+} // namespace jsoncons
+
+#endif
diff --git a/include/jsoncons_ext/ubjson/decode_ubjson.hpp b/include/jsoncons_ext/ubjson/decode_ubjson.hpp
new file mode 100644
index 0000000..91c9c0f
--- /dev/null
+++ b/include/jsoncons_ext/ubjson/decode_ubjson.hpp
@@ -0,0 +1,201 @@
+// Copyright 2013 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_UBJSON_DECODE_UBJSON_HPP
+#define JSONCONS_UBJSON_DECODE_UBJSON_HPP
+
+#include <string>
+#include <vector>
+#include <memory>
+#include <type_traits> // std::enable_if
+#include <istream> // std::basic_istream
+#include <jsoncons/json.hpp>
+#include <jsoncons/config/jsoncons_config.hpp>
+#include <jsoncons_ext/ubjson/ubjson_reader.hpp>
+#include <jsoncons_ext/ubjson/ubjson_cursor.hpp>
+
+namespace jsoncons {
+namespace ubjson {
+
+ template<class T, class Source>
+ typename std::enable_if<type_traits::is_basic_json<T>::value &&
+ type_traits::is_byte_sequence<Source>::value,T>::type
+ decode_ubjson(const Source& v,
+ const ubjson_decode_options& options = ubjson_decode_options())
+ {
+ jsoncons::json_decoder<T> decoder;
+ auto adaptor = make_json_visitor_adaptor<json_visitor>(decoder);
+ basic_ubjson_reader<jsoncons::bytes_source> 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<class T, class Source>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value &&
+ type_traits::is_byte_sequence<Source>::value,T>::type
+ decode_ubjson(const Source& v,
+ const ubjson_decode_options& options = ubjson_decode_options())
+ {
+ basic_ubjson_cursor<bytes_source> cursor(v, options);
+ json_decoder<basic_json<char,sorted_policy>> decoder{};
+
+ std::error_code ec;
+ T val = decode_traits<T,char>::decode(cursor, decoder, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column()));
+ }
+ return val;
+ }
+
+ template<class T>
+ typename std::enable_if<type_traits::is_basic_json<T>::value,T>::type
+ decode_ubjson(std::istream& is,
+ const ubjson_decode_options& options = ubjson_decode_options())
+ {
+ jsoncons::json_decoder<T> decoder;
+ auto adaptor = make_json_visitor_adaptor<json_visitor>(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<class T>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value,T>::type
+ decode_ubjson(std::istream& is,
+ const ubjson_decode_options& options = ubjson_decode_options())
+ {
+ basic_ubjson_cursor<binary_stream_source> cursor(is, options);
+ json_decoder<basic_json<char,sorted_policy>> decoder{};
+
+ std::error_code ec;
+ T val = decode_traits<T,char>::decode(cursor, decoder, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column()));
+ }
+ return val;
+ }
+
+ template<class T, class InputIt>
+ typename std::enable_if<type_traits::is_basic_json<T>::value,T>::type
+ decode_ubjson(InputIt first, InputIt last,
+ const ubjson_decode_options& options = ubjson_decode_options())
+ {
+ jsoncons::json_decoder<T> decoder;
+ auto adaptor = make_json_visitor_adaptor<json_visitor>(decoder);
+ basic_ubjson_reader<binary_iterator_source<InputIt>> reader(binary_iterator_source<InputIt>(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<class T, class InputIt>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value,T>::type
+ decode_ubjson(InputIt first, InputIt last,
+ const ubjson_decode_options& options = ubjson_decode_options())
+ {
+ basic_ubjson_cursor<binary_iterator_source<InputIt>> cursor(binary_iterator_source<InputIt>(first, last), options);
+ json_decoder<basic_json<char,sorted_policy>> decoder{};
+
+ std::error_code ec;
+ T val = decode_traits<T,char>::decode(cursor, decoder, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column()));
+ }
+ return val;
+ }
+
+ // With leading allocator parameter
+
+ template<class T, class Source, class TempAllocator>
+ typename std::enable_if<type_traits::is_basic_json<T>::value &&
+ type_traits::is_byte_sequence<Source>::value,T>::type
+ decode_ubjson(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ const Source& v,
+ const ubjson_decode_options& options = ubjson_decode_options())
+ {
+ json_decoder<T,TempAllocator> decoder(temp_alloc);
+ auto adaptor = make_json_visitor_adaptor<json_visitor>(decoder);
+ basic_ubjson_reader<jsoncons::bytes_source,TempAllocator> reader(v, adaptor, options, temp_alloc);
+ reader.read();
+ if (!decoder.is_valid())
+ {
+ JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column()));
+ }
+ return decoder.get_result();
+ }
+
+ template<class T, class Source, class TempAllocator>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value &&
+ type_traits::is_byte_sequence<Source>::value,T>::type
+ decode_ubjson(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ const Source& v,
+ const ubjson_decode_options& options = ubjson_decode_options())
+ {
+ basic_ubjson_cursor<bytes_source,TempAllocator> cursor(v, options, temp_alloc);
+ json_decoder<basic_json<char,sorted_policy,TempAllocator>,TempAllocator> decoder(temp_alloc, temp_alloc);
+
+ std::error_code ec;
+ T val = decode_traits<T,char>::decode(cursor, decoder, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column()));
+ }
+ return val;
+ }
+
+ template<class T,class TempAllocator>
+ typename std::enable_if<type_traits::is_basic_json<T>::value,T>::type
+ decode_ubjson(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ std::istream& is,
+ const ubjson_decode_options& options = ubjson_decode_options())
+ {
+ json_decoder<T,TempAllocator> decoder(temp_alloc);
+ auto adaptor = make_json_visitor_adaptor<json_visitor>(decoder);
+ basic_ubjson_reader<jsoncons::binary_stream_source,TempAllocator> reader(is, adaptor, options, temp_alloc);
+ reader.read();
+ if (!decoder.is_valid())
+ {
+ JSONCONS_THROW(ser_error(conv_errc::conversion_failed, reader.line(), reader.column()));
+ }
+ return decoder.get_result();
+ }
+
+ template<class T,class TempAllocator>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value,T>::type
+ decode_ubjson(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ std::istream& is,
+ const ubjson_decode_options& options = ubjson_decode_options())
+ {
+ basic_ubjson_cursor<binary_stream_source,TempAllocator> cursor(is, options, temp_alloc);
+ json_decoder<basic_json<char,sorted_policy,TempAllocator>,TempAllocator> decoder(temp_alloc, temp_alloc);
+
+ std::error_code ec;
+ T val = decode_traits<T,char>::decode(cursor, decoder, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec, cursor.context().line(), cursor.context().column()));
+ }
+ return val;
+ }
+
+} // ubjson
+} // jsoncons
+
+#endif
diff --git a/include/jsoncons_ext/ubjson/encode_ubjson.hpp b/include/jsoncons_ext/ubjson/encode_ubjson.hpp
new file mode 100644
index 0000000..e8a244b
--- /dev/null
+++ b/include/jsoncons_ext/ubjson/encode_ubjson.hpp
@@ -0,0 +1,142 @@
+// Copyright 2013 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_UBJSON_ENCODE_UBJSON_HPP
+#define JSONCONS_UBJSON_ENCODE_UBJSON_HPP
+
+#include <string>
+#include <vector>
+#include <memory>
+#include <type_traits> // std::enable_if
+#include <istream> // std::basic_istream
+#include <jsoncons/json.hpp>
+#include <jsoncons/config/jsoncons_config.hpp>
+#include <jsoncons_ext/ubjson/ubjson_encoder.hpp>
+#include <jsoncons_ext/ubjson/ubjson_reader.hpp>
+
+namespace jsoncons {
+namespace ubjson {
+
+ template<class T, class Container>
+ typename std::enable_if<type_traits::is_basic_json<T>::value &&
+ type_traits::is_back_insertable_byte_container<Container>::value,void>::type
+ encode_ubjson(const T& j,
+ Container& v,
+ const ubjson_encode_options& options = ubjson_encode_options())
+ {
+ using char_type = typename T::char_type;
+ basic_ubjson_encoder<jsoncons::bytes_sink<Container>> encoder(v, options);
+ auto adaptor = make_json_visitor_adaptor<basic_json_visitor<char_type>>(encoder);
+ j.dump(adaptor);
+ }
+
+ template<class T, class Container>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value &&
+ type_traits::is_back_insertable_byte_container<Container>::value,void>::type
+ encode_ubjson(const T& val,
+ Container& v,
+ const ubjson_encode_options& options = ubjson_encode_options())
+ {
+ basic_ubjson_encoder<jsoncons::bytes_sink<Container>> encoder(v, options);
+ std::error_code ec;
+ encode_traits<T,char>::encode(val, encoder, json(), ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec));
+ }
+ }
+
+ template<class T>
+ typename std::enable_if<type_traits::is_basic_json<T>::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<basic_json_visitor<char_type>>(encoder);
+ j.dump(adaptor);
+ }
+
+ template<class T>
+ typename std::enable_if<!type_traits::is_basic_json<T>::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<T,char>::encode(val, encoder, json(), ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec));
+ }
+ }
+
+ // with temp_allocator_arg_t
+
+ template<class T, class Container, class TempAllocator>
+ typename std::enable_if<type_traits::is_basic_json<T>::value &&
+ type_traits::is_back_insertable_byte_container<Container>::value,void>::type
+ encode_ubjson(temp_allocator_arg_t, const TempAllocator& temp_alloc,const T& j,
+ Container& v,
+ const ubjson_encode_options& options = ubjson_encode_options())
+ {
+ using char_type = typename T::char_type;
+ basic_ubjson_encoder<jsoncons::bytes_sink<Container>,TempAllocator> encoder(v, options, temp_alloc);
+ auto adaptor = make_json_visitor_adaptor<basic_json_visitor<char_type>>(encoder);
+ j.dump(adaptor);
+ }
+
+ template<class T, class Container, class TempAllocator>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value &&
+ type_traits::is_back_insertable_byte_container<Container>::value,void>::type
+ encode_ubjson(temp_allocator_arg_t, const TempAllocator& temp_alloc,const T& val,
+ Container& v,
+ const ubjson_encode_options& options = ubjson_encode_options())
+ {
+ basic_ubjson_encoder<jsoncons::bytes_sink<Container>,TempAllocator> encoder(v, options, temp_alloc);
+ std::error_code ec;
+ encode_traits<T,char>::encode(val, encoder, json(), ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec));
+ }
+ }
+
+ template<class T,class TempAllocator>
+ typename std::enable_if<type_traits::is_basic_json<T>::value,void>::type
+ encode_ubjson(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ const T& j,
+ std::ostream& os,
+ const ubjson_encode_options& options = ubjson_encode_options())
+ {
+ using char_type = typename T::char_type;
+ basic_ubjson_encoder<jsoncons::binary_stream_sink,TempAllocator> encoder(os, options, temp_alloc);
+ auto adaptor = make_json_visitor_adaptor<basic_json_visitor<char_type>>(encoder);
+ j.dump(adaptor);
+ }
+
+ template<class T,class TempAllocator>
+ typename std::enable_if<!type_traits::is_basic_json<T>::value,void>::type
+ encode_ubjson(temp_allocator_arg_t, const TempAllocator& temp_alloc,
+ const T& val,
+ std::ostream& os,
+ const ubjson_encode_options& options = ubjson_encode_options())
+ {
+ basic_ubjson_encoder<jsoncons::binary_stream_sink,TempAllocator> encoder(os, options, temp_alloc);
+ std::error_code ec;
+ encode_traits<T,char>::encode(val, encoder, json(), ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec));
+ }
+ }
+
+} // ubjson
+} // jsoncons
+
+#endif
diff --git a/include/jsoncons_ext/ubjson/ubjson.hpp b/include/jsoncons_ext/ubjson/ubjson.hpp
new file mode 100644
index 0000000..c2729bd
--- /dev/null
+++ b/include/jsoncons_ext/ubjson/ubjson.hpp
@@ -0,0 +1,23 @@
+// Copyright 2013 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_UBJSON_UBJSON_HPP
+#define JSONCONS_UBJSON_UBJSON_HPP
+
+#include <string>
+#include <vector>
+#include <memory>
+#include <type_traits> // std::enable_if
+#include <istream> // std::basic_istream
+#include <jsoncons/json.hpp>
+#include <jsoncons/config/jsoncons_config.hpp>
+#include <jsoncons_ext/ubjson/ubjson_encoder.hpp>
+#include <jsoncons_ext/ubjson/ubjson_reader.hpp>
+#include <jsoncons_ext/ubjson/ubjson_cursor.hpp>
+#include <jsoncons_ext/ubjson/encode_ubjson.hpp>
+#include <jsoncons_ext/ubjson/decode_ubjson.hpp>
+
+#endif
diff --git a/include/jsoncons_ext/ubjson/ubjson_cursor.hpp b/include/jsoncons_ext/ubjson/ubjson_cursor.hpp
new file mode 100644
index 0000000..f60825e
--- /dev/null
+++ b/include/jsoncons_ext/ubjson/ubjson_cursor.hpp
@@ -0,0 +1,307 @@
+// 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)
+
+// See https://github.com/danielaparker/jsoncons for latest version
+
+#ifndef JSONCONS_UBJSON_UBJSON_CURSOR_HPP
+#define JSONCONS_UBJSON_UBJSON_CURSOR_HPP
+
+#include <memory> // std::allocator
+#include <string>
+#include <vector>
+#include <stdexcept>
+#include <system_error>
+#include <ios>
+#include <istream> // std::basic_istream
+#include <jsoncons/byte_string.hpp>
+#include <jsoncons/config/jsoncons_config.hpp>
+#include <jsoncons/json_visitor.hpp>
+#include <jsoncons/json_exception.hpp>
+#include <jsoncons/staj_cursor.hpp>
+#include <jsoncons/source.hpp>
+#include <jsoncons_ext/ubjson/ubjson_parser.hpp>
+
+namespace jsoncons {
+namespace ubjson {
+
+template<class Source=jsoncons::binary_stream_source,class Allocator=std::allocator<char>>
+class basic_ubjson_cursor : public basic_staj_cursor<char>, private virtual ser_context
+{
+public:
+ using source_type = Source;
+ using char_type = char;
+ using allocator_type = Allocator;
+private:
+ basic_ubjson_parser<Source,Allocator> parser_;
+ basic_staj_visitor<char_type> cursor_visitor_;
+ bool eof_;
+
+ // Noncopyable and nonmoveable
+ basic_ubjson_cursor(const basic_ubjson_cursor&) = delete;
+ basic_ubjson_cursor& operator=(const basic_ubjson_cursor&) = delete;
+
+public:
+ using string_view_type = string_view;
+
+ template <class Sourceable>
+ basic_ubjson_cursor(Sourceable&& source,
+ const ubjson_decode_options& options = ubjson_decode_options(),
+ const Allocator& alloc = Allocator())
+ : parser_(std::forward<Sourceable>(source), options, alloc),
+ cursor_visitor_(accept_all),
+ eof_(false)
+ {
+ if (!done())
+ {
+ next();
+ }
+ }
+
+ // Constructors that set parse error codes
+
+ template <class Sourceable>
+ basic_ubjson_cursor(Sourceable&& source,
+ std::error_code& ec)
+ : basic_ubjson_cursor(std::allocator_arg, Allocator(),
+ std::forward<Sourceable>(source),
+ ubjson_decode_options(),
+ ec)
+ {
+ }
+
+ template <class Sourceable>
+ basic_ubjson_cursor(Sourceable&& source,
+ const ubjson_decode_options& options,
+ std::error_code& ec)
+ : basic_ubjson_cursor(std::allocator_arg, Allocator(),
+ std::forward<Sourceable>(source),
+ options,
+ ec)
+ {
+ }
+
+ template <class Sourceable>
+ basic_ubjson_cursor(std::allocator_arg_t, const Allocator& alloc,
+ Sourceable&& source,
+ const ubjson_decode_options& options,
+ std::error_code& ec)
+ : parser_(std::forward<Sourceable>(source), options, alloc),
+ cursor_visitor_(accept_all),
+ eof_(false)
+ {
+ if (!done())
+ {
+ next(ec);
+ }
+ }
+
+ void reset()
+ {
+ parser_.reset();
+ cursor_visitor_.reset();
+ eof_ = false;
+ if (!done())
+ {
+ next();
+ }
+ }
+
+ template <class Sourceable>
+ void reset(Sourceable&& source)
+ {
+ parser_.reset(std::forward<Sourceable>(source));
+ cursor_visitor_.reset();
+ eof_ = false;
+ if (!done())
+ {
+ next();
+ }
+ }
+
+ void reset(std::error_code& ec)
+ {
+ parser_.reset();
+ cursor_visitor_.reset();
+ eof_ = false;
+ if (!done())
+ {
+ next(ec);
+ }
+ }
+
+ template <class Sourceable>
+ void reset(Sourceable&& source, std::error_code& ec)
+ {
+ parser_.reset(std::forward<Sourceable>(source));
+ cursor_visitor_.reset();
+ eof_ = false;
+ if (!done())
+ {
+ next(ec);
+ }
+ }
+
+ bool done() const override
+ {
+ return parser_.done();
+ }
+
+ const staj_event& current() const override
+ {
+ return cursor_visitor_.event();
+ }
+
+ void read_to(basic_json_visitor<char_type>& visitor) override
+ {
+ std::error_code ec;
+ read_to(visitor, ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column()));
+ }
+ }
+
+ void read_to(basic_json_visitor<char_type>& visitor,
+ std::error_code& ec) override
+ {
+ if (staj_to_saj_event(cursor_visitor_.event(), visitor, *this, ec))
+ {
+ read_next(visitor, ec);
+ }
+ }
+
+ void next() override
+ {
+ std::error_code ec;
+ next(ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec,parser_.line(),parser_.column()));
+ }
+ }
+
+ void next(std::error_code& ec) override
+ {
+ read_next(ec);
+ }
+
+ const ser_context& context() const override
+ {
+ return *this;
+ }
+
+ bool eof() const
+ {
+ return eof_;
+ }
+
+ std::size_t line() const override
+ {
+ return parser_.line();
+ }
+
+ std::size_t column() const override
+ {
+ return parser_.column();
+ }
+
+ friend
+ staj_filter_view operator|(basic_ubjson_cursor& cursor,
+ std::function<bool(const staj_event&, const ser_context&)> pred)
+ {
+ return staj_filter_view(cursor, pred);
+ }
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+
+ template <class Sourceable>
+ JSONCONS_DEPRECATED_MSG("Instead, use pipe syntax for filter")
+ basic_ubjson_cursor(Sourceable&& source,
+ std::function<bool(const staj_event&, const ser_context&)> filter,
+ const ubjson_decode_options& options = ubjson_decode_options(),
+ const Allocator& alloc = Allocator())
+ : parser_(std::forward<Sourceable>(source), options, alloc),
+ cursor_visitor_(filter),
+ eof_(false)
+ {
+ if (!done())
+ {
+ next();
+ }
+ }
+
+ template <class Sourceable>
+ JSONCONS_DEPRECATED_MSG("Instead, use pipe syntax for filter")
+ basic_ubjson_cursor(Sourceable&& source,
+ std::function<bool(const staj_event&, const ser_context&)> filter,
+ std::error_code& ec)
+ : basic_ubjson_cursor(std::allocator_arg, Allocator(),
+ std::forward<Sourceable>(source), filter, ec)
+ {
+ }
+
+ template <class Sourceable>
+ JSONCONS_DEPRECATED_MSG("Instead, use pipe syntax for filter")
+ basic_ubjson_cursor(std::allocator_arg_t, const Allocator& alloc,
+ Sourceable&& source,
+ std::function<bool(const staj_event&, const ser_context&)> filter,
+ std::error_code& ec)
+ : parser_(std::forward<Sourceable>(source), alloc),
+ cursor_visitor_(filter),
+ eof_(false)
+ {
+ if (!done())
+ {
+ next(ec);
+ }
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use read_to(basic_json_visitor<char_type>&)")
+ void read(basic_json_visitor<char_type>& visitor)
+ {
+ read_to(visitor);
+ }
+
+ JSONCONS_DEPRECATED_MSG("Instead, use read_to(basic_json_visitor<char_type>&, std::error_code&)")
+ void read(basic_json_visitor<char_type>& visitor,
+ std::error_code& ec)
+ {
+ read_to(visitor, ec);
+ }
+#endif
+private:
+ static bool accept_all(const staj_event&, const ser_context&)
+ {
+ return true;
+ }
+
+ void read_next(std::error_code& ec)
+ {
+ parser_.restart();
+ while (!parser_.stopped())
+ {
+ parser_.parse(cursor_visitor_, ec);
+ if (ec) return;
+ }
+ }
+
+ void read_next(basic_json_visitor<char_type>& visitor, std::error_code& ec)
+ {
+ parser_.restart();
+ while (!parser_.stopped())
+ {
+ parser_.parse(visitor, ec);
+ if (ec) return;
+ }
+ }
+};
+
+using ubjson_stream_cursor = basic_ubjson_cursor<jsoncons::binary_stream_source>;
+using ubjson_bytes_cursor = basic_ubjson_cursor<jsoncons::bytes_source>;
+
+} // namespace ubjson
+} // namespace jsoncons
+
+#endif
+
diff --git a/include/jsoncons_ext/ubjson/ubjson_encoder.hpp b/include/jsoncons_ext/ubjson/ubjson_encoder.hpp
new file mode 100644
index 0000000..2d90e4a
--- /dev/null
+++ b/include/jsoncons_ext/ubjson/ubjson_encoder.hpp
@@ -0,0 +1,502 @@
+// 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)
+
+// See https://github.com/danielaparker/jsoncons for latest version
+
+#ifndef JSONCONS_UBJSON_UBJSON_ENCODER_HPP
+#define JSONCONS_UBJSON_UBJSON_ENCODER_HPP
+
+#include <string>
+#include <vector>
+#include <limits> // std::numeric_limits
+#include <memory>
+#include <utility> // std::move
+#include <jsoncons/json_exception.hpp>
+#include <jsoncons/json_visitor.hpp>
+#include <jsoncons/config/jsoncons_config.hpp>
+#include <jsoncons/sink.hpp>
+#include <jsoncons/detail/parse_number.hpp>
+#include <jsoncons_ext/ubjson/ubjson_type.hpp>
+#include <jsoncons_ext/ubjson/ubjson_error.hpp>
+#include <jsoncons_ext/ubjson/ubjson_options.hpp>
+
+namespace jsoncons { namespace ubjson {
+
+enum class ubjson_container_type {object, indefinite_length_object, array, indefinite_length_array};
+
+template<class Sink=jsoncons::binary_stream_sink,class Allocator=std::allocator<char>>
+class basic_ubjson_encoder final : public basic_json_visitor<char>
+{
+
+ enum class decimal_parse_state { start, integer, exp1, exp2, fraction1 };
+public:
+ using allocator_type = Allocator;
+ using typename basic_json_visitor<char>::string_view_type;
+ using sink_type = Sink;
+
+private:
+ struct stack_item
+ {
+ ubjson_container_type type_;
+ std::size_t length_;
+ std::size_t count_;
+
+ stack_item(ubjson_container_type type, std::size_t length = 0) noexcept
+ : type_(type), length_(length), count_(0)
+ {
+ }
+
+ std::size_t length() const
+ {
+ return length_;
+ }
+
+ std::size_t count() const
+ {
+ return count_;
+ }
+
+ bool is_object() const
+ {
+ return type_ == ubjson_container_type::object || type_ == ubjson_container_type::indefinite_length_object;
+ }
+
+ bool is_indefinite_length() const
+ {
+ return type_ == ubjson_container_type::indefinite_length_array || type_ == ubjson_container_type::indefinite_length_object;
+ }
+
+ };
+
+ Sink sink_;
+ const ubjson_encode_options options_;
+ allocator_type alloc_;
+
+ std::vector<stack_item> stack_;
+ int nesting_depth_;
+
+ // Noncopyable and nonmoveable
+ basic_ubjson_encoder(const basic_ubjson_encoder&) = delete;
+ basic_ubjson_encoder& operator=(const basic_ubjson_encoder&) = delete;
+public:
+ basic_ubjson_encoder(Sink&& sink,
+ const Allocator& alloc = Allocator())
+ : basic_ubjson_encoder(std::forward<Sink>(sink), ubjson_encode_options(), alloc)
+ {
+ }
+
+ explicit basic_ubjson_encoder(Sink&& sink,
+ const ubjson_encode_options& options,
+ const Allocator& alloc = Allocator())
+ : sink_(std::forward<Sink>(sink)),
+ options_(options),
+ alloc_(alloc),
+ nesting_depth_(0)
+ {
+ }
+
+ void reset()
+ {
+ stack_.clear();
+ nesting_depth_ = 0;
+ }
+
+ void reset(Sink&& sink)
+ {
+ sink_ = std::move(sink);
+ reset();
+ }
+
+ ~basic_ubjson_encoder() noexcept
+ {
+ JSONCONS_TRY
+ {
+ sink_.flush();
+ }
+ JSONCONS_CATCH(...)
+ {
+ }
+ }
+
+private:
+ // Implementing methods
+
+ void visit_flush() override
+ {
+ sink_.flush();
+ }
+
+ bool visit_begin_object(semantic_tag, const ser_context&, std::error_code& ec) override
+ {
+ if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth()))
+ {
+ ec = ubjson_errc::max_nesting_depth_exceeded;
+ return false;
+ }
+ stack_.emplace_back(ubjson_container_type::indefinite_length_object);
+ sink_.push_back(jsoncons::ubjson::ubjson_type::start_object_marker);
+
+ return true;
+ }
+
+ bool 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()))
+ {
+ ec = ubjson_errc::max_nesting_depth_exceeded;
+ return false;
+ }
+ stack_.emplace_back(ubjson_container_type::object, length);
+ sink_.push_back(jsoncons::ubjson::ubjson_type::start_object_marker);
+ sink_.push_back(jsoncons::ubjson::ubjson_type::count_marker);
+ put_length(length);
+
+ return true;
+ }
+
+ bool visit_end_object(const ser_context&, std::error_code& ec) override
+ {
+ JSONCONS_ASSERT(!stack_.empty());
+ --nesting_depth_;
+
+ if (stack_.back().is_indefinite_length())
+ {
+ sink_.push_back(jsoncons::ubjson::ubjson_type::end_object_marker);
+ }
+ else
+ {
+ if (stack_.back().count() < stack_.back().length())
+ {
+ ec = ubjson_errc::too_few_items;
+ return false;
+ }
+ if (stack_.back().count() > stack_.back().length())
+ {
+ ec = ubjson_errc::too_many_items;
+ return false;
+ }
+ }
+ stack_.pop_back();
+ end_value();
+ return true;
+ }
+
+ bool visit_begin_array(semantic_tag, const ser_context&, std::error_code& ec) override
+ {
+ if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth()))
+ {
+ ec = ubjson_errc::max_nesting_depth_exceeded;
+ return false;
+ }
+ stack_.emplace_back(ubjson_container_type::indefinite_length_array);
+ sink_.push_back(jsoncons::ubjson::ubjson_type::start_array_marker);
+
+ return true;
+ }
+
+ bool 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()))
+ {
+ ec = ubjson_errc::max_nesting_depth_exceeded;
+ return false;
+ }
+ stack_.emplace_back(ubjson_container_type::array, length);
+ sink_.push_back(jsoncons::ubjson::ubjson_type::start_array_marker);
+ sink_.push_back(jsoncons::ubjson::ubjson_type::count_marker);
+ put_length(length);
+
+ return true;
+ }
+
+ bool visit_end_array(const ser_context&, std::error_code& ec) override
+ {
+ JSONCONS_ASSERT(!stack_.empty());
+ --nesting_depth_;
+
+ if (stack_.back().is_indefinite_length())
+ {
+ sink_.push_back(jsoncons::ubjson::ubjson_type::end_array_marker);
+ }
+ else
+ {
+ if (stack_.back().count() < stack_.back().length())
+ {
+ ec = ubjson_errc::too_few_items;
+ return false;
+ }
+ if (stack_.back().count() > stack_.back().length())
+ {
+ ec = ubjson_errc::too_many_items;
+ return false;
+ }
+ }
+ stack_.pop_back();
+ end_value();
+ return true;
+ }
+
+ bool visit_key(const string_view_type& name, const ser_context&, std::error_code& ec) override
+ {
+ auto sink = unicode_traits::validate(name.data(), name.size());
+ if (sink.ec != unicode_traits::conv_errc())
+ {
+ ec = ubjson_errc::invalid_utf8_text_string;
+ return false;
+ }
+
+ put_length(name.length());
+
+ for (auto c : name)
+ {
+ sink_.push_back(c);
+ }
+ return true;
+ }
+
+ bool visit_null(semantic_tag, const ser_context&, std::error_code&) override
+ {
+ // nil
+ binary::native_to_big(static_cast<uint8_t>(jsoncons::ubjson::ubjson_type::null_type), std::back_inserter(sink_));
+ end_value();
+ return true;
+ }
+
+ bool visit_string(const string_view_type& sv, semantic_tag tag, const ser_context&, std::error_code& ec) override
+ {
+ switch (tag)
+ {
+ case semantic_tag::bigint:
+ case semantic_tag::bigdec:
+ {
+ sink_.push_back(jsoncons::ubjson::ubjson_type::high_precision_number_type);
+ break;
+ }
+ default:
+ {
+ sink_.push_back(jsoncons::ubjson::ubjson_type::string_type);
+ break;
+ }
+ }
+
+ auto sink = unicode_traits::validate(sv.data(), sv.size());
+ if (sink.ec != unicode_traits::conv_errc())
+ {
+ ec = ubjson_errc::invalid_utf8_text_string;
+ return false;
+ }
+
+ put_length(sv.length());
+
+ for (auto c : sv)
+ {
+ sink_.push_back(c);
+ }
+
+ end_value();
+ return true;
+ }
+
+ void put_length(std::size_t length)
+ {
+ if (length <= (std::numeric_limits<uint8_t>::max)())
+ {
+ sink_.push_back(ubjson_type::uint8_type);
+ binary::native_to_big(static_cast<uint8_t>(length), std::back_inserter(sink_));
+ }
+ else if (length <= (std::size_t)(std::numeric_limits<int16_t>::max)())
+ {
+ sink_.push_back(ubjson_type::int16_type);
+ binary::native_to_big(static_cast<uint16_t>(length), std::back_inserter(sink_));
+ }
+ else if (length <= (std::size_t)(std::numeric_limits<int32_t>::max)())
+ {
+ sink_.push_back(ubjson_type::int32_type);
+ binary::native_to_big(static_cast<uint32_t>(length),std::back_inserter(sink_));
+ }
+ else if (length <= (std::size_t)(std::numeric_limits<int64_t>::max)())
+ {
+ sink_.push_back(ubjson_type::int64_type);
+ binary::native_to_big(static_cast<uint64_t>(length),std::back_inserter(sink_));
+ }
+ else
+ {
+ JSONCONS_THROW(ser_error(ubjson_errc::too_many_items));
+ }
+ }
+
+ bool visit_byte_string(const byte_string_view& b,
+ semantic_tag,
+ const ser_context&,
+ std::error_code&) override
+ {
+
+ const size_t length = b.size();
+ sink_.push_back(jsoncons::ubjson::ubjson_type::start_array_marker);
+ binary::native_to_big(static_cast<uint8_t>(jsoncons::ubjson::ubjson_type::type_marker), std::back_inserter(sink_));
+ binary::native_to_big(static_cast<uint8_t>(jsoncons::ubjson::ubjson_type::uint8_type), std::back_inserter(sink_));
+ put_length(length);
+
+ for (auto c : b)
+ {
+ sink_.push_back(c);
+ }
+
+ end_value();
+ return true;
+ }
+
+ bool visit_double(double val,
+ semantic_tag,
+ const ser_context&,
+ std::error_code&) override
+ {
+ float valf = (float)val;
+ if ((double)valf == val)
+ {
+ // float 32
+ sink_.push_back(static_cast<uint8_t>(jsoncons::ubjson::ubjson_type::float32_type));
+ binary::native_to_big(valf,std::back_inserter(sink_));
+ }
+ else
+ {
+ // float 64
+ sink_.push_back(static_cast<uint8_t>(jsoncons::ubjson::ubjson_type::float64_type));
+ binary::native_to_big(val,std::back_inserter(sink_));
+ }
+
+ // write double
+
+ end_value();
+ return true;
+ }
+
+ bool visit_int64(int64_t val,
+ semantic_tag,
+ const ser_context&,
+ std::error_code&) override
+ {
+ if (val >= 0)
+ {
+ if (val <= (std::numeric_limits<uint8_t>::max)())
+ {
+ // uint 8 stores a 8-bit unsigned integer
+ sink_.push_back(jsoncons::ubjson::ubjson_type::uint8_type);
+ binary::native_to_big(static_cast<uint8_t>(val),std::back_inserter(sink_));
+ }
+ else if (val <= (std::numeric_limits<int16_t>::max)())
+ {
+ // uint 16 stores a 16-bit big-endian unsigned integer
+ sink_.push_back(jsoncons::ubjson::ubjson_type::int16_type);
+ binary::native_to_big(static_cast<int16_t>(val),std::back_inserter(sink_));
+ }
+ else if (val <= (std::numeric_limits<int32_t>::max)())
+ {
+ // uint 32 stores a 32-bit big-endian unsigned integer
+ sink_.push_back(jsoncons::ubjson::ubjson_type::int32_type);
+ binary::native_to_big(static_cast<int32_t>(val),std::back_inserter(sink_));
+ }
+ else if (val <= (std::numeric_limits<int64_t>::max)())
+ {
+ // int 64 stores a 64-bit big-endian signed integer
+ sink_.push_back(jsoncons::ubjson::ubjson_type::int64_type);
+ binary::native_to_big(static_cast<int64_t>(val),std::back_inserter(sink_));
+ }
+ else
+ {
+ // big integer
+ }
+ }
+ else
+ {
+ if (val >= (std::numeric_limits<int8_t>::lowest)())
+ {
+ // int 8 stores a 8-bit signed integer
+ sink_.push_back(jsoncons::ubjson::ubjson_type::int8_type);
+ binary::native_to_big(static_cast<int8_t>(val),std::back_inserter(sink_));
+ }
+ else if (val >= (std::numeric_limits<int16_t>::lowest)())
+ {
+ // int 16 stores a 16-bit big-endian signed integer
+ sink_.push_back(jsoncons::ubjson::ubjson_type::int16_type);
+ binary::native_to_big(static_cast<int16_t>(val),std::back_inserter(sink_));
+ }
+ else if (val >= (std::numeric_limits<int32_t>::lowest)())
+ {
+ // int 32 stores a 32-bit big-endian signed integer
+ sink_.push_back(jsoncons::ubjson::ubjson_type::int32_type);
+ binary::native_to_big(static_cast<int32_t>(val),std::back_inserter(sink_));
+ }
+ else if (val >= (std::numeric_limits<int64_t>::lowest)())
+ {
+ // int 64 stores a 64-bit big-endian signed integer
+ sink_.push_back(jsoncons::ubjson::ubjson_type::int64_type);
+ binary::native_to_big(static_cast<int64_t>(val),std::back_inserter(sink_));
+ }
+ }
+ end_value();
+ return true;
+ }
+
+ bool visit_uint64(uint64_t val,
+ semantic_tag,
+ const ser_context&,
+ std::error_code&) override
+ {
+ if (val <= (std::numeric_limits<uint8_t>::max)())
+ {
+ sink_.push_back(jsoncons::ubjson::ubjson_type::uint8_type);
+ binary::native_to_big(static_cast<uint8_t>(val),std::back_inserter(sink_));
+ }
+ else if (val <= static_cast<uint64_t>((std::numeric_limits<int16_t>::max)()))
+ {
+ sink_.push_back(jsoncons::ubjson::ubjson_type::int16_type);
+ binary::native_to_big(static_cast<int16_t>(val),std::back_inserter(sink_));
+ }
+ else if (val <= static_cast<uint64_t>((std::numeric_limits<int32_t>::max)()))
+ {
+ sink_.push_back(jsoncons::ubjson::ubjson_type::int32_type);
+ binary::native_to_big(static_cast<int32_t>(val),std::back_inserter(sink_));
+ }
+ else if (val <= static_cast<uint64_t>((std::numeric_limits<int64_t>::max)()))
+ {
+ sink_.push_back(jsoncons::ubjson::ubjson_type::int64_type);
+ binary::native_to_big(static_cast<int64_t>(val),std::back_inserter(sink_));
+ }
+ end_value();
+ return true;
+ }
+
+ bool visit_bool(bool val, semantic_tag, const ser_context&, std::error_code&) override
+ {
+ // true and false
+ sink_.push_back(static_cast<uint8_t>(val ? jsoncons::ubjson::ubjson_type::true_type : jsoncons::ubjson::ubjson_type::false_type));
+
+ end_value();
+ return true;
+ }
+
+ void end_value()
+ {
+ if (!stack_.empty())
+ {
+ ++stack_.back().count_;
+ }
+ }
+};
+
+using ubjson_stream_encoder = basic_ubjson_encoder<jsoncons::binary_stream_sink>;
+using ubjson_bytes_encoder = basic_ubjson_encoder<jsoncons::bytes_sink<std::vector<uint8_t>>>;
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+template<class Sink=jsoncons::binary_stream_sink>
+using basic_ubjson_serializer = basic_ubjson_encoder<Sink>;
+
+JSONCONS_DEPRECATED_MSG("Instead, use ubjson_stream_encoder") typedef ubjson_stream_encoder ubjson_encoder;
+JSONCONS_DEPRECATED_MSG("Instead, use ubjson_stream_encoder") typedef ubjson_stream_encoder ubjson_serializer;
+JSONCONS_DEPRECATED_MSG("Instead, use ubjson_bytes_encoder") typedef ubjson_bytes_encoder ubjson_buffer_serializer;
+#endif
+
+}}
+#endif
diff --git a/include/jsoncons_ext/ubjson/ubjson_error.hpp b/include/jsoncons_ext/ubjson/ubjson_error.hpp
new file mode 100644
index 0000000..cc7f5b8
--- /dev/null
+++ b/include/jsoncons_ext/ubjson/ubjson_error.hpp
@@ -0,0 +1,100 @@
+/// 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)
+
+// See https://github.com/danielaparker/jsoncons for latest version
+
+#ifndef JSONCONS_UBJSON_UBJSON_ERROR_HPP
+#define JSONCONS_UBJSON_UBJSON_ERROR_HPP
+
+#include <system_error>
+#include <jsoncons/config/jsoncons_config.hpp>
+
+namespace jsoncons { namespace ubjson {
+
+enum class ubjson_errc
+{
+ success = 0,
+ unexpected_eof = 1,
+ source_error,
+ count_required_after_type,
+ length_is_negative,
+ length_must_be_integer,
+ unknown_type,
+ invalid_utf8_text_string,
+ too_many_items,
+ too_few_items,
+ number_too_large,
+ max_nesting_depth_exceeded,
+ key_expected,
+ max_items_exceeded
+};
+
+class ubjson_error_category_impl
+ : public std::error_category
+{
+public:
+ const char* name() const noexcept override
+ {
+ return "jsoncons/ubjson";
+ }
+ std::string message(int ev) const override
+ {
+ switch (static_cast<ubjson_errc>(ev))
+ {
+ case ubjson_errc::unexpected_eof:
+ return "Unexpected end of file";
+ case ubjson_errc::source_error:
+ return "Source error";
+ case ubjson_errc::count_required_after_type:
+ return "Type is specified for container, but count is not specified";
+ case ubjson_errc::length_is_negative:
+ return "Request for the length of an array, map or string returned a negative result";
+ case ubjson_errc::length_must_be_integer:
+ return "Length must be a integer numeric type (int8, uint8, int16, int32, int64)";
+ case ubjson_errc::unknown_type:
+ return "Unknown type";
+ case ubjson_errc::invalid_utf8_text_string:
+ return "Illegal UTF-8 encoding in text string";
+ case ubjson_errc::too_many_items:
+ return "Too many items were added to a UBJSON object or array of known length";
+ case ubjson_errc::too_few_items:
+ return "Too few items were added to a UBJSON object or array of known length";
+ case ubjson_errc::number_too_large:
+ return "Number exceeds implementation limits";
+ case ubjson_errc::max_nesting_depth_exceeded:
+ return "Data item nesting exceeds limit in options";
+ case ubjson_errc::key_expected:
+ return "Text string key in a map expected";
+ case ubjson_errc::max_items_exceeded:
+ return "Number of items in UBJSON object or array exceeds limit set in options";
+ default:
+ return "Unknown UBJSON parser error";
+ }
+ }
+};
+
+inline
+const std::error_category& ubjson_error_category()
+{
+ static ubjson_error_category_impl instance;
+ return instance;
+}
+
+inline
+std::error_code make_error_code(ubjson_errc e)
+{
+ return std::error_code(static_cast<int>(e),ubjson_error_category());
+}
+
+
+}}
+
+namespace std {
+ template<>
+ struct is_error_code_enum<jsoncons::ubjson::ubjson_errc> : public true_type
+ {
+ };
+}
+
+#endif
diff --git a/include/jsoncons_ext/ubjson/ubjson_options.hpp b/include/jsoncons_ext/ubjson/ubjson_options.hpp
new file mode 100644
index 0000000..1498d50
--- /dev/null
+++ b/include/jsoncons_ext/ubjson/ubjson_options.hpp
@@ -0,0 +1,87 @@
+// Copyright 2019 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_UBJSON_UBJSON_OPTIONS_HPP
+#define JSONCONS_UBJSON_UBJSON_OPTIONS_HPP
+
+#include <string>
+#include <limits> // std::numeric_limits
+#include <cwchar>
+#include <jsoncons/json_exception.hpp>
+
+namespace jsoncons { namespace ubjson {
+
+class ubjson_options;
+
+class ubjson_options_common
+{
+ friend class ubjson_options;
+
+ int max_nesting_depth_;
+protected:
+ virtual ~ubjson_options_common() = default;
+
+ ubjson_options_common()
+ : max_nesting_depth_(1024)
+ {
+ }
+
+ 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;
+public:
+ int max_nesting_depth() const
+ {
+ return max_nesting_depth_;
+ }
+};
+
+class ubjson_decode_options : public virtual ubjson_options_common
+{
+ friend class ubjson_options;
+ std::size_t max_items_;
+public:
+ ubjson_decode_options() :
+ max_items_(1 << 24)
+ {
+ }
+
+ std::size_t max_items() const
+ {
+ return max_items_;
+ }
+};
+
+class ubjson_encode_options : public virtual ubjson_options_common
+{
+ friend class ubjson_options;
+public:
+ ubjson_encode_options()
+ {
+ }
+};
+
+class ubjson_options final : public ubjson_decode_options, public ubjson_encode_options
+{
+public:
+ using ubjson_options_common::max_nesting_depth;
+
+ ubjson_options& max_nesting_depth(int value)
+ {
+ this->max_nesting_depth_ = value;
+ return *this;
+ }
+
+ ubjson_options& max_items(std::size_t value)
+ {
+ this->max_items_ = value;
+ return *this;
+ }
+};
+
+}}
+#endif
diff --git a/include/jsoncons_ext/ubjson/ubjson_parser.hpp b/include/jsoncons_ext/ubjson/ubjson_parser.hpp
new file mode 100644
index 0000000..468f139
--- /dev/null
+++ b/include/jsoncons_ext/ubjson/ubjson_parser.hpp
@@ -0,0 +1,880 @@
+// Copyright 2017 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_UBJSON_UBJSON_PARSER_HPP
+#define JSONCONS_UBJSON_UBJSON_PARSER_HPP
+
+#include <string>
+#include <memory>
+#include <utility> // std::move
+#include <jsoncons/json.hpp>
+#include <jsoncons/source.hpp>
+#include <jsoncons/json_visitor.hpp>
+#include <jsoncons/config/jsoncons_config.hpp>
+#include <jsoncons_ext/ubjson/ubjson_type.hpp>
+#include <jsoncons_ext/ubjson/ubjson_error.hpp>
+#include <jsoncons_ext/ubjson/ubjson_options.hpp>
+
+namespace jsoncons { namespace ubjson {
+
+enum class parse_mode {root,accept,array,indefinite_array,strongly_typed_array,map_key,map_value,strongly_typed_map_key,strongly_typed_map_value,indefinite_map_key,indefinite_map_value};
+
+struct parse_state
+{
+ parse_mode mode;
+ std::size_t length;
+ uint8_t type;
+ std::size_t index;
+
+ parse_state(parse_mode mode, std::size_t length, uint8_t type = 0) noexcept
+ : mode(mode), length(length), type(type), index(0)
+ {
+ }
+
+ parse_state(const parse_state&) = default;
+ parse_state(parse_state&&) = default;
+};
+
+template <class Source,class Allocator=std::allocator<char>>
+class basic_ubjson_parser : public ser_context
+{
+ using char_type = char;
+ using char_traits_type = std::char_traits<char>;
+ using temp_allocator_type = Allocator;
+ using char_allocator_type = typename std::allocator_traits<temp_allocator_type>:: template rebind_alloc<char_type>;
+ using byte_allocator_type = typename std::allocator_traits<temp_allocator_type>:: template rebind_alloc<uint8_t>;
+ using parse_state_allocator_type = typename std::allocator_traits<temp_allocator_type>:: template rebind_alloc<parse_state>;
+
+ Source source_;
+ ubjson_decode_options options_;
+ bool more_;
+ bool done_;
+ std::basic_string<char,std::char_traits<char>,char_allocator_type> text_buffer_;
+ std::vector<parse_state,parse_state_allocator_type> state_stack_;
+ int nesting_depth_;
+public:
+ template <class Sourceable>
+ basic_ubjson_parser(Sourceable&& source,
+ const ubjson_decode_options& options = ubjson_decode_options(),
+ const Allocator alloc = Allocator())
+ : source_(std::forward<Sourceable>(source)),
+ options_(options),
+ more_(true),
+ done_(false),
+ text_buffer_(alloc),
+ state_stack_(alloc),
+ nesting_depth_(0)
+ {
+ state_stack_.emplace_back(parse_mode::root,0);
+ }
+
+ void restart()
+ {
+ more_ = true;
+ }
+
+ void reset()
+ {
+ more_ = true;
+ done_ = false;
+ text_buffer_.clear();
+ state_stack_.clear();
+ state_stack_.emplace_back(parse_mode::root,0,0);
+ nesting_depth_ = 0;
+ }
+
+ template <class Sourceable>
+ void reset(Sourceable&& source)
+ {
+ source_ = std::forward<Sourceable>(source);
+ reset();
+ }
+
+ bool done() const
+ {
+ return done_;
+ }
+
+ bool stopped() const
+ {
+ return !more_;
+ }
+
+ std::size_t line() const override
+ {
+ return 0;
+ }
+
+ std::size_t column() const override
+ {
+ return source_.position();
+ }
+
+ void parse(json_visitor& visitor, std::error_code& ec)
+ {
+ while (!done_ && more_)
+ {
+ switch (state_stack_.back().mode)
+ {
+ case parse_mode::array:
+ {
+ if (state_stack_.back().index < state_stack_.back().length)
+ {
+ ++state_stack_.back().index;
+ read_type_and_value(visitor, ec);
+ if (ec)
+ {
+ return;
+ }
+ }
+ else
+ {
+ end_array(visitor, ec);
+ }
+ break;
+ }
+ case parse_mode::strongly_typed_array:
+ {
+ if (state_stack_.back().index < state_stack_.back().length)
+ {
+ ++state_stack_.back().index;
+ read_value(visitor, state_stack_.back().type, ec);
+ if (ec)
+ {
+ return;
+ }
+ }
+ else
+ {
+ end_array(visitor, ec);
+ }
+ break;
+ }
+ case parse_mode::indefinite_array:
+ {
+ auto c = source_.peek();
+ if (c.eof)
+ {
+ ec = ubjson_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ if (c.value == jsoncons::ubjson::ubjson_type::end_array_marker)
+ {
+ source_.ignore(1);
+ end_array(visitor, ec);
+ if (ec)
+ {
+ return;
+ }
+ }
+ else
+ {
+ if (++state_stack_.back().index > options_.max_items())
+ {
+ ec = ubjson_errc::max_items_exceeded;
+ more_ = false;
+ return;
+ }
+ read_type_and_value(visitor, ec);
+ if (ec)
+ {
+ return;
+ }
+ }
+ break;
+ }
+ case parse_mode::map_key:
+ {
+ if (state_stack_.back().index < state_stack_.back().length)
+ {
+ ++state_stack_.back().index;
+ read_key(visitor, ec);
+ if (ec)
+ {
+ return;
+ }
+ state_stack_.back().mode = parse_mode::map_value;
+ }
+ else
+ {
+ end_object(visitor, ec);
+ }
+ break;
+ }
+ case parse_mode::map_value:
+ {
+ state_stack_.back().mode = parse_mode::map_key;
+ read_type_and_value(visitor, ec);
+ if (ec)
+ {
+ return;
+ }
+ break;
+ }
+ case parse_mode::strongly_typed_map_key:
+ {
+ if (state_stack_.back().index < state_stack_.back().length)
+ {
+ ++state_stack_.back().index;
+ read_key(visitor, ec);
+ if (ec)
+ {
+ return;
+ }
+ state_stack_.back().mode = parse_mode::strongly_typed_map_value;
+ }
+ else
+ {
+ end_object(visitor, ec);
+ }
+ break;
+ }
+ case parse_mode::strongly_typed_map_value:
+ {
+ state_stack_.back().mode = parse_mode::strongly_typed_map_key;
+ read_value(visitor, state_stack_.back().type, ec);
+ if (ec)
+ {
+ return;
+ }
+ break;
+ }
+ case parse_mode::indefinite_map_key:
+ {
+ auto c = source_.peek();
+ if (c.eof)
+ {
+ ec = ubjson_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ if (c.value == jsoncons::ubjson::ubjson_type::end_object_marker)
+ {
+ source_.ignore(1);
+ end_object(visitor, ec);
+ if (ec)
+ {
+ return;
+ }
+ }
+ else
+ {
+ if (++state_stack_.back().index > options_.max_items())
+ {
+ ec = ubjson_errc::max_items_exceeded;
+ more_ = false;
+ return;
+ }
+ read_key(visitor, ec);
+ if (ec)
+ {
+ return;
+ }
+ state_stack_.back().mode = parse_mode::indefinite_map_value;
+ }
+ break;
+ }
+ case parse_mode::indefinite_map_value:
+ {
+ state_stack_.back().mode = parse_mode::indefinite_map_key;
+ read_type_and_value(visitor, ec);
+ if (ec)
+ {
+ return;
+ }
+ break;
+ }
+ case parse_mode::root:
+ {
+ state_stack_.back().mode = parse_mode::accept;
+ read_type_and_value(visitor, ec);
+ if (ec)
+ {
+ return;
+ }
+ break;
+ }
+ case parse_mode::accept:
+ {
+ JSONCONS_ASSERT(state_stack_.size() == 1);
+ state_stack_.clear();
+ more_ = false;
+ done_ = true;
+ visitor.flush();
+ break;
+ }
+ }
+ }
+ }
+private:
+ void read_type_and_value(json_visitor& visitor, std::error_code& ec)
+ {
+ if (source_.is_error())
+ {
+ ec = ubjson_errc::source_error;
+ more_ = false;
+ return;
+ }
+
+ uint8_t b;
+ if (source_.read(&b, 1) == 0)
+ {
+ ec = ubjson_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ read_value(visitor, b, ec);
+ }
+
+ void read_value(json_visitor& visitor, uint8_t type, std::error_code& ec)
+ {
+ switch (type)
+ {
+ case jsoncons::ubjson::ubjson_type::null_type:
+ {
+ more_ = visitor.null_value(semantic_tag::none, *this, ec);
+ break;
+ }
+ case jsoncons::ubjson::ubjson_type::no_op_type:
+ {
+ break;
+ }
+ case jsoncons::ubjson::ubjson_type::true_type:
+ {
+ more_ = visitor.bool_value(true, semantic_tag::none, *this, ec);
+ break;
+ }
+ case jsoncons::ubjson::ubjson_type::false_type:
+ {
+ more_ = visitor.bool_value(false, semantic_tag::none, *this, ec);
+ break;
+ }
+ case jsoncons::ubjson::ubjson_type::int8_type:
+ {
+ uint8_t buf[sizeof(int8_t)];
+ if (source_.read(buf, sizeof(int8_t)) != sizeof(int8_t))
+ {
+ ec = ubjson_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ int8_t val = binary::big_to_native<int8_t>(buf, sizeof(buf));
+ more_ = visitor.int64_value(val, semantic_tag::none, *this, ec);
+ break;
+ }
+ case jsoncons::ubjson::ubjson_type::uint8_type:
+ {
+ uint8_t b;
+ if (source_.read(&b, 1) == 0)
+ {
+ ec = ubjson_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ more_ = visitor.uint64_value(b, semantic_tag::none, *this, ec);
+ break;
+ }
+ case jsoncons::ubjson::ubjson_type::int16_type:
+ {
+ uint8_t buf[sizeof(int16_t)];
+ if (source_.read(buf, sizeof(int16_t)) != sizeof(int16_t))
+ {
+ ec = ubjson_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ int16_t val = binary::big_to_native<int16_t>(buf, sizeof(buf));
+ more_ = visitor.int64_value(val, semantic_tag::none, *this, ec);
+ break;
+ }
+ case jsoncons::ubjson::ubjson_type::int32_type:
+ {
+ uint8_t buf[sizeof(int32_t)];
+ if (source_.read(buf, sizeof(int32_t)) != sizeof(int32_t))
+ {
+ ec = ubjson_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ int32_t val = binary::big_to_native<int32_t>(buf, sizeof(buf));
+ more_ = visitor.int64_value(val, semantic_tag::none, *this, ec);
+ break;
+ }
+ case jsoncons::ubjson::ubjson_type::int64_type:
+ {
+ uint8_t buf[sizeof(int64_t)];
+ if (source_.read(buf, sizeof(int64_t)) != sizeof(int64_t))
+ {
+ ec = ubjson_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ int64_t val = binary::big_to_native<int64_t>(buf, sizeof(buf));
+ more_ = visitor.int64_value(val, semantic_tag::none, *this, ec);
+ break;
+ }
+ case jsoncons::ubjson::ubjson_type::float32_type:
+ {
+ uint8_t buf[sizeof(float)];
+ if (source_.read(buf, sizeof(float)) != sizeof(float))
+ {
+ ec = ubjson_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ float val = binary::big_to_native<float>(buf, sizeof(buf));
+ more_ = visitor.double_value(val, semantic_tag::none, *this, ec);
+ break;
+ }
+ case jsoncons::ubjson::ubjson_type::float64_type:
+ {
+ uint8_t buf[sizeof(double)];
+ if (source_.read(buf, sizeof(double)) != sizeof(double))
+ {
+ ec = ubjson_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ double val = binary::big_to_native<double>(buf, sizeof(buf));
+ more_ = visitor.double_value(val, semantic_tag::none, *this, ec);
+ break;
+ }
+ case jsoncons::ubjson::ubjson_type::char_type:
+ {
+ text_buffer_.clear();
+ if (source_reader<Source>::read(source_,text_buffer_,1) != 1)
+ {
+ ec = ubjson_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ auto result = unicode_traits::validate(text_buffer_.data(),text_buffer_.size());
+ if (result.ec != unicode_traits::conv_errc())
+ {
+ ec = ubjson_errc::invalid_utf8_text_string;
+ more_ = false;
+ return;
+ }
+ more_ = visitor.string_value(text_buffer_, semantic_tag::none, *this, ec);
+ break;
+ }
+ case jsoncons::ubjson::ubjson_type::string_type:
+ {
+ std::size_t length = get_length(ec);
+ if (ec)
+ {
+ return;
+ }
+ text_buffer_.clear();
+ if (source_reader<Source>::read(source_,text_buffer_,length) != length)
+ {
+ ec = ubjson_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ auto result = unicode_traits::validate(text_buffer_.data(),text_buffer_.size());
+ if (result.ec != unicode_traits::conv_errc())
+ {
+ ec = ubjson_errc::invalid_utf8_text_string;
+ more_ = false;
+ return;
+ }
+ more_ = visitor.string_value(jsoncons::basic_string_view<char>(text_buffer_.data(),text_buffer_.length()), semantic_tag::none, *this, ec);
+ break;
+ }
+ case jsoncons::ubjson::ubjson_type::high_precision_number_type:
+ {
+ std::size_t length = get_length(ec);
+ if (ec)
+ {
+ return;
+ }
+ text_buffer_.clear();
+ if (source_reader<Source>::read(source_,text_buffer_,length) != length)
+ {
+ ec = ubjson_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ if (jsoncons::detail::is_base10(text_buffer_.data(),text_buffer_.length()))
+ {
+ more_ = visitor.string_value(jsoncons::basic_string_view<char>(text_buffer_.data(),text_buffer_.length()), semantic_tag::bigint, *this, ec);
+ }
+ else
+ {
+ more_ = visitor.string_value(jsoncons::basic_string_view<char>(text_buffer_.data(),text_buffer_.length()), semantic_tag::bigdec, *this, ec);
+ }
+ break;
+ }
+ case jsoncons::ubjson::ubjson_type::start_array_marker:
+ {
+ begin_array(visitor,ec);
+ break;
+ }
+ case jsoncons::ubjson::ubjson_type::start_object_marker:
+ {
+ begin_object(visitor, ec);
+ break;
+ }
+ default:
+ {
+ ec = ubjson_errc::unknown_type;
+ break;
+ }
+ }
+ if (ec)
+ {
+ more_ = false;
+ }
+ }
+
+ void begin_array(json_visitor& visitor, std::error_code& ec)
+ {
+ if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth()))
+ {
+ ec = ubjson_errc::max_nesting_depth_exceeded;
+ more_ = false;
+ return;
+ }
+
+ auto c = source_.peek();
+ if (c.eof)
+ {
+ ec = ubjson_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ if (c.value == jsoncons::ubjson::ubjson_type::type_marker)
+ {
+ source_.ignore(1);
+ uint8_t b;
+ if (source_.read(&b, 1) == 0)
+ {
+ ec = ubjson_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ c = source_.peek();
+ if (c.eof)
+ {
+ ec = ubjson_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ if (c.value == jsoncons::ubjson::ubjson_type::count_marker)
+ {
+ source_.ignore(1);
+ std::size_t length = get_length(ec);
+ if (ec)
+ {
+ return;
+ }
+ if (length > options_.max_items())
+ {
+ ec = ubjson_errc::max_items_exceeded;
+ more_ = false;
+ return;
+ }
+ state_stack_.emplace_back(parse_mode::strongly_typed_array,length,b);
+ more_ = visitor.begin_array(length, semantic_tag::none, *this, ec);
+ }
+ else
+ {
+ ec = ubjson_errc::count_required_after_type;
+ more_ = false;
+ return;
+ }
+ }
+ else if (c.value == jsoncons::ubjson::ubjson_type::count_marker)
+ {
+ source_.ignore(1);
+ std::size_t length = get_length(ec);
+ if (ec)
+ {
+ return;
+ }
+ if (length > options_.max_items())
+ {
+ ec = ubjson_errc::max_items_exceeded;
+ more_ = false;
+ return;
+ }
+ state_stack_.emplace_back(parse_mode::array,length);
+ more_ = visitor.begin_array(length, semantic_tag::none, *this, ec);
+ }
+ else
+ {
+ state_stack_.emplace_back(parse_mode::indefinite_array,0);
+ more_ = visitor.begin_array(semantic_tag::none, *this, ec);
+ }
+ }
+
+ void end_array(json_visitor& visitor, std::error_code& ec)
+ {
+ --nesting_depth_;
+
+ more_ = visitor.end_array(*this, ec);
+ state_stack_.pop_back();
+ }
+
+ void begin_object(json_visitor& visitor, std::error_code& ec)
+ {
+ if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth()))
+ {
+ ec = ubjson_errc::max_nesting_depth_exceeded;
+ more_ = false;
+ return;
+ }
+
+ auto c = source_.peek();
+ if (c.eof)
+ {
+ ec = ubjson_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ if (c.value == jsoncons::ubjson::ubjson_type::type_marker)
+ {
+ source_.ignore(1);
+ uint8_t b;
+ if (source_.read(&b, 1) == 0)
+ {
+ ec = ubjson_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ c = source_.peek();
+ if (c.eof)
+ {
+ ec = ubjson_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ if (c.value == jsoncons::ubjson::ubjson_type::count_marker)
+ {
+ source_.ignore(1);
+ std::size_t length = get_length(ec);
+ if (ec)
+ {
+ return;
+ }
+ if (length > options_.max_items())
+ {
+ ec = ubjson_errc::max_items_exceeded;
+ more_ = false;
+ return;
+ }
+ state_stack_.emplace_back(parse_mode::strongly_typed_map_key,length,b);
+ more_ = visitor.begin_object(length, semantic_tag::none, *this, ec);
+ }
+ else
+ {
+ ec = ubjson_errc::count_required_after_type;
+ more_ = false;
+ return;
+ }
+ }
+ else
+ {
+ c = source_.peek();
+ if (c.eof)
+ {
+ ec = ubjson_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+ if (c.value == jsoncons::ubjson::ubjson_type::count_marker)
+ {
+ source_.ignore(1);
+ std::size_t length = get_length(ec);
+ if (ec)
+ {
+ return;
+ }
+ if (length > options_.max_items())
+ {
+ ec = ubjson_errc::max_items_exceeded;
+ more_ = false;
+ return;
+ }
+ state_stack_.emplace_back(parse_mode::map_key,length);
+ more_ = visitor.begin_object(length, semantic_tag::none, *this, ec);
+ }
+ else
+ {
+ state_stack_.emplace_back(parse_mode::indefinite_map_key,0);
+ more_ = visitor.begin_object(semantic_tag::none, *this, ec);
+ }
+ }
+ }
+
+ void end_object(json_visitor& visitor, std::error_code& ec)
+ {
+ --nesting_depth_;
+ more_ = visitor.end_object(*this, ec);
+ state_stack_.pop_back();
+ }
+
+ std::size_t get_length(std::error_code& ec)
+ {
+ std::size_t length = 0;
+ uint8_t type;
+ if (source_.read(&type, 1) == 0)
+ {
+ ec = ubjson_errc::unexpected_eof;
+ more_ = false;
+ return length;
+ }
+ switch (type)
+ {
+ case jsoncons::ubjson::ubjson_type::int8_type:
+ {
+ uint8_t buf[sizeof(int8_t)];
+ if (source_.read(buf, sizeof(int8_t)) != sizeof(int8_t))
+ {
+ ec = ubjson_errc::unexpected_eof;
+ more_ = false;
+ return length;
+ }
+ int8_t val = binary::big_to_native<int8_t>(buf, sizeof(buf));
+ if (val >= 0)
+ {
+ length = val;
+ }
+ else
+ {
+ ec = ubjson_errc::length_is_negative;
+ more_ = false;
+ return length;
+ }
+ break;
+ }
+ case jsoncons::ubjson::ubjson_type::uint8_type:
+ {
+ uint8_t b;
+ if (source_.read(&b, 1) == 0)
+ {
+ ec = ubjson_errc::unexpected_eof;
+ more_ = false;
+ return length;
+ }
+ length = b;
+ break;
+ }
+ case jsoncons::ubjson::ubjson_type::int16_type:
+ {
+ uint8_t buf[sizeof(int16_t)];
+ if (source_.read(buf, sizeof(int16_t)) != sizeof(int16_t))
+ {
+ ec = ubjson_errc::unexpected_eof;
+ more_ = false;
+ return length;
+ }
+ int16_t val = binary::big_to_native<int16_t>(buf, sizeof(buf));
+ if (val >= 0)
+ {
+ length = val;
+ }
+ else
+ {
+ ec = ubjson_errc::length_is_negative;
+ more_ = false;
+ return length;
+ }
+ break;
+ }
+ case jsoncons::ubjson::ubjson_type::int32_type:
+ {
+ uint8_t buf[sizeof(int32_t)];
+ if (source_.read(buf, sizeof(int32_t)) != sizeof(int32_t))
+ {
+ ec = ubjson_errc::unexpected_eof;
+ more_ = false;
+ return length;
+ }
+ int32_t val = binary::big_to_native<int32_t>(buf, sizeof(buf));
+ if (val >= 0)
+ {
+ length = static_cast<std::size_t>(val);
+ }
+ else
+ {
+ ec = ubjson_errc::length_is_negative;
+ more_ = false;
+ return length;
+ }
+ break;
+ }
+ case jsoncons::ubjson::ubjson_type::int64_type:
+ {
+ uint8_t buf[sizeof(int64_t)];
+ if (source_.read(buf, sizeof(int64_t)) != sizeof(int64_t))
+ {
+ ec = ubjson_errc::unexpected_eof;
+ more_ = false;
+ return length;
+ }
+ int64_t val = binary::big_to_native<int64_t>(buf, sizeof(buf));
+ if (val >= 0)
+ {
+ length = (std::size_t)val;
+ if (length != (uint64_t)val)
+ {
+ ec = ubjson_errc::number_too_large;
+ more_ = false;
+ return length;
+ }
+ }
+ else
+ {
+ ec = ubjson_errc::length_is_negative;
+ more_ = false;
+ return length;
+ }
+ break;
+ }
+ default:
+ {
+ ec = ubjson_errc::length_must_be_integer;
+ more_ = false;
+ return length;
+ }
+ }
+ return length;
+ }
+
+ void read_key(json_visitor& visitor, std::error_code& ec)
+ {
+ std::size_t length = get_length(ec);
+ if (ec)
+ {
+ ec = ubjson_errc::key_expected;
+ more_ = false;
+ return;
+ }
+ text_buffer_.clear();
+ if (source_reader<Source>::read(source_,text_buffer_,length) != length)
+ {
+ ec = ubjson_errc::unexpected_eof;
+ more_ = false;
+ return;
+ }
+
+ auto result = unicode_traits::validate(text_buffer_.data(),text_buffer_.size());
+ if (result.ec != unicode_traits::conv_errc())
+ {
+ ec = ubjson_errc::invalid_utf8_text_string;
+ more_ = false;
+ return;
+ }
+ more_ = visitor.key(jsoncons::basic_string_view<char>(text_buffer_.data(),text_buffer_.length()), *this, ec);
+ }
+};
+
+}}
+
+#endif
diff --git a/include/jsoncons_ext/ubjson/ubjson_reader.hpp b/include/jsoncons_ext/ubjson/ubjson_reader.hpp
new file mode 100644
index 0000000..210403a
--- /dev/null
+++ b/include/jsoncons_ext/ubjson/ubjson_reader.hpp
@@ -0,0 +1,92 @@
+// Copyright 2017 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_UBJSON_UBJSON_READER_HPP
+#define JSONCONS_UBJSON_UBJSON_READER_HPP
+
+#include <string>
+#include <memory>
+#include <utility> // std::move
+#include <jsoncons/json.hpp>
+#include <jsoncons/source.hpp>
+#include <jsoncons/json_visitor.hpp>
+#include <jsoncons/config/jsoncons_config.hpp>
+#include <jsoncons_ext/ubjson/ubjson_type.hpp>
+#include <jsoncons_ext/ubjson/ubjson_error.hpp>
+#include <jsoncons_ext/ubjson/ubjson_parser.hpp>
+
+namespace jsoncons { namespace ubjson {
+
+template <class Source,class Allocator=std::allocator<char>>
+class basic_ubjson_reader
+{
+ basic_ubjson_parser<Source,Allocator> parser_;
+ json_visitor& visitor_;
+public:
+ template <class Sourceable>
+ basic_ubjson_reader(Sourceable&& source,
+ json_visitor& visitor,
+ const Allocator alloc)
+ : basic_ubjson_reader(std::forward<Sourceable>(source),
+ visitor,
+ ubjson_decode_options(),
+ alloc)
+ {
+ }
+
+ template <class Sourceable>
+ basic_ubjson_reader(Sourceable&& source,
+ json_visitor& visitor,
+ const ubjson_decode_options& options = ubjson_decode_options(),
+ const Allocator alloc=Allocator())
+ : parser_(std::forward<Sourceable>(source), options, alloc),
+ visitor_(visitor)
+ {
+ }
+
+ void read()
+ {
+ std::error_code ec;
+ read(ec);
+ if (ec)
+ {
+ JSONCONS_THROW(ser_error(ec,line(),column()));
+ }
+ }
+
+ void read(std::error_code& ec)
+ {
+ parser_.reset();
+ parser_.parse(visitor_, ec);
+ if (ec)
+ {
+ return;
+ }
+ }
+
+ std::size_t line() const
+ {
+ return parser_.line();
+ }
+
+ std::size_t column() const
+ {
+ return parser_.column();
+ }
+};
+
+using ubjson_stream_reader = basic_ubjson_reader<jsoncons::binary_stream_source>;
+
+using ubjson_bytes_reader = basic_ubjson_reader<jsoncons::bytes_source>;
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+JSONCONS_DEPRECATED_MSG("Instead, use ubjson_stream_reader") typedef ubjson_stream_reader ubjson_reader;
+JSONCONS_DEPRECATED_MSG("Instead, use ubjson_bytes_reader") typedef ubjson_bytes_reader ubjson_buffer_reader;
+#endif
+
+}}
+
+#endif
diff --git a/include/jsoncons_ext/ubjson/ubjson_type.hpp b/include/jsoncons_ext/ubjson/ubjson_type.hpp
new file mode 100644
index 0000000..ef219ce
--- /dev/null
+++ b/include/jsoncons_ext/ubjson/ubjson_type.hpp
@@ -0,0 +1,43 @@
+// Copyright 2013 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_UBJSON_UBJSON_TYPE_HPP
+#define JSONCONS_UBJSON_UBJSON_TYPE_HPP
+
+#include <string>
+#include <memory>
+#include <jsoncons/config/jsoncons_config.hpp>
+
+namespace jsoncons { namespace ubjson {
+
+ namespace ubjson_type
+ {
+ const uint8_t null_type = 'Z';
+ const uint8_t no_op_type = 'N';
+ const uint8_t true_type = 'T';
+ const uint8_t false_type = 'F';
+ const uint8_t int8_type = 'i';
+ const uint8_t uint8_type = 'U';
+ const uint8_t int16_type = 'I';
+ const uint8_t int32_type = 'l';
+ const uint8_t int64_type = 'L';
+ const uint8_t float32_type = 'd';
+ const uint8_t float64_type = 'D';
+ const uint8_t high_precision_number_type = 'H';
+ const uint8_t char_type = 'C';
+ const uint8_t string_type = 'S';
+ const uint8_t start_array_marker = '[';
+ const uint8_t end_array_marker = ']';
+ const uint8_t start_object_marker = '{';
+ const uint8_t end_object_marker = '}';
+ const uint8_t type_marker = '$';
+ const uint8_t count_marker = '#';
+ }
+
+} // namespace ubjson
+} // namespace jsoncons
+
+#endif