From d3b6ebb1d7cfd4f970977c72dc54455e7ef68a91 Mon Sep 17 00:00:00 2001 From: Steffen Jaeckel Date: Tue, 29 Aug 2023 10:37:10 +0200 Subject: [PATCH] Less allocations in `str_replace()` If `n` is the number of `substr` replace operations, the old implementation did `n+1` allocations and always first copied start, then replacement, then the remaining (unsearched) part of the original string. Now calculate the number of occurences of `substr` before allocating the resulting string. Change the algorithm to parse through the original string and only copy the parts that must be kept into the resulting string plus the replacements where they belong at. Now we also only have to go through the original string twice plus we only do a single allocation. Signed-off-by: Steffen Jaeckel --- src/common.c | 53 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/src/common.c b/src/common.c index 3924a260..5d6a1125 100644 --- a/src/common.c +++ b/src/common.c @@ -174,9 +174,12 @@ char* str_replace(const char* string, const char* substr, const char* replacement) { - char* tok = NULL; + const char* head = NULL; + const char* tok = NULL; char* newstr = NULL; - char* head = NULL; + char* wr = NULL; + size_t num_substr = 0; + size_t len_string, len_substr, len_replacement, len_string_remains, len_newstr; if (string == NULL) return NULL; @@ -184,26 +187,40 @@ str_replace(const char* string, const char* substr, if (substr == NULL || replacement == NULL || (strcmp(substr, "") == 0)) return strdup(string); - newstr = strdup(string); - head = newstr; + tok = string; + while ((tok = strstr(tok, substr))) { + num_substr++; + tok++; + } + + if (num_substr == 0) + return strdup(string); + + len_string = strlen(string); + len_substr = strlen(substr); + len_replacement = strlen(replacement); + len_newstr = len_string - (num_substr * len_substr) + (num_substr * len_replacement); + newstr = malloc(len_newstr + 1); + if (newstr == NULL) { + return NULL; + } + len_string_remains = len_string; + + head = string; + wr = newstr; while ((tok = strstr(head, substr))) { - auto_char char* oldstr = newstr; - newstr = malloc(strlen(oldstr) - strlen(substr) + strlen(replacement) + 1); + size_t l = tok - head; + memcpy(wr, head, l); + wr += l; + memcpy(wr, replacement, len_replacement); + wr += len_replacement; + len_string_remains -= len_substr + l; - if (newstr == NULL) { - return NULL; - } - - memcpy(newstr, oldstr, tok - oldstr); - memcpy(newstr + (tok - oldstr), replacement, strlen(replacement)); - memcpy(newstr + (tok - oldstr) + strlen(replacement), - tok + strlen(substr), - strlen(oldstr) - strlen(substr) - (tok - oldstr)); - memset(newstr + strlen(oldstr) - strlen(substr) + strlen(replacement), 0, 1); - - head = newstr + (tok - oldstr) + strlen(replacement); + head = tok + len_substr; } + memcpy(wr, head, len_string_remains); + newstr[len_newstr] = '\0'; return newstr; }