Skip to content

Commit

Permalink
Merge pull request #28 from faustodavid/IListImplementation
Browse files Browse the repository at this point in the history
implement IList, add additional test for enumerator, refactor tests
  • Loading branch information
faustodavid authored Dec 31, 2019
2 parents 4540d3c + eff7202 commit 16ee2c6
Show file tree
Hide file tree
Showing 13 changed files with 1,996 additions and 798 deletions.
1 change: 1 addition & 0 deletions perf/ListPool.Benchmarks/ListPoolClearBenchmarks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public class ListPoolClearBenchmarks

[Params(1000)]
public int N { get; set; }

[Params(0.10, 0.50, 0.80, 1)]
public double CapacityFilled { get; set; }

Expand Down
8 changes: 4 additions & 4 deletions perf/ListPool.Benchmarks/ListPoolContainsBenchmark.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,19 @@ namespace ListPool.Benchmarks
[GcConcurrent]
public class ListPoolContainsBenchmark
{
[Params(10, 100, 1000, 10000)]
public int N { get; set; }

private List<int> _list;
private ListPool<int> _listPool;

[Params(10, 100, 1000, 10000)]
public int N { get; set; }

[IterationSetup]
public void IterationSetup()
{
_list = new List<int>(N);
_listPool = new ListPool<int>(N);

for (int i = 1; i <= N; i++)
for (int i = 1; i <= N; i++)
{
_list.Add(i);
_listPool.Add(i);
Expand Down
8 changes: 4 additions & 4 deletions perf/ListPool.Benchmarks/ListPoolCopyToBenchmarks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ namespace ListPool.Benchmarks
[GcConcurrent]
public class ListPoolCopyToBenchmarks
{
[Params(10, 100, 1000, 10000)]
public int N { get; set; }

private List<int> _list;
private ListPool<int> _listPool;
private int[] _listCopy;
private ListPool<int> _listPool;
private int[] _listPoolCopy;

[Params(10, 100, 1000, 10000)]
public int N { get; set; }

[IterationSetup]
public void IterationSetup()
{
Expand Down
6 changes: 3 additions & 3 deletions perf/ListPool.Benchmarks/ListPoolIndexOfBenchmarks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ namespace ListPool.Benchmarks
[GcConcurrent]
public class ListPoolIndexOfBenchmarks
{
[Params(10, 100, 1000, 10000)]
public int N { get; set; }

private List<int> _list;
private ListPool<int> _listPool;

[Params(10, 100, 1000, 10000)]
public int N { get; set; }

[IterationSetup]
public void IterationSetup()
{
Expand Down
6 changes: 3 additions & 3 deletions perf/ListPool.Benchmarks/ListPoolRemoveAtBenchmarks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ namespace ListPool.Benchmarks
[GcConcurrent]
public class ListPoolRemoveAtBenchmarks
{
[Params(10, 100, 1000, 10000)]
public int N { get; set; }

private List<int> _list;
private ListPool<int> _listPool;

[Params(10, 100, 1000, 10000)]
public int N { get; set; }

[IterationSetup]
public void IterationSetup()
{
Expand Down
6 changes: 3 additions & 3 deletions perf/ListPool.Benchmarks/ListPoolRemoveBenchmarks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ namespace ListPool.Benchmarks
[GcConcurrent]
public class ListPoolRemoveBenchmarks
{
[Params(10, 100, 1000, 10000)]
public int N { get; set; }

private List<int> _list;
private ListPool<int> _listPool;

[Params(10, 100, 1000, 10000)]
public int N { get; set; }

[IterationSetup]
public void IterationSetup()
{
Expand Down
134 changes: 128 additions & 6 deletions src/ListPool/ListPool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,51 @@
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Runtime.CompilerServices;
using System.Threading;

namespace ListPool
{
public struct ListPool<TSource> : IList<TSource>, IReadOnlyList<TSource>, IDisposable,
public struct ListPool<TSource> : IList<TSource>, IList, IReadOnlyList<TSource>, IDisposable,
IValueEnumerable<TSource>

{
public readonly int Capacity => _bufferOwner.IsValid ? _bufferOwner.Buffer.Length : 0;
public readonly int Capacity => _bufferOwner.IsValid ? _bufferOwner.Buffer.Length : 0;
public readonly int Count => _itemsCount;
public readonly bool IsReadOnly => false;

int ICollection.Count => _itemsCount;
readonly bool IList.IsFixedSize => false;
bool ICollection.IsSynchronized => false;
readonly bool IList.IsReadOnly => false;
private BufferOwner<TSource> _bufferOwner;
private int _itemsCount;
private const int MinimumCapacity = 128;

[NonSerialized]
private object _syncRoot;

object ICollection.SyncRoot
{
get
{
if (_syncRoot == null)
{
_ = Interlocked.CompareExchange<object>(ref _syncRoot, new object(), null);
}

return _syncRoot;
}
}

public ListPool(int length)
{
_syncRoot = null;
_bufferOwner = new BufferOwner<TSource>(length < MinimumCapacity ? MinimumCapacity : length);
_itemsCount = 0;
}

public ListPool(IEnumerable<TSource> source)
{
_syncRoot = null;
if (source is ICollection<TSource> collection)
{
_bufferOwner = new BufferOwner<TSource>(collection.Count);
Expand Down Expand Up @@ -56,14 +78,84 @@ public void Add(TSource item)
_bufferOwner.Buffer[_itemsCount++] = item;
}

int IList.Add(object item)
{
if (item is TSource itemAsTSource)
{
Add(itemAsTSource);
}
else
{
throw new ArgumentException($"Wrong value type. Expected {typeof(TSource)}, got: '{item}'.",
nameof(item));
}

return Count - 1;
}


public void Clear() => _itemsCount = 0;
public readonly bool Contains(TSource item) => IndexOf(item) > -1;

public readonly int IndexOf(TSource item) => _bufferOwner.IsValid ? Array.IndexOf(_bufferOwner.Buffer, item, 0, _itemsCount) : -1;
bool IList.Contains(object item)
{
if (item is TSource itemAsTSource)
{
return Contains(itemAsTSource);
}

throw new ArgumentException($"Wrong value type. Expected {typeof(TSource)}, got: '{item}'.", nameof(item));
}

int IList.IndexOf(object item)
{
if (item is TSource itemAsTSource)
{
return IndexOf(itemAsTSource);
}

throw new ArgumentException($"Wrong value type. Expected {typeof(TSource)}, got: '{item}'.", nameof(item));
}

void IList.Remove(object item)
{
if (item is null)
{
return;
}

if (!(item is TSource itemAsTSource))
{
throw new ArgumentException($"Wrong value type. Expected {typeof(TSource)}, got: '{item}'.",
nameof(item));
}

Remove(itemAsTSource);
}

void IList.Insert(int index, object item)
{
if (!(item is TSource itemAsTSource))
{
throw new ArgumentException($"Wrong value type. Expected {typeof(TSource)}, got: '{item}'.",
nameof(item));
}

Insert(index, itemAsTSource);
}

public readonly int IndexOf(TSource item) =>
_bufferOwner.IsValid ? Array.IndexOf(_bufferOwner.Buffer, item, 0, _itemsCount) : -1;

public readonly void CopyTo(TSource[] array, int arrayIndex) =>
Array.Copy(_bufferOwner.Buffer, 0, array, arrayIndex, _itemsCount);

void ICollection.CopyTo(Array array, int arrayIndex)
{
// Array.Copy will check for NULL.
Array.Copy(_bufferOwner.Buffer, 0, array, arrayIndex, _itemsCount);
}

public bool Remove(TSource item)
{
if (item == null) return false;
Expand Down Expand Up @@ -105,21 +197,51 @@ public readonly TSource this[int index]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
if (index < 0 || index >= _bufferOwner.Buffer.Length || index >= _itemsCount)
if (index < 0 || index >= _itemsCount)
throw new ArgumentOutOfRangeException(nameof(index));

return _bufferOwner.Buffer[index];
}

set
{
if (index < 0 || index >= _bufferOwner.Buffer.Length || index >= _itemsCount)
if (index < 0 || index >= _itemsCount)
throw new ArgumentOutOfRangeException(nameof(index));

_bufferOwner.Buffer[index] = value;
}
}

[MaybeNull]
readonly object IList.this[int index]
{
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
if (index < 0 || index >= _itemsCount)
throw new ArgumentOutOfRangeException(nameof(index));

return _bufferOwner.Buffer[index];
}

set
{
if (index < 0 || index >= _itemsCount)
throw new ArgumentOutOfRangeException(nameof(index));

if (value is TSource valueAsTSource)
{
_bufferOwner.Buffer[index] = valueAsTSource;
}
else
{
throw new ArgumentException($"Wrong value type. Expected {typeof(TSource)}, got: '{value}'.",
nameof(value));
}
}
}

[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Enumerator<TSource> GetEnumerator() =>
Expand Down
15 changes: 15 additions & 0 deletions tests/ListPool.UnitTests/EnumeratorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,20 @@ public void Current_is_updated_in_each_iteration()
}
}

[Fact]
public void Current_is_updated_in_each_iteration_using_IEnumerator()
{
string[] items = s_fixture.CreateMany<string>(10).ToArray();
IEnumerator expectedEnumerator = items.GetEnumerator();
IEnumerator sut = new Enumerator<string>(items, items.Length);

while (expectedEnumerator.MoveNext())
{
Assert.True(sut.MoveNext());
Assert.Equal(expectedEnumerator.Current, sut.Current);
}
}

[Fact]
public void Reset_allows_enumerator_to_be_enumerate_again()
{
Expand All @@ -35,6 +49,7 @@ public void Reset_allows_enumerator_to_be_enumerate_again()
Assert.True(sut.MoveNext());
Assert.Equal(expectedEnumerator.Current, sut.Current);
}

Assert.False(sut.MoveNext());
sut.Reset();
expectedEnumerator.Reset();
Expand Down
Loading

0 comments on commit 16ee2c6

Please sign in to comment.