3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2025-08-03 09:50:24 +00:00

Making stringf() use the format conversion specs as-is without widening them.

And make sure our fast-path for `%d` and `%u` narrows to `int` correctly.

Resolves #5260
This commit is contained in:
Robert O'Callahan 2025-07-31 10:19:44 +00:00
parent 262b00d5e5
commit ffd52a0d8e
3 changed files with 76 additions and 32 deletions

View file

@ -405,37 +405,9 @@ std::string unescape_format_string(std::string_view fmt)
static std::string string_view_stringf(std::string_view spec, ...) static std::string string_view_stringf(std::string_view spec, ...)
{ {
std::string fmt(spec);
char format_specifier = fmt[fmt.size() - 1];
switch (format_specifier) {
case 'd':
case 'i':
case 'o':
case 'u':
case 'x':
case 'X': {
// Strip any length modifier off `fmt`
std::string long_fmt;
for (size_t i = 0; i + 1 < fmt.size(); ++i) {
char ch = fmt[i];
if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
break;
}
long_fmt.push_back(ch);
}
// Add `lld` or whatever
long_fmt += "ll";
long_fmt.push_back(format_specifier);
fmt = long_fmt;
break;
}
default:
break;
}
va_list ap; va_list ap;
va_start(ap, spec); va_start(ap, spec);
std::string result = vstringf(fmt.c_str(), ap); std::string result = vstringf(std::string(spec).c_str(), ap);
va_end(ap); va_end(ap);
return result; return result;
} }
@ -464,7 +436,7 @@ void format_emit_long_long(std::string &result, std::string_view spec, int *dyna
{ {
if (spec == "%d") { if (spec == "%d") {
// Format checking will have guaranteed num_dynamic_ints == 0. // Format checking will have guaranteed num_dynamic_ints == 0.
result += std::to_string(arg); result += std::to_string(static_cast<int>(arg));
return; return;
} }
format_emit_stringf(result, spec, dynamic_ints, num_dynamic_ints, arg); format_emit_stringf(result, spec, dynamic_ints, num_dynamic_ints, arg);
@ -475,7 +447,7 @@ void format_emit_unsigned_long_long(std::string &result, std::string_view spec,
{ {
if (spec == "%u") { if (spec == "%u") {
// Format checking will have guaranteed num_dynamic_ints == 0. // Format checking will have guaranteed num_dynamic_ints == 0.
result += std::to_string(arg); result += std::to_string(static_cast<unsigned int>(arg));
return; return;
} }
if (spec == "%c") { if (spec == "%c") {

View file

@ -62,7 +62,7 @@ enum ConversionSpecifier : uint8_t
CONVSPEC_UNSIGNED_INT, CONVSPEC_UNSIGNED_INT,
// Consumes a "double" // Consumes a "double"
CONVSPEC_DOUBLE, CONVSPEC_DOUBLE,
// Consumes a "const char*" // Consumes a "const char*" or other string type
CONVSPEC_CHAR_PTR, CONVSPEC_CHAR_PTR,
// Consumes a "void*" // Consumes a "void*"
CONVSPEC_VOID_PTR, CONVSPEC_VOID_PTR,

View file

@ -0,0 +1,72 @@
#include <gtest/gtest.h>
#include "kernel/io.h"
YOSYS_NAMESPACE_BEGIN
TEST(KernelStringfTest, integerTruncation)
{
EXPECT_EQ(stringf("%d", 1LL << 32), "0");
EXPECT_EQ(stringf("%u", 1LL << 32), "0");
EXPECT_EQ(stringf("%x", 0xff12345678LL), "12345678");
EXPECT_EQ(stringf("%hu", 0xff12345678LL), "22136");
}
TEST(KernelStringfTest, charFormat)
{
EXPECT_EQ(stringf("%c", 256), std::string_view("\0", 1));
EXPECT_EQ(stringf("%c", -1), "\377");
}
TEST(KernelStringfTest, floatFormat)
{
EXPECT_EQ(stringf("%g", 1.0), "1");
}
TEST(KernelStringfTest, intToFloat)
{
EXPECT_EQ(stringf("%g", 1), "1");
}
TEST(KernelStringfTest, floatToInt)
{
EXPECT_EQ(stringf("%d", 1.0), "1");
EXPECT_EQ(stringf("%d", -1.6), "-1");
}
TEST(KernelStringfTest, stringParam)
{
EXPECT_EQ(stringf("%s", std::string("hello")), "hello");
}
TEST(KernelStringfTest, stringViewParam)
{
EXPECT_EQ(stringf("%s", std::string_view("hello")), "hello");
}
TEST(KernelStringfTest, escapePercent)
{
EXPECT_EQ(stringf("%%"), "%");
}
TEST(KernelStringfTest, trailingPercent)
{
EXPECT_EQ(stringf("%"), "%");
}
TEST(KernelStringfTest, dynamicWidth)
{
EXPECT_EQ(stringf("%*s", 8, "hello"), " hello");
}
TEST(KernelStringfTest, dynamicPrecision)
{
EXPECT_EQ(stringf("%.*f", 4, 1.0), "1.0000");
}
TEST(KernelStringfTest, dynamicWidthAndPrecision)
{
EXPECT_EQ(stringf("%*.*f", 8, 4, 1.0), " 1.0000");
}
YOSYS_NAMESPACE_END