196 lines
4.9 KiB
C++
196 lines
4.9 KiB
C++
// cc.h
|
|
//
|
|
// Copyright (C) 2021-present, Celestia Development Team.
|
|
//
|
|
// from_chars() functions family as in C++17.
|
|
//
|
|
// This program is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU General Public License
|
|
// as published by the Free Software Foundation; either version 2
|
|
// of the License, or (at your option) any later version.
|
|
|
|
#pragma once
|
|
|
|
#ifdef HAVE_CHARCONV
|
|
#include <charconv>
|
|
#endif
|
|
#include <limits>
|
|
#include <system_error>
|
|
#include <type_traits>
|
|
|
|
namespace celestia::compat
|
|
{
|
|
#ifdef HAVE_CHARCONV
|
|
using std::from_chars_result;
|
|
#else
|
|
struct from_chars_result
|
|
{
|
|
const char* ptr;
|
|
std::errc ec;
|
|
};
|
|
#endif
|
|
|
|
template <typename T, typename std::enable_if<std::numeric_limits<T>::is_integer, T>::type = 0>
|
|
from_chars_result
|
|
from_chars(const char* first, const char* last, T& value, int base = 10);
|
|
|
|
enum class chars_format : unsigned int
|
|
{
|
|
scientific = 1,
|
|
fixed = 2,
|
|
hex = 4,
|
|
general = fixed | scientific
|
|
};
|
|
|
|
constexpr chars_format
|
|
operator&(chars_format lhs, chars_format rhs) noexcept
|
|
{
|
|
return static_cast<chars_format>(
|
|
static_cast<unsigned int>(lhs) & static_cast<unsigned int>(rhs));
|
|
}
|
|
|
|
constexpr chars_format
|
|
operator|(chars_format lhs, chars_format rhs) noexcept
|
|
{
|
|
return static_cast<chars_format>(
|
|
static_cast<unsigned int>(lhs) | static_cast<unsigned int>(rhs));
|
|
}
|
|
|
|
constexpr chars_format
|
|
operator^(chars_format lhs, chars_format rhs) noexcept
|
|
{
|
|
return static_cast<chars_format>(
|
|
static_cast<unsigned int>(lhs) ^ static_cast<unsigned int>(rhs));
|
|
}
|
|
|
|
constexpr chars_format
|
|
operator~(chars_format rhs) noexcept
|
|
{
|
|
return static_cast<chars_format>((~static_cast<unsigned int>(rhs)) & 7);
|
|
}
|
|
|
|
constexpr chars_format&
|
|
operator&=(chars_format& lhs, chars_format rhs) noexcept
|
|
{
|
|
return lhs = lhs & rhs;
|
|
}
|
|
|
|
constexpr chars_format&
|
|
operator|=(chars_format& lhs, chars_format rhs) noexcept
|
|
{
|
|
return lhs = lhs | rhs;
|
|
}
|
|
|
|
constexpr chars_format&
|
|
operator^=(chars_format& lhs, chars_format rhs) noexcept
|
|
{
|
|
return lhs = lhs ^ rhs;
|
|
}
|
|
|
|
from_chars_result
|
|
from_chars(const char* first, const char* last, float &value,
|
|
chars_format fmt = chars_format::general);
|
|
|
|
from_chars_result
|
|
from_chars(const char* first, const char* last, double &value,
|
|
chars_format fmt = chars_format::general);
|
|
|
|
from_chars_result
|
|
from_chars(const char* first, const char* last, long double &value,
|
|
chars_format fmt = chars_format::general);
|
|
|
|
|
|
#ifdef HAVE_CHARCONV
|
|
template <typename T, typename std::enable_if<std::numeric_limits<T>::is_integer, T>::type>
|
|
from_chars_result
|
|
from_chars(const char* first, const char* last, T& value, int base)
|
|
{
|
|
return std::from_chars(first, last, value, base);
|
|
}
|
|
#else
|
|
namespace detail
|
|
{
|
|
template <typename T>
|
|
from_chars_result
|
|
int_from_chars(const char* first, const char* last, T& value, int base) noexcept
|
|
{
|
|
auto p = first;
|
|
bool has_minus = false;
|
|
if (std::numeric_limits<T>::is_signed && *p == '-')
|
|
{
|
|
has_minus = true;
|
|
p++;
|
|
}
|
|
|
|
using U = typename std::make_unsigned<T>::type;
|
|
|
|
U risky_value;
|
|
U max_digit;
|
|
if (has_minus)
|
|
{
|
|
risky_value = (static_cast<U>(std::numeric_limits<T>::max()) + 1) / base;
|
|
max_digit = (static_cast<U>(std::numeric_limits<T>::max()) + 1) % base;
|
|
}
|
|
else
|
|
{
|
|
risky_value = static_cast<U>(std::numeric_limits<T>::max() / base);
|
|
max_digit = static_cast<U>(std::numeric_limits<T>::max() % base);
|
|
}
|
|
|
|
char last_n = '0' + base;
|
|
char last_ll = 'a' + base - 10;
|
|
char last_ul = 'A' + base - 10;
|
|
|
|
bool overflow = false;
|
|
U result = 0;
|
|
for (; p < last; p++)
|
|
{
|
|
U digit;
|
|
if (base <= 10)
|
|
{
|
|
if (*p < '0' || *p >= last_n)
|
|
break;
|
|
digit = static_cast<U>(*p - '0');
|
|
}
|
|
else
|
|
{
|
|
if (*p >= '0' && *p <= '9')
|
|
digit = static_cast<U>(*p - '0');
|
|
else if (*p >= 'a' && *p < last_ll)
|
|
digit = static_cast<U>(*p - 'a' + 10);
|
|
else if (*p >= 'A' && *p < last_ul)
|
|
digit = static_cast<U>(*p - 'A' + 10);
|
|
else
|
|
break;
|
|
}
|
|
|
|
if (result > risky_value || (result == risky_value && digit > max_digit))
|
|
overflow = true;
|
|
|
|
result = result * base + digit;
|
|
}
|
|
if (p == first || (has_minus && p == first + 1))
|
|
return { first, std::errc::invalid_argument };
|
|
|
|
if (overflow)
|
|
return { p, std::errc::result_out_of_range };
|
|
|
|
if (has_minus)
|
|
value = -1 - static_cast<T>(result - 1);
|
|
else
|
|
value = static_cast<T>(result);
|
|
return { p, std::errc() };
|
|
}
|
|
|
|
} // end namespace detail
|
|
|
|
template <typename T, typename std::enable_if<std::numeric_limits<T>::is_integer, T>::type>
|
|
from_chars_result
|
|
from_chars(const char* first, const char* last, T& value, int base)
|
|
{
|
|
return detail::int_from_chars(first, last, value, base);
|
|
}
|
|
#endif
|
|
|
|
} // end namespace celestia::compat
|