12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010 |
- // Copyright (c) 2021-2022 configor - Nomango
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to deal
- // in the Software without restriction, including without limitation the rights
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- // copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- // THE SOFTWARE.
- #pragma once
- #include "configor.hpp"
- #include <algorithm> // std::for_each
- #include <iomanip> // std::setprecision
- #include <ios> // std::noskipws, std::noshowbase, std::right
- namespace configor
- {
- namespace detail
- {
- template <typename _ValTy, typename _SourceCharTy>
- class json_parser;
- template <typename _ValTy, typename _TargetCharTy>
- class json_serializer;
- } // namespace detail
- template <typename _Args, template <typename> class _DefaultEncoding = encoding::auto_utf>
- class basic_json final
- : public detail::serializable<_Args, detail::json_serializer, _DefaultEncoding>
- , public detail::parsable<_Args, detail::json_parser, _DefaultEncoding>
- , public detail::value_maker<basic_value<_Args>>
- , public detail::iostream_wrapper_maker<basic_json<_Args, _DefaultEncoding>, basic_value<_Args>>
- {
- public:
- using value = basic_value<_Args>;
- using serializer =
- typename detail::serializable<_Args, detail::json_serializer,
- _DefaultEncoding>::template serializer_type<typename value::char_type>;
- using parser = typename detail::parsable<_Args, detail::json_parser,
- _DefaultEncoding>::template parser_type<typename value::char_type>;
- };
- using json = basic_json<value_tplargs>;
- using wjson = basic_json<wvalue_tplargs>;
- // type traits
- template <typename _Ty>
- struct is_json : std::false_type
- {
- };
- template <typename _Args, template <typename> class _DefaultEncoding>
- struct is_json<basic_json<_Args, _DefaultEncoding>> : std::true_type
- {
- };
- namespace detail
- {
- template <typename _IntTy>
- struct json_hex
- {
- const _IntTy i;
- template <typename _CharTy>
- friend inline std::basic_ostream<_CharTy>& operator<<(std::basic_ostream<_CharTy>& os, const json_hex& i)
- {
- os << std::setfill(_CharTy('0')) << std::hex << std::uppercase;
- os << '\\' << 'u' << std::setw(sizeof(i.i)) << i.i;
- os << std::dec << std::nouppercase;
- return os;
- }
- };
- namespace
- {
- template <typename _IntTy>
- inline json_hex<_IntTy> make_json_hex(const _IntTy i)
- {
- return json_hex<_IntTy>{ i };
- }
- } // namespace
- //
- // json_parser
- //
- template <typename _ValTy, typename _SourceCharTy>
- class json_parser : public basic_parser<_ValTy, _SourceCharTy>
- {
- public:
- using value_type = _ValTy;
- using source_char_type = _SourceCharTy;
- using target_char_type = typename value_type::char_type;
- using option = std::function<void(json_parser&)>;
- static option with_error_handler(error_handler* eh)
- {
- return [=](json_parser& p) { p.set_error_handler(eh); };
- }
- template <template <class> class _Encoding>
- static option with_encoding()
- {
- return [=](json_parser& p)
- {
- p.template set_source_encoding<_Encoding>();
- p.template set_target_encoding<_Encoding>();
- };
- }
- template <template <class> class _Encoding>
- static option with_source_encoding()
- {
- return [=](json_parser& p) { p.template set_source_encoding<_Encoding>(); };
- }
- template <template <class> class _Encoding>
- static option with_target_encoding()
- {
- return [=](json_parser& p) { p.template set_target_encoding<_Encoding>(); };
- }
- explicit json_parser(std::basic_istream<source_char_type>& is)
- : basic_parser<value_type, source_char_type>(is)
- , is_negative_(false)
- , current_(0)
- , number_integer_(0)
- , number_float_(0)
- {
- }
- inline void prepare(std::initializer_list<option> options)
- {
- std::for_each(options.begin(), options.end(), [&](const option& option) { option(*this); });
- }
- virtual void parse(value_type& c) override
- {
- // read first char
- read_next();
- basic_parser<value_type, source_char_type>::parse(c);
- }
- virtual void get_integer(typename value_type::integer_type& out) override
- {
- out = is_negative_ ? -number_integer_ : number_integer_;
- }
- virtual void get_float(typename value_type::float_type& out) override
- {
- out = is_negative_ ? -number_float_ : number_float_;
- }
- virtual void get_string(typename value_type::string_type& out) override
- {
- scan_string(out);
- }
- virtual token_type scan() override
- {
- skip_spaces();
- if (this->is_.eof())
- return token_type::end_of_input;
- token_type result = token_type::uninitialized;
- switch (current_)
- {
- case '[':
- result = token_type::begin_array;
- break;
- case ']':
- result = token_type::end_array;
- break;
- case '{':
- result = token_type::begin_object;
- break;
- case '}':
- result = token_type::end_object;
- break;
- case ':':
- result = token_type::name_separator;
- break;
- case ',':
- result = token_type::value_separator;
- break;
- case 't':
- return scan_literal({ 't', 'r', 'u', 'e' }, token_type::literal_true);
- case 'f':
- return scan_literal({ 'f', 'a', 'l', 's', 'e' }, token_type::literal_false);
- case 'n':
- return scan_literal({ 'n', 'u', 'l', 'l' }, token_type::literal_null);
- case '\"':
- // lazy load
- return token_type::value_string;
- case '-':
- case '+':
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- return scan_number();
- case '\0':
- return token_type::end_of_input;
- default:
- {
- fail("unexpected character", current_);
- }
- }
- // skip next char
- read_next();
- return result;
- }
- uint32_t read_next()
- {
- if (this->source_decoder_(this->is_, current_))
- {
- if (!this->is_.good())
- {
- fail("decoding failed with codepoint", current_);
- }
- }
- else
- {
- current_ = 0;
- }
- return current_;
- }
- void skip_spaces()
- {
- while (current_ == ' ' || current_ == '\t' || current_ == '\n' || current_ == '\r')
- read_next();
- // skip comments
- if (current_ == '/')
- {
- skip_comments();
- }
- }
- void skip_comments()
- {
- read_next();
- if (current_ == '/')
- {
- // one line comment
- while (true)
- {
- read_next();
- if (current_ == '\n' || current_ == '\r')
- {
- // end of comment
- skip_spaces();
- break;
- }
- if (this->is_.eof())
- {
- break;
- }
- }
- }
- else if (current_ == '*')
- {
- // multiple line comment
- while (true)
- {
- if (read_next() == '*')
- {
- if (read_next() == '/')
- {
- // end of comment
- read_next();
- break;
- }
- }
- if (this->is_.eof())
- {
- fail("unexpected eof while reading comment");
- }
- }
- skip_spaces();
- }
- else
- {
- fail("unexpected character '/'");
- }
- }
- token_type scan_literal(std::initializer_list<source_char_type> text, token_type result)
- {
- for (const auto ch : text)
- {
- if (ch != std::char_traits<source_char_type>::to_char_type(current_))
- {
- detail::fast_ostringstream ss;
- ss << "unexpected character '" << static_cast<char>(current_) << "'";
- ss << " (expected literal '";
- for (const auto ch : text)
- ss.put(static_cast<char>(ch));
- ss << "')";
- fail(ss.str());
- }
- read_next();
- }
- return result;
- }
- void scan_string(typename value_type::string_type& out)
- {
- CONFIGOR_ASSERT(current_ == '\"');
- detail::fast_string_ostreambuf<target_char_type> buf{ out };
- std::basic_ostream<target_char_type> oss{ &buf };
- while (true)
- {
- read_next();
- if (this->is_.eof())
- {
- fail("unexpected end of string");
- }
- switch (current_)
- {
- case '\"':
- {
- // end of string
- // skip last `\"`
- read_next();
- return;
- }
- case 0x00:
- case 0x01:
- case 0x02:
- case 0x03:
- case 0x04:
- case 0x05:
- case 0x06:
- case 0x07:
- case 0x08:
- case 0x09:
- case 0x0A:
- case 0x0B:
- case 0x0C:
- case 0x0D:
- case 0x0E:
- case 0x0F:
- case 0x10:
- case 0x11:
- case 0x12:
- case 0x13:
- case 0x14:
- case 0x15:
- case 0x16:
- case 0x17:
- case 0x18:
- case 0x19:
- case 0x1A:
- case 0x1B:
- case 0x1C:
- case 0x1D:
- case 0x1E:
- case 0x1F:
- {
- fail("invalid control character", current_);
- }
- case '\\':
- {
- switch (read_next())
- {
- case '\"':
- oss << '\"';
- break;
- case '\\':
- oss << '\\';
- break;
- case '/':
- oss << '/';
- break;
- case 'b':
- oss << '\b';
- break;
- case 'f':
- oss << '\f';
- break;
- case 'n':
- oss << '\n';
- break;
- case 'r':
- oss << '\r';
- break;
- case 't':
- oss << '\t';
- break;
- case 'u':
- {
- uint32_t codepoint = read_escaped_codepoint();
- if (encoding::unicode::is_lead_surrogate(codepoint))
- {
- if (read_next() != '\\' || read_next() != 'u')
- {
- fail("lead surrogate must be followed by trail surrogate, but got", current_);
- }
- const auto lead_surrogate = codepoint;
- const auto trail_surrogate = read_escaped_codepoint();
- if (!encoding::unicode::is_trail_surrogate(trail_surrogate))
- {
- fail("surrogate U+D800...U+DBFF must be followed by U+DC00...U+DFFF, but got",
- trail_surrogate);
- }
- codepoint = encoding::unicode::decode_surrogates(lead_surrogate, trail_surrogate);
- }
- this->target_encoder_(oss, codepoint);
- if (!oss.good())
- {
- fail("encoding failed with codepoint", codepoint);
- }
- break;
- }
- default:
- {
- fail("invalid escaped character", current_);
- }
- }
- break;
- }
- default:
- {
- this->target_encoder_(oss, current_);
- if (!oss.good())
- {
- fail("encoding failed with codepoint", current_);
- }
- }
- }
- }
- }
- token_type scan_number()
- {
- is_negative_ = false;
- number_integer_ = 0;
- if (current_ == '-' || current_ == '+')
- {
- is_negative_ = (current_ == '-');
- read_next();
- }
- if (current_ == '0')
- {
- read_next();
- if (current_ == '.')
- return scan_float();
- if (current_ == 'e' || current_ == 'E')
- fail("invalid exponent");
- // zero
- return token_type::value_integer;
- }
- return scan_integer();
- }
- token_type scan_integer()
- {
- using integer_type = typename value_type::integer_type;
- CONFIGOR_ASSERT(is_digit(current_));
- number_integer_ = static_cast<integer_type>(current_ - '0');
- while (true)
- {
- const auto ch = read_next();
- if (ch == '.' || ch == 'e' || ch == 'E')
- return scan_float();
- if (is_digit(ch))
- number_integer_ = number_integer_ * 10 + static_cast<integer_type>(ch - '0');
- else
- break;
- }
- return token_type::value_integer;
- }
- token_type scan_float()
- {
- using float_type = typename value_type::float_type;
- number_float_ = static_cast<float_type>(number_integer_);
- if (current_ == 'e' || current_ == 'E')
- return scan_exponent();
- CONFIGOR_ASSERT(current_ == '.');
- if (!is_digit(read_next()))
- fail("invalid float number, got", current_);
- float_type fraction = static_cast<float_type>(0.1);
- number_float_ += static_cast<float_type>(static_cast<uint32_t>(current_ - '0')) * fraction;
- while (true)
- {
- const auto ch = read_next();
- if (ch == 'e' || ch == 'E')
- return scan_exponent();
- if (is_digit(ch))
- {
- fraction *= static_cast<float_type>(0.1);
- number_float_ += static_cast<float_type>(static_cast<uint32_t>(current_ - '0')) * fraction;
- }
- else
- break;
- }
- return token_type::value_float;
- }
- token_type scan_exponent()
- {
- using float_type = typename value_type::float_type;
- CONFIGOR_ASSERT(current_ == 'e' || current_ == 'E');
- read_next();
- const bool invalid = (is_digit(current_) && current_ != '0') || (current_ == '-') || (current_ == '+');
- if (!invalid)
- fail("invalid exponent number, got", current_);
- float_type base = 10;
- if (current_ == '+')
- {
- read_next();
- }
- else if (current_ == '-')
- {
- base = static_cast<float_type>(0.1);
- read_next();
- }
- uint32_t exponent = static_cast<uint32_t>(current_ - '0');
- while (is_digit(read_next()))
- {
- exponent = (exponent * 10) + static_cast<uint32_t>(current_ - '0');
- }
- float_type power = 1;
- for (; exponent; exponent >>= 1, base *= base)
- if (exponent & 1)
- power *= base;
- number_float_ *= power;
- return token_type::value_float;
- }
- uint32_t read_escaped_codepoint()
- {
- uint32_t code = 0;
- for (const auto factor : { 12, 8, 4, 0 })
- {
- const auto ch = read_next();
- if (ch >= '0' && ch <= '9')
- {
- code += ((ch - '0') << factor);
- }
- else if (ch >= 'A' && ch <= 'F')
- {
- code += ((ch - 'A' + 10) << factor);
- }
- else if (ch >= 'a' && ch <= 'f')
- {
- code += ((ch - 'a' + 10) << factor);
- }
- else
- {
- fail("'\\u' must be followed by 4 hex digits, but got", ch);
- }
- }
- return code;
- }
- inline bool is_digit(source_char_type ch) const
- {
- return source_char_type('0') <= ch && ch <= source_char_type('9');
- }
- inline void fail(const std::string& msg)
- {
- throw configor_deserialization_error(msg);
- }
- template <typename _IntTy>
- inline void fail(const std::string& msg, _IntTy code)
- {
- detail::fast_ostringstream ss;
- ss << msg << " '" << make_json_hex(code) << "'";
- fail(ss.str());
- }
- private:
- bool is_negative_;
- uint32_t current_;
- typename value_type::integer_type number_integer_;
- typename value_type::float_type number_float_;
- };
- //
- // json_serializer
- //
- template <typename _ValTy, typename _TargetCharTy>
- class json_serializer : public basic_serializer<_ValTy, _TargetCharTy>
- {
- public:
- using value_type = _ValTy;
- using source_char_type = typename value_type::char_type;
- using target_char_type = _TargetCharTy;
- using option = std::function<void(json_serializer&)>;
- static option with_indent(uint8_t indent_step, target_char_type indent_char = ' ')
- {
- return [=](json_serializer& s)
- {
- s.indent_ = indent<target_char_type>{ indent_step, indent_char };
- s.pretty_print_ = indent_step > 0;
- };
- }
- static option with_unicode_escaping(bool enabled)
- {
- return [=](json_serializer& s) { s.unicode_escaping_ = enabled; };
- }
- static option with_precision(int precision, std::ios_base::fmtflags floatflags = std::ios_base::fixed)
- {
- return [=](json_serializer& s)
- {
- s.os_.precision(static_cast<std::streamsize>(precision));
- s.os_.setf(floatflags, std::ios_base::floatfield);
- };
- }
- static option with_error_handler(error_handler* eh)
- {
- return [=](json_serializer& s) { s.set_error_handler(eh); };
- }
- template <template <class> class _Encoding>
- static option with_encoding()
- {
- return [=](json_serializer& s)
- {
- s.template set_source_encoding<_Encoding>();
- s.template set_target_encoding<_Encoding>();
- };
- }
- template <template <class> class _Encoding>
- static option with_source_encoding()
- {
- return [=](json_serializer& s) { s.template set_source_encoding<_Encoding>(); };
- }
- template <template <class> class _Encoding>
- static option with_target_encoding()
- {
- return [=](json_serializer& s) { s.template set_target_encoding<_Encoding>(); };
- }
- explicit json_serializer(std::basic_ostream<target_char_type>& os)
- : basic_serializer<value_type, target_char_type>(os)
- , pretty_print_(os.width() > 0)
- , object_or_array_began_(false)
- , unicode_escaping_(false)
- , last_token_(token_type::uninitialized)
- , indent_(static_cast<uint8_t>(os.width()), os.fill())
- {
- this->os_ << std::setprecision(os.precision());
- os.width(0); // clear width
- }
- inline void prepare(std::initializer_list<option> options)
- {
- std::for_each(options.begin(), options.end(), [&](const option& option) { option(*this); });
- if ((this->os_.flags() & std::ios_base::floatfield) == (std::ios_base::fixed | std::ios_base::scientific))
- {
- // hexfloat is disabled
- this->os_.unsetf(std::ios_base::floatfield);
- }
- }
- virtual void next(token_type token) override
- {
- if (object_or_array_began_)
- {
- object_or_array_began_ = false;
- switch (token)
- {
- case token_type::end_array:
- case token_type::end_object:
- // empty object or array
- break;
- case token_type::literal_true:
- case token_type::literal_false:
- case token_type::literal_null:
- case token_type::value_string:
- case token_type::value_integer:
- case token_type::value_float:
- case token_type::begin_array:
- case token_type::begin_object:
- output_newline();
- break;
- default:
- // unexpected
- break;
- }
- }
- if (pretty_print_ && last_token_ != token_type::name_separator)
- {
- switch (token)
- {
- case token_type::literal_true:
- case token_type::literal_false:
- case token_type::literal_null:
- case token_type::value_string:
- case token_type::value_integer:
- case token_type::value_float:
- case token_type::begin_array:
- case token_type::begin_object:
- output_indent();
- break;
- default:
- break;
- }
- }
- switch (token)
- {
- case token_type::uninitialized:
- break;
- case token_type::literal_true:
- {
- output({ 't', 'r', 'u', 'e' });
- break;
- }
- case token_type::literal_false:
- {
- output({ 'f', 'a', 'l', 's', 'e' });
- break;
- }
- case token_type::literal_null:
- {
- output({ 'n', 'u', 'l', 'l' });
- break;
- }
- case token_type::value_string:
- break;
- case token_type::value_integer:
- break;
- case token_type::value_float:
- break;
- case token_type::begin_array:
- {
- output('[');
- object_or_array_began_ = true;
- ++indent_;
- break;
- }
- case token_type::end_array:
- {
- --indent_;
- output_newline();
- output_indent();
- output(']');
- break;
- }
- case token_type::begin_object:
- {
- output('{');
- object_or_array_began_ = true;
- ++indent_;
- break;
- }
- case token_type::end_object:
- {
- --indent_;
- output_newline();
- output_indent();
- output('}');
- break;
- }
- case token_type::name_separator:
- {
- output(':');
- output_indent(1);
- break;
- }
- case token_type::value_separator:
- {
- output(',');
- output_newline();
- break;
- }
- case token_type::end_of_input:
- break;
- }
- last_token_ = token;
- }
- virtual void put_integer(typename value_type::integer_type i) override
- {
- this->os_ << i;
- }
- virtual void put_float(typename value_type::float_type f) override
- {
- if (std::ceil(f) == std::floor(f))
- {
- // integer
- this->os_ << static_cast<int64_t>(f) << ".0";
- }
- else
- {
- this->os_ << f;
- }
- }
- virtual void put_string(const typename value_type::string_type& s) override
- {
- output('\"');
- fast_string_istreambuf<source_char_type> buf{ s };
- std::basic_istream<source_char_type> iss{ &buf };
- uint32_t codepoint = 0;
- while (this->source_decoder_(iss, codepoint))
- {
- if (!iss.good())
- {
- fail("unexpected character", codepoint);
- }
- switch (codepoint)
- {
- case '\t':
- {
- output({ '\\', 't' });
- break;
- }
- case '\r':
- {
- output({ '\\', 'r' });
- break;
- }
- case '\n':
- {
- output({ '\\', 'n' });
- break;
- }
- case '\b':
- {
- output({ '\\', 'b' });
- break;
- }
- case '\f':
- {
- output({ '\\', 'f' });
- break;
- }
- case '\"':
- {
- output({ '\\', '\"' });
- break;
- }
- case '\\':
- {
- output({ '\\', '\\' });
- break;
- }
- default:
- {
- // escape control characters
- // and non-ASCII characters (if `escape_unicode` is true)
- const bool need_escape = (codepoint <= 0x1F || (unicode_escaping_ && codepoint >= 0x7F));
- if (!need_escape)
- {
- // ASCII or BMP (U+0000...U+007F)
- this->target_encoder_(this->os_, codepoint);
- }
- else
- {
- if (codepoint <= 0xFFFF)
- {
- // BMP: U+007F...U+FFFF
- this->os_ << make_json_hex(static_cast<uint16_t>(codepoint));
- }
- else
- {
- // supplementary planes: U+10000...U+10FFFF
- uint32_t lead_surrogate = 0, trail_surrogate = 0;
- encoding::unicode::encode_surrogates(codepoint, lead_surrogate, trail_surrogate);
- this->os_ << make_json_hex(lead_surrogate) << make_json_hex(trail_surrogate);
- }
- }
- if (!this->os_.good())
- {
- fail("encoding failed with codepoint", codepoint);
- }
- break;
- }
- }
- }
- output('\"');
- }
- void output(target_char_type ch)
- {
- this->os_.put(ch);
- }
- void output(std::initializer_list<target_char_type> list)
- {
- this->os_.write(list.begin(), static_cast<std::streamsize>(list.size()));
- }
- void output_indent()
- {
- if (pretty_print_)
- this->os_ << indent_;
- }
- void output_indent(int length)
- {
- if (pretty_print_)
- indent_.put(this->os_, length);
- }
- void output_newline()
- {
- if (pretty_print_)
- this->os_.put('\n');
- }
- inline void fail(const std::string& msg, uint32_t codepoint)
- {
- fast_ostringstream ss;
- ss << msg << " '" << make_json_hex(codepoint) << "'";
- throw configor_serialization_error(ss.str());
- }
- private:
- bool pretty_print_;
- bool object_or_array_began_;
- bool unicode_escaping_;
- token_type last_token_;
- indent<target_char_type> indent_;
- };
- } // namespace detail
- } // namespace configor
|