Table of Contents

List implementation of Span<T> for .NET / Unity

Runtime/System/SpanList.cs   HeaderDoc

(c) 2025 Sator Imaging, Licensed under the MIT License https://github.com/sator-imaging/Unity-Fundamentals

Simple container especially designed to work in conjunction with multiple Span<char>s for formatting and replacing text at once.

It's similar with Memory<T> but by contrast from that, SpanList<T> provides segmented range access to its internal buffer so that it can be used for cache store of list of span objects.

Example Usage

Formatting text by replacing named holes: https://messagetemplates.org/

// you can stored list of `Span<char>` on class or struct field.
private SpanList<char> m_fromList = new(null, 3, "{foo}", "{bar}", "{baz}");

// helper function that automatically manage array pool buffer.
using (SpanList.GetTransientScope(out var toList, 3, "Blah", "blah", "..."))
{
    // replace result
    // --> Blah blah ...
    var result = "{foo} {bar} {baz}".ReplaceNonAlloc(m_fromList, toList);

    // when 'from' list consists of tokens starting with '{' and ending with '}',
    // use `FormatNonAlloc` instead to achieve more efficient operation by reducing required buffer size.
    return "{foo} {bar} {baz}".FormatNonAlloc(m_fromList, toList);

    // buffer return to array pool automatically on using-block exit
}

Advanced Usage

You can take direct reference to internal data of SpanList<T>.

// take 64 x 3 span from pool
using var _ SpanList.GetTransientScope(out var spanList, stackalloc int[] { 64, 64, 64 });

// you can perform validation and formatting without extra heap allocation
ref var range = ref spanList.GetSpanUnsafe(0, out var span);
~~~~~~~         ~~~ taking `ref Range`

// write validated result into taken span
int charsWritten = Validate(rawFoo, span);
range = range.SetLength(charsWritten);  // update internal span range directly by `ref var`

// thanks to C#'s `ref return` feature, we can set new length on the fly
spanList.GetSpanUnsafe(1, out span).SetLength(Validate(rawBar, span);

// but, there is safe API to write data into internal buffer ^_^
spanList.Write(2, "my text", static (span, arg) => Validate(arg, span));
s = spanList[2];  // "my text"
spanList.Write(2, "updated text", static (span, arg) => Validate(arg, span));
s = spanList[2];  // "updated text"

return "{foo} {bar} {baz}".FormatNonAlloc(m_fromList, spanList);