1 #ifndef _C4_CHARCONV_HPP_
2 #define _C4_CHARCONV_HPP_
8 #include "c4/language.hpp"
10 #include <type_traits>
15 #include "c4/config.hpp"
17 #include "c4/std/std_fwd.hpp"
18 #include "c4/memory_util.hpp"
20 #ifndef C4CORE_NO_FAST_FLOAT
22 # if defined(_MSC_VER)
23 # if (C4_MSVC_VERSION >= C4_MSVC_VERSION_2019)
25 # define C4CORE_HAVE_STD_TOCHARS 1
26 # define C4CORE_HAVE_STD_FROMCHARS 0
27 # define C4CORE_HAVE_FAST_FLOAT 1
29 # define C4CORE_HAVE_STD_TOCHARS 0
30 # define C4CORE_HAVE_STD_FROMCHARS 0
31 # define C4CORE_HAVE_FAST_FLOAT 1
34 # if __has_include(<charconv>)
36 # if defined(__cpp_lib_to_chars)
37 # define C4CORE_HAVE_STD_TOCHARS 1
38 # define C4CORE_HAVE_STD_FROMCHARS 0
39 # define C4CORE_HAVE_FAST_FLOAT 1
41 # define C4CORE_HAVE_STD_TOCHARS 0
42 # define C4CORE_HAVE_STD_FROMCHARS 0
43 # define C4CORE_HAVE_FAST_FLOAT 1
46 # define C4CORE_HAVE_STD_TOCHARS 0
47 # define C4CORE_HAVE_STD_FROMCHARS 0
48 # define C4CORE_HAVE_FAST_FLOAT 1
52 # define C4CORE_HAVE_STD_TOCHARS 0
53 # define C4CORE_HAVE_STD_FROMCHARS 0
54 # define C4CORE_HAVE_FAST_FLOAT 1
56 # if C4CORE_HAVE_FAST_FLOAT
57 # include "c4/ext/fast_float.hpp"
60 # define C4CORE_HAVE_FAST_FLOAT 0
61 # if defined(_MSC_VER)
62 # if (C4_MSVC_VERSION >= C4_MSVC_VERSION_2019)
64 # define C4CORE_HAVE_STD_TOCHARS 1
65 # define C4CORE_HAVE_STD_FROMCHARS 1
67 # define C4CORE_HAVE_STD_TOCHARS 0
68 # define C4CORE_HAVE_STD_FROMCHARS 0
71 # if __has_include(<charconv>)
73 # if defined(__cpp_lib_to_chars)
74 # define C4CORE_HAVE_STD_TOCHARS 1
75 # define C4CORE_HAVE_STD_FROMCHARS 1
77 # define C4CORE_HAVE_STD_TOCHARS 0
78 # define C4CORE_HAVE_STD_FROMCHARS 0
81 # define C4CORE_HAVE_STD_TOCHARS 0
82 # define C4CORE_HAVE_STD_FROMCHARS 0
86 # define C4CORE_HAVE_STD_TOCHARS 0
87 # define C4CORE_HAVE_STD_FROMCHARS 0
88 # define C4CORE_HAVE_FAST_FLOAT 0
92 #if !C4CORE_HAVE_STD_FROMCHARS
97 #if defined(_MSC_VER) && !defined(__clang__)
98 # pragma warning(push)
99 # pragma warning(disable: 4996)
100 # if C4_MSVC_VERSION != C4_MSVC_VERSION_2017
101 # pragma warning(disable: 4800)
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"
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"
113 # pragma GCC diagnostic ignored "-Wuseless-cast"
114 # pragma GCC diagnostic ignored "-Wold-style-cast"
117 #if defined(__clang__)
118 #define C4_NO_UBSAN_IOVRFLW __attribute__((no_sanitize("signed-integer-overflow")))
119 #elif defined(__GNUC__)
121 #define C4_NO_UBSAN_IOVRFLW __attribute__((no_sanitize("signed-integer-overflow")))
123 #define C4_NO_UBSAN_IOVRFLW
126 #define C4_NO_UBSAN_IOVRFLW
178 #if C4CORE_HAVE_STD_TOCHARS
180 typedef enum : std::underlying_type<std::chars_format>::type {
182 FTOA_FLOAT =
static_cast<std::underlying_type<std::chars_format>::type
>(std::chars_format::fixed),
184 FTOA_SCIENT =
static_cast<std::underlying_type<std::chars_format>::type
>(std::chars_format::scientific),
186 FTOA_FLEX =
static_cast<std::underlying_type<std::chars_format>::type
>(std::chars_format::general),
192 typedef enum :
char {
209 struct is_fixed_length
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)),
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)),
225 value = value_i || value_u
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"
243 # pragma GCC diagnostic ignored "-Wnull-dereference"
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>;
266 template<>
struct charconv_digits_<1u, true>
269 maxdigits_bin = 1 + 2 + 8,
270 maxdigits_oct = 1 + 2 + 3,
271 maxdigits_dec = 1 + 3,
272 maxdigits_hex = 1 + 2 + 2,
273 maxdigits_bin_nopfx = 8,
274 maxdigits_oct_nopfx = 3,
275 maxdigits_dec_nopfx = 3,
276 maxdigits_hex_nopfx = 2,
279 static constexpr csubstr min_value_dec() noexcept {
return csubstr(
"128"); }
280 static constexpr csubstr min_value_hex() noexcept {
return csubstr(
"80"); }
281 static constexpr csubstr min_value_oct() noexcept {
return csubstr(
"200"); }
282 static constexpr csubstr min_value_bin() noexcept {
return csubstr(
"10000000"); }
283 static constexpr csubstr max_value_dec() noexcept {
return csubstr(
"127"); }
284 static constexpr
bool is_oct_overflow(csubstr str) noexcept {
return !((str.len < 3) || (str.len == 3 && str[0] <=
'1')); }
286 template<>
struct charconv_digits_<1u, false>
289 maxdigits_bin = 2 + 8,
290 maxdigits_oct = 2 + 3,
292 maxdigits_hex = 2 + 2,
293 maxdigits_bin_nopfx = 8,
294 maxdigits_oct_nopfx = 3,
295 maxdigits_dec_nopfx = 3,
296 maxdigits_hex_nopfx = 2,
298 static constexpr csubstr max_value_dec() noexcept {
return csubstr(
"255"); }
299 static constexpr
bool is_oct_overflow(csubstr str) noexcept {
return !((str.len < 3) || (str.len == 3 && str[0] <=
'3')); }
301 template<>
struct charconv_digits_<2u, true>
304 maxdigits_bin = 1 + 2 + 16,
305 maxdigits_oct = 1 + 2 + 6,
306 maxdigits_dec = 1 + 5,
307 maxdigits_hex = 1 + 2 + 4,
308 maxdigits_bin_nopfx = 16,
309 maxdigits_oct_nopfx = 6,
310 maxdigits_dec_nopfx = 5,
311 maxdigits_hex_nopfx = 4,
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)); }
321 template<>
struct charconv_digits_<2u, false>
324 maxdigits_bin = 2 + 16,
325 maxdigits_oct = 2 + 6,
327 maxdigits_hex = 2 + 4,
328 maxdigits_bin_nopfx = 16,
329 maxdigits_oct_nopfx = 6,
330 maxdigits_dec_nopfx = 6,
331 maxdigits_hex_nopfx = 4,
333 static constexpr csubstr max_value_dec() noexcept {
return csubstr(
"65535"); }
334 static constexpr
bool is_oct_overflow(csubstr str) noexcept {
return !((str.len < 6) || (str.len == 6 && str[0] <=
'1')); }
336 template<>
struct charconv_digits_<4u, true>
339 maxdigits_bin = 1 + 2 + 32,
340 maxdigits_oct = 1 + 2 + 11,
341 maxdigits_dec = 1 + 10,
342 maxdigits_hex = 1 + 2 + 8,
343 maxdigits_bin_nopfx = 32,
344 maxdigits_oct_nopfx = 11,
345 maxdigits_dec_nopfx = 10,
346 maxdigits_hex_nopfx = 8,
349 static constexpr csubstr min_value_dec() noexcept {
return csubstr(
"2147483648"); }
350 static constexpr csubstr min_value_hex() noexcept {
return csubstr(
"80000000"); }
351 static constexpr csubstr min_value_oct() noexcept {
return csubstr(
"20000000000"); }
352 static constexpr csubstr min_value_bin() noexcept {
return csubstr(
"10000000000000000000000000000000"); }
353 static constexpr csubstr max_value_dec() noexcept {
return csubstr(
"2147483647"); }
354 static constexpr
bool is_oct_overflow(csubstr str) noexcept {
return !((str.len < 11) || (str.len == 11 && str[0] <=
'1')); }
356 template<>
struct charconv_digits_<4u, false>
359 maxdigits_bin = 2 + 32,
360 maxdigits_oct = 2 + 11,
362 maxdigits_hex = 2 + 8,
363 maxdigits_bin_nopfx = 32,
364 maxdigits_oct_nopfx = 11,
365 maxdigits_dec_nopfx = 10,
366 maxdigits_hex_nopfx = 8,
368 static constexpr csubstr max_value_dec() noexcept {
return csubstr(
"4294967295"); }
369 static constexpr
bool is_oct_overflow(csubstr str) noexcept {
return !((str.len < 11) || (str.len == 11 && str[0] <=
'3')); }
371 template<>
struct charconv_digits_<8u, true>
374 maxdigits_bin = 1 + 2 + 64,
375 maxdigits_oct = 1 + 2 + 22,
376 maxdigits_dec = 1 + 19,
377 maxdigits_hex = 1 + 2 + 16,
378 maxdigits_bin_nopfx = 64,
379 maxdigits_oct_nopfx = 22,
380 maxdigits_dec_nopfx = 19,
381 maxdigits_hex_nopfx = 16,
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)); }
390 template<>
struct charconv_digits_<8u, false>
393 maxdigits_bin = 2 + 64,
394 maxdigits_oct = 2 + 22,
396 maxdigits_hex = 2 + 16,
397 maxdigits_bin_nopfx = 64,
398 maxdigits_oct_nopfx = 22,
399 maxdigits_dec_nopfx = 20,
400 maxdigits_hex_nopfx = 16,
402 static constexpr csubstr max_value_dec() noexcept {
return csubstr(
"18446744073709551615"); }
403 static constexpr
bool is_oct_overflow(csubstr str) noexcept {
return !((str.len < 22) || (str.len == 22 && str[0] <=
'1')); }
408 #define _c4append(c) { if(C4_LIKELY(pos < buf.len)) { buf.str[pos++] = static_cast<char>(c); } else { ++pos; } }
409 #define _c4appendhex(i) { if(C4_LIKELY(pos < buf.len)) { buf.str[pos++] = hexchars[i]; } else { ++pos; } }
432 C4_CONSTEXPR14 C4_ALWAYS_INLINE
434 ->
typename std::enable_if<
sizeof(T) == 1u,
unsigned>::type
436 C4_STATIC_ASSERT(std::is_integral<T>::value);
438 return ((v >= 100) ? 3u : ((v >= 10) ? 2u : 1u));
443 C4_CONSTEXPR14 C4_ALWAYS_INLINE
445 ->
typename std::enable_if<
sizeof(T) == 2u,
unsigned>::type
447 C4_STATIC_ASSERT(std::is_integral<T>::value);
449 return ((v >= 10000) ? 5u : (v >= 1000) ? 4u : (v >= 100) ? 3u : (v >= 10) ? 2u : 1u);
454 C4_CONSTEXPR14 C4_ALWAYS_INLINE
456 ->
typename std::enable_if<
sizeof(T) == 4u,
unsigned>::type
458 C4_STATIC_ASSERT(std::is_integral<T>::value);
460 return ((v >= 1000000000) ? 10u : (v >= 100000000) ? 9u : (v >= 10000000) ? 8u :
461 (v >= 1000000) ? 7u : (v >= 100000) ? 6u : (v >= 10000) ? 5u :
462 (v >= 1000) ? 4u : (v >= 100) ? 3u : (v >= 10) ? 2u : 1u);
467 C4_CONSTEXPR14 C4_ALWAYS_INLINE
469 ->
typename std::enable_if<
sizeof(T) == 8u,
unsigned>::type
473 C4_STATIC_ASSERT(std::is_integral<T>::value);
477 if(v >= 100000000000000)
479 if(v >= 100000000000000000)
481 if((
typename std::make_unsigned<T>::type)v >= 10000000000000000000u)
484 return (v >= 1000000000000000000) ? 19u : 18u;
486 else if(v >= 10000000000000000)
489 return(v >= 1000000000000000) ? 16u : 15u;
491 else if(v >= 1000000000000)
492 return (v >= 10000000000000) ? 14u : 13u;
493 else if(v >= 100000000000)
496 return(v >= 10000000000) ? 11u : 10u;
501 return (v >= 100000000) ? 9u : 8u;
502 else if(v >= 1000000)
505 return (v >= 100000) ? 6u : 5u;
508 return (v >= 1000) ? 4u : 3u;
510 return (v >= 10) ? 2u : 1u;
516 C4_CONSTEXPR14 C4_ALWAYS_INLINE
unsigned digits_hex(T v) noexcept
518 C4_STATIC_ASSERT(std::is_integral<T>::value);
520 return v ? 1u + (msb((
typename std::make_unsigned<T>::type)v) >> 2u) : 1u;
525 C4_CONSTEXPR14 C4_ALWAYS_INLINE
unsigned digits_bin(T v) noexcept
527 C4_STATIC_ASSERT(std::is_integral<T>::value);
529 return v ? 1u + msb((
typename std::make_unsigned<T>::type)v) : 1u;
534 C4_CONSTEXPR14 C4_ALWAYS_INLINE
unsigned digits_oct(T v_) noexcept
537 C4_STATIC_ASSERT(std::is_integral<T>::value);
539 using U =
typename std::conditional<
sizeof(T) <=
sizeof(
unsigned),
541 typename std::make_unsigned<T>::type>::type;
547 __b4 = 64u * 8u * 8u,
573 C4_INLINE_CONSTEXPR
const char hexchars[] =
"0123456789abcdef";
574 C4_INLINE_CONSTEXPR
const char digits0099[] =
575 "0001020304050607080910111213141516171819"
576 "2021222324252627282930313233343536373839"
577 "4041424344454647484950515253545556575859"
578 "6061626364656667686970717273747576777879"
579 "8081828384858687888990919293949596979899";
583 C4_SUPPRESS_WARNING_GCC_PUSH
584 C4_SUPPRESS_WARNING_GCC(
"-Warray-bounds")
585 #if (defined(__GNUC__) && (__GNUC__ >= 7))
586 C4_SUPPRESS_WARNING_GCC(
"-Wstringop-overflow")
600 C4_HOT C4_ALWAYS_INLINE
603 C4_STATIC_ASSERT(std::is_integral<T>::value);
605 C4_ASSERT(buf.len >= digits_v);
612 const auto num = (v - quo * T(100)) << 1u;
614 buf.str[--digits_v] = detail::digits0099[num + 1];
615 buf.str[--digits_v] = detail::digits0099[num];
619 C4_ASSERT(digits_v == 2);
620 const auto num = v << 1u;
621 buf.str[1] = detail::digits0099[num + 1];
622 buf.str[0] = detail::digits0099[num];
626 C4_ASSERT(digits_v == 1);
627 buf.str[0] = (char)(
'0' + v);
633 C4_HOT C4_ALWAYS_INLINE
636 C4_STATIC_ASSERT(std::is_integral<T>::value);
638 C4_ASSERT(buf.len >= digits_v);
641 buf.str[--digits_v] = detail::hexchars[v & T(15)];
644 C4_ASSERT(digits_v == 0);
649 C4_HOT C4_ALWAYS_INLINE
652 C4_STATIC_ASSERT(std::is_integral<T>::value);
654 C4_ASSERT(buf.len >= digits_v);
657 buf.str[--digits_v] = (char)(
'0' + (v & T(7)));
660 C4_ASSERT(digits_v == 0);
665 C4_HOT C4_ALWAYS_INLINE
668 C4_STATIC_ASSERT(std::is_integral<T>::value);
670 C4_ASSERT(buf.len >= digits_v);
673 buf.str[--digits_v] = (char)(
'0' + (v & T(1)));
676 C4_ASSERT(digits_v == 0);
701 C4_ALWAYS_INLINE
size_t write_dec(substr buf, T v) noexcept
703 C4_STATIC_ASSERT(std::is_integral<T>::value);
706 if(C4_LIKELY(buf.len >= digits))
720 C4_ALWAYS_INLINE
size_t write_hex(substr buf, T v) noexcept
722 C4_STATIC_ASSERT(std::is_integral<T>::value);
725 if(C4_LIKELY(buf.len >= digits))
739 C4_ALWAYS_INLINE
size_t write_oct(substr buf, T v) noexcept
741 C4_STATIC_ASSERT(std::is_integral<T>::value);
744 if(C4_LIKELY(buf.len >= digits))
758 C4_ALWAYS_INLINE
size_t write_bin(substr buf, T v) noexcept
760 C4_STATIC_ASSERT(std::is_integral<T>::value);
763 C4_ASSERT(digits > 0);
764 if(C4_LIKELY(buf.len >= digits))
772 template<
class U>
using NumberWriter = size_t (*)(substr, U);
773 template<
class T, NumberWriter<T> writer>
774 size_t write_num_digits(substr buf, T v,
size_t num_digits) noexcept
776 C4_STATIC_ASSERT(std::is_integral<T>::value);
777 const size_t ret = writer(buf, v);
778 if(ret >= num_digits)
780 else if(ret >= buf.len || num_digits > buf.len)
782 C4_ASSERT(num_digits >= ret);
783 const size_t delta =
static_cast<size_t>(num_digits - ret);
784 C4_ASSERT(ret + delta <= buf.len);
786 memmove(buf.str + delta, buf.str, ret);
788 memset(buf.str,
'0', delta);
799 C4_ALWAYS_INLINE
size_t write_dec(substr buf, T val,
size_t num_digits) noexcept
801 return detail::write_num_digits<T, &write_dec<T>>(buf, val, num_digits);
808 C4_ALWAYS_INLINE
size_t write_hex(substr buf, T val,
size_t num_digits) noexcept
810 return detail::write_num_digits<T, &write_hex<T>>(buf, val, num_digits);
817 C4_ALWAYS_INLINE
size_t write_bin(substr buf, T val,
size_t num_digits) noexcept
819 return detail::write_num_digits<T, &write_bin<T>>(buf, val, num_digits);
826 C4_ALWAYS_INLINE
size_t write_oct(substr buf, T val,
size_t num_digits) noexcept
828 return detail::write_num_digits<T, &write_oct<T>>(buf, val, num_digits);
833 C4_SUPPRESS_WARNING_GCC_POP
841 C4_SUPPRESS_WARNING_MSVC_PUSH
842 C4_SUPPRESS_WARNING_MSVC(4365)
861 C4_ALWAYS_INLINE
bool read_dec(csubstr s, I *C4_RESTRICT v) noexcept
863 C4_STATIC_ASSERT(std::is_integral<I>::value);
864 C4_ASSERT(!s.empty());
868 if(C4_UNLIKELY(c < '0' || c >
'9'))
870 *v = ((*v) * I(10)) + (I(c) - I(
'0'));
889 C4_ALWAYS_INLINE
bool read_hex(csubstr s, I *C4_RESTRICT v) noexcept
891 C4_STATIC_ASSERT(std::is_integral<I>::value);
892 C4_ASSERT(!s.empty());
897 if(c >=
'0' && c <=
'9')
899 else if(c >=
'a' && c <=
'f')
900 cv = I(10) + (I(c) - I(
'a'));
901 else if(c >=
'A' && c <=
'F')
902 cv = I(10) + (I(c) - I(
'A'));
905 *v = ((*v) * I(16)) + cv;
924 C4_ALWAYS_INLINE
bool read_bin(csubstr s, I *C4_RESTRICT v) noexcept
926 C4_STATIC_ASSERT(std::is_integral<I>::value);
927 C4_ASSERT(!s.empty());
954 C4_ALWAYS_INLINE
bool read_oct(csubstr s, I *C4_RESTRICT v) noexcept
956 C4_STATIC_ASSERT(std::is_integral<I>::value);
957 C4_ASSERT(!s.empty());
961 if(C4_UNLIKELY(c < '0' || c >
'7'))
963 *v = ((*v) * I(8)) + (I(c) - I(
'0'));
970 C4_SUPPRESS_WARNING_MSVC_POP
977 C4_SUPPRESS_WARNING_GCC_WITH_PUSH(
"-Wswitch-default")
981 inline size_t _itoa2buf(substr buf,
size_t pos, csubstr val) noexcept
983 C4_ASSERT(pos < buf.len);
984 C4_ASSERT(pos + val.len <= buf.len);
985 C4_ASSERT(val.len > 0);
986 memcpy(buf.str + pos, val.str, val.len);
987 return pos + val.len;
989 inline size_t _itoa2bufwithdigits(substr buf,
size_t pos,
size_t num_digits, csubstr val) noexcept
991 num_digits = num_digits > val.len ? num_digits - val.len : 0;
992 C4_ASSERT(num_digits + val.len <= buf.len);
993 for(
size_t i = 0; i < num_digits; ++i)
995 return detail::_itoa2buf(buf, pos, val);
998 C4_NO_INLINE
size_t _itoadec2buf(substr buf) noexcept
1000 using digits_type = detail::charconv_digits<I>;
1001 if(C4_UNLIKELY(buf.len < digits_type::maxdigits_dec))
1002 return digits_type::maxdigits_dec;
1004 return detail::_itoa2buf(buf, 1, digits_type::min_value_dec());
1007 C4_NO_INLINE
size_t _itoa2buf(substr buf, I radix) noexcept
1009 using digits_type = detail::charconv_digits<I>;
1011 if(C4_LIKELY(buf.len > 0))
1012 buf.str[pos++] =
'-';
1016 if(C4_UNLIKELY(buf.len < digits_type::maxdigits_dec))
1017 return digits_type::maxdigits_dec;
1018 pos =_itoa2buf(buf, pos, digits_type::min_value_dec());
1021 if(C4_UNLIKELY(buf.len < digits_type::maxdigits_hex))
1022 return digits_type::maxdigits_hex;
1023 buf.str[pos++] =
'0';
1024 buf.str[pos++] =
'x';
1025 pos = _itoa2buf(buf, pos, digits_type::min_value_hex());
1028 if(C4_UNLIKELY(buf.len < digits_type::maxdigits_bin))
1029 return digits_type::maxdigits_bin;
1030 buf.str[pos++] =
'0';
1031 buf.str[pos++] =
'b';
1032 pos = _itoa2buf(buf, pos, digits_type::min_value_bin());
1035 if(C4_UNLIKELY(buf.len < digits_type::maxdigits_oct))
1036 return digits_type::maxdigits_oct;
1037 buf.str[pos++] =
'0';
1038 buf.str[pos++] =
'o';
1039 pos = _itoa2buf(buf, pos, digits_type::min_value_oct());
1045 C4_NO_INLINE
size_t _itoa2buf(substr buf, I radix,
size_t num_digits) noexcept
1047 using digits_type = detail::charconv_digits<I>;
1049 size_t needed_digits = 0;
1050 if(C4_LIKELY(buf.len > 0))
1051 buf.str[pos++] =
'-';
1056 needed_digits = num_digits+1 > digits_type::maxdigits_dec ? num_digits+1 : digits_type::maxdigits_dec;
1057 if(C4_UNLIKELY(buf.len < needed_digits))
1058 return needed_digits;
1059 pos = _itoa2bufwithdigits(buf, pos, num_digits, digits_type::min_value_dec());
1063 needed_digits = num_digits+3 > digits_type::maxdigits_hex ? num_digits+3 : digits_type::maxdigits_hex;
1064 if(C4_UNLIKELY(buf.len < needed_digits))
1065 return needed_digits;
1066 buf.str[pos++] =
'0';
1067 buf.str[pos++] =
'x';
1068 pos = _itoa2bufwithdigits(buf, pos, num_digits, digits_type::min_value_hex());
1072 needed_digits = num_digits+3 > digits_type::maxdigits_bin ? num_digits+3 : digits_type::maxdigits_bin;
1073 if(C4_UNLIKELY(buf.len < needed_digits))
1074 return needed_digits;
1075 C4_ASSERT(buf.len >= digits_type::maxdigits_bin);
1076 buf.str[pos++] =
'0';
1077 buf.str[pos++] =
'b';
1078 pos = _itoa2bufwithdigits(buf, pos, num_digits, digits_type::min_value_bin());
1082 needed_digits = num_digits+3 > digits_type::maxdigits_oct ? num_digits+3 : digits_type::maxdigits_oct;
1083 if(C4_UNLIKELY(buf.len < needed_digits))
1084 return needed_digits;
1085 C4_ASSERT(buf.len >= digits_type::maxdigits_oct);
1086 buf.str[pos++] =
'0';
1087 buf.str[pos++] =
'o';
1088 pos = _itoa2bufwithdigits(buf, pos, num_digits, digits_type::min_value_oct());
1107 C4_ALWAYS_INLINE
size_t itoa(substr buf, T v) noexcept
1109 C4_STATIC_ASSERT(std::is_signed<T>::value);
1117 if(C4_LIKELY(v != std::numeric_limits<T>::min()))
1121 if(C4_LIKELY(buf.len >= digits + 1u))
1128 return detail::_itoadec2buf<T>(buf);
1139 C4_ALWAYS_INLINE
size_t itoa(substr buf, T v, T radix) noexcept
1141 C4_STATIC_ASSERT(std::is_signed<T>::value);
1142 C4_ASSERT(radix == 2 || radix == 8 || radix == 10 || radix == 16);
1143 C4_SUPPRESS_WARNING_GCC_PUSH
1144 #if (defined(__GNUC__) && (__GNUC__ >= 7))
1145 C4_SUPPRESS_WARNING_GCC(
"-Wstringop-overflow")
1149 if(C4_LIKELY(v != std::numeric_limits<T>::min()))
1155 if(C4_LIKELY(buf.len > 0))
1159 unsigned digits = 0;
1164 if(C4_LIKELY(buf.len >= pos + digits))
1169 if(C4_LIKELY(buf.len >= pos + 2u + digits))
1171 buf.str[pos + 0] =
'0';
1172 buf.str[pos + 1] =
'x';
1179 if(C4_LIKELY(buf.len >= pos + 2u + digits))
1181 buf.str[pos + 0] =
'0';
1182 buf.str[pos + 1] =
'b';
1189 if(C4_LIKELY(buf.len >= pos + 2u + digits))
1191 buf.str[pos + 0] =
'0';
1192 buf.str[pos + 1] =
'o';
1198 return pos + digits;
1200 C4_SUPPRESS_WARNING_GCC_POP
1203 return detail::_itoa2buf<T>(buf, radix);
1216 C4_ALWAYS_INLINE
size_t itoa(substr buf, T v, T radix,
size_t num_digits) noexcept
1218 C4_STATIC_ASSERT(std::is_signed<T>::value);
1219 C4_ASSERT(radix == 2 || radix == 8 || radix == 10 || radix == 16);
1220 C4_SUPPRESS_WARNING_GCC_PUSH
1221 #if (defined(__GNUC__) && (__GNUC__ >= 7))
1222 C4_SUPPRESS_WARNING_GCC(
"-Wstringop-overflow")
1226 if(C4_LIKELY(v != std::numeric_limits<T>::min()))
1232 if(C4_LIKELY(buf.len > 0))
1236 unsigned total_digits = 0;
1241 total_digits = pos + (unsigned)(num_digits > total_digits ? num_digits : total_digits);
1242 if(C4_LIKELY(buf.len >= total_digits))
1247 total_digits = pos + 2u + (unsigned)(num_digits > total_digits ? num_digits : total_digits);
1248 if(C4_LIKELY(buf.len >= total_digits))
1250 buf.str[pos + 0] =
'0';
1251 buf.str[pos + 1] =
'x';
1252 write_hex(buf.sub(pos + 2), v, num_digits);
1257 total_digits = pos + 2u + (unsigned)(num_digits > total_digits ? num_digits : total_digits);
1258 if(C4_LIKELY(buf.len >= total_digits))
1260 buf.str[pos + 0] =
'0';
1261 buf.str[pos + 1] =
'b';
1262 write_bin(buf.sub(pos + 2), v, num_digits);
1267 total_digits = pos + 2u + (unsigned)(num_digits > total_digits ? num_digits : total_digits);
1268 if(C4_LIKELY(buf.len >= total_digits))
1270 buf.str[pos + 0] =
'0';
1271 buf.str[pos + 1] =
'o';
1272 write_oct(buf.sub(pos + 2), v, num_digits);
1276 return total_digits;
1278 C4_SUPPRESS_WARNING_GCC_POP
1281 return detail::_itoa2buf<T>(buf, radix, num_digits);
1302 C4_ALWAYS_INLINE
size_t utoa(substr buf, T v) noexcept
1304 C4_STATIC_ASSERT(std::is_unsigned<T>::value);
1317 C4_ALWAYS_INLINE
size_t utoa(substr buf, T v, T radix) noexcept
1319 C4_STATIC_ASSERT(std::is_unsigned<T>::value);
1320 C4_ASSERT(radix == 10 || radix == 16 || radix == 2 || radix == 8);
1321 unsigned digits = 0;
1326 if(C4_LIKELY(buf.len >= digits))
1331 if(C4_LIKELY(buf.len >= digits+2u))
1341 if(C4_LIKELY(buf.len >= digits+2u))
1351 if(C4_LIKELY(buf.len >= digits+2u))
1372 C4_ALWAYS_INLINE
size_t utoa(substr buf, T v, T radix,
size_t num_digits) noexcept
1374 C4_STATIC_ASSERT(std::is_unsigned<T>::value);
1375 C4_ASSERT(radix == 10 || radix == 16 || radix == 2 || radix == 8);
1376 unsigned total_digits = 0;
1381 total_digits = (unsigned)(num_digits > total_digits ? num_digits : total_digits);
1382 if(C4_LIKELY(buf.len >= total_digits))
1387 total_digits = 2u + (unsigned)(num_digits > total_digits ? num_digits : total_digits);
1388 if(C4_LIKELY(buf.len >= total_digits))
1397 total_digits = 2u + (unsigned)(num_digits > total_digits ? num_digits : total_digits);
1398 if(C4_LIKELY(buf.len >= total_digits))
1407 total_digits = 2u + (unsigned)(num_digits > total_digits ? num_digits : total_digits);
1408 if(C4_LIKELY(buf.len >= total_digits))
1416 return total_digits;
1418 C4_SUPPRESS_WARNING_GCC_POP
1453 C4_ALWAYS_INLINE
bool atoi(csubstr str, T * C4_RESTRICT v) noexcept
1455 C4_STATIC_ASSERT(std::is_integral<T>::value);
1456 C4_STATIC_ASSERT(std::is_signed<T>::value);
1458 if(C4_UNLIKELY(str.len == 0))
1461 C4_ASSERT(str.str[0] !=
'+');
1465 if(str.str[0] ==
'-')
1467 if(C4_UNLIKELY(str.len == ++start))
1472 bool parsed_ok =
true;
1473 if(str.str[start] !=
'0')
1475 parsed_ok =
read_dec(str.sub(start), v);
1477 else if(str.len > start + 1)
1480 const char pfx = str.str[start + 1];
1481 if(pfx ==
'x' || pfx ==
'X')
1482 parsed_ok = str.len > start + 2 &&
read_hex(str.sub(start + 2), v);
1483 else if(pfx ==
'b' || pfx ==
'B')
1484 parsed_ok = str.len > start + 2 &&
read_bin(str.sub(start + 2), v);
1485 else if(pfx ==
'o' || pfx ==
'O')
1486 parsed_ok = str.len > start + 2 &&
read_oct(str.sub(start + 2), v);
1488 parsed_ok =
read_dec(str.sub(start + 1), v);
1492 parsed_ok =
read_dec(str.sub(start), v);
1494 if(C4_LIKELY(parsed_ok))
1507 C4_ALWAYS_INLINE
size_t atoi_first(csubstr str, T * C4_RESTRICT v)
1509 csubstr trimmed = str.first_int_span();
1510 if(trimmed.len == 0)
1512 if(
atoi(trimmed, v))
1513 return static_cast<size_t>(trimmed.end() - str.begin());
1546 bool atou(csubstr str, T * C4_RESTRICT v) noexcept
1548 C4_STATIC_ASSERT(std::is_integral<T>::value);
1550 if(C4_UNLIKELY(str.len == 0 || str.front() ==
'-'))
1553 bool parsed_ok =
true;
1554 if(str.str[0] !=
'0')
1562 const char pfx = str.str[1];
1563 if(pfx ==
'x' || pfx ==
'X')
1564 parsed_ok = str.len > 2 &&
read_hex(str.sub(2), v);
1565 else if(pfx ==
'b' || pfx ==
'B')
1566 parsed_ok = str.len > 2 &&
read_bin(str.sub(2), v);
1567 else if(pfx ==
'o' || pfx ==
'O')
1568 parsed_ok = str.len > 2 &&
read_oct(str.sub(2), v);
1590 csubstr trimmed = str.first_uint_span();
1591 if(trimmed.len == 0)
1593 if(
atou(trimmed, v))
1594 return static_cast<size_t>(trimmed.end() - str.begin());
1601 #if defined(_MSC_VER) && !defined(__clang__)
1602 # pragma warning(pop)
1603 #elif defined(__clang__)
1604 # pragma clang diagnostic pop
1605 #elif defined(__GNUC__)
1606 # pragma GCC diagnostic pop
1616 inline bool check_overflow(csubstr str, csubstr limit) noexcept
1618 if(str.len != limit.len)
1619 return str.len > limit.len;
1620 for(
size_t i = 0; i < limit.len; ++i)
1622 if(str[i] < limit[i])
1624 else if(str[i] > limit[i])
1645 ->
typename std::enable_if<std::is_unsigned<T>::value,
bool>::type
1647 C4_STATIC_ASSERT(std::is_integral<T>::value);
1649 if(C4_UNLIKELY(str.len == 0))
1653 else if(str.str[0] ==
'0')
1662 size_t fno = str.first_not_of(
'0', 2);
1665 return !(str.len <= fno + (
sizeof(T) * 2));
1670 size_t fno = str.first_not_of(
'0', 2);
1673 return !(str.len <= fno +(
sizeof(T) * 8));
1678 size_t fno = str.first_not_of(
'0', 2);
1681 return detail::charconv_digits<T>::is_oct_overflow(str.sub(fno));
1685 size_t fno = str.first_not_of(
'0', 1);
1688 return detail::check_overflow(str.sub(fno), detail::charconv_digits<T>::max_value_dec());
1692 else if(C4_UNLIKELY(str[0] ==
'-'))
1698 return detail::check_overflow(str, detail::charconv_digits<T>::max_value_dec());
1712 ->
typename std::enable_if<std::is_signed<T>::value,
bool>::type
1714 C4_STATIC_ASSERT(std::is_integral<T>::value);
1715 if(C4_UNLIKELY(str.len == 0))
1717 if(str.str[0] ==
'-')
1719 if(str.str[1] ==
'0')
1728 size_t fno = str.first_not_of(
'0', 3);
1731 return detail::check_overflow(str.sub(fno), detail::charconv_digits<T>::min_value_hex());
1736 size_t fno = str.first_not_of(
'0', 3);
1739 return detail::check_overflow(str.sub(fno), detail::charconv_digits<T>::min_value_bin());
1744 size_t fno = str.first_not_of(
'0', 3);
1747 return detail::check_overflow(str.sub(fno), detail::charconv_digits<T>::min_value_oct());
1751 size_t fno = str.first_not_of(
'0', 2);
1754 return detail::check_overflow(str.sub(fno), detail::charconv_digits<T>::min_value_dec());
1760 return detail::check_overflow(str.sub(1), detail::charconv_digits<T>::min_value_dec());
1763 else if(str.str[0] ==
'0')
1772 size_t fno = str.first_not_of(
'0', 2);
1775 const size_t len = str.len - fno;
1776 return !((len <
sizeof (T) * 2) || (len ==
sizeof(T) * 2 && str[fno] <=
'7'));
1781 size_t fno = str.first_not_of(
'0', 2);
1784 return !(str.len <= fno + (
sizeof(T) * 8 - 1));
1789 size_t fno = str.first_not_of(
'0', 2);
1792 return detail::charconv_digits<T>::is_oct_overflow(str.sub(fno));
1796 size_t fno = str.first_not_of(
'0', 1);
1799 return detail::check_overflow(str.sub(fno), detail::charconv_digits<T>::max_value_dec());
1805 return detail::check_overflow(str, detail::charconv_digits<T>::max_value_dec());
1820 #if (!C4CORE_HAVE_STD_FROMCHARS)
1823 void get_real_format_str(
char (& C4_RESTRICT fmt)[N],
int precision,
RealFormat_e formatting,
const char* length_modifier=
"")
1827 iret = snprintf(fmt,
sizeof(fmt),
"%%%s%c", length_modifier, formatting);
1828 else if(precision == 0)
1829 iret = snprintf(fmt,
sizeof(fmt),
"%%.%s%c", length_modifier, formatting);
1831 iret = snprintf(fmt,
sizeof(fmt),
"%%.%d%s%c", precision, length_modifier, formatting);
1832 C4_ASSERT(iret >= 2 &&
size_t(iret) <
sizeof(fmt));
1850 size_t print_one(substr str,
const char* full_fmt, T v)
1856 int iret = _snprintf(str.str, str.len, full_fmt, v);
1862 iret = snprintf(
nullptr, 0, full_fmt, v);
1863 C4_ASSERT(iret > 0);
1865 size_t ret = (size_t) iret;
1868 int iret = snprintf(str.str, str.len, full_fmt, v);
1869 C4_ASSERT(iret >= 0);
1870 size_t ret = (size_t) iret;
1879 #if (!C4CORE_HAVE_STD_FROMCHARS) && (!C4CORE_HAVE_FAST_FLOAT)
1884 template<
typename T>
1885 inline size_t scan_one(csubstr str,
const char *type_fmt, T *v)
1904 int iret = std::snprintf(fmt,
sizeof(fmt),
"%%" "%zu" "%s" "%%n", str.len, type_fmt);
1906 C4_ASSERT(iret >= 0 &&
size_t(iret) < C4_COUNTOF(fmt));
1910 iret = std::sscanf(str.str, fmt, v, &num_chars);
1913 C4_ASSERT(num_chars >= 0);
1914 return (
size_t)(num_chars);
1919 #if C4CORE_HAVE_STD_TOCHARS
1921 C4_ALWAYS_INLINE
size_t rtoa(substr buf, T v,
int precision=-1,
RealFormat_e formatting=
FTOA_FLEX) noexcept
1923 std::to_chars_result result;
1927 if(buf.len >
size_t(2))
1935 result =
std::to_chars(buf.str + pos, buf.str + buf.len, v, (std::chars_format)formatting);
1937 result =
std::to_chars(buf.str + pos, buf.str + buf.len, v, (std::chars_format)formatting, precision);
1938 if(result.ec == std::errc())
1941 C4_ASSERT(result.ptr >= buf.str);
1942 ptrdiff_t delta = result.ptr - buf.str;
1943 return static_cast<size_t>(delta);
1945 C4_ASSERT(result.ec == std::errc::value_too_large);
1956 size_t ret =
static_cast<size_t>(std::numeric_limits<T>::max_digits10);
1957 return ret > buf.len ? ret : buf.len + 1;
1962 #if C4CORE_HAVE_FAST_FLOAT
1964 C4_ALWAYS_INLINE
bool scan_rhex(csubstr s, T *C4_RESTRICT val) noexcept
1966 C4_ASSERT(s.len > 0);
1967 C4_ASSERT(s.str[0] !=
'-');
1968 C4_ASSERT(s.str[0] !=
'+');
1969 C4_ASSERT(!s.begins_with(
"0x"));
1970 C4_ASSERT(!s.begins_with(
"0X"));
1973 for( ; pos < s.len; ++pos)
1975 const char c = s.str[pos];
1976 if(c >=
'0' && c <=
'9')
1977 *val = (*val * T(16)) + T(c -
'0');
1978 else if(c >=
'a' && c <=
'f')
1979 *val = (*val * T(16)) + T(c -
'a');
1980 else if(c >=
'A' && c <=
'F')
1981 *val = (*val * T(16)) + T(c -
'A');
1987 else if(c ==
'p' || c ==
'P')
2000 for(T digit = T(0.0625); pos < s.len; ++pos, digit /= T(16))
2002 const char c = s.str[pos];
2003 if(c >=
'0' && c <=
'9')
2004 *val += digit * T(c -
'0');
2005 else if(c >=
'a' && c <=
'f')
2006 *val += digit * T(c -
'a');
2007 else if(c >=
'A' && c <=
'F')
2008 *val += digit * T(c -
'A');
2009 else if(c ==
'p' || c ==
'P')
2022 if(C4_LIKELY(pos < s.len))
2024 if(s.str[pos] ==
'+')
2026 if(C4_LIKELY(pos < s.len))
2028 int16_t powval = {};
2029 if(C4_LIKELY(
atoi(s.sub(pos), &powval)))
2031 *val *= ipow<T, int16_t, 16>(powval);
2062 #if C4CORE_HAVE_STD_TOCHARS
2063 return detail::rtoa(str, v, precision, formatting);
2066 detail::get_real_format_str(fmt, precision, formatting,
"");
2067 return detail::print_one(str, fmt, v);
2088 #if C4CORE_HAVE_STD_TOCHARS
2089 return detail::rtoa(str, v, precision, formatting);
2092 detail::get_real_format_str(fmt, precision, formatting,
"l");
2093 return detail::print_one(str, fmt, v);
2110 C4_ALWAYS_INLINE
bool atof(csubstr str,
float * C4_RESTRICT v) noexcept
2112 C4_ASSERT(str.len > 0);
2113 C4_ASSERT(str.triml(
" \r\t\n").len == str.len);
2114 #if C4CORE_HAVE_FAST_FLOAT
2116 bool isneg = (str.str[0] ==
'-');
2117 csubstr rem = str.sub(isneg || str.str[0] ==
'+');
2118 if(!(rem.len >= 2 && (rem.str[0] ==
'0' && (rem.str[1] ==
'x' || rem.str[1] ==
'X'))))
2120 fast_float::from_chars_result result;
2122 return result.ec == std::errc();
2124 else if(detail::scan_rhex(rem.sub(2), v))
2126 *v *= isneg ? -1.f : 1.f;
2130 #elif C4CORE_HAVE_STD_FROMCHARS
2131 std::from_chars_result result;
2133 return result.ec == std::errc();
2135 csubstr rem = str.sub(str.str[0] ==
'-' || str.str[0] ==
'+');
2136 if(!(rem.len >= 2 && (rem.str[0] ==
'0' && (rem.str[1] ==
'x' || rem.str[1] ==
'X'))))
2148 inline size_t atof_first(csubstr str,
float * C4_RESTRICT v) noexcept
2150 csubstr trimmed = str.first_real_span();
2151 if(trimmed.len == 0)
2153 if(
atof(trimmed, v))
2154 return static_cast<size_t>(trimmed.end() - str.begin());
2171 C4_ALWAYS_INLINE
bool atod(csubstr str,
double * C4_RESTRICT v) noexcept
2173 C4_ASSERT(str.len > 0);
2174 C4_ASSERT(str.triml(
" \r\t\n").len == str.len);
2175 #if C4CORE_HAVE_FAST_FLOAT
2177 bool isneg = (str.str[0] ==
'-');
2178 csubstr rem = str.sub(isneg || str.str[0] ==
'+');
2179 if(!(rem.len >= 2 && (rem.str[0] ==
'0' && (rem.str[1] ==
'x' || rem.str[1] ==
'X'))))
2181 fast_float::from_chars_result result;
2187 return result.ec == std::errc();
2189 else if(detail::scan_rhex(rem.sub(2), v))
2191 *v *= isneg ? -1. : 1.;
2195 #elif C4CORE_HAVE_STD_FROMCHARS
2196 std::from_chars_result result;
2198 return result.ec == std::errc();
2200 csubstr rem = str.sub(str.str[0] ==
'-' || str.str[0] ==
'+');
2201 if(!(rem.len >= 2 && (rem.str[0] ==
'0' && (rem.str[1] ==
'x' || rem.str[1] ==
'X'))))
2213 inline size_t atod_first(csubstr str,
double * C4_RESTRICT v) noexcept
2215 csubstr trimmed = str.first_real_span();
2216 if(trimmed.len == 0)
2218 if(
atod(trimmed, v))
2219 return static_cast<size_t>(trimmed.end() - str.begin());
2234 #define _C4_IF_NOT_FIXED_LENGTH_I(T, ty) typename std::enable_if<std:: is_signed<T>::value && !is_fixed_length<T>::value_i, ty>
2235 #define _C4_IF_NOT_FIXED_LENGTH_U(T, ty) typename std::enable_if<std::is_unsigned<T>::value && !is_fixed_length<T>::value_u, ty>
2245 C4_ALWAYS_INLINE
size_t xtoa(substr s, uint8_t v) noexcept {
return write_dec(s, v); }
2246 C4_ALWAYS_INLINE
size_t xtoa(substr s, uint16_t v) noexcept {
return write_dec(s, v); }
2247 C4_ALWAYS_INLINE
size_t xtoa(substr s, uint32_t v) noexcept {
return write_dec(s, v); }
2248 C4_ALWAYS_INLINE
size_t xtoa(substr s, uint64_t v) noexcept {
return write_dec(s, v); }
2249 C4_ALWAYS_INLINE
size_t xtoa(substr s, int8_t v) noexcept {
return itoa(s, v); }
2250 C4_ALWAYS_INLINE
size_t xtoa(substr s, int16_t v) noexcept {
return itoa(s, v); }
2251 C4_ALWAYS_INLINE
size_t xtoa(substr s, int32_t v) noexcept {
return itoa(s, v); }
2252 C4_ALWAYS_INLINE
size_t xtoa(substr s, int64_t v) noexcept {
return itoa(s, v); }
2253 C4_ALWAYS_INLINE
size_t xtoa(substr s,
float v) noexcept {
return ftoa(s, v); }
2254 C4_ALWAYS_INLINE
size_t xtoa(substr s,
double v) noexcept {
return dtoa(s, v); }
2256 C4_ALWAYS_INLINE
size_t xtoa(substr s, uint8_t v, uint8_t radix) noexcept {
return utoa(s, v, radix); }
2257 C4_ALWAYS_INLINE
size_t xtoa(substr s, uint16_t v, uint16_t radix) noexcept {
return utoa(s, v, radix); }
2258 C4_ALWAYS_INLINE
size_t xtoa(substr s, uint32_t v, uint32_t radix) noexcept {
return utoa(s, v, radix); }
2259 C4_ALWAYS_INLINE
size_t xtoa(substr s, uint64_t v, uint64_t radix) noexcept {
return utoa(s, v, radix); }
2260 C4_ALWAYS_INLINE
size_t xtoa(substr s, int8_t v, int8_t radix) noexcept {
return itoa(s, v, radix); }
2261 C4_ALWAYS_INLINE
size_t xtoa(substr s, int16_t v, int16_t radix) noexcept {
return itoa(s, v, radix); }
2262 C4_ALWAYS_INLINE
size_t xtoa(substr s, int32_t v, int32_t radix) noexcept {
return itoa(s, v, radix); }
2263 C4_ALWAYS_INLINE
size_t xtoa(substr s, int64_t v, int64_t radix) noexcept {
return itoa(s, v, radix); }
2265 C4_ALWAYS_INLINE
size_t xtoa(substr s, uint8_t v, uint8_t radix,
size_t num_digits) noexcept {
return utoa(s, v, radix, num_digits); }
2266 C4_ALWAYS_INLINE
size_t xtoa(substr s, uint16_t v, uint16_t radix,
size_t num_digits) noexcept {
return utoa(s, v, radix, num_digits); }
2267 C4_ALWAYS_INLINE
size_t xtoa(substr s, uint32_t v, uint32_t radix,
size_t num_digits) noexcept {
return utoa(s, v, radix, num_digits); }
2268 C4_ALWAYS_INLINE
size_t xtoa(substr s, uint64_t v, uint64_t radix,
size_t num_digits) noexcept {
return utoa(s, v, radix, num_digits); }
2269 C4_ALWAYS_INLINE
size_t xtoa(substr s, int8_t v, int8_t radix,
size_t num_digits) noexcept {
return itoa(s, v, radix, num_digits); }
2270 C4_ALWAYS_INLINE
size_t xtoa(substr s, int16_t v, int16_t radix,
size_t num_digits) noexcept {
return itoa(s, v, radix, num_digits); }
2271 C4_ALWAYS_INLINE
size_t xtoa(substr s, int32_t v, int32_t radix,
size_t num_digits) noexcept {
return itoa(s, v, radix, num_digits); }
2272 C4_ALWAYS_INLINE
size_t xtoa(substr s, int64_t v, int64_t radix,
size_t num_digits) noexcept {
return itoa(s, v, radix, num_digits); }
2277 template <
class T> C4_ALWAYS_INLINE
auto xtoa(substr buf, T v) noexcept -> _C4_IF_NOT_FIXED_LENGTH_I(T,
size_t)::type { return
itoa(buf, v); }
2278 template <
class T> C4_ALWAYS_INLINE
auto xtoa(substr buf, T v) noexcept -> _C4_IF_NOT_FIXED_LENGTH_U(T,
size_t)::type { return
write_dec(buf, v); }
2280 C4_ALWAYS_INLINE
size_t xtoa(substr s, T *v) noexcept {
return itoa(s, (intptr_t)v, (intptr_t)16); }
2291 C4_ALWAYS_INLINE
bool atox(csubstr s, uint8_t *C4_RESTRICT v) noexcept {
return atou(s, v); }
2292 C4_ALWAYS_INLINE
bool atox(csubstr s, uint16_t *C4_RESTRICT v) noexcept {
return atou(s, v); }
2293 C4_ALWAYS_INLINE
bool atox(csubstr s, uint32_t *C4_RESTRICT v) noexcept {
return atou(s, v); }
2294 C4_ALWAYS_INLINE
bool atox(csubstr s, uint64_t *C4_RESTRICT v) noexcept {
return atou(s, v); }
2295 C4_ALWAYS_INLINE
bool atox(csubstr s, int8_t *C4_RESTRICT v) noexcept {
return atoi(s, v); }
2296 C4_ALWAYS_INLINE
bool atox(csubstr s, int16_t *C4_RESTRICT v) noexcept {
return atoi(s, v); }
2297 C4_ALWAYS_INLINE
bool atox(csubstr s, int32_t *C4_RESTRICT v) noexcept {
return atoi(s, v); }
2298 C4_ALWAYS_INLINE
bool atox(csubstr s, int64_t *C4_RESTRICT v) noexcept {
return atoi(s, v); }
2299 C4_ALWAYS_INLINE
bool atox(csubstr s,
float *C4_RESTRICT v) noexcept {
return atof(s, v); }
2300 C4_ALWAYS_INLINE
bool atox(csubstr s,
double *C4_RESTRICT v) noexcept {
return atod(s, v); }
2302 template <
class T> C4_ALWAYS_INLINE
auto atox(csubstr buf, T *C4_RESTRICT v) noexcept -> _C4_IF_NOT_FIXED_LENGTH_I(T,
bool)::type { return
atoi(buf, v); }
2303 template <
class T> C4_ALWAYS_INLINE
auto atox(csubstr buf, T *C4_RESTRICT v) noexcept -> _C4_IF_NOT_FIXED_LENGTH_U(T,
bool)::type { return
atou(buf, v); }
2305 C4_ALWAYS_INLINE
bool atox(csubstr s, T **v) noexcept { intptr_t tmp;
bool ret =
atox(s, &tmp);
if(ret) { *v = (T*)tmp; }
return ret; }
2335 C4_ALWAYS_INLINE
size_t to_chars(substr buf, int8_t v) noexcept {
return itoa(buf, v); }
2336 C4_ALWAYS_INLINE
size_t to_chars(substr buf, int16_t v) noexcept {
return itoa(buf, v); }
2337 C4_ALWAYS_INLINE
size_t to_chars(substr buf, int32_t v) noexcept {
return itoa(buf, v); }
2338 C4_ALWAYS_INLINE
size_t to_chars(substr buf, int64_t v) noexcept {
return itoa(buf, v); }
2339 C4_ALWAYS_INLINE
size_t to_chars(substr buf,
float v) noexcept {
return ftoa(buf, v); }
2340 C4_ALWAYS_INLINE
size_t to_chars(substr buf,
double v) noexcept {
return dtoa(buf, v); }
2342 template <
class T> C4_ALWAYS_INLINE
auto to_chars(substr buf, T v) noexcept -> _C4_IF_NOT_FIXED_LENGTH_I(T,
size_t)::type { return
itoa(buf, v); }
2343 template <
class T> C4_ALWAYS_INLINE
auto to_chars(substr buf, T v) noexcept -> _C4_IF_NOT_FIXED_LENGTH_U(T,
size_t)::type { return
write_dec(buf, v); }
2345 C4_ALWAYS_INLINE
size_t to_chars(substr s, T *v) noexcept {
return itoa(s, (intptr_t)v, (intptr_t)16); }
2366 C4_ALWAYS_INLINE
bool from_chars(csubstr buf, uint8_t *C4_RESTRICT v) noexcept {
return atou(buf, v); }
2367 C4_ALWAYS_INLINE
bool from_chars(csubstr buf, uint16_t *C4_RESTRICT v) noexcept {
return atou(buf, v); }
2368 C4_ALWAYS_INLINE
bool from_chars(csubstr buf, uint32_t *C4_RESTRICT v) noexcept {
return atou(buf, v); }
2369 C4_ALWAYS_INLINE
bool from_chars(csubstr buf, uint64_t *C4_RESTRICT v) noexcept {
return atou(buf, v); }
2370 C4_ALWAYS_INLINE
bool from_chars(csubstr buf, int8_t *C4_RESTRICT v) noexcept {
return atoi(buf, v); }
2371 C4_ALWAYS_INLINE
bool from_chars(csubstr buf, int16_t *C4_RESTRICT v) noexcept {
return atoi(buf, v); }
2372 C4_ALWAYS_INLINE
bool from_chars(csubstr buf, int32_t *C4_RESTRICT v) noexcept {
return atoi(buf, v); }
2373 C4_ALWAYS_INLINE
bool from_chars(csubstr buf, int64_t *C4_RESTRICT v) noexcept {
return atoi(buf, v); }
2374 C4_ALWAYS_INLINE
bool from_chars(csubstr buf,
float *C4_RESTRICT v) noexcept {
return atof(buf, v); }
2375 C4_ALWAYS_INLINE
bool from_chars(csubstr buf,
double *C4_RESTRICT v) noexcept {
return atod(buf, v); }
2377 template <
class T> C4_ALWAYS_INLINE
auto from_chars(csubstr buf, T *C4_RESTRICT v) noexcept -> _C4_IF_NOT_FIXED_LENGTH_I(T,
bool)::type { return
atoi(buf, v); }
2378 template <
class T> C4_ALWAYS_INLINE
auto from_chars(csubstr buf, T *C4_RESTRICT v) noexcept -> _C4_IF_NOT_FIXED_LENGTH_U(T,
bool)::type { return
atou(buf, v); }
2380 C4_ALWAYS_INLINE
bool from_chars(csubstr buf, T **v) noexcept { intptr_t tmp;
bool ret =
from_chars(buf, &tmp);
if(ret) { *v = (T*)tmp; }
return ret; }
2405 template <
class T> C4_ALWAYS_INLINE
auto from_chars_first(csubstr buf, T *C4_RESTRICT v) noexcept -> _C4_IF_NOT_FIXED_LENGTH_I(T,
size_t)::type { return
atoi_first(buf, v); }
2406 template <
class T> C4_ALWAYS_INLINE
auto from_chars_first(csubstr buf, T *C4_RESTRICT v) noexcept -> _C4_IF_NOT_FIXED_LENGTH_U(T,
size_t)::type { return
atou_first(buf, v); }
2414 #undef _C4_IF_NOT_FIXED_LENGTH_I
2415 #undef _C4_IF_NOT_FIXED_LENGTH_U
2430 C4_ALWAYS_INLINE substr
to_chars_sub(substr buf, T
const& C4_RESTRICT v) noexcept
2433 return buf.left_of(sz <= buf.len ? sz : buf.len);
2442 C4_ALWAYS_INLINE
size_t to_chars(substr buf,
bool v) noexcept
2449 inline bool from_chars(csubstr buf,
bool * C4_RESTRICT v) noexcept
2453 *v =
false;
return true;
2457 *v =
true;
return true;
2459 else if(buf ==
"false")
2461 *v =
false;
return true;
2463 else if(buf ==
"true")
2465 *v =
true;
return true;
2467 else if(buf ==
"False")
2469 *v =
false;
return true;
2471 else if(buf ==
"True")
2473 *v =
true;
return true;
2475 else if(buf ==
"FALSE")
2477 *v =
false;
return true;
2479 else if(buf ==
"TRUE")
2481 *v =
true;
return true;
2496 csubstr trimmed = buf.first_non_empty_span();
2511 C4_XASSERT(buf.str);
2521 inline bool from_chars(csubstr buf,
char * C4_RESTRICT v) noexcept
2525 C4_XASSERT(buf.str);
2546 C4_ASSERT(!buf.overlaps(v));
2547 size_t len = buf.len < v.len ? buf.len : v.len;
2553 C4_ASSERT(buf.str !=
nullptr);
2554 C4_ASSERT(v.str !=
nullptr);
2555 memcpy(buf.str, v.str, len);
2561 inline bool from_chars(csubstr buf, csubstr *C4_RESTRICT v) noexcept
2570 csubstr trimmed = buf.first_non_empty_span();
2571 if(trimmed.len == 0)
2574 return static_cast<size_t>(trimmed.end() - buf.begin());
2584 C4_ASSERT(!buf.overlaps(v));
2585 size_t len = buf.len < v.len ? buf.len : v.len;
2591 C4_ASSERT(buf.str !=
nullptr);
2592 C4_ASSERT(v.str !=
nullptr);
2593 memcpy(buf.str, v.str, len);
2599 inline bool from_chars(csubstr buf, substr * C4_RESTRICT v) noexcept
2601 C4_ASSERT(!buf.overlaps(*v));
2603 if(v->len >= buf.len)
2610 C4_ASSERT(buf.str !=
nullptr);
2611 C4_ASSERT(v->str !=
nullptr);
2612 memcpy(v->str, buf.str, buf.len);
2623 csubstr trimmed = buf.first_non_empty_span();
2624 C4_ASSERT(!trimmed.overlaps(*v));
2625 if(C4_UNLIKELY(trimmed.len == 0))
2627 size_t len = trimmed.len > v->len ? v->len : trimmed.len;
2633 C4_ASSERT(buf.str !=
nullptr);
2634 C4_ASSERT(v->str !=
nullptr);
2635 memcpy(v->str, trimmed.str, len);
2637 if(C4_UNLIKELY(trimmed.len > v->len))
2639 return static_cast<size_t>(trimmed.end() - buf.begin());
2647 inline size_t to_chars(substr buf,
const char (& C4_RESTRICT v)[N]) noexcept
2654 inline size_t to_chars(substr buf,
const char * C4_RESTRICT v) noexcept
2665 #if defined(_MSC_VER) && !defined(__clang__)
2666 # pragma warning(pop)
2667 #elif defined(__clang__)
2668 # pragma clang diagnostic pop
2669 #elif defined(__GNUC__)
2670 # pragma GCC diagnostic pop
#define C4_NO_UBSAN_IOVRFLW
bool atod(csubstr str, double *v) noexcept
Convert a string to a double precision real number.
size_t atod_first(csubstr str, double *v) noexcept
Convert a string to a double precision real number.
size_t atof_first(csubstr str, float *v) noexcept
Convert a string to a single precision real number.
bool atof(csubstr str, float *v) noexcept
Convert a string to a single precision real number.
bool atoi(csubstr str, T *v) noexcept
Convert a trimmed string to a signed integral value.
size_t atoi_first(csubstr str, T *v)
Select the next range of characters in the string that can be parsed as a signed integral value,...
bool atou(csubstr str, T *v) noexcept
Convert a trimmed string to an unsigned integral value.
size_t atou_first(csubstr str, T *v)
Select the next range of characters in the string that can be parsed as an unsigned integral value,...
bool atox(csubstr s, T **v) noexcept
@ FTOA_FLEX
print the real number in flexible format (like g)
@ FTOA_SCIENT
print the real number in scientific format (like e)
@ FTOA_FLOAT
print the real number in floating point format (like f)
@ FTOA_HEXA
print the real number in hexadecimal format (like a)
auto digits_dec(T v) noexcept -> typename std::enable_if< sizeof(T)==1u, unsigned >::type
decimal digits for 8 bit integers
unsigned digits_oct(T v_) noexcept
return the number of digits required to encode an octal number.
unsigned digits_hex(T v) noexcept
return the number of digits required to encode an hexadecimal number.
unsigned digits_bin(T v) noexcept
return the number of digits required to encode a binary number.
size_t dtoa(substr str, double v, int precision=-1, RealFormat_e formatting=FTOA_FLEX) noexcept
Convert a double-precision real number to string.
size_t from_chars_first(csubstr buf, substr *v) noexcept
bool from_chars(csubstr buf, substr *v) noexcept
size_t ftoa(substr str, float v, int precision=-1, RealFormat_e formatting=FTOA_FLEX) noexcept
Convert a single-precision real number to string.
integral_< intptr_t > hex(std::nullptr_t)
format null as an hexadecimal value
size_t itoa(substr buf, T v) noexcept
convert an integral signed decimal to a string.
size_t itoa(substr buf, T v, T radix, size_t num_digits) noexcept
same as c4::itoa(), but pad with zeroes on the left such that the resulting string is num_digits wide...
auto overflows(csubstr str) noexcept -> typename std::enable_if< std::is_unsigned< T >::value, bool >::type
Test if the following string would overflow when converted to associated integral types; this functio...
bool read_bin(csubstr s, I *v) noexcept
read a binary integer from a string.
bool read_oct(csubstr s, I *v) noexcept
read an octal integer from a string.
bool read_hex(csubstr s, I *v) noexcept
read an hexadecimal integer from a string.
bool read_dec(csubstr s, I *v) noexcept
read a decimal integer from a string.
csubstr to_csubstr(substr s) noexcept
neutral version for use in generic code
substr to_chars_sub(substr buf, T const &v) noexcept
call to_chars() and return a substr consisting of the written portion of the input buffer.
size_t to_chars(substr buf, const char *v) noexcept
size_t utoa(substr buf, T v) noexcept
convert an integral unsigned decimal to a string.
void write_bin_unchecked(substr buf, T v, unsigned digits_v) noexcept
void write_dec_unchecked(substr buf, T v, unsigned digits_v) noexcept
void write_hex_unchecked(substr buf, T v, unsigned digits_v) noexcept
void write_oct_unchecked(substr buf, T v, unsigned digits_v) noexcept
size_t write_hex(substr buf, T v) noexcept
write an integer to a string in hexadecimal format.
size_t write_dec(substr buf, T v) noexcept
write an integer to a string in decimal format.
size_t write_bin(substr buf, T v) noexcept
write an integer to a string in binary format.
size_t write_dec(substr buf, T val, size_t num_digits) noexcept
same as c4::write_dec(), but pad with zeroes on the left such that the resulting string is num_digits...
size_t write_oct(substr buf, T v) noexcept
write an integer to a string in octal format.
size_t xtoa(substr s, uint8_t v) noexcept
size_t xtoa(substr s, T *v) noexcept
@ npos
a null string position
(Undefined by default) Use shorter error message from checks/asserts: do not show the check condition...
#define _c4append(first, last)