From 9911c6e07c92bfcaf3b429882d38d00d06cbc644 Mon Sep 17 00:00:00 2001 From: Lynix Date: Sun, 15 Oct 2017 12:25:08 +0200 Subject: [PATCH] Core: Optimize StringStream --- include/Nazara/Core/StringStream.hpp | 7 +- src/Nazara/Core/StringStream.cpp | 147 +++++++++++++++------------ 2 files changed, 87 insertions(+), 67 deletions(-) diff --git a/include/Nazara/Core/StringStream.hpp b/include/Nazara/Core/StringStream.hpp index 6898e5b6a..baabda3b0 100644 --- a/include/Nazara/Core/StringStream.hpp +++ b/include/Nazara/Core/StringStream.hpp @@ -17,8 +17,8 @@ namespace Nz class NAZARA_CORE_API StringStream { public: - StringStream(); - StringStream(const String& str); + StringStream() = default; + StringStream(String str); StringStream(const StringStream&) = default; StringStream(StringStream&&) noexcept = default; @@ -53,8 +53,7 @@ namespace Nz operator String() const; private: - std::vector m_strings; - std::size_t m_bufferSize; + String m_result; }; } diff --git a/src/Nazara/Core/StringStream.cpp b/src/Nazara/Core/StringStream.cpp index d39b3afb8..06a6e03c4 100644 --- a/src/Nazara/Core/StringStream.cpp +++ b/src/Nazara/Core/StringStream.cpp @@ -3,6 +3,9 @@ // For conditions of distribution and use, see copyright notice in Config.hpp #include +#include +#include +#include #include namespace Nz @@ -13,23 +16,14 @@ namespace Nz * \brief Core class that represents a stream of strings */ - /*! - * \brief Constructs a StringStream object by default - */ - StringStream::StringStream() : - m_bufferSize(0) - { - } - /*! * \brief Constructs a StringStream object with a string * * \param str First value of the stream */ - StringStream::StringStream(const String& str) : - m_bufferSize(str.GetSize()) + StringStream::StringStream(String str) : + m_result(std::move(str)) { - m_strings.push_back(str); } /*! @@ -37,8 +31,7 @@ namespace Nz */ void StringStream::Clear() { - m_bufferSize = 0; - m_strings.clear(); + m_result.Clear(); } /*! @@ -47,7 +40,7 @@ namespace Nz */ std::size_t StringStream::GetBufferSize() const { - return m_bufferSize; + return m_result.GetSize(); } /*! @@ -56,13 +49,7 @@ namespace Nz */ String StringStream::ToString() const { - String string; - string.Reserve(m_bufferSize); - - for (const String& str : m_strings) - string += str; - - return string; + return m_result; } /*! @@ -73,8 +60,10 @@ namespace Nz */ StringStream& StringStream::operator<<(bool boolean) { - m_strings.push_back(String::Boolean(boolean)); - m_bufferSize += m_strings.back().GetSize(); + std::size_t size = (boolean) ? 4 : 5; + std::size_t start = m_result.GetSize(); + m_result.Resize(start + size); + std::memcpy(&m_result[start], (boolean) ? "true" : "false", size); return *this; } @@ -87,8 +76,11 @@ namespace Nz */ StringStream& StringStream::operator<<(short number) { - m_strings.push_back(String::Number(number)); - m_bufferSize += m_strings.back().GetSize(); + constexpr std::size_t maxSize = std::numeric_limits::digits10 + 2; + std::size_t start = m_result.GetSize(); + m_result.Resize(start + maxSize); + std::size_t written = std::snprintf(&m_result[start], maxSize + 1, "%hd", number); + m_result.Resize(start + written); return *this; } @@ -101,8 +93,11 @@ namespace Nz */ StringStream& StringStream::operator<<(unsigned short number) { - m_strings.push_back(String::Number(number)); - m_bufferSize += m_strings.back().GetSize(); + constexpr std::size_t maxSize = std::numeric_limits::digits10 + 1; + std::size_t start = m_result.GetSize(); + m_result.Resize(start + maxSize); + std::size_t written = std::snprintf(&m_result[start], maxSize + 1, "%hu", number); + m_result.Resize(start + written); return *this; } @@ -115,8 +110,11 @@ namespace Nz */ StringStream& StringStream::operator<<(int number) { - m_strings.push_back(String::Number(number)); - m_bufferSize += m_strings.back().GetSize(); + constexpr std::size_t maxSize = std::numeric_limits::digits10 + 2; + std::size_t start = m_result.GetSize(); + m_result.Resize(start + maxSize); + std::size_t written = std::snprintf(&m_result[start], maxSize + 1, "%d", number); + m_result.Resize(start + written); return *this; } @@ -129,8 +127,11 @@ namespace Nz */ StringStream& StringStream::operator<<(unsigned int number) { - m_strings.push_back(String::Number(number)); - m_bufferSize += m_strings.back().GetSize(); + constexpr std::size_t maxSize = std::numeric_limits::digits10 + 1; + std::size_t start = m_result.GetSize(); + m_result.Resize(start + maxSize); + std::size_t written = std::snprintf(&m_result[start], maxSize + 1, "%u", number); + m_result.Resize(start + written); return *this; } @@ -143,8 +144,11 @@ namespace Nz */ StringStream& StringStream::operator<<(long number) { - m_strings.push_back(String::Number(number)); - m_bufferSize += m_strings.back().GetSize(); + constexpr std::size_t maxSize = std::numeric_limits::digits10 + 2; + std::size_t start = m_result.GetSize(); + m_result.Resize(start + maxSize); + std::size_t written = std::snprintf(&m_result[start], maxSize + 1, "%ld", number); + m_result.Resize(start + written); return *this; } @@ -157,8 +161,12 @@ namespace Nz */ StringStream& StringStream::operator<<(unsigned long number) { - m_strings.push_back(String::Number(number)); - m_bufferSize += m_strings.back().GetSize(); + constexpr std::size_t maxSize = std::numeric_limits::digits10 + 1; + std::size_t start = m_result.GetSize(); + m_result.Resize(start + maxSize); + std::size_t written = std::snprintf(&m_result[start], maxSize + 1, "%lu", number); + m_result.Resize(start + written); + return *this; } @@ -171,8 +179,11 @@ namespace Nz */ StringStream& StringStream::operator<<(long long number) { - m_strings.push_back(String::Number(number)); - m_bufferSize += m_strings.back().GetSize(); + constexpr std::size_t maxSize = std::numeric_limits::digits10 + 2; + std::size_t start = m_result.GetSize(); + m_result.Resize(start + maxSize); + std::size_t written = std::snprintf(&m_result[start], maxSize + 1, "%lld", number); + m_result.Resize(start + written); return *this; } @@ -185,8 +196,11 @@ namespace Nz */ StringStream& StringStream::operator<<(unsigned long long number) { - m_strings.push_back(String::Number(number)); - m_bufferSize += m_strings.back().GetSize(); + constexpr std::size_t maxSize = std::numeric_limits::digits10 + 1; + std::size_t start = m_result.GetSize(); + m_result.Resize(start + maxSize); + std::size_t written = std::snprintf(&m_result[start], maxSize + 1, "%llu", number); + m_result.Resize(start + written); return *this; } @@ -199,10 +213,7 @@ namespace Nz */ StringStream& StringStream::operator<<(float number) { - m_strings.push_back(String::Number(number)); - m_bufferSize += m_strings.back().GetSize(); - - return *this; + return operator<<(double(number)); //< snprintf doesn't support float anyway } /*! @@ -213,8 +224,16 @@ namespace Nz */ StringStream& StringStream::operator<<(double number) { - m_strings.push_back(String::Number(number)); - m_bufferSize += m_strings.back().GetSize(); + // https://stackoverflow.com/questions/1701055/what-is-the-maximum-length-in-chars-needed-to-represent-any-double-value + const std::size_t maxSize = 3 + std::numeric_limits::digits - std::numeric_limits::min_exponent; + + // Use a temporary buffer to prevent 1kb string capacity growth + std::array buffer; + std::size_t written = std::snprintf(buffer.data(), buffer.size(), "%f", number); + + std::size_t start = m_result.GetSize(); + m_result.Resize(start + written); + std::memcpy(&m_result[start], buffer.data(), written); return *this; } @@ -227,8 +246,16 @@ namespace Nz */ StringStream& StringStream::operator<<(long double number) { - m_strings.push_back(String::Number(number)); - m_bufferSize += m_strings.back().GetSize(); + // https://stackoverflow.com/questions/1701055/what-is-the-maximum-length-in-chars-needed-to-represent-any-double-value + const std::size_t maxSize = 3 + std::numeric_limits::digits - std::numeric_limits::min_exponent; + + // Use a temporary buffer to prevent 1kb string capacity growth + std::array buffer; + std::size_t written = std::snprintf(buffer.data(), buffer.size(), "%f", number); + + std::size_t start = m_result.GetSize(); + m_result.Resize(start + written); + std::memcpy(&m_result[start], buffer.data(), written); return *this; } @@ -241,9 +268,7 @@ namespace Nz */ StringStream& StringStream::operator<<(char character) { - m_strings.push_back(String(character)); - m_bufferSize++; - + m_result.Append(character); return *this; } @@ -255,9 +280,7 @@ namespace Nz */ StringStream& StringStream::operator<<(unsigned char character) { - m_strings.push_back(String(static_cast(character))); - m_bufferSize++; - + m_result.Append(static_cast(character)); return *this; } @@ -269,9 +292,7 @@ namespace Nz */ StringStream& StringStream::operator<<(const char* string) { - m_strings.push_back(string); - m_bufferSize += m_strings.back().GetSize(); - + m_result.Append(string); return *this; } @@ -283,9 +304,7 @@ namespace Nz */ StringStream& StringStream::operator<<(const std::string& string) { - m_strings.push_back(string); - m_bufferSize += string.size(); - + m_result.Append(string.data(), string.size()); return *this; } @@ -297,8 +316,7 @@ namespace Nz */ StringStream& StringStream::operator<<(const String& string) { - m_strings.push_back(string); - m_bufferSize += string.GetSize(); + m_result.Append(string); return *this; } @@ -311,8 +329,11 @@ namespace Nz */ StringStream& StringStream::operator<<(const void* ptr) { - m_strings.push_back(String::Pointer(ptr)); - m_bufferSize += m_strings.back().GetSize(); + constexpr std::size_t maxSize = sizeof(void*) * 2 + 2; + std::size_t start = m_result.GetSize(); + m_result.Resize(start + maxSize); + std::size_t written = std::snprintf(&m_result[start], maxSize + 1, "0x%p", ptr); + m_result.Resize(start + written); return *this; }