Created
August 8, 2025 10:53
-
-
Save Tzrlk/78740395dd6a813db9a58320e2c3e65d to your computer and use it in GitHub Desktop.
A stack with ephemeral history (in C#).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.Collections; | |
| using System.Collections.Generic; | |
| /// <summary> | |
| /// This is really meant to be "the history of a value", but with only a fixed number of versions being held onto before they transition to weak references. | |
| /// An ephemeral stack. | |
| /// </summary> | |
| /// <typeparam name="T"></typeparam> | |
| /// <param name="value"></param> | |
| /// <param name="prev"></param> | |
| public partial class SmokeStack<T>(T value, SmokeStack<T> prev = null) | |
| { | |
| /// <summary> | |
| /// The current value of this stack frame. | |
| /// </summary> | |
| public readonly T Value = value; | |
| /// <summary> | |
| /// This reference holds the next stack head containing the value previous | |
| /// to the current one. | |
| /// </summary> | |
| readonly WeakReference<SmokeStack<T>> Next = new(prev); | |
| /// <summary> | |
| /// Provides access to the next stack frame, containing the previous value. | |
| /// </summary> | |
| public SmokeStack<T> Pop() | |
| => Next.TryGetTarget(out SmokeStack<T> value) ? value : null; | |
| public bool Pop(out SmokeStack<T> value) | |
| { | |
| var ok = Next.TryGetTarget(out value); | |
| if (!ok) value = null; | |
| return ok; | |
| } | |
| /// <summary> | |
| /// Puts another value on the stack, returning a new stack frame | |
| /// weakly linked-back to this one. | |
| /// </summary> | |
| /// <param name="value">The value to add.</param> | |
| /// <returns>A back-linked stack frame containing the value.</returns> | |
| public SmokeStack<T> Push(T value) | |
| => new(value, this); | |
| } | |
| public partial class SmokeStack<T> : IEnumerable<T> | |
| { | |
| /// <summary> | |
| /// Enumerate over this, and any values into the stack that are still | |
| /// available by the time they're iterated to. | |
| /// </summary> | |
| /// <returns>An enumeration over the stack values from this point.</returns> | |
| public IEnumerator<T> GetEnumerator() | |
| => new Enumerator(this); | |
| /// <see cref="GetEnumerator"/> | |
| IEnumerator IEnumerable.GetEnumerator() | |
| => GetEnumerator(); | |
| /// <summary> | |
| /// This is just a basic iterator implementation that just maintains a pair | |
| /// of pointers to the | |
| /// </summary> | |
| /// <param name="start">The SmokeStack to start from.</param> | |
| public partial class Enumerator(SmokeStack<T> start) : IEnumerator<T> | |
| { | |
| private readonly SmokeStack<T> Start = start; | |
| private SmokeStack<T> Next = start; | |
| } | |
| public partial class Enumerator : IEnumerator<T> | |
| { | |
| public T Current | |
| => Next.Value; | |
| object IEnumerator.Current | |
| => Current; | |
| public bool MoveNext() | |
| => Next.Pop(out Next); | |
| public void Reset() | |
| => Next = Start; | |
| } | |
| public partial class Enumerator : IDisposable | |
| { | |
| protected virtual void Dispose(bool disposing) | |
| { | |
| if (disposing) | |
| Next = null; | |
| } | |
| /// <summary> | |
| /// Nothing to actually dispose of here. | |
| /// </summary> | |
| public void Dispose() | |
| { | |
| Dispose(true); | |
| GC.SuppressFinalize(this); | |
| } | |
| } | |
| public partial class Enumerator : IEnumerable<T> | |
| { | |
| /// <summary> | |
| /// Creates a new enumerator starting from the current frame. | |
| /// </summary> | |
| /// <returns></returns> | |
| public IEnumerator<T> GetEnumerator() | |
| => new Enumerator(Next); | |
| /// <see cref="GetEnumerator"/> | |
| IEnumerator IEnumerable.GetEnumerator() | |
| => GetEnumerator(); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment