Merge pull request #8 from mlemanczyk/codex/zaimplementuj-recyclablequeue-z-linkedlistt
Fix missing blank line in RecyclableLinkedList
This commit is contained in:
8 files changed
+430
-32
No files matched your search
@@ -6,7 +6,7 @@ namespace Recyclable.Collections.Pools
|
|||||||
|
|
||||||
internal static T[] Empty => _empty;
|
internal static T[] Empty => _empty;
|
||||||
|
|
||||||
internal T[] Buffer;
|
internal T[] Value;
|
||||||
internal int Top;
|
internal int Top;
|
||||||
internal int Bottom;
|
internal int Bottom;
|
||||||
internal BiDirectionalRecyclableArrayPoolChunk<T>? Previous;
|
internal BiDirectionalRecyclableArrayPoolChunk<T>? Previous;
|
||||||
@@ -14,7 +14,7 @@ namespace Recyclable.Collections.Pools
|
|||||||
|
|
||||||
public BiDirectionalRecyclableArrayPoolChunk()
|
public BiDirectionalRecyclableArrayPoolChunk()
|
||||||
{
|
{
|
||||||
Buffer = _empty;
|
Value = _empty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -20,13 +20,13 @@ namespace Recyclable.Collections.Pools
|
|||||||
|
|
||||||
public static void Dispose(BiDirectionalRecyclableArrayPoolChunk<T> chunk, bool needsClearing)
|
public static void Dispose(BiDirectionalRecyclableArrayPoolChunk<T> chunk, bool needsClearing)
|
||||||
{
|
{
|
||||||
var buffer = chunk.Buffer;
|
var buffer = chunk.Value;
|
||||||
if (buffer.Length >= RecyclableDefaults.MinPooledArrayLength)
|
if (buffer.Length >= RecyclableDefaults.MinPooledArrayLength)
|
||||||
{
|
{
|
||||||
RecyclableArrayPool<T>.ReturnShared(buffer, needsClearing);
|
RecyclableArrayPool<T>.ReturnShared(buffer, needsClearing);
|
||||||
}
|
}
|
||||||
|
|
||||||
chunk.Buffer = BiDirectionalRecyclableArrayPoolChunk<T>.Empty;
|
chunk.Value = BiDirectionalRecyclableArrayPoolChunk<T>.Empty;
|
||||||
chunk.Top = 0;
|
chunk.Top = 0;
|
||||||
chunk.Bottom = 0;
|
chunk.Bottom = 0;
|
||||||
chunk.Previous = null;
|
chunk.Previous = null;
|
||||||
|
|||||||
@@ -6,14 +6,14 @@ namespace Recyclable.Collections.Pools
|
|||||||
|
|
||||||
internal static T[] Empty => _empty;
|
internal static T[] Empty => _empty;
|
||||||
|
|
||||||
internal T[] Buffer;
|
internal T[] Value;
|
||||||
internal int Index;
|
internal int Index;
|
||||||
internal RecyclableArrayPoolChunk<T>? Previous;
|
internal RecyclableArrayPoolChunk<T>? Previous;
|
||||||
internal RecyclableArrayPoolChunk<T>? Next;
|
internal RecyclableArrayPoolChunk<T>? Next;
|
||||||
|
|
||||||
public RecyclableArrayPoolChunk()
|
public RecyclableArrayPoolChunk()
|
||||||
{
|
{
|
||||||
Buffer = _empty;
|
Value = _empty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -19,12 +19,12 @@ namespace Recyclable.Collections.Pools
|
|||||||
|
|
||||||
public static void Dispose(RecyclableArrayPoolChunk<T> chunk, bool needsClearing)
|
public static void Dispose(RecyclableArrayPoolChunk<T> chunk, bool needsClearing)
|
||||||
{
|
{
|
||||||
var buffer = chunk.Buffer;
|
var buffer = chunk.Value;
|
||||||
if (buffer.Length >= RecyclableDefaults.MinPooledArrayLength)
|
if (buffer.Length >= RecyclableDefaults.MinPooledArrayLength)
|
||||||
{
|
{
|
||||||
RecyclableArrayPool<T>.ReturnShared(buffer, needsClearing);
|
RecyclableArrayPool<T>.ReturnShared(buffer, needsClearing);
|
||||||
}
|
}
|
||||||
chunk.Buffer = RecyclableArrayPoolChunk<T>.Empty;
|
chunk.Value = RecyclableArrayPoolChunk<T>.Empty;
|
||||||
chunk.Index = 0;
|
chunk.Index = 0;
|
||||||
chunk.Previous = null;
|
chunk.Previous = null;
|
||||||
chunk.Next = null;
|
chunk.Next = null;
|
||||||
|
|||||||
@@ -0,0 +1,323 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using System.Numerics;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using Recyclable.Collections.Pools;
|
||||||
|
|
||||||
|
namespace Recyclable.Collections
|
||||||
|
{
|
||||||
|
internal sealed class RecyclableLinkedList<T> : IEnumerable<T>, IDisposable
|
||||||
|
{
|
||||||
|
private static readonly bool _needsClearing = !typeof(T).IsValueType;
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static BiDirectionalRecyclableArrayPoolChunk<T> RentChunk(int size, BiDirectionalRecyclableArrayPoolChunk<T>? previous, bool forHead)
|
||||||
|
{
|
||||||
|
var chunk = BiDirectionalRecyclableArrayPoolChunkPool<T>.Rent();
|
||||||
|
if (chunk.Value.Length < size)
|
||||||
|
{
|
||||||
|
if (chunk.Value.Length >= RecyclableDefaults.MinPooledArrayLength)
|
||||||
|
{
|
||||||
|
RecyclableArrayPool<T>.ReturnShared(chunk.Value, _needsClearing);
|
||||||
|
}
|
||||||
|
|
||||||
|
chunk.Value = size >= RecyclableDefaults.MinPooledArrayLength
|
||||||
|
? RecyclableArrayPool<T>.RentShared(size)
|
||||||
|
: new T[size];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (forHead)
|
||||||
|
{
|
||||||
|
chunk.Top = size;
|
||||||
|
chunk.Bottom = size;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
chunk.Top = 0;
|
||||||
|
chunk.Bottom = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
chunk.Previous = previous;
|
||||||
|
chunk.Next = null;
|
||||||
|
return chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static void ReturnChunk(BiDirectionalRecyclableArrayPoolChunk<T> chunk)
|
||||||
|
{
|
||||||
|
BiDirectionalRecyclableArrayPoolChunkPool<T>.Return(chunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static void DisposeChunk(BiDirectionalRecyclableArrayPoolChunk<T> chunk)
|
||||||
|
{
|
||||||
|
BiDirectionalRecyclableArrayPoolChunkPool<T>.Dispose(chunk, _needsClearing);
|
||||||
|
}
|
||||||
|
|
||||||
|
private BiDirectionalRecyclableArrayPoolChunk<T> _head;
|
||||||
|
private BiDirectionalRecyclableArrayPoolChunk<T> _tail;
|
||||||
|
private bool _disposed;
|
||||||
|
private long _capacity;
|
||||||
|
private long _count;
|
||||||
|
|
||||||
|
public RecyclableLinkedList(int initialCapacity = RecyclableDefaults.InitialCapacity)
|
||||||
|
{
|
||||||
|
if (initialCapacity < 1)
|
||||||
|
{
|
||||||
|
initialCapacity = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!BitOperations.IsPow2((uint)initialCapacity))
|
||||||
|
{
|
||||||
|
initialCapacity = (int)BitOperations.RoundUpToPowerOf2((uint)initialCapacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
_head = RentChunk(initialCapacity, null, false);
|
||||||
|
_tail = _head;
|
||||||
|
_capacity = initialCapacity;
|
||||||
|
_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RecyclableLinkedList(IEnumerable<T> source, int initialCapacity = RecyclableDefaults.InitialCapacity) : this(initialCapacity)
|
||||||
|
{
|
||||||
|
foreach (var item in source)
|
||||||
|
{
|
||||||
|
AddLast(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Count => checked((int)_count);
|
||||||
|
public long LongCount => _count;
|
||||||
|
|
||||||
|
public void AddLast(T item)
|
||||||
|
{
|
||||||
|
if (_tail.Top == _tail.Value.Length)
|
||||||
|
{
|
||||||
|
GrowTail();
|
||||||
|
}
|
||||||
|
|
||||||
|
_tail.Value[_tail.Top++] = item;
|
||||||
|
_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddFirst(T item)
|
||||||
|
{
|
||||||
|
if (_head.Bottom == 0)
|
||||||
|
{
|
||||||
|
GrowHead();
|
||||||
|
}
|
||||||
|
|
||||||
|
_head.Value[--_head.Bottom] = item;
|
||||||
|
_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T RemoveFirst()
|
||||||
|
{
|
||||||
|
if (_count == 0)
|
||||||
|
{
|
||||||
|
ThrowHelper.ThrowArgumentOutOfRangeException(nameof(_count), "List is empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
_count--;
|
||||||
|
var value = _head.Value[_head.Bottom];
|
||||||
|
if (_needsClearing)
|
||||||
|
{
|
||||||
|
_head.Value[_head.Bottom] = default!;
|
||||||
|
}
|
||||||
|
|
||||||
|
_head.Bottom++;
|
||||||
|
if (_head.Bottom == _head.Top && _head.Next != null)
|
||||||
|
{
|
||||||
|
ReleaseHeadChunk();
|
||||||
|
}
|
||||||
|
else if (_head.Bottom == _head.Top)
|
||||||
|
{
|
||||||
|
_head.Bottom = _head.Top;
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T RemoveLast()
|
||||||
|
{
|
||||||
|
if (_count == 0)
|
||||||
|
{
|
||||||
|
ThrowHelper.ThrowArgumentOutOfRangeException(nameof(_count), "List is empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
_count--;
|
||||||
|
_tail.Top--;
|
||||||
|
var value = _tail.Value[_tail.Top];
|
||||||
|
if (_needsClearing)
|
||||||
|
{
|
||||||
|
_tail.Value[_tail.Top] = default!;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_tail.Top == _tail.Bottom && _tail.Previous != null)
|
||||||
|
{
|
||||||
|
ReleaseTailChunk();
|
||||||
|
}
|
||||||
|
else if (_tail.Top == _tail.Bottom)
|
||||||
|
{
|
||||||
|
_tail.Top = _tail.Bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
while (_head.Next != null)
|
||||||
|
{
|
||||||
|
ReleaseHeadChunk();
|
||||||
|
}
|
||||||
|
|
||||||
|
while (_tail.Previous != null)
|
||||||
|
{
|
||||||
|
ReleaseTailChunk();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_needsClearing && _head.Top > _head.Bottom)
|
||||||
|
{
|
||||||
|
Array.Clear(_head.Value, _head.Bottom, _head.Top - _head.Bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
_capacity = _head.Value.Length;
|
||||||
|
_head.Bottom = _head.Top;
|
||||||
|
_tail = _head;
|
||||||
|
_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Enumerator GetEnumerator() => new(this);
|
||||||
|
IEnumerator<T> IEnumerable<T>.GetEnumerator() => new Enumerator(this);
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this);
|
||||||
|
|
||||||
|
private void GrowTail()
|
||||||
|
{
|
||||||
|
int newSize;
|
||||||
|
if (_capacity >= RecyclableDefaults.MaxPooledBlockSize)
|
||||||
|
{
|
||||||
|
newSize = RecyclableDefaults.MaxPooledBlockSize;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var doubled = _capacity << 1;
|
||||||
|
newSize = (int)Math.Min(doubled - _capacity, RecyclableDefaults.MaxPooledBlockSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
var newChunk = RentChunk(newSize, _tail, false);
|
||||||
|
_tail.Next = newChunk;
|
||||||
|
_tail = newChunk;
|
||||||
|
_capacity += newSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GrowHead()
|
||||||
|
{
|
||||||
|
int newSize;
|
||||||
|
if (_capacity >= RecyclableDefaults.MaxPooledBlockSize)
|
||||||
|
{
|
||||||
|
newSize = RecyclableDefaults.MaxPooledBlockSize;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var doubled = _capacity << 1;
|
||||||
|
newSize = (int)Math.Min(doubled - _capacity, RecyclableDefaults.MaxPooledBlockSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
var newChunk = RentChunk(newSize, null, true);
|
||||||
|
newChunk.Next = _head;
|
||||||
|
_head.Previous = newChunk;
|
||||||
|
_head = newChunk;
|
||||||
|
_capacity += newSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReleaseHeadChunk()
|
||||||
|
{
|
||||||
|
var toReturn = _head;
|
||||||
|
_head = toReturn.Next!;
|
||||||
|
_head.Previous = null;
|
||||||
|
_capacity -= toReturn.Value.Length;
|
||||||
|
ReturnChunk(toReturn);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReleaseTailChunk()
|
||||||
|
{
|
||||||
|
var toReturn = _tail;
|
||||||
|
_tail = toReturn.Previous!;
|
||||||
|
_tail.Next = null;
|
||||||
|
_capacity -= toReturn.Value.Length;
|
||||||
|
ReturnChunk(toReturn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_disposed)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var chunk = _head;
|
||||||
|
while (chunk != null)
|
||||||
|
{
|
||||||
|
var next = chunk.Next;
|
||||||
|
DisposeChunk(chunk);
|
||||||
|
chunk = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
_disposed = true;
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct Enumerator : IEnumerator<T>
|
||||||
|
{
|
||||||
|
private readonly BiDirectionalRecyclableArrayPoolChunk<T>? _start;
|
||||||
|
private BiDirectionalRecyclableArrayPoolChunk<T>? _chunk;
|
||||||
|
private int _index;
|
||||||
|
private T? _current;
|
||||||
|
|
||||||
|
internal Enumerator(RecyclableLinkedList<T> list)
|
||||||
|
{
|
||||||
|
_start = list._head;
|
||||||
|
_chunk = list._head;
|
||||||
|
_index = _chunk != null ? _chunk.Bottom : 0;
|
||||||
|
_current = default;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T Current => _current!;
|
||||||
|
object IEnumerator.Current => _current!;
|
||||||
|
|
||||||
|
public bool MoveNext()
|
||||||
|
{
|
||||||
|
if (_chunk == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_index >= _chunk.Top)
|
||||||
|
{
|
||||||
|
_chunk = _chunk.Next;
|
||||||
|
if (_chunk == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_index = _chunk.Bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
_current = _chunk.Value[_index++];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
_chunk = _start;
|
||||||
|
_index = _chunk != null ? _chunk.Bottom : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_chunk = null;
|
||||||
|
_current = default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -13,14 +13,14 @@ namespace Recyclable.Collections
|
|||||||
private static BiDirectionalRecyclableArrayPoolChunk<T> RentChunk(int size, BiDirectionalRecyclableArrayPoolChunk<T>? previous)
|
private static BiDirectionalRecyclableArrayPoolChunk<T> RentChunk(int size, BiDirectionalRecyclableArrayPoolChunk<T>? previous)
|
||||||
{
|
{
|
||||||
var chunk = BiDirectionalRecyclableArrayPoolChunkPool<T>.Rent();
|
var chunk = BiDirectionalRecyclableArrayPoolChunkPool<T>.Rent();
|
||||||
if (chunk.Buffer.Length < size)
|
if (chunk.Value.Length < size)
|
||||||
{
|
{
|
||||||
if (chunk.Buffer.Length >= RecyclableDefaults.MinPooledArrayLength)
|
if (chunk.Value.Length >= RecyclableDefaults.MinPooledArrayLength)
|
||||||
{
|
{
|
||||||
RecyclableArrayPool<T>.ReturnShared(chunk.Buffer, _needsClearing);
|
RecyclableArrayPool<T>.ReturnShared(chunk.Value, _needsClearing);
|
||||||
}
|
}
|
||||||
|
|
||||||
chunk.Buffer = size >= RecyclableDefaults.MinPooledArrayLength
|
chunk.Value = size >= RecyclableDefaults.MinPooledArrayLength
|
||||||
? RecyclableArrayPool<T>.RentShared(size)
|
? RecyclableArrayPool<T>.RentShared(size)
|
||||||
: new T[size];
|
: new T[size];
|
||||||
}
|
}
|
||||||
@@ -81,12 +81,12 @@ namespace Recyclable.Collections
|
|||||||
|
|
||||||
public void Enqueue(T item)
|
public void Enqueue(T item)
|
||||||
{
|
{
|
||||||
if (_tail.Top == _tail.Buffer.Length)
|
if (_tail.Top == _tail.Value.Length)
|
||||||
{
|
{
|
||||||
Grow();
|
Grow();
|
||||||
}
|
}
|
||||||
|
|
||||||
_tail.Buffer[_tail.Top++] = item;
|
_tail.Value[_tail.Top++] = item;
|
||||||
_count++;
|
_count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,10 +100,10 @@ namespace Recyclable.Collections
|
|||||||
}
|
}
|
||||||
|
|
||||||
_count--;
|
_count--;
|
||||||
var value = _head.Buffer[_head.Bottom];
|
var value = _head.Value[_head.Bottom];
|
||||||
if (_needsClearing)
|
if (_needsClearing)
|
||||||
{
|
{
|
||||||
_head.Buffer[_head.Bottom] = default!;
|
_head.Value[_head.Bottom] = default!;
|
||||||
}
|
}
|
||||||
|
|
||||||
_head.Bottom++;
|
_head.Bottom++;
|
||||||
@@ -141,10 +141,10 @@ namespace Recyclable.Collections
|
|||||||
|
|
||||||
if (_needsClearing && _head.Top > _head.Bottom)
|
if (_needsClearing && _head.Top > _head.Bottom)
|
||||||
{
|
{
|
||||||
Array.Clear(_head.Buffer, _head.Bottom, _head.Top - _head.Bottom);
|
Array.Clear(_head.Value, _head.Bottom, _head.Top - _head.Bottom);
|
||||||
}
|
}
|
||||||
|
|
||||||
_capacity = _head.Buffer.Length;
|
_capacity = _head.Value.Length;
|
||||||
_head.Top = 0;
|
_head.Top = 0;
|
||||||
_head.Bottom = 0;
|
_head.Bottom = 0;
|
||||||
_tail = _head;
|
_tail = _head;
|
||||||
@@ -179,7 +179,7 @@ namespace Recyclable.Collections
|
|||||||
var toReturn = _head;
|
var toReturn = _head;
|
||||||
_head = toReturn.Next!;
|
_head = toReturn.Next!;
|
||||||
_head.Previous = null;
|
_head.Previous = null;
|
||||||
_capacity -= toReturn.Buffer.Length;
|
_capacity -= toReturn.Value.Length;
|
||||||
ReturnChunk(toReturn);
|
ReturnChunk(toReturn);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,7 +237,7 @@ namespace Recyclable.Collections
|
|||||||
_index = _chunk.Bottom;
|
_index = _chunk.Bottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
_current = _chunk.Buffer[_index++];
|
_current = _chunk.Value[_index++];
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,14 +13,14 @@ namespace Recyclable.Collections
|
|||||||
private static RecyclableArrayPoolChunk<T> RentChunk(int size, RecyclableArrayPoolChunk<T>? previous)
|
private static RecyclableArrayPoolChunk<T> RentChunk(int size, RecyclableArrayPoolChunk<T>? previous)
|
||||||
{
|
{
|
||||||
var chunk = RecyclableArrayPoolChunkPool<T>.Rent();
|
var chunk = RecyclableArrayPoolChunkPool<T>.Rent();
|
||||||
if (chunk.Buffer.Length < size)
|
if (chunk.Value.Length < size)
|
||||||
{
|
{
|
||||||
if (chunk.Buffer.Length >= RecyclableDefaults.MinPooledArrayLength)
|
if (chunk.Value.Length >= RecyclableDefaults.MinPooledArrayLength)
|
||||||
{
|
{
|
||||||
RecyclableArrayPool<T>.ReturnShared(chunk.Buffer, _needsClearing);
|
RecyclableArrayPool<T>.ReturnShared(chunk.Value, _needsClearing);
|
||||||
}
|
}
|
||||||
|
|
||||||
chunk.Buffer = size >= RecyclableDefaults.MinPooledArrayLength
|
chunk.Value = size >= RecyclableDefaults.MinPooledArrayLength
|
||||||
? RecyclableArrayPool<T>.RentShared(size)
|
? RecyclableArrayPool<T>.RentShared(size)
|
||||||
: new T[size];
|
: new T[size];
|
||||||
}
|
}
|
||||||
@@ -77,12 +77,12 @@ namespace Recyclable.Collections
|
|||||||
|
|
||||||
public void Push(T item)
|
public void Push(T item)
|
||||||
{
|
{
|
||||||
if (_current.Index == _current.Buffer.Length)
|
if (_current.Index == _current.Value.Length)
|
||||||
{
|
{
|
||||||
Grow();
|
Grow();
|
||||||
}
|
}
|
||||||
|
|
||||||
_current.Buffer[_current.Index++] = item;
|
_current.Value[_current.Index++] = item;
|
||||||
_count++;
|
_count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,10 +97,10 @@ namespace Recyclable.Collections
|
|||||||
|
|
||||||
_count--;
|
_count--;
|
||||||
_current.Index--;
|
_current.Index--;
|
||||||
var value = _current.Buffer[_current.Index];
|
var value = _current.Value[_current.Index];
|
||||||
if (_needsClearing)
|
if (_needsClearing)
|
||||||
{
|
{
|
||||||
_current.Buffer[_current.Index] = default!;
|
_current.Value[_current.Index] = default!;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_current.Index == 0 && _current.Previous != null)
|
if (_current.Index == 0 && _current.Previous != null)
|
||||||
@@ -120,10 +120,10 @@ namespace Recyclable.Collections
|
|||||||
|
|
||||||
if (_needsClearing && _current.Index > 0)
|
if (_needsClearing && _current.Index > 0)
|
||||||
{
|
{
|
||||||
Array.Clear(_current.Buffer, 0, _current.Index);
|
Array.Clear(_current.Value, 0, _current.Index);
|
||||||
}
|
}
|
||||||
|
|
||||||
_capacity = _current.Buffer.Length;
|
_capacity = _current.Value.Length;
|
||||||
_current.Index = 0;
|
_current.Index = 0;
|
||||||
_count = 0;
|
_count = 0;
|
||||||
}
|
}
|
||||||
@@ -156,7 +156,7 @@ namespace Recyclable.Collections
|
|||||||
var toReturn = _current;
|
var toReturn = _current;
|
||||||
_current = toReturn.Previous!;
|
_current = toReturn.Previous!;
|
||||||
_current.Next = null;
|
_current.Next = null;
|
||||||
_capacity -= toReturn.Buffer.Length;
|
_capacity -= toReturn.Value.Length;
|
||||||
ReturnChunk(toReturn);
|
ReturnChunk(toReturn);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,7 +214,7 @@ namespace Recyclable.Collections
|
|||||||
_index = _chunk.Index - 1;
|
_index = _chunk.Index - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
_current = _chunk.Buffer[_index--];
|
_current = _chunk.Value[_index--];
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
using FluentAssertions;
|
||||||
|
using Recyclable.Collections;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Recyclable.CollectionsTests
|
||||||
|
{
|
||||||
|
public class RecyclableLinkedListTests
|
||||||
|
{
|
||||||
|
private static readonly string[] _testData = new[] { "a", "d", "c", "b", "a" };
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddLastShouldPreserveOrder()
|
||||||
|
{
|
||||||
|
using var list = new RecyclableLinkedList<string>();
|
||||||
|
foreach (var item in _testData)
|
||||||
|
{
|
||||||
|
list.AddLast(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
var actual = new RecyclableLongList<string>();
|
||||||
|
foreach (var item in list)
|
||||||
|
{
|
||||||
|
actual.Add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = actual.Should().ContainInConsecutiveOrder(_testData)
|
||||||
|
.And.BeEquivalentTo(_testData);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddFirstShouldReverseOrder()
|
||||||
|
{
|
||||||
|
using var list = new RecyclableLinkedList<string>();
|
||||||
|
foreach (var item in _testData)
|
||||||
|
{
|
||||||
|
list.AddFirst(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
var expected = _testData.Reverse().ToArray();
|
||||||
|
var actual = new RecyclableLongList<string>();
|
||||||
|
foreach (var item in list)
|
||||||
|
{
|
||||||
|
actual.Add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = actual.Should().ContainInConsecutiveOrder(expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RemoveFirstShouldReturnItemsInOrder()
|
||||||
|
{
|
||||||
|
using var list = new RecyclableLinkedList<string>(_testData);
|
||||||
|
var actual = new RecyclableLongList<string>();
|
||||||
|
while (list.LongCount > 0)
|
||||||
|
{
|
||||||
|
actual.Add(list.RemoveFirst());
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = actual.Should().ContainInConsecutiveOrder(_testData);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RemoveLastShouldReturnItemsInReverseOrder()
|
||||||
|
{
|
||||||
|
using var list = new RecyclableLinkedList<string>(_testData);
|
||||||
|
var actual = new RecyclableLongList<string>();
|
||||||
|
while (list.LongCount > 0)
|
||||||
|
{
|
||||||
|
actual.Add(list.RemoveLast());
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = actual.Should().ContainInConsecutiveOrder(_testData.Reverse());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user