1
0
Fork 0

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 <jaeckel-floss@eyet-services.de>
This commit is contained in:
Steffen Jaeckel 2023-08-29 10:37:10 +02:00
parent b36562c6b6
commit d3b6ebb1d7
1 changed files with 35 additions and 18 deletions

View File

@ -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;
}