rapidyaml  0.13.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 such that
796  * the resulting string is @p num_digits wide. If the given number
797  * 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 such that
805  * the resulting string is @p num_digits wide. If the given number
806  * 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 such that
814  * the resulting string is @p num_digits wide. If the given number
815  * 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 such that
823  * the resulting string is @p num_digits wide. If the given number
824  * 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 && atoi(trimmed, v))
1511  return static_cast<size_t>(trimmed.end() - str.begin());
1512  return csubstr::npos;
1513 }
1514 
1515 /** @} */
1516 
1517 
1518 //-----------------------------------------------------------------------------
1519 //-----------------------------------------------------------------------------
1520 //-----------------------------------------------------------------------------
1521 
1522 /** @defgroup doc_atou atou: chars to unsigned
1523  *
1524  * @{ */
1525 
1526 /** Convert a trimmed string to an unsigned integral value. The string can be
1527  * formatted as decimal, binary (prefix 0b or 0B), octal (prefix 0o or 0O)
1528  * or hexadecimal (prefix 0x or 0X). Every character in the input string is read
1529  * for the conversion; it must not contain any leading or trailing whitespace.
1530  *
1531  * @return true if the conversion was successful.
1532  *
1533  * @note overflow is not detected: the return status is true even if
1534  * the conversion would return a value outside of the type's range, in
1535  * which case the result will wrap around the type's range. See @ref
1536  * doc_overflows and @ref doc_overflow_checked for overflow checking
1537  * utilities.
1538  *
1539  * @note If the string has a minus character, the return status
1540  * will be false.
1541  *
1542  * @see atou_first() if the string is not trimmed to the value to read. */
1543 template<class T>
1544 bool atou(csubstr str, T * C4_RESTRICT v) noexcept
1545 {
1546  C4_STATIC_ASSERT(std::is_integral<T>::value);
1547 
1548  if(C4_UNLIKELY(str.len == 0 || str.front() == '-'))
1549  return false;
1550 
1551  bool parsed_ok = true;
1552  if(str.str[0] != '0')
1553  {
1554  parsed_ok = read_dec(str, v);
1555  }
1556  else
1557  {
1558  if(str.len > 1)
1559  {
1560  const char pfx = str.str[1];
1561  if(pfx == 'x' || pfx == 'X')
1562  parsed_ok = str.len > 2 && read_hex(str.sub(2), v);
1563  else if(pfx == 'b' || pfx == 'B')
1564  parsed_ok = str.len > 2 && read_bin(str.sub(2), v);
1565  else if(pfx == 'o' || pfx == 'O')
1566  parsed_ok = str.len > 2 && read_oct(str.sub(2), v);
1567  else
1568  parsed_ok = read_dec(str, v);
1569  }
1570  else
1571  {
1572  *v = 0; // we know the first character is 0
1573  }
1574  }
1575  return parsed_ok;
1576 }
1577 
1578 
1579 /** Select the next range of characters in the string that can be parsed
1580  * as an unsigned integral value, and convert it using atou(). Leading
1581  * whitespace (space, newline, tabs) is skipped.
1582  * @return the number of characters read for conversion, or csubstr::npos if the conversion faileds
1583  * @see atou() if the string is already trimmed to the value to read.
1584  * @see csubstr::first_uint_span() */
1585 template<class T>
1586 C4_ALWAYS_INLINE size_t atou_first(csubstr str, T *v)
1587 {
1588  csubstr trimmed = str.first_uint_span();
1589  if(trimmed.len && atou(trimmed, v))
1590  return static_cast<size_t>(trimmed.end() - str.begin());
1591  return csubstr::npos;
1592 }
1593 
1594 
1595 /** @} */
1596 
1597 #if defined(_MSC_VER) && !defined(__clang__)
1598 # pragma warning(pop)
1599 #elif defined(__clang__)
1600 # pragma clang diagnostic pop
1601 #elif defined(__GNUC__)
1602 # pragma GCC diagnostic pop
1603 #endif
1604 
1605 
1606 //-----------------------------------------------------------------------------
1607 //-----------------------------------------------------------------------------
1608 //-----------------------------------------------------------------------------
1609 
1610 /** @cond dev */
1611 namespace detail {
1612 inline bool check_overflow(csubstr str, csubstr limit) noexcept
1613 {
1614  if(str.len != limit.len)
1615  return str.len > limit.len;
1616  for(size_t i = 0; i < limit.len; ++i)
1617  {
1618  if(str[i] < limit[i])
1619  return false;
1620  else if(str[i] > limit[i])
1621  return true;
1622  }
1623  return false;
1624 }
1625 } // namespace detail
1626 /** @endcond */
1627 
1628 
1629 /** @defgroup doc_overflows overflows: does a number string overflow a type
1630  *
1631  * @{ */
1632 
1633 /** Test if the following string would overflow when converted to
1634  * associated integral types; this function is dispatched with SFINAE
1635  * to handle differently signed and unsigned types.
1636  * @return true if number will overflow, false if it fits (or doesn't parse)
1637  * @see doc_overflow_checked for format specifiers to enforce no-overflow reads
1638  */
1639 template<class T>
1640 auto overflows(csubstr str) noexcept
1641  -> typename std::enable_if<std::is_unsigned<T>::value, bool>::type
1642 {
1643  C4_STATIC_ASSERT(std::is_integral<T>::value);
1644 
1645  if(C4_UNLIKELY(str.len == 0))
1646  {
1647  return false;
1648  }
1649  else if(str.str[0] == '0')
1650  {
1651  if (str.len == 1)
1652  return false;
1653  switch (str.str[1])
1654  {
1655  case 'x':
1656  case 'X':
1657  {
1658  size_t fno = str.first_not_of('0', 2);
1659  if (fno == csubstr::npos)
1660  return false;
1661  return !(str.len <= fno + (sizeof(T) * 2));
1662  }
1663  case 'b':
1664  case 'B':
1665  {
1666  size_t fno = str.first_not_of('0', 2);
1667  if (fno == csubstr::npos)
1668  return false;
1669  return !(str.len <= fno +(sizeof(T) * 8));
1670  }
1671  case 'o':
1672  case 'O':
1673  {
1674  size_t fno = str.first_not_of('0', 2);
1675  if(fno == csubstr::npos)
1676  return false;
1677  return detail::charconv_digits<T>::is_oct_overflow(str.sub(fno));
1678  }
1679  default:
1680  {
1681  size_t fno = str.first_not_of('0', 1);
1682  if(fno == csubstr::npos)
1683  return false;
1684  return detail::check_overflow(str.sub(fno), detail::charconv_digits<T>::max_value_dec());
1685  }
1686  }
1687  }
1688  else if(C4_UNLIKELY(str[0] == '-'))
1689  {
1690  return true;
1691  }
1692  else
1693  {
1694  return detail::check_overflow(str, detail::charconv_digits<T>::max_value_dec());
1695  }
1696 }
1697 
1698 
1699 /** Test if the following string would overflow when converted to
1700  * associated integral types; this function is dispatched with SFINAE
1701  * to handle differently signed and unsigned types.
1702  *
1703  * @return true if number will overflow, false if it fits (or doesn't parse)
1704  * @see doc_overflow_checked for format specifiers to enforce no-overflow reads
1705  */
1706 template<class T>
1707 auto overflows(csubstr str) noexcept
1708  -> typename std::enable_if<std::is_signed<T>::value, bool>::type
1709 {
1710  C4_STATIC_ASSERT(std::is_integral<T>::value);
1711  if(C4_UNLIKELY(str.len == 0))
1712  return false;
1713  if(str.str[0] == '-')
1714  {
1715  if(str.str[1] == '0')
1716  {
1717  if(str.len == 2)
1718  return false;
1719  switch(str.str[2])
1720  {
1721  case 'x':
1722  case 'X':
1723  {
1724  size_t fno = str.first_not_of('0', 3);
1725  if (fno == csubstr::npos)
1726  return false;
1727  return detail::check_overflow(str.sub(fno), detail::charconv_digits<T>::min_value_hex());
1728  }
1729  case 'b':
1730  case 'B':
1731  {
1732  size_t fno = str.first_not_of('0', 3);
1733  if (fno == csubstr::npos)
1734  return false;
1735  return detail::check_overflow(str.sub(fno), detail::charconv_digits<T>::min_value_bin());
1736  }
1737  case 'o':
1738  case 'O':
1739  {
1740  size_t fno = str.first_not_of('0', 3);
1741  if(fno == csubstr::npos)
1742  return false;
1743  return detail::check_overflow(str.sub(fno), detail::charconv_digits<T>::min_value_oct());
1744  }
1745  default:
1746  {
1747  size_t fno = str.first_not_of('0', 2);
1748  if(fno == csubstr::npos)
1749  return false;
1750  return detail::check_overflow(str.sub(fno), detail::charconv_digits<T>::min_value_dec());
1751  }
1752  }
1753  }
1754  else
1755  {
1756  return detail::check_overflow(str.sub(1), detail::charconv_digits<T>::min_value_dec());
1757  }
1758  }
1759  else if(str.str[0] == '0')
1760  {
1761  if (str.len == 1)
1762  return false;
1763  switch(str.str[1])
1764  {
1765  case 'x':
1766  case 'X':
1767  {
1768  size_t fno = str.first_not_of('0', 2);
1769  if (fno == csubstr::npos)
1770  return false;
1771  const size_t len = str.len - fno;
1772  return !((len < sizeof (T) * 2) || (len == sizeof(T) * 2 && str[fno] <= '7'));
1773  }
1774  case 'b':
1775  case 'B':
1776  {
1777  size_t fno = str.first_not_of('0', 2);
1778  if (fno == csubstr::npos)
1779  return false;
1780  return !(str.len <= fno + (sizeof(T) * 8 - 1));
1781  }
1782  case 'o':
1783  case 'O':
1784  {
1785  size_t fno = str.first_not_of('0', 2);
1786  if(fno == csubstr::npos)
1787  return false;
1788  return detail::charconv_digits<T>::is_oct_overflow(str.sub(fno));
1789  }
1790  default:
1791  {
1792  size_t fno = str.first_not_of('0', 1);
1793  if(fno == csubstr::npos)
1794  return false;
1795  return detail::check_overflow(str.sub(fno), detail::charconv_digits<T>::max_value_dec());
1796  }
1797  }
1798  }
1799  else
1800  {
1801  return detail::check_overflow(str, detail::charconv_digits<T>::max_value_dec());
1802  }
1803 }
1804 
1805 /** @} */
1806 
1807 
1808 //-----------------------------------------------------------------------------
1809 //-----------------------------------------------------------------------------
1810 //-----------------------------------------------------------------------------
1811 
1812 /** @cond dev */
1813 namespace detail {
1814 
1815 
1816 #if (!C4CORE_HAVE_STD_FROMCHARS)
1817 /** @see http://www.exploringbinary.com/ for many good examples on float-str conversion */
1818 template<size_t N>
1819 void get_real_format_str(char (& C4_RESTRICT fmt)[N], int precision, RealFormat_e formatting, const char* length_modifier="")
1820 {
1821  int iret;
1822  if(precision == -1)
1823  iret = snprintf(fmt, sizeof(fmt), "%%%s%c", length_modifier, formatting);
1824  else if(precision == 0)
1825  iret = snprintf(fmt, sizeof(fmt), "%%.%s%c", length_modifier, formatting);
1826  else
1827  iret = snprintf(fmt, sizeof(fmt), "%%.%d%s%c", precision, length_modifier, formatting);
1828  C4_ASSERT(iret >= 2 && size_t(iret) < sizeof(fmt));
1829  C4_UNUSED(iret);
1830 }
1831 
1832 
1833 /** @todo we're depending on snprintf()/sscanf() for converting to/from
1834  * floating point numbers. Apparently, this increases the binary size
1835  * by a considerable amount. There are some lightweight printf
1836  * implementations:
1837  *
1838  * @see http://www.sparetimelabs.com/tinyprintf/tinyprintf.php (BSD)
1839  * @see https://github.com/weiss/c99-snprintf
1840  * @see https://github.com/nothings/stb/blob/master/stb_sprintf.h
1841  * @see http://www.exploringbinary.com/
1842  * @see https://blog.benoitblanchon.fr/lightweight-float-to-string/
1843  * @see http://www.ryanjuckett.com/programming/printing-floating-point-numbers/
1844  */
1845 template<class T>
1846 size_t print_one(substr str, const char* full_fmt, T v)
1847 {
1848 #ifdef _MSC_VER
1849  /** use _snprintf() to prevent early termination of the output
1850  * for writing the null character at the last position
1851  * @see https://msdn.microsoft.com/en-us/library/2ts7cx93.aspx */
1852  int iret = _snprintf(str.str, str.len, full_fmt, v);
1853  if(iret < 0)
1854  {
1855  /* when buf.len is not enough, VS returns a negative value.
1856  * so call it again with a negative value for getting an
1857  * actual length of the string */
1858  iret = snprintf(nullptr, 0, full_fmt, v);
1859  C4_ASSERT(iret > 0);
1860  }
1861  size_t ret = (size_t) iret;
1862  return ret;
1863 #else
1864  int iret = snprintf(str.str, str.len, full_fmt, v);
1865  C4_ASSERT(iret >= 0);
1866  size_t ret = (size_t) iret;
1867  if(ret >= str.len)
1868  ++ret; /* snprintf() reserves the last character to write \0 */
1869  return ret;
1870 #endif
1871 }
1872 #endif // (!C4CORE_HAVE_STD_FROMCHARS)
1873 
1874 
1875 #if (!C4CORE_HAVE_STD_FROMCHARS) && (!C4CORE_HAVE_FAST_FLOAT)
1876 /** scans a string using the given type format, while at the same time
1877  * allowing non-null-terminated strings AND guaranteeing that the given
1878  * string length is strictly respected, so that no buffer overflows
1879  * might occur. */
1880 template<typename T>
1881 inline size_t scan_one(csubstr str, const char *type_fmt, T *v)
1882 {
1883  /* snscanf() is absolutely needed here as we must be sure that
1884  * str.len is strictly respected, because substr is
1885  * generally not null-terminated.
1886  *
1887  * Alas, there is no snscanf().
1888  *
1889  * So we fake it by using a dynamic format with an explicit
1890  * field size set to the length of the given span.
1891  * This trick is taken from:
1892  * https://stackoverflow.com/a/18368910/5875572 */
1893 
1894  /* this is the actual format we'll use for scanning */
1895  char fmt[16];
1896 
1897  /* write the length into it. Eg "%12f".
1898  * Also, get the number of characters read from the string.
1899  * So the final format ends up as "%12f%n"*/
1900  int iret = std::snprintf(fmt, sizeof(fmt), "%%" "%zu" "%s" "%%n", str.len, type_fmt);
1901  /* no nasty surprises, please! */
1902  C4_ASSERT(iret >= 0 && size_t(iret) < C4_COUNTOF(fmt));
1903 
1904  /* now we scan with confidence that the span length is respected */
1905  int num_chars;
1906  iret = std::sscanf(str.str, fmt, v, &num_chars);
1907  /* scanf returns the number of successful conversions */
1908  if(iret != 1) return csubstr::npos;
1909  C4_ASSERT(num_chars >= 0);
1910  return (size_t)(num_chars);
1911 }
1912 #endif // (!C4CORE_HAVE_STD_FROMCHARS) && (!C4CORE_HAVE_FAST_FLOAT)
1913 
1914 
1915 #if C4CORE_HAVE_STD_TOCHARS
1916 template<class T>
1917 C4_ALWAYS_INLINE size_t rtoa(substr buf, T v, int precision=-1, RealFormat_e formatting=FTOA_FLEX) noexcept
1918 {
1919  std::to_chars_result result;
1920  size_t pos = 0;
1921  if(formatting == FTOA_HEXA)
1922  {
1923  if(buf.len > size_t(2))
1924  {
1925  buf.str[0] = '0';
1926  buf.str[1] = 'x';
1927  }
1928  pos += size_t(2);
1929  }
1930  if(precision == -1)
1931  result = std::to_chars(buf.str + pos, buf.str + buf.len, v, (std::chars_format)formatting);
1932  else
1933  result = std::to_chars(buf.str + pos, buf.str + buf.len, v, (std::chars_format)formatting, precision);
1934  if(result.ec == std::errc())
1935  {
1936  // all good, no errors.
1937  C4_ASSERT(result.ptr >= buf.str);
1938  ptrdiff_t delta = result.ptr - buf.str;
1939  return static_cast<size_t>(delta);
1940  }
1941  C4_ASSERT(result.ec == std::errc::value_too_large);
1942  // This is unfortunate.
1943  //
1944  // When the result can't fit in the given buffer,
1945  // std::to_chars() returns the end pointer it was originally
1946  // given, which is useless because here we would like to know
1947  // _exactly_ how many characters the buffer must have to fit
1948  // the result.
1949  //
1950  // So we take the pessimistic view, and assume as many digits
1951  // as could ever be required:
1952  size_t ret = static_cast<size_t>(std::numeric_limits<T>::max_digits10);
1953  return ret > buf.len ? ret : buf.len + 1;
1954 }
1955 #endif // C4CORE_HAVE_STD_TOCHARS
1956 
1957 
1958 #if C4CORE_HAVE_FAST_FLOAT
1959 template<class T>
1960 C4_ALWAYS_INLINE bool scan_rhex(csubstr s, T *C4_RESTRICT val) noexcept
1961 {
1962  C4_ASSERT(s.len > 0);
1963  C4_ASSERT(s.str[0] != '-');
1964  C4_ASSERT(s.str[0] != '+');
1965  C4_ASSERT(!s.begins_with("0x"));
1966  C4_ASSERT(!s.begins_with("0X"));
1967  size_t pos = 0;
1968  // integer part
1969  for( ; pos < s.len; ++pos)
1970  {
1971  const char c = s.str[pos];
1972  if(c >= '0' && c <= '9')
1973  *val = (*val * T(16)) + T(c - '0');
1974  else if(c >= 'a' && c <= 'f')
1975  *val = (*val * T(16)) + T(c - 'a');
1976  else if(c >= 'A' && c <= 'F')
1977  *val = (*val * T(16)) + T(c - 'A');
1978  else if(c == '.')
1979  {
1980  ++pos;
1981  break; // follow on to mantissa
1982  }
1983  else if(c == 'p' || c == 'P')
1984  {
1985  ++pos;
1986  goto power; // no mantissa given, jump to power // NOLINT
1987  }
1988  else
1989  {
1990  return false;
1991  }
1992  }
1993  // mantissa
1994  {
1995  // 0.0625 == 1/16 == value of first digit after the comma
1996  for(T digit = T(0.0625); pos < s.len; ++pos, digit /= T(16)) // NOLINT
1997  {
1998  const char c = s.str[pos];
1999  if(c >= '0' && c <= '9')
2000  *val += digit * T(c - '0');
2001  else if(c >= 'a' && c <= 'f')
2002  *val += digit * T(c - 'a');
2003  else if(c >= 'A' && c <= 'F')
2004  *val += digit * T(c - 'A');
2005  else if(c == 'p' || c == 'P')
2006  {
2007  ++pos;
2008  goto power; // mantissa finished, jump to power // NOLINT
2009  }
2010  else
2011  {
2012  return false;
2013  }
2014  }
2015  }
2016  return true;
2017 power:
2018  if(C4_LIKELY(pos < s.len))
2019  {
2020  if(s.str[pos] == '+') // atoi() cannot handle a leading '+'
2021  ++pos;
2022  if(C4_LIKELY(pos < s.len))
2023  {
2024  int16_t powval = {};
2025  if(C4_LIKELY(atoi(s.sub(pos), &powval)))
2026  {
2027  *val *= ipow<T, int16_t, 16>(powval);
2028  return true;
2029  }
2030  }
2031  }
2032  return false;
2033 }
2034 #endif
2035 
2036 } // namespace detail
2037 /** @endcond */
2038 
2039 
2040 #undef _c4appendhex
2041 #undef _c4append
2042 
2043 
2044 /** @defgroup doc_ftoa ftoa: float32 to chars
2045  *
2046  * @{ */
2047 
2048 /** Convert a single-precision real number to string. The string will
2049  * in general be NOT null-terminated. For FTOA_FLEX, \p precision is
2050  * the number of significand digits. Otherwise \p precision is the
2051  * number of decimals. It is safe to call this function with an empty
2052  * or too-small buffer.
2053  *
2054  * @return the size of the buffer needed to write the number
2055  */
2056 C4_ALWAYS_INLINE size_t ftoa(substr str, float v, int precision=-1, RealFormat_e formatting=FTOA_FLEX) noexcept
2057 {
2058 #if C4CORE_HAVE_STD_TOCHARS
2059  return detail::rtoa(str, v, precision, formatting);
2060 #else
2061  char fmt[16];
2062  detail::get_real_format_str(fmt, precision, formatting, /*length_modifier*/"");
2063  return detail::print_one(str, fmt, v);
2064 #endif
2065 }
2066 
2067 /** @} */
2068 
2069 
2070 /** @defgroup doc_dtoa dtoa: float64 to chars
2071  *
2072  * @{ */
2073 
2074 /** Convert a double-precision real number to string. The string will
2075  * in general be NOT null-terminated. For FTOA_FLEX, \p precision is
2076  * the number of significand digits. Otherwise \p precision is the
2077  * number of decimals. It is safe to call this function with an empty
2078  * or too-small buffer.
2079  *
2080  * @return the size of the buffer needed to write the number
2081  */
2082 C4_ALWAYS_INLINE size_t dtoa(substr str, double v, int precision=-1, RealFormat_e formatting=FTOA_FLEX) noexcept
2083 {
2084 #if C4CORE_HAVE_STD_TOCHARS
2085  return detail::rtoa(str, v, precision, formatting);
2086 #else
2087  char fmt[16];
2088  detail::get_real_format_str(fmt, precision, formatting, /*length_modifier*/"l");
2089  return detail::print_one(str, fmt, v);
2090 #endif
2091 }
2092 
2093 /** @} */
2094 
2095 
2096 /** @defgroup doc_atof atof: chars to float32
2097  *
2098  * @{ */
2099 
2100 /** Convert a string to a single precision real number.
2101  * The input string must be trimmed to the value, ie
2102  * no leading or trailing whitespace can be present.
2103  * @return true iff the conversion succeeded
2104  * @see atof_first() if the string is not trimmed
2105  */
2106 C4_ALWAYS_INLINE bool atof(csubstr str, float * C4_RESTRICT v) noexcept
2107 {
2108  C4_ASSERT(str.len > 0);
2109  C4_ASSERT(str.triml(" \r\t\n").len == str.len);
2110 #if C4CORE_HAVE_FAST_FLOAT
2111  // fastfloat cannot parse hexadecimal floats
2112  bool isneg = (str.str[0] == '-');
2113  csubstr rem = str.sub(isneg || str.str[0] == '+');
2114  if(!(rem.len >= 2 && (rem.str[0] == '0' && (rem.str[1] == 'x' || rem.str[1] == 'X'))))
2115  {
2116  fast_float::from_chars_result result;
2117  result = fast_float::from_chars(str.str, str.str + str.len, *v);
2118  return result.ec == std::errc();
2119  }
2120  else if(detail::scan_rhex(rem.sub(2), v))
2121  {
2122  *v *= isneg ? -1.f : 1.f;
2123  return true;
2124  }
2125  return false;
2126 #elif C4CORE_HAVE_STD_FROMCHARS
2127  std::from_chars_result result;
2128  result = std::from_chars(str.str, str.str + str.len, *v);
2129  return result.ec == std::errc();
2130 #else
2131  csubstr rem = str.sub(str.str[0] == '-' || str.str[0] == '+');
2132  if(!(rem.len >= 2 && (rem.str[0] == '0' && (rem.str[1] == 'x' || rem.str[1] == 'X'))))
2133  return detail::scan_one(str, "f", v) != csubstr::npos;
2134  else
2135  return detail::scan_one(str, "a", v) != csubstr::npos;
2136 #endif
2137 }
2138 
2139 
2140 /** Convert a string to a single precision real number.
2141  * Leading whitespace is skipped until valid characters are found.
2142  * @return the number of characters read from the string, or npos if
2143  * conversion was not successful or if the string was empty */
2144 inline size_t atof_first(csubstr str, float * C4_RESTRICT v) noexcept
2145 {
2146  csubstr trimmed = str.first_real_span();
2147  if(trimmed.len == 0)
2148  return csubstr::npos;
2149  if(atof(trimmed, v))
2150  return static_cast<size_t>(trimmed.end() - str.begin());
2151  return csubstr::npos;
2152 }
2153 
2154 /** @} */
2155 
2156 
2157 /** @defgroup doc_atod atod: chars to float64
2158  *
2159  * @{ */
2160 
2161 /** Convert a string to a double precision real number.
2162  * The input string must be trimmed to the value, ie
2163  * no leading or trailing whitespace can be present.
2164  * @return true iff the conversion succeeded
2165  * @see atod_first() if the string is not trimmed
2166  */
2167 C4_ALWAYS_INLINE bool atod(csubstr str, double * C4_RESTRICT v) noexcept
2168 {
2169  C4_ASSERT(str.len > 0);
2170  C4_ASSERT(str.triml(" \r\t\n").len == str.len);
2171 #if C4CORE_HAVE_FAST_FLOAT
2172  // fastfloat cannot parse hexadecimal floats
2173  bool isneg = (str.str[0] == '-');
2174  csubstr rem = str.sub(isneg || str.str[0] == '+');
2175  if(!(rem.len >= 2 && (rem.str[0] == '0' && (rem.str[1] == 'x' || rem.str[1] == 'X'))))
2176  {
2177  fast_float::from_chars_result result;
2178  #ifndef CLANG_TIDY // suppress a false-positive error (cannot be done with NOLINT: https://stackoverflow.com/questions/62838193/ )
2179  result = fast_float::from_chars(str.str, str.str + str.len, *v);
2180  #else
2181  result = {};
2182  #endif
2183  return result.ec == std::errc();
2184  }
2185  else if(detail::scan_rhex(rem.sub(2), v))
2186  {
2187  *v *= isneg ? -1. : 1.;
2188  return true;
2189  }
2190  return false;
2191 #elif C4CORE_HAVE_STD_FROMCHARS
2192  std::from_chars_result result;
2193  result = std::from_chars(str.str, str.str + str.len, *v);
2194  return result.ec == std::errc();
2195 #else
2196  csubstr rem = str.sub(str.str[0] == '-' || str.str[0] == '+');
2197  if(!(rem.len >= 2 && (rem.str[0] == '0' && (rem.str[1] == 'x' || rem.str[1] == 'X'))))
2198  return detail::scan_one(str, "lf", v) != csubstr::npos;
2199  else
2200  return detail::scan_one(str, "la", v) != csubstr::npos;
2201 #endif
2202 }
2203 
2204 
2205 /** Convert a string to a double precision real number.
2206  * Leading whitespace is skipped until valid characters are found.
2207  * @return the number of characters read from the string, or npos if
2208  * conversion was not successful or if the string was empty */
2209 inline size_t atod_first(csubstr str, double * C4_RESTRICT v) noexcept
2210 {
2211  csubstr trimmed = str.first_real_span();
2212  if(trimmed.len == 0)
2213  return csubstr::npos;
2214  if(atod(trimmed, v))
2215  return static_cast<size_t>(trimmed.end() - str.begin());
2216  return csubstr::npos;
2217 }
2218 
2219 /** @} */
2220 
2221 
2222 //-----------------------------------------------------------------------------
2223 //-----------------------------------------------------------------------------
2224 //-----------------------------------------------------------------------------
2225 // generic versions
2226 
2227 /** @cond dev */
2228 // on some platforms, (unsigned) int and (unsigned) long
2229 // are not any of the fixed length types above
2230 #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>
2231 #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>
2232 /** @endcond*/
2233 
2234 
2235 /** @defgroup doc_xtoa xtoa: generic value to chars
2236  *
2237  * Dispatches to the most appropriate and efficient conversion
2238  * function
2239  *
2240  * @{ */
2241 C4_ALWAYS_INLINE size_t xtoa(substr s, uint8_t v) noexcept { return write_dec(s, v); }
2242 C4_ALWAYS_INLINE size_t xtoa(substr s, uint16_t v) noexcept { return write_dec(s, v); }
2243 C4_ALWAYS_INLINE size_t xtoa(substr s, uint32_t v) noexcept { return write_dec(s, v); }
2244 C4_ALWAYS_INLINE size_t xtoa(substr s, uint64_t v) noexcept { return write_dec(s, v); }
2245 C4_ALWAYS_INLINE size_t xtoa(substr s, int8_t v) noexcept { return itoa(s, v); }
2246 C4_ALWAYS_INLINE size_t xtoa(substr s, int16_t v) noexcept { return itoa(s, v); }
2247 C4_ALWAYS_INLINE size_t xtoa(substr s, int32_t v) noexcept { return itoa(s, v); }
2248 C4_ALWAYS_INLINE size_t xtoa(substr s, int64_t v) noexcept { return itoa(s, v); }
2249 C4_ALWAYS_INLINE size_t xtoa(substr s, float v) noexcept { return ftoa(s, v); }
2250 C4_ALWAYS_INLINE size_t xtoa(substr s, double v) noexcept { return dtoa(s, v); }
2251 
2252 C4_ALWAYS_INLINE size_t xtoa(substr s, uint8_t v, uint8_t radix) noexcept { return utoa(s, v, radix); }
2253 C4_ALWAYS_INLINE size_t xtoa(substr s, uint16_t v, uint16_t radix) noexcept { return utoa(s, v, radix); }
2254 C4_ALWAYS_INLINE size_t xtoa(substr s, uint32_t v, uint32_t radix) noexcept { return utoa(s, v, radix); }
2255 C4_ALWAYS_INLINE size_t xtoa(substr s, uint64_t v, uint64_t radix) noexcept { return utoa(s, v, radix); }
2256 C4_ALWAYS_INLINE size_t xtoa(substr s, int8_t v, int8_t radix) noexcept { return itoa(s, v, radix); }
2257 C4_ALWAYS_INLINE size_t xtoa(substr s, int16_t v, int16_t radix) noexcept { return itoa(s, v, radix); }
2258 C4_ALWAYS_INLINE size_t xtoa(substr s, int32_t v, int32_t radix) noexcept { return itoa(s, v, radix); }
2259 C4_ALWAYS_INLINE size_t xtoa(substr s, int64_t v, int64_t radix) noexcept { return itoa(s, v, radix); }
2260 
2261 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); }
2262 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); }
2263 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); }
2264 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); }
2265 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); }
2266 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); }
2267 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); }
2268 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); }
2269 
2270 C4_ALWAYS_INLINE size_t xtoa(substr s, float v, int precision, RealFormat_e formatting=FTOA_FLEX) noexcept { return ftoa(s, v, precision, formatting); }
2271 C4_ALWAYS_INLINE size_t xtoa(substr s, double v, int precision, RealFormat_e formatting=FTOA_FLEX) noexcept { return dtoa(s, v, precision, formatting); }
2272 
2273 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); }
2274 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); }
2275 template <class T>
2276 C4_ALWAYS_INLINE size_t xtoa(substr s, T *v) noexcept { return itoa(s, (intptr_t)v, (intptr_t)16); }
2277 
2278 /** @} */
2279 
2280 /** @defgroup doc_atox atox: generic chars to value
2281  *
2282  * Dispatches to the most appropriate and efficient conversion
2283  * function
2284  *
2285  * @{ */
2286 
2287 C4_ALWAYS_INLINE bool atox(csubstr s, uint8_t *C4_RESTRICT v) noexcept { return atou(s, v); }
2288 C4_ALWAYS_INLINE bool atox(csubstr s, uint16_t *C4_RESTRICT v) noexcept { return atou(s, v); }
2289 C4_ALWAYS_INLINE bool atox(csubstr s, uint32_t *C4_RESTRICT v) noexcept { return atou(s, v); }
2290 C4_ALWAYS_INLINE bool atox(csubstr s, uint64_t *C4_RESTRICT v) noexcept { return atou(s, v); }
2291 C4_ALWAYS_INLINE bool atox(csubstr s, int8_t *C4_RESTRICT v) noexcept { return atoi(s, v); }
2292 C4_ALWAYS_INLINE bool atox(csubstr s, int16_t *C4_RESTRICT v) noexcept { return atoi(s, v); }
2293 C4_ALWAYS_INLINE bool atox(csubstr s, int32_t *C4_RESTRICT v) noexcept { return atoi(s, v); }
2294 C4_ALWAYS_INLINE bool atox(csubstr s, int64_t *C4_RESTRICT v) noexcept { return atoi(s, v); }
2295 C4_ALWAYS_INLINE bool atox(csubstr s, float *C4_RESTRICT v) noexcept { return atof(s, v); }
2296 C4_ALWAYS_INLINE bool atox(csubstr s, double *C4_RESTRICT v) noexcept { return atod(s, v); }
2297 
2298 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); }
2299 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); }
2300 template <class T>
2301 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; }
2302 
2303 /** @} */
2304 
2305 
2306 /** @defgroup doc_to_chars to_chars: generalized chars to value
2307  *
2308  * Convert the given value, writing into the string. The resulting
2309  * string will NOT be null-terminated. Return the number of
2310  * characters needed. This function is safe to call when the string
2311  * is too small - no writes will occur beyond the string's last
2312  * character.
2313  *
2314  * Dispatches to the most appropriate and efficient conversion
2315  * function.
2316  *
2317  * @see write_dec, doc_utoa, doc_itoa, doc_ftoa, doc_dtoa
2318  *
2319  * @warning When serializing floating point values (float or double),
2320  * be aware that because it uses defaults, to_chars() may cause a
2321  * truncation of the precision. To enforce a particular precision, use
2322  * for example @ref c4::fmt::real, or call directly @ref c4::ftoa or
2323  * @ref c4::dtoa.
2324  *
2325  * @{ */
2326 
2327 C4_ALWAYS_INLINE size_t to_chars(substr buf, uint8_t v) noexcept { return write_dec(buf, v); }
2328 C4_ALWAYS_INLINE size_t to_chars(substr buf, uint16_t v) noexcept { return write_dec(buf, v); }
2329 C4_ALWAYS_INLINE size_t to_chars(substr buf, uint32_t v) noexcept { return write_dec(buf, v); }
2330 C4_ALWAYS_INLINE size_t to_chars(substr buf, uint64_t v) noexcept { return write_dec(buf, v); }
2331 C4_ALWAYS_INLINE size_t to_chars(substr buf, int8_t v) noexcept { return itoa(buf, v); }
2332 C4_ALWAYS_INLINE size_t to_chars(substr buf, int16_t v) noexcept { return itoa(buf, v); }
2333 C4_ALWAYS_INLINE size_t to_chars(substr buf, int32_t v) noexcept { return itoa(buf, v); }
2334 C4_ALWAYS_INLINE size_t to_chars(substr buf, int64_t v) noexcept { return itoa(buf, v); }
2335 C4_ALWAYS_INLINE size_t to_chars(substr buf, float v) noexcept { return ftoa(buf, v); }
2336 C4_ALWAYS_INLINE size_t to_chars(substr buf, double v) noexcept { return dtoa(buf, v); }
2337 
2338 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); }
2339 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); }
2340 template <class T>
2341 C4_ALWAYS_INLINE auto to_chars(substr s, T *v) noexcept
2342  -> typename std::enable_if<!std::is_same<T, char>::value &&
2343  !std::is_same<T, const char>::value,
2344  size_t>::type
2345 {
2346  return itoa(s, (intptr_t)v, (intptr_t)16);
2347 }
2348 
2349 /** @} */
2350 
2351 
2352 /** @defgroup doc_from_chars from_chars: generalized chars to value
2353  *
2354  * Read a value from the string, which must be trimmed to the value
2355  * (ie, no leading/trailing whitespace). return true if the
2356  * conversion succeeded. There is no check for overflow; the value
2357  * wraps around in a way similar to the standard C/C++ overflow
2358  * behavior. For example, from_chars<int8_t>("128", &val) returns true
2359  * and val will be set tot 0. See @ref doc_overflows and @ref
2360  * doc_overflow_checked for facilities enforcing no-overflow.
2361  *
2362  * Dispatches to the most appropriate and efficient conversion
2363  * function
2364  *
2365  * @see doc_from_chars_first, atou, atoi, atof, atod
2366  * @{ */
2367 
2368 C4_ALWAYS_INLINE bool from_chars(csubstr buf, uint8_t *C4_RESTRICT v) noexcept { return atou(buf, v); }
2369 C4_ALWAYS_INLINE bool from_chars(csubstr buf, uint16_t *C4_RESTRICT v) noexcept { return atou(buf, v); }
2370 C4_ALWAYS_INLINE bool from_chars(csubstr buf, uint32_t *C4_RESTRICT v) noexcept { return atou(buf, v); }
2371 C4_ALWAYS_INLINE bool from_chars(csubstr buf, uint64_t *C4_RESTRICT v) noexcept { return atou(buf, v); }
2372 C4_ALWAYS_INLINE bool from_chars(csubstr buf, int8_t *C4_RESTRICT v) noexcept { return atoi(buf, v); }
2373 C4_ALWAYS_INLINE bool from_chars(csubstr buf, int16_t *C4_RESTRICT v) noexcept { return atoi(buf, v); }
2374 C4_ALWAYS_INLINE bool from_chars(csubstr buf, int32_t *C4_RESTRICT v) noexcept { return atoi(buf, v); }
2375 C4_ALWAYS_INLINE bool from_chars(csubstr buf, int64_t *C4_RESTRICT v) noexcept { return atoi(buf, v); }
2376 C4_ALWAYS_INLINE bool from_chars(csubstr buf, float *C4_RESTRICT v) noexcept { return atof(buf, v); }
2377 C4_ALWAYS_INLINE bool from_chars(csubstr buf, double *C4_RESTRICT v) noexcept { return atod(buf, v); }
2378 
2379 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); }
2380 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); }
2381 template <class T>
2382 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; }
2383 
2384 /** @defgroup doc_from_chars_first from_chars_first: generalized chars to value
2385  *
2386  * Read the first valid sequence of characters from the string,
2387  * skipping leading whitespace, and convert it using @ref doc_from_chars .
2388  * Return the number of characters read for converting.
2389  *
2390  * Dispatches to the most appropriate and efficient conversion
2391  * function.
2392  *
2393  * @see atou_first, atoi_first, atof_first, atod_first
2394  * @{ */
2395 
2396 C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, uint8_t *C4_RESTRICT v) noexcept { return atou_first(buf, v); }
2397 C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, uint16_t *C4_RESTRICT v) noexcept { return atou_first(buf, v); }
2398 C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, uint32_t *C4_RESTRICT v) noexcept { return atou_first(buf, v); }
2399 C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, uint64_t *C4_RESTRICT v) noexcept { return atou_first(buf, v); }
2400 C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, int8_t *C4_RESTRICT v) noexcept { return atoi_first(buf, v); }
2401 C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, int16_t *C4_RESTRICT v) noexcept { return atoi_first(buf, v); }
2402 C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, int32_t *C4_RESTRICT v) noexcept { return atoi_first(buf, v); }
2403 C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, int64_t *C4_RESTRICT v) noexcept { return atoi_first(buf, v); }
2404 C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, float *C4_RESTRICT v) noexcept { return atof_first(buf, v); }
2405 C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, double *C4_RESTRICT v) noexcept { return atod_first(buf, v); }
2406 
2407 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); }
2408 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); }
2409 template <class T>
2410 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; }
2411 
2412 /** @} */
2413 
2414 /** @} */
2415 
2416 #undef _C4_IF_NOT_FIXED_LENGTH_I
2417 #undef _C4_IF_NOT_FIXED_LENGTH_U
2418 
2419 
2420 //-----------------------------------------------------------------------------
2421 //-----------------------------------------------------------------------------
2422 //-----------------------------------------------------------------------------
2423 /** call to_chars() and return a substr consisting of the
2424  * written portion of the input buffer. Ie, same as to_chars(),
2425  * but return a substr instead of a size_t.
2426  * Convert the given value to a string using to_chars(), and
2427  * return the resulting string, up to and including the last
2428  * written character.
2429  * @ingroup doc_to_chars
2430  * @see to_chars() */
2431 template<class T>
2432 C4_ALWAYS_INLINE substr to_chars_sub(substr buf, T const& C4_RESTRICT v) noexcept
2433 {
2434  size_t sz = to_chars(buf, v);
2435  return buf.left_of(sz <= buf.len ? sz : buf.len);
2436 }
2437 
2438 //-----------------------------------------------------------------------------
2439 //-----------------------------------------------------------------------------
2440 //-----------------------------------------------------------------------------
2441 // bool implementation
2442 
2443 /** @ingroup doc_to_chars */
2444 C4_ALWAYS_INLINE size_t to_chars(substr buf, bool v) noexcept
2445 {
2446  int val = v;
2447  return to_chars(buf, val);
2448 }
2449 
2450 /** @ingroup doc_from_chars */
2451 inline bool from_chars(csubstr buf, bool * C4_RESTRICT v) noexcept
2452 {
2453  if(buf.len == 1)
2454  {
2455  if(buf.str[0] == '0')
2456  {
2457  *v = false; return true;
2458  }
2459  else if(buf.str[0] == '1')
2460  {
2461  *v = true; return true;
2462  }
2463  }
2464  else if(buf.len == 4)
2465  {
2466  if(((buf.str[0] == 't') && (0 == memcmp(buf.str + 1, "rue", 3)))
2467  ||
2468  ((buf.str[0] == 'T') && ((0 == memcmp(buf.str + 1, "rue", 3) ||
2469  0 == memcmp(buf.str + 1, "RUE", 3)))))
2470  {
2471  *v = true; return true;
2472  }
2473  }
2474  else if(buf.len == 5)
2475  {
2476  if(((buf.str[0] == 'f') && (0 == memcmp(buf.str + 1, "alse", 4)))
2477  ||
2478  ((buf.str[0] == 'F') && ((0 == memcmp(buf.str + 1, "alse", 4) ||
2479  0 == memcmp(buf.str + 1, "ALSE", 4)))))
2480  {
2481  *v = false; return true;
2482  }
2483  }
2484  // fallback to c-style int bools
2485  int val = 0;
2486  bool ret = from_chars(buf, &val);
2487  if(C4_LIKELY(ret))
2488  {
2489  *v = (val != 0);
2490  }
2491  return ret;
2492 }
2493 
2494 /** @ingroup doc_from_chars_first */
2495 inline size_t from_chars_first(csubstr buf, bool * C4_RESTRICT v) noexcept
2496 {
2497  csubstr trimmed = buf.first_non_empty_span();
2498  if(trimmed.len == 0 || !from_chars(buf, v))
2499  return csubstr::npos;
2500  return trimmed.len;
2501 }
2502 
2503 
2504 //-----------------------------------------------------------------------------
2505 // single-char implementation
2506 
2507 /** @ingroup doc_to_chars */
2508 inline size_t to_chars(substr buf, char v) noexcept
2509 {
2510  if(buf.len > 0)
2511  {
2512  C4_XASSERT(buf.str);
2513  buf.str[0] = v;
2514  }
2515  return 1;
2516 }
2517 
2518 /** extract a single character from a substring
2519  * @note to extract a string instead and not just a single character, use the csubstr overload
2520  * @ingroup doc_from_chars
2521  * */
2522 inline bool from_chars(csubstr buf, char * C4_RESTRICT v) noexcept
2523 {
2524  if(buf.len != 1)
2525  return false;
2526  C4_XASSERT(buf.str);
2527  *v = buf.str[0];
2528  return true;
2529 }
2530 
2531 /** @ingroup doc_from_chars_first */
2532 inline size_t from_chars_first(csubstr buf, char * C4_RESTRICT v) noexcept
2533 {
2534  if(buf.len < 1)
2535  return csubstr::npos;
2536  *v = buf.str[0];
2537  return 1;
2538 }
2539 
2540 
2541 //-----------------------------------------------------------------------------
2542 // csubstr implementation
2543 
2544 /** @ingroup doc_to_chars */
2545 inline size_t to_chars(substr buf, csubstr v) noexcept
2546 {
2547  C4_ASSERT(!buf.overlaps(v));
2548  size_t len = buf.len < v.len ? buf.len : v.len;
2549  // calling memcpy with null strings is undefined behavior
2550  // and will wreak havoc in calling code's branches.
2551  // see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637
2552  if(len)
2553  {
2554  C4_ASSERT(buf.str != nullptr);
2555  C4_ASSERT(v.str != nullptr);
2556  memcpy(buf.str, v.str, len);
2557  }
2558  return v.len;
2559 }
2560 
2561 /** @ingroup doc_from_chars */
2562 inline bool from_chars(csubstr buf, csubstr *C4_RESTRICT v) noexcept
2563 {
2564  *v = buf;
2565  return true;
2566 }
2567 
2568 /** @ingroup doc_from_chars_first */
2569 inline size_t from_chars_first(substr buf, csubstr * C4_RESTRICT v) noexcept
2570 {
2571  csubstr trimmed = buf.first_non_empty_span();
2572  if(trimmed.len == 0)
2573  return csubstr::npos;
2574  *v = trimmed;
2575  return static_cast<size_t>(trimmed.end() - buf.begin());
2576 }
2577 
2578 
2579 //-----------------------------------------------------------------------------
2580 // substr
2581 
2582 /** @ingroup doc_to_chars */
2583 inline size_t to_chars(substr buf, substr v) noexcept
2584 {
2585  C4_ASSERT(!buf.overlaps(v));
2586  size_t len = buf.len < v.len ? buf.len : v.len;
2587  // calling memcpy zero len is undefined behavior
2588  // and will wreak havoc in calling code's branches.
2589  // see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637
2590  if(len)
2591  {
2592  C4_ASSERT(buf.str != nullptr);
2593  C4_ASSERT(v.str != nullptr);
2594  memcpy(buf.str, v.str, len);
2595  }
2596  return v.len;
2597 }
2598 
2599 /** @ingroup doc_from_chars */
2600 inline bool from_chars(csubstr buf, substr * C4_RESTRICT v) noexcept
2601 {
2602  C4_ASSERT(!buf.overlaps(*v));
2603  // is the destination buffer wide enough?
2604  if(v->len >= buf.len)
2605  {
2606  // calling memcpy with zero len is undefined behavior
2607  // and will wreak havoc in calling code's branches.
2608  // see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637
2609  if(buf.len)
2610  {
2611  C4_ASSERT(buf.str != nullptr);
2612  C4_ASSERT(v->str != nullptr);
2613  memcpy(v->str, buf.str, buf.len);
2614  }
2615  v->len = buf.len;
2616  return true;
2617  }
2618  return false;
2619 }
2620 
2621 /** @ingroup doc_from_chars_first */
2622 inline size_t from_chars_first(csubstr buf, substr * C4_RESTRICT v) noexcept
2623 {
2624  csubstr trimmed = buf.first_non_empty_span();
2625  C4_ASSERT(!trimmed.overlaps(*v));
2626  if(C4_UNLIKELY(trimmed.len == 0))
2627  return csubstr::npos;
2628  size_t len = trimmed.len > v->len ? v->len : trimmed.len;
2629  // calling memcpy with zero len is undefined behavior
2630  // and will wreak havoc in calling code's branches.
2631  // see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637
2632  if(len)
2633  {
2634  C4_ASSERT(buf.str != nullptr);
2635  C4_ASSERT(v->str != nullptr);
2636  memcpy(v->str, trimmed.str, len);
2637  }
2638  if(C4_UNLIKELY(trimmed.len > v->len))
2639  return csubstr::npos;
2640  return static_cast<size_t>(trimmed.end() - buf.begin());
2641 }
2642 
2643 
2644 //-----------------------------------------------------------------------------
2645 
2646 /** @ingroup doc_to_chars
2647  * (1) overload for `char(&)[N]` and `const char(&)[N]` */
2648 template<size_t N>
2649 size_t to_chars(substr buf, const char (& C4_RESTRICT v)[N]) noexcept
2650 {
2651  return to_chars(buf, csubstr{v, N-1});
2652 }
2653 
2654 /** @ingroup doc_to_chars
2655  * (2) overload for `char*` and `const char*`. Must be zero-terminated!
2656  * @warning will call strlen()
2657  * @note this overload uses SFINAE to prevent it from overriding the array overload
2658  * @see For a more detailed explanation on why the plain pointer overloads cannot
2659  * coexist with the array overloads, see http://cplusplus.bordoon.com/specializeForCharacterArrays.html */
2660 template<class CharPtr>
2661 auto to_chars(substr buf, CharPtr v) noexcept
2662  -> typename std::enable_if<std::is_same<CharPtr, char*>::value
2663  ||
2664  std::is_same<CharPtr, const char*>::value, size_t>::type
2665 {
2666  return v ? to_chars(buf, csubstr{v, strlen(v)}) : 0;
2667 }
2668 
2669 
2670 //-----------------------------------------------------------------------------
2671 // nullptr implementation
2672 
2673 /** @ingroup doc_to_chars */
2674 C4_ALWAYS_INLINE size_t to_chars(substr, std::nullptr_t) noexcept
2675 {
2676  return 0;
2677 }
2678 
2679 // from_chars() and from_chars_sub() must not exist for nullptr
2680 
2681 
2682 /** @} */
2683 
2684 } // namespace c4
2685 
2686 // NOLINTEND(hicpp-signed-bitwise)
2687 
2688 #if defined(_MSC_VER) && !defined(__clang__)
2689 # pragma warning(pop)
2690 #elif defined(__clang__)
2691 # pragma clang diagnostic pop
2692 #elif defined(__GNUC__)
2693 # pragma GCC diagnostic pop
2694 #endif
2695 
2696 #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:2167
size_t atod_first(csubstr str, double *v) noexcept
Convert a string to a double precision real number.
Definition: charconv.hpp:2209
size_t atof_first(csubstr str, float *v) noexcept
Convert a string to a single precision real number.
Definition: charconv.hpp:2144
bool atof(csubstr str, float *v) noexcept
Convert a string to a single precision real number.
Definition: charconv.hpp:2106
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:1544
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:1586
bool atox(csubstr s, T **v) noexcept
Definition: charconv.hpp:2301
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:2082
size_t from_chars_first(csubstr buf, substr *v) noexcept
Definition: charconv.hpp:2622
bool from_chars(csubstr buf, substr *v) noexcept
Definition: charconv.hpp:2600
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:2056
integral_< intptr_t > hex(std::nullptr_t)
format null as an hexadecimal value
Definition: format.hpp:160
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:1640
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
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:2432
size_t to_chars(substr, std::nullptr_t) noexcept
Definition: charconv.hpp:2674
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:2241
size_t xtoa(substr s, T *v) noexcept
Definition: charconv.hpp:2276
@ 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)