So I have been working with System.Buffers
and specifically the ReadOnlySequence<T>
class lately.
I have a struct of primitives defined with the following:
[StructLayout(LayoutKind.Sequential,Pack =1,CharSet=CharSet.Unicode)]
public partial struct MessageHeader
{
public MessageVersion Version;
...
And I can pass this back and forward across the network without any issues and I am leveraging System.IO.Pipelines
for this.
Converting the ReadOnlySequence<byte>
back into the struct has caused some head aches.
I started out with this:
private void ExtractMessageHeaderOld(ReadOnlySequence<byte> ros, out MessageHeader messageHeader)
{
var x = ros.ToArray<byte>();
ReadOnlySpan<MessageHeader> mhSpan = MemoryMarshal.Cast<byte, MessageHeader>(x);
messageHeader = mhSpan[0];
}
Which created thousands of little byte[] arrays over the life of the server, (which on there own aren't a problem), but with everything else the system is trying to do these put a bit of added pressure on the GC.
So I have moved to using:
private void ExtractMessageHeader(ReadOnlySequence<byte> ros, out MessageHeader messageHeader, ref byte[] workingSpace)
{
var i = 0;
foreach (var rom in ros)
foreach (var b in rom.Span)
{
workingSpace[i] = b;
i++;
}
ReadOnlySpan<MessageHeader> mhSpan = MemoryMarshal.Cast<byte, MessageHeader>(workingSpace);
messageHeader = mhSpan[0];
}
This doesn't do any memory allocations, but I just feel like there should be a better way.
The foreach
loops, and the MemoryMarshal.Cast()
to a messageHeader[1]
so I can extract element 0 is a bit of a hack I just happened to stumble upon while reading the source.
I can't find an API to cleanly extract the contents of the ReadOnlySequence<bytes>
into the messageHeader
, and while what I have is working, I just feel like one should exist...
Edit 1:
I just stumbled across BuffersExtensions.CopyTo<T>(ReadOnlySequence<T>, Span<T>)
which can replace the foreach loops with.
ros.CopyTo(workingSpace);