charconv.hpp (92598B)
1 #ifndef _C4_CHARCONV_HPP_ 2 #define _C4_CHARCONV_HPP_ 3 4 /** @file charconv.hpp Lightweight generic type-safe wrappers for 5 * converting individual values to/from strings. 6 * 7 * These are the main functions: 8 * 9 * @code{.cpp} 10 * // Convert the given value, writing into the string. 11 * // The resulting string will NOT be null-terminated. 12 * // Return the number of characters needed. 13 * // This function is safe to call when the string is too small - 14 * // no writes will occur beyond the string's last character. 15 * template<class T> size_t c4::to_chars(substr buf, T const& C4_RESTRICT val); 16 * 17 * 18 * // Convert the given value to a string using to_chars(), and 19 * // return the resulting string, up to and including the last 20 * // written character. 21 * template<class T> substr c4::to_chars_sub(substr buf, T const& C4_RESTRICT val); 22 * 23 * 24 * // Read a value from the string, which must be 25 * // trimmed to the value (ie, no leading/trailing whitespace). 26 * // return true if the conversion succeeded. 27 * // There is no check for overflow; the value wraps around in a way similar 28 * // to the standard C/C++ overflow behavior. For example, 29 * // from_chars<int8_t>("128", &val) returns true and val will be 30 * // set tot 0. 31 * template<class T> bool c4::from_chars(csubstr buf, T * C4_RESTRICT val); 32 * 33 * 34 * // Read the first valid sequence of characters from the string, 35 * // skipping leading whitespace, and convert it using from_chars(). 36 * // Return the number of characters read for converting. 37 * template<class T> size_t c4::from_chars_first(csubstr buf, T * C4_RESTRICT val); 38 * @endcode 39 */ 40 41 #include "c4/language.hpp" 42 #include <inttypes.h> 43 #include <type_traits> 44 #include <climits> 45 #include <limits> 46 #include <utility> 47 48 #include "c4/config.hpp" 49 #include "c4/substr.hpp" 50 #include "c4/std/std_fwd.hpp" 51 #include "c4/memory_util.hpp" 52 #include "c4/szconv.hpp" 53 54 #ifndef C4CORE_NO_FAST_FLOAT 55 # if (C4_CPP >= 17) 56 # if defined(_MSC_VER) 57 # if (C4_MSVC_VERSION >= C4_MSVC_VERSION_2019) // VS2017 and lower do not have these macros 58 # include <charconv> 59 # define C4CORE_HAVE_STD_TOCHARS 1 60 # define C4CORE_HAVE_STD_FROMCHARS 0 // prefer fast_float with MSVC 61 # define C4CORE_HAVE_FAST_FLOAT 1 62 # else 63 # define C4CORE_HAVE_STD_TOCHARS 0 64 # define C4CORE_HAVE_STD_FROMCHARS 0 65 # define C4CORE_HAVE_FAST_FLOAT 1 66 # endif 67 # else 68 # if __has_include(<charconv>) 69 # include <charconv> 70 # if defined(__cpp_lib_to_chars) 71 # define C4CORE_HAVE_STD_TOCHARS 1 72 # define C4CORE_HAVE_STD_FROMCHARS 0 // glibc uses fast_float internally 73 # define C4CORE_HAVE_FAST_FLOAT 1 74 # else 75 # define C4CORE_HAVE_STD_TOCHARS 0 76 # define C4CORE_HAVE_STD_FROMCHARS 0 77 # define C4CORE_HAVE_FAST_FLOAT 1 78 # endif 79 # else 80 # define C4CORE_HAVE_STD_TOCHARS 0 81 # define C4CORE_HAVE_STD_FROMCHARS 0 82 # define C4CORE_HAVE_FAST_FLOAT 1 83 # endif 84 # endif 85 # else 86 # define C4CORE_HAVE_STD_TOCHARS 0 87 # define C4CORE_HAVE_STD_FROMCHARS 0 88 # define C4CORE_HAVE_FAST_FLOAT 1 89 # endif 90 # if C4CORE_HAVE_FAST_FLOAT 91 C4_SUPPRESS_WARNING_GCC_WITH_PUSH("-Wsign-conversion") 92 C4_SUPPRESS_WARNING_GCC("-Warray-bounds") 93 # if defined(__GNUC__) && __GNUC__ >= 5 94 C4_SUPPRESS_WARNING_GCC("-Wshift-count-overflow") 95 # endif 96 //# include "c4/ext/fast_float.hpp" 97 #include "fast_float/fast_float.h" 98 C4_SUPPRESS_WARNING_GCC_POP 99 # endif 100 #elif (C4_CPP >= 17) 101 # define C4CORE_HAVE_FAST_FLOAT 0 102 # if defined(_MSC_VER) 103 # if (C4_MSVC_VERSION >= C4_MSVC_VERSION_2019) // VS2017 and lower do not have these macros 104 # include <charconv> 105 # define C4CORE_HAVE_STD_TOCHARS 1 106 # define C4CORE_HAVE_STD_FROMCHARS 1 107 # else 108 # define C4CORE_HAVE_STD_TOCHARS 0 109 # define C4CORE_HAVE_STD_FROMCHARS 0 110 # endif 111 # else 112 # if __has_include(<charconv>) 113 # include <charconv> 114 # if defined(__cpp_lib_to_chars) 115 # define C4CORE_HAVE_STD_TOCHARS 1 116 # define C4CORE_HAVE_STD_FROMCHARS 1 // glibc uses fast_float internally 117 # else 118 # define C4CORE_HAVE_STD_TOCHARS 0 119 # define C4CORE_HAVE_STD_FROMCHARS 0 120 # endif 121 # else 122 # define C4CORE_HAVE_STD_TOCHARS 0 123 # define C4CORE_HAVE_STD_FROMCHARS 0 124 # endif 125 # endif 126 #else 127 # define C4CORE_HAVE_STD_TOCHARS 0 128 # define C4CORE_HAVE_STD_FROMCHARS 0 129 # define C4CORE_HAVE_FAST_FLOAT 0 130 #endif 131 132 133 #if !C4CORE_HAVE_STD_FROMCHARS 134 #include <cstdio> 135 #endif 136 137 138 #if defined(_MSC_VER) 139 # pragma warning(push) 140 # pragma warning(disable: 4996) // snprintf/scanf: this function or variable may be unsafe 141 # if C4_MSVC_VERSION != C4_MSVC_VERSION_2017 142 # pragma warning(disable: 4800) //'int': forcing value to bool 'true' or 'false' (performance warning) 143 # endif 144 #endif 145 146 #if defined(__clang__) 147 # pragma clang diagnostic push 148 # pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare" 149 # pragma clang diagnostic ignored "-Wformat-nonliteral" 150 # pragma clang diagnostic ignored "-Wdouble-promotion" // implicit conversion increases floating-point precision 151 # pragma clang diagnostic ignored "-Wold-style-cast" 152 #elif defined(__GNUC__) 153 # pragma GCC diagnostic push 154 # pragma GCC diagnostic ignored "-Wformat-nonliteral" 155 # pragma GCC diagnostic ignored "-Wdouble-promotion" // implicit conversion increases floating-point precision 156 # pragma GCC diagnostic ignored "-Wuseless-cast" 157 # pragma GCC diagnostic ignored "-Wold-style-cast" 158 #endif 159 160 161 namespace c4 { 162 163 #if C4CORE_HAVE_STD_TOCHARS 164 /** @warning Use only the symbol. Do not rely on the type or naked value of this enum. */ 165 typedef enum : std::underlying_type<std::chars_format>::type { 166 /** print the real number in floating point format (like %f) */ 167 FTOA_FLOAT = static_cast<std::underlying_type<std::chars_format>::type>(std::chars_format::fixed), 168 /** print the real number in scientific format (like %e) */ 169 FTOA_SCIENT = static_cast<std::underlying_type<std::chars_format>::type>(std::chars_format::scientific), 170 /** print the real number in flexible format (like %g) */ 171 FTOA_FLEX = static_cast<std::underlying_type<std::chars_format>::type>(std::chars_format::general), 172 /** print the real number in hexadecimal format (like %a) */ 173 FTOA_HEXA = static_cast<std::underlying_type<std::chars_format>::type>(std::chars_format::hex), 174 } RealFormat_e; 175 #else 176 /** @warning Use only the symbol. Do not rely on the type or naked value of this enum. */ 177 typedef enum : char { 178 /** print the real number in floating point format (like %f) */ 179 FTOA_FLOAT = 'f', 180 /** print the real number in scientific format (like %e) */ 181 FTOA_SCIENT = 'e', 182 /** print the real number in flexible format (like %g) */ 183 FTOA_FLEX = 'g', 184 /** print the real number in hexadecimal format (like %a) */ 185 FTOA_HEXA = 'a', 186 } RealFormat_e; 187 #endif 188 189 190 /** in some platforms, int,unsigned int 191 * are not any of int8_t...int64_t and 192 * long,unsigned long are not any of uint8_t...uint64_t */ 193 template<class T> 194 struct is_fixed_length 195 { 196 enum : bool { 197 /** true if T is one of the fixed length signed types */ 198 value_i = (std::is_integral<T>::value 199 && (std::is_same<T, int8_t>::value 200 || std::is_same<T, int16_t>::value 201 || std::is_same<T, int32_t>::value 202 || std::is_same<T, int64_t>::value)), 203 /** true if T is one of the fixed length unsigned types */ 204 value_u = (std::is_integral<T>::value 205 && (std::is_same<T, uint8_t>::value 206 || std::is_same<T, uint16_t>::value 207 || std::is_same<T, uint32_t>::value 208 || std::is_same<T, uint64_t>::value)), 209 /** true if T is one of the fixed length signed or unsigned types */ 210 value = value_i || value_u 211 }; 212 }; 213 214 215 //----------------------------------------------------------------------------- 216 //----------------------------------------------------------------------------- 217 //----------------------------------------------------------------------------- 218 219 #ifdef _MSC_VER 220 # pragma warning(push) 221 #elif defined(__clang__) 222 # pragma clang diagnostic push 223 #elif defined(__GNUC__) 224 # pragma GCC diagnostic push 225 # pragma GCC diagnostic ignored "-Wconversion" 226 # if __GNUC__ >= 6 227 # pragma GCC diagnostic ignored "-Wnull-dereference" 228 # endif 229 #endif 230 231 namespace detail { 232 233 /* python command to get the values below: 234 def dec(v): 235 return str(v) 236 for bits in (8, 16, 32, 64): 237 imin, imax, umax = (-(1 << (bits - 1))), (1 << (bits - 1)) - 1, (1 << bits) - 1 238 for vname, v in (("imin", imin), ("imax", imax), ("umax", umax)): 239 for f in (bin, oct, dec, hex): 240 print(f"{bits}b: {vname}={v} {f.__name__}: len={len(f(v)):2d}: {v} {f(v)}") 241 */ 242 243 // do not use the type as the template argument because in some 244 // platforms long!=int32 and long!=int64. Just use the numbytes 245 // which is more generic and spares lengthy SFINAE code. 246 template<size_t num_bytes, bool is_signed> struct charconv_digits_; 247 template<class T> using charconv_digits = charconv_digits_<sizeof(T), std::is_signed<T>::value>; 248 249 template<> struct charconv_digits_<1u, true> // int8_t 250 { 251 enum : size_t { 252 maxdigits_bin = 1 + 2 + 8, // -128==-0b10000000 253 maxdigits_oct = 1 + 2 + 3, // -128==-0o200 254 maxdigits_dec = 1 + 3, // -128 255 maxdigits_hex = 1 + 2 + 2, // -128==-0x80 256 maxdigits_bin_nopfx = 8, // -128==-0b10000000 257 maxdigits_oct_nopfx = 3, // -128==-0o200 258 maxdigits_dec_nopfx = 3, // -128 259 maxdigits_hex_nopfx = 2, // -128==-0x80 260 }; 261 // min values without sign! 262 static constexpr csubstr min_value_dec() noexcept { return csubstr("128"); } 263 static constexpr csubstr min_value_hex() noexcept { return csubstr("80"); } 264 static constexpr csubstr min_value_oct() noexcept { return csubstr("200"); } 265 static constexpr csubstr min_value_bin() noexcept { return csubstr("10000000"); } 266 static constexpr csubstr max_value_dec() noexcept { return csubstr("127"); } 267 static constexpr bool is_oct_overflow(csubstr str) noexcept { return !((str.len < 3) || (str.len == 3 && str[0] <= '1')); } 268 }; 269 template<> struct charconv_digits_<1u, false> // uint8_t 270 { 271 enum : size_t { 272 maxdigits_bin = 2 + 8, // 255 0b11111111 273 maxdigits_oct = 2 + 3, // 255 0o377 274 maxdigits_dec = 3, // 255 275 maxdigits_hex = 2 + 2, // 255 0xff 276 maxdigits_bin_nopfx = 8, // 255 0b11111111 277 maxdigits_oct_nopfx = 3, // 255 0o377 278 maxdigits_dec_nopfx = 3, // 255 279 maxdigits_hex_nopfx = 2, // 255 0xff 280 }; 281 static constexpr csubstr max_value_dec() noexcept { return csubstr("255"); } 282 static constexpr bool is_oct_overflow(csubstr str) noexcept { return !((str.len < 3) || (str.len == 3 && str[0] <= '3')); } 283 }; 284 template<> struct charconv_digits_<2u, true> // int16_t 285 { 286 enum : size_t { 287 maxdigits_bin = 1 + 2 + 16, // -32768 -0b1000000000000000 288 maxdigits_oct = 1 + 2 + 6, // -32768 -0o100000 289 maxdigits_dec = 1 + 5, // -32768 -32768 290 maxdigits_hex = 1 + 2 + 4, // -32768 -0x8000 291 maxdigits_bin_nopfx = 16, // -32768 -0b1000000000000000 292 maxdigits_oct_nopfx = 6, // -32768 -0o100000 293 maxdigits_dec_nopfx = 5, // -32768 -32768 294 maxdigits_hex_nopfx = 4, // -32768 -0x8000 295 }; 296 // min values without sign! 297 static constexpr csubstr min_value_dec() noexcept { return csubstr("32768"); } 298 static constexpr csubstr min_value_hex() noexcept { return csubstr("8000"); } 299 static constexpr csubstr min_value_oct() noexcept { return csubstr("100000"); } 300 static constexpr csubstr min_value_bin() noexcept { return csubstr("1000000000000000"); } 301 static constexpr csubstr max_value_dec() noexcept { return csubstr("32767"); } 302 static constexpr bool is_oct_overflow(csubstr str) noexcept { return !((str.len < 6)); } 303 }; 304 template<> struct charconv_digits_<2u, false> // uint16_t 305 { 306 enum : size_t { 307 maxdigits_bin = 2 + 16, // 65535 0b1111111111111111 308 maxdigits_oct = 2 + 6, // 65535 0o177777 309 maxdigits_dec = 6, // 65535 65535 310 maxdigits_hex = 2 + 4, // 65535 0xffff 311 maxdigits_bin_nopfx = 16, // 65535 0b1111111111111111 312 maxdigits_oct_nopfx = 6, // 65535 0o177777 313 maxdigits_dec_nopfx = 6, // 65535 65535 314 maxdigits_hex_nopfx = 4, // 65535 0xffff 315 }; 316 static constexpr csubstr max_value_dec() noexcept { return csubstr("65535"); } 317 static constexpr bool is_oct_overflow(csubstr str) noexcept { return !((str.len < 6) || (str.len == 6 && str[0] <= '1')); } 318 }; 319 template<> struct charconv_digits_<4u, true> // int32_t 320 { 321 enum : size_t { 322 maxdigits_bin = 1 + 2 + 32, // len=35: -2147483648 -0b10000000000000000000000000000000 323 maxdigits_oct = 1 + 2 + 11, // len=14: -2147483648 -0o20000000000 324 maxdigits_dec = 1 + 10, // len=11: -2147483648 -2147483648 325 maxdigits_hex = 1 + 2 + 8, // len=11: -2147483648 -0x80000000 326 maxdigits_bin_nopfx = 32, // len=35: -2147483648 -0b10000000000000000000000000000000 327 maxdigits_oct_nopfx = 11, // len=14: -2147483648 -0o20000000000 328 maxdigits_dec_nopfx = 10, // len=11: -2147483648 -2147483648 329 maxdigits_hex_nopfx = 8, // len=11: -2147483648 -0x80000000 330 }; 331 // min values without sign! 332 static constexpr csubstr min_value_dec() noexcept { return csubstr("2147483648"); } 333 static constexpr csubstr min_value_hex() noexcept { return csubstr("80000000"); } 334 static constexpr csubstr min_value_oct() noexcept { return csubstr("20000000000"); } 335 static constexpr csubstr min_value_bin() noexcept { return csubstr("10000000000000000000000000000000"); } 336 static constexpr csubstr max_value_dec() noexcept { return csubstr("2147483647"); } 337 static constexpr bool is_oct_overflow(csubstr str) noexcept { return !((str.len < 11) || (str.len == 11 && str[0] <= '1')); } 338 }; 339 template<> struct charconv_digits_<4u, false> // uint32_t 340 { 341 enum : size_t { 342 maxdigits_bin = 2 + 32, // len=34: 4294967295 0b11111111111111111111111111111111 343 maxdigits_oct = 2 + 11, // len=13: 4294967295 0o37777777777 344 maxdigits_dec = 10, // len=10: 4294967295 4294967295 345 maxdigits_hex = 2 + 8, // len=10: 4294967295 0xffffffff 346 maxdigits_bin_nopfx = 32, // len=34: 4294967295 0b11111111111111111111111111111111 347 maxdigits_oct_nopfx = 11, // len=13: 4294967295 0o37777777777 348 maxdigits_dec_nopfx = 10, // len=10: 4294967295 4294967295 349 maxdigits_hex_nopfx = 8, // len=10: 4294967295 0xffffffff 350 }; 351 static constexpr csubstr max_value_dec() noexcept { return csubstr("4294967295"); } 352 static constexpr bool is_oct_overflow(csubstr str) noexcept { return !((str.len < 11) || (str.len == 11 && str[0] <= '3')); } 353 }; 354 template<> struct charconv_digits_<8u, true> // int32_t 355 { 356 enum : size_t { 357 maxdigits_bin = 1 + 2 + 64, // len=67: -9223372036854775808 -0b1000000000000000000000000000000000000000000000000000000000000000 358 maxdigits_oct = 1 + 2 + 22, // len=25: -9223372036854775808 -0o1000000000000000000000 359 maxdigits_dec = 1 + 19, // len=20: -9223372036854775808 -9223372036854775808 360 maxdigits_hex = 1 + 2 + 16, // len=19: -9223372036854775808 -0x8000000000000000 361 maxdigits_bin_nopfx = 64, // len=67: -9223372036854775808 -0b1000000000000000000000000000000000000000000000000000000000000000 362 maxdigits_oct_nopfx = 22, // len=25: -9223372036854775808 -0o1000000000000000000000 363 maxdigits_dec_nopfx = 19, // len=20: -9223372036854775808 -9223372036854775808 364 maxdigits_hex_nopfx = 16, // len=19: -9223372036854775808 -0x8000000000000000 365 }; 366 static constexpr csubstr min_value_dec() noexcept { return csubstr("9223372036854775808"); } 367 static constexpr csubstr min_value_hex() noexcept { return csubstr("8000000000000000"); } 368 static constexpr csubstr min_value_oct() noexcept { return csubstr("1000000000000000000000"); } 369 static constexpr csubstr min_value_bin() noexcept { return csubstr("1000000000000000000000000000000000000000000000000000000000000000"); } 370 static constexpr csubstr max_value_dec() noexcept { return csubstr("9223372036854775807"); } 371 static constexpr bool is_oct_overflow(csubstr str) noexcept { return !((str.len < 22)); } 372 }; 373 template<> struct charconv_digits_<8u, false> 374 { 375 enum : size_t { 376 maxdigits_bin = 2 + 64, // len=66: 18446744073709551615 0b1111111111111111111111111111111111111111111111111111111111111111 377 maxdigits_oct = 2 + 22, // len=24: 18446744073709551615 0o1777777777777777777777 378 maxdigits_dec = 20, // len=20: 18446744073709551615 18446744073709551615 379 maxdigits_hex = 2 + 16, // len=18: 18446744073709551615 0xffffffffffffffff 380 maxdigits_bin_nopfx = 64, // len=66: 18446744073709551615 0b1111111111111111111111111111111111111111111111111111111111111111 381 maxdigits_oct_nopfx = 22, // len=24: 18446744073709551615 0o1777777777777777777777 382 maxdigits_dec_nopfx = 20, // len=20: 18446744073709551615 18446744073709551615 383 maxdigits_hex_nopfx = 16, // len=18: 18446744073709551615 0xffffffffffffffff 384 }; 385 static constexpr csubstr max_value_dec() noexcept { return csubstr("18446744073709551615"); } 386 static constexpr bool is_oct_overflow(csubstr str) noexcept { return !((str.len < 22) || (str.len == 22 && str[0] <= '1')); } 387 }; 388 } // namespace detail 389 390 391 //----------------------------------------------------------------------------- 392 //----------------------------------------------------------------------------- 393 //----------------------------------------------------------------------------- 394 395 // Helper macros, undefined below 396 #define _c4append(c) { if(C4_LIKELY(pos < buf.len)) { buf.str[pos++] = static_cast<char>(c); } else { ++pos; } } 397 #define _c4appendhex(i) { if(C4_LIKELY(pos < buf.len)) { buf.str[pos++] = hexchars[i]; } else { ++pos; } } 398 399 /** @name digits_dec return the number of digits required to encode a 400 * decimal number. 401 * 402 * @note At first sight this code may look heavily branchy and 403 * therefore inefficient. However, measurements revealed this to be 404 * the fastest among the alternatives. 405 * 406 * @see https://github.com/biojppm/c4core/pull/77 */ 407 /** @{ */ 408 409 template<class T> 410 C4_CONSTEXPR14 C4_ALWAYS_INLINE 411 auto digits_dec(T v) noexcept 412 -> typename std::enable_if<sizeof(T) == 1u, unsigned>::type 413 { 414 C4_STATIC_ASSERT(std::is_integral<T>::value); 415 C4_ASSERT(v >= 0); 416 return ((v >= 100) ? 3u : ((v >= 10) ? 2u : 1u)); 417 } 418 419 template<class T> 420 C4_CONSTEXPR14 C4_ALWAYS_INLINE 421 auto digits_dec(T v) noexcept 422 -> typename std::enable_if<sizeof(T) == 2u, unsigned>::type 423 { 424 C4_STATIC_ASSERT(std::is_integral<T>::value); 425 C4_ASSERT(v >= 0); 426 return ((v >= 10000) ? 5u : (v >= 1000) ? 4u : (v >= 100) ? 3u : (v >= 10) ? 2u : 1u); 427 } 428 429 template<class T> 430 C4_CONSTEXPR14 C4_ALWAYS_INLINE 431 auto digits_dec(T v) noexcept 432 -> typename std::enable_if<sizeof(T) == 4u, unsigned>::type 433 { 434 C4_STATIC_ASSERT(std::is_integral<T>::value); 435 C4_ASSERT(v >= 0); 436 return ((v >= 1000000000) ? 10u : (v >= 100000000) ? 9u : (v >= 10000000) ? 8u : 437 (v >= 1000000) ? 7u : (v >= 100000) ? 6u : (v >= 10000) ? 5u : 438 (v >= 1000) ? 4u : (v >= 100) ? 3u : (v >= 10) ? 2u : 1u); 439 } 440 441 template<class T> 442 C4_CONSTEXPR14 C4_ALWAYS_INLINE 443 auto digits_dec(T v) noexcept 444 -> typename std::enable_if<sizeof(T) == 8u, unsigned>::type 445 { 446 // thanks @fargies!!! 447 // https://github.com/biojppm/c4core/pull/77#issuecomment-1063753568 448 C4_STATIC_ASSERT(std::is_integral<T>::value); 449 C4_ASSERT(v >= 0); 450 if(v >= 1000000000) // 10 451 { 452 if(v >= 100000000000000) // 15 [15-20] range 453 { 454 if(v >= 100000000000000000) // 18 (15 + (20 - 15) / 2) 455 { 456 if((typename std::make_unsigned<T>::type)v >= 10000000000000000000u) // 20 457 return 20u; 458 else 459 return (v >= 1000000000000000000) ? 19u : 18u; 460 } 461 else if(v >= 10000000000000000) // 17 462 return 17u; 463 else 464 return(v >= 1000000000000000) ? 16u : 15u; 465 } 466 else if(v >= 1000000000000) // 13 467 return (v >= 10000000000000) ? 14u : 13u; 468 else if(v >= 100000000000) // 12 469 return 12; 470 else 471 return(v >= 10000000000) ? 11u : 10u; 472 } 473 else if(v >= 10000) // 5 [5-9] range 474 { 475 if(v >= 10000000) // 8 476 return (v >= 100000000) ? 9u : 8u; 477 else if(v >= 1000000) // 7 478 return 7; 479 else 480 return (v >= 100000) ? 6u : 5u; 481 } 482 else if(v >= 100) 483 return (v >= 1000) ? 4u : 3u; 484 else 485 return (v >= 10) ? 2u : 1u; 486 } 487 488 /** @} */ 489 490 491 template<class T> 492 C4_CONSTEXPR14 C4_ALWAYS_INLINE unsigned digits_hex(T v) noexcept 493 { 494 C4_STATIC_ASSERT(std::is_integral<T>::value); 495 C4_ASSERT(v >= 0); 496 return v ? 1u + (msb((typename std::make_unsigned<T>::type)v) >> 2u) : 1u; 497 } 498 499 template<class T> 500 C4_CONSTEXPR14 C4_ALWAYS_INLINE unsigned digits_bin(T v) noexcept 501 { 502 C4_STATIC_ASSERT(std::is_integral<T>::value); 503 C4_ASSERT(v >= 0); 504 return v ? 1u + msb((typename std::make_unsigned<T>::type)v) : 1u; 505 } 506 507 template<class T> 508 C4_CONSTEXPR14 C4_ALWAYS_INLINE unsigned digits_oct(T v_) noexcept 509 { 510 // TODO: is there a better way? 511 C4_STATIC_ASSERT(std::is_integral<T>::value); 512 C4_ASSERT(v_ >= 0); 513 using U = typename 514 std::conditional<sizeof(T) <= sizeof(unsigned), 515 unsigned, 516 typename std::make_unsigned<T>::type>::type; 517 U v = (U) v_; // safe because we require v_ >= 0 518 unsigned __n = 1; 519 const unsigned __b2 = 64u; 520 const unsigned __b3 = __b2 * 8u; 521 const unsigned long __b4 = __b3 * 8u; 522 while(true) 523 { 524 if(v < 8u) 525 return __n; 526 if(v < __b2) 527 return __n + 1; 528 if(v < __b3) 529 return __n + 2; 530 if(v < __b4) 531 return __n + 3; 532 v /= (U) __b4; 533 __n += 4; 534 } 535 } 536 537 538 //----------------------------------------------------------------------------- 539 //----------------------------------------------------------------------------- 540 //----------------------------------------------------------------------------- 541 542 namespace detail { 543 C4_INLINE_CONSTEXPR const char hexchars[] = "0123456789abcdef"; 544 C4_INLINE_CONSTEXPR const char digits0099[] = 545 "0001020304050607080910111213141516171819" 546 "2021222324252627282930313233343536373839" 547 "4041424344454647484950515253545556575859" 548 "6061626364656667686970717273747576777879" 549 "8081828384858687888990919293949596979899"; 550 } // namespace detail 551 552 C4_SUPPRESS_WARNING_GCC_PUSH 553 C4_SUPPRESS_WARNING_GCC("-Warray-bounds") // gcc has false positives here 554 #if (defined(__GNUC__) && (__GNUC__ >= 7)) 555 C4_SUPPRESS_WARNING_GCC("-Wstringop-overflow") // gcc has false positives here 556 #endif 557 558 template<class T> 559 C4_HOT C4_ALWAYS_INLINE 560 void write_dec_unchecked(substr buf, T v, unsigned digits_v) noexcept 561 { 562 C4_STATIC_ASSERT(std::is_integral<T>::value); 563 C4_ASSERT(v >= 0); 564 C4_ASSERT(buf.len >= digits_v); 565 C4_XASSERT(digits_v == digits_dec(v)); 566 // in bm_xtoa: checkoncelog_singlediv_write2 567 while(v >= T(100)) 568 { 569 T quo = v; 570 quo /= T(100); 571 const auto num = (v - quo * T(100)) << 1u; 572 v = quo; 573 buf.str[--digits_v] = detail::digits0099[num + 1]; 574 buf.str[--digits_v] = detail::digits0099[num]; 575 } 576 if(v >= T(10)) 577 { 578 C4_ASSERT(digits_v == 2); 579 const auto num = v << 1u; 580 buf.str[1] = detail::digits0099[num + 1]; 581 buf.str[0] = detail::digits0099[num]; 582 } 583 else 584 { 585 C4_ASSERT(digits_v == 1); 586 buf.str[0] = (char)('0' + v); 587 } 588 } 589 590 591 template<class T> 592 C4_HOT C4_ALWAYS_INLINE 593 void write_hex_unchecked(substr buf, T v, unsigned digits_v) noexcept 594 { 595 C4_STATIC_ASSERT(std::is_integral<T>::value); 596 C4_ASSERT(v >= 0); 597 C4_ASSERT(buf.len >= digits_v); 598 C4_XASSERT(digits_v == digits_hex(v)); 599 do { 600 buf.str[--digits_v] = detail::hexchars[v & T(15)]; 601 v >>= 4; 602 } while(v); 603 C4_ASSERT(digits_v == 0); 604 } 605 606 607 template<class T> 608 C4_HOT C4_ALWAYS_INLINE 609 void write_oct_unchecked(substr buf, T v, unsigned digits_v) noexcept 610 { 611 C4_STATIC_ASSERT(std::is_integral<T>::value); 612 C4_ASSERT(v >= 0); 613 C4_ASSERT(buf.len >= digits_v); 614 C4_XASSERT(digits_v == digits_oct(v)); 615 do { 616 buf.str[--digits_v] = (char)('0' + (v & T(7))); 617 v >>= 3; 618 } while(v); 619 C4_ASSERT(digits_v == 0); 620 } 621 622 623 template<class T> 624 C4_HOT C4_ALWAYS_INLINE 625 void write_bin_unchecked(substr buf, T v, unsigned digits_v) noexcept 626 { 627 C4_STATIC_ASSERT(std::is_integral<T>::value); 628 C4_ASSERT(v >= 0); 629 C4_ASSERT(buf.len >= digits_v); 630 C4_XASSERT(digits_v == digits_bin(v)); 631 do { 632 buf.str[--digits_v] = (char)('0' + (v & T(1))); 633 v >>= 1; 634 } while(v); 635 C4_ASSERT(digits_v == 0); 636 } 637 638 639 /** write an integer to a string in decimal format. This is the 640 * lowest level (and the fastest) function to do this task. 641 * @note does not accept negative numbers 642 * @note the resulting string is NOT zero-terminated. 643 * @note it is ok to call this with an empty or too-small buffer; 644 * no writes will occur, and the required size will be returned 645 * @return the number of characters required for the buffer. */ 646 template<class T> 647 C4_ALWAYS_INLINE size_t write_dec(substr buf, T v) noexcept 648 { 649 C4_STATIC_ASSERT(std::is_integral<T>::value); 650 C4_ASSERT(v >= 0); 651 unsigned digits = digits_dec(v); 652 if(C4_LIKELY(buf.len >= digits)) 653 write_dec_unchecked(buf, v, digits); 654 return digits; 655 } 656 657 /** write an integer to a string in hexadecimal format. This is the 658 * lowest level (and the fastest) function to do this task. 659 * @note does not accept negative numbers 660 * @note does not prefix with 0x 661 * @note the resulting string is NOT zero-terminated. 662 * @note it is ok to call this with an empty or too-small buffer; 663 * no writes will occur, and the required size will be returned 664 * @return the number of characters required for the buffer. */ 665 template<class T> 666 C4_ALWAYS_INLINE size_t write_hex(substr buf, T v) noexcept 667 { 668 C4_STATIC_ASSERT(std::is_integral<T>::value); 669 C4_ASSERT(v >= 0); 670 unsigned digits = digits_hex(v); 671 if(C4_LIKELY(buf.len >= digits)) 672 write_hex_unchecked(buf, v, digits); 673 return digits; 674 } 675 676 /** write an integer to a string in octal format. This is the 677 * lowest level (and the fastest) function to do this task. 678 * @note does not accept negative numbers 679 * @note does not prefix with 0o 680 * @note the resulting string is NOT zero-terminated. 681 * @note it is ok to call this with an empty or too-small buffer; 682 * no writes will occur, and the required size will be returned 683 * @return the number of characters required for the buffer. */ 684 template<class T> 685 C4_ALWAYS_INLINE size_t write_oct(substr buf, T v) noexcept 686 { 687 C4_STATIC_ASSERT(std::is_integral<T>::value); 688 C4_ASSERT(v >= 0); 689 unsigned digits = digits_oct(v); 690 if(C4_LIKELY(buf.len >= digits)) 691 write_oct_unchecked(buf, v, digits); 692 return digits; 693 } 694 695 /** write an integer to a string in binary format. This is the 696 * lowest level (and the fastest) function to do this task. 697 * @note does not accept negative numbers 698 * @note does not prefix with 0b 699 * @note the resulting string is NOT zero-terminated. 700 * @note it is ok to call this with an empty or too-small buffer; 701 * no writes will occur, and the required size will be returned 702 * @return the number of characters required for the buffer. */ 703 template<class T> 704 C4_ALWAYS_INLINE size_t write_bin(substr buf, T v) noexcept 705 { 706 C4_STATIC_ASSERT(std::is_integral<T>::value); 707 C4_ASSERT(v >= 0); 708 unsigned digits = digits_bin(v); 709 C4_ASSERT(digits > 0); 710 if(C4_LIKELY(buf.len >= digits)) 711 write_bin_unchecked(buf, v, digits); 712 return digits; 713 } 714 715 716 namespace detail { 717 template<class U> using NumberWriter = size_t (*)(substr, U); 718 template<class T, NumberWriter<T> writer> 719 size_t write_num_digits(substr buf, T v, size_t num_digits) noexcept 720 { 721 C4_STATIC_ASSERT(std::is_integral<T>::value); 722 size_t ret = writer(buf, v); 723 if(ret >= num_digits) 724 return ret; 725 else if(ret >= buf.len || num_digits > buf.len) 726 return num_digits; 727 C4_ASSERT(num_digits >= ret); 728 size_t delta = static_cast<size_t>(num_digits - ret); 729 memmove(buf.str + delta, buf.str, ret); 730 memset(buf.str, '0', delta); 731 return num_digits; 732 } 733 } // namespace detail 734 735 736 /** same as c4::write_dec(), but pad with zeroes on the left 737 * such that the resulting string is @p num_digits wide. 738 * If the given number is requires more than num_digits, then the number prevails. */ 739 template<class T> 740 C4_ALWAYS_INLINE size_t write_dec(substr buf, T val, size_t num_digits) noexcept 741 { 742 return detail::write_num_digits<T, &write_dec<T>>(buf, val, num_digits); 743 } 744 745 /** same as c4::write_hex(), but pad with zeroes on the left 746 * such that the resulting string is @p num_digits wide. 747 * If the given number is requires more than num_digits, then the number prevails. */ 748 template<class T> 749 C4_ALWAYS_INLINE size_t write_hex(substr buf, T val, size_t num_digits) noexcept 750 { 751 return detail::write_num_digits<T, &write_hex<T>>(buf, val, num_digits); 752 } 753 754 /** same as c4::write_bin(), but pad with zeroes on the left 755 * such that the resulting string is @p num_digits wide. 756 * If the given number is requires more than num_digits, then the number prevails. */ 757 template<class T> 758 C4_ALWAYS_INLINE size_t write_bin(substr buf, T val, size_t num_digits) noexcept 759 { 760 return detail::write_num_digits<T, &write_bin<T>>(buf, val, num_digits); 761 } 762 763 /** same as c4::write_oct(), but pad with zeroes on the left 764 * such that the resulting string is @p num_digits wide. 765 * If the given number is requires more than num_digits, then the number prevails. */ 766 template<class T> 767 C4_ALWAYS_INLINE size_t write_oct(substr buf, T val, size_t num_digits) noexcept 768 { 769 return detail::write_num_digits<T, &write_oct<T>>(buf, val, num_digits); 770 } 771 772 C4_SUPPRESS_WARNING_GCC_POP 773 774 775 //----------------------------------------------------------------------------- 776 //----------------------------------------------------------------------------- 777 //----------------------------------------------------------------------------- 778 779 780 C4_SUPPRESS_WARNING_MSVC_PUSH 781 C4_SUPPRESS_WARNING_MSVC(4365) // '=': conversion from 'int' to 'I', signed/unsigned mismatch 782 783 /** read a decimal integer from a string. This is the 784 * lowest level (and the fastest) function to do this task. 785 * @note does not accept negative numbers 786 * @note The string must be trimmed. Whitespace is not accepted. 787 * @note the string must not be empty 788 * @note there is no check for overflow; the value wraps around 789 * in a way similar to the standard C/C++ overflow behavior. 790 * For example, `read_dec<int8_t>("128", &val)` returns true 791 * and val will be set to 0 because 127 is the max i8 value. 792 * @see overflows<T>() to find out if a number string overflows a type range 793 * @return true if the conversion was successful (no overflow check) */ 794 template<class I> 795 C4_ALWAYS_INLINE bool read_dec(csubstr s, I *C4_RESTRICT v) noexcept 796 { 797 C4_STATIC_ASSERT(std::is_integral<I>::value); 798 C4_ASSERT(!s.empty()); 799 *v = 0; 800 for(char c : s) 801 { 802 if(C4_UNLIKELY(c < '0' || c > '9')) 803 return false; 804 *v = (*v) * I(10) + (I(c) - I('0')); 805 } 806 return true; 807 } 808 809 /** read an hexadecimal integer from a string. This is the 810 * lowest level (and the fastest) function to do this task. 811 * @note does not accept negative numbers 812 * @note does not accept leading 0x or 0X 813 * @note the string must not be empty 814 * @note the string must be trimmed. Whitespace is not accepted. 815 * @note there is no check for overflow; the value wraps around 816 * in a way similar to the standard C/C++ overflow behavior. 817 * For example, `read_hex<int8_t>("80", &val)` returns true 818 * and val will be set to 0 because 7f is the max i8 value. 819 * @see overflows<T>() to find out if a number string overflows a type range 820 * @return true if the conversion was successful (no overflow check) */ 821 template<class I> 822 C4_ALWAYS_INLINE bool read_hex(csubstr s, I *C4_RESTRICT v) noexcept 823 { 824 C4_STATIC_ASSERT(std::is_integral<I>::value); 825 C4_ASSERT(!s.empty()); 826 *v = 0; 827 for(char c : s) 828 { 829 I cv; 830 if(c >= '0' && c <= '9') 831 cv = I(c) - I('0'); 832 else if(c >= 'a' && c <= 'f') 833 cv = I(10) + (I(c) - I('a')); 834 else if(c >= 'A' && c <= 'F') 835 cv = I(10) + (I(c) - I('A')); 836 else 837 return false; 838 *v = (*v) * I(16) + cv; 839 } 840 return true; 841 } 842 843 /** read a binary integer from a string. This is the 844 * lowest level (and the fastest) function to do this task. 845 * @note does not accept negative numbers 846 * @note does not accept leading 0b or 0B 847 * @note the string must not be empty 848 * @note the string must be trimmed. Whitespace is not accepted. 849 * @note there is no check for overflow; the value wraps around 850 * in a way similar to the standard C/C++ overflow behavior. 851 * For example, `read_bin<int8_t>("10000000", &val)` returns true 852 * and val will be set to 0 because 1111111 is the max i8 value. 853 * @see overflows<T>() to find out if a number string overflows a type range 854 * @return true if the conversion was successful (no overflow check) */ 855 template<class I> 856 C4_ALWAYS_INLINE bool read_bin(csubstr s, I *C4_RESTRICT v) noexcept 857 { 858 C4_STATIC_ASSERT(std::is_integral<I>::value); 859 C4_ASSERT(!s.empty()); 860 *v = 0; 861 for(char c : s) 862 { 863 *v <<= 1; 864 if(c == '1') 865 *v |= 1; 866 else if(c != '0') 867 return false; 868 } 869 return true; 870 } 871 872 /** read an octal integer from a string. This is the 873 * lowest level (and the fastest) function to do this task. 874 * @note does not accept negative numbers 875 * @note does not accept leading 0o or 0O 876 * @note the string must not be empty 877 * @note the string must be trimmed. Whitespace is not accepted. 878 * @note there is no check for overflow; the value wraps around 879 * in a way similar to the standard C/C++ overflow behavior. 880 * For example, `read_oct<int8_t>("200", &val)` returns true 881 * and val will be set to 0 because 177 is the max i8 value. 882 * @see overflows<T>() to find out if a number string overflows a type range 883 * @return true if the conversion was successful (no overflow check) */ 884 template<class I> 885 C4_ALWAYS_INLINE bool read_oct(csubstr s, I *C4_RESTRICT v) noexcept 886 { 887 C4_STATIC_ASSERT(std::is_integral<I>::value); 888 C4_ASSERT(!s.empty()); 889 *v = 0; 890 for(char c : s) 891 { 892 if(C4_UNLIKELY(c < '0' || c > '7')) 893 return false; 894 *v = (*v) * I(8) + (I(c) - I('0')); 895 } 896 return true; 897 } 898 899 C4_SUPPRESS_WARNING_MSVC_POP 900 901 902 //----------------------------------------------------------------------------- 903 //----------------------------------------------------------------------------- 904 //----------------------------------------------------------------------------- 905 906 C4_SUPPRESS_WARNING_GCC_WITH_PUSH("-Wswitch-default") 907 908 namespace detail { 909 inline size_t _itoa2buf(substr buf, size_t pos, csubstr val) noexcept 910 { 911 C4_ASSERT(pos + val.len <= buf.len); 912 memcpy(buf.str + pos, val.str, val.len); 913 return pos + val.len; 914 } 915 inline size_t _itoa2bufwithdigits(substr buf, size_t pos, size_t num_digits, csubstr val) noexcept 916 { 917 num_digits = num_digits > val.len ? num_digits - val.len : 0; 918 C4_ASSERT(num_digits + val.len <= buf.len); 919 for(size_t i = 0; i < num_digits; ++i) 920 _c4append('0'); 921 return detail::_itoa2buf(buf, pos, val); 922 } 923 template<class I> 924 C4_NO_INLINE size_t _itoadec2buf(substr buf) noexcept 925 { 926 using digits_type = detail::charconv_digits<I>; 927 if(C4_UNLIKELY(buf.len < digits_type::maxdigits_dec)) 928 return digits_type::maxdigits_dec; 929 buf.str[0] = '-'; 930 return detail::_itoa2buf(buf, 1, digits_type::min_value_dec()); 931 } 932 template<class I> 933 C4_NO_INLINE size_t _itoa2buf(substr buf, I radix) noexcept 934 { 935 using digits_type = detail::charconv_digits<I>; 936 size_t pos = 0; 937 if(C4_LIKELY(buf.len > 0)) 938 buf.str[pos++] = '-'; 939 switch(radix) 940 { 941 case I(10): 942 if(C4_UNLIKELY(buf.len < digits_type::maxdigits_dec)) 943 return digits_type::maxdigits_dec; 944 pos =_itoa2buf(buf, pos, digits_type::min_value_dec()); 945 break; 946 case I(16): 947 if(C4_UNLIKELY(buf.len < digits_type::maxdigits_hex)) 948 return digits_type::maxdigits_hex; 949 buf.str[pos++] = '0'; 950 buf.str[pos++] = 'x'; 951 pos = _itoa2buf(buf, pos, digits_type::min_value_hex()); 952 break; 953 case I( 2): 954 if(C4_UNLIKELY(buf.len < digits_type::maxdigits_bin)) 955 return digits_type::maxdigits_bin; 956 buf.str[pos++] = '0'; 957 buf.str[pos++] = 'b'; 958 pos = _itoa2buf(buf, pos, digits_type::min_value_bin()); 959 break; 960 case I( 8): 961 if(C4_UNLIKELY(buf.len < digits_type::maxdigits_oct)) 962 return digits_type::maxdigits_oct; 963 buf.str[pos++] = '0'; 964 buf.str[pos++] = 'o'; 965 pos = _itoa2buf(buf, pos, digits_type::min_value_oct()); 966 break; 967 } 968 return pos; 969 } 970 template<class I> 971 C4_NO_INLINE size_t _itoa2buf(substr buf, I radix, size_t num_digits) noexcept 972 { 973 using digits_type = detail::charconv_digits<I>; 974 size_t pos = 0; 975 size_t needed_digits = 0; 976 if(C4_LIKELY(buf.len > 0)) 977 buf.str[pos++] = '-'; 978 switch(radix) 979 { 980 case I(10): 981 // add 1 to account for - 982 needed_digits = num_digits+1 > digits_type::maxdigits_dec ? num_digits+1 : digits_type::maxdigits_dec; 983 if(C4_UNLIKELY(buf.len < needed_digits)) 984 return needed_digits; 985 pos = _itoa2bufwithdigits(buf, pos, num_digits, digits_type::min_value_dec()); 986 break; 987 case I(16): 988 // add 3 to account for -0x 989 needed_digits = num_digits+3 > digits_type::maxdigits_hex ? num_digits+3 : digits_type::maxdigits_hex; 990 if(C4_UNLIKELY(buf.len < needed_digits)) 991 return needed_digits; 992 buf.str[pos++] = '0'; 993 buf.str[pos++] = 'x'; 994 pos = _itoa2bufwithdigits(buf, pos, num_digits, digits_type::min_value_hex()); 995 break; 996 case I(2): 997 // add 3 to account for -0b 998 needed_digits = num_digits+3 > digits_type::maxdigits_bin ? num_digits+3 : digits_type::maxdigits_bin; 999 if(C4_UNLIKELY(buf.len < needed_digits)) 1000 return needed_digits; 1001 C4_ASSERT(buf.len >= digits_type::maxdigits_bin); 1002 buf.str[pos++] = '0'; 1003 buf.str[pos++] = 'b'; 1004 pos = _itoa2bufwithdigits(buf, pos, num_digits, digits_type::min_value_bin()); 1005 break; 1006 case I(8): 1007 // add 3 to account for -0o 1008 needed_digits = num_digits+3 > digits_type::maxdigits_oct ? num_digits+3 : digits_type::maxdigits_oct; 1009 if(C4_UNLIKELY(buf.len < needed_digits)) 1010 return needed_digits; 1011 C4_ASSERT(buf.len >= digits_type::maxdigits_oct); 1012 buf.str[pos++] = '0'; 1013 buf.str[pos++] = 'o'; 1014 pos = _itoa2bufwithdigits(buf, pos, num_digits, digits_type::min_value_oct()); 1015 break; 1016 } 1017 return pos; 1018 } 1019 } // namespace detail 1020 1021 1022 /** convert an integral signed decimal to a string. 1023 * @note the resulting string is NOT zero-terminated. 1024 * @note it is ok to call this with an empty or too-small buffer; 1025 * no writes will occur, and the needed size will be returned 1026 * @return the number of characters required for the buffer. */ 1027 template<class T> 1028 C4_ALWAYS_INLINE size_t itoa(substr buf, T v) noexcept 1029 { 1030 C4_STATIC_ASSERT(std::is_signed<T>::value); 1031 if(v >= T(0)) 1032 { 1033 // write_dec() checks the buffer size, so no need to check here 1034 return write_dec(buf, v); 1035 } 1036 // when T is the min value (eg i8: -128), negating it 1037 // will overflow, so treat the min as a special case 1038 else if(C4_LIKELY(v != std::numeric_limits<T>::min())) 1039 { 1040 v = -v; 1041 unsigned digits = digits_dec(v); 1042 if(C4_LIKELY(buf.len >= digits + 1u)) 1043 { 1044 buf.str[0] = '-'; 1045 write_dec_unchecked(buf.sub(1), v, digits); 1046 } 1047 return digits + 1u; 1048 } 1049 return detail::_itoadec2buf<T>(buf); 1050 } 1051 1052 /** convert an integral signed integer to a string, using a specific 1053 * radix. The radix must be 2, 8, 10 or 16. 1054 * 1055 * @note the resulting string is NOT zero-terminated. 1056 * @note it is ok to call this with an empty or too-small buffer; 1057 * no writes will occur, and the needed size will be returned 1058 * @return the number of characters required for the buffer. */ 1059 template<class T> 1060 C4_ALWAYS_INLINE size_t itoa(substr buf, T v, T radix) noexcept 1061 { 1062 C4_STATIC_ASSERT(std::is_signed<T>::value); 1063 C4_ASSERT(radix == 2 || radix == 8 || radix == 10 || radix == 16); 1064 C4_SUPPRESS_WARNING_GCC_PUSH 1065 #if (defined(__GNUC__) && (__GNUC__ >= 7)) 1066 C4_SUPPRESS_WARNING_GCC("-Wstringop-overflow") // gcc has a false positive here 1067 #endif 1068 // when T is the min value (eg i8: -128), negating it 1069 // will overflow, so treat the min as a special case 1070 if(C4_LIKELY(v != std::numeric_limits<T>::min())) 1071 { 1072 unsigned pos = 0; 1073 if(v < 0) 1074 { 1075 v = -v; 1076 if(C4_LIKELY(buf.len > 0)) 1077 buf.str[pos] = '-'; 1078 ++pos; 1079 } 1080 unsigned digits = 0; 1081 switch(radix) 1082 { 1083 case T(10): 1084 digits = digits_dec(v); 1085 if(C4_LIKELY(buf.len >= pos + digits)) 1086 write_dec_unchecked(buf.sub(pos), v, digits); 1087 break; 1088 case T(16): 1089 digits = digits_hex(v); 1090 if(C4_LIKELY(buf.len >= pos + 2u + digits)) 1091 { 1092 buf.str[pos + 0] = '0'; 1093 buf.str[pos + 1] = 'x'; 1094 write_hex_unchecked(buf.sub(pos + 2), v, digits); 1095 } 1096 digits += 2u; 1097 break; 1098 case T(2): 1099 digits = digits_bin(v); 1100 if(C4_LIKELY(buf.len >= pos + 2u + digits)) 1101 { 1102 buf.str[pos + 0] = '0'; 1103 buf.str[pos + 1] = 'b'; 1104 write_bin_unchecked(buf.sub(pos + 2), v, digits); 1105 } 1106 digits += 2u; 1107 break; 1108 case T(8): 1109 digits = digits_oct(v); 1110 if(C4_LIKELY(buf.len >= pos + 2u + digits)) 1111 { 1112 buf.str[pos + 0] = '0'; 1113 buf.str[pos + 1] = 'o'; 1114 write_oct_unchecked(buf.sub(pos + 2), v, digits); 1115 } 1116 digits += 2u; 1117 break; 1118 } 1119 return pos + digits; 1120 } 1121 C4_SUPPRESS_WARNING_GCC_POP 1122 // when T is the min value (eg i8: -128), negating it 1123 // will overflow 1124 return detail::_itoa2buf<T>(buf, radix); 1125 } 1126 1127 1128 /** same as c4::itoa(), but pad with zeroes on the left such that the 1129 * resulting string is @p num_digits wide, not accounting for radix 1130 * prefix (0x,0o,0b). The @p radix must be 2, 8, 10 or 16. 1131 * 1132 * @note the resulting string is NOT zero-terminated. 1133 * @note it is ok to call this with an empty or too-small buffer; 1134 * no writes will occur, and the needed size will be returned 1135 * @return the number of characters required for the buffer. */ 1136 template<class T> 1137 C4_ALWAYS_INLINE size_t itoa(substr buf, T v, T radix, size_t num_digits) noexcept 1138 { 1139 C4_STATIC_ASSERT(std::is_signed<T>::value); 1140 C4_ASSERT(radix == 2 || radix == 8 || radix == 10 || radix == 16); 1141 C4_SUPPRESS_WARNING_GCC_PUSH 1142 #if (defined(__GNUC__) && (__GNUC__ >= 7)) 1143 C4_SUPPRESS_WARNING_GCC("-Wstringop-overflow") // gcc has a false positive here 1144 #endif 1145 // when T is the min value (eg i8: -128), negating it 1146 // will overflow, so treat the min as a special case 1147 if(C4_LIKELY(v != std::numeric_limits<T>::min())) 1148 { 1149 unsigned pos = 0; 1150 if(v < 0) 1151 { 1152 v = -v; 1153 if(C4_LIKELY(buf.len > 0)) 1154 buf.str[pos] = '-'; 1155 ++pos; 1156 } 1157 unsigned total_digits = 0; 1158 switch(radix) 1159 { 1160 case T(10): 1161 total_digits = digits_dec(v); 1162 total_digits = pos + (unsigned)(num_digits > total_digits ? num_digits : total_digits); 1163 if(C4_LIKELY(buf.len >= total_digits)) 1164 write_dec(buf.sub(pos), v, num_digits); 1165 break; 1166 case T(16): 1167 total_digits = digits_hex(v); 1168 total_digits = pos + 2u + (unsigned)(num_digits > total_digits ? num_digits : total_digits); 1169 if(C4_LIKELY(buf.len >= total_digits)) 1170 { 1171 buf.str[pos + 0] = '0'; 1172 buf.str[pos + 1] = 'x'; 1173 write_hex(buf.sub(pos + 2), v, num_digits); 1174 } 1175 break; 1176 case T(2): 1177 total_digits = digits_bin(v); 1178 total_digits = pos + 2u + (unsigned)(num_digits > total_digits ? num_digits : total_digits); 1179 if(C4_LIKELY(buf.len >= total_digits)) 1180 { 1181 buf.str[pos + 0] = '0'; 1182 buf.str[pos + 1] = 'b'; 1183 write_bin(buf.sub(pos + 2), v, num_digits); 1184 } 1185 break; 1186 case T(8): 1187 total_digits = digits_oct(v); 1188 total_digits = pos + 2u + (unsigned)(num_digits > total_digits ? num_digits : total_digits); 1189 if(C4_LIKELY(buf.len >= total_digits)) 1190 { 1191 buf.str[pos + 0] = '0'; 1192 buf.str[pos + 1] = 'o'; 1193 write_oct(buf.sub(pos + 2), v, num_digits); 1194 } 1195 break; 1196 } 1197 return total_digits; 1198 } 1199 C4_SUPPRESS_WARNING_GCC_POP 1200 // when T is the min value (eg i8: -128), negating it 1201 // will overflow 1202 return detail::_itoa2buf<T>(buf, radix, num_digits); 1203 } 1204 1205 1206 //----------------------------------------------------------------------------- 1207 //----------------------------------------------------------------------------- 1208 //----------------------------------------------------------------------------- 1209 1210 /** convert an integral unsigned decimal to a string. 1211 * 1212 * @note the resulting string is NOT zero-terminated. 1213 * @note it is ok to call this with an empty or too-small buffer; 1214 * no writes will occur, and the needed size will be returned 1215 * @return the number of characters required for the buffer. */ 1216 template<class T> 1217 C4_ALWAYS_INLINE size_t utoa(substr buf, T v) noexcept 1218 { 1219 C4_STATIC_ASSERT(std::is_unsigned<T>::value); 1220 // write_dec() does the buffer length check, so no need to check here 1221 return write_dec(buf, v); 1222 } 1223 1224 /** convert an integral unsigned integer to a string, using a specific 1225 * radix. The radix must be 2, 8, 10 or 16. 1226 * 1227 * @note the resulting string is NOT zero-terminated. 1228 * @note it is ok to call this with an empty or too-small buffer; 1229 * no writes will occur, and the needed size will be returned 1230 * @return the number of characters required for the buffer. */ 1231 template<class T> 1232 C4_ALWAYS_INLINE size_t utoa(substr buf, T v, T radix) noexcept 1233 { 1234 C4_STATIC_ASSERT(std::is_unsigned<T>::value); 1235 C4_ASSERT(radix == 10 || radix == 16 || radix == 2 || radix == 8); 1236 unsigned digits = 0; 1237 switch(radix) 1238 { 1239 case T(10): 1240 digits = digits_dec(v); 1241 if(C4_LIKELY(buf.len >= digits)) 1242 write_dec_unchecked(buf, v, digits); 1243 break; 1244 case T(16): 1245 digits = digits_hex(v); 1246 if(C4_LIKELY(buf.len >= digits+2u)) 1247 { 1248 buf.str[0] = '0'; 1249 buf.str[1] = 'x'; 1250 write_hex_unchecked(buf.sub(2), v, digits); 1251 } 1252 digits += 2u; 1253 break; 1254 case T(2): 1255 digits = digits_bin(v); 1256 if(C4_LIKELY(buf.len >= digits+2u)) 1257 { 1258 buf.str[0] = '0'; 1259 buf.str[1] = 'b'; 1260 write_bin_unchecked(buf.sub(2), v, digits); 1261 } 1262 digits += 2u; 1263 break; 1264 case T(8): 1265 digits = digits_oct(v); 1266 if(C4_LIKELY(buf.len >= digits+2u)) 1267 { 1268 buf.str[0] = '0'; 1269 buf.str[1] = 'o'; 1270 write_oct_unchecked(buf.sub(2), v, digits); 1271 } 1272 digits += 2u; 1273 break; 1274 } 1275 return digits; 1276 } 1277 1278 /** same as c4::utoa(), but pad with zeroes on the left such that the 1279 * resulting string is @p num_digits wide. The @p radix must be 2, 1280 * 8, 10 or 16. 1281 * 1282 * @note the resulting string is NOT zero-terminated. 1283 * @note it is ok to call this with an empty or too-small buffer; 1284 * no writes will occur, and the needed size will be returned 1285 * @return the number of characters required for the buffer. */ 1286 template<class T> 1287 C4_ALWAYS_INLINE size_t utoa(substr buf, T v, T radix, size_t num_digits) noexcept 1288 { 1289 C4_STATIC_ASSERT(std::is_unsigned<T>::value); 1290 C4_ASSERT(radix == 10 || radix == 16 || radix == 2 || radix == 8); 1291 unsigned total_digits = 0; 1292 switch(radix) 1293 { 1294 case T(10): 1295 total_digits = digits_dec(v); 1296 total_digits = (unsigned)(num_digits > total_digits ? num_digits : total_digits); 1297 if(C4_LIKELY(buf.len >= total_digits)) 1298 write_dec(buf, v, num_digits); 1299 break; 1300 case T(16): 1301 total_digits = digits_hex(v); 1302 total_digits = 2u + (unsigned)(num_digits > total_digits ? num_digits : total_digits); 1303 if(C4_LIKELY(buf.len >= total_digits)) 1304 { 1305 buf.str[0] = '0'; 1306 buf.str[1] = 'x'; 1307 write_hex(buf.sub(2), v, num_digits); 1308 } 1309 break; 1310 case T(2): 1311 total_digits = digits_bin(v); 1312 total_digits = 2u + (unsigned)(num_digits > total_digits ? num_digits : total_digits); 1313 if(C4_LIKELY(buf.len >= total_digits)) 1314 { 1315 buf.str[0] = '0'; 1316 buf.str[1] = 'b'; 1317 write_bin(buf.sub(2), v, num_digits); 1318 } 1319 break; 1320 case T(8): 1321 total_digits = digits_oct(v); 1322 total_digits = 2u + (unsigned)(num_digits > total_digits ? num_digits : total_digits); 1323 if(C4_LIKELY(buf.len >= total_digits)) 1324 { 1325 buf.str[0] = '0'; 1326 buf.str[1] = 'o'; 1327 write_oct(buf.sub(2), v, num_digits); 1328 } 1329 break; 1330 } 1331 return total_digits; 1332 } 1333 C4_SUPPRESS_WARNING_GCC_POP 1334 1335 1336 //----------------------------------------------------------------------------- 1337 //----------------------------------------------------------------------------- 1338 //----------------------------------------------------------------------------- 1339 1340 /** Convert a trimmed string to a signed integral value. The input 1341 * string can be formatted as decimal, binary (prefix 0b or 0B), octal 1342 * (prefix 0o or 0O) or hexadecimal (prefix 0x or 0X). Strings with 1343 * leading zeroes are considered as decimal and not octal (unlike the 1344 * C/C++ convention). Every character in the input string is read for 1345 * the conversion; the input string must not contain any leading or 1346 * trailing whitespace. 1347 * 1348 * @return true if the conversion was successful. 1349 * 1350 * @note overflow is not detected: the return status is true even if 1351 * the conversion would return a value outside of the type's range, in 1352 * which case the result will wrap around the type's range. 1353 * This is similar to native behavior. 1354 * 1355 * @note a positive sign is not accepted. ie, the string must not 1356 * start with '+' 1357 * 1358 * @see atoi_first() if the string is not trimmed to the value to read. */ 1359 template<class T> 1360 C4_ALWAYS_INLINE bool atoi(csubstr str, T * C4_RESTRICT v) noexcept 1361 { 1362 C4_STATIC_ASSERT(std::is_integral<T>::value); 1363 C4_STATIC_ASSERT(std::is_signed<T>::value); 1364 1365 if(C4_UNLIKELY(str.len == 0)) 1366 return false; 1367 1368 C4_ASSERT(str.str[0] != '+'); 1369 1370 T sign = 1; 1371 size_t start = 0; 1372 if(str.str[0] == '-') 1373 { 1374 if(C4_UNLIKELY(str.len == ++start)) 1375 return false; 1376 sign = -1; 1377 } 1378 1379 bool parsed_ok = true; 1380 if(str.str[start] != '0') // this should be the common case, so put it first 1381 { 1382 parsed_ok = read_dec(str.sub(start), v); 1383 } 1384 else if(str.len > start + 1) 1385 { 1386 // starts with 0: is it 0x, 0o, 0b? 1387 const char pfx = str.str[start + 1]; 1388 if(pfx == 'x' || pfx == 'X') 1389 parsed_ok = str.len > start + 2 && read_hex(str.sub(start + 2), v); 1390 else if(pfx == 'b' || pfx == 'B') 1391 parsed_ok = str.len > start + 2 && read_bin(str.sub(start + 2), v); 1392 else if(pfx == 'o' || pfx == 'O') 1393 parsed_ok = str.len > start + 2 && read_oct(str.sub(start + 2), v); 1394 else 1395 parsed_ok = read_dec(str.sub(start + 1), v); 1396 } 1397 else 1398 { 1399 parsed_ok = read_dec(str.sub(start), v); 1400 } 1401 if(C4_LIKELY(parsed_ok)) 1402 *v *= sign; 1403 return parsed_ok; 1404 } 1405 1406 1407 /** Select the next range of characters in the string that can be parsed 1408 * as a signed integral value, and convert it using atoi(). Leading 1409 * whitespace (space, newline, tabs) is skipped. 1410 * @return the number of characters read for conversion, or csubstr::npos if the conversion failed 1411 * @see atoi() if the string is already trimmed to the value to read. 1412 * @see csubstr::first_int_span() */ 1413 template<class T> 1414 C4_ALWAYS_INLINE size_t atoi_first(csubstr str, T * C4_RESTRICT v) 1415 { 1416 csubstr trimmed = str.first_int_span(); 1417 if(trimmed.len == 0) 1418 return csubstr::npos; 1419 if(atoi(trimmed, v)) 1420 return static_cast<size_t>(trimmed.end() - str.begin()); 1421 return csubstr::npos; 1422 } 1423 1424 1425 //----------------------------------------------------------------------------- 1426 1427 /** Convert a trimmed string to an unsigned integral value. The string can be 1428 * formatted as decimal, binary (prefix 0b or 0B), octal (prefix 0o or 0O) 1429 * or hexadecimal (prefix 0x or 0X). Every character in the input string is read 1430 * for the conversion; it must not contain any leading or trailing whitespace. 1431 * 1432 * @return true if the conversion was successful. 1433 * 1434 * @note overflow is not detected: the return status is true even if 1435 * the conversion would return a value outside of the type's range, in 1436 * which case the result will wrap around the type's range. 1437 * 1438 * @note If the string has a minus character, the return status 1439 * will be false. 1440 * 1441 * @see atou_first() if the string is not trimmed to the value to read. */ 1442 template<class T> 1443 bool atou(csubstr str, T * C4_RESTRICT v) noexcept 1444 { 1445 C4_STATIC_ASSERT(std::is_integral<T>::value); 1446 1447 if(C4_UNLIKELY(str.len == 0 || str.front() == '-')) 1448 return false; 1449 1450 bool parsed_ok = true; 1451 if(str.str[0] != '0') 1452 { 1453 parsed_ok = read_dec(str, v); 1454 } 1455 else 1456 { 1457 if(str.len > 1) 1458 { 1459 const char pfx = str.str[1]; 1460 if(pfx == 'x' || pfx == 'X') 1461 parsed_ok = str.len > 2 && read_hex(str.sub(2), v); 1462 else if(pfx == 'b' || pfx == 'B') 1463 parsed_ok = str.len > 2 && read_bin(str.sub(2), v); 1464 else if(pfx == 'o' || pfx == 'O') 1465 parsed_ok = str.len > 2 && read_oct(str.sub(2), v); 1466 else 1467 parsed_ok = read_dec(str, v); 1468 } 1469 else 1470 { 1471 *v = 0; // we know the first character is 0 1472 } 1473 } 1474 return parsed_ok; 1475 } 1476 1477 1478 /** Select the next range of characters in the string that can be parsed 1479 * as an unsigned integral value, and convert it using atou(). Leading 1480 * whitespace (space, newline, tabs) is skipped. 1481 * @return the number of characters read for conversion, or csubstr::npos if the conversion faileds 1482 * @see atou() if the string is already trimmed to the value to read. 1483 * @see csubstr::first_uint_span() */ 1484 template<class T> 1485 C4_ALWAYS_INLINE size_t atou_first(csubstr str, T *v) 1486 { 1487 csubstr trimmed = str.first_uint_span(); 1488 if(trimmed.len == 0) 1489 return csubstr::npos; 1490 if(atou(trimmed, v)) 1491 return static_cast<size_t>(trimmed.end() - str.begin()); 1492 return csubstr::npos; 1493 } 1494 1495 1496 #ifdef _MSC_VER 1497 # pragma warning(pop) 1498 #elif defined(__clang__) 1499 # pragma clang diagnostic pop 1500 #elif defined(__GNUC__) 1501 # pragma GCC diagnostic pop 1502 #endif 1503 1504 1505 //----------------------------------------------------------------------------- 1506 //----------------------------------------------------------------------------- 1507 //----------------------------------------------------------------------------- 1508 namespace detail { 1509 inline bool check_overflow(csubstr str, csubstr limit) noexcept 1510 { 1511 if(str.len == limit.len) 1512 { 1513 for(size_t i = 0; i < limit.len; ++i) 1514 { 1515 if(str[i] < limit[i]) 1516 return false; 1517 else if(str[i] > limit[i]) 1518 return true; 1519 } 1520 return false; 1521 } 1522 else 1523 return str.len > limit.len; 1524 } 1525 } // namespace detail 1526 1527 1528 /** Test if the following string would overflow when converted to associated 1529 * types. 1530 * @return true if number will overflow, false if it fits (or doesn't parse) 1531 */ 1532 template<class T> 1533 auto overflows(csubstr str) noexcept 1534 -> typename std::enable_if<std::is_unsigned<T>::value, bool>::type 1535 { 1536 C4_STATIC_ASSERT(std::is_integral<T>::value); 1537 1538 if(C4_UNLIKELY(str.len == 0)) 1539 { 1540 return false; 1541 } 1542 else if(str.str[0] == '0') 1543 { 1544 if (str.len == 1) 1545 return false; 1546 switch (str.str[1]) 1547 { 1548 case 'x': 1549 case 'X': 1550 { 1551 size_t fno = str.first_not_of('0', 2); 1552 if (fno == csubstr::npos) 1553 return false; 1554 return !(str.len <= fno + (sizeof(T) * 2)); 1555 } 1556 case 'b': 1557 case 'B': 1558 { 1559 size_t fno = str.first_not_of('0', 2); 1560 if (fno == csubstr::npos) 1561 return false; 1562 return !(str.len <= fno +(sizeof(T) * 8)); 1563 } 1564 case 'o': 1565 case 'O': 1566 { 1567 size_t fno = str.first_not_of('0', 2); 1568 if(fno == csubstr::npos) 1569 return false; 1570 return detail::charconv_digits<T>::is_oct_overflow(str.sub(fno)); 1571 } 1572 default: 1573 { 1574 size_t fno = str.first_not_of('0', 1); 1575 if(fno == csubstr::npos) 1576 return false; 1577 return detail::check_overflow(str.sub(fno), detail::charconv_digits<T>::max_value_dec()); 1578 } 1579 } 1580 } 1581 else if(C4_UNLIKELY(str[0] == '-')) 1582 { 1583 return true; 1584 } 1585 else 1586 { 1587 return detail::check_overflow(str, detail::charconv_digits<T>::max_value_dec()); 1588 } 1589 } 1590 1591 1592 /** Test if the following string would overflow when converted to associated 1593 * types. 1594 * @return true if number will overflow, false if it fits (or doesn't parse) 1595 */ 1596 template<class T> 1597 auto overflows(csubstr str) 1598 -> typename std::enable_if<std::is_signed<T>::value, bool>::type 1599 { 1600 C4_STATIC_ASSERT(std::is_integral<T>::value); 1601 if(C4_UNLIKELY(str.len == 0)) 1602 return false; 1603 if(str.str[0] == '-') 1604 { 1605 if(str.str[1] == '0') 1606 { 1607 if(str.len == 2) 1608 return false; 1609 switch(str.str[2]) 1610 { 1611 case 'x': 1612 case 'X': 1613 { 1614 size_t fno = str.first_not_of('0', 3); 1615 if (fno == csubstr::npos) 1616 return false; 1617 return detail::check_overflow(str.sub(fno), detail::charconv_digits<T>::min_value_hex()); 1618 } 1619 case 'b': 1620 case 'B': 1621 { 1622 size_t fno = str.first_not_of('0', 3); 1623 if (fno == csubstr::npos) 1624 return false; 1625 return detail::check_overflow(str.sub(fno), detail::charconv_digits<T>::min_value_bin()); 1626 } 1627 case 'o': 1628 case 'O': 1629 { 1630 size_t fno = str.first_not_of('0', 3); 1631 if(fno == csubstr::npos) 1632 return false; 1633 return detail::check_overflow(str.sub(fno), detail::charconv_digits<T>::min_value_oct()); 1634 } 1635 default: 1636 { 1637 size_t fno = str.first_not_of('0', 2); 1638 if(fno == csubstr::npos) 1639 return false; 1640 return detail::check_overflow(str.sub(fno), detail::charconv_digits<T>::min_value_dec()); 1641 } 1642 } 1643 } 1644 else 1645 return detail::check_overflow(str.sub(1), detail::charconv_digits<T>::min_value_dec()); 1646 } 1647 else if(str.str[0] == '0') 1648 { 1649 if (str.len == 1) 1650 return false; 1651 switch(str.str[1]) 1652 { 1653 case 'x': 1654 case 'X': 1655 { 1656 size_t fno = str.first_not_of('0', 2); 1657 if (fno == csubstr::npos) 1658 return false; 1659 const size_t len = str.len - fno; 1660 return !((len < sizeof (T) * 2) || (len == sizeof(T) * 2 && str[fno] <= '7')); 1661 } 1662 case 'b': 1663 case 'B': 1664 { 1665 size_t fno = str.first_not_of('0', 2); 1666 if (fno == csubstr::npos) 1667 return false; 1668 return !(str.len <= fno + (sizeof(T) * 8 - 1)); 1669 } 1670 case 'o': 1671 case 'O': 1672 { 1673 size_t fno = str.first_not_of('0', 2); 1674 if(fno == csubstr::npos) 1675 return false; 1676 return detail::charconv_digits<T>::is_oct_overflow(str.sub(fno)); 1677 } 1678 default: 1679 { 1680 size_t fno = str.first_not_of('0', 1); 1681 if(fno == csubstr::npos) 1682 return false; 1683 return detail::check_overflow(str.sub(fno), detail::charconv_digits<T>::max_value_dec()); 1684 } 1685 } 1686 } 1687 else 1688 return detail::check_overflow(str, detail::charconv_digits<T>::max_value_dec()); 1689 } 1690 1691 1692 //----------------------------------------------------------------------------- 1693 //----------------------------------------------------------------------------- 1694 //----------------------------------------------------------------------------- 1695 1696 namespace detail { 1697 1698 1699 #if (!C4CORE_HAVE_STD_FROMCHARS) 1700 /** @see http://www.exploringbinary.com/ for many good examples on float-str conversion */ 1701 template<size_t N> 1702 void get_real_format_str(char (& C4_RESTRICT fmt)[N], int precision, RealFormat_e formatting, const char* length_modifier="") 1703 { 1704 int iret; 1705 if(precision == -1) 1706 iret = snprintf(fmt, sizeof(fmt), "%%%s%c", length_modifier, formatting); 1707 else if(precision == 0) 1708 iret = snprintf(fmt, sizeof(fmt), "%%.%s%c", length_modifier, formatting); 1709 else 1710 iret = snprintf(fmt, sizeof(fmt), "%%.%d%s%c", precision, length_modifier, formatting); 1711 C4_ASSERT(iret >= 2 && size_t(iret) < sizeof(fmt)); 1712 C4_UNUSED(iret); 1713 } 1714 1715 1716 /** @todo we're depending on snprintf()/sscanf() for converting to/from 1717 * floating point numbers. Apparently, this increases the binary size 1718 * by a considerable amount. There are some lightweight printf 1719 * implementations: 1720 * 1721 * @see http://www.sparetimelabs.com/tinyprintf/tinyprintf.php (BSD) 1722 * @see https://github.com/weiss/c99-snprintf 1723 * @see https://github.com/nothings/stb/blob/master/stb_sprintf.h 1724 * @see http://www.exploringbinary.com/ 1725 * @see https://blog.benoitblanchon.fr/lightweight-float-to-string/ 1726 * @see http://www.ryanjuckett.com/programming/printing-floating-point-numbers/ 1727 */ 1728 template<class T> 1729 size_t print_one(substr str, const char* full_fmt, T v) 1730 { 1731 #ifdef _MSC_VER 1732 /** use _snprintf() to prevent early termination of the output 1733 * for writing the null character at the last position 1734 * @see https://msdn.microsoft.com/en-us/library/2ts7cx93.aspx */ 1735 int iret = _snprintf(str.str, str.len, full_fmt, v); 1736 if(iret < 0) 1737 { 1738 /* when buf.len is not enough, VS returns a negative value. 1739 * so call it again with a negative value for getting an 1740 * actual length of the string */ 1741 iret = snprintf(nullptr, 0, full_fmt, v); 1742 C4_ASSERT(iret > 0); 1743 } 1744 size_t ret = (size_t) iret; 1745 return ret; 1746 #else 1747 int iret = snprintf(str.str, str.len, full_fmt, v); 1748 C4_ASSERT(iret >= 0); 1749 size_t ret = (size_t) iret; 1750 if(ret >= str.len) 1751 ++ret; /* snprintf() reserves the last character to write \0 */ 1752 return ret; 1753 #endif 1754 } 1755 #endif // (!C4CORE_HAVE_STD_FROMCHARS) 1756 1757 1758 #if (!C4CORE_HAVE_STD_FROMCHARS) && (!C4CORE_HAVE_FAST_FLOAT) 1759 /** scans a string using the given type format, while at the same time 1760 * allowing non-null-terminated strings AND guaranteeing that the given 1761 * string length is strictly respected, so that no buffer overflows 1762 * might occur. */ 1763 template<typename T> 1764 inline size_t scan_one(csubstr str, const char *type_fmt, T *v) 1765 { 1766 /* snscanf() is absolutely needed here as we must be sure that 1767 * str.len is strictly respected, because substr is 1768 * generally not null-terminated. 1769 * 1770 * Alas, there is no snscanf(). 1771 * 1772 * So we fake it by using a dynamic format with an explicit 1773 * field size set to the length of the given span. 1774 * This trick is taken from: 1775 * https://stackoverflow.com/a/18368910/5875572 */ 1776 1777 /* this is the actual format we'll use for scanning */ 1778 char fmt[16]; 1779 1780 /* write the length into it. Eg "%12f". 1781 * Also, get the number of characters read from the string. 1782 * So the final format ends up as "%12f%n"*/ 1783 int iret = std::snprintf(fmt, sizeof(fmt), "%%" "%zu" "%s" "%%n", str.len, type_fmt); 1784 /* no nasty surprises, please! */ 1785 C4_ASSERT(iret >= 0 && size_t(iret) < C4_COUNTOF(fmt)); 1786 1787 /* now we scan with confidence that the span length is respected */ 1788 int num_chars; 1789 iret = std::sscanf(str.str, fmt, v, &num_chars); 1790 /* scanf returns the number of successful conversions */ 1791 if(iret != 1) return csubstr::npos; 1792 C4_ASSERT(num_chars >= 0); 1793 return (size_t)(num_chars); 1794 } 1795 #endif // (!C4CORE_HAVE_STD_FROMCHARS) && (!C4CORE_HAVE_FAST_FLOAT) 1796 1797 1798 #if C4CORE_HAVE_STD_TOCHARS 1799 template<class T> 1800 C4_ALWAYS_INLINE size_t rtoa(substr buf, T v, int precision=-1, RealFormat_e formatting=FTOA_FLEX) noexcept 1801 { 1802 std::to_chars_result result; 1803 size_t pos = 0; 1804 if(formatting == FTOA_HEXA) 1805 { 1806 if(buf.len > size_t(2)) 1807 { 1808 buf.str[0] = '0'; 1809 buf.str[1] = 'x'; 1810 } 1811 pos += size_t(2); 1812 } 1813 if(precision == -1) 1814 result = std::to_chars(buf.str + pos, buf.str + buf.len, v, (std::chars_format)formatting); 1815 else 1816 result = std::to_chars(buf.str + pos, buf.str + buf.len, v, (std::chars_format)formatting, precision); 1817 if(result.ec == std::errc()) 1818 { 1819 // all good, no errors. 1820 C4_ASSERT(result.ptr >= buf.str); 1821 ptrdiff_t delta = result.ptr - buf.str; 1822 return static_cast<size_t>(delta); 1823 } 1824 C4_ASSERT(result.ec == std::errc::value_too_large); 1825 // This is unfortunate. 1826 // 1827 // When the result can't fit in the given buffer, 1828 // std::to_chars() returns the end pointer it was originally 1829 // given, which is useless because here we would like to know 1830 // _exactly_ how many characters the buffer must have to fit 1831 // the result. 1832 // 1833 // So we take the pessimistic view, and assume as many digits 1834 // as could ever be required: 1835 size_t ret = static_cast<size_t>(std::numeric_limits<T>::max_digits10); 1836 return ret > buf.len ? ret : buf.len + 1; 1837 } 1838 #endif // C4CORE_HAVE_STD_TOCHARS 1839 1840 1841 #if C4CORE_HAVE_FAST_FLOAT 1842 template<class T> 1843 C4_ALWAYS_INLINE bool scan_rhex(csubstr s, T *C4_RESTRICT val) noexcept 1844 { 1845 C4_ASSERT(s.len > 0); 1846 C4_ASSERT(s.str[0] != '-'); 1847 C4_ASSERT(s.str[0] != '+'); 1848 C4_ASSERT(!s.begins_with("0x")); 1849 C4_ASSERT(!s.begins_with("0X")); 1850 size_t pos = 0; 1851 // integer part 1852 for( ; pos < s.len; ++pos) 1853 { 1854 const char c = s.str[pos]; 1855 if(c >= '0' && c <= '9') 1856 *val = *val * T(16) + T(c - '0'); 1857 else if(c >= 'a' && c <= 'f') 1858 *val = *val * T(16) + T(c - 'a'); 1859 else if(c >= 'A' && c <= 'F') 1860 *val = *val * T(16) + T(c - 'A'); 1861 else if(c == '.') 1862 { 1863 ++pos; 1864 break; // follow on to mantissa 1865 } 1866 else if(c == 'p' || c == 'P') 1867 { 1868 ++pos; 1869 goto power; // no mantissa given, jump to power 1870 } 1871 else 1872 { 1873 return false; 1874 } 1875 } 1876 // mantissa 1877 { 1878 // 0.0625 == 1/16 == value of first digit after the comma 1879 for(T digit = T(0.0625); pos < s.len; ++pos, digit /= T(16)) 1880 { 1881 const char c = s.str[pos]; 1882 if(c >= '0' && c <= '9') 1883 *val += digit * T(c - '0'); 1884 else if(c >= 'a' && c <= 'f') 1885 *val += digit * T(c - 'a'); 1886 else if(c >= 'A' && c <= 'F') 1887 *val += digit * T(c - 'A'); 1888 else if(c == 'p' || c == 'P') 1889 { 1890 ++pos; 1891 goto power; // mantissa finished, jump to power 1892 } 1893 else 1894 { 1895 return false; 1896 } 1897 } 1898 } 1899 return true; 1900 power: 1901 if(C4_LIKELY(pos < s.len)) 1902 { 1903 if(s.str[pos] == '+') // atoi() cannot handle a leading '+' 1904 ++pos; 1905 if(C4_LIKELY(pos < s.len)) 1906 { 1907 int16_t powval = {}; 1908 if(C4_LIKELY(atoi(s.sub(pos), &powval))) 1909 { 1910 *val *= ipow<T, int16_t, 16>(powval); 1911 return true; 1912 } 1913 } 1914 } 1915 return false; 1916 } 1917 #endif 1918 1919 } // namespace detail 1920 1921 1922 #undef _c4appendhex 1923 #undef _c4append 1924 1925 1926 /** Convert a single-precision real number to string. The string will 1927 * in general be NOT null-terminated. For FTOA_FLEX, \p precision is 1928 * the number of significand digits. Otherwise \p precision is the 1929 * number of decimals. It is safe to call this function with an empty 1930 * or too-small buffer. 1931 * 1932 * @return the size of the buffer needed to write the number 1933 */ 1934 C4_ALWAYS_INLINE size_t ftoa(substr str, float v, int precision=-1, RealFormat_e formatting=FTOA_FLEX) noexcept 1935 { 1936 #if C4CORE_HAVE_STD_TOCHARS 1937 return detail::rtoa(str, v, precision, formatting); 1938 #else 1939 char fmt[16]; 1940 detail::get_real_format_str(fmt, precision, formatting, /*length_modifier*/""); 1941 return detail::print_one(str, fmt, v); 1942 #endif 1943 } 1944 1945 1946 /** Convert a double-precision real number to string. The string will 1947 * in general be NOT null-terminated. For FTOA_FLEX, \p precision is 1948 * the number of significand digits. Otherwise \p precision is the 1949 * number of decimals. It is safe to call this function with an empty 1950 * or too-small buffer. 1951 * 1952 * @return the size of the buffer needed to write the number 1953 */ 1954 C4_ALWAYS_INLINE size_t dtoa(substr str, double v, int precision=-1, RealFormat_e formatting=FTOA_FLEX) noexcept 1955 { 1956 #if C4CORE_HAVE_STD_TOCHARS 1957 return detail::rtoa(str, v, precision, formatting); 1958 #else 1959 char fmt[16]; 1960 detail::get_real_format_str(fmt, precision, formatting, /*length_modifier*/"l"); 1961 return detail::print_one(str, fmt, v); 1962 #endif 1963 } 1964 1965 1966 /** Convert a string to a single precision real number. 1967 * The input string must be trimmed to the value, ie 1968 * no leading or trailing whitespace can be present. 1969 * @return true iff the conversion succeeded 1970 * @see atof_first() if the string is not trimmed 1971 */ 1972 C4_ALWAYS_INLINE bool atof(csubstr str, float * C4_RESTRICT v) noexcept 1973 { 1974 C4_ASSERT(str.len > 0); 1975 C4_ASSERT(str.triml(" \r\t\n").len == str.len); 1976 #if C4CORE_HAVE_FAST_FLOAT 1977 // fastfloat cannot parse hexadecimal floats 1978 bool isneg = (str.str[0] == '-'); 1979 csubstr rem = str.sub(isneg || str.str[0] == '+'); 1980 if(!(rem.len >= 2 && (rem.str[0] == '0' && (rem.str[1] == 'x' || rem.str[1] == 'X')))) 1981 { 1982 fast_float::from_chars_result result; 1983 result = fast_float::from_chars(str.str, str.str + str.len, *v); 1984 return result.ec == std::errc(); 1985 } 1986 else if(detail::scan_rhex(rem.sub(2), v)) 1987 { 1988 *v *= isneg ? -1.f : 1.f; 1989 return true; 1990 } 1991 return false; 1992 #elif C4CORE_HAVE_STD_FROMCHARS 1993 std::from_chars_result result; 1994 result = std::from_chars(str.str, str.str + str.len, *v); 1995 return result.ec == std::errc(); 1996 #else 1997 csubstr rem = str.sub(str.str[0] == '-' || str.str[0] == '+'); 1998 if(!(rem.len >= 2 && (rem.str[0] == '0' && (rem.str[1] == 'x' || rem.str[1] == 'X')))) 1999 return detail::scan_one(str, "f", v) != csubstr::npos; 2000 else 2001 return detail::scan_one(str, "a", v) != csubstr::npos; 2002 #endif 2003 } 2004 2005 2006 /** Convert a string to a double precision real number. 2007 * The input string must be trimmed to the value, ie 2008 * no leading or trailing whitespace can be present. 2009 * @return true iff the conversion succeeded 2010 * @see atod_first() if the string is not trimmed 2011 */ 2012 C4_ALWAYS_INLINE bool atod(csubstr str, double * C4_RESTRICT v) noexcept 2013 { 2014 C4_ASSERT(str.triml(" \r\t\n").len == str.len); 2015 #if C4CORE_HAVE_FAST_FLOAT 2016 // fastfloat cannot parse hexadecimal floats 2017 bool isneg = (str.str[0] == '-'); 2018 csubstr rem = str.sub(isneg || str.str[0] == '+'); 2019 if(!(rem.len >= 2 && (rem.str[0] == '0' && (rem.str[1] == 'x' || rem.str[1] == 'X')))) 2020 { 2021 fast_float::from_chars_result result; 2022 result = fast_float::from_chars(str.str, str.str + str.len, *v); 2023 return result.ec == std::errc(); 2024 } 2025 else if(detail::scan_rhex(rem.sub(2), v)) 2026 { 2027 *v *= isneg ? -1. : 1.; 2028 return true; 2029 } 2030 return false; 2031 #elif C4CORE_HAVE_STD_FROMCHARS 2032 std::from_chars_result result; 2033 result = std::from_chars(str.str, str.str + str.len, *v); 2034 return result.ec == std::errc(); 2035 #else 2036 csubstr rem = str.sub(str.str[0] == '-' || str.str[0] == '+'); 2037 if(!(rem.len >= 2 && (rem.str[0] == '0' && (rem.str[1] == 'x' || rem.str[1] == 'X')))) 2038 return detail::scan_one(str, "lf", v) != csubstr::npos; 2039 else 2040 return detail::scan_one(str, "la", v) != csubstr::npos; 2041 #endif 2042 } 2043 2044 2045 /** Convert a string to a single precision real number. 2046 * Leading whitespace is skipped until valid characters are found. 2047 * @return the number of characters read from the string, or npos if 2048 * conversion was not successful or if the string was empty */ 2049 inline size_t atof_first(csubstr str, float * C4_RESTRICT v) noexcept 2050 { 2051 csubstr trimmed = str.first_real_span(); 2052 if(trimmed.len == 0) 2053 return csubstr::npos; 2054 if(atof(trimmed, v)) 2055 return static_cast<size_t>(trimmed.end() - str.begin()); 2056 return csubstr::npos; 2057 } 2058 2059 2060 /** Convert a string to a double precision real number. 2061 * Leading whitespace is skipped until valid characters are found. 2062 * @return the number of characters read from the string, or npos if 2063 * conversion was not successful or if the string was empty */ 2064 inline size_t atod_first(csubstr str, double * C4_RESTRICT v) noexcept 2065 { 2066 csubstr trimmed = str.first_real_span(); 2067 if(trimmed.len == 0) 2068 return csubstr::npos; 2069 if(atod(trimmed, v)) 2070 return static_cast<size_t>(trimmed.end() - str.begin()); 2071 return csubstr::npos; 2072 } 2073 2074 2075 //----------------------------------------------------------------------------- 2076 //----------------------------------------------------------------------------- 2077 //----------------------------------------------------------------------------- 2078 // generic versions 2079 2080 C4_ALWAYS_INLINE size_t xtoa(substr s, uint8_t v) noexcept { return write_dec(s, v); } 2081 C4_ALWAYS_INLINE size_t xtoa(substr s, uint16_t v) noexcept { return write_dec(s, v); } 2082 C4_ALWAYS_INLINE size_t xtoa(substr s, uint32_t v) noexcept { return write_dec(s, v); } 2083 C4_ALWAYS_INLINE size_t xtoa(substr s, uint64_t v) noexcept { return write_dec(s, v); } 2084 C4_ALWAYS_INLINE size_t xtoa(substr s, int8_t v) noexcept { return itoa(s, v); } 2085 C4_ALWAYS_INLINE size_t xtoa(substr s, int16_t v) noexcept { return itoa(s, v); } 2086 C4_ALWAYS_INLINE size_t xtoa(substr s, int32_t v) noexcept { return itoa(s, v); } 2087 C4_ALWAYS_INLINE size_t xtoa(substr s, int64_t v) noexcept { return itoa(s, v); } 2088 C4_ALWAYS_INLINE size_t xtoa(substr s, float v) noexcept { return ftoa(s, v); } 2089 C4_ALWAYS_INLINE size_t xtoa(substr s, double v) noexcept { return dtoa(s, v); } 2090 2091 C4_ALWAYS_INLINE size_t xtoa(substr s, uint8_t v, uint8_t radix) noexcept { return utoa(s, v, radix); } 2092 C4_ALWAYS_INLINE size_t xtoa(substr s, uint16_t v, uint16_t radix) noexcept { return utoa(s, v, radix); } 2093 C4_ALWAYS_INLINE size_t xtoa(substr s, uint32_t v, uint32_t radix) noexcept { return utoa(s, v, radix); } 2094 C4_ALWAYS_INLINE size_t xtoa(substr s, uint64_t v, uint64_t radix) noexcept { return utoa(s, v, radix); } 2095 C4_ALWAYS_INLINE size_t xtoa(substr s, int8_t v, int8_t radix) noexcept { return itoa(s, v, radix); } 2096 C4_ALWAYS_INLINE size_t xtoa(substr s, int16_t v, int16_t radix) noexcept { return itoa(s, v, radix); } 2097 C4_ALWAYS_INLINE size_t xtoa(substr s, int32_t v, int32_t radix) noexcept { return itoa(s, v, radix); } 2098 C4_ALWAYS_INLINE size_t xtoa(substr s, int64_t v, int64_t radix) noexcept { return itoa(s, v, radix); } 2099 2100 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); } 2101 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); } 2102 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); } 2103 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); } 2104 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); } 2105 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); } 2106 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); } 2107 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); } 2108 2109 C4_ALWAYS_INLINE size_t xtoa(substr s, float v, int precision, RealFormat_e formatting=FTOA_FLEX) noexcept { return ftoa(s, v, precision, formatting); } 2110 C4_ALWAYS_INLINE size_t xtoa(substr s, double v, int precision, RealFormat_e formatting=FTOA_FLEX) noexcept { return dtoa(s, v, precision, formatting); } 2111 2112 C4_ALWAYS_INLINE bool atox(csubstr s, uint8_t *C4_RESTRICT v) noexcept { return atou(s, v); } 2113 C4_ALWAYS_INLINE bool atox(csubstr s, uint16_t *C4_RESTRICT v) noexcept { return atou(s, v); } 2114 C4_ALWAYS_INLINE bool atox(csubstr s, uint32_t *C4_RESTRICT v) noexcept { return atou(s, v); } 2115 C4_ALWAYS_INLINE bool atox(csubstr s, uint64_t *C4_RESTRICT v) noexcept { return atou(s, v); } 2116 C4_ALWAYS_INLINE bool atox(csubstr s, int8_t *C4_RESTRICT v) noexcept { return atoi(s, v); } 2117 C4_ALWAYS_INLINE bool atox(csubstr s, int16_t *C4_RESTRICT v) noexcept { return atoi(s, v); } 2118 C4_ALWAYS_INLINE bool atox(csubstr s, int32_t *C4_RESTRICT v) noexcept { return atoi(s, v); } 2119 C4_ALWAYS_INLINE bool atox(csubstr s, int64_t *C4_RESTRICT v) noexcept { return atoi(s, v); } 2120 C4_ALWAYS_INLINE bool atox(csubstr s, float *C4_RESTRICT v) noexcept { return atof(s, v); } 2121 C4_ALWAYS_INLINE bool atox(csubstr s, double *C4_RESTRICT v) noexcept { return atod(s, v); } 2122 2123 C4_ALWAYS_INLINE size_t to_chars(substr buf, uint8_t v) noexcept { return write_dec(buf, v); } 2124 C4_ALWAYS_INLINE size_t to_chars(substr buf, uint16_t v) noexcept { return write_dec(buf, v); } 2125 C4_ALWAYS_INLINE size_t to_chars(substr buf, uint32_t v) noexcept { return write_dec(buf, v); } 2126 C4_ALWAYS_INLINE size_t to_chars(substr buf, uint64_t v) noexcept { return write_dec(buf, v); } 2127 C4_ALWAYS_INLINE size_t to_chars(substr buf, int8_t v) noexcept { return itoa(buf, v); } 2128 C4_ALWAYS_INLINE size_t to_chars(substr buf, int16_t v) noexcept { return itoa(buf, v); } 2129 C4_ALWAYS_INLINE size_t to_chars(substr buf, int32_t v) noexcept { return itoa(buf, v); } 2130 C4_ALWAYS_INLINE size_t to_chars(substr buf, int64_t v) noexcept { return itoa(buf, v); } 2131 C4_ALWAYS_INLINE size_t to_chars(substr buf, float v) noexcept { return ftoa(buf, v); } 2132 C4_ALWAYS_INLINE size_t to_chars(substr buf, double v) noexcept { return dtoa(buf, v); } 2133 2134 C4_ALWAYS_INLINE bool from_chars(csubstr buf, uint8_t *C4_RESTRICT v) noexcept { return atou(buf, v); } 2135 C4_ALWAYS_INLINE bool from_chars(csubstr buf, uint16_t *C4_RESTRICT v) noexcept { return atou(buf, v); } 2136 C4_ALWAYS_INLINE bool from_chars(csubstr buf, uint32_t *C4_RESTRICT v) noexcept { return atou(buf, v); } 2137 C4_ALWAYS_INLINE bool from_chars(csubstr buf, uint64_t *C4_RESTRICT v) noexcept { return atou(buf, v); } 2138 C4_ALWAYS_INLINE bool from_chars(csubstr buf, int8_t *C4_RESTRICT v) noexcept { return atoi(buf, v); } 2139 C4_ALWAYS_INLINE bool from_chars(csubstr buf, int16_t *C4_RESTRICT v) noexcept { return atoi(buf, v); } 2140 C4_ALWAYS_INLINE bool from_chars(csubstr buf, int32_t *C4_RESTRICT v) noexcept { return atoi(buf, v); } 2141 C4_ALWAYS_INLINE bool from_chars(csubstr buf, int64_t *C4_RESTRICT v) noexcept { return atoi(buf, v); } 2142 C4_ALWAYS_INLINE bool from_chars(csubstr buf, float *C4_RESTRICT v) noexcept { return atof(buf, v); } 2143 C4_ALWAYS_INLINE bool from_chars(csubstr buf, double *C4_RESTRICT v) noexcept { return atod(buf, v); } 2144 2145 C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, uint8_t *C4_RESTRICT v) noexcept { return atou_first(buf, v); } 2146 C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, uint16_t *C4_RESTRICT v) noexcept { return atou_first(buf, v); } 2147 C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, uint32_t *C4_RESTRICT v) noexcept { return atou_first(buf, v); } 2148 C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, uint64_t *C4_RESTRICT v) noexcept { return atou_first(buf, v); } 2149 C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, int8_t *C4_RESTRICT v) noexcept { return atoi_first(buf, v); } 2150 C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, int16_t *C4_RESTRICT v) noexcept { return atoi_first(buf, v); } 2151 C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, int32_t *C4_RESTRICT v) noexcept { return atoi_first(buf, v); } 2152 C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, int64_t *C4_RESTRICT v) noexcept { return atoi_first(buf, v); } 2153 C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, float *C4_RESTRICT v) noexcept { return atof_first(buf, v); } 2154 C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, double *C4_RESTRICT v) noexcept { return atod_first(buf, v); } 2155 2156 2157 //----------------------------------------------------------------------------- 2158 // on some platforms, (unsigned) int and (unsigned) long 2159 // are not any of the fixed length types above 2160 2161 #define _C4_IF_NOT_FIXED_LENGTH_I(T, ty) C4_ALWAYS_INLINE typename std::enable_if<std:: is_signed<T>::value && !is_fixed_length<T>::value_i, ty> 2162 #define _C4_IF_NOT_FIXED_LENGTH_U(T, ty) C4_ALWAYS_INLINE typename std::enable_if<std::is_unsigned<T>::value && !is_fixed_length<T>::value_u, ty> 2163 2164 template <class T> _C4_IF_NOT_FIXED_LENGTH_I(T, size_t)::type xtoa(substr buf, T v) noexcept { return itoa(buf, v); } 2165 template <class T> _C4_IF_NOT_FIXED_LENGTH_U(T, size_t)::type xtoa(substr buf, T v) noexcept { return write_dec(buf, v); } 2166 2167 template <class T> _C4_IF_NOT_FIXED_LENGTH_I(T, bool )::type atox(csubstr buf, T *C4_RESTRICT v) noexcept { return atoi(buf, v); } 2168 template <class T> _C4_IF_NOT_FIXED_LENGTH_U(T, bool )::type atox(csubstr buf, T *C4_RESTRICT v) noexcept { return atou(buf, v); } 2169 2170 template <class T> _C4_IF_NOT_FIXED_LENGTH_I(T, size_t)::type to_chars(substr buf, T v) noexcept { return itoa(buf, v); } 2171 template <class T> _C4_IF_NOT_FIXED_LENGTH_U(T, size_t)::type to_chars(substr buf, T v) noexcept { return write_dec(buf, v); } 2172 2173 template <class T> _C4_IF_NOT_FIXED_LENGTH_I(T, bool )::type from_chars(csubstr buf, T *C4_RESTRICT v) noexcept { return atoi(buf, v); } 2174 template <class T> _C4_IF_NOT_FIXED_LENGTH_U(T, bool )::type from_chars(csubstr buf, T *C4_RESTRICT v) noexcept { return atou(buf, v); } 2175 2176 template <class T> _C4_IF_NOT_FIXED_LENGTH_I(T, size_t)::type from_chars_first(csubstr buf, T *C4_RESTRICT v) noexcept { return atoi_first(buf, v); } 2177 template <class T> _C4_IF_NOT_FIXED_LENGTH_U(T, size_t)::type from_chars_first(csubstr buf, T *C4_RESTRICT v) noexcept { return atou_first(buf, v); } 2178 2179 #undef _C4_IF_NOT_FIXED_LENGTH_I 2180 #undef _C4_IF_NOT_FIXED_LENGTH_U 2181 2182 2183 //----------------------------------------------------------------------------- 2184 // for pointers 2185 2186 template <class T> C4_ALWAYS_INLINE size_t xtoa(substr s, T *v) noexcept { return itoa(s, (intptr_t)v, (intptr_t)16); } 2187 template <class T> 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; } 2188 template <class T> C4_ALWAYS_INLINE size_t to_chars(substr s, T *v) noexcept { return itoa(s, (intptr_t)v, (intptr_t)16); } 2189 template <class T> 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; } 2190 template <class T> C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, T **v) noexcept { intptr_t tmp; bool ret = from_chars_first(buf, &tmp); if(ret) { *v = (T*)tmp; } return ret; } 2191 2192 2193 //----------------------------------------------------------------------------- 2194 //----------------------------------------------------------------------------- 2195 //----------------------------------------------------------------------------- 2196 /** call to_chars() and return a substr consisting of the 2197 * written portion of the input buffer. Ie, same as to_chars(), 2198 * but return a substr instead of a size_t. 2199 * 2200 * @see to_chars() */ 2201 template<class T> 2202 C4_ALWAYS_INLINE substr to_chars_sub(substr buf, T const& C4_RESTRICT v) noexcept 2203 { 2204 size_t sz = to_chars(buf, v); 2205 return buf.left_of(sz <= buf.len ? sz : buf.len); 2206 } 2207 2208 //----------------------------------------------------------------------------- 2209 //----------------------------------------------------------------------------- 2210 //----------------------------------------------------------------------------- 2211 // bool implementation 2212 2213 C4_ALWAYS_INLINE size_t to_chars(substr buf, bool v) noexcept 2214 { 2215 int val = v; 2216 return to_chars(buf, val); 2217 } 2218 2219 inline bool from_chars(csubstr buf, bool * C4_RESTRICT v) noexcept 2220 { 2221 if(buf == '0') 2222 { 2223 *v = false; return true; 2224 } 2225 else if(buf == '1') 2226 { 2227 *v = true; return true; 2228 } 2229 else if(buf == "false") 2230 { 2231 *v = false; return true; 2232 } 2233 else if(buf == "true") 2234 { 2235 *v = true; return true; 2236 } 2237 else if(buf == "False") 2238 { 2239 *v = false; return true; 2240 } 2241 else if(buf == "True") 2242 { 2243 *v = true; return true; 2244 } 2245 else if(buf == "FALSE") 2246 { 2247 *v = false; return true; 2248 } 2249 else if(buf == "TRUE") 2250 { 2251 *v = true; return true; 2252 } 2253 // fallback to c-style int bools 2254 int val = 0; 2255 bool ret = from_chars(buf, &val); 2256 if(C4_LIKELY(ret)) 2257 { 2258 *v = (val != 0); 2259 } 2260 return ret; 2261 } 2262 2263 inline size_t from_chars_first(csubstr buf, bool * C4_RESTRICT v) noexcept 2264 { 2265 csubstr trimmed = buf.first_non_empty_span(); 2266 if(trimmed.len == 0 || !from_chars(buf, v)) 2267 return csubstr::npos; 2268 return trimmed.len; 2269 } 2270 2271 2272 //----------------------------------------------------------------------------- 2273 // single-char implementation 2274 2275 inline size_t to_chars(substr buf, char v) noexcept 2276 { 2277 if(buf.len > 0) 2278 { 2279 C4_XASSERT(buf.str); 2280 buf.str[0] = v; 2281 } 2282 return 1; 2283 } 2284 2285 /** extract a single character from a substring 2286 * @note to extract a string instead and not just a single character, use the csubstr overload */ 2287 inline bool from_chars(csubstr buf, char * C4_RESTRICT v) noexcept 2288 { 2289 if(buf.len != 1) 2290 return false; 2291 C4_XASSERT(buf.str); 2292 *v = buf.str[0]; 2293 return true; 2294 } 2295 2296 inline size_t from_chars_first(csubstr buf, char * C4_RESTRICT v) noexcept 2297 { 2298 if(buf.len < 1) 2299 return csubstr::npos; 2300 *v = buf.str[0]; 2301 return 1; 2302 } 2303 2304 2305 //----------------------------------------------------------------------------- 2306 // csubstr implementation 2307 2308 inline size_t to_chars(substr buf, csubstr v) noexcept 2309 { 2310 C4_ASSERT(!buf.overlaps(v)); 2311 size_t len = buf.len < v.len ? buf.len : v.len; 2312 // calling memcpy with null strings is undefined behavior 2313 // and will wreak havoc in calling code's branches. 2314 // see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637 2315 if(len) 2316 { 2317 C4_ASSERT(buf.str != nullptr); 2318 C4_ASSERT(v.str != nullptr); 2319 memcpy(buf.str, v.str, len); 2320 } 2321 return v.len; 2322 } 2323 2324 inline bool from_chars(csubstr buf, csubstr *C4_RESTRICT v) noexcept 2325 { 2326 *v = buf; 2327 return true; 2328 } 2329 2330 inline size_t from_chars_first(substr buf, csubstr * C4_RESTRICT v) noexcept 2331 { 2332 csubstr trimmed = buf.first_non_empty_span(); 2333 if(trimmed.len == 0) 2334 return csubstr::npos; 2335 *v = trimmed; 2336 return static_cast<size_t>(trimmed.end() - buf.begin()); 2337 } 2338 2339 2340 //----------------------------------------------------------------------------- 2341 // substr 2342 2343 inline size_t to_chars(substr buf, substr v) noexcept 2344 { 2345 C4_ASSERT(!buf.overlaps(v)); 2346 size_t len = buf.len < v.len ? buf.len : v.len; 2347 // calling memcpy with null strings is undefined behavior 2348 // and will wreak havoc in calling code's branches. 2349 // see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637 2350 if(len) 2351 { 2352 C4_ASSERT(buf.str != nullptr); 2353 C4_ASSERT(v.str != nullptr); 2354 memcpy(buf.str, v.str, len); 2355 } 2356 return v.len; 2357 } 2358 2359 inline bool from_chars(csubstr buf, substr * C4_RESTRICT v) noexcept 2360 { 2361 C4_ASSERT(!buf.overlaps(*v)); 2362 // is the destination buffer wide enough? 2363 if(v->len >= buf.len) 2364 { 2365 // calling memcpy with null strings is undefined behavior 2366 // and will wreak havoc in calling code's branches. 2367 // see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637 2368 if(buf.len) 2369 { 2370 C4_ASSERT(buf.str != nullptr); 2371 C4_ASSERT(v->str != nullptr); 2372 memcpy(v->str, buf.str, buf.len); 2373 } 2374 v->len = buf.len; 2375 return true; 2376 } 2377 return false; 2378 } 2379 2380 inline size_t from_chars_first(csubstr buf, substr * C4_RESTRICT v) noexcept 2381 { 2382 csubstr trimmed = buf.first_non_empty_span(); 2383 C4_ASSERT(!trimmed.overlaps(*v)); 2384 if(C4_UNLIKELY(trimmed.len == 0)) 2385 return csubstr::npos; 2386 size_t len = trimmed.len > v->len ? v->len : trimmed.len; 2387 // calling memcpy with null strings is undefined behavior 2388 // and will wreak havoc in calling code's branches. 2389 // see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637 2390 if(len) 2391 { 2392 C4_ASSERT(buf.str != nullptr); 2393 C4_ASSERT(v->str != nullptr); 2394 memcpy(v->str, trimmed.str, len); 2395 } 2396 if(C4_UNLIKELY(trimmed.len > v->len)) 2397 return csubstr::npos; 2398 return static_cast<size_t>(trimmed.end() - buf.begin()); 2399 } 2400 2401 2402 //----------------------------------------------------------------------------- 2403 2404 template<size_t N> 2405 inline size_t to_chars(substr buf, const char (& C4_RESTRICT v)[N]) noexcept 2406 { 2407 csubstr sp(v); 2408 return to_chars(buf, sp); 2409 } 2410 2411 inline size_t to_chars(substr buf, const char * C4_RESTRICT v) noexcept 2412 { 2413 return to_chars(buf, to_csubstr(v)); 2414 } 2415 2416 } // namespace c4 2417 2418 #ifdef _MSC_VER 2419 # pragma warning(pop) 2420 #endif 2421 2422 #if defined(__clang__) 2423 # pragma clang diagnostic pop 2424 #elif defined(__GNUC__) 2425 # pragma GCC diagnostic pop 2426 #endif 2427 2428 #endif /* _C4_CHARCONV_HPP_ */