gnss-sim/3rdparty/boost/charconv/detail/fallback_routines.hpp

246 lines
7.2 KiB
C++

// Copyright 2024 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_FALLBACK_ROUTINES_HPP
#define BOOST_FALLBACK_ROUTINES_HPP
#include <boost/charconv/detail/to_chars_integer_impl.hpp>
#include <boost/charconv/detail/dragonbox/floff.hpp>
#include <boost/charconv/detail/config.hpp>
#include <boost/charconv/detail/from_chars_result.hpp>
#include <boost/charconv/chars_format.hpp>
#include <system_error>
#include <type_traits>
#include <locale>
#include <clocale>
#include <cstring>
#include <cstdio>
namespace boost {
namespace charconv {
namespace detail {
template <typename T>
inline int print_val(char* first, std::size_t size, char* format, T value) noexcept
{
return std::snprintf(first, size, format, value);
}
template <typename T>
to_chars_result to_chars_printf_impl(char* first, char* last, T value, chars_format fmt, int precision)
{
// v % + . + num_digits(INT_MAX) + specifier + null terminator
// 1 + 1 + 10 + 1 + 1
char format[14] {};
std::memcpy(format, "%", 1); // NOLINT : No null terminator is purposeful
std::size_t pos = 1;
// precision of -1 is unspecified
if (precision != -1 && fmt != chars_format::fixed)
{
format[pos] = '.';
++pos;
const auto unsigned_precision = static_cast<std::uint32_t>(precision);
if (unsigned_precision < 10)
{
boost::charconv::detail::print_1_digit(unsigned_precision, format + pos);
++pos;
}
else if (unsigned_precision < 100)
{
boost::charconv::detail::print_2_digits(unsigned_precision, format + pos);
pos += 2;
}
else
{
boost::charconv::detail::to_chars_int(format + pos, format + sizeof(format), precision);
pos = std::strlen(format);
}
}
else if (fmt == chars_format::fixed)
{
// Force 0 decimal places
std::memcpy(format + pos, ".0", 2); // NOLINT : No null terminator is purposeful
pos += 2;
}
// Add the type identifier
BOOST_CHARCONV_IF_CONSTEXPR (std::is_same<T, long double>::value)
{
format[pos] = 'L';
++pos;
}
// Add the format character
switch (fmt)
{
case boost::charconv::chars_format::general:
format[pos] = 'g';
break;
case boost::charconv::chars_format::scientific:
format[pos] = 'e';
break;
case boost::charconv::chars_format::fixed:
format[pos] = 'f';
break;
case boost::charconv::chars_format::hex:
format[pos] = 'a';
break;
}
const auto rv = print_val(first, static_cast<std::size_t>(last - first), format, value);
if (rv <= 0)
{
return {last, static_cast<std::errc>(errno)};
}
return {first + rv, std::errc()};
}
#ifdef BOOST_MSVC
# pragma warning(push)
# pragma warning(disable: 4244) // Implict converion when BOOST_IF_CONSTEXPR expands to if
#elif defined(__GNUC__) && __GNUC__ >= 5
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wmissing-field-initializers"
# pragma GCC diagnostic ignored "-Wfloat-conversion"
#elif defined(__clang__) && __clang_major__ > 7
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wimplicit-float-conversion"
#elif defined(__clang__) && __clang_major__ <= 7
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wconversion"
#endif
// We know that the string is in the "C" locale because it would have previously passed through our parser.
// Convert the string into the current locale so that the strto* family of functions
// works correctly for the given locale.
//
// We are operating on our own copy of the buffer, so we are free to modify it.
inline void convert_string_locale(char* buffer) noexcept
{
const auto locale_decimal_point = *std::localeconv()->decimal_point;
if (locale_decimal_point != '.')
{
auto p = std::strchr(buffer, '.');
if (p != nullptr)
{
*p = locale_decimal_point;
}
}
}
template <typename T>
from_chars_result from_chars_strtod_impl(const char* first, const char* last, T& value, char* buffer) noexcept
{
// For strto(f/d)
// Floating point value corresponding to the contents of str on success.
// If the converted value falls out of range of corresponding return type, range error occurs and HUGE_VAL, HUGE_VALF or HUGE_VALL is returned.
// If no conversion can be performed, 0 is returned and *str_end is set to str.
std::memcpy(buffer, first, static_cast<std::size_t>(last - first));
buffer[last - first] = '\0';
convert_string_locale(buffer);
char* str_end;
T return_value {};
from_chars_result r {nullptr, std::errc()};
BOOST_IF_CONSTEXPR (std::is_same<T, float>::value)
{
return_value = std::strtof(buffer, &str_end);
#ifndef __INTEL_LLVM_COMPILER
if (return_value == HUGE_VALF)
#else
if (return_value >= std::numeric_limits<T>::max())
#endif
{
r = {last, std::errc::result_out_of_range};
}
}
else BOOST_IF_CONSTEXPR (std::is_same<T, double>::value)
{
return_value = std::strtod(buffer, &str_end);
#ifndef __INTEL_LLVM_COMPILER
if (return_value == HUGE_VAL)
#else
if (return_value >= std::numeric_limits<T>::max())
#endif
{
r = {last, std::errc::result_out_of_range};
}
}
else BOOST_IF_CONSTEXPR (std::is_same<T, long double>::value)
{
return_value = std::strtold(buffer, &str_end);
#ifndef __INTEL_LLVM_COMPILER
if (return_value == HUGE_VALL)
#else
if (return_value >= std::numeric_limits<T>::max())
#endif
{
r = {last, std::errc::result_out_of_range};
}
}
// Since this is a fallback routine we are safe to check for 0
if (return_value == 0 && str_end == last)
{
r = {first, std::errc::result_out_of_range};
}
if (r)
{
value = return_value;
r = {first + (str_end - buffer), std::errc()};
}
return r;
}
template <typename T>
inline from_chars_result from_chars_strtod(const char* first, const char* last, T& value) noexcept
{
if (last - first < 1024)
{
char buffer[1024];
return from_chars_strtod_impl(first, last, value, buffer);
}
// If the string to be parsed does not fit into the 1024 byte static buffer than we have to allocate a buffer.
// malloc is used here because it does not throw on allocation failure.
char* buffer = static_cast<char*>(std::malloc(static_cast<std::size_t>(last - first + 1)));
if (buffer == nullptr)
{
return {first, std::errc::not_enough_memory};
}
auto r = from_chars_strtod_impl(first, last, value, buffer);
std::free(buffer);
return r;
}
#ifdef BOOST_MSVC
# pragma warning(pop)
#elif defined(__GNUC__) && __GNUC__ >= 5
# pragma GCC diagnostic pop
#elif defined(__clang__)
# pragma clang diagnostic pop
#endif
} //namespace detail
} //namespace charconv
} //namespace boost
#endif //BOOST_FALLBACK_ROUTINES_HPP