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;