将字节数组烘焙到动态 IL

Baking a byte array into dynamic IL

我正在通过发出 IL 编写面向性能的数据反序列化程序。序列化数据为UTF8,字段表示为字符串。

[FieldA]: 22
[FieldB]: 16

我已经编写了一个自定义 reader,它正确标记了序列化数据并在逐步处理序列化数据时提供 ReadOnlySpan<byte>

我想要一个静态的内联反序列化器,它能够打包字段的字节签名,以便我可以轻松地创建跳转 table 并设置适当的字段。

非动态代码的外观:

// byteSpan is a ReadOnlySpan<byte> containing the signature

var signatureA = Encoding.UTF8.GetBytes( "FieldA" );
var signatureB = Encoding.UTF8.GetBytes( "FieldB" );
if( byteSpan.SequenceEqual( signatureA ) )
  DoSomething();
else if ( byteSpan.SequenceEqual( signatureB ) )
  DoSomething();
...

跳跃 table 的发射方式:

var fieldSignatures = GetTypeSignatures<T>(); // Returns a Tuple<byte[], FieldInfo>
var setFieldLabels = new List<Tuple<FieldInfo, Label>>();

foreach( (byte[] signature, FieldInfo field) in fieldSignatures )
{
  var setFieldLabel = il.DefineLabel();
  setFieldLabels.Add( Tuple.Create( field, setFieldLabel ) );

  il.Emit( OpCodes.Ldloc_1 ); // Load the current ReadOnlySpan<byte>
  // Inline load byte[] signature here
  il.Emit( OpCodes.Call, METHOD_SEQUENCEEQUAL );
  il.Emit( OpCodes.Brtrue, setFieldLabel );
}

EmitFieldSetters( setFieldLabels, ref il );

有没有一种方法可以将签名字节数组直接烘焙到我发出的 IL 中,以便它们成为委托的一部分?

这些签名是在运行时根据类型信息生成的,因此在静态 class 中手动定义它们是不可行的。一种解决方法是定义一个新的动态 AssemblyType 并将字节存储在那里,但我想尽可能避免这样做。

您可能想要将一组签名字节数组 (byte[][]) 作为隐藏的第一个参数传递给动态方法。

您可以通过类似以下方式加载适当的字节数组:

// Load the first byte[][] signatures array argument
il.Emit( OpCodes.LdArg_0 );
// Load the index into the signatures array
il.Emit( OpCodes.Ldc_I4, signatureIndex );
// Fetch the signature byte[] element from the array
il.Emit( OpCodes.Ldelem_Ref );

然后当您从动态方法创建委托时,您可以使用带有目标对象的重载,它成为(隐藏的)第一个参数:

var deserializerDelegate = dynamicMethod.CreateDelegate(typeof(YourDelegateType), signatures);

综上所述,请参阅我对您上面关于使用替代签名查找算法的问题的评论,该算法可能比线性搜索更优化,即使使用动态生成的 IL。