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

Clean up some usages of LowLevelList<T> #105407

Merged
merged 10 commits into from
Jul 26, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ namespace System.Collections.Generic
// LowLevelList with no interface implementation minimizes both code and data size.
// Data size is smaller because there will be minimal virtual function table.
// Code size is smaller because only functions called will be in the binary.
// Use LowLevelListWithIList<T> for IList support
[DebuggerDisplay("Count = {Count}")]
#if TYPE_LOADER_IMPLEMENTATION
[System.Runtime.CompilerServices.ForceDictionaryLookups]
Expand Down Expand Up @@ -196,356 +195,5 @@ private void EnsureCapacity(int min)
Capacity = newCapacity;
}
}

#if !TYPE_LOADER_IMPLEMENTATION
// Adds the elements of the given collection to the end of this list. If
// required, the capacity of the list is increased to twice the previous
// capacity or the new size, whichever is larger.
//
public void AddRange(IEnumerable<T> collection)
{

InsertRange(_size, collection);
}

// Clears the contents of List.
public void Clear()
{
if (_size > 0)
{
Array.Clear(_items, 0, _size); // Don't need to doc this but we clear the elements so that the gc can reclaim the references.
_size = 0;
}
_version++;
}

// Contains returns true if the specified element is in the List.
// It does a linear, O(n) search. Equality is determined by calling
// item.Equals().
//
public bool Contains(T item)
{
if ((object?)item == null)
{
for (int i = 0; i < _size; i++)
if ((object?)_items[i] == null)
return true;
return false;
}
else
{
int index = IndexOf(item);
if (index >= 0)
return true;
return false;
}
}


// Copies a section of this list to the given array at the given index.
//
// The method uses the Array.Copy method to copy the elements.
//
public void CopyTo(int index, T[] array, int arrayIndex, int count)
{
if (_size - index < count)
{
throw new ArgumentException();
}

// Delegate rest of error checking to Array.Copy.
Array.Copy(_items, index, array, arrayIndex, count);
}

public void CopyTo(T[] array, int arrayIndex)
{
// Delegate rest of error checking to Array.Copy.
Array.Copy(_items, 0, array, arrayIndex, _size);
}

// Returns the index of the first occurrence of a given value in a range of
// this list. The list is searched forwards from beginning to end.
// The elements of the list are compared to the given value using the
// Object.Equals method.
//
// This method uses the Array.IndexOf method to perform the
// search.
//
public int IndexOf(T item)
{
return Array.IndexOf(_items, item, 0, _size);
}


// Returns the index of the first occurrence of a given value in a range of
// this list. The list is searched forwards, starting at index
// index and ending at count number of elements. The
// elements of the list are compared to the given value using the
// Object.Equals method.
//
// This method uses the Array.IndexOf method to perform the
// search.
//
public int IndexOf(T item, int index)
{
ArgumentOutOfRangeException.ThrowIfGreaterThan(index, _size);
return Array.IndexOf(_items, item, index, _size - index);
}

// Inserts an element into this list at a given index. The size of the list
// is increased by one. If required, the capacity of the list is doubled
// before inserting the new element.
//
public void Insert(int index, T item)
{
// Note that insertions at the end are legal.
ArgumentOutOfRangeException.ThrowIfGreaterThan((uint)index, (uint)_size, nameof(index));

if (_size == _items.Length) EnsureCapacity(_size + 1);
if (index < _size)
{
Array.Copy(_items, index, _items, index + 1, _size - index);
}
_items[index] = item;
_size++;
_version++;
}

// Inserts the elements of the given collection at a given index. If
// required, the capacity of the list is increased to twice the previous
// capacity or the new size, whichever is larger. Ranges may be added
// to the end of the list by setting index to the List's size.
//
public void InsertRange(int index, IEnumerable<T> collection)
{
ArgumentNullException.ThrowIfNull(collection);
ArgumentOutOfRangeException.ThrowIfGreaterThan((uint)index, (uint)_size, nameof(index));

ICollection<T>? c = collection as ICollection<T>;
if (c != null)
{ // if collection is ICollection<T>
int count = c.Count;
if (count > 0)
{
EnsureCapacity(_size + count);
if (index < _size)
{
Array.Copy(_items, index, _items, index + count, _size - index);
}

// If we're inserting a List into itself, we want to be able to deal with that.
if (this == c)
{
// Copy first part of _items to insert location
Array.Copy(_items, 0, _items, index, index);
// Copy last part of _items back to inserted location
Array.Copy(_items, index + count, _items, index * 2, _size - index);
}
else
{
T[] itemsToInsert = new T[count];
c.CopyTo(itemsToInsert, 0);
Array.Copy(itemsToInsert, 0, _items, index, count);
}
_size += count;
}
}
else
{
using (IEnumerator<T> en = collection.GetEnumerator())
{
while (en.MoveNext())
{
Insert(index++, en.Current);
}
}
}
_version++;
}

// Removes the element at the given index. The size of the list is
// decreased by one.
//
public bool Remove(T item)
{
int index = IndexOf(item);
if (index >= 0)
{
RemoveAt(index);
return true;
}

return false;
}

// This method removes all items which matches the predicate.
// The complexity is O(n).
public int RemoveAll(Predicate<T> match)
{
ArgumentNullException.ThrowIfNull(match);

int freeIndex = 0; // the first free slot in items array

// Find the first item which needs to be removed.
while (freeIndex < _size && !match(_items[freeIndex]!)) freeIndex++;
if (freeIndex >= _size) return 0;

int current = freeIndex + 1;
while (current < _size)
{
// Find the first item which needs to be kept.
while (current < _size && match(_items[current]!)) current++;

if (current < _size)
{
// copy item to the free slot.
_items[freeIndex++] = _items[current++];
}
}

Array.Clear(_items, freeIndex, _size - freeIndex);
int result = _size - freeIndex;
_size = freeIndex;
_version++;
return result;
}

// Removes the element at the given index. The size of the list is
// decreased by one.
//
public void RemoveAt(int index)
{
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, (uint)_size, nameof(index));
_size--;
if (index < _size)
{
Array.Copy(_items, index + 1, _items, index, _size - index);
}
_items[_size] = default!;
_version++;
}

// ToArray returns a new Object array containing the contents of the List.
// This requires copying the List, which is an O(n) operation.
public T[] ToArray()
{
T[] array = new T[_size];
Array.Copy(_items, 0, array, 0, _size);
return array;
}
#endif
}

#if !TYPE_LOADER_IMPLEMENTATION
// LowLevelList<T> with full IList<T> implementation
internal sealed class LowLevelListWithIList<T> : LowLevelList<T>, IList<T>
{
public LowLevelListWithIList()
{
}

public LowLevelListWithIList(int capacity)
: base(capacity)
{
}

public LowLevelListWithIList(IEnumerable<T> collection)
: base(collection)
{
}

// Is this List read-only?
bool ICollection<T>.IsReadOnly
{
get { return false; }
}

/// <internalonly/>
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return new Enumerator(this);
}

System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return new Enumerator(this);
}

private struct Enumerator : IEnumerator<T>, System.Collections.IEnumerator
{
private LowLevelListWithIList<T> _list;
private int _index;
private int _version;
private T? _current;

internal Enumerator(LowLevelListWithIList<T> list)
{
_list = list;
_index = 0;
_version = list._version;
_current = default(T);
}

public void Dispose()
{
}

public bool MoveNext()
{
LowLevelListWithIList<T> localList = _list;

if (_version == localList._version && ((uint)_index < (uint)localList._size))
{
_current = localList._items[_index];
_index++;
return true;
}
return MoveNextRare();
}

private bool MoveNextRare()
{
if (_version != _list._version)
{
throw new InvalidOperationException();
}

_index = _list._size + 1;
_current = default(T);
return false;
}

public T Current
{
get
{
return _current!;
}
}

object? System.Collections.IEnumerator.Current
{
get
{
if (_index == 0 || _index == _list._size + 1)
{
throw new InvalidOperationException();
}
return Current;
}
}

void System.Collections.IEnumerator.Reset()
{
if (_version != _list._version)
{
throw new InvalidOperationException();
}

_index = 0;
_current = default(T);
}
}
}
#endif // !TYPE_LOADER_IMPLEMENTATION
}
Original file line number Diff line number Diff line change
Expand Up @@ -135,18 +135,23 @@ private static Attribute OneOrNull(IEnumerable<CustomAttributeData> results)
Justification = "Arrays of reference types are safe to create.")]
private static Attribute[] Instantiate(IEnumerable<CustomAttributeData> cads, Type actualElementType)
{
LowLevelList<Attribute> attributes = new LowLevelList<Attribute>();
ArrayBuilder<Attribute> attributes = default;
foreach (CustomAttributeData cad in cads)
{
Attribute instantiatedAttribute = cad.Instantiate();
attributes.Add(instantiatedAttribute);
}
int count = attributes.Count;
Attribute[] result = actualElementType.ContainsGenericParameters
? new Attribute[count]
: (Attribute[])Array.CreateInstance(actualElementType, count);
attributes.CopyTo(result, 0);
return result;

if (actualElementType.ContainsGenericParameters)
{
Attribute[] result = (Attribute[])Array.CreateInstance(actualElementType, attributes.Count);
attributes.AsSpan(0).CopyTo(result);
return result;
}
else
{
return attributes.ToArray();
}
}
}
}
Loading
Loading