aboutsummaryrefslogtreecommitdiff
path: root/include/jsoncons/json_encoder.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'include/jsoncons/json_encoder.hpp')
-rw-r--r--include/jsoncons/json_encoder.hpp1587
1 files changed, 1587 insertions, 0 deletions
diff --git a/include/jsoncons/json_encoder.hpp b/include/jsoncons/json_encoder.hpp
new file mode 100644
index 0000000..df0d3fa
--- /dev/null
+++ b/include/jsoncons/json_encoder.hpp
@@ -0,0 +1,1587 @@
+// Copyright 2013 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_JSON_ENCODER_HPP
+#define JSONCONS_JSON_ENCODER_HPP
+
+#include <array> // std::array
+#include <string>
+#include <vector>
+#include <cmath> // std::isfinite, std::isnan
+#include <limits> // std::numeric_limits
+#include <memory>
+#include <utility> // std::move
+#include <jsoncons/config/jsoncons_config.hpp>
+#include <jsoncons/json_exception.hpp>
+#include <jsoncons/byte_string.hpp>
+#include <jsoncons/bigint.hpp>
+#include <jsoncons/json_options.hpp>
+#include <jsoncons/json_error.hpp>
+#include <jsoncons/json_visitor.hpp>
+#include <jsoncons/sink.hpp>
+#include <jsoncons/detail/write_number.hpp>
+
+namespace jsoncons {
+namespace detail {
+
+ inline
+ bool is_control_character(uint32_t c)
+ {
+ return c <= 0x1F || c == 0x7f;
+ }
+
+ inline
+ bool is_non_ascii_codepoint(uint32_t cp)
+ {
+ return cp >= 0x80;
+ }
+
+ template <class CharT, class Sink>
+ std::size_t escape_string(const CharT* s, std::size_t length,
+ bool escape_all_non_ascii, bool escape_solidus,
+ Sink& sink)
+ {
+ std::size_t count = 0;
+ const CharT* begin = s;
+ const CharT* end = s + length;
+ for (const CharT* it = begin; it != end; ++it)
+ {
+ CharT c = *it;
+ switch (c)
+ {
+ case '\\':
+ sink.push_back('\\');
+ sink.push_back('\\');
+ count += 2;
+ break;
+ case '"':
+ sink.push_back('\\');
+ sink.push_back('\"');
+ count += 2;
+ break;
+ case '\b':
+ sink.push_back('\\');
+ sink.push_back('b');
+ count += 2;
+ break;
+ case '\f':
+ sink.push_back('\\');
+ sink.push_back('f');
+ count += 2;
+ break;
+ case '\n':
+ sink.push_back('\\');
+ sink.push_back('n');
+ count += 2;
+ break;
+ case '\r':
+ sink.push_back('\\');
+ sink.push_back('r');
+ count += 2;
+ break;
+ case '\t':
+ sink.push_back('\\');
+ sink.push_back('t');
+ count += 2;
+ break;
+ default:
+ if (escape_solidus && c == '/')
+ {
+ sink.push_back('\\');
+ sink.push_back('/');
+ count += 2;
+ }
+ else if (is_control_character(c) || escape_all_non_ascii)
+ {
+ // convert to codepoint
+ uint32_t cp;
+ auto r = unicode_traits::to_codepoint(it, end, cp, unicode_traits::conv_flags::strict);
+ if (r.ec != unicode_traits::conv_errc())
+ {
+ JSONCONS_THROW(ser_error(json_errc::illegal_codepoint));
+ }
+ it = r.ptr - 1;
+ if (is_non_ascii_codepoint(cp) || is_control_character(c))
+ {
+ if (cp > 0xFFFF)
+ {
+ cp -= 0x10000;
+ uint32_t first = (cp >> 10) + 0xD800;
+ uint32_t second = ((cp & 0x03FF) + 0xDC00);
+
+ sink.push_back('\\');
+ sink.push_back('u');
+ sink.push_back(jsoncons::detail::to_hex_character(first >> 12 & 0x000F));
+ sink.push_back(jsoncons::detail::to_hex_character(first >> 8 & 0x000F));
+ sink.push_back(jsoncons::detail::to_hex_character(first >> 4 & 0x000F));
+ sink.push_back(jsoncons::detail::to_hex_character(first & 0x000F));
+ sink.push_back('\\');
+ sink.push_back('u');
+ sink.push_back(jsoncons::detail::to_hex_character(second >> 12 & 0x000F));
+ sink.push_back(jsoncons::detail::to_hex_character(second >> 8 & 0x000F));
+ sink.push_back(jsoncons::detail::to_hex_character(second >> 4 & 0x000F));
+ sink.push_back(jsoncons::detail::to_hex_character(second & 0x000F));
+ count += 12;
+ }
+ else
+ {
+ sink.push_back('\\');
+ sink.push_back('u');
+ sink.push_back(jsoncons::detail::to_hex_character(cp >> 12 & 0x000F));
+ sink.push_back(jsoncons::detail::to_hex_character(cp >> 8 & 0x000F));
+ sink.push_back(jsoncons::detail::to_hex_character(cp >> 4 & 0x000F));
+ sink.push_back(jsoncons::detail::to_hex_character(cp & 0x000F));
+ count += 6;
+ }
+ }
+ else
+ {
+ sink.push_back(c);
+ ++count;
+ }
+ }
+ else
+ {
+ sink.push_back(c);
+ ++count;
+ }
+ break;
+ }
+ }
+ return count;
+ }
+
+ inline
+ byte_string_chars_format resolve_byte_string_chars_format(byte_string_chars_format format1,
+ byte_string_chars_format format2,
+ byte_string_chars_format default_format = byte_string_chars_format::base64url)
+ {
+ byte_string_chars_format sink;
+ switch (format1)
+ {
+ case byte_string_chars_format::base16:
+ case byte_string_chars_format::base64:
+ case byte_string_chars_format::base64url:
+ sink = format1;
+ break;
+ default:
+ switch (format2)
+ {
+ case byte_string_chars_format::base64url:
+ case byte_string_chars_format::base64:
+ case byte_string_chars_format::base16:
+ sink = format2;
+ break;
+ default: // base64url
+ {
+ sink = default_format;
+ break;
+ }
+ }
+ break;
+ }
+ return sink;
+ }
+
+} // namespace detail
+
+ template<class CharT,class Sink=jsoncons::stream_sink<CharT>,class Allocator=std::allocator<char>>
+ class basic_json_encoder final : public basic_json_visitor<CharT>
+ {
+ static const jsoncons::basic_string_view<CharT> null_constant()
+ {
+ static const jsoncons::basic_string_view<CharT> k = JSONCONS_STRING_VIEW_CONSTANT(CharT, "null");
+ return k;
+ }
+ static const jsoncons::basic_string_view<CharT> true_constant()
+ {
+ static const jsoncons::basic_string_view<CharT> k = JSONCONS_STRING_VIEW_CONSTANT(CharT, "true");
+ return k;
+ }
+ static const jsoncons::basic_string_view<CharT> false_constant()
+ {
+ static const jsoncons::basic_string_view<CharT> k = JSONCONS_STRING_VIEW_CONSTANT(CharT, "false");
+ return k;
+ }
+ public:
+ using allocator_type = Allocator;
+ using char_type = CharT;
+ using typename basic_json_visitor<CharT>::string_view_type;
+ using sink_type = Sink;
+ using string_type = typename basic_json_encode_options<CharT>::string_type;
+
+ private:
+ enum class container_type {object, array};
+
+ class encoding_context
+ {
+ container_type type_;
+ std::size_t count_;
+ line_split_kind line_splits_;
+ bool indent_before_;
+ bool new_line_after_;
+ std::size_t begin_pos_;
+ std::size_t data_pos_;
+ public:
+ encoding_context(container_type type, line_split_kind split_lines, bool indent_once,
+ std::size_t begin_pos, std::size_t data_pos) noexcept
+ : type_(type), count_(0), line_splits_(split_lines), indent_before_(indent_once), new_line_after_(false),
+ begin_pos_(begin_pos), data_pos_(data_pos)
+ {
+ }
+
+ encoding_context(const encoding_context&) = default;
+ encoding_context& operator=(const encoding_context&) = default;
+
+ void set_position(std::size_t pos)
+ {
+ data_pos_ = pos;
+ }
+
+ std::size_t begin_pos() const
+ {
+ return begin_pos_;
+ }
+
+ std::size_t data_pos() const
+ {
+ return data_pos_;
+ }
+
+ std::size_t count() const
+ {
+ return count_;
+ }
+
+ void increment_count()
+ {
+ ++count_;
+ }
+
+ bool new_line_after() const
+ {
+ return new_line_after_;
+ }
+
+ void new_line_after(bool value)
+ {
+ new_line_after_ = value;
+ }
+
+ bool is_object() const
+ {
+ return type_ == container_type::object;
+ }
+
+ bool is_array() const
+ {
+ return type_ == container_type::array;
+ }
+
+ bool is_same_line() const
+ {
+ return line_splits_ == line_split_kind::same_line;
+ }
+
+ bool is_new_line() const
+ {
+ return line_splits_ == line_split_kind::new_line;
+ }
+
+ bool is_multi_line() const
+ {
+ return line_splits_ == line_split_kind::multi_line;
+ }
+
+ bool is_indent_once() const
+ {
+ return count_ == 0 ? indent_before_ : false;
+ }
+
+ };
+ typedef typename std::allocator_traits<allocator_type>:: template rebind_alloc<encoding_context> encoding_context_allocator_type;
+
+ Sink sink_;
+ basic_json_encode_options<CharT> options_;
+ jsoncons::detail::write_double fp_;
+
+ std::vector<encoding_context,encoding_context_allocator_type> stack_;
+ int indent_amount_;
+ std::size_t column_;
+ std::basic_string<CharT> colon_str_;
+ std::basic_string<CharT> comma_str_;
+ std::basic_string<CharT> open_object_brace_str_;
+ std::basic_string<CharT> close_object_brace_str_;
+ std::basic_string<CharT> open_array_bracket_str_;
+ std::basic_string<CharT> close_array_bracket_str_;
+ int nesting_depth_;
+
+ // Noncopyable and nonmoveable
+ basic_json_encoder(const basic_json_encoder&) = delete;
+ basic_json_encoder& operator=(const basic_json_encoder&) = delete;
+ public:
+ basic_json_encoder(Sink&& sink,
+ const Allocator& alloc = Allocator())
+ : basic_json_encoder(std::forward<Sink>(sink), basic_json_encode_options<CharT>(), alloc)
+ {
+ }
+
+ basic_json_encoder(Sink&& sink,
+ const basic_json_encode_options<CharT>& options,
+ const Allocator& alloc = Allocator())
+ : sink_(std::forward<Sink>(sink)),
+ options_(options),
+ fp_(options.float_format(), options.precision()),
+ stack_(alloc),
+ indent_amount_(0),
+ column_(0),
+ nesting_depth_(0)
+ {
+ switch (options.spaces_around_colon())
+ {
+ case spaces_option::space_after:
+ colon_str_ = std::basic_string<CharT>({':',' '});
+ break;
+ case spaces_option::space_before:
+ colon_str_ = std::basic_string<CharT>({' ',':'});
+ break;
+ case spaces_option::space_before_and_after:
+ colon_str_ = std::basic_string<CharT>({' ',':',' '});
+ break;
+ default:
+ colon_str_.push_back(':');
+ break;
+ }
+ switch (options.spaces_around_comma())
+ {
+ case spaces_option::space_after:
+ comma_str_ = std::basic_string<CharT>({',',' '});
+ break;
+ case spaces_option::space_before:
+ comma_str_ = std::basic_string<CharT>({' ',','});
+ break;
+ case spaces_option::space_before_and_after:
+ comma_str_ = std::basic_string<CharT>({' ',',',' '});
+ break;
+ default:
+ comma_str_.push_back(',');
+ break;
+ }
+ if (options.pad_inside_object_braces())
+ {
+ open_object_brace_str_ = std::basic_string<CharT>({'{', ' '});
+ close_object_brace_str_ = std::basic_string<CharT>({' ', '}'});
+ }
+ else
+ {
+ open_object_brace_str_.push_back('{');
+ close_object_brace_str_.push_back('}');
+ }
+ if (options.pad_inside_array_brackets())
+ {
+ open_array_bracket_str_ = std::basic_string<CharT>({'[', ' '});
+ close_array_bracket_str_ = std::basic_string<CharT>({' ', ']'});
+ }
+ else
+ {
+ open_array_bracket_str_.push_back('[');
+ close_array_bracket_str_.push_back(']');
+ }
+ }
+
+ ~basic_json_encoder() noexcept
+ {
+ JSONCONS_TRY
+ {
+ sink_.flush();
+ }
+ JSONCONS_CATCH(...)
+ {
+ }
+ }
+
+ void reset()
+ {
+ stack_.clear();
+ indent_amount_ = 0;
+ column_ = 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 = json_errc::max_nesting_depth_exceeded;
+ return false;
+ }
+ if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0)
+ {
+ sink_.append(comma_str_.data(),comma_str_.length());
+ column_ += comma_str_.length();
+ }
+
+ if (!stack_.empty()) // object or array
+ {
+ if (stack_.back().is_object())
+ {
+ switch (options_.object_object_line_splits())
+ {
+ case line_split_kind::same_line:
+ if (column_ >= options_.line_length_limit())
+ {
+ break_line();
+ }
+ break;
+ case line_split_kind::new_line:
+ if (column_ >= options_.line_length_limit())
+ {
+ break_line();
+ }
+ break;
+ default: // multi_line
+ break;
+ }
+ stack_.emplace_back(container_type::object,options_.object_object_line_splits(), false,
+ column_, column_+open_object_brace_str_.length());
+ }
+ else // array
+ {
+ switch (options_.array_object_line_splits())
+ {
+ case line_split_kind::same_line:
+ if (column_ >= options_.line_length_limit())
+ {
+ //stack_.back().new_line_after(true);
+ new_line();
+ }
+ break;
+ case line_split_kind::new_line:
+ stack_.back().new_line_after(true);
+ new_line();
+ break;
+ default: // multi_line
+ stack_.back().new_line_after(true);
+ new_line();
+ break;
+ }
+ stack_.emplace_back(container_type::object,options_.array_object_line_splits(), false,
+ column_, column_+open_object_brace_str_.length());
+ }
+ }
+ else
+ {
+ stack_.emplace_back(container_type::object, line_split_kind::multi_line, false,
+ column_, column_+open_object_brace_str_.length());
+ }
+ indent();
+
+ sink_.append(open_object_brace_str_.data(), open_object_brace_str_.length());
+ column_ += open_object_brace_str_.length();
+ return true;
+ }
+
+ bool visit_end_object(const ser_context&, std::error_code&) override
+ {
+ JSONCONS_ASSERT(!stack_.empty());
+ --nesting_depth_;
+
+ unindent();
+ if (stack_.back().new_line_after())
+ {
+ new_line();
+ }
+ stack_.pop_back();
+ sink_.append(close_object_brace_str_.data(), close_object_brace_str_.length());
+ column_ += close_object_brace_str_.length();
+
+ 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 = json_errc::max_nesting_depth_exceeded;
+ return false;
+ }
+ if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0)
+ {
+ sink_.append(comma_str_.data(),comma_str_.length());
+ column_ += comma_str_.length();
+ }
+ if (!stack_.empty())
+ {
+ if (stack_.back().is_object())
+ {
+ switch (options_.object_array_line_splits())
+ {
+ case line_split_kind::same_line:
+ stack_.emplace_back(container_type::array,options_.object_array_line_splits(),false,
+ column_, column_ + open_array_bracket_str_.length());
+ break;
+ case line_split_kind::new_line:
+ {
+ stack_.emplace_back(container_type::array,options_.object_array_line_splits(),true,
+ column_, column_+open_array_bracket_str_.length());
+ break;
+ }
+ default: // multi_line
+ stack_.emplace_back(container_type::array,options_.object_array_line_splits(),true,
+ column_, column_+open_array_bracket_str_.length());
+ break;
+ }
+ }
+ else // array
+ {
+ switch (options_.array_array_line_splits())
+ {
+ case line_split_kind::same_line:
+ if (stack_.back().is_multi_line())
+ {
+ stack_.back().new_line_after(true);
+ new_line();
+ }
+ stack_.emplace_back(container_type::array,options_.array_array_line_splits(), false,
+ column_, column_+open_array_bracket_str_.length());
+ break;
+ case line_split_kind::new_line:
+ stack_.back().new_line_after(true);
+ new_line();
+ stack_.emplace_back(container_type::array,options_.array_array_line_splits(), false,
+ column_, column_+open_array_bracket_str_.length());
+ break;
+ default: // multi_line
+ stack_.back().new_line_after(true);
+ new_line();
+ stack_.emplace_back(container_type::array,options_.array_array_line_splits(), false,
+ column_, column_+open_array_bracket_str_.length());
+ //new_line();
+ break;
+ }
+ }
+ }
+ else
+ {
+ stack_.emplace_back(container_type::array, line_split_kind::multi_line, false,
+ column_, column_+open_array_bracket_str_.length());
+ }
+ indent();
+ sink_.append(open_array_bracket_str_.data(), open_array_bracket_str_.length());
+ column_ += open_array_bracket_str_.length();
+ return true;
+ }
+
+ bool visit_end_array(const ser_context&, std::error_code&) override
+ {
+ JSONCONS_ASSERT(!stack_.empty());
+ --nesting_depth_;
+
+ unindent();
+ if (stack_.back().new_line_after())
+ {
+ new_line();
+ }
+ stack_.pop_back();
+ sink_.append(close_array_bracket_str_.data(), close_array_bracket_str_.length());
+ column_ += close_array_bracket_str_.length();
+ end_value();
+ return true;
+ }
+
+ bool visit_key(const string_view_type& name, const ser_context&, std::error_code&) override
+ {
+ JSONCONS_ASSERT(!stack_.empty());
+ if (stack_.back().count() > 0)
+ {
+ sink_.append(comma_str_.data(),comma_str_.length());
+ column_ += comma_str_.length();
+ }
+
+ if (stack_.back().is_multi_line())
+ {
+ stack_.back().new_line_after(true);
+ new_line();
+ }
+ else if (stack_.back().count() > 0 && column_ >= options_.line_length_limit())
+ {
+ //stack_.back().new_line_after(true);
+ new_line(stack_.back().data_pos());
+ }
+
+ if (stack_.back().count() == 0)
+ {
+ stack_.back().set_position(column_);
+ }
+ sink_.push_back('\"');
+ std::size_t length = jsoncons::detail::escape_string(name.data(), name.length(),options_.escape_all_non_ascii(),options_.escape_solidus(),sink_);
+ sink_.push_back('\"');
+ sink_.append(colon_str_.data(),colon_str_.length());
+ column_ += (length+2+colon_str_.length());
+ return true;
+ }
+
+ bool visit_null(semantic_tag, const ser_context&, std::error_code&) override
+ {
+ if (!stack_.empty())
+ {
+ if (stack_.back().is_array())
+ {
+ begin_scalar_value();
+ }
+ if (!stack_.back().is_multi_line() && column_ >= options_.line_length_limit())
+ {
+ break_line();
+ }
+ }
+
+ sink_.append(null_constant().data(), null_constant().size());
+ column_ += null_constant().size();
+
+ end_value();
+ return true;
+ }
+
+ bool visit_string(const string_view_type& sv, semantic_tag tag, const ser_context&, std::error_code&) override
+ {
+ if (!stack_.empty())
+ {
+ if (stack_.back().is_array())
+ {
+ begin_scalar_value();
+ }
+ if (!stack_.back().is_multi_line() && column_ >= options_.line_length_limit())
+ {
+ break_line();
+ }
+ }
+
+ switch (tag)
+ {
+ case semantic_tag::bigint:
+ write_bigint_value(sv);
+ break;
+ default:
+ {
+ sink_.push_back('\"');
+ std::size_t length = jsoncons::detail::escape_string(sv.data(), sv.length(),options_.escape_all_non_ascii(),options_.escape_solidus(),sink_);
+ sink_.push_back('\"');
+ column_ += (length+2);
+ break;
+ }
+ }
+
+ end_value();
+ return true;
+ }
+
+ bool visit_byte_string(const byte_string_view& b,
+ semantic_tag tag,
+ const ser_context&,
+ std::error_code&) override
+ {
+ if (!stack_.empty())
+ {
+ if (stack_.back().is_array())
+ {
+ begin_scalar_value();
+ }
+ if (!stack_.back().is_multi_line() && column_ >= options_.line_length_limit())
+ {
+ break_line();
+ }
+ }
+
+ 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;
+ }
+
+ byte_string_chars_format format = jsoncons::detail::resolve_byte_string_chars_format(options_.byte_string_format(),
+ encoding_hint,
+ byte_string_chars_format::base64url);
+ switch (format)
+ {
+ case byte_string_chars_format::base16:
+ {
+ sink_.push_back('\"');
+ std::size_t length = encode_base16(b.begin(),b.end(),sink_);
+ sink_.push_back('\"');
+ column_ += (length + 2);
+ break;
+ }
+ case byte_string_chars_format::base64:
+ {
+ sink_.push_back('\"');
+ std::size_t length = encode_base64(b.begin(), b.end(), sink_);
+ sink_.push_back('\"');
+ column_ += (length + 2);
+ break;
+ }
+ case byte_string_chars_format::base64url:
+ {
+ sink_.push_back('\"');
+ std::size_t length = encode_base64url(b.begin(),b.end(),sink_);
+ sink_.push_back('\"');
+ column_ += (length + 2);
+ break;
+ }
+ default:
+ {
+ JSONCONS_UNREACHABLE();
+ }
+ }
+
+ end_value();
+ return true;
+ }
+
+ bool visit_double(double value,
+ semantic_tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ if (!stack_.empty())
+ {
+ if (stack_.back().is_array())
+ {
+ begin_scalar_value();
+ }
+ if (!stack_.back().is_multi_line() && column_ >= options_.line_length_limit())
+ {
+ break_line();
+ }
+ }
+
+ if (!std::isfinite(value))
+ {
+ if ((std::isnan)(value))
+ {
+ if (options_.enable_nan_to_num())
+ {
+ sink_.append(options_.nan_to_num().data(), options_.nan_to_num().length());
+ column_ += options_.nan_to_num().length();
+ }
+ else if (options_.enable_nan_to_str())
+ {
+ visit_string(options_.nan_to_str(), semantic_tag::none, context, ec);
+ }
+ else
+ {
+ sink_.append(null_constant().data(), null_constant().size());
+ column_ += null_constant().size();
+ }
+ }
+ else if (value == std::numeric_limits<double>::infinity())
+ {
+ if (options_.enable_inf_to_num())
+ {
+ sink_.append(options_.inf_to_num().data(), options_.inf_to_num().length());
+ column_ += options_.inf_to_num().length();
+ }
+ else if (options_.enable_inf_to_str())
+ {
+ visit_string(options_.inf_to_str(), semantic_tag::none, context, ec);
+ }
+ else
+ {
+ sink_.append(null_constant().data(), null_constant().size());
+ column_ += null_constant().size();
+ }
+ }
+ else
+ {
+ if (options_.enable_neginf_to_num())
+ {
+ sink_.append(options_.neginf_to_num().data(), options_.neginf_to_num().length());
+ column_ += options_.neginf_to_num().length();
+ }
+ else if (options_.enable_neginf_to_str())
+ {
+ visit_string(options_.neginf_to_str(), semantic_tag::none, context, ec);
+ }
+ else
+ {
+ sink_.append(null_constant().data(), null_constant().size());
+ column_ += null_constant().size();
+ }
+ }
+ }
+ else
+ {
+ std::size_t length = fp_(value, sink_);
+ column_ += length;
+ }
+
+ end_value();
+ return true;
+ }
+
+ bool visit_int64(int64_t value,
+ semantic_tag,
+ const ser_context&,
+ std::error_code&) override
+ {
+ if (!stack_.empty())
+ {
+ if (stack_.back().is_array())
+ {
+ begin_scalar_value();
+ }
+ if (!stack_.back().is_multi_line() && column_ >= options_.line_length_limit())
+ {
+ break_line();
+ }
+ }
+ std::size_t length = jsoncons::detail::from_integer(value, sink_);
+ column_ += length;
+ end_value();
+ return true;
+ }
+
+ bool visit_uint64(uint64_t value,
+ semantic_tag,
+ const ser_context&,
+ std::error_code&) override
+ {
+ if (!stack_.empty())
+ {
+ if (stack_.back().is_array())
+ {
+ begin_scalar_value();
+ }
+ if (!stack_.back().is_multi_line() && column_ >= options_.line_length_limit())
+ {
+ break_line();
+ }
+ }
+ std::size_t length = jsoncons::detail::from_integer(value, sink_);
+ column_ += length;
+ end_value();
+ return true;
+ }
+
+ bool visit_bool(bool value, semantic_tag, const ser_context&, std::error_code&) override
+ {
+ if (!stack_.empty())
+ {
+ if (stack_.back().is_array())
+ {
+ begin_scalar_value();
+ }
+ if (!stack_.back().is_multi_line() && column_ >= options_.line_length_limit())
+ {
+ break_line();
+ }
+ }
+
+ if (value)
+ {
+ sink_.append(true_constant().data(), true_constant().size());
+ column_ += true_constant().size();
+ }
+ else
+ {
+ sink_.append(false_constant().data(), false_constant().size());
+ column_ += false_constant().size();
+ }
+
+ end_value();
+ return true;
+ }
+
+ void begin_scalar_value()
+ {
+ if (!stack_.empty())
+ {
+ if (stack_.back().count() > 0)
+ {
+ sink_.append(comma_str_.data(),comma_str_.length());
+ column_ += comma_str_.length();
+ }
+ if (stack_.back().is_multi_line() || stack_.back().is_indent_once())
+ {
+ stack_.back().new_line_after(true);
+ new_line();
+ }
+ }
+ }
+
+ void write_bigint_value(const string_view_type& sv)
+ {
+ switch (options_.bigint_format())
+ {
+ case bigint_chars_format::number:
+ {
+ sink_.append(sv.data(),sv.size());
+ column_ += sv.size();
+ break;
+ }
+ case bigint_chars_format::base64:
+ {
+ bigint n = bigint::from_string(sv.data(), sv.length());
+ bool is_neg = n < 0;
+ if (is_neg)
+ {
+ n = - n -1;
+ }
+ int signum;
+ std::vector<uint8_t> v;
+ n.write_bytes_be(signum, v);
+
+ sink_.push_back('\"');
+ if (is_neg)
+ {
+ sink_.push_back('~');
+ ++column_;
+ }
+ std::size_t length = encode_base64(v.begin(), v.end(), sink_);
+ sink_.push_back('\"');
+ column_ += (length+2);
+ break;
+ }
+ case bigint_chars_format::base64url:
+ {
+ bigint n = bigint::from_string(sv.data(), sv.length());
+ bool is_neg = n < 0;
+ if (is_neg)
+ {
+ n = - n -1;
+ }
+ int signum;
+ std::vector<uint8_t> v;
+ n.write_bytes_be(signum, v);
+
+ sink_.push_back('\"');
+ if (is_neg)
+ {
+ sink_.push_back('~');
+ ++column_;
+ }
+ std::size_t length = encode_base64url(v.begin(), v.end(), sink_);
+ sink_.push_back('\"');
+ column_ += (length+2);
+ break;
+ }
+ default:
+ {
+ sink_.push_back('\"');
+ sink_.append(sv.data(),sv.size());
+ sink_.push_back('\"');
+ column_ += (sv.size() + 2);
+ break;
+ }
+ }
+ }
+
+ void end_value()
+ {
+ if (!stack_.empty())
+ {
+ stack_.back().increment_count();
+ }
+ }
+
+ void indent()
+ {
+ indent_amount_ += static_cast<int>(options_.indent_size());
+ }
+
+ void unindent()
+ {
+ indent_amount_ -= static_cast<int>(options_.indent_size());
+ }
+
+ void new_line()
+ {
+ sink_.append(options_.new_line_chars().data(),options_.new_line_chars().length());
+ for (int i = 0; i < indent_amount_; ++i)
+ {
+ sink_.push_back(' ');
+ }
+ column_ = indent_amount_;
+ }
+
+ void new_line(std::size_t len)
+ {
+ sink_.append(options_.new_line_chars().data(),options_.new_line_chars().length());
+ for (std::size_t i = 0; i < len; ++i)
+ {
+ sink_.push_back(' ');
+ }
+ column_ = len;
+ }
+
+ void break_line()
+ {
+ stack_.back().new_line_after(true);
+ new_line();
+ }
+ };
+
+ template<class CharT,class Sink=jsoncons::stream_sink<CharT>,class Allocator=std::allocator<char>>
+ class basic_compact_json_encoder final : public basic_json_visitor<CharT>
+ {
+ static const std::array<CharT, 4>& null_constant()
+ {
+ static constexpr std::array<CharT,4> k{'n','u','l','l'};
+ return k;
+ }
+ static const std::array<CharT, 4>& true_constant()
+ {
+ static constexpr std::array<CharT,4> k{'t','r','u','e'};
+ return k;
+ }
+ static const std::array<CharT, 5>& false_constant()
+ {
+ static constexpr std::array<CharT,5> k{'f','a','l','s','e'};
+ return k;
+ }
+ public:
+ using allocator_type = Allocator;
+ using char_type = CharT;
+ using typename basic_json_visitor<CharT>::string_view_type;
+ using sink_type = Sink;
+ using string_type = typename basic_json_encode_options<CharT>::string_type;
+
+ private:
+ enum class container_type {object, array};
+
+ class encoding_context
+ {
+ container_type type_;
+ std::size_t count_;
+ public:
+ encoding_context(container_type type) noexcept
+ : type_(type), count_(0)
+ {
+ }
+
+ std::size_t count() const
+ {
+ return count_;
+ }
+
+ void increment_count()
+ {
+ ++count_;
+ }
+
+ bool is_array() const
+ {
+ return type_ == container_type::array;
+ }
+ };
+ typedef typename std::allocator_traits<allocator_type>:: template rebind_alloc<encoding_context> encoding_context_allocator_type;
+
+ Sink sink_;
+ basic_json_encode_options<CharT> options_;
+ jsoncons::detail::write_double fp_;
+ std::vector<encoding_context,encoding_context_allocator_type> stack_;
+ int nesting_depth_;
+
+ // Noncopyable
+ basic_compact_json_encoder(const basic_compact_json_encoder&) = delete;
+ basic_compact_json_encoder& operator=(const basic_compact_json_encoder&) = delete;
+ public:
+ basic_compact_json_encoder(Sink&& sink,
+ const Allocator& alloc = Allocator())
+ : basic_compact_json_encoder(std::forward<Sink>(sink), basic_json_encode_options<CharT>(), alloc)
+ {
+ }
+
+ basic_compact_json_encoder(Sink&& sink,
+ const basic_json_encode_options<CharT>& options,
+ const Allocator& alloc = Allocator())
+ : sink_(std::forward<Sink>(sink)),
+ options_(options),
+ fp_(options.float_format(), options.precision()),
+ stack_(alloc),
+ nesting_depth_(0)
+ {
+ }
+
+ basic_compact_json_encoder(basic_compact_json_encoder&&) = default;
+ basic_compact_json_encoder& operator=(basic_compact_json_encoder&&) = default;
+
+ ~basic_compact_json_encoder() noexcept
+ {
+ JSONCONS_TRY
+ {
+ sink_.flush();
+ }
+ JSONCONS_CATCH(...)
+ {
+ }
+ }
+
+ 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
+ {
+ if (JSONCONS_UNLIKELY(++nesting_depth_ > options_.max_nesting_depth()))
+ {
+ ec = json_errc::max_nesting_depth_exceeded;
+ return false;
+ }
+ if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0)
+ {
+ sink_.push_back(',');
+ }
+
+ stack_.emplace_back(container_type::object);
+ sink_.push_back('{');
+ return true;
+ }
+
+ bool visit_end_object(const ser_context&, std::error_code&) override
+ {
+ JSONCONS_ASSERT(!stack_.empty());
+ --nesting_depth_;
+
+ stack_.pop_back();
+ sink_.push_back('}');
+
+ if (!stack_.empty())
+ {
+ stack_.back().increment_count();
+ }
+ 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 = json_errc::max_nesting_depth_exceeded;
+ return false;
+ }
+ if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0)
+ {
+ sink_.push_back(',');
+ }
+ stack_.emplace_back(container_type::array);
+ sink_.push_back('[');
+ return true;
+ }
+
+ bool visit_end_array(const ser_context&, std::error_code&) override
+ {
+ JSONCONS_ASSERT(!stack_.empty());
+ --nesting_depth_;
+
+ stack_.pop_back();
+ sink_.push_back(']');
+ if (!stack_.empty())
+ {
+ stack_.back().increment_count();
+ }
+ return true;
+ }
+
+ bool visit_key(const string_view_type& name, const ser_context&, std::error_code&) override
+ {
+ if (!stack_.empty() && stack_.back().count() > 0)
+ {
+ sink_.push_back(',');
+ }
+
+ sink_.push_back('\"');
+ jsoncons::detail::escape_string(name.data(), name.length(),options_.escape_all_non_ascii(),options_.escape_solidus(),sink_);
+ sink_.push_back('\"');
+ sink_.push_back(':');
+ return true;
+ }
+
+ bool visit_null(semantic_tag, const ser_context&, std::error_code&) override
+ {
+ if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0)
+ {
+ sink_.push_back(',');
+ }
+
+ sink_.append(null_constant().data(), null_constant().size());
+
+ if (!stack_.empty())
+ {
+ stack_.back().increment_count();
+ }
+ return true;
+ }
+
+ void write_bigint_value(const string_view_type& sv)
+ {
+ switch (options_.bigint_format())
+ {
+ case bigint_chars_format::number:
+ {
+ sink_.append(sv.data(),sv.size());
+ break;
+ }
+ case bigint_chars_format::base64:
+ {
+ bigint n = bigint::from_string(sv.data(), sv.length());
+ bool is_neg = n < 0;
+ if (is_neg)
+ {
+ n = - n -1;
+ }
+ int signum;
+ std::vector<uint8_t> v;
+ n.write_bytes_be(signum, v);
+
+ sink_.push_back('\"');
+ if (is_neg)
+ {
+ sink_.push_back('~');
+ }
+ encode_base64(v.begin(), v.end(), sink_);
+ sink_.push_back('\"');
+ break;
+ }
+ case bigint_chars_format::base64url:
+ {
+ bigint n = bigint::from_string(sv.data(), sv.length());
+ bool is_neg = n < 0;
+ if (is_neg)
+ {
+ n = - n -1;
+ }
+ int signum;
+ std::vector<uint8_t> v;
+ n.write_bytes_be(signum, v);
+
+ sink_.push_back('\"');
+ if (is_neg)
+ {
+ sink_.push_back('~');
+ }
+ encode_base64url(v.begin(), v.end(), sink_);
+ sink_.push_back('\"');
+ break;
+ }
+ default:
+ {
+ sink_.push_back('\"');
+ sink_.append(sv.data(),sv.size());
+ sink_.push_back('\"');
+ break;
+ }
+ }
+ }
+
+ bool visit_string(const string_view_type& sv, semantic_tag tag, const ser_context&, std::error_code&) override
+ {
+ if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0)
+ {
+ sink_.push_back(',');
+ }
+
+ switch (tag)
+ {
+ case semantic_tag::bigint:
+ write_bigint_value(sv);
+ break;
+ default:
+ {
+ sink_.push_back('\"');
+ jsoncons::detail::escape_string(sv.data(), sv.length(),options_.escape_all_non_ascii(),options_.escape_solidus(),sink_);
+ sink_.push_back('\"');
+ break;
+ }
+ }
+
+ if (!stack_.empty())
+ {
+ stack_.back().increment_count();
+ }
+ return true;
+ }
+
+ bool visit_byte_string(const byte_string_view& b,
+ semantic_tag tag,
+ const ser_context&,
+ std::error_code&) override
+ {
+ if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0)
+ {
+ sink_.push_back(',');
+ }
+
+ 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;
+ }
+
+ byte_string_chars_format format = jsoncons::detail::resolve_byte_string_chars_format(options_.byte_string_format(),
+ encoding_hint,
+ byte_string_chars_format::base64url);
+ switch (format)
+ {
+ case byte_string_chars_format::base16:
+ {
+ sink_.push_back('\"');
+ encode_base16(b.begin(),b.end(),sink_);
+ sink_.push_back('\"');
+ break;
+ }
+ case byte_string_chars_format::base64:
+ {
+ sink_.push_back('\"');
+ encode_base64(b.begin(), b.end(), sink_);
+ sink_.push_back('\"');
+ break;
+ }
+ case byte_string_chars_format::base64url:
+ {
+ sink_.push_back('\"');
+ encode_base64url(b.begin(),b.end(),sink_);
+ sink_.push_back('\"');
+ break;
+ }
+ default:
+ {
+ JSONCONS_UNREACHABLE();
+ }
+ }
+
+ if (!stack_.empty())
+ {
+ stack_.back().increment_count();
+ }
+ return true;
+ }
+
+ bool visit_double(double value,
+ semantic_tag,
+ const ser_context& context,
+ std::error_code& ec) override
+ {
+ if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0)
+ {
+ sink_.push_back(',');
+ }
+
+ if (JSONCONS_UNLIKELY(!std::isfinite(value)))
+ {
+ if ((std::isnan)(value))
+ {
+ if (options_.enable_nan_to_num())
+ {
+ sink_.append(options_.nan_to_num().data(), options_.nan_to_num().length());
+ }
+ else if (options_.enable_nan_to_str())
+ {
+ visit_string(options_.nan_to_str(), semantic_tag::none, context, ec);
+ }
+ else
+ {
+ sink_.append(null_constant().data(), null_constant().size());
+ }
+ }
+ else if (value == std::numeric_limits<double>::infinity())
+ {
+ if (options_.enable_inf_to_num())
+ {
+ sink_.append(options_.inf_to_num().data(), options_.inf_to_num().length());
+ }
+ else if (options_.enable_inf_to_str())
+ {
+ visit_string(options_.inf_to_str(), semantic_tag::none, context, ec);
+ }
+ else
+ {
+ sink_.append(null_constant().data(), null_constant().size());
+ }
+ }
+ else
+ {
+ if (options_.enable_neginf_to_num())
+ {
+ sink_.append(options_.neginf_to_num().data(), options_.neginf_to_num().length());
+ }
+ else if (options_.enable_neginf_to_str())
+ {
+ visit_string(options_.neginf_to_str(), semantic_tag::none, context, ec);
+ }
+ else
+ {
+ sink_.append(null_constant().data(), null_constant().size());
+ }
+ }
+ }
+ else
+ {
+ fp_(value, sink_);
+ }
+
+ if (!stack_.empty())
+ {
+ stack_.back().increment_count();
+ }
+ return true;
+ }
+
+ bool visit_int64(int64_t value,
+ semantic_tag,
+ const ser_context&,
+ std::error_code&) override
+ {
+ if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0)
+ {
+ sink_.push_back(',');
+ }
+ jsoncons::detail::from_integer(value, sink_);
+ if (!stack_.empty())
+ {
+ stack_.back().increment_count();
+ }
+ return true;
+ }
+
+ bool visit_uint64(uint64_t value,
+ semantic_tag,
+ const ser_context&,
+ std::error_code&) override
+ {
+ if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0)
+ {
+ sink_.push_back(',');
+ }
+ jsoncons::detail::from_integer(value, sink_);
+ if (!stack_.empty())
+ {
+ stack_.back().increment_count();
+ }
+ return true;
+ }
+
+ bool visit_bool(bool value, semantic_tag, const ser_context&, std::error_code&) override
+ {
+ if (!stack_.empty() && stack_.back().is_array() && stack_.back().count() > 0)
+ {
+ sink_.push_back(',');
+ }
+
+ if (value)
+ {
+ sink_.append(true_constant().data(), true_constant().size());
+ }
+ else
+ {
+ sink_.append(false_constant().data(), false_constant().size());
+ }
+
+ if (!stack_.empty())
+ {
+ stack_.back().increment_count();
+ }
+ return true;
+ }
+ };
+
+ using json_stream_encoder = basic_json_encoder<char,jsoncons::stream_sink<char>>;
+ using wjson_stream_encoder = basic_json_encoder<wchar_t,jsoncons::stream_sink<wchar_t>>;
+ using compact_json_stream_encoder = basic_compact_json_encoder<char,jsoncons::stream_sink<char>>;
+ using compact_wjson_stream_encoder = basic_compact_json_encoder<wchar_t,jsoncons::stream_sink<wchar_t>>;
+
+ using json_string_encoder = basic_json_encoder<char,jsoncons::string_sink<std::string>>;
+ using wjson_string_encoder = basic_json_encoder<wchar_t,jsoncons::string_sink<std::wstring>>;
+ using compact_json_string_encoder = basic_compact_json_encoder<char,jsoncons::string_sink<std::string>>;
+ using compact_wjson_string_encoder = basic_compact_json_encoder<wchar_t,jsoncons::string_sink<std::wstring>>;
+
+ #if !defined(JSONCONS_NO_DEPRECATED)
+ template<class CharT,class Sink=jsoncons::stream_sink<CharT>>
+ using basic_json_serializer = basic_json_encoder<CharT,Sink>;
+
+ template<class CharT,class Sink=jsoncons::stream_sink<CharT>>
+ using basic_json_compressed_serializer = basic_compact_json_encoder<CharT,Sink>;
+
+ template<class CharT,class Sink=jsoncons::stream_sink<CharT>>
+ using basic_json_compressed_encoder = basic_compact_json_encoder<CharT,Sink>;
+
+ JSONCONS_DEPRECATED_MSG("Instead, use compact_json_stream_encoder") typedef basic_compact_json_encoder<char,jsoncons::stream_sink<char>> json_compressed_stream_encoder;
+ JSONCONS_DEPRECATED_MSG("Instead, use compact_wjson_stream_encoder")typedef basic_compact_json_encoder<wchar_t,jsoncons::stream_sink<wchar_t>> wjson_compressed_stream_encoder;
+ JSONCONS_DEPRECATED_MSG("Instead, use compact_json_string_encoder") typedef basic_compact_json_encoder<char,jsoncons::string_sink<char>> json_compressed_string_encoder;
+ JSONCONS_DEPRECATED_MSG("Instead, use compact_wjson_string_encoder")typedef basic_compact_json_encoder<wchar_t,jsoncons::string_sink<wchar_t>> wjson_compressed_string_encoder;
+
+ JSONCONS_DEPRECATED_MSG("Instead, use json_stream_encoder") typedef json_stream_encoder json_encoder;
+ JSONCONS_DEPRECATED_MSG("Instead, use wjson_stream_encoder") typedef wjson_stream_encoder wjson_encoder;
+ JSONCONS_DEPRECATED_MSG("Instead, use compact_json_stream_encoder") typedef compact_json_stream_encoder compact_json_encoder;
+ JSONCONS_DEPRECATED_MSG("Instead, use compact_wjson_stream_encoder") typedef compact_wjson_stream_encoder wcompact_json_encoder;
+
+ JSONCONS_DEPRECATED_MSG("Instead, use json_stream_encoder") typedef basic_json_encoder<char,jsoncons::stream_sink<char>> json_serializer;
+ JSONCONS_DEPRECATED_MSG("Instead, use wjson_stream_encoder") typedef basic_json_encoder<wchar_t,jsoncons::stream_sink<wchar_t>> wjson_serializer;
+
+ JSONCONS_DEPRECATED_MSG("Instead, use compact_json_stream_encoder") typedef basic_compact_json_encoder<char,jsoncons::stream_sink<char>> json_compressed_serializer;
+ JSONCONS_DEPRECATED_MSG("Instead, use compact_wjson_stream_encoder") typedef basic_compact_json_encoder<wchar_t,jsoncons::stream_sink<wchar_t>> wjson_compressed_serializer;
+
+ JSONCONS_DEPRECATED_MSG("Instead, use json_string_encoder") typedef basic_json_encoder<char,jsoncons::string_sink<std::string>> json_string_serializer;
+ JSONCONS_DEPRECATED_MSG("Instead, use wjson_string_encoder") typedef basic_json_encoder<wchar_t,jsoncons::string_sink<std::wstring>> wjson_string_serializer;
+
+ JSONCONS_DEPRECATED_MSG("Instead, use compact_json_string_encoder") typedef basic_compact_json_encoder<char,jsoncons::string_sink<std::string>> json_compressed_string_serializer;
+ JSONCONS_DEPRECATED_MSG("Instead, use wcompact_json_string_encoder") typedef basic_compact_json_encoder<wchar_t,jsoncons::string_sink<std::wstring>> wjson_compressed_string_serializer;
+ #endif
+
+} // namespace jsoncons
+
+#endif