diff --git a/Sources/Plasma/CoreLib/plFormat.cpp b/Sources/Plasma/CoreLib/plFormat.cpp index abe8c465..1ab5c5a8 100644 --- a/Sources/Plasma/CoreLib/plFormat.cpp +++ b/Sources/Plasma/CoreLib/plFormat.cpp @@ -45,6 +45,7 @@ Mead, WA 99021 #include "HeadSpin.h" #include #include +#include #define BADCHAR_REPLACEMENT (0xFFFDul) @@ -226,7 +227,8 @@ static void _formatNumeric(const plFormat_Private::FormatSpec &format, plStringStream &output, _IType value, int radix, bool upperCase = false) { - char pad = format.fPadChar ? format.fPadChar : ' '; + static_assert(std::is_unsigned<_IType>::value, + "Signed numerics are currently only supported in Decimal formatting"); size_t format_size = 0; _IType temp = value; @@ -250,8 +252,8 @@ template static void _formatDecimal(const plFormat_Private::FormatSpec &format, plStringStream &output, _IType value) { - char pad = format.fPadChar ? format.fPadChar : ' '; - _IType abs = (value < 0) ? -value : value; + typedef typename std::make_unsigned<_IType>::type _UType; + _UType abs = (value < 0) ? -(_UType)value : value; size_t format_size = 0; _IType temp = abs; @@ -266,18 +268,15 @@ static void _formatDecimal(const plFormat_Private::FormatSpec &format, if (value < 0 || format.fDigitClass == plFormat_Private::kDigitDecAlwaysSigned) ++format_size; - hsAssert(format_size < 21, "Format length too long"); - - char buffer[21]; - _IFormatNumeric_Impl<_IType>(buffer + format_size, abs, 10); + hsAssert(format_size < 24, "Format length too long"); - int signPos = arrsize(buffer) - static_cast(format_size); - hsAssert(signPos >= 0, "Format buffer not large enough for sign"); + char buffer[24]; + _IFormatNumeric_Impl<_UType>(buffer + format_size, abs, 10); if (value < 0) - buffer[signPos] = '-'; + buffer[0] = '-'; else if (format.fDigitClass == plFormat_Private::kDigitDecAlwaysSigned) - buffer[signPos] = '+'; + buffer[0] = '+'; _formatString(format, output, buffer, format_size, plFormat_Private::kAlignRight); } @@ -477,20 +476,20 @@ PL_FORMAT_IMPL(wchar_t) { switch (format.fDigitClass) { case plFormat_Private::kDigitBin: - _formatNumeric(format, output, value, 2); + _formatNumeric(format, output, value, 2); break; case plFormat_Private::kDigitOct: - _formatNumeric(format, output, value, 8); + _formatNumeric(format, output, value, 8); break; case plFormat_Private::kDigitHex: - _formatNumeric(format, output, value, 16, false); + _formatNumeric(format, output, value, 16, false); break; case plFormat_Private::kDigitHexUpper: - _formatNumeric(format, output,value, 16, true); + _formatNumeric(format, output, value, 16, true); break; case plFormat_Private::kDigitDec: case plFormat_Private::kDigitDecAlwaysSigned: - _formatDecimal(format, output, value); + _formatDecimal(format, output, value); break; case plFormat_Private::kDigitChar: case plFormat_Private::kDigitDefault: diff --git a/Sources/Tests/CoreTests/test_plFormat.cpp b/Sources/Tests/CoreTests/test_plFormat.cpp index 39b3de38..a15e8705 100644 --- a/Sources/Tests/CoreTests/test_plFormat.cpp +++ b/Sources/Tests/CoreTests/test_plFormat.cpp @@ -3,6 +3,7 @@ #include #include +#include TEST(plFormat, Escapes) { @@ -83,3 +84,62 @@ TEST(plFormat, Char) EXPECT_EQ(plString("xx\xf4\x8f\xbf\xbfxx"), plFormat("xx{c}xx", (int64_t)0x10ffff)); EXPECT_EQ(plString("xx\xf4\x8f\xbf\xbfxx"), plFormat("xx{c}xx", (uint64_t)0x10ffff)); } + +TEST(plFormat, Decimal) +{ + EXPECT_EQ(plString("xx1234xx"), plFormat("xx{}xx", 1234)); + EXPECT_EQ(plString("xx1234xx"), plFormat("xx{d}xx", 1234)); + EXPECT_EQ(plString("xx1234xx"), plFormat("xx{2}xx", 1234)); + EXPECT_EQ(plString("xx1234xx"), plFormat("xx{>2}xx", 1234)); + EXPECT_EQ(plString("xx1234xx"), plFormat("xx{<2}xx", 1234)); + EXPECT_EQ(plString("xx 1234xx"), plFormat("xx{6}xx", 1234)); + EXPECT_EQ(plString("xx 1234xx"), plFormat("xx{>6}xx", 1234)); + EXPECT_EQ(plString("xx1234 xx"), plFormat("xx{<6}xx", 1234)); + + // Character types + EXPECT_EQ(plString("xx0xx"), plFormat("xx{d}xx", '\0')); + EXPECT_EQ(plString("xx65xx"), plFormat("xx{d}xx", 'A')); + EXPECT_EQ(plString("xx0xx"), plFormat("xx{d}xx", L'\0')); + EXPECT_EQ(plString("xx65xx"), plFormat("xx{d}xx", L'A')); + EXPECT_EQ(plString("xx32767xx"), plFormat("xx{d}xx", L'\u7fff')); + + // Numeric char types + EXPECT_EQ(plString("xx0xx"), plFormat("xx{}xx", (signed char)0)); + EXPECT_EQ(plString("xx127xx"), plFormat("xx{}xx", std::numeric_limits::max())); + EXPECT_EQ(plString("xx+127xx"), plFormat("xx{+}xx", std::numeric_limits::max())); + EXPECT_EQ(plString("xx-128xx"), plFormat("xx{}xx", std::numeric_limits::min())); + EXPECT_EQ(plString("xx-128xx"), plFormat("xx{+}xx", std::numeric_limits::min())); + EXPECT_EQ(plString("xx0xx"), plFormat("xx{}xx", (unsigned char)0)); + EXPECT_EQ(plString("xx255xx"), plFormat("xx{}xx", std::numeric_limits::max())); + EXPECT_EQ(plString("xx+255xx"), plFormat("xx{+}xx", std::numeric_limits::max())); + + // 16-bit ints + EXPECT_EQ(plString("xx0xx"), plFormat("xx{}xx", (short)0)); + EXPECT_EQ(plString("xx32767xx"), plFormat("xx{}xx", std::numeric_limits::max())); + EXPECT_EQ(plString("xx+32767xx"), plFormat("xx{+}xx", std::numeric_limits::max())); + EXPECT_EQ(plString("xx-32768xx"), plFormat("xx{}xx", std::numeric_limits::min())); + EXPECT_EQ(plString("xx-32768xx"), plFormat("xx{+}xx", std::numeric_limits::min())); + EXPECT_EQ(plString("xx0xx"), plFormat("xx{}xx", (unsigned short)0)); + EXPECT_EQ(plString("xx65535xx"), plFormat("xx{}xx", std::numeric_limits::max())); + EXPECT_EQ(plString("xx+65535xx"), plFormat("xx{+}xx", std::numeric_limits::max())); + + // 32-bit ints + EXPECT_EQ(plString("xx0xx"), plFormat("xx{}xx", (int)0)); + EXPECT_EQ(plString("xx2147483647xx"), plFormat("xx{}xx", std::numeric_limits::max())); + EXPECT_EQ(plString("xx+2147483647xx"), plFormat("xx{+}xx", std::numeric_limits::max())); + EXPECT_EQ(plString("xx-2147483648xx"), plFormat("xx{}xx", std::numeric_limits::min())); + EXPECT_EQ(plString("xx-2147483648xx"), plFormat("xx{+}xx", std::numeric_limits::min())); + EXPECT_EQ(plString("xx0xx"), plFormat("xx{}xx", (unsigned int)0)); + EXPECT_EQ(plString("xx4294967295xx"), plFormat("xx{}xx", std::numeric_limits::max())); + EXPECT_EQ(plString("xx+4294967295xx"), plFormat("xx{+}xx", std::numeric_limits::max())); + + // 64-bit ints + EXPECT_EQ(plString("xx0xx"), plFormat("xx{}xx", (int64_t)0)); + EXPECT_EQ(plString("xx9223372036854775807xx"), plFormat("xx{}xx", std::numeric_limits::max())); + EXPECT_EQ(plString("xx+9223372036854775807xx"), plFormat("xx{+}xx", std::numeric_limits::max())); + EXPECT_EQ(plString("xx-9223372036854775808xx"), plFormat("xx{}xx", std::numeric_limits::min())); + EXPECT_EQ(plString("xx-9223372036854775808xx"), plFormat("xx{+}xx", std::numeric_limits::min())); + EXPECT_EQ(plString("xx0xx"), plFormat("xx{}xx", (uint64_t)0)); + EXPECT_EQ(plString("xx18446744073709551615xx"), plFormat("xx{}xx", std::numeric_limits::max())); + EXPECT_EQ(plString("xx+18446744073709551615xx"), plFormat("xx{+}xx", std::numeric_limits::max())); +}