22// The .NET Foundation licenses this file to you under the MIT license.
33
44using System . Diagnostics ;
5+ using System . Runtime . CompilerServices ;
56
67namespace System . Collections . Generic
78{
@@ -11,7 +12,9 @@ namespace System.Collections.Generic
1112 /// <typeparam name="T">The element type.</typeparam>
1213 internal struct ArrayBuilder < T >
1314 {
14- private const int DefaultCapacity = 4 ;
15+ private InlineArray16 < T > _stackAllocatedBuffer = default ;
16+ private const int StackAllocatedCapacity = 16 ;
17+ private const int DefaultHeapCapacity = 4 ;
1518
1619 private T [ ] ? _array ; // Starts out null, initialized on first Add.
1720 private int _count ; // Number of items into _array we're using.
@@ -23,20 +26,17 @@ internal struct ArrayBuilder<T>
2326 public ArrayBuilder ( int capacity ) : this ( )
2427 {
2528 Debug . Assert ( capacity >= 0 ) ;
26- if ( capacity > 0 )
29+ if ( capacity > StackAllocatedCapacity )
2730 {
28- _array = new T [ capacity ] ;
31+ _array = new T [ capacity - StackAllocatedCapacity ] ;
2932 }
3033 }
3134
3235 /// <summary>
3336 /// Gets the number of items this instance can store without re-allocating,
3437 /// or 0 if the backing array is <c>null</c>.
3538 /// </summary>
36- public int Capacity => _array ? . Length ?? 0 ;
37-
38- /// <summary>Gets the current underlying array.</summary>
39- public T [ ] ? Buffer => _array ;
39+ public int Capacity => _array ? . Length + StackAllocatedCapacity ?? StackAllocatedCapacity ;
4040
4141 /// <summary>
4242 /// Gets the number of items in the array currently in use.
@@ -52,7 +52,7 @@ public T this[int index]
5252 get
5353 {
5454 Debug . Assert ( index >= 0 && index < _count ) ;
55- return _array ! [ index ] ;
55+ return index < StackAllocatedCapacity ? _stackAllocatedBuffer [ index ] : _array ! [ index - StackAllocatedCapacity ] ;
5656 }
5757 }
5858
@@ -76,7 +76,7 @@ public void Add(T item)
7676 public T First ( )
7777 {
7878 Debug . Assert ( _count > 0 ) ;
79- return _array ! [ 0 ] ;
79+ return _stackAllocatedBuffer [ 0 ] ;
8080 }
8181
8282 /// <summary>
@@ -85,7 +85,7 @@ public T First()
8585 public T Last ( )
8686 {
8787 Debug . Assert ( _count > 0 ) ;
88- return _array ! [ _count - 1 ] ;
88+ return _count <= StackAllocatedCapacity ? _stackAllocatedBuffer [ _count - 1 ] : _array ! [ _count - StackAllocatedCapacity - 1 ] ;
8989 }
9090
9191 /// <summary>
@@ -101,17 +101,19 @@ public T[] ToArray()
101101 return Array . Empty < T > ( ) ;
102102 }
103103
104- Debug . Assert ( _array != null ) ; // Nonzero _count should imply this
105-
106- T [ ] result = _array ;
107- if ( _count < result . Length )
104+ T [ ] result = new T [ _count ] ;
105+ int index = 0 ;
106+ foreach ( T stackAllocatedValue in _stackAllocatedBuffer )
108107 {
109- // Avoid a bit of overhead (method call, some branches, extra codegen)
110- // which would be incurred by using Array.Resize
111- result = new T [ _count ] ;
112- Array . Copy ( _array , result , _count ) ;
108+ result [ index ++ ] = stackAllocatedValue ;
109+ if ( index >= _count )
110+ {
111+ return result ;
112+ }
113113 }
114114
115+ _array . AsSpan ( 0 , _count - StackAllocatedCapacity ) . CopyTo ( result . AsSpan ( start : StackAllocatedCapacity ) ) ;
116+
115117#if DEBUG
116118 // Try to prevent callers from using the ArrayBuilder after ToArray, if _count != 0.
117119 _count = - 1 ;
@@ -132,25 +134,42 @@ public T[] ToArray()
132134 public void UncheckedAdd ( T item )
133135 {
134136 Debug . Assert ( _count < Capacity ) ;
135-
136- _array ! [ _count ++ ] = item ;
137+ if ( _count < StackAllocatedCapacity )
138+ {
139+ _stackAllocatedBuffer [ _count ++ ] = item ;
140+ }
141+ else
142+ {
143+ _array ! [ _count ++ - StackAllocatedCapacity ] = item ;
144+ }
137145 }
138146
139147 private void EnsureCapacity ( int minimum )
140148 {
141149 Debug . Assert ( minimum > Capacity ) ;
142150
143- int capacity = Capacity ;
144- int nextCapacity = capacity == 0 ? DefaultCapacity : 2 * capacity ;
151+ if ( minimum < StackAllocatedCapacity )
152+ {
153+ return ;
154+ }
155+
156+ if ( _array == null )
157+ {
158+ // Initial capacity has not been set correctly, we will use the default size
159+ _array = new T [ DefaultHeapCapacity ] ;
160+ return ;
161+ }
162+
163+ int nextHeapCapacity = 2 * _array . Length ;
145164
146- if ( ( uint ) nextCapacity > ( uint ) Array . MaxLength )
165+ if ( ( uint ) nextHeapCapacity > ( uint ) Array . MaxLength )
147166 {
148- nextCapacity = Math . Max ( capacity + 1 , Array . MaxLength ) ;
167+ nextHeapCapacity = Math . Max ( _array . Length + 1 , Array . MaxLength ) ;
149168 }
150169
151- nextCapacity = Math . Max ( nextCapacity , minimum ) ;
170+ nextHeapCapacity = Math . Max ( nextHeapCapacity , minimum ) ;
152171
153- T [ ] next = new T [ nextCapacity ] ;
172+ T [ ] next = new T [ nextHeapCapacity ] ;
154173 if ( _count > 0 )
155174 {
156175 Array . Copy ( _array ! , next , _count ) ;
0 commit comments