From 55acbe779450e3c9d5dd2d4aa8ae465c94a01077 Mon Sep 17 00:00:00 2001 From: akiraveliara Date: Sat, 17 Aug 2024 14:42:04 +0200 Subject: [PATCH] append-only value list --- Directory.Build.props | 8 ++- src/Bundles/Bundles.csproj | 3 +- .../UnmanagedStack.cs} | 48 ++++--------- .../ValueAppendList.Enumerator.cs | 18 +++++ .../ValueCollections/ValueAppendList.cs | 68 +++++++++++++++++++ tests/Bundles.Tests/Bundles.Tests.csproj | 2 +- 6 files changed, 108 insertions(+), 39 deletions(-) rename src/Bundles/{ValueCollections/ValueStack.cs => Unmanaged/UnmanagedStack.cs} (87%) create mode 100644 src/Bundles/ValueCollections/ValueAppendList.Enumerator.cs create mode 100644 src/Bundles/ValueCollections/ValueAppendList.cs diff --git a/Directory.Build.props b/Directory.Build.props index 5e69cd6..c838f5b 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,22 +1,26 @@ + True preview $(NoWarn);CS1591 enable - net8.0 + net9.0 True + True true true + true true true snupkg + akiraveliara, dotnet/corefxlab DictionarySlim contributors @@ -30,9 +34,11 @@ git https://github.com/DSharpPlus/Bundles + + \ No newline at end of file diff --git a/src/Bundles/Bundles.csproj b/src/Bundles/Bundles.csproj index f0e35ff..e1ab065 100644 --- a/src/Bundles/Bundles.csproj +++ b/src/Bundles/Bundles.csproj @@ -1,9 +1,8 @@  true - true Library Bundles - 1.2.0 + 2.0.0 \ No newline at end of file diff --git a/src/Bundles/ValueCollections/ValueStack.cs b/src/Bundles/Unmanaged/UnmanagedStack.cs similarity index 87% rename from src/Bundles/ValueCollections/ValueStack.cs rename to src/Bundles/Unmanaged/UnmanagedStack.cs index 5cd6d0f..b811366 100644 --- a/src/Bundles/ValueCollections/ValueStack.cs +++ b/src/Bundles/Unmanaged/UnmanagedStack.cs @@ -8,60 +8,50 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -namespace Bundles.ValueCollections; +namespace Bundles.Unmanaged; /// /// Represents an allocation-free stack of unmanaged items. This stack cannot resize. /// /// The item type of this collection. [DebuggerDisplay("Capacity = {Capacity}; Count = {Count}")] -public unsafe ref struct ValueStack +public unsafe ref struct UnmanagedStack where T : unmanaged { private readonly Span items; private int count; /// - /// Creates a new, all-zero . + /// Creates a new, all-zero . /// - public ValueStack() + public UnmanagedStack() { this.items = []; this.count = 0; } /// - /// Creates a new . + /// Creates a new . /// /// The backing memory for this instance. - public ValueStack - ( - Span memory - ) + public UnmanagedStack(Span memory) { this.items = memory; this.count = 0; } - public ValueStack - ( - T* memory, - nuint length - ) + public UnmanagedStack(T* memory, nuint length) { this.items = MemoryMarshal.CreateSpan(ref Unsafe.AsRef(memory), (int)length); this.count = 0; } /// - /// Creates a new by implicitly casting, enabling syntax such as + /// Creates a new by implicitly casting, enabling syntax such as /// stack = stackalloc int[4];]]> /// /// The backing memory for this instance - public static implicit operator ValueStack - ( - Span memory - ) + public static implicit operator UnmanagedStack(Span memory) => new(memory); /// @@ -95,10 +85,7 @@ public readonly ref readonly T GetPinnableReference() /// /// The item to push to the stack. /// true to indicate success, false to indicate failure. - public bool TryPush - ( - T item - ) + public bool TryPush(T item) { if (this.count == this.items.Length) { @@ -114,10 +101,7 @@ T item /// /// The item popped from the stack. /// true to indicate success, false to indicate failure. - public bool TryPop - ( - out T item - ) + public bool TryPop(out T item) { if (this.count == 0) { @@ -134,10 +118,7 @@ out T item /// /// The item peeked from the stack. /// true to indicate success, false to indicate failure. - public readonly bool TryPeek - ( - out T item - ) + public readonly bool TryPeek(out T item) { if (this.count == 0) { @@ -153,10 +134,7 @@ out T item /// Pushes a new item to the stack. /// /// Thrown if the stack was full. - public void Push - ( - T item - ) + public void Push(T item) { if (!this.TryPush(item)) { diff --git a/src/Bundles/ValueCollections/ValueAppendList.Enumerator.cs b/src/Bundles/ValueCollections/ValueAppendList.Enumerator.cs new file mode 100644 index 0000000..be050f4 --- /dev/null +++ b/src/Bundles/ValueCollections/ValueAppendList.Enumerator.cs @@ -0,0 +1,18 @@ +// This Source Code form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +namespace Bundles.ValueCollections; + +partial record struct ValueAppendList +{ + public struct Enumerator(ValueAppendList list) + { + private int index; + + public ref readonly T Current => ref list[this.index]; + + public bool MoveNext() + => ++this.index != list.Count; + } +} diff --git a/src/Bundles/ValueCollections/ValueAppendList.cs b/src/Bundles/ValueCollections/ValueAppendList.cs new file mode 100644 index 0000000..6899892 --- /dev/null +++ b/src/Bundles/ValueCollections/ValueAppendList.cs @@ -0,0 +1,68 @@ +// This Source Code form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +using System.Buffers; + +namespace Bundles.ValueCollections; + +/// +/// A value-type list type that can only be modified by appending, never by removing. +/// +/// The element type controlled by this list. +public partial record struct ValueAppendList +{ + private T[] buffer; + private int index; + + /// + /// Gets a readonly reference to the element at the specified index. + /// + public readonly ref readonly T this[int index] => ref this.buffer[index]; + + /// + /// Gets the current capacity of this list. + /// + public readonly int Capacity => this.buffer.Length; + + /// + /// Gets the element count of this list. + /// + public readonly int Count => this.index + 1; + + /// + /// Adds an element to the list, returning a readonly reference to the newly added element. + /// + public ref readonly T Add(T value) + { + if (++this.index == buffer.Length) + { + this.ResizeBuffer(); + } + + this.buffer[this.index] = value; + return ref this.buffer[this.index]; + } + + /// + /// Gets an enumerator over the present structure. + /// + /// + public readonly Enumerator GetEnumerator() + => new(this); + + private void ResizeBuffer() + { + int newLength = this.buffer.Length * 2; + T[] newBuffer = typeof(T).IsPrimitive ? ArrayPool.Shared.Rent(newLength) : new T[newLength]; + + this.buffer.CopyTo(newBuffer, 0); + + if (typeof(T).IsPrimitive) + { + ArrayPool.Shared.Return(this.buffer); + } + + this.buffer = newBuffer; + } +} diff --git a/tests/Bundles.Tests/Bundles.Tests.csproj b/tests/Bundles.Tests/Bundles.Tests.csproj index 7da1e3b..ebd1862 100644 --- a/tests/Bundles.Tests/Bundles.Tests.csproj +++ b/tests/Bundles.Tests/Bundles.Tests.csproj @@ -4,7 +4,7 @@ false true enable - net8.0 + net9.0 $(NoWarn);CA1869