/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 | | } |