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 # include "c4/ext/fast_float.hpp"
61 # define C4CORE_HAVE_FAST_FLOAT 0
62 # if defined(_MSC_VER)
63 # if (C4_MSVC_VERSION >= C4_MSVC_VERSION_2019)
65 # define C4CORE_HAVE_STD_TOCHARS 1
66 # define C4CORE_HAVE_STD_FROMCHARS 1
68 # define C4CORE_HAVE_STD_TOCHARS 0
69 # define C4CORE_HAVE_STD_FROMCHARS 0
72 # if __has_include(<charconv>)
74 # if defined(__cpp_lib_to_chars)
75 # define C4CORE_HAVE_STD_TOCHARS 1
76 # define C4CORE_HAVE_STD_FROMCHARS 1
78 # define C4CORE_HAVE_STD_TOCHARS 0
79 # define C4CORE_HAVE_STD_FROMCHARS 0
82 # define C4CORE_HAVE_STD_TOCHARS 0
83 # define C4CORE_HAVE_STD_FROMCHARS 0
87 # define C4CORE_HAVE_STD_TOCHARS 0
88 # define C4CORE_HAVE_STD_FROMCHARS 0
89 # define C4CORE_HAVE_FAST_FLOAT 0
93 #if !C4CORE_HAVE_STD_FROMCHARS
98 #if defined(_MSC_VER) && !defined(__clang__)
99 # pragma warning(push)
100 # pragma warning(disable: 4996)
101 # if C4_MSVC_VERSION != C4_MSVC_VERSION_2017
102 # pragma warning(disable: 4800)
104 #elif defined(__clang__)
105 # pragma clang diagnostic push
106 # pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare"
107 # pragma clang diagnostic ignored "-Wformat-nonliteral"
108 # pragma clang diagnostic ignored "-Wdouble-promotion"
109 # pragma clang diagnostic ignored "-Wold-style-cast"
110 #elif defined(__GNUC__)
111 # pragma GCC diagnostic push
112 # pragma GCC diagnostic ignored "-Wformat-nonliteral"
113 # pragma GCC diagnostic ignored "-Wdouble-promotion"
114 # pragma GCC diagnostic ignored "-Wuseless-cast"
115 # pragma GCC diagnostic ignored "-Wold-style-cast"
118 #if defined(__clang__)
119 #define C4_NO_UBSAN_IOVRFLW __attribute__((no_sanitize("signed-integer-overflow")))
120 #elif defined(__GNUC__)
122 #define C4_NO_UBSAN_IOVRFLW __attribute__((no_sanitize("signed-integer-overflow")))
124 #define C4_NO_UBSAN_IOVRFLW
127 #define C4_NO_UBSAN_IOVRFLW
179 #if C4CORE_HAVE_STD_TOCHARS
181 typedef enum : std::underlying_type<std::chars_format>::type {
183 FTOA_FLOAT =
static_cast<std::underlying_type<std::chars_format>::type
>(std::chars_format::fixed),
185 FTOA_SCIENT =
static_cast<std::underlying_type<std::chars_format>::type
>(std::chars_format::scientific),
187 FTOA_FLEX =
static_cast<std::underlying_type<std::chars_format>::type
>(std::chars_format::general),
193 typedef enum :
char {
210 struct is_fixed_length
214 value_i = (std::is_integral<T>::value
215 && (std::is_same<T, int8_t>::value
216 || std::is_same<T, int16_t>::value
217 || std::is_same<T, int32_t>::value
218 || std::is_same<T, int64_t>::value)),
220 value_u = (std::is_integral<T>::value
221 && (std::is_same<T, uint8_t>::value
222 || std::is_same<T, uint16_t>::value
223 || std::is_same<T, uint32_t>::value
224 || std::is_same<T, uint64_t>::value)),
226 value = value_i || value_u
236 #if defined(_MSC_VER) && !defined(__clang__)
237 # pragma warning(push)
238 #elif defined(__clang__)
239 # pragma clang diagnostic push
240 #elif defined(__GNUC__)
241 # pragma GCC diagnostic push
242 # pragma GCC diagnostic ignored "-Wconversion"
244 # pragma GCC diagnostic ignored "-Wnull-dereference"
264 template<
size_t num_
bytes,
bool is_
signed>
struct charconv_digits_;
265 template<
class T>
using charconv_digits = charconv_digits_<
sizeof(T), std::is_signed<T>::value>;
267 template<>
struct charconv_digits_<1u, true>
270 maxdigits_bin = 1 + 2 + 8,
271 maxdigits_oct = 1 + 2 + 3,
272 maxdigits_dec = 1 + 3,
273 maxdigits_hex = 1 + 2 + 2,
274 maxdigits_bin_nopfx = 8,
275 maxdigits_oct_nopfx = 3,
276 maxdigits_dec_nopfx = 3,
277 maxdigits_hex_nopfx = 2,
280 static constexpr csubstr min_value_dec() noexcept {
return csubstr(
"128"); }
281 static constexpr csubstr min_value_hex() noexcept {
return csubstr(
"80"); }
282 static constexpr csubstr min_value_oct() noexcept {
return csubstr(
"200"); }
283 static constexpr csubstr min_value_bin() noexcept {
return csubstr(
"10000000"); }
284 static constexpr csubstr max_value_dec() noexcept {
return csubstr(
"127"); }
285 static constexpr
bool is_oct_overflow(csubstr str) noexcept {
return !((str.len < 3) || (str.len == 3 && str[0] <=
'1')); }
287 template<>
struct charconv_digits_<1u, false>
290 maxdigits_bin = 2 + 8,
291 maxdigits_oct = 2 + 3,
293 maxdigits_hex = 2 + 2,
294 maxdigits_bin_nopfx = 8,
295 maxdigits_oct_nopfx = 3,
296 maxdigits_dec_nopfx = 3,
297 maxdigits_hex_nopfx = 2,
299 static constexpr csubstr max_value_dec() noexcept {
return csubstr(
"255"); }
300 static constexpr
bool is_oct_overflow(csubstr str) noexcept {
return !((str.len < 3) || (str.len == 3 && str[0] <=
'3')); }
302 template<>
struct charconv_digits_<2u, true>
305 maxdigits_bin = 1 + 2 + 16,
306 maxdigits_oct = 1 + 2 + 6,
307 maxdigits_dec = 1 + 5,
308 maxdigits_hex = 1 + 2 + 4,
309 maxdigits_bin_nopfx = 16,
310 maxdigits_oct_nopfx = 6,
311 maxdigits_dec_nopfx = 5,
312 maxdigits_hex_nopfx = 4,
315 static constexpr csubstr min_value_dec() noexcept {
return csubstr(
"32768"); }
316 static constexpr csubstr min_value_hex() noexcept {
return csubstr(
"8000"); }
317 static constexpr csubstr min_value_oct() noexcept {
return csubstr(
"100000"); }
318 static constexpr csubstr min_value_bin() noexcept {
return csubstr(
"1000000000000000"); }
319 static constexpr csubstr max_value_dec() noexcept {
return csubstr(
"32767"); }
320 static constexpr
bool is_oct_overflow(csubstr str) noexcept {
return !((str.len < 6)); }
322 template<>
struct charconv_digits_<2u, false>
325 maxdigits_bin = 2 + 16,
326 maxdigits_oct = 2 + 6,
328 maxdigits_hex = 2 + 4,
329 maxdigits_bin_nopfx = 16,
330 maxdigits_oct_nopfx = 6,
331 maxdigits_dec_nopfx = 6,
332 maxdigits_hex_nopfx = 4,
334 static constexpr csubstr max_value_dec() noexcept {
return csubstr(
"65535"); }
335 static constexpr
bool is_oct_overflow(csubstr str) noexcept {
return !((str.len < 6) || (str.len == 6 && str[0] <=
'1')); }
337 template<>
struct charconv_digits_<4u, true>
340 maxdigits_bin = 1 + 2 + 32,
341 maxdigits_oct = 1 + 2 + 11,
342 maxdigits_dec = 1 + 10,
343 maxdigits_hex = 1 + 2 + 8,
344 maxdigits_bin_nopfx = 32,
345 maxdigits_oct_nopfx = 11,
346 maxdigits_dec_nopfx = 10,
347 maxdigits_hex_nopfx = 8,
350 static constexpr csubstr min_value_dec() noexcept {
return csubstr(
"2147483648"); }
351 static constexpr csubstr min_value_hex() noexcept {
return csubstr(
"80000000"); }
352 static constexpr csubstr min_value_oct() noexcept {
return csubstr(
"20000000000"); }
353 static constexpr csubstr min_value_bin() noexcept {
return csubstr(
"10000000000000000000000000000000"); }
354 static constexpr csubstr max_value_dec() noexcept {
return csubstr(
"2147483647"); }
355 static constexpr
bool is_oct_overflow(csubstr str) noexcept {
return !((str.len < 11) || (str.len == 11 && str[0] <=
'1')); }
357 template<>
struct charconv_digits_<4u, false>
360 maxdigits_bin = 2 + 32,
361 maxdigits_oct = 2 + 11,
363 maxdigits_hex = 2 + 8,
364 maxdigits_bin_nopfx = 32,
365 maxdigits_oct_nopfx = 11,
366 maxdigits_dec_nopfx = 10,
367 maxdigits_hex_nopfx = 8,
369 static constexpr csubstr max_value_dec() noexcept {
return csubstr(
"4294967295"); }
370 static constexpr
bool is_oct_overflow(csubstr str) noexcept {
return !((str.len < 11) || (str.len == 11 && str[0] <=
'3')); }
372 template<>
struct charconv_digits_<8u, true>
375 maxdigits_bin = 1 + 2 + 64,
376 maxdigits_oct = 1 + 2 + 22,
377 maxdigits_dec = 1 + 19,
378 maxdigits_hex = 1 + 2 + 16,
379 maxdigits_bin_nopfx = 64,
380 maxdigits_oct_nopfx = 22,
381 maxdigits_dec_nopfx = 19,
382 maxdigits_hex_nopfx = 16,
384 static constexpr csubstr min_value_dec() noexcept {
return csubstr(
"9223372036854775808"); }
385 static constexpr csubstr min_value_hex() noexcept {
return csubstr(
"8000000000000000"); }
386 static constexpr csubstr min_value_oct() noexcept {
return csubstr(
"1000000000000000000000"); }
387 static constexpr csubstr min_value_bin() noexcept {
return csubstr(
"1000000000000000000000000000000000000000000000000000000000000000"); }
388 static constexpr csubstr max_value_dec() noexcept {
return csubstr(
"9223372036854775807"); }
389 static constexpr
bool is_oct_overflow(csubstr str) noexcept {
return !((str.len < 22)); }
391 template<>
struct charconv_digits_<8u, false>
394 maxdigits_bin = 2 + 64,
395 maxdigits_oct = 2 + 22,
397 maxdigits_hex = 2 + 16,
398 maxdigits_bin_nopfx = 64,
399 maxdigits_oct_nopfx = 22,
400 maxdigits_dec_nopfx = 20,
401 maxdigits_hex_nopfx = 16,
403 static constexpr csubstr max_value_dec() noexcept {
return csubstr(
"18446744073709551615"); }
404 static constexpr
bool is_oct_overflow(csubstr str) noexcept {
return !((str.len < 22) || (str.len == 22 && str[0] <=
'1')); }
409 #define _c4append(c) { if(C4_LIKELY(pos < buf.len)) { buf.str[pos++] = static_cast<char>(c); } else { ++pos; } }
410 #define _c4appendhex(i) { if(C4_LIKELY(pos < buf.len)) { buf.str[pos++] = hexchars[i]; } else { ++pos; } }
433 C4_CONSTEXPR14 C4_ALWAYS_INLINE
435 ->
typename std::enable_if<
sizeof(T) == 1u,
unsigned>::type
437 C4_STATIC_ASSERT(std::is_integral<T>::value);
439 return ((v >= 100) ? 3u : ((v >= 10) ? 2u : 1u));
444 C4_CONSTEXPR14 C4_ALWAYS_INLINE
446 ->
typename std::enable_if<
sizeof(T) == 2u,
unsigned>::type
448 C4_STATIC_ASSERT(std::is_integral<T>::value);
450 return ((v >= 10000) ? 5u : (v >= 1000) ? 4u : (v >= 100) ? 3u : (v >= 10) ? 2u : 1u);
455 C4_CONSTEXPR14 C4_ALWAYS_INLINE
457 ->
typename std::enable_if<
sizeof(T) == 4u,
unsigned>::type
459 C4_STATIC_ASSERT(std::is_integral<T>::value);
461 return ((v >= 1000000000) ? 10u : (v >= 100000000) ? 9u : (v >= 10000000) ? 8u :
462 (v >= 1000000) ? 7u : (v >= 100000) ? 6u : (v >= 10000) ? 5u :
463 (v >= 1000) ? 4u : (v >= 100) ? 3u : (v >= 10) ? 2u : 1u);
468 C4_CONSTEXPR14 C4_ALWAYS_INLINE
470 ->
typename std::enable_if<
sizeof(T) == 8u,
unsigned>::type
474 C4_STATIC_ASSERT(std::is_integral<T>::value);
478 if(v >= 100000000000000)
480 if(v >= 100000000000000000)
482 if((
typename std::make_unsigned<T>::type)v >= 10000000000000000000u)
485 return (v >= 1000000000000000000) ? 19u : 18u;
487 else if(v >= 10000000000000000)
490 return(v >= 1000000000000000) ? 16u : 15u;
492 else if(v >= 1000000000000)
493 return (v >= 10000000000000) ? 14u : 13u;
494 else if(v >= 100000000000)
497 return(v >= 10000000000) ? 11u : 10u;
502 return (v >= 100000000) ? 9u : 8u;
503 else if(v >= 1000000)
506 return (v >= 100000) ? 6u : 5u;
509 return (v >= 1000) ? 4u : 3u;
511 return (v >= 10) ? 2u : 1u;
517 C4_CONSTEXPR14 C4_ALWAYS_INLINE
unsigned digits_hex(T v) noexcept
519 C4_STATIC_ASSERT(std::is_integral<T>::value);
521 return v ? 1u + (msb((
typename std::make_unsigned<T>::type)v) >> 2u) : 1u;
526 C4_CONSTEXPR14 C4_ALWAYS_INLINE
unsigned digits_bin(T v) noexcept
528 C4_STATIC_ASSERT(std::is_integral<T>::value);
530 return v ? 1u + msb((
typename std::make_unsigned<T>::type)v) : 1u;
535 C4_CONSTEXPR14 C4_ALWAYS_INLINE
unsigned digits_oct(T v_) noexcept
538 C4_STATIC_ASSERT(std::is_integral<T>::value);
540 using U =
typename std::conditional<
sizeof(T) <=
sizeof(
unsigned),
542 typename std::make_unsigned<T>::type>::type;
548 __b4 = 64u * 8u * 8u,
574 C4_INLINE_CONSTEXPR
const char hexchars[] =
"0123456789abcdef";
575 C4_INLINE_CONSTEXPR
const char digits0099[] =
576 "0001020304050607080910111213141516171819"
577 "2021222324252627282930313233343536373839"
578 "4041424344454647484950515253545556575859"
579 "6061626364656667686970717273747576777879"
580 "8081828384858687888990919293949596979899";
584 C4_SUPPRESS_WARNING_GCC_PUSH
585 C4_SUPPRESS_WARNING_GCC(
"-Warray-bounds")
586 #if (defined(__GNUC__) && (__GNUC__ >= 7))
587 C4_SUPPRESS_WARNING_GCC(
"-Wstringop-overflow")
601 C4_HOT C4_ALWAYS_INLINE
604 C4_STATIC_ASSERT(std::is_integral<T>::value);
606 C4_ASSERT(buf.len >= digits_v);
613 const auto num = (v - quo * T(100)) << 1u;
615 buf.str[--digits_v] = detail::digits0099[num + 1];
616 buf.str[--digits_v] = detail::digits0099[num];
620 C4_ASSERT(digits_v == 2);
621 const auto num = v << 1u;
622 buf.str[1] = detail::digits0099[num + 1];
623 buf.str[0] = detail::digits0099[num];
627 C4_ASSERT(digits_v == 1);
628 buf.str[0] = (char)(
'0' + v);
634 C4_HOT C4_ALWAYS_INLINE
637 C4_STATIC_ASSERT(std::is_integral<T>::value);
639 C4_ASSERT(buf.len >= digits_v);
642 buf.str[--digits_v] = detail::hexchars[v & T(15)];
645 C4_ASSERT(digits_v == 0);
650 C4_HOT C4_ALWAYS_INLINE
653 C4_STATIC_ASSERT(std::is_integral<T>::value);
655 C4_ASSERT(buf.len >= digits_v);
658 buf.str[--digits_v] = (char)(
'0' + (v & T(7)));
661 C4_ASSERT(digits_v == 0);
666 C4_HOT C4_ALWAYS_INLINE
669 C4_STATIC_ASSERT(std::is_integral<T>::value);
671 C4_ASSERT(buf.len >= digits_v);
674 buf.str[--digits_v] = (char)(
'0' + (v & T(1)));
677 C4_ASSERT(digits_v == 0);
702 C4_ALWAYS_INLINE
size_t write_dec(substr buf, T v) noexcept
704 C4_STATIC_ASSERT(std::is_integral<T>::value);
707 if(C4_LIKELY(buf.len >= digits))
721 C4_ALWAYS_INLINE
size_t write_hex(substr buf, T v) noexcept
723 C4_STATIC_ASSERT(std::is_integral<T>::value);
726 if(C4_LIKELY(buf.len >= digits))
740 C4_ALWAYS_INLINE
size_t write_oct(substr buf, T v) noexcept
742 C4_STATIC_ASSERT(std::is_integral<T>::value);
745 if(C4_LIKELY(buf.len >= digits))
759 C4_ALWAYS_INLINE
size_t write_bin(substr buf, T v) noexcept
761 C4_STATIC_ASSERT(std::is_integral<T>::value);
764 C4_ASSERT(digits > 0);
765 if(C4_LIKELY(buf.len >= digits))
773 template<
class U>
using NumberWriter = size_t (*)(substr, U);
774 template<
class T, NumberWriter<T> writer>
775 size_t write_num_digits(substr buf, T v,
size_t num_digits) noexcept
777 C4_STATIC_ASSERT(std::is_integral<T>::value);
778 const size_t ret = writer(buf, v);
779 if(ret >= num_digits)
781 else if(ret >= buf.len || num_digits > buf.len)
783 C4_ASSERT(num_digits >= ret);
784 const size_t delta =
static_cast<size_t>(num_digits - ret);
785 C4_ASSERT(ret + delta <= buf.len);
787 memmove(buf.str + delta, buf.str, ret);
789 memset(buf.str,
'0', delta);
800 C4_ALWAYS_INLINE
size_t write_dec(substr buf, T val,
size_t num_digits) noexcept
802 return detail::write_num_digits<T, &write_dec<T>>(buf, val, num_digits);
809 C4_ALWAYS_INLINE
size_t write_hex(substr buf, T val,
size_t num_digits) noexcept
811 return detail::write_num_digits<T, &write_hex<T>>(buf, val, num_digits);
818 C4_ALWAYS_INLINE
size_t write_bin(substr buf, T val,
size_t num_digits) noexcept
820 return detail::write_num_digits<T, &write_bin<T>>(buf, val, num_digits);
827 C4_ALWAYS_INLINE
size_t write_oct(substr buf, T val,
size_t num_digits) noexcept
829 return detail::write_num_digits<T, &write_oct<T>>(buf, val, num_digits);
834 C4_SUPPRESS_WARNING_GCC_POP
842 C4_SUPPRESS_WARNING_MSVC_PUSH
843 C4_SUPPRESS_WARNING_MSVC(4365)
862 C4_ALWAYS_INLINE
bool read_dec(csubstr s, I *C4_RESTRICT v) noexcept
864 C4_STATIC_ASSERT(std::is_integral<I>::value);
865 C4_ASSERT(!s.empty());
869 if(C4_UNLIKELY(c < '0' || c >
'9'))
871 *v = (*v) * I(10) + (I(c) - I(
'0'));
890 C4_ALWAYS_INLINE
bool read_hex(csubstr s, I *C4_RESTRICT v) noexcept
892 C4_STATIC_ASSERT(std::is_integral<I>::value);
893 C4_ASSERT(!s.empty());
898 if(c >=
'0' && c <=
'9')
900 else if(c >=
'a' && c <=
'f')
901 cv = I(10) + (I(c) - I(
'a'));
902 else if(c >=
'A' && c <=
'F')
903 cv = I(10) + (I(c) - I(
'A'));
906 *v = (*v) * I(16) + cv;
925 C4_ALWAYS_INLINE
bool read_bin(csubstr s, I *C4_RESTRICT v) noexcept
927 C4_STATIC_ASSERT(std::is_integral<I>::value);
928 C4_ASSERT(!s.empty());
955 C4_ALWAYS_INLINE
bool read_oct(csubstr s, I *C4_RESTRICT v) noexcept
957 C4_STATIC_ASSERT(std::is_integral<I>::value);
958 C4_ASSERT(!s.empty());
962 if(C4_UNLIKELY(c < '0' || c >
'7'))
964 *v = (*v) * I(8) + (I(c) - I(
'0'));
971 C4_SUPPRESS_WARNING_MSVC_POP
978 C4_SUPPRESS_WARNING_GCC_WITH_PUSH(
"-Wswitch-default")
982 inline size_t _itoa2buf(substr buf,
size_t pos, csubstr val) noexcept
984 C4_ASSERT(pos < buf.len);
985 C4_ASSERT(pos + val.len <= buf.len);
986 C4_ASSERT(val.len > 0);
987 memcpy(buf.str + pos, val.str, val.len);
988 return pos + val.len;
990 inline size_t _itoa2bufwithdigits(substr buf,
size_t pos,
size_t num_digits, csubstr val) noexcept
992 num_digits = num_digits > val.len ? num_digits - val.len : 0;
993 C4_ASSERT(num_digits + val.len <= buf.len);
994 for(
size_t i = 0; i < num_digits; ++i)
996 return detail::_itoa2buf(buf, pos, val);
999 C4_NO_INLINE
size_t _itoadec2buf(substr buf) noexcept
1001 using digits_type = detail::charconv_digits<I>;
1002 if(C4_UNLIKELY(buf.len < digits_type::maxdigits_dec))
1003 return digits_type::maxdigits_dec;
1005 return detail::_itoa2buf(buf, 1, digits_type::min_value_dec());
1008 C4_NO_INLINE
size_t _itoa2buf(substr buf, I radix) noexcept
1010 using digits_type = detail::charconv_digits<I>;
1012 if(C4_LIKELY(buf.len > 0))
1013 buf.str[pos++] =
'-';
1017 if(C4_UNLIKELY(buf.len < digits_type::maxdigits_dec))
1018 return digits_type::maxdigits_dec;
1019 pos =_itoa2buf(buf, pos, digits_type::min_value_dec());
1022 if(C4_UNLIKELY(buf.len < digits_type::maxdigits_hex))
1023 return digits_type::maxdigits_hex;
1024 buf.str[pos++] =
'0';
1025 buf.str[pos++] =
'x';
1026 pos = _itoa2buf(buf, pos, digits_type::min_value_hex());
1029 if(C4_UNLIKELY(buf.len < digits_type::maxdigits_bin))
1030 return digits_type::maxdigits_bin;
1031 buf.str[pos++] =
'0';
1032 buf.str[pos++] =
'b';
1033 pos = _itoa2buf(buf, pos, digits_type::min_value_bin());
1036 if(C4_UNLIKELY(buf.len < digits_type::maxdigits_oct))
1037 return digits_type::maxdigits_oct;
1038 buf.str[pos++] =
'0';
1039 buf.str[pos++] =
'o';
1040 pos = _itoa2buf(buf, pos, digits_type::min_value_oct());
1046 C4_NO_INLINE
size_t _itoa2buf(substr buf, I radix,
size_t num_digits) noexcept
1048 using digits_type = detail::charconv_digits<I>;
1050 size_t needed_digits = 0;
1051 if(C4_LIKELY(buf.len > 0))
1052 buf.str[pos++] =
'-';
1057 needed_digits = num_digits+1 > digits_type::maxdigits_dec ? num_digits+1 : digits_type::maxdigits_dec;
1058 if(C4_UNLIKELY(buf.len < needed_digits))
1059 return needed_digits;
1060 pos = _itoa2bufwithdigits(buf, pos, num_digits, digits_type::min_value_dec());
1064 needed_digits = num_digits+3 > digits_type::maxdigits_hex ? num_digits+3 : digits_type::maxdigits_hex;
1065 if(C4_UNLIKELY(buf.len < needed_digits))
1066 return needed_digits;
1067 buf.str[pos++] =
'0';
1068 buf.str[pos++] =
'x';
1069 pos = _itoa2bufwithdigits(buf, pos, num_digits, digits_type::min_value_hex());
1073 needed_digits = num_digits+3 > digits_type::maxdigits_bin ? num_digits+3 : digits_type::maxdigits_bin;
1074 if(C4_UNLIKELY(buf.len < needed_digits))
1075 return needed_digits;
1076 C4_ASSERT(buf.len >= digits_type::maxdigits_bin);
1077 buf.str[pos++] =
'0';
1078 buf.str[pos++] =
'b';
1079 pos = _itoa2bufwithdigits(buf, pos, num_digits, digits_type::min_value_bin());
1083 needed_digits = num_digits+3 > digits_type::maxdigits_oct ? num_digits+3 : digits_type::maxdigits_oct;
1084 if(C4_UNLIKELY(buf.len < needed_digits))
1085 return needed_digits;
1086 C4_ASSERT(buf.len >= digits_type::maxdigits_oct);
1087 buf.str[pos++] =
'0';
1088 buf.str[pos++] =
'o';
1089 pos = _itoa2bufwithdigits(buf, pos, num_digits, digits_type::min_value_oct());
1108 C4_ALWAYS_INLINE
size_t itoa(substr buf, T v) noexcept
1110 C4_STATIC_ASSERT(std::is_signed<T>::value);
1118 if(C4_LIKELY(v != std::numeric_limits<T>::min()))
1122 if(C4_LIKELY(buf.len >= digits + 1u))
1129 return detail::_itoadec2buf<T>(buf);
1140 C4_ALWAYS_INLINE
size_t itoa(substr buf, T v, T radix) noexcept
1142 C4_STATIC_ASSERT(std::is_signed<T>::value);
1143 C4_ASSERT(radix == 2 || radix == 8 || radix == 10 || radix == 16);
1144 C4_SUPPRESS_WARNING_GCC_PUSH
1145 #if (defined(__GNUC__) && (__GNUC__ >= 7))
1146 C4_SUPPRESS_WARNING_GCC(
"-Wstringop-overflow")
1150 if(C4_LIKELY(v != std::numeric_limits<T>::min()))
1156 if(C4_LIKELY(buf.len > 0))
1160 unsigned digits = 0;
1165 if(C4_LIKELY(buf.len >= pos + digits))
1170 if(C4_LIKELY(buf.len >= pos + 2u + digits))
1172 buf.str[pos + 0] =
'0';
1173 buf.str[pos + 1] =
'x';
1180 if(C4_LIKELY(buf.len >= pos + 2u + digits))
1182 buf.str[pos + 0] =
'0';
1183 buf.str[pos + 1] =
'b';
1190 if(C4_LIKELY(buf.len >= pos + 2u + digits))
1192 buf.str[pos + 0] =
'0';
1193 buf.str[pos + 1] =
'o';
1199 return pos + digits;
1201 C4_SUPPRESS_WARNING_GCC_POP
1204 return detail::_itoa2buf<T>(buf, radix);
1217 C4_ALWAYS_INLINE
size_t itoa(substr buf, T v, T radix,
size_t num_digits) noexcept
1219 C4_STATIC_ASSERT(std::is_signed<T>::value);
1220 C4_ASSERT(radix == 2 || radix == 8 || radix == 10 || radix == 16);
1221 C4_SUPPRESS_WARNING_GCC_PUSH
1222 #if (defined(__GNUC__) && (__GNUC__ >= 7))
1223 C4_SUPPRESS_WARNING_GCC(
"-Wstringop-overflow")
1227 if(C4_LIKELY(v != std::numeric_limits<T>::min()))
1233 if(C4_LIKELY(buf.len > 0))
1237 unsigned total_digits = 0;
1242 total_digits = pos + (unsigned)(num_digits > total_digits ? num_digits : total_digits);
1243 if(C4_LIKELY(buf.len >= total_digits))
1248 total_digits = pos + 2u + (unsigned)(num_digits > total_digits ? num_digits : total_digits);
1249 if(C4_LIKELY(buf.len >= total_digits))
1251 buf.str[pos + 0] =
'0';
1252 buf.str[pos + 1] =
'x';
1253 write_hex(buf.sub(pos + 2), v, num_digits);
1258 total_digits = pos + 2u + (unsigned)(num_digits > total_digits ? num_digits : total_digits);
1259 if(C4_LIKELY(buf.len >= total_digits))
1261 buf.str[pos + 0] =
'0';
1262 buf.str[pos + 1] =
'b';
1263 write_bin(buf.sub(pos + 2), v, num_digits);
1268 total_digits = pos + 2u + (unsigned)(num_digits > total_digits ? num_digits : total_digits);
1269 if(C4_LIKELY(buf.len >= total_digits))
1271 buf.str[pos + 0] =
'0';
1272 buf.str[pos + 1] =
'o';
1273 write_oct(buf.sub(pos + 2), v, num_digits);
1277 return total_digits;
1279 C4_SUPPRESS_WARNING_GCC_POP
1282 return detail::_itoa2buf<T>(buf, radix, num_digits);
1303 C4_ALWAYS_INLINE
size_t utoa(substr buf, T v) noexcept
1305 C4_STATIC_ASSERT(std::is_unsigned<T>::value);
1318 C4_ALWAYS_INLINE
size_t utoa(substr buf, T v, T radix) noexcept
1320 C4_STATIC_ASSERT(std::is_unsigned<T>::value);
1321 C4_ASSERT(radix == 10 || radix == 16 || radix == 2 || radix == 8);
1322 unsigned digits = 0;
1327 if(C4_LIKELY(buf.len >= digits))
1332 if(C4_LIKELY(buf.len >= digits+2u))
1342 if(C4_LIKELY(buf.len >= digits+2u))
1352 if(C4_LIKELY(buf.len >= digits+2u))
1373 C4_ALWAYS_INLINE
size_t utoa(substr buf, T v, T radix,
size_t num_digits) noexcept
1375 C4_STATIC_ASSERT(std::is_unsigned<T>::value);
1376 C4_ASSERT(radix == 10 || radix == 16 || radix == 2 || radix == 8);
1377 unsigned total_digits = 0;
1382 total_digits = (unsigned)(num_digits > total_digits ? num_digits : total_digits);
1383 if(C4_LIKELY(buf.len >= total_digits))
1388 total_digits = 2u + (unsigned)(num_digits > total_digits ? num_digits : total_digits);
1389 if(C4_LIKELY(buf.len >= total_digits))
1398 total_digits = 2u + (unsigned)(num_digits > total_digits ? num_digits : total_digits);
1399 if(C4_LIKELY(buf.len >= total_digits))
1408 total_digits = 2u + (unsigned)(num_digits > total_digits ? num_digits : total_digits);
1409 if(C4_LIKELY(buf.len >= total_digits))
1417 return total_digits;
1419 C4_SUPPRESS_WARNING_GCC_POP
1454 C4_ALWAYS_INLINE
bool atoi(csubstr str, T * C4_RESTRICT v) noexcept
1456 C4_STATIC_ASSERT(std::is_integral<T>::value);
1457 C4_STATIC_ASSERT(std::is_signed<T>::value);
1459 if(C4_UNLIKELY(str.len == 0))
1462 C4_ASSERT(str.str[0] !=
'+');
1466 if(str.str[0] ==
'-')
1468 if(C4_UNLIKELY(str.len == ++start))
1473 bool parsed_ok =
true;
1474 if(str.str[start] !=
'0')
1476 parsed_ok =
read_dec(str.sub(start), v);
1478 else if(str.len > start + 1)
1481 const char pfx = str.str[start + 1];
1482 if(pfx ==
'x' || pfx ==
'X')
1483 parsed_ok = str.len > start + 2 &&
read_hex(str.sub(start + 2), v);
1484 else if(pfx ==
'b' || pfx ==
'B')
1485 parsed_ok = str.len > start + 2 &&
read_bin(str.sub(start + 2), v);
1486 else if(pfx ==
'o' || pfx ==
'O')
1487 parsed_ok = str.len > start + 2 &&
read_oct(str.sub(start + 2), v);
1489 parsed_ok =
read_dec(str.sub(start + 1), v);
1493 parsed_ok =
read_dec(str.sub(start), v);
1495 if(C4_LIKELY(parsed_ok))
1508 C4_ALWAYS_INLINE
size_t atoi_first(csubstr str, T * C4_RESTRICT v)
1510 csubstr trimmed = str.first_int_span();
1511 if(trimmed.len == 0)
1513 if(
atoi(trimmed, v))
1514 return static_cast<size_t>(trimmed.end() - str.begin());
1547 bool atou(csubstr str, T * C4_RESTRICT v) noexcept
1549 C4_STATIC_ASSERT(std::is_integral<T>::value);
1551 if(C4_UNLIKELY(str.len == 0 || str.front() ==
'-'))
1554 bool parsed_ok =
true;
1555 if(str.str[0] !=
'0')
1563 const char pfx = str.str[1];
1564 if(pfx ==
'x' || pfx ==
'X')
1565 parsed_ok = str.len > 2 &&
read_hex(str.sub(2), v);
1566 else if(pfx ==
'b' || pfx ==
'B')
1567 parsed_ok = str.len > 2 &&
read_bin(str.sub(2), v);
1568 else if(pfx ==
'o' || pfx ==
'O')
1569 parsed_ok = str.len > 2 &&
read_oct(str.sub(2), v);
1591 csubstr trimmed = str.first_uint_span();
1592 if(trimmed.len == 0)
1594 if(
atou(trimmed, v))
1595 return static_cast<size_t>(trimmed.end() - str.begin());
1602 #if defined(_MSC_VER) && !defined(__clang__)
1603 # pragma warning(pop)
1604 #elif defined(__clang__)
1605 # pragma clang diagnostic pop
1606 #elif defined(__GNUC__)
1607 # pragma GCC diagnostic pop
1617 inline bool check_overflow(csubstr str, csubstr limit) noexcept
1619 if(str.len != limit.len)
1620 return str.len > limit.len;
1621 for(
size_t i = 0; i < limit.len; ++i)
1623 if(str[i] < limit[i])
1625 else if(str[i] > limit[i])
1646 ->
typename std::enable_if<std::is_unsigned<T>::value,
bool>::type
1648 C4_STATIC_ASSERT(std::is_integral<T>::value);
1650 if(C4_UNLIKELY(str.len == 0))
1654 else if(str.str[0] ==
'0')
1663 size_t fno = str.first_not_of(
'0', 2);
1666 return !(str.len <= fno + (
sizeof(T) * 2));
1671 size_t fno = str.first_not_of(
'0', 2);
1674 return !(str.len <= fno +(
sizeof(T) * 8));
1679 size_t fno = str.first_not_of(
'0', 2);
1682 return detail::charconv_digits<T>::is_oct_overflow(str.sub(fno));
1686 size_t fno = str.first_not_of(
'0', 1);
1689 return detail::check_overflow(str.sub(fno), detail::charconv_digits<T>::max_value_dec());
1693 else if(C4_UNLIKELY(str[0] ==
'-'))
1699 return detail::check_overflow(str, detail::charconv_digits<T>::max_value_dec());
1713 ->
typename std::enable_if<std::is_signed<T>::value,
bool>::type
1715 C4_STATIC_ASSERT(std::is_integral<T>::value);
1716 if(C4_UNLIKELY(str.len == 0))
1718 if(str.str[0] ==
'-')
1720 if(str.str[1] ==
'0')
1729 size_t fno = str.first_not_of(
'0', 3);
1732 return detail::check_overflow(str.sub(fno), detail::charconv_digits<T>::min_value_hex());
1737 size_t fno = str.first_not_of(
'0', 3);
1740 return detail::check_overflow(str.sub(fno), detail::charconv_digits<T>::min_value_bin());
1745 size_t fno = str.first_not_of(
'0', 3);
1748 return detail::check_overflow(str.sub(fno), detail::charconv_digits<T>::min_value_oct());
1752 size_t fno = str.first_not_of(
'0', 2);
1755 return detail::check_overflow(str.sub(fno), detail::charconv_digits<T>::min_value_dec());
1761 return detail::check_overflow(str.sub(1), detail::charconv_digits<T>::min_value_dec());
1764 else if(str.str[0] ==
'0')
1773 size_t fno = str.first_not_of(
'0', 2);
1776 const size_t len = str.len - fno;
1777 return !((len <
sizeof (T) * 2) || (len ==
sizeof(T) * 2 && str[fno] <=
'7'));
1782 size_t fno = str.first_not_of(
'0', 2);
1785 return !(str.len <= fno + (
sizeof(T) * 8 - 1));
1790 size_t fno = str.first_not_of(
'0', 2);
1793 return detail::charconv_digits<T>::is_oct_overflow(str.sub(fno));
1797 size_t fno = str.first_not_of(
'0', 1);
1800 return detail::check_overflow(str.sub(fno), detail::charconv_digits<T>::max_value_dec());
1806 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) 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) 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_ALWAYS_INLINE
auto xtoa(substr buf, T v) noexcept -> _C4_IF_NOT_FIXED_LENGTH_I(T,
size_t)::type { return
itoa(buf, v); }
2275 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); }
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_ALWAYS_INLINE
auto atox(csubstr buf, T *C4_RESTRICT v) noexcept -> _C4_IF_NOT_FIXED_LENGTH_I(T,
bool)::type { return
atoi(buf, v); }
2300 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); }
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_ALWAYS_INLINE
auto to_chars(substr buf, T v) noexcept -> _C4_IF_NOT_FIXED_LENGTH_I(T,
size_t)::type { return
itoa(buf, v); }
2340 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); }
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_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); }
2375 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); }
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_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); }
2403 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); }
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
2662 #if defined(_MSC_VER) && !defined(__clang__)
2663 # pragma warning(pop)
2664 #elif 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)