Skip to content

Instantly share code, notes, and snippets.

@Tzrlk
Created August 8, 2025 10:53
Show Gist options
  • Select an option

  • Save Tzrlk/78740395dd6a813db9a58320e2c3e65d to your computer and use it in GitHub Desktop.

Select an option

Save Tzrlk/78740395dd6a813db9a58320e2c3e65d to your computer and use it in GitHub Desktop.
A stack with ephemeral history (in C#).
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