diff --git a/AGENTS.md b/AGENTS.md
index 21a44e3..6dd4a2f 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -2,6 +2,13 @@
This repository hosts various libraries distributed as NuGet packages. Keep these links up to date so contributors and tools can easily locate them.
+Only the .NET SDK **8.0** is available in the execution environment. Use this version when building or running tests.
+
+All `*.csproj` files should enable nullable reference types with `enable`.
+Indent code using four spaces or a single tab per level. Place one blank line after each closing curly brace of a code block, except when several closing braces appear sequentially. Always use braces for `if` and `else` bodies.
+All tests must use **xUnit** together with **FluentAssertions**.
+Implementations should be optimized for maximum performance even if that introduces some code duplication. Consider applying attributes such as `MethodImplOptions.AggressiveInlining` or faster alternatives to common operations when appropriate.
+
## NuGet packages
- [Recyclable.Collections](https://github.com/mlemanczyk/Recyclable.Collections)
- [Recyclable.Collections.Compatibility.List](https://github.com/mlemanczyk/Recyclable.Collections.Compatibility.List)
diff --git a/Recyclable.Collections/Pools/RecyclableArrayPoolChunk.cs b/Recyclable.Collections/Pools/RecyclableArrayPoolChunk.cs
new file mode 100644
index 0000000..847c3cd
--- /dev/null
+++ b/Recyclable.Collections/Pools/RecyclableArrayPoolChunk.cs
@@ -0,0 +1,19 @@
+namespace Recyclable.Collections.Pools
+{
+ public sealed class RecyclableArrayPoolChunk
+ {
+ private static readonly T[] _empty = Array.Empty();
+
+ internal static T[] Empty => _empty;
+
+ internal T[] Buffer;
+ internal int Index;
+ internal RecyclableArrayPoolChunk? Previous;
+ internal RecyclableArrayPoolChunk? Next;
+
+ public RecyclableArrayPoolChunk()
+ {
+ Buffer = _empty;
+ }
+ }
+}
diff --git a/Recyclable.Collections/Pools/RecyclableArrayPoolChunkPool.cs b/Recyclable.Collections/Pools/RecyclableArrayPoolChunkPool.cs
new file mode 100644
index 0000000..2738239
--- /dev/null
+++ b/Recyclable.Collections/Pools/RecyclableArrayPoolChunkPool.cs
@@ -0,0 +1,34 @@
+using Microsoft.Extensions.ObjectPool;
+
+namespace Recyclable.Collections.Pools
+{
+ public static class RecyclableArrayPoolChunkPool
+ {
+ private static readonly ObjectPool> _pool =
+ new DefaultObjectPool>(new DefaultPooledObjectPolicy>());
+
+ public static RecyclableArrayPoolChunk Rent() => _pool.Get();
+
+ public static void Return(RecyclableArrayPoolChunk chunk)
+ {
+ chunk.Index = 0;
+ chunk.Previous = null;
+ chunk.Next = null;
+ _pool.Return(chunk);
+ }
+
+ public static void Dispose(RecyclableArrayPoolChunk chunk, bool needsClearing)
+ {
+ var buffer = chunk.Buffer;
+ if (buffer.Length >= RecyclableDefaults.MinPooledArrayLength)
+ {
+ RecyclableArrayPool.ReturnShared(buffer, needsClearing);
+ }
+ chunk.Buffer = RecyclableArrayPoolChunk.Empty;
+ chunk.Index = 0;
+ chunk.Previous = null;
+ chunk.Next = null;
+ _pool.Return(chunk);
+ }
+ }
+}
diff --git a/Recyclable.Collections/RecyclableStack.cs b/Recyclable.Collections/RecyclableStack.cs
index c84cf0e..21f8403 100644
--- a/Recyclable.Collections/RecyclableStack.cs
+++ b/Recyclable.Collections/RecyclableStack.cs
@@ -1,6 +1,5 @@
using System.Collections;
using System.Numerics;
-using Microsoft.Extensions.ObjectPool;
using System.Runtime.CompilerServices;
using Recyclable.Collections.Pools;
@@ -9,23 +8,22 @@ namespace Recyclable.Collections
internal sealed class RecyclableStack : IEnumerable, IDisposable
{
private static readonly bool _needsClearing = !typeof(T).IsValueType;
- private static readonly ObjectPool _chunkPool = new DefaultObjectPool(new DefaultPooledObjectPolicy());
-
- private sealed class Chunk
- {
- internal T[] Buffer = Array.Empty();
- internal int Index;
- internal Chunk? Previous;
- internal Chunk? Next;
- }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static Chunk RentChunk(int size, Chunk? previous)
+ private static RecyclableArrayPoolChunk RentChunk(int size, RecyclableArrayPoolChunk? previous)
{
- var chunk = _chunkPool.Get();
- chunk.Buffer = size >= RecyclableDefaults.MinPooledArrayLength
- ? RecyclableArrayPool.RentShared(size)
- : new T[size];
+ var chunk = RecyclableArrayPoolChunkPool.Rent();
+ if (chunk.Buffer.Length < size)
+ {
+ if (chunk.Buffer.Length >= RecyclableDefaults.MinPooledArrayLength)
+ {
+ RecyclableArrayPool.ReturnShared(chunk.Buffer, _needsClearing);
+ }
+
+ chunk.Buffer = size >= RecyclableDefaults.MinPooledArrayLength
+ ? RecyclableArrayPool.RentShared(size)
+ : new T[size];
+ }
chunk.Index = 0;
chunk.Previous = previous;
chunk.Next = null;
@@ -33,22 +31,19 @@ namespace Recyclable.Collections
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static void ReturnChunk(Chunk chunk)
+ private static void ReturnChunk(RecyclableArrayPoolChunk chunk)
{
- var buffer = chunk.Buffer;
- if (buffer.Length >= RecyclableDefaults.MinPooledArrayLength)
- {
- RecyclableArrayPool.ReturnShared(buffer, _needsClearing);
- }
- chunk.Buffer = Array.Empty();
- chunk.Index = 0;
- chunk.Previous = null;
- chunk.Next = null;
- _chunkPool.Return(chunk);
+ RecyclableArrayPoolChunkPool.Return(chunk);
}
- private Chunk _current;
- private Chunk _bottom;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static void DisposeChunk(RecyclableArrayPoolChunk chunk)
+ {
+ RecyclableArrayPoolChunkPool.Dispose(chunk, _needsClearing);
+ }
+
+ private RecyclableArrayPoolChunk _current;
+ private RecyclableArrayPoolChunk _bottom;
private bool _disposed;
private long _capacity;
private long _count;
@@ -179,16 +174,22 @@ namespace Recyclable.Collections
return;
}
- Clear();
- ReturnChunk(_current);
+ var chunk = _current;
+ while (chunk != null)
+ {
+ var previous = chunk.Previous;
+ DisposeChunk(chunk);
+ chunk = previous;
+ }
+
_disposed = true;
GC.SuppressFinalize(this);
}
public struct Enumerator : IEnumerator
{
- private readonly Chunk? _start;
- private Chunk? _chunk;
+ private readonly RecyclableArrayPoolChunk? _start;
+ private RecyclableArrayPoolChunk? _chunk;
private int _index;
private T? _current;