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