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)
63  , indent_string(512, indent_char)
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); // LCOV_EXCL_LINE
362  }
363  }
364 
365  private:
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<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  (std::snprintf)(string_buffer.data() + bytes, 7, "\\u%04x",
458  static_cast<std::uint16_t>(codepoint));
459  bytes += 6;
460  }
461  else
462  {
463  (std::snprintf)(string_buffer.data() + bytes, 13, "\\u%04x\\u%04x",
464  static_cast<std::uint16_t>(0xD7C0u + (codepoint >> 10u)),
465  static_cast<std::uint16_t>(0xDC00u + (codepoint & 0x3FFu)));
466  bytes += 12;
467  }
468  }
469  else
470  {
471  // copy byte to buffer (all previous bytes
472  // been copied have in default case above)
473  string_buffer[bytes++] = s[i];
474  }
475  break;
476  }
477  }
478 
479  // write buffer and reset index; there must be 13 bytes
480  // left, as this is the maximal number of bytes to be
481  // written ("\uxxxx\uxxxx\0") for one code point
482  if (string_buffer.size() - bytes < 13)
483  {
484  o->write_characters(string_buffer.data(), bytes);
485  bytes = 0;
486  }
487 
488  // remember the byte position of this accept
489  bytes_after_last_accept = bytes;
490  undumped_chars = 0;
491  break;
492  }
493 
494  case UTF8_REJECT: // decode found invalid UTF-8 byte
495  {
496  switch (error_handler)
497  {
499  {
500  std::string sn(3, '\0');
501  (std::snprintf)(&sn[0], sn.size(), "%.2X", byte);
502  JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + sn));
503  }
504 
507  {
508  // in case we saw this character the first time, we
509  // would like to read it again, because the byte
510  // may be OK for itself, but just not OK for the
511  // previous sequence
512  if (undumped_chars > 0)
513  {
514  --i;
515  }
516 
517  // reset length buffer to the last accepted index;
518  // thus removing/ignoring the invalid characters
519  bytes = bytes_after_last_accept;
520 
521  if (error_handler == error_handler_t::replace)
522  {
523  // add a replacement character
524  if (ensure_ascii)
525  {
526  string_buffer[bytes++] = '\\';
527  string_buffer[bytes++] = 'u';
528  string_buffer[bytes++] = 'f';
529  string_buffer[bytes++] = 'f';
530  string_buffer[bytes++] = 'f';
531  string_buffer[bytes++] = 'd';
532  }
533  else
534  {
535  string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\xEF');
536  string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\xBF');
537  string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\xBD');
538  }
539 
540  // write buffer and reset index; there must be 13 bytes
541  // left, as this is the maximal number of bytes to be
542  // written ("\uxxxx\uxxxx\0") for one code point
543  if (string_buffer.size() - bytes < 13)
544  {
545  o->write_characters(string_buffer.data(), bytes);
546  bytes = 0;
547  }
548 
549  bytes_after_last_accept = bytes;
550  }
551 
552  undumped_chars = 0;
553 
554  // continue processing the string
555  state = UTF8_ACCEPT;
556  break;
557  }
558 
559  default: // LCOV_EXCL_LINE
560  JSON_ASSERT(false); // LCOV_EXCL_LINE
561  }
562  break;
563  }
564 
565  default: // decode found yet incomplete multi-byte code point
566  {
567  if (!ensure_ascii)
568  {
569  // code point will not be escaped - copy byte to buffer
570  string_buffer[bytes++] = s[i];
571  }
572  ++undumped_chars;
573  break;
574  }
575  }
576  }
577 
578  // we finished processing the string
579  if (JSON_HEDLEY_LIKELY(state == UTF8_ACCEPT))
580  {
581  // write buffer
582  if (bytes > 0)
583  {
584  o->write_characters(string_buffer.data(), bytes);
585  }
586  }
587  else
588  {
589  // we finish reading, but do not accept: string was incomplete
590  switch (error_handler)
591  {
593  {
594  std::string sn(3, '\0');
595  (std::snprintf)(&sn[0], sn.size(), "%.2X", static_cast<std::uint8_t>(s.back()));
596  JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + sn));
597  }
598 
600  {
601  // write all accepted bytes
602  o->write_characters(string_buffer.data(), bytes_after_last_accept);
603  break;
604  }
605 
607  {
608  // write all accepted bytes
609  o->write_characters(string_buffer.data(), bytes_after_last_accept);
610  // add a replacement character
611  if (ensure_ascii)
612  {
613  o->write_characters("\\ufffd", 6);
614  }
615  else
616  {
617  o->write_characters("\xEF\xBF\xBD", 3);
618  }
619  break;
620  }
621 
622  default: // LCOV_EXCL_LINE
623  JSON_ASSERT(false); // LCOV_EXCL_LINE
624  }
625  }
626  }
627 
636  inline unsigned int count_digits(number_unsigned_t x) noexcept
637  {
638  unsigned int n_digits = 1;
639  for (;;)
640  {
641  if (x < 10)
642  {
643  return n_digits;
644  }
645  if (x < 100)
646  {
647  return n_digits + 1;
648  }
649  if (x < 1000)
650  {
651  return n_digits + 2;
652  }
653  if (x < 10000)
654  {
655  return n_digits + 3;
656  }
657  x = x / 10000u;
658  n_digits += 4;
659  }
660  }
661 
671  template < typename NumberType, detail::enable_if_t <
672  std::is_same<NumberType, number_unsigned_t>::value ||
673  std::is_same<NumberType, number_integer_t>::value ||
674  std::is_same<NumberType, binary_char_t>::value,
675  int > = 0 >
676  void dump_integer(NumberType x)
677  {
678  static constexpr std::array<std::array<char, 2>, 100> digits_to_99
679  {
680  {
681  {{'0', '0'}}, {{'0', '1'}}, {{'0', '2'}}, {{'0', '3'}}, {{'0', '4'}}, {{'0', '5'}}, {{'0', '6'}}, {{'0', '7'}}, {{'0', '8'}}, {{'0', '9'}},
682  {{'1', '0'}}, {{'1', '1'}}, {{'1', '2'}}, {{'1', '3'}}, {{'1', '4'}}, {{'1', '5'}}, {{'1', '6'}}, {{'1', '7'}}, {{'1', '8'}}, {{'1', '9'}},
683  {{'2', '0'}}, {{'2', '1'}}, {{'2', '2'}}, {{'2', '3'}}, {{'2', '4'}}, {{'2', '5'}}, {{'2', '6'}}, {{'2', '7'}}, {{'2', '8'}}, {{'2', '9'}},
684  {{'3', '0'}}, {{'3', '1'}}, {{'3', '2'}}, {{'3', '3'}}, {{'3', '4'}}, {{'3', '5'}}, {{'3', '6'}}, {{'3', '7'}}, {{'3', '8'}}, {{'3', '9'}},
685  {{'4', '0'}}, {{'4', '1'}}, {{'4', '2'}}, {{'4', '3'}}, {{'4', '4'}}, {{'4', '5'}}, {{'4', '6'}}, {{'4', '7'}}, {{'4', '8'}}, {{'4', '9'}},
686  {{'5', '0'}}, {{'5', '1'}}, {{'5', '2'}}, {{'5', '3'}}, {{'5', '4'}}, {{'5', '5'}}, {{'5', '6'}}, {{'5', '7'}}, {{'5', '8'}}, {{'5', '9'}},
687  {{'6', '0'}}, {{'6', '1'}}, {{'6', '2'}}, {{'6', '3'}}, {{'6', '4'}}, {{'6', '5'}}, {{'6', '6'}}, {{'6', '7'}}, {{'6', '8'}}, {{'6', '9'}},
688  {{'7', '0'}}, {{'7', '1'}}, {{'7', '2'}}, {{'7', '3'}}, {{'7', '4'}}, {{'7', '5'}}, {{'7', '6'}}, {{'7', '7'}}, {{'7', '8'}}, {{'7', '9'}},
689  {{'8', '0'}}, {{'8', '1'}}, {{'8', '2'}}, {{'8', '3'}}, {{'8', '4'}}, {{'8', '5'}}, {{'8', '6'}}, {{'8', '7'}}, {{'8', '8'}}, {{'8', '9'}},
690  {{'9', '0'}}, {{'9', '1'}}, {{'9', '2'}}, {{'9', '3'}}, {{'9', '4'}}, {{'9', '5'}}, {{'9', '6'}}, {{'9', '7'}}, {{'9', '8'}}, {{'9', '9'}},
691  }
692  };
693 
694  // special case for "0"
695  if (x == 0)
696  {
697  o->write_character('0');
698  return;
699  }
700 
701  // use a pointer to fill the buffer
702  auto buffer_ptr = number_buffer.begin();
703 
704  const bool is_negative = std::is_same<NumberType, number_integer_t>::value && !(x >= 0); // see issue #755
705  number_unsigned_t abs_value;
706 
707  unsigned int n_chars;
708 
709  if (is_negative)
710  {
711  *buffer_ptr = '-';
712  abs_value = remove_sign(static_cast<number_integer_t>(x));
713 
714  // account one more byte for the minus sign
715  n_chars = 1 + count_digits(abs_value);
716  }
717  else
718  {
719  abs_value = static_cast<number_unsigned_t>(x);
720  n_chars = count_digits(abs_value);
721  }
722 
723  // spare 1 byte for '\0'
724  JSON_ASSERT(n_chars < number_buffer.size() - 1);
725 
726  // jump to the end to generate the string from backward
727  // so we later avoid reversing the result
728  buffer_ptr += n_chars;
729 
730  // Fast int2ascii implementation inspired by "Fastware" talk by Andrei Alexandrescu
731  // See: https://www.youtube.com/watch?v=o4-CwDo2zpg
732  while (abs_value >= 100)
733  {
734  const auto digits_index = static_cast<unsigned>((abs_value % 100));
735  abs_value /= 100;
736  *(--buffer_ptr) = digits_to_99[digits_index][1];
737  *(--buffer_ptr) = digits_to_99[digits_index][0];
738  }
739 
740  if (abs_value >= 10)
741  {
742  const auto digits_index = static_cast<unsigned>(abs_value);
743  *(--buffer_ptr) = digits_to_99[digits_index][1];
744  *(--buffer_ptr) = digits_to_99[digits_index][0];
745  }
746  else
747  {
748  *(--buffer_ptr) = static_cast<char>('0' + abs_value);
749  }
750 
751  o->write_characters(number_buffer.data(), n_chars);
752  }
753 
762  void dump_float(number_float_t x)
763  {
764  // NaN / inf
765  if (!std::isfinite(x))
766  {
767  o->write_characters("null", 4);
768  return;
769  }
770 
771  // If number_float_t is an IEEE-754 single or double precision number,
772  // use the Grisu2 algorithm to produce short numbers which are
773  // guaranteed to round-trip, using strtof and strtod, resp.
774  //
775  // NB: The test below works if <long double> == <double>.
776  static constexpr bool is_ieee_single_or_double
777  = (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) ||
778  (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);
779 
780  dump_float(x, std::integral_constant<bool, is_ieee_single_or_double>());
781  }
782 
783  void dump_float(number_float_t x, std::true_type /*is_ieee_single_or_double*/)
784  {
785  char* begin = number_buffer.data();
786  char* end = ::nlohmann::detail::to_chars(begin, begin + number_buffer.size(), x);
787 
788  o->write_characters(begin, static_cast<size_t>(end - begin));
789  }
790 
791  void dump_float(number_float_t x, std::false_type /*is_ieee_single_or_double*/)
792  {
793  // get number of digits for a float -> text -> float round-trip
794  static constexpr auto d = std::numeric_limits<number_float_t>::max_digits10;
795 
796  // the actual conversion
797  std::ptrdiff_t len = (std::snprintf)(number_buffer.data(), number_buffer.size(), "%.*g", d, x);
798 
799  // negative value indicates an error
800  JSON_ASSERT(len > 0);
801  // check if buffer was large enough
802  JSON_ASSERT(static_cast<std::size_t>(len) < number_buffer.size());
803 
804  // erase thousands separator
805  if (thousands_sep != '\0')
806  {
807  const auto end = std::remove(number_buffer.begin(),
808  number_buffer.begin() + len, thousands_sep);
809  std::fill(end, number_buffer.end(), '\0');
810  JSON_ASSERT((end - number_buffer.begin()) <= len);
811  len = (end - number_buffer.begin());
812  }
813 
814  // convert decimal point to '.'
815  if (decimal_point != '\0' && decimal_point != '.')
816  {
817  const auto dec_pos = std::find(number_buffer.begin(), number_buffer.end(), decimal_point);
818  if (dec_pos != number_buffer.end())
819  {
820  *dec_pos = '.';
821  }
822  }
823 
824  o->write_characters(number_buffer.data(), static_cast<std::size_t>(len));
825 
826  // determine if need to append ".0"
827  const bool value_is_int_like =
828  std::none_of(number_buffer.begin(), number_buffer.begin() + len + 1,
829  [](char c)
830  {
831  return c == '.' || c == 'e';
832  });
833 
834  if (value_is_int_like)
835  {
836  o->write_characters(".0", 2);
837  }
838  }
839 
861  static std::uint8_t decode(std::uint8_t& state, std::uint32_t& codep, const std::uint8_t byte) noexcept
862  {
863  static const std::array<std::uint8_t, 400> utf8d =
864  {
865  {
866  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
867  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
868  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
869  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
870  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
871  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
872  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
873  0xA, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, // E0..EF
874  0xB, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, // F0..FF
875  0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0
876  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
877  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
878  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
879  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
880  }
881  };
882 
883  const std::uint8_t type = utf8d[byte];
884 
885  codep = (state != UTF8_ACCEPT)
886  ? (byte & 0x3fu) | (codep << 6u)
887  : (0xFFu >> type) & (byte);
888 
889  std::size_t index = 256u + static_cast<size_t>(state) * 16u + static_cast<size_t>(type);
890  JSON_ASSERT(index < 400);
891  state = utf8d[index];
892  return state;
893  }
894 
895  /*
896  * Overload to make the compiler happy while it is instantiating
897  * dump_integer for number_unsigned_t.
898  * Must never be called.
899  */
900  number_unsigned_t remove_sign(number_unsigned_t x)
901  {
902  JSON_ASSERT(false); // LCOV_EXCL_LINE
903  return x; // LCOV_EXCL_LINE
904  }
905 
906  /*
907  * Helper function for dump_integer
908  *
909  * This function takes a negative signed integer and returns its absolute
910  * value as unsigned integer. The plus/minus shuffling is necessary as we can
911  * not directly remove the sign of an arbitrary signed integer as the
912  * absolute values of INT_MIN and INT_MAX are usually not the same. See
913  * #1708 for details.
914  */
915  inline number_unsigned_t remove_sign(number_integer_t x) noexcept
916  {
917  JSON_ASSERT(x < 0 && x < (std::numeric_limits<number_integer_t>::max)());
918  return static_cast<number_unsigned_t>(-(x + 1)) + 1;
919  }
920 
921  private:
923  output_adapter_t<char> o = nullptr;
924 
926  std::array<char, 64> number_buffer{{}};
927 
929  const std::lconv* loc = nullptr;
931  const char thousands_sep = '\0';
933  const char decimal_point = '\0';
934 
936  std::array<char, 512> string_buffer{{}};
937 
939  const char indent_char;
941  string_t indent_string;
942 
944  const error_handler_t error_handler;
945 };
946 } // namespace detail
947 } // namespace nlohmann
Definition: serializer.hpp:41
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
serializer(output_adapter_t< char > s, const char ichar, error_handler_t error_handler_=error_handler_t::strict)
Definition: serializer.hpp:56
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:27
namespace for Niels Lohmann
Definition: adl_serializer.hpp:9