diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 566099554..de91907a7 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -107,6 +107,7 @@ set(libdevilutionx_SRCS utils/console.cpp utils/display.cpp utils/file_util.cpp + utils/format_int.cpp utils/language.cpp utils/logged_fstream.cpp utils/paths.cpp diff --git a/Source/qol/xpbar.cpp b/Source/qol/xpbar.cpp index f2cece0c9..9ef1fe3f6 100644 --- a/Source/qol/xpbar.cpp +++ b/Source/qol/xpbar.cpp @@ -7,6 +7,8 @@ #include +#include + #include "DiabloUI/art_draw.h" #include "control.h" #include "engine/point.hpp" diff --git a/Source/utils/format_int.cpp b/Source/utils/format_int.cpp new file mode 100644 index 000000000..68e98d8ad --- /dev/null +++ b/Source/utils/format_int.cpp @@ -0,0 +1,48 @@ +#include "utils/format_int.hpp" + +#include +#include + +#include "utils/language.h" +#include "utils/stdcompat/string_view.hpp" + +namespace devilution { + +std::string FormatInteger(int n) +{ + constexpr size_t GroupSize = 3; + + char buf[40]; + char *begin = buf; + const char *end = fmt::format_to(buf, FMT_COMPILE("{}"), n); + const size_t len = end - begin; + + std::string out; + const size_t prefixLen = n < 0 ? 1 : 0; + const size_t numLen = len - prefixLen; + if (numLen <= GroupSize) { + out.append(begin, len); + return out; + } + + const string_view separator = _(/* TRANSLATORS: Thousands separator */ ","); + out.reserve(len + separator.size() * (numLen - 1) / GroupSize); + if (n < 0) { + out += '-'; + ++begin; + } + + size_t mlen = numLen % GroupSize; + if (mlen == 0) + mlen = GroupSize; + out.append(begin, mlen); + begin += mlen; + for (; begin != end; begin += GroupSize) { + AppendStrView(out, separator); + out.append(begin, GroupSize); + } + + return out; +} + +} // namespace devilution diff --git a/Source/utils/format_int.hpp b/Source/utils/format_int.hpp index eae4bae91..733eb5512 100644 --- a/Source/utils/format_int.hpp +++ b/Source/utils/format_int.hpp @@ -1,33 +1,12 @@ -#pragma once - -#include - -#include - -#include "utils/language.h" -#include "utils/stdcompat/string_view.hpp" - -namespace devilution { - -/** - * @brief Formats integer with thousands separator. - */ -inline std::string FormatInteger(int n) -{ - std::string number = fmt::format("{:d}", n); - std::string out = ""; - - int length = number.length(); - int mlength = length % 3; - if (mlength == 0) - mlength = 3; - out.append(number.substr(0, mlength)); - for (int i = mlength; i < length; i += 3) { - AppendStrView(out, _(/* TRANSLATORS: Thousands separator */ ",")); - out.append(number.substr(i, 3)); - } - - return out; -} - -} // namespace devilution +#pragma once + +#include + +namespace devilution { + +/** + * @brief Formats integer with thousands separator. + */ +std::string FormatInteger(int n); + +} // namespace devilution diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 38189e8f4..ca1120dd4 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -25,6 +25,7 @@ set(tests drlg_l4_test effects_test file_util_test + format_int_test inv_test lighting_test math_test diff --git a/test/format_int_test.cpp b/test/format_int_test.cpp new file mode 100644 index 000000000..4514a10a3 --- /dev/null +++ b/test/format_int_test.cpp @@ -0,0 +1,40 @@ + +#include + +#include "utils/format_int.hpp" + +namespace devilution { +namespace { + +TEST(FormatIntegerTest, OneDigit) +{ + EXPECT_EQ(FormatInteger(1), "1"); + EXPECT_EQ(FormatInteger(-1), "-1"); +} + +TEST(FormatIntegerTest, TwoDigits) +{ + EXPECT_EQ(FormatInteger(12), "12"); + EXPECT_EQ(FormatInteger(-12), "-12"); +} + +TEST(FormatIntegerTest, ThreeDigits) +{ + EXPECT_EQ(FormatInteger(123), "123"); + EXPECT_EQ(FormatInteger(-123), "-123"); +} + +TEST(FormatIntegerTest, FourDigits) +{ + EXPECT_EQ(FormatInteger(1234), "1,234"); + EXPECT_EQ(FormatInteger(-1234), "-1,234"); +} + +TEST(FormatIntegerTest, SevenDigits) +{ + EXPECT_EQ(FormatInteger(1234567), "1,234,567"); + EXPECT_EQ(FormatInteger(-1234567), "-1,234,567"); +} + +} // namespace +} // namespace devilution