Reputation: 63806
I need to serialize simple types into a binary buffer for a predefined messaging protocol so I wrote this one as an example, which copies 2 bytes into a buffer:
public static int Copy(this byte[] buffer,Int16 value,int destinationIndex)
{
buffer[destinationIndex++] = (byte)(value >> 8);
buffer[destinationIndex++] = (byte)(value);
return destinationIndex;
}
It feels very low-level for C#, it is basically C code, which rings some alarm bells. Is this the best way to do it in .NET or are there some framework/language features that are better? I will need a way to reverse it too, like:
public static Int16 Parse(this byte[] buffer, int index)
{
var v = buffer[index++] << 8;
v += buffer[index++];
return (Int16)v;
}
Upvotes: 5
Views: 1072
Reputation: 1063714
The are many ways to do these kinds of translations; historically, BitConverter
has been useful here, but it is rather allocatey, especially when serializing - it returns a new byte[]
each time. It also has the unfortunately feature of being CPU-dependent, meaning: it returns the data in whatever the current CPU is using - not ideal for serialization, where the writing and reading machines might be different architectures.
Recently, BinaryPrimitives
is a more compelling option, since it doesn't allocate. It works on spans, meaning it can work on byte-arrays, but it can also work on unmanaged memory, stack-allocated blocks, fixed-size buffers, and other scenarios. It is also well-defined in terms of endianness, using the JIT's newer ability to interpret CPU endianness as an invariant and remove the unnecessary code, so that only the "right" code remains at runtime.
In this case, what you are showing is big-endian encoding, so the appropriate methods would be BinaryPrimitives.[Try]ReadInt16BigEndian(...)
and BinaryPrimitives.[Try]WriteInt16BigEndian(...)
.
Upvotes: 6
Reputation: 11080
I'd strongly advocate for BinaryReader
and BinaryWriter
here as a more idiomatic way of handling such a messaging protocol.
You can wrap the reader/writer around a MemoryStream
if you're only using the messaging protocol for the same process (in memory), or a PipeStream
or NetworkStream
for other use cases, or you could implement your own Stream to use.
Such low-level code as the lines in your question would then be obviated in favor of the already-implemented Read/Write methods on the BinaryReader
/BinaryWriter
:
public static int Copy(this BinaryWriter writer,Int16 value,int destinationIndex)
{
writer.Seek(destinationIndex, SeekOrigin.Begin);
writer.write(value);
return writer.BaseStream.Position;
}
And the reader:
public static Int16 Parse(this BinaryReader reader, int index)
{
reader.Seek(index, SeekOrigin.Begin);
return reader.ReadInt16();
}
I don't know how these methods are being consumed, but chances are pretty good that you can get rid of them altogether if you switch to using readers and writers.
Upvotes: 1