Skip to content

Commit 4285e43

Browse files
Bring back old array enumerator code (#88371)
This is a 0.3% size saving for Stage1. We not only allow array enumerators to be preinitialized again, but also avoid introducing many array `MethodTables` (looks like the new enumerators force array MethodTables for cases where we could have avoided them). Fixes #82993.
1 parent 6085dc2 commit 4285e43

File tree

4 files changed

+27
-34
lines changed

4 files changed

+27
-34
lines changed

src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -400,7 +400,8 @@ internal IEnumerator<T> GetEnumerator<T>()
400400
// ! Warning: "this" is an array, not an SZArrayHelper. See comments above
401401
// ! or you may introduce a security hole!
402402
T[] @this = Unsafe.As<T[]>(this);
403-
return @this.Length == 0 ? SZGenericArrayEnumerator<T>.Empty : new SZGenericArrayEnumerator<T>(@this);
403+
int length = @this.Length;
404+
return length == 0 ? SZGenericArrayEnumerator<T>.Empty : new SZGenericArrayEnumerator<T>(@this, length);
404405
}
405406

406407
private void CopyTo<T>(T[] array, int index)

src/coreclr/nativeaot/System.Private.CoreLib/src/System/Array.NativeAot.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1099,7 +1099,10 @@ private Array() { }
10991099
public new IEnumerator<T> GetEnumerator()
11001100
{
11011101
T[] @this = Unsafe.As<T[]>(this);
1102-
return @this.Length == 0 ? SZGenericArrayEnumerator<T>.Empty : new SZGenericArrayEnumerator<T>(@this);
1102+
// get length so we don't have to call the Length property again in ArrayEnumerator constructor
1103+
// and avoid more checking there too.
1104+
int length = @this.Length;
1105+
return length == 0 ? SZGenericArrayEnumerator<T>.Empty : new SZGenericArrayEnumerator<T>(@this, length);
11031106
}
11041107

11051108
public int Count

src/libraries/System.Private.CoreLib/src/System/Array.Enumerators.cs

Lines changed: 19 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -69,72 +69,60 @@ public void Reset()
6969

7070
internal abstract class SZGenericArrayEnumeratorBase : IDisposable
7171
{
72-
protected readonly Array _array;
7372
protected int _index;
73+
protected readonly int _endIndex;
7474

75-
protected SZGenericArrayEnumeratorBase(Array array)
75+
protected SZGenericArrayEnumeratorBase(int endIndex)
7676
{
77-
Debug.Assert(array != null);
78-
79-
_array = array;
8077
_index = -1;
78+
_endIndex = endIndex;
8179
}
8280

8381
public bool MoveNext()
8482
{
8583
int index = _index + 1;
86-
uint length = (uint)_array.NativeLength;
87-
if ((uint)index >= length)
84+
if ((uint)index < (uint)_endIndex)
8885
{
89-
_index = (int)length;
90-
return false;
86+
_index = index;
87+
return true;
9188
}
92-
_index = index;
93-
return true;
89+
_index = _endIndex;
90+
return false;
9491
}
9592

9693
public void Reset() => _index = -1;
9794

98-
#pragma warning disable CA1822 // https://github.com/dotnet/roslyn-analyzers/issues/5911
9995
public void Dispose()
10096
{
10197
}
102-
#pragma warning restore CA1822
10398
}
10499

105100
internal sealed class SZGenericArrayEnumerator<T> : SZGenericArrayEnumeratorBase, IEnumerator<T>
106101
{
102+
private readonly T[]? _array;
103+
107104
/// <summary>Provides an empty enumerator singleton.</summary>
108105
/// <remarks>
109106
/// If the consumer is using SZGenericArrayEnumerator elsewhere or is otherwise likely
110107
/// to be using T[] elsewhere, this singleton should be used. Otherwise, GenericEmptyEnumerator's
111108
/// singleton should be used instead, as it doesn't reference T[] in order to reduce footprint.
112109
/// </remarks>
113-
#pragma warning disable CA1825
114-
internal static readonly SZGenericArrayEnumerator<T> Empty =
115-
// Array.Empty is intentionally omitted here, since we don't want to pay for generic instantiations
116-
// that wouldn't have otherwise been used.
117-
new SZGenericArrayEnumerator<T>(new T[0]);
118-
#pragma warning restore CA1825
119-
120-
public SZGenericArrayEnumerator(T[] array)
121-
: base(array)
110+
internal static readonly SZGenericArrayEnumerator<T> Empty = new SZGenericArrayEnumerator<T>(null, 0);
111+
112+
internal SZGenericArrayEnumerator(T[]? array, int endIndex)
113+
: base(endIndex)
122114
{
115+
Debug.Assert(array == null || endIndex == array.Length);
116+
_array = array;
123117
}
124118

125119
public T Current
126120
{
127121
get
128122
{
129-
int index = _index;
130-
T[] array = Unsafe.As<T[]>(_array);
131-
132-
if ((uint)index >= (uint)array.Length)
133-
{
134-
ThrowHelper.ThrowInvalidOperationException_EnumCurrent(index);
135-
}
136-
137-
return array[index];
123+
if ((uint)_index >= (uint)_endIndex)
124+
ThrowHelper.ThrowInvalidOperationException_EnumCurrent(_index);
125+
return _array![_index];
138126
}
139127
}
140128

src/mono/System.Private.CoreLib/src/System/Array.Mono.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -468,7 +468,8 @@ internal bool InternalArray__ICollection_get_IsReadOnly()
468468

469469
internal IEnumerator<T> InternalArray__IEnumerable_GetEnumerator<T>()
470470
{
471-
return Length == 0 ? SZGenericArrayEnumerator<T>.Empty : new SZGenericArrayEnumerator<T>(Unsafe.As<T[]>(this));
471+
int length = Length;
472+
return length == 0 ? SZGenericArrayEnumerator<T>.Empty : new SZGenericArrayEnumerator<T>(Unsafe.As<T[]>(this), length);
472473
}
473474

474475
internal void InternalArray__ICollection_Clear()

0 commit comments

Comments
 (0)