diff --git a/Source/Core/StringUtilities.cpp b/Source/Core/StringUtilities.cpp index 63878115b..e04e397cf 100644 --- a/Source/Core/StringUtilities.cpp +++ b/Source/Core/StringUtilities.cpp @@ -37,23 +37,36 @@ namespace Rml { -static int FormatString(String& string, size_t max_size, const char* format, va_list argument_list) +static int FormatString(String& string, const char* format, va_list argument_list) { - const int INTERNAL_BUFFER_SIZE = 1024; - static char buffer[INTERNAL_BUFFER_SIZE]; + constexpr size_t InternalBufferSize = 256; + char buffer[InternalBufferSize]; char* buffer_ptr = buffer; - if (max_size + 1 > INTERNAL_BUFFER_SIZE) - buffer_ptr = new char[max_size + 1]; + size_t max_size = InternalBufferSize; + int length = 0; - int length = vsnprintf(buffer_ptr, max_size, format, argument_list); - buffer_ptr[length >= 0 ? length : max_size] = '\0'; -#ifdef RMLUI_DEBUG - if (length == -1) + for (int i = 0; i < 2; i++) { - Log::Message(Log::LT_WARNING, "FormatString: String truncated to %zu bytes when processing %s", max_size, format); + va_list argument_list_copy; + va_copy(argument_list_copy, argument_list); + + length = vsnprintf(buffer_ptr, max_size, format, argument_list_copy); + + va_end(argument_list_copy); + + if (length < 0) + { + RMLUI_ERRORMSG("Error while formatting string"); + return 0; + } + + if ((size_t)length < max_size || i > 0) + break; + + max_size = (size_t)length + 1; + buffer_ptr = new char[max_size]; } -#endif string = buffer_ptr; @@ -63,21 +76,20 @@ static int FormatString(String& string, size_t max_size, const char* format, va_ return length; } -int FormatString(String& string, size_t max_size, const char* format, ...) +int FormatString(String& string, size_t /*max_size*/, const char* format, ...) { va_list argument_list; va_start(argument_list, format); - int result = FormatString(string, (int)max_size, format, argument_list); + int result = FormatString(string, format, argument_list); va_end(argument_list); return result; } -String CreateString(size_t max_size, const char* format, ...) +String CreateString(size_t /*max_size*/, const char* format, ...) { String result; - result.reserve(max_size); va_list argument_list; va_start(argument_list, format); - FormatString(result, max_size, format, argument_list); + FormatString(result, format, argument_list); va_end(argument_list); return result; } @@ -164,7 +176,7 @@ String StringUtilities::DecodeRml(const String& s) size_t j = 0; for (; j < 8; j++) { - auto const& c = s[start + j]; + const auto& c = s[start + j]; if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) break; } @@ -188,7 +200,7 @@ String StringUtilities::DecodeRml(const String& s) size_t j = 0; for (; j < 8; j++) { - auto const& c = s[start + j]; + const auto& c = s[start + j]; if (!(c >= '0' && c <= '9')) break; } diff --git a/Tests/Source/UnitTests/StringUtilities.cpp b/Tests/Source/UnitTests/StringUtilities.cpp index 08b956803..499ef4844 100644 --- a/Tests/Source/UnitTests/StringUtilities.cpp +++ b/Tests/Source/UnitTests/StringUtilities.cpp @@ -148,3 +148,36 @@ TEST_CASE("StringUtilities::ConvertCharacterOffsetToByteOffset") CHECK(ConvertCharacterOffsetToByteOffset("a\xE2\x82\xAC" "b", 4) == 5); // clang-format on } + +TEST_CASE("CreateString") +{ + CHECK(Rml::CreateString(0, "Hello %s!", "world") == "Hello world!"); + CHECK(Rml::CreateString(0, "%g, %d, %.2f", 0.5f, 5, 2.f) == "0.5, 5, 2.00"); + + constexpr int InternalBufferSize = 256; + for (int string_size : {InternalBufferSize - 1, InternalBufferSize, InternalBufferSize + 1}) + { + Rml::String large_string(string_size, 'x'); + CHECK(Rml::CreateString(0, "%s", large_string.c_str()) == large_string); + } +} + +TEST_CASE("FormatString") +{ + { + Rml::String result; + int length = Rml::FormatString(result, 0, "Hello %s!", "world"); + CHECK(result == "Hello world!"); + CHECK(length == 12); + } + + constexpr int InternalBufferSize = 256; + for (int string_size : {InternalBufferSize - 1, InternalBufferSize, InternalBufferSize + 1}) + { + const Rml::String large_string(string_size, 'x'); + Rml::String result; + int length = Rml::FormatString(result, 0, "%s", large_string.c_str()); + CHECK(result == large_string); + CHECK(length == string_size); + } +}