1// __ _____ _____ _____ 2// __| | __| | | | JSON for Modern C++ (supporting code) 3// | | |__ | | | | | | version 3.11.2 4// |_____|_____|_____|_|___| https://github.com/nlohmann/json 5// 6// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me> 7// SPDX-License-Identifier: MIT 8 9#include "doctest_compatibility.h" 10 11// for some reason including this after the json header leads to linker errors with VS 2017... 12#include <locale> 13 14#include <nlohmann/json.hpp> 15using nlohmann::json; 16 17#include <fstream> 18#include <sstream> 19#include <iostream> 20#include <iomanip> 21#include "make_test_data_available.hpp" 22 23// this test suite uses static variables with non-trivial destructors 24DOCTEST_CLANG_SUPPRESS_WARNING_PUSH 25DOCTEST_CLANG_SUPPRESS_WARNING("-Wexit-time-destructors") 26 27namespace 28{ 29extern size_t calls; 30size_t calls = 0; 31 32void check_utf8dump(bool success_expected, int byte1, int byte2, int byte3, int byte4); 33 34void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3 = -1, int byte4 = -1) 35{ 36 static std::string json_string; 37 json_string.clear(); 38 39 CAPTURE(byte1) 40 CAPTURE(byte2) 41 CAPTURE(byte3) 42 CAPTURE(byte4) 43 44 json_string += std::string(1, static_cast<char>(byte1)); 45 46 if (byte2 != -1) 47 { 48 json_string += std::string(1, static_cast<char>(byte2)); 49 } 50 51 if (byte3 != -1) 52 { 53 json_string += std::string(1, static_cast<char>(byte3)); 54 } 55 56 if (byte4 != -1) 57 { 58 json_string += std::string(1, static_cast<char>(byte4)); 59 } 60 61 CAPTURE(json_string) 62 63 // store the string in a JSON value 64 static json j; 65 static json j2; 66 j = json_string; 67 j2 = "abc" + json_string + "xyz"; 68 69 static std::string s_ignored; 70 static std::string s_ignored2; 71 static std::string s_ignored_ascii; 72 static std::string s_ignored2_ascii; 73 static std::string s_replaced; 74 static std::string s_replaced2; 75 static std::string s_replaced_ascii; 76 static std::string s_replaced2_ascii; 77 78 // dumping with ignore/replace must not throw in any case 79 s_ignored = j.dump(-1, ' ', false, json::error_handler_t::ignore); 80 s_ignored2 = j2.dump(-1, ' ', false, json::error_handler_t::ignore); 81 s_ignored_ascii = j.dump(-1, ' ', true, json::error_handler_t::ignore); 82 s_ignored2_ascii = j2.dump(-1, ' ', true, json::error_handler_t::ignore); 83 s_replaced = j.dump(-1, ' ', false, json::error_handler_t::replace); 84 s_replaced2 = j2.dump(-1, ' ', false, json::error_handler_t::replace); 85 s_replaced_ascii = j.dump(-1, ' ', true, json::error_handler_t::replace); 86 s_replaced2_ascii = j2.dump(-1, ' ', true, json::error_handler_t::replace); 87 88 if (success_expected) 89 { 90 static std::string s_strict; 91 // strict mode must not throw if success is expected 92 s_strict = j.dump(); 93 // all dumps should agree on the string 94 CHECK(s_strict == s_ignored); 95 CHECK(s_strict == s_replaced); 96 } 97 else 98 { 99 // strict mode must throw if success is not expected 100 CHECK_THROWS_AS(j.dump(), json::type_error&); 101 // ignore and replace must create different dumps 102 CHECK(s_ignored != s_replaced); 103 104 // check that replace string contains a replacement character 105 CHECK(s_replaced.find("\xEF\xBF\xBD") != std::string::npos); 106 } 107 108 // check that prefix and suffix are preserved 109 CHECK(s_ignored2.substr(1, 3) == "abc"); 110 CHECK(s_ignored2.substr(s_ignored2.size() - 4, 3) == "xyz"); 111 CHECK(s_ignored2_ascii.substr(1, 3) == "abc"); 112 CHECK(s_ignored2_ascii.substr(s_ignored2_ascii.size() - 4, 3) == "xyz"); 113 CHECK(s_replaced2.substr(1, 3) == "abc"); 114 CHECK(s_replaced2.substr(s_replaced2.size() - 4, 3) == "xyz"); 115 CHECK(s_replaced2_ascii.substr(1, 3) == "abc"); 116 CHECK(s_replaced2_ascii.substr(s_replaced2_ascii.size() - 4, 3) == "xyz"); 117} 118 119void check_utf8string(bool success_expected, int byte1, int byte2, int byte3, int byte4); 120 121// create and check a JSON string with up to four UTF-8 bytes 122void check_utf8string(bool success_expected, int byte1, int byte2 = -1, int byte3 = -1, int byte4 = -1) 123{ 124 if (++calls % 100000 == 0) 125 { 126 std::cout << calls << " of 455355 UTF-8 strings checked" << std::endl; 127 } 128 129 static std::string json_string; 130 json_string = "\""; 131 132 CAPTURE(byte1) 133 json_string += std::string(1, static_cast<char>(byte1)); 134 135 if (byte2 != -1) 136 { 137 CAPTURE(byte2) 138 json_string += std::string(1, static_cast<char>(byte2)); 139 } 140 141 if (byte3 != -1) 142 { 143 CAPTURE(byte3) 144 json_string += std::string(1, static_cast<char>(byte3)); 145 } 146 147 if (byte4 != -1) 148 { 149 CAPTURE(byte4) 150 json_string += std::string(1, static_cast<char>(byte4)); 151 } 152 153 json_string += "\""; 154 155 CAPTURE(json_string) 156 157 json _; 158 if (success_expected) 159 { 160 CHECK_NOTHROW(_ = json::parse(json_string)); 161 } 162 else 163 { 164 CHECK_THROWS_AS(_ = json::parse(json_string), json::parse_error&); 165 } 166} 167} // namespace 168 169TEST_CASE("Unicode (2/5)" * doctest::skip()) 170{ 171 SECTION("RFC 3629") 172 { 173 /* 174 RFC 3629 describes in Sect. 4 the syntax of UTF-8 byte sequences as 175 follows: 176 177 A UTF-8 string is a sequence of octets representing a sequence of UCS 178 characters. An octet sequence is valid UTF-8 only if it matches the 179 following syntax, which is derived from the rules for encoding UTF-8 180 and is expressed in the ABNF of [RFC2234]. 181 182 UTF8-octets = *( UTF8-char ) 183 UTF8-char = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4 184 UTF8-1 = %x00-7F 185 UTF8-2 = %xC2-DF UTF8-tail 186 UTF8-3 = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) / 187 %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail ) 188 UTF8-4 = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) / 189 %xF4 %x80-8F 2( UTF8-tail ) 190 UTF8-tail = %x80-BF 191 */ 192 193 SECTION("ill-formed first byte") 194 { 195 for (int byte1 = 0x80; byte1 <= 0xC1; ++byte1) 196 { 197 check_utf8string(false, byte1); 198 check_utf8dump(false, byte1); 199 } 200 201 for (int byte1 = 0xF5; byte1 <= 0xFF; ++byte1) 202 { 203 check_utf8string(false, byte1); 204 check_utf8dump(false, byte1); 205 } 206 } 207 208 SECTION("UTF8-1 (x00-x7F)") 209 { 210 SECTION("well-formed") 211 { 212 for (int byte1 = 0x00; byte1 <= 0x7F; ++byte1) 213 { 214 // unescaped control characters are parse errors in JSON 215 if (0x00 <= byte1 && byte1 <= 0x1F) 216 { 217 check_utf8string(false, byte1); 218 continue; 219 } 220 221 // a single quote is a parse error in JSON 222 if (byte1 == 0x22) 223 { 224 check_utf8string(false, byte1); 225 continue; 226 } 227 228 // a single backslash is a parse error in JSON 229 if (byte1 == 0x5C) 230 { 231 check_utf8string(false, byte1); 232 continue; 233 } 234 235 // all other characters are OK 236 check_utf8string(true, byte1); 237 check_utf8dump(true, byte1); 238 } 239 } 240 } 241 242 SECTION("UTF8-2 (xC2-xDF UTF8-tail)") 243 { 244 SECTION("well-formed") 245 { 246 for (int byte1 = 0xC2; byte1 <= 0xDF; ++byte1) 247 { 248 for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2) 249 { 250 check_utf8string(true, byte1, byte2); 251 check_utf8dump(true, byte1, byte2); 252 } 253 } 254 } 255 256 SECTION("ill-formed: missing second byte") 257 { 258 for (int byte1 = 0xC2; byte1 <= 0xDF; ++byte1) 259 { 260 check_utf8string(false, byte1); 261 check_utf8dump(false, byte1); 262 } 263 } 264 265 SECTION("ill-formed: wrong second byte") 266 { 267 for (int byte1 = 0xC2; byte1 <= 0xDF; ++byte1) 268 { 269 for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2) 270 { 271 // skip correct second byte 272 if (0x80 <= byte2 && byte2 <= 0xBF) 273 { 274 continue; 275 } 276 277 check_utf8string(false, byte1, byte2); 278 check_utf8dump(false, byte1, byte2); 279 } 280 } 281 } 282 } 283 284 SECTION("UTF8-3 (xE0 xA0-BF UTF8-tail)") 285 { 286 SECTION("well-formed") 287 { 288 for (int byte1 = 0xE0; byte1 <= 0xE0; ++byte1) 289 { 290 for (int byte2 = 0xA0; byte2 <= 0xBF; ++byte2) 291 { 292 for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3) 293 { 294 check_utf8string(true, byte1, byte2, byte3); 295 check_utf8dump(true, byte1, byte2, byte3); 296 } 297 } 298 } 299 } 300 301 SECTION("ill-formed: missing second byte") 302 { 303 for (int byte1 = 0xE0; byte1 <= 0xE0; ++byte1) 304 { 305 check_utf8string(false, byte1); 306 check_utf8dump(false, byte1); 307 } 308 } 309 310 SECTION("ill-formed: missing third byte") 311 { 312 for (int byte1 = 0xE0; byte1 <= 0xE0; ++byte1) 313 { 314 for (int byte2 = 0xA0; byte2 <= 0xBF; ++byte2) 315 { 316 check_utf8string(false, byte1, byte2); 317 check_utf8dump(false, byte1, byte2); 318 } 319 } 320 } 321 322 SECTION("ill-formed: wrong second byte") 323 { 324 for (int byte1 = 0xE0; byte1 <= 0xE0; ++byte1) 325 { 326 for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2) 327 { 328 // skip correct second byte 329 if (0xA0 <= byte2 && byte2 <= 0xBF) 330 { 331 continue; 332 } 333 334 for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3) 335 { 336 check_utf8string(false, byte1, byte2, byte3); 337 check_utf8dump(false, byte1, byte2, byte3); 338 } 339 } 340 } 341 } 342 343 SECTION("ill-formed: wrong third byte") 344 { 345 for (int byte1 = 0xE0; byte1 <= 0xE0; ++byte1) 346 { 347 for (int byte2 = 0xA0; byte2 <= 0xBF; ++byte2) 348 { 349 for (int byte3 = 0x00; byte3 <= 0xFF; ++byte3) 350 { 351 // skip correct third byte 352 if (0x80 <= byte3 && byte3 <= 0xBF) 353 { 354 continue; 355 } 356 357 check_utf8string(false, byte1, byte2, byte3); 358 check_utf8dump(false, byte1, byte2, byte3); 359 } 360 } 361 } 362 } 363 } 364 365 SECTION("UTF8-3 (xE1-xEC UTF8-tail UTF8-tail)") 366 { 367 SECTION("well-formed") 368 { 369 for (int byte1 = 0xE1; byte1 <= 0xEC; ++byte1) 370 { 371 for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2) 372 { 373 for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3) 374 { 375 check_utf8string(true, byte1, byte2, byte3); 376 check_utf8dump(true, byte1, byte2, byte3); 377 } 378 } 379 } 380 } 381 382 SECTION("ill-formed: missing second byte") 383 { 384 for (int byte1 = 0xE1; byte1 <= 0xEC; ++byte1) 385 { 386 check_utf8string(false, byte1); 387 check_utf8dump(false, byte1); 388 } 389 } 390 391 SECTION("ill-formed: missing third byte") 392 { 393 for (int byte1 = 0xE1; byte1 <= 0xEC; ++byte1) 394 { 395 for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2) 396 { 397 check_utf8string(false, byte1, byte2); 398 check_utf8dump(false, byte1, byte2); 399 } 400 } 401 } 402 403 SECTION("ill-formed: wrong second byte") 404 { 405 for (int byte1 = 0xE1; byte1 <= 0xEC; ++byte1) 406 { 407 for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2) 408 { 409 // skip correct second byte 410 if (0x80 <= byte2 && byte2 <= 0xBF) 411 { 412 continue; 413 } 414 415 for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3) 416 { 417 check_utf8string(false, byte1, byte2, byte3); 418 check_utf8dump(false, byte1, byte2, byte3); 419 } 420 } 421 } 422 } 423 424 SECTION("ill-formed: wrong third byte") 425 { 426 for (int byte1 = 0xE1; byte1 <= 0xEC; ++byte1) 427 { 428 for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2) 429 { 430 for (int byte3 = 0x00; byte3 <= 0xFF; ++byte3) 431 { 432 // skip correct third byte 433 if (0x80 <= byte3 && byte3 <= 0xBF) 434 { 435 continue; 436 } 437 438 check_utf8string(false, byte1, byte2, byte3); 439 check_utf8dump(false, byte1, byte2, byte3); 440 } 441 } 442 } 443 } 444 } 445 446 SECTION("UTF8-3 (xED x80-9F UTF8-tail)") 447 { 448 SECTION("well-formed") 449 { 450 for (int byte1 = 0xED; byte1 <= 0xED; ++byte1) 451 { 452 for (int byte2 = 0x80; byte2 <= 0x9F; ++byte2) 453 { 454 for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3) 455 { 456 check_utf8string(true, byte1, byte2, byte3); 457 check_utf8dump(true, byte1, byte2, byte3); 458 } 459 } 460 } 461 } 462 463 SECTION("ill-formed: missing second byte") 464 { 465 for (int byte1 = 0xED; byte1 <= 0xED; ++byte1) 466 { 467 check_utf8string(false, byte1); 468 check_utf8dump(false, byte1); 469 } 470 } 471 472 SECTION("ill-formed: missing third byte") 473 { 474 for (int byte1 = 0xED; byte1 <= 0xED; ++byte1) 475 { 476 for (int byte2 = 0x80; byte2 <= 0x9F; ++byte2) 477 { 478 check_utf8string(false, byte1, byte2); 479 check_utf8dump(false, byte1, byte2); 480 } 481 } 482 } 483 484 SECTION("ill-formed: wrong second byte") 485 { 486 for (int byte1 = 0xED; byte1 <= 0xED; ++byte1) 487 { 488 for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2) 489 { 490 // skip correct second byte 491 if (0x80 <= byte2 && byte2 <= 0x9F) 492 { 493 continue; 494 } 495 496 for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3) 497 { 498 check_utf8string(false, byte1, byte2, byte3); 499 check_utf8dump(false, byte1, byte2, byte3); 500 } 501 } 502 } 503 } 504 505 SECTION("ill-formed: wrong third byte") 506 { 507 for (int byte1 = 0xED; byte1 <= 0xED; ++byte1) 508 { 509 for (int byte2 = 0x80; byte2 <= 0x9F; ++byte2) 510 { 511 for (int byte3 = 0x00; byte3 <= 0xFF; ++byte3) 512 { 513 // skip correct third byte 514 if (0x80 <= byte3 && byte3 <= 0xBF) 515 { 516 continue; 517 } 518 519 check_utf8string(false, byte1, byte2, byte3); 520 check_utf8dump(false, byte1, byte2, byte3); 521 } 522 } 523 } 524 } 525 } 526 527 SECTION("UTF8-3 (xEE-xEF UTF8-tail UTF8-tail)") 528 { 529 SECTION("well-formed") 530 { 531 for (int byte1 = 0xEE; byte1 <= 0xEF; ++byte1) 532 { 533 for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2) 534 { 535 for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3) 536 { 537 check_utf8string(true, byte1, byte2, byte3); 538 check_utf8dump(true, byte1, byte2, byte3); 539 } 540 } 541 } 542 } 543 544 SECTION("ill-formed: missing second byte") 545 { 546 for (int byte1 = 0xEE; byte1 <= 0xEF; ++byte1) 547 { 548 check_utf8string(false, byte1); 549 check_utf8dump(false, byte1); 550 } 551 } 552 553 SECTION("ill-formed: missing third byte") 554 { 555 for (int byte1 = 0xEE; byte1 <= 0xEF; ++byte1) 556 { 557 for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2) 558 { 559 check_utf8string(false, byte1, byte2); 560 check_utf8dump(false, byte1, byte2); 561 } 562 } 563 } 564 565 SECTION("ill-formed: wrong second byte") 566 { 567 for (int byte1 = 0xEE; byte1 <= 0xEF; ++byte1) 568 { 569 for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2) 570 { 571 // skip correct second byte 572 if (0x80 <= byte2 && byte2 <= 0xBF) 573 { 574 continue; 575 } 576 577 for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3) 578 { 579 check_utf8string(false, byte1, byte2, byte3); 580 check_utf8dump(false, byte1, byte2, byte3); 581 } 582 } 583 } 584 } 585 586 SECTION("ill-formed: wrong third byte") 587 { 588 for (int byte1 = 0xEE; byte1 <= 0xEF; ++byte1) 589 { 590 for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2) 591 { 592 for (int byte3 = 0x00; byte3 <= 0xFF; ++byte3) 593 { 594 // skip correct third byte 595 if (0x80 <= byte3 && byte3 <= 0xBF) 596 { 597 continue; 598 } 599 600 check_utf8string(false, byte1, byte2, byte3); 601 check_utf8dump(false, byte1, byte2, byte3); 602 } 603 } 604 } 605 } 606 } 607 } 608} 609 610DOCTEST_CLANG_SUPPRESS_WARNING_POP 611