diff options
Diffstat (limited to 'include/jsoncons_ext/msgpack/msgpack_encoder.hpp')
-rw-r--r-- | include/jsoncons_ext/msgpack/msgpack_encoder.hpp | 753 |
1 files changed, 753 insertions, 0 deletions
diff --git a/include/jsoncons_ext/msgpack/msgpack_encoder.hpp b/include/jsoncons_ext/msgpack/msgpack_encoder.hpp new file mode 100644 index 0000000..34e882b --- /dev/null +++ b/include/jsoncons_ext/msgpack/msgpack_encoder.hpp @@ -0,0 +1,753 @@ +// Copyright 2018 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_MSGPACK_MSGPACK_ENCODER_HPP +#define JSONCONS_MSGPACK_MSGPACK_ENCODER_HPP + +#include <string> +#include <vector> +#include <limits> // std::numeric_limits +#include <memory> +#include <utility> // std::move +#include <jsoncons/json_exception.hpp> +#include <jsoncons/json_visitor.hpp> +#include <jsoncons/config/jsoncons_config.hpp> +#include <jsoncons/sink.hpp> +#include <jsoncons/detail/parse_number.hpp> +#include <jsoncons_ext/msgpack/msgpack_type.hpp> +#include <jsoncons_ext/msgpack/msgpack_error.hpp> +#include <jsoncons_ext/msgpack/msgpack_options.hpp> + +namespace jsoncons { +namespace msgpack { + + enum class msgpack_container_type {object, array}; + + template<class Sink=jsoncons::binary_stream_sink,class Allocator=std::allocator<char>> + class basic_msgpack_encoder final : public basic_json_visitor<char> + { + enum class decimal_parse_state { start, integer, exp1, exp2, fraction1 }; + + static constexpr int64_t nanos_in_milli = 1000000; + static constexpr int64_t nanos_in_second = 1000000000; + static constexpr int64_t millis_in_second = 1000; + public: + using allocator_type = Allocator; + using char_type = char; + using typename basic_json_visitor<char>::string_view_type; + using sink_type = Sink; + + private: + struct stack_item + { + msgpack_container_type type_; + std::size_t length_; + std::size_t count_; + + stack_item(msgpack_container_type type, std::size_t length = 0) noexcept + : type_(type), length_(length), count_(0) + { + } + + std::size_t length() const + { + return length_; + } + + std::size_t count() const + { + return count_; + } + + bool is_object() const + { + return type_ == msgpack_container_type::object; + } + }; + + Sink sink_; + const msgpack_encode_options options_; + allocator_type alloc_; + + std::vector<stack_item> stack_; + int nesting_depth_; + + // Noncopyable and nonmoveable + basic_msgpack_encoder(const basic_msgpack_encoder&) = delete; + basic_msgpack_encoder& operator=(const basic_msgpack_encoder&) = delete; + public: + explicit basic_msgpack_encoder(Sink&& sink, + const Allocator& alloc = Allocator()) + : basic_msgpack_encoder(std::forward<Sink>(sink), msgpack_encode_options(), alloc) + { + } + + explicit basic_msgpack_encoder(Sink&& sink, + const msgpack_encode_options& options, + const Allocator& alloc = Allocator()) + : sink_(std::forward<Sink>(sink)), + options_(options), + alloc_(alloc), + nesting_depth_(0) + { + } + + ~basic_msgpack_encoder() noexcept + { + sink_.flush(); + } + + void reset() + { + stack_.clear(); + nesting_depth_ = 0; + } + + void reset(Sink&& sink) + { + sink_ = std::move(sink); + reset(); + } + + private: + // Implementing methods + + void visit_flush() override + { + sink_.flush(); + } + + bool visit_begin_object(semantic_tag, const ser_context&, std::error_code& ec) override + { + ec = msgpack_errc::object_length_required; + return false; + } + + bool visit_begin_object(std::size_t length, semantic_tag, const ser_context&, std::error_code& ec) override + { + if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth())) + { + ec = msgpack_errc::max_nesting_depth_exceeded; + return false; + } + stack_.emplace_back(msgpack_container_type::object, length); + + if (length <= 15) + { + // fixmap + sink_.push_back(jsoncons::msgpack::msgpack_type::fixmap_base_type | (length & 0xf)); + } + else if (length <= 65535) + { + // map 16 + sink_.push_back(jsoncons::msgpack::msgpack_type::map16_type); + binary::native_to_big(static_cast<uint16_t>(length), + std::back_inserter(sink_)); + } + else if (length <= 4294967295) + { + // map 32 + sink_.push_back(jsoncons::msgpack::msgpack_type::map32_type); + binary::native_to_big(static_cast<uint32_t>(length), + std::back_inserter(sink_)); + } + + return true; + } + + bool visit_end_object(const ser_context&, std::error_code& ec) override + { + JSONCONS_ASSERT(!stack_.empty()); + --nesting_depth_; + + if (stack_.back().count() < stack_.back().length()) + { + ec = msgpack_errc::too_few_items; + return false; + } + else if (stack_.back().count() > stack_.back().length()) + { + ec = msgpack_errc::too_many_items; + return false; + } + + stack_.pop_back(); + end_value(); + return true; + } + + bool visit_begin_array(semantic_tag, const ser_context&, std::error_code& ec) override + { + ec = msgpack_errc::array_length_required; + return false; + } + + bool visit_begin_array(std::size_t length, semantic_tag, const ser_context&, std::error_code& ec) override + { + if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth())) + { + ec = msgpack_errc::max_nesting_depth_exceeded; + return false; + } + stack_.emplace_back(msgpack_container_type::array, length); + if (length <= 15) + { + // fixarray + sink_.push_back(jsoncons::msgpack::msgpack_type::fixarray_base_type | (length & 0xf)); + } + else if (length <= (std::numeric_limits<uint16_t>::max)()) + { + // array 16 + sink_.push_back(jsoncons::msgpack::msgpack_type::array16_type); + binary::native_to_big(static_cast<uint16_t>(length),std::back_inserter(sink_)); + } + else if (length <= (std::numeric_limits<uint32_t>::max)()) + { + // array 32 + sink_.push_back(jsoncons::msgpack::msgpack_type::array32_type); + binary::native_to_big(static_cast<uint32_t>(length),std::back_inserter(sink_)); + } + return true; + } + + bool visit_end_array(const ser_context&, std::error_code& ec) override + { + JSONCONS_ASSERT(!stack_.empty()); + + --nesting_depth_; + + if (stack_.back().count() < stack_.back().length()) + { + ec = msgpack_errc::too_few_items; + return false; + } + else if (stack_.back().count() > stack_.back().length()) + { + ec = msgpack_errc::too_many_items; + return false; + } + + stack_.pop_back(); + end_value(); + return true; + } + + bool visit_key(const string_view_type& name, const ser_context&, std::error_code&) override + { + write_string_value(name); + return true; + } + + bool visit_null(semantic_tag, const ser_context&, std::error_code&) override + { + // nil + sink_.push_back(jsoncons::msgpack::msgpack_type::nil_type); + end_value(); + return true; + } + + void write_timestamp(int64_t seconds, int64_t nanoseconds) + { + if ((seconds >> 34) == 0) + { + uint64_t data64 = (nanoseconds << 34) | seconds; + if ((data64 & 0xffffffff00000000L) == 0) + { + // timestamp 32 + sink_.push_back(jsoncons::msgpack::msgpack_type::fixext4_type); + sink_.push_back(0xff); + binary::native_to_big(static_cast<uint32_t>(data64), std::back_inserter(sink_)); + } + else + { + // timestamp 64 + sink_.push_back(jsoncons::msgpack::msgpack_type::fixext8_type); + sink_.push_back(0xff); + binary::native_to_big(static_cast<uint64_t>(data64), std::back_inserter(sink_)); + } + } + else + { + // timestamp 96 + sink_.push_back(jsoncons::msgpack::msgpack_type::ext8_type); + sink_.push_back(0x0c); // 12 + sink_.push_back(0xff); + binary::native_to_big(static_cast<uint32_t>(nanoseconds), std::back_inserter(sink_)); + binary::native_to_big(static_cast<uint64_t>(seconds), std::back_inserter(sink_)); + } + } + + bool visit_string(const string_view_type& sv, semantic_tag tag, const ser_context&, std::error_code& ec) override + { + switch (tag) + { + case semantic_tag::epoch_second: + { + int64_t seconds; + auto result = jsoncons::detail::to_integer(sv.data(), sv.length(), seconds); + if (!result) + { + ec = msgpack_errc::invalid_timestamp; + return false; + } + write_timestamp(seconds, 0); + break; + } + case semantic_tag::epoch_milli: + { + bigint n = bigint::from_string(sv.data(), sv.length()); + if (n != 0) + { + bigint q; + bigint rem; + n.divide(millis_in_second, q, rem, true); + int64_t seconds = static_cast<int64_t>(q); + int64_t nanoseconds = static_cast<int64_t>(rem) * nanos_in_milli; + if (nanoseconds < 0) + { + nanoseconds = -nanoseconds; + } + write_timestamp(seconds, nanoseconds); + } + else + { + write_timestamp(0, 0); + } + break; + } + case semantic_tag::epoch_nano: + { + bigint n = bigint::from_string(sv.data(), sv.length()); + if (n != 0) + { + bigint q; + bigint rem; + n.divide(nanos_in_second, q, rem, true); + int64_t seconds = static_cast<int64_t>(q); + int64_t nanoseconds = static_cast<int64_t>(rem); + if (nanoseconds < 0) + { + nanoseconds = -nanoseconds; + } + write_timestamp(seconds, nanoseconds); + } + else + { + write_timestamp(0, 0); + } + break; + } + default: + { + write_string_value(sv); + end_value(); + break; + } + } + return true; + } + + void write_string_value(const string_view_type& sv) + { + auto sink = unicode_traits::validate(sv.data(), sv.size()); + if (sink.ec != unicode_traits::conv_errc()) + { + JSONCONS_THROW(ser_error(msgpack_errc::invalid_utf8_text_string)); + } + + const size_t length = sv.length(); + if (length <= 31) + { + // fixstr stores a byte array whose length is upto 31 bytes + sink_.push_back(jsoncons::msgpack::msgpack_type::fixstr_base_type | static_cast<uint8_t>(length)); + } + else if (length <= (std::numeric_limits<uint8_t>::max)()) + { + // str 8 stores a byte array whose length is upto (2^8)-1 bytes + sink_.push_back(jsoncons::msgpack::msgpack_type::str8_type); + sink_.push_back(static_cast<uint8_t>(length)); + } + else if (length <= (std::numeric_limits<uint16_t>::max)()) + { + // str 16 stores a byte array whose length is upto (2^16)-1 bytes + sink_.push_back(jsoncons::msgpack::msgpack_type::str16_type); + binary::native_to_big(static_cast<uint16_t>(length), std::back_inserter(sink_)); + } + else if (length <= (std::numeric_limits<uint32_t>::max)()) + { + // str 32 stores a byte array whose length is upto (2^32)-1 bytes + sink_.push_back(jsoncons::msgpack::msgpack_type::str32_type); + binary::native_to_big(static_cast<uint32_t>(length),std::back_inserter(sink_)); + } + + for (auto c : sv) + { + sink_.push_back(c); + } + } + + bool visit_byte_string(const byte_string_view& b, + semantic_tag, + const ser_context&, + std::error_code&) override + { + + const std::size_t length = b.size(); + if (length <= (std::numeric_limits<uint8_t>::max)()) + { + // bin 8 stores a byte array whose length is upto (2^8)-1 bytes + sink_.push_back(jsoncons::msgpack::msgpack_type::bin8_type); + sink_.push_back(static_cast<uint8_t>(length)); + } + else if (length <= (std::numeric_limits<uint16_t>::max)()) + { + // bin 16 stores a byte array whose length is upto (2^16)-1 bytes + sink_.push_back(jsoncons::msgpack::msgpack_type::bin16_type); + binary::native_to_big(static_cast<uint16_t>(length), std::back_inserter(sink_)); + } + else if (length <= (std::numeric_limits<uint32_t>::max)()) + { + // bin 32 stores a byte array whose length is upto (2^32)-1 bytes + sink_.push_back(jsoncons::msgpack::msgpack_type::bin32_type); + binary::native_to_big(static_cast<uint32_t>(length),std::back_inserter(sink_)); + } + + for (auto c : b) + { + sink_.push_back(c); + } + + end_value(); + return true; + } + + bool visit_byte_string(const byte_string_view& b, + uint64_t ext_tag, + const ser_context&, + std::error_code&) override + { + const std::size_t length = b.size(); + switch (length) + { + case 1: + sink_.push_back(jsoncons::msgpack::msgpack_type::fixext1_type); + sink_.push_back(static_cast<uint8_t>(ext_tag)); + break; + case 2: + sink_.push_back(jsoncons::msgpack::msgpack_type::fixext2_type); + sink_.push_back(static_cast<uint8_t>(ext_tag)); + break; + case 4: + sink_.push_back(jsoncons::msgpack::msgpack_type::fixext4_type); + sink_.push_back(static_cast<uint8_t>(ext_tag)); + break; + case 8: + sink_.push_back(jsoncons::msgpack::msgpack_type::fixext8_type); + sink_.push_back(static_cast<uint8_t>(ext_tag)); + break; + case 16: + sink_.push_back(jsoncons::msgpack::msgpack_type::fixext16_type); + sink_.push_back(static_cast<uint8_t>(ext_tag)); + break; + default: + if (length <= (std::numeric_limits<uint8_t>::max)()) + { + sink_.push_back(jsoncons::msgpack::msgpack_type::ext8_type); + sink_.push_back(static_cast<uint8_t>(length)); + sink_.push_back(static_cast<uint8_t>(ext_tag)); + } + else if (length <= (std::numeric_limits<uint16_t>::max)()) + { + sink_.push_back(jsoncons::msgpack::msgpack_type::ext16_type); + binary::native_to_big(static_cast<uint16_t>(length), std::back_inserter(sink_)); + sink_.push_back(static_cast<uint8_t>(ext_tag)); + } + else if (length <= (std::numeric_limits<uint32_t>::max)()) + { + sink_.push_back(jsoncons::msgpack::msgpack_type::ext32_type); + binary::native_to_big(static_cast<uint32_t>(length),std::back_inserter(sink_)); + sink_.push_back(static_cast<uint8_t>(ext_tag)); + } + break; + } + + for (auto c : b) + { + sink_.push_back(c); + } + + end_value(); + return true; + } + + bool visit_double(double val, + semantic_tag, + const ser_context&, + std::error_code&) override + { + float valf = (float)val; + if ((double)valf == val) + { + // float 32 + sink_.push_back(jsoncons::msgpack::msgpack_type::float32_type); + binary::native_to_big(valf,std::back_inserter(sink_)); + } + else + { + // float 64 + sink_.push_back(jsoncons::msgpack::msgpack_type::float64_type); + binary::native_to_big(val,std::back_inserter(sink_)); + } + + // write double + + end_value(); + return true; + } + + bool visit_int64(int64_t val, + semantic_tag tag, + const ser_context&, + std::error_code&) override + { + switch (tag) + { + case semantic_tag::epoch_second: + write_timestamp(val, 0); + break; + case semantic_tag::epoch_milli: + { + if (val != 0) + { + auto dv = std::div(val,millis_in_second); + int64_t seconds = dv.quot; + int64_t nanoseconds = dv.rem*nanos_in_milli; + if (nanoseconds < 0) + { + nanoseconds = -nanoseconds; + } + write_timestamp(seconds, nanoseconds); + } + else + { + write_timestamp(0, 0); + } + break; + } + case semantic_tag::epoch_nano: + { + if (val != 0) + { + auto dv = std::div(val,static_cast<int64_t>(nanos_in_second)); + int64_t seconds = dv.quot; + int64_t nanoseconds = dv.rem; + if (nanoseconds < 0) + { + nanoseconds = -nanoseconds; + } + write_timestamp(seconds, nanoseconds); + } + else + { + write_timestamp(0, 0); + } + break; + } + default: + { + if (val >= 0) + { + if (val <= 0x7f) + { + // positive fixnum stores 7-bit positive integer + sink_.push_back(static_cast<uint8_t>(val)); + } + else if (val <= (std::numeric_limits<uint8_t>::max)()) + { + // uint 8 stores a 8-bit unsigned integer + sink_.push_back(jsoncons::msgpack::msgpack_type::uint8_type); + sink_.push_back(static_cast<uint8_t>(val)); + } + else if (val <= (std::numeric_limits<uint16_t>::max)()) + { + // uint 16 stores a 16-bit big-endian unsigned integer + sink_.push_back(jsoncons::msgpack::msgpack_type::uint16_type); + binary::native_to_big(static_cast<uint16_t>(val),std::back_inserter(sink_)); + } + else if (val <= (std::numeric_limits<uint32_t>::max)()) + { + // uint 32 stores a 32-bit big-endian unsigned integer + sink_.push_back(jsoncons::msgpack::msgpack_type::uint32_type); + binary::native_to_big(static_cast<uint32_t>(val),std::back_inserter(sink_)); + } + else if (val <= (std::numeric_limits<int64_t>::max)()) + { + // int 64 stores a 64-bit big-endian signed integer + sink_.push_back(jsoncons::msgpack::msgpack_type::uint64_type); + binary::native_to_big(static_cast<uint64_t>(val),std::back_inserter(sink_)); + } + } + else + { + if (val >= -32) + { + // negative fixnum stores 5-bit negative integer + binary::native_to_big(static_cast<int8_t>(val), std::back_inserter(sink_)); + } + else if (val >= (std::numeric_limits<int8_t>::lowest)()) + { + // int 8 stores a 8-bit signed integer + sink_.push_back(jsoncons::msgpack::msgpack_type::int8_type); + binary::native_to_big(static_cast<int8_t>(val),std::back_inserter(sink_)); + } + else if (val >= (std::numeric_limits<int16_t>::lowest)()) + { + // int 16 stores a 16-bit big-endian signed integer + sink_.push_back(jsoncons::msgpack::msgpack_type::int16_type); + binary::native_to_big(static_cast<int16_t>(val),std::back_inserter(sink_)); + } + else if (val >= (std::numeric_limits<int32_t>::lowest)()) + { + // int 32 stores a 32-bit big-endian signed integer + sink_.push_back(jsoncons::msgpack::msgpack_type::int32_type); + binary::native_to_big(static_cast<int32_t>(val),std::back_inserter(sink_)); + } + else if (val >= (std::numeric_limits<int64_t>::lowest)()) + { + // int 64 stores a 64-bit big-endian signed integer + sink_.push_back(jsoncons::msgpack::msgpack_type::int64_type); + binary::native_to_big(static_cast<int64_t>(val),std::back_inserter(sink_)); + } + } + } + break; + } + end_value(); + return true; + } + + bool visit_uint64(uint64_t val, + semantic_tag tag, + const ser_context&, + std::error_code&) override + { + switch (tag) + { + case semantic_tag::epoch_second: + write_timestamp(static_cast<int64_t>(val), 0); + break; + case semantic_tag::epoch_milli: + { + if (val != 0) + { + auto dv = std::div(static_cast<int64_t>(val), static_cast<int64_t>(millis_in_second)); + int64_t seconds = dv.quot; + int64_t nanoseconds = dv.rem*nanos_in_milli; + if (nanoseconds < 0) + { + nanoseconds = -nanoseconds; + } + write_timestamp(seconds, nanoseconds); + } + else + { + write_timestamp(0, 0); + } + break; + } + case semantic_tag::epoch_nano: + { + if (val != 0) + { + auto dv = std::div(static_cast<int64_t>(val), static_cast<int64_t>(nanos_in_second)); + int64_t seconds = dv.quot; + int64_t nanoseconds = dv.rem; + if (nanoseconds < 0) + { + nanoseconds = -nanoseconds; + } + write_timestamp(seconds, nanoseconds); + } + else + { + write_timestamp(0, 0); + } + break; + } + default: + { + if (val <= static_cast<uint64_t>((std::numeric_limits<int8_t>::max)())) + { + // positive fixnum stores 7-bit positive integer + sink_.push_back(static_cast<uint8_t>(val)); + } + else if (val <= (std::numeric_limits<uint8_t>::max)()) + { + // uint 8 stores a 8-bit unsigned integer + sink_.push_back(jsoncons::msgpack::msgpack_type::uint8_type); + sink_.push_back(static_cast<uint8_t>(val)); + } + else if (val <= (std::numeric_limits<uint16_t>::max)()) + { + // uint 16 stores a 16-bit big-endian unsigned integer + sink_.push_back(jsoncons::msgpack::msgpack_type::uint16_type); + binary::native_to_big(static_cast<uint16_t>(val),std::back_inserter(sink_)); + } + else if (val <= (std::numeric_limits<uint32_t>::max)()) + { + // uint 32 stores a 32-bit big-endian unsigned integer + sink_.push_back(jsoncons::msgpack::msgpack_type::uint32_type); + binary::native_to_big(static_cast<uint32_t>(val),std::back_inserter(sink_)); + } + else if (val <= (std::numeric_limits<uint64_t>::max)()) + { + // uint 64 stores a 64-bit big-endian unsigned integer + sink_.push_back(jsoncons::msgpack::msgpack_type::uint64_type); + binary::native_to_big(static_cast<uint64_t>(val),std::back_inserter(sink_)); + } + break; + } + } + end_value(); + return true; + } + + bool visit_bool(bool val, semantic_tag, const ser_context&, std::error_code&) override + { + // true and false + sink_.push_back(static_cast<uint8_t>(val ? jsoncons::msgpack::msgpack_type::true_type : jsoncons::msgpack::msgpack_type::false_type)); + + end_value(); + return true; + } + + void end_value() + { + if (!stack_.empty()) + { + ++stack_.back().count_; + } + } + }; + + using msgpack_stream_encoder = basic_msgpack_encoder<jsoncons::binary_stream_sink>; + using msgpack_bytes_encoder = basic_msgpack_encoder<jsoncons::bytes_sink<std::vector<uint8_t>>>; + + #if !defined(JSONCONS_NO_DEPRECATED) + JSONCONS_DEPRECATED_MSG("Instead, use msgpack_bytes_encoder") typedef msgpack_bytes_encoder msgpack_bytes_serializer; + + template<class Sink=jsoncons::binary_stream_sink> + using basic_msgpack_serializer = basic_msgpack_encoder<Sink>; + + JSONCONS_DEPRECATED_MSG("Instead, use msgpack_stream_encoder") typedef msgpack_stream_encoder msgpack_encoder; + JSONCONS_DEPRECATED_MSG("Instead, use msgpack_stream_encoder") typedef msgpack_stream_encoder msgpack_serializer; + JSONCONS_DEPRECATED_MSG("Instead, use msgpack_bytes_encoder") typedef msgpack_bytes_encoder msgpack_buffer_serializer; + #endif + +} // namespace msgpack +} // namespace jsoncons + +#endif |