mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-01-12 15:36:17 +01:00
Merge branch 'feature' into 'main'
remove quadratic behavior in g_string_replace See merge request GNOME/glib!4313
This commit is contained in:
commit
303f04286e
@ -1023,38 +1023,112 @@ g_string_replace (GString *string,
|
|||||||
const gchar *replace,
|
const gchar *replace,
|
||||||
guint limit)
|
guint limit)
|
||||||
{
|
{
|
||||||
gsize f_len, r_len, pos;
|
GString *new_string = NULL;
|
||||||
gchar *cur, *next;
|
gsize f_len, r_len, new_len;
|
||||||
guint n = 0;
|
gchar *cur, *next, *first, *dst;
|
||||||
|
guint n;
|
||||||
|
|
||||||
g_return_val_if_fail (string != NULL, 0);
|
g_return_val_if_fail (string != NULL, 0);
|
||||||
g_return_val_if_fail (find != NULL, 0);
|
g_return_val_if_fail (find != NULL, 0);
|
||||||
g_return_val_if_fail (replace != NULL, 0);
|
g_return_val_if_fail (replace != NULL, 0);
|
||||||
|
|
||||||
|
first = strstr (string->str, find);
|
||||||
|
|
||||||
|
if (first == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
new_len = string->len;
|
||||||
f_len = strlen (find);
|
f_len = strlen (find);
|
||||||
r_len = strlen (replace);
|
r_len = strlen (replace);
|
||||||
cur = string->str;
|
|
||||||
|
|
||||||
|
/* Potentially do two passes: the first to calculate the length of the new string,
|
||||||
|
* new_len, if it’s going to be longer than the original string; and the second to
|
||||||
|
* do the replacements. The first pass is skipped if the new string is going to be
|
||||||
|
* no longer than the original. */
|
||||||
|
do
|
||||||
|
{
|
||||||
|
dst = first;
|
||||||
|
cur = first;
|
||||||
|
n = 0;
|
||||||
while ((next = strstr (cur, find)) != NULL)
|
while ((next = strstr (cur, find)) != NULL)
|
||||||
{
|
{
|
||||||
pos = next - string->str;
|
|
||||||
g_string_erase (string, pos, f_len);
|
|
||||||
g_string_insert (string, pos, replace);
|
|
||||||
cur = string->str + pos + r_len;
|
|
||||||
n++;
|
n++;
|
||||||
|
if G_UNLIKELY (f_len == 0)
|
||||||
|
{
|
||||||
|
g_string_insert_len (string, next - string->str, replace, r_len);
|
||||||
|
cur = next + r_len;
|
||||||
/* Only match the empty string once at any given position, to
|
/* Only match the empty string once at any given position, to
|
||||||
* avoid infinite loops */
|
* avoid infinite loops */
|
||||||
if (f_len == 0)
|
|
||||||
{
|
|
||||||
if (cur[0] == '\0')
|
if (cur[0] == '\0')
|
||||||
break;
|
break;
|
||||||
else
|
else
|
||||||
cur++;
|
cur++;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (r_len <= f_len)
|
||||||
|
{
|
||||||
|
memmove (dst, cur, next - cur);
|
||||||
|
dst += next - cur;
|
||||||
|
memcpy (dst, replace, r_len);
|
||||||
|
dst += r_len;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (new_string == NULL)
|
||||||
|
{
|
||||||
|
new_len += r_len - f_len;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_string_append_len (new_string, cur, next - cur);
|
||||||
|
g_string_append_len (new_string, replace, r_len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cur = next + f_len;
|
||||||
|
}
|
||||||
if (n == limit)
|
if (n == limit)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* @find is an empty string. */
|
||||||
|
if (f_len == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Append the trailing characters from after the final instance of @find
|
||||||
|
* in the input string. */
|
||||||
|
if (r_len <= f_len)
|
||||||
|
{
|
||||||
|
/* First pass skipped. */
|
||||||
|
gchar *end = string->str + string->len;
|
||||||
|
memmove (dst, cur, end - cur);
|
||||||
|
end = dst + (end - cur);
|
||||||
|
*end = 0;
|
||||||
|
string->len = end - string->str;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (new_string == NULL)
|
||||||
|
{
|
||||||
|
/* First pass. */
|
||||||
|
new_string = g_string_sized_new (new_len);
|
||||||
|
g_string_append_len (new_string, string->str, first - string->str);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Second pass. */
|
||||||
|
g_string_append_len (new_string, cur, (string->str + string->len) - cur);
|
||||||
|
g_free (string->str);
|
||||||
|
string->allocated_len = new_string->allocated_len;
|
||||||
|
string->len = new_string->len;
|
||||||
|
string->str = g_string_free_and_steal (g_steal_pointer (&new_string));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (1);
|
||||||
|
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user