Запекание байтового массива в динамический IL - PullRequest
1 голос
/ 20 мая 2019

Я пишу ориентированный на производительность десериализатор данных, испуская IL. Сериализованные данные UTF8, а поля обозначены как строки.

[FieldA]: 22
[FieldB]: 16

Я уже написал специальную программу чтения, которая должным образом токенизирует сериализованные данные и предоставляет ReadOnlySpan<byte> при пошаговом выполнении сериализованных данных.

Мне бы хотелось иметь статический встроенный десериализатор, который мог бы иметь упакованные байтовые подписи полей, чтобы я мог легко создать таблицу переходов и установить соответствующие поля.

Как может выглядеть не динамический код:

// 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();
...

Как создается таблица переходов:

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, который я излучаю, чтобы они были частью делегата?

Эти сигнатуры генерируются во время выполнения на основе информации о типе, поэтому их определение вручную в статическом классе невозможно. Обходным путем может быть определение новых динамических Assembly и Type и сохранение там байтов, но я хотел бы избежать необходимости делать это, если это возможно.

1 Ответ

2 голосов
/ 21 мая 2019

Что вы можете сделать, это передать массив байтовых массивов подписи (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.

...