Skip to content

Commit

Permalink
Merge pull request #29 from faustodavid/ListPoolAsClassAndListPoolValue
Browse files Browse the repository at this point in the history
List pool as class and list pool value
  • Loading branch information
faustodavid authored Dec 31, 2019
2 parents 16ee2c6 + a3575a1 commit 1422aea
Show file tree
Hide file tree
Showing 18 changed files with 1,962 additions and 91 deletions.
167 changes: 83 additions & 84 deletions src/ListPool/ListPool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,47 +8,29 @@

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

{
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 const int MinimumCapacity = 128;
private BufferOwner<TSource> _bufferOwner;
private int _itemsCount;
private const int MinimumCapacity = 128;

[NonSerialized]
private object _syncRoot;
private object? _syncRoot;

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

return _syncRoot;
}
_bufferOwner = new BufferOwner<TSource>(MinimumCapacity);
}

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 All @@ -59,7 +41,6 @@ public ListPool(IEnumerable<TSource> source)
else
{
_bufferOwner = new BufferOwner<TSource>(MinimumCapacity);
_itemsCount = 0;

using IEnumerator<TSource> enumerator = source.GetEnumerator();
while (enumerator.MoveNext())
Expand All @@ -69,13 +50,30 @@ public ListPool(IEnumerable<TSource> source)
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add(TSource item)
public int Capacity => _bufferOwner.IsValid ? _bufferOwner.Buffer.Length : 0;

public void Dispose()
{
if (!_bufferOwner.IsValid) _bufferOwner = new BufferOwner<TSource>(MinimumCapacity);
if (_itemsCount >= _bufferOwner.Buffer.Length) _bufferOwner.GrowDoubleSize();
_bufferOwner.Dispose();
_itemsCount = 0;
}

_bufferOwner.Buffer[_itemsCount++] = item;
int ICollection.Count => _itemsCount;
bool IList.IsFixedSize => false;
bool ICollection.IsSynchronized => false;
bool IList.IsReadOnly => false;

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

return _syncRoot;
}
}

int IList.Add(object item)
Expand All @@ -93,10 +91,6 @@ int IList.Add(object item)
return Count - 1;
}


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

bool IList.Contains(object item)
{
if (item is TSource itemAsTSource)
Expand Down Expand Up @@ -144,21 +138,64 @@ void IList.Insert(int index, object 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);
}

[MaybeNull]
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));
}
}
}

public int Count => _itemsCount;
public bool IsReadOnly => false;

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add(TSource item)
{
if (_itemsCount >= _bufferOwner.Buffer.Length) _bufferOwner.GrowDoubleSize();

_bufferOwner.Buffer[_itemsCount++] = item;
}


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

public int IndexOf(TSource item) => Array.IndexOf(_bufferOwner.Buffer, item, 0, _itemsCount);

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

public bool Remove(TSource item)
{
if (item == null) return false;
if (item is null) return false;

int index = IndexOf(item);

Expand All @@ -173,7 +210,6 @@ public bool Remove(TSource item)
public void Insert(int index, TSource item)
{
if (index < 0 || index > _itemsCount) throw new ArgumentOutOfRangeException(nameof(index));
if (!_bufferOwner.IsValid) _bufferOwner = new BufferOwner<TSource>(MinimumCapacity);
if (index >= _bufferOwner.Buffer.Length) _bufferOwner.GrowDoubleSize();
if (index < _itemsCount)
Array.Copy(_bufferOwner.Buffer, index, _bufferOwner.Buffer, index + 1, _itemsCount - index);
Expand All @@ -191,7 +227,7 @@ public void RemoveAt(int index)
}

[MaybeNull]
public readonly TSource this[int index]
public TSource this[int index]
{
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand All @@ -212,50 +248,13 @@ public readonly TSource this[int index]
}
}

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

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));
}
}
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Enumerator<TSource> GetEnumerator() =>
public Enumerator<TSource> GetEnumerator() =>
new Enumerator<TSource>(in _bufferOwner.Buffer, in _itemsCount);

readonly IEnumerator<TSource> IEnumerable<TSource>.GetEnumerator() => GetEnumerator();

readonly IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

public void Dispose()
{
_itemsCount = 0;
if (_bufferOwner.IsValid)
_bufferOwner.Dispose();
}
}
}
8 changes: 7 additions & 1 deletion src/ListPool/ListPool.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,17 @@
<LangCode>8.0</LangCode>
<Authors>Fausto David Suarez Rosario</Authors>
<Title>ListPool</Title>
<AssemblyName>ListPool</AssemblyName>
<PackageId>ListPool</PackageId>
<Description>ListPool is an optimized allocation free implementation of IList using ArrayPool.</Description>
<PackageTags>ASP.NET;List;System.Buffers;ArrayPool;ListPool;Performance</PackageTags>
<RepositoryUrl>https://github.com/faustodavid/ListPool</RepositoryUrl>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageVersion>1.0.2</PackageVersion>
<PackageVersion>2.0.0</PackageVersion>
<PackageReleaseNotes>ListPool is now reference type which can be serialized and pass as reference. For high-performance
scenarios we introduced ListPoolValue which is a ValueType with zero allocations</PackageReleaseNotes>
<Copyright>Copyright © Fausto David Suarez Rosario 2019</Copyright>
</PropertyGroup>

<ItemGroup>
Expand Down
8 changes: 7 additions & 1 deletion src/ListPool/ListPoolExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,14 @@ public static class ListPoolExtensions
{
public static ListPool<TSource> ToListPool<TSource>(this IEnumerable<TSource> source)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (source is null) throw new ArgumentNullException(nameof(source));
return new ListPool<TSource>(source);
}

public static ListPoolValue<TSource> ToListPoolValue<TSource>(this IEnumerable<TSource> source)
{
if (source is null) throw new ArgumentNullException(nameof(source));
return new ListPoolValue<TSource>(source);
}
}
}
Loading

0 comments on commit 1422aea

Please sign in to comment.