1#ifndef _C4_CHARCONV_HPP_
2#define _C4_CHARCONV_HPP_
8#include "c4/language.hpp"
15#include "c4/config.hpp"
17#include "c4/std/std_fwd.hpp"
18#include "c4/memory_util.hpp"
20#ifndef C4CORE_NO_FAST_FLOAT
23# if (C4_MSVC_VERSION >= C4_MSVC_VERSION_2019)
25# define C4CORE_HAVE_STD_TOCHARS 1
26# define C4CORE_HAVE_STD_FROMCHARS 0
27# define C4CORE_HAVE_FAST_FLOAT 1
29# define C4CORE_HAVE_STD_TOCHARS 0
30# define C4CORE_HAVE_STD_FROMCHARS 0
31# define C4CORE_HAVE_FAST_FLOAT 1
34# if __has_include(<charconv>)
36# if defined(__cpp_lib_to_chars)
37# define C4CORE_HAVE_STD_TOCHARS 1
38# define C4CORE_HAVE_STD_FROMCHARS 0
39# define C4CORE_HAVE_FAST_FLOAT 1
41# define C4CORE_HAVE_STD_TOCHARS 0
42# define C4CORE_HAVE_STD_FROMCHARS 0
43# define C4CORE_HAVE_FAST_FLOAT 1
46# define C4CORE_HAVE_STD_TOCHARS 0
47# define C4CORE_HAVE_STD_FROMCHARS 0
48# define C4CORE_HAVE_FAST_FLOAT 1
52# define C4CORE_HAVE_STD_TOCHARS 0
53# define C4CORE_HAVE_STD_FROMCHARS 0
54# define C4CORE_HAVE_FAST_FLOAT 1
56# if C4CORE_HAVE_FAST_FLOAT
57# include "c4/ext/fast_float.hpp"
60# define C4CORE_HAVE_FAST_FLOAT 0
62# if (C4_MSVC_VERSION >= C4_MSVC_VERSION_2019)
64# define C4CORE_HAVE_STD_TOCHARS 1
65# define C4CORE_HAVE_STD_FROMCHARS 1
67# define C4CORE_HAVE_STD_TOCHARS 0
68# define C4CORE_HAVE_STD_FROMCHARS 0
71# if __has_include(<charconv>)
73# if defined(__cpp_lib_to_chars)
74# define C4CORE_HAVE_STD_TOCHARS 1
75# define C4CORE_HAVE_STD_FROMCHARS 1
77# define C4CORE_HAVE_STD_TOCHARS 0
78# define C4CORE_HAVE_STD_FROMCHARS 0
81# define C4CORE_HAVE_STD_TOCHARS 0
82# define C4CORE_HAVE_STD_FROMCHARS 0
86# define C4CORE_HAVE_STD_TOCHARS 0
87# define C4CORE_HAVE_STD_FROMCHARS 0
88# define C4CORE_HAVE_FAST_FLOAT 0
92#if !C4CORE_HAVE_STD_FROMCHARS
97#if defined(_MSC_VER) && !defined(__clang__)
99# pragma warning(disable: 4996)
100# if C4_MSVC_VERSION != C4_MSVC_VERSION_2017
101# pragma warning(disable: 4800)
103#elif defined(__clang__)
104# pragma clang diagnostic push
105# pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare"
106# pragma clang diagnostic ignored "-Wformat-nonliteral"
107# pragma clang diagnostic ignored "-Wdouble-promotion"
108# pragma clang diagnostic ignored "-Wold-style-cast"
109#elif defined(__GNUC__)
110# pragma GCC diagnostic push
111# pragma GCC diagnostic ignored "-Wformat-nonliteral"
112# pragma GCC diagnostic ignored "-Wdouble-promotion"
113# pragma GCC diagnostic ignored "-Wuseless-cast"
114# pragma GCC diagnostic ignored "-Wold-style-cast"
117#if defined(__clang__)
118#define C4_NO_UBSAN_IOVRFLW __attribute__((no_sanitize("signed-integer-overflow")))
119#elif defined(__GNUC__)
121#define C4_NO_UBSAN_IOVRFLW __attribute__((no_sanitize("signed-integer-overflow")))
123#define C4_NO_UBSAN_IOVRFLW
126#define C4_NO_UBSAN_IOVRFLW
178#if C4CORE_HAVE_STD_TOCHARS
180typedef enum : std::underlying_type<std::chars_format>::type {
182 FTOA_FLOAT =
static_cast<std::underlying_type<std::chars_format>::type
>(std::chars_format::fixed),
184 FTOA_SCIENT =
static_cast<std::underlying_type<std::chars_format>::type
>(std::chars_format::scientific),
186 FTOA_FLEX =
static_cast<std::underlying_type<std::chars_format>::type
>(std::chars_format::general),
188 FTOA_HEXA =
static_cast<std::underlying_type<std::chars_format>::type
>(std::chars_format::hex),
209struct is_fixed_length
213 value_i = (std::is_integral<T>::value
214 && (std::is_same<T, int8_t>::value
215 || std::is_same<T, int16_t>::value
216 || std::is_same<T, int32_t>::value
217 || std::is_same<T, int64_t>::value)),
219 value_u = (std::is_integral<T>::value
220 && (std::is_same<T, uint8_t>::value
221 || std::is_same<T, uint16_t>::value
222 || std::is_same<T, uint32_t>::value
223 || std::is_same<T, uint64_t>::value)),
225 value = value_i || value_u
235#if defined(_MSC_VER) && !defined(__clang__)
236# pragma warning(push)
237#elif defined(__clang__)
238# pragma clang diagnostic push
239#elif defined(__GNUC__)
240# pragma GCC diagnostic push
241# pragma GCC diagnostic ignored "-Wconversion"
243# pragma GCC diagnostic ignored "-Wnull-dereference"
263template<
size_t num_
bytes,
bool is_
signed>
struct charconv_digits_;
264template<
class T>
using charconv_digits = charconv_digits_<
sizeof(T), std::is_signed<T>::value>;
266template<>
struct charconv_digits_<1u, true>
269 maxdigits_bin = 1 + 2 + 8,
270 maxdigits_oct = 1 + 2 + 3,
271 maxdigits_dec = 1 + 3,
272 maxdigits_hex = 1 + 2 + 2,
273 maxdigits_bin_nopfx = 8,
274 maxdigits_oct_nopfx = 3,
275 maxdigits_dec_nopfx = 3,
276 maxdigits_hex_nopfx = 2,
279 static constexpr csubstr min_value_dec() noexcept {
return csubstr(
"128"); }
280 static constexpr csubstr min_value_hex() noexcept {
return csubstr(
"80"); }
281 static constexpr csubstr min_value_oct() noexcept {
return csubstr(
"200"); }
282 static constexpr csubstr min_value_bin() noexcept {
return csubstr(
"10000000"); }
283 static constexpr csubstr max_value_dec() noexcept {
return csubstr(
"127"); }
284 static constexpr bool is_oct_overflow(csubstr str)
noexcept {
return !((str.len < 3) || (str.len == 3 && str.str[0] <=
'1')); }
286template<>
struct charconv_digits_<1u, false>
289 maxdigits_bin = 2 + 8,
290 maxdigits_oct = 2 + 3,
292 maxdigits_hex = 2 + 2,
293 maxdigits_bin_nopfx = 8,
294 maxdigits_oct_nopfx = 3,
295 maxdigits_dec_nopfx = 3,
296 maxdigits_hex_nopfx = 2,
298 static constexpr csubstr max_value_dec() noexcept {
return csubstr(
"255"); }
299 static constexpr bool is_oct_overflow(csubstr str)
noexcept {
return !((str.len < 3) || (str.len == 3 && str.str[0] <=
'3')); }
301template<>
struct charconv_digits_<2u, true>
304 maxdigits_bin = 1 + 2 + 16,
305 maxdigits_oct = 1 + 2 + 6,
306 maxdigits_dec = 1 + 5,
307 maxdigits_hex = 1 + 2 + 4,
308 maxdigits_bin_nopfx = 16,
309 maxdigits_oct_nopfx = 6,
310 maxdigits_dec_nopfx = 5,
311 maxdigits_hex_nopfx = 4,
314 static constexpr csubstr min_value_dec() noexcept {
return csubstr(
"32768"); }
315 static constexpr csubstr min_value_hex() noexcept {
return csubstr(
"8000"); }
316 static constexpr csubstr min_value_oct() noexcept {
return csubstr(
"100000"); }
317 static constexpr csubstr min_value_bin() noexcept {
return csubstr(
"1000000000000000"); }
318 static constexpr csubstr max_value_dec() noexcept {
return csubstr(
"32767"); }
319 static constexpr bool is_oct_overflow(csubstr str)
noexcept {
return !(str.len < 6); }
321template<>
struct charconv_digits_<2u, false>
324 maxdigits_bin = 2 + 16,
325 maxdigits_oct = 2 + 6,
327 maxdigits_hex = 2 + 4,
328 maxdigits_bin_nopfx = 16,
329 maxdigits_oct_nopfx = 6,
330 maxdigits_dec_nopfx = 6,
331 maxdigits_hex_nopfx = 4,
333 static constexpr csubstr max_value_dec() noexcept {
return csubstr(
"65535"); }
334 static constexpr bool is_oct_overflow(csubstr str)
noexcept {
return !((str.len < 6) || (str.len == 6 && str.str[0] <=
'1')); }
336template<>
struct charconv_digits_<4u, true>
339 maxdigits_bin = 1 + 2 + 32,
340 maxdigits_oct = 1 + 2 + 11,
341 maxdigits_dec = 1 + 10,
342 maxdigits_hex = 1 + 2 + 8,
343 maxdigits_bin_nopfx = 32,
344 maxdigits_oct_nopfx = 11,
345 maxdigits_dec_nopfx = 10,
346 maxdigits_hex_nopfx = 8,
349 static constexpr csubstr min_value_dec() noexcept {
return csubstr(
"2147483648"); }
350 static constexpr csubstr min_value_hex() noexcept {
return csubstr(
"80000000"); }
351 static constexpr csubstr min_value_oct() noexcept {
return csubstr(
"20000000000"); }
352 static constexpr csubstr min_value_bin() noexcept {
return csubstr(
"10000000000000000000000000000000"); }
353 static constexpr csubstr max_value_dec() noexcept {
return csubstr(
"2147483647"); }
354 static constexpr bool is_oct_overflow(csubstr str)
noexcept {
return !((str.len < 11) || (str.len == 11 && str.str[0] <=
'1')); }
356template<>
struct charconv_digits_<4u, false>
359 maxdigits_bin = 2 + 32,
360 maxdigits_oct = 2 + 11,
362 maxdigits_hex = 2 + 8,
363 maxdigits_bin_nopfx = 32,
364 maxdigits_oct_nopfx = 11,
365 maxdigits_dec_nopfx = 10,
366 maxdigits_hex_nopfx = 8,
368 static constexpr csubstr max_value_dec() noexcept {
return csubstr(
"4294967295"); }
369 static constexpr bool is_oct_overflow(csubstr str)
noexcept {
return !((str.len < 11) || (str.len == 11 && str.str[0] <=
'3')); }
371template<>
struct charconv_digits_<8u, true>
374 maxdigits_bin = 1 + 2 + 64,
375 maxdigits_oct = 1 + 2 + 22,
376 maxdigits_dec = 1 + 19,
377 maxdigits_hex = 1 + 2 + 16,
378 maxdigits_bin_nopfx = 64,
379 maxdigits_oct_nopfx = 22,
380 maxdigits_dec_nopfx = 19,
381 maxdigits_hex_nopfx = 16,
383 static constexpr csubstr min_value_dec() noexcept {
return csubstr(
"9223372036854775808"); }
384 static constexpr csubstr min_value_hex() noexcept {
return csubstr(
"8000000000000000"); }
385 static constexpr csubstr min_value_oct() noexcept {
return csubstr(
"1000000000000000000000"); }
386 static constexpr csubstr min_value_bin() noexcept {
return csubstr(
"1000000000000000000000000000000000000000000000000000000000000000"); }
387 static constexpr csubstr max_value_dec() noexcept {
return csubstr(
"9223372036854775807"); }
388 static constexpr bool is_oct_overflow(csubstr str)
noexcept {
return !(str.len < 22); }
390template<>
struct charconv_digits_<8u, false>
393 maxdigits_bin = 2 + 64,
394 maxdigits_oct = 2 + 22,
396 maxdigits_hex = 2 + 16,
397 maxdigits_bin_nopfx = 64,
398 maxdigits_oct_nopfx = 22,
399 maxdigits_dec_nopfx = 20,
400 maxdigits_hex_nopfx = 16,
402 static constexpr csubstr max_value_dec() noexcept {
return csubstr(
"18446744073709551615"); }
403 static constexpr bool is_oct_overflow(csubstr str)
noexcept {
return !((str.len < 22) || (str.len == 22 && str.str[0] <=
'1')); }
408#define _c4append(c) { if(C4_LIKELY(pos < buf.len)) { buf.str[pos++] = static_cast<char>(c); } else { ++pos; } }
409#define _c4appendhex(i) { if(C4_LIKELY(pos < buf.len)) { buf.str[pos++] = hexchars[i]; } else { ++pos; } }
432C4_CONSTEXPR14 C4_ALWAYS_INLINE
434 ->
typename std::enable_if<
sizeof(T) == 1u,
unsigned>::type
436 C4_STATIC_ASSERT(std::is_integral<T>::value);
438 return ((v >= 100) ? 3u : ((v >= 10) ? 2u : 1u));
443C4_CONSTEXPR14 C4_ALWAYS_INLINE
445 ->
typename std::enable_if<
sizeof(T) == 2u,
unsigned>::type
447 C4_STATIC_ASSERT(std::is_integral<T>::value);
449 return ((v >= 10000) ? 5u : (v >= 1000) ? 4u : (v >= 100) ? 3u : (v >= 10) ? 2u : 1u);
454C4_CONSTEXPR14 C4_ALWAYS_INLINE
456 ->
typename std::enable_if<
sizeof(T) == 4u,
unsigned>::type
458 C4_STATIC_ASSERT(std::is_integral<T>::value);
460 return ((v >= 1000000000) ? 10u : (v >= 100000000) ? 9u : (v >= 10000000) ? 8u :
461 (v >= 1000000) ? 7u : (v >= 100000) ? 6u : (v >= 10000) ? 5u :
462 (v >= 1000) ? 4u : (v >= 100) ? 3u : (v >= 10) ? 2u : 1u);
467C4_CONSTEXPR14 C4_ALWAYS_INLINE
469 ->
typename std::enable_if<
sizeof(T) == 8u,
unsigned>::type
473 C4_STATIC_ASSERT(std::is_integral<T>::value);
477 if(v >= 100000000000000)
479 if(v >= 100000000000000000)
481 if((
typename std::make_unsigned<T>::type)v >= 10000000000000000000u)
484 return (v >= 1000000000000000000) ? 19u : 18u;
486 else if(v >= 10000000000000000)
492 return(v >= 1000000000000000) ? 16u : 15u;
495 else if(v >= 1000000000000)
497 return (v >= 10000000000000) ? 14u : 13u;
499 else if(v >= 100000000000)
505 return(v >= 10000000000) ? 11u : 10u;
511 return (v >= 100000000) ? 9u : 8u;
512 else if(v >= 1000000)
515 return (v >= 100000) ? 6u : 5u;
519 return (v >= 1000) ? 4u : 3u;
523 return (v >= 10) ? 2u : 1u;
530C4_CONSTEXPR14 C4_ALWAYS_INLINE
unsigned digits_hex(T v)
noexcept
532 C4_STATIC_ASSERT(std::is_integral<T>::value);
534 return v ? 1u + (msb((
typename std::make_unsigned<T>::type)v) >> 2u) : 1u;
539C4_CONSTEXPR14 C4_ALWAYS_INLINE
unsigned digits_bin(T v)
noexcept
541 C4_STATIC_ASSERT(std::is_integral<T>::value);
543 return v ? 1u + msb((
typename std::make_unsigned<T>::type)v) : 1u;
548C4_CONSTEXPR14 C4_ALWAYS_INLINE
unsigned digits_oct(T v_)
noexcept
551 C4_STATIC_ASSERT(std::is_integral<T>::value);
553 using U =
typename std::conditional<
sizeof(T) <=
sizeof(
unsigned),
555 typename std::make_unsigned<T>::type>::type;
561 __b4 = 64u * 8u * 8u,
587C4_INLINE_CONSTEXPR
const char hexchars[] =
"0123456789abcdef";
588C4_INLINE_CONSTEXPR
const char digits0099[] =
589 "0001020304050607080910111213141516171819"
590 "2021222324252627282930313233343536373839"
591 "4041424344454647484950515253545556575859"
592 "6061626364656667686970717273747576777879"
593 "8081828384858687888990919293949596979899";
597C4_SUPPRESS_WARNING_GCC_PUSH
598C4_SUPPRESS_WARNING_GCC(
"-Warray-bounds")
599#if (defined(__GNUC__) && (__GNUC__ >= 7))
600C4_SUPPRESS_WARNING_GCC(
"-Wstringop-overflow")
614C4_HOT C4_ALWAYS_INLINE
617 C4_STATIC_ASSERT(std::is_integral<T>::value);
619 C4_ASSERT(buf.len >= digits_v);
626 const auto num = (v - quo * T(100)) << 1u;
628 buf.str[--digits_v] = detail::digits0099[num + 1];
629 buf.str[--digits_v] = detail::digits0099[num];
633 C4_ASSERT(digits_v == 2);
634 const auto num = v << 1u;
635 buf.str[1] = detail::digits0099[num + 1];
636 buf.str[0] = detail::digits0099[num];
640 C4_ASSERT(digits_v == 1);
641 buf.str[0] = (char)(
'0' + v);
647C4_HOT C4_ALWAYS_INLINE
650 C4_STATIC_ASSERT(std::is_integral<T>::value);
652 C4_ASSERT(buf.len >= digits_v);
655 buf.str[--digits_v] = detail::hexchars[v & T(15)];
658 C4_ASSERT(digits_v == 0);
663C4_HOT C4_ALWAYS_INLINE
666 C4_STATIC_ASSERT(std::is_integral<T>::value);
668 C4_ASSERT(buf.len >= digits_v);
671 buf.str[--digits_v] = (char)(
'0' + (v & T(7)));
674 C4_ASSERT(digits_v == 0);
679C4_HOT C4_ALWAYS_INLINE
682 C4_STATIC_ASSERT(std::is_integral<T>::value);
684 C4_ASSERT(buf.len >= digits_v);
687 buf.str[--digits_v] = (char)(
'0' + (v & T(1)));
690 C4_ASSERT(digits_v == 0);
717 C4_STATIC_ASSERT(std::is_integral<T>::value);
720 if(C4_LIKELY(buf.len >= digits))
736 C4_STATIC_ASSERT(std::is_integral<T>::value);
739 if(C4_LIKELY(buf.len >= digits))
755 C4_STATIC_ASSERT(std::is_integral<T>::value);
758 if(C4_LIKELY(buf.len >= digits))
774 C4_STATIC_ASSERT(std::is_integral<T>::value);
777 C4_ASSERT(digits > 0);
778 if(C4_LIKELY(buf.len >= digits))
786template<
class U>
using NumberWriter = size_t (*)(substr, U);
787template<
class T, NumberWriter<T> writer>
788size_t write_num_digits(substr buf, T v,
size_t num_digits)
noexcept
790 C4_STATIC_ASSERT(std::is_integral<T>::value);
791 const size_t ret = writer(buf, v);
792 if(ret >= num_digits)
794 else if(ret >= buf.len || num_digits > buf.len)
796 C4_ASSERT(num_digits >= ret);
797 const size_t delta =
static_cast<size_t>(num_digits - ret);
798 C4_ASSERT(ret + delta <= buf.len);
800 memmove(buf.str + delta, buf.str, ret);
802 memset(buf.str,
'0', delta);
815 return detail::write_num_digits<T, &write_dec<T>>(buf, val, num_digits);
824 return detail::write_num_digits<T, &write_hex<T>>(buf, val, num_digits);
833 return detail::write_num_digits<T, &write_bin<T>>(buf, val, num_digits);
842 return detail::write_num_digits<T, &write_oct<T>>(buf, val, num_digits);
847C4_SUPPRESS_WARNING_GCC_POP
855C4_SUPPRESS_WARNING_MSVC_PUSH
856C4_SUPPRESS_WARNING_MSVC(4365)
877 C4_STATIC_ASSERT(std::is_integral<I>::value);
878 C4_ASSERT(!s.empty());
882 if(C4_UNLIKELY(c <
'0' || c >
'9'))
884 *v = ((*v) * I(10)) + (I(c) - I(
'0'));
905 C4_STATIC_ASSERT(std::is_integral<I>::value);
906 C4_ASSERT(!s.empty());
911 if(c >=
'0' && c <=
'9')
913 else if(c >=
'a' && c <=
'f')
914 cv = I(10) + (I(c) - I(
'a'));
915 else if(c >=
'A' && c <=
'F')
916 cv = I(10) + (I(c) - I(
'A'));
919 *v = ((*v) * I(16)) + cv;
940 C4_STATIC_ASSERT(std::is_integral<I>::value);
941 C4_ASSERT(!s.empty());
970 C4_STATIC_ASSERT(std::is_integral<I>::value);
971 C4_ASSERT(!s.empty());
975 if(C4_UNLIKELY(c <
'0' || c >
'7'))
977 *v = ((*v) * I(8)) + (I(c) - I(
'0'));
984C4_SUPPRESS_WARNING_MSVC_POP
991C4_SUPPRESS_WARNING_GCC_WITH_PUSH(
"-Wswitch-default")
995inline size_t _itoa2buf(
substr buf,
size_t pos,
csubstr val)
noexcept
997 C4_ASSERT(pos < buf.len);
998 C4_ASSERT(pos + val.len <= buf.len);
999 C4_ASSERT(val.len > 0);
1000 memcpy(buf.str + pos, val.str, val.len);
1001 return pos + val.len;
1003inline size_t _itoa2bufwithdigits(
substr buf,
size_t pos,
size_t num_digits,
csubstr val)
noexcept
1005 num_digits = num_digits > val.len ? num_digits - val.len : 0;
1006 C4_ASSERT(num_digits + val.len <= buf.len);
1007 for(
size_t i = 0; i < num_digits; ++i)
1009 return detail::_itoa2buf(buf, pos, val);
1012C4_NO_INLINE
size_t _itoadec2buf(
substr buf)
noexcept
1014 using digits_type = detail::charconv_digits<I>;
1015 if(C4_UNLIKELY(buf.len < digits_type::maxdigits_dec))
1016 return digits_type::maxdigits_dec;
1018 return detail::_itoa2buf(buf, 1, digits_type::min_value_dec());
1021C4_NO_INLINE
size_t _itoa2buf(
substr buf, I radix)
noexcept
1023 using digits_type = detail::charconv_digits<I>;
1025 if(C4_LIKELY(buf.len > 0))
1026 buf.str[pos++] =
'-';
1030 if(C4_UNLIKELY(buf.len < digits_type::maxdigits_dec))
1031 return digits_type::maxdigits_dec;
1032 pos =_itoa2buf(buf, pos, digits_type::min_value_dec());
1035 if(C4_UNLIKELY(buf.len < digits_type::maxdigits_hex))
1036 return digits_type::maxdigits_hex;
1037 buf.str[pos++] =
'0';
1038 buf.str[pos++] =
'x';
1039 pos = _itoa2buf(buf, pos, digits_type::min_value_hex());
1042 if(C4_UNLIKELY(buf.len < digits_type::maxdigits_bin))
1043 return digits_type::maxdigits_bin;
1044 buf.str[pos++] =
'0';
1045 buf.str[pos++] =
'b';
1046 pos = _itoa2buf(buf, pos, digits_type::min_value_bin());
1049 if(C4_UNLIKELY(buf.len < digits_type::maxdigits_oct))
1050 return digits_type::maxdigits_oct;
1051 buf.str[pos++] =
'0';
1052 buf.str[pos++] =
'o';
1053 pos = _itoa2buf(buf, pos, digits_type::min_value_oct());
1059C4_NO_INLINE
size_t _itoa2buf(
substr buf, I radix,
size_t num_digits)
noexcept
1061 using digits_type = detail::charconv_digits<I>;
1063 size_t needed_digits = 0;
1064 if(C4_LIKELY(buf.len > 0))
1065 buf.str[pos++] =
'-';
1070 needed_digits = num_digits+1 > digits_type::maxdigits_dec ? num_digits+1 : digits_type::maxdigits_dec;
1071 if(C4_UNLIKELY(buf.len < needed_digits))
1072 return needed_digits;
1073 pos = _itoa2bufwithdigits(buf, pos, num_digits, digits_type::min_value_dec());
1077 needed_digits = num_digits+3 > digits_type::maxdigits_hex ? num_digits+3 : digits_type::maxdigits_hex;
1078 if(C4_UNLIKELY(buf.len < needed_digits))
1079 return needed_digits;
1080 buf.str[pos++] =
'0';
1081 buf.str[pos++] =
'x';
1082 pos = _itoa2bufwithdigits(buf, pos, num_digits, digits_type::min_value_hex());
1086 needed_digits = num_digits+3 > digits_type::maxdigits_bin ? num_digits+3 : digits_type::maxdigits_bin;
1087 if(C4_UNLIKELY(buf.len < needed_digits))
1088 return needed_digits;
1089 C4_ASSERT(buf.len >= digits_type::maxdigits_bin);
1090 buf.str[pos++] =
'0';
1091 buf.str[pos++] =
'b';
1092 pos = _itoa2bufwithdigits(buf, pos, num_digits, digits_type::min_value_bin());
1096 needed_digits = num_digits+3 > digits_type::maxdigits_oct ? num_digits+3 : digits_type::maxdigits_oct;
1097 if(C4_UNLIKELY(buf.len < needed_digits))
1098 return needed_digits;
1099 C4_ASSERT(buf.len >= digits_type::maxdigits_oct);
1100 buf.str[pos++] =
'0';
1101 buf.str[pos++] =
'o';
1102 pos = _itoa2bufwithdigits(buf, pos, num_digits, digits_type::min_value_oct());
1123 C4_STATIC_ASSERT(std::is_signed<T>::value);
1131 if(C4_LIKELY(v != std::numeric_limits<T>::min()))
1135 if(C4_LIKELY(buf.len >= digits + 1u))
1142 return detail::_itoadec2buf<T>(buf);
1155 C4_STATIC_ASSERT(std::is_signed<T>::value);
1156 C4_ASSERT(radix == 2 || radix == 8 || radix == 10 || radix == 16);
1157 C4_SUPPRESS_WARNING_GCC_PUSH
1158 #if (defined(__GNUC__) && (__GNUC__ >= 7))
1159 C4_SUPPRESS_WARNING_GCC(
"-Wstringop-overflow")
1163 if(C4_LIKELY(v != std::numeric_limits<T>::min()))
1169 if(C4_LIKELY(buf.len > 0))
1173 unsigned digits = 0;
1178 if(C4_LIKELY(buf.len >= pos + digits))
1183 if(C4_LIKELY(buf.len >= pos + 2u + digits))
1185 buf.str[pos + 0] =
'0';
1186 buf.str[pos + 1] =
'x';
1193 if(C4_LIKELY(buf.len >= pos + 2u + digits))
1195 buf.str[pos + 0] =
'0';
1196 buf.str[pos + 1] =
'b';
1203 if(C4_LIKELY(buf.len >= pos + 2u + digits))
1205 buf.str[pos + 0] =
'0';
1206 buf.str[pos + 1] =
'o';
1212 return pos + digits;
1214 C4_SUPPRESS_WARNING_GCC_POP
1217 return detail::_itoa2buf<T>(buf, radix);
1230C4_ALWAYS_INLINE
size_t itoa(
substr buf, T v, T radix,
size_t num_digits)
noexcept
1232 C4_STATIC_ASSERT(std::is_signed<T>::value);
1233 C4_ASSERT(radix == 2 || radix == 8 || radix == 10 || radix == 16);
1234 C4_SUPPRESS_WARNING_GCC_PUSH
1235 #if (defined(__GNUC__) && (__GNUC__ >= 7))
1236 C4_SUPPRESS_WARNING_GCC(
"-Wstringop-overflow")
1240 if(C4_LIKELY(v != std::numeric_limits<T>::min()))
1246 if(C4_LIKELY(buf.len > 0))
1250 unsigned total_digits = 0;
1255 total_digits = pos + (unsigned)(num_digits > total_digits ? num_digits : total_digits);
1256 if(C4_LIKELY(buf.len >= total_digits))
1261 total_digits = pos + 2u + (unsigned)(num_digits > total_digits ? num_digits : total_digits);
1262 if(C4_LIKELY(buf.len >= total_digits))
1264 buf.str[pos + 0] =
'0';
1265 buf.str[pos + 1] =
'x';
1266 write_hex(buf.sub(pos + 2), v, num_digits);
1271 total_digits = pos + 2u + (unsigned)(num_digits > total_digits ? num_digits : total_digits);
1272 if(C4_LIKELY(buf.len >= total_digits))
1274 buf.str[pos + 0] =
'0';
1275 buf.str[pos + 1] =
'b';
1276 write_bin(buf.sub(pos + 2), v, num_digits);
1281 total_digits = pos + 2u + (unsigned)(num_digits > total_digits ? num_digits : total_digits);
1282 if(C4_LIKELY(buf.len >= total_digits))
1284 buf.str[pos + 0] =
'0';
1285 buf.str[pos + 1] =
'o';
1286 write_oct(buf.sub(pos + 2), v, num_digits);
1290 return total_digits;
1292 C4_SUPPRESS_WARNING_GCC_POP
1295 return detail::_itoa2buf<T>(buf, radix, num_digits);
1318 C4_STATIC_ASSERT(std::is_unsigned<T>::value);
1333 C4_STATIC_ASSERT(std::is_unsigned<T>::value);
1334 C4_ASSERT(radix == 10 || radix == 16 || radix == 2 || radix == 8);
1335 unsigned digits = 0;
1340 if(C4_LIKELY(buf.len >= digits))
1345 if(C4_LIKELY(buf.len >= digits+2u))
1355 if(C4_LIKELY(buf.len >= digits+2u))
1365 if(C4_LIKELY(buf.len >= digits+2u))
1386C4_ALWAYS_INLINE
size_t utoa(
substr buf, T v, T radix,
size_t num_digits)
noexcept
1388 C4_STATIC_ASSERT(std::is_unsigned<T>::value);
1389 C4_ASSERT(radix == 10 || radix == 16 || radix == 2 || radix == 8);
1390 unsigned total_digits = 0;
1395 total_digits = (unsigned)(num_digits > total_digits ? num_digits : total_digits);
1396 if(C4_LIKELY(buf.len >= total_digits))
1401 total_digits = 2u + (unsigned)(num_digits > total_digits ? num_digits : total_digits);
1402 if(C4_LIKELY(buf.len >= total_digits))
1411 total_digits = 2u + (unsigned)(num_digits > total_digits ? num_digits : total_digits);
1412 if(C4_LIKELY(buf.len >= total_digits))
1421 total_digits = 2u + (unsigned)(num_digits > total_digits ? num_digits : total_digits);
1422 if(C4_LIKELY(buf.len >= total_digits))
1430 return total_digits;
1432C4_SUPPRESS_WARNING_GCC_POP
1469 C4_STATIC_ASSERT(std::is_integral<T>::value);
1470 C4_STATIC_ASSERT(std::is_signed<T>::value);
1472 if(C4_UNLIKELY(str.len == 0))
1475 C4_ASSERT(str.str[0] !=
'+');
1479 if(str.str[0] ==
'-')
1481 if(C4_UNLIKELY(str.len == ++start))
1486 bool parsed_ok =
true;
1487 if(str.str[start] !=
'0')
1489 parsed_ok =
read_dec(str.sub(start), v);
1491 else if(str.len > start + 1)
1494 const char pfx = str.str[start + 1];
1495 if(pfx ==
'x' || pfx ==
'X')
1496 parsed_ok = str.len > start + 2 &&
read_hex(str.sub(start + 2), v);
1497 else if(pfx ==
'b' || pfx ==
'B')
1498 parsed_ok = str.len > start + 2 &&
read_bin(str.sub(start + 2), v);
1499 else if(pfx ==
'o' || pfx ==
'O')
1500 parsed_ok = str.len > start + 2 &&
read_oct(str.sub(start + 2), v);
1502 parsed_ok =
read_dec(str.sub(start + 1), v);
1506 parsed_ok =
read_dec(str.sub(start), v);
1508 if(C4_LIKELY(parsed_ok))
1524 if(trimmed.
len &&
atoi(trimmed, v))
1525 return static_cast<size_t>(trimmed.
end() - str.
begin());
1560 C4_STATIC_ASSERT(std::is_integral<T>::value);
1562 if(C4_UNLIKELY(str.len == 0 || str.front() ==
'-'))
1565 bool parsed_ok =
true;
1566 if(str.str[0] !=
'0')
1574 const char pfx = str.str[1];
1575 if(pfx ==
'x' || pfx ==
'X')
1576 parsed_ok = str.len > 2 &&
read_hex(str.sub(2), v);
1577 else if(pfx ==
'b' || pfx ==
'B')
1578 parsed_ok = str.len > 2 &&
read_bin(str.sub(2), v);
1579 else if(pfx ==
'o' || pfx ==
'O')
1580 parsed_ok = str.len > 2 &&
read_oct(str.sub(2), v);
1603 if(trimmed.
len &&
atou(trimmed, v))
1604 return static_cast<size_t>(trimmed.
end() - str.
begin());
1611#if defined(_MSC_VER) && !defined(__clang__)
1612# pragma warning(pop)
1613#elif defined(__clang__)
1614# pragma clang diagnostic pop
1615#elif defined(__GNUC__)
1616# pragma GCC diagnostic pop
1626inline bool check_overflow(csubstr str, csubstr limit)
noexcept
1628 if(str.len != limit.len)
1629 return str.len > limit.len;
1630 for(
size_t i = 0; i < limit.len; ++i)
1632 if(str.str[i] < limit.str[i])
1634 else if(str.str[i] > limit.str[i])
1655 ->
typename std::enable_if<std::is_unsigned<T>::value,
bool>::type
1657 C4_STATIC_ASSERT(std::is_integral<T>::value);
1659 if(C4_UNLIKELY(str.len == 0))
1663 else if(str.str[0] ==
'0')
1672 size_t fno = str.first_not_of(
'0', 2);
1675 return !(str.len <= fno + (
sizeof(T) * 2));
1680 size_t fno = str.first_not_of(
'0', 2);
1683 return !(str.len <= fno +(
sizeof(T) * 8));
1688 size_t fno = str.first_not_of(
'0', 2);
1691 return detail::charconv_digits<T>::is_oct_overflow(str.sub(fno));
1695 size_t fno = str.first_not_of(
'0', 1);
1698 return detail::check_overflow(str.sub(fno), detail::charconv_digits<T>::max_value_dec());
1702 else if(C4_UNLIKELY(str.str[0] ==
'-'))
1708 return detail::check_overflow(str, detail::charconv_digits<T>::max_value_dec());
1722 ->
typename std::enable_if<std::is_signed<T>::value,
bool>::type
1724 C4_STATIC_ASSERT(std::is_integral<T>::value);
1725 if(C4_UNLIKELY(str.len == 0))
1727 if(str.str[0] ==
'-')
1729 if(str.str[1] ==
'0')
1738 size_t fno = str.first_not_of(
'0', 3);
1741 return detail::check_overflow(str.sub(fno), detail::charconv_digits<T>::min_value_hex());
1746 size_t fno = str.first_not_of(
'0', 3);
1749 return detail::check_overflow(str.sub(fno), detail::charconv_digits<T>::min_value_bin());
1754 size_t fno = str.first_not_of(
'0', 3);
1757 return detail::check_overflow(str.sub(fno), detail::charconv_digits<T>::min_value_oct());
1761 size_t fno = str.first_not_of(
'0', 2);
1764 return detail::check_overflow(str.sub(fno), detail::charconv_digits<T>::min_value_dec());
1770 return detail::check_overflow(str.sub(1), detail::charconv_digits<T>::min_value_dec());
1773 else if(str.str[0] ==
'0')
1782 size_t fno = str.first_not_of(
'0', 2);
1785 const size_t len = str.len - fno;
1786 return !((len <
sizeof (T) * 2) || (len ==
sizeof(T) * 2 && str.str[fno] <=
'7'));
1791 size_t fno = str.first_not_of(
'0', 2);
1794 return !(str.len <= fno + (
sizeof(T) * 8 - 1));
1799 size_t fno = str.first_not_of(
'0', 2);
1802 return detail::charconv_digits<T>::is_oct_overflow(str.sub(fno));
1806 size_t fno = str.first_not_of(
'0', 1);
1809 return detail::check_overflow(str.sub(fno), detail::charconv_digits<T>::max_value_dec());
1815 return detail::check_overflow(str, detail::charconv_digits<T>::max_value_dec());
1830#if (!C4CORE_HAVE_STD_FROMCHARS)
1833void get_real_format_str(
char (& C4_RESTRICT fmt)[N],
int precision, RealFormat_e formatting,
const char* length_modifier=
"")
1837 iret = snprintf(fmt,
sizeof(fmt),
"%%%s%c", length_modifier, formatting);
1838 else if(precision == 0)
1839 iret = snprintf(fmt,
sizeof(fmt),
"%%.%s%c", length_modifier, formatting);
1841 iret = snprintf(fmt,
sizeof(fmt),
"%%.%d%s%c", precision, length_modifier, formatting);
1842 C4_ASSERT(iret >= 2 &&
size_t(iret) <
sizeof(fmt));
1860size_t print_one(substr str,
const char* full_fmt, T v)
1866 int iret = _snprintf(str.str, str.len, full_fmt, v);
1872 iret = snprintf(
nullptr, 0, full_fmt, v);
1873 C4_ASSERT(iret > 0);
1875 size_t ret = (size_t) iret;
1878 int iret = snprintf(str.str, str.len, full_fmt, v);
1879 C4_ASSERT(iret >= 0);
1880 size_t ret = (size_t) iret;
1889#if (!C4CORE_HAVE_STD_FROMCHARS) && (!C4CORE_HAVE_FAST_FLOAT)
1895inline size_t scan_one(csubstr str,
const char *type_fmt, T *v)
1914 int iret = std::snprintf(fmt,
sizeof(fmt),
"%%" "%zu" "%s" "%%n", str.len, type_fmt);
1916 C4_ASSERT(iret >= 0 &&
size_t(iret) < C4_COUNTOF(fmt));
1920 iret = std::sscanf(str.str, fmt, v, &num_chars);
1922 if(iret != 1)
return csubstr::npos;
1923 C4_ASSERT(num_chars >= 0);
1924 return (
size_t)(num_chars);
1929#if C4CORE_HAVE_STD_TOCHARS
1931C4_ALWAYS_INLINE
size_t rtoa(substr buf, T v,
int precision=-1, RealFormat_e formatting=FTOA_FLEX)
noexcept
1933 std::to_chars_result result;
1935 if(formatting == FTOA_HEXA)
1937 if(buf.len >
size_t(2))
1945 result = std::to_chars(buf.str + pos, buf.str + buf.len, v, (std::chars_format)formatting);
1947 result = std::to_chars(buf.str + pos, buf.str + buf.len, v, (std::chars_format)formatting, precision);
1948 if(result.ec == std::errc())
1951 C4_ASSERT(result.ptr >= buf.str);
1952 ptrdiff_t delta = result.ptr - buf.str;
1953 return static_cast<size_t>(delta);
1955 C4_ASSERT(result.ec == std::errc::value_too_large);
1966 size_t ret =
static_cast<size_t>(std::numeric_limits<T>::max_digits10);
1967 return ret > buf.len ? ret : buf.len + 1;
1972#if C4CORE_HAVE_FAST_FLOAT
1974C4_ALWAYS_INLINE
bool scan_rhex(csubstr s, T *C4_RESTRICT val)
noexcept
1976 C4_ASSERT(s.len > 0);
1977 C4_ASSERT(s.str[0] !=
'-');
1978 C4_ASSERT(s.str[0] !=
'+');
1979 C4_ASSERT(!s.begins_with(
"0x"));
1980 C4_ASSERT(!s.begins_with(
"0X"));
1983 for( ; pos < s.len; ++pos)
1985 const char c = s.str[pos];
1986 if(c >=
'0' && c <=
'9')
1987 *val = (*val * T(16)) + T(c -
'0');
1988 else if(c >=
'a' && c <=
'f')
1989 *val = (*val * T(16)) + T(c -
'a');
1990 else if(c >=
'A' && c <=
'F')
1991 *val = (*val * T(16)) + T(c -
'A');
1997 else if(c ==
'p' || c ==
'P')
2010 for(T digit = T(0.0625); pos < s.len; ++pos, digit /= T(16))
2012 const char c = s.str[pos];
2013 if(c >=
'0' && c <=
'9')
2014 *val += digit * T(c -
'0');
2015 else if(c >=
'a' && c <=
'f')
2016 *val += digit * T(c -
'a');
2017 else if(c >=
'A' && c <=
'F')
2018 *val += digit * T(c -
'A');
2019 else if(c ==
'p' || c ==
'P')
2032 if(C4_LIKELY(pos < s.len))
2034 if(s.str[pos] ==
'+')
2036 if(C4_LIKELY(pos < s.len))
2038 int16_t powval = {};
2039 if(C4_LIKELY(
atoi(s.sub(pos), &powval)))
2041 *val *= ipow<T, int16_t, 16>(powval);
2072#if C4CORE_HAVE_STD_TOCHARS
2073 return detail::rtoa(str, v, precision, formatting);
2076 detail::get_real_format_str(
fmt, precision, formatting,
"");
2077 return detail::print_one(str,
fmt, v);
2098#if C4CORE_HAVE_STD_TOCHARS
2099 return detail::rtoa(str, v, precision, formatting);
2102 detail::get_real_format_str(
fmt, precision, formatting,
"l");
2103 return detail::print_one(str,
fmt, v);
2120C4_ALWAYS_INLINE
bool atof(
csubstr str,
float * C4_RESTRICT v)
noexcept
2122 C4_ASSERT(str.len > 0);
2123 C4_ASSERT(str.triml(
" \r\t\n").len == str.len);
2124#if C4CORE_HAVE_FAST_FLOAT
2126 bool isneg = (str.str[0] ==
'-');
2127 csubstr rem = str.sub(isneg || str.str[0] ==
'+');
2128 if(!(rem.
len >= 2 && (rem.
str[0] ==
'0' && (rem.
str[1] ==
'x' || rem.
str[1] ==
'X'))))
2130 fast_float::from_chars_result result;
2131 result = fast_float::from_chars(str.str, str.str + str.len, *v);
2132 return result.ec == std::errc();
2134 else if(detail::scan_rhex(rem.
sub(2), v))
2136 *v *= isneg ? -1.f : 1.f;
2140#elif C4CORE_HAVE_STD_FROMCHARS
2141 std::from_chars_result result;
2142 result = std::from_chars(str.str, str.str + str.len, *v);
2143 return result.ec == std::errc();
2145 csubstr rem = str.
sub(str.str[0] ==
'-' || str.str[0] ==
'+');
2146 if(!(rem.
len >= 2 && (rem.
str[0] ==
'0' && (rem.
str[1] ==
'x' || rem.
str[1] ==
'X'))))
2160 csubstr trimmed = str.first_real_span();
2161 if(trimmed.
len == 0)
2163 if(
atof(trimmed, v))
2164 return static_cast<size_t>(trimmed.
end() - str.begin());
2181C4_ALWAYS_INLINE
bool atod(
csubstr str,
double * C4_RESTRICT v)
noexcept
2183 C4_ASSERT(str.len > 0);
2184 C4_ASSERT(str.triml(
" \r\t\n").len == str.len);
2185#if C4CORE_HAVE_FAST_FLOAT
2187 bool isneg = (str.str[0] ==
'-');
2188 csubstr rem = str.sub(isneg || str.str[0] ==
'+');
2189 if(!(rem.
len >= 2 && (rem.
str[0] ==
'0' && (rem.
str[1] ==
'x' || rem.
str[1] ==
'X'))))
2191 fast_float::from_chars_result result;
2193 result = fast_float::from_chars(str.str, str.str + str.len, *v);
2197 return result.ec == std::errc();
2199 else if(detail::scan_rhex(rem.
sub(2), v))
2201 *v *= isneg ? -1. : 1.;
2205#elif C4CORE_HAVE_STD_FROMCHARS
2206 std::from_chars_result result;
2207 result = std::from_chars(str.str, str.str + str.len, *v);
2208 return result.ec == std::errc();
2210 csubstr rem = str.
sub(str.str[0] ==
'-' || str.str[0] ==
'+');
2211 if(!(rem.
len >= 2 && (rem.
str[0] ==
'0' && (rem.
str[1] ==
'x' || rem.
str[1] ==
'X'))))
2225 csubstr trimmed = str.first_real_span();
2226 if(trimmed.
len == 0)
2228 if(
atod(trimmed, v))
2229 return static_cast<size_t>(trimmed.
end() - str.begin());
2244#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>
2245#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>
2266C4_ALWAYS_INLINE
size_t xtoa(
substr s, uint8_t v, uint8_t radix)
noexcept {
return utoa(s, v, radix); }
2267C4_ALWAYS_INLINE
size_t xtoa(
substr s, uint16_t v, uint16_t radix)
noexcept {
return utoa(s, v, radix); }
2268C4_ALWAYS_INLINE
size_t xtoa(
substr s, uint32_t v, uint32_t radix)
noexcept {
return utoa(s, v, radix); }
2269C4_ALWAYS_INLINE
size_t xtoa(
substr s, uint64_t v, uint64_t radix)
noexcept {
return utoa(s, v, radix); }
2270C4_ALWAYS_INLINE
size_t xtoa(
substr s, int8_t v, int8_t radix)
noexcept {
return itoa(s, v, radix); }
2271C4_ALWAYS_INLINE
size_t xtoa(
substr s, int16_t v, int16_t radix)
noexcept {
return itoa(s, v, radix); }
2272C4_ALWAYS_INLINE
size_t xtoa(
substr s, int32_t v, int32_t radix)
noexcept {
return itoa(s, v, radix); }
2273C4_ALWAYS_INLINE
size_t xtoa(
substr s, int64_t v, int64_t radix)
noexcept {
return itoa(s, v, radix); }
2275C4_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); }
2276C4_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); }
2277C4_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); }
2278C4_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); }
2279C4_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); }
2280C4_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); }
2281C4_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); }
2282C4_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); }
2287template <
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); }
2288template <
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); }
2290C4_ALWAYS_INLINE
size_t xtoa(
substr s, T *v)
noexcept {
return itoa(s, (intptr_t)v, (intptr_t)16); }
2301C4_ALWAYS_INLINE
bool atox(
csubstr s, uint8_t *C4_RESTRICT v)
noexcept {
return atou(s, v); }
2302C4_ALWAYS_INLINE
bool atox(
csubstr s, uint16_t *C4_RESTRICT v)
noexcept {
return atou(s, v); }
2303C4_ALWAYS_INLINE
bool atox(
csubstr s, uint32_t *C4_RESTRICT v)
noexcept {
return atou(s, v); }
2304C4_ALWAYS_INLINE
bool atox(
csubstr s, uint64_t *C4_RESTRICT v)
noexcept {
return atou(s, v); }
2305C4_ALWAYS_INLINE
bool atox(
csubstr s, int8_t *C4_RESTRICT v)
noexcept {
return atoi(s, v); }
2306C4_ALWAYS_INLINE
bool atox(
csubstr s, int16_t *C4_RESTRICT v)
noexcept {
return atoi(s, v); }
2307C4_ALWAYS_INLINE
bool atox(
csubstr s, int32_t *C4_RESTRICT v)
noexcept {
return atoi(s, v); }
2308C4_ALWAYS_INLINE
bool atox(
csubstr s, int64_t *C4_RESTRICT v)
noexcept {
return atoi(s, v); }
2309C4_ALWAYS_INLINE
bool atox(
csubstr s,
float *C4_RESTRICT v)
noexcept {
return atof(s, v); }
2310C4_ALWAYS_INLINE
bool atox(
csubstr s,
double *C4_RESTRICT v)
noexcept {
return atod(s, v); }
2312template <
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); }
2313template <
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); }
2315C4_ALWAYS_INLINE
bool atox(
csubstr s, T **v)
noexcept { intptr_t tmp;
bool ret =
atox(s, &tmp);
if(ret) { *v = (T*)tmp; }
return ret; }
2352template <
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); }
2353template <
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); }
2356 ->
typename std::enable_if<!std::is_same<T, char>::value &&
2357 !std::is_same<T, const char>::value,
2360 return itoa(s, (intptr_t)v, (intptr_t)16);
2393template <
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); }
2394template <
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); }
2396C4_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; }
2422template <
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); }
2430#undef _C4_IF_NOT_FIXED_LENGTH_I
2431#undef _C4_IF_NOT_FIXED_LENGTH_U
2449 return buf.left_of(sz <= buf.len ? sz : buf.len);
2469 if(buf.str[0] ==
'0')
2471 *v =
false;
return true;
2473 else if(buf.str[0] ==
'1')
2475 *v =
true;
return true;
2478 else if(buf.len == 4)
2480 if(((buf.str[0] ==
't') && (0 == memcmp(buf.str + 1,
"rue", 3)))
2482 ((buf.str[0] ==
'T') && (0 == memcmp(buf.str + 1,
"rue", 3) ||
2483 0 == memcmp(buf.str + 1,
"RUE", 3))))
2485 *v =
true;
return true;
2488 else if(buf.len == 5)
2490 if(((buf.str[0] ==
'f') && (0 == memcmp(buf.str + 1,
"alse", 4)))
2492 ((buf.str[0] ==
'F') && (0 == memcmp(buf.str + 1,
"alse", 4) ||
2493 0 == memcmp(buf.str + 1,
"ALSE", 4))))
2495 *v =
false;
return true;
2511 csubstr trimmed = buf.first_non_empty_span();
2526 C4_XASSERT(buf.str);
2540 C4_XASSERT(buf.str);
2561 C4_ASSERT(!buf.overlaps(v));
2562 size_t len = buf.len < v.len ? buf.len : v.len;
2568 C4_ASSERT(buf.str !=
nullptr);
2569 C4_ASSERT(v.str !=
nullptr);
2570 memcpy(buf.str, v.str, len);
2585 csubstr trimmed = buf.first_non_empty_span();
2586 if(trimmed.
len == 0)
2589 return static_cast<size_t>(trimmed.
end() - buf.begin());
2599 C4_ASSERT(!buf.overlaps(v));
2600 size_t len = buf.len < v.len ? buf.len : v.len;
2606 C4_ASSERT(buf.str !=
nullptr);
2607 C4_ASSERT(v.str !=
nullptr);
2608 memcpy(buf.str, v.str, len);
2616 C4_ASSERT(!buf.overlaps(*v));
2618 if(v->len >= buf.len)
2625 C4_ASSERT(buf.str !=
nullptr);
2626 C4_ASSERT(v->str !=
nullptr);
2627 memcpy(v->str, buf.str, buf.len);
2638 csubstr trimmed = buf.first_non_empty_span();
2640 if(C4_UNLIKELY(trimmed.
len == 0))
2642 size_t len = trimmed.
len > v->len ? v->len : trimmed.
len;
2648 C4_ASSERT(buf.str !=
nullptr);
2649 C4_ASSERT(v->str !=
nullptr);
2650 memcpy(v->str, trimmed.
str, len);
2652 if(C4_UNLIKELY(trimmed.
len > v->len))
2654 return static_cast<size_t>(trimmed.
end() - buf.begin());
2674template<
class CharPtr>
2676 ->
typename std::enable_if<std::is_same<CharPtr, char*>::value
2678 std::is_same<CharPtr, const char*>::value,
size_t>::type
2702#if defined(_MSC_VER) && !defined(__clang__)
2703# pragma warning(pop)
2704#elif defined(__clang__)
2705# pragma clang diagnostic pop
2706#elif defined(__GNUC__)
2707# 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, uint8_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, uint8_t *v) noexcept
bool from_chars(csubstr buf, uint8_t *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.
size_t itoa(substr buf, T v) noexcept
convert an integral signed decimal to a string.
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.
bool from_chars(ryml::csubstr buf, vec2< T > *v)
size_t to_chars(ryml::substr buf, vec2< T > v)
basic_substring< char > substr
a mutable string view
basic_substring< const char > csubstr
an immutable string view
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 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_oct(substr buf, T v) noexcept
write an integer to a string in octal format.
size_t xtoa(substr s, uint8_t v) noexcept
(Undefined by default) Use shorter error message from checks/asserts: do not show the check condition...
basic_substring first_uint_span() const
get the first span which can be interpreted as an unsigned integer
basic_substring first_int_span() const
get the first span which can be interpreted as a signed integer
size_t len
the length of the substring
iterator begin() noexcept
bool overlaps(ro_substr const that) const noexcept
true if there is overlap of at least one element between that and *this
basic_substring sub(size_t first) const noexcept
return [first,len[
C * str
a restricted pointer to the first character of the substring
#define _c4append(first, last)