From 46ae7ea83dc061225f5959feee042d443ef042e1 Mon Sep 17 00:00:00 2001 From: Alecto Irene Perez Date: Fri, 3 Mar 2023 12:53:25 -0500 Subject: [PATCH] Fix code causing spurious Wstringop-overflow warning See #2989, #3054, and others --- include/fmt/core.h | 16 ++++++++++++++-- test/core-test.cc | 15 +++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/include/fmt/core.h b/include/fmt/core.h index 6c9d968b..22d0aab1 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -2204,20 +2204,32 @@ constexpr auto to_ascii(Char c) -> char { return c <= 0xff ? static_cast(c) : '\0'; } +// Returns the length of a codepoint. Returns 0 for invalid codepoints. FMT_CONSTEXPR inline auto code_point_length_impl(char c) -> int { return "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0\0\0\2\2\2\2\3\3\4" [static_cast(c) >> 3]; } +// Returns the length of a codepoint. Returns 1 for invalid codepoints. +// This is equivalent to +// +// int len = code_point_length_impl(c); +// return len + !len; +// +// This is useful because it allows the compiler to check that the +// length is within the range [1, 4] +FMT_CONSTEXPR inline auto code_point_length_impl_2(char c) -> int { + return ((0x3a55000000000000ull >> (2 * (static_cast(c) >> 3))) & 0x3) + 1; +} template FMT_CONSTEXPR auto code_point_length(const Char* begin) -> int { if (const_check(sizeof(Char) != 1)) return 1; - int len = code_point_length_impl(static_cast(*begin)); + int len = code_point_length_impl_2(static_cast(*begin)); // Compute the pointer to the next character early so that the next // iteration can start working on the next character. Neither Clang // nor GCC figure out this reordering on their own. - return len + !len; + return len; } // Return the result via the out param to workaround gcc bug 77539. diff --git a/test/core-test.cc b/test/core-test.cc index e57c92b3..8a372993 100644 --- a/test/core-test.cc +++ b/test/core-test.cc @@ -898,3 +898,18 @@ TEST(core_test, has_const_formatter) { TEST(core_test, format_nonconst) { EXPECT_EQ(fmt::format("{}", nonconst_formattable()), "test"); } + +TEST(core_test, code_point_length_impl) { + // code_point_length_impl_2 is a bit-shifted version of code_point_length_impl + // that returns 1 for invalid codepoints, so that length is always in [1..4] + int min = CHAR_MIN; + int max = CHAR_MAX; + + for(int ch = min; ch <= max; ch++) { + char c = static_cast(ch); + int len1 = fmt::detail::code_point_length_impl(c); + int len2 = fmt::detail::code_point_length_impl_2(c); + + ASSERT_EQ(len1 + !len1, len2); + } +}