• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

libbitcoin / libbitcoin-system / 39899818290

23 Oct 2026 04:20AM UTC coverage: 97.2% (-0.03%) from 97.234%
39899818290

push

github

web-flow
Merge pull request #2111 from evoskuil/master

Replace Boost.JSON with system json.

1247 of 1289 new or added lines in 17 files covered. (96.74%)

1 existing line in 1 file now uncovered.

21627 of 22250 relevant lines covered (97.2%)

2775774.21 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.46
/src/serial/json/parser.cpp
1
/**
2
 * Copyright (c) 2011-2026 libbitcoin developers
3
 *
4
 * This file is part of libbitcoin.
5
 *
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU Affero General Public License as published by
8
 * the Free Software Foundation, either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU Affero General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Affero General Public License
17
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
 */
19
#include <bitcoin/system/serial/json/parser.hpp>
20

21
#include <charconv>
22
#include <bitcoin/system/define.hpp>
23
#include <bitcoin/system/math/math.hpp>
24

25
namespace libbitcoin {
26
namespace system {
27
namespace json {
28

29
BC_PUSH_WARNING(NO_ARRAY_INDEXING)
30
BC_PUSH_WARNING(NO_POINTER_ARITHMETIC)
31

32
constexpr uint32_t high_surrogate_begin = 0xd800;
33
constexpr uint32_t low_surrogate_begin = 0xdc00;
34
constexpr uint32_t low_surrogate_end = 0xdfff;
35
constexpr uint32_t surrogate_offset = 0x10000;
36
constexpr uint8_t continuation_lower = 0x80;
37
constexpr uint8_t continuation_upper = 0xbf;
38
constexpr uint8_t control_end = 0x20;
39

40
static bool is_decimal(char character) NOEXCEPT
8,034 ✔
41
{
42
    return character >= '0' && character <= '9';
8,034 ✔
43
}
44

45
// The magnitude of an out of range number is below the double domain.
46
static bool is_underflow(std::string_view token) NOEXCEPT
64 ✔
47
{
48
    constexpr int32_t saturation = 1'000'000;
64 ✔
49
    auto it = token.cbegin();
50
    if (*it == '-')
64 ✔
51
        ++it;
20 ✔
52

53
    int32_t magnitude{};
54
    if (*it == '0')
64 ✔
55
    {
56
        ++it;
19 ✔
57
        if (it != token.cend() && *it == '.')
19 ✔
58
            for (++it; it != token.cend() && *it == '0'; ++it)
133 ✔
59
                magnitude = std::max(magnitude - 1, -saturation);
114 ✔
60
    }
61
    else
62
    {
63
        for (; it != token.cend() && is_decimal(*it); ++it)
90 ✔
64
            magnitude = std::min(magnitude + 1, saturation);
45 ✔
65
    }
66

67
    while (it != token.cend() && *it != 'e' && *it != 'E')
83 ✔
68
        ++it;
19 ✔
69

70
    if (it == token.cend())
64 ✔
NEW
71
        return magnitude < 0;
×
72

73
    ++it;
64 ✔
74
    const auto negative = (*it == '-');
64 ✔
75
    if (*it == '-' || *it == '+')
64 ✔
76
        ++it;
42 ✔
77

78
    int32_t exponent{};
79
    for (; it != token.cend(); ++it)
270 ✔
80
        exponent = std::min(exponent * 10 + (*it - '0'), saturation);
206 ✔
81

82
    return (negative ? magnitude - exponent : magnitude + exponent) < 0;
64 ✔
83
}
84

85
// Return the hex value of the character, or -1.
86
static int32_t to_hex(char character) NOEXCEPT
1,442 ✔
87
{
88
    if (character >= '0' && character <= '9')
1,442 ✔
89
        return character - '0';
835 ✔
90
    if (character >= 'a' && character <= 'f')
607 ✔
91
        return character - 'a' + 10;
210 ✔
92
    if (character >= 'A' && character <= 'F')
397 ✔
93
        return character - 'A' + 10;
364 ✔
94

95
    return -1;
96
}
97

98
parser::parser(const parse_options& options) NOEXCEPT
1,676 ✔
99
  : options_{ options }
1,676 ✔
100
{
101
}
1,676 ✔
102

103
parser::~parser() NOEXCEPT
1,676 ✔
104
{
105
}
1,676 ✔
106

107
void parser::reset() NOEXCEPT
891 ✔
108
{
109
    stack_.clear();
891 ✔
110
    result_.emplace_null();
891 ✔
111
    token_.clear();
112
    ec_.clear();
113
    state_ = state::value;
891 ✔
114
    numeric_ = numeric::sign;
891 ✔
115
    literal_ = {};
891 ✔
116
    matched_ = zero;
891 ✔
117
    is_key_ = false;
891 ✔
118
    integral_ = false;
891 ✔
119
    hex_count_ = 0;
891 ✔
120
    hex_ = 0;
891 ✔
121
    high_ = 0;
891 ✔
122
    utf8_remain_ = 0;
891 ✔
123
    utf8_lower_ = 0;
891 ✔
124
    utf8_upper_ = 0;
891 ✔
125
}
891 ✔
126

127
bool parser::done() const NOEXCEPT
1,822 ✔
128
{
129
    return state_ == state::done;
1,822 ✔
130
}
131

132
value parser::release() NOEXCEPT
892 ✔
133
{
134
    if (!done())
892 ✔
135
        return {};
2 ✔
136

137
    auto out = std::move(result_);
890 ✔
138
    reset();
890 ✔
139
    return out;
890 ✔
140
}
890 ✔
141

142
void parser::finish(code& ec) THROWS
924 ✔
143
{
144
    if (ec_)
924 ✔
145
    {
NEW
146
        ec = ec_;
×
NEW
147
        return;
×
148
    }
149

150
    if (state_ == state::number && stack_.empty() && is_terminal(numeric_))
924 ✔
151
    {
152
        ec = end_number();
8 ✔
153
        if (ec)
8 ✔
154
        {
155
            ec_ = ec;
7 ✔
156
            return;
7 ✔
157
        }
158
    }
159

160
    ec = done() ? code{} : fail(error::json_incomplete);
1,804 ✔
161
}
162

163
size_t parser::write(const char* data, size_t size, code& ec) THROWS
1,376 ✔
164
{
165
    const auto consumed = write_some(data, size, ec);
1,376 ✔
166
    if (!ec && consumed < size)
1,376 ✔
167
        ec = fail(error::json_extra_data);
39 ✔
168

169
    return consumed;
1,376 ✔
170
}
171

172
size_t parser::write_some(const char* data, size_t size, code& ec) THROWS
3,394 ✔
173
{
174
    if (ec_)
3,394 ✔
175
    {
176
        ec = ec_;
1 ✔
177
        return zero;
1 ✔
178
    }
179

180
    ec.clear();
181
    size_t index{};
182
    while (index < size)
206,389 ✔
183
    {
184
        const auto character = data[index];
203,751 ✔
185
        const auto byte = to_unsigned(character);
186

187
        switch (state_)
203,751 ✔
188
        {
189
            case state::done:
142 ✔
190
            {
191
                if (!is_whitespace(character))
142 ✔
192
                    return index;
193

194
                ++index;
94 ✔
195
                continue;
94 ✔
196
            }
197
            case state::value:
90,412 ✔
198
            case state::element:
199
            {
200
                if (is_whitespace(character))
90,412 ✔
201
                {
202
                    ++index;
73,795 ✔
203
                    continue;
73,795 ✔
204
                }
205

206
                if (character == ']' && state_ == state::element)
16,617 ✔
207
                    ec = close();
171 ✔
208
                else if (character == '{')
16,446 ✔
209
                    ec = open(true);
3,103 ✔
210
                else if (character == '[')
13,343 ✔
211
                    ec = open(false);
8,132 ✔
212
                else if (character == '"')
5,211 ✔
213
                {
214
                    is_key_ = false;
4,097 ✔
215
                    state_ = state::text;
4,097 ✔
216
                }
217
                else if (character == '-' || is_decimal(character))
1,114 ✔
218
                    ec = start_number(character);
935 ✔
219
                else
220
                    ec = start_literal(character);
179 ✔
221

222
                break;
223
            }
224
            case state::member:
51,552 ✔
225
            case state::key:
226
            {
227
                if (is_whitespace(character))
51,552 ✔
228
                {
229
                    ++index;
47,152 ✔
230
                    continue;
47,152 ✔
231
                }
232

233
                if (character == '}' && state_ == state::member)
4,400 ✔
234
                    ec = close();
21 ✔
235
                else if (character == '"')
4,379 ✔
236
                {
237
                    is_key_ = true;
4,354 ✔
238
                    state_ = state::text;
4,354 ✔
239
                }
240
                else
241
                    ec = fail(error::json_syntax);
25 ✔
242

243
                break;
244
            }
245
            case state::colon:
4,408 ✔
246
            {
247
                if (is_whitespace(character))
4,408 ✔
248
                {
249
                    ++index;
59 ✔
250
                    continue;
59 ✔
251
                }
252

253
                if (character == ':')
4,349 ✔
254
                    state_ = state::value;
4,342 ✔
255
                else
256
                    ec = fail(error::json_syntax);
7 ✔
257

258
                break;
259
            }
260
            case state::after:
30,761 ✔
261
            {
262
                if (is_whitespace(character))
30,761 ✔
263
                {
264
                    ++index;
22,693 ✔
265
                    continue;
22,693 ✔
266
                }
267

268
                const auto is_object = stack_.back().container.is_object();
8,068 ✔
269
                if (character == ',')
8,068 ✔
270
                    state_ = is_object ? state::key : state::value;
6,648 ✔
271
                else if (character == (is_object ? '}' : ']'))
7,253 ✔
272
                    ec = close();
4,058 ✔
273
                else
274
                    ec = fail(error::json_syntax);
37 ✔
275

276
                break;
277
            }
278
            case state::text:
279
            {
280
                auto end = index;
281
                for (; end < size; ++end)
277,848 ✔
282
                {
283
                    const auto next = data[end];
277,791 ✔
284
                    const auto unsigned_next = to_unsigned(next);
285
                    if (next == '"' || next == '\\' ||
277,791 ✔
286
                        unsigned_next < control_end ||
261,346 ✔
287
                        unsigned_next >= continuation_lower)
288
                        break;
289
                }
290

291
                if (end > index)
16,741 ✔
292
                {
293
                    ec = append(&data[index], end - index);
7,855 ✔
294
                    if (ec)
7,855 ✔
295
                        break;
296

297
                    index = end;
298
                    continue;
7,853 ✔
299
                }
300

301
                if (character == '"')
8,886 ✔
302
                {
303
                    ec = end_text();
8,159 ✔
304
                }
305
                else if (character == '\\')
727 ✔
306
                {
307
                    state_ = state::escape;
489 ✔
308
                }
309
                else if (byte < control_end)
238 ✔
310
                {
311
                    ec = fail(error::json_syntax);
16 ✔
312
                }
313
                else
314
                {
315
                    if (byte >= 0xc2 && byte <= 0xdf)
222 ✔
316
                    {
317
                        utf8_remain_ = 1;
33 ✔
318
                        utf8_lower_ = continuation_lower;
33 ✔
319
                        utf8_upper_ = continuation_upper;
33 ✔
320
                    }
321
                    else if (byte == 0xe0)
189 ✔
322
                    {
323
                        utf8_remain_ = 2;
30 ✔
324
                        utf8_lower_ = 0xa0;
30 ✔
325
                        utf8_upper_ = continuation_upper;
30 ✔
326
                    }
327
                    else if (byte == 0xed)
159 ✔
328
                    {
329
                        utf8_remain_ = 2;
30 ✔
330
                        utf8_lower_ = continuation_lower;
30 ✔
331
                        utf8_upper_ = 0x9f;
30 ✔
332
                    }
333
                    else if (byte >= 0xe1 && byte <= 0xef)
129 ✔
334
                    {
335
                        utf8_remain_ = 2;
26 ✔
336
                        utf8_lower_ = continuation_lower;
26 ✔
337
                        utf8_upper_ = continuation_upper;
26 ✔
338
                    }
339
                    else if (byte == 0xf0)
103 ✔
340
                    {
341
                        utf8_remain_ = 3;
31 ✔
342
                        utf8_lower_ = 0x90;
31 ✔
343
                        utf8_upper_ = continuation_upper;
31 ✔
344
                    }
345
                    else if (byte >= 0xf1 && byte <= 0xf3)
72 ✔
346
                    {
NEW
347
                        utf8_remain_ = 3;
×
NEW
348
                        utf8_lower_ = continuation_lower;
×
NEW
349
                        utf8_upper_ = continuation_upper;
×
350
                    }
351
                    else if (byte == 0xf4)
72 ✔
352
                    {
353
                        utf8_remain_ = 3;
23 ✔
354
                        utf8_lower_ = continuation_lower;
23 ✔
355
                        utf8_upper_ = 0x8f;
23 ✔
356
                    }
357
                    else
358
                    {
359
                        ec = fail(error::json_invalid_utf8);
49 ✔
360
                        break;
49 ✔
361
                    }
362

363
                    ec = append(&data[index], one);
173 ✔
364
                    if (!ec)
173 ✔
365
                        state_ = state::utf8;
173 ✔
366
                }
367

368
                break;
369
            }
370
            case state::utf8:
291 ✔
371
            {
372
                if (byte < utf8_lower_ || byte > utf8_upper_)
291 ✔
373
                {
374
                    ec = fail(error::json_invalid_utf8);
69 ✔
375
                    break;
69 ✔
376
                }
377

378
                ec = append(&data[index], one);
222 ✔
379
                if (ec)
222 ✔
380
                    break;
381

382
                utf8_lower_ = continuation_lower;
222 ✔
383
                utf8_upper_ = continuation_upper;
222 ✔
384
                if (is_zero(--utf8_remain_))
222 ✔
385
                    state_ = state::text;
104 ✔
386

387
                break;
388
            }
389
            case state::escape:
489 ✔
390
            {
391
                char escaped{};
489 ✔
392
                switch (character)
489 ✔
393
                {
394
                    case '"': escaped = '"'; break;
24 ✔
395
                    case '\\': escaped = '\\'; break;
23 ✔
396
                    case '/': escaped = '/'; break;
23 ✔
397
                    case 'b': escaped = '\b'; break;
23 ✔
398
                    case 'f': escaped = '\f'; break;
23 ✔
399
                    case 'n': escaped = '\n'; break;
23 ✔
400
                    case 'r': escaped = '\r'; break;
23 ✔
401
                    case 't': escaped = '\t'; break;
23 ✔
402
                    case 'u':
280 ✔
403
                    {
404
                        hex_count_ = 0;
280 ✔
405
                        hex_ = 0;
280 ✔
406
                        state_ = state::unicode;
280 ✔
407
                        break;
280 ✔
408
                    }
409
                    default:
24 ✔
410
                    {
411
                        ec = fail(error::json_invalid_escape);
24 ✔
412
                        break;
24 ✔
413
                    }
414
                }
415

416
                if (ec || state_ == state::unicode)
489 ✔
417
                    break;
418

419
                ec = append(&escaped, one);
185 ✔
420
                if (!ec)
185 ✔
421
                    state_ = state::text;
185 ✔
422

423
                break;
424
            }
425
            case state::unicode:
1,442 ✔
426
            case state::surrogate:
427
            {
428
                const auto digit = to_hex(character);
1,442 ✔
429
                if (is_negative(digit))
1,442 ✔
430
                {
431
                    ec = fail(error::json_expected_hex_digit);
33 ✔
432
                    break;
33 ✔
433
                }
434

435
                hex_ = (hex_ << 4) | to_unsigned(digit);
1,409 ✔
436
                if (++hex_count_ < 4)
1,409 ✔
437
                    break;
438

439
                if (state_ == state::surrogate)
332 ✔
440
                {
441
                    if (hex_ < low_surrogate_begin || hex_ > low_surrogate_end)
70 ✔
442
                    {
443
                        ec = fail(error::json_illegal_trailing_surrogate);
32 ✔
444
                        break;
32 ✔
445
                    }
446

447
                    const auto point = surrogate_offset +
38 ✔
448
                        ((high_ - high_surrogate_begin) << 10) +
38 ✔
449
                        (hex_ - low_surrogate_begin);
450

451
                    ec = append_code_point(point);
38 ✔
452
                    if (!ec)
38 ✔
453
                        state_ = state::text;
38 ✔
454
                }
455
                else if (hex_ >= high_surrogate_begin &&
262 ✔
456
                    hex_ < low_surrogate_begin)
457
                {
458
                    high_ = hex_;
118 ✔
459
                    state_ = state::surrogate_slash;
118 ✔
460
                }
461
                else if (hex_ >= low_surrogate_begin &&
144 ✔
462
                    hex_ <= low_surrogate_end)
463
                {
464
                    ec = fail(error::json_illegal_leading_surrogate);
26 ✔
465
                }
466
                else
467
                {
468
                    ec = append_code_point(hex_);
118 ✔
469
                    if (!ec)
118 ✔
470
                        state_ = state::text;
117 ✔
471
                }
472

473
                break;
474
            }
475
            case state::surrogate_slash:
118 ✔
476
            {
477
                if (character == '\\')
118 ✔
478
                    state_ = state::surrogate_u;
97 ✔
479
                else
480
                    ec = fail(error::json_expected_utf16_escape);
21 ✔
481

482
                break;
483
            }
484
            case state::surrogate_u:
97 ✔
485
            {
486
                if (character == 'u')
97 ✔
487
                {
488
                    hex_count_ = 0;
85 ✔
489
                    hex_ = 0;
85 ✔
490
                    state_ = state::surrogate;
85 ✔
491
                }
492
                else
493
                {
494
                    ec = fail(error::json_expected_utf16_escape);
12 ✔
495
                }
496

497
                break;
498
            }
499
            case state::number:
6,988 ✔
500
            {
501
                ec = step_number(character);
6,988 ✔
502
                if (ec)
6,988 ✔
503
                    break;
504

505
                // The terminating character is not consumed.
506
                if (state_ != state::number)
6,918 ✔
507
                    continue;
855 ✔
508

509
                if (token_.size() >= options_.max_number)
6,063 ✔
510
                {
511
                    ec = fail(error::json_number_too_large);
1 ✔
512
                    break;
1 ✔
513
                }
514

515
                token_.push_back(character);
6,062 ✔
516
                break;
6,062 ✔
517
            }
518
            case state::literal:
310 ✔
519
            {
520
                if (character != literal_[matched_])
310 ✔
521
                {
522
                    ec = fail(error::json_syntax);
6 ✔
523
                    break;
6 ✔
524
                }
525

526
                if (++matched_ < literal_.size())
304 ✔
527
                    break;
528

529
                if (literal_.front() == 't')
88 ✔
530
                    ec = push(json::value(true));
57 ✔
531
                else if (literal_.front() == 'f')
31 ✔
532
                    ec = push(json::value(false));
10 ✔
533
                else
534
                    ec = push(json::value(nullptr));
21 ✔
535

536
                break;
537
            }
538
        }
94 ✔
539

540
        if (ec)
51,202 ✔
541
            return index;
542

543
        ++index;
50,495 ✔
544
    }
545

546
    return index;
547
}
548

549
// private
550
// ----------------------------------------------------------------------------
551

552
bool parser::is_whitespace(char character) NOEXCEPT
177,275 ✔
553
{
554
    return character == ' '
177,275 ✔
555
        || character == '\t'
177,275 ✔
556
        || character == '\r'
557
        || character == '\n';
177,275 ✔
558
}
559

560
bool parser::is_terminal(numeric step) NOEXCEPT
8 ✔
561
{
562
    return step == numeric::zero
563
        || step == numeric::whole
8 ✔
564
        || step == numeric::fraction
8 ✔
565
        || step == numeric::exponent_digits;
8 ✔
566
}
567

568
code parser::fail(error::json_error_t ec) NOEXCEPT
783 ✔
569
{
570
    ec_ = ec;
783 ✔
571
    return ec_;
783 ✔
572
}
573

574
code parser::push(json::value&& element) THROWS
9,002 ✔
575
{
576
    if (stack_.empty())
9,002 ✔
577
    {
578
        result_ = std::move(element);
932 ✔
579
        state_ = state::done;
932 ✔
580
        return {};
932 ✔
581
    }
582

583
    auto& top = stack_.back();
8,070 ✔
584
    if (const auto object = top.container.if_object())
585
    {
586
        if (object->size() >= options_.max_object)
2,236 ✔
587
            return fail(error::json_object_too_large);
1 ✔
588

589
        object->insert_or_assign(top.key, std::move(element));
2,235 ✔
590
    }
591
    else
592
    {
593
        auto& array = top.container.get_array();
5,834 ✔
594
        if (array.size() >= options_.max_array)
5,834 ✔
595
            return fail(error::json_array_too_large);
1 ✔
596

597
        array.push_back(std::move(element));
5,833 ✔
598
    }
599

600
    state_ = state::after;
8,068 ✔
601
    return {};
8,068 ✔
602
}
603

604
code parser::open(bool is_object) THROWS
11,235 ✔
605
{
606
    if (stack_.size() >= options_.max_depth)
11,235 ✔
607
        return fail(error::json_too_deep);
197 ✔
608

609
    if (is_object)
11,038 ✔
610
    {
611
        stack_.push_back({ json::value(json::object{}), {} });
6,206 ✔
612
        state_ = state::member;
3,103 ✔
613
    }
614
    else
615
    {
616
        stack_.push_back({ json::value(json::array{}), {} });
15,870 ✔
617
        state_ = state::element;
7,935 ✔
618
    }
619

620
    return {};
11,038 ✔
621
}
11,038 ✔
622

623
code parser::close() THROWS
4,250 ✔
624
{
625
    auto container = std::move(stack_.back().container);
4,250 ✔
626
    stack_.pop_back();
627
    return push(std::move(container));
8,500 ✔
628
}
4,250 ✔
629

630
code parser::append(const char* data, size_t size) THROWS
8,591 ✔
631
{
632
    const auto limit = is_key_ ? options_.max_key : options_.max_string;
8,591 ✔
633
    if (size > limit || token_.size() > limit - size)
8,591 ✔
634
        return fail(is_key_ ? error::json_key_too_large :
5 ✔
635
            error::json_string_too_large);
3 ✔
636

637
    token_.append(data, size);
8,588 ✔
638
    return {};
8,588 ✔
639
}
640

641
code parser::append_code_point(uint32_t point) THROWS
156 ✔
642
{
643
    char bytes[4]{};
156 ✔
644
    size_t size{};
645
    if (point < 0x80)
156 ✔
646
    {
647
        bytes[0] = narrow_sign_cast<char>(point);
72 ✔
648
        size = 1;
649
    }
650
    else if (point < 0x800)
84 ✔
651
    {
652
        bytes[0] = narrow_sign_cast<char>(0xc0 | (point >> 6));
20 ✔
653
        bytes[1] = narrow_sign_cast<char>(0x80 | (point & 0x3f));
20 ✔
654
        size = 2;
655
    }
656
    else if (point < surrogate_offset)
64 ✔
657
    {
658
        bytes[0] = narrow_sign_cast<char>(0xe0 | (point >> 12));
26 ✔
659
        bytes[1] = narrow_sign_cast<char>(0x80 | ((point >> 6) & 0x3f));
26 ✔
660
        bytes[2] = narrow_sign_cast<char>(0x80 | (point & 0x3f));
26 ✔
661
        size = 3;
662
    }
663
    else
664
    {
665
        bytes[0] = narrow_sign_cast<char>(0xf0 | (point >> 18));
38 ✔
666
        bytes[1] = narrow_sign_cast<char>(0x80 | ((point >> 12) & 0x3f));
38 ✔
667
        bytes[2] = narrow_sign_cast<char>(0x80 | ((point >> 6) & 0x3f));
38 ✔
668
        bytes[3] = narrow_sign_cast<char>(0x80 | (point & 0x3f));
38 ✔
669
        size = 4;
670
    }
671

672
    return append(&bytes[0], size);
156 ✔
673
}
674

675
code parser::end_text() THROWS
8,159 ✔
676
{
677
    if (is_key_)
8,159 ✔
678
    {
679
        stack_.back().key = std::move(token_);
4,353 ✔
680
        token_.clear();
681
        state_ = state::colon;
4,353 ✔
682
        return {};
4,353 ✔
683
    }
684

685
    auto text = std::move(token_);
3,806 ✔
686
    token_.clear();
687
    return push(json::value(std::move(text)));
3,806 ✔
688
}
689

690
code parser::start_number(char character) THROWS
935 ✔
691
{
692
    token_.clear();
693
    token_.push_back(character);
935 ✔
694
    integral_ = true;
935 ✔
695
    numeric_ = character == '-' ? numeric::sign :
935 ✔
696
        (character == '0' ? numeric::zero : numeric::whole);
777 ✔
697
    state_ = state::number;
935 ✔
698
    return {};
935 ✔
699
}
700

701
// Advance the number grammar, or end the number (state change) on a
702
// character that cannot continue it.
703
code parser::step_number(char character) THROWS
6,988 ✔
704
{
705
    const auto digit = is_decimal(character);
6,988 ✔
706
    const auto exponent = (character == 'e' || character == 'E');
6,988 ✔
707

708
    switch (numeric_)
6,988 ✔
709
    {
710
        case numeric::sign:
158 ✔
711
        {
712
            if (!digit)
158 ✔
713
                return fail(error::json_syntax);
19 ✔
714

715
            numeric_ = character == '0' ? numeric::zero : numeric::whole;
219 ✔
716
            return {};
139 ✔
717
        }
718
        case numeric::zero:
237 ✔
719
        {
720
            if (digit)
237 ✔
721
                return fail(error::json_syntax);
9 ✔
722

723
            if (character == '.')
228 ✔
724
            {
725
                integral_ = false;
99 ✔
726
                numeric_ = numeric::dot;
99 ✔
727
                return {};
99 ✔
728
            }
729

730
            if (exponent)
129 ✔
731
            {
732
                integral_ = false;
8 ✔
733
                numeric_ = numeric::exponent;
8 ✔
734
                return {};
8 ✔
735
            }
736

737
            break;
738
        }
739
        case numeric::whole:
3,526 ✔
740
        {
741
            if (digit)
3,526 ✔
742
                return {};
2,849 ✔
743

744
            if (character == '.')
677 ✔
745
            {
746
                integral_ = false;
107 ✔
747
                numeric_ = numeric::dot;
107 ✔
748
                return {};
107 ✔
749
            }
750

751
            if (exponent)
570 ✔
752
            {
753
                integral_ = false;
86 ✔
754
                numeric_ = numeric::exponent;
86 ✔
755
                return {};
86 ✔
756
            }
757

758
            break;
759
        }
760
        case numeric::dot:
206 ✔
761
        {
762
            if (!digit)
206 ✔
763
                return fail(error::json_syntax);
10 ✔
764

765
            numeric_ = numeric::fraction;
196 ✔
766
            return {};
196 ✔
767
        }
768
        case numeric::fraction:
2,230 ✔
769
        {
770
            if (digit)
2,230 ✔
771
                return {};
2,035 ✔
772

773
            if (exponent)
195 ✔
774
            {
775
                numeric_ = numeric::exponent;
75 ✔
776
                return {};
75 ✔
777
            }
778

779
            break;
780
        }
781
        case numeric::exponent:
169 ✔
782
        {
783
            if (character == '+' || character == '-')
169 ✔
784
            {
785
                numeric_ = numeric::exponent_sign;
78 ✔
786
                return {};
78 ✔
787
            }
788

789
            if (!digit)
91 ✔
790
                return fail(error::json_syntax);
10 ✔
791

792
            numeric_ = numeric::exponent_digits;
81 ✔
793
            return {};
81 ✔
794
        }
795
        case numeric::exponent_sign:
78 ✔
796
        {
797
            if (!digit)
78 ✔
798
                return fail(error::json_syntax);
5 ✔
799

800
            numeric_ = numeric::exponent_digits;
73 ✔
801
            return {};
73 ✔
802
        }
803
        case numeric::exponent_digits:
384 ✔
804
        {
805
            if (digit)
384 ✔
806
                return {};
237 ✔
807

808
            break;
809
        }
810
    }
811

812
    return end_number();
872 ✔
813
}
814

815
code parser::end_number() THROWS
880 ✔
816
{
817
    json::value number{};
880 ✔
818
    const auto ec = to_number(number);
880 ✔
819
    token_.clear();
820
    return ec ? ec : push(std::move(number));
1,760 ✔
821
}
880 ✔
822

823
code parser::to_number(json::value& out) NOEXCEPT
880 ✔
824
{
825
    const auto first = token_.data();
826
    const auto last = std::next(first, token_.size());
880 ✔
827

828
    if (integral_)
880 ✔
829
    {
830
        if (token_.front() == '-')
605 ✔
831
        {
832
            int64_t signed_{};
66 ✔
833
            const auto result = std::from_chars(first, last, signed_);
66 ✔
834
            if (result.ec == std::errc{})
66 ✔
835
            {
836
                out = signed_;
41 ✔
837
                return {};
41 ✔
838
            }
839
        }
840
        else
841
        {
842
            uint64_t unsigned_{};
539 ✔
843
            const auto result = std::from_chars(first, last, unsigned_);
539 ✔
844
            if (result.ec == std::errc{})
539 ✔
845
            {
846
                if (unsigned_ <= to_unsigned(max_int64))
514 ✔
847
                    out = to_signed(unsigned_);
848
                else
849
                    out = unsigned_;
850

851
                return {};
514 ✔
852
            }
853
        }
854
    }
855

856
    double number{};
325 ✔
857
    const auto result = std::from_chars(first, last, number);
325 ✔
858
    if (result.ec == std::errc::result_out_of_range)
325 ✔
859
    {
860
        if (!is_underflow(token_))
64 ✔
861
            return fail(error::json_exponent_overflow);
22 ✔
862

863
        number = token_.front() == '-' ? -0.0 : 0.0;
72 ✔
864
    }
865
    else if (result.ec != std::errc{} || result.ptr != last)
261 ✔
866
    {
NEW
867
        return fail(error::json_syntax);
×
868
    }
869

870
    out = number;
303 ✔
871
    return {};
303 ✔
872
}
873

874
code parser::start_literal(char character) NOEXCEPT
179 ✔
875
{
876
    if (character == 't')
179 ✔
877
        literal_ = "true";
60 ✔
878
    else if (character == 'f')
119 ✔
879
        literal_ = "false";
16 ✔
880
    else if (character == 'n')
103 ✔
881
        literal_ = "null";
24 ✔
882
    else
883
        return fail(error::json_syntax);
79 ✔
884

885
    matched_ = one;
100 ✔
886
    state_ = state::literal;
100 ✔
887
    return {};
100 ✔
888
}
889

890
// parse
891
// ----------------------------------------------------------------------------
892

893
value parse(std::string_view text, code& ec,
300 ✔
894
    const parse_options& options) THROWS
895
{
896
    parser reader{ options };
300 ✔
897
    reader.write(text.data(), text.size(), ec);
300 ✔
898
    if (!ec)
300 ✔
899
        reader.finish(ec);
208 ✔
900

901
    return ec ? value{} : reader.release();
600 ✔
902
}
300 ✔
903

904
value parse(std::string_view text, const parse_options& options) THROWS
138 ✔
905
{
906
    code ec{};
907
    auto out = parse(text, ec, options);
138 ✔
908
    if (ec)
138 ✔
909
        throw_error(ec);
1 ✔
910

911
    return out;
137 ✔
912
}
1 ✔
913

914
BC_POP_WARNING()
915
BC_POP_WARNING()
916

917
} // namespace json
918
} // namespace system
919
} // namespace libbitcoin
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc