diff options
author | Richard <q@1bpm.net> | 2022-09-04 00:32:56 +0100 |
---|---|---|
committer | Richard <q@1bpm.net> | 2022-09-04 00:32:56 +0100 |
commit | 1d055261b4144dbf86b2658437015b15d4dd9bff (patch) | |
tree | 6049b19d1bf953a650383de1a5e438b8b82679f6 /include/jsoncons_ext/cbor | |
download | csound-json-1d055261b4144dbf86b2658437015b15d4dd9bff.tar.gz csound-json-1d055261b4144dbf86b2658437015b15d4dd9bff.tar.bz2 csound-json-1d055261b4144dbf86b2658437015b15d4dd9bff.zip |
initial
Diffstat (limited to 'include/jsoncons_ext/cbor')
-rw-r--r-- | include/jsoncons_ext/cbor/cbor.hpp | 26 | ||||
-rw-r--r-- | include/jsoncons_ext/cbor/cbor_cursor.hpp | 351 | ||||
-rw-r--r-- | include/jsoncons_ext/cbor/cbor_cursor2.hpp | 265 | ||||
-rw-r--r-- | include/jsoncons_ext/cbor/cbor_detail.hpp | 93 | ||||
-rw-r--r-- | include/jsoncons_ext/cbor/cbor_encoder.hpp | 1766 | ||||
-rw-r--r-- | include/jsoncons_ext/cbor/cbor_error.hpp | 105 | ||||
-rw-r--r-- | include/jsoncons_ext/cbor/cbor_options.hpp | 113 | ||||
-rw-r--r-- | include/jsoncons_ext/cbor/cbor_parser.hpp | 1942 | ||||
-rw-r--r-- | include/jsoncons_ext/cbor/cbor_reader.hpp | 116 | ||||
-rw-r--r-- | include/jsoncons_ext/cbor/decode_cbor.hpp | 203 | ||||
-rw-r--r-- | include/jsoncons_ext/cbor/encode_cbor.hpp | 151 |
11 files changed, 5131 insertions, 0 deletions
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 |