IReadOnlyList<T> & IList interfaces implemented in RecyclableLongList.
This commit is contained in:
1 parent
9e07e0181c
commit
bcab0bbfe2
3 files changed
+159
-28
No files matched your search
@@ -1,5 +1,5 @@
|
|||||||
# Recyclable.Collections
|
# Recyclable.Collections
|
||||||
`Recyclable.Collections` project is an open source framework for operating dynamic lists at performance close to raw arrays, but fairly unlimited in size. It aims at providing minimal memory footprint. It implements `IList<T>`'s interface and is targeted as direct replacements of `List<T>`, `SortableList<T>`, `PriorityQueue<T>` & similar.
|
`Recyclable.Collections` project is an open source framework for operating dynamic lists at performance close to raw arrays, but fairly unlimited in size. It aims at providing minimal memory footprint. It implements `IList<T>` interface and is targeted as direct replacements of `List<T>`, `SortableList<T>`, `PriorityQueue<T>` & similar.
|
||||||
|
|
||||||
## Included
|
## Included
|
||||||
* `RecyclableList<T>`
|
* `RecyclableList<T>`
|
||||||
@@ -78,14 +78,14 @@
|
|||||||
1. ✅ Add support for `ReadOnlySpan<T>`
|
1. ✅ Add support for `ReadOnlySpan<T>`
|
||||||
1. ✅ Release 0.0.3-alpha
|
1. ✅ Release 0.0.3-alpha
|
||||||
1. 👉 Implement `List<T>` interfaces
|
1. 👉 Implement `List<T>` interfaces
|
||||||
1. 🅿️ `ICollection<T>`
|
1. ✅ `IList<T>`
|
||||||
1. 🅿️ `IEnumerable<T>`
|
1. ✅ `ICollection<T>`
|
||||||
1. 🅿️ `IEnumerable`
|
1. ✅ `IEnumerable<T>`
|
||||||
1. 🅿️ `IList<T>`
|
1. ✅ `IEnumerable`
|
||||||
|
1. 👉 `IReadOnlyList<T>`
|
||||||
1. 🅿️ `IReadOnlyCollection<T>`
|
1. 🅿️ `IReadOnlyCollection<T>`
|
||||||
1. 🅿️ `IReadOnlyList<T>`
|
|
||||||
1. 🅿️ `ICollection`
|
|
||||||
1. 🅿️ `IList`
|
1. 🅿️ `IList`
|
||||||
|
1. 🅿️ `ICollection`
|
||||||
1. 🅿️ Implement list versioning to allow data change identification
|
1. 🅿️ Implement list versioning to allow data change identification
|
||||||
1. 🅿️ Make sure that `NeedsClearing` is used & items are cleared in
|
1. 🅿️ Make sure that `NeedsClearing` is used & items are cleared in
|
||||||
1. 🅿️ `Clear`
|
1. 🅿️ `Clear`
|
||||||
@@ -106,6 +106,7 @@
|
|||||||
1. 🅿️ Replace `blockSize` sums by powers of 2, minus 1
|
1. 🅿️ Replace `blockSize` sums by powers of 2, minus 1
|
||||||
1. 🅿️ Remove type castings, if possible
|
1. 🅿️ Remove type castings, if possible
|
||||||
1. 🅿️ Convert generic methods to non-generic
|
1. 🅿️ Convert generic methods to non-generic
|
||||||
|
1. 🅿️ Replace integer comparisons with comparisons to 0 / 1, if possible
|
||||||
1. 🅿️ Overflow review
|
1. 🅿️ Overflow review
|
||||||
1. 🅿️ Add type casting to `long` for `<<` & `>>` operations, where required
|
1. 🅿️ Add type casting to `long` for `<<` & `>>` operations, where required
|
||||||
1. 🅿️ Make type castings `checked`
|
1. 🅿️ Make type castings `checked`
|
||||||
@@ -148,6 +149,10 @@
|
|||||||
1. 🅿️ `SpinLockSlimmer`
|
1. 🅿️ `SpinLockSlimmer`
|
||||||
1. 🅿️ Multi-threading benchmarks
|
1. 🅿️ Multi-threading benchmarks
|
||||||
1. 🅿️ Release 0.0.9-beta
|
1. 🅿️ Release 0.0.9-beta
|
||||||
|
1. 🅿️ Extend unit tests
|
||||||
|
1. 🅿️ `.Add` / `.AddRange` must allow `null` values
|
||||||
|
1. 🅿️ `.Remove` / `.RemoveAt` / `.Clear` must clear reference when reference type
|
||||||
|
1. 🅿️ `RecyclableLongListExtensions.CopyTo`
|
||||||
1. 🅿️ Cleanup
|
1. 🅿️ Cleanup
|
||||||
1. 🅿️ Replace `LastBlockWithData` property with `_lastBlockWithData` field
|
1. 🅿️ Replace `LastBlockWithData` property with `_lastBlockWithData` field
|
||||||
1. 🅿️ Cleanup `RecyclableLongListExtensions`
|
1. 🅿️ Cleanup `RecyclableLongListExtensions`
|
||||||
@@ -157,6 +162,7 @@
|
|||||||
1. 🅿️ `ListExtensions`
|
1. 🅿️ `ListExtensions`
|
||||||
1. 🅿️ `MathUtils`
|
1. 🅿️ `MathUtils`
|
||||||
1. 🅿️ `SystemRandomNumberGenerator`
|
1. 🅿️ `SystemRandomNumberGenerator`
|
||||||
|
1. 🅿️ `RecyclableLongListExtensions`
|
||||||
1. 🅿️ Review and remove warnings & hints
|
1. 🅿️ Review and remove warnings & hints
|
||||||
1. 🅿️ Warnings
|
1. 🅿️ Warnings
|
||||||
1. 🅿️ Hints
|
1. 🅿️ Hints
|
||||||
@@ -166,6 +172,7 @@
|
|||||||
1. 🅿️ Release 1.0.0
|
1. 🅿️ Release 1.0.0
|
||||||
1. 🅿️ Optimize
|
1. 🅿️ Optimize
|
||||||
1. 🅿️ `RecyclableLongList<T>.Resize`
|
1. 🅿️ `RecyclableLongList<T>.Resize`
|
||||||
|
1. 🅿️ `RecyclableLongList<T>.CopyTo`
|
||||||
1. 🅿️ Check if we can benefit from Sse2 in `.IndexOf`/`.Contains` methods as given in [MS blog](https://devblogs.microsoft.com/dotnet/hardware-intrinsics-in-net-core/).
|
1. 🅿️ Check if we can benefit from Sse2 in `.IndexOf`/`.Contains` methods as given in [MS blog](https://devblogs.microsoft.com/dotnet/hardware-intrinsics-in-net-core/).
|
||||||
1. 🅿️ Add support for `ICollection<T>` interface in `.AddRange` & `constructor`
|
1. 🅿️ Add support for `ICollection<T>` interface in `.AddRange` & `constructor`
|
||||||
1. 🅿️ Release 2.0.0
|
1. 🅿️ Release 2.0.0
|
||||||
@@ -425,9 +432,3 @@ public void RecyclableListExample()
|
|||||||
list.Clear();
|
list.Clear();
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
## `RecyclableList<T>`
|
|
||||||
|
|
||||||
## `RecyclableList<T>`
|
|
||||||
|
|
||||||
## `RecyclableList<T>`
|
|
||||||
|
|
||||||
@@ -9,12 +9,15 @@ using Recyclable.Collections.Pools;
|
|||||||
|
|
||||||
namespace Recyclable.Collections
|
namespace Recyclable.Collections
|
||||||
{
|
{
|
||||||
public partial class RecyclableLongList<T> : IDisposable, IList<T>
|
public partial class RecyclableLongList<T> : IDisposable, IList<T>, IReadOnlyList<T>, IList
|
||||||
{
|
{
|
||||||
private static readonly ArrayPool<T[]> _defaultMemoryBlocksPool = ArrayPool<T[]>.Create();
|
private static readonly ArrayPool<T[]> _defaultMemoryBlocksPool = ArrayPool<T[]>.Create();
|
||||||
private static readonly ArrayPool<T> _defaultBlockArrayPool = ArrayPool<T>.Create();
|
private static readonly ArrayPool<T> _defaultBlockArrayPool = ArrayPool<T>.Create();
|
||||||
private static readonly T[][] _emptyMemoryBlocksArray = new T[0][];
|
private static readonly T[][] _emptyMemoryBlocksArray = new T[0][];
|
||||||
private static readonly T[] _emptyBlockArray = new T[0];
|
private static readonly T[] _emptyBlockArray = new T[0];
|
||||||
|
private static readonly bool _defaultIsNull = default(T) == null;
|
||||||
|
private static void ThrowIndexOutOfRangeException(in string message) => throw new ArgumentOutOfRangeException("index", message);
|
||||||
|
private static void ThrowArgumentOutOfRange(in string argumentName, in string message) => throw new ArgumentOutOfRangeException(argumentName, message);
|
||||||
|
|
||||||
internal int _blockSize;
|
internal int _blockSize;
|
||||||
internal byte _blockSizePow2BitShift;
|
internal byte _blockSizePow2BitShift;
|
||||||
@@ -102,8 +105,6 @@ namespace Recyclable.Collections
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ThrowArgumentOutOfRangeException(in string message) => throw new ArgumentOutOfRangeException("index", message);
|
|
||||||
|
|
||||||
protected void AddRangeEnumerated(IEnumerable<T> source, int growByCount)
|
protected void AddRangeEnumerated(IEnumerable<T> source, int growByCount)
|
||||||
{
|
{
|
||||||
long i, copied = _longCount;
|
long i, copied = _longCount;
|
||||||
@@ -1223,7 +1224,7 @@ namespace Recyclable.Collections
|
|||||||
{
|
{
|
||||||
if (index >= _longCount || index < 0)
|
if (index >= _longCount || index < 0)
|
||||||
{
|
{
|
||||||
ThrowArgumentOutOfRangeException($"Argument \"{nameof(index)}\" = {index} is out of range. Expected value between 0 and {_longCount - 1}");
|
ThrowIndexOutOfRangeException($"Argument \"{nameof(index)}\" = {index} is out of range. Expected value between 0 and {_longCount - 1}");
|
||||||
}
|
}
|
||||||
|
|
||||||
_longCount--;
|
_longCount--;
|
||||||
@@ -1262,7 +1263,7 @@ namespace Recyclable.Collections
|
|||||||
{
|
{
|
||||||
if (index >= _longCount || index < 0)
|
if (index >= _longCount || index < 0)
|
||||||
{
|
{
|
||||||
ThrowArgumentOutOfRangeException($"Argument \"{nameof(index)}\" = {index} is out of range. Expected value between 0 and {_longCount - 1}");
|
ThrowIndexOutOfRangeException($"Argument \"{nameof(index)}\" = {index} is out of range. Expected value between 0 and {_longCount - 1}");
|
||||||
}
|
}
|
||||||
|
|
||||||
_longCount--;
|
_longCount--;
|
||||||
@@ -1317,5 +1318,120 @@ namespace Recyclable.Collections
|
|||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region IList implementation
|
||||||
|
|
||||||
|
public bool IsFixedSize => false;
|
||||||
|
public bool IsSynchronized => false;
|
||||||
|
|
||||||
|
private object? _syncRoot;
|
||||||
|
public object SyncRoot
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_syncRoot == null)
|
||||||
|
{
|
||||||
|
_ = Interlocked.CompareExchange(ref _syncRoot, new object(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _syncRoot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object? IList.this[int index]
|
||||||
|
{
|
||||||
|
get => _memoryBlocks[index >> _blockSizePow2BitShift][index & _blockSizeMinus1];
|
||||||
|
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value is T typedValue)
|
||||||
|
{
|
||||||
|
new Span<T>(_memoryBlocks[index >> _blockSizePow2BitShift])[index & _blockSizeMinus1] = typedValue;
|
||||||
|
}
|
||||||
|
else if (value == null && _defaultIsNull)
|
||||||
|
{
|
||||||
|
#nullable disable
|
||||||
|
new Span<T>(_memoryBlocks[index >> _blockSizePow2BitShift])[index & _blockSizeMinus1] = default;
|
||||||
|
#nullable restore
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ThrowArgumentOutOfRange(nameof(value), $"Argument is invalid. {(value != null ? "List element type is incompatible with the given value" : "Element is null and the list doesn't allow null values.")}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Add(object? value)
|
||||||
|
{
|
||||||
|
if (value is T typeValue)
|
||||||
|
{
|
||||||
|
Add(typeValue);
|
||||||
|
return (int)(_longCount - 1);
|
||||||
|
}
|
||||||
|
else if (value == null && _defaultIsNull)
|
||||||
|
{
|
||||||
|
Add(null);
|
||||||
|
return (int)(_longCount - 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ThrowArgumentOutOfRange(nameof(value), $"Argument is invalid. { (value != null ? "List element type is incompatible with the given value" : "Element is null and the list doesn't allow null values.") }");
|
||||||
|
return RecyclableDefaults.ItemNotFoundIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Contains(object? value) => value is T typeValue
|
||||||
|
? Contains(typeValue)
|
||||||
|
#nullable disable
|
||||||
|
: value == null && _defaultIsNull && Contains(default);
|
||||||
|
#nullable restore
|
||||||
|
|
||||||
|
public int IndexOf(object? value) => value is T typeValue
|
||||||
|
? IndexOf(typeValue)
|
||||||
|
: value == null && _defaultIsNull
|
||||||
|
#nullable disable
|
||||||
|
? IndexOf(default)
|
||||||
|
#nullable restore
|
||||||
|
: RecyclableDefaults.ItemNotFoundIndex;
|
||||||
|
|
||||||
|
public void Insert(int index, object? value)
|
||||||
|
{
|
||||||
|
if (value is T typeValue)
|
||||||
|
{
|
||||||
|
Insert(index, typeValue);
|
||||||
|
}
|
||||||
|
else if (value == null && _defaultIsNull)
|
||||||
|
{
|
||||||
|
#nullable disable
|
||||||
|
Insert(index, default);
|
||||||
|
#nullable restore
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ThrowArgumentOutOfRange(nameof(value), $"Argument is invalid. {(value != null ? "List element type is incompatible with the given value" : "Element is null and the list doesn't allow null values.")}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Remove(object? value)
|
||||||
|
{
|
||||||
|
if (value is T typeValue)
|
||||||
|
{
|
||||||
|
_ = Remove(typeValue);
|
||||||
|
}
|
||||||
|
else if (value == null && _defaultIsNull)
|
||||||
|
{
|
||||||
|
#nullable disable
|
||||||
|
_ = Remove(default);
|
||||||
|
#nullable restore
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ThrowArgumentOutOfRange(nameof(value), $"Argument is invalid. {(value != null ? "List element type is incompatible with the given value" : "Element is null and the list doesn't allow null values.")}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CopyTo(Array array, int index) => _memoryBlocks.CopyTo(0, _blockSize, _longCount, array, index);
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,16 +6,6 @@ namespace Recyclable.Collections
|
|||||||
internal static class RecyclableLongListExtensions
|
internal static class RecyclableLongListExtensions
|
||||||
{
|
{
|
||||||
private const int _minPooledArraySize = RecyclableDefaults.MinPooledArrayLength;
|
private const int _minPooledArraySize = RecyclableDefaults.MinPooledArrayLength;
|
||||||
private const long ItemNotFoundIndexLong = -1L;
|
|
||||||
private const int ItemNotFoundIndex = -1;
|
|
||||||
|
|
||||||
public static void CopyItems<T>(this T[] memory, long startingIndex, long count, ref T[] destArray, long offset = 1)
|
|
||||||
{
|
|
||||||
for (long destItemIndex = startingIndex, sourceItemIndex = offset; destItemIndex < startingIndex + count; destItemIndex++, sourceItemIndex++)
|
|
||||||
{
|
|
||||||
destArray[destItemIndex] = memory[sourceItemIndex];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void CopyTo<T>(this T[][] sourceMemoryBlocks, long startingIndex, int blockSize, long itemsCount, T[] destinationArray, int destinationArrayIndex)
|
public static void CopyTo<T>(this T[][] sourceMemoryBlocks, long startingIndex, int blockSize, long itemsCount, T[] destinationArray, int destinationArrayIndex)
|
||||||
{
|
{
|
||||||
@@ -45,6 +35,30 @@ namespace Recyclable.Collections
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void CopyTo<T>(this T[][] sourceMemoryBlocks, long startingIndex, int blockSize, long itemsCount, Array destinationArray, int destinationArrayIndex)
|
||||||
|
{
|
||||||
|
if (itemsCount <= 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int startingBlockIndex = (int)(startingIndex / blockSize);
|
||||||
|
int lastBlockIndex = (int)((itemsCount / blockSize) + (itemsCount % blockSize > 0 ? 1 : 0)) - 1;
|
||||||
|
|
||||||
|
var destinationIndex = destinationArrayIndex;
|
||||||
|
Span<T[]> sourceMemoryBlocksSpan = new(sourceMemoryBlocks, startingBlockIndex, lastBlockIndex + 1);
|
||||||
|
for (int memoryBlockIndex = 0; memoryBlockIndex < lastBlockIndex; memoryBlockIndex++)
|
||||||
|
{
|
||||||
|
Array.ConstrainedCopy(sourceMemoryBlocksSpan[memoryBlockIndex], 0, destinationArray, destinationIndex, blockSize);
|
||||||
|
destinationIndex += blockSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (itemsCount - destinationIndex > 0)
|
||||||
|
{
|
||||||
|
Array.ConstrainedCopy(sourceMemoryBlocksSpan[lastBlockIndex], 0, destinationArray, destinationIndex, (int)(itemsCount - destinationIndex));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
//public static bool Contains<T>(this RecyclableLongList<T[]> arrays, T item) => arrays.Any(x => x.Contains(item));
|
//public static bool Contains<T>(this RecyclableLongList<T[]> arrays, T item) => arrays.Any(x => x.Contains(item));
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user