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