serializer.hpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631
  1. #pragma once
  2. #include <algorithm> // reverse, remove, fill, find, none_of
  3. #include <array> // array
  4. #include <cassert> // assert
  5. #include <ciso646> // and, or
  6. #include <clocale> // localeconv, lconv
  7. #include <cmath> // labs, isfinite, isnan, signbit
  8. #include <cstddef> // size_t, ptrdiff_t
  9. #include <cstdint> // uint8_t
  10. #include <cstdio> // snprintf
  11. #include <limits> // numeric_limits
  12. #include <string> // string
  13. #include <type_traits> // is_same
  14. #include <nlohmann/detail/exceptions.hpp>
  15. #include <nlohmann/detail/conversions/to_chars.hpp>
  16. #include <nlohmann/detail/macro_scope.hpp>
  17. #include <nlohmann/detail/meta.hpp>
  18. #include <nlohmann/detail/output/output_adapters.hpp>
  19. #include <nlohmann/detail/value_t.hpp>
  20. namespace nlohmann
  21. {
  22. namespace detail
  23. {
  24. ///////////////////
  25. // serialization //
  26. ///////////////////
  27. template<typename BasicJsonType>
  28. class serializer
  29. {
  30. using string_t = typename BasicJsonType::string_t;
  31. using number_float_t = typename BasicJsonType::number_float_t;
  32. using number_integer_t = typename BasicJsonType::number_integer_t;
  33. using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
  34. static constexpr uint8_t UTF8_ACCEPT = 0;
  35. static constexpr uint8_t UTF8_REJECT = 1;
  36. public:
  37. /*!
  38. @param[in] s output stream to serialize to
  39. @param[in] ichar indentation character to use
  40. */
  41. serializer(output_adapter_t<char> s, const char ichar)
  42. : o(std::move(s)), loc(std::localeconv()),
  43. thousands_sep(loc->thousands_sep == nullptr ? '\0' : * (loc->thousands_sep)),
  44. decimal_point(loc->decimal_point == nullptr ? '\0' : * (loc->decimal_point)),
  45. indent_char(ichar), indent_string(512, indent_char)
  46. {}
  47. // delete because of pointer members
  48. serializer(const serializer&) = delete;
  49. serializer& operator=(const serializer&) = delete;
  50. /*!
  51. @brief internal implementation of the serialization function
  52. This function is called by the public member function dump and organizes
  53. the serialization internally. The indentation level is propagated as
  54. additional parameter. In case of arrays and objects, the function is
  55. called recursively.
  56. - strings and object keys are escaped using `escape_string()`
  57. - integer numbers are converted implicitly via `operator<<`
  58. - floating-point numbers are converted to a string using `"%g"` format
  59. @param[in] val value to serialize
  60. @param[in] pretty_print whether the output shall be pretty-printed
  61. @param[in] indent_step the indent level
  62. @param[in] current_indent the current indent level (only used internally)
  63. */
  64. void dump(const BasicJsonType& val, const bool pretty_print,
  65. const bool ensure_ascii,
  66. const unsigned int indent_step,
  67. const unsigned int current_indent = 0)
  68. {
  69. switch (val.m_type)
  70. {
  71. case value_t::object:
  72. {
  73. if (val.m_value.object->empty())
  74. {
  75. o->write_characters("{}", 2);
  76. return;
  77. }
  78. if (pretty_print)
  79. {
  80. o->write_characters("{\n", 2);
  81. // variable to hold indentation for recursive calls
  82. const auto new_indent = current_indent + indent_step;
  83. if (JSON_UNLIKELY(indent_string.size() < new_indent))
  84. {
  85. indent_string.resize(indent_string.size() * 2, ' ');
  86. }
  87. // first n-1 elements
  88. auto i = val.m_value.object->cbegin();
  89. for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)
  90. {
  91. o->write_characters(indent_string.c_str(), new_indent);
  92. o->write_character('\"');
  93. dump_escaped(i->first, ensure_ascii);
  94. o->write_characters("\": ", 3);
  95. dump(i->second, true, ensure_ascii, indent_step, new_indent);
  96. o->write_characters(",\n", 2);
  97. }
  98. // last element
  99. assert(i != val.m_value.object->cend());
  100. assert(std::next(i) == val.m_value.object->cend());
  101. o->write_characters(indent_string.c_str(), new_indent);
  102. o->write_character('\"');
  103. dump_escaped(i->first, ensure_ascii);
  104. o->write_characters("\": ", 3);
  105. dump(i->second, true, ensure_ascii, indent_step, new_indent);
  106. o->write_character('\n');
  107. o->write_characters(indent_string.c_str(), current_indent);
  108. o->write_character('}');
  109. }
  110. else
  111. {
  112. o->write_character('{');
  113. // first n-1 elements
  114. auto i = val.m_value.object->cbegin();
  115. for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)
  116. {
  117. o->write_character('\"');
  118. dump_escaped(i->first, ensure_ascii);
  119. o->write_characters("\":", 2);
  120. dump(i->second, false, ensure_ascii, indent_step, current_indent);
  121. o->write_character(',');
  122. }
  123. // last element
  124. assert(i != val.m_value.object->cend());
  125. assert(std::next(i) == val.m_value.object->cend());
  126. o->write_character('\"');
  127. dump_escaped(i->first, ensure_ascii);
  128. o->write_characters("\":", 2);
  129. dump(i->second, false, ensure_ascii, indent_step, current_indent);
  130. o->write_character('}');
  131. }
  132. return;
  133. }
  134. case value_t::array:
  135. {
  136. if (val.m_value.array->empty())
  137. {
  138. o->write_characters("[]", 2);
  139. return;
  140. }
  141. if (pretty_print)
  142. {
  143. o->write_characters("[\n", 2);
  144. // variable to hold indentation for recursive calls
  145. const auto new_indent = current_indent + indent_step;
  146. if (JSON_UNLIKELY(indent_string.size() < new_indent))
  147. {
  148. indent_string.resize(indent_string.size() * 2, ' ');
  149. }
  150. // first n-1 elements
  151. for (auto i = val.m_value.array->cbegin();
  152. i != val.m_value.array->cend() - 1; ++i)
  153. {
  154. o->write_characters(indent_string.c_str(), new_indent);
  155. dump(*i, true, ensure_ascii, indent_step, new_indent);
  156. o->write_characters(",\n", 2);
  157. }
  158. // last element
  159. assert(not val.m_value.array->empty());
  160. o->write_characters(indent_string.c_str(), new_indent);
  161. dump(val.m_value.array->back(), true, ensure_ascii, indent_step, new_indent);
  162. o->write_character('\n');
  163. o->write_characters(indent_string.c_str(), current_indent);
  164. o->write_character(']');
  165. }
  166. else
  167. {
  168. o->write_character('[');
  169. // first n-1 elements
  170. for (auto i = val.m_value.array->cbegin();
  171. i != val.m_value.array->cend() - 1; ++i)
  172. {
  173. dump(*i, false, ensure_ascii, indent_step, current_indent);
  174. o->write_character(',');
  175. }
  176. // last element
  177. assert(not val.m_value.array->empty());
  178. dump(val.m_value.array->back(), false, ensure_ascii, indent_step, current_indent);
  179. o->write_character(']');
  180. }
  181. return;
  182. }
  183. case value_t::string:
  184. {
  185. o->write_character('\"');
  186. dump_escaped(*val.m_value.string, ensure_ascii);
  187. o->write_character('\"');
  188. return;
  189. }
  190. case value_t::boolean:
  191. {
  192. if (val.m_value.boolean)
  193. {
  194. o->write_characters("true", 4);
  195. }
  196. else
  197. {
  198. o->write_characters("false", 5);
  199. }
  200. return;
  201. }
  202. case value_t::number_integer:
  203. {
  204. dump_integer(val.m_value.number_integer);
  205. return;
  206. }
  207. case value_t::number_unsigned:
  208. {
  209. dump_integer(val.m_value.number_unsigned);
  210. return;
  211. }
  212. case value_t::number_float:
  213. {
  214. dump_float(val.m_value.number_float);
  215. return;
  216. }
  217. case value_t::discarded:
  218. {
  219. o->write_characters("<discarded>", 11);
  220. return;
  221. }
  222. case value_t::null:
  223. {
  224. o->write_characters("null", 4);
  225. return;
  226. }
  227. }
  228. }
  229. private:
  230. /*!
  231. @brief dump escaped string
  232. Escape a string by replacing certain special characters by a sequence of an
  233. escape character (backslash) and another character and other control
  234. characters by a sequence of "\u" followed by a four-digit hex
  235. representation. The escaped string is written to output stream @a o.
  236. @param[in] s the string to escape
  237. @param[in] ensure_ascii whether to escape non-ASCII characters with
  238. \uXXXX sequences
  239. @complexity Linear in the length of string @a s.
  240. */
  241. void dump_escaped(const string_t& s, const bool ensure_ascii)
  242. {
  243. uint32_t codepoint;
  244. uint8_t state = UTF8_ACCEPT;
  245. std::size_t bytes = 0; // number of bytes written to string_buffer
  246. for (std::size_t i = 0; i < s.size(); ++i)
  247. {
  248. const auto byte = static_cast<uint8_t>(s[i]);
  249. switch (decode(state, codepoint, byte))
  250. {
  251. case UTF8_ACCEPT: // decode found a new code point
  252. {
  253. switch (codepoint)
  254. {
  255. case 0x08: // backspace
  256. {
  257. string_buffer[bytes++] = '\\';
  258. string_buffer[bytes++] = 'b';
  259. break;
  260. }
  261. case 0x09: // horizontal tab
  262. {
  263. string_buffer[bytes++] = '\\';
  264. string_buffer[bytes++] = 't';
  265. break;
  266. }
  267. case 0x0A: // newline
  268. {
  269. string_buffer[bytes++] = '\\';
  270. string_buffer[bytes++] = 'n';
  271. break;
  272. }
  273. case 0x0C: // formfeed
  274. {
  275. string_buffer[bytes++] = '\\';
  276. string_buffer[bytes++] = 'f';
  277. break;
  278. }
  279. case 0x0D: // carriage return
  280. {
  281. string_buffer[bytes++] = '\\';
  282. string_buffer[bytes++] = 'r';
  283. break;
  284. }
  285. case 0x22: // quotation mark
  286. {
  287. string_buffer[bytes++] = '\\';
  288. string_buffer[bytes++] = '\"';
  289. break;
  290. }
  291. case 0x5C: // reverse solidus
  292. {
  293. string_buffer[bytes++] = '\\';
  294. string_buffer[bytes++] = '\\';
  295. break;
  296. }
  297. default:
  298. {
  299. // escape control characters (0x00..0x1F) or, if
  300. // ensure_ascii parameter is used, non-ASCII characters
  301. if ((codepoint <= 0x1F) or (ensure_ascii and (codepoint >= 0x7F)))
  302. {
  303. if (codepoint <= 0xFFFF)
  304. {
  305. std::snprintf(string_buffer.data() + bytes, 7, "\\u%04x",
  306. static_cast<uint16_t>(codepoint));
  307. bytes += 6;
  308. }
  309. else
  310. {
  311. std::snprintf(string_buffer.data() + bytes, 13, "\\u%04x\\u%04x",
  312. static_cast<uint16_t>(0xD7C0 + (codepoint >> 10)),
  313. static_cast<uint16_t>(0xDC00 + (codepoint & 0x3FF)));
  314. bytes += 12;
  315. }
  316. }
  317. else
  318. {
  319. // copy byte to buffer (all previous bytes
  320. // been copied have in default case above)
  321. string_buffer[bytes++] = s[i];
  322. }
  323. break;
  324. }
  325. }
  326. // write buffer and reset index; there must be 13 bytes
  327. // left, as this is the maximal number of bytes to be
  328. // written ("\uxxxx\uxxxx\0") for one code point
  329. if (string_buffer.size() - bytes < 13)
  330. {
  331. o->write_characters(string_buffer.data(), bytes);
  332. bytes = 0;
  333. }
  334. break;
  335. }
  336. case UTF8_REJECT: // decode found invalid UTF-8 byte
  337. {
  338. std::string sn(3, '\0');
  339. snprintf(&sn[0], sn.size(), "%.2X", byte);
  340. JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + sn));
  341. }
  342. default: // decode found yet incomplete multi-byte code point
  343. {
  344. if (not ensure_ascii)
  345. {
  346. // code point will not be escaped - copy byte to buffer
  347. string_buffer[bytes++] = s[i];
  348. }
  349. break;
  350. }
  351. }
  352. }
  353. if (JSON_LIKELY(state == UTF8_ACCEPT))
  354. {
  355. // write buffer
  356. if (bytes > 0)
  357. {
  358. o->write_characters(string_buffer.data(), bytes);
  359. }
  360. }
  361. else
  362. {
  363. // we finish reading, but do not accept: string was incomplete
  364. std::string sn(3, '\0');
  365. snprintf(&sn[0], sn.size(), "%.2X", static_cast<uint8_t>(s.back()));
  366. JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + sn));
  367. }
  368. }
  369. /*!
  370. @brief dump an integer
  371. Dump a given integer to output stream @a o. Works internally with
  372. @a number_buffer.
  373. @param[in] x integer number (signed or unsigned) to dump
  374. @tparam NumberType either @a number_integer_t or @a number_unsigned_t
  375. */
  376. template<typename NumberType, detail::enable_if_t<
  377. std::is_same<NumberType, number_unsigned_t>::value or
  378. std::is_same<NumberType, number_integer_t>::value,
  379. int> = 0>
  380. void dump_integer(NumberType x)
  381. {
  382. // special case for "0"
  383. if (x == 0)
  384. {
  385. o->write_character('0');
  386. return;
  387. }
  388. const bool is_negative = (x <= 0) and (x != 0); // see issue #755
  389. std::size_t i = 0;
  390. while (x != 0)
  391. {
  392. // spare 1 byte for '\0'
  393. assert(i < number_buffer.size() - 1);
  394. const auto digit = std::labs(static_cast<long>(x % 10));
  395. number_buffer[i++] = static_cast<char>('0' + digit);
  396. x /= 10;
  397. }
  398. if (is_negative)
  399. {
  400. // make sure there is capacity for the '-'
  401. assert(i < number_buffer.size() - 2);
  402. number_buffer[i++] = '-';
  403. }
  404. std::reverse(number_buffer.begin(), number_buffer.begin() + i);
  405. o->write_characters(number_buffer.data(), i);
  406. }
  407. /*!
  408. @brief dump a floating-point number
  409. Dump a given floating-point number to output stream @a o. Works internally
  410. with @a number_buffer.
  411. @param[in] x floating-point number to dump
  412. */
  413. void dump_float(number_float_t x)
  414. {
  415. // NaN / inf
  416. if (not std::isfinite(x))
  417. {
  418. o->write_characters("null", 4);
  419. return;
  420. }
  421. // If number_float_t is an IEEE-754 single or double precision number,
  422. // use the Grisu2 algorithm to produce short numbers which are
  423. // guaranteed to round-trip, using strtof and strtod, resp.
  424. //
  425. // NB: The test below works if <long double> == <double>.
  426. static constexpr bool is_ieee_single_or_double
  427. = (std::numeric_limits<number_float_t>::is_iec559 and std::numeric_limits<number_float_t>::digits == 24 and std::numeric_limits<number_float_t>::max_exponent == 128) or
  428. (std::numeric_limits<number_float_t>::is_iec559 and std::numeric_limits<number_float_t>::digits == 53 and std::numeric_limits<number_float_t>::max_exponent == 1024);
  429. dump_float(x, std::integral_constant<bool, is_ieee_single_or_double>());
  430. }
  431. void dump_float(number_float_t x, std::true_type /*is_ieee_single_or_double*/)
  432. {
  433. char* begin = number_buffer.data();
  434. char* end = ::nlohmann::detail::to_chars(begin, begin + number_buffer.size(), x);
  435. o->write_characters(begin, static_cast<size_t>(end - begin));
  436. }
  437. void dump_float(number_float_t x, std::false_type /*is_ieee_single_or_double*/)
  438. {
  439. // get number of digits for a float -> text -> float round-trip
  440. static constexpr auto d = std::numeric_limits<number_float_t>::max_digits10;
  441. // the actual conversion
  442. std::ptrdiff_t len = snprintf(number_buffer.data(), number_buffer.size(), "%.*g", d, x);
  443. // negative value indicates an error
  444. assert(len > 0);
  445. // check if buffer was large enough
  446. assert(static_cast<std::size_t>(len) < number_buffer.size());
  447. // erase thousands separator
  448. if (thousands_sep != '\0')
  449. {
  450. const auto end = std::remove(number_buffer.begin(),
  451. number_buffer.begin() + len, thousands_sep);
  452. std::fill(end, number_buffer.end(), '\0');
  453. assert((end - number_buffer.begin()) <= len);
  454. len = (end - number_buffer.begin());
  455. }
  456. // convert decimal point to '.'
  457. if (decimal_point != '\0' and decimal_point != '.')
  458. {
  459. const auto dec_pos = std::find(number_buffer.begin(), number_buffer.end(), decimal_point);
  460. if (dec_pos != number_buffer.end())
  461. {
  462. *dec_pos = '.';
  463. }
  464. }
  465. o->write_characters(number_buffer.data(), static_cast<std::size_t>(len));
  466. // determine if need to append ".0"
  467. const bool value_is_int_like =
  468. std::none_of(number_buffer.begin(), number_buffer.begin() + len + 1,
  469. [](char c)
  470. {
  471. return (c == '.' or c == 'e');
  472. });
  473. if (value_is_int_like)
  474. {
  475. o->write_characters(".0", 2);
  476. }
  477. }
  478. /*!
  479. @brief check whether a string is UTF-8 encoded
  480. The function checks each byte of a string whether it is UTF-8 encoded. The
  481. result of the check is stored in the @a state parameter. The function must
  482. be called initially with state 0 (accept). State 1 means the string must
  483. be rejected, because the current byte is not allowed. If the string is
  484. completely processed, but the state is non-zero, the string ended
  485. prematurely; that is, the last byte indicated more bytes should have
  486. followed.
  487. @param[in,out] state the state of the decoding
  488. @param[in,out] codep codepoint (valid only if resulting state is UTF8_ACCEPT)
  489. @param[in] byte next byte to decode
  490. @return new state
  491. @note The function has been edited: a std::array is used.
  492. @copyright Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
  493. @sa http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
  494. */
  495. static uint8_t decode(uint8_t& state, uint32_t& codep, const uint8_t byte) noexcept
  496. {
  497. static const std::array<uint8_t, 400> utf8d =
  498. {
  499. {
  500. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00..1F
  501. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20..3F
  502. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40..5F
  503. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60..7F
  504. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 80..9F
  505. 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // A0..BF
  506. 8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C0..DF
  507. 0xA, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, // E0..EF
  508. 0xB, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, // F0..FF
  509. 0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0
  510. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, // s1..s2
  511. 1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // s3..s4
  512. 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, // s5..s6
  513. 1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // s7..s8
  514. }
  515. };
  516. const uint8_t type = utf8d[byte];
  517. codep = (state != UTF8_ACCEPT)
  518. ? (byte & 0x3fu) | (codep << 6)
  519. : static_cast<uint32_t>(0xff >> type) & (byte);
  520. state = utf8d[256u + state * 16u + type];
  521. return state;
  522. }
  523. private:
  524. /// the output of the serializer
  525. output_adapter_t<char> o = nullptr;
  526. /// a (hopefully) large enough character buffer
  527. std::array<char, 64> number_buffer{{}};
  528. /// the locale
  529. const std::lconv* loc = nullptr;
  530. /// the locale's thousand separator character
  531. const char thousands_sep = '\0';
  532. /// the locale's decimal point character
  533. const char decimal_point = '\0';
  534. /// string buffer
  535. std::array<char, 512> string_buffer{{}};
  536. /// the indentation character
  537. const char indent_char;
  538. /// the indentation string
  539. string_t indent_string;
  540. };
  541. }
  542. }