Files
UnrealEngine/Engine/Source/Programs/Shared/EpicGames.Serialization/Converters/CbEnumConverter.cs
2025-05-18 13:04:45 +08:00

108 lines
3.5 KiB
C#

// Copyright Epic Games, Inc. All Rights Reserved.
using System;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
namespace EpicGames.Serialization.Converters
{
class CbEnumConverter<T> : CbConverter<T>, ICbConverterMethods where T : Enum
{
readonly Func<CbField, T> _readFunc;
readonly Action<CbWriter, T> _writeFunc;
readonly Action<CbWriter, CbFieldName, T> _writeNamedFunc;
public CbEnumConverter()
{
Type type = typeof(T);
ReadMethod = new DynamicMethod($"Read_{type.Name}", type, [typeof(CbField)]);
CreateEnumReader(ReadMethod.GetILGenerator());
_readFunc = (Func<CbField, T>)ReadMethod.CreateDelegate(typeof(Func<CbField, T>));
WriteMethod = new DynamicMethod($"Write_{type.Name}", null, [typeof(CbWriter), type]);
CreateEnumWriter(WriteMethod.GetILGenerator());
_writeFunc = (Action<CbWriter, T>)WriteMethod.CreateDelegate(typeof(Action<CbWriter, T>));
WriteNamedMethod = new DynamicMethod($"WriteNamed_{type.Name}", null, [typeof(CbWriter), typeof(CbFieldName), type]);
CreateNamedEnumWriter(WriteNamedMethod.GetILGenerator());
_writeNamedFunc = (Action<CbWriter, CbFieldName, T>)WriteNamedMethod.CreateDelegate(typeof(Action<CbWriter, CbFieldName, T>));
}
public DynamicMethod ReadMethod { get; }
MethodInfo ICbConverterMethods.ReadMethod => ReadMethod;
public DynamicMethod WriteMethod { get; }
MethodInfo ICbConverterMethods.WriteMethod => WriteMethod;
public DynamicMethod WriteNamedMethod { get; }
MethodInfo ICbConverterMethods.WriteNamedMethod => WriteNamedMethod;
public override T Read(CbField field) => _readFunc(field);
public override void Write(CbWriter writer, T value) => _writeFunc(writer, value);
public override void WriteNamed(CbWriter writer, CbFieldName name, T value) => _writeNamedFunc(writer, name, value);
static void CreateEnumReader(ILGenerator generator)
{
generator.Emit(OpCodes.Ldarg_0);
generator.EmitCall(OpCodes.Call, GetMethodInfo<CbField>(x => x.AsInt32()), null);
generator.Emit(OpCodes.Ret);
}
static void CreateEnumWriter(ILGenerator generator)
{
generator.Emit(OpCodes.Ldarg_1);
Label skipLabel = generator.DefineLabel();
generator.Emit(OpCodes.Brfalse, skipLabel);
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldarg_1);
generator.Emit(OpCodes.Conv_I8);
generator.EmitCall(OpCodes.Call, GetMethodInfo<CbWriter>(x => x.WriteIntegerValue(0L)), null);
generator.MarkLabel(skipLabel);
generator.Emit(OpCodes.Ret);
}
static void CreateNamedEnumWriter(ILGenerator generator)
{
generator.Emit(OpCodes.Ldarg_2);
Label skipLabel = generator.DefineLabel();
generator.Emit(OpCodes.Brfalse, skipLabel);
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldarg_1);
generator.Emit(OpCodes.Ldarg_2);
generator.Emit(OpCodes.Conv_I8);
generator.EmitCall(OpCodes.Call, GetMethodInfo<CbWriter>(x => x.WriteInteger(default, 0L)), null);
generator.MarkLabel(skipLabel);
generator.Emit(OpCodes.Ret);
}
static MethodInfo GetMethodInfo<TArg>(Expression<Action<TArg>> expr)
{
return ((MethodCallExpression)expr.Body).Method;
}
}
class CbEnumConverterFactory : CbConverterFactory
{
public override CbConverter? CreateConverter(Type type)
{
CbConverter? converter = null;
if (type.IsEnum)
{
Type converterType = typeof(CbEnumConverter<>).MakeGenericType(type);
converter = (CbConverter?)Activator.CreateInstance(converterType);
}
return converter;
}
}
}