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/cbor_encoder.hpp | |
download | csound-json-1d055261b4144dbf86b2658437015b15d4dd9bff.tar.gz csound-json-1d055261b4144dbf86b2658437015b15d4dd9bff.tar.bz2 csound-json-1d055261b4144dbf86b2658437015b15d4dd9bff.zip |
initial
Diffstat (limited to 'include/jsoncons_ext/cbor/cbor_encoder.hpp')
-rw-r--r-- | include/jsoncons_ext/cbor/cbor_encoder.hpp | 1766 |
1 files changed, 1766 insertions, 0 deletions
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 |