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 Source
s 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?
unsafe
code exists; to trade safety for speed. – Porebyte* Start
could be justint Start
, but not a lot. But the question is less about my specific case — I can live withunsafe
if I have to — than about the general question: What's the best way to do memory-safe double-pointer indirection on the stack in C#? – SandrasandroSpan
(or equivalent) if I could get it. The introduction ofSpan
in C# showed I could have my cake and eat it too, and now I want more :-) – SandrasandroMemory<T>
is essentially aSpan<T>
without theref
. – PoreMemory<T>
doesn't work. It's not compatible with the return types ofstackalloc
, so to get aMemory<T>
, you have to use pointers anyway. It doesn't have an indexer. You have to eitherPin
it and get a pointer, or convert it to aSpan
to use it, which have high performance costs if you're iterating over hundreds ofMemory<T>
instances in an inner loop. I looked at usingMemory<T>
, but it's not really a viable alternative. – Sandrasandro