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 {
29 ///////////////////
30 // serialization //
31 ///////////////////
32 
33 /// how to treat decoding errors
34 enum class error_handler_t
35 {
36     strict,  ///< throw a type_error exception in case of invalid UTF-8
37     replace, ///< replace invalid UTF-8 sequences with U+FFFD
38     ignore   ///< ignore invalid UTF-8 sequences
39 };
40 
41 template<typename BasicJsonType>
42 class serializer
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:
52     /*!
53     @param[in] s  output stream to serialize to
54     @param[in] ichar  indentation character to use
55     @param[in] error_handler_  how to react on decoding errors
56     */
serializer(output_adapter_t<char> s,const char ichar,error_handler_t error_handler_=error_handler_t::strict)57     serializer(output_adapter_t<char> s, const char ichar,
58                error_handler_t error_handler_ = error_handler_t::strict)
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 
75     /*!
76     @brief internal implementation of the serialization function
77 
78     This function is called by the public member function dump and organizes
79     the serialization internally. The indentation level is propagated as
80     additional parameter. In case of arrays and objects, the function is
81     called recursively.
82 
83     - strings and object keys are escaped using `escape_string()`
84     - integer numbers are converted implicitly via `operator<<`
85     - floating-point numbers are converted to a string using `"%g"` format
86 
87     @param[in] val             value to serialize
88     @param[in] pretty_print    whether the output shall be pretty-printed
89     @param[in] indent_step     the indent level
90     @param[in] current_indent  the current indent level (only used internally)
91     */
dump(const BasicJsonType & val,const bool pretty_print,const bool ensure_ascii,const unsigned int indent_step,const unsigned int current_indent=0)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_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_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 
252             case value_t::number_integer:
253             {
254                 dump_integer(val.m_value.number_integer);
255                 return;
256             }
257 
258             case value_t::number_unsigned:
259             {
260                 dump_integer(val.m_value.number_unsigned);
261                 return;
262             }
263 
264             case value_t::number_float:
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:
288     /*!
289     @brief dump escaped string
290 
291     Escape a string by replacing certain special characters by a sequence of an
292     escape character (backslash) and another character and other control
293     characters by a sequence of "\u" followed by a four-digit hex
294     representation. The escaped string is written to output stream @a o.
295 
296     @param[in] s  the string to escape
297     @param[in] ensure_ascii  whether to escape non-ASCII characters with
298                              \uXXXX sequences
299 
300     @complexity Linear in the length of string @a s.
301     */
dump_escaped(const string_t & s,const bool ensure_ascii)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                     {
420                         case error_handler_t::strict:
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 
427                         case error_handler_t::ignore:
428                         case error_handler_t::replace:
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_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             {
514                 case error_handler_t::strict:
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 
521                 case error_handler_t::ignore:
522                 {
523                     // write all accepted bytes
524                     o->write_characters(string_buffer.data(), bytes_after_last_accept);
525                     break;
526                 }
527 
528                 case error_handler_t::replace:
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 
550     /*!
551     @brief count digits
552 
553     Count the number of decimal (base 10) digits for an input unsigned integer.
554 
555     @param[in] x  unsigned integer number to count its digits
556     @return    number of decimal digits
557     */
count_digits(number_unsigned_t x)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 
584     /*!
585     @brief dump an integer
586 
587     Dump a given integer to output stream @a o. Works internally with
588     @a number_buffer.
589 
590     @param[in] x  integer number (signed or unsigned) to dump
591     @tparam NumberType either @a number_integer_t or @a number_unsigned_t
592     */
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>
dump_integer(NumberType x)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 = static_cast<number_unsigned_t>(std::abs(static_cast<std::intmax_t>(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 
675     /*!
676     @brief dump a floating-point number
677 
678     Dump a given floating-point number to output stream @a o. Works internally
679     with @a number_buffer.
680 
681     @param[in] x  floating-point number to dump
682     */
dump_float(number_float_t x)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 
dump_float(number_float_t x,std::true_type)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 
dump_float(number_float_t x,std::false_type)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 
761     /*!
762     @brief check whether a string is UTF-8 encoded
763 
764     The function checks each byte of a string whether it is UTF-8 encoded. The
765     result of the check is stored in the @a state parameter. The function must
766     be called initially with state 0 (accept). State 1 means the string must
767     be rejected, because the current byte is not allowed. If the string is
768     completely processed, but the state is non-zero, the string ended
769     prematurely; that is, the last byte indicated more bytes should have
770     followed.
771 
772     @param[in,out] state  the state of the decoding
773     @param[in,out] codep  codepoint (valid only if resulting state is UTF8_ACCEPT)
774     @param[in] byte       next byte to decode
775     @return               new state
776 
777     @note The function has been edited: a std::array is used.
778 
779     @copyright Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
780     @sa http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
781     */
decode(std::uint8_t & state,std::uint32_t & codep,const std::uint8_t byte)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   private:
815     /// the output of the serializer
816     output_adapter_t<char> o = nullptr;
817 
818     /// a (hopefully) large enough character buffer
819     std::array<char, 64> number_buffer{{}};
820 
821     /// the locale
822     const std::lconv* loc = nullptr;
823     /// the locale's thousand separator character
824     const char thousands_sep = '\0';
825     /// the locale's decimal point character
826     const char decimal_point = '\0';
827 
828     /// string buffer
829     std::array<char, 512> string_buffer{{}};
830 
831     /// the indentation character
832     const char indent_char;
833     /// the indentation string
834     string_t indent_string;
835 
836     /// error_handler how to react on decoding errors
837     const error_handler_t error_handler;
838 };
839 }  // namespace detail
840 }  // namespace nlohmann
841