将字节数组烘焙到动态 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 中手动定义它们是不可行的。一种解决方法是定义一个新的动态 Assembly
和 Type
并将字节存储在那里,但我想尽可能避免这样做。
您可能想要将一组签名字节数组 (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。
我正在通过发出 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 中手动定义它们是不可行的。一种解决方法是定义一个新的动态 Assembly
和 Type
并将字节存储在那里,但我想尽可能避免这样做。
您可能想要将一组签名字节数组 (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。