Skip to content

Commit

Permalink
Merge pull request #46 from mborland/from_chars_failures
Browse files Browse the repository at this point in the history
Avoid fallback routine buffer overruns
  • Loading branch information
mborland authored May 22, 2023
2 parents 7d8d344 + 8c69553 commit 212cdf9
Show file tree
Hide file tree
Showing 2 changed files with 664 additions and 32 deletions.
46 changes: 40 additions & 6 deletions include/boost/charconv/from_chars.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <boost/charconv/config.hpp>
#include <boost/charconv/chars_format.hpp>
#include <system_error>
#include <cstdlib>
#include <cmath>

namespace boost { namespace charconv {
Expand Down Expand Up @@ -94,35 +95,37 @@ BOOST_CHARCONV_GCC5_CONSTEXPR from_chars_result from_chars(const char* first, co
namespace detail {

template <typename T>
from_chars_result from_chars_strtod(const char* first, const char* last, T& value) noexcept
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.

value = 0;
std::memcpy(buffer, first, static_cast<std::size_t>(last - first));
buffer[last - first] = '\0';

char* str_end;
T return_value {};
BOOST_IF_CONSTEXPR (std::is_same<T, float>::value)
{
return_value = std::strtof(first, &str_end);
return_value = std::strtof(buffer, &str_end);
if (return_value == HUGE_VALF)
{
return {last, std::errc::result_out_of_range};
}
}
else BOOST_IF_CONSTEXPR (std::is_same<T, double>::value)
{
return_value = std::strtod(first, &str_end);
return_value = std::strtod(buffer, &str_end);
if (return_value == HUGE_VAL)
{
return {last, std::errc::result_out_of_range};
}
}
else
{
return_value = std::strtold(first, &str_end);
return_value = std::strtold(buffer, &str_end);
if (return_value == HUGE_VALL)
{
return {last, std::errc::result_out_of_range};
Expand All @@ -136,7 +139,31 @@ from_chars_result from_chars_strtod(const char* first, const char* last, T& valu
}

value = return_value;
return {str_end, std::errc()};
return {first + (str_end - buffer), std::errc()};
}

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(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;
}

template <typename T>
Expand All @@ -156,6 +183,13 @@ from_chars_result from_chars_float_impl(const char* first, const char* last, T&
value = sign ? static_cast<T>(-0.0L) : static_cast<T>(0.0L);
return r;
}
else if (exponent == -1)
{
// A full length significand e.g. -1985444280612224 with a power of -1 sometimes
// fails in compute_float64 but is trivial to calculate
// Found investigating GitHub issue #47
value = (sign ? -static_cast<T>(significand) : static_cast<T>(significand)) / 10;
}

bool success {};
T return_val {};
Expand Down
Loading

0 comments on commit 212cdf9

Please sign in to comment.