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