rapidyaml 0.14.0
parse and emit YAML, and do it fast
Loading...
Searching...
No Matches
charconv.hpp
Go to the documentation of this file.
1#ifndef _C4_CHARCONV_HPP_
2#define _C4_CHARCONV_HPP_
3
4/** @file charconv.hpp Lightweight generic type-safe wrappers for
5 * converting individual values to/from strings.
6 */
7
8#include "c4/language.hpp"
9#include <inttypes.h>
10#include <type_traits>
11#include <climits>
12#include <limits>
13#include <utility>
14
15#include "c4/config.hpp"
16#include "c4/substr.hpp"
17#include "c4/std/std_fwd.hpp"
18#include "c4/memory_util.hpp"
19
20#ifndef C4CORE_NO_FAST_FLOAT
21# if (C4_CPP >= 17)
22# if defined(_MSC_VER)
23# if (C4_MSVC_VERSION >= C4_MSVC_VERSION_2019) // VS2017 and lower do not have these macros
24# include <charconv>
25# define C4CORE_HAVE_STD_TOCHARS 1
26# define C4CORE_HAVE_STD_FROMCHARS 0 // prefer fast_float with MSVC
27# define C4CORE_HAVE_FAST_FLOAT 1
28# else
29# define C4CORE_HAVE_STD_TOCHARS 0
30# define C4CORE_HAVE_STD_FROMCHARS 0
31# define C4CORE_HAVE_FAST_FLOAT 1
32# endif
33# else
34# if __has_include(<charconv>)
35# include <charconv>
36# if defined(__cpp_lib_to_chars)
37# define C4CORE_HAVE_STD_TOCHARS 1
38# define C4CORE_HAVE_STD_FROMCHARS 0 // glibc uses fast_float internally
39# define C4CORE_HAVE_FAST_FLOAT 1
40# else
41# define C4CORE_HAVE_STD_TOCHARS 0
42# define C4CORE_HAVE_STD_FROMCHARS 0
43# define C4CORE_HAVE_FAST_FLOAT 1
44# endif
45# else
46# define C4CORE_HAVE_STD_TOCHARS 0
47# define C4CORE_HAVE_STD_FROMCHARS 0
48# define C4CORE_HAVE_FAST_FLOAT 1
49# endif
50# endif
51# else
52# define C4CORE_HAVE_STD_TOCHARS 0
53# define C4CORE_HAVE_STD_FROMCHARS 0
54# define C4CORE_HAVE_FAST_FLOAT 1
55# endif
56# if C4CORE_HAVE_FAST_FLOAT
57# include "c4/ext/fast_float.hpp"
58# endif
59#elif (C4_CPP >= 17)
60# define C4CORE_HAVE_FAST_FLOAT 0
61# if defined(_MSC_VER)
62# if (C4_MSVC_VERSION >= C4_MSVC_VERSION_2019) // VS2017 and lower do not have these macros
63# include <charconv>
64# define C4CORE_HAVE_STD_TOCHARS 1
65# define C4CORE_HAVE_STD_FROMCHARS 1
66# else
67# define C4CORE_HAVE_STD_TOCHARS 0
68# define C4CORE_HAVE_STD_FROMCHARS 0
69# endif
70# else
71# if __has_include(<charconv>)
72# include <charconv>
73# if defined(__cpp_lib_to_chars)
74# define C4CORE_HAVE_STD_TOCHARS 1
75# define C4CORE_HAVE_STD_FROMCHARS 1 // glibc uses fast_float internally
76# else
77# define C4CORE_HAVE_STD_TOCHARS 0
78# define C4CORE_HAVE_STD_FROMCHARS 0
79# endif
80# else
81# define C4CORE_HAVE_STD_TOCHARS 0
82# define C4CORE_HAVE_STD_FROMCHARS 0
83# endif
84# endif
85#else
86# define C4CORE_HAVE_STD_TOCHARS 0
87# define C4CORE_HAVE_STD_FROMCHARS 0
88# define C4CORE_HAVE_FAST_FLOAT 0
89#endif
90
91
92#if !C4CORE_HAVE_STD_FROMCHARS
93#include <cstdio>
94#endif
95
96
97#if defined(_MSC_VER) && !defined(__clang__)
98# pragma warning(push)
99# pragma warning(disable: 4996) // snprintf/scanf: this function or variable may be unsafe
100# if C4_MSVC_VERSION != C4_MSVC_VERSION_2017
101# pragma warning(disable: 4800) //'int': forcing value to bool 'true' or 'false' (performance warning)
102# endif
103#elif defined(__clang__)
104# pragma clang diagnostic push
105# pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare"
106# pragma clang diagnostic ignored "-Wformat-nonliteral"
107# pragma clang diagnostic ignored "-Wdouble-promotion" // implicit conversion increases floating-point precision
108# pragma clang diagnostic ignored "-Wold-style-cast"
109#elif defined(__GNUC__)
110# pragma GCC diagnostic push
111# pragma GCC diagnostic ignored "-Wformat-nonliteral"
112# pragma GCC diagnostic ignored "-Wdouble-promotion" // implicit conversion increases floating-point precision
113# pragma GCC diagnostic ignored "-Wuseless-cast"
114# pragma GCC diagnostic ignored "-Wold-style-cast"
115#endif
116
117#if defined(__clang__)
118#define C4_NO_UBSAN_IOVRFLW __attribute__((no_sanitize("signed-integer-overflow")))
119#elif defined(__GNUC__)
120#if __GNUC__ > 7
121#define C4_NO_UBSAN_IOVRFLW __attribute__((no_sanitize("signed-integer-overflow")))
122#else
123#define C4_NO_UBSAN_IOVRFLW
124#endif
125#else
126#define C4_NO_UBSAN_IOVRFLW
127#endif
128
129// NOLINTBEGIN(hicpp-signed-bitwise)
130
131namespace c4 {
132
133/** @defgroup doc_charconv Charconv utilities
134 *
135 * Lightweight, very fast generic type-safe wrappers for converting
136 * individual values to/from strings. These are the main generic
137 * functions:
138 * - @ref doc_to_chars and its alias @ref xtoa(): implemented by calling @ref itoa() / @ref utoa() / @ref ftoa() / @ref dtoa() (or generically @ref xtoa())
139 * - @ref doc_from_chars and its alias @ref atox(): implemented by calling @ref atoi() / @ref atou() / @ref atof() / @ref atod() (or generically @ref atox())
140 * - @ref to_chars_sub()
141 * - @ref from_chars_first()
142 * - @ref xtoa() and @ref atox() are implemented in terms of @ref write_dec() / @ref read_dec() et al (see @ref doc_write / @ref doc_read())
143 *
144 * And also some modest brag is in order: these functions are really
145 * fast: faster even than C++17 `std::to_chars()` and
146 * `std::to_chars()`, and many dozens of times faster than the
147 * iostream abominations.
148 *
149 * For example, here are some benchmark comparisons for @ref
150 * doc_from_chars (link leads to the main project README, where these
151 * results are shown more systematically).
152 *
153 * <table>
154 * <caption id="atox-i64-results">atox,int64_t</caption>
155 * <tr><th>g++12, linux <th>Visual Studio 2019
156 * <tr><td> \image html linux-x86_64-gxx12.1-Release-c4core-bm-charconv-atox-mega_bytes_per_second-i64.png <td> \image html windows-x86_64-vs2019-Release-c4core-bm-charconv-atox-mega_bytes_per_second-i64.png
157 * </table>
158 *
159 * <table>
160 * <caption id="xtoa-i64-results">xtoa,int64_t</caption>
161 * <tr><th>g++12, linux <th>Visual Studio 2019
162 * <tr><td> \image html linux-x86_64-gxx12.1-Release-c4core-bm-charconv-xtoa-mega_bytes_per_second-i64.png <td> \image html windows-x86_64-vs2019-Release-c4core-bm-charconv-xtoa-mega_bytes_per_second-i64.png
163 * </table>
164 *
165 * To parse floating point, c4core uses
166 * [fastfloat](https://github.com/fastfloat/fast_float), which is
167 * extremely fast, by an even larger factor:
168 *
169 * <table>
170 * <caption id="atox-float-results">atox,float</caption>
171 * <tr><th>g++12, linux <th>Visual Studio 2019
172 * <tr><td> \image html linux-x86_64-gxx12.1-Release-c4core-bm-charconv-atof-mega_bytes_per_second-float.png <td> \image html windows-x86_64-vs2019-Release-c4core-bm-charconv-atof-mega_bytes_per_second-float.png
173 * </table>
174 *
175 * @{
176 */
177
178#if C4CORE_HAVE_STD_TOCHARS
179/** @warning Use only the symbol. Do not rely on the type or naked value of this enum. */
180typedef enum : std::underlying_type<std::chars_format>::type {
181 /** print the real number in floating point format (like %f) */
182 FTOA_FLOAT = static_cast<std::underlying_type<std::chars_format>::type>(std::chars_format::fixed),
183 /** print the real number in scientific format (like %e) */
184 FTOA_SCIENT = static_cast<std::underlying_type<std::chars_format>::type>(std::chars_format::scientific),
185 /** print the real number in flexible format (like %g) */
186 FTOA_FLEX = static_cast<std::underlying_type<std::chars_format>::type>(std::chars_format::general),
187 /** print the real number in hexadecimal format (like %a) */
188 FTOA_HEXA = static_cast<std::underlying_type<std::chars_format>::type>(std::chars_format::hex),
190#else
191/** @warning Use only the symbol. Do not rely on the type or naked value of this enum. */
192typedef enum : char {
193 /** print the real number in floating point format (like %f) */
195 /** print the real number in scientific format (like %e) */
197 /** print the real number in flexible format (like %g) */
199 /** print the real number in hexadecimal format (like %a) */
202#endif
203
204/** @cond dev */
205/** in some platforms, int,unsigned int
206 * are not any of int8_t...int64_t and
207 * long,unsigned long are not any of uint8_t...uint64_t */
208template<class T>
209struct is_fixed_length
210{
211 enum : bool {
212 /** true if T is one of the fixed length signed types */
213 value_i = (std::is_integral<T>::value
214 && (std::is_same<T, int8_t>::value
215 || std::is_same<T, int16_t>::value
216 || std::is_same<T, int32_t>::value
217 || std::is_same<T, int64_t>::value)),
218 /** true if T is one of the fixed length unsigned types */
219 value_u = (std::is_integral<T>::value
220 && (std::is_same<T, uint8_t>::value
221 || std::is_same<T, uint16_t>::value
222 || std::is_same<T, uint32_t>::value
223 || std::is_same<T, uint64_t>::value)),
224 /** true if T is one of the fixed length signed or unsigned types */
225 value = value_i || value_u
226 };
227};
228/** @endcond */
229
230
231//-----------------------------------------------------------------------------
232//-----------------------------------------------------------------------------
233//-----------------------------------------------------------------------------
234
235#if defined(_MSC_VER) && !defined(__clang__)
236# pragma warning(push)
237#elif defined(__clang__)
238# pragma clang diagnostic push
239#elif defined(__GNUC__)
240# pragma GCC diagnostic push
241# pragma GCC diagnostic ignored "-Wconversion"
242# if __GNUC__ >= 6
243# pragma GCC diagnostic ignored "-Wnull-dereference"
244# endif
245#endif
246
247/** @cond dev */
248namespace detail {
249
250/* python command to get the values below:
251def dec(v):
252 return str(v)
253for bits in (8, 16, 32, 64):
254 imin, imax, umax = (-(1 << (bits - 1))), (1 << (bits - 1)) - 1, (1 << bits) - 1
255 for vname, v in (("imin", imin), ("imax", imax), ("umax", umax)):
256 for f in (bin, oct, dec, hex):
257 print(f"{bits}b: {vname}={v} {f.__name__}: len={len(f(v)):2d}: {v} {f(v)}")
258*/
259
260// do not use the type as the template argument because in some
261// platforms long!=int32 and long!=int64. Just use the numbytes
262// which is more generic and spares lengthy SFINAE code.
263template<size_t num_bytes, bool is_signed> struct charconv_digits_;
264template<class T> using charconv_digits = charconv_digits_<sizeof(T), std::is_signed<T>::value>;
265
266template<> struct charconv_digits_<1u, true> // int8_t
267{
268 enum : size_t {
269 maxdigits_bin = 1 + 2 + 8, // -128==-0b10000000
270 maxdigits_oct = 1 + 2 + 3, // -128==-0o200
271 maxdigits_dec = 1 + 3, // -128
272 maxdigits_hex = 1 + 2 + 2, // -128==-0x80
273 maxdigits_bin_nopfx = 8, // -128==-0b10000000
274 maxdigits_oct_nopfx = 3, // -128==-0o200
275 maxdigits_dec_nopfx = 3, // -128
276 maxdigits_hex_nopfx = 2, // -128==-0x80
277 };
278 // min values without sign!
279 static constexpr csubstr min_value_dec() noexcept { return csubstr("128"); }
280 static constexpr csubstr min_value_hex() noexcept { return csubstr("80"); }
281 static constexpr csubstr min_value_oct() noexcept { return csubstr("200"); }
282 static constexpr csubstr min_value_bin() noexcept { return csubstr("10000000"); }
283 static constexpr csubstr max_value_dec() noexcept { return csubstr("127"); }
284 static constexpr bool is_oct_overflow(csubstr str) noexcept { return !((str.len < 3) || (str.len == 3 && str.str[0] <= '1')); }
285};
286template<> struct charconv_digits_<1u, false> // uint8_t
287{
288 enum : size_t {
289 maxdigits_bin = 2 + 8, // 255 0b11111111
290 maxdigits_oct = 2 + 3, // 255 0o377
291 maxdigits_dec = 3, // 255
292 maxdigits_hex = 2 + 2, // 255 0xff
293 maxdigits_bin_nopfx = 8, // 255 0b11111111
294 maxdigits_oct_nopfx = 3, // 255 0o377
295 maxdigits_dec_nopfx = 3, // 255
296 maxdigits_hex_nopfx = 2, // 255 0xff
297 };
298 static constexpr csubstr max_value_dec() noexcept { return csubstr("255"); }
299 static constexpr bool is_oct_overflow(csubstr str) noexcept { return !((str.len < 3) || (str.len == 3 && str.str[0] <= '3')); }
300};
301template<> struct charconv_digits_<2u, true> // int16_t
302{
303 enum : size_t {
304 maxdigits_bin = 1 + 2 + 16, // -32768 -0b1000000000000000
305 maxdigits_oct = 1 + 2 + 6, // -32768 -0o100000
306 maxdigits_dec = 1 + 5, // -32768 -32768
307 maxdigits_hex = 1 + 2 + 4, // -32768 -0x8000
308 maxdigits_bin_nopfx = 16, // -32768 -0b1000000000000000
309 maxdigits_oct_nopfx = 6, // -32768 -0o100000
310 maxdigits_dec_nopfx = 5, // -32768 -32768
311 maxdigits_hex_nopfx = 4, // -32768 -0x8000
312 };
313 // min values without sign!
314 static constexpr csubstr min_value_dec() noexcept { return csubstr("32768"); }
315 static constexpr csubstr min_value_hex() noexcept { return csubstr("8000"); }
316 static constexpr csubstr min_value_oct() noexcept { return csubstr("100000"); }
317 static constexpr csubstr min_value_bin() noexcept { return csubstr("1000000000000000"); }
318 static constexpr csubstr max_value_dec() noexcept { return csubstr("32767"); }
319 static constexpr bool is_oct_overflow(csubstr str) noexcept { return !(str.len < 6); }
320};
321template<> struct charconv_digits_<2u, false> // uint16_t
322{
323 enum : size_t {
324 maxdigits_bin = 2 + 16, // 65535 0b1111111111111111
325 maxdigits_oct = 2 + 6, // 65535 0o177777
326 maxdigits_dec = 6, // 65535 65535
327 maxdigits_hex = 2 + 4, // 65535 0xffff
328 maxdigits_bin_nopfx = 16, // 65535 0b1111111111111111
329 maxdigits_oct_nopfx = 6, // 65535 0o177777
330 maxdigits_dec_nopfx = 6, // 65535 65535
331 maxdigits_hex_nopfx = 4, // 65535 0xffff
332 };
333 static constexpr csubstr max_value_dec() noexcept { return csubstr("65535"); }
334 static constexpr bool is_oct_overflow(csubstr str) noexcept { return !((str.len < 6) || (str.len == 6 && str.str[0] <= '1')); }
335};
336template<> struct charconv_digits_<4u, true> // int32_t
337{
338 enum : size_t {
339 maxdigits_bin = 1 + 2 + 32, // len=35: -2147483648 -0b10000000000000000000000000000000
340 maxdigits_oct = 1 + 2 + 11, // len=14: -2147483648 -0o20000000000
341 maxdigits_dec = 1 + 10, // len=11: -2147483648 -2147483648
342 maxdigits_hex = 1 + 2 + 8, // len=11: -2147483648 -0x80000000
343 maxdigits_bin_nopfx = 32, // len=35: -2147483648 -0b10000000000000000000000000000000
344 maxdigits_oct_nopfx = 11, // len=14: -2147483648 -0o20000000000
345 maxdigits_dec_nopfx = 10, // len=11: -2147483648 -2147483648
346 maxdigits_hex_nopfx = 8, // len=11: -2147483648 -0x80000000
347 };
348 // min values without sign!
349 static constexpr csubstr min_value_dec() noexcept { return csubstr("2147483648"); }
350 static constexpr csubstr min_value_hex() noexcept { return csubstr("80000000"); }
351 static constexpr csubstr min_value_oct() noexcept { return csubstr("20000000000"); }
352 static constexpr csubstr min_value_bin() noexcept { return csubstr("10000000000000000000000000000000"); }
353 static constexpr csubstr max_value_dec() noexcept { return csubstr("2147483647"); }
354 static constexpr bool is_oct_overflow(csubstr str) noexcept { return !((str.len < 11) || (str.len == 11 && str.str[0] <= '1')); }
355};
356template<> struct charconv_digits_<4u, false> // uint32_t
357{
358 enum : size_t {
359 maxdigits_bin = 2 + 32, // len=34: 4294967295 0b11111111111111111111111111111111
360 maxdigits_oct = 2 + 11, // len=13: 4294967295 0o37777777777
361 maxdigits_dec = 10, // len=10: 4294967295 4294967295
362 maxdigits_hex = 2 + 8, // len=10: 4294967295 0xffffffff
363 maxdigits_bin_nopfx = 32, // len=34: 4294967295 0b11111111111111111111111111111111
364 maxdigits_oct_nopfx = 11, // len=13: 4294967295 0o37777777777
365 maxdigits_dec_nopfx = 10, // len=10: 4294967295 4294967295
366 maxdigits_hex_nopfx = 8, // len=10: 4294967295 0xffffffff
367 };
368 static constexpr csubstr max_value_dec() noexcept { return csubstr("4294967295"); }
369 static constexpr bool is_oct_overflow(csubstr str) noexcept { return !((str.len < 11) || (str.len == 11 && str.str[0] <= '3')); }
370};
371template<> struct charconv_digits_<8u, true> // int64_t
372{
373 enum : size_t {
374 maxdigits_bin = 1 + 2 + 64, // len=67: -9223372036854775808 -0b1000000000000000000000000000000000000000000000000000000000000000
375 maxdigits_oct = 1 + 2 + 22, // len=25: -9223372036854775808 -0o1000000000000000000000
376 maxdigits_dec = 1 + 19, // len=20: -9223372036854775808 -9223372036854775808
377 maxdigits_hex = 1 + 2 + 16, // len=19: -9223372036854775808 -0x8000000000000000
378 maxdigits_bin_nopfx = 64, // len=67: -9223372036854775808 -0b1000000000000000000000000000000000000000000000000000000000000000
379 maxdigits_oct_nopfx = 22, // len=25: -9223372036854775808 -0o1000000000000000000000
380 maxdigits_dec_nopfx = 19, // len=20: -9223372036854775808 -9223372036854775808
381 maxdigits_hex_nopfx = 16, // len=19: -9223372036854775808 -0x8000000000000000
382 };
383 static constexpr csubstr min_value_dec() noexcept { return csubstr("9223372036854775808"); }
384 static constexpr csubstr min_value_hex() noexcept { return csubstr("8000000000000000"); }
385 static constexpr csubstr min_value_oct() noexcept { return csubstr("1000000000000000000000"); }
386 static constexpr csubstr min_value_bin() noexcept { return csubstr("1000000000000000000000000000000000000000000000000000000000000000"); }
387 static constexpr csubstr max_value_dec() noexcept { return csubstr("9223372036854775807"); }
388 static constexpr bool is_oct_overflow(csubstr str) noexcept { return !(str.len < 22); }
389};
390template<> struct charconv_digits_<8u, false> // uint64_t
391{
392 enum : size_t {
393 maxdigits_bin = 2 + 64, // len=66: 18446744073709551615 0b1111111111111111111111111111111111111111111111111111111111111111
394 maxdigits_oct = 2 + 22, // len=24: 18446744073709551615 0o1777777777777777777777
395 maxdigits_dec = 20, // len=20: 18446744073709551615 18446744073709551615
396 maxdigits_hex = 2 + 16, // len=18: 18446744073709551615 0xffffffffffffffff
397 maxdigits_bin_nopfx = 64, // len=66: 18446744073709551615 0b1111111111111111111111111111111111111111111111111111111111111111
398 maxdigits_oct_nopfx = 22, // len=24: 18446744073709551615 0o1777777777777777777777
399 maxdigits_dec_nopfx = 20, // len=20: 18446744073709551615 18446744073709551615
400 maxdigits_hex_nopfx = 16, // len=18: 18446744073709551615 0xffffffffffffffff
401 };
402 static constexpr csubstr max_value_dec() noexcept { return csubstr("18446744073709551615"); }
403 static constexpr bool is_oct_overflow(csubstr str) noexcept { return !((str.len < 22) || (str.len == 22 && str.str[0] <= '1')); }
404};
405} // namespace detail
406
407// Helper macros, undefined below
408#define _c4append(c) { if(C4_LIKELY(pos < buf.len)) { buf.str[pos++] = static_cast<char>(c); } else { ++pos; } }
409#define _c4appendhex(i) { if(C4_LIKELY(pos < buf.len)) { buf.str[pos++] = hexchars[i]; } else { ++pos; } }
410
411/** @endcond */
412
413
414//-----------------------------------------------------------------------------
415//-----------------------------------------------------------------------------
416//-----------------------------------------------------------------------------
417
418
419/** @defgroup doc_digits Get number of digits
420 *
421 * @note At first sight this code may look heavily branchy and
422 * therefore inefficient. However, measurements revealed this to be
423 * the fastest among the alternatives.
424 *
425 * @see https://github.com/biojppm/c4core/pull/77
426 *
427 * @{
428 */
429
430/** decimal digits for 8 bit integers */
431template<class T>
432C4_CONSTEXPR14 C4_ALWAYS_INLINE
433auto digits_dec(T v) noexcept
434 -> typename std::enable_if<sizeof(T) == 1u, unsigned>::type
435{
436 C4_STATIC_ASSERT(std::is_integral<T>::value);
437 C4_ASSERT(v >= 0);
438 return ((v >= 100) ? 3u : ((v >= 10) ? 2u : 1u));
439}
440
441/** decimal digits for 16 bit integers */
442template<class T>
443C4_CONSTEXPR14 C4_ALWAYS_INLINE
444auto digits_dec(T v) noexcept
445 -> typename std::enable_if<sizeof(T) == 2u, unsigned>::type
446{
447 C4_STATIC_ASSERT(std::is_integral<T>::value);
448 C4_ASSERT(v >= 0);
449 return ((v >= 10000) ? 5u : (v >= 1000) ? 4u : (v >= 100) ? 3u : (v >= 10) ? 2u : 1u);
450}
451
452/** decimal digits for 32 bit integers */
453template<class T>
454C4_CONSTEXPR14 C4_ALWAYS_INLINE
455auto digits_dec(T v) noexcept
456 -> typename std::enable_if<sizeof(T) == 4u, unsigned>::type
457{
458 C4_STATIC_ASSERT(std::is_integral<T>::value);
459 C4_ASSERT(v >= 0);
460 return ((v >= 1000000000) ? 10u : (v >= 100000000) ? 9u : (v >= 10000000) ? 8u :
461 (v >= 1000000) ? 7u : (v >= 100000) ? 6u : (v >= 10000) ? 5u :
462 (v >= 1000) ? 4u : (v >= 100) ? 3u : (v >= 10) ? 2u : 1u);
463}
464
465/** decimal digits for 64 bit integers */
466template<class T>
467C4_CONSTEXPR14 C4_ALWAYS_INLINE
468auto digits_dec(T v) noexcept
469 -> typename std::enable_if<sizeof(T) == 8u, unsigned>::type
470{
471 // thanks @fargies!!!
472 // https://github.com/biojppm/c4core/pull/77#issuecomment-1063753568
473 C4_STATIC_ASSERT(std::is_integral<T>::value);
474 C4_ASSERT(v >= 0);
475 if(v >= 1000000000) // 10
476 {
477 if(v >= 100000000000000) // 15 [15-20] range
478 {
479 if(v >= 100000000000000000) // 18 (15 + (20 - 15) / 2)
480 {
481 if((typename std::make_unsigned<T>::type)v >= 10000000000000000000u) // 20
482 return 20u;
483 else
484 return (v >= 1000000000000000000) ? 19u : 18u;
485 }
486 else if(v >= 10000000000000000) // 17
487 {
488 return 17u;
489 }
490 else
491 {
492 return(v >= 1000000000000000) ? 16u : 15u;
493 }
494 }
495 else if(v >= 1000000000000) // 13
496 {
497 return (v >= 10000000000000) ? 14u : 13u;
498 }
499 else if(v >= 100000000000) // 12
500 {
501 return 12;
502 }
503 else
504 {
505 return(v >= 10000000000) ? 11u : 10u;
506 }
507 }
508 else if(v >= 10000) // 5 [5-9] range
509 {
510 if(v >= 10000000) // 8
511 return (v >= 100000000) ? 9u : 8u;
512 else if(v >= 1000000) // 7
513 return 7;
514 else
515 return (v >= 100000) ? 6u : 5u;
516 }
517 else if(v >= 100)
518 {
519 return (v >= 1000) ? 4u : 3u;
520 }
521 else
522 {
523 return (v >= 10) ? 2u : 1u;
524 }
525}
526
527
528/** return the number of digits required to encode an hexadecimal number. */
529template<class T>
530C4_CONSTEXPR14 C4_ALWAYS_INLINE unsigned digits_hex(T v) noexcept
531{
532 C4_STATIC_ASSERT(std::is_integral<T>::value);
533 C4_ASSERT(v >= 0);
534 return v ? 1u + (msb((typename std::make_unsigned<T>::type)v) >> 2u) : 1u;
535}
536
537/** return the number of digits required to encode a binary number. */
538template<class T>
539C4_CONSTEXPR14 C4_ALWAYS_INLINE unsigned digits_bin(T v) noexcept
540{
541 C4_STATIC_ASSERT(std::is_integral<T>::value);
542 C4_ASSERT(v >= 0);
543 return v ? 1u + msb((typename std::make_unsigned<T>::type)v) : 1u;
544}
545
546/** return the number of digits required to encode an octal number. */
547template<class T>
548C4_CONSTEXPR14 C4_ALWAYS_INLINE unsigned digits_oct(T v_) noexcept
549{
550 // TODO: is there a better way?
551 C4_STATIC_ASSERT(std::is_integral<T>::value);
552 C4_ASSERT(v_ >= 0);
553 using U = typename std::conditional<sizeof(T) <= sizeof(unsigned),
554 unsigned,
555 typename std::make_unsigned<T>::type>::type;
556 U v = (U) v_; // safe because we require v_ >= 0 // NOLINT
557 uint32_t __n = 1;
558 enum : U {
559 __b2 = 64u,
560 __b3 = 64u * 8u,
561 __b4 = 64u * 8u * 8u,
562 };
563 while(true)
564 {
565 if(v < 8u)
566 return __n;
567 else if(v < __b2)
568 return __n + 1;
569 else if(v < __b3)
570 return __n + 2;
571 else if(v < __b4)
572 return __n + 3;
573 v /= (U) __b4;
574 __n += 4;
575 }
576}
577
578/** @} */
579
580
581//-----------------------------------------------------------------------------
582//-----------------------------------------------------------------------------
583//-----------------------------------------------------------------------------
584
585/** @cond dev */
586namespace detail {
587C4_INLINE_CONSTEXPR const char hexchars[] = "0123456789abcdef";
588C4_INLINE_CONSTEXPR const char digits0099[] =
589 "0001020304050607080910111213141516171819"
590 "2021222324252627282930313233343536373839"
591 "4041424344454647484950515253545556575859"
592 "6061626364656667686970717273747576777879"
593 "8081828384858687888990919293949596979899";
594} // namespace detail
595/** @endcond */
596
597C4_SUPPRESS_WARNING_GCC_PUSH
598C4_SUPPRESS_WARNING_GCC("-Warray-bounds") // gcc has false positives here
599#if (defined(__GNUC__) && (__GNUC__ >= 7))
600C4_SUPPRESS_WARNING_GCC("-Wstringop-overflow") // gcc has false positives here
601#endif
602
603/** @defgroup doc_write_unchecked Write with known number of digits
604 *
605 * Writes a value without checking the buffer length with regards to
606 * the required number of digits to encode the value. It is the
607 * responsibility of the caller to ensure that the provided number of
608 * digits is enough to write the given value. Notwithstanding the
609 * name, assertions are liberally performed, so this code is safe.
610 *
611 * @{ */
612
613template<class T>
614C4_HOT C4_ALWAYS_INLINE
615void write_dec_unchecked(substr buf, T v, unsigned digits_v) noexcept
616{
617 C4_STATIC_ASSERT(std::is_integral<T>::value);
618 C4_ASSERT(v >= 0);
619 C4_ASSERT(buf.len >= digits_v);
620 C4_XASSERT(digits_v == digits_dec(v));
621 // in bm_xtoa: checkoncelog_singlediv_write2
622 while(v >= T(100))
623 {
624 T quo = v;
625 quo /= T(100);
626 const auto num = (v - quo * T(100)) << 1u; // NOLINT
627 v = quo;
628 buf.str[--digits_v] = detail::digits0099[num + 1];
629 buf.str[--digits_v] = detail::digits0099[num];
630 }
631 if(v >= T(10))
632 {
633 C4_ASSERT(digits_v == 2);
634 const auto num = v << 1u;
635 buf.str[1] = detail::digits0099[num + 1];
636 buf.str[0] = detail::digits0099[num];
637 }
638 else
639 {
640 C4_ASSERT(digits_v == 1);
641 buf.str[0] = (char)('0' + v);
642 }
643}
644
645
646template<class T>
647C4_HOT C4_ALWAYS_INLINE
648void write_hex_unchecked(substr buf, T v, unsigned digits_v) noexcept
649{
650 C4_STATIC_ASSERT(std::is_integral<T>::value);
651 C4_ASSERT(v >= 0);
652 C4_ASSERT(buf.len >= digits_v);
653 C4_XASSERT(digits_v == digits_hex(v));
654 do {
655 buf.str[--digits_v] = detail::hexchars[v & T(15)];
656 v >>= 4;
657 } while(v);
658 C4_ASSERT(digits_v == 0);
659}
660
661
662template<class T>
663C4_HOT C4_ALWAYS_INLINE
664void write_oct_unchecked(substr buf, T v, unsigned digits_v) noexcept
665{
666 C4_STATIC_ASSERT(std::is_integral<T>::value);
667 C4_ASSERT(v >= 0);
668 C4_ASSERT(buf.len >= digits_v);
669 C4_XASSERT(digits_v == digits_oct(v));
670 do {
671 buf.str[--digits_v] = (char)('0' + (v & T(7)));
672 v >>= 3;
673 } while(v);
674 C4_ASSERT(digits_v == 0);
675}
676
677
678template<class T>
679C4_HOT C4_ALWAYS_INLINE
680void write_bin_unchecked(substr buf, T v, unsigned digits_v) noexcept
681{
682 C4_STATIC_ASSERT(std::is_integral<T>::value);
683 C4_ASSERT(v >= 0);
684 C4_ASSERT(buf.len >= digits_v);
685 C4_XASSERT(digits_v == digits_bin(v));
686 do {
687 buf.str[--digits_v] = (char)('0' + (v & T(1)));
688 v >>= 1;
689 } while(v);
690 C4_ASSERT(digits_v == 0);
691}
692
693/** @} */ // write_unchecked
694
695
696//-----------------------------------------------------------------------------
697//-----------------------------------------------------------------------------
698//-----------------------------------------------------------------------------
699
700/** @defgroup doc_write Write a value
701 *
702 * Writes a value without checking the buffer length
703 * decimal number -- but asserting.
704 *
705 * @{ */
706
707/** write an integer to a string in decimal format. This is the
708 * lowest level (and the fastest) function to do this task.
709 * @note does not accept negative numbers
710 * @note the resulting string is NOT zero-terminated.
711 * @note it is ok to call this with an empty or too-small buffer;
712 * no writes will occur, and the required size will be returned
713 * @return the number of characters required for the buffer. */
714template<class T>
715C4_ALWAYS_INLINE size_t write_dec(substr buf, T v) noexcept
716{
717 C4_STATIC_ASSERT(std::is_integral<T>::value);
718 C4_ASSERT(v >= 0);
719 unsigned digits = digits_dec(v);
720 if(C4_LIKELY(buf.len >= digits))
721 write_dec_unchecked(buf, v, digits);
722 return digits;
723}
724
725/** write an integer to a string in hexadecimal format. This is the
726 * lowest level (and the fastest) function to do this task.
727 * @note does not accept negative numbers
728 * @note does not prefix with 0x
729 * @note the resulting string is NOT zero-terminated.
730 * @note it is ok to call this with an empty or too-small buffer;
731 * no writes will occur, and the required size will be returned
732 * @return the number of characters required for the buffer. */
733template<class T>
734C4_ALWAYS_INLINE size_t write_hex(substr buf, T v) noexcept
735{
736 C4_STATIC_ASSERT(std::is_integral<T>::value);
737 C4_ASSERT(v >= 0);
738 unsigned digits = digits_hex(v);
739 if(C4_LIKELY(buf.len >= digits))
740 write_hex_unchecked(buf, v, digits);
741 return digits;
742}
743
744/** write an integer to a string in octal format. This is the
745 * lowest level (and the fastest) function to do this task.
746 * @note does not accept negative numbers
747 * @note does not prefix with 0o
748 * @note the resulting string is NOT zero-terminated.
749 * @note it is ok to call this with an empty or too-small buffer;
750 * no writes will occur, and the required size will be returned
751 * @return the number of characters required for the buffer. */
752template<class T>
753C4_ALWAYS_INLINE size_t write_oct(substr buf, T v) noexcept
754{
755 C4_STATIC_ASSERT(std::is_integral<T>::value);
756 C4_ASSERT(v >= 0);
757 unsigned digits = digits_oct(v);
758 if(C4_LIKELY(buf.len >= digits))
759 write_oct_unchecked(buf, v, digits);
760 return digits;
761}
762
763/** write an integer to a string in binary format. This is the
764 * lowest level (and the fastest) function to do this task.
765 * @note does not accept negative numbers
766 * @note does not prefix with 0b
767 * @note the resulting string is NOT zero-terminated.
768 * @note it is ok to call this with an empty or too-small buffer;
769 * no writes will occur, and the required size will be returned
770 * @return the number of characters required for the buffer. */
771template<class T>
772C4_ALWAYS_INLINE size_t write_bin(substr buf, T v) noexcept
773{
774 C4_STATIC_ASSERT(std::is_integral<T>::value);
775 C4_ASSERT(v >= 0);
776 unsigned digits = digits_bin(v);
777 C4_ASSERT(digits > 0);
778 if(C4_LIKELY(buf.len >= digits))
779 write_bin_unchecked(buf, v, digits);
780 return digits;
781}
782
783
784/** @cond dev */
785namespace detail {
786template<class U> using NumberWriter = size_t (*)(substr, U);
787template<class T, NumberWriter<T> writer>
788size_t write_num_digits(substr buf, T v, size_t num_digits) noexcept
789{
790 C4_STATIC_ASSERT(std::is_integral<T>::value);
791 const size_t ret = writer(buf, v);
792 if(ret >= num_digits)
793 return ret;
794 else if(ret >= buf.len || num_digits > buf.len)
795 return num_digits;
796 C4_ASSERT(num_digits >= ret);
797 const size_t delta = static_cast<size_t>(num_digits - ret); // NOLINT
798 C4_ASSERT(ret + delta <= buf.len);
799 if(ret)
800 memmove(buf.str + delta, buf.str, ret);
801 if(delta)
802 memset(buf.str, '0', delta);
803 return num_digits;
804}
805} // namespace detail
806/** @endcond */
807
808
809/** same as c4::write_dec(), but pad with zeroes on the left such that
810 * the resulting string is @p num_digits wide. If the given number
811 * requires more than num_digits, then the number prevails. */
812template<class T>
813C4_ALWAYS_INLINE size_t write_dec(substr buf, T val, size_t num_digits) noexcept
814{
815 return detail::write_num_digits<T, &write_dec<T>>(buf, val, num_digits);
816}
817
818/** same as c4::write_hex(), but pad with zeroes on the left such that
819 * the resulting string is @p num_digits wide. If the given number
820 * requires more than num_digits, then the number prevails. */
821template<class T>
822C4_ALWAYS_INLINE size_t write_hex(substr buf, T val, size_t num_digits) noexcept
823{
824 return detail::write_num_digits<T, &write_hex<T>>(buf, val, num_digits);
825}
826
827/** same as c4::write_bin(), but pad with zeroes on the left such that
828 * the resulting string is @p num_digits wide. If the given number
829 * requires more than num_digits, then the number prevails. */
830template<class T>
831C4_ALWAYS_INLINE size_t write_bin(substr buf, T val, size_t num_digits) noexcept
832{
833 return detail::write_num_digits<T, &write_bin<T>>(buf, val, num_digits);
834}
835
836/** same as c4::write_oct(), but pad with zeroes on the left such that
837 * the resulting string is @p num_digits wide. If the given number
838 * requires more than num_digits, then the number prevails. */
839template<class T>
840C4_ALWAYS_INLINE size_t write_oct(substr buf, T val, size_t num_digits) noexcept
841{
842 return detail::write_num_digits<T, &write_oct<T>>(buf, val, num_digits);
843}
844
845/** @} */ // write
846
847C4_SUPPRESS_WARNING_GCC_POP
848
849
850//-----------------------------------------------------------------------------
851//-----------------------------------------------------------------------------
852//-----------------------------------------------------------------------------
853
854
855C4_SUPPRESS_WARNING_MSVC_PUSH
856C4_SUPPRESS_WARNING_MSVC(4365) // '=': conversion from 'int' to 'I', signed/unsigned mismatch
857
858/** @defgroup doc_read Read a value
859 *
860 * @{ */
861
862/** read a decimal integer from a string. This is the
863 * lowest level (and the fastest) function to do this task.
864 * @note does not accept negative numbers
865 * @note The string must be trimmed. Whitespace is not accepted.
866 * @note the string must not be empty
867 * @note there is no check for overflow; the value wraps around
868 * in a way similar to the standard C/C++ overflow behavior.
869 * For example, `read_dec<int8_t>("128", &val)` returns true
870 * and val will be set to 0 because 127 is the max i8 value.
871 * @see overflows<T>() to find out if a number string overflows a type range
872 * @return true if the conversion was successful (no overflow check) */
873template<class I>
875C4_ALWAYS_INLINE bool read_dec(csubstr s, I *C4_RESTRICT v) noexcept
876{
877 C4_STATIC_ASSERT(std::is_integral<I>::value);
878 C4_ASSERT(!s.empty());
879 *v = 0;
880 for(char c : s)
881 {
882 if(C4_UNLIKELY(c < '0' || c > '9'))
883 return false;
884 *v = ((*v) * I(10)) + (I(c) - I('0'));
885 }
886 return true;
887}
888
889/** read an hexadecimal integer from a string. This is the
890 * lowest level (and the fastest) function to do this task.
891 * @note does not accept negative numbers
892 * @note does not accept leading 0x or 0X
893 * @note the string must not be empty
894 * @note the string must be trimmed. Whitespace is not accepted.
895 * @note there is no check for overflow; the value wraps around
896 * in a way similar to the standard C/C++ overflow behavior.
897 * For example, `read_hex<int8_t>("80", &val)` returns true
898 * and val will be set to 0 because 7f is the max i8 value.
899 * @see overflows<T>() to find out if a number string overflows a type range
900 * @return true if the conversion was successful (no overflow check) */
901template<class I>
903C4_ALWAYS_INLINE bool read_hex(csubstr s, I *C4_RESTRICT v) noexcept
904{
905 C4_STATIC_ASSERT(std::is_integral<I>::value);
906 C4_ASSERT(!s.empty());
907 *v = 0;
908 for(char c : s)
909 {
910 I cv;
911 if(c >= '0' && c <= '9')
912 cv = I(c) - I('0');
913 else if(c >= 'a' && c <= 'f')
914 cv = I(10) + (I(c) - I('a'));
915 else if(c >= 'A' && c <= 'F')
916 cv = I(10) + (I(c) - I('A'));
917 else
918 return false;
919 *v = ((*v) * I(16)) + cv;
920 }
921 return true;
922}
923
924/** read a binary integer from a string. This is the
925 * lowest level (and the fastest) function to do this task.
926 * @note does not accept negative numbers
927 * @note does not accept leading 0b or 0B
928 * @note the string must not be empty
929 * @note the string must be trimmed. Whitespace is not accepted.
930 * @note there is no check for overflow; the value wraps around
931 * in a way similar to the standard C/C++ overflow behavior.
932 * For example, `read_bin<int8_t>("10000000", &val)` returns true
933 * and val will be set to 0 because 1111111 is the max i8 value.
934 * @see overflows<T>() to find out if a number string overflows a type range
935 * @return true if the conversion was successful (no overflow check) */
936template<class I>
938C4_ALWAYS_INLINE bool read_bin(csubstr s, I *C4_RESTRICT v) noexcept
939{
940 C4_STATIC_ASSERT(std::is_integral<I>::value);
941 C4_ASSERT(!s.empty());
942 *v = 0;
943 for(char c : s)
944 {
945 *v <<= 1;
946 if(c == '1')
947 *v |= 1;
948 else if(c != '0')
949 return false;
950 }
951 return true;
952}
953
954/** read an octal integer from a string. This is the
955 * lowest level (and the fastest) function to do this task.
956 * @note does not accept negative numbers
957 * @note does not accept leading 0o or 0O
958 * @note the string must not be empty
959 * @note the string must be trimmed. Whitespace is not accepted.
960 * @note there is no check for overflow; the value wraps around
961 * in a way similar to the standard C/C++ overflow behavior.
962 * For example, `read_oct<int8_t>("200", &val)` returns true
963 * and val will be set to 0 because 177 is the max i8 value.
964 * @see overflows<T>() to find out if a number string overflows a type range
965 * @return true if the conversion was successful (no overflow check) */
966template<class I>
968C4_ALWAYS_INLINE bool read_oct(csubstr s, I *C4_RESTRICT v) noexcept
969{
970 C4_STATIC_ASSERT(std::is_integral<I>::value);
971 C4_ASSERT(!s.empty());
972 *v = 0;
973 for(char c : s)
974 {
975 if(C4_UNLIKELY(c < '0' || c > '7'))
976 return false;
977 *v = ((*v) * I(8)) + (I(c) - I('0'));
978 }
979 return true;
980}
981
982/** @} */
983
984C4_SUPPRESS_WARNING_MSVC_POP
985
986
987//-----------------------------------------------------------------------------
988//-----------------------------------------------------------------------------
989//-----------------------------------------------------------------------------
990
991C4_SUPPRESS_WARNING_GCC_WITH_PUSH("-Wswitch-default")
992
993/** @cond dev */
994namespace detail {
995inline size_t _itoa2buf(substr buf, size_t pos, csubstr val) noexcept
996{
997 C4_ASSERT(pos < buf.len);
998 C4_ASSERT(pos + val.len <= buf.len);
999 C4_ASSERT(val.len > 0);
1000 memcpy(buf.str + pos, val.str, val.len);
1001 return pos + val.len;
1002}
1003inline size_t _itoa2bufwithdigits(substr buf, size_t pos, size_t num_digits, csubstr val) noexcept
1004{
1005 num_digits = num_digits > val.len ? num_digits - val.len : 0;
1006 C4_ASSERT(num_digits + val.len <= buf.len);
1007 for(size_t i = 0; i < num_digits; ++i)
1008 _c4append('0');
1009 return detail::_itoa2buf(buf, pos, val);
1010}
1011template<class I>
1012C4_NO_INLINE size_t _itoadec2buf(substr buf) noexcept
1013{
1014 using digits_type = detail::charconv_digits<I>;
1015 if(C4_UNLIKELY(buf.len < digits_type::maxdigits_dec))
1016 return digits_type::maxdigits_dec;
1017 buf.str[0] = '-';
1018 return detail::_itoa2buf(buf, 1, digits_type::min_value_dec());
1019}
1020template<class I>
1021C4_NO_INLINE size_t _itoa2buf(substr buf, I radix) noexcept
1022{
1023 using digits_type = detail::charconv_digits<I>;
1024 size_t pos = 0;
1025 if(C4_LIKELY(buf.len > 0))
1026 buf.str[pos++] = '-';
1027 switch(radix) // NOLINT(hicpp-multiway-paths-covered)
1028 {
1029 case I(10):
1030 if(C4_UNLIKELY(buf.len < digits_type::maxdigits_dec))
1031 return digits_type::maxdigits_dec;
1032 pos =_itoa2buf(buf, pos, digits_type::min_value_dec());
1033 break;
1034 case I(16):
1035 if(C4_UNLIKELY(buf.len < digits_type::maxdigits_hex))
1036 return digits_type::maxdigits_hex;
1037 buf.str[pos++] = '0';
1038 buf.str[pos++] = 'x';
1039 pos = _itoa2buf(buf, pos, digits_type::min_value_hex());
1040 break;
1041 case I( 2):
1042 if(C4_UNLIKELY(buf.len < digits_type::maxdigits_bin))
1043 return digits_type::maxdigits_bin;
1044 buf.str[pos++] = '0';
1045 buf.str[pos++] = 'b';
1046 pos = _itoa2buf(buf, pos, digits_type::min_value_bin());
1047 break;
1048 case I( 8):
1049 if(C4_UNLIKELY(buf.len < digits_type::maxdigits_oct))
1050 return digits_type::maxdigits_oct;
1051 buf.str[pos++] = '0';
1052 buf.str[pos++] = 'o';
1053 pos = _itoa2buf(buf, pos, digits_type::min_value_oct());
1054 break;
1055 }
1056 return pos;
1057}
1058template<class I>
1059C4_NO_INLINE size_t _itoa2buf(substr buf, I radix, size_t num_digits) noexcept
1060{
1061 using digits_type = detail::charconv_digits<I>;
1062 size_t pos = 0;
1063 size_t needed_digits = 0;
1064 if(C4_LIKELY(buf.len > 0))
1065 buf.str[pos++] = '-';
1066 switch(radix) // NOLINT(hicpp-multiway-paths-covered)
1067 {
1068 case I(10):
1069 // add 1 to account for -
1070 needed_digits = num_digits+1 > digits_type::maxdigits_dec ? num_digits+1 : digits_type::maxdigits_dec;
1071 if(C4_UNLIKELY(buf.len < needed_digits))
1072 return needed_digits;
1073 pos = _itoa2bufwithdigits(buf, pos, num_digits, digits_type::min_value_dec());
1074 break;
1075 case I(16):
1076 // add 3 to account for -0x
1077 needed_digits = num_digits+3 > digits_type::maxdigits_hex ? num_digits+3 : digits_type::maxdigits_hex;
1078 if(C4_UNLIKELY(buf.len < needed_digits))
1079 return needed_digits;
1080 buf.str[pos++] = '0';
1081 buf.str[pos++] = 'x';
1082 pos = _itoa2bufwithdigits(buf, pos, num_digits, digits_type::min_value_hex());
1083 break;
1084 case I(2):
1085 // add 3 to account for -0b
1086 needed_digits = num_digits+3 > digits_type::maxdigits_bin ? num_digits+3 : digits_type::maxdigits_bin;
1087 if(C4_UNLIKELY(buf.len < needed_digits))
1088 return needed_digits;
1089 C4_ASSERT(buf.len >= digits_type::maxdigits_bin);
1090 buf.str[pos++] = '0';
1091 buf.str[pos++] = 'b';
1092 pos = _itoa2bufwithdigits(buf, pos, num_digits, digits_type::min_value_bin());
1093 break;
1094 case I(8):
1095 // add 3 to account for -0o
1096 needed_digits = num_digits+3 > digits_type::maxdigits_oct ? num_digits+3 : digits_type::maxdigits_oct;
1097 if(C4_UNLIKELY(buf.len < needed_digits))
1098 return needed_digits;
1099 C4_ASSERT(buf.len >= digits_type::maxdigits_oct);
1100 buf.str[pos++] = '0';
1101 buf.str[pos++] = 'o';
1102 pos = _itoa2bufwithdigits(buf, pos, num_digits, digits_type::min_value_oct());
1103 break;
1104 }
1105 return pos;
1106}
1107} // namespace detail
1108/** @endcond */
1109
1110
1111/** @defgroup doc_itoa itoa: signed to chars
1112 *
1113 * @{ */
1114
1115/** convert an integral signed decimal to a string.
1116 * @note the resulting string is NOT zero-terminated.
1117 * @note it is ok to call this with an empty or too-small buffer;
1118 * no writes will occur, and the needed size will be returned
1119 * @return the number of characters required for the buffer. */
1120template<class T>
1121C4_ALWAYS_INLINE size_t itoa(substr buf, T v) noexcept
1122{
1123 C4_STATIC_ASSERT(std::is_signed<T>::value);
1124 if(v >= T(0))
1125 {
1126 // write_dec() checks the buffer size, so no need to check here
1127 return write_dec(buf, v);
1128 }
1129 // when T is the min value (eg i8: -128), negating it
1130 // will overflow, so treat the min as a special case
1131 if(C4_LIKELY(v != std::numeric_limits<T>::min()))
1132 {
1133 v = (T)-v;
1134 unsigned digits = digits_dec(v);
1135 if(C4_LIKELY(buf.len >= digits + 1u))
1136 {
1137 buf.str[0] = '-';
1138 write_dec_unchecked(buf.sub(1), v, digits);
1139 }
1140 return digits + 1u;
1141 }
1142 return detail::_itoadec2buf<T>(buf);
1143}
1144
1145/** convert an integral signed integer to a string, using a specific
1146 * radix. The radix must be 2, 8, 10 or 16.
1147 *
1148 * @note the resulting string is NOT zero-terminated.
1149 * @note it is ok to call this with an empty or too-small buffer;
1150 * no writes will occur, and the needed size will be returned
1151 * @return the number of characters required for the buffer. */
1152template<class T>
1153C4_ALWAYS_INLINE size_t itoa(substr buf, T v, T radix) noexcept
1154{
1155 C4_STATIC_ASSERT(std::is_signed<T>::value);
1156 C4_ASSERT(radix == 2 || radix == 8 || radix == 10 || radix == 16);
1157 C4_SUPPRESS_WARNING_GCC_PUSH
1158 #if (defined(__GNUC__) && (__GNUC__ >= 7))
1159 C4_SUPPRESS_WARNING_GCC("-Wstringop-overflow") // gcc has a false positive here
1160 #endif
1161 // when T is the min value (eg i8: -128), negating it
1162 // will overflow, so treat the min as a special case
1163 if(C4_LIKELY(v != std::numeric_limits<T>::min()))
1164 {
1165 unsigned pos = 0;
1166 if(v < 0)
1167 {
1168 v = (T)-v;
1169 if(C4_LIKELY(buf.len > 0))
1170 buf.str[pos] = '-';
1171 ++pos;
1172 }
1173 unsigned digits = 0;
1174 switch(radix) // NOLINT(hicpp-multiway-paths-covered)
1175 {
1176 case T(10):
1177 digits = digits_dec(v);
1178 if(C4_LIKELY(buf.len >= pos + digits))
1179 write_dec_unchecked(buf.sub(pos), v, digits);
1180 break;
1181 case T(16):
1182 digits = digits_hex(v);
1183 if(C4_LIKELY(buf.len >= pos + 2u + digits))
1184 {
1185 buf.str[pos + 0] = '0';
1186 buf.str[pos + 1] = 'x';
1187 write_hex_unchecked(buf.sub(pos + 2), v, digits);
1188 }
1189 digits += 2u;
1190 break;
1191 case T(2):
1192 digits = digits_bin(v);
1193 if(C4_LIKELY(buf.len >= pos + 2u + digits))
1194 {
1195 buf.str[pos + 0] = '0';
1196 buf.str[pos + 1] = 'b';
1197 write_bin_unchecked(buf.sub(pos + 2), v, digits);
1198 }
1199 digits += 2u;
1200 break;
1201 case T(8):
1202 digits = digits_oct(v);
1203 if(C4_LIKELY(buf.len >= pos + 2u + digits))
1204 {
1205 buf.str[pos + 0] = '0';
1206 buf.str[pos + 1] = 'o';
1207 write_oct_unchecked(buf.sub(pos + 2), v, digits);
1208 }
1209 digits += 2u;
1210 break;
1211 }
1212 return pos + digits;
1213 }
1214 C4_SUPPRESS_WARNING_GCC_POP
1215 // when T is the min value (eg i8: -128), negating it
1216 // will overflow
1217 return detail::_itoa2buf<T>(buf, radix);
1218}
1219
1220
1221/** same as c4::itoa(), but pad with zeroes on the left such that the
1222 * resulting string is @p num_digits wide, not accounting for radix
1223 * prefix (0x,0o,0b). The @p radix must be 2, 8, 10 or 16.
1224 *
1225 * @note the resulting string is NOT zero-terminated.
1226 * @note it is ok to call this with an empty or too-small buffer;
1227 * no writes will occur, and the needed size will be returned
1228 * @return the number of characters required for the buffer. */
1229template<class T>
1230C4_ALWAYS_INLINE size_t itoa(substr buf, T v, T radix, size_t num_digits) noexcept
1231{
1232 C4_STATIC_ASSERT(std::is_signed<T>::value);
1233 C4_ASSERT(radix == 2 || radix == 8 || radix == 10 || radix == 16);
1234 C4_SUPPRESS_WARNING_GCC_PUSH
1235 #if (defined(__GNUC__) && (__GNUC__ >= 7))
1236 C4_SUPPRESS_WARNING_GCC("-Wstringop-overflow") // gcc has a false positive here
1237 #endif
1238 // when T is the min value (eg i8: -128), negating it
1239 // will overflow, so treat the min as a special case
1240 if(C4_LIKELY(v != std::numeric_limits<T>::min()))
1241 {
1242 unsigned pos = 0;
1243 if(v < 0)
1244 {
1245 v = (T)-v;
1246 if(C4_LIKELY(buf.len > 0))
1247 buf.str[pos] = '-';
1248 ++pos;
1249 }
1250 unsigned total_digits = 0;
1251 switch(radix) // NOLINT(hicpp-multiway-paths-covered)
1252 {
1253 case T(10):
1254 total_digits = digits_dec(v);
1255 total_digits = pos + (unsigned)(num_digits > total_digits ? num_digits : total_digits);
1256 if(C4_LIKELY(buf.len >= total_digits))
1257 write_dec(buf.sub(pos), v, num_digits);
1258 break;
1259 case T(16):
1260 total_digits = digits_hex(v);
1261 total_digits = pos + 2u + (unsigned)(num_digits > total_digits ? num_digits : total_digits);
1262 if(C4_LIKELY(buf.len >= total_digits))
1263 {
1264 buf.str[pos + 0] = '0';
1265 buf.str[pos + 1] = 'x';
1266 write_hex(buf.sub(pos + 2), v, num_digits);
1267 }
1268 break;
1269 case T(2):
1270 total_digits = digits_bin(v);
1271 total_digits = pos + 2u + (unsigned)(num_digits > total_digits ? num_digits : total_digits);
1272 if(C4_LIKELY(buf.len >= total_digits))
1273 {
1274 buf.str[pos + 0] = '0';
1275 buf.str[pos + 1] = 'b';
1276 write_bin(buf.sub(pos + 2), v, num_digits);
1277 }
1278 break;
1279 case T(8):
1280 total_digits = digits_oct(v);
1281 total_digits = pos + 2u + (unsigned)(num_digits > total_digits ? num_digits : total_digits);
1282 if(C4_LIKELY(buf.len >= total_digits))
1283 {
1284 buf.str[pos + 0] = '0';
1285 buf.str[pos + 1] = 'o';
1286 write_oct(buf.sub(pos + 2), v, num_digits);
1287 }
1288 break;
1289 }
1290 return total_digits;
1291 }
1292 C4_SUPPRESS_WARNING_GCC_POP
1293 // when T is the min value (eg i8: -128), negating it
1294 // will overflow
1295 return detail::_itoa2buf<T>(buf, radix, num_digits);
1296}
1297
1298/** @} */
1299
1300
1301//-----------------------------------------------------------------------------
1302//-----------------------------------------------------------------------------
1303//-----------------------------------------------------------------------------
1304
1305/** @defgroup doc_utoa utoa: unsigned to chars
1306 *
1307 * @{ */
1308
1309/** convert an integral unsigned decimal to a string.
1310 *
1311 * @note the resulting string is NOT zero-terminated.
1312 * @note it is ok to call this with an empty or too-small buffer;
1313 * no writes will occur, and the needed size will be returned
1314 * @return the number of characters required for the buffer. */
1315template<class T>
1316C4_ALWAYS_INLINE size_t utoa(substr buf, T v) noexcept
1317{
1318 C4_STATIC_ASSERT(std::is_unsigned<T>::value);
1319 // write_dec() does the buffer length check, so no need to check here
1320 return write_dec(buf, v);
1321}
1322
1323/** convert an integral unsigned integer to a string, using a specific
1324 * radix. The radix must be 2, 8, 10 or 16.
1325 *
1326 * @note the resulting string is NOT zero-terminated.
1327 * @note it is ok to call this with an empty or too-small buffer;
1328 * no writes will occur, and the needed size will be returned
1329 * @return the number of characters required for the buffer. */
1330template<class T>
1331C4_ALWAYS_INLINE size_t utoa(substr buf, T v, T radix) noexcept
1332{
1333 C4_STATIC_ASSERT(std::is_unsigned<T>::value);
1334 C4_ASSERT(radix == 10 || radix == 16 || radix == 2 || radix == 8);
1335 unsigned digits = 0;
1336 switch(radix) // NOLINT(hicpp-multiway-paths-covered)
1337 {
1338 case T(10):
1339 digits = digits_dec(v);
1340 if(C4_LIKELY(buf.len >= digits))
1341 write_dec_unchecked(buf, v, digits);
1342 break;
1343 case T(16):
1344 digits = digits_hex(v);
1345 if(C4_LIKELY(buf.len >= digits+2u))
1346 {
1347 buf.str[0] = '0';
1348 buf.str[1] = 'x';
1349 write_hex_unchecked(buf.sub(2), v, digits);
1350 }
1351 digits += 2u;
1352 break;
1353 case T(2):
1354 digits = digits_bin(v);
1355 if(C4_LIKELY(buf.len >= digits+2u))
1356 {
1357 buf.str[0] = '0';
1358 buf.str[1] = 'b';
1359 write_bin_unchecked(buf.sub(2), v, digits);
1360 }
1361 digits += 2u;
1362 break;
1363 case T(8):
1364 digits = digits_oct(v);
1365 if(C4_LIKELY(buf.len >= digits+2u))
1366 {
1367 buf.str[0] = '0';
1368 buf.str[1] = 'o';
1369 write_oct_unchecked(buf.sub(2), v, digits);
1370 }
1371 digits += 2u;
1372 break;
1373 }
1374 return digits;
1375}
1376
1377/** same as c4::utoa(), but pad with zeroes on the left such that the
1378 * resulting string is @p num_digits wide. The @p radix must be 2,
1379 * 8, 10 or 16.
1380 *
1381 * @note the resulting string is NOT zero-terminated.
1382 * @note it is ok to call this with an empty or too-small buffer;
1383 * no writes will occur, and the needed size will be returned
1384 * @return the number of characters required for the buffer. */
1385template<class T>
1386C4_ALWAYS_INLINE size_t utoa(substr buf, T v, T radix, size_t num_digits) noexcept
1387{
1388 C4_STATIC_ASSERT(std::is_unsigned<T>::value);
1389 C4_ASSERT(radix == 10 || radix == 16 || radix == 2 || radix == 8);
1390 unsigned total_digits = 0;
1391 switch(radix) // NOLINT(hicpp-multiway-paths-covered)
1392 {
1393 case T(10):
1394 total_digits = digits_dec(v);
1395 total_digits = (unsigned)(num_digits > total_digits ? num_digits : total_digits);
1396 if(C4_LIKELY(buf.len >= total_digits))
1397 write_dec(buf, v, num_digits);
1398 break;
1399 case T(16):
1400 total_digits = digits_hex(v);
1401 total_digits = 2u + (unsigned)(num_digits > total_digits ? num_digits : total_digits);
1402 if(C4_LIKELY(buf.len >= total_digits))
1403 {
1404 buf.str[0] = '0';
1405 buf.str[1] = 'x';
1406 write_hex(buf.sub(2), v, num_digits);
1407 }
1408 break;
1409 case T(2):
1410 total_digits = digits_bin(v);
1411 total_digits = 2u + (unsigned)(num_digits > total_digits ? num_digits : total_digits);
1412 if(C4_LIKELY(buf.len >= total_digits))
1413 {
1414 buf.str[0] = '0';
1415 buf.str[1] = 'b';
1416 write_bin(buf.sub(2), v, num_digits);
1417 }
1418 break;
1419 case T(8):
1420 total_digits = digits_oct(v);
1421 total_digits = 2u + (unsigned)(num_digits > total_digits ? num_digits : total_digits);
1422 if(C4_LIKELY(buf.len >= total_digits))
1423 {
1424 buf.str[0] = '0';
1425 buf.str[1] = 'o';
1426 write_oct(buf.sub(2), v, num_digits);
1427 }
1428 break;
1429 }
1430 return total_digits;
1431}
1432C4_SUPPRESS_WARNING_GCC_POP
1433
1434/** @} */
1435
1436
1437//-----------------------------------------------------------------------------
1438//-----------------------------------------------------------------------------
1439//-----------------------------------------------------------------------------
1440
1441/** @defgroup doc_atoi atoi: chars to signed
1442 *
1443 * @{ */
1444
1445/** Convert a trimmed string to a signed integral value. The input
1446 * string can be formatted as decimal, binary (prefix 0b or 0B), octal
1447 * (prefix 0o or 0O) or hexadecimal (prefix 0x or 0X). Strings with
1448 * leading zeroes are considered as decimal and not octal (unlike the
1449 * C/C++ convention). Every character in the input string is read for
1450 * the conversion; the input string must not contain any leading or
1451 * trailing whitespace.
1452 *
1453 * @return true if the conversion was successful.
1454 *
1455 * @note a positive sign is not accepted. ie, the string must not
1456 * start with '+'
1457 *
1458 * @note overflow is not detected: the return status is true even if
1459 * the conversion would return a value outside of the type's range, in
1460 * which case the result will wrap around the type's range. This is
1461 * similar to native behavior. See @ref doc_overflows and @ref
1462 * doc_overflow_checked for overflow checking utilities.
1463 *
1464 * @see atoi_first() if the string is not trimmed to the value to read. */
1465template<class T>
1467C4_ALWAYS_INLINE bool atoi(csubstr str, T * C4_RESTRICT v) noexcept
1468{
1469 C4_STATIC_ASSERT(std::is_integral<T>::value);
1470 C4_STATIC_ASSERT(std::is_signed<T>::value);
1471
1472 if(C4_UNLIKELY(str.len == 0))
1473 return false;
1474
1475 C4_ASSERT(str.str[0] != '+');
1476
1477 T sign = 1;
1478 size_t start = 0;
1479 if(str.str[0] == '-')
1480 {
1481 if(C4_UNLIKELY(str.len == ++start))
1482 return false;
1483 sign = -1;
1484 }
1485
1486 bool parsed_ok = true;
1487 if(str.str[start] != '0') // this should be the common case, so put it first
1488 {
1489 parsed_ok = read_dec(str.sub(start), v);
1490 }
1491 else if(str.len > start + 1)
1492 {
1493 // starts with 0: is it 0x, 0o, 0b?
1494 const char pfx = str.str[start + 1];
1495 if(pfx == 'x' || pfx == 'X')
1496 parsed_ok = str.len > start + 2 && read_hex(str.sub(start + 2), v);
1497 else if(pfx == 'b' || pfx == 'B')
1498 parsed_ok = str.len > start + 2 && read_bin(str.sub(start + 2), v);
1499 else if(pfx == 'o' || pfx == 'O')
1500 parsed_ok = str.len > start + 2 && read_oct(str.sub(start + 2), v);
1501 else
1502 parsed_ok = read_dec(str.sub(start + 1), v);
1503 }
1504 else
1505 {
1506 parsed_ok = read_dec(str.sub(start), v);
1507 }
1508 if(C4_LIKELY(parsed_ok))
1509 *v *= sign;
1510 return parsed_ok;
1511}
1512
1513
1514/** Select the next range of characters in the string that can be parsed
1515 * as a signed integral value, and convert it using atoi(). Leading
1516 * whitespace (space, newline, tabs) is skipped.
1517 * @return the number of characters read for conversion, or csubstr::npos if the conversion failed
1518 * @see atoi() if the string is already trimmed to the value to read.
1519 * @see csubstr::first_int_span() */
1520template<class T>
1521C4_ALWAYS_INLINE size_t atoi_first(csubstr str, T * C4_RESTRICT v)
1522{
1523 csubstr trimmed = str.first_int_span();
1524 if(trimmed.len && atoi(trimmed, v))
1525 return static_cast<size_t>(trimmed.end() - str.begin());
1526 return csubstr::npos;
1527}
1528
1529/** @} */
1530
1531
1532//-----------------------------------------------------------------------------
1533//-----------------------------------------------------------------------------
1534//-----------------------------------------------------------------------------
1535
1536/** @defgroup doc_atou atou: chars to unsigned
1537 *
1538 * @{ */
1539
1540/** Convert a trimmed string to an unsigned integral value. The string can be
1541 * formatted as decimal, binary (prefix 0b or 0B), octal (prefix 0o or 0O)
1542 * or hexadecimal (prefix 0x or 0X). Every character in the input string is read
1543 * for the conversion; it must not contain any leading or trailing whitespace.
1544 *
1545 * @return true if the conversion was successful.
1546 *
1547 * @note overflow is not detected: the return status is true even if
1548 * the conversion would return a value outside of the type's range, in
1549 * which case the result will wrap around the type's range. See @ref
1550 * doc_overflows and @ref doc_overflow_checked for overflow checking
1551 * utilities.
1552 *
1553 * @note If the string has a minus character, the return status
1554 * will be false.
1555 *
1556 * @see atou_first() if the string is not trimmed to the value to read. */
1557template<class T>
1558bool atou(csubstr str, T * C4_RESTRICT v) noexcept
1559{
1560 C4_STATIC_ASSERT(std::is_integral<T>::value);
1561
1562 if(C4_UNLIKELY(str.len == 0 || str.front() == '-'))
1563 return false;
1564
1565 bool parsed_ok = true;
1566 if(str.str[0] != '0')
1567 {
1568 parsed_ok = read_dec(str, v);
1569 }
1570 else
1571 {
1572 if(str.len > 1)
1573 {
1574 const char pfx = str.str[1];
1575 if(pfx == 'x' || pfx == 'X')
1576 parsed_ok = str.len > 2 && read_hex(str.sub(2), v);
1577 else if(pfx == 'b' || pfx == 'B')
1578 parsed_ok = str.len > 2 && read_bin(str.sub(2), v);
1579 else if(pfx == 'o' || pfx == 'O')
1580 parsed_ok = str.len > 2 && read_oct(str.sub(2), v);
1581 else
1582 parsed_ok = read_dec(str, v);
1583 }
1584 else
1585 {
1586 *v = 0; // we know the first character is 0
1587 }
1588 }
1589 return parsed_ok;
1590}
1591
1592
1593/** Select the next range of characters in the string that can be parsed
1594 * as an unsigned integral value, and convert it using atou(). Leading
1595 * whitespace (space, newline, tabs) is skipped.
1596 * @return the number of characters read for conversion, or csubstr::npos if the conversion faileds
1597 * @see atou() if the string is already trimmed to the value to read.
1598 * @see csubstr::first_uint_span() */
1599template<class T>
1600C4_ALWAYS_INLINE size_t atou_first(csubstr str, T *v)
1601{
1602 csubstr trimmed = str.first_uint_span();
1603 if(trimmed.len && atou(trimmed, v))
1604 return static_cast<size_t>(trimmed.end() - str.begin());
1605 return csubstr::npos;
1606}
1607
1608
1609/** @} */
1610
1611#if defined(_MSC_VER) && !defined(__clang__)
1612# pragma warning(pop)
1613#elif defined(__clang__)
1614# pragma clang diagnostic pop
1615#elif defined(__GNUC__)
1616# pragma GCC diagnostic pop
1617#endif
1618
1619
1620//-----------------------------------------------------------------------------
1621//-----------------------------------------------------------------------------
1622//-----------------------------------------------------------------------------
1623
1624/** @cond dev */
1625namespace detail {
1626inline bool check_overflow(csubstr str, csubstr limit) noexcept
1627{
1628 if(str.len != limit.len)
1629 return str.len > limit.len;
1630 for(size_t i = 0; i < limit.len; ++i)
1631 {
1632 if(str.str[i] < limit.str[i])
1633 return false;
1634 else if(str.str[i] > limit.str[i])
1635 return true;
1636 }
1637 return false;
1638}
1639} // namespace detail
1640/** @endcond */
1641
1642
1643/** @defgroup doc_overflows overflows: does a number string overflow a type
1644 *
1645 * @{ */
1646
1647/** Test if the following string would overflow when converted to
1648 * associated integral types; this function is dispatched with SFINAE
1649 * to handle differently signed and unsigned types.
1650 * @return true if number will overflow, false if it fits (or doesn't parse)
1651 * @see doc_overflow_checked for format specifiers to enforce no-overflow reads
1652 */
1653template<class T>
1654auto overflows(csubstr str) noexcept
1655 -> typename std::enable_if<std::is_unsigned<T>::value, bool>::type
1656{
1657 C4_STATIC_ASSERT(std::is_integral<T>::value);
1658
1659 if(C4_UNLIKELY(str.len == 0))
1660 {
1661 return false;
1662 }
1663 else if(str.str[0] == '0')
1664 {
1665 if (str.len == 1)
1666 return false;
1667 switch (str.str[1])
1668 {
1669 case 'x':
1670 case 'X':
1671 {
1672 size_t fno = str.first_not_of('0', 2);
1673 if (fno == csubstr::npos)
1674 return false;
1675 return !(str.len <= fno + (sizeof(T) * 2));
1676 }
1677 case 'b':
1678 case 'B':
1679 {
1680 size_t fno = str.first_not_of('0', 2);
1681 if (fno == csubstr::npos)
1682 return false;
1683 return !(str.len <= fno +(sizeof(T) * 8));
1684 }
1685 case 'o':
1686 case 'O':
1687 {
1688 size_t fno = str.first_not_of('0', 2);
1689 if(fno == csubstr::npos)
1690 return false;
1691 return detail::charconv_digits<T>::is_oct_overflow(str.sub(fno));
1692 }
1693 default:
1694 {
1695 size_t fno = str.first_not_of('0', 1);
1696 if(fno == csubstr::npos)
1697 return false;
1698 return detail::check_overflow(str.sub(fno), detail::charconv_digits<T>::max_value_dec());
1699 }
1700 }
1701 }
1702 else if(C4_UNLIKELY(str.str[0] == '-'))
1703 {
1704 return true;
1705 }
1706 else
1707 {
1708 return detail::check_overflow(str, detail::charconv_digits<T>::max_value_dec());
1709 }
1710}
1711
1712
1713/** Test if the following string would overflow when converted to
1714 * associated integral types; this function is dispatched with SFINAE
1715 * to handle differently signed and unsigned types.
1716 *
1717 * @return true if number will overflow, false if it fits (or doesn't parse)
1718 * @see doc_overflow_checked for format specifiers to enforce no-overflow reads
1719 */
1720template<class T>
1721auto overflows(csubstr str) noexcept
1722 -> typename std::enable_if<std::is_signed<T>::value, bool>::type
1723{
1724 C4_STATIC_ASSERT(std::is_integral<T>::value);
1725 if(C4_UNLIKELY(str.len == 0))
1726 return false;
1727 if(str.str[0] == '-')
1728 {
1729 if(str.str[1] == '0')
1730 {
1731 if(str.len == 2)
1732 return false;
1733 switch(str.str[2])
1734 {
1735 case 'x':
1736 case 'X':
1737 {
1738 size_t fno = str.first_not_of('0', 3);
1739 if (fno == csubstr::npos)
1740 return false;
1741 return detail::check_overflow(str.sub(fno), detail::charconv_digits<T>::min_value_hex());
1742 }
1743 case 'b':
1744 case 'B':
1745 {
1746 size_t fno = str.first_not_of('0', 3);
1747 if (fno == csubstr::npos)
1748 return false;
1749 return detail::check_overflow(str.sub(fno), detail::charconv_digits<T>::min_value_bin());
1750 }
1751 case 'o':
1752 case 'O':
1753 {
1754 size_t fno = str.first_not_of('0', 3);
1755 if(fno == csubstr::npos)
1756 return false;
1757 return detail::check_overflow(str.sub(fno), detail::charconv_digits<T>::min_value_oct());
1758 }
1759 default:
1760 {
1761 size_t fno = str.first_not_of('0', 2);
1762 if(fno == csubstr::npos)
1763 return false;
1764 return detail::check_overflow(str.sub(fno), detail::charconv_digits<T>::min_value_dec());
1765 }
1766 }
1767 }
1768 else
1769 {
1770 return detail::check_overflow(str.sub(1), detail::charconv_digits<T>::min_value_dec());
1771 }
1772 }
1773 else if(str.str[0] == '0')
1774 {
1775 if (str.len == 1)
1776 return false;
1777 switch(str.str[1])
1778 {
1779 case 'x':
1780 case 'X':
1781 {
1782 size_t fno = str.first_not_of('0', 2);
1783 if (fno == csubstr::npos)
1784 return false;
1785 const size_t len = str.len - fno;
1786 return !((len < sizeof (T) * 2) || (len == sizeof(T) * 2 && str.str[fno] <= '7'));
1787 }
1788 case 'b':
1789 case 'B':
1790 {
1791 size_t fno = str.first_not_of('0', 2);
1792 if (fno == csubstr::npos)
1793 return false;
1794 return !(str.len <= fno + (sizeof(T) * 8 - 1));
1795 }
1796 case 'o':
1797 case 'O':
1798 {
1799 size_t fno = str.first_not_of('0', 2);
1800 if(fno == csubstr::npos)
1801 return false;
1802 return detail::charconv_digits<T>::is_oct_overflow(str.sub(fno));
1803 }
1804 default:
1805 {
1806 size_t fno = str.first_not_of('0', 1);
1807 if(fno == csubstr::npos)
1808 return false;
1809 return detail::check_overflow(str.sub(fno), detail::charconv_digits<T>::max_value_dec());
1810 }
1811 }
1812 }
1813 else
1814 {
1815 return detail::check_overflow(str, detail::charconv_digits<T>::max_value_dec());
1816 }
1817}
1818
1819/** @} */
1820
1821
1822//-----------------------------------------------------------------------------
1823//-----------------------------------------------------------------------------
1824//-----------------------------------------------------------------------------
1825
1826/** @cond dev */
1827namespace detail {
1828
1829
1830#if (!C4CORE_HAVE_STD_FROMCHARS)
1831/** @see http://www.exploringbinary.com/ for many good examples on float-str conversion */
1832template<size_t N>
1833void get_real_format_str(char (& C4_RESTRICT fmt)[N], int precision, RealFormat_e formatting, const char* length_modifier="")
1834{
1835 int iret;
1836 if(precision == -1)
1837 iret = snprintf(fmt, sizeof(fmt), "%%%s%c", length_modifier, formatting);
1838 else if(precision == 0)
1839 iret = snprintf(fmt, sizeof(fmt), "%%.%s%c", length_modifier, formatting);
1840 else
1841 iret = snprintf(fmt, sizeof(fmt), "%%.%d%s%c", precision, length_modifier, formatting);
1842 C4_ASSERT(iret >= 2 && size_t(iret) < sizeof(fmt));
1843 C4_UNUSED(iret);
1844}
1845
1846
1847/** @todo we're depending on snprintf()/sscanf() for converting to/from
1848 * floating point numbers. Apparently, this increases the binary size
1849 * by a considerable amount. There are some lightweight printf
1850 * implementations:
1851 *
1852 * @see http://www.sparetimelabs.com/tinyprintf/tinyprintf.php (BSD)
1853 * @see https://github.com/weiss/c99-snprintf
1854 * @see https://github.com/nothings/stb/blob/master/stb_sprintf.h
1855 * @see http://www.exploringbinary.com/
1856 * @see https://blog.benoitblanchon.fr/lightweight-float-to-string/
1857 * @see http://www.ryanjuckett.com/programming/printing-floating-point-numbers/
1858 */
1859template<class T>
1860size_t print_one(substr str, const char* full_fmt, T v)
1861{
1862#ifdef _MSC_VER
1863 /** use _snprintf() to prevent early termination of the output
1864 * for writing the null character at the last position
1865 * @see https://msdn.microsoft.com/en-us/library/2ts7cx93.aspx */
1866 int iret = _snprintf(str.str, str.len, full_fmt, v);
1867 if(iret < 0)
1868 {
1869 /* when buf.len is not enough, VS returns a negative value.
1870 * so call it again with a negative value for getting an
1871 * actual length of the string */
1872 iret = snprintf(nullptr, 0, full_fmt, v);
1873 C4_ASSERT(iret > 0);
1874 }
1875 size_t ret = (size_t) iret;
1876 return ret;
1877#else
1878 int iret = snprintf(str.str, str.len, full_fmt, v);
1879 C4_ASSERT(iret >= 0);
1880 size_t ret = (size_t) iret;
1881 if(ret >= str.len)
1882 ++ret; /* snprintf() reserves the last character to write \0 */
1883 return ret;
1884#endif
1885}
1886#endif // (!C4CORE_HAVE_STD_FROMCHARS)
1887
1888
1889#if (!C4CORE_HAVE_STD_FROMCHARS) && (!C4CORE_HAVE_FAST_FLOAT)
1890/** scans a string using the given type format, while at the same time
1891 * allowing non-null-terminated strings AND guaranteeing that the given
1892 * string length is strictly respected, so that no buffer overflows
1893 * might occur. */
1894template<typename T>
1895inline size_t scan_one(csubstr str, const char *type_fmt, T *v)
1896{
1897 /* snscanf() is absolutely needed here as we must be sure that
1898 * str.len is strictly respected, because substr is
1899 * generally not null-terminated.
1900 *
1901 * Alas, there is no snscanf().
1902 *
1903 * So we fake it by using a dynamic format with an explicit
1904 * field size set to the length of the given span.
1905 * This trick is taken from:
1906 * https://stackoverflow.com/a/18368910/5875572 */
1907
1908 /* this is the actual format we'll use for scanning */
1909 char fmt[16];
1910
1911 /* write the length into it. Eg "%12f".
1912 * Also, get the number of characters read from the string.
1913 * So the final format ends up as "%12f%n"*/
1914 int iret = std::snprintf(fmt, sizeof(fmt), "%%" "%zu" "%s" "%%n", str.len, type_fmt);
1915 /* no nasty surprises, please! */
1916 C4_ASSERT(iret >= 0 && size_t(iret) < C4_COUNTOF(fmt));
1917
1918 /* now we scan with confidence that the span length is respected */
1919 int num_chars;
1920 iret = std::sscanf(str.str, fmt, v, &num_chars);
1921 /* scanf returns the number of successful conversions */
1922 if(iret != 1) return csubstr::npos;
1923 C4_ASSERT(num_chars >= 0);
1924 return (size_t)(num_chars);
1925}
1926#endif // (!C4CORE_HAVE_STD_FROMCHARS) && (!C4CORE_HAVE_FAST_FLOAT)
1927
1928
1929#if C4CORE_HAVE_STD_TOCHARS
1930template<class T>
1931C4_ALWAYS_INLINE size_t rtoa(substr buf, T v, int precision=-1, RealFormat_e formatting=FTOA_FLEX) noexcept
1932{
1933 std::to_chars_result result;
1934 size_t pos = 0;
1935 if(formatting == FTOA_HEXA)
1936 {
1937 if(buf.len > size_t(2))
1938 {
1939 buf.str[0] = '0';
1940 buf.str[1] = 'x';
1941 }
1942 pos += size_t(2);
1943 }
1944 if(precision == -1)
1945 result = std::to_chars(buf.str + pos, buf.str + buf.len, v, (std::chars_format)formatting);
1946 else
1947 result = std::to_chars(buf.str + pos, buf.str + buf.len, v, (std::chars_format)formatting, precision);
1948 if(result.ec == std::errc())
1949 {
1950 // all good, no errors.
1951 C4_ASSERT(result.ptr >= buf.str);
1952 ptrdiff_t delta = result.ptr - buf.str;
1953 return static_cast<size_t>(delta);
1954 }
1955 C4_ASSERT(result.ec == std::errc::value_too_large);
1956 // This is unfortunate.
1957 //
1958 // When the result can't fit in the given buffer,
1959 // std::to_chars() returns the end pointer it was originally
1960 // given, which is useless because here we would like to know
1961 // _exactly_ how many characters the buffer must have to fit
1962 // the result.
1963 //
1964 // So we take the pessimistic view, and assume as many digits
1965 // as could ever be required:
1966 size_t ret = static_cast<size_t>(std::numeric_limits<T>::max_digits10);
1967 return ret > buf.len ? ret : buf.len + 1;
1968}
1969#endif // C4CORE_HAVE_STD_TOCHARS
1970
1971
1972#if C4CORE_HAVE_FAST_FLOAT
1973template<class T>
1974C4_ALWAYS_INLINE bool scan_rhex(csubstr s, T *C4_RESTRICT val) noexcept
1975{
1976 C4_ASSERT(s.len > 0);
1977 C4_ASSERT(s.str[0] != '-');
1978 C4_ASSERT(s.str[0] != '+');
1979 C4_ASSERT(!s.begins_with("0x"));
1980 C4_ASSERT(!s.begins_with("0X"));
1981 size_t pos = 0;
1982 // integer part
1983 for( ; pos < s.len; ++pos)
1984 {
1985 const char c = s.str[pos];
1986 if(c >= '0' && c <= '9')
1987 *val = (*val * T(16)) + T(c - '0');
1988 else if(c >= 'a' && c <= 'f')
1989 *val = (*val * T(16)) + T(c - 'a');
1990 else if(c >= 'A' && c <= 'F')
1991 *val = (*val * T(16)) + T(c - 'A');
1992 else if(c == '.')
1993 {
1994 ++pos;
1995 break; // follow on to mantissa
1996 }
1997 else if(c == 'p' || c == 'P')
1998 {
1999 ++pos;
2000 goto power; // no mantissa given, jump to power // NOLINT
2001 }
2002 else
2003 {
2004 return false;
2005 }
2006 }
2007 // mantissa
2008 {
2009 // 0.0625 == 1/16 == value of first digit after the comma
2010 for(T digit = T(0.0625); pos < s.len; ++pos, digit /= T(16)) // NOLINT
2011 {
2012 const char c = s.str[pos];
2013 if(c >= '0' && c <= '9')
2014 *val += digit * T(c - '0');
2015 else if(c >= 'a' && c <= 'f')
2016 *val += digit * T(c - 'a');
2017 else if(c >= 'A' && c <= 'F')
2018 *val += digit * T(c - 'A');
2019 else if(c == 'p' || c == 'P')
2020 {
2021 ++pos;
2022 goto power; // mantissa finished, jump to power // NOLINT
2023 }
2024 else
2025 {
2026 return false;
2027 }
2028 }
2029 }
2030 return true;
2031power:
2032 if(C4_LIKELY(pos < s.len))
2033 {
2034 if(s.str[pos] == '+') // atoi() cannot handle a leading '+'
2035 ++pos;
2036 if(C4_LIKELY(pos < s.len))
2037 {
2038 int16_t powval = {};
2039 if(C4_LIKELY(atoi(s.sub(pos), &powval)))
2040 {
2041 *val *= ipow<T, int16_t, 16>(powval);
2042 return true;
2043 }
2044 }
2045 }
2046 return false;
2047}
2048#endif
2049
2050} // namespace detail
2051/** @endcond */
2052
2053
2054#undef _c4appendhex
2055#undef _c4append
2056
2057
2058/** @defgroup doc_ftoa ftoa: float32 to chars
2059 *
2060 * @{ */
2061
2062/** Convert a single-precision real number to string. The string will
2063 * in general be NOT null-terminated. For FTOA_FLEX, \p precision is
2064 * the number of significand digits. Otherwise \p precision is the
2065 * number of decimals. It is safe to call this function with an empty
2066 * or too-small buffer.
2067 *
2068 * @return the size of the buffer needed to write the number
2069 */
2070C4_ALWAYS_INLINE size_t ftoa(substr str, float v, int precision=-1, RealFormat_e formatting=FTOA_FLEX) noexcept
2071{
2072#if C4CORE_HAVE_STD_TOCHARS
2073 return detail::rtoa(str, v, precision, formatting);
2074#else
2075 char fmt[16];
2076 detail::get_real_format_str(fmt, precision, formatting, /*length_modifier*/"");
2077 return detail::print_one(str, fmt, v);
2078#endif
2079}
2080
2081/** @} */
2082
2083
2084/** @defgroup doc_dtoa dtoa: float64 to chars
2085 *
2086 * @{ */
2087
2088/** Convert a double-precision real number to string. The string will
2089 * in general be NOT null-terminated. For FTOA_FLEX, \p precision is
2090 * the number of significand digits. Otherwise \p precision is the
2091 * number of decimals. It is safe to call this function with an empty
2092 * or too-small buffer.
2093 *
2094 * @return the size of the buffer needed to write the number
2095 */
2096C4_ALWAYS_INLINE size_t dtoa(substr str, double v, int precision=-1, RealFormat_e formatting=FTOA_FLEX) noexcept
2097{
2098#if C4CORE_HAVE_STD_TOCHARS
2099 return detail::rtoa(str, v, precision, formatting);
2100#else
2101 char fmt[16];
2102 detail::get_real_format_str(fmt, precision, formatting, /*length_modifier*/"l");
2103 return detail::print_one(str, fmt, v);
2104#endif
2105}
2106
2107/** @} */
2108
2109
2110/** @defgroup doc_atof atof: chars to float32
2111 *
2112 * @{ */
2113
2114/** Convert a string to a single precision real number.
2115 * The input string must be trimmed to the value, ie
2116 * no leading or trailing whitespace can be present.
2117 * @return true iff the conversion succeeded
2118 * @see atof_first() if the string is not trimmed
2119 */
2120C4_ALWAYS_INLINE bool atof(csubstr str, float * C4_RESTRICT v) noexcept
2121{
2122 C4_ASSERT(str.len > 0);
2123 C4_ASSERT(str.triml(" \r\t\n").len == str.len);
2124#if C4CORE_HAVE_FAST_FLOAT
2125 // fastfloat cannot parse hexadecimal floats
2126 bool isneg = (str.str[0] == '-');
2127 csubstr rem = str.sub(isneg || str.str[0] == '+');
2128 if(!(rem.len >= 2 && (rem.str[0] == '0' && (rem.str[1] == 'x' || rem.str[1] == 'X'))))
2129 {
2130 fast_float::from_chars_result result;
2131 result = fast_float::from_chars(str.str, str.str + str.len, *v);
2132 return result.ec == std::errc();
2133 }
2134 else if(detail::scan_rhex(rem.sub(2), v))
2135 {
2136 *v *= isneg ? -1.f : 1.f;
2137 return true;
2138 }
2139 return false;
2140#elif C4CORE_HAVE_STD_FROMCHARS
2141 std::from_chars_result result;
2142 result = std::from_chars(str.str, str.str + str.len, *v);
2143 return result.ec == std::errc();
2144#else
2145 csubstr rem = str.sub(str.str[0] == '-' || str.str[0] == '+');
2146 if(!(rem.len >= 2 && (rem.str[0] == '0' && (rem.str[1] == 'x' || rem.str[1] == 'X'))))
2147 return detail::scan_one(str, "f", v) != csubstr::npos;
2148 else
2149 return detail::scan_one(str, "a", v) != csubstr::npos;
2150#endif
2151}
2152
2153
2154/** Convert a string to a single precision real number.
2155 * Leading whitespace is skipped until valid characters are found.
2156 * @return the number of characters read from the string, or npos if
2157 * conversion was not successful or if the string was empty */
2158inline size_t atof_first(csubstr str, float * C4_RESTRICT v) noexcept
2159{
2160 csubstr trimmed = str.first_real_span();
2161 if(trimmed.len == 0)
2162 return csubstr::npos;
2163 if(atof(trimmed, v))
2164 return static_cast<size_t>(trimmed.end() - str.begin());
2165 return csubstr::npos;
2166}
2167
2168/** @} */
2169
2170
2171/** @defgroup doc_atod atod: chars to float64
2172 *
2173 * @{ */
2174
2175/** Convert a string to a double precision real number.
2176 * The input string must be trimmed to the value, ie
2177 * no leading or trailing whitespace can be present.
2178 * @return true iff the conversion succeeded
2179 * @see atod_first() if the string is not trimmed
2180 */
2181C4_ALWAYS_INLINE bool atod(csubstr str, double * C4_RESTRICT v) noexcept
2182{
2183 C4_ASSERT(str.len > 0);
2184 C4_ASSERT(str.triml(" \r\t\n").len == str.len);
2185#if C4CORE_HAVE_FAST_FLOAT
2186 // fastfloat cannot parse hexadecimal floats
2187 bool isneg = (str.str[0] == '-');
2188 csubstr rem = str.sub(isneg || str.str[0] == '+');
2189 if(!(rem.len >= 2 && (rem.str[0] == '0' && (rem.str[1] == 'x' || rem.str[1] == 'X'))))
2190 {
2191 fast_float::from_chars_result result;
2192 #ifndef CLANG_TIDY // suppress a false-positive error (cannot be done with NOLINT: https://stackoverflow.com/questions/62838193/ )
2193 result = fast_float::from_chars(str.str, str.str + str.len, *v);
2194 #else
2195 result = {};
2196 #endif
2197 return result.ec == std::errc();
2198 }
2199 else if(detail::scan_rhex(rem.sub(2), v))
2200 {
2201 *v *= isneg ? -1. : 1.;
2202 return true;
2203 }
2204 return false;
2205#elif C4CORE_HAVE_STD_FROMCHARS
2206 std::from_chars_result result;
2207 result = std::from_chars(str.str, str.str + str.len, *v);
2208 return result.ec == std::errc();
2209#else
2210 csubstr rem = str.sub(str.str[0] == '-' || str.str[0] == '+');
2211 if(!(rem.len >= 2 && (rem.str[0] == '0' && (rem.str[1] == 'x' || rem.str[1] == 'X'))))
2212 return detail::scan_one(str, "lf", v) != csubstr::npos;
2213 else
2214 return detail::scan_one(str, "la", v) != csubstr::npos;
2215#endif
2216}
2217
2218
2219/** Convert a string to a double precision real number.
2220 * Leading whitespace is skipped until valid characters are found.
2221 * @return the number of characters read from the string, or npos if
2222 * conversion was not successful or if the string was empty */
2223inline size_t atod_first(csubstr str, double * C4_RESTRICT v) noexcept
2224{
2225 csubstr trimmed = str.first_real_span();
2226 if(trimmed.len == 0)
2227 return csubstr::npos;
2228 if(atod(trimmed, v))
2229 return static_cast<size_t>(trimmed.end() - str.begin());
2230 return csubstr::npos;
2231}
2232
2233/** @} */
2234
2235
2236//-----------------------------------------------------------------------------
2237//-----------------------------------------------------------------------------
2238//-----------------------------------------------------------------------------
2239// generic versions
2240
2241/** @cond dev */
2242// on some platforms, (unsigned) int and (unsigned) long
2243// are not any of the fixed length types above
2244#define _C4_IF_NOT_FIXED_LENGTH_I(T, ty) typename std::enable_if<std:: is_signed<T>::value && !is_fixed_length<T>::value_i, ty>
2245#define _C4_IF_NOT_FIXED_LENGTH_U(T, ty) typename std::enable_if<std::is_unsigned<T>::value && !is_fixed_length<T>::value_u, ty>
2246/** @endcond*/
2247
2248
2249/** @defgroup doc_xtoa xtoa: generic value to chars
2250 *
2251 * Dispatches to the most appropriate and efficient conversion
2252 * function
2253 *
2254 * @{ */
2255C4_ALWAYS_INLINE size_t xtoa(substr s, uint8_t v) noexcept { return write_dec(s, v); }
2256C4_ALWAYS_INLINE size_t xtoa(substr s, uint16_t v) noexcept { return write_dec(s, v); }
2257C4_ALWAYS_INLINE size_t xtoa(substr s, uint32_t v) noexcept { return write_dec(s, v); }
2258C4_ALWAYS_INLINE size_t xtoa(substr s, uint64_t v) noexcept { return write_dec(s, v); }
2259C4_ALWAYS_INLINE size_t xtoa(substr s, int8_t v) noexcept { return itoa(s, v); }
2260C4_ALWAYS_INLINE size_t xtoa(substr s, int16_t v) noexcept { return itoa(s, v); }
2261C4_ALWAYS_INLINE size_t xtoa(substr s, int32_t v) noexcept { return itoa(s, v); }
2262C4_ALWAYS_INLINE size_t xtoa(substr s, int64_t v) noexcept { return itoa(s, v); }
2263C4_ALWAYS_INLINE size_t xtoa(substr s, float v) noexcept { return ftoa(s, v); }
2264C4_ALWAYS_INLINE size_t xtoa(substr s, double v) noexcept { return dtoa(s, v); }
2265
2266C4_ALWAYS_INLINE size_t xtoa(substr s, uint8_t v, uint8_t radix) noexcept { return utoa(s, v, radix); }
2267C4_ALWAYS_INLINE size_t xtoa(substr s, uint16_t v, uint16_t radix) noexcept { return utoa(s, v, radix); }
2268C4_ALWAYS_INLINE size_t xtoa(substr s, uint32_t v, uint32_t radix) noexcept { return utoa(s, v, radix); }
2269C4_ALWAYS_INLINE size_t xtoa(substr s, uint64_t v, uint64_t radix) noexcept { return utoa(s, v, radix); }
2270C4_ALWAYS_INLINE size_t xtoa(substr s, int8_t v, int8_t radix) noexcept { return itoa(s, v, radix); }
2271C4_ALWAYS_INLINE size_t xtoa(substr s, int16_t v, int16_t radix) noexcept { return itoa(s, v, radix); }
2272C4_ALWAYS_INLINE size_t xtoa(substr s, int32_t v, int32_t radix) noexcept { return itoa(s, v, radix); }
2273C4_ALWAYS_INLINE size_t xtoa(substr s, int64_t v, int64_t radix) noexcept { return itoa(s, v, radix); }
2274
2275C4_ALWAYS_INLINE size_t xtoa(substr s, uint8_t v, uint8_t radix, size_t num_digits) noexcept { return utoa(s, v, radix, num_digits); }
2276C4_ALWAYS_INLINE size_t xtoa(substr s, uint16_t v, uint16_t radix, size_t num_digits) noexcept { return utoa(s, v, radix, num_digits); }
2277C4_ALWAYS_INLINE size_t xtoa(substr s, uint32_t v, uint32_t radix, size_t num_digits) noexcept { return utoa(s, v, radix, num_digits); }
2278C4_ALWAYS_INLINE size_t xtoa(substr s, uint64_t v, uint64_t radix, size_t num_digits) noexcept { return utoa(s, v, radix, num_digits); }
2279C4_ALWAYS_INLINE size_t xtoa(substr s, int8_t v, int8_t radix, size_t num_digits) noexcept { return itoa(s, v, radix, num_digits); }
2280C4_ALWAYS_INLINE size_t xtoa(substr s, int16_t v, int16_t radix, size_t num_digits) noexcept { return itoa(s, v, radix, num_digits); }
2281C4_ALWAYS_INLINE size_t xtoa(substr s, int32_t v, int32_t radix, size_t num_digits) noexcept { return itoa(s, v, radix, num_digits); }
2282C4_ALWAYS_INLINE size_t xtoa(substr s, int64_t v, int64_t radix, size_t num_digits) noexcept { return itoa(s, v, radix, num_digits); }
2283
2284C4_ALWAYS_INLINE size_t xtoa(substr s, float v, int precision, RealFormat_e formatting=FTOA_FLEX) noexcept { return ftoa(s, v, precision, formatting); }
2285C4_ALWAYS_INLINE size_t xtoa(substr s, double v, int precision, RealFormat_e formatting=FTOA_FLEX) noexcept { return dtoa(s, v, precision, formatting); }
2286
2287template <class T> C4_ALWAYS_INLINE auto xtoa(substr buf, T v) noexcept -> _C4_IF_NOT_FIXED_LENGTH_I(T, size_t)::type { return itoa(buf, v); }
2288template <class T> C4_ALWAYS_INLINE auto xtoa(substr buf, T v) noexcept -> _C4_IF_NOT_FIXED_LENGTH_U(T, size_t)::type { return write_dec(buf, v); }
2289template <class T>
2290C4_ALWAYS_INLINE size_t xtoa(substr s, T *v) noexcept { return itoa(s, (intptr_t)v, (intptr_t)16); }
2291
2292/** @} */
2293
2294/** @defgroup doc_atox atox: generic chars to value
2295 *
2296 * Dispatches to the most appropriate and efficient conversion
2297 * function
2298 *
2299 * @{ */
2300
2301C4_ALWAYS_INLINE bool atox(csubstr s, uint8_t *C4_RESTRICT v) noexcept { return atou(s, v); }
2302C4_ALWAYS_INLINE bool atox(csubstr s, uint16_t *C4_RESTRICT v) noexcept { return atou(s, v); }
2303C4_ALWAYS_INLINE bool atox(csubstr s, uint32_t *C4_RESTRICT v) noexcept { return atou(s, v); }
2304C4_ALWAYS_INLINE bool atox(csubstr s, uint64_t *C4_RESTRICT v) noexcept { return atou(s, v); }
2305C4_ALWAYS_INLINE bool atox(csubstr s, int8_t *C4_RESTRICT v) noexcept { return atoi(s, v); }
2306C4_ALWAYS_INLINE bool atox(csubstr s, int16_t *C4_RESTRICT v) noexcept { return atoi(s, v); }
2307C4_ALWAYS_INLINE bool atox(csubstr s, int32_t *C4_RESTRICT v) noexcept { return atoi(s, v); }
2308C4_ALWAYS_INLINE bool atox(csubstr s, int64_t *C4_RESTRICT v) noexcept { return atoi(s, v); }
2309C4_ALWAYS_INLINE bool atox(csubstr s, float *C4_RESTRICT v) noexcept { return atof(s, v); }
2310C4_ALWAYS_INLINE bool atox(csubstr s, double *C4_RESTRICT v) noexcept { return atod(s, v); }
2311
2312template <class T> C4_ALWAYS_INLINE auto atox(csubstr buf, T *C4_RESTRICT v) noexcept -> _C4_IF_NOT_FIXED_LENGTH_I(T, bool)::type { return atoi(buf, v); }
2313template <class T> C4_ALWAYS_INLINE auto atox(csubstr buf, T *C4_RESTRICT v) noexcept -> _C4_IF_NOT_FIXED_LENGTH_U(T, bool)::type { return atou(buf, v); }
2314template <class T>
2315C4_ALWAYS_INLINE bool atox(csubstr s, T **v) noexcept { intptr_t tmp; bool ret = atox(s, &tmp); if(ret) { *v = (T*)tmp; } return ret; }
2316
2317/** @} */
2318
2319
2320/** @defgroup doc_to_chars to_chars: generalized chars to value
2321 *
2322 * Convert the given value, writing into the string. The resulting
2323 * string will NOT be null-terminated. Return the number of
2324 * characters needed. This function is safe to call when the string
2325 * is too small - no writes will occur beyond the string's last
2326 * character.
2327 *
2328 * Dispatches to the most appropriate and efficient conversion
2329 * function.
2330 *
2331 * @see write_dec, doc_utoa, doc_itoa, doc_ftoa, doc_dtoa
2332 *
2333 * @warning When serializing floating point values (float or double),
2334 * be aware that because it uses defaults, to_chars() may cause a
2335 * truncation of the precision. To enforce a particular precision, use
2336 * for example @ref c4::fmt::real, or call directly @ref c4::ftoa or
2337 * @ref c4::dtoa.
2338 *
2339 * @{ */
2340
2341C4_ALWAYS_INLINE size_t to_chars(substr buf, uint8_t v) noexcept { return write_dec(buf, v); }
2342C4_ALWAYS_INLINE size_t to_chars(substr buf, uint16_t v) noexcept { return write_dec(buf, v); }
2343C4_ALWAYS_INLINE size_t to_chars(substr buf, uint32_t v) noexcept { return write_dec(buf, v); }
2344C4_ALWAYS_INLINE size_t to_chars(substr buf, uint64_t v) noexcept { return write_dec(buf, v); }
2345C4_ALWAYS_INLINE size_t to_chars(substr buf, int8_t v) noexcept { return itoa(buf, v); }
2346C4_ALWAYS_INLINE size_t to_chars(substr buf, int16_t v) noexcept { return itoa(buf, v); }
2347C4_ALWAYS_INLINE size_t to_chars(substr buf, int32_t v) noexcept { return itoa(buf, v); }
2348C4_ALWAYS_INLINE size_t to_chars(substr buf, int64_t v) noexcept { return itoa(buf, v); }
2349C4_ALWAYS_INLINE size_t to_chars(substr buf, float v) noexcept { return ftoa(buf, v); }
2350C4_ALWAYS_INLINE size_t to_chars(substr buf, double v) noexcept { return dtoa(buf, v); }
2351
2352template <class T> C4_ALWAYS_INLINE auto to_chars(substr buf, T v) noexcept -> _C4_IF_NOT_FIXED_LENGTH_I(T, size_t)::type { return itoa(buf, v); }
2353template <class T> C4_ALWAYS_INLINE auto to_chars(substr buf, T v) noexcept -> _C4_IF_NOT_FIXED_LENGTH_U(T, size_t)::type { return write_dec(buf, v); }
2354template <class T>
2355C4_ALWAYS_INLINE auto to_chars(substr s, T *v) noexcept
2356 -> typename std::enable_if<!std::is_same<T, char>::value &&
2357 !std::is_same<T, const char>::value,
2358 size_t>::type
2359{
2360 return itoa(s, (intptr_t)v, (intptr_t)16);
2361}
2362
2363/** @} */
2364
2365
2366/** @defgroup doc_from_chars from_chars: generalized chars to value
2367 *
2368 * Read a value from the string, which must be trimmed to the value
2369 * (ie, no leading/trailing whitespace). return true if the
2370 * conversion succeeded. There is no check for overflow; the value
2371 * wraps around in a way similar to the standard C/C++ overflow
2372 * behavior. For example, from_chars<int8_t>("128", &val) returns true
2373 * and val will be set tot 0. See @ref doc_overflows and @ref
2374 * doc_overflow_checked for facilities enforcing no-overflow.
2375 *
2376 * Dispatches to the most appropriate and efficient conversion
2377 * function
2378 *
2379 * @see doc_from_chars_first, atou, atoi, atof, atod
2380 * @{ */
2381
2382C4_ALWAYS_INLINE bool from_chars(csubstr buf, uint8_t *C4_RESTRICT v) noexcept { return atou(buf, v); }
2383C4_ALWAYS_INLINE bool from_chars(csubstr buf, uint16_t *C4_RESTRICT v) noexcept { return atou(buf, v); }
2384C4_ALWAYS_INLINE bool from_chars(csubstr buf, uint32_t *C4_RESTRICT v) noexcept { return atou(buf, v); }
2385C4_ALWAYS_INLINE bool from_chars(csubstr buf, uint64_t *C4_RESTRICT v) noexcept { return atou(buf, v); }
2386C4_ALWAYS_INLINE bool from_chars(csubstr buf, int8_t *C4_RESTRICT v) noexcept { return atoi(buf, v); }
2387C4_ALWAYS_INLINE bool from_chars(csubstr buf, int16_t *C4_RESTRICT v) noexcept { return atoi(buf, v); }
2388C4_ALWAYS_INLINE bool from_chars(csubstr buf, int32_t *C4_RESTRICT v) noexcept { return atoi(buf, v); }
2389C4_ALWAYS_INLINE bool from_chars(csubstr buf, int64_t *C4_RESTRICT v) noexcept { return atoi(buf, v); }
2390C4_ALWAYS_INLINE bool from_chars(csubstr buf, float *C4_RESTRICT v) noexcept { return atof(buf, v); }
2391C4_ALWAYS_INLINE bool from_chars(csubstr buf, double *C4_RESTRICT v) noexcept { return atod(buf, v); }
2392
2393template <class T> C4_ALWAYS_INLINE auto from_chars(csubstr buf, T *C4_RESTRICT v) noexcept -> _C4_IF_NOT_FIXED_LENGTH_I(T, bool)::type { return atoi(buf, v); }
2394template <class T> C4_ALWAYS_INLINE auto from_chars(csubstr buf, T *C4_RESTRICT v) noexcept -> _C4_IF_NOT_FIXED_LENGTH_U(T, bool)::type { return atou(buf, v); }
2395template <class T>
2396C4_ALWAYS_INLINE bool from_chars(csubstr buf, T **v) noexcept { intptr_t tmp; bool ret = from_chars(buf, &tmp); if(ret) { *v = (T*)tmp; } return ret; }
2397
2398/** @defgroup doc_from_chars_first from_chars_first: generalized chars to value
2399 *
2400 * Read the first valid sequence of characters from the string,
2401 * skipping leading whitespace, and convert it using @ref doc_from_chars .
2402 * Return the number of characters read for converting.
2403 *
2404 * Dispatches to the most appropriate and efficient conversion
2405 * function.
2406 *
2407 * @see atou_first, atoi_first, atof_first, atod_first
2408 * @{ */
2409
2410C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, uint8_t *C4_RESTRICT v) noexcept { return atou_first(buf, v); }
2411C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, uint16_t *C4_RESTRICT v) noexcept { return atou_first(buf, v); }
2412C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, uint32_t *C4_RESTRICT v) noexcept { return atou_first(buf, v); }
2413C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, uint64_t *C4_RESTRICT v) noexcept { return atou_first(buf, v); }
2414C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, int8_t *C4_RESTRICT v) noexcept { return atoi_first(buf, v); }
2415C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, int16_t *C4_RESTRICT v) noexcept { return atoi_first(buf, v); }
2416C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, int32_t *C4_RESTRICT v) noexcept { return atoi_first(buf, v); }
2417C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, int64_t *C4_RESTRICT v) noexcept { return atoi_first(buf, v); }
2418C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, float *C4_RESTRICT v) noexcept { return atof_first(buf, v); }
2419C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, double *C4_RESTRICT v) noexcept { return atod_first(buf, v); }
2420
2421template <class T> C4_ALWAYS_INLINE auto from_chars_first(csubstr buf, T *C4_RESTRICT v) noexcept -> _C4_IF_NOT_FIXED_LENGTH_I(T, size_t)::type { return atoi_first(buf, v); }
2422template <class T> C4_ALWAYS_INLINE auto from_chars_first(csubstr buf, T *C4_RESTRICT v) noexcept -> _C4_IF_NOT_FIXED_LENGTH_U(T, size_t)::type { return atou_first(buf, v); }
2423template <class T>
2424C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, T **v) noexcept { intptr_t tmp; bool ret = from_chars_first(buf, &tmp); if(ret) { *v = (T*)tmp; } return ret; }
2425
2426/** @} */
2427
2428/** @} */
2429
2430#undef _C4_IF_NOT_FIXED_LENGTH_I
2431#undef _C4_IF_NOT_FIXED_LENGTH_U
2432
2433
2434//-----------------------------------------------------------------------------
2435//-----------------------------------------------------------------------------
2436//-----------------------------------------------------------------------------
2437/** call to_chars() and return a substr consisting of the
2438 * written portion of the input buffer. Ie, same as to_chars(),
2439 * but return a substr instead of a size_t.
2440 * Convert the given value to a string using to_chars(), and
2441 * return the resulting string, up to and including the last
2442 * written character.
2443 * @ingroup doc_to_chars
2444 * @see to_chars() */
2445template<class T>
2446C4_ALWAYS_INLINE substr to_chars_sub(substr buf, T const& C4_RESTRICT v) noexcept
2447{
2448 size_t sz = to_chars(buf, v);
2449 return buf.left_of(sz <= buf.len ? sz : buf.len);
2450}
2451
2452//-----------------------------------------------------------------------------
2453//-----------------------------------------------------------------------------
2454//-----------------------------------------------------------------------------
2455// bool implementation
2456
2457/** @ingroup doc_to_chars */
2458C4_ALWAYS_INLINE size_t to_chars(substr buf, bool v) noexcept
2459{
2460 int val = v;
2461 return to_chars(buf, val);
2462}
2463
2464/** @ingroup doc_from_chars */
2465inline bool from_chars(csubstr buf, bool * C4_RESTRICT v) noexcept
2466{
2467 if(buf.len == 1)
2468 {
2469 if(buf.str[0] == '0')
2470 {
2471 *v = false; return true;
2472 }
2473 else if(buf.str[0] == '1')
2474 {
2475 *v = true; return true;
2476 }
2477 }
2478 else if(buf.len == 4)
2479 {
2480 if(((buf.str[0] == 't') && (0 == memcmp(buf.str + 1, "rue", 3)))
2481 ||
2482 ((buf.str[0] == 'T') && (0 == memcmp(buf.str + 1, "rue", 3) ||
2483 0 == memcmp(buf.str + 1, "RUE", 3))))
2484 {
2485 *v = true; return true;
2486 }
2487 }
2488 else if(buf.len == 5)
2489 {
2490 if(((buf.str[0] == 'f') && (0 == memcmp(buf.str + 1, "alse", 4)))
2491 ||
2492 ((buf.str[0] == 'F') && (0 == memcmp(buf.str + 1, "alse", 4) ||
2493 0 == memcmp(buf.str + 1, "ALSE", 4))))
2494 {
2495 *v = false; return true;
2496 }
2497 }
2498 // fallback to c-style int bools
2499 int val = 0;
2500 bool ret = from_chars(buf, &val);
2501 if(C4_LIKELY(ret))
2502 {
2503 *v = (val != 0);
2504 }
2505 return ret;
2506}
2507
2508/** @ingroup doc_from_chars_first */
2509inline size_t from_chars_first(csubstr buf, bool * C4_RESTRICT v) noexcept
2510{
2511 csubstr trimmed = buf.first_non_empty_span();
2512 if(trimmed.len == 0 || !from_chars(buf, v))
2513 return csubstr::npos;
2514 return trimmed.len;
2515}
2516
2517
2518//-----------------------------------------------------------------------------
2519// single-char implementation
2520
2521/** @ingroup doc_to_chars */
2522inline size_t to_chars(substr buf, char v) noexcept
2523{
2524 if(buf.len > 0)
2525 {
2526 C4_XASSERT(buf.str);
2527 buf.str[0] = v;
2528 }
2529 return 1;
2530}
2531
2532/** extract a single character from a substring
2533 * @note to extract a string instead and not just a single character, use the csubstr overload
2534 * @ingroup doc_from_chars
2535 * */
2536inline bool from_chars(csubstr buf, char * C4_RESTRICT v) noexcept
2537{
2538 if(buf.len != 1)
2539 return false;
2540 C4_XASSERT(buf.str);
2541 *v = buf.str[0];
2542 return true;
2543}
2544
2545/** @ingroup doc_from_chars_first */
2546inline size_t from_chars_first(csubstr buf, char * C4_RESTRICT v) noexcept
2547{
2548 if(buf.len < 1)
2549 return csubstr::npos;
2550 *v = buf.str[0];
2551 return 1;
2552}
2553
2554
2555//-----------------------------------------------------------------------------
2556// csubstr implementation
2557
2558/** @ingroup doc_to_chars */
2559inline size_t to_chars(substr buf, csubstr v) noexcept
2560{
2561 C4_ASSERT(!buf.overlaps(v));
2562 size_t len = buf.len < v.len ? buf.len : v.len;
2563 // calling memcpy with null strings is undefined behavior
2564 // and will wreak havoc in calling code's branches.
2565 // see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637
2566 if(len)
2567 {
2568 C4_ASSERT(buf.str != nullptr);
2569 C4_ASSERT(v.str != nullptr);
2570 memcpy(buf.str, v.str, len);
2571 }
2572 return v.len;
2573}
2574
2575/** @ingroup doc_from_chars */
2576inline bool from_chars(csubstr buf, csubstr *C4_RESTRICT v) noexcept
2577{
2578 *v = buf;
2579 return true;
2580}
2581
2582/** @ingroup doc_from_chars_first */
2583inline size_t from_chars_first(csubstr buf, csubstr * C4_RESTRICT v) noexcept
2584{
2585 csubstr trimmed = buf.first_non_empty_span();
2586 if(trimmed.len == 0)
2587 return csubstr::npos;
2588 *v = trimmed;
2589 return static_cast<size_t>(trimmed.end() - buf.begin());
2590}
2591
2592
2593//-----------------------------------------------------------------------------
2594// substr
2595
2596/** @ingroup doc_to_chars */
2597inline size_t to_chars(substr buf, substr v) noexcept
2598{
2599 C4_ASSERT(!buf.overlaps(v));
2600 size_t len = buf.len < v.len ? buf.len : v.len;
2601 // calling memcpy zero len is undefined behavior
2602 // and will wreak havoc in calling code's branches.
2603 // see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637
2604 if(len)
2605 {
2606 C4_ASSERT(buf.str != nullptr);
2607 C4_ASSERT(v.str != nullptr);
2608 memcpy(buf.str, v.str, len);
2609 }
2610 return v.len;
2611}
2612
2613/** @ingroup doc_from_chars */
2614inline bool from_chars(csubstr buf, substr * C4_RESTRICT v) noexcept
2615{
2616 C4_ASSERT(!buf.overlaps(*v));
2617 // is the destination buffer wide enough?
2618 if(v->len >= buf.len)
2619 {
2620 // calling memcpy with zero len is undefined behavior
2621 // and will wreak havoc in calling code's branches.
2622 // see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637
2623 if(buf.len)
2624 {
2625 C4_ASSERT(buf.str != nullptr);
2626 C4_ASSERT(v->str != nullptr);
2627 memcpy(v->str, buf.str, buf.len);
2628 }
2629 v->len = buf.len;
2630 return true;
2631 }
2632 return false;
2633}
2634
2635/** @ingroup doc_from_chars_first */
2636inline size_t from_chars_first(csubstr buf, substr * C4_RESTRICT v) noexcept
2637{
2638 csubstr trimmed = buf.first_non_empty_span();
2639 C4_ASSERT(!trimmed.overlaps(*v));
2640 if(C4_UNLIKELY(trimmed.len == 0))
2641 return csubstr::npos;
2642 size_t len = trimmed.len > v->len ? v->len : trimmed.len;
2643 // calling memcpy with zero len is undefined behavior
2644 // and will wreak havoc in calling code's branches.
2645 // see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637
2646 if(len)
2647 {
2648 C4_ASSERT(buf.str != nullptr);
2649 C4_ASSERT(v->str != nullptr);
2650 memcpy(v->str, trimmed.str, len);
2651 }
2652 if(C4_UNLIKELY(trimmed.len > v->len))
2653 return csubstr::npos;
2654 return static_cast<size_t>(trimmed.end() - buf.begin());
2655}
2656
2657
2658//-----------------------------------------------------------------------------
2659
2660/** @ingroup doc_to_chars
2661 * (1) overload for `char(&)[N]` and `const char(&)[N]` */
2662template<size_t N>
2663size_t to_chars(substr buf, const char (& C4_RESTRICT v)[N]) noexcept
2664{
2665 return to_chars(buf, csubstr{v, N-1});
2666}
2667
2668/** @ingroup doc_to_chars
2669 * (2) overload for `char*` and `const char*`. Must be zero-terminated!
2670 * @warning will call strlen()
2671 * @note this overload uses SFINAE to prevent it from overriding the array overload
2672 * @see For a more detailed explanation on why the plain pointer overloads cannot
2673 * coexist with the array overloads, see http://cplusplus.bordoon.com/specializeForCharacterArrays.html */
2674template<class CharPtr>
2675auto to_chars(substr buf, CharPtr v) noexcept
2676 -> typename std::enable_if<std::is_same<CharPtr, char*>::value
2677 ||
2678 std::is_same<CharPtr, const char*>::value, size_t>::type
2679{
2680 return v ? to_chars(buf, csubstr{v, strlen(v)}) : 0;
2681}
2682
2683
2684//-----------------------------------------------------------------------------
2685// nullptr implementation
2686
2687/** @ingroup doc_to_chars */
2688C4_ALWAYS_INLINE size_t to_chars(substr, std::nullptr_t) noexcept
2689{
2690 return 0;
2691}
2692
2693// from_chars() and from_chars_sub() must not exist for nullptr
2694
2695
2696/** @} */
2697
2698} // namespace c4
2699
2700// NOLINTEND(hicpp-signed-bitwise)
2701
2702#if defined(_MSC_VER) && !defined(__clang__)
2703# pragma warning(pop)
2704#elif defined(__clang__)
2705# pragma clang diagnostic pop
2706#elif defined(__GNUC__)
2707# pragma GCC diagnostic pop
2708#endif
2709
2710#endif /* _C4_CHARCONV_HPP_ */
#define C4_NO_UBSAN_IOVRFLW
Definition charconv.hpp:126
bool atod(csubstr str, double *v) noexcept
Convert a string to a double precision real number.
size_t atod_first(csubstr str, double *v) noexcept
Convert a string to a double precision real number.
size_t atof_first(csubstr str, float *v) noexcept
Convert a string to a single precision real number.
bool atof(csubstr str, float *v) noexcept
Convert a string to a single precision real number.
bool atoi(csubstr str, T *v) noexcept
Convert a trimmed string to a signed integral value.
size_t atoi_first(csubstr str, T *v)
Select the next range of characters in the string that can be parsed as a signed integral value,...
bool atou(csubstr str, T *v) noexcept
Convert a trimmed string to an unsigned integral value.
size_t atou_first(csubstr str, T *v)
Select the next range of characters in the string that can be parsed as an unsigned integral value,...
bool atox(csubstr s, uint8_t *v) noexcept
RealFormat_e
Definition charconv.hpp:192
@ FTOA_FLEX
print the real number in flexible format (like g)
Definition charconv.hpp:198
@ FTOA_SCIENT
print the real number in scientific format (like e)
Definition charconv.hpp:196
@ FTOA_FLOAT
print the real number in floating point format (like f)
Definition charconv.hpp:194
@ FTOA_HEXA
print the real number in hexadecimal format (like a)
Definition charconv.hpp:200
auto digits_dec(T v) noexcept -> typename std::enable_if< sizeof(T)==1u, unsigned >::type
decimal digits for 8 bit integers
Definition charconv.hpp:433
unsigned digits_oct(T v_) noexcept
return the number of digits required to encode an octal number.
Definition charconv.hpp:548
unsigned digits_hex(T v) noexcept
return the number of digits required to encode an hexadecimal number.
Definition charconv.hpp:530
unsigned digits_bin(T v) noexcept
return the number of digits required to encode a binary number.
Definition charconv.hpp:539
size_t dtoa(substr str, double v, int precision=-1, RealFormat_e formatting=FTOA_FLEX) noexcept
Convert a double-precision real number to string.
size_t from_chars_first(csubstr buf, uint8_t *v) noexcept
bool from_chars(csubstr buf, uint8_t *v) noexcept
size_t ftoa(substr str, float v, int precision=-1, RealFormat_e formatting=FTOA_FLEX) noexcept
Convert a single-precision real number to string.
size_t itoa(substr buf, T v) noexcept
convert an integral signed decimal to a string.
auto overflows(csubstr str) noexcept -> typename std::enable_if< std::is_unsigned< T >::value, bool >::type
Test if the following string would overflow when converted to associated integral types; this functio...
bool read_bin(csubstr s, I *v) noexcept
read a binary integer from a string.
Definition charconv.hpp:938
bool read_oct(csubstr s, I *v) noexcept
read an octal integer from a string.
Definition charconv.hpp:968
bool read_hex(csubstr s, I *v) noexcept
read an hexadecimal integer from a string.
Definition charconv.hpp:903
bool read_dec(csubstr s, I *v) noexcept
read a decimal integer from a string.
Definition charconv.hpp:875
bool from_chars(ryml::csubstr buf, vec2< T > *v)
size_t to_chars(ryml::substr buf, vec2< T > v)
basic_substring< char > substr
a mutable string view
Definition substr.hpp:2356
basic_substring< const char > csubstr
an immutable string view
Definition substr.hpp:2357
substr to_chars_sub(substr buf, T const &v) noexcept
call to_chars() and return a substr consisting of the written portion of the input buffer.
size_t utoa(substr buf, T v) noexcept
convert an integral unsigned decimal to a string.
void write_bin_unchecked(substr buf, T v, unsigned digits_v) noexcept
Definition charconv.hpp:680
void write_dec_unchecked(substr buf, T v, unsigned digits_v) noexcept
Definition charconv.hpp:615
void write_hex_unchecked(substr buf, T v, unsigned digits_v) noexcept
Definition charconv.hpp:648
void write_oct_unchecked(substr buf, T v, unsigned digits_v) noexcept
Definition charconv.hpp:664
size_t write_hex(substr buf, T v) noexcept
write an integer to a string in hexadecimal format.
Definition charconv.hpp:734
size_t write_dec(substr buf, T v) noexcept
write an integer to a string in decimal format.
Definition charconv.hpp:715
size_t write_bin(substr buf, T v) noexcept
write an integer to a string in binary format.
Definition charconv.hpp:772
size_t write_oct(substr buf, T v) noexcept
write an integer to a string in octal format.
Definition charconv.hpp:753
size_t xtoa(substr s, uint8_t v) noexcept
(Undefined by default) Use shorter error message from checks/asserts: do not show the check condition...
Definition common.cpp:14
basic_substring first_uint_span() const
get the first span which can be interpreted as an unsigned integer
Definition substr.hpp:1278
basic_substring first_int_span() const
get the first span which can be interpreted as a signed integer
Definition substr.hpp:1290
size_t len
the length of the substring
Definition substr.hpp:218
iterator begin() noexcept
Definition substr.hpp:360
bool overlaps(ro_substr const that) const noexcept
true if there is overlap of at least one element between that and *this
Definition substr.hpp:494
iterator end() noexcept
Definition substr.hpp:361
basic_substring sub(size_t first) const noexcept
return [first,len[
Definition substr.hpp:503
C * str
a restricted pointer to the first character of the substring
Definition substr.hpp:216
read+write string views
#define _c4append(first, last)