@@ -659,36 +659,57 @@ public StringBuilder Append(char value, int repeatCount)
659659 return this ;
660660 }
661661
662- // this is where we can check if the repeatCount will put us over m_MaxCapacity
663- // We are doing the check here to prevent the corruption of the StringBuilder.
664- int newLength = Length + repeatCount ;
665- if ( newLength > m_MaxCapacity || newLength < repeatCount )
662+ char [ ] chunkChars = m_ChunkChars ;
663+ int chunkLength = m_ChunkLength ;
664+
665+ // Try to fit the whole repeatCount in the current chunk
666+ // Use the same check as Span<T>.Slice for 64-bit so it can be folded
667+ // Since repeatCount can't be negative, there's no risk for it to overflow on 32 bit
668+ if ( ( ( nuint ) ( uint ) chunkLength + ( nuint ) ( uint ) repeatCount ) <= ( nuint ) ( uint ) chunkChars . Length )
666669 {
667- throw new ArgumentOutOfRangeException ( nameof ( repeatCount ) , SR . ArgumentOutOfRange_LengthGreaterThanCapacity ) ;
670+ chunkChars . AsSpan ( chunkLength , repeatCount ) . Fill ( value ) ;
671+ m_ChunkLength += repeatCount ;
668672 }
669-
670- int index = m_ChunkLength ;
671- while ( repeatCount > 0 )
673+ else
672674 {
673- if ( index < m_ChunkChars . Length )
674- {
675- m_ChunkChars [ index ++ ] = value ;
676- -- repeatCount ;
677- }
678- else
679- {
680- m_ChunkLength = index ;
681- ExpandByABlock ( repeatCount ) ;
682- Debug . Assert ( m_ChunkLength == 0 ) ;
683- index = 0 ;
684- }
675+ AppendWithExpansion ( value , repeatCount ) ;
685676 }
686677
687- m_ChunkLength = index ;
688678 AssertInvariants ( ) ;
689679 return this ;
690680 }
691681
682+ private void AppendWithExpansion ( char value , int repeatCount )
683+ {
684+ Debug . Assert ( repeatCount > 0 , "Invalid length; should have been validated by caller." ) ;
685+
686+ // Check if the repeatCount will put us over m_MaxCapacity
687+ if ( ( uint ) ( repeatCount + Length ) > ( uint ) m_MaxCapacity )
688+ {
689+ throw new ArgumentOutOfRangeException ( nameof ( repeatCount ) , SR . ArgumentOutOfRange_LengthGreaterThanCapacity ) ;
690+ }
691+
692+ char [ ] chunkChars = m_ChunkChars ;
693+ int chunkLength = m_ChunkLength ;
694+
695+ // Fill the rest of the current chunk
696+ int firstLength = chunkChars . Length - chunkLength ;
697+ if ( firstLength > 0 )
698+ {
699+ chunkChars . AsSpan ( chunkLength , firstLength ) . Fill ( value ) ;
700+ m_ChunkLength = chunkChars . Length ;
701+ }
702+
703+ // Expand the builder to add another chunk
704+ int restLength = repeatCount - firstLength ;
705+ ExpandByABlock ( restLength ) ;
706+ Debug . Assert ( m_ChunkLength == 0 , "A new block was not created." ) ;
707+
708+ // Fill the new chunk with the remaining part of repeatCount
709+ m_ChunkChars . AsSpan ( 0 , restLength ) . Fill ( value ) ;
710+ m_ChunkLength = restLength ;
711+ }
712+
692713 /// <summary>
693714 /// Appends a range of characters to the end of this builder.
694715 /// </summary>
@@ -990,12 +1011,21 @@ public StringBuilder Append(char value)
9901011 }
9911012 else
9921013 {
993- Append ( value , 1 ) ;
1014+ AppendWithExpansion ( value ) ;
9941015 }
9951016
9961017 return this ;
9971018 }
9981019
1020+ [ MethodImpl ( MethodImplOptions . NoInlining ) ]
1021+ private void AppendWithExpansion ( char value )
1022+ {
1023+ ExpandByABlock ( 1 ) ;
1024+ Debug . Assert ( m_ChunkLength == 0 , "A new block was not created." ) ;
1025+ m_ChunkChars [ 0 ] = value ;
1026+ m_ChunkLength ++ ;
1027+ }
1028+
9991029 [ CLSCompliant ( false ) ]
10001030 public StringBuilder Append ( sbyte value ) => AppendSpanFormattable ( value ) ;
10011031
0 commit comments