From 1d055261b4144dbf86b2658437015b15d4dd9bff Mon Sep 17 00:00:00 2001 From: Richard Date: Sun, 4 Sep 2022 00:32:56 +0100 Subject: initial --- include/jsoncons_ext/bson/bson_encoder.hpp | 585 +++++++++++++++++++++++++++++ 1 file changed, 585 insertions(+) create mode 100644 include/jsoncons_ext/bson/bson_encoder.hpp (limited to 'include/jsoncons_ext/bson/bson_encoder.hpp') diff --git a/include/jsoncons_ext/bson/bson_encoder.hpp b/include/jsoncons_ext/bson/bson_encoder.hpp new file mode 100644 index 0000000..4569f06 --- /dev/null +++ b/include/jsoncons_ext/bson/bson_encoder.hpp @@ -0,0 +1,585 @@ +// Copyright 2018 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_BSON_BSON_ENCODER_HPP +#define JSONCONS_BSON_BSON_ENCODER_HPP + +#include +#include +#include // std::numeric_limits +#include +#include // std::move +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace jsoncons { namespace bson { + +template> +class basic_bson_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 + { + jsoncons::bson::bson_container_type type_; + std::size_t offset_; + std::size_t name_offset_; + std::size_t index_; + + stack_item(jsoncons::bson::bson_container_type type, std::size_t offset) noexcept + : type_(type), offset_(offset), name_offset_(0), index_(0) + { + } + + std::size_t offset() const + { + return offset_; + } + + std::size_t member_offset() const + { + return name_offset_; + } + + void member_offset(std::size_t offset) + { + name_offset_ = offset; + } + + std::size_t next_index() + { + return index_++; + } + + bool is_object() const + { + return type_ == jsoncons::bson::bson_container_type::document; + } + + + }; + + sink_type sink_; + const bson_encode_options options_; + allocator_type alloc_; + + std::vector stack_; + std::vector buffer_; + int nesting_depth_; + + // Noncopyable and nonmoveable + basic_bson_encoder(const basic_bson_encoder&) = delete; + basic_bson_encoder& operator=(const basic_bson_encoder&) = delete; +public: + explicit basic_bson_encoder(Sink&& sink, + const Allocator& alloc = Allocator()) + : basic_bson_encoder(std::forward(sink), + bson_encode_options(), + alloc) + { + } + + explicit basic_bson_encoder(Sink&& sink, + const bson_encode_options& options, + const Allocator& alloc = Allocator()) + : sink_(std::forward(sink)), + options_(options), + alloc_(alloc), + nesting_depth_(0) + { + } + + ~basic_bson_encoder() noexcept + { + sink_.flush(); + } + + void reset() + { + stack_.clear(); + buffer_.clear(); + nesting_depth_ = 0; + } + + void reset(Sink&& sink) + { + sink_ = std::move(sink); + reset(); + } + +private: + // Implementing methods + + void visit_flush() override + { + sink_.flush(); + } + + bool visit_begin_object(semantic_tag, const ser_context&, std::error_code& ec) override + { + if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth())) + { + ec = bson_errc::max_nesting_depth_exceeded; + return false; + } + if (buffer_.size() > 0) + { + if (stack_.empty()) + { + ec = bson_errc::expected_bson_document; + return false; + } + before_value(jsoncons::bson::bson_type::document_type); + } + + stack_.emplace_back(jsoncons::bson::bson_container_type::document, buffer_.size()); + buffer_.insert(buffer_.end(), sizeof(int32_t), 0); + + return true; + } + + bool visit_end_object(const ser_context&, std::error_code&) override + { + JSONCONS_ASSERT(!stack_.empty()); + --nesting_depth_; + + buffer_.push_back(0x00); + + std::size_t length = buffer_.size() - stack_.back().offset(); + binary::native_to_little(static_cast(length), buffer_.begin()+stack_.back().offset()); + + stack_.pop_back(); + if (stack_.empty()) + { + for (auto c : buffer_) + { + sink_.push_back(c); + } + } + return true; + } + + bool visit_begin_array(semantic_tag, const ser_context&, std::error_code& ec) override + { + if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth())) + { + ec = bson_errc::max_nesting_depth_exceeded; + return false; + } + if (buffer_.size() > 0) + { + if (stack_.empty()) + { + ec = bson_errc::expected_bson_document; + return false; + } + before_value(jsoncons::bson::bson_type::array_type); + } + stack_.emplace_back(jsoncons::bson::bson_container_type::array, buffer_.size()); + buffer_.insert(buffer_.end(), sizeof(int32_t), 0); + return true; + } + + bool visit_end_array(const ser_context&, std::error_code&) override + { + JSONCONS_ASSERT(!stack_.empty()); + --nesting_depth_; + + buffer_.push_back(0x00); + + std::size_t length = buffer_.size() - stack_.back().offset(); + binary::native_to_little(static_cast(length), buffer_.begin()+stack_.back().offset()); + + stack_.pop_back(); + if (stack_.empty()) + { + for (auto c : buffer_) + { + sink_.push_back(c); + } + } + return true; + } + + bool visit_key(const string_view_type& name, const ser_context&, std::error_code&) override + { + stack_.back().member_offset(buffer_.size()); + buffer_.push_back(0x00); // reserve space for code + for (auto c : name) + { + buffer_.push_back(c); + } + buffer_.push_back(0x00); + return true; + } + + bool visit_null(semantic_tag tag, const ser_context&, std::error_code& ec) override + { + if (stack_.empty()) + { + ec = bson_errc::expected_bson_document; + return false; + } + switch (tag) + { + case semantic_tag::undefined: + before_value(jsoncons::bson::bson_type::undefined_type); + break; + default: + before_value(jsoncons::bson::bson_type::null_type); + break; + } + return true; + } + + bool visit_bool(bool val, semantic_tag, const ser_context&, std::error_code& ec) override + { + if (stack_.empty()) + { + ec = bson_errc::expected_bson_document; + return false; + } + before_value(jsoncons::bson::bson_type::bool_type); + if (val) + { + buffer_.push_back(0x01); + } + else + { + buffer_.push_back(0x00); + } + + return true; + } + + bool visit_string(const string_view_type& sv, semantic_tag tag, const ser_context&, std::error_code& ec) override + { + if (stack_.empty()) + { + ec = bson_errc::expected_bson_document; + return false; + } + + switch (tag) + { + case semantic_tag::float128: + { + before_value(jsoncons::bson::bson_type::decimal128_type); + decimal128_t dec; + auto rc = decimal128_from_chars(sv.data(), sv.data()+sv.size(), dec); + if (rc.ec != std::errc()) + { + ec = bson_errc::invalid_decimal128_string; + return false; + } + binary::native_to_little(dec.low,std::back_inserter(buffer_)); + binary::native_to_little(dec.high,std::back_inserter(buffer_)); + break; + } + case semantic_tag::id: + { + before_value(jsoncons::bson::bson_type::object_id_type); + oid_t oid(sv); + for (auto b : oid) + { + buffer_.push_back(b); + } + break; + } + case semantic_tag::regex: + { + before_value(jsoncons::bson::bson_type::regex_type); + std::size_t first = sv.find_first_of('/'); + std::size_t last = sv.find_last_of('/'); + if (first == string_view::npos || last == string_view::npos || first == last) + { + ec = bson_errc::invalid_regex_string; + return false; + } + string_view regex = sv.substr(first+1,last-1); + for (auto c : regex) + { + buffer_.push_back(c); + } + buffer_.push_back(0x00); + string_view options = sv.substr(last+1); + for (auto c : options) + { + buffer_.push_back(c); + } + buffer_.push_back(0x00); + break; + } + default: + switch (tag) + { + case semantic_tag::code: + before_value(jsoncons::bson::bson_type::javascript_type); + break; + default: + before_value(jsoncons::bson::bson_type::string_type); + break; + } + std::size_t offset = buffer_.size(); + buffer_.insert(buffer_.end(), sizeof(int32_t), 0); + std::size_t string_offset = buffer_.size(); + auto sink = unicode_traits::validate(sv.data(), sv.size()); + if (sink.ec != unicode_traits::conv_errc()) + { + ec = bson_errc::invalid_utf8_text_string; + return false; + } + for (auto c : sv) + { + buffer_.push_back(c); + } + buffer_.push_back(0x00); + std::size_t length = buffer_.size() - string_offset; + binary::native_to_little(static_cast(length), buffer_.begin()+offset); + break; + } + + return true; + } + + bool visit_byte_string(const byte_string_view& b, + semantic_tag, + const ser_context&, + std::error_code& ec) override + { + if (stack_.empty()) + { + ec = bson_errc::expected_bson_document; + return false; + } + before_value(jsoncons::bson::bson_type::binary_type); + + std::size_t offset = buffer_.size(); + buffer_.insert(buffer_.end(), sizeof(int32_t), 0); + std::size_t string_offset = buffer_.size(); + + buffer_.push_back(0x80); // default subtype + + for (auto c : b) + { + buffer_.push_back(c); + } + std::size_t length = buffer_.size() - string_offset - 1; + binary::native_to_little(static_cast(length), buffer_.begin()+offset); + + return true; + } + + bool visit_byte_string(const byte_string_view& b, + uint64_t ext_tag, + const ser_context&, + std::error_code& ec) override + { + if (stack_.empty()) + { + ec = bson_errc::expected_bson_document; + return false; + } + before_value(jsoncons::bson::bson_type::binary_type); + + std::size_t offset = buffer_.size(); + buffer_.insert(buffer_.end(), sizeof(int32_t), 0); + std::size_t string_offset = buffer_.size(); + + buffer_.push_back(static_cast(ext_tag)); // default subtype + + for (auto c : b) + { + buffer_.push_back(c); + } + std::size_t length = buffer_.size() - string_offset - 1; + binary::native_to_little(static_cast(length), buffer_.begin()+offset); + + return true; + } + + bool visit_int64(int64_t val, + semantic_tag tag, + const ser_context&, + std::error_code& ec) override + { + static constexpr int64_t min_value_div_1000 = (std::numeric_limits::min)() / 1000; + static constexpr int64_t max_value_div_1000 = (std::numeric_limits::max)() / 1000; + if (stack_.empty()) + { + ec = bson_errc::expected_bson_document; + return false; + } + + switch (tag) + { + case semantic_tag::epoch_second: + if (val < min_value_div_1000) + { + ec = bson_errc::datetime_too_small; + return false; + } + if (val > max_value_div_1000) + { + ec = bson_errc::datetime_too_large; + return false; + } + before_value(jsoncons::bson::bson_type::datetime_type); + binary::native_to_little(val*millis_in_second,std::back_inserter(buffer_)); + return true; + case semantic_tag::epoch_milli: + before_value(jsoncons::bson::bson_type::datetime_type); + binary::native_to_little(val,std::back_inserter(buffer_)); + return true; + case semantic_tag::epoch_nano: + before_value(jsoncons::bson::bson_type::datetime_type); + if (val != 0) + { + val /= nanos_in_milli; + } + binary::native_to_little(static_cast(val),std::back_inserter(buffer_)); + return true; + default: + { + if (val >= (std::numeric_limits::lowest)() && val <= (std::numeric_limits::max)()) + { + before_value(jsoncons::bson::bson_type::int32_type); + binary::native_to_little(static_cast(val),std::back_inserter(buffer_)); + } + else + { + before_value(jsoncons::bson::bson_type::int64_type); + binary::native_to_little(static_cast(val),std::back_inserter(buffer_)); + } + return true; + } + } + } + + bool visit_uint64(uint64_t val, + semantic_tag tag, + const ser_context&, + std::error_code& ec) override + { + static constexpr uint64_t max_value_div_1000 = (std::numeric_limits::max)() / 1000; + if (stack_.empty()) + { + ec = bson_errc::expected_bson_document; + return false; + } + + switch (tag) + { + case semantic_tag::epoch_second: + if (val > max_value_div_1000) + { + ec = bson_errc::datetime_too_large; + return false; + } + before_value(jsoncons::bson::bson_type::datetime_type); + binary::native_to_little(static_cast(val*millis_in_second),std::back_inserter(buffer_)); + return true; + case semantic_tag::epoch_milli: + before_value(jsoncons::bson::bson_type::datetime_type); + binary::native_to_little(static_cast(val),std::back_inserter(buffer_)); + return true; + case semantic_tag::epoch_nano: + before_value(jsoncons::bson::bson_type::datetime_type); + if (val != 0) + { + val /= nanos_in_second; + } + binary::native_to_little(static_cast(val),std::back_inserter(buffer_)); + return true; + default: + { + bool more; + if (val <= static_cast((std::numeric_limits::max)())) + { + before_value(jsoncons::bson::bson_type::int32_type); + binary::native_to_little(static_cast(val),std::back_inserter(buffer_)); + more = true; + } + else if (val <= static_cast((std::numeric_limits::max)())) + { + before_value(jsoncons::bson::bson_type::int64_type); + binary::native_to_little(static_cast(val),std::back_inserter(buffer_)); + more = true; + } + else + { + ec = bson_errc::number_too_large; + more = false; + } + return more; + } + } + } + + bool visit_double(double val, + semantic_tag, + const ser_context&, + std::error_code& ec) override + { + if (stack_.empty()) + { + ec = bson_errc::expected_bson_document; + return false; + } + before_value(jsoncons::bson::bson_type::double_type); + binary::native_to_little(val,std::back_inserter(buffer_)); + return true; + } + + void before_value(uint8_t code) + { + JSONCONS_ASSERT(!stack_.empty()); + if (stack_.back().is_object()) + { + buffer_[stack_.back().member_offset()] = code; + } + else + { + buffer_.push_back(code); + std::string name = std::to_string(stack_.back().next_index()); + buffer_.insert(buffer_.end(), name.begin(), name.end()); + buffer_.push_back(0x00); + } + } +}; + +using bson_stream_encoder = basic_bson_encoder; +using bson_bytes_encoder = basic_bson_encoder>>; + +#if !defined(JSONCONS_NO_DEPRECATED) +template +using basic_bson_serializer = basic_bson_encoder; + +JSONCONS_DEPRECATED_MSG("Instead, use bson_stream_encoder") typedef bson_stream_encoder bson_encoder; +JSONCONS_DEPRECATED_MSG("Instead, use bson_stream_encoder") typedef bson_stream_encoder bson_serializer; +JSONCONS_DEPRECATED_MSG("Instead, use bson_bytes_encoder") typedef bson_bytes_encoder bson_buffer_serializer; + +#endif + +}} +#endif -- cgit v1.2.3