Skip to content

Commit

Permalink
Some improvements to FrugalList (#6280)
Browse files Browse the repository at this point in the history
* Some improvements to FrugalList

- FrugalStructList's `ICollection<T>`-based constructor uses foreach to enumerate the contents of the collection.  If it's an `IList<T>`, we can instead index and avoid allocating the enumerator.
- Avoid multiple interface calls to `ICollection<T>.Count` in FrugalStructList's ctor
- Delete a dead ctor on `ArrayItemList<T>`.  That ctor was the only reason an array field may have been left null, so we can also remove subsequent null checks when accessing that array.
- Use Span/Array in ArrayItemList for Clear, Contains, IndexOf, ToArray, and CopyTo rather than open-coding them

* Update src/Microsoft.DotNet.Wpf/src/Shared/MS/Utility/FrugalList.cs

Co-authored-by: Bradley Grainger <[email protected]>

Co-authored-by: Bradley Grainger <[email protected]>
  • Loading branch information
stephentoub and bgrainger authored Jul 21, 2022
1 parent f1cfb42 commit 9949509
Showing 1 changed file with 52 additions and 100 deletions.
152 changes: 52 additions & 100 deletions src/Microsoft.DotNet.Wpf/src/Shared/MS/Utility/FrugalList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
using System.Diagnostics;
using System.Collections;
using System.Collections.Generic;
using System.Windows;
using System.Diagnostics.CodeAnalysis;
#if SYSTEM_XAML
using System.Xaml;
#else
Expand Down Expand Up @@ -1284,10 +1282,6 @@ private void SetCount(int value)
/// </summary>
internal sealed class ArrayItemList<T> : FrugalListBase<T>
{
public ArrayItemList()
{
}

public ArrayItemList(int size)
{
// Make size a multiple of GROWTH
Expand All @@ -1298,74 +1292,53 @@ public ArrayItemList(int size)

public ArrayItemList(ICollection collection)
{
if (collection != null)
{
_count = collection.Count;
_entries = new T[_count];
collection.CopyTo(_entries, 0);
}
Debug.Assert(collection is not null);
_count = collection.Count;
_entries = new T[_count];
collection.CopyTo(_entries, 0);
}

public ArrayItemList(ICollection<T> collection)
{
if (collection != null)
{
_count = collection.Count;
_entries = new T[_count];
collection.CopyTo(_entries, 0);
}
Debug.Assert(collection is not null);
_count = collection.Count;
_entries = new T[_count];
collection.CopyTo(_entries, 0);
}

// Capacity of this store
public override int Capacity
{
get
{
if (_entries != null)
{
return _entries.Length;
}
return 0;
}
}
public override int Capacity => _entries.Length;

public override FrugalListStoreState Add(T value)
{
// If we don't have any entries or the existing entry is being overwritten,
// then we can use this store. Otherwise we have to promote.
if ((null != _entries) && (_count < _entries.Length))
if (_count < _entries.Length)
{
_entries[_count] = value;
++_count;
}
else
{
if (null != _entries)
{
int size = _entries.Length;
int size = _entries.Length;

// Grow the list slowly while it is small but
// faster once it reaches the LARGEGROWTH size
if (size < LARGEGROWTH)
{
size += GROWTH;
}
else
{
size += size >> 2;
}

T[] destEntries = new T[size];

// Copy old array
Array.Copy(_entries, 0, destEntries, 0, _entries.Length);
_entries = destEntries;
// Grow the list slowly while it is small but
// faster once it reaches the LARGEGROWTH size
if (size < LARGEGROWTH)
{
size += GROWTH;
}
else
{
_entries = new T[MINSIZE];
size += size >> 2;
}

T[] destEntries = new T[size];

// Copy old array
Array.Copy(_entries, 0, destEntries, 0, _entries.Length);
_entries = destEntries;

// Insert into new array
_entries[_count] = value;
++_count;
Expand All @@ -1375,34 +1348,17 @@ public override FrugalListStoreState Add(T value)

public override void Clear()
{
// Wipe out the info.
for (int i = 0; i < _count; ++i)
{
_entries[i] = default(T);
}
_entries.AsSpan(0, _count).Clear();
_count = 0;
}

public override bool Contains(T value)
{
return (-1 != IndexOf(value));
}
public override bool Contains(T value) => IndexOf(value) >= 0;

public override int IndexOf(T value)
{
for (int index = 0; index < _count; ++index)
{
if (EqualityComparer<T>.Default.Equals(_entries[index], value))
{
return index;
}
}
return -1;
}
public override int IndexOf(T value) => Array.IndexOf(_entries, value, 0, _count);

public override void Insert(int index, T value)
{
if ((null != _entries) && (_count < _entries.Length))
if (_count < _entries.Length)
{
// Move down the required number of items
Array.Copy(_entries, index, _entries, index + 1, _count - index);
Expand All @@ -1423,13 +1379,11 @@ public override void SetAt(int index, T value)

public override bool Remove(T value)
{
for (int index = 0; index < _count; ++index)
int index = IndexOf(value);
if (index >= 0)
{
if (EqualityComparer<T>.Default.Equals(_entries[index], value))
{
RemoveAt(index);
return true;
}
RemoveAt(index);
return true;
}

return false;
Expand Down Expand Up @@ -1543,24 +1497,9 @@ public void Promote(ArrayItemList<T> oldList)
}
}

public override T[] ToArray()
{
T[] array = new T[_count];
public override T[] ToArray() => _entries.AsSpan(0, _count).ToArray();

for (int i = 0; i < _count; ++i)
{
array[i] = _entries[i];
}
return array;
}

public override void CopyTo(T[] array, int index)
{
for (int i = 0; i < _count; ++i)
{
array[index+i] = _entries[i];
}
}
public override void CopyTo(T[] array, int index) => _entries.AsSpan(0, _count).CopyTo(array.AsSpan(index));

public override object Clone()
{
Expand Down Expand Up @@ -2032,14 +1971,15 @@ public FrugalStructList(int size)

public FrugalStructList(ICollection collection)
{
if (collection.Count > 6)
int count = collection.Count;
if (count > 6)
{
_listStore = new ArrayItemList<T>(collection);
}
else
{
_listStore = null;
Capacity = collection.Count;
Capacity = count;
foreach (T item in collection)
{
Add(item);
Expand All @@ -2049,17 +1989,29 @@ public FrugalStructList(ICollection collection)

public FrugalStructList(ICollection<T> collection)
{
if (collection.Count > 6)
int count = collection.Count;
if (count > 6)
{
_listStore = new ArrayItemList<T>(collection);
}
else
{
_listStore = null;
Capacity = collection.Count;
foreach (T item in collection)
Capacity = count;

if (collection is IList<T> list)
{
Add(item);
for (int i = 0; i < count; i++)
{
Add(list[i]);
}
}
else
{
foreach (T item in collection)
{
Add(item);
}
}
}
}
Expand Down

0 comments on commit 9949509

Please sign in to comment.