Coverage Report

Created: 2026-10-28 06:31

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/glaze/include/glaze/util/atoi.hpp
Line
Count
Source
1
#pragma once
2
3
#include <array>
4
#include <bit>
5
#include <cmath>
6
#include <concepts>
7
#include <cstdint>
8
#include <cstring>
9
#include <iterator>
10
#include <limits>
11
#include <type_traits>
12
13
#include "glaze/util/for_each.hpp"
14
#include "glaze/util/inline.hpp"
15
#include "glaze/util/type_traits.hpp"
16
17
// Characters to integer parsing
18
19
// - We don't allow decimals in integer parsing
20
// - We don't allow negative exponents
21
// These cases can produce fractions which slow performance and add confusion
22
// as to how the integer ought to be parsed (truncation, rounding, etc.)
23
// This integer parsing is designed to be straightforward and fast
24
// Values like 1e6 are allowed because it enables less typing from the user
25
// and has a clear integer value
26
27
// Valid JSON integer examples
28
// 1234
29
// 1234e1
30
// 1e9
31
32
// Invalid for this atoi algorithm
33
// 1.234
34
// 1234e-1
35
// 0.0
36
37
// The standard JSON specification for numbers and the associated rules apply
38
39
// *** We ensure that a decimal value being parsed will result in an error
40
// 1.2 should not produce 1, but rather an error, even when a single field is parsed
41
// This ensures that we get proper errors when parsing and don't get confusing errors
42
// It isn't technically required, because end validation would handle it, but it produces
43
// much clearer errors, especially when we don't perform trailing validation.
44
45
#if defined(_MSC_VER) && !defined(__clang__)
46
// Turn off MSVC warning for possible loss of data: we are intentionally allowing well defined unsigned integer
47
// overflows
48
#pragma warning(push)
49
#pragma warning(disable : 4244)
50
#endif
51
52
namespace glz
53
{
54
   inline constexpr std::array<uint64_t, 20> powers_of_ten_int{1ull,
55
                                                               10ull,
56
                                                               100ull,
57
                                                               1000ull,
58
                                                               10000ull,
59
                                                               100000ull,
60
                                                               1000000ull,
61
                                                               10000000ull,
62
                                                               100000000ull,
63
                                                               1000000000ull,
64
                                                               10000000000ull,
65
                                                               100000000000ull,
66
                                                               1000000000000ull,
67
                                                               10000000000000ull,
68
                                                               100000000000000ull,
69
                                                               1000000000000000ull,
70
                                                               10000000000000000ull,
71
                                                               100000000000000000ull,
72
                                                               1000000000000000000ull,
73
                                                               10000000000000000000ull};
74
75
   inline constexpr std::array<bool, 256> exp_dec_table = [] {
76
      std::array<bool, 256> t{};
77
      t['.'] = true;
78
      t['E'] = true;
79
      t['e'] = true;
80
      return t;
81
   }();
82
83
   inline constexpr std::array<bool, 256> non_exp_table = [] {
84
      std::array<bool, 256> t{};
85
      t.fill(true);
86
      t['E'] = false;
87
      t['e'] = false;
88
      return t;
89
   }();
90
91
   inline constexpr std::array<bool, 256> digit_table = [] {
92
      std::array<bool, 256> t{};
93
      t['0'] = true;
94
      t['1'] = true;
95
      t['2'] = true;
96
      t['3'] = true;
97
      t['4'] = true;
98
      t['5'] = true;
99
      t['6'] = true;
100
      t['7'] = true;
101
      t['8'] = true;
102
      t['9'] = true;
103
      return t;
104
   }();
105
106
0
   GLZ_ALWAYS_INLINE constexpr bool is_digit(const uint8_t c) noexcept { return c <= '9' && c >= '0'; }
107
108
   // Exponents at or beyond this magnitude are out of range for every integer width, so the exponent
109
   // accumulators clamp here rather than growing without bound. Sits far above the largest accepted
110
   // exponent (19, for uint64_t) and low enough that a clamped value cannot overflow uint32_t.
111
   inline constexpr uint32_t exponent_clamp = 1000;
112
113
   // Consumes the run of exponent digits starting at `c` and returns its value, clamped at
114
   // exponent_clamp. Clamping is what keeps the result meaningful: a narrow accumulator wraps mod its
115
   // width, which aliases an out-of-range exponent onto an accepted one. Every digit is consumed so
116
   // that leading zeros reach their true value -- JSON forbids a leading zero in the integer part but
117
   // permits any run of digits in the exponent, making "1e007" a valid spelling of 10^7 -- and so the
118
   // caller resumes past the whole exponent rather than mid-number.
119
   //
120
   // Requires *c to be a digit and the buffer to be terminated by a non-digit (the null terminator
121
   // counts); this scan is bounded by the input, not by a digit count.
122
   template <class Char>
123
   GLZ_ALWAYS_INLINE constexpr uint32_t parse_exponent(Char*& c) noexcept
124
0
   {
125
0
      uint32_t exp = uint32_t(*c - '0');
126
0
      ++c;
127
0
      while (is_digit(*c)) {
128
0
         // Written as a select rather than a branch: the clamp only ever engages on absurdly long
129
0
         // exponents, so a branch here would be a mispredict risk on the path that matters.
130
0
         exp = exp < exponent_clamp ? exp * 10 + uint32_t(*c - '0') : exp;
131
0
         ++c;
132
0
      }
133
0
      return exp;
134
0
   }
135
136
   // Computed overflow checks - used instead of lookup tables to save 4KB+ of binary size
137
   // Uses adjusted threshold for branch-free single comparison (7-12% faster than bitwise approach)
138
   template <class T>
139
   GLZ_ALWAYS_INLINE constexpr bool would_overflow_positive(std::remove_volatile_t<T> v, uint8_t next_digit) noexcept
140
0
   {
141
0
      using U = std::remove_volatile_t<T>;
142
0
      constexpr auto max_val = static_cast<uint64_t>((std::numeric_limits<U>::max)());
143
0
      constexpr auto threshold = max_val / 10;
144
0
      constexpr auto last_digit = max_val % 10;
145
0
      const auto uv = static_cast<uint64_t>(v);
146
0
      const auto digit = static_cast<uint64_t>(next_digit - '0');
147
0
      // When digit > last_digit, effective threshold is one less
148
0
      // This is branch-free and faster than bitwise OR/AND approach
149
0
      return uv > (threshold - uint64_t(digit > last_digit));
150
0
   }
Unexecuted instantiation: _ZN3glz23would_overflow_positiveIlEEbu17__remove_volatileIT_Eh
Unexecuted instantiation: _ZN3glz23would_overflow_positiveImEEbu17__remove_volatileIT_Eh
151
152
   template <class T>
153
   GLZ_ALWAYS_INLINE constexpr bool would_overflow_negative(std::remove_volatile_t<T> v, uint8_t next_digit) noexcept
154
0
   {
155
0
      // For negative: max magnitude is max + 1 (e.g., -2147483648 for int32)
156
0
      using U = std::remove_volatile_t<T>;
157
0
      constexpr auto max_val = static_cast<uint64_t>((std::numeric_limits<U>::max)()) + 1;
158
0
      constexpr auto threshold = max_val / 10;
159
0
      constexpr auto last_digit = max_val % 10;
160
0
      const auto uv = static_cast<uint64_t>(v);
161
0
      const auto digit = static_cast<uint64_t>(next_digit - '0');
162
0
      // When digit > last_digit, effective threshold is one less
163
0
      return uv > (threshold - uint64_t(digit > last_digit));
164
0
   }
165
166
   struct value128 final
167
   {
168
      uint64_t low;
169
      uint64_t high;
170
   };
171
172
   // slow emulation routine for 32-bit
173
0
   GLZ_ALWAYS_INLINE constexpr uint64_t emulu(uint32_t x, uint32_t y) { return x * (uint64_t)y; }
174
175
   GLZ_ALWAYS_INLINE constexpr uint64_t umul128_generic(uint64_t ab, uint64_t cd, uint64_t* hi)
176
0
   {
177
0
      uint64_t ad = emulu((uint32_t)(ab >> 32), (uint32_t)cd);
178
0
      uint64_t bd = emulu((uint32_t)ab, (uint32_t)cd);
179
0
      uint64_t adbc = ad + emulu((uint32_t)ab, (uint32_t)(cd >> 32));
180
0
      uint64_t adbc_carry = (uint64_t)(adbc < ad);
181
0
      uint64_t lo = bd + (adbc << 32);
182
0
      *hi = emulu((uint32_t)(ab >> 32), (uint32_t)(cd >> 32)) + (adbc >> 32) + (adbc_carry << 32) + (uint64_t)(lo < bd);
183
0
      return lo;
184
0
   }
185
186
   // compute 64-bit a*b
187
   GLZ_ALWAYS_INLINE constexpr value128 full_multiplication(uint64_t a, uint64_t b)
188
0
   {
189
0
      if consteval {
190
0
         value128 answer;
191
0
         answer.low = umul128_generic(a, b, &answer.high);
192
0
         return answer;
193
0
      }
194
0
      value128 answer;
195
0
#if defined(_M_ARM64) && !defined(__MINGW32__)
196
0
      // ARM64 has native support for 64-bit multiplications, no need to emulate
197
0
      // But MinGW on ARM64 doesn't have native support for 64-bit multiplications
198
0
      answer.high = __umulh(a, b);
199
0
      answer.low = a * b;
200
0
#elif defined(GLZ_FASTFLOAT_32BIT) || (defined(_WIN64) && !defined(__clang__) && !defined(__MINGW32__))
201
0
      answer.low = _umul128(a, b, &answer.high); // _umul128 not available on ARM64
202
0
#elif defined(GLZ_FASTFLOAT_64BIT) && defined(__SIZEOF_INT128__)
203
0
      __uint128_t r = ((__uint128_t)a) * b;
204
0
      answer.low = uint64_t(r);
205
0
      answer.high = uint64_t(r >> 64);
206
0
#else
207
0
      answer.low = umul128_generic(a, b, &answer.high);
208
0
#endif
209
0
      return answer;
210
0
   }
211
212
   template <std::integral T>
213
      requires(std::is_unsigned_v<T> && (sizeof(T) <= 8))
214
   GLZ_ALWAYS_INLINE constexpr const uint8_t* parse_int(T& v, const uint8_t* c) noexcept
215
0
   {
216
0
      if (is_digit(*c)) [[likely]] {
217
0
         v = *c - '0';
218
0
         ++c;
219
0
      }
220
0
      else [[unlikely]] {
221
0
         return {};
222
0
      }
223
0
224
0
      if (is_digit(*c)) {
225
0
         v = v * 10 + (*c - '0');
226
0
         ++c;
227
0
      }
228
0
      else {
229
0
         return c;
230
0
      }
231
0
232
0
      if (c[-2] == '0') [[unlikely]] {
233
0
         return {};
234
0
      }
235
0
236
0
      if constexpr (sizeof(T) > 1) {
237
0
         if (is_digit(*c)) {
238
0
            v = v * 10 + (*c - '0');
239
0
            ++c;
240
0
         }
241
0
         else {
242
0
            return c;
243
0
         }
244
0
245
0
         if (is_digit(*c)) {
246
0
            v = v * 10 + (*c - '0');
247
0
            ++c;
248
0
         }
249
0
         else {
250
0
            return c;
251
0
         }
252
0
253
0
         if constexpr (sizeof(T) > 2) {
254
0
            if (is_digit(*c)) {
255
0
               v = v * 10 + (*c - '0');
256
0
               ++c;
257
0
            }
258
0
            else {
259
0
               return c;
260
0
            }
261
0
262
0
            if (is_digit(*c)) {
263
0
               v = v * 10 + (*c - '0');
264
0
               ++c;
265
0
            }
266
0
            else {
267
0
               return c;
268
0
            }
269
0
270
0
            if (is_digit(*c)) {
271
0
               v = v * 10 + (*c - '0');
272
0
               ++c;
273
0
            }
274
0
            else {
275
0
               return c;
276
0
            }
277
0
278
0
            if (is_digit(*c)) {
279
0
               v = v * 10 + (*c - '0');
280
0
               ++c;
281
0
            }
282
0
            else {
283
0
               return c;
284
0
            }
285
0
286
0
            if (is_digit(*c)) {
287
0
               v = v * 10 + (*c - '0');
288
0
               ++c;
289
0
            }
290
0
            else {
291
0
               return c;
292
0
            }
293
0
294
0
            if constexpr (sizeof(T) > 4) {
295
0
               if (is_digit(*c)) {
296
0
                  v = v * 10 + (*c - '0');
297
0
                  ++c;
298
0
               }
299
0
               else {
300
0
                  return c;
301
0
               }
302
0
303
0
               if (is_digit(*c)) {
304
0
                  v = v * 10 + (*c - '0');
305
0
                  ++c;
306
0
               }
307
0
               else {
308
0
                  return c;
309
0
               }
310
0
311
0
               if (is_digit(*c)) {
312
0
                  v = v * 10 + (*c - '0');
313
0
                  ++c;
314
0
               }
315
0
               else {
316
0
                  return c;
317
0
               }
318
0
319
0
               if (is_digit(*c)) {
320
0
                  v = v * 10 + (*c - '0');
321
0
                  ++c;
322
0
               }
323
0
               else {
324
0
                  return c;
325
0
               }
326
0
327
0
               if (is_digit(*c)) {
328
0
                  v = v * 10 + (*c - '0');
329
0
                  ++c;
330
0
               }
331
0
               else {
332
0
                  return c;
333
0
               }
334
0
335
0
               if (is_digit(*c)) {
336
0
                  v = v * 10 + (*c - '0');
337
0
                  ++c;
338
0
               }
339
0
               else {
340
0
                  return c;
341
0
               }
342
0
343
0
               if (is_digit(*c)) {
344
0
                  v = v * 10 + (*c - '0');
345
0
                  ++c;
346
0
               }
347
0
               else {
348
0
                  return c;
349
0
               }
350
0
351
0
               if (is_digit(*c)) {
352
0
                  v = v * 10 + (*c - '0');
353
0
                  ++c;
354
0
               }
355
0
               else {
356
0
                  return c;
357
0
               }
358
0
359
0
               if (is_digit(*c)) {
360
0
                  v = v * 10 + (*c - '0');
361
0
                  ++c;
362
0
               }
363
0
               else {
364
0
                  return c;
365
0
               }
366
0
367
0
               if (is_digit(*c)) {
368
0
                  v = v * 10 + (*c - '0');
369
0
                  ++c;
370
0
               }
371
0
               else {
372
0
                  return c;
373
0
               }
374
0
            }
375
0
         }
376
0
      }
377
0
378
0
      if (is_digit(*c)) {
379
0
         // Test before multiplying. The previous guard multiplied first and then compared the
380
0
         // already-wrapped value against max/10 - 10, which only caught wraps that happened to land
381
0
         // below that threshold: "300" wraps to 44 for uint8_t and sailed through, as did most
382
0
         // out-of-range values. would_overflow_positive is the same branch-free check the signed
383
0
         // path above already uses, so the two paths now reject on the same rule.
384
0
         if (would_overflow_positive<T>(v, *c)) [[unlikely]] {
385
0
            return {};
386
0
         }
387
0
         v = v * 10 + (*c - '0');
388
0
         ++c;
389
0
         if (is_digit(*c)) [[unlikely]] {
390
0
            return {};
391
0
         }
392
0
      }
393
0
394
0
      return c;
395
0
   }
396
397
   template <std::integral T, class Char>
398
      requires(std::is_unsigned_v<T>)
399
   GLZ_ALWAYS_INLINE constexpr bool atoi(T& v, Char*& c) noexcept
400
0
   {
401
0
      if (auto ptr = parse_int(v, reinterpret_cast<const uint8_t*>(c))) [[likely]] {
402
0
         c = reinterpret_cast<const Char*>(ptr);
403
0
         if (*c == 'e' || *c == 'E') {
404
0
            ++c;
405
0
         }
406
0
         else {
407
0
            if (*c == '.') [[unlikely]] {
408
0
               return false;
409
0
            }
410
0
            return true;
411
0
         }
412
0
413
0
         c += (*c == '+');
414
0
415
0
         if (not is_digit(*c)) [[unlikely]] {
416
0
            return false;
417
0
         }
418
0
         const uint32_t exp = parse_exponent(c);
419
0
         // An exponent past the width's limit overflows any non-zero magnitude, but zero stays zero
420
0
         // however far it is scaled, so "0e19" is in range for every width. Testing the magnitude
421
0
         // here rather than ahead of the dispatch keeps it off the hot path: it only runs once the
422
0
         // exponent has already failed the range check.
423
0
         if constexpr (sizeof(T) == 1) {
424
0
            if (exp > 2) [[unlikely]] {
425
0
               return v == 0;
426
0
            }
427
0
         }
428
0
         else if constexpr (sizeof(T) == 2) {
429
0
            if (exp > 4) [[unlikely]] {
430
0
               return v == 0;
431
0
            }
432
0
         }
433
0
         else if constexpr (sizeof(T) == 4) {
434
0
            if (exp > 9) [[unlikely]] {
435
0
               return v == 0;
436
0
            }
437
0
         }
438
0
         else {
439
0
            if (exp > 19) [[unlikely]] {
440
0
               return v == 0;
441
0
            }
442
0
         }
443
0
444
0
         if constexpr (sizeof(T) == 1) {
445
0
            static constexpr std::array<uint8_t, 3> powers_of_ten{1, 10, 100};
446
0
            const uint64_t i = v * powers_of_ten[exp];
447
0
            v = T(i);
448
0
            return i <= (std::numeric_limits<T>::max)();
449
0
         }
450
0
         else if constexpr (sizeof(T) == 2) {
451
0
            static constexpr std::array<uint16_t, 5> powers_of_ten{1, 10, 100, 1000, 10000};
452
0
            const uint64_t i = v * powers_of_ten[exp];
453
0
            v = T(i);
454
0
            return i <= (std::numeric_limits<T>::max)();
455
0
         }
456
0
         else if constexpr (sizeof(T) < 8) {
457
0
            const uint64_t i = v * powers_of_ten_int[exp];
458
0
            v = T(i);
459
0
            return i <= (std::numeric_limits<T>::max)();
460
0
         }
461
0
         else {
462
0
#if defined(__SIZEOF_INT128__)
463
0
            const __uint128_t res = __uint128_t(v) * powers_of_ten_int[exp];
464
0
            v = T(res);
465
0
            return res <= (std::numeric_limits<T>::max)();
466
0
#else
467
0
            const auto res = full_multiplication(v, powers_of_ten_int[exp]);
468
0
            v = T(res.low);
469
0
            return res.high == 0;
470
0
#endif
471
0
         }
472
0
      }
473
0
      return false;
474
0
   }
475
476
   template <std::integral T>
477
      requires(std::is_signed_v<T> && (sizeof(T) <= 8))
478
   GLZ_ALWAYS_INLINE constexpr const uint8_t* parse_int(T& v, const uint8_t* c) noexcept
479
0
   {
480
0
      const uint8_t sign = *c == '-';
481
0
      c += sign;
482
0
483
0
      if (is_digit(*c)) [[likely]] {
484
0
         v = *c - '0';
485
0
         ++c;
486
0
      }
487
0
      else [[unlikely]] {
488
0
         return {};
489
0
      }
490
0
491
0
      if (is_digit(*c)) {
492
0
         v = v * 10 + (*c - '0');
493
0
         ++c;
494
0
      }
495
0
      else {
496
0
         if (sign) {
497
0
            v = -v;
498
0
         }
499
0
         return c;
500
0
      }
501
0
502
0
      if (c[-2] == '0') [[unlikely]] {
503
0
         return {};
504
0
      }
505
0
506
0
      if constexpr (sizeof(T) > 1) {
507
0
         if (is_digit(*c)) {
508
0
            v = v * 10 + (*c - '0');
509
0
            ++c;
510
0
         }
511
0
         else {
512
0
            if (sign) {
513
0
               v = -v;
514
0
            }
515
0
            return c;
516
0
         }
517
0
518
0
         if (is_digit(*c)) {
519
0
            v = v * 10 + (*c - '0');
520
0
            ++c;
521
0
         }
522
0
         else {
523
0
            if (sign) {
524
0
               v = -v;
525
0
            }
526
0
            return c;
527
0
         }
528
0
529
0
         if constexpr (sizeof(T) > 2) {
530
0
            if (is_digit(*c)) {
531
0
               v = v * 10 + (*c - '0');
532
0
               ++c;
533
0
            }
534
0
            else {
535
0
               if (sign) {
536
0
                  v = -v;
537
0
               }
538
0
               return c;
539
0
            }
540
0
541
0
            if (is_digit(*c)) {
542
0
               v = v * 10 + (*c - '0');
543
0
               ++c;
544
0
            }
545
0
            else {
546
0
               if (sign) {
547
0
                  v = -v;
548
0
               }
549
0
               return c;
550
0
            }
551
0
552
0
            if (is_digit(*c)) {
553
0
               v = v * 10 + (*c - '0');
554
0
               ++c;
555
0
            }
556
0
            else {
557
0
               if (sign) {
558
0
                  v = -v;
559
0
               }
560
0
               return c;
561
0
            }
562
0
563
0
            if (is_digit(*c)) {
564
0
               v = v * 10 + (*c - '0');
565
0
               ++c;
566
0
            }
567
0
            else {
568
0
               if (sign) {
569
0
                  v = -v;
570
0
               }
571
0
               return c;
572
0
            }
573
0
574
0
            if (is_digit(*c)) {
575
0
               v = v * 10 + (*c - '0');
576
0
               ++c;
577
0
            }
578
0
            else {
579
0
               if (sign) {
580
0
                  v = -v;
581
0
               }
582
0
               return c;
583
0
            }
584
0
585
0
            if constexpr (sizeof(T) > 4) {
586
0
               if (is_digit(*c)) {
587
0
                  v = v * 10 + (*c - '0');
588
0
                  ++c;
589
0
               }
590
0
               else {
591
0
                  if (sign) {
592
0
                     v = -v;
593
0
                  }
594
0
                  return c;
595
0
               }
596
0
597
0
               if (is_digit(*c)) {
598
0
                  v = v * 10 + (*c - '0');
599
0
                  ++c;
600
0
               }
601
0
               else {
602
0
                  if (sign) {
603
0
                     v = -v;
604
0
                  }
605
0
                  return c;
606
0
               }
607
0
608
0
               if (is_digit(*c)) {
609
0
                  v = v * 10 + (*c - '0');
610
0
                  ++c;
611
0
               }
612
0
               else {
613
0
                  if (sign) {
614
0
                     v = -v;
615
0
                  }
616
0
                  return c;
617
0
               }
618
0
619
0
               if (is_digit(*c)) {
620
0
                  v = v * 10 + (*c - '0');
621
0
                  ++c;
622
0
               }
623
0
               else {
624
0
                  if (sign) {
625
0
                     v = -v;
626
0
                  }
627
0
                  return c;
628
0
               }
629
0
630
0
               if (is_digit(*c)) {
631
0
                  v = v * 10 + (*c - '0');
632
0
                  ++c;
633
0
               }
634
0
               else {
635
0
                  if (sign) {
636
0
                     v = -v;
637
0
                  }
638
0
                  return c;
639
0
               }
640
0
641
0
               if (is_digit(*c)) {
642
0
                  v = v * 10 + (*c - '0');
643
0
                  ++c;
644
0
               }
645
0
               else {
646
0
                  if (sign) {
647
0
                     v = -v;
648
0
                  }
649
0
                  return c;
650
0
               }
651
0
652
0
               if (is_digit(*c)) {
653
0
                  v = v * 10 + (*c - '0');
654
0
                  ++c;
655
0
               }
656
0
               else {
657
0
                  if (sign) {
658
0
                     v = -v;
659
0
                  }
660
0
                  return c;
661
0
               }
662
0
663
0
               if (is_digit(*c)) {
664
0
                  v = v * 10 + (*c - '0');
665
0
                  ++c;
666
0
               }
667
0
               else {
668
0
                  if (sign) {
669
0
                     v = -v;
670
0
                  }
671
0
                  return c;
672
0
               }
673
0
674
0
               if (is_digit(*c)) {
675
0
                  v = v * 10 + (*c - '0');
676
0
                  ++c;
677
0
               }
678
0
               else {
679
0
                  if (sign) {
680
0
                     v = -v;
681
0
                  }
682
0
                  return c;
683
0
               }
684
0
            }
685
0
         }
686
0
      }
687
0
688
0
      if (is_digit(*c)) {
689
0
         if (sign) {
690
0
            if (would_overflow_negative<T>(v, *c)) [[unlikely]] {
691
0
               return {};
692
0
            }
693
0
            v = -1 * v;
694
0
            v = v * 10 - (*c - '0');
695
0
         }
696
0
         else {
697
0
            if (would_overflow_positive<T>(v, *c)) [[unlikely]] {
698
0
               return {};
699
0
            }
700
0
            v = v * 10 + (*c - '0');
701
0
         }
702
0
         ++c;
703
0
         if (is_digit(*c)) [[unlikely]] {
704
0
            return {};
705
0
         }
706
0
         return c;
707
0
      }
708
0
709
0
      if (sign) {
710
0
         v = -v;
711
0
      }
712
0
      return c;
713
0
   }
714
715
   template <std::integral T, class Char>
716
      requires(std::is_signed_v<T>)
717
   GLZ_ALWAYS_INLINE constexpr bool atoi(T& v, Char*& c) noexcept
718
0
   {
719
0
      using X = std::decay_t<T>;
720
0
      using utype = std::make_unsigned_t<X>;
721
0
722
0
      const uint8_t sign = *c == '-';
723
0
      if (auto ptr = parse_int(v, reinterpret_cast<const uint8_t*>(c))) [[likely]] {
724
0
         c = reinterpret_cast<const Char*>(ptr);
725
0
         if (*c == 'e' || *c == 'E') {
726
0
            ++c;
727
0
         }
728
0
         else {
729
0
            if (*c == '.') [[unlikely]] {
730
0
               return false;
731
0
            }
732
0
            return true;
733
0
         }
734
0
735
0
         c += (*c == '+');
736
0
737
0
         if (not is_digit(*c)) [[unlikely]] {
738
0
            return false;
739
0
         }
740
0
         const uint32_t exp = parse_exponent(c);
741
0
         // As in the unsigned overload: only a non-zero magnitude can overflow, so "0e19" and
742
0
         // "-0e19" are in range for every width. `v` is already the signed mantissa, and negative
743
0
         // zero compares equal to zero, so both spellings land here with the value they should keep.
744
0
         if constexpr (sizeof(T) == 1) {
745
0
            if (exp > 2) [[unlikely]] {
746
0
               return v == 0;
747
0
            }
748
0
         }
749
0
         else if constexpr (sizeof(T) == 2) {
750
0
            if (exp > 4) [[unlikely]] {
751
0
               return v == 0;
752
0
            }
753
0
         }
754
0
         else if constexpr (sizeof(T) == 4) {
755
0
            if (exp > 9) [[unlikely]] {
756
0
               return v == 0;
757
0
            }
758
0
         }
759
0
         else {
760
0
            if (exp > 18) [[unlikely]] {
761
0
               return v == 0;
762
0
            }
763
0
         }
764
0
765
0
         utype i = utype((utype(v) ^ -sign) + sign);
766
0
         if constexpr (sizeof(T) < 8) {
767
0
            // Scale in a width the product cannot wrap, then range check before narrowing. Scaling
768
0
            // inside `utype` truncated first and checked afterwards, so an out-of-range magnitude
769
0
            // aliased onto an accepted one: "13e2" read as 20 for int8_t and "5e9" as 705032704 for
770
0
            // int32_t. The widest case here is a 4-byte magnitude scaled by 10^9, which stays well
771
0
            // inside uint64_t. The unsigned path already widens the same way.
772
0
            const uint64_t scaled = uint64_t(i) * powers_of_ten_int[exp];
773
0
            v = T((utype(scaled) ^ -sign) + sign);
774
0
            // Bound the magnitude directly rather than subtracting the sign from it: a negative
775
0
            // zero makes `scaled - sign` underflow, and the old narrow expression only survived
776
0
            // that because it promoted to int. A negative value may reach one past the positive
777
0
            // limit, which is exactly INT_MIN's magnitude.
778
0
            return scaled <= uint64_t((std::numeric_limits<T>::max)()) + sign;
779
0
         }
780
0
         else {
781
0
            // Scale the sign-stripped magnitude `i`, not the two's-complement bit pattern of `v`:
782
0
            // for a negative value that pattern is a huge unsigned number, so every negative
783
0
            // 64-bit integer written with an exponent ("-1e2") overflowed and was rejected. The
784
0
            // narrower branches above already scale `i`.
785
0
#if defined(__SIZEOF_INT128__)
786
0
            const __uint128_t res = __uint128_t(i) * powers_of_ten_int[exp];
787
0
            v = T((uint64_t(res) ^ -sign) + sign);
788
0
            // Compare the full 128-bit product. Narrowing it to 64 bits first would let an
789
0
            // out-of-range magnitude alias onto an accepted one, e.g. 9e36 truncating into range.
790
0
            return res <= __uint128_t(9223372036854775807ull + sign);
791
0
#else
792
0
            const auto res = full_multiplication(i, powers_of_ten_int[exp]);
793
0
            v = T((uint64_t(res.low) ^ -sign) + sign);
794
0
            return res.high == 0 && (uint64_t(res.low) <= (9223372036854775807ull + sign));
795
0
#endif
796
0
         }
797
0
      }
798
0
      return false;
799
0
   }
800
801
   // Increase by 8 to support exponentials
802
   inline constexpr std::array<size_t, 4> int_buffer_lengths{16, 16, 24, 32};
803
804
   template <std::integral T, class Char>
805
   GLZ_ALWAYS_INLINE constexpr bool atoi(T& v, const Char*& it, const Char* end) noexcept
806
0
   {
807
0
      // The number of characters needed at most for each type, rounded to nearest 8 bytes
808
0
      constexpr auto buffer_length = int_buffer_lengths[std::bit_width(sizeof(T)) - 1];
809
0
      // We copy the rest of the buffer, or buffer_length bytes, into a null terminated buffer. The
810
0
      // trailing byte is never written by the copy, so the null-terminated atoi below always halts
811
0
      // inside the array even when the input fills it: the exponent scan stops at a non-digit rather
812
0
      // than after a fixed digit count, and a value-initialized array alone would not terminate a
813
0
      // copy that covers every byte.
814
0
      std::array<char, buffer_length + 1> data{};
815
0
      const auto n = size_t(end - it);
816
0
      if (n > 0) [[likely]] {
817
0
         const auto truncated = n > buffer_length;
818
0
         std::memcpy(data.data(), it, truncated ? buffer_length : n);
819
0
820
0
         const auto start = data.data();
821
0
         const auto* c = start;
822
0
         const auto valid = glz::atoi(v, c);
823
0
         const auto consumed = size_t(c - start);
824
0
         it += consumed;
825
0
         // Reaching the end of a truncated copy means the number was cut off: parsing halted on the
826
0
         // terminator this buffer supplies rather than on a character of the input, so what parsed is
827
0
         // a prefix and its value is not the number's. Only zero padding can stretch a number this
828
0
         // far -- no in-range integer needs buffer_length characters -- but a caller that ignores
829
0
         // trailing content would otherwise take the prefix's value as the answer.
830
0
         return valid && not(truncated && consumed == buffer_length);
831
0
      }
832
0
      else [[unlikely]] {
833
0
         return false;
834
0
      }
835
0
   }
Unexecuted instantiation: _ZN3glz4atoiITkNSt3__18integralElcEEbRT_RPKT0_S6_
Unexecuted instantiation: _ZN3glz4atoiITkNSt3__18integralEmcEEbRT_RPKT0_S6_
836
}
837
838
namespace glz::detail
839
{
840
   GLZ_ALWAYS_INLINE constexpr bool is_safe_addition(uint64_t a, uint64_t b) noexcept
841
0
   {
842
0
      return a <= (std::numeric_limits<uint64_t>::max)() - b;
843
0
   }
844
845
   GLZ_ALWAYS_INLINE constexpr bool is_safe_multiplication10(uint64_t a) noexcept
846
0
   {
847
0
      constexpr auto b = (std::numeric_limits<uint64_t>::max)() / 10;
848
0
      return a <= b;
849
0
   }
850
851
   template <class T = uint64_t>
852
   GLZ_ALWAYS_INLINE constexpr bool stoui64(uint64_t& res, const char*& c) noexcept
853
0
   {
854
0
      if (!digit_table[uint8_t(*c)]) [[unlikely]] {
855
0
         return false;
856
0
      }
857
0
858
0
      // maximum number of digits need is: 3, 5, 10, 20, for byte sizes of 1, 2, 4, 8
859
0
      // we need to store one extra space for a digit for sizes of 1, 2, and 4 because we avoid checking for overflow
860
0
      // since we store in a uint64_t
861
0
      constexpr std::array<int64_t, 4> max_digits_from_size = {4, 6, 11, 20};
862
0
      constexpr auto N = max_digits_from_size[std::bit_width(sizeof(T)) - 1];
863
0
864
0
      std::array<uint8_t, N> digits{0};
865
0
      auto next_digit = digits.begin();
866
0
      auto consume_digit = [&c, &next_digit, &digits]() {
867
0
         if (next_digit < digits.cend()) [[likely]] {
868
0
            *next_digit = (*c - '0');
869
0
            ++next_digit;
870
0
         }
871
0
         ++c;
872
0
      };
873
0
874
0
      if (*c == '0') {
875
0
         // digits[i] = 0; already set to zero
876
0
         ++c;
877
0
         ++next_digit;
878
0
879
0
         if (*c == '0') [[unlikely]] {
880
0
            return false;
881
0
         }
882
0
      }
883
0
884
0
      while (digit_table[uint8_t(*c)]) {
885
0
         consume_digit();
886
0
      }
887
0
      auto n = int64_t(std::distance(digits.begin(), next_digit));
888
0
889
0
      if (*c == '.') {
890
0
         ++c;
891
0
         while (digit_table[uint8_t(*c)]) {
892
0
            consume_digit();
893
0
         }
894
0
      }
895
0
896
0
      if (*c == 'e' || *c == 'E') {
897
0
         ++c;
898
0
899
0
         bool negative = false;
900
0
         if (*c == '+' || *c == '-') {
901
0
            negative = (*c == '-');
902
0
            ++c;
903
0
         }
904
0
         // Clamp instead of wrapping: a uint8_t accumulator turns "1e256" into exponent 0, which
905
0
         // aliases an out-of-range magnitude onto an accepted one ("1e256" decoding as 1). The old
906
0
         // `exp < 128` guard could not catch that, since the wrap happened before the test, and it
907
0
         // also left `c` parked mid-number once it did trip.
908
0
         int32_t exp = 0;
909
0
         while (digit_table[uint8_t(*c)]) {
910
0
            if (exp < int32_t(exponent_clamp)) {
911
0
               exp = 10 * exp + (*c - '0');
912
0
            }
913
0
            ++c;
914
0
         }
915
0
         n += negative ? -exp : exp;
916
0
      }
917
0
918
0
      res = 0;
919
0
      if (n < 0) [[unlikely]] {
920
0
         return true;
921
0
      }
922
0
923
0
      if constexpr (std::same_as<T, uint64_t>) {
924
0
         if (n > 20) [[unlikely]] {
925
0
            return false;
926
0
         }
927
0
928
0
         if (n == 20) [[unlikely]] {
929
0
            for (size_t k = 0; k < 19; ++k) {
930
0
               res = 10 * res + digits[k];
931
0
            }
932
0
933
0
            if (is_safe_multiplication10(res)) [[likely]] {
934
0
               res *= 10;
935
0
            }
936
0
            else [[unlikely]] {
937
0
               return false;
938
0
            }
939
0
            if (is_safe_addition(res, digits.back())) [[likely]] {
940
0
               res += digits.back();
941
0
            }
942
0
            else [[unlikely]] {
943
0
               return false;
944
0
            }
945
0
         }
946
0
         else [[likely]] {
947
0
            for (int64_t k = 0; k < n; ++k) {
948
0
               res = 10 * res + digits[k];
949
0
            }
950
0
         }
951
0
      }
952
0
      else {
953
0
         // a value of n == N would result in reading digits[N], which is invalid
954
0
         if (n >= N) [[unlikely]] {
955
0
            return false;
956
0
         }
957
0
         else [[likely]] {
958
0
            for (int64_t k = 0; k < n; ++k) {
959
0
               res = 10 * res + digits[k];
960
0
            }
961
0
         }
962
0
      }
963
0
964
0
      return true;
965
0
   }
966
967
   template <class T = uint64_t>
968
   GLZ_ALWAYS_INLINE constexpr bool stoui64(uint64_t& res, auto& it) noexcept
969
   {
970
      static_assert(sizeof(*it) == sizeof(char));
971
      const char* cur = reinterpret_cast<const char*>(it);
972
      const char* beg = cur;
973
      if (stoui64(res, cur)) {
974
         it += (cur - beg);
975
         return true;
976
      }
977
      return false;
978
   }
979
}
980
981
#if defined(_MSC_VER) && !defined(__clang__)
982
// restore disabled warnings
983
#pragma warning(pop)
984
#endif