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