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