From d6d1fd32fb90e9adafa8edc6b281c169b4a0689d Mon Sep 17 00:00:00 2001 From: Rokas Kupstys <19151258+rokups@users.noreply.github.com> Date: Wed, 16 Jun 2021 12:13:47 +0300 Subject: [PATCH 1/3] Add Str::replace(find, repl) function. --- Str.h | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/Str.h b/Str.h index 6d3fbee..93b83b3 100644 --- a/Str.h +++ b/Str.h @@ -161,6 +161,7 @@ class STR_API Str int append_from(int idx, const char* s, const char* s_end = NULL); // If you know the string length or want to append from a certain point int appendf_from(int idx, const char* fmt, ...); int appendfv_from(int idx, const char* fmt, va_list args); + void replace(const char* find, const char* repl); void clear(); void reserve(int cap); @@ -651,6 +652,49 @@ int Str::appendf(const char* fmt, ...) return len; } +void Str::replace(const char* find, const char* repl) +{ + STR_ASSERT(Owned == 1); + STR_ASSERT(find != NULL && *find); + STR_ASSERT(repl != NULL); + int find_len = (int)strlen(find); + int repl_len = (int)strlen(repl); + int repl_diff = repl_len - find_len; + + // Estimate required length of new buffer if string size increases. + if (repl_diff > 0) + { + int need_capacity = length(); + for (char* p = Data, *end = Data + length(); p != NULL && p < end;) + { + p = (char*)memmem(p, end - p, find, find_len); + if (p) + { + need_capacity += repl_diff; + p += find_len; + } + } + if (need_capacity > Capacity) + { + reserve(need_capacity); + STR_ASSERT(Capacity >= need_capacity); + } + } + + // Replace data. + for (char* p = Data, *end = Data + length(); p != NULL && p < end;) + { + p = (char*)memmem(p, end - p, find, find_len); + if (p) + { + memmove(p + repl_len, p + find_len, end - p - find_len + 1); + memcpy(p, repl, repl_len); + p += repl_len; + end += repl_diff; + } + } +} + #endif // #define STR_IMPLEMENTATION //------------------------------------------------------------------------- From 39c8e1b9f08159a7e24cda405d40c854bdc3f126 Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Fri, 18 Jun 2021 12:52:12 +0300 Subject: [PATCH 2/3] [SQUASH] Take ownership of non-owned string when replacing and fix allocating too small buffer when buffer resize is needed. --- Str.h | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/Str.h b/Str.h index 93b83b3..aa96ec6 100644 --- a/Str.h +++ b/Str.h @@ -652,9 +652,8 @@ int Str::appendf(const char* fmt, ...) return len; } -void Str::replace(const char* find, const char* repl) +void Str::replace(const char* find, const char* repl) { - STR_ASSERT(Owned == 1); STR_ASSERT(find != NULL && *find); STR_ASSERT(repl != NULL); int find_len = (int)strlen(find); @@ -662,9 +661,10 @@ void Str::replace(const char* find, const char* repl) int repl_diff = repl_len - find_len; // Estimate required length of new buffer if string size increases. + int need_capacity = Capacity; if (repl_diff > 0) { - int need_capacity = length(); + need_capacity = length() + 1; for (char* p = Data, *end = Data + length(); p != NULL && p < end;) { p = (char*)memmem(p, end - p, find, find_len); @@ -674,13 +674,14 @@ void Str::replace(const char* find, const char* repl) p += find_len; } } - if (need_capacity > Capacity) - { - reserve(need_capacity); - STR_ASSERT(Capacity >= need_capacity); - } } + const char* not_owned_data = Owned ? NULL : Data; + if (!Owned || need_capacity > Capacity) + reserve(need_capacity); + if (not_owned_data != NULL) + set(not_owned_data); + // Replace data. for (char* p = Data, *end = Data + length(); p != NULL && p < end;) { From e7f8701757b9762f6b79e08313209ffa480a57bb Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Fri, 18 Jun 2021 13:13:25 +0300 Subject: [PATCH 3/3] [SQUASH] Attempt early out from replacement loop when possible. --- Str.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Str.h b/Str.h index aa96ec6..3a2c7bb 100644 --- a/Str.h +++ b/Str.h @@ -662,8 +662,10 @@ void Str::replace(const char* find, const char* repl) // Estimate required length of new buffer if string size increases. int need_capacity = Capacity; + int num_matches = INT_MAX; if (repl_diff > 0) { + num_matches = 0; need_capacity = length() + 1; for (char* p = Data, *end = Data + length(); p != NULL && p < end;) { @@ -672,10 +674,14 @@ void Str::replace(const char* find, const char* repl) { need_capacity += repl_diff; p += find_len; + num_matches++; } } } + if (num_matches == 0) + return; + const char* not_owned_data = Owned ? NULL : Data; if (!Owned || need_capacity > Capacity) reserve(need_capacity); @@ -683,7 +689,7 @@ void Str::replace(const char* find, const char* repl) set(not_owned_data); // Replace data. - for (char* p = Data, *end = Data + length(); p != NULL && p < end;) + for (char* p = Data, *end = Data + length(); p != NULL && p < end && num_matches--;) { p = (char*)memmem(p, end - p, find, find_len); if (p)