Copying a `System.Guid` to `byte[]` without allocating
The solution I settled on came from some help from the Jil project by Kevin Montrose. I didn't go with that exact solution, but it inspired me to come up with something that I think is fairly elegant.
Note: The following code uses Fixed Size Buffers and requires that your project be built with the /unsafe
switch (and in all likelihood requires Full Trust to run).
[StructLayout(LayoutKind.Explicit)]
unsafe struct GuidBuffer
{
[FieldOffset(0)]
fixed long buffer[2];
[FieldOffset(0)]
public Guid Guid;
public GuidBuffer(Guid guid)
: this()
{
Guid = guid;
}
public void CopyTo(byte[] dest, int offset)
{
if (dest.Length - offset < 16)
throw new ArgumentException("Destination buffer is too small");
fixed (byte* bDestRoot = dest)
fixed (long* bSrc = buffer)
{
byte* bDestOffset = bDestRoot + offset;
long* bDest = (long*)bDestOffset;
bDest[0] = bSrc[0];
bDest[1] = bSrc[1];
}
}
}
Usage is simple:
var myGuid = Guid.NewGuid(); // however you get it
var guidBuffer = new GuidBuffer(myGuid);
var buffer = new buffer[16];
guidBuffer.CopyTo(buffer, 0);
Timing this yielded an average duration of 1-2 ticks for the copy. Should be fast enough for most any application.
However, if you want to eke out the absolute best performance, one possibility (suggested by Kevin) is to ensure that the offset
parameter is long
-aligned (on an 8-byte boundary). My particular use case favors memory over speed, but if speed is the most important thing that would be a good way to go about it.
If speed is the main consideration, you can shave a good bit of time by using the Guid
directly instead going through the GuidBuffer
struct. Here's the extension method I'm using.
public static unsafe void Encode(this byte[] array, int offset, Guid value)
{
if (array.Length - offset < 16) throw new ArgumentException("buffer too small");
fixed (byte* pArray = array)
{
var pGuid = (long*)&value;
var pDest = (long*)(pArray + offset);
pDest[0] = pGuid[0];
pDest[1] = pGuid[1];
}
}
Usage:
var guid = Guid.NewGuid();
var array = new byte[16];
array.Encode(0, guid);