charconv (2422B)
1 // -*- c++ -*- 2 #pragma once 3 4 #include <type_traits> 5 #include <system_error> 6 7 namespace std 8 { 9 10 // TODO: non base-10 11 12 struct from_chars_result 13 { 14 const char* ptr; 15 std::errc ec; 16 }; 17 18 inline constexpr bool _IsSpace(char c) noexcept 19 { return (c >= 9 && c <= 13) || c == ' '; } 20 21 template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>> 22 std::from_chars_result from_chars(const char* begin, const char* end, 23 T& value) 24 { 25 bool minus = false; 26 while (begin < end && _IsSpace(*begin)) ++begin; 27 if (begin >= end) return { end, errc::invalid_argument }; 28 if constexpr (std::is_signed_v<T>) 29 if (*begin == '-') 30 { 31 minus = true; 32 ++begin; 33 } 34 35 bool was = false, err = false;; 36 T out{}; 37 for (; begin < end && (*begin >= '0' && *begin <= '9'); ++begin) 38 { 39 was = true; 40 T tmp; 41 if (__builtin_mul_overflow(out, 10, &tmp) || 42 __builtin_add_overflow(tmp, (*begin - '0'), &out)) 43 err = true; 44 } 45 if (!was) return { begin, errc::invalid_argument }; 46 if (minus && __builtin_sub_overflow(0, out, &out)) err = true; 47 48 if (!err) value = out; 49 return { begin, err ? errc::result_out_of_range : errc() }; 50 } 51 52 53 struct to_chars_result 54 { 55 char* ptr; 56 std::errc ec; 57 }; 58 59 to_chars_result to_chars(char*, char*, bool) = delete; 60 template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>> 61 to_chars_result to_chars(char* begin, char* end, T value) 62 { 63 if (end - begin <= 1) return { end, errc::value_too_large }; 64 std::make_unsigned_t<T> uval; 65 if (value < 0) { *begin++ = '-'; uval = -value; } 66 else uval = value; 67 68 // On error, returns a value of type to_chars_result holding 69 // std::errc::value_too_large in ec, a copy of the value last in ptr, and 70 // leaves the contents of the range [first, last) in unspecified state. 71 // => so we can construct it in a temporary buffer and only copy back if 72 // everything is alright 73 char buf[std::numeric_limits<T>::digits10+1]; 74 auto p = buf + std::numeric_limits<T>::digits10; 75 *p = '0'; 76 if (uval) { ++p; for (; uval; uval /= 10) *--p = '0' + uval % 10; } 77 78 const auto len = buf + std::numeric_limits<T>::digits10+1 - p; 79 if (len > end-begin) return { end, errc::value_too_large }; 80 ::memcpy(begin, p, len); 81 return { begin + len, errc() }; 82 } 83 84 }