You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
85 lines
2.4 KiB
C++
85 lines
2.4 KiB
C++
// -*- c++ -*-
|
|
#pragma once
|
|
|
|
#include <type_traits>
|
|
#include <system_error>
|
|
|
|
namespace std
|
|
{
|
|
|
|
// TODO: non base-10
|
|
|
|
struct from_chars_result
|
|
{
|
|
const char* ptr;
|
|
std::errc ec;
|
|
};
|
|
|
|
inline constexpr bool _IsSpace(char c) noexcept
|
|
{ return (c >= 9 && c <= 13) || c == ' '; }
|
|
|
|
template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
|
|
std::from_chars_result from_chars(const char* begin, const char* end,
|
|
T& value)
|
|
{
|
|
bool minus = false;
|
|
while (begin < end && _IsSpace(*begin)) ++begin;
|
|
if (begin >= end) return { end, errc::invalid_argument };
|
|
if constexpr (std::is_signed_v<T>)
|
|
if (*begin == '-')
|
|
{
|
|
minus = true;
|
|
++begin;
|
|
}
|
|
|
|
bool was = false, err = false;;
|
|
T out{};
|
|
for (; begin < end && (*begin >= '0' && *begin <= '9'); ++begin)
|
|
{
|
|
was = true;
|
|
T tmp;
|
|
if (__builtin_mul_overflow(out, 10, &tmp) ||
|
|
__builtin_add_overflow(tmp, (*begin - '0'), &out))
|
|
err = true;
|
|
}
|
|
if (!was) return { begin, errc::invalid_argument };
|
|
if (minus && __builtin_sub_overflow(0, out, &out)) err = true;
|
|
|
|
if (!err) value = out;
|
|
return { begin, err ? errc::result_out_of_range : errc() };
|
|
}
|
|
|
|
|
|
struct to_chars_result
|
|
{
|
|
char* ptr;
|
|
std::errc ec;
|
|
};
|
|
|
|
to_chars_result to_chars(char*, char*, bool) = delete;
|
|
template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
|
|
to_chars_result to_chars(char* begin, char* end, T value)
|
|
{
|
|
if (end - begin <= 1) return { end, errc::value_too_large };
|
|
std::make_unsigned_t<T> uval;
|
|
if (value < 0) { *begin++ = '-'; uval = -value; }
|
|
else uval = value;
|
|
|
|
// On error, returns a value of type to_chars_result holding
|
|
// std::errc::value_too_large in ec, a copy of the value last in ptr, and
|
|
// leaves the contents of the range [first, last) in unspecified state.
|
|
// => so we can construct it in a temporary buffer and only copy back if
|
|
// everything is alright
|
|
char buf[std::numeric_limits<T>::digits10+1];
|
|
auto p = buf + std::numeric_limits<T>::digits10;
|
|
*p = '0';
|
|
if (uval) { ++p; for (; uval; uval /= 10) *--p = '0' + uval % 10; }
|
|
|
|
const auto len = buf + std::numeric_limits<T>::digits10+1 - p;
|
|
if (len > end-begin) return { end, errc::value_too_large };
|
|
::memcpy(begin, p, len);
|
|
return { begin + len, errc() };
|
|
}
|
|
|
|
}
|