diff --git a/libs/utils/src/CString.cpp b/libs/utils/src/CString.cpp index d515d02457..34a998cb23 100644 --- a/libs/utils/src/CString.cpp +++ b/libs/utils/src/CString.cpp @@ -77,9 +77,24 @@ CString& CString::replace(size_type const pos, size_type len, char const* str, s len = std::min(len, size() - pos); - // The new size of the string, after the replacement. const size_type newSize = size() - len + l; + // if the new string is not longer, we can do it in-place, which is much faster. + if (newSize <= size()) { + if (mCStr) { + // This is equivalent to l <= len because capacity() == size() + // move the tail of the string. +1 to move the null-terminator + std::copy(mCStr + pos + len, mCStr + size() + 1, mCStr + pos + l); + // copy the new content + std::copy_n(str, l, mCStr + pos); + // update the size + (mData - 1)->length = newSize; + } + // if mCStr is null, newSize<=size() implies l<=len and since size is 0, len is 0, so l is 0. + // so we're replacing nothing with nothing and there is nothing to do. + return *this; + } + // Allocate enough memory to hold the new string. Data* const p = static_cast(std::malloc(sizeof(Data) + newSize + 1)); assert(p); diff --git a/libs/utils/test/test_CString.cpp b/libs/utils/test/test_CString.cpp index e37861aa92..ab5d12c41a 100644 --- a/libs/utils/test/test_CString.cpp +++ b/libs/utils/test/test_CString.cpp @@ -477,6 +477,62 @@ TEST(CString, Replace) { } } +TEST(CString, ReplaceInPlace) { + // Shrinking replacement + { + CString str("0123456789"); + const char* const original_cstr = str.c_str(); + str.replace(3, 3, "ab"); + EXPECT_STREQ("012ab6789", str.c_str()); + EXPECT_EQ(9, str.length()); + EXPECT_EQ(original_cstr, str.c_str()); + } + { + CString str("0123456789"); + const char* const original_cstr = str.c_str(); + str.replace(0, 3, "ab"); + EXPECT_STREQ("ab3456789", str.c_str()); + EXPECT_EQ(9, str.length()); + EXPECT_EQ(original_cstr, str.c_str()); + } + { + CString str("0123456789"); + const char* const original_cstr = str.c_str(); + str.replace(7, 3, "ab"); + EXPECT_STREQ("0123456ab", str.c_str()); + EXPECT_EQ(9, str.length()); + EXPECT_EQ(original_cstr, str.c_str()); + } + { + CString str("0123456789"); + const char* const original_cstr = str.c_str(); + str.replace(0, 10, "ab"); + EXPECT_STREQ("ab", str.c_str()); + EXPECT_EQ(2, str.length()); + EXPECT_EQ(original_cstr, str.c_str()); + } + + // Same size replacement + { + CString str("0123456789"); + const char* const original_cstr = str.c_str(); + str.replace(0, 10, "abcdefghij"); + EXPECT_STREQ("abcdefghij", str.c_str()); + EXPECT_EQ(10, str.length()); + EXPECT_EQ(original_cstr, str.c_str()); + } + + // Shrink to empty + { + CString str("0123456789"); + const char* const original_cstr = str.c_str(); + str.replace(3, 3, ""); + EXPECT_STREQ("0126789", str.c_str()); + EXPECT_EQ(7, str.length()); + EXPECT_EQ(original_cstr, str.c_str()); + } +} + TEST(CString, ReplaceZeroLength) { { std::string str("foobar");