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