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

Bring back old array enumerator code #88371

Merged
merged 4 commits into from
Jul 7, 2023
Merged
Show file tree
Hide file tree
Changes from 2 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 @@ -400,7 +400,8 @@ internal IEnumerator<T> GetEnumerator<T>()
// ! Warning: "this" is an array, not an SZArrayHelper. See comments above
// ! or you may introduce a security hole!
T[] @this = Unsafe.As<T[]>(this);
return @this.Length == 0 ? SZGenericArrayEnumerator<T>.Empty : new SZGenericArrayEnumerator<T>(@this);
int length = @this.Length;
return length == 0 ? SZGenericArrayEnumerator<T>.Empty : new SZGenericArrayEnumerator<T>(@this, length);
}

private void CopyTo<T>(T[] array, int index)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1099,7 +1099,10 @@ private Array() { }
public new IEnumerator<T> GetEnumerator()
{
T[] @this = Unsafe.As<T[]>(this);
return @this.Length == 0 ? SZGenericArrayEnumerator<T>.Empty : new SZGenericArrayEnumerator<T>(@this);
// get length so we don't have to call the Length property again in ArrayEnumerator constructor
// and avoid more checking there too.
int length = @this.Length;
return length == 0 ? SZGenericArrayEnumerator<T>.Empty : new SZGenericArrayEnumerator<T>(@this, length);
}

public int Count
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,72 +69,58 @@ public void Reset()

internal abstract class SZGenericArrayEnumeratorBase : IDisposable
{
protected readonly Array _array;
protected int _index;
protected readonly int _endIndex;

protected SZGenericArrayEnumeratorBase(Array array)
protected SZGenericArrayEnumeratorBase(int endIndex)
{
Debug.Assert(array != null);

_array = array;
_index = -1;
_endIndex = endIndex;
}

public bool MoveNext()
Copy link
Member

Choose a reason for hiding this comment

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

        public bool MoveNext()
        {
            int index = _index + 1;
            if ((uint)index < (uint)_endIndex)
            {
                _index = index;
                return true;
            }
            _index = _endIndex;
            return false;
        }

This should be better, on some architectures at least. (One less comparison.)

{
int index = _index + 1;
uint length = (uint)_array.NativeLength;
if ((uint)index >= length)
if (_index < _endIndex)
{
_index = (int)length;
return false;
_index++;
return (_index < _endIndex);
}
_index = index;
return true;
return false;
}

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

#pragma warning disable CA1822 // https://github.com/dotnet/roslyn-analyzers/issues/5911
public void Dispose()
{
}
#pragma warning restore CA1822
}

internal sealed class SZGenericArrayEnumerator<T> : SZGenericArrayEnumeratorBase, IEnumerator<T>
{
private readonly T[]? _array;

/// <summary>Provides an empty enumerator singleton.</summary>
/// <remarks>
/// If the consumer is using SZGenericArrayEnumerator elsewhere or is otherwise likely
/// to be using T[] elsewhere, this singleton should be used. Otherwise, GenericEmptyEnumerator's
/// singleton should be used instead, as it doesn't reference T[] in order to reduce footprint.
/// </remarks>
#pragma warning disable CA1825
internal static readonly SZGenericArrayEnumerator<T> Empty =
// Array.Empty is intentionally omitted here, since we don't want to pay for generic instantiations
// that wouldn't have otherwise been used.
new SZGenericArrayEnumerator<T>(new T[0]);
#pragma warning restore CA1825

public SZGenericArrayEnumerator(T[] array)
: base(array)
internal static readonly SZGenericArrayEnumerator<T> Empty = new SZGenericArrayEnumerator<T>(null, -1);
MichalStrehovsky marked this conversation as resolved.
Show resolved Hide resolved

internal SZGenericArrayEnumerator(T[]? array, int endIndex)
: base(endIndex)
{
Debug.Assert(array == null || endIndex == array.Length);
_array = array;
}

public T Current
{
get
{
int index = _index;
T[] array = Unsafe.As<T[]>(_array);

if ((uint)index >= (uint)array.Length)
{
ThrowHelper.ThrowInvalidOperationException_EnumCurrent(index);
}

return array[index];
if ((uint)_index >= (uint)_endIndex)
ThrowHelper.ThrowInvalidOperationException();
MichalStrehovsky marked this conversation as resolved.
Show resolved Hide resolved
return _array![_index];
}
}

Expand Down
3 changes: 2 additions & 1 deletion src/mono/System.Private.CoreLib/src/System/Array.Mono.cs
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,8 @@ internal bool InternalArray__ICollection_get_IsReadOnly()

internal IEnumerator<T> InternalArray__IEnumerable_GetEnumerator<T>()
{
return Length == 0 ? SZGenericArrayEnumerator<T>.Empty : new SZGenericArrayEnumerator<T>(Unsafe.As<T[]>(this));
int length = Length;
return length == 0 ? SZGenericArrayEnumerator<T>.Empty : new SZGenericArrayEnumerator<T>(Unsafe.As<T[]>(this), length);
}

internal void InternalArray__ICollection_Clear()
Expand Down
Loading