Skip to content

Commit

Permalink
append-only value list
Browse files Browse the repository at this point in the history
  • Loading branch information
akiraveliara committed Aug 17, 2024
1 parent 78555fa commit 55acbe7
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 39 deletions.
8 changes: 7 additions & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,22 +1,26 @@
<Project>

<!-- build -->
<PropertyGroup>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<LangVersion>preview</LangVersion>
<NoWarn>$(NoWarn);CS1591</NoWarn>
<Nullable>enable</Nullable>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>

<!-- build and versioning setup -->
<ManagePackageVersionsCentrally>True</ManagePackageVersionsCentrally>
<NuGetAudit>true</NuGetAudit>
<UseArtifactsOutput>true</UseArtifactsOutput>

<!-- sourcelink -->
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<IncludeSymbols>true</IncludeSymbols>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
</PropertyGroup>

<!-- nuget -->
<PropertyGroup>
<Authors>akiraveliara, dotnet/corefxlab DictionarySlim contributors</Authors>
Expand All @@ -30,9 +34,11 @@
<RepositoryType>git</RepositoryType>
<RepositoryUrl>https://github.com/DSharpPlus/Bundles</RepositoryUrl>
</PropertyGroup>

<ItemGroup>
<EmbeddedResource Include="../../LICENSE" Pack="true" PackagePath=""/>
<EmbeddedResource Include="../../README.md" Pack="true" PackagePath=""/>
<EmbeddedResource Include="../../img/logo.png" Pack="true" PackagePath=""/>
</ItemGroup>

</Project>
3 changes: 1 addition & 2 deletions src/Bundles/Bundles.csproj
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<IsAotCompatible>true</IsAotCompatible>
<OutputType>Library</OutputType>
<PackageId>Bundles</PackageId>
<VersionPrefix>1.2.0</VersionPrefix>
<VersionPrefix>2.0.0</VersionPrefix>
</PropertyGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -8,60 +8,50 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace Bundles.ValueCollections;
namespace Bundles.Unmanaged;

/// <summary>
/// Represents an allocation-free stack of unmanaged items. This stack cannot resize.
/// </summary>
/// <typeparam name="T">The item type of this collection.</typeparam>
[DebuggerDisplay("Capacity = {Capacity}; Count = {Count}")]
public unsafe ref struct ValueStack<T>
public unsafe ref struct UnmanagedStack<T>
where T : unmanaged
{
private readonly Span<T> items;
private int count;

/// <summary>
/// Creates a new, all-zero <see cref="ValueStack{T}"/>.
/// Creates a new, all-zero <see cref="UnmanagedStack{T}"/>.
/// </summary>
public ValueStack()
public UnmanagedStack()
{
this.items = [];
this.count = 0;
}

/// <summary>
/// Creates a new <see cref="ValueStack{T}"/>.
/// Creates a new <see cref="UnmanagedStack{T}"/>.
/// </summary>
/// <param name="memory">The backing memory for this instance.</param>
public ValueStack
(
Span<T> memory
)
public UnmanagedStack(Span<T> 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<T>(memory), (int)length);
this.count = 0;
}

/// <summary>
/// Creates a new <see cref="ValueStack{T}"/> by implicitly casting, enabling syntax such as
/// Creates a new <see cref="UnmanagedStack{T}"/> by implicitly casting, enabling syntax such as
/// <c><![CDATA[ValueStack<int> stack = stackalloc int[4];]]></c>
/// </summary>
/// <param name="memory">The backing memory for this instance</param>
public static implicit operator ValueStack<T>
(
Span<T> memory
)
public static implicit operator UnmanagedStack<T>(Span<T> memory)
=> new(memory);

/// <summary>
Expand Down Expand Up @@ -95,10 +85,7 @@ public readonly ref readonly T GetPinnableReference()
/// </summary>
/// <param name="item">The item to push to the stack.</param>
/// <returns><c>true</c> to indicate success, <c>false</c> to indicate failure.</returns>
public bool TryPush
(
T item
)
public bool TryPush(T item)
{
if (this.count == this.items.Length)
{
Expand All @@ -114,10 +101,7 @@ T item
/// </summary>
/// <param name="item">The item popped from the stack.</param>
/// <returns><c>true</c> to indicate success, <c>false</c> to indicate failure.</returns>
public bool TryPop
(
out T item
)
public bool TryPop(out T item)
{
if (this.count == 0)
{
Expand All @@ -134,10 +118,7 @@ out T item
/// </summary>
/// <param name="item">The item peeked from the stack.</param>
/// <returns><c>true</c> to indicate success, <c>false</c> to indicate failure.</returns>
public readonly bool TryPeek
(
out T item
)
public readonly bool TryPeek(out T item)
{
if (this.count == 0)
{
Expand All @@ -153,10 +134,7 @@ out T item
/// Pushes a new item to the stack.
/// </summary>
/// <exception cref="InvalidOperationException">Thrown if the stack was full.</exception>
public void Push
(
T item
)
public void Push(T item)
{
if (!this.TryPush(item))
{
Expand Down
18 changes: 18 additions & 0 deletions src/Bundles/ValueCollections/ValueAppendList.Enumerator.cs
Original file line number Diff line number Diff line change
@@ -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<T>
{
public struct Enumerator(ValueAppendList<T> list)
{
private int index;

public ref readonly T Current => ref list[this.index];

public bool MoveNext()
=> ++this.index != list.Count;
}
}
68 changes: 68 additions & 0 deletions src/Bundles/ValueCollections/ValueAppendList.cs
Original file line number Diff line number Diff line change
@@ -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;

/// <summary>
/// A value-type list type that can only be modified by appending, never by removing.
/// </summary>
/// <typeparam name="T">The element type controlled by this list.</typeparam>
public partial record struct ValueAppendList<T>
{
private T[] buffer;
private int index;

/// <summary>
/// Gets a readonly reference to the element at the specified index.
/// </summary>
public readonly ref readonly T this[int index] => ref this.buffer[index];

/// <summary>
/// Gets the current capacity of this list.
/// </summary>
public readonly int Capacity => this.buffer.Length;

/// <summary>
/// Gets the element count of this list.
/// </summary>
public readonly int Count => this.index + 1;

/// <summary>
/// Adds an element to the list, returning a readonly reference to the newly added element.
/// </summary>
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];
}

/// <summary>
/// Gets an enumerator over the present structure.
/// </summary>
/// <returns></returns>
public readonly Enumerator GetEnumerator()
=> new(this);

private void ResizeBuffer()
{
int newLength = this.buffer.Length * 2;
T[] newBuffer = typeof(T).IsPrimitive ? ArrayPool<T>.Shared.Rent(newLength) : new T[newLength];

this.buffer.CopyTo(newBuffer, 0);

if (typeof(T).IsPrimitive)
{
ArrayPool<T>.Shared.Return(this.buffer);
}

this.buffer = newBuffer;
}
}
2 changes: 1 addition & 1 deletion tests/Bundles.Tests/Bundles.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
<Nullable>enable</Nullable>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
<NoWarn>$(NoWarn);CA1869</NoWarn>
</PropertyGroup>

Expand Down

0 comments on commit 55acbe7

Please sign in to comment.