What is an efficient equivalent in C# for Span<Span<T>>, which does not exist?

330 views Asked by At

I was porting some older high-speed C++ code to C#, and the existing code made use of a pointer-based double-indirection pattern like this (written here in a C# syntax), using the stack as efficient temporary storage:

public struct Source {
    public byte* Start;
    public int Count;
}

public struct SourceSet {
    public Source* Start;
    public int Count;
}

Source* sources = stackalloc Source[n*k];
SourceSet* sourceSets = stackalloc SourceSet[n];

It populates the sources with sets of segments of the data, and populates the sourceSets with how many Sources are in each set.

The code works fine, but ideally, I would like to convert it to no longer use pointers and unsafe — to instead use something memory-safe like this, where each SourceSet would be populated by a .Slice() of the sources:

public struct Source {
    public int Start;
    public int Count;
}

Span<Source> sources = stackalloc Source[n*k];
Span<Span<Source>> sourceSets = stackalloc Span<Source>[n];

But I can't write that, because Span<Span<T>> can't exist in C# — Span<T> is a ref struct and thus can't be used as the type parameter of another Span<T>. And I can't use Memory<Memory<T>> as a replacement, because stackalloc only produces a Span<T> (or a pointer, but the goal is to avoid pointers).

(And yes, I understand that Span<Span<T>> will likely never be added to the language, because it could potentially allow the rules about span lifetimes to be violated.)

So what's a good, efficient equivalent in C# for safe double-pointer indirection on the stack, something like Span<Span<T>>, but that actually exists?

1

There are 1 answers

1
David Xuang On

Try Span2D<T>:

Span2D<byte> span2d = (stackalloc byte[height * width]).AsSpan2D(height, width);