Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions src/EndlessEscapade/Core/Collections/_Generic/DenseSet.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using System.Numerics;

namespace EndlessEscapade.Framework.Collections;

public sealed class DenseSet<T>
{
private T[] _items;

public DenseSet(int capacity = 4)
{
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(capacity);
_items = new T[capacity];
}

public ref T this[int index]
{
get
{
var locItems = _items;
if ((uint)index < (uint)_items.Length)
{
return ref locItems[index];
}

return ref ResizeAndGet(index);
}
}

private ref T ResizeAndGet(int index)
{
Array.Resize(ref _items, (int)BitOperations.RoundUpToPowerOf2((uint)index + 1));
return ref _items[index];
}
}
137 changes: 137 additions & 0 deletions src/EndlessEscapade/Core/Collections/_Generic/FastStack.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
using System.Collections.Generic;
using System.Collections;
using System;
using System.Runtime.CompilerServices;
using System.Diagnostics.CodeAnalysis;
using System.Security;

namespace EndlessEscapade.Core.Collections;

/// <summary>
/// This struct is meant to be used purely inside of classes as fields.
/// </summary>
/// <remarks>It is a light wrapper over an array. It does not track versions.</remarks>
public struct FastStack<T> : IEnumerable<T>, IEnumerable
where T : notnull
{
private T[] _buffer;
private int _nextIndex;

public FastStack() : this(4)
{

}

public FastStack(int initalCapacity)
{
ArgumentOutOfRangeException.ThrowIfNegative(initalCapacity);
_buffer = initalCapacity == 0 ? [] : new T[initalCapacity];
_nextIndex = 0;
}

public int Count => _nextIndex;

public ref T this[int index]
{
get
{
if ((uint)index < (uint)_nextIndex)
{
return ref _buffer[index];
}

return ref Throw_OutOfRange();
}
}

public void Push(T item)
{
var buffer = _buffer;
if (_nextIndex < _buffer.Length)
{
buffer[_nextIndex++] = item;
return;
}

ResizeAndPush(item);
}

public T Pop()
{
var buffer = _buffer;
if ((uint)(--_nextIndex) < buffer.Length)
{
ref T item = ref buffer[_nextIndex];
if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
{
var returnValue = item;
item = default!;
return returnValue;
}
return item;
}

return Throw_EmptyStack();
}

public bool TryPop([NotNullWhen(true)] out T? item)
{
var buffer = _buffer;
if ((uint)(--_nextIndex) < buffer.Length)
{
ref T slot = ref buffer[_nextIndex];
item = slot;
if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
item = default!;
return true;
}

item = default;
return false;
}

public Span<T> AsSpan() => _buffer.AsSpan(0, _nextIndex);

private void ResizeAndPush(in T item)
{
Array.Resize(ref _buffer, _buffer.Length + (_buffer.Length >> 1));
_buffer[_nextIndex++] = item;
}

[MethodImpl(MethodImplOptions.NoInlining)]
private T Throw_EmptyStack()
{
throw new InvalidOperationException("Stack is empty!");
}
[MethodImpl(MethodImplOptions.NoInlining)]
private ref T Throw_OutOfRange()
{
throw new ArgumentOutOfRangeException();
}

public void Clear()
{
if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
AsSpan().Clear();
_nextIndex = 0;
}

#region Enumerable
public Enumerator GetEnumerator() => new(this);
IEnumerator<T> IEnumerable<T>.GetEnumerator() => GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

public struct Enumerator(FastStack<T> stack) : IEnumerator<T>, IEnumerator
{
private T[] _buffer = stack._buffer;
private int _nextIndex = stack._nextIndex;
private int _currentIndex = -1;
public readonly T Current => _buffer[_currentIndex];
readonly object IEnumerator.Current => Current;

public readonly void Dispose() { }
public bool MoveNext() => ++_currentIndex < _nextIndex;
public void Reset() => _currentIndex = -1;
}
#endregion Enumerable
}
Loading
Loading