aboutsummaryrefslogtreecommitdiff
path: root/include/jsoncons_ext/ubjson/ubjson_encoder.hpp
diff options
context:
space:
mode:
authorRichard <q@1bpm.net>2022-09-04 00:32:56 +0100
committerRichard <q@1bpm.net>2022-09-04 00:32:56 +0100
commit1d055261b4144dbf86b2658437015b15d4dd9bff (patch)
tree6049b19d1bf953a650383de1a5e438b8b82679f6 /include/jsoncons_ext/ubjson/ubjson_encoder.hpp
downloadcsound-json-1d055261b4144dbf86b2658437015b15d4dd9bff.tar.gz
csound-json-1d055261b4144dbf86b2658437015b15d4dd9bff.tar.bz2
csound-json-1d055261b4144dbf86b2658437015b15d4dd9bff.zip
initial
Diffstat (limited to 'include/jsoncons_ext/ubjson/ubjson_encoder.hpp')
-rw-r--r--include/jsoncons_ext/ubjson/ubjson_encoder.hpp502
1 files changed, 502 insertions, 0 deletions
diff --git a/include/jsoncons_ext/ubjson/ubjson_encoder.hpp b/include/jsoncons_ext/ubjson/ubjson_encoder.hpp
new file mode 100644
index 0000000..2d90e4a
--- /dev/null
+++ b/include/jsoncons_ext/ubjson/ubjson_encoder.hpp
@@ -0,0 +1,502 @@
+// 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_UBJSON_UBJSON_ENCODER_HPP
+#define JSONCONS_UBJSON_UBJSON_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/ubjson/ubjson_type.hpp>
+#include <jsoncons_ext/ubjson/ubjson_error.hpp>
+#include <jsoncons_ext/ubjson/ubjson_options.hpp>
+
+namespace jsoncons { namespace ubjson {
+
+enum class ubjson_container_type {object, indefinite_length_object, array, indefinite_length_array};
+
+template<class Sink=jsoncons::binary_stream_sink,class Allocator=std::allocator<char>>
+class basic_ubjson_encoder final : public basic_json_visitor<char>
+{
+
+ enum class decimal_parse_state { start, integer, exp1, exp2, fraction1 };
+public:
+ using allocator_type = Allocator;
+ using typename basic_json_visitor<char>::string_view_type;
+ using sink_type = Sink;
+
+private:
+ struct stack_item
+ {
+ ubjson_container_type type_;
+ std::size_t length_;
+ std::size_t count_;
+
+ stack_item(ubjson_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_ == ubjson_container_type::object || type_ == ubjson_container_type::indefinite_length_object;
+ }
+
+ bool is_indefinite_length() const
+ {
+ return type_ == ubjson_container_type::indefinite_length_array || type_ == ubjson_container_type::indefinite_length_object;
+ }
+
+ };
+
+ Sink sink_;
+ const ubjson_encode_options options_;
+ allocator_type alloc_;
+
+ std::vector<stack_item> stack_;
+ int nesting_depth_;
+
+ // Noncopyable and nonmoveable
+ basic_ubjson_encoder(const basic_ubjson_encoder&) = delete;
+ basic_ubjson_encoder& operator=(const basic_ubjson_encoder&) = delete;
+public:
+ basic_ubjson_encoder(Sink&& sink,
+ const Allocator& alloc = Allocator())
+ : basic_ubjson_encoder(std::forward<Sink>(sink), ubjson_encode_options(), alloc)
+ {
+ }
+
+ explicit basic_ubjson_encoder(Sink&& sink,
+ const ubjson_encode_options& options,
+ const Allocator& alloc = Allocator())
+ : sink_(std::forward<Sink>(sink)),
+ options_(options),
+ alloc_(alloc),
+ nesting_depth_(0)
+ {
+ }
+
+ void reset()
+ {
+ stack_.clear();
+ nesting_depth_ = 0;
+ }
+
+ void reset(Sink&& sink)
+ {
+ sink_ = std::move(sink);
+ reset();
+ }
+
+ ~basic_ubjson_encoder() noexcept
+ {
+ JSONCONS_TRY
+ {
+ sink_.flush();
+ }
+ JSONCONS_CATCH(...)
+ {
+ }
+ }
+
+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 = ubjson_errc::max_nesting_depth_exceeded;
+ return false;
+ }
+ stack_.emplace_back(ubjson_container_type::indefinite_length_object);
+ sink_.push_back(jsoncons::ubjson::ubjson_type::start_object_marker);
+
+ 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 = ubjson_errc::max_nesting_depth_exceeded;
+ return false;
+ }
+ stack_.emplace_back(ubjson_container_type::object, length);
+ sink_.push_back(jsoncons::ubjson::ubjson_type::start_object_marker);
+ sink_.push_back(jsoncons::ubjson::ubjson_type::count_marker);
+ put_length(length);
+
+ 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(jsoncons::ubjson::ubjson_type::end_object_marker);
+ }
+ else
+ {
+ if (stack_.back().count() < stack_.back().length())
+ {
+ ec = ubjson_errc::too_few_items;
+ return false;
+ }
+ if (stack_.back().count() > stack_.back().length())
+ {
+ ec = ubjson_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 = ubjson_errc::max_nesting_depth_exceeded;
+ return false;
+ }
+ stack_.emplace_back(ubjson_container_type::indefinite_length_array);
+ sink_.push_back(jsoncons::ubjson::ubjson_type::start_array_marker);
+
+ 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 = ubjson_errc::max_nesting_depth_exceeded;
+ return false;
+ }
+ stack_.emplace_back(ubjson_container_type::array, length);
+ sink_.push_back(jsoncons::ubjson::ubjson_type::start_array_marker);
+ sink_.push_back(jsoncons::ubjson::ubjson_type::count_marker);
+ put_length(length);
+
+ 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(jsoncons::ubjson::ubjson_type::end_array_marker);
+ }
+ else
+ {
+ if (stack_.back().count() < stack_.back().length())
+ {
+ ec = ubjson_errc::too_few_items;
+ return false;
+ }
+ if (stack_.back().count() > stack_.back().length())
+ {
+ ec = ubjson_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& ec) override
+ {
+ auto sink = unicode_traits::validate(name.data(), name.size());
+ if (sink.ec != unicode_traits::conv_errc())
+ {
+ ec = ubjson_errc::invalid_utf8_text_string;
+ return false;
+ }
+
+ put_length(name.length());
+
+ for (auto c : name)
+ {
+ sink_.push_back(c);
+ }
+ return true;
+ }
+
+ bool visit_null(semantic_tag, const ser_context&, std::error_code&) override
+ {
+ // nil
+ binary::native_to_big(static_cast<uint8_t>(jsoncons::ubjson::ubjson_type::null_type), std::back_inserter(sink_));
+ end_value();
+ return true;
+ }
+
+ bool visit_string(const string_view_type& sv, semantic_tag tag, const ser_context&, std::error_code& ec) override
+ {
+ switch (tag)
+ {
+ case semantic_tag::bigint:
+ case semantic_tag::bigdec:
+ {
+ sink_.push_back(jsoncons::ubjson::ubjson_type::high_precision_number_type);
+ break;
+ }
+ default:
+ {
+ sink_.push_back(jsoncons::ubjson::ubjson_type::string_type);
+ break;
+ }
+ }
+
+ auto sink = unicode_traits::validate(sv.data(), sv.size());
+ if (sink.ec != unicode_traits::conv_errc())
+ {
+ ec = ubjson_errc::invalid_utf8_text_string;
+ return false;
+ }
+
+ put_length(sv.length());
+
+ for (auto c : sv)
+ {
+ sink_.push_back(c);
+ }
+
+ end_value();
+ return true;
+ }
+
+ void put_length(std::size_t length)
+ {
+ if (length <= (std::numeric_limits<uint8_t>::max)())
+ {
+ sink_.push_back(ubjson_type::uint8_type);
+ binary::native_to_big(static_cast<uint8_t>(length), std::back_inserter(sink_));
+ }
+ else if (length <= (std::size_t)(std::numeric_limits<int16_t>::max)())
+ {
+ sink_.push_back(ubjson_type::int16_type);
+ binary::native_to_big(static_cast<uint16_t>(length), std::back_inserter(sink_));
+ }
+ else if (length <= (std::size_t)(std::numeric_limits<int32_t>::max)())
+ {
+ sink_.push_back(ubjson_type::int32_type);
+ binary::native_to_big(static_cast<uint32_t>(length),std::back_inserter(sink_));
+ }
+ else if (length <= (std::size_t)(std::numeric_limits<int64_t>::max)())
+ {
+ sink_.push_back(ubjson_type::int64_type);
+ binary::native_to_big(static_cast<uint64_t>(length),std::back_inserter(sink_));
+ }
+ else
+ {
+ JSONCONS_THROW(ser_error(ubjson_errc::too_many_items));
+ }
+ }
+
+ bool visit_byte_string(const byte_string_view& b,
+ semantic_tag,
+ const ser_context&,
+ std::error_code&) override
+ {
+
+ const size_t length = b.size();
+ sink_.push_back(jsoncons::ubjson::ubjson_type::start_array_marker);
+ binary::native_to_big(static_cast<uint8_t>(jsoncons::ubjson::ubjson_type::type_marker), std::back_inserter(sink_));
+ binary::native_to_big(static_cast<uint8_t>(jsoncons::ubjson::ubjson_type::uint8_type), std::back_inserter(sink_));
+ put_length(length);
+
+ 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(static_cast<uint8_t>(jsoncons::ubjson::ubjson_type::float32_type));
+ binary::native_to_big(valf,std::back_inserter(sink_));
+ }
+ else
+ {
+ // float 64
+ sink_.push_back(static_cast<uint8_t>(jsoncons::ubjson::ubjson_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,
+ const ser_context&,
+ std::error_code&) override
+ {
+ if (val >= 0)
+ {
+ if (val <= (std::numeric_limits<uint8_t>::max)())
+ {
+ // uint 8 stores a 8-bit unsigned integer
+ sink_.push_back(jsoncons::ubjson::ubjson_type::uint8_type);
+ binary::native_to_big(static_cast<uint8_t>(val),std::back_inserter(sink_));
+ }
+ else if (val <= (std::numeric_limits<int16_t>::max)())
+ {
+ // uint 16 stores a 16-bit big-endian unsigned integer
+ sink_.push_back(jsoncons::ubjson::ubjson_type::int16_type);
+ binary::native_to_big(static_cast<int16_t>(val),std::back_inserter(sink_));
+ }
+ else if (val <= (std::numeric_limits<int32_t>::max)())
+ {
+ // uint 32 stores a 32-bit big-endian unsigned integer
+ sink_.push_back(jsoncons::ubjson::ubjson_type::int32_type);
+ binary::native_to_big(static_cast<int32_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::ubjson::ubjson_type::int64_type);
+ binary::native_to_big(static_cast<int64_t>(val),std::back_inserter(sink_));
+ }
+ else
+ {
+ // big integer
+ }
+ }
+ else
+ {
+ if (val >= (std::numeric_limits<int8_t>::lowest)())
+ {
+ // int 8 stores a 8-bit signed integer
+ sink_.push_back(jsoncons::ubjson::ubjson_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::ubjson::ubjson_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::ubjson::ubjson_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::ubjson::ubjson_type::int64_type);
+ binary::native_to_big(static_cast<int64_t>(val),std::back_inserter(sink_));
+ }
+ }
+ end_value();
+ return true;
+ }
+
+ bool visit_uint64(uint64_t val,
+ semantic_tag,
+ const ser_context&,
+ std::error_code&) override
+ {
+ if (val <= (std::numeric_limits<uint8_t>::max)())
+ {
+ sink_.push_back(jsoncons::ubjson::ubjson_type::uint8_type);
+ binary::native_to_big(static_cast<uint8_t>(val),std::back_inserter(sink_));
+ }
+ else if (val <= static_cast<uint64_t>((std::numeric_limits<int16_t>::max)()))
+ {
+ sink_.push_back(jsoncons::ubjson::ubjson_type::int16_type);
+ binary::native_to_big(static_cast<int16_t>(val),std::back_inserter(sink_));
+ }
+ else if (val <= static_cast<uint64_t>((std::numeric_limits<int32_t>::max)()))
+ {
+ sink_.push_back(jsoncons::ubjson::ubjson_type::int32_type);
+ binary::native_to_big(static_cast<int32_t>(val),std::back_inserter(sink_));
+ }
+ else if (val <= static_cast<uint64_t>((std::numeric_limits<int64_t>::max)()))
+ {
+ sink_.push_back(jsoncons::ubjson::ubjson_type::int64_type);
+ binary::native_to_big(static_cast<int64_t>(val),std::back_inserter(sink_));
+ }
+ 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::ubjson::ubjson_type::true_type : jsoncons::ubjson::ubjson_type::false_type));
+
+ end_value();
+ return true;
+ }
+
+ void end_value()
+ {
+ if (!stack_.empty())
+ {
+ ++stack_.back().count_;
+ }
+ }
+};
+
+using ubjson_stream_encoder = basic_ubjson_encoder<jsoncons::binary_stream_sink>;
+using ubjson_bytes_encoder = basic_ubjson_encoder<jsoncons::bytes_sink<std::vector<uint8_t>>>;
+
+#if !defined(JSONCONS_NO_DEPRECATED)
+template<class Sink=jsoncons::binary_stream_sink>
+using basic_ubjson_serializer = basic_ubjson_encoder<Sink>;
+
+JSONCONS_DEPRECATED_MSG("Instead, use ubjson_stream_encoder") typedef ubjson_stream_encoder ubjson_encoder;
+JSONCONS_DEPRECATED_MSG("Instead, use ubjson_stream_encoder") typedef ubjson_stream_encoder ubjson_serializer;
+JSONCONS_DEPRECATED_MSG("Instead, use ubjson_bytes_encoder") typedef ubjson_bytes_encoder ubjson_buffer_serializer;
+#endif
+
+}}
+#endif