我正在通过Marshal.AllocHGlobal
. 然后,我将一组字节复制到该位置,并将生成的内存段转换为 a struct
,然后再通过Marshal.FreeHGlobal
.
这是方法:
public static T Deserialize<T>(byte[] messageBytes, int start, int length)
where T : struct
{
if (start + length > messageBytes.Length)
throw new ArgumentOutOfRangeException();
int typeSize = Marshal.SizeOf(typeof(T));
int bytesToCopy = Math.Min(typeSize, length);
IntPtr targetBytes = Marshal.AllocHGlobal(typeSize);
Marshal.Copy(messageBytes, start, targetBytes, bytesToCopy);
if (length < typeSize)
{
// Zero out additional bytes at the end of the struct
}
T item = (T)Marshal.PtrToStructure(targetBytes, typeof(T));
Marshal.FreeHGlobal(targetBytes);
return item;
}
这在大多数情况下都有效,但是如果我的字节数少于所需的大小struct
,则将“随机”值分配给最后一个字段(我LayoutKind.Sequential
在目标结构上使用)。我想尽可能有效地将这些悬挂区域归零。
就上下文而言,此代码正在反序列化从 Linux 上的 C++ 发送的高频多播消息。
这是一个失败的测试用例:
// Give only one byte, which is too few for the struct
var s3 = MessageSerializer.Deserialize<S3>(new[] { (byte)0x21 });
Assert.AreEqual(0x21, s3.Byte);
Assert.AreEqual(0x0000, s3.Int); // hanging field should be zero, but isn't
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
private struct S3
{
public byte Byte;
public int Int;
}
重复运行此测试会导致第二个断言每次失败并显示不同的值。
编辑
最后,我使用了leppie 的建议 gounsafe
and using stackalloc
。这分配了一个根据需要清零的字节数组,并将吞吐量从 50% 提高到 100%,具体取决于消息大小(更大的消息会带来更大的好处)。
最后的方法最终类似于:
public static T Deserialize<T>(byte[] messageBytes, int startIndex, int length)
where T : struct
{
if (length <= 0)
throw new ArgumentOutOfRangeException("length", length, "Must be greater than zero.");
if (startIndex < 0)
throw new ArgumentOutOfRangeException("startIndex", startIndex, "Must be greater than or equal to zero.");
if (startIndex + length > messageBytes.Length)
throw new ArgumentOutOfRangeException("length", length, "startIndex + length must be <= messageBytes.Length");
int typeSize = Marshal.SizeOf(typeof(T));
unsafe
{
byte* basePtr = stackalloc byte[typeSize];
byte* b = basePtr;
int end = startIndex + Math.Min(length, typeSize);
for (int srcPos = startIndex; srcPos < end; srcPos++)
*b++ = messageBytes[srcPos];
return (T)Marshal.PtrToStructure(new IntPtr(basePtr), typeof(T));
}
}
不幸的是,这仍然需要调用Marshal.PtrToStructure
将字节转换为目标类型。