mirror of
https://github.com/OpenRCT2/OpenRCT2
synced 2025-12-23 15:52:55 +01:00
Merge pull request #13662 from ZehMatt/formatter-perf
Replace std::stringstream with custom string buffer
This commit is contained in:
@@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
namespace OpenRCT2
|
namespace OpenRCT2
|
||||||
{
|
{
|
||||||
static void FormatMonthYear(std::stringstream& ss, int32_t month, int32_t year);
|
static void FormatMonthYear(FormatBuffer& ss, int32_t month, int32_t year);
|
||||||
|
|
||||||
static std::optional<int32_t> ParseNumericToken(std::string_view s)
|
static std::optional<int32_t> ParseNumericToken(std::string_view s)
|
||||||
{
|
{
|
||||||
@@ -278,7 +278,7 @@ namespace OpenRCT2
|
|||||||
return sz != nullptr ? sz : std::string_view();
|
return sz != nullptr ? sz : std::string_view();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormatRealName(std::stringstream& ss, rct_string_id id)
|
void FormatRealName(FormatBuffer& ss, rct_string_id id)
|
||||||
{
|
{
|
||||||
if (IsRealNameStringId(id))
|
if (IsRealNameStringId(id))
|
||||||
{
|
{
|
||||||
@@ -301,7 +301,7 @@ namespace OpenRCT2
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<size_t TDecimalPlace, bool TDigitSep, typename T> void FormatNumber(std::stringstream& ss, T value)
|
template<size_t TDecimalPlace, bool TDigitSep, typename T> void FormatNumber(FormatBuffer& ss, T value)
|
||||||
{
|
{
|
||||||
char buffer[32];
|
char buffer[32];
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
@@ -379,7 +379,7 @@ namespace OpenRCT2
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<size_t TDecimalPlace, bool TDigitSep, typename T> void FormatCurrency(std::stringstream& ss, T rawValue)
|
template<size_t TDecimalPlace, bool TDigitSep, typename T> void FormatCurrency(FormatBuffer& ss, T rawValue)
|
||||||
{
|
{
|
||||||
auto currencyDesc = &CurrencyDescriptors[EnumValue(gConfigGeneral.currency_format)];
|
auto currencyDesc = &CurrencyDescriptors[EnumValue(gConfigGeneral.currency_format)];
|
||||||
auto value = static_cast<int64_t>(rawValue) * currencyDesc->rate;
|
auto value = static_cast<int64_t>(rawValue) * currencyDesc->rate;
|
||||||
@@ -434,7 +434,7 @@ namespace OpenRCT2
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T> static void FormatMinutesSeconds(std::stringstream& ss, T value)
|
template<typename T> static void FormatMinutesSeconds(FormatBuffer& ss, T value)
|
||||||
{
|
{
|
||||||
static constexpr const rct_string_id Formats[][2] = {
|
static constexpr const rct_string_id Formats[][2] = {
|
||||||
{ STR_DURATION_SEC, STR_DURATION_SECS },
|
{ STR_DURATION_SEC, STR_DURATION_SECS },
|
||||||
@@ -456,7 +456,7 @@ namespace OpenRCT2
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T> static void FormatHoursMinutes(std::stringstream& ss, T value)
|
template<typename T> static void FormatHoursMinutes(FormatBuffer& ss, T value)
|
||||||
{
|
{
|
||||||
static constexpr const rct_string_id Formats[][2] = {
|
static constexpr const rct_string_id Formats[][2] = {
|
||||||
{ STR_REALTIME_MIN, STR_REALTIME_MINS },
|
{ STR_REALTIME_MIN, STR_REALTIME_MINS },
|
||||||
@@ -478,7 +478,7 @@ namespace OpenRCT2
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T> void FormatArgument(std::stringstream& ss, FormatToken token, T arg)
|
template<typename T> void FormatArgument(FormatBuffer& ss, FormatToken token, T arg)
|
||||||
{
|
{
|
||||||
switch (token)
|
switch (token)
|
||||||
{
|
{
|
||||||
@@ -593,31 +593,17 @@ namespace OpenRCT2
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case FormatToken::String:
|
case FormatToken::String:
|
||||||
if constexpr (std::is_same<T, const char*>())
|
|
||||||
{
|
|
||||||
if (arg != nullptr)
|
|
||||||
{
|
|
||||||
ss << arg;
|
ss << arg;
|
||||||
}
|
|
||||||
}
|
|
||||||
else if constexpr (std::is_same<T, const std::string&>())
|
|
||||||
{
|
|
||||||
ss << arg.c_str();
|
|
||||||
}
|
|
||||||
else if constexpr (std::is_same<T, std::string>())
|
|
||||||
{
|
|
||||||
ss << arg.c_str();
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case FormatToken::Sprite:
|
case FormatToken::Sprite:
|
||||||
if constexpr (std::is_integral<T>())
|
if constexpr (std::is_integral<T>())
|
||||||
{
|
{
|
||||||
auto idx = static_cast<uint32_t>(arg);
|
auto idx = static_cast<uint32_t>(arg);
|
||||||
ss << "{INLINE_SPRITE}";
|
char inlineBuf[64];
|
||||||
ss << "{" << ((idx >> 0) & 0xFF) << "}";
|
size_t len = snprintf(
|
||||||
ss << "{" << ((idx >> 8) & 0xFF) << "}";
|
inlineBuf, sizeof(inlineBuf), "{INLINE_SPRITE}{%u}{%u}{%u}{%u}", ((idx >> 0) & 0xFF),
|
||||||
ss << "{" << ((idx >> 16) & 0xFF) << "}";
|
((idx >> 8) & 0xFF), ((idx >> 16) & 0xFF), ((idx >> 24) & 0xFF));
|
||||||
ss << "{" << ((idx >> 24) & 0xFF) << "}";
|
ss.append(inlineBuf, len);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -625,12 +611,12 @@ namespace OpenRCT2
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template void FormatArgument(std::stringstream&, FormatToken, uint16_t);
|
template void FormatArgument(FormatBuffer&, FormatToken, uint16_t);
|
||||||
template void FormatArgument(std::stringstream&, FormatToken, int16_t);
|
template void FormatArgument(FormatBuffer&, FormatToken, int16_t);
|
||||||
template void FormatArgument(std::stringstream&, FormatToken, int32_t);
|
template void FormatArgument(FormatBuffer&, FormatToken, int32_t);
|
||||||
template void FormatArgument(std::stringstream&, FormatToken, int64_t);
|
template void FormatArgument(FormatBuffer&, FormatToken, int64_t);
|
||||||
template void FormatArgument(std::stringstream&, FormatToken, uint64_t);
|
template void FormatArgument(FormatBuffer&, FormatToken, uint64_t);
|
||||||
template void FormatArgument(std::stringstream&, FormatToken, const char*);
|
template void FormatArgument(FormatBuffer&, FormatToken, const char*);
|
||||||
|
|
||||||
bool IsRealNameStringId(rct_string_id id)
|
bool IsRealNameStringId(rct_string_id id)
|
||||||
{
|
{
|
||||||
@@ -643,27 +629,24 @@ namespace OpenRCT2
|
|||||||
return FmtString(fmtc);
|
return FmtString(fmtc);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::stringstream& GetThreadFormatStream()
|
FormatBuffer& GetThreadFormatStream()
|
||||||
{
|
{
|
||||||
thread_local std::stringstream ss;
|
thread_local FormatBuffer ss;
|
||||||
// Reset the buffer (reported as most efficient way)
|
ss.clear();
|
||||||
std::stringstream().swap(ss);
|
|
||||||
return ss;
|
return ss;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t CopyStringStreamToBuffer(char* buffer, size_t bufferLen, std::stringstream& ss)
|
size_t CopyStringStreamToBuffer(char* buffer, size_t bufferLen, FormatBuffer& ss)
|
||||||
{
|
{
|
||||||
auto stringLen = ss.tellp();
|
auto copyLen = std::min<size_t>(bufferLen - 1, ss.size());
|
||||||
auto copyLen = std::min<size_t>(bufferLen - 1, stringLen);
|
|
||||||
|
|
||||||
ss.seekg(0, std::ios::beg);
|
std::copy(ss.data(), ss.data() + copyLen, buffer);
|
||||||
ss.read(buffer, copyLen);
|
|
||||||
buffer[copyLen] = '\0';
|
buffer[copyLen] = '\0';
|
||||||
|
|
||||||
return stringLen;
|
return ss.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void FormatArgumentAny(std::stringstream& ss, FormatToken token, const FormatArg_t& value)
|
static void FormatArgumentAny(FormatBuffer& ss, FormatToken token, const FormatArg_t& value)
|
||||||
{
|
{
|
||||||
if (std::holds_alternative<uint16_t>(value))
|
if (std::holds_alternative<uint16_t>(value))
|
||||||
{
|
{
|
||||||
@@ -687,8 +670,7 @@ namespace OpenRCT2
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void FormatStringAny(
|
static void FormatStringAny(FormatBuffer& ss, const FmtString& fmt, const std::vector<FormatArg_t>& args, size_t& argIndex)
|
||||||
std::stringstream& ss, const FmtString& fmt, const std::vector<FormatArg_t>& args, size_t& argIndex)
|
|
||||||
{
|
{
|
||||||
for (const auto& token : fmt)
|
for (const auto& token : fmt)
|
||||||
{
|
{
|
||||||
@@ -735,7 +717,7 @@ namespace OpenRCT2
|
|||||||
auto& ss = GetThreadFormatStream();
|
auto& ss = GetThreadFormatStream();
|
||||||
size_t argIndex = 0;
|
size_t argIndex = 0;
|
||||||
FormatStringAny(ss, fmt, args, argIndex);
|
FormatStringAny(ss, fmt, args, argIndex);
|
||||||
return ss.str();
|
return ss.data();
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t FormatStringAny(char* buffer, size_t bufferLen, const FmtString& fmt, const std::vector<FormatArg_t>& args)
|
size_t FormatStringAny(char* buffer, size_t bufferLen, const FmtString& fmt, const std::vector<FormatArg_t>& args)
|
||||||
@@ -815,7 +797,7 @@ namespace OpenRCT2
|
|||||||
return FormatStringAny(buffer, bufferLen, fmt, anyArgs);
|
return FormatStringAny(buffer, bufferLen, fmt, anyArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void FormatMonthYear(std::stringstream& ss, int32_t month, int32_t year)
|
static void FormatMonthYear(FormatBuffer& ss, int32_t month, int32_t year)
|
||||||
{
|
{
|
||||||
thread_local std::vector<FormatArg_t> tempArgs;
|
thread_local std::vector<FormatArg_t> tempArgs;
|
||||||
tempArgs.clear();
|
tempArgs.clear();
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
#include "FormatCodes.h"
|
#include "FormatCodes.h"
|
||||||
#include "Language.h"
|
#include "Language.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <stack>
|
#include <stack>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -23,6 +24,127 @@
|
|||||||
|
|
||||||
namespace OpenRCT2
|
namespace OpenRCT2
|
||||||
{
|
{
|
||||||
|
template<typename T, size_t StackSize = 256, typename TTraits = std::char_traits<T>> class FormatBufferBase
|
||||||
|
{
|
||||||
|
T _storage[StackSize];
|
||||||
|
T* _buffer;
|
||||||
|
size_t _size;
|
||||||
|
// NOTE: Capacity is on purpose uint32_t to have a fixed position for the flag on each architecture.
|
||||||
|
uint32_t _capacity;
|
||||||
|
TTraits _traits;
|
||||||
|
|
||||||
|
static constexpr uint32_t FlagLocalStorage = (1u << 31);
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit FormatBufferBase()
|
||||||
|
: _storage{}
|
||||||
|
, _buffer(_storage)
|
||||||
|
, _size{}
|
||||||
|
, _capacity(FlagLocalStorage | static_cast<uint32_t>(StackSize))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~FormatBufferBase()
|
||||||
|
{
|
||||||
|
if (_capacity & FlagLocalStorage)
|
||||||
|
return;
|
||||||
|
delete[] _buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t size() const
|
||||||
|
{
|
||||||
|
return _size;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t capacity() const
|
||||||
|
{
|
||||||
|
return _capacity & ~FlagLocalStorage;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
_size = 0;
|
||||||
|
_buffer[0] = T{};
|
||||||
|
}
|
||||||
|
|
||||||
|
const T* data() const
|
||||||
|
{
|
||||||
|
return _buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
T* data()
|
||||||
|
{
|
||||||
|
return _buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<size_t N> auto& operator<<(T const (&v)[N])
|
||||||
|
{
|
||||||
|
append(v, N);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& operator<<(const T v)
|
||||||
|
{
|
||||||
|
append(&v, 1);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& operator<<(const T* v)
|
||||||
|
{
|
||||||
|
if (!v)
|
||||||
|
return *this;
|
||||||
|
|
||||||
|
append(v, _traits.length(v));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& operator<<(const std::basic_string_view<T> v)
|
||||||
|
{
|
||||||
|
append(v.data(), v.size());
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& operator<<(const std::basic_string<T>& v)
|
||||||
|
{
|
||||||
|
append(v.data(), v.size());
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void append(const T* buf, size_t len)
|
||||||
|
{
|
||||||
|
ensure_capacity(len);
|
||||||
|
|
||||||
|
std::copy(buf, buf + len, _buffer + _size);
|
||||||
|
|
||||||
|
_size += len;
|
||||||
|
_buffer[_size] = T{};
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void ensure_capacity(size_t additionalSize)
|
||||||
|
{
|
||||||
|
const size_t curSize = size();
|
||||||
|
const size_t curCapacity = capacity();
|
||||||
|
const bool isLocalStorage = _capacity & FlagLocalStorage;
|
||||||
|
|
||||||
|
if (curSize + additionalSize < curCapacity)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const size_t newCapacity = (curCapacity + additionalSize + 1) << 1;
|
||||||
|
|
||||||
|
T* newBuf = new T[newCapacity];
|
||||||
|
std::copy(_buffer, _buffer + curSize, newBuf);
|
||||||
|
|
||||||
|
if (!isLocalStorage)
|
||||||
|
delete[] _buffer;
|
||||||
|
|
||||||
|
_capacity = static_cast<uint32_t>(newCapacity);
|
||||||
|
_buffer = newBuf;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using FormatBuffer = FormatBufferBase<char>;
|
||||||
|
|
||||||
using FormatArg_t = std::variant<uint16_t, int32_t, const char*, std::string>;
|
using FormatArg_t = std::variant<uint16_t, int32_t, const char*, std::string>;
|
||||||
|
|
||||||
class FmtString
|
class FmtString
|
||||||
@@ -76,15 +198,15 @@ namespace OpenRCT2
|
|||||||
std::string WithoutFormatTokens() const;
|
std::string WithoutFormatTokens() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T> void FormatArgument(std::stringstream& ss, FormatToken token, T arg);
|
template<typename T> void FormatArgument(FormatBuffer& ss, FormatToken token, T arg);
|
||||||
|
|
||||||
bool IsRealNameStringId(rct_string_id id);
|
bool IsRealNameStringId(rct_string_id id);
|
||||||
void FormatRealName(std::stringstream& ss, rct_string_id id);
|
void FormatRealName(FormatBuffer& ss, rct_string_id id);
|
||||||
FmtString GetFmtStringById(rct_string_id id);
|
FmtString GetFmtStringById(rct_string_id id);
|
||||||
std::stringstream& GetThreadFormatStream();
|
FormatBuffer& GetThreadFormatStream();
|
||||||
size_t CopyStringStreamToBuffer(char* buffer, size_t bufferLen, std::stringstream& ss);
|
size_t CopyStringStreamToBuffer(char* buffer, size_t bufferLen, FormatBuffer& ss);
|
||||||
|
|
||||||
inline void FormatString(std::stringstream& ss, std::stack<FmtString::iterator>& stack)
|
inline void FormatString(FormatBuffer& ss, std::stack<FmtString::iterator>& stack)
|
||||||
{
|
{
|
||||||
while (!stack.empty())
|
while (!stack.empty())
|
||||||
{
|
{
|
||||||
@@ -103,7 +225,7 @@ namespace OpenRCT2
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename TArg0, typename... TArgs>
|
template<typename TArg0, typename... TArgs>
|
||||||
static void FormatString(std::stringstream& ss, std::stack<FmtString::iterator>& stack, TArg0 arg0, TArgs&&... argN)
|
static void FormatString(FormatBuffer& ss, std::stack<FmtString::iterator>& stack, TArg0 arg0, TArgs&&... argN)
|
||||||
{
|
{
|
||||||
while (!stack.empty())
|
while (!stack.empty())
|
||||||
{
|
{
|
||||||
@@ -144,7 +266,7 @@ namespace OpenRCT2
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename... TArgs> static void FormatString(std::stringstream& ss, const FmtString& fmt, TArgs&&... argN)
|
template<typename... TArgs> static void FormatString(FormatBuffer& ss, const FmtString& fmt, TArgs&&... argN)
|
||||||
{
|
{
|
||||||
std::stack<FmtString::iterator> stack;
|
std::stack<FmtString::iterator> stack;
|
||||||
stack.push(fmt.begin());
|
stack.push(fmt.begin());
|
||||||
@@ -155,7 +277,7 @@ namespace OpenRCT2
|
|||||||
{
|
{
|
||||||
auto& ss = GetThreadFormatStream();
|
auto& ss = GetThreadFormatStream();
|
||||||
FormatString(ss, fmt, argN...);
|
FormatString(ss, fmt, argN...);
|
||||||
return ss.str();
|
return ss.data();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename... TArgs>
|
template<typename... TArgs>
|
||||||
@@ -166,7 +288,7 @@ namespace OpenRCT2
|
|||||||
return CopyStringStreamToBuffer(buffer, bufferLen, ss);
|
return CopyStringStreamToBuffer(buffer, bufferLen, ss);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename... TArgs> static void FormatStringId(std::stringstream& ss, rct_string_id id, TArgs&&... argN)
|
template<typename... TArgs> static void FormatStringId(FormatBuffer& ss, rct_string_id id, TArgs&&... argN)
|
||||||
{
|
{
|
||||||
auto fmt = GetFmtStringById(id);
|
auto fmt = GetFmtStringById(id);
|
||||||
FormatString(ss, fmt, argN...);
|
FormatString(ss, fmt, argN...);
|
||||||
|
|||||||
@@ -341,260 +341,260 @@ TEST_F(FormattingTests, using_legacy_buffer_args)
|
|||||||
|
|
||||||
TEST_F(FormattingTests, format_number_basic)
|
TEST_F(FormattingTests, format_number_basic)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
FormatBuffer ss;
|
||||||
// test basic integral conversion
|
// test basic integral conversion
|
||||||
FormatArgument<int32_t>(ss, FormatToken::UInt16, 123);
|
FormatArgument<int32_t>(ss, FormatToken::UInt16, 123);
|
||||||
ASSERT_STREQ("123", ss.str().c_str());
|
ASSERT_STREQ("123", ss.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(FormattingTests, format_number_basic_int32)
|
TEST_F(FormattingTests, format_number_basic_int32)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
FormatBuffer ss;
|
||||||
// test that case fallthrough works
|
// test that case fallthrough works
|
||||||
FormatArgument<int32_t>(ss, FormatToken::Int32, 123);
|
FormatArgument<int32_t>(ss, FormatToken::Int32, 123);
|
||||||
ASSERT_STREQ("123", ss.str().c_str());
|
ASSERT_STREQ("123", ss.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(FormattingTests, format_number_negative)
|
TEST_F(FormattingTests, format_number_negative)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
FormatBuffer ss;
|
||||||
// test negative conversion
|
// test negative conversion
|
||||||
FormatArgument<int32_t>(ss, FormatToken::Int32, -123);
|
FormatArgument<int32_t>(ss, FormatToken::Int32, -123);
|
||||||
ASSERT_STREQ("-123", ss.str().c_str());
|
ASSERT_STREQ("-123", ss.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(FormattingTests, format_number_comma16_basic)
|
TEST_F(FormattingTests, format_number_comma16_basic)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
FormatBuffer ss;
|
||||||
// test separator formatter
|
// test separator formatter
|
||||||
// test base case separator formatter
|
// test base case separator formatter
|
||||||
FormatArgument<int32_t>(ss, FormatToken::Comma16, 123);
|
FormatArgument<int32_t>(ss, FormatToken::Comma16, 123);
|
||||||
ASSERT_STREQ("123", ss.str().c_str());
|
ASSERT_STREQ("123", ss.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(FormattingTests, format_number_comma16_negative)
|
TEST_F(FormattingTests, format_number_comma16_negative)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
FormatBuffer ss;
|
||||||
// test separator formatter
|
// test separator formatter
|
||||||
// test base case separator formatter
|
// test base case separator formatter
|
||||||
FormatArgument<int32_t>(ss, FormatToken::Comma16, -123);
|
FormatArgument<int32_t>(ss, FormatToken::Comma16, -123);
|
||||||
ASSERT_STREQ("-123", ss.str().c_str());
|
ASSERT_STREQ("-123", ss.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(FormattingTests, format_number_comma16_large)
|
TEST_F(FormattingTests, format_number_comma16_large)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
FormatBuffer ss;
|
||||||
// test larger value for separator formatter
|
// test larger value for separator formatter
|
||||||
FormatArgument<int32_t>(ss, FormatToken::Comma16, 123456789);
|
FormatArgument<int32_t>(ss, FormatToken::Comma16, 123456789);
|
||||||
ASSERT_STREQ("123,456,789", ss.str().c_str());
|
ASSERT_STREQ("123,456,789", ss.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(FormattingTests, format_number_comma16_large_negative)
|
TEST_F(FormattingTests, format_number_comma16_large_negative)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
FormatBuffer ss;
|
||||||
// test larger value for separator formatter with negative
|
// test larger value for separator formatter with negative
|
||||||
FormatArgument<int32_t>(ss, FormatToken::Comma16, -123456789);
|
FormatArgument<int32_t>(ss, FormatToken::Comma16, -123456789);
|
||||||
ASSERT_STREQ("-123,456,789", ss.str().c_str());
|
ASSERT_STREQ("-123,456,789", ss.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(FormattingTests, format_number_comma16_uneven)
|
TEST_F(FormattingTests, format_number_comma16_uneven)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
FormatBuffer ss;
|
||||||
// test non-multiple of 3
|
// test non-multiple of 3
|
||||||
FormatArgument<int32_t>(ss, FormatToken::Comma16, 12345678);
|
FormatArgument<int32_t>(ss, FormatToken::Comma16, 12345678);
|
||||||
ASSERT_STREQ("12,345,678", ss.str().c_str());
|
ASSERT_STREQ("12,345,678", ss.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(FormattingTests, format_number_comma16_uneven_negative)
|
TEST_F(FormattingTests, format_number_comma16_uneven_negative)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
FormatBuffer ss;
|
||||||
// test non-multiple of 3 with negative
|
// test non-multiple of 3 with negative
|
||||||
FormatArgument<int32_t>(ss, FormatToken::Comma16, -12345678);
|
FormatArgument<int32_t>(ss, FormatToken::Comma16, -12345678);
|
||||||
ASSERT_STREQ("-12,345,678", ss.str().c_str());
|
ASSERT_STREQ("-12,345,678", ss.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(FormattingTests, format_number_comma16_zero)
|
TEST_F(FormattingTests, format_number_comma16_zero)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
FormatBuffer ss;
|
||||||
// test zero
|
// test zero
|
||||||
FormatArgument<int32_t>(ss, FormatToken::Comma16, 0);
|
FormatArgument<int32_t>(ss, FormatToken::Comma16, 0);
|
||||||
ASSERT_STREQ("0", ss.str().c_str());
|
ASSERT_STREQ("0", ss.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(FormattingTests, format_number_comma1dp16_zero)
|
TEST_F(FormattingTests, format_number_comma1dp16_zero)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
FormatBuffer ss;
|
||||||
// zero case
|
// zero case
|
||||||
FormatArgument<int32_t>(ss, FormatToken::Comma1dp16, 0);
|
FormatArgument<int32_t>(ss, FormatToken::Comma1dp16, 0);
|
||||||
ASSERT_STREQ("0.0", ss.str().c_str());
|
ASSERT_STREQ("0.0", ss.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(FormattingTests, format_number_comma1dp16_leading_zero)
|
TEST_F(FormattingTests, format_number_comma1dp16_leading_zero)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
FormatBuffer ss;
|
||||||
// test leading zero
|
// test leading zero
|
||||||
FormatArgument<int32_t>(ss, FormatToken::Comma1dp16, 5);
|
FormatArgument<int32_t>(ss, FormatToken::Comma1dp16, 5);
|
||||||
ASSERT_STREQ("0.5", ss.str().c_str());
|
ASSERT_STREQ("0.5", ss.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(FormattingTests, format_number_comma1dp16_leading_zero_negative)
|
TEST_F(FormattingTests, format_number_comma1dp16_leading_zero_negative)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
FormatBuffer ss;
|
||||||
// test leading zero with negative value
|
// test leading zero with negative value
|
||||||
FormatArgument<int32_t>(ss, FormatToken::Comma1dp16, -5);
|
FormatArgument<int32_t>(ss, FormatToken::Comma1dp16, -5);
|
||||||
ASSERT_STREQ("-0.5", ss.str().c_str());
|
ASSERT_STREQ("-0.5", ss.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(FormattingTests, format_number_comma1dp16_small_value)
|
TEST_F(FormattingTests, format_number_comma1dp16_small_value)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
FormatBuffer ss;
|
||||||
// test small value
|
// test small value
|
||||||
FormatArgument<int32_t>(ss, FormatToken::Comma1dp16, 75);
|
FormatArgument<int32_t>(ss, FormatToken::Comma1dp16, 75);
|
||||||
ASSERT_STREQ("7.5", ss.str().c_str());
|
ASSERT_STREQ("7.5", ss.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(FormattingTests, format_number_comma1dp16_small_value_negative)
|
TEST_F(FormattingTests, format_number_comma1dp16_small_value_negative)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
FormatBuffer ss;
|
||||||
// test small value with negative
|
// test small value with negative
|
||||||
FormatArgument<int32_t>(ss, FormatToken::Comma1dp16, -75);
|
FormatArgument<int32_t>(ss, FormatToken::Comma1dp16, -75);
|
||||||
ASSERT_STREQ("-7.5", ss.str().c_str());
|
ASSERT_STREQ("-7.5", ss.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(FormattingTests, format_number_comma1dp16_trailing_zeros)
|
TEST_F(FormattingTests, format_number_comma1dp16_trailing_zeros)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
FormatBuffer ss;
|
||||||
// test value with trailing zero, no commas
|
// test value with trailing zero, no commas
|
||||||
FormatArgument<int32_t>(ss, FormatToken::Comma1dp16, 1000);
|
FormatArgument<int32_t>(ss, FormatToken::Comma1dp16, 1000);
|
||||||
ASSERT_STREQ("100.0", ss.str().c_str());
|
ASSERT_STREQ("100.0", ss.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(FormattingTests, format_number_comma1dp16_trailing_zeros_negative)
|
TEST_F(FormattingTests, format_number_comma1dp16_trailing_zeros_negative)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
FormatBuffer ss;
|
||||||
// test value with trailing zero, no commas
|
// test value with trailing zero, no commas
|
||||||
FormatArgument<int32_t>(ss, FormatToken::Comma1dp16, -1000);
|
FormatArgument<int32_t>(ss, FormatToken::Comma1dp16, -1000);
|
||||||
ASSERT_STREQ("-100.0", ss.str().c_str());
|
ASSERT_STREQ("-100.0", ss.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(FormattingTests, format_number_comma1dp16_large_trailing_zeros)
|
TEST_F(FormattingTests, format_number_comma1dp16_large_trailing_zeros)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
FormatBuffer ss;
|
||||||
// test value with commas and trailing zeros
|
// test value with commas and trailing zeros
|
||||||
FormatArgument<int32_t>(ss, FormatToken::Comma1dp16, 10000000);
|
FormatArgument<int32_t>(ss, FormatToken::Comma1dp16, 10000000);
|
||||||
ASSERT_STREQ("1,000,000.0", ss.str().c_str());
|
ASSERT_STREQ("1,000,000.0", ss.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(FormattingTests, format_number_comma1dp16_large_trailing_zeros_negative)
|
TEST_F(FormattingTests, format_number_comma1dp16_large_trailing_zeros_negative)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
FormatBuffer ss;
|
||||||
// test value with commas and trailing zeros
|
// test value with commas and trailing zeros
|
||||||
FormatArgument<int32_t>(ss, FormatToken::Comma1dp16, -10000000);
|
FormatArgument<int32_t>(ss, FormatToken::Comma1dp16, -10000000);
|
||||||
ASSERT_STREQ("-1,000,000.0", ss.str().c_str());
|
ASSERT_STREQ("-1,000,000.0", ss.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(FormattingTests, format_number_comma1dp16_large_value)
|
TEST_F(FormattingTests, format_number_comma1dp16_large_value)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
FormatBuffer ss;
|
||||||
// test large value
|
// test large value
|
||||||
FormatArgument<int32_t>(ss, FormatToken::Comma1dp16, 123456789);
|
FormatArgument<int32_t>(ss, FormatToken::Comma1dp16, 123456789);
|
||||||
ASSERT_STREQ("12,345,678.9", ss.str().c_str());
|
ASSERT_STREQ("12,345,678.9", ss.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(FormattingTests, format_number_comma1dp16_large_value_negative)
|
TEST_F(FormattingTests, format_number_comma1dp16_large_value_negative)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
FormatBuffer ss;
|
||||||
// test large value
|
// test large value
|
||||||
FormatArgument<int32_t>(ss, FormatToken::Comma1dp16, -123456789);
|
FormatArgument<int32_t>(ss, FormatToken::Comma1dp16, -123456789);
|
||||||
ASSERT_STREQ("-12,345,678.9", ss.str().c_str());
|
ASSERT_STREQ("-12,345,678.9", ss.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(FormattingTests, format_number_comma2dp32_zero)
|
TEST_F(FormattingTests, format_number_comma2dp32_zero)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
FormatBuffer ss;
|
||||||
// zero case
|
// zero case
|
||||||
FormatArgument<int32_t>(ss, FormatToken::Comma2dp32, 0);
|
FormatArgument<int32_t>(ss, FormatToken::Comma2dp32, 0);
|
||||||
ASSERT_STREQ("0.00", ss.str().c_str());
|
ASSERT_STREQ("0.00", ss.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(FormattingTests, format_number_comma2dp32_less_sig_figs)
|
TEST_F(FormattingTests, format_number_comma2dp32_less_sig_figs)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
FormatBuffer ss;
|
||||||
// test leading zero
|
// test leading zero
|
||||||
FormatArgument<int32_t>(ss, FormatToken::Comma2dp32, 5);
|
FormatArgument<int32_t>(ss, FormatToken::Comma2dp32, 5);
|
||||||
ASSERT_STREQ("0.05", ss.str().c_str());
|
ASSERT_STREQ("0.05", ss.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(FormattingTests, format_number_comma2dp32_less_sig_figs_negative)
|
TEST_F(FormattingTests, format_number_comma2dp32_less_sig_figs_negative)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
FormatBuffer ss;
|
||||||
// test leading zero
|
// test leading zero
|
||||||
FormatArgument<int32_t>(ss, FormatToken::Comma2dp32, -5);
|
FormatArgument<int32_t>(ss, FormatToken::Comma2dp32, -5);
|
||||||
ASSERT_STREQ("-0.05", ss.str().c_str());
|
ASSERT_STREQ("-0.05", ss.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(FormattingTests, format_number_comma2dp32_leading_zero)
|
TEST_F(FormattingTests, format_number_comma2dp32_leading_zero)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
FormatBuffer ss;
|
||||||
// test small value
|
// test small value
|
||||||
FormatArgument<int32_t>(ss, FormatToken::Comma2dp32, 75);
|
FormatArgument<int32_t>(ss, FormatToken::Comma2dp32, 75);
|
||||||
ASSERT_STREQ("0.75", ss.str().c_str());
|
ASSERT_STREQ("0.75", ss.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(FormattingTests, format_number_comma2dp32_leading_zero_negative)
|
TEST_F(FormattingTests, format_number_comma2dp32_leading_zero_negative)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
FormatBuffer ss;
|
||||||
// test small value
|
// test small value
|
||||||
FormatArgument<int32_t>(ss, FormatToken::Comma2dp32, -75);
|
FormatArgument<int32_t>(ss, FormatToken::Comma2dp32, -75);
|
||||||
ASSERT_STREQ("-0.75", ss.str().c_str());
|
ASSERT_STREQ("-0.75", ss.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(FormattingTests, format_number_comma2dp32_trailing_zeros)
|
TEST_F(FormattingTests, format_number_comma2dp32_trailing_zeros)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
FormatBuffer ss;
|
||||||
// test value with trailing zero, no commas
|
// test value with trailing zero, no commas
|
||||||
FormatArgument<int32_t>(ss, FormatToken::Comma2dp32, 1000);
|
FormatArgument<int32_t>(ss, FormatToken::Comma2dp32, 1000);
|
||||||
ASSERT_STREQ("10.00", ss.str().c_str());
|
ASSERT_STREQ("10.00", ss.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(FormattingTests, format_number_comma2dp32_trailing_zeros_negative)
|
TEST_F(FormattingTests, format_number_comma2dp32_trailing_zeros_negative)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
FormatBuffer ss;
|
||||||
// test value with trailing zero, no commas
|
// test value with trailing zero, no commas
|
||||||
FormatArgument<int32_t>(ss, FormatToken::Comma2dp32, -1000);
|
FormatArgument<int32_t>(ss, FormatToken::Comma2dp32, -1000);
|
||||||
ASSERT_STREQ("-10.00", ss.str().c_str());
|
ASSERT_STREQ("-10.00", ss.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(FormattingTests, format_number_comma2dp32_large_trailing_zeros)
|
TEST_F(FormattingTests, format_number_comma2dp32_large_trailing_zeros)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
FormatBuffer ss;
|
||||||
// test value with commas and trailing zeros
|
// test value with commas and trailing zeros
|
||||||
FormatArgument<int32_t>(ss, FormatToken::Comma2dp32, 10000000);
|
FormatArgument<int32_t>(ss, FormatToken::Comma2dp32, 10000000);
|
||||||
ASSERT_STREQ("100,000.00", ss.str().c_str());
|
ASSERT_STREQ("100,000.00", ss.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(FormattingTests, format_number_comma2dp32_large_trailing_zeros_negative)
|
TEST_F(FormattingTests, format_number_comma2dp32_large_trailing_zeros_negative)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
FormatBuffer ss;
|
||||||
// test value with commas and trailing zeros
|
// test value with commas and trailing zeros
|
||||||
FormatArgument<int32_t>(ss, FormatToken::Comma2dp32, -10000000);
|
FormatArgument<int32_t>(ss, FormatToken::Comma2dp32, -10000000);
|
||||||
ASSERT_STREQ("-100,000.00", ss.str().c_str());
|
ASSERT_STREQ("-100,000.00", ss.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(FormattingTests, format_number_comma2dp32_large_value)
|
TEST_F(FormattingTests, format_number_comma2dp32_large_value)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
FormatBuffer ss;
|
||||||
// test large value
|
// test large value
|
||||||
FormatArgument<int32_t>(ss, FormatToken::Comma2dp32, 123456789);
|
FormatArgument<int32_t>(ss, FormatToken::Comma2dp32, 123456789);
|
||||||
ASSERT_STREQ("1,234,567.89", ss.str().c_str());
|
ASSERT_STREQ("1,234,567.89", ss.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(FormattingTests, format_number_comma2dp32_large_value_negative)
|
TEST_F(FormattingTests, format_number_comma2dp32_large_value_negative)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
FormatBuffer ss;
|
||||||
// test large value
|
// test large value
|
||||||
FormatArgument<int32_t>(ss, FormatToken::Comma2dp32, -123456789);
|
FormatArgument<int32_t>(ss, FormatToken::Comma2dp32, -123456789);
|
||||||
ASSERT_STREQ("-1,234,567.89", ss.str().c_str());
|
ASSERT_STREQ("-1,234,567.89", ss.data());
|
||||||
|
|
||||||
// extra note:
|
// extra note:
|
||||||
// for some reason the FormatArgument function contains constexpr
|
// for some reason the FormatArgument function contains constexpr
|
||||||
@@ -604,3 +604,14 @@ TEST_F(FormattingTests, format_number_comma2dp32_large_value_negative)
|
|||||||
// the necessary symbol to link with.
|
// the necessary symbol to link with.
|
||||||
// FormatArgument<double>(ss, FormatToken::Comma1dp16, 12.372);
|
// FormatArgument<double>(ss, FormatToken::Comma1dp16, 12.372);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(FormattingTests, buffer_storage_swap)
|
||||||
|
{
|
||||||
|
FormatBufferBase<char, 16> ss;
|
||||||
|
ss << "Hello World";
|
||||||
|
ASSERT_STREQ(ss.data(), "Hello World");
|
||||||
|
ss << ", Exceeding local storage";
|
||||||
|
ASSERT_STREQ(ss.data(), "Hello World, Exceeding local storage");
|
||||||
|
ss << ", extended";
|
||||||
|
ASSERT_STREQ(ss.data(), "Hello World, Exceeding local storage, extended");
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user