libshit

Just some random shit
git clone https://git.neptards.moe/neptards/libshit.git
Log | Files | Refs | Submodules | README | LICENSE

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 }