Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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,58 @@ 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;
}

[MethodImpl(MethodImplOptions.NoInlining)]
private void AppendWithExpansion(char value, int repeatCount)
{
Debug.Assert(repeatCount > 0, "Invalid length; should have been validated by caller.");

// Check if the repeatCount will put us over m_MaxCapacity
if ((uint)(repeatCount + Length) > (uint)m_MaxCapacity)
{
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;
}

// Expand the builder to add another chunk
int restLength = repeatCount - firstLength;
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 +1012,21 @@ public StringBuilder Append(char value)
}
else
{
Append(value, 1);
AppendWithExpansion(value);
}

return this;
}

[MethodImpl(MethodImplOptions.NoInlining)]
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