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"
19 #include "c4/szconv.hpp"
21 #ifndef C4CORE_NO_FAST_FLOAT
23 # if defined(_MSC_VER)
24 # if (C4_MSVC_VERSION >= C4_MSVC_VERSION_2019)
26 # define C4CORE_HAVE_STD_TOCHARS 1
27 # define C4CORE_HAVE_STD_FROMCHARS 0
28 # define C4CORE_HAVE_FAST_FLOAT 1
30 # define C4CORE_HAVE_STD_TOCHARS 0
31 # define C4CORE_HAVE_STD_FROMCHARS 0
32 # define C4CORE_HAVE_FAST_FLOAT 1
35 # if __has_include(<charconv>)
37 # if defined(__cpp_lib_to_chars)
38 # define C4CORE_HAVE_STD_TOCHARS 1
39 # define C4CORE_HAVE_STD_FROMCHARS 0
40 # define C4CORE_HAVE_FAST_FLOAT 1
42 # define C4CORE_HAVE_STD_TOCHARS 0
43 # define C4CORE_HAVE_STD_FROMCHARS 0
44 # define C4CORE_HAVE_FAST_FLOAT 1
47 # define C4CORE_HAVE_STD_TOCHARS 0
48 # define C4CORE_HAVE_STD_FROMCHARS 0
49 # define C4CORE_HAVE_FAST_FLOAT 1
53 # define C4CORE_HAVE_STD_TOCHARS 0
54 # define C4CORE_HAVE_STD_FROMCHARS 0
55 # define C4CORE_HAVE_FAST_FLOAT 1
57 # if C4CORE_HAVE_FAST_FLOAT
58 C4_SUPPRESS_WARNING_GCC_WITH_PUSH(
"-Wsign-conversion")
59 C4_SUPPRESS_WARNING_GCC("-Warray-bounds")
60 # if defined(__GNUC__) && __GNUC__ >= 5
61 C4_SUPPRESS_WARNING_GCC(
"-Wshift-count-overflow")
63 # include "c4/ext/fast_float.hpp"
64 C4_SUPPRESS_WARNING_GCC_POP
67 # define C4CORE_HAVE_FAST_FLOAT 0
68 # if defined(_MSC_VER)
69 # if (C4_MSVC_VERSION >= C4_MSVC_VERSION_2019)
71 # define C4CORE_HAVE_STD_TOCHARS 1
72 # define C4CORE_HAVE_STD_FROMCHARS 1
74 # define C4CORE_HAVE_STD_TOCHARS 0
75 # define C4CORE_HAVE_STD_FROMCHARS 0
78 # if __has_include(<charconv>)
80 # if defined(__cpp_lib_to_chars)
81 # define C4CORE_HAVE_STD_TOCHARS 1
82 # define C4CORE_HAVE_STD_FROMCHARS 1
84 # define C4CORE_HAVE_STD_TOCHARS 0
85 # define C4CORE_HAVE_STD_FROMCHARS 0
88 # define C4CORE_HAVE_STD_TOCHARS 0
89 # define C4CORE_HAVE_STD_FROMCHARS 0
93 # define C4CORE_HAVE_STD_TOCHARS 0
94 # define C4CORE_HAVE_STD_FROMCHARS 0
95 # define C4CORE_HAVE_FAST_FLOAT 0
99 #if !C4CORE_HAVE_STD_FROMCHARS
104 #if defined(_MSC_VER)
105 # pragma warning(push)
106 # pragma warning(disable: 4996)
107 # if C4_MSVC_VERSION != C4_MSVC_VERSION_2017
108 # pragma warning(disable: 4800)
112 #if defined(__clang__)
113 # pragma clang diagnostic push
114 # pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare"
115 # pragma clang diagnostic ignored "-Wformat-nonliteral"
116 # pragma clang diagnostic ignored "-Wdouble-promotion"
117 # pragma clang diagnostic ignored "-Wold-style-cast"
118 #elif defined(__GNUC__)
119 # pragma GCC diagnostic push
120 # pragma GCC diagnostic ignored "-Wformat-nonliteral"
121 # pragma GCC diagnostic ignored "-Wdouble-promotion"
122 # pragma GCC diagnostic ignored "-Wuseless-cast"
123 # pragma GCC diagnostic ignored "-Wold-style-cast"
126 #if defined(__clang__)
127 #define C4_NO_UBSAN_IOVRFLW __attribute__((no_sanitize("signed-integer-overflow")))
128 #elif defined(__GNUC__)
130 #define C4_NO_UBSAN_IOVRFLW __attribute__((no_sanitize("signed-integer-overflow")))
132 #define C4_NO_UBSAN_IOVRFLW
135 #define C4_NO_UBSAN_IOVRFLW
186 #if C4CORE_HAVE_STD_TOCHARS
188 typedef enum : std::underlying_type<std::chars_format>::type {
190 FTOA_FLOAT =
static_cast<std::underlying_type<std::chars_format>::type
>(std::chars_format::fixed),
192 FTOA_SCIENT =
static_cast<std::underlying_type<std::chars_format>::type
>(std::chars_format::scientific),
194 FTOA_FLEX =
static_cast<std::underlying_type<std::chars_format>::type
>(std::chars_format::general),
200 typedef enum :
char {
217 struct is_fixed_length
221 value_i = (std::is_integral<T>::value
222 && (std::is_same<T, int8_t>::value
223 || std::is_same<T, int16_t>::value
224 || std::is_same<T, int32_t>::value
225 || std::is_same<T, int64_t>::value)),
227 value_u = (std::is_integral<T>::value
228 && (std::is_same<T, uint8_t>::value
229 || std::is_same<T, uint16_t>::value
230 || std::is_same<T, uint32_t>::value
231 || std::is_same<T, uint64_t>::value)),
233 value = value_i || value_u
244 # pragma warning(push)
245 #elif defined(__clang__)
246 # pragma clang diagnostic push
247 #elif defined(__GNUC__)
248 # pragma GCC diagnostic push
249 # pragma GCC diagnostic ignored "-Wconversion"
251 # pragma GCC diagnostic ignored "-Wnull-dereference"
271 template<
size_t num_
bytes,
bool is_
signed>
struct charconv_digits_;
272 template<
class T>
using charconv_digits = charconv_digits_<
sizeof(T), std::is_signed<T>::value>;
274 template<>
struct charconv_digits_<1u, true>
277 maxdigits_bin = 1 + 2 + 8,
278 maxdigits_oct = 1 + 2 + 3,
279 maxdigits_dec = 1 + 3,
280 maxdigits_hex = 1 + 2 + 2,
281 maxdigits_bin_nopfx = 8,
282 maxdigits_oct_nopfx = 3,
283 maxdigits_dec_nopfx = 3,
284 maxdigits_hex_nopfx = 2,
287 static constexpr csubstr min_value_dec() noexcept {
return csubstr(
"128"); }
288 static constexpr csubstr min_value_hex() noexcept {
return csubstr(
"80"); }
289 static constexpr csubstr min_value_oct() noexcept {
return csubstr(
"200"); }
290 static constexpr csubstr min_value_bin() noexcept {
return csubstr(
"10000000"); }
291 static constexpr csubstr max_value_dec() noexcept {
return csubstr(
"127"); }
292 static constexpr
bool is_oct_overflow(csubstr str) noexcept {
return !((str.len < 3) || (str.len == 3 && str[0] <=
'1')); }
294 template<>
struct charconv_digits_<1u, false>
297 maxdigits_bin = 2 + 8,
298 maxdigits_oct = 2 + 3,
300 maxdigits_hex = 2 + 2,
301 maxdigits_bin_nopfx = 8,
302 maxdigits_oct_nopfx = 3,
303 maxdigits_dec_nopfx = 3,
304 maxdigits_hex_nopfx = 2,
306 static constexpr csubstr max_value_dec() noexcept {
return csubstr(
"255"); }
307 static constexpr
bool is_oct_overflow(csubstr str) noexcept {
return !((str.len < 3) || (str.len == 3 && str[0] <=
'3')); }
309 template<>
struct charconv_digits_<2u, true>
312 maxdigits_bin = 1 + 2 + 16,
313 maxdigits_oct = 1 + 2 + 6,
314 maxdigits_dec = 1 + 5,
315 maxdigits_hex = 1 + 2 + 4,
316 maxdigits_bin_nopfx = 16,
317 maxdigits_oct_nopfx = 6,
318 maxdigits_dec_nopfx = 5,
319 maxdigits_hex_nopfx = 4,
322 static constexpr csubstr min_value_dec() noexcept {
return csubstr(
"32768"); }
323 static constexpr csubstr min_value_hex() noexcept {
return csubstr(
"8000"); }
324 static constexpr csubstr min_value_oct() noexcept {
return csubstr(
"100000"); }
325 static constexpr csubstr min_value_bin() noexcept {
return csubstr(
"1000000000000000"); }
326 static constexpr csubstr max_value_dec() noexcept {
return csubstr(
"32767"); }
327 static constexpr
bool is_oct_overflow(csubstr str) noexcept {
return !((str.len < 6)); }
329 template<>
struct charconv_digits_<2u, false>
332 maxdigits_bin = 2 + 16,
333 maxdigits_oct = 2 + 6,
335 maxdigits_hex = 2 + 4,
336 maxdigits_bin_nopfx = 16,
337 maxdigits_oct_nopfx = 6,
338 maxdigits_dec_nopfx = 6,
339 maxdigits_hex_nopfx = 4,
341 static constexpr csubstr max_value_dec() noexcept {
return csubstr(
"65535"); }
342 static constexpr
bool is_oct_overflow(csubstr str) noexcept {
return !((str.len < 6) || (str.len == 6 && str[0] <=
'1')); }
344 template<>
struct charconv_digits_<4u, true>
347 maxdigits_bin = 1 + 2 + 32,
348 maxdigits_oct = 1 + 2 + 11,
349 maxdigits_dec = 1 + 10,
350 maxdigits_hex = 1 + 2 + 8,
351 maxdigits_bin_nopfx = 32,
352 maxdigits_oct_nopfx = 11,
353 maxdigits_dec_nopfx = 10,
354 maxdigits_hex_nopfx = 8,
357 static constexpr csubstr min_value_dec() noexcept {
return csubstr(
"2147483648"); }
358 static constexpr csubstr min_value_hex() noexcept {
return csubstr(
"80000000"); }
359 static constexpr csubstr min_value_oct() noexcept {
return csubstr(
"20000000000"); }
360 static constexpr csubstr min_value_bin() noexcept {
return csubstr(
"10000000000000000000000000000000"); }
361 static constexpr csubstr max_value_dec() noexcept {
return csubstr(
"2147483647"); }
362 static constexpr
bool is_oct_overflow(csubstr str) noexcept {
return !((str.len < 11) || (str.len == 11 && str[0] <=
'1')); }
364 template<>
struct charconv_digits_<4u, false>
367 maxdigits_bin = 2 + 32,
368 maxdigits_oct = 2 + 11,
370 maxdigits_hex = 2 + 8,
371 maxdigits_bin_nopfx = 32,
372 maxdigits_oct_nopfx = 11,
373 maxdigits_dec_nopfx = 10,
374 maxdigits_hex_nopfx = 8,
376 static constexpr csubstr max_value_dec() noexcept {
return csubstr(
"4294967295"); }
377 static constexpr
bool is_oct_overflow(csubstr str) noexcept {
return !((str.len < 11) || (str.len == 11 && str[0] <=
'3')); }
379 template<>
struct charconv_digits_<8u, true>
382 maxdigits_bin = 1 + 2 + 64,
383 maxdigits_oct = 1 + 2 + 22,
384 maxdigits_dec = 1 + 19,
385 maxdigits_hex = 1 + 2 + 16,
386 maxdigits_bin_nopfx = 64,
387 maxdigits_oct_nopfx = 22,
388 maxdigits_dec_nopfx = 19,
389 maxdigits_hex_nopfx = 16,
391 static constexpr csubstr min_value_dec() noexcept {
return csubstr(
"9223372036854775808"); }
392 static constexpr csubstr min_value_hex() noexcept {
return csubstr(
"8000000000000000"); }
393 static constexpr csubstr min_value_oct() noexcept {
return csubstr(
"1000000000000000000000"); }
394 static constexpr csubstr min_value_bin() noexcept {
return csubstr(
"1000000000000000000000000000000000000000000000000000000000000000"); }
395 static constexpr csubstr max_value_dec() noexcept {
return csubstr(
"9223372036854775807"); }
396 static constexpr
bool is_oct_overflow(csubstr str) noexcept {
return !((str.len < 22)); }
398 template<>
struct charconv_digits_<8u, false>
401 maxdigits_bin = 2 + 64,
402 maxdigits_oct = 2 + 22,
404 maxdigits_hex = 2 + 16,
405 maxdigits_bin_nopfx = 64,
406 maxdigits_oct_nopfx = 22,
407 maxdigits_dec_nopfx = 20,
408 maxdigits_hex_nopfx = 16,
410 static constexpr csubstr max_value_dec() noexcept {
return csubstr(
"18446744073709551615"); }
411 static constexpr
bool is_oct_overflow(csubstr str) noexcept {
return !((str.len < 22) || (str.len == 22 && str[0] <=
'1')); }
416 #define _c4append(c) { if(C4_LIKELY(pos < buf.len)) { buf.str[pos++] = static_cast<char>(c); } else { ++pos; } }
417 #define _c4appendhex(i) { if(C4_LIKELY(pos < buf.len)) { buf.str[pos++] = hexchars[i]; } else { ++pos; } }
440 C4_CONSTEXPR14 C4_ALWAYS_INLINE
442 ->
typename std::enable_if<
sizeof(T) == 1u,
unsigned>::type
444 C4_STATIC_ASSERT(std::is_integral<T>::value);
446 return ((v >= 100) ? 3u : ((v >= 10) ? 2u : 1u));
451 C4_CONSTEXPR14 C4_ALWAYS_INLINE
453 ->
typename std::enable_if<
sizeof(T) == 2u,
unsigned>::type
455 C4_STATIC_ASSERT(std::is_integral<T>::value);
457 return ((v >= 10000) ? 5u : (v >= 1000) ? 4u : (v >= 100) ? 3u : (v >= 10) ? 2u : 1u);
462 C4_CONSTEXPR14 C4_ALWAYS_INLINE
464 ->
typename std::enable_if<
sizeof(T) == 4u,
unsigned>::type
466 C4_STATIC_ASSERT(std::is_integral<T>::value);
468 return ((v >= 1000000000) ? 10u : (v >= 100000000) ? 9u : (v >= 10000000) ? 8u :
469 (v >= 1000000) ? 7u : (v >= 100000) ? 6u : (v >= 10000) ? 5u :
470 (v >= 1000) ? 4u : (v >= 100) ? 3u : (v >= 10) ? 2u : 1u);
475 C4_CONSTEXPR14 C4_ALWAYS_INLINE
477 ->
typename std::enable_if<
sizeof(T) == 8u,
unsigned>::type
481 C4_STATIC_ASSERT(std::is_integral<T>::value);
485 if(v >= 100000000000000)
487 if(v >= 100000000000000000)
489 if((
typename std::make_unsigned<T>::type)v >= 10000000000000000000u)
492 return (v >= 1000000000000000000) ? 19u : 18u;
494 else if(v >= 10000000000000000)
497 return(v >= 1000000000000000) ? 16u : 15u;
499 else if(v >= 1000000000000)
500 return (v >= 10000000000000) ? 14u : 13u;
501 else if(v >= 100000000000)
504 return(v >= 10000000000) ? 11u : 10u;
509 return (v >= 100000000) ? 9u : 8u;
510 else if(v >= 1000000)
513 return (v >= 100000) ? 6u : 5u;
516 return (v >= 1000) ? 4u : 3u;
518 return (v >= 10) ? 2u : 1u;
524 C4_CONSTEXPR14 C4_ALWAYS_INLINE
unsigned digits_hex(T v) noexcept
526 C4_STATIC_ASSERT(std::is_integral<T>::value);
528 return v ? 1u + (msb((
typename std::make_unsigned<T>::type)v) >> 2u) : 1u;
533 C4_CONSTEXPR14 C4_ALWAYS_INLINE
unsigned digits_bin(T v) noexcept
535 C4_STATIC_ASSERT(std::is_integral<T>::value);
537 return v ? 1u + msb((
typename std::make_unsigned<T>::type)v) : 1u;
542 C4_CONSTEXPR14 C4_ALWAYS_INLINE
unsigned digits_oct(T v_) noexcept
545 C4_STATIC_ASSERT(std::is_integral<T>::value);
548 std::conditional<
sizeof(T) <=
sizeof(
unsigned),
550 typename std::make_unsigned<T>::type>::type;
553 const unsigned __b2 = 64u;
554 const unsigned __b3 = __b2 * 8u;
555 const unsigned long __b4 = __b3 * 8u;
580 C4_INLINE_CONSTEXPR
const char hexchars[] =
"0123456789abcdef";
581 C4_INLINE_CONSTEXPR
const char digits0099[] =
582 "0001020304050607080910111213141516171819"
583 "2021222324252627282930313233343536373839"
584 "4041424344454647484950515253545556575859"
585 "6061626364656667686970717273747576777879"
586 "8081828384858687888990919293949596979899";
590 C4_SUPPRESS_WARNING_GCC_PUSH
591 C4_SUPPRESS_WARNING_GCC(
"-Warray-bounds")
592 #if (defined(__GNUC__) && (__GNUC__ >= 7))
593 C4_SUPPRESS_WARNING_GCC(
"-Wstringop-overflow")
607 C4_HOT C4_ALWAYS_INLINE
610 C4_STATIC_ASSERT(std::is_integral<T>::value);
612 C4_ASSERT(buf.len >= digits_v);
619 const auto num = (v - quo * T(100)) << 1u;
621 buf.str[--digits_v] = detail::digits0099[num + 1];
622 buf.str[--digits_v] = detail::digits0099[num];
626 C4_ASSERT(digits_v == 2);
627 const auto num = v << 1u;
628 buf.str[1] = detail::digits0099[num + 1];
629 buf.str[0] = detail::digits0099[num];
633 C4_ASSERT(digits_v == 1);
634 buf.str[0] = (char)(
'0' + v);
640 C4_HOT C4_ALWAYS_INLINE
643 C4_STATIC_ASSERT(std::is_integral<T>::value);
645 C4_ASSERT(buf.len >= digits_v);
648 buf.str[--digits_v] = detail::hexchars[v & T(15)];
651 C4_ASSERT(digits_v == 0);
656 C4_HOT C4_ALWAYS_INLINE
659 C4_STATIC_ASSERT(std::is_integral<T>::value);
661 C4_ASSERT(buf.len >= digits_v);
664 buf.str[--digits_v] = (char)(
'0' + (v & T(7)));
667 C4_ASSERT(digits_v == 0);
672 C4_HOT C4_ALWAYS_INLINE
675 C4_STATIC_ASSERT(std::is_integral<T>::value);
677 C4_ASSERT(buf.len >= digits_v);
680 buf.str[--digits_v] = (char)(
'0' + (v & T(1)));
683 C4_ASSERT(digits_v == 0);
708 C4_ALWAYS_INLINE
size_t write_dec(substr buf, T v) noexcept
710 C4_STATIC_ASSERT(std::is_integral<T>::value);
713 if(C4_LIKELY(buf.len >= digits))
727 C4_ALWAYS_INLINE
size_t write_hex(substr buf, T v) noexcept
729 C4_STATIC_ASSERT(std::is_integral<T>::value);
732 if(C4_LIKELY(buf.len >= digits))
746 C4_ALWAYS_INLINE
size_t write_oct(substr buf, T v) noexcept
748 C4_STATIC_ASSERT(std::is_integral<T>::value);
751 if(C4_LIKELY(buf.len >= digits))
765 C4_ALWAYS_INLINE
size_t write_bin(substr buf, T v) noexcept
767 C4_STATIC_ASSERT(std::is_integral<T>::value);
770 C4_ASSERT(digits > 0);
771 if(C4_LIKELY(buf.len >= digits))
779 template<
class U>
using NumberWriter = size_t (*)(substr, U);
780 template<
class T, NumberWriter<T> writer>
781 size_t write_num_digits(substr buf, T v,
size_t num_digits) noexcept
783 C4_STATIC_ASSERT(std::is_integral<T>::value);
784 size_t ret = writer(buf, v);
785 if(ret >= num_digits)
787 else if(ret >= buf.len || num_digits > buf.len)
789 C4_ASSERT(num_digits >= ret);
790 size_t delta =
static_cast<size_t>(num_digits - ret);
791 memmove(buf.str + delta, buf.str, ret);
792 memset(buf.str,
'0', delta);
803 C4_ALWAYS_INLINE
size_t write_dec(substr buf, T val,
size_t num_digits) noexcept
805 return detail::write_num_digits<T, &write_dec<T>>(buf, val, num_digits);
812 C4_ALWAYS_INLINE
size_t write_hex(substr buf, T val,
size_t num_digits) noexcept
814 return detail::write_num_digits<T, &write_hex<T>>(buf, val, num_digits);
821 C4_ALWAYS_INLINE
size_t write_bin(substr buf, T val,
size_t num_digits) noexcept
823 return detail::write_num_digits<T, &write_bin<T>>(buf, val, num_digits);
830 C4_ALWAYS_INLINE
size_t write_oct(substr buf, T val,
size_t num_digits) noexcept
832 return detail::write_num_digits<T, &write_oct<T>>(buf, val, num_digits);
837 C4_SUPPRESS_WARNING_GCC_POP
845 C4_SUPPRESS_WARNING_MSVC_PUSH
846 C4_SUPPRESS_WARNING_MSVC(4365)
865 C4_ALWAYS_INLINE
bool read_dec(csubstr s, I *C4_RESTRICT v) noexcept
867 C4_STATIC_ASSERT(std::is_integral<I>::value);
868 C4_ASSERT(!s.empty());
872 if(C4_UNLIKELY(c < '0' || c >
'9'))
874 *v = (*v) * I(10) + (I(c) - I(
'0'));
893 C4_ALWAYS_INLINE
bool read_hex(csubstr s, I *C4_RESTRICT v) noexcept
895 C4_STATIC_ASSERT(std::is_integral<I>::value);
896 C4_ASSERT(!s.empty());
901 if(c >=
'0' && c <=
'9')
903 else if(c >=
'a' && c <=
'f')
904 cv = I(10) + (I(c) - I(
'a'));
905 else if(c >=
'A' && c <=
'F')
906 cv = I(10) + (I(c) - I(
'A'));
909 *v = (*v) * I(16) + cv;
928 C4_ALWAYS_INLINE
bool read_bin(csubstr s, I *C4_RESTRICT v) noexcept
930 C4_STATIC_ASSERT(std::is_integral<I>::value);
931 C4_ASSERT(!s.empty());
958 C4_ALWAYS_INLINE
bool read_oct(csubstr s, I *C4_RESTRICT v) noexcept
960 C4_STATIC_ASSERT(std::is_integral<I>::value);
961 C4_ASSERT(!s.empty());
965 if(C4_UNLIKELY(c < '0' || c >
'7'))
967 *v = (*v) * I(8) + (I(c) - I(
'0'));
974 C4_SUPPRESS_WARNING_MSVC_POP
981 C4_SUPPRESS_WARNING_GCC_WITH_PUSH(
"-Wswitch-default")
985 inline size_t _itoa2buf(substr buf,
size_t pos, csubstr val) noexcept
987 C4_ASSERT(pos + val.len <= buf.len);
988 memcpy(buf.str + pos, val.str, val.len);
989 return pos + val.len;
991 inline size_t _itoa2bufwithdigits(substr buf,
size_t pos,
size_t num_digits, csubstr val) noexcept
993 num_digits = num_digits > val.len ? num_digits - val.len : 0;
994 C4_ASSERT(num_digits + val.len <= buf.len);
995 for(
size_t i = 0; i < num_digits; ++i)
997 return detail::_itoa2buf(buf, pos, val);
1000 C4_NO_INLINE
size_t _itoadec2buf(substr buf) noexcept
1002 using digits_type = detail::charconv_digits<I>;
1003 if(C4_UNLIKELY(buf.len < digits_type::maxdigits_dec))
1004 return digits_type::maxdigits_dec;
1006 return detail::_itoa2buf(buf, 1, digits_type::min_value_dec());
1009 C4_NO_INLINE
size_t _itoa2buf(substr buf, I radix) noexcept
1011 using digits_type = detail::charconv_digits<I>;
1013 if(C4_LIKELY(buf.len > 0))
1014 buf.str[pos++] =
'-';
1018 if(C4_UNLIKELY(buf.len < digits_type::maxdigits_dec))
1019 return digits_type::maxdigits_dec;
1020 pos =_itoa2buf(buf, pos, digits_type::min_value_dec());
1023 if(C4_UNLIKELY(buf.len < digits_type::maxdigits_hex))
1024 return digits_type::maxdigits_hex;
1025 buf.str[pos++] =
'0';
1026 buf.str[pos++] =
'x';
1027 pos = _itoa2buf(buf, pos, digits_type::min_value_hex());
1030 if(C4_UNLIKELY(buf.len < digits_type::maxdigits_bin))
1031 return digits_type::maxdigits_bin;
1032 buf.str[pos++] =
'0';
1033 buf.str[pos++] =
'b';
1034 pos = _itoa2buf(buf, pos, digits_type::min_value_bin());
1037 if(C4_UNLIKELY(buf.len < digits_type::maxdigits_oct))
1038 return digits_type::maxdigits_oct;
1039 buf.str[pos++] =
'0';
1040 buf.str[pos++] =
'o';
1041 pos = _itoa2buf(buf, pos, digits_type::min_value_oct());
1047 C4_NO_INLINE
size_t _itoa2buf(substr buf, I radix,
size_t num_digits) noexcept
1049 using digits_type = detail::charconv_digits<I>;
1051 size_t needed_digits = 0;
1052 if(C4_LIKELY(buf.len > 0))
1053 buf.str[pos++] =
'-';
1058 needed_digits = num_digits+1 > digits_type::maxdigits_dec ? num_digits+1 : digits_type::maxdigits_dec;
1059 if(C4_UNLIKELY(buf.len < needed_digits))
1060 return needed_digits;
1061 pos = _itoa2bufwithdigits(buf, pos, num_digits, digits_type::min_value_dec());
1065 needed_digits = num_digits+3 > digits_type::maxdigits_hex ? num_digits+3 : digits_type::maxdigits_hex;
1066 if(C4_UNLIKELY(buf.len < needed_digits))
1067 return needed_digits;
1068 buf.str[pos++] =
'0';
1069 buf.str[pos++] =
'x';
1070 pos = _itoa2bufwithdigits(buf, pos, num_digits, digits_type::min_value_hex());
1074 needed_digits = num_digits+3 > digits_type::maxdigits_bin ? num_digits+3 : digits_type::maxdigits_bin;
1075 if(C4_UNLIKELY(buf.len < needed_digits))
1076 return needed_digits;
1077 C4_ASSERT(buf.len >= digits_type::maxdigits_bin);
1078 buf.str[pos++] =
'0';
1079 buf.str[pos++] =
'b';
1080 pos = _itoa2bufwithdigits(buf, pos, num_digits, digits_type::min_value_bin());
1084 needed_digits = num_digits+3 > digits_type::maxdigits_oct ? num_digits+3 : digits_type::maxdigits_oct;
1085 if(C4_UNLIKELY(buf.len < needed_digits))
1086 return needed_digits;
1087 C4_ASSERT(buf.len >= digits_type::maxdigits_oct);
1088 buf.str[pos++] =
'0';
1089 buf.str[pos++] =
'o';
1090 pos = _itoa2bufwithdigits(buf, pos, num_digits, digits_type::min_value_oct());
1109 C4_ALWAYS_INLINE
size_t itoa(substr buf, T v) noexcept
1111 C4_STATIC_ASSERT(std::is_signed<T>::value);
1119 else if(C4_LIKELY(v != std::numeric_limits<T>::min()))
1123 if(C4_LIKELY(buf.len >= digits + 1u))
1130 return detail::_itoadec2buf<T>(buf);
1141 C4_ALWAYS_INLINE
size_t itoa(substr buf, T v, T radix) noexcept
1143 C4_STATIC_ASSERT(std::is_signed<T>::value);
1144 C4_ASSERT(radix == 2 || radix == 8 || radix == 10 || radix == 16);
1145 C4_SUPPRESS_WARNING_GCC_PUSH
1146 #if (defined(__GNUC__) && (__GNUC__ >= 7))
1147 C4_SUPPRESS_WARNING_GCC(
"-Wstringop-overflow")
1151 if(C4_LIKELY(v != std::numeric_limits<T>::min()))
1157 if(C4_LIKELY(buf.len > 0))
1161 unsigned digits = 0;
1166 if(C4_LIKELY(buf.len >= pos + digits))
1171 if(C4_LIKELY(buf.len >= pos + 2u + digits))
1173 buf.str[pos + 0] =
'0';
1174 buf.str[pos + 1] =
'x';
1181 if(C4_LIKELY(buf.len >= pos + 2u + digits))
1183 buf.str[pos + 0] =
'0';
1184 buf.str[pos + 1] =
'b';
1191 if(C4_LIKELY(buf.len >= pos + 2u + digits))
1193 buf.str[pos + 0] =
'0';
1194 buf.str[pos + 1] =
'o';
1200 return pos + digits;
1202 C4_SUPPRESS_WARNING_GCC_POP
1205 return detail::_itoa2buf<T>(buf, radix);
1218 C4_ALWAYS_INLINE
size_t itoa(substr buf, T v, T radix,
size_t num_digits) noexcept
1220 C4_STATIC_ASSERT(std::is_signed<T>::value);
1221 C4_ASSERT(radix == 2 || radix == 8 || radix == 10 || radix == 16);
1222 C4_SUPPRESS_WARNING_GCC_PUSH
1223 #if (defined(__GNUC__) && (__GNUC__ >= 7))
1224 C4_SUPPRESS_WARNING_GCC(
"-Wstringop-overflow")
1228 if(C4_LIKELY(v != std::numeric_limits<T>::min()))
1234 if(C4_LIKELY(buf.len > 0))
1238 unsigned total_digits = 0;
1243 total_digits = pos + (unsigned)(num_digits > total_digits ? num_digits : total_digits);
1244 if(C4_LIKELY(buf.len >= total_digits))
1249 total_digits = pos + 2u + (unsigned)(num_digits > total_digits ? num_digits : total_digits);
1250 if(C4_LIKELY(buf.len >= total_digits))
1252 buf.str[pos + 0] =
'0';
1253 buf.str[pos + 1] =
'x';
1254 write_hex(buf.sub(pos + 2), v, num_digits);
1259 total_digits = pos + 2u + (unsigned)(num_digits > total_digits ? num_digits : total_digits);
1260 if(C4_LIKELY(buf.len >= total_digits))
1262 buf.str[pos + 0] =
'0';
1263 buf.str[pos + 1] =
'b';
1264 write_bin(buf.sub(pos + 2), v, num_digits);
1269 total_digits = pos + 2u + (unsigned)(num_digits > total_digits ? num_digits : total_digits);
1270 if(C4_LIKELY(buf.len >= total_digits))
1272 buf.str[pos + 0] =
'0';
1273 buf.str[pos + 1] =
'o';
1274 write_oct(buf.sub(pos + 2), v, num_digits);
1278 return total_digits;
1280 C4_SUPPRESS_WARNING_GCC_POP
1283 return detail::_itoa2buf<T>(buf, radix, num_digits);
1304 C4_ALWAYS_INLINE
size_t utoa(substr buf, T v) noexcept
1306 C4_STATIC_ASSERT(std::is_unsigned<T>::value);
1319 C4_ALWAYS_INLINE
size_t utoa(substr buf, T v, T radix) noexcept
1321 C4_STATIC_ASSERT(std::is_unsigned<T>::value);
1322 C4_ASSERT(radix == 10 || radix == 16 || radix == 2 || radix == 8);
1323 unsigned digits = 0;
1328 if(C4_LIKELY(buf.len >= digits))
1333 if(C4_LIKELY(buf.len >= digits+2u))
1343 if(C4_LIKELY(buf.len >= digits+2u))
1353 if(C4_LIKELY(buf.len >= digits+2u))
1374 C4_ALWAYS_INLINE
size_t utoa(substr buf, T v, T radix,
size_t num_digits) noexcept
1376 C4_STATIC_ASSERT(std::is_unsigned<T>::value);
1377 C4_ASSERT(radix == 10 || radix == 16 || radix == 2 || radix == 8);
1378 unsigned total_digits = 0;
1383 total_digits = (unsigned)(num_digits > total_digits ? num_digits : total_digits);
1384 if(C4_LIKELY(buf.len >= total_digits))
1389 total_digits = 2u + (unsigned)(num_digits > total_digits ? num_digits : total_digits);
1390 if(C4_LIKELY(buf.len >= total_digits))
1399 total_digits = 2u + (unsigned)(num_digits > total_digits ? num_digits : total_digits);
1400 if(C4_LIKELY(buf.len >= total_digits))
1409 total_digits = 2u + (unsigned)(num_digits > total_digits ? num_digits : total_digits);
1410 if(C4_LIKELY(buf.len >= total_digits))
1418 return total_digits;
1420 C4_SUPPRESS_WARNING_GCC_POP
1455 C4_ALWAYS_INLINE
bool atoi(csubstr str, T * C4_RESTRICT v) noexcept
1457 C4_STATIC_ASSERT(std::is_integral<T>::value);
1458 C4_STATIC_ASSERT(std::is_signed<T>::value);
1460 if(C4_UNLIKELY(str.len == 0))
1463 C4_ASSERT(str.str[0] !=
'+');
1467 if(str.str[0] ==
'-')
1469 if(C4_UNLIKELY(str.len == ++start))
1474 bool parsed_ok =
true;
1475 if(str.str[start] !=
'0')
1477 parsed_ok =
read_dec(str.sub(start), v);
1479 else if(str.len > start + 1)
1482 const char pfx = str.str[start + 1];
1483 if(pfx ==
'x' || pfx ==
'X')
1484 parsed_ok = str.len > start + 2 &&
read_hex(str.sub(start + 2), v);
1485 else if(pfx ==
'b' || pfx ==
'B')
1486 parsed_ok = str.len > start + 2 &&
read_bin(str.sub(start + 2), v);
1487 else if(pfx ==
'o' || pfx ==
'O')
1488 parsed_ok = str.len > start + 2 &&
read_oct(str.sub(start + 2), v);
1490 parsed_ok =
read_dec(str.sub(start + 1), v);
1494 parsed_ok =
read_dec(str.sub(start), v);
1496 if(C4_LIKELY(parsed_ok))
1509 C4_ALWAYS_INLINE
size_t atoi_first(csubstr str, T * C4_RESTRICT v)
1511 csubstr trimmed = str.first_int_span();
1512 if(trimmed.len == 0)
1514 if(
atoi(trimmed, v))
1515 return static_cast<size_t>(trimmed.end() - str.begin());
1548 bool atou(csubstr str, T * C4_RESTRICT v) noexcept
1550 C4_STATIC_ASSERT(std::is_integral<T>::value);
1552 if(C4_UNLIKELY(str.len == 0 || str.front() ==
'-'))
1555 bool parsed_ok =
true;
1556 if(str.str[0] !=
'0')
1564 const char pfx = str.str[1];
1565 if(pfx ==
'x' || pfx ==
'X')
1566 parsed_ok = str.len > 2 &&
read_hex(str.sub(2), v);
1567 else if(pfx ==
'b' || pfx ==
'B')
1568 parsed_ok = str.len > 2 &&
read_bin(str.sub(2), v);
1569 else if(pfx ==
'o' || pfx ==
'O')
1570 parsed_ok = str.len > 2 &&
read_oct(str.sub(2), v);
1592 csubstr trimmed = str.first_uint_span();
1593 if(trimmed.len == 0)
1595 if(
atou(trimmed, v))
1596 return static_cast<size_t>(trimmed.end() - str.begin());
1604 # pragma warning(pop)
1605 #elif defined(__clang__)
1606 # pragma clang diagnostic pop
1607 #elif defined(__GNUC__)
1608 # pragma GCC diagnostic pop
1618 inline bool check_overflow(csubstr str, csubstr limit) noexcept
1620 if(str.len == limit.len)
1622 for(
size_t i = 0; i < limit.len; ++i)
1624 if(str[i] < limit[i])
1626 else if(str[i] > limit[i])
1632 return str.len > limit.len;
1650 ->
typename std::enable_if<std::is_unsigned<T>::value,
bool>::type
1652 C4_STATIC_ASSERT(std::is_integral<T>::value);
1654 if(C4_UNLIKELY(str.len == 0))
1658 else if(str.str[0] ==
'0')
1667 size_t fno = str.first_not_of(
'0', 2);
1670 return !(str.len <= fno + (
sizeof(T) * 2));
1675 size_t fno = str.first_not_of(
'0', 2);
1678 return !(str.len <= fno +(
sizeof(T) * 8));
1683 size_t fno = str.first_not_of(
'0', 2);
1686 return detail::charconv_digits<T>::is_oct_overflow(str.sub(fno));
1690 size_t fno = str.first_not_of(
'0', 1);
1693 return detail::check_overflow(str.sub(fno), detail::charconv_digits<T>::max_value_dec());
1697 else if(C4_UNLIKELY(str[0] ==
'-'))
1703 return detail::check_overflow(str, detail::charconv_digits<T>::max_value_dec());
1717 ->
typename std::enable_if<std::is_signed<T>::value,
bool>::type
1719 C4_STATIC_ASSERT(std::is_integral<T>::value);
1720 if(C4_UNLIKELY(str.len == 0))
1722 if(str.str[0] ==
'-')
1724 if(str.str[1] ==
'0')
1733 size_t fno = str.first_not_of(
'0', 3);
1736 return detail::check_overflow(str.sub(fno), detail::charconv_digits<T>::min_value_hex());
1741 size_t fno = str.first_not_of(
'0', 3);
1744 return detail::check_overflow(str.sub(fno), detail::charconv_digits<T>::min_value_bin());
1749 size_t fno = str.first_not_of(
'0', 3);
1752 return detail::check_overflow(str.sub(fno), detail::charconv_digits<T>::min_value_oct());
1756 size_t fno = str.first_not_of(
'0', 2);
1759 return detail::check_overflow(str.sub(fno), detail::charconv_digits<T>::min_value_dec());
1764 return detail::check_overflow(str.sub(1), detail::charconv_digits<T>::min_value_dec());
1766 else if(str.str[0] ==
'0')
1775 size_t fno = str.first_not_of(
'0', 2);
1778 const size_t len = str.len - fno;
1779 return !((len <
sizeof (T) * 2) || (len ==
sizeof(T) * 2 && str[fno] <=
'7'));
1784 size_t fno = str.first_not_of(
'0', 2);
1787 return !(str.len <= fno + (
sizeof(T) * 8 - 1));
1792 size_t fno = str.first_not_of(
'0', 2);
1795 return detail::charconv_digits<T>::is_oct_overflow(str.sub(fno));
1799 size_t fno = str.first_not_of(
'0', 1);
1802 return detail::check_overflow(str.sub(fno), detail::charconv_digits<T>::max_value_dec());
1807 return detail::check_overflow(str, detail::charconv_digits<T>::max_value_dec());
1821 #if (!C4CORE_HAVE_STD_FROMCHARS)
1824 void get_real_format_str(
char (& C4_RESTRICT fmt)[N],
int precision,
RealFormat_e formatting,
const char* length_modifier=
"")
1828 iret = snprintf(fmt,
sizeof(fmt),
"%%%s%c", length_modifier, formatting);
1829 else if(precision == 0)
1830 iret = snprintf(fmt,
sizeof(fmt),
"%%.%s%c", length_modifier, formatting);
1832 iret = snprintf(fmt,
sizeof(fmt),
"%%.%d%s%c", precision, length_modifier, formatting);
1833 C4_ASSERT(iret >= 2 &&
size_t(iret) <
sizeof(fmt));
1851 size_t print_one(substr str,
const char* full_fmt, T v)
1857 int iret = _snprintf(str.str, str.len, full_fmt, v);
1863 iret = snprintf(
nullptr, 0, full_fmt, v);
1864 C4_ASSERT(iret > 0);
1866 size_t ret = (size_t) iret;
1869 int iret = snprintf(str.str, str.len, full_fmt, v);
1870 C4_ASSERT(iret >= 0);
1871 size_t ret = (size_t) iret;
1880 #if (!C4CORE_HAVE_STD_FROMCHARS) && (!C4CORE_HAVE_FAST_FLOAT)
1885 template<
typename T>
1886 inline size_t scan_one(csubstr str,
const char *type_fmt, T *v)
1905 int iret = std::snprintf(fmt,
sizeof(fmt),
"%%" "%zu" "%s" "%%n", str.len, type_fmt);
1907 C4_ASSERT(iret >= 0 &&
size_t(iret) < C4_COUNTOF(fmt));
1911 iret = std::sscanf(str.str, fmt, v, &num_chars);
1914 C4_ASSERT(num_chars >= 0);
1915 return (
size_t)(num_chars);
1920 #if C4CORE_HAVE_STD_TOCHARS
1922 C4_ALWAYS_INLINE
size_t rtoa(substr buf, T v,
int precision=-1,
RealFormat_e formatting=
FTOA_FLEX) noexcept
1924 std::to_chars_result result;
1928 if(buf.len >
size_t(2))
1936 result =
std::to_chars(buf.str + pos, buf.str + buf.len, v, (std::chars_format)formatting);
1938 result =
std::to_chars(buf.str + pos, buf.str + buf.len, v, (std::chars_format)formatting, precision);
1939 if(result.ec == std::errc())
1942 C4_ASSERT(result.ptr >= buf.str);
1943 ptrdiff_t delta = result.ptr - buf.str;
1944 return static_cast<size_t>(delta);
1946 C4_ASSERT(result.ec == std::errc::value_too_large);
1957 size_t ret =
static_cast<size_t>(std::numeric_limits<T>::max_digits10);
1958 return ret > buf.len ? ret : buf.len + 1;
1963 #if C4CORE_HAVE_FAST_FLOAT
1965 C4_ALWAYS_INLINE
bool scan_rhex(csubstr s, T *C4_RESTRICT val) noexcept
1967 C4_ASSERT(s.len > 0);
1968 C4_ASSERT(s.str[0] !=
'-');
1969 C4_ASSERT(s.str[0] !=
'+');
1970 C4_ASSERT(!s.begins_with(
"0x"));
1971 C4_ASSERT(!s.begins_with(
"0X"));
1974 for( ; pos < s.len; ++pos)
1976 const char c = s.str[pos];
1977 if(c >=
'0' && c <=
'9')
1978 *val = *val * T(16) + T(c -
'0');
1979 else if(c >=
'a' && c <=
'f')
1980 *val = *val * T(16) + T(c -
'a');
1981 else if(c >=
'A' && c <=
'F')
1982 *val = *val * T(16) + T(c -
'A');
1988 else if(c ==
'p' || c ==
'P')
2001 for(T digit = T(0.0625); pos < s.len; ++pos, digit /= T(16))
2003 const char c = s.str[pos];
2004 if(c >=
'0' && c <=
'9')
2005 *val += digit * T(c -
'0');
2006 else if(c >=
'a' && c <=
'f')
2007 *val += digit * T(c -
'a');
2008 else if(c >=
'A' && c <=
'F')
2009 *val += digit * T(c -
'A');
2010 else if(c ==
'p' || c ==
'P')
2023 if(C4_LIKELY(pos < s.len))
2025 if(s.str[pos] ==
'+')
2027 if(C4_LIKELY(pos < s.len))
2029 int16_t powval = {};
2030 if(C4_LIKELY(
atoi(s.sub(pos), &powval)))
2032 *val *= ipow<T, int16_t, 16>(powval);
2063 #if C4CORE_HAVE_STD_TOCHARS
2064 return detail::rtoa(str, v, precision, formatting);
2067 detail::get_real_format_str(fmt, precision, formatting,
"");
2068 return detail::print_one(str, fmt, v);
2089 #if C4CORE_HAVE_STD_TOCHARS
2090 return detail::rtoa(str, v, precision, formatting);
2093 detail::get_real_format_str(fmt, precision, formatting,
"l");
2094 return detail::print_one(str, fmt, v);
2111 C4_ALWAYS_INLINE
bool atof(csubstr str,
float * C4_RESTRICT v) noexcept
2113 C4_ASSERT(str.len > 0);
2114 C4_ASSERT(str.triml(
" \r\t\n").len == str.len);
2115 #if C4CORE_HAVE_FAST_FLOAT
2117 bool isneg = (str.str[0] ==
'-');
2118 csubstr rem = str.sub(isneg || str.str[0] ==
'+');
2119 if(!(rem.len >= 2 && (rem.str[0] ==
'0' && (rem.str[1] ==
'x' || rem.str[1] ==
'X'))))
2121 fast_float::from_chars_result result;
2123 return result.ec == std::errc();
2125 else if(detail::scan_rhex(rem.sub(2), v))
2127 *v *= isneg ? -1.f : 1.f;
2131 #elif C4CORE_HAVE_STD_FROMCHARS
2132 std::from_chars_result result;
2134 return result.ec == std::errc();
2136 csubstr rem = str.sub(str.str[0] ==
'-' || str.str[0] ==
'+');
2137 if(!(rem.len >= 2 && (rem.str[0] ==
'0' && (rem.str[1] ==
'x' || rem.str[1] ==
'X'))))
2149 inline size_t atof_first(csubstr str,
float * C4_RESTRICT v) noexcept
2151 csubstr trimmed = str.first_real_span();
2152 if(trimmed.len == 0)
2154 if(
atof(trimmed, v))
2155 return static_cast<size_t>(trimmed.end() - str.begin());
2172 C4_ALWAYS_INLINE
bool atod(csubstr str,
double * C4_RESTRICT v) noexcept
2174 C4_ASSERT(str.len > 0);
2175 C4_ASSERT(str.triml(
" \r\t\n").len == str.len);
2176 #if C4CORE_HAVE_FAST_FLOAT
2178 bool isneg = (str.str[0] ==
'-');
2179 csubstr rem = str.sub(isneg || str.str[0] ==
'+');
2180 if(!(rem.len >= 2 && (rem.str[0] ==
'0' && (rem.str[1] ==
'x' || rem.str[1] ==
'X'))))
2182 fast_float::from_chars_result result;
2184 return result.ec == std::errc();
2186 else if(detail::scan_rhex(rem.sub(2), v))
2188 *v *= isneg ? -1. : 1.;
2192 #elif C4CORE_HAVE_STD_FROMCHARS
2193 std::from_chars_result result;
2195 return result.ec == std::errc();
2197 csubstr rem = str.sub(str.str[0] ==
'-' || str.str[0] ==
'+');
2198 if(!(rem.len >= 2 && (rem.str[0] ==
'0' && (rem.str[1] ==
'x' || rem.str[1] ==
'X'))))
2210 inline size_t atod_first(csubstr str,
double * C4_RESTRICT v) noexcept
2212 csubstr trimmed = str.first_real_span();
2213 if(trimmed.len == 0)
2215 if(
atod(trimmed, v))
2216 return static_cast<size_t>(trimmed.end() - str.begin());
2231 #define _C4_IF_NOT_FIXED_LENGTH_I(T, ty) C4_ALWAYS_INLINE typename std::enable_if<std:: is_signed<T>::value && !is_fixed_length<T>::value_i, ty>
2232 #define _C4_IF_NOT_FIXED_LENGTH_U(T, ty) C4_ALWAYS_INLINE typename std::enable_if<std::is_unsigned<T>::value && !is_fixed_length<T>::value_u, ty>
2242 C4_ALWAYS_INLINE
size_t xtoa(substr s, uint8_t v) noexcept {
return write_dec(s, v); }
2243 C4_ALWAYS_INLINE
size_t xtoa(substr s, uint16_t v) noexcept {
return write_dec(s, v); }
2244 C4_ALWAYS_INLINE
size_t xtoa(substr s, uint32_t v) noexcept {
return write_dec(s, v); }
2245 C4_ALWAYS_INLINE
size_t xtoa(substr s, uint64_t v) noexcept {
return write_dec(s, v); }
2246 C4_ALWAYS_INLINE
size_t xtoa(substr s, int8_t v) noexcept {
return itoa(s, v); }
2247 C4_ALWAYS_INLINE
size_t xtoa(substr s, int16_t v) noexcept {
return itoa(s, v); }
2248 C4_ALWAYS_INLINE
size_t xtoa(substr s, int32_t v) noexcept {
return itoa(s, v); }
2249 C4_ALWAYS_INLINE
size_t xtoa(substr s, int64_t v) noexcept {
return itoa(s, v); }
2250 C4_ALWAYS_INLINE
size_t xtoa(substr s,
float v) noexcept {
return ftoa(s, v); }
2251 C4_ALWAYS_INLINE
size_t xtoa(substr s,
double v) noexcept {
return dtoa(s, v); }
2253 C4_ALWAYS_INLINE
size_t xtoa(substr s, uint8_t v, uint8_t radix) noexcept {
return utoa(s, v, radix); }
2254 C4_ALWAYS_INLINE
size_t xtoa(substr s, uint16_t v, uint16_t radix) noexcept {
return utoa(s, v, radix); }
2255 C4_ALWAYS_INLINE
size_t xtoa(substr s, uint32_t v, uint32_t radix) noexcept {
return utoa(s, v, radix); }
2256 C4_ALWAYS_INLINE
size_t xtoa(substr s, uint64_t v, uint64_t radix) noexcept {
return utoa(s, v, radix); }
2257 C4_ALWAYS_INLINE
size_t xtoa(substr s, int8_t v, int8_t radix) noexcept {
return itoa(s, v, radix); }
2258 C4_ALWAYS_INLINE
size_t xtoa(substr s, int16_t v, int16_t radix) noexcept {
return itoa(s, v, radix); }
2259 C4_ALWAYS_INLINE
size_t xtoa(substr s, int32_t v, int32_t radix) noexcept {
return itoa(s, v, radix); }
2260 C4_ALWAYS_INLINE
size_t xtoa(substr s, int64_t v, int64_t radix) noexcept {
return itoa(s, v, radix); }
2262 C4_ALWAYS_INLINE
size_t xtoa(substr s, uint8_t v, uint8_t radix,
size_t num_digits) noexcept {
return utoa(s, v, radix, num_digits); }
2263 C4_ALWAYS_INLINE
size_t xtoa(substr s, uint16_t v, uint16_t radix,
size_t num_digits) noexcept {
return utoa(s, v, radix, num_digits); }
2264 C4_ALWAYS_INLINE
size_t xtoa(substr s, uint32_t v, uint32_t radix,
size_t num_digits) noexcept {
return utoa(s, v, radix, num_digits); }
2265 C4_ALWAYS_INLINE
size_t xtoa(substr s, uint64_t v, uint64_t radix,
size_t num_digits) noexcept {
return utoa(s, v, radix, num_digits); }
2266 C4_ALWAYS_INLINE
size_t xtoa(substr s, int8_t v, int8_t radix,
size_t num_digits) noexcept {
return itoa(s, v, radix, num_digits); }
2267 C4_ALWAYS_INLINE
size_t xtoa(substr s, int16_t v, int16_t radix,
size_t num_digits) noexcept {
return itoa(s, v, radix, num_digits); }
2268 C4_ALWAYS_INLINE
size_t xtoa(substr s, int32_t v, int32_t radix,
size_t num_digits) noexcept {
return itoa(s, v, radix, num_digits); }
2269 C4_ALWAYS_INLINE
size_t xtoa(substr s, int64_t v, int64_t radix,
size_t num_digits) noexcept {
return itoa(s, v, radix, num_digits); }
2274 template <
class T> _C4_IF_NOT_FIXED_LENGTH_I(T,
size_t)::type
xtoa(substr buf, T v) noexcept { return
itoa(buf, v); }
2275 template <
class T> _C4_IF_NOT_FIXED_LENGTH_U(T,
size_t)::type
xtoa(substr buf, T v) noexcept { return
write_dec(buf, v); }
2277 C4_ALWAYS_INLINE
size_t xtoa(substr s, T *v) noexcept {
return itoa(s, (intptr_t)v, (intptr_t)16); }
2288 C4_ALWAYS_INLINE
bool atox(csubstr s, uint8_t *C4_RESTRICT v) noexcept {
return atou(s, v); }
2289 C4_ALWAYS_INLINE
bool atox(csubstr s, uint16_t *C4_RESTRICT v) noexcept {
return atou(s, v); }
2290 C4_ALWAYS_INLINE
bool atox(csubstr s, uint32_t *C4_RESTRICT v) noexcept {
return atou(s, v); }
2291 C4_ALWAYS_INLINE
bool atox(csubstr s, uint64_t *C4_RESTRICT v) noexcept {
return atou(s, v); }
2292 C4_ALWAYS_INLINE
bool atox(csubstr s, int8_t *C4_RESTRICT v) noexcept {
return atoi(s, v); }
2293 C4_ALWAYS_INLINE
bool atox(csubstr s, int16_t *C4_RESTRICT v) noexcept {
return atoi(s, v); }
2294 C4_ALWAYS_INLINE
bool atox(csubstr s, int32_t *C4_RESTRICT v) noexcept {
return atoi(s, v); }
2295 C4_ALWAYS_INLINE
bool atox(csubstr s, int64_t *C4_RESTRICT v) noexcept {
return atoi(s, v); }
2296 C4_ALWAYS_INLINE
bool atox(csubstr s,
float *C4_RESTRICT v) noexcept {
return atof(s, v); }
2297 C4_ALWAYS_INLINE
bool atox(csubstr s,
double *C4_RESTRICT v) noexcept {
return atod(s, v); }
2299 template <
class T> _C4_IF_NOT_FIXED_LENGTH_I(T,
bool )::type
atox(csubstr buf, T *C4_RESTRICT v) noexcept { return
atoi(buf, v); }
2300 template <
class T> _C4_IF_NOT_FIXED_LENGTH_U(T,
bool )::type
atox(csubstr buf, T *C4_RESTRICT v) noexcept { return
atou(buf, v); }
2302 C4_ALWAYS_INLINE
bool atox(csubstr s, T **v) noexcept { intptr_t tmp;
bool ret =
atox(s, &tmp);
if(ret) { *v = (T*)tmp; }
return ret; }
2332 C4_ALWAYS_INLINE
size_t to_chars(substr buf, int8_t v) noexcept {
return itoa(buf, v); }
2333 C4_ALWAYS_INLINE
size_t to_chars(substr buf, int16_t v) noexcept {
return itoa(buf, v); }
2334 C4_ALWAYS_INLINE
size_t to_chars(substr buf, int32_t v) noexcept {
return itoa(buf, v); }
2335 C4_ALWAYS_INLINE
size_t to_chars(substr buf, int64_t v) noexcept {
return itoa(buf, v); }
2336 C4_ALWAYS_INLINE
size_t to_chars(substr buf,
float v) noexcept {
return ftoa(buf, v); }
2337 C4_ALWAYS_INLINE
size_t to_chars(substr buf,
double v) noexcept {
return dtoa(buf, v); }
2339 template <
class T> _C4_IF_NOT_FIXED_LENGTH_I(T,
size_t)::type
to_chars(substr buf, T v) noexcept { return
itoa(buf, v); }
2340 template <
class T> _C4_IF_NOT_FIXED_LENGTH_U(T,
size_t)::type
to_chars(substr buf, T v) noexcept { return
write_dec(buf, v); }
2342 C4_ALWAYS_INLINE
size_t to_chars(substr s, T *v) noexcept {
return itoa(s, (intptr_t)v, (intptr_t)16); }
2363 C4_ALWAYS_INLINE
bool from_chars(csubstr buf, uint8_t *C4_RESTRICT v) noexcept {
return atou(buf, v); }
2364 C4_ALWAYS_INLINE
bool from_chars(csubstr buf, uint16_t *C4_RESTRICT v) noexcept {
return atou(buf, v); }
2365 C4_ALWAYS_INLINE
bool from_chars(csubstr buf, uint32_t *C4_RESTRICT v) noexcept {
return atou(buf, v); }
2366 C4_ALWAYS_INLINE
bool from_chars(csubstr buf, uint64_t *C4_RESTRICT v) noexcept {
return atou(buf, v); }
2367 C4_ALWAYS_INLINE
bool from_chars(csubstr buf, int8_t *C4_RESTRICT v) noexcept {
return atoi(buf, v); }
2368 C4_ALWAYS_INLINE
bool from_chars(csubstr buf, int16_t *C4_RESTRICT v) noexcept {
return atoi(buf, v); }
2369 C4_ALWAYS_INLINE
bool from_chars(csubstr buf, int32_t *C4_RESTRICT v) noexcept {
return atoi(buf, v); }
2370 C4_ALWAYS_INLINE
bool from_chars(csubstr buf, int64_t *C4_RESTRICT v) noexcept {
return atoi(buf, v); }
2371 C4_ALWAYS_INLINE
bool from_chars(csubstr buf,
float *C4_RESTRICT v) noexcept {
return atof(buf, v); }
2372 C4_ALWAYS_INLINE
bool from_chars(csubstr buf,
double *C4_RESTRICT v) noexcept {
return atod(buf, v); }
2374 template <
class T> _C4_IF_NOT_FIXED_LENGTH_I(T,
bool )::type
from_chars(csubstr buf, T *C4_RESTRICT v) noexcept { return
atoi(buf, v); }
2375 template <
class T> _C4_IF_NOT_FIXED_LENGTH_U(T,
bool )::type
from_chars(csubstr buf, T *C4_RESTRICT v) noexcept { return
atou(buf, v); }
2377 C4_ALWAYS_INLINE
bool from_chars(csubstr buf, T **v) noexcept { intptr_t tmp;
bool ret =
from_chars(buf, &tmp);
if(ret) { *v = (T*)tmp; }
return ret; }
2402 template <
class T> _C4_IF_NOT_FIXED_LENGTH_I(T,
size_t)::type
from_chars_first(csubstr buf, T *C4_RESTRICT v) noexcept { return
atoi_first(buf, v); }
2403 template <
class T> _C4_IF_NOT_FIXED_LENGTH_U(T,
size_t)::type
from_chars_first(csubstr buf, T *C4_RESTRICT v) noexcept { return
atou_first(buf, v); }
2411 #undef _C4_IF_NOT_FIXED_LENGTH_I
2412 #undef _C4_IF_NOT_FIXED_LENGTH_U
2427 C4_ALWAYS_INLINE substr
to_chars_sub(substr buf, T
const& C4_RESTRICT v) noexcept
2430 return buf.left_of(sz <= buf.len ? sz : buf.len);
2439 C4_ALWAYS_INLINE
size_t to_chars(substr buf,
bool v) noexcept
2446 inline bool from_chars(csubstr buf,
bool * C4_RESTRICT v) noexcept
2450 *v =
false;
return true;
2454 *v =
true;
return true;
2456 else if(buf ==
"false")
2458 *v =
false;
return true;
2460 else if(buf ==
"true")
2462 *v =
true;
return true;
2464 else if(buf ==
"False")
2466 *v =
false;
return true;
2468 else if(buf ==
"True")
2470 *v =
true;
return true;
2472 else if(buf ==
"FALSE")
2474 *v =
false;
return true;
2476 else if(buf ==
"TRUE")
2478 *v =
true;
return true;
2493 csubstr trimmed = buf.first_non_empty_span();
2508 C4_XASSERT(buf.str);
2518 inline bool from_chars(csubstr buf,
char * C4_RESTRICT v) noexcept
2522 C4_XASSERT(buf.str);
2543 C4_ASSERT(!buf.overlaps(v));
2544 size_t len = buf.len < v.len ? buf.len : v.len;
2550 C4_ASSERT(buf.str !=
nullptr);
2551 C4_ASSERT(v.str !=
nullptr);
2552 memcpy(buf.str, v.str, len);
2558 inline bool from_chars(csubstr buf, csubstr *C4_RESTRICT v) noexcept
2567 csubstr trimmed = buf.first_non_empty_span();
2568 if(trimmed.len == 0)
2571 return static_cast<size_t>(trimmed.end() - buf.begin());
2581 C4_ASSERT(!buf.overlaps(v));
2582 size_t len = buf.len < v.len ? buf.len : v.len;
2588 C4_ASSERT(buf.str !=
nullptr);
2589 C4_ASSERT(v.str !=
nullptr);
2590 memcpy(buf.str, v.str, len);
2596 inline bool from_chars(csubstr buf, substr * C4_RESTRICT v) noexcept
2598 C4_ASSERT(!buf.overlaps(*v));
2600 if(v->len >= buf.len)
2607 C4_ASSERT(buf.str !=
nullptr);
2608 C4_ASSERT(v->str !=
nullptr);
2609 memcpy(v->str, buf.str, buf.len);
2620 csubstr trimmed = buf.first_non_empty_span();
2621 C4_ASSERT(!trimmed.overlaps(*v));
2622 if(C4_UNLIKELY(trimmed.len == 0))
2624 size_t len = trimmed.len > v->len ? v->len : trimmed.len;
2630 C4_ASSERT(buf.str !=
nullptr);
2631 C4_ASSERT(v->str !=
nullptr);
2632 memcpy(v->str, trimmed.str, len);
2634 if(C4_UNLIKELY(trimmed.len > v->len))
2636 return static_cast<size_t>(trimmed.end() - buf.begin());
2644 inline size_t to_chars(substr buf,
const char (& C4_RESTRICT v)[N]) noexcept
2651 inline size_t to_chars(substr buf,
const char * C4_RESTRICT v) noexcept
2661 # pragma warning(pop)
2664 #if defined(__clang__)
2665 # pragma clang diagnostic pop
2666 #elif defined(__GNUC__)
2667 # 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
#define _c4append(first, last)