Skip to content

Commit

Permalink
implement IList, add additional test for enumerator, refactor tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Fausto David committed Dec 31, 2019
1 parent 4540d3c commit 5852fa9
Show file tree
Hide file tree
Showing 6 changed files with 1,309 additions and 116 deletions.
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
14 changes: 14 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 Down
Loading

0 comments on commit 5852fa9

Please sign in to comment.