rapidyaml 0.15.2
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
410/** @endcond */
411
412
413//-----------------------------------------------------------------------------
414//-----------------------------------------------------------------------------
415//-----------------------------------------------------------------------------
416
417
418/** @defgroup doc_digits Get number of digits
419 *
420 * @note At first sight this code may look heavily branchy and
421 * therefore inefficient. However, measurements revealed this to be
422 * the fastest among the alternatives.
423 *
424 * @see https://github.com/biojppm/c4core/pull/77
425 *
426 * @{
427 */
428
429/** decimal digits for 8 bit integers */
430template<class T>
431C4_CONSTEXPR14 C4_ALWAYS_INLINE
432auto digits_dec(T v) noexcept
433 -> typename std::enable_if<sizeof(T) == 1u, unsigned>::type
434{
435 C4_STATIC_ASSERT(std::is_integral<T>::value);
436 C4_ASSERT(v >= 0);
437 return ((v >= 100) ? 3u : ((v >= 10) ? 2u : 1u));
438}
439
440/** decimal digits for 16 bit integers */
441template<class T>
442C4_CONSTEXPR14 C4_ALWAYS_INLINE
443auto digits_dec(T v) noexcept
444 -> typename std::enable_if<sizeof(T) == 2u, unsigned>::type
445{
446 C4_STATIC_ASSERT(std::is_integral<T>::value);
447 C4_ASSERT(v >= 0);
448 return ((v >= 10000) ? 5u : (v >= 1000) ? 4u : (v >= 100) ? 3u : (v >= 10) ? 2u : 1u);
449}
450
451/** decimal digits for 32 bit integers */
452template<class T>
453C4_CONSTEXPR14 C4_ALWAYS_INLINE
454auto digits_dec(T v) noexcept
455 -> typename std::enable_if<sizeof(T) == 4u, unsigned>::type
456{
457 C4_STATIC_ASSERT(std::is_integral<T>::value);
458 C4_ASSERT(v >= 0);
459 return ((v >= 1000000000) ? 10u : (v >= 100000000) ? 9u : (v >= 10000000) ? 8u :
460 (v >= 1000000) ? 7u : (v >= 100000) ? 6u : (v >= 10000) ? 5u :
461 (v >= 1000) ? 4u : (v >= 100) ? 3u : (v >= 10) ? 2u : 1u);
462}
463
464/** decimal digits for 64 bit integers */
465template<class T>
466C4_CONSTEXPR14 C4_ALWAYS_INLINE
467auto digits_dec(T v) noexcept
468 -> typename std::enable_if<sizeof(T) == 8u, unsigned>::type
469{
470 // thanks @fargies!!!
471 // https://github.com/biojppm/c4core/pull/77#issuecomment-1063753568
472 C4_STATIC_ASSERT(std::is_integral<T>::value);
473 C4_ASSERT(v >= 0);
474 if(v >= 1000000000) // 10
475 {
476 if(v >= 100000000000000) // 15 [15-20] range
477 {
478 if(v >= 100000000000000000) // 18 (15 + (20 - 15) / 2)
479 {
480 if((typename std::make_unsigned<T>::type)v >= 10000000000000000000u) // 20
481 return 20u;
482 else
483 return (v >= 1000000000000000000) ? 19u : 18u;
484 }
485 else if(v >= 10000000000000000) // 17
486 {
487 return 17u;
488 }
489 else
490 {
491 return(v >= 1000000000000000) ? 16u : 15u;
492 }
493 }
494 else if(v >= 1000000000000) // 13
495 {
496 return (v >= 10000000000000) ? 14u : 13u;
497 }
498 else if(v >= 100000000000) // 12
499 {
500 return 12;
501 }
502 else
503 {
504 return(v >= 10000000000) ? 11u : 10u;
505 }
506 }
507 else if(v >= 10000) // 5 [5-9] range
508 {
509 if(v >= 10000000) // 8
510 return (v >= 100000000) ? 9u : 8u;
511 else if(v >= 1000000) // 7
512 return 7;
513 else
514 return (v >= 100000) ? 6u : 5u;
515 }
516 else if(v >= 100)
517 {
518 return (v >= 1000) ? 4u : 3u;
519 }
520 else
521 {
522 return (v >= 10) ? 2u : 1u;
523 }
524}
525
526
527/** return the number of digits required to encode an hexadecimal number. */
528template<class T>
529C4_CONSTEXPR14 C4_ALWAYS_INLINE unsigned digits_hex(T v) noexcept
530{
531 C4_STATIC_ASSERT(std::is_integral<T>::value);
532 C4_ASSERT(v >= 0);
533 return v ? 1u + (msb((typename std::make_unsigned<T>::type)v) >> 2u) : 1u;
534}
535
536/** return the number of digits required to encode a binary number. */
537template<class T>
538C4_CONSTEXPR14 C4_ALWAYS_INLINE unsigned digits_bin(T v) noexcept
539{
540 C4_STATIC_ASSERT(std::is_integral<T>::value);
541 C4_ASSERT(v >= 0);
542 return v ? 1u + msb((typename std::make_unsigned<T>::type)v) : 1u;
543}
544
545/** return the number of digits required to encode an octal number. */
546template<class T>
547C4_CONSTEXPR14 C4_ALWAYS_INLINE unsigned digits_oct(T v_) noexcept
548{
549 // TODO: is there a better way?
550 C4_STATIC_ASSERT(std::is_integral<T>::value);
551 C4_ASSERT(v_ >= 0);
552 using U = typename std::conditional<sizeof(T) <= sizeof(unsigned),
553 unsigned,
554 typename std::make_unsigned<T>::type>::type;
555 U v = (U) v_; // safe because we require v_ >= 0 // NOLINT
556 uint32_t n_ = 1;
557 enum : U {
558 b2_ = 64u,
559 b3_ = 64u * 8u,
560 b4_ = 64u * 8u * 8u,
561 };
562 while(true)
563 {
564 if(v < 8u)
565 return n_;
566 else if(v < b2_)
567 return n_ + 1;
568 else if(v < b3_)
569 return n_ + 2;
570 else if(v < b4_)
571 return n_ + 3;
572 v /= (U) b4_;
573 n_ += 4;
574 }
575}
576
577/** @} */
578
579
580//-----------------------------------------------------------------------------
581//-----------------------------------------------------------------------------
582//-----------------------------------------------------------------------------
583
584/** @cond dev */
585namespace detail {
586C4_INLINE_CONSTEXPR const char hexchars[] = "0123456789abcdef";
587C4_INLINE_CONSTEXPR const char digits0099[] =
588 "0001020304050607080910111213141516171819"
589 "2021222324252627282930313233343536373839"
590 "4041424344454647484950515253545556575859"
591 "6061626364656667686970717273747576777879"
592 "8081828384858687888990919293949596979899";
593} // namespace detail
594/** @endcond */
595
596C4_SUPPRESS_WARNING_GCC_PUSH
597C4_SUPPRESS_WARNING_GCC("-Warray-bounds") // gcc has false positives here
598#if (defined(__GNUC__) && (__GNUC__ >= 7))
599C4_SUPPRESS_WARNING_GCC("-Wstringop-overflow") // gcc has false positives here
600#endif
601
602/** @defgroup doc_write_unchecked Write with known number of digits
603 *
604 * Writes a value without checking the buffer length with regards to
605 * the required number of digits to encode the value. It is the
606 * responsibility of the caller to ensure that the provided number of
607 * digits is enough to write the given value. Notwithstanding the
608 * name, assertions are liberally performed, so this code is safe.
609 *
610 * @{ */
611
612template<class T>
613C4_HOT C4_ALWAYS_INLINE
614void write_dec_unchecked(substr buf, T v, unsigned digits_v) noexcept
615{
616 C4_STATIC_ASSERT(std::is_integral<T>::value);
617 C4_ASSERT(v >= 0);
618 C4_ASSERT(buf.len >= digits_v);
619 C4_XASSERT(digits_v == digits_dec(v));
620 // in bm_xtoa: checkoncelog_singlediv_write2
621 while(v >= T(100))
622 {
623 T quo = v;
624 quo /= T(100);
625 const auto num = (v - quo * T(100)) << 1u; // NOLINT
626 v = quo;
627 buf.str[--digits_v] = detail::digits0099[num + 1];
628 buf.str[--digits_v] = detail::digits0099[num];
629 }
630 if(v >= T(10))
631 {
632 C4_ASSERT(digits_v == 2);
633 const auto num = v << 1u;
634 buf.str[1] = detail::digits0099[num + 1];
635 buf.str[0] = detail::digits0099[num];
636 }
637 else
638 {
639 C4_ASSERT(digits_v == 1);
640 buf.str[0] = (char)('0' + v);
641 }
642}
643
644
645template<class T>
646C4_HOT C4_ALWAYS_INLINE
647void write_hex_unchecked(substr buf, T v, unsigned digits_v) noexcept
648{
649 C4_STATIC_ASSERT(std::is_integral<T>::value);
650 C4_ASSERT(v >= 0);
651 C4_ASSERT(buf.len >= digits_v);
652 C4_XASSERT(digits_v == digits_hex(v));
653 do {
654 buf.str[--digits_v] = detail::hexchars[v & T(15)];
655 v >>= 4;
656 } while(v);
657 C4_ASSERT(digits_v == 0);
658}
659
660
661template<class T>
662C4_HOT C4_ALWAYS_INLINE
663void write_oct_unchecked(substr buf, T v, unsigned digits_v) noexcept
664{
665 C4_STATIC_ASSERT(std::is_integral<T>::value);
666 C4_ASSERT(v >= 0);
667 C4_ASSERT(buf.len >= digits_v);
668 C4_XASSERT(digits_v == digits_oct(v));
669 do {
670 buf.str[--digits_v] = (char)('0' + (v & T(7)));
671 v >>= 3;
672 } while(v);
673 C4_ASSERT(digits_v == 0);
674}
675
676
677template<class T>
678C4_HOT C4_ALWAYS_INLINE
679void write_bin_unchecked(substr buf, T v, unsigned digits_v) noexcept
680{
681 C4_STATIC_ASSERT(std::is_integral<T>::value);
682 C4_ASSERT(v >= 0);
683 C4_ASSERT(buf.len >= digits_v);
684 C4_XASSERT(digits_v == digits_bin(v));
685 do {
686 buf.str[--digits_v] = (char)('0' + (v & T(1)));
687 v >>= 1;
688 } while(v);
689 C4_ASSERT(digits_v == 0);
690}
691
692/** @} */ // write_unchecked
693
694
695//-----------------------------------------------------------------------------
696//-----------------------------------------------------------------------------
697//-----------------------------------------------------------------------------
698
699/** @defgroup doc_write Write a value
700 *
701 * Writes a value without checking the buffer length
702 * decimal number -- but asserting.
703 *
704 * @{ */
705
706/** write an integer to a string in decimal format. This is the
707 * lowest level (and the fastest) function to do this task.
708 * @note does not accept negative numbers
709 * @note the resulting string is NOT zero-terminated.
710 * @note it is ok to call this with an empty or too-small buffer;
711 * no writes will occur, and the required size will be returned
712 * @return the number of characters required for the buffer. */
713template<class T>
714C4_ALWAYS_INLINE size_t write_dec(substr buf, T v) noexcept
715{
716 C4_STATIC_ASSERT(std::is_integral<T>::value);
717 C4_ASSERT(v >= 0);
718 unsigned digits = digits_dec(v);
719 if C4_LIKELY(buf.len >= digits)
720 write_dec_unchecked(buf, v, digits);
721 return digits;
722}
723
724/** write an integer to a string in hexadecimal format. This is the
725 * lowest level (and the fastest) function to do this task.
726 * @note does not accept negative numbers
727 * @note does not prefix with 0x
728 * @note the resulting string is NOT zero-terminated.
729 * @note it is ok to call this with an empty or too-small buffer;
730 * no writes will occur, and the required size will be returned
731 * @return the number of characters required for the buffer. */
732template<class T>
733C4_ALWAYS_INLINE size_t write_hex(substr buf, T v) noexcept
734{
735 C4_STATIC_ASSERT(std::is_integral<T>::value);
736 C4_ASSERT(v >= 0);
737 unsigned digits = digits_hex(v);
738 if C4_LIKELY(buf.len >= digits)
739 write_hex_unchecked(buf, v, digits);
740 return digits;
741}
742
743/** write an integer to a string in octal format. This is the
744 * lowest level (and the fastest) function to do this task.
745 * @note does not accept negative numbers
746 * @note does not prefix with 0o
747 * @note the resulting string is NOT zero-terminated.
748 * @note it is ok to call this with an empty or too-small buffer;
749 * no writes will occur, and the required size will be returned
750 * @return the number of characters required for the buffer. */
751template<class T>
752C4_ALWAYS_INLINE size_t write_oct(substr buf, T v) noexcept
753{
754 C4_STATIC_ASSERT(std::is_integral<T>::value);
755 C4_ASSERT(v >= 0);
756 unsigned digits = digits_oct(v);
757 if C4_LIKELY(buf.len >= digits)
758 write_oct_unchecked(buf, v, digits);
759 return digits;
760}
761
762/** write an integer to a string in binary format. This is the
763 * lowest level (and the fastest) function to do this task.
764 * @note does not accept negative numbers
765 * @note does not prefix with 0b
766 * @note the resulting string is NOT zero-terminated.
767 * @note it is ok to call this with an empty or too-small buffer;
768 * no writes will occur, and the required size will be returned
769 * @return the number of characters required for the buffer. */
770template<class T>
771C4_ALWAYS_INLINE size_t write_bin(substr buf, T v) noexcept
772{
773 C4_STATIC_ASSERT(std::is_integral<T>::value);
774 C4_ASSERT(v >= 0);
775 unsigned digits = digits_bin(v);
776 C4_ASSERT(digits > 0);
777 if C4_LIKELY(buf.len >= digits)
778 write_bin_unchecked(buf, v, digits);
779 return digits;
780}
781
782
783/** @cond dev */
784namespace detail {
785template<class U> using NumberWriter = size_t (*)(substr, U);
786template<class T, NumberWriter<T> writer>
787size_t write_num_digits(substr buf, T v, size_t num_digits) noexcept
788{
789 C4_STATIC_ASSERT(std::is_integral<T>::value);
790 const size_t ret = writer(buf, v);
791 if(ret >= num_digits)
792 return ret;
793 else if(ret >= buf.len || num_digits > buf.len)
794 return num_digits;
795 C4_ASSERT(num_digits >= ret);
796 const size_t delta = static_cast<size_t>(num_digits - ret); // NOLINT
797 C4_ASSERT(ret + delta <= buf.len);
798 if(ret)
799 memmove(buf.str + delta, buf.str, ret);
800 if(delta)
801 memset(buf.str, '0', delta);
802 return num_digits;
803}
804} // namespace detail
805/** @endcond */
806
807
808/** same as c4::write_dec(), but pad with zeroes on the left such that
809 * the resulting string is @p num_digits wide. If the given number
810 * requires more than num_digits, then the number prevails. */
811template<class T>
812C4_ALWAYS_INLINE size_t write_dec(substr buf, T val, size_t num_digits) noexcept
813{
814 return detail::write_num_digits<T, &write_dec<T>>(buf, val, num_digits);
815}
816
817/** same as c4::write_hex(), but pad with zeroes on the left such that
818 * the resulting string is @p num_digits wide. If the given number
819 * requires more than num_digits, then the number prevails. */
820template<class T>
821C4_ALWAYS_INLINE size_t write_hex(substr buf, T val, size_t num_digits) noexcept
822{
823 return detail::write_num_digits<T, &write_hex<T>>(buf, val, num_digits);
824}
825
826/** same as c4::write_bin(), but pad with zeroes on the left such that
827 * the resulting string is @p num_digits wide. If the given number
828 * requires more than num_digits, then the number prevails. */
829template<class T>
830C4_ALWAYS_INLINE size_t write_bin(substr buf, T val, size_t num_digits) noexcept
831{
832 return detail::write_num_digits<T, &write_bin<T>>(buf, val, num_digits);
833}
834
835/** same as c4::write_oct(), but pad with zeroes on the left such that
836 * the resulting string is @p num_digits wide. If the given number
837 * requires more than num_digits, then the number prevails. */
838template<class T>
839C4_ALWAYS_INLINE size_t write_oct(substr buf, T val, size_t num_digits) noexcept
840{
841 return detail::write_num_digits<T, &write_oct<T>>(buf, val, num_digits);
842}
843
844/** @} */ // write
845
846C4_SUPPRESS_WARNING_GCC_POP
847
848
849//-----------------------------------------------------------------------------
850//-----------------------------------------------------------------------------
851//-----------------------------------------------------------------------------
852
853
854C4_SUPPRESS_WARNING_MSVC_PUSH
855C4_SUPPRESS_WARNING_MSVC(4365) // '=': conversion from 'int' to 'I', signed/unsigned mismatch
856
857/** @defgroup doc_read Read a value
858 *
859 * @{ */
860
861/** read a decimal integer from a string. This is the
862 * lowest level (and the fastest) function to do this task.
863 * @note does not accept negative numbers
864 * @note The string must be trimmed. Whitespace is not accepted.
865 * @note the string must not be empty
866 * @note there is no check for overflow; the value wraps around
867 * in a way similar to the standard C/C++ overflow behavior.
868 * For example, `read_dec<int8_t>("128", &val)` returns true
869 * and val will be set to 0 because 127 is the max i8 value.
870 * @see overflows<T>() to find out if a number string overflows a type range
871 * @return true if the conversion was successful (no overflow check) */
872template<class I>
874C4_ALWAYS_INLINE bool read_dec(csubstr s, I *C4_RESTRICT v) noexcept
875{
876 C4_STATIC_ASSERT(std::is_integral<I>::value);
877 C4_ASSERT(!s.empty());
878 *v = 0;
879 for(char c : s)
880 {
881 if C4_UNLIKELY(c < '0' || c > '9')
882 return false;
883 *v = ((*v) * I(10)) + (I(c) - I('0'));
884 }
885 return true;
886}
887
888/** read an hexadecimal integer from a string. This is the
889 * lowest level (and the fastest) function to do this task.
890 * @note does not accept negative numbers
891 * @note does not accept leading 0x or 0X
892 * @note the string must not be empty
893 * @note the string must be trimmed. Whitespace is not accepted.
894 * @note there is no check for overflow; the value wraps around
895 * in a way similar to the standard C/C++ overflow behavior.
896 * For example, `read_hex<int8_t>("80", &val)` returns true
897 * and val will be set to 0 because 7f is the max i8 value.
898 * @see overflows<T>() to find out if a number string overflows a type range
899 * @return true if the conversion was successful (no overflow check) */
900template<class I>
902C4_ALWAYS_INLINE bool read_hex(csubstr s, I *C4_RESTRICT v) noexcept
903{
904 C4_STATIC_ASSERT(std::is_integral<I>::value);
905 C4_ASSERT(!s.empty());
906 *v = 0;
907 for(char c : s)
908 {
909 I cv;
910 if(c >= '0' && c <= '9')
911 cv = I(c) - I('0');
912 else if(c >= 'a' && c <= 'f')
913 cv = I(10) + (I(c) - I('a'));
914 else if(c >= 'A' && c <= 'F')
915 cv = I(10) + (I(c) - I('A'));
916 else
917 return false;
918 *v = ((*v) * I(16)) + cv;
919 }
920 return true;
921}
922
923/** read a binary integer from a string. This is the
924 * lowest level (and the fastest) function to do this task.
925 * @note does not accept negative numbers
926 * @note does not accept leading 0b or 0B
927 * @note the string must not be empty
928 * @note the string must be trimmed. Whitespace is not accepted.
929 * @note there is no check for overflow; the value wraps around
930 * in a way similar to the standard C/C++ overflow behavior.
931 * For example, `read_bin<int8_t>("10000000", &val)` returns true
932 * and val will be set to 0 because 1111111 is the max i8 value.
933 * @see overflows<T>() to find out if a number string overflows a type range
934 * @return true if the conversion was successful (no overflow check) */
935template<class I>
937C4_ALWAYS_INLINE bool read_bin(csubstr s, I *C4_RESTRICT v) noexcept
938{
939 C4_STATIC_ASSERT(std::is_integral<I>::value);
940 C4_ASSERT(!s.empty());
941 *v = 0;
942 for(char c : s)
943 {
944 *v <<= 1;
945 if(c == '1')
946 *v |= 1;
947 else if(c != '0')
948 return false;
949 }
950 return true;
951}
952
953/** read an octal integer from a string. This is the
954 * lowest level (and the fastest) function to do this task.
955 * @note does not accept negative numbers
956 * @note does not accept leading 0o or 0O
957 * @note the string must not be empty
958 * @note the string must be trimmed. Whitespace is not accepted.
959 * @note there is no check for overflow; the value wraps around
960 * in a way similar to the standard C/C++ overflow behavior.
961 * For example, `read_oct<int8_t>("200", &val)` returns true
962 * and val will be set to 0 because 177 is the max i8 value.
963 * @see overflows<T>() to find out if a number string overflows a type range
964 * @return true if the conversion was successful (no overflow check) */
965template<class I>
967C4_ALWAYS_INLINE bool read_oct(csubstr s, I *C4_RESTRICT v) noexcept
968{
969 C4_STATIC_ASSERT(std::is_integral<I>::value);
970 C4_ASSERT(!s.empty());
971 *v = 0;
972 for(char c : s)
973 {
974 if C4_UNLIKELY(c < '0' || c > '7')
975 return false;
976 *v = ((*v) * I(8)) + (I(c) - I('0'));
977 }
978 return true;
979}
980
981/** @} */
982
983C4_SUPPRESS_WARNING_MSVC_POP
984
985
986//-----------------------------------------------------------------------------
987//-----------------------------------------------------------------------------
988//-----------------------------------------------------------------------------
989
990C4_SUPPRESS_WARNING_GCC_WITH_PUSH("-Wswitch-default")
991
992/** @cond dev */
993namespace detail {
994inline size_t _itoa2buf(substr buf, size_t pos, csubstr val) noexcept
995{
996 C4_ASSERT(pos < buf.len);
997 C4_ASSERT(pos + val.len <= buf.len);
998 C4_ASSERT(val.len > 0);
999 memcpy(buf.str + pos, val.str, val.len);
1000 return pos + val.len;
1001}
1002inline size_t _itoa2bufwithdigits(substr buf, size_t pos, size_t num_digits, csubstr val) noexcept
1003{
1004 num_digits = num_digits > val.len ? num_digits - val.len : 0;
1005 C4_ASSERT(num_digits + val.len <= buf.len);
1006 for(size_t i = 0; i < num_digits; ++i)
1007 c4append_('0')
1008 return detail::_itoa2buf(buf, pos, val);
1009}
1010template<class I>
1011C4_NO_INLINE size_t _itoadec2buf(substr buf) noexcept
1012{
1013 using digits_type = detail::charconv_digits<I>;
1014 if C4_UNLIKELY(buf.len < digits_type::maxdigits_dec)
1015 return digits_type::maxdigits_dec;
1016 buf.str[0] = '-';
1017 return detail::_itoa2buf(buf, 1, digits_type::min_value_dec());
1018}
1019template<class I>
1020C4_NO_INLINE size_t _itoa2buf(substr buf, I radix) noexcept
1021{
1022 using digits_type = detail::charconv_digits<I>;
1023 size_t pos = 0;
1024 if C4_LIKELY(buf.len > 0)
1025 buf.str[pos++] = '-';
1026 switch(radix) // NOLINT(hicpp-multiway-paths-covered)
1027 {
1028 case I(10):
1029 if C4_UNLIKELY(buf.len < digits_type::maxdigits_dec)
1030 return digits_type::maxdigits_dec;
1031 pos =_itoa2buf(buf, pos, digits_type::min_value_dec());
1032 break;
1033 case I(16):
1034 if C4_UNLIKELY(buf.len < digits_type::maxdigits_hex)
1035 return digits_type::maxdigits_hex;
1036 buf.str[pos++] = '0';
1037 buf.str[pos++] = 'x';
1038 pos = _itoa2buf(buf, pos, digits_type::min_value_hex());
1039 break;
1040 case I( 2):
1041 if C4_UNLIKELY(buf.len < digits_type::maxdigits_bin)
1042 return digits_type::maxdigits_bin;
1043 buf.str[pos++] = '0';
1044 buf.str[pos++] = 'b';
1045 pos = _itoa2buf(buf, pos, digits_type::min_value_bin());
1046 break;
1047 case I( 8):
1048 if C4_UNLIKELY(buf.len < digits_type::maxdigits_oct)
1049 return digits_type::maxdigits_oct;
1050 buf.str[pos++] = '0';
1051 buf.str[pos++] = 'o';
1052 pos = _itoa2buf(buf, pos, digits_type::min_value_oct());
1053 break;
1054 }
1055 return pos;
1056}
1057template<class I>
1058C4_NO_INLINE size_t _itoa2buf(substr buf, I radix, size_t num_digits) noexcept
1059{
1060 using digits_type = detail::charconv_digits<I>;
1061 size_t pos = 0;
1062 size_t needed_digits = 0;
1063 if C4_LIKELY(buf.len > 0)
1064 buf.str[pos++] = '-';
1065 switch(radix) // NOLINT(hicpp-multiway-paths-covered)
1066 {
1067 case I(10):
1068 // add 1 to account for -
1069 needed_digits = num_digits+1 > digits_type::maxdigits_dec ? num_digits+1 : digits_type::maxdigits_dec;
1070 if C4_UNLIKELY(buf.len < needed_digits)
1071 return needed_digits;
1072 pos = _itoa2bufwithdigits(buf, pos, num_digits, digits_type::min_value_dec());
1073 break;
1074 case I(16):
1075 // add 3 to account for -0x
1076 needed_digits = num_digits+3 > digits_type::maxdigits_hex ? num_digits+3 : digits_type::maxdigits_hex;
1077 if C4_UNLIKELY(buf.len < needed_digits)
1078 return needed_digits;
1079 buf.str[pos++] = '0';
1080 buf.str[pos++] = 'x';
1081 pos = _itoa2bufwithdigits(buf, pos, num_digits, digits_type::min_value_hex());
1082 break;
1083 case I(2):
1084 // add 3 to account for -0b
1085 needed_digits = num_digits+3 > digits_type::maxdigits_bin ? num_digits+3 : digits_type::maxdigits_bin;
1086 if C4_UNLIKELY(buf.len < needed_digits)
1087 return needed_digits;
1088 C4_ASSERT(buf.len >= digits_type::maxdigits_bin);
1089 buf.str[pos++] = '0';
1090 buf.str[pos++] = 'b';
1091 pos = _itoa2bufwithdigits(buf, pos, num_digits, digits_type::min_value_bin());
1092 break;
1093 case I(8):
1094 // add 3 to account for -0o
1095 needed_digits = num_digits+3 > digits_type::maxdigits_oct ? num_digits+3 : digits_type::maxdigits_oct;
1096 if C4_UNLIKELY(buf.len < needed_digits)
1097 return needed_digits;
1098 C4_ASSERT(buf.len >= digits_type::maxdigits_oct);
1099 buf.str[pos++] = '0';
1100 buf.str[pos++] = 'o';
1101 pos = _itoa2bufwithdigits(buf, pos, num_digits, digits_type::min_value_oct());
1102 break;
1103 }
1104 return pos;
1105}
1106} // namespace detail
1107/** @endcond */
1108
1109
1110/** @defgroup doc_itoa itoa: signed to chars
1111 *
1112 * @{ */
1113
1114/** convert an integral signed decimal to a string.
1115 * @note the resulting string is NOT zero-terminated.
1116 * @note it is ok to call this with an empty or too-small buffer;
1117 * no writes will occur, and the needed size will be returned
1118 * @return the number of characters required for the buffer. */
1119template<class T>
1120C4_ALWAYS_INLINE size_t itoa(substr buf, T v) noexcept
1121{
1122 C4_STATIC_ASSERT(std::is_signed<T>::value);
1123 if(v >= T(0))
1124 {
1125 // write_dec() checks the buffer size, so no need to check here
1126 return write_dec(buf, v);
1127 }
1128 // when T is the min value (eg i8: -128), negating it
1129 // will overflow, so treat the min as a special case
1130 if C4_LIKELY(v != std::numeric_limits<T>::min())
1131 {
1132 v = (T)-v;
1133 unsigned digits = digits_dec(v);
1134 if C4_LIKELY(buf.len >= digits + 1u)
1135 {
1136 buf.str[0] = '-';
1137 write_dec_unchecked(buf.sub(1), v, digits);
1138 }
1139 return digits + 1u;
1140 }
1141 return detail::_itoadec2buf<T>(buf);
1142}
1143
1144/** convert an integral signed integer to a string, using a specific
1145 * radix. The radix must be 2, 8, 10 or 16.
1146 *
1147 * @note the resulting string is NOT zero-terminated.
1148 * @note it is ok to call this with an empty or too-small buffer;
1149 * no writes will occur, and the needed size will be returned
1150 * @return the number of characters required for the buffer. */
1151template<class T>
1152C4_ALWAYS_INLINE size_t itoa(substr buf, T v, T radix) noexcept
1153{
1154 C4_STATIC_ASSERT(std::is_signed<T>::value);
1155 C4_ASSERT(radix == 2 || radix == 8 || radix == 10 || radix == 16);
1156 C4_SUPPRESS_WARNING_GCC_PUSH
1157 #if (defined(__GNUC__) && (__GNUC__ >= 7))
1158 C4_SUPPRESS_WARNING_GCC("-Wstringop-overflow") // gcc has a false positive here
1159 #endif
1160 // when T is the min value (eg i8: -128), negating it
1161 // will overflow, so treat the min as a special case
1162 if C4_LIKELY(v != std::numeric_limits<T>::min())
1163 {
1164 unsigned pos = 0;
1165 if(v < 0)
1166 {
1167 v = (T)-v;
1168 if C4_LIKELY(buf.len > 0)
1169 buf.str[pos] = '-';
1170 ++pos;
1171 }
1172 unsigned digits = 0;
1173 switch(radix) // NOLINT(hicpp-multiway-paths-covered)
1174 {
1175 case T(10):
1176 digits = digits_dec(v);
1177 if C4_LIKELY(buf.len >= pos + digits)
1178 write_dec_unchecked(buf.sub(pos), v, digits);
1179 break;
1180 case T(16):
1181 digits = digits_hex(v);
1182 if C4_LIKELY(buf.len >= pos + 2u + digits)
1183 {
1184 buf.str[pos + 0] = '0';
1185 buf.str[pos + 1] = 'x';
1186 write_hex_unchecked(buf.sub(pos + 2), v, digits);
1187 }
1188 digits += 2u;
1189 break;
1190 case T(2):
1191 digits = digits_bin(v);
1192 if C4_LIKELY(buf.len >= pos + 2u + digits)
1193 {
1194 buf.str[pos + 0] = '0';
1195 buf.str[pos + 1] = 'b';
1196 write_bin_unchecked(buf.sub(pos + 2), v, digits);
1197 }
1198 digits += 2u;
1199 break;
1200 case T(8):
1201 digits = digits_oct(v);
1202 if C4_LIKELY(buf.len >= pos + 2u + digits)
1203 {
1204 buf.str[pos + 0] = '0';
1205 buf.str[pos + 1] = 'o';
1206 write_oct_unchecked(buf.sub(pos + 2), v, digits);
1207 }
1208 digits += 2u;
1209 break;
1210 }
1211 return pos + digits;
1212 }
1213 C4_SUPPRESS_WARNING_GCC_POP
1214 // when T is the min value (eg i8: -128), negating it
1215 // will overflow
1216 return detail::_itoa2buf<T>(buf, radix);
1217}
1218
1219
1220/** same as c4::itoa(), but pad with zeroes on the left such that the
1221 * resulting string is @p num_digits wide, not accounting for radix
1222 * prefix (0x,0o,0b). The @p radix must be 2, 8, 10 or 16.
1223 *
1224 * @note the resulting string is NOT zero-terminated.
1225 * @note it is ok to call this with an empty or too-small buffer;
1226 * no writes will occur, and the needed size will be returned
1227 * @return the number of characters required for the buffer. */
1228template<class T>
1229C4_ALWAYS_INLINE size_t itoa(substr buf, T v, T radix, size_t num_digits) noexcept
1230{
1231 C4_STATIC_ASSERT(std::is_signed<T>::value);
1232 C4_ASSERT(radix == 2 || radix == 8 || radix == 10 || radix == 16);
1233 C4_SUPPRESS_WARNING_GCC_PUSH
1234 #if (defined(__GNUC__) && (__GNUC__ >= 7))
1235 C4_SUPPRESS_WARNING_GCC("-Wstringop-overflow") // gcc has a false positive here
1236 #endif
1237 // when T is the min value (eg i8: -128), negating it
1238 // will overflow, so treat the min as a special case
1239 if C4_LIKELY(v != std::numeric_limits<T>::min())
1240 {
1241 unsigned pos = 0;
1242 if(v < 0)
1243 {
1244 v = (T)-v;
1245 if C4_LIKELY(buf.len > 0)
1246 buf.str[pos] = '-';
1247 ++pos;
1248 }
1249 unsigned total_digits = 0;
1250 switch(radix) // NOLINT(hicpp-multiway-paths-covered)
1251 {
1252 case T(10):
1253 total_digits = digits_dec(v);
1254 total_digits = pos + (unsigned)(num_digits > total_digits ? num_digits : total_digits);
1255 if C4_LIKELY(buf.len >= total_digits)
1256 write_dec(buf.sub(pos), v, num_digits);
1257 break;
1258 case T(16):
1259 total_digits = digits_hex(v);
1260 total_digits = pos + 2u + (unsigned)(num_digits > total_digits ? num_digits : total_digits);
1261 if C4_LIKELY(buf.len >= total_digits)
1262 {
1263 buf.str[pos + 0] = '0';
1264 buf.str[pos + 1] = 'x';
1265 write_hex(buf.sub(pos + 2), v, num_digits);
1266 }
1267 break;
1268 case T(2):
1269 total_digits = digits_bin(v);
1270 total_digits = pos + 2u + (unsigned)(num_digits > total_digits ? num_digits : total_digits);
1271 if C4_LIKELY(buf.len >= total_digits)
1272 {
1273 buf.str[pos + 0] = '0';
1274 buf.str[pos + 1] = 'b';
1275 write_bin(buf.sub(pos + 2), v, num_digits);
1276 }
1277 break;
1278 case T(8):
1279 total_digits = digits_oct(v);
1280 total_digits = pos + 2u + (unsigned)(num_digits > total_digits ? num_digits : total_digits);
1281 if C4_LIKELY(buf.len >= total_digits)
1282 {
1283 buf.str[pos + 0] = '0';
1284 buf.str[pos + 1] = 'o';
1285 write_oct(buf.sub(pos + 2), v, num_digits);
1286 }
1287 break;
1288 }
1289 return total_digits;
1290 }
1291 C4_SUPPRESS_WARNING_GCC_POP
1292 // when T is the min value (eg i8: -128), negating it
1293 // will overflow
1294 return detail::_itoa2buf<T>(buf, radix, num_digits);
1295}
1296
1297/** @} */
1298
1299
1300//-----------------------------------------------------------------------------
1301//-----------------------------------------------------------------------------
1302//-----------------------------------------------------------------------------
1303
1304/** @defgroup doc_utoa utoa: unsigned to chars
1305 *
1306 * @{ */
1307
1308/** convert an integral unsigned decimal to a string.
1309 *
1310 * @note the resulting string is NOT zero-terminated.
1311 * @note it is ok to call this with an empty or too-small buffer;
1312 * no writes will occur, and the needed size will be returned
1313 * @return the number of characters required for the buffer. */
1314template<class T>
1315C4_ALWAYS_INLINE size_t utoa(substr buf, T v) noexcept
1316{
1317 C4_STATIC_ASSERT(std::is_unsigned<T>::value);
1318 // write_dec() does the buffer length check, so no need to check here
1319 return write_dec(buf, v);
1320}
1321
1322/** convert an integral unsigned integer to a string, using a specific
1323 * radix. The radix must be 2, 8, 10 or 16.
1324 *
1325 * @note the resulting string is NOT zero-terminated.
1326 * @note it is ok to call this with an empty or too-small buffer;
1327 * no writes will occur, and the needed size will be returned
1328 * @return the number of characters required for the buffer. */
1329template<class T>
1330C4_ALWAYS_INLINE size_t utoa(substr buf, T v, T radix) noexcept
1331{
1332 C4_STATIC_ASSERT(std::is_unsigned<T>::value);
1333 C4_ASSERT(radix == 10 || radix == 16 || radix == 2 || radix == 8);
1334 unsigned digits = 0;
1335 switch(radix) // NOLINT(hicpp-multiway-paths-covered)
1336 {
1337 case T(10):
1338 digits = digits_dec(v);
1339 if C4_LIKELY(buf.len >= digits)
1340 write_dec_unchecked(buf, v, digits);
1341 break;
1342 case T(16):
1343 digits = digits_hex(v);
1344 if C4_LIKELY(buf.len >= digits+2u)
1345 {
1346 buf.str[0] = '0';
1347 buf.str[1] = 'x';
1348 write_hex_unchecked(buf.sub(2), v, digits);
1349 }
1350 digits += 2u;
1351 break;
1352 case T(2):
1353 digits = digits_bin(v);
1354 if C4_LIKELY(buf.len >= digits+2u)
1355 {
1356 buf.str[0] = '0';
1357 buf.str[1] = 'b';
1358 write_bin_unchecked(buf.sub(2), v, digits);
1359 }
1360 digits += 2u;
1361 break;
1362 case T(8):
1363 digits = digits_oct(v);
1364 if C4_LIKELY(buf.len >= digits+2u)
1365 {
1366 buf.str[0] = '0';
1367 buf.str[1] = 'o';
1368 write_oct_unchecked(buf.sub(2), v, digits);
1369 }
1370 digits += 2u;
1371 break;
1372 }
1373 return digits;
1374}
1375
1376/** same as c4::utoa(), but pad with zeroes on the left such that the
1377 * resulting string is @p num_digits wide. The @p radix must be 2,
1378 * 8, 10 or 16.
1379 *
1380 * @note the resulting string is NOT zero-terminated.
1381 * @note it is ok to call this with an empty or too-small buffer;
1382 * no writes will occur, and the needed size will be returned
1383 * @return the number of characters required for the buffer. */
1384template<class T>
1385C4_ALWAYS_INLINE size_t utoa(substr buf, T v, T radix, size_t num_digits) noexcept
1386{
1387 C4_STATIC_ASSERT(std::is_unsigned<T>::value);
1388 C4_ASSERT(radix == 10 || radix == 16 || radix == 2 || radix == 8);
1389 unsigned total_digits = 0;
1390 switch(radix) // NOLINT(hicpp-multiway-paths-covered)
1391 {
1392 case T(10):
1393 total_digits = digits_dec(v);
1394 total_digits = (unsigned)(num_digits > total_digits ? num_digits : total_digits);
1395 if C4_LIKELY(buf.len >= total_digits)
1396 write_dec(buf, v, num_digits);
1397 break;
1398 case T(16):
1399 total_digits = digits_hex(v);
1400 total_digits = 2u + (unsigned)(num_digits > total_digits ? num_digits : total_digits);
1401 if C4_LIKELY(buf.len >= total_digits)
1402 {
1403 buf.str[0] = '0';
1404 buf.str[1] = 'x';
1405 write_hex(buf.sub(2), v, num_digits);
1406 }
1407 break;
1408 case T(2):
1409 total_digits = digits_bin(v);
1410 total_digits = 2u + (unsigned)(num_digits > total_digits ? num_digits : total_digits);
1411 if C4_LIKELY(buf.len >= total_digits)
1412 {
1413 buf.str[0] = '0';
1414 buf.str[1] = 'b';
1415 write_bin(buf.sub(2), v, num_digits);
1416 }
1417 break;
1418 case T(8):
1419 total_digits = digits_oct(v);
1420 total_digits = 2u + (unsigned)(num_digits > total_digits ? num_digits : total_digits);
1421 if C4_LIKELY(buf.len >= total_digits)
1422 {
1423 buf.str[0] = '0';
1424 buf.str[1] = 'o';
1425 write_oct(buf.sub(2), v, num_digits);
1426 }
1427 break;
1428 }
1429 return total_digits;
1430}
1431C4_SUPPRESS_WARNING_GCC_POP
1432
1433/** @} */
1434
1435
1436//-----------------------------------------------------------------------------
1437//-----------------------------------------------------------------------------
1438//-----------------------------------------------------------------------------
1439
1440/** @defgroup doc_atoi atoi: chars to signed
1441 *
1442 * @{ */
1443
1444/** Convert a trimmed string to a signed integral value. The input
1445 * string can be formatted as decimal, binary (prefix 0b or 0B), octal
1446 * (prefix 0o or 0O) or hexadecimal (prefix 0x or 0X). Strings with
1447 * leading zeroes are considered as decimal and not octal (unlike the
1448 * C/C++ convention). Every character in the input string is read for
1449 * the conversion; the input string must not contain any leading or
1450 * trailing whitespace.
1451 *
1452 * @return true if the conversion was successful.
1453 *
1454 * @note a positive sign is not accepted. ie, the string must not
1455 * start with '+'
1456 *
1457 * @note overflow is not detected: the return status is true even if
1458 * the conversion would return a value outside of the type's range, in
1459 * which case the result will wrap around the type's range. This is
1460 * similar to native behavior. See @ref doc_overflows and @ref
1461 * doc_overflow_checked for overflow checking utilities.
1462 *
1463 * @see atoi_first() if the string is not trimmed to the value to read. */
1464template<class T>
1466C4_ALWAYS_INLINE bool atoi(csubstr str, T * C4_RESTRICT v) noexcept
1467{
1468 C4_STATIC_ASSERT(std::is_integral<T>::value);
1469 C4_STATIC_ASSERT(std::is_signed<T>::value);
1470
1471 if C4_UNLIKELY(str.len == 0)
1472 return false;
1473 // no need for the assertion, but we leave it here to document
1474 // the expectation:
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 // no need for these asserts, but we leave them here to show
1977 // the expectation:
1978 //C4_ASSERT(s.len > 0);
1979 //C4_ASSERT(s.str[0] != '-');
1980 //C4_ASSERT(s.str[0] != '+');
1981 //C4_ASSERT(!s.begins_with("0x"));
1982 //C4_ASSERT(!s.begins_with("0X"));
1983 size_t pos = 0;
1984 // integer part
1985 for( ; pos < s.len; ++pos)
1986 {
1987 const char c = s.str[pos];
1988 if(c >= '0' && c <= '9')
1989 *val = (*val * T(16)) + T(c - '0');
1990 else if(c >= 'a' && c <= 'f')
1991 *val = (*val * T(16)) + T(c - 'a');
1992 else if(c >= 'A' && c <= 'F')
1993 *val = (*val * T(16)) + T(c - 'A');
1994 else if(c == '.')
1995 {
1996 ++pos;
1997 break; // follow on to mantissa
1998 }
1999 else if(c == 'p' || c == 'P')
2000 {
2001 ++pos;
2002 goto power; // no mantissa given, jump to power // NOLINT
2003 }
2004 else
2005 {
2006 return false;
2007 }
2008 }
2009 // mantissa
2010 {
2011 // 0.0625 == 1/16 == value of first digit after the comma
2012 for(T digit = T(0.0625); pos < s.len; ++pos, digit /= T(16)) // NOLINT
2013 {
2014 const char c = s.str[pos];
2015 if(c >= '0' && c <= '9')
2016 *val += digit * T(c - '0');
2017 else if(c >= 'a' && c <= 'f')
2018 *val += digit * T(c - 'a');
2019 else if(c >= 'A' && c <= 'F')
2020 *val += digit * T(c - 'A');
2021 else if(c == 'p' || c == 'P')
2022 {
2023 ++pos;
2024 goto power; // mantissa finished, jump to power // NOLINT
2025 }
2026 else
2027 {
2028 return false;
2029 }
2030 }
2031 }
2032 return true;
2033power:
2034 if C4_LIKELY(pos < s.len)
2035 {
2036 if(s.str[pos] == '+') // atoi() cannot handle a leading '+'
2037 ++pos;
2038 if C4_LIKELY(pos < s.len)
2039 {
2040 int16_t powval = {};
2041 if C4_LIKELY(atoi(s.sub(pos), &powval))
2042 {
2043 *val *= ipow<T, int16_t, 16>(powval);
2044 return true;
2045 }
2046 }
2047 }
2048 return false;
2049}
2050#endif
2051
2052} // namespace detail
2053/** @endcond */
2054
2055
2056#undef _c4append
2057
2058
2059/** @defgroup doc_ftoa ftoa: float32 to chars
2060 *
2061 * @{ */
2062
2063/** Convert a single-precision real number to string. The string will
2064 * in general be NOT null-terminated. For FTOA_FLEX, \p precision is
2065 * the number of significand digits. Otherwise \p precision is the
2066 * number of decimals. It is safe to call this function with an empty
2067 * or too-small buffer.
2068 *
2069 * @return the size of the buffer needed to write the number
2070 */
2071C4_ALWAYS_INLINE size_t ftoa(substr str, float v, int precision=-1, RealFormat_e formatting=FTOA_FLEX) noexcept
2072{
2073#if C4CORE_HAVE_STD_TOCHARS
2074 return detail::rtoa(str, v, precision, formatting);
2075#else
2076 char fmt[16];
2077 detail::get_real_format_str(fmt, precision, formatting, /*length_modifier*/"");
2078 return detail::print_one(str, fmt, v);
2079#endif
2080}
2081
2082/** @} */
2083
2084
2085/** @defgroup doc_dtoa dtoa: float64 to chars
2086 *
2087 * @{ */
2088
2089/** Convert a double-precision real number to string. The string will
2090 * in general be NOT null-terminated. For FTOA_FLEX, \p precision is
2091 * the number of significand digits. Otherwise \p precision is the
2092 * number of decimals. It is safe to call this function with an empty
2093 * or too-small buffer.
2094 *
2095 * @return the size of the buffer needed to write the number
2096 */
2097C4_ALWAYS_INLINE size_t dtoa(substr str, double v, int precision=-1, RealFormat_e formatting=FTOA_FLEX) noexcept
2098{
2099#if C4CORE_HAVE_STD_TOCHARS
2100 return detail::rtoa(str, v, precision, formatting);
2101#else
2102 char fmt[16];
2103 detail::get_real_format_str(fmt, precision, formatting, /*length_modifier*/"l");
2104 return detail::print_one(str, fmt, v);
2105#endif
2106}
2107
2108/** @} */
2109
2110
2111/** @defgroup doc_atof atof: chars to float32
2112 *
2113 * @{ */
2114
2115/** Convert a string to a single precision real number.
2116 * The input string must be trimmed to the value, ie
2117 * no leading or trailing whitespace can be present.
2118 * @return true iff the conversion succeeded
2119 * @see atof_first() if the string is not trimmed
2120 */
2121C4_ALWAYS_INLINE bool atof(csubstr str, float * C4_RESTRICT v) noexcept
2122{
2123 C4_ASSERT(str.len > 0);
2124 C4_ASSERT(str.triml(" \r\t\n").len == str.len);
2125#if C4CORE_HAVE_FAST_FLOAT
2126 // fastfloat cannot parse hexadecimal floats
2127 bool isneg = (str.str[0] == '-');
2128 csubstr rem = str.sub(isneg || str.str[0] == '+');
2129 if( ! (rem.len >= 2 && (rem.str[0] == '0' && (rem.str[1] == 'x' || rem.str[1] == 'X'))))
2130 {
2131 fast_float::from_chars_result result;
2132 result = fast_float::from_chars(str.str, str.str + str.len, *v);
2133 return result.ec == std::errc();
2134 }
2135 else if(detail::scan_rhex(rem.sub(2), v))
2136 {
2137 *v *= isneg ? -1.f : 1.f;
2138 return true;
2139 }
2140 return false;
2141#elif C4CORE_HAVE_STD_FROMCHARS
2142 std::from_chars_result result;
2143 result = std::from_chars(str.str, str.str + str.len, *v);
2144 return result.ec == std::errc();
2145#else
2146 csubstr rem = str.sub(str.str[0] == '-' || str.str[0] == '+');
2147 if(!(rem.len >= 2 && (rem.str[0] == '0' && (rem.str[1] == 'x' || rem.str[1] == 'X'))))
2148 return detail::scan_one(str, "f", v) != csubstr::npos;
2149 else
2150 return detail::scan_one(str, "a", v) != csubstr::npos;
2151#endif
2152}
2153
2154
2155/** Convert a string to a single precision real number.
2156 * Leading whitespace is skipped until valid characters are found.
2157 * @return the number of characters read from the string, or npos if
2158 * conversion was not successful or if the string was empty */
2159inline size_t atof_first(csubstr str, float * C4_RESTRICT v) noexcept
2160{
2161 csubstr trimmed = str.first_real_span();
2162 if(trimmed.len == 0)
2163 return csubstr::npos;
2164 if(atof(trimmed, v))
2165 return static_cast<size_t>(trimmed.end() - str.begin());
2166 return csubstr::npos;
2167}
2168
2169/** @} */
2170
2171
2172/** @defgroup doc_atod atod: chars to float64
2173 *
2174 * @{ */
2175
2176/** Convert a string to a double precision real number.
2177 * The input string must be trimmed to the value, ie
2178 * no leading or trailing whitespace can be present.
2179 * @return true iff the conversion succeeded
2180 * @see atod_first() if the string is not trimmed
2181 */
2182C4_ALWAYS_INLINE bool atod(csubstr str, double * C4_RESTRICT v) noexcept
2183{
2184 C4_ASSERT(str.len > 0);
2185 C4_ASSERT(str.triml(" \r\t\n").len == str.len);
2186#if C4CORE_HAVE_FAST_FLOAT
2187 // fastfloat cannot parse hexadecimal floats
2188 bool isneg = (str.str[0] == '-');
2189 csubstr rem = str.sub(isneg || str.str[0] == '+');
2190 if( ! (rem.len >= 2 && (rem.str[0] == '0' && (rem.str[1] == 'x' || rem.str[1] == 'X'))))
2191 {
2192 fast_float::from_chars_result result;
2193 #ifndef CLANG_TIDY // suppress a false-positive error (cannot be done with NOLINT: https://stackoverflow.com/questions/62838193/ )
2194 result = fast_float::from_chars(str.str, str.str + str.len, *v);
2195 #else
2196 result = {};
2197 #endif
2198 return result.ec == std::errc();
2199 }
2200 else if(detail::scan_rhex(rem.sub(2), v))
2201 {
2202 *v *= isneg ? -1. : 1.;
2203 return true;
2204 }
2205 return false;
2206#elif C4CORE_HAVE_STD_FROMCHARS
2207 std::from_chars_result result;
2208 result = std::from_chars(str.str, str.str + str.len, *v);
2209 return result.ec == std::errc();
2210#else
2211 csubstr rem = str.sub(str.str[0] == '-' || str.str[0] == '+');
2212 if(!(rem.len >= 2 && (rem.str[0] == '0' && (rem.str[1] == 'x' || rem.str[1] == 'X'))))
2213 return detail::scan_one(str, "lf", v) != csubstr::npos;
2214 else
2215 return detail::scan_one(str, "la", v) != csubstr::npos;
2216#endif
2217}
2218
2219
2220/** Convert a string to a double precision real number.
2221 * Leading whitespace is skipped until valid characters are found.
2222 * @return the number of characters read from the string, or npos if
2223 * conversion was not successful or if the string was empty */
2224inline size_t atod_first(csubstr str, double * C4_RESTRICT v) noexcept
2225{
2226 csubstr trimmed = str.first_real_span();
2227 if(trimmed.len == 0)
2228 return csubstr::npos;
2229 if(atod(trimmed, v))
2230 return static_cast<size_t>(trimmed.end() - str.begin());
2231 return csubstr::npos;
2232}
2233
2234/** @} */
2235
2236
2237//-----------------------------------------------------------------------------
2238//-----------------------------------------------------------------------------
2239//-----------------------------------------------------------------------------
2240// generic versions
2241
2242/** @cond dev */
2243// on some platforms, (unsigned) int and (unsigned) long
2244// are not any of the fixed length types above
2245#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>
2246#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>
2247/** @endcond*/
2248
2249
2250/** @defgroup doc_xtoa xtoa: generic value to chars
2251 *
2252 * Dispatches to the most appropriate and efficient conversion
2253 * function
2254 *
2255 * @{ */
2256C4_ALWAYS_INLINE size_t xtoa(substr s, uint8_t v) noexcept { return write_dec(s, v); }
2257C4_ALWAYS_INLINE size_t xtoa(substr s, uint16_t v) noexcept { return write_dec(s, v); }
2258C4_ALWAYS_INLINE size_t xtoa(substr s, uint32_t v) noexcept { return write_dec(s, v); }
2259C4_ALWAYS_INLINE size_t xtoa(substr s, uint64_t v) noexcept { return write_dec(s, v); }
2260C4_ALWAYS_INLINE size_t xtoa(substr s, int8_t v) noexcept { return itoa(s, v); }
2261C4_ALWAYS_INLINE size_t xtoa(substr s, int16_t v) noexcept { return itoa(s, v); }
2262C4_ALWAYS_INLINE size_t xtoa(substr s, int32_t v) noexcept { return itoa(s, v); }
2263C4_ALWAYS_INLINE size_t xtoa(substr s, int64_t v) noexcept { return itoa(s, v); }
2264C4_ALWAYS_INLINE size_t xtoa(substr s, float v) noexcept { return ftoa(s, v); }
2265C4_ALWAYS_INLINE size_t xtoa(substr s, double v) noexcept { return dtoa(s, v); }
2266
2267C4_ALWAYS_INLINE size_t xtoa(substr s, uint8_t v, uint8_t radix) noexcept { return utoa(s, v, radix); }
2268C4_ALWAYS_INLINE size_t xtoa(substr s, uint16_t v, uint16_t radix) noexcept { return utoa(s, v, radix); }
2269C4_ALWAYS_INLINE size_t xtoa(substr s, uint32_t v, uint32_t radix) noexcept { return utoa(s, v, radix); }
2270C4_ALWAYS_INLINE size_t xtoa(substr s, uint64_t v, uint64_t radix) noexcept { return utoa(s, v, radix); }
2271C4_ALWAYS_INLINE size_t xtoa(substr s, int8_t v, int8_t radix) noexcept { return itoa(s, v, radix); }
2272C4_ALWAYS_INLINE size_t xtoa(substr s, int16_t v, int16_t radix) noexcept { return itoa(s, v, radix); }
2273C4_ALWAYS_INLINE size_t xtoa(substr s, int32_t v, int32_t radix) noexcept { return itoa(s, v, radix); }
2274C4_ALWAYS_INLINE size_t xtoa(substr s, int64_t v, int64_t radix) noexcept { return itoa(s, v, radix); }
2275
2276C4_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); }
2277C4_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); }
2278C4_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); }
2279C4_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); }
2280C4_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); }
2281C4_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); }
2282C4_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); }
2283C4_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); }
2284
2285C4_ALWAYS_INLINE size_t xtoa(substr s, float v, int precision, RealFormat_e formatting=FTOA_FLEX) noexcept { return ftoa(s, v, precision, formatting); }
2286C4_ALWAYS_INLINE size_t xtoa(substr s, double v, int precision, RealFormat_e formatting=FTOA_FLEX) noexcept { return dtoa(s, v, precision, formatting); }
2287
2288template <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); }
2289template <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); }
2290template <class T>
2291C4_ALWAYS_INLINE size_t xtoa(substr s, T *v) noexcept { return itoa(s, (intptr_t)v, (intptr_t)16); }
2292
2293/** @} */
2294
2295/** @defgroup doc_atox atox: generic chars to value
2296 *
2297 * Dispatches to the most appropriate and efficient conversion
2298 * function
2299 *
2300 * @{ */
2301
2302C4_ALWAYS_INLINE bool atox(csubstr s, uint8_t *C4_RESTRICT v) noexcept { return atou(s, v); }
2303C4_ALWAYS_INLINE bool atox(csubstr s, uint16_t *C4_RESTRICT v) noexcept { return atou(s, v); }
2304C4_ALWAYS_INLINE bool atox(csubstr s, uint32_t *C4_RESTRICT v) noexcept { return atou(s, v); }
2305C4_ALWAYS_INLINE bool atox(csubstr s, uint64_t *C4_RESTRICT v) noexcept { return atou(s, v); }
2306C4_ALWAYS_INLINE bool atox(csubstr s, int8_t *C4_RESTRICT v) noexcept { return atoi(s, v); }
2307C4_ALWAYS_INLINE bool atox(csubstr s, int16_t *C4_RESTRICT v) noexcept { return atoi(s, v); }
2308C4_ALWAYS_INLINE bool atox(csubstr s, int32_t *C4_RESTRICT v) noexcept { return atoi(s, v); }
2309C4_ALWAYS_INLINE bool atox(csubstr s, int64_t *C4_RESTRICT v) noexcept { return atoi(s, v); }
2310C4_ALWAYS_INLINE bool atox(csubstr s, float *C4_RESTRICT v) noexcept { return atof(s, v); }
2311C4_ALWAYS_INLINE bool atox(csubstr s, double *C4_RESTRICT v) noexcept { return atod(s, v); }
2312
2313template <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); }
2314template <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); }
2315template <class T>
2316C4_ALWAYS_INLINE bool atox(csubstr s, T **v) noexcept { intptr_t tmp; bool ret = atox(s, &tmp); if(ret) { *v = (T*)tmp; } return ret; }
2317
2318/** @} */
2319
2320
2321/** @defgroup doc_to_chars to_chars: generalized chars to value
2322 *
2323 * Convert the given value, writing into the string. The resulting
2324 * string will NOT be null-terminated. Return the number of
2325 * characters needed. This function is safe to call when the string
2326 * is too small - no writes will occur beyond the string's last
2327 * character.
2328 *
2329 * Dispatches to the most appropriate and efficient conversion
2330 * function.
2331 *
2332 * @see write_dec, doc_utoa, doc_itoa, doc_ftoa, doc_dtoa
2333 *
2334 * @warning When serializing floating point values (float or double),
2335 * be aware that because it uses defaults, to_chars() may cause a
2336 * truncation of the precision. To enforce a particular precision, use
2337 * for example @ref c4::fmt::real, or call directly @ref c4::ftoa or
2338 * @ref c4::dtoa.
2339 *
2340 * @{ */
2341
2342C4_ALWAYS_INLINE size_t to_chars(substr buf, uint8_t v) noexcept { return write_dec(buf, v); }
2343C4_ALWAYS_INLINE size_t to_chars(substr buf, uint16_t v) noexcept { return write_dec(buf, v); }
2344C4_ALWAYS_INLINE size_t to_chars(substr buf, uint32_t v) noexcept { return write_dec(buf, v); }
2345C4_ALWAYS_INLINE size_t to_chars(substr buf, uint64_t v) noexcept { return write_dec(buf, v); }
2346C4_ALWAYS_INLINE size_t to_chars(substr buf, int8_t v) noexcept { return itoa(buf, v); }
2347C4_ALWAYS_INLINE size_t to_chars(substr buf, int16_t v) noexcept { return itoa(buf, v); }
2348C4_ALWAYS_INLINE size_t to_chars(substr buf, int32_t v) noexcept { return itoa(buf, v); }
2349C4_ALWAYS_INLINE size_t to_chars(substr buf, int64_t v) noexcept { return itoa(buf, v); }
2350C4_ALWAYS_INLINE size_t to_chars(substr buf, float v) noexcept { return ftoa(buf, v); }
2351C4_ALWAYS_INLINE size_t to_chars(substr buf, double v) noexcept { return dtoa(buf, v); }
2352
2353template <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); }
2354template <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); }
2355template <class T>
2356C4_ALWAYS_INLINE auto to_chars(substr s, T *v) noexcept
2357 -> typename std::enable_if<!std::is_same<T, char>::value &&
2358 !std::is_same<T, const char>::value,
2359 size_t>::type
2360{
2361 return itoa(s, (intptr_t)v, (intptr_t)16);
2362}
2363
2364/** @} */
2365
2366
2367/** @defgroup doc_from_chars from_chars: generalized chars to value
2368 *
2369 * Read a value from the string, which must be trimmed to the value
2370 * (ie, no leading/trailing whitespace). return true if the
2371 * conversion succeeded. There is no check for overflow; the value
2372 * wraps around in a way similar to the standard C/C++ overflow
2373 * behavior. For example, from_chars<int8_t>("128", &val) returns true
2374 * and val will be set tot 0. See @ref doc_overflows and @ref
2375 * doc_overflow_checked for facilities enforcing no-overflow.
2376 *
2377 * Dispatches to the most appropriate and efficient conversion
2378 * function
2379 *
2380 * @see doc_from_chars_first, atou, atoi, atof, atod
2381 * @{ */
2382
2383C4_ALWAYS_INLINE bool from_chars(csubstr buf, uint8_t *C4_RESTRICT v) noexcept { return atou(buf, v); }
2384C4_ALWAYS_INLINE bool from_chars(csubstr buf, uint16_t *C4_RESTRICT v) noexcept { return atou(buf, v); }
2385C4_ALWAYS_INLINE bool from_chars(csubstr buf, uint32_t *C4_RESTRICT v) noexcept { return atou(buf, v); }
2386C4_ALWAYS_INLINE bool from_chars(csubstr buf, uint64_t *C4_RESTRICT v) noexcept { return atou(buf, v); }
2387C4_ALWAYS_INLINE bool from_chars(csubstr buf, int8_t *C4_RESTRICT v) noexcept { return atoi(buf, v); }
2388C4_ALWAYS_INLINE bool from_chars(csubstr buf, int16_t *C4_RESTRICT v) noexcept { return atoi(buf, v); }
2389C4_ALWAYS_INLINE bool from_chars(csubstr buf, int32_t *C4_RESTRICT v) noexcept { return atoi(buf, v); }
2390C4_ALWAYS_INLINE bool from_chars(csubstr buf, int64_t *C4_RESTRICT v) noexcept { return atoi(buf, v); }
2391C4_ALWAYS_INLINE bool from_chars(csubstr buf, float *C4_RESTRICT v) noexcept { return atof(buf, v); }
2392C4_ALWAYS_INLINE bool from_chars(csubstr buf, double *C4_RESTRICT v) noexcept { return atod(buf, v); }
2393
2394template <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); }
2395template <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); }
2396template <class T>
2397C4_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; }
2398
2399/** @defgroup doc_from_chars_first from_chars_first: generalized chars to value
2400 *
2401 * Read the first valid sequence of characters from the string,
2402 * skipping leading whitespace, and convert it using @ref doc_from_chars .
2403 * Return the number of characters read for converting.
2404 *
2405 * Dispatches to the most appropriate and efficient conversion
2406 * function.
2407 *
2408 * @see atou_first, atoi_first, atof_first, atod_first
2409 * @{ */
2410
2411C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, uint8_t *C4_RESTRICT v) noexcept { return atou_first(buf, v); }
2412C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, uint16_t *C4_RESTRICT v) noexcept { return atou_first(buf, v); }
2413C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, uint32_t *C4_RESTRICT v) noexcept { return atou_first(buf, v); }
2414C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, uint64_t *C4_RESTRICT v) noexcept { return atou_first(buf, v); }
2415C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, int8_t *C4_RESTRICT v) noexcept { return atoi_first(buf, v); }
2416C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, int16_t *C4_RESTRICT v) noexcept { return atoi_first(buf, v); }
2417C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, int32_t *C4_RESTRICT v) noexcept { return atoi_first(buf, v); }
2418C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, int64_t *C4_RESTRICT v) noexcept { return atoi_first(buf, v); }
2419C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, float *C4_RESTRICT v) noexcept { return atof_first(buf, v); }
2420C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, double *C4_RESTRICT v) noexcept { return atod_first(buf, v); }
2421
2422template <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); }
2423template <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); }
2424template <class T>
2425C4_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; }
2426
2427/** @} */
2428
2429/** @} */
2430
2431#undef C4_IF_NOT_FIXED_LENGTH_I_
2432#undef C4_IF_NOT_FIXED_LENGTH_U_
2433
2434
2435//-----------------------------------------------------------------------------
2436//-----------------------------------------------------------------------------
2437//-----------------------------------------------------------------------------
2438/** call to_chars() and return a substr consisting of the
2439 * written portion of the input buffer. Ie, same as to_chars(),
2440 * but return a substr instead of a size_t.
2441 * Convert the given value to a string using to_chars(), and
2442 * return the resulting string, up to and including the last
2443 * written character.
2444 * @ingroup doc_to_chars
2445 * @see to_chars() */
2446template<class T>
2447C4_ALWAYS_INLINE substr to_chars_sub(substr buf, T const& C4_RESTRICT v) noexcept
2448{
2449 size_t sz = to_chars(buf, v);
2450 return buf.left_of(sz <= buf.len ? sz : buf.len);
2451}
2452
2453//-----------------------------------------------------------------------------
2454//-----------------------------------------------------------------------------
2455//-----------------------------------------------------------------------------
2456// bool implementation
2457
2458/** @ingroup doc_to_chars */
2459C4_ALWAYS_INLINE size_t to_chars(substr buf, bool v) noexcept
2460{
2461 int val = v;
2462 return to_chars(buf, val);
2463}
2464
2465/** @ingroup doc_from_chars */
2466inline bool from_chars(csubstr buf, bool * C4_RESTRICT v) noexcept
2467{
2468 if(buf.len == 1)
2469 {
2470 if(buf.str[0] == '0')
2471 {
2472 *v = false; return true;
2473 }
2474 else if(buf.str[0] == '1')
2475 {
2476 *v = true; return true;
2477 }
2478 }
2479 else if(buf.len == 4)
2480 {
2481 if(((buf.str[0] == 't') && (0 == memcmp(buf.str + 1, "rue", 3)))
2482 ||
2483 ((buf.str[0] == 'T') && (0 == memcmp(buf.str + 1, "rue", 3) ||
2484 0 == memcmp(buf.str + 1, "RUE", 3))))
2485 {
2486 *v = true; return true;
2487 }
2488 }
2489 else if(buf.len == 5)
2490 {
2491 if(((buf.str[0] == 'f') && (0 == memcmp(buf.str + 1, "alse", 4)))
2492 ||
2493 ((buf.str[0] == 'F') && (0 == memcmp(buf.str + 1, "alse", 4) ||
2494 0 == memcmp(buf.str + 1, "ALSE", 4))))
2495 {
2496 *v = false; return true;
2497 }
2498 }
2499 // fallback to c-style int bools
2500 int val = 0;
2501 bool ret = from_chars(buf, &val);
2502 if C4_LIKELY(ret)
2503 {
2504 *v = (val != 0);
2505 }
2506 return ret;
2507}
2508
2509/** @ingroup doc_from_chars_first */
2510inline size_t from_chars_first(csubstr buf, bool * C4_RESTRICT v) noexcept
2511{
2512 csubstr trimmed = buf.first_non_empty_span();
2513 if(trimmed.len == 0 || !from_chars(buf, v))
2514 return csubstr::npos;
2515 return trimmed.len;
2516}
2517
2518
2519//-----------------------------------------------------------------------------
2520// single-char implementation
2521
2522/** @ingroup doc_to_chars */
2523inline size_t to_chars(substr buf, char v) noexcept
2524{
2525 if(buf.len > 0)
2526 {
2527 C4_XASSERT(buf.str);
2528 buf.str[0] = v;
2529 }
2530 return 1;
2531}
2532
2533/** extract a single character from a substring
2534 * @note to extract a string instead and not just a single character, use the csubstr overload
2535 * @ingroup doc_from_chars
2536 * */
2537inline bool from_chars(csubstr buf, char * C4_RESTRICT v) noexcept
2538{
2539 if(buf.len != 1)
2540 return false;
2541 C4_XASSERT(buf.str);
2542 *v = buf.str[0];
2543 return true;
2544}
2545
2546/** @ingroup doc_from_chars_first */
2547inline size_t from_chars_first(csubstr buf, char * C4_RESTRICT v) noexcept
2548{
2549 if(buf.len < 1)
2550 return csubstr::npos;
2551 *v = buf.str[0];
2552 return 1;
2553}
2554
2555
2556//-----------------------------------------------------------------------------
2557// csubstr implementation
2558
2559/** @ingroup doc_to_chars */
2560inline size_t to_chars(substr buf, csubstr v) noexcept
2561{
2562 C4_ASSERT(!buf.overlaps(v));
2563 size_t len = buf.len < v.len ? buf.len : v.len;
2564 // calling memcpy with null strings is undefined behavior
2565 // and will wreak havoc in calling code's branches.
2566 // see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637
2567 if(len)
2568 {
2569 C4_ASSERT(buf.str != nullptr);
2570 C4_ASSERT(v.str != nullptr);
2571 memcpy(buf.str, v.str, len);
2572 }
2573 return v.len;
2574}
2575
2576/** @ingroup doc_from_chars */
2577inline bool from_chars(csubstr buf, csubstr *C4_RESTRICT v) noexcept
2578{
2579 *v = buf;
2580 return true;
2581}
2582
2583/** @ingroup doc_from_chars_first */
2584inline size_t from_chars_first(csubstr buf, csubstr * C4_RESTRICT v) noexcept
2585{
2586 csubstr trimmed = buf.first_non_empty_span();
2587 if(trimmed.len == 0)
2588 return csubstr::npos;
2589 *v = trimmed;
2590 return static_cast<size_t>(trimmed.end() - buf.begin());
2591}
2592
2593
2594//-----------------------------------------------------------------------------
2595// substr
2596
2597/** @ingroup doc_to_chars */
2598inline size_t to_chars(substr buf, substr v) noexcept
2599{
2600 C4_ASSERT(!buf.overlaps(v));
2601 size_t len = buf.len < v.len ? buf.len : v.len;
2602 // calling memcpy zero len is undefined behavior
2603 // and will wreak havoc in calling code's branches.
2604 // see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637
2605 if(len)
2606 {
2607 C4_ASSERT(buf.str != nullptr);
2608 C4_ASSERT(v.str != nullptr);
2609 memcpy(buf.str, v.str, len);
2610 }
2611 return v.len;
2612}
2613
2614/** @ingroup doc_from_chars */
2615inline bool from_chars(csubstr buf, substr * C4_RESTRICT v) noexcept
2616{
2617 C4_ASSERT(!buf.overlaps(*v));
2618 // is the destination buffer wide enough?
2619 if(v->len >= buf.len)
2620 {
2621 // calling memcpy with zero len is undefined behavior
2622 // and will wreak havoc in calling code's branches.
2623 // see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637
2624 if(buf.len)
2625 {
2626 C4_ASSERT(buf.str != nullptr);
2627 C4_ASSERT(v->str != nullptr);
2628 memcpy(v->str, buf.str, buf.len);
2629 }
2630 v->len = buf.len;
2631 return true;
2632 }
2633 return false;
2634}
2635
2636/** @ingroup doc_from_chars_first */
2637inline size_t from_chars_first(csubstr buf, substr * C4_RESTRICT v) noexcept
2638{
2639 csubstr trimmed = buf.first_non_empty_span();
2640 C4_ASSERT(!trimmed.overlaps(*v));
2641 if C4_UNLIKELY(trimmed.len == 0)
2642 return csubstr::npos;
2643 size_t len = trimmed.len > v->len ? v->len : trimmed.len;
2644 // calling memcpy with zero len is undefined behavior
2645 // and will wreak havoc in calling code's branches.
2646 // see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637
2647 if(len)
2648 {
2649 C4_ASSERT(buf.str != nullptr);
2650 C4_ASSERT(v->str != nullptr);
2651 memcpy(v->str, trimmed.str, len);
2652 }
2653 if C4_UNLIKELY(trimmed.len > v->len)
2654 return csubstr::npos;
2655 return static_cast<size_t>(trimmed.end() - buf.begin());
2656}
2657
2658
2659//-----------------------------------------------------------------------------
2660
2661/** @ingroup doc_to_chars
2662 * (1) overload for `char(&)[N]` and `const char(&)[N]` */
2663template<size_t N>
2664size_t to_chars(substr buf, const char (& C4_RESTRICT v)[N]) noexcept
2665{
2666 return to_chars(buf, csubstr{v, N-1});
2667}
2668
2669/** @ingroup doc_to_chars
2670 * (2) overload for `char*` and `const char*`. Must be zero-terminated!
2671 * @warning will call strlen()
2672 * @note this overload uses SFINAE to prevent it from overriding the array overload
2673 * @see For a more detailed explanation on why the plain pointer overloads cannot
2674 * coexist with the array overloads, see http://cplusplus.bordoon.com/specializeForCharacterArrays.html */
2675template<class CharPtr>
2676auto to_chars(substr buf, CharPtr v) noexcept
2677 -> typename std::enable_if<std::is_same<CharPtr, char*>::value
2678 ||
2679 std::is_same<CharPtr, const char*>::value, size_t>::type
2680{
2681 return v ? to_chars(buf, csubstr{v, strlen(v)}) : 0;
2682}
2683
2684
2685//-----------------------------------------------------------------------------
2686// nullptr implementation
2687
2688/** @ingroup doc_to_chars */
2689C4_ALWAYS_INLINE size_t to_chars(substr, std::nullptr_t) noexcept
2690{
2691 return 0;
2692}
2693
2694// from_chars() and from_chars_sub() must not exist for nullptr
2695
2696
2697/** @} */
2698
2699} // namespace c4
2700
2701// NOLINTEND(hicpp-signed-bitwise)
2702
2703#if defined(_MSC_VER) && !defined(__clang__)
2704# pragma warning(pop)
2705#elif defined(__clang__)
2706# pragma clang diagnostic pop
2707#elif defined(__GNUC__)
2708# pragma GCC diagnostic pop
2709#endif
2710
2711#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:432
unsigned digits_oct(T v_) noexcept
return the number of digits required to encode an octal number.
Definition charconv.hpp:547
unsigned digits_hex(T v) noexcept
return the number of digits required to encode an hexadecimal number.
Definition charconv.hpp:529
unsigned digits_bin(T v) noexcept
return the number of digits required to encode a binary number.
Definition charconv.hpp:538
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:937
bool read_oct(csubstr s, I *v) noexcept
read an octal integer from a string.
Definition charconv.hpp:967
bool read_hex(csubstr s, I *v) noexcept
read an hexadecimal integer from a string.
Definition charconv.hpp:902
bool read_dec(csubstr s, I *v) noexcept
read a decimal integer from a string.
Definition charconv.hpp:874
size_t to_chars(ryml::substr buf, vec2< T > v)
bool from_chars(ryml::csubstr buf, vec2< T > *v)
basic_substring< char > substr
a mutable string view
Definition substr.hpp:2355
basic_substring< const char > csubstr
an immutable string view
Definition substr.hpp:2356
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:679
void write_dec_unchecked(substr buf, T v, unsigned digits_v) noexcept
Definition charconv.hpp:614
void write_hex_unchecked(substr buf, T v, unsigned digits_v) noexcept
Definition charconv.hpp:647
void write_oct_unchecked(substr buf, T v, unsigned digits_v) noexcept
Definition charconv.hpp:663
size_t write_hex(substr buf, T v) noexcept
write an integer to a string in hexadecimal format.
Definition charconv.hpp:733
size_t write_dec(substr buf, T v) noexcept
write an integer to a string in decimal format.
Definition charconv.hpp:714
size_t write_bin(substr buf, T v) noexcept
write an integer to a string in binary format.
Definition charconv.hpp:771
size_t write_oct(substr buf, T v) noexcept
write an integer to a string in octal format.
Definition charconv.hpp:752
size_t xtoa(substr s, uint8_t v) noexcept
basic_substring first_uint_span() const
get the first span which can be interpreted as an unsigned integer
Definition substr.hpp:1277
basic_substring first_int_span() const
get the first span which can be interpreted as a signed integer
Definition substr.hpp:1289
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:493
iterator end() noexcept
Definition substr.hpp:361
basic_substring sub(size_t first) const noexcept
return [first,len[
Definition substr.hpp:502
C * str
a restricted pointer to the first character of the substring
Definition substr.hpp:216
read+write string views