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:
parent
262b00d5e5
commit
ffd52a0d8e
3 changed files with 76 additions and 32 deletions
34
kernel/io.cc
34
kernel/io.cc
|
@ -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") {
|
||||||
|
|
|
@ -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,
|
||||||
|
|
72
tests/unit/kernel/ioTest.cc
Normal file
72
tests/unit/kernel/ioTest.cc
Normal 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
|
Loading…
Add table
Add a link
Reference in a new issue