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
25namespace nlohmann
26{
27namespace detail
28{
29///////////////////
30// serialization //
31///////////////////
32
33/// how to treat decoding errors
34enum 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
41template<typename BasicJsonType>
42class 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 */
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 */
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
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 */
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_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 {
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 */
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>
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
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 */
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
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 */
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:
841 /// the output of the serializer
842 output_adapter_t<char> o = nullptr;
843
844 /// a (hopefully) large enough character buffer
845 std::array<char, 64> number_buffer{{}};
846
847 /// the locale
848 const std::lconv* loc = nullptr;
849 /// the locale's thousand separator character
850 const char thousands_sep = '\0';
851 /// the locale's decimal point character
852 const char decimal_point = '\0';
853
854 /// string buffer
855 std::array<char, 512> string_buffer{{}};
856
857 /// the indentation character
858 const char indent_char;
859 /// the indentation string
860 string_t indent_string;
861
862 /// error_handler how to react on decoding errors
863 const error_handler_t error_handler;
864};
865} // namespace detail
866} // namespace nlohmann
867