Branch data Line data Source code
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 : : */
57 : 49 : serializer(output_adapter_t<char> s, const char ichar,
58 : : error_handler_t error_handler_ = error_handler_t::strict)
59 : 49 : : o(std::move(s))
60 : 49 : , loc(std::localeconv())
61 : 49 : , thousands_sep(loc->thousands_sep == nullptr ? '\0' : * (loc->thousands_sep))
62 : 49 : , decimal_point(loc->decimal_point == nullptr ? '\0' : * (loc->decimal_point))
63 : 49 : , indent_char(ichar)
64 : 49 : , indent_string(512, indent_char)
65 : 49 : , error_handler(error_handler_)
66 : 49 : {}
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 : 49 : ~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 : 7974 : 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 : 7974 : switch (val.m_type)
98 : : {
99 : : case value_t::object:
100 : : {
101 : 53 : if (val.m_value.object->empty())
102 : : {
103 : 0 : o->write_characters("{}", 2);
104 : 0 : return;
105 : : }
106 : :
107 : 53 : if (pretty_print)
108 : : {
109 : 0 : o->write_characters("{\n", 2);
110 : :
111 : : // variable to hold indentation for recursive calls
112 : 0 : const auto new_indent = current_indent + indent_step;
113 : 0 : if (JSON_UNLIKELY(indent_string.size() < new_indent))
114 : : {
115 : 0 : indent_string.resize(indent_string.size() * 2, ' ');
116 : 0 : }
117 : :
118 : : // first n-1 elements
119 : 0 : auto i = val.m_value.object->cbegin();
120 : 0 : for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)
121 : : {
122 : 0 : o->write_characters(indent_string.c_str(), new_indent);
123 : 0 : o->write_character('\"');
124 : 0 : dump_escaped(i->first, ensure_ascii);
125 : 0 : o->write_characters("\": ", 3);
126 : 0 : dump(i->second, true, ensure_ascii, indent_step, new_indent);
127 : 0 : o->write_characters(",\n", 2);
128 : 0 : }
129 : :
130 : : // last element
131 : 0 : assert(i != val.m_value.object->cend());
132 : 0 : assert(std::next(i) == val.m_value.object->cend());
133 : 0 : o->write_characters(indent_string.c_str(), new_indent);
134 : 0 : o->write_character('\"');
135 : 0 : dump_escaped(i->first, ensure_ascii);
136 : 0 : o->write_characters("\": ", 3);
137 : 0 : dump(i->second, true, ensure_ascii, indent_step, new_indent);
138 : :
139 : 0 : o->write_character('\n');
140 : 0 : o->write_characters(indent_string.c_str(), current_indent);
141 : 0 : o->write_character('}');
142 : 0 : }
143 : : else
144 : : {
145 : 53 : o->write_character('{');
146 : :
147 : : // first n-1 elements
148 : 53 : auto i = val.m_value.object->cbegin();
149 : 106 : for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)
150 : : {
151 : 53 : o->write_character('\"');
152 : 53 : dump_escaped(i->first, ensure_ascii);
153 : 53 : o->write_characters("\":", 2);
154 : 53 : dump(i->second, false, ensure_ascii, indent_step, current_indent);
155 : 53 : o->write_character(',');
156 : 53 : }
157 : :
158 : : // last element
159 : 53 : assert(i != val.m_value.object->cend());
160 : 53 : assert(std::next(i) == val.m_value.object->cend());
161 : 53 : o->write_character('\"');
162 : 53 : dump_escaped(i->first, ensure_ascii);
163 : 53 : o->write_characters("\":", 2);
164 : 53 : dump(i->second, false, ensure_ascii, indent_step, current_indent);
165 : :
166 : 53 : o->write_character('}');
167 : : }
168 : :
169 : 53 : return;
170 : : }
171 : :
172 : : case value_t::array:
173 : : {
174 : 2357 : if (val.m_value.array->empty())
175 : : {
176 : 12 : o->write_characters("[]", 2);
177 : 12 : return;
178 : : }
179 : :
180 : 2345 : if (pretty_print)
181 : : {
182 : 0 : o->write_characters("[\n", 2);
183 : :
184 : : // variable to hold indentation for recursive calls
185 : 0 : const auto new_indent = current_indent + indent_step;
186 : 0 : if (JSON_UNLIKELY(indent_string.size() < new_indent))
187 : : {
188 : 0 : indent_string.resize(indent_string.size() * 2, ' ');
189 : 0 : }
190 : :
191 : : // first n-1 elements
192 : 0 : for (auto i = val.m_value.array->cbegin();
193 : 0 : i != val.m_value.array->cend() - 1; ++i)
194 : : {
195 : 0 : o->write_characters(indent_string.c_str(), new_indent);
196 : 0 : dump(*i, true, ensure_ascii, indent_step, new_indent);
197 : 0 : o->write_characters(",\n", 2);
198 : 0 : }
199 : :
200 : : // last element
201 : 0 : assert(not val.m_value.array->empty());
202 : 0 : o->write_characters(indent_string.c_str(), new_indent);
203 : 0 : dump(val.m_value.array->back(), true, ensure_ascii, indent_step, new_indent);
204 : :
205 : 0 : o->write_character('\n');
206 : 0 : o->write_characters(indent_string.c_str(), current_indent);
207 : 0 : o->write_character(']');
208 : 0 : }
209 : : else
210 : : {
211 : 2345 : o->write_character('[');
212 : :
213 : : // first n-1 elements
214 : 7819 : for (auto i = val.m_value.array->cbegin();
215 : 7819 : i != val.m_value.array->cend() - 1; ++i)
216 : : {
217 : 5474 : dump(*i, false, ensure_ascii, indent_step, current_indent);
218 : 5474 : o->write_character(',');
219 : 5474 : }
220 : :
221 : : // last element
222 : 2345 : assert(not val.m_value.array->empty());
223 : 2345 : dump(val.m_value.array->back(), false, ensure_ascii, indent_step, current_indent);
224 : :
225 : 2345 : o->write_character(']');
226 : : }
227 : :
228 : 2345 : return;
229 : : }
230 : :
231 : : case value_t::string:
232 : : {
233 : 53 : o->write_character('\"');
234 : 53 : dump_escaped(*val.m_value.string, ensure_ascii);
235 : 53 : o->write_character('\"');
236 : 53 : return;
237 : : }
238 : :
239 : : case value_t::boolean:
240 : : {
241 : 0 : if (val.m_value.boolean)
242 : : {
243 : 0 : o->write_characters("true", 4);
244 : 0 : }
245 : : else
246 : : {
247 : 0 : o->write_characters("false", 5);
248 : : }
249 : 0 : return;
250 : : }
251 : :
252 : : case value_t::number_integer:
253 : : {
254 : 0 : dump_integer(val.m_value.number_integer);
255 : 0 : return;
256 : : }
257 : :
258 : : case value_t::number_unsigned:
259 : : {
260 : 0 : dump_integer(val.m_value.number_unsigned);
261 : 0 : return;
262 : : }
263 : :
264 : : case value_t::number_float:
265 : : {
266 : 5510 : dump_float(val.m_value.number_float);
267 : 5510 : return;
268 : : }
269 : :
270 : : case value_t::discarded:
271 : : {
272 : 0 : o->write_characters("<discarded>", 11);
273 : 0 : return;
274 : : }
275 : :
276 : : case value_t::null:
277 : : {
278 : 1 : o->write_characters("null", 4);
279 : 1 : return;
280 : : }
281 : :
282 : : default: // LCOV_EXCL_LINE
283 : : assert(false); // LCOV_EXCL_LINE
284 : : }
285 : 7974 : }
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 : 159 : void dump_escaped(const string_t& s, const bool ensure_ascii)
303 : : {
304 : : std::uint32_t codepoint;
305 : 159 : std::uint8_t state = UTF8_ACCEPT;
306 : 159 : 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 : 159 : std::size_t bytes_after_last_accept = 0;
310 : 159 : std::size_t undumped_chars = 0;
311 : :
312 : 1510 : for (std::size_t i = 0; i < s.size(); ++i)
313 : : {
314 : 1351 : const auto byte = static_cast<uint8_t>(s[i]);
315 : :
316 : 1351 : switch (decode(state, codepoint, byte))
317 : : {
318 : : case UTF8_ACCEPT: // decode found a new code point
319 : : {
320 : 1351 : switch (codepoint)
321 : : {
322 : : case 0x08: // backspace
323 : : {
324 : 0 : string_buffer[bytes++] = '\\';
325 : 0 : string_buffer[bytes++] = 'b';
326 : 0 : break;
327 : : }
328 : :
329 : : case 0x09: // horizontal tab
330 : : {
331 : 0 : string_buffer[bytes++] = '\\';
332 : 0 : string_buffer[bytes++] = 't';
333 : 0 : break;
334 : : }
335 : :
336 : : case 0x0A: // newline
337 : : {
338 : 0 : string_buffer[bytes++] = '\\';
339 : 0 : string_buffer[bytes++] = 'n';
340 : 0 : break;
341 : : }
342 : :
343 : : case 0x0C: // formfeed
344 : : {
345 : 0 : string_buffer[bytes++] = '\\';
346 : 0 : string_buffer[bytes++] = 'f';
347 : 0 : break;
348 : : }
349 : :
350 : : case 0x0D: // carriage return
351 : : {
352 : 0 : string_buffer[bytes++] = '\\';
353 : 0 : string_buffer[bytes++] = 'r';
354 : 0 : break;
355 : : }
356 : :
357 : : case 0x22: // quotation mark
358 : : {
359 : 0 : string_buffer[bytes++] = '\\';
360 : 0 : string_buffer[bytes++] = '\"';
361 : 0 : break;
362 : : }
363 : :
364 : : case 0x5C: // reverse solidus
365 : : {
366 : 0 : string_buffer[bytes++] = '\\';
367 : 0 : string_buffer[bytes++] = '\\';
368 : 0 : break;
369 : : }
370 : :
371 : : default:
372 : : {
373 : : // escape control characters (0x00..0x1F) or, if
374 : : // ensure_ascii parameter is used, non-ASCII characters
375 : 1351 : if ((codepoint <= 0x1F) or (ensure_ascii and (codepoint >= 0x7F)))
376 : : {
377 : 0 : if (codepoint <= 0xFFFF)
378 : : {
379 : 0 : (std::snprintf)(string_buffer.data() + bytes, 7, "\\u%04x",
380 : 0 : static_cast<std::uint16_t>(codepoint));
381 : 0 : bytes += 6;
382 : 0 : }
383 : : else
384 : : {
385 : 0 : (std::snprintf)(string_buffer.data() + bytes, 13, "\\u%04x\\u%04x",
386 : 0 : static_cast<std::uint16_t>(0xD7C0u + (codepoint >> 10u)),
387 : 0 : static_cast<std::uint16_t>(0xDC00u + (codepoint & 0x3FFu)));
388 : 0 : bytes += 12;
389 : : }
390 : 0 : }
391 : : else
392 : : {
393 : : // copy byte to buffer (all previous bytes
394 : : // been copied have in default case above)
395 : 1351 : string_buffer[bytes++] = s[i];
396 : : }
397 : 1351 : 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 : 1351 : if (string_buffer.size() - bytes < 13)
405 : : {
406 : 0 : o->write_characters(string_buffer.data(), bytes);
407 : 0 : bytes = 0;
408 : 0 : }
409 : :
410 : : // remember the byte position of this accept
411 : 1351 : bytes_after_last_accept = bytes;
412 : 1351 : undumped_chars = 0;
413 : 1351 : break;
414 : : }
415 : :
416 : : case UTF8_REJECT: // decode found invalid UTF-8 byte
417 : : {
418 : 0 : switch (error_handler)
419 : : {
420 : : case error_handler_t::strict:
421 : : {
422 : 0 : std::string sn(3, '\0');
423 : 0 : (std::snprintf)(&sn[0], sn.size(), "%.2X", byte);
424 : 0 : JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + sn));
425 : 0 : }
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 : 0 : if (undumped_chars > 0)
435 : : {
436 : 0 : --i;
437 : 0 : }
438 : :
439 : : // reset length buffer to the last accepted index;
440 : : // thus removing/ignoring the invalid characters
441 : 0 : bytes = bytes_after_last_accept;
442 : :
443 : 0 : if (error_handler == error_handler_t::replace)
444 : : {
445 : : // add a replacement character
446 : 0 : if (ensure_ascii)
447 : : {
448 : 0 : string_buffer[bytes++] = '\\';
449 : 0 : string_buffer[bytes++] = 'u';
450 : 0 : string_buffer[bytes++] = 'f';
451 : 0 : string_buffer[bytes++] = 'f';
452 : 0 : string_buffer[bytes++] = 'f';
453 : 0 : string_buffer[bytes++] = 'd';
454 : 0 : }
455 : : else
456 : : {
457 : 0 : string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\xEF');
458 : 0 : string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\xBF');
459 : 0 : 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 : 0 : if (string_buffer.size() - bytes < 13)
466 : : {
467 : 0 : o->write_characters(string_buffer.data(), bytes);
468 : 0 : bytes = 0;
469 : 0 : }
470 : :
471 : 0 : bytes_after_last_accept = bytes;
472 : 0 : }
473 : :
474 : 0 : undumped_chars = 0;
475 : :
476 : : // continue processing the string
477 : 0 : state = UTF8_ACCEPT;
478 : 0 : break;
479 : : }
480 : :
481 : : default: // LCOV_EXCL_LINE
482 : : assert(false); // LCOV_EXCL_LINE
483 : : }
484 : 0 : break;
485 : : }
486 : :
487 : : default: // decode found yet incomplete multi-byte code point
488 : : {
489 : 0 : if (not ensure_ascii)
490 : : {
491 : : // code point will not be escaped - copy byte to buffer
492 : 0 : string_buffer[bytes++] = s[i];
493 : 0 : }
494 : 0 : ++undumped_chars;
495 : 0 : break;
496 : : }
497 : : }
498 : 1351 : }
499 : :
500 : : // we finished processing the string
501 : 159 : if (JSON_LIKELY(state == UTF8_ACCEPT))
502 : : {
503 : : // write buffer
504 : 159 : if (bytes > 0)
505 : : {
506 : 159 : o->write_characters(string_buffer.data(), bytes);
507 : 159 : }
508 : 159 : }
509 : : else
510 : : {
511 : : // we finish reading, but do not accept: string was incomplete
512 : 0 : switch (error_handler)
513 : : {
514 : : case error_handler_t::strict:
515 : : {
516 : 0 : std::string sn(3, '\0');
517 : 0 : (std::snprintf)(&sn[0], sn.size(), "%.2X", static_cast<std::uint8_t>(s.back()));
518 : 0 : JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + sn));
519 : 0 : }
520 : :
521 : : case error_handler_t::ignore:
522 : : {
523 : : // write all accepted bytes
524 : 0 : o->write_characters(string_buffer.data(), bytes_after_last_accept);
525 : 0 : break;
526 : : }
527 : :
528 : : case error_handler_t::replace:
529 : : {
530 : : // write all accepted bytes
531 : 0 : o->write_characters(string_buffer.data(), bytes_after_last_accept);
532 : : // add a replacement character
533 : 0 : if (ensure_ascii)
534 : : {
535 : 0 : o->write_characters("\\ufffd", 6);
536 : 0 : }
537 : : else
538 : : {
539 : 0 : o->write_characters("\xEF\xBF\xBD", 3);
540 : : }
541 : 0 : break;
542 : : }
543 : :
544 : : default: // LCOV_EXCL_LINE
545 : : assert(false); // LCOV_EXCL_LINE
546 : : }
547 : : }
548 : 159 : }
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 : 0 : inline unsigned int count_digits(number_unsigned_t x) noexcept
559 : : {
560 : 0 : unsigned int n_digits = 1;
561 : 0 : for (;;)
562 : : {
563 : 0 : if (x < 10)
564 : : {
565 : 0 : return n_digits;
566 : : }
567 : 0 : if (x < 100)
568 : : {
569 : 0 : return n_digits + 1;
570 : : }
571 : 0 : if (x < 1000)
572 : : {
573 : 0 : return n_digits + 2;
574 : : }
575 : 0 : if (x < 10000)
576 : : {
577 : 0 : return n_digits + 3;
578 : : }
579 : 0 : x = x / 10000u;
580 : 0 : n_digits += 4;
581 : : }
582 : 0 : }
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 : 0 : 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 : 0 : if (x == 0)
617 : : {
618 : 0 : o->write_character('0');
619 : 0 : return;
620 : : }
621 : :
622 : : // use a pointer to fill the buffer
623 : 0 : auto buffer_ptr = number_buffer.begin();
624 : :
625 : 0 : 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 : 0 : if (is_negative)
631 : : {
632 : 0 : *buffer_ptr = '-';
633 : 0 : 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 : 0 : n_chars = 1 + count_digits(abs_value);
637 : 0 : }
638 : : else
639 : : {
640 : 0 : abs_value = static_cast<number_unsigned_t>(x);
641 : 0 : n_chars = count_digits(abs_value);
642 : : }
643 : :
644 : : // spare 1 byte for '\0'
645 : 0 : 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 : 0 : 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 : 0 : while (abs_value >= 100)
654 : : {
655 : 0 : const auto digits_index = static_cast<unsigned>((abs_value % 100));
656 : 0 : abs_value /= 100;
657 : 0 : *(--buffer_ptr) = digits_to_99[digits_index][1];
658 : 0 : *(--buffer_ptr) = digits_to_99[digits_index][0];
659 : : }
660 : :
661 : 0 : if (abs_value >= 10)
662 : : {
663 : 0 : const auto digits_index = static_cast<unsigned>(abs_value);
664 : 0 : *(--buffer_ptr) = digits_to_99[digits_index][1];
665 : 0 : *(--buffer_ptr) = digits_to_99[digits_index][0];
666 : 0 : }
667 : : else
668 : : {
669 : 0 : *(--buffer_ptr) = static_cast<char>('0' + abs_value);
670 : : }
671 : :
672 : 0 : o->write_characters(number_buffer.data(), n_chars);
673 : 0 : }
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 : 5510 : void dump_float(number_float_t x)
684 : : {
685 : : // NaN / inf
686 : 5510 : if (not std::isfinite(x))
687 : : {
688 : 0 : o->write_characters("null", 4);
689 : 0 : 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 : 5510 : dump_float(x, std::integral_constant<bool, is_ieee_single_or_double>());
702 : 5510 : }
703 : :
704 : 5510 : void dump_float(number_float_t x, std::true_type /*is_ieee_single_or_double*/)
705 : : {
706 : 5510 : char* begin = number_buffer.data();
707 : 5510 : char* end = ::nlohmann::detail::to_chars(begin, begin + number_buffer.size(), x);
708 : :
709 : 5510 : o->write_characters(begin, static_cast<size_t>(end - begin));
710 : 5510 : }
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 : 1351 : 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 : 1351 : const std::uint8_t type = utf8d[byte];
805 : :
806 : 1351 : codep = (state != UTF8_ACCEPT)
807 : 0 : ? (byte & 0x3fu) | (codep << 6u)
808 : 1351 : : (0xFFu >> type) & (byte);
809 : :
810 : 1351 : state = utf8d[256u + state * 16u + type];
811 : 1351 : 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 : 49 : 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 : 49 : 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
|