Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

StringBuilder: use Span.Fill in Append repeating char #86287

Merged
merged 5 commits into from
Jul 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -659,36 +659,57 @@ public StringBuilder Append(char value, int repeatCount)
return this;
}

// this is where we can check if the repeatCount will put us over m_MaxCapacity
// We are doing the check here to prevent the corruption of the StringBuilder.
int newLength = Length + repeatCount;
if (newLength > m_MaxCapacity || newLength < repeatCount)
char[] chunkChars = m_ChunkChars;
int chunkLength = m_ChunkLength;

// Try to fit the whole repeatCount in the current chunk
// Use the same check as Span<T>.Slice for 64-bit so it can be folded
// Since repeatCount can't be negative, there's no risk for it to overflow on 32 bit
if (((nuint)(uint)chunkLength + (nuint)(uint)repeatCount) <= (nuint)(uint)chunkChars.Length)
{
throw new ArgumentOutOfRangeException(nameof(repeatCount), SR.ArgumentOutOfRange_LengthGreaterThanCapacity);
chunkChars.AsSpan(chunkLength, repeatCount).Fill(value);
m_ChunkLength += repeatCount;
}

int index = m_ChunkLength;
while (repeatCount > 0)
else
{
if (index < m_ChunkChars.Length)
{
m_ChunkChars[index++] = value;
--repeatCount;
}
else
{
m_ChunkLength = index;
ExpandByABlock(repeatCount);
Debug.Assert(m_ChunkLength == 0);
index = 0;
}
AppendWithExpansion(value, repeatCount);
}

m_ChunkLength = index;
AssertInvariants();
return this;
}

private void AppendWithExpansion(char value, int repeatCount)
{
Debug.Assert(repeatCount > 0, "Invalid length; should have been validated by caller.");
yesmey marked this conversation as resolved.
Show resolved Hide resolved

// Check if the repeatCount will put us over m_MaxCapacity
if ((uint)(repeatCount + Length) > (uint)m_MaxCapacity)
yesmey marked this conversation as resolved.
Show resolved Hide resolved
{
throw new ArgumentOutOfRangeException(nameof(repeatCount), SR.ArgumentOutOfRange_LengthGreaterThanCapacity);
}

char[] chunkChars = m_ChunkChars;
int chunkLength = m_ChunkLength;

// Fill the rest of the current chunk
int firstLength = chunkChars.Length - chunkLength;
if (firstLength > 0)
{
chunkChars.AsSpan(chunkLength, firstLength).Fill(value);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Insert

Debug.Assert(firstLength < repeatCount, "We shouldn't be called if there was enough space for the entire run.");

m_ChunkLength = chunkChars.Length;
}
yesmey marked this conversation as resolved.
Show resolved Hide resolved

// Expand the builder to add another chunk
int restLength = repeatCount - firstLength;
yesmey marked this conversation as resolved.
Show resolved Hide resolved
ExpandByABlock(restLength);
Debug.Assert(m_ChunkLength == 0, "A new block was not created.");

// Fill the new chunk with the remaining part of repeatCount
m_ChunkChars.AsSpan(0, restLength).Fill(value);
m_ChunkLength = restLength;
}

/// <summary>
/// Appends a range of characters to the end of this builder.
/// </summary>
Expand Down Expand Up @@ -990,12 +1011,21 @@ public StringBuilder Append(char value)
}
else
{
Append(value, 1);
AppendWithExpansion(value);
}

return this;
}

[MethodImpl(MethodImplOptions.NoInlining)]
yesmey marked this conversation as resolved.
Show resolved Hide resolved
private void AppendWithExpansion(char value)
{
ExpandByABlock(1);
Debug.Assert(m_ChunkLength == 0, "A new block was not created.");
m_ChunkChars[0] = value;
m_ChunkLength++;
}

[CLSCompliant(false)]
public StringBuilder Append(sbyte value) => AppendSpanFormattable(value);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -550,7 +550,7 @@ public static void Append_Char_NoSpareCapacity_ThrowsArgumentOutOfRangeException
var builder = new StringBuilder(0, 5);
builder.Append("Hello");

AssertExtensions.Throws<ArgumentOutOfRangeException>("repeatCount", "requiredLength", () => builder.Append('a'));
AssertExtensions.Throws<ArgumentOutOfRangeException>("requiredLength", () => builder.Append('a'));
AssertExtensions.Throws<ArgumentOutOfRangeException>("repeatCount", "requiredLength", () => builder.Append('a', 1));
}

Expand Down