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