Coverage Report

Created: 2026-09-01 06:56

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