Horizon
serializer.hpp
1 #pragma once
2 
3 #include <algorithm> // reverse, remove, fill, find, none_of
4 #include <array> // array
5 #include <clocale> // localeconv, lconv
6 #include <cmath> // labs, isfinite, isnan, signbit
7 #include <cstddef> // size_t, ptrdiff_t
8 #include <cstdint> // uint8_t
9 #include <cstdio> // snprintf
10 #include <limits> // numeric_limits
11 #include <string> // string, char_traits
12 #include <type_traits> // is_same
13 #include <utility> // move
14 
15 #include <nlohmann/detail/conversions/to_chars.hpp>
16 #include <nlohmann/detail/exceptions.hpp>
17 #include <nlohmann/detail/macro_scope.hpp>
18 #include <nlohmann/detail/meta/cpp_future.hpp>
19 #include <nlohmann/detail/output/binary_writer.hpp>
20 #include <nlohmann/detail/output/output_adapters.hpp>
21 #include <nlohmann/detail/value_t.hpp>
22 
23 namespace nlohmann
24 {
25 namespace detail
26 {
28 // serialization //
30 
32 enum class error_handler_t
33 {
34  strict,
35  replace,
36  ignore
37 };
38 
39 template<typename BasicJsonType>
41 {
42  using string_t = typename BasicJsonType::string_t;
43  using number_float_t = typename BasicJsonType::number_float_t;
44  using number_integer_t = typename BasicJsonType::number_integer_t;
45  using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
46  using binary_char_t = typename BasicJsonType::binary_t::value_type;
47  static constexpr std::uint8_t UTF8_ACCEPT = 0;
48  static constexpr std::uint8_t UTF8_REJECT = 1;
49 
50  public:
56  serializer(output_adapter_t<char> s, const char ichar,
58  : o(std::move(s))
59  , loc(std::localeconv())
60  , thousands_sep(loc->thousands_sep == nullptr ? '\0' : std::char_traits<char>::to_char_type(* (loc->thousands_sep)))
61  , decimal_point(loc->decimal_point == nullptr ? '\0' : std::char_traits<char>::to_char_type(* (loc->decimal_point)))
62  , indent_char(ichar)
64  , error_handler(error_handler_)
65  {}
66 
67  // delete because of pointer members
68  serializer(const serializer&) = delete;
69  serializer& operator=(const serializer&) = delete;
70  serializer(serializer&&) = delete;
71  serializer& operator=(serializer&&) = delete;
72  ~serializer() = default;
73 
96  void dump(const BasicJsonType& val,
97  const bool pretty_print,
98  const bool ensure_ascii,
99  const unsigned int indent_step,
100  const unsigned int current_indent = 0)
101  {
102  switch (val.m_type)
103  {
104  case value_t::object:
105  {
106  if (val.m_value.object->empty())
107  {
108  o->write_characters("{}", 2);
109  return;
110  }
111 
112  if (pretty_print)
113  {
114  o->write_characters("{\n", 2);
115 
116  // variable to hold indentation for recursive calls
117  const auto new_indent = current_indent + indent_step;
118  if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))
119  {
120  indent_string.resize(indent_string.size() * 2, ' ');
121  }
122 
123  // first n-1 elements
124  auto i = val.m_value.object->cbegin();
125  for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)
126  {
127  o->write_characters(indent_string.c_str(), new_indent);
128  o->write_character('\"');
129  dump_escaped(i->first, ensure_ascii);
130  o->write_characters("\": ", 3);
131  dump(i->second, true, ensure_ascii, indent_step, new_indent);
132  o->write_characters(",\n", 2);
133  }
134 
135  // last element
136  JSON_ASSERT(i != val.m_value.object->cend());
137  JSON_ASSERT(std::next(i) == val.m_value.object->cend());
138  o->write_characters(indent_string.c_str(), new_indent);
139  o->write_character('\"');
140  dump_escaped(i->first, ensure_ascii);
141  o->write_characters("\": ", 3);
142  dump(i->second, true, ensure_ascii, indent_step, new_indent);
143 
144  o->write_character('\n');
145  o->write_characters(indent_string.c_str(), current_indent);
146  o->write_character('}');
147  }
148  else
149  {
150  o->write_character('{');
151 
152  // first n-1 elements
153  auto i = val.m_value.object->cbegin();
154  for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)
155  {
156  o->write_character('\"');
157  dump_escaped(i->first, ensure_ascii);
158  o->write_characters("\":", 2);
159  dump(i->second, false, ensure_ascii, indent_step, current_indent);
160  o->write_character(',');
161  }
162 
163  // last element
164  JSON_ASSERT(i != val.m_value.object->cend());
165  JSON_ASSERT(std::next(i) == val.m_value.object->cend());
166  o->write_character('\"');
167  dump_escaped(i->first, ensure_ascii);
168  o->write_characters("\":", 2);
169  dump(i->second, false, ensure_ascii, indent_step, current_indent);
170 
171  o->write_character('}');
172  }
173 
174  return;
175  }
176 
177  case value_t::array:
178  {
179  if (val.m_value.array->empty())
180  {
181  o->write_characters("[]", 2);
182  return;
183  }
184 
185  if (pretty_print)
186  {
187  o->write_characters("[\n", 2);
188 
189  // variable to hold indentation for recursive calls
190  const auto new_indent = current_indent + indent_step;
191  if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))
192  {
193  indent_string.resize(indent_string.size() * 2, ' ');
194  }
195 
196  // first n-1 elements
197  for (auto i = val.m_value.array->cbegin();
198  i != val.m_value.array->cend() - 1; ++i)
199  {
200  o->write_characters(indent_string.c_str(), new_indent);
201  dump(*i, true, ensure_ascii, indent_step, new_indent);
202  o->write_characters(",\n", 2);
203  }
204 
205  // last element
206  JSON_ASSERT(!val.m_value.array->empty());
207  o->write_characters(indent_string.c_str(), new_indent);
208  dump(val.m_value.array->back(), true, ensure_ascii, indent_step, new_indent);
209 
210  o->write_character('\n');
211  o->write_characters(indent_string.c_str(), current_indent);
212  o->write_character(']');
213  }
214  else
215  {
216  o->write_character('[');
217 
218  // first n-1 elements
219  for (auto i = val.m_value.array->cbegin();
220  i != val.m_value.array->cend() - 1; ++i)
221  {
222  dump(*i, false, ensure_ascii, indent_step, current_indent);
223  o->write_character(',');
224  }
225 
226  // last element
227  JSON_ASSERT(!val.m_value.array->empty());
228  dump(val.m_value.array->back(), false, ensure_ascii, indent_step, current_indent);
229 
230  o->write_character(']');
231  }
232 
233  return;
234  }
235 
236  case value_t::string:
237  {
238  o->write_character('\"');
239  dump_escaped(*val.m_value.string, ensure_ascii);
240  o->write_character('\"');
241  return;
242  }
243 
244  case value_t::binary:
245  {
246  if (pretty_print)
247  {
248  o->write_characters("{\n", 2);
249 
250  // variable to hold indentation for recursive calls
251  const auto new_indent = current_indent + indent_step;
252  if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))
253  {
254  indent_string.resize(indent_string.size() * 2, ' ');
255  }
256 
257  o->write_characters(indent_string.c_str(), new_indent);
258 
259  o->write_characters("\"bytes\": [", 10);
260 
261  if (!val.m_value.binary->empty())
262  {
263  for (auto i = val.m_value.binary->cbegin();
264  i != val.m_value.binary->cend() - 1; ++i)
265  {
266  dump_integer(*i);
267  o->write_characters(", ", 2);
268  }
269  dump_integer(val.m_value.binary->back());
270  }
271 
272  o->write_characters("],\n", 3);
273  o->write_characters(indent_string.c_str(), new_indent);
274 
275  o->write_characters("\"subtype\": ", 11);
276  if (val.m_value.binary->has_subtype())
277  {
278  dump_integer(val.m_value.binary->subtype());
279  }
280  else
281  {
282  o->write_characters("null", 4);
283  }
284  o->write_character('\n');
285  o->write_characters(indent_string.c_str(), current_indent);
286  o->write_character('}');
287  }
288  else
289  {
290  o->write_characters("{\"bytes\":[", 10);
291 
292  if (!val.m_value.binary->empty())
293  {
294  for (auto i = val.m_value.binary->cbegin();
295  i != val.m_value.binary->cend() - 1; ++i)
296  {
297  dump_integer(*i);
298  o->write_character(',');
299  }
300  dump_integer(val.m_value.binary->back());
301  }
302 
303  o->write_characters("],\"subtype\":", 12);
304  if (val.m_value.binary->has_subtype())
305  {
306  dump_integer(val.m_value.binary->subtype());
307  o->write_character('}');
308  }
309  else
310  {
311  o->write_characters("null}", 5);
312  }
313  }
314  return;
315  }
316 
317  case value_t::boolean:
318  {
319  if (val.m_value.boolean)
320  {
321  o->write_characters("true", 4);
322  }
323  else
324  {
325  o->write_characters("false", 5);
326  }
327  return;
328  }
329 
331  {
332  dump_integer(val.m_value.number_integer);
333  return;
334  }
335 
337  {
338  dump_integer(val.m_value.number_unsigned);
339  return;
340  }
341 
343  {
344  dump_float(val.m_value.number_float);
345  return;
346  }
347 
348  case value_t::discarded:
349  {
350  o->write_characters("<discarded>", 11);
351  return;
352  }
353 
354  case value_t::null:
355  {
356  o->write_characters("null", 4);
357  return;
358  }
359 
360  default: // LCOV_EXCL_LINE
361  JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
362  }
363  }
364 
365  JSON_PRIVATE_UNLESS_TESTED:
380  void dump_escaped(const string_t& s, const bool ensure_ascii)
381  {
382  std::uint32_t codepoint{};
383  std::uint8_t state = UTF8_ACCEPT;
384  std::size_t bytes = 0; // number of bytes written to string_buffer
385 
386  // number of bytes written at the point of the last valid byte
387  std::size_t bytes_after_last_accept = 0;
388  std::size_t undumped_chars = 0;
389 
390  for (std::size_t i = 0; i < s.size(); ++i)
391  {
392  const auto byte = static_cast<std::uint8_t>(s[i]);
393 
394  switch (decode(state, codepoint, byte))
395  {
396  case UTF8_ACCEPT: // decode found a new code point
397  {
398  switch (codepoint)
399  {
400  case 0x08: // backspace
401  {
402  string_buffer[bytes++] = '\\';
403  string_buffer[bytes++] = 'b';
404  break;
405  }
406 
407  case 0x09: // horizontal tab
408  {
409  string_buffer[bytes++] = '\\';
410  string_buffer[bytes++] = 't';
411  break;
412  }
413 
414  case 0x0A: // newline
415  {
416  string_buffer[bytes++] = '\\';
417  string_buffer[bytes++] = 'n';
418  break;
419  }
420 
421  case 0x0C: // formfeed
422  {
423  string_buffer[bytes++] = '\\';
424  string_buffer[bytes++] = 'f';
425  break;
426  }
427 
428  case 0x0D: // carriage return
429  {
430  string_buffer[bytes++] = '\\';
431  string_buffer[bytes++] = 'r';
432  break;
433  }
434 
435  case 0x22: // quotation mark
436  {
437  string_buffer[bytes++] = '\\';
438  string_buffer[bytes++] = '\"';
439  break;
440  }
441 
442  case 0x5C: // reverse solidus
443  {
444  string_buffer[bytes++] = '\\';
445  string_buffer[bytes++] = '\\';
446  break;
447  }
448 
449  default:
450  {
451  // escape control characters (0x00..0x1F) or, if
452  // ensure_ascii parameter is used, non-ASCII characters
453  if ((codepoint <= 0x1F) || (ensure_ascii && (codepoint >= 0x7F)))
454  {
455  if (codepoint <= 0xFFFF)
456  {
457  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
458  (std::snprintf)(string_buffer.data() + bytes, 7, "\\u%04x",
459  static_cast<std::uint16_t>(codepoint));
460  bytes += 6;
461  }
462  else
463  {
464  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
465  (std::snprintf)(string_buffer.data() + bytes, 13, "\\u%04x\\u%04x",
466  static_cast<std::uint16_t>(0xD7C0u + (codepoint >> 10u)),
467  static_cast<std::uint16_t>(0xDC00u + (codepoint & 0x3FFu)));
468  bytes += 12;
469  }
470  }
471  else
472  {
473  // copy byte to buffer (all previous bytes
474  // been copied have in default case above)
475  string_buffer[bytes++] = s[i];
476  }
477  break;
478  }
479  }
480 
481  // write buffer and reset index; there must be 13 bytes
482  // left, as this is the maximal number of bytes to be
483  // written ("\uxxxx\uxxxx\0") for one code point
484  if (string_buffer.size() - bytes < 13)
485  {
486  o->write_characters(string_buffer.data(), bytes);
487  bytes = 0;
488  }
489 
490  // remember the byte position of this accept
491  bytes_after_last_accept = bytes;
492  undumped_chars = 0;
493  break;
494  }
495 
496  case UTF8_REJECT: // decode found invalid UTF-8 byte
497  {
498  switch (error_handler)
499  {
501  {
502  std::string sn(9, '\0');
503  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
504  (std::snprintf)(&sn[0], sn.size(), "%.2X", byte);
505  JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + sn, BasicJsonType()));
506  }
507 
510  {
511  // in case we saw this character the first time, we
512  // would like to read it again, because the byte
513  // may be OK for itself, but just not OK for the
514  // previous sequence
515  if (undumped_chars > 0)
516  {
517  --i;
518  }
519 
520  // reset length buffer to the last accepted index;
521  // thus removing/ignoring the invalid characters
522  bytes = bytes_after_last_accept;
523 
525  {
526  // add a replacement character
527  if (ensure_ascii)
528  {
529  string_buffer[bytes++] = '\\';
530  string_buffer[bytes++] = 'u';
531  string_buffer[bytes++] = 'f';
532  string_buffer[bytes++] = 'f';
533  string_buffer[bytes++] = 'f';
534  string_buffer[bytes++] = 'd';
535  }
536  else
537  {
538  string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\xEF');
539  string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\xBF');
540  string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\xBD');
541  }
542 
543  // write buffer and reset index; there must be 13 bytes
544  // left, as this is the maximal number of bytes to be
545  // written ("\uxxxx\uxxxx\0") for one code point
546  if (string_buffer.size() - bytes < 13)
547  {
548  o->write_characters(string_buffer.data(), bytes);
549  bytes = 0;
550  }
551 
552  bytes_after_last_accept = bytes;
553  }
554 
555  undumped_chars = 0;
556 
557  // continue processing the string
558  state = UTF8_ACCEPT;
559  break;
560  }
561 
562  default: // LCOV_EXCL_LINE
563  JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
564  }
565  break;
566  }
567 
568  default: // decode found yet incomplete multi-byte code point
569  {
570  if (!ensure_ascii)
571  {
572  // code point will not be escaped - copy byte to buffer
573  string_buffer[bytes++] = s[i];
574  }
575  ++undumped_chars;
576  break;
577  }
578  }
579  }
580 
581  // we finished processing the string
582  if (JSON_HEDLEY_LIKELY(state == UTF8_ACCEPT))
583  {
584  // write buffer
585  if (bytes > 0)
586  {
587  o->write_characters(string_buffer.data(), bytes);
588  }
589  }
590  else
591  {
592  // we finish reading, but do not accept: string was incomplete
593  switch (error_handler)
594  {
596  {
597  std::string sn(9, '\0');
598  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
599  (std::snprintf)(&sn[0], sn.size(), "%.2X", static_cast<std::uint8_t>(s.back()));
600  JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + sn, BasicJsonType()));
601  }
602 
604  {
605  // write all accepted bytes
606  o->write_characters(string_buffer.data(), bytes_after_last_accept);
607  break;
608  }
609 
611  {
612  // write all accepted bytes
613  o->write_characters(string_buffer.data(), bytes_after_last_accept);
614  // add a replacement character
615  if (ensure_ascii)
616  {
617  o->write_characters("\\ufffd", 6);
618  }
619  else
620  {
621  o->write_characters("\xEF\xBF\xBD", 3);
622  }
623  break;
624  }
625 
626  default: // LCOV_EXCL_LINE
627  JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
628  }
629  }
630  }
631 
632  private:
641  inline unsigned int count_digits(number_unsigned_t x) noexcept
642  {
643  unsigned int n_digits = 1;
644  for (;;)
645  {
646  if (x < 10)
647  {
648  return n_digits;
649  }
650  if (x < 100)
651  {
652  return n_digits + 1;
653  }
654  if (x < 1000)
655  {
656  return n_digits + 2;
657  }
658  if (x < 10000)
659  {
660  return n_digits + 3;
661  }
662  x = x / 10000u;
663  n_digits += 4;
664  }
665  }
666 
676  template < typename NumberType, detail::enable_if_t <
677  std::is_integral<NumberType>::value ||
678  std::is_same<NumberType, number_unsigned_t>::value ||
679  std::is_same<NumberType, number_integer_t>::value ||
680  std::is_same<NumberType, binary_char_t>::value,
681  int > = 0 >
682  void dump_integer(NumberType x)
683  {
684  static constexpr std::array<std::array<char, 2>, 100> digits_to_99
685  {
686  {
687  {{'0', '0'}}, {{'0', '1'}}, {{'0', '2'}}, {{'0', '3'}}, {{'0', '4'}}, {{'0', '5'}}, {{'0', '6'}}, {{'0', '7'}}, {{'0', '8'}}, {{'0', '9'}},
688  {{'1', '0'}}, {{'1', '1'}}, {{'1', '2'}}, {{'1', '3'}}, {{'1', '4'}}, {{'1', '5'}}, {{'1', '6'}}, {{'1', '7'}}, {{'1', '8'}}, {{'1', '9'}},
689  {{'2', '0'}}, {{'2', '1'}}, {{'2', '2'}}, {{'2', '3'}}, {{'2', '4'}}, {{'2', '5'}}, {{'2', '6'}}, {{'2', '7'}}, {{'2', '8'}}, {{'2', '9'}},
690  {{'3', '0'}}, {{'3', '1'}}, {{'3', '2'}}, {{'3', '3'}}, {{'3', '4'}}, {{'3', '5'}}, {{'3', '6'}}, {{'3', '7'}}, {{'3', '8'}}, {{'3', '9'}},
691  {{'4', '0'}}, {{'4', '1'}}, {{'4', '2'}}, {{'4', '3'}}, {{'4', '4'}}, {{'4', '5'}}, {{'4', '6'}}, {{'4', '7'}}, {{'4', '8'}}, {{'4', '9'}},
692  {{'5', '0'}}, {{'5', '1'}}, {{'5', '2'}}, {{'5', '3'}}, {{'5', '4'}}, {{'5', '5'}}, {{'5', '6'}}, {{'5', '7'}}, {{'5', '8'}}, {{'5', '9'}},
693  {{'6', '0'}}, {{'6', '1'}}, {{'6', '2'}}, {{'6', '3'}}, {{'6', '4'}}, {{'6', '5'}}, {{'6', '6'}}, {{'6', '7'}}, {{'6', '8'}}, {{'6', '9'}},
694  {{'7', '0'}}, {{'7', '1'}}, {{'7', '2'}}, {{'7', '3'}}, {{'7', '4'}}, {{'7', '5'}}, {{'7', '6'}}, {{'7', '7'}}, {{'7', '8'}}, {{'7', '9'}},
695  {{'8', '0'}}, {{'8', '1'}}, {{'8', '2'}}, {{'8', '3'}}, {{'8', '4'}}, {{'8', '5'}}, {{'8', '6'}}, {{'8', '7'}}, {{'8', '8'}}, {{'8', '9'}},
696  {{'9', '0'}}, {{'9', '1'}}, {{'9', '2'}}, {{'9', '3'}}, {{'9', '4'}}, {{'9', '5'}}, {{'9', '6'}}, {{'9', '7'}}, {{'9', '8'}}, {{'9', '9'}},
697  }
698  };
699 
700  // special case for "0"
701  if (x == 0)
702  {
703  o->write_character('0');
704  return;
705  }
706 
707  // use a pointer to fill the buffer
708  auto buffer_ptr = number_buffer.begin(); // NOLINT(llvm-qualified-auto,readability-qualified-auto,cppcoreguidelines-pro-type-vararg,hicpp-vararg)
709 
710  const bool is_negative = std::is_signed<NumberType>::value && !(x >= 0); // see issue #755
711  number_unsigned_t abs_value;
712 
713  unsigned int n_chars{};
714 
715  if (is_negative)
716  {
717  *buffer_ptr = '-';
718  abs_value = remove_sign(static_cast<number_integer_t>(x));
719 
720  // account one more byte for the minus sign
721  n_chars = 1 + count_digits(abs_value);
722  }
723  else
724  {
725  abs_value = static_cast<number_unsigned_t>(x);
726  n_chars = count_digits(abs_value);
727  }
728 
729  // spare 1 byte for '\0'
730  JSON_ASSERT(n_chars < number_buffer.size() - 1);
731 
732  // jump to the end to generate the string from backward
733  // so we later avoid reversing the result
734  buffer_ptr += n_chars;
735 
736  // Fast int2ascii implementation inspired by "Fastware" talk by Andrei Alexandrescu
737  // See: https://www.youtube.com/watch?v=o4-CwDo2zpg
738  while (abs_value >= 100)
739  {
740  const auto digits_index = static_cast<unsigned>((abs_value % 100));
741  abs_value /= 100;
742  *(--buffer_ptr) = digits_to_99[digits_index][1];
743  *(--buffer_ptr) = digits_to_99[digits_index][0];
744  }
745 
746  if (abs_value >= 10)
747  {
748  const auto digits_index = static_cast<unsigned>(abs_value);
749  *(--buffer_ptr) = digits_to_99[digits_index][1];
750  *(--buffer_ptr) = digits_to_99[digits_index][0];
751  }
752  else
753  {
754  *(--buffer_ptr) = static_cast<char>('0' + abs_value);
755  }
756 
757  o->write_characters(number_buffer.data(), n_chars);
758  }
759 
768  void dump_float(number_float_t x)
769  {
770  // NaN / inf
771  if (!std::isfinite(x))
772  {
773  o->write_characters("null", 4);
774  return;
775  }
776 
777  // If number_float_t is an IEEE-754 single or double precision number,
778  // use the Grisu2 algorithm to produce short numbers which are
779  // guaranteed to round-trip, using strtof and strtod, resp.
780  //
781  // NB: The test below works if <long double> == <double>.
782  static constexpr bool is_ieee_single_or_double
783  = (std::numeric_limits<number_float_t>::is_iec559 && std::numeric_limits<number_float_t>::digits == 24 && std::numeric_limits<number_float_t>::max_exponent == 128) ||
784  (std::numeric_limits<number_float_t>::is_iec559 && std::numeric_limits<number_float_t>::digits == 53 && std::numeric_limits<number_float_t>::max_exponent == 1024);
785 
786  dump_float(x, std::integral_constant<bool, is_ieee_single_or_double>());
787  }
788 
789  void dump_float(number_float_t x, std::true_type /*is_ieee_single_or_double*/)
790  {
791  auto* begin = number_buffer.data();
792  auto* end = ::nlohmann::detail::to_chars(begin, begin + number_buffer.size(), x);
793 
794  o->write_characters(begin, static_cast<size_t>(end - begin));
795  }
796 
797  void dump_float(number_float_t x, std::false_type /*is_ieee_single_or_double*/)
798  {
799  // get number of digits for a float -> text -> float round-trip
800  static constexpr auto d = std::numeric_limits<number_float_t>::max_digits10;
801 
802  // the actual conversion
803  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
804  std::ptrdiff_t len = (std::snprintf)(number_buffer.data(), number_buffer.size(), "%.*g", d, x);
805 
806  // negative value indicates an error
807  JSON_ASSERT(len > 0);
808  // check if buffer was large enough
809  JSON_ASSERT(static_cast<std::size_t>(len) < number_buffer.size());
810 
811  // erase thousands separator
812  if (thousands_sep != '\0')
813  {
814  auto* const end = std::remove(number_buffer.begin(),
815  number_buffer.begin() + len, thousands_sep);
816  std::fill(end, number_buffer.end(), '\0');
817  JSON_ASSERT((end - number_buffer.begin()) <= len);
818  len = (end - number_buffer.begin());
819  }
820 
821  // convert decimal point to '.'
822  if (decimal_point != '\0' && decimal_point != '.')
823  {
824  auto* const dec_pos = std::find(number_buffer.begin(), number_buffer.end(), decimal_point);
825  if (dec_pos != number_buffer.end())
826  {
827  *dec_pos = '.';
828  }
829  }
830 
831  o->write_characters(number_buffer.data(), static_cast<std::size_t>(len));
832 
833  // determine if need to append ".0"
834  const bool value_is_int_like =
835  std::none_of(number_buffer.begin(), number_buffer.begin() + len + 1,
836  [](char c)
837  {
838  return c == '.' || c == 'e';
839  });
840 
841  if (value_is_int_like)
842  {
843  o->write_characters(".0", 2);
844  }
845  }
846 
868  static std::uint8_t decode(std::uint8_t& state, std::uint32_t& codep, const std::uint8_t byte) noexcept
869  {
870  static const std::array<std::uint8_t, 400> utf8d =
871  {
872  {
873  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
874  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
875  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
876  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
877  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
878  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
879  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
880  0xA, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, // E0..EF
881  0xB, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, // F0..FF
882  0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0
883  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
884  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
885  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
886  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
887  }
888  };
889 
890  JSON_ASSERT(byte < utf8d.size());
891  const std::uint8_t type = utf8d[byte];
892 
893  codep = (state != UTF8_ACCEPT)
894  ? (byte & 0x3fu) | (codep << 6u)
895  : (0xFFu >> type) & (byte);
896 
897  std::size_t index = 256u + static_cast<size_t>(state) * 16u + static_cast<size_t>(type);
898  JSON_ASSERT(index < 400);
899  state = utf8d[index];
900  return state;
901  }
902 
903  /*
904  * Overload to make the compiler happy while it is instantiating
905  * dump_integer for number_unsigned_t.
906  * Must never be called.
907  */
908  number_unsigned_t remove_sign(number_unsigned_t x)
909  {
910  JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
911  return x; // LCOV_EXCL_LINE
912  }
913 
914  /*
915  * Helper function for dump_integer
916  *
917  * This function takes a negative signed integer and returns its absolute
918  * value as unsigned integer. The plus/minus shuffling is necessary as we can
919  * not directly remove the sign of an arbitrary signed integer as the
920  * absolute values of INT_MIN and INT_MAX are usually not the same. See
921  * #1708 for details.
922  */
923  inline number_unsigned_t remove_sign(number_integer_t x) noexcept
924  {
925  JSON_ASSERT(x < 0 && x < (std::numeric_limits<number_integer_t>::max)()); // NOLINT(misc-redundant-expression)
926  return static_cast<number_unsigned_t>(-(x + 1)) + 1;
927  }
928 
929  private:
931  output_adapter_t<char> o = nullptr;
932 
934  std::array<char, 64> number_buffer{{}};
935 
937  const std::lconv* loc = nullptr;
939  const char thousands_sep = '\0';
941  const char decimal_point = '\0';
942 
944  std::array<char, 512> string_buffer{{}};
945 
947  const char indent_char;
949  string_t indent_string;
950 
953 };
954 } // namespace detail
955 } // namespace nlohmann
Definition: serializer.hpp:41
const error_handler_t error_handler
error_handler how to react on decoding errors
Definition: serializer.hpp:952
const std::lconv * loc
the locale
Definition: serializer.hpp:937
std::array< char, 64 > number_buffer
a (hopefully) large enough character buffer
Definition: serializer.hpp:934
const char decimal_point
the locale's decimal point character
Definition: serializer.hpp:941
const char thousands_sep
the locale's thousand separator character
Definition: serializer.hpp:939
void dump(const BasicJsonType &val, const bool pretty_print, const bool ensure_ascii, const unsigned int indent_step, const unsigned int current_indent=0)
internal implementation of the serialization function
Definition: serializer.hpp:96
const char indent_char
the indentation character
Definition: serializer.hpp:947
std::array< char, 512 > string_buffer
string buffer
Definition: serializer.hpp:944
serializer(output_adapter_t< char > s, const char ichar, error_handler_t error_handler_=error_handler_t::strict)
Definition: serializer.hpp:56
string_t indent_string
the indentation string
Definition: serializer.hpp:949
zip_uint32_t uint32_t
zip_uint32_t typedef.
Definition: zip.hpp:98
zip_uint8_t uint8_t
zip_uint8_t typedef.
Definition: zip.hpp:78
zip_uint16_t uint16_t
zip_uint16_t typedef.
Definition: zip.hpp:88
@ number_integer
number value (signed integer)
@ discarded
discarded by the parser callback function
@ binary
binary array (ordered collection of bytes)
@ object
object (unordered set of name/value pairs)
@ number_float
number value (floating-point)
@ number_unsigned
number value (unsigned integer)
@ array
array (ordered collection of values)
error_handler_t
how to treat decoding errors
Definition: serializer.hpp:33
@ strict
throw a type_error exception in case of invalid UTF-8
@ ignore
ignore invalid UTF-8 sequences
@ replace
replace invalid UTF-8 sequences with U+FFFD
std::shared_ptr< output_adapter_protocol< CharType > > output_adapter_t
a type to simplify interfaces
Definition: output_adapters.hpp:37
namespace for Niels Lohmann
Definition: adl_serializer.hpp:12