Coverage Report

Created: 2025-11-27 06:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/glaze/include/glaze/util/itoa.hpp
Line
Count
Source
1
// Glaze Library
2
// For the license information refer to glaze.hpp
3
4
#pragma once
5
6
// Refactored from: https://github.com/ibireme/c_numconv_benchmark/blob/master/src/itoa/itoa_yy.c
7
8
/*
9
 * Integer to ascii conversion (ANSI C)
10
 *
11
 * Description
12
 *     The itoa function converts an integer value to a character string in
13
 *     decimal and stores the result in the buffer. If value is negative, the
14
 *     resulting string is preceded with a minus sign (-).
15
 *
16
 * Parameters
17
 *     val: Value to be converted.
18
 *     buf: Buffer that holds the result of the conversion.
19
 *
20
 * Return Value
21
 *     A pointer to the end of resulting string.
22
 *
23
 * Notice
24
 *     The resulting string is not null-terminated.
25
 *     The buffer should be large enough to hold any possible result:
26
 *         uint32_t: 10 bytes
27
 *         uint64_t: 20 bytes
28
 *         int32_t: 11 bytes
29
 *         int64_t: 20 bytes
30
 *
31
 * Copyright (c) 2018 YaoYuan <ibireme@gmail.com>.
32
 * Released under the MIT license (MIT).
33
 */
34
35
#include <concepts>
36
#include <cstdint>
37
38
namespace glz
39
{
40
   inline constexpr char char_table[200] = {
41
      '0', '0', '0', '1', '0', '2', '0', '3', '0', '4', '0', '5', '0', '6', '0', '7', '0', '8', '0', '9', '1', '0', '1',
42
      '1', '1', '2', '1', '3', '1', '4', '1', '5', '1', '6', '1', '7', '1', '8', '1', '9', '2', '0', '2', '1', '2', '2',
43
      '2', '3', '2', '4', '2', '5', '2', '6', '2', '7', '2', '8', '2', '9', '3', '0', '3', '1', '3', '2', '3', '3', '3',
44
      '4', '3', '5', '3', '6', '3', '7', '3', '8', '3', '9', '4', '0', '4', '1', '4', '2', '4', '3', '4', '4', '4', '5',
45
      '4', '6', '4', '7', '4', '8', '4', '9', '5', '0', '5', '1', '5', '2', '5', '3', '5', '4', '5', '5', '5', '6', '5',
46
      '7', '5', '8', '5', '9', '6', '0', '6', '1', '6', '2', '6', '3', '6', '4', '6', '5', '6', '6', '6', '7', '6', '8',
47
      '6', '9', '7', '0', '7', '1', '7', '2', '7', '3', '7', '4', '7', '5', '7', '6', '7', '7', '7', '8', '7', '9', '8',
48
      '0', '8', '1', '8', '2', '8', '3', '8', '4', '8', '5', '8', '6', '8', '7', '8', '8', '8', '9', '9', '0', '9', '1',
49
      '9', '2', '9', '3', '9', '4', '9', '5', '9', '6', '9', '7', '9', '8', '9', '9'};
50
51
   template <class T>
52
      requires std::same_as<std::remove_cvref_t<T>, uint32_t>
53
   auto* to_chars(auto* buf, T val) noexcept
54
   {
55
      /* The maximum value of uint32_t is 4294967295 (10 digits), */
56
      /* these digits are named as 'aabbccddee' here.             */
57
      uint32_t aa, bb, cc, dd, ee, aabb, bbcc, ccdd, ddee, aabbcc;
58
59
      /* Leading zero count in the first pair.                    */
60
      uint32_t lz;
61
62
      /* Although most compilers may convert the "division by     */
63
      /* constant value" into "multiply and shift", manual        */
64
      /* conversion can still help some compilers generate        */
65
      /* fewer and better instructions.                           */
66
67
      if (val < 100) { /* 1-2 digits: aa */
68
         lz = val < 10;
69
         std::memcpy(buf, char_table + (val * 2 + lz), 2);
70
         buf -= lz;
71
         return buf + 2;
72
      }
73
      else if (val < 10000) { /* 3-4 digits: aabb */
74
         aa = (val * 5243) >> 19; /* (val / 100) */
75
         bb = val - aa * 100; /* (val % 100) */
76
         lz = aa < 10;
77
         std::memcpy(buf, char_table + (aa * 2 + lz), 2);
78
         buf -= lz;
79
         std::memcpy(&buf[2], char_table + (2 * bb), 2);
80
81
         return buf + 4;
82
      }
83
      else if (val < 1000000) { /* 5-6 digits: aabbcc */
84
         aa = uint32_t((uint64_t(val) * 429497) >> 32); /* (val / 10000) */
85
         bbcc = val - aa * 10000; /* (val % 10000) */
86
         bb = (bbcc * 5243) >> 19; /* (bbcc / 100) */
87
         cc = bbcc - bb * 100; /* (bbcc % 100) */
88
         lz = aa < 10;
89
         std::memcpy(buf, char_table + aa * 2 + lz, 2);
90
         buf -= lz;
91
         std::memcpy(buf + 2, char_table + bb * 2, 2);
92
         std::memcpy(buf + 4, char_table + cc * 2, 2);
93
         return buf + 6;
94
      }
95
      else if (val < 100000000) { /* 7~8 digits: aabbccdd */
96
         /* (val / 10000) */
97
         aabb = uint32_t((uint64_t(val) * 109951163) >> 40);
98
         ccdd = val - aabb * 10000; /* (val % 10000) */
99
         aa = (aabb * 5243) >> 19; /* (aabb / 100) */
100
         cc = (ccdd * 5243) >> 19; /* (ccdd / 100) */
101
         bb = aabb - aa * 100; /* (aabb % 100) */
102
         dd = ccdd - cc * 100; /* (ccdd % 100) */
103
         lz = aa < 10;
104
         std::memcpy(buf, char_table + aa * 2 + lz, 2);
105
         buf -= lz;
106
         std::memcpy(buf + 2, char_table + bb * 2, 2);
107
         std::memcpy(buf + 4, char_table + cc * 2, 2);
108
         std::memcpy(buf + 6, char_table + dd * 2, 2);
109
         return buf + 8;
110
      }
111
      else { /* 9~10 digits: aabbccddee */
112
         /* (val / 10000) */
113
         aabbcc = uint32_t((uint64_t(val) * 3518437209ul) >> 45);
114
         /* (aabbcc / 10000) */
115
         aa = uint32_t((uint64_t(aabbcc) * 429497) >> 32);
116
         ddee = val - aabbcc * 10000; /* (val % 10000) */
117
         bbcc = aabbcc - aa * 10000; /* (aabbcc % 10000) */
118
         bb = (bbcc * 5243) >> 19; /* (bbcc / 100) */
119
         dd = (ddee * 5243) >> 19; /* (ddee / 100) */
120
         cc = bbcc - bb * 100; /* (bbcc % 100) */
121
         ee = ddee - dd * 100; /* (ddee % 100) */
122
         lz = aa < 10;
123
         std::memcpy(buf, char_table + aa * 2 + lz, 2);
124
         buf -= lz;
125
         std::memcpy(buf + 2, char_table + bb * 2, 2);
126
         std::memcpy(buf + 4, char_table + cc * 2, 2);
127
         std::memcpy(buf + 6, char_table + dd * 2, 2);
128
         std::memcpy(buf + 8, char_table + ee * 2, 2);
129
         return buf + 10;
130
      }
131
   }
132
133
   template <class T>
134
      requires std::same_as<std::remove_cvref_t<T>, int32_t>
135
   auto* to_chars(auto* buf, T x) noexcept
136
   {
137
      *buf = '-';
138
      // shifts are necessary to have the numeric_limits<int32_t>::min case
139
      return to_chars(buf + (x < 0), uint32_t(x ^ (x >> 31)) - (x >> 31));
140
   }
141
142
   template <class T>
143
      requires(std::same_as<std::remove_cvref_t<T>, uint32_t>)
144
   GLZ_ALWAYS_INLINE auto* to_chars_u64_len_8(auto* buf, T val) noexcept
145
0
   {
146
0
      /* 8 digits: aabbccdd */
147
0
      const uint32_t aabb = uint32_t((uint64_t(val) * 109951163) >> 40); /* (val / 10000) */
148
0
      const uint32_t ccdd = val - aabb * 10000; /* (val % 10000) */
149
0
      const uint32_t aa = (aabb * 5243) >> 19; /* (aabb / 100) */
150
0
      const uint32_t cc = (ccdd * 5243) >> 19; /* (ccdd / 100) */
151
0
      const uint32_t bb = aabb - aa * 100; /* (aabb % 100) */
152
0
      const uint32_t dd = ccdd - cc * 100; /* (ccdd % 100) */
153
0
      std::memcpy(buf, char_table + aa * 2, 2);
154
0
      std::memcpy(buf + 2, char_table + bb * 2, 2);
155
0
      std::memcpy(buf + 4, char_table + cc * 2, 2);
156
0
      std::memcpy(buf + 6, char_table + dd * 2, 2);
157
0
      return buf + 8;
158
0
   }
159
160
   template <class T>
161
      requires(std::same_as<std::remove_cvref_t<T>, uint32_t>)
162
   GLZ_ALWAYS_INLINE auto* to_chars_u64_len_4(auto* buf, T val) noexcept
163
0
   {
164
0
      /* 4 digits: aabb */
165
0
      const uint32_t aa = (val * 5243) >> 19; /* (val / 100) */
166
0
      const uint32_t bb = val - aa * 100; /* (val % 100) */
167
0
      std::memcpy(buf, char_table + aa * 2, 2);
168
0
      std::memcpy(buf + 2, char_table + bb * 2, 2);
169
0
      return buf + 4;
170
0
   }
171
172
   template <class T>
173
      requires(std::same_as<std::remove_cvref_t<T>, uint32_t>)
174
   inline auto* to_chars_u64_len_1_8(auto* buf, T val) noexcept
175
0
   {
176
0
      uint32_t aa, bb, cc, dd, aabb, bbcc, ccdd, lz;
177
0
178
0
      if (val < 100) { /* 1-2 digits: aa */
179
0
         lz = val < 10;
180
0
         std::memcpy(buf, char_table + val * 2 + lz, 2);
181
0
         buf -= lz;
182
0
         return buf + 2;
183
0
      }
184
0
      else if (val < 10000) { /* 3-4 digits: aabb */
185
0
         aa = (val * 5243) >> 19; /* (val / 100) */
186
0
         bb = val - aa * 100; /* (val % 100) */
187
0
         lz = aa < 10;
188
0
         std::memcpy(buf, char_table + aa * 2 + lz, 2);
189
0
         buf -= lz;
190
0
         std::memcpy(buf + 2, char_table + bb * 2, 2);
191
0
         return buf + 4;
192
0
      }
193
0
      else if (val < 1000000) { /* 5-6 digits: aabbcc */
194
0
         aa = uint32_t((uint64_t(val) * 429497) >> 32); /* (val / 10000) */
195
0
         bbcc = val - aa * 10000; /* (val % 10000) */
196
0
         bb = (bbcc * 5243) >> 19; /* (bbcc / 100) */
197
0
         cc = bbcc - bb * 100; /* (bbcc % 100) */
198
0
         lz = aa < 10;
199
0
         std::memcpy(buf, char_table + aa * 2 + lz, 2);
200
0
         buf -= lz;
201
0
         std::memcpy(buf + 2, char_table + bb * 2, 2);
202
0
         std::memcpy(buf + 4, char_table + cc * 2, 2);
203
0
         return buf + 6;
204
0
      }
205
0
      else { /* 7-8 digits: aabbccdd */
206
0
         /* (val / 10000) */
207
0
         aabb = uint32_t((uint64_t(val) * 109951163) >> 40);
208
0
         ccdd = val - aabb * 10000; /* (val % 10000) */
209
0
         aa = (aabb * 5243) >> 19; /* (aabb / 100) */
210
0
         cc = (ccdd * 5243) >> 19; /* (ccdd / 100) */
211
0
         bb = aabb - aa * 100; /* (aabb % 100) */
212
0
         dd = ccdd - cc * 100; /* (ccdd % 100) */
213
0
         lz = aa < 10;
214
0
         std::memcpy(buf, char_table + aa * 2 + lz, 2);
215
0
         buf -= lz;
216
0
         std::memcpy(buf + 2, char_table + bb * 2, 2);
217
0
         std::memcpy(buf + 4, char_table + cc * 2, 2);
218
0
         std::memcpy(buf + 6, char_table + dd * 2, 2);
219
0
         return buf + 8;
220
0
      }
221
0
   }
222
223
   template <class T>
224
      requires(std::same_as<std::remove_cvref_t<T>, uint32_t>)
225
   auto* to_chars_u64_len_5_8(auto* buf, T val) noexcept
226
0
   {
227
0
      if (val < 1000000) { /* 5-6 digits: aabbcc */
228
0
         const uint32_t aa = uint32_t((uint64_t(val) * 429497) >> 32); /* (val / 10000) */
229
0
         const uint32_t bbcc = val - aa * 10000; /* (val % 10000) */
230
0
         const uint32_t bb = (bbcc * 5243) >> 19; /* (bbcc / 100) */
231
0
         const uint32_t cc = bbcc - bb * 100; /* (bbcc % 100) */
232
0
         const uint32_t lz = aa < 10;
233
0
         std::memcpy(buf, char_table + aa * 2 + lz, 2);
234
0
         buf -= lz;
235
0
         std::memcpy(buf + 2, char_table + bb * 2, 2);
236
0
         std::memcpy(buf + 4, char_table + cc * 2, 2);
237
0
         return buf + 6;
238
0
      }
239
0
      else { /* 7-8 digits: aabbccdd */
240
0
         /* (val / 10000) */
241
0
         const uint32_t aabb = uint32_t((uint64_t(val) * 109951163) >> 40);
242
0
         const uint32_t ccdd = val - aabb * 10000; /* (val % 10000) */
243
0
         const uint32_t aa = (aabb * 5243) >> 19; /* (aabb / 100) */
244
0
         const uint32_t cc = (ccdd * 5243) >> 19; /* (ccdd / 100) */
245
0
         const uint32_t bb = aabb - aa * 100; /* (aabb % 100) */
246
0
         const uint32_t dd = ccdd - cc * 100; /* (ccdd % 100) */
247
0
         const uint32_t lz = aa < 10;
248
0
         std::memcpy(buf, char_table + aa * 2 + lz, 2);
249
0
         buf -= lz;
250
0
         std::memcpy(buf + 2, char_table + bb * 2, 2);
251
0
         std::memcpy(buf + 4, char_table + cc * 2, 2);
252
0
         std::memcpy(buf + 6, char_table + dd * 2, 2);
253
0
         return buf + 8;
254
0
      }
255
0
   }
256
257
   template <class T>
258
      requires(std::same_as<std::remove_cvref_t<T>, uint64_t>)
259
   auto* to_chars(auto* buf, T val) noexcept
260
0
   {
261
0
      if (val < 100000000) { /* 1-8 digits */
262
0
         buf = to_chars_u64_len_1_8(buf, uint32_t(val));
263
0
         return buf;
264
0
      }
265
0
      else if (val < 100000000ull * 100000000ull) { /* 9-16 digits */
266
0
         const uint64_t hgh = val / 100000000;
267
0
         const auto low = uint32_t(val - hgh * 100000000); /* (val % 100000000) */
268
0
         buf = to_chars_u64_len_1_8(buf, uint32_t(hgh));
269
0
         buf = to_chars_u64_len_8(buf, low);
270
0
         return buf;
271
0
      }
272
0
      else { /* 17-20 digits */
273
0
         const uint64_t tmp = val / 100000000;
274
0
         const auto low = uint32_t(val - tmp * 100000000); /* (val % 100000000) */
275
0
         const auto hgh = uint32_t(tmp / 10000);
276
0
         const auto mid = uint32_t(tmp - hgh * 10000); /* (tmp % 10000) */
277
0
         buf = to_chars_u64_len_5_8(buf, hgh);
278
0
         buf = to_chars_u64_len_4(buf, mid);
279
0
         buf = to_chars_u64_len_8(buf, low);
280
0
         return buf;
281
0
      }
282
0
   }
283
284
   template <class T>
285
      requires std::same_as<std::remove_cvref_t<T>, int64_t>
286
   auto* to_chars(auto* buf, T x) noexcept
287
   {
288
      *buf = '-';
289
      // shifts are necessary to have the numeric_limits<int64_t>::min case
290
      return to_chars(buf + (x < 0), uint64_t(x ^ (x >> 63)) - (x >> 63));
291
   }
292
}