fix: truncating conversion issues in fmt fixed

This commit is contained in:
Mateusz Pusz
2021-09-20 15:43:28 +02:00
parent b1bd4cab2d
commit 55456e5d42
2 changed files with 42 additions and 27 deletions

View File

@@ -151,14 +151,14 @@ struct dynamic_format_specs : basic_format_specs<Char> {
template<typename CharT> template<typename CharT>
[[nodiscard]] constexpr int on_dynamic_arg(size_t arg_id, STD_FMT::basic_format_parse_context<CharT>& context) [[nodiscard]] constexpr int on_dynamic_arg(size_t arg_id, STD_FMT::basic_format_parse_context<CharT>& context)
{ {
context.check_arg_id(FMT_ARG_ID(arg_id)); context.check_arg_id(FMT_TO_ARG_ID(arg_id));
return verify_dynamic_arg_index_in_range(arg_id); return verify_dynamic_arg_index_in_range(arg_id);
} }
template<typename CharT> template<typename CharT>
[[nodiscard]] constexpr int on_dynamic_arg(auto_id, STD_FMT::basic_format_parse_context<CharT>& context) [[nodiscard]] constexpr int on_dynamic_arg(auto_id, STD_FMT::basic_format_parse_context<CharT>& context)
{ {
return verify_dynamic_arg_index_in_range(context.next_arg_id()); return verify_dynamic_arg_index_in_range(FMT_FROM_ARG_ID(context.next_arg_id()));
} }
template<class Handler, typename FormatContext> template<class Handler, typename FormatContext>
@@ -173,25 +173,36 @@ template<class Handler, typename FormatContext>
// Parses the range [begin, end) as an unsigned integer. This function assumes // Parses the range [begin, end) as an unsigned integer. This function assumes
// that the range is non-empty and the first character is a digit. // that the range is non-empty and the first character is a digit.
template<std::forward_iterator It, std::sentinel_for<It> S> template<std::forward_iterator It, std::sentinel_for<It> S, typename IDHandler>
[[nodiscard]] constexpr int parse_nonnegative_int(It& begin, S end, int error_value) noexcept [[nodiscard]] constexpr It parse_nonnegative_int(It begin, S end, IDHandler&& handler, size_t& value) noexcept
{ {
gsl_Expects(begin != end && '0' <= *begin && *begin <= '9'); gsl_Expects(begin != end && '0' <= *begin && *begin <= '9');
unsigned value = 0, prev = 0; constexpr auto max_int = static_cast<unsigned>(std::numeric_limits<int>::max());
auto p = begin; constexpr auto big_int = max_int / 10u;
value = 0;
do { do {
prev = value; if (value > big_int) {
value = value * 10 + unsigned(*p - '0'); value = max_int + 1;
++p; break;
} while (p != end && '0' <= *p && *p <= '9'); }
auto num_digits = p - begin; value = value * 10 + static_cast<unsigned int>(*begin - '0');
begin = p; ++begin;
if (num_digits <= std::numeric_limits<int>::digits10) return static_cast<int>(value); } while (begin != end && '0' <= *begin && *begin <= '9');
// Check for overflow.
const unsigned max = static_cast<unsigned>(std::numeric_limits<int>::max()); if (value > max_int) handler.on_error("Number is too big");
return num_digits == std::numeric_limits<int>::digits10 + 1 && prev * 10ull + unsigned(p[-1] - '0') <= max
? static_cast<int>(value) return begin;
: error_value; }
template<std::forward_iterator It, std::sentinel_for<It> S, typename IDHandler>
[[nodiscard]] constexpr It parse_nonnegative_int(It begin, S end, IDHandler&& handler, int& value) noexcept
{
size_t val_unsigned = 0;
begin = parse_nonnegative_int(begin, end, handler, val_unsigned);
// Never invalid because parse_nonnegative_integer throws an error for values that don't fit in signed integers
value = static_cast<int>(val_unsigned);
return begin;
} }
template<std::forward_iterator It, std::sentinel_for<It> S, typename IDHandler> template<std::forward_iterator It, std::sentinel_for<It> S, typename IDHandler>
@@ -200,9 +211,9 @@ template<std::forward_iterator It, std::sentinel_for<It> S, typename IDHandler>
gsl_Expects(begin != end); gsl_Expects(begin != end);
auto c = *begin; auto c = *begin;
if (c >= '0' && c <= '9') { if (c >= '0' && c <= '9') {
int index = 0; size_t index = 0;
if (c != '0') if (c != '0')
index = parse_nonnegative_int(begin, end, std::numeric_limits<int>::max()); begin = parse_nonnegative_int(begin, end, handler, index);
else else
++begin; ++begin;
if (begin == end || (*begin != '}' && *begin != ':')) if (begin == end || (*begin != '}' && *begin != ':'))
@@ -253,7 +264,7 @@ template<std::forward_iterator It, std::sentinel_for<It> S, typename Handler>
struct width_adapter { struct width_adapter {
Handler& handler; Handler& handler;
constexpr void operator()() { handler.on_dynamic_width(auto_id{}); } constexpr void operator()() { handler.on_dynamic_width(auto_id{}); }
constexpr void operator()(int id) { handler.on_dynamic_width(id); } constexpr void operator()(size_t id) { handler.on_dynamic_width(id); }
constexpr void on_error(const char* message) constexpr void on_error(const char* message)
{ {
if (message) handler.on_error(message); if (message) handler.on_error(message);
@@ -262,7 +273,8 @@ template<std::forward_iterator It, std::sentinel_for<It> S, typename Handler>
gsl_Expects(begin != end); gsl_Expects(begin != end);
if ('0' <= *begin && *begin <= '9') { if ('0' <= *begin && *begin <= '9') {
int width = parse_nonnegative_int(begin, end, -1); int width = 0;
begin = parse_nonnegative_int(begin, end, handler, width);
if (width != -1) if (width != -1)
handler.on_width(width); handler.on_width(width);
else else
@@ -282,7 +294,7 @@ template<std::forward_iterator It, std::sentinel_for<It> S, typename Handler>
struct precision_adapter { struct precision_adapter {
Handler& handler; Handler& handler;
constexpr void operator()() { handler.on_dynamic_precision(auto_id{}); } constexpr void operator()() { handler.on_dynamic_precision(auto_id{}); }
constexpr void operator()(int id) { handler.on_dynamic_precision(id); } constexpr void operator()(size_t id) { handler.on_dynamic_precision(id); }
constexpr void on_error(const char* message) constexpr void on_error(const char* message)
{ {
if (message) handler.on_error(message); if (message) handler.on_error(message);
@@ -292,7 +304,8 @@ template<std::forward_iterator It, std::sentinel_for<It> S, typename Handler>
++begin; ++begin;
auto c = begin != end ? *begin : std::iter_value_t<It>(); auto c = begin != end ? *begin : std::iter_value_t<It>();
if ('0' <= c && c <= '9') { if ('0' <= c && c <= '9') {
auto precision = parse_nonnegative_int(begin, end, -1); auto precision = 0;
begin = parse_nonnegative_int(begin, end, handler, precision);
if (precision != -1) if (precision != -1)
handler.on_precision(precision); handler.on_precision(precision);
else else

View File

@@ -40,7 +40,8 @@ UNITS_DIAGNOSTIC_POP
#define STD_FMT fmt #define STD_FMT fmt
#define FMT_RUNTIME(arg) fmt::runtime(arg) #define FMT_RUNTIME(arg) fmt::runtime(arg)
#define FMT_LOCALE(loc) (loc).template get<std::locale>() #define FMT_LOCALE(loc) (loc).template get<std::locale>()
#define FMT_ARG_ID(arg) static_cast<int>(arg) #define FMT_TO_ARG_ID(arg) static_cast<int>(arg)
#define FMT_FROM_ARG_ID(arg) static_cast<size_t>(arg)
#else #else
@@ -53,6 +54,7 @@ UNITS_DIAGNOSTIC_POP
#define STD_FMT std #define STD_FMT std
#define FMT_RUNTIME(arg) arg #define FMT_RUNTIME(arg) arg
#define FMT_LOCALE(loc) loc #define FMT_LOCALE(loc) loc
#define FMT_ARG_ID(arg) arg #define FMT_TO_ARG_ID(arg) arg
#define FMT_FROM_ARG_ID(arg) arg
#endif #endif