mirror of
https://github.com/fmtlib/fmt.git
synced 2026-07-30 16:26:27 +08:00
Fix out-of-range integers with the 'c' presentation type (#4839)
Formatting an integer with ':c' used the magnitude (abs_value) and never range-checked, so negatives were mangled and out-of-range values silently truncated. Copy the value as a character and report an error when it is out of range, treating all character types as unsigned for portability.
This commit is contained in:
parent
d45179c1e7
commit
7852fc384c
@ -1961,6 +1961,15 @@ FMT_CONSTEXPR inline void prefix_append(unsigned& prefix, unsigned value) {
|
|||||||
prefix += (1u + (value > 0xff ? 1 : 0)) << 24;
|
prefix += (1u + (value > 0xff ? 1 : 0)) << 24;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Writes an integer as a character, treating chars as unsigned.
|
||||||
|
template <typename Char, typename OutputIt, typename UInt>
|
||||||
|
FMT_CONSTEXPR auto write_int_chr(OutputIt out, UInt abs_value, bool negative,
|
||||||
|
const format_specs& specs) -> OutputIt {
|
||||||
|
if (negative || abs_value > max_value<make_unsigned_t<Char>>())
|
||||||
|
report_error("character value out of range");
|
||||||
|
return write_char<Char>(out, static_cast<Char>(abs_value), specs);
|
||||||
|
}
|
||||||
|
|
||||||
// Writes a decimal integer with digit grouping.
|
// Writes a decimal integer with digit grouping.
|
||||||
template <typename OutputIt, typename UInt, typename Char>
|
template <typename OutputIt, typename UInt, typename Char>
|
||||||
auto write_int(OutputIt out, UInt value, unsigned prefix,
|
auto write_int(OutputIt out, UInt value, unsigned prefix,
|
||||||
@ -1997,7 +2006,7 @@ auto write_int(OutputIt out, UInt value, unsigned prefix,
|
|||||||
format_base2e<char>(1, appender(buffer), value, num_digits);
|
format_base2e<char>(1, appender(buffer), value, num_digits);
|
||||||
break;
|
break;
|
||||||
case presentation_type::chr:
|
case presentation_type::chr:
|
||||||
return write_char<Char>(out, static_cast<Char>(value), specs);
|
return write_int_chr<Char>(out, value, (prefix & 0xff) == '-', specs);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned size = (prefix != 0 ? prefix >> 24 : 0) + to_unsigned(num_digits) +
|
unsigned size = (prefix != 0 ? prefix >> 24 : 0) + to_unsigned(num_digits) +
|
||||||
@ -2124,7 +2133,7 @@ FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg<T> arg,
|
|||||||
prefix_append(prefix, unsigned(specs.upper() ? 'B' : 'b') << 8 | '0');
|
prefix_append(prefix, unsigned(specs.upper() ? 'B' : 'b') << 8 | '0');
|
||||||
break;
|
break;
|
||||||
case presentation_type::chr:
|
case presentation_type::chr:
|
||||||
return write_char<Char>(out, static_cast<Char>(abs_value), specs);
|
return write_int_chr<Char>(out, abs_value, (prefix & 0xff) == '-', specs);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write an integer in the format
|
// Write an integer in the format
|
||||||
|
|||||||
@ -1334,6 +1334,16 @@ TEST(format_test, format_int) {
|
|||||||
"invalid format specifier");
|
"invalid format specifier");
|
||||||
check_unknown_types(42, "bBdoxXnLc", "integer");
|
check_unknown_types(42, "bBdoxXnLc", "integer");
|
||||||
EXPECT_EQ(fmt::format("{:c}", static_cast<int>('x')), "x");
|
EXPECT_EQ(fmt::format("{:c}", static_cast<int>('x')), "x");
|
||||||
|
// The 'c' type treats all character types as unsigned for portability, so the
|
||||||
|
// representable range for char is [0, 255] and out-of-range values are
|
||||||
|
// reported as an error.
|
||||||
|
EXPECT_EQ(fmt::format("{:c}", 200), std::string(1, static_cast<char>(200)));
|
||||||
|
EXPECT_EQ(fmt::format("{:c}", 255), std::string(1, static_cast<char>(255)));
|
||||||
|
const char* msg = "character value out of range";
|
||||||
|
EXPECT_THROW_MSG((void)fmt::format("{:c}", -1), format_error, msg);
|
||||||
|
EXPECT_THROW_MSG((void)fmt::format("{:c}", -104), format_error, msg);
|
||||||
|
EXPECT_THROW_MSG((void)fmt::format("{:c}", 256), format_error, msg);
|
||||||
|
EXPECT_THROW_MSG((void)fmt::format("{:c}", 400u), format_error, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(format_test, format_bin) {
|
TEST(format_test, format_bin) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user