// 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 #include #include // std::numeric_limits #include #include // std::move #include #include #include #include #include #include #include #include namespace jsoncons { namespace msgpack { enum class msgpack_container_type {object, array}; template> class basic_msgpack_encoder final : public basic_json_visitor { 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::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_; 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), msgpack_encode_options(), alloc) { } explicit basic_msgpack_encoder(Sink&& sink, const msgpack_encode_options& options, const Allocator& alloc = Allocator()) : sink_(std::forward(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(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(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::max)()) { // array 16 sink_.push_back(jsoncons::msgpack::msgpack_type::array16_type); binary::native_to_big(static_cast(length),std::back_inserter(sink_)); } else if (length <= (std::numeric_limits::max)()) { // array 32 sink_.push_back(jsoncons::msgpack::msgpack_type::array32_type); binary::native_to_big(static_cast(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(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(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(nanoseconds), std::back_inserter(sink_)); binary::native_to_big(static_cast(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(q); int64_t nanoseconds = static_cast(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(q); int64_t nanoseconds = static_cast(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(length)); } else if (length <= (std::numeric_limits::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(length)); } else if (length <= (std::numeric_limits::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(length), std::back_inserter(sink_)); } else if (length <= (std::numeric_limits::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(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::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(length)); } else if (length <= (std::numeric_limits::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(length), std::back_inserter(sink_)); } else if (length <= (std::numeric_limits::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(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(ext_tag)); break; case 2: sink_.push_back(jsoncons::msgpack::msgpack_type::fixext2_type); sink_.push_back(static_cast(ext_tag)); break; case 4: sink_.push_back(jsoncons::msgpack::msgpack_type::fixext4_type); sink_.push_back(static_cast(ext_tag)); break; case 8: sink_.push_back(jsoncons::msgpack::msgpack_type::fixext8_type); sink_.push_back(static_cast(ext_tag)); break; case 16: sink_.push_back(jsoncons::msgpack::msgpack_type::fixext16_type); sink_.push_back(static_cast(ext_tag)); break; default: if (length <= (std::numeric_limits::max)()) { sink_.push_back(jsoncons::msgpack::msgpack_type::ext8_type); sink_.push_back(static_cast(length)); sink_.push_back(static_cast(ext_tag)); } else if (length <= (std::numeric_limits::max)()) { sink_.push_back(jsoncons::msgpack::msgpack_type::ext16_type); binary::native_to_big(static_cast(length), std::back_inserter(sink_)); sink_.push_back(static_cast(ext_tag)); } else if (length <= (std::numeric_limits::max)()) { sink_.push_back(jsoncons::msgpack::msgpack_type::ext32_type); binary::native_to_big(static_cast(length),std::back_inserter(sink_)); sink_.push_back(static_cast(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(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(val)); } else if (val <= (std::numeric_limits::max)()) { // uint 8 stores a 8-bit unsigned integer sink_.push_back(jsoncons::msgpack::msgpack_type::uint8_type); sink_.push_back(static_cast(val)); } else if (val <= (std::numeric_limits::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(val),std::back_inserter(sink_)); } else if (val <= (std::numeric_limits::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(val),std::back_inserter(sink_)); } else if (val <= (std::numeric_limits::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(val),std::back_inserter(sink_)); } } else { if (val >= -32) { // negative fixnum stores 5-bit negative integer binary::native_to_big(static_cast(val), std::back_inserter(sink_)); } else if (val >= (std::numeric_limits::lowest)()) { // int 8 stores a 8-bit signed integer sink_.push_back(jsoncons::msgpack::msgpack_type::int8_type); binary::native_to_big(static_cast(val),std::back_inserter(sink_)); } else if (val >= (std::numeric_limits::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(val),std::back_inserter(sink_)); } else if (val >= (std::numeric_limits::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(val),std::back_inserter(sink_)); } else if (val >= (std::numeric_limits::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(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(val), 0); break; case semantic_tag::epoch_milli: { if (val != 0) { auto dv = std::div(static_cast(val), static_cast(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(val), static_cast(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((std::numeric_limits::max)())) { // positive fixnum stores 7-bit positive integer sink_.push_back(static_cast(val)); } else if (val <= (std::numeric_limits::max)()) { // uint 8 stores a 8-bit unsigned integer sink_.push_back(jsoncons::msgpack::msgpack_type::uint8_type); sink_.push_back(static_cast(val)); } else if (val <= (std::numeric_limits::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(val),std::back_inserter(sink_)); } else if (val <= (std::numeric_limits::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(val),std::back_inserter(sink_)); } else if (val <= (std::numeric_limits::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(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(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; using msgpack_bytes_encoder = basic_msgpack_encoder>>; #if !defined(JSONCONS_NO_DEPRECATED) JSONCONS_DEPRECATED_MSG("Instead, use msgpack_bytes_encoder") typedef msgpack_bytes_encoder msgpack_bytes_serializer; template using basic_msgpack_serializer = basic_msgpack_encoder; 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