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` 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
|
||||
* `RecyclableList<T>`
|
||||
@@ -78,14 +78,14 @@
|
||||
1. ✅ Add support for `ReadOnlySpan<T>`
|
||||
1. ✅ Release 0.0.3-alpha
|
||||
1. 👉 Implement `List<T>` interfaces
|
||||
1. 🅿️ `ICollection<T>`
|
||||
1. 🅿️ `IEnumerable<T>`
|
||||
1. 🅿️ `IEnumerable`
|
||||
1. 🅿️ `IList<T>`
|
||||
1. ✅ `IList<T>`
|
||||
1. ✅ `ICollection<T>`
|
||||
1. ✅ `IEnumerable<T>`
|
||||
1. ✅ `IEnumerable`
|
||||
1. 👉 `IReadOnlyList<T>`
|
||||
1. 🅿️ `IReadOnlyCollection<T>`
|
||||
1. 🅿️ `IReadOnlyList<T>`
|
||||
1. 🅿️ `ICollection`
|
||||
1. 🅿️ `IList`
|
||||
1. 🅿️ `ICollection`
|
||||
1. 🅿️ Implement list versioning to allow data change identification
|
||||
1. 🅿️ Make sure that `NeedsClearing` is used & items are cleared in
|
||||
1. 🅿️ `Clear`
|
||||
@@ -106,6 +106,7 @@
|
||||
1. 🅿️ Replace `blockSize` sums by powers of 2, minus 1
|
||||
1. 🅿️ Remove type castings, if possible
|
||||
1. 🅿️ Convert generic methods to non-generic
|
||||
1. 🅿️ Replace integer comparisons with comparisons to 0 / 1, if possible
|
||||
1. 🅿️ Overflow review
|
||||
1. 🅿️ Add type casting to `long` for `<<` & `>>` operations, where required
|
||||
1. 🅿️ Make type castings `checked`
|
||||
@@ -148,6 +149,10 @@
|
||||
1. 🅿️ `SpinLockSlimmer`
|
||||
1. 🅿️ Multi-threading benchmarks
|
||||
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. 🅿️ Replace `LastBlockWithData` property with `_lastBlockWithData` field
|
||||
1. 🅿️ Cleanup `RecyclableLongListExtensions`
|
||||
@@ -157,6 +162,7 @@
|
||||
1. 🅿️ `ListExtensions`
|
||||
1. 🅿️ `MathUtils`
|
||||
1. 🅿️ `SystemRandomNumberGenerator`
|
||||
1. 🅿️ `RecyclableLongListExtensions`
|
||||
1. 🅿️ Review and remove warnings & hints
|
||||
1. 🅿️ Warnings
|
||||
1. 🅿️ Hints
|
||||
@@ -166,6 +172,7 @@
|
||||
1. 🅿️ Release 1.0.0
|
||||
1. 🅿️ Optimize
|
||||
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. 🅿️ Add support for `ICollection<T>` interface in `.AddRange` & `constructor`
|
||||
1. 🅿️ Release 2.0.0
|
||||
@@ -425,9 +432,3 @@ public void RecyclableListExample()
|
||||
list.Clear();
|
||||
}
|
||||
```
|
||||
## `RecyclableList<T>`
|
||||
|
||||
## `RecyclableList<T>`
|
||||
|
||||
## `RecyclableList<T>`
|
||||
|
||||
@@ -9,12 +9,15 @@ using Recyclable.Collections.Pools;
|
||||
|
||||
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> _defaultBlockArrayPool = ArrayPool<T>.Create();
|
||||
private static readonly T[][] _emptyMemoryBlocksArray = 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 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)
|
||||
{
|
||||
long i, copied = _longCount;
|
||||
@@ -1223,7 +1224,7 @@ namespace Recyclable.Collections
|
||||
{
|
||||
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--;
|
||||
@@ -1262,7 +1263,7 @@ namespace Recyclable.Collections
|
||||
{
|
||||
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--;
|
||||
@@ -1317,5 +1318,120 @@ namespace Recyclable.Collections
|
||||
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
|
||||
{
|
||||
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)
|
||||
{
|
||||
@@ -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)]
|
||||
//public static bool Contains<T>(this RecyclableLongList<T[]> arrays, T item) => arrays.Any(x => x.Contains(item));
|
||||
|
||||
|
||||
Reference in new issue
Block a user