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