Skip to content

Commit

Permalink
Less allocations in str_replace()
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
sjaeckel committed Sep 1, 2023
1 parent b36562c commit d3b6ebb
Showing 1 changed file with 34 additions and 17 deletions.
51 changes: 34 additions & 17 deletions src/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -174,36 +174,53 @@ 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;

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

while ((tok = strstr(head, substr))) {
auto_char char* oldstr = newstr;
newstr = malloc(strlen(oldstr) - strlen(substr) + strlen(replacement) + 1);
if (num_substr == 0)
return strdup(string);

if (newstr == NULL) {
return NULL;
}
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;

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 = string;
wr = newstr;

head = newstr + (tok - oldstr) + strlen(replacement);
while ((tok = strstr(head, substr))) {
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;

head = tok + len_substr;
}
memcpy(wr, head, len_string_remains);
newstr[len_newstr] = '\0';

return newstr;
}
Expand Down

0 comments on commit d3b6ebb

Please sign in to comment.