Skip to content

Commit fc3787d

Browse files
authored
Avoid a small int[] allocation in StringBuilder.Replace (#60406)
1 parent 862a90f commit fc3787d

File tree

1 file changed

+10
-15
lines changed

1 file changed

+10
-15
lines changed

src/libraries/System.Private.CoreLib/src/System/Text/StringBuilder.cs

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1970,7 +1970,7 @@ public StringBuilder Replace(string oldValue, string? newValue, int startIndex,
19701970

19711971
newValue ??= string.Empty;
19721972

1973-
int[]? replacements = null; // A list of replacement positions in a chunk to apply
1973+
Span<int> replacements = stackalloc int[5]; // A list of replacement positions in a chunk to apply
19741974
int replacementsCount = 0;
19751975

19761976
// Find the chunk, indexInChunk for the starting point
@@ -1985,13 +1985,11 @@ public StringBuilder Replace(string oldValue, string? newValue, int startIndex,
19851985
// Push it on the replacements array (with growth), we will do all replacements in a
19861986
// given chunk in one operation below (see ReplaceAllInChunk) so we don't have to slide
19871987
// many times.
1988-
if (replacements == null)
1988+
if (replacementsCount >= replacements.Length)
19891989
{
1990-
replacements = new int[5];
1991-
}
1992-
else if (replacementsCount >= replacements.Length)
1993-
{
1994-
Array.Resize(ref replacements, replacements.Length * 3 / 2 + 4); // Grow by ~1.5x, but more in the beginning
1990+
int[] tmp = new int[replacements.Length * 3 / 2 + 4]; // Grow by ~1.5x, but more in the beginning
1991+
replacements.CopyTo(tmp);
1992+
replacements = tmp;
19951993
}
19961994
replacements[replacementsCount++] = indexInChunk;
19971995
indexInChunk += oldValue.Length;
@@ -2009,8 +2007,7 @@ public StringBuilder Replace(string oldValue, string? newValue, int startIndex,
20092007
int index = indexInChunk + chunk.m_ChunkOffset;
20102008

20112009
// See if we accumulated any replacements, if so apply them.
2012-
Debug.Assert(replacements != null || replacementsCount == 0, "replacements was null and replacementsCount != 0");
2013-
ReplaceAllInChunk(replacements, replacementsCount, chunk, oldValue.Length, newValue);
2010+
ReplaceAllInChunk(replacements.Slice(0, replacementsCount), chunk, oldValue.Length, newValue);
20142011
// The replacement has affected the logical index. Adjust it.
20152012
index += ((newValue.Length - oldValue.Length) * replacementsCount);
20162013
replacementsCount = 0;
@@ -2162,16 +2159,15 @@ private unsafe void Insert(int index, char* value, int valueCount)
21622159
/// Replaces strings at specified indices with a new string in a chunk.
21632160
/// </summary>
21642161
/// <param name="replacements">The list of indices, relative to the beginning of the chunk, to remove at.</param>
2165-
/// <param name="replacementsCount">The number of replacements to make.</param>
21662162
/// <param name="sourceChunk">The source chunk.</param>
21672163
/// <param name="removeCount">The number of characters to remove at each replacement.</param>
21682164
/// <param name="value">The string to insert at each replacement.</param>
21692165
/// <remarks>
21702166
/// This routine is very efficient because it does replacements in bulk.
21712167
/// </remarks>
2172-
private void ReplaceAllInChunk(int[]? replacements, int replacementsCount, StringBuilder sourceChunk, int removeCount, string value)
2168+
private void ReplaceAllInChunk(ReadOnlySpan<int> replacements, StringBuilder sourceChunk, int removeCount, string value)
21732169
{
2174-
if (replacementsCount <= 0)
2170+
if (replacements.IsEmpty)
21752171
{
21762172
return;
21772173
}
@@ -2180,9 +2176,8 @@ private void ReplaceAllInChunk(int[]? replacements, int replacementsCount, Strin
21802176
{
21812177
fixed (char* valuePtr = value)
21822178
{
2183-
Debug.Assert(replacements != null, "replacements was null when replacementsCount > 0");
21842179
// calculate the total amount of extra space or space needed for all the replacements.
2185-
long longDelta = (value.Length - removeCount) * (long)replacementsCount;
2180+
long longDelta = (value.Length - removeCount) * (long)replacements.Length;
21862181
int delta = (int)longDelta;
21872182
if (delta != longDelta)
21882183
{
@@ -2206,7 +2201,7 @@ private void ReplaceAllInChunk(int[]? replacements, int replacementsCount, Strin
22062201
ReplaceInPlaceAtChunk(ref targetChunk!, ref targetIndexInChunk, valuePtr, value.Length);
22072202
int gapStart = replacements[i] + removeCount;
22082203
i++;
2209-
if (i >= replacementsCount)
2204+
if ((uint)i >= replacements.Length)
22102205
{
22112206
break;
22122207
}

0 commit comments

Comments
 (0)