patterncsharpMinor
Fastest De-/Serialise struct in .Net
Viewed 0 times
netstructfastestserialise
Problem
I wrote this code to De- and serialise structures afap using IL generation (comments describes C# analogue). What can be improved here?
```
using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.InteropServices;
namespace StructInterop
{
public static class StructInterOp
{
static readonly ConstructorInfo IntPtrCtor = typeof(IntPtr).GetConstructor(new[] { typeof(void*) });
static readonly MethodInfo MarshalCopy = typeof(Marshal).GetMethod("Copy", new[] { typeof(IntPtr), typeof(byte[]), typeof(int), typeof(int) });
private static class DelegateHolder where T : struct
{
// ReSharper disable MemberHidesStaticFromOuterClass
// ReSharper disable StaticMemberInGenericType
public static readonly Type TypeOfT = typeof(T);
public static readonly int SizeInBytes = Marshal.SizeOf(TypeOfT);
public static readonly Func Serialize = CreateSerializationDelegate();
public static readonly Func Deserialize = CreateDeserializationDelegate();
//public static byte[] Serialize(T value)
//{
// IntPtr p = new IntPtr(&value);
// byte[] result = new byte[sizeof(T)];
// Marshal.Copy(p, result, 0, result.Length);
// return result;
//}
private static Func CreateSerializationDelegate()
{
var dm = new DynamicMethod("Serialize" + TypeOfT.Name,
typeof(byte[]),
new[] { TypeOfT },
Assembly.GetExecutingAssembly().ManifestModule);
dm.DefineParameter(1, ParameterAttributes.None, "value");
var generator = dm.GetILGenerator();
generator.DeclareLocal(typeof(byte[]));
//IntPtr p = new IntPtr(&value);
generator.Emit(OpCodes.Ldarga_S, (byte)0);
generator.Emit(OpCodes.Conv_U)
```
using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.InteropServices;
namespace StructInterop
{
public static class StructInterOp
{
static readonly ConstructorInfo IntPtrCtor = typeof(IntPtr).GetConstructor(new[] { typeof(void*) });
static readonly MethodInfo MarshalCopy = typeof(Marshal).GetMethod("Copy", new[] { typeof(IntPtr), typeof(byte[]), typeof(int), typeof(int) });
private static class DelegateHolder where T : struct
{
// ReSharper disable MemberHidesStaticFromOuterClass
// ReSharper disable StaticMemberInGenericType
public static readonly Type TypeOfT = typeof(T);
public static readonly int SizeInBytes = Marshal.SizeOf(TypeOfT);
public static readonly Func Serialize = CreateSerializationDelegate();
public static readonly Func Deserialize = CreateDeserializationDelegate();
//public static byte[] Serialize(T value)
//{
// IntPtr p = new IntPtr(&value);
// byte[] result = new byte[sizeof(T)];
// Marshal.Copy(p, result, 0, result.Length);
// return result;
//}
private static Func CreateSerializationDelegate()
{
var dm = new DynamicMethod("Serialize" + TypeOfT.Name,
typeof(byte[]),
new[] { TypeOfT },
Assembly.GetExecutingAssembly().ManifestModule);
dm.DefineParameter(1, ParameterAttributes.None, "value");
var generator = dm.GetILGenerator();
generator.DeclareLocal(typeof(byte[]));
//IntPtr p = new IntPtr(&value);
generator.Emit(OpCodes.Ldarga_S, (byte)0);
generator.Emit(OpCodes.Conv_U)
Solution
Naming
Given that the
Structure
You could remove a lot of your comments by spliting up the generation a bit. (The names for the new methods aren't great...)
Comments
That's not a good comment. It should summarise what the method does:
Interop should be treated as a single word like in System.Runtime.InteropServices. You should also not name a class the same as its namespace. Which is why this is really not good:namespace StructInterop
{
public static class StructInterOp
{Given that the
StructInterOp class has methods like Serialize and Deserialize - why not call it StructSerializer?Structure
You could remove a lot of your comments by spliting up the generation a bit. (The names for the new methods aren't great...)
//public static byte[] Serialize(T value)
//{
// IntPtr p = new IntPtr(&value);
// byte[] result = new byte[sizeof(T)];
// Marshal.Copy(p, result, 0, result.Length);
// return result;
//}
private static Func CreateSerializationDelegate()
{
var dm = new DynamicMethod("Serialize" + TypeOfT.Name,
typeof(byte[]),
new[] { TypeOfT },
Assembly.GetExecutingAssembly().ManifestModule);
dm.DefineParameter(1, ParameterAttributes.None, "value");
var generator = dm.GetILGenerator();
generator.DeclareLocal(typeof(byte[]));
GenerateIntPtr(generator);
GenerateResultArray(generator);
GenerateCopy(generator);
GenerateReturn(generator);
return (Func)dm.CreateDelegate(typeof(Func));
}
private static void GenerateReturn(ILGenerator generator)
{
generator.Emit(OpCodes.Ldloc_0);
generator.Emit(OpCodes.Ret);
}
private static void GenerateCopy(ILGenerator generator)
{
generator.Emit(OpCodes.Stloc_0);
generator.Emit(OpCodes.Ldloc_0);
generator.Emit(OpCodes.Ldc_I4_0);
generator.Emit(OpCodes.Ldloc_0);
generator.Emit(OpCodes.Ldlen);
generator.Emit(OpCodes.Conv_I4);
generator.EmitCall(OpCodes.Call, MarshalCopy, null);
}
private static void GenerateResultArray(ILGenerator generator)
{
OpCode ldcStructSize = SizeInBytes < sbyte.MaxValue ? OpCodes.Ldc_I4_S : OpCodes.Ldc_I4;
generator.Emit(ldcStructSize, SizeInBytes);
generator.Emit(OpCodes.Newarr, typeof(byte));
}
private static void GenerateIntPtr(ILGenerator generator)
{
generator.Emit(OpCodes.Ldarga_S, (byte)0);
generator.Emit(OpCodes.Conv_U);
generator.Emit(OpCodes.Newobj, IntPtrCtor);
}Comments
///
/// Do not check array bounds, possible buffer overflow
///
public static T Deserialize(byte[] data) where T : structThat's not a good comment. It should summarise what the method does:
///
/// Deserializes the given data to the specified type.
///
/// The serialized struct data.
/// The type of the struct to be deserialized.
/// An instance of type Code Snippets
namespace StructInterop
{
public static class StructInterOp
{//public static byte[] Serialize(T value)
//{
// IntPtr p = new IntPtr(&value);
// byte[] result = new byte[sizeof(T)];
// Marshal.Copy(p, result, 0, result.Length);
// return result;
//}
private static Func<T, byte[]> CreateSerializationDelegate()
{
var dm = new DynamicMethod("Serialize" + TypeOfT.Name,
typeof(byte[]),
new[] { TypeOfT },
Assembly.GetExecutingAssembly().ManifestModule);
dm.DefineParameter(1, ParameterAttributes.None, "value");
var generator = dm.GetILGenerator();
generator.DeclareLocal(typeof(byte[]));
GenerateIntPtr(generator);
GenerateResultArray(generator);
GenerateCopy(generator);
GenerateReturn(generator);
return (Func<T, byte[]>)dm.CreateDelegate(typeof(Func<T, byte[]>));
}
private static void GenerateReturn(ILGenerator generator)
{
generator.Emit(OpCodes.Ldloc_0);
generator.Emit(OpCodes.Ret);
}
private static void GenerateCopy(ILGenerator generator)
{
generator.Emit(OpCodes.Stloc_0);
generator.Emit(OpCodes.Ldloc_0);
generator.Emit(OpCodes.Ldc_I4_0);
generator.Emit(OpCodes.Ldloc_0);
generator.Emit(OpCodes.Ldlen);
generator.Emit(OpCodes.Conv_I4);
generator.EmitCall(OpCodes.Call, MarshalCopy, null);
}
private static void GenerateResultArray(ILGenerator generator)
{
OpCode ldcStructSize = SizeInBytes < sbyte.MaxValue ? OpCodes.Ldc_I4_S : OpCodes.Ldc_I4;
generator.Emit(ldcStructSize, SizeInBytes);
generator.Emit(OpCodes.Newarr, typeof(byte));
}
private static void GenerateIntPtr(ILGenerator generator)
{
generator.Emit(OpCodes.Ldarga_S, (byte)0);
generator.Emit(OpCodes.Conv_U);
generator.Emit(OpCodes.Newobj, IntPtrCtor);
}/// <summary>
/// Do not check array bounds, possible buffer overflow
/// </summary>
public static T Deserialize<T>(byte[] data) where T : struct/// <summary>
/// Deserializes the given data to the specified type.
/// </summary>
/// <paramref name="data">The serialized struct data.</paramref>
/// <typeparam name="T">The type of the struct to be deserialized.</typeparam>
/// <returns>An instance of type <typeparamref name="T"/></returns>Context
StackExchange Code Review Q#97120, answer score: 7
Revisions (0)
No revisions yet.