"7c9e6679-7425-40de-944b-e07fc1f90ae7|48972|Alice in Wonderland"
public override void Serialize(TObject theObject, Stream stream) { foreach (var property in _properties) { var val = property.GetValue(theObject); if (property.PropertyType == typeof(byte)) { stream.WriteByte((byte)val); } else if (property.PropertyType == typeof(bool)) { var bytes = BitConverter.GetBytes((bool)val); stream.Write(bytes, 0, bytes.Length); } else if (property.PropertyType == typeof(int)) { var bytes = BitConverter.GetBytes((int)val); stream.Write(bytes, 0, bytes.Length); } else if (property.PropertyType == typeof(Guid)) { var bytes = ((Guid)val).ToByteArray(); stream.Write(bytes, 0, bytes.Length); } ... } } public override TObject Deserialize(Stream stream) { var theObject = Activator.CreateInstance<TObject>(); foreach (var property in _properties) { object val; if (property.PropertyType == typeof(byte)) { val = stream.ReadByte(); } var bytesCount = TypesInfo.GetBytesCount(type); var valueBytes = new byte[bytesCount]; stream.Read(valueBytes, 0, valueBytes.Length); if (property.PropertyType == typeof(bool)) { val = BitConverter.ToBoolean(valueBytes, 0); } else if (property.PropertyType == typeof(int)) { val = BitConverter.ToInt32(valueBytes, 0); } else if (property.PropertyType == typeof(Guid)) { val = new Guid(valueBytes); } ... property.SetValue(theObject, val); } }
public virtual string GetTypeVersion() { return typeof(TObject).Assembly.GetName().Version.ToString(); }
var reflectionSerializer = new ReflectionCompactSerializer<Entity>(); typeVersion = reflectionSerializer.GetTypeVersion(); reflectionSerializer.WriteVersion(stream, typeVersion); reflectionSerializer.Serialize(originalEntity, stream); var version = reflectionSerializer.ReadObjectVersion(stream); deserializedEntity = reflectionSerializer.Deserialize(stream);
public class ReflectionCompactSerializer<TObject> : CompactSerializerBase<TObject> where TObject: class, new () { private readonly PropertyInfo[] _properties = typeof(TObject).GetProperties(BindingFlags.Instance | BindingFlags.Public); ... }
public static EmitSerializer<TObject> Generate<TObject>() where TObject : class, new() { var propertiesWriter = new DynamicMethod( "WriteProperties", null, new Type[] { typeof(Stream), typeof(TObject) }, typeof(EmitSerializer<TObject>)); var writerIlGenerator = propertiesWriter.GetILGenerator(); var writerEmitter = new CodeEmitter(writerIlGenerator); var propertiesReader = new DynamicMethod( "ReadProperties", null, new Type[] { typeof(Stream), typeof(TObject) }, typeof(EmitSerializer<TObject>)); var readerIlGenerator = propertiesReader.GetILGenerator(); var readerEmitter = new CodeEmitter(readerIlGenerator); var properties = typeof(TObject) .GetProperties(BindingFlags.Instance | BindingFlags.Public); foreach(var property in properties) { if (property.PropertyType == typeof(byte)) { writerEmitter.EmitWriteBytePropertyCode(property); readerEmitter.EmitReadBytePropertyCode(property); } else if (property.PropertyType == typeof(Guid)) { writerEmitter.EmitWriteGuidPropertyCode(property); readerEmitter.EmitReadGuidPropertyCode(property); } … } var writePropertiesDelegate = (Action<Stream,TObject>)propertiesWriter .CreateDelegate(typeof(Action<Stream, TObject>)); var readPropertiesDelegate = (Action<Stream, TObject>)propertiesReader .CreateDelegate(typeof(Action<Stream, TObject>)); return new EmitSerializer<TObject>( writePropertiesDelegate, readPropertiesDelegate); } }
monodis < > --output=< >
private static void WritePrimitiveTypeProperty(Stream stream, Entity entity) { var index = entity.Index; var valueBytes = BitConverter.GetBytes(index); stream.Write(valueBytes, 0, valueBytes.Length); }
.method private static hidebysig default void WritePrimitiveTypeProperty (class [mscorlib]System.IO.Stream 'stream', class SourcesForIL.Entity entity) cil managed { // Method begins at RVA 0x241c // Code size 28 (0x1c) .maxstack 4 .locals init ( int32 V_0, unsigned int8[] V_1) IL_0000: nop IL_0001: ldarg.1 IL_0002: callvirt instance int32 class SourcesForIL.Entity::get_Index() IL_0007: stloc.0 IL_0008: ldloc.0 IL_0009: call unsigned int8[] class [mscorlib]System.BitConverter::GetBytes(int32) IL_000e: stloc.1 IL_000f: ldarg.0 IL_0010: ldloc.1 IL_0011: ldc.i4.0 IL_0012: ldloc.1 IL_0013: ldlen IL_0014: conv.i4 IL_0015: callvirt instance void class [mscorlib]System.IO.Stream::Write(unsigned int8[], int32, int32) IL_001a: nop IL_001b: ret }
var byteArray = _ilGenerator.DeclareLocal(typeof(byte[])); // load object under serialization onto the evaluation stack _ilGenerator.Emit(OpCodes.Ldarg_1); // get property value _ilGenerator.EmitCall(OpCodes.Callvirt, property.GetMethod, null); // get value's representation in bytes _ilGenerator.EmitCall(OpCodes.Call, BitConverterMethodsInfo.ChooseGetBytesOverloadByType(valueType), null); // save the bytes array from the stack in local variable _ilGenerator.Emit(OpCodes.Stloc, byteArray); // load stream parameter onto the evaluation stack _ilGenerator.Emit(OpCodes.Ldarg_0); // load bytesCount array _ilGenerator.Emit(OpCodes.Ldloc_S, bytesArray); // load offset parameter == 0 onto the stack _ilGenerator.Emit(OpCodes.Ldc_I4_0); // load bytesCount array _ilGenerator.Emit(OpCodes.Ldloc_S, bytesArray); // calculate the array length _ilGenerator.Emit(OpCodes.Ldlen); // convert it to Int32 _ilGenerator.Emit(OpCodes.Conv_I4); // write array to stream _ilGenerator.EmitCall(OpCodes.Callvirt, StreamMethodsInfo.Write, null);
public static MethodInfo ChooseGetBytesOverloadByType(Type type) { if (_getBytesMethods.ContainsKey(type)) { return _getBytesMethods[type]; } var method = typeof(BitConverter).GetMethod("GetBytes", new Type[] { type }); if (method == null) { throw new InvalidOperationException("No overload for parameter of type " + type.Name); } _getBytesMethods[type] = method; return method; }
public static byte[] GetDecimalBytes(decimal value) { var bits = decimal.GetBits((decimal)value); var bytes = new List<byte>(); foreach (var bitsPart in bits) { bytes.AddRange(BitConverter.GetBytes(bitsPart)); } return bytes.ToArray(); } public static decimal BytesToDecimal(byte[] bytes, int startIndex) { var valueBytes = bytes.Skip(startIndex).ToArray(); if (valueBytes.Length != 16) throw new Exception("A decimal must be created from exactly 16 bytes"); var bits = new Int32[4]; for (var bitsPart = 0; bitsPart <= 15; bitsPart += 4) { bits[bitsPart/4] = BitConverter.ToInt32(valueBytes, bitsPart); } return new decimal(bits); }
private static void WriteGuidProperty(Stream stream, Entity entity) { var id = entity.Id; var valueBytes = id.ToByteArray(); stream.Write(valueBytes, 0, valueBytes.Length); } private static void ReadGuidProperty(Stream stream, Entity entity) { var valueBytes = new byte[16]; stream.Read(valueBytes, 0, valueBytes.Length); entity.Id = new Guid(valueBytes); }
private void EmitWriteDateTimeOffsetVariable(LocalBuilder dateTimeOffset) { var offset = _ilGenerator.DeclareLocal(typeof(TimeSpan)); var dateTimeTicks = _ilGenerator.DeclareLocal(typeof(long)); var dateTimeTicksByteArray = _ilGenerator.DeclareLocal(typeof(byte[])); var offsetTicksByteArray = _ilGenerator.DeclareLocal(typeof(byte[])); // load the variable address to the stack _ilGenerator.Emit(OpCodes.Ldloca_S, dateTimeOffset); // call method to get Offset property _ilGenerator.EmitCall(OpCodes.Call, DateTimeOffsetMembersInfo.OffsetProperty, null); // save it to local variable _ilGenerator.Emit(OpCodes.Stloc, offset); // load the variable address to the stack _ilGenerator.Emit(OpCodes.Ldloca_S, offset); // call method to get offset Ticks property _ilGenerator.EmitCall(OpCodes.Call, TimeSpanMembersInfo.TicksProperty, null); // convert it to byte array _ilGenerator.EmitCall(OpCodes.Call, GetInt64BytesMethodInfo, null); // save it to local variable _ilGenerator.Emit(OpCodes.Stloc, offsetTicksByteArray); EmitWriteBytesArrayToStream(offsetTicksByteArray); // load the dateTimeOffset variable address to the stack _ilGenerator.Emit(OpCodes.Ldloca_S, dateTimeOffset); // call method to get Ticks property _ilGenerator.EmitCall(OpCodes.Call, DateTimeOffsetMembersInfo.TicksProperty, null); // save it to local variable _ilGenerator.Emit(OpCodes.Stloc, dateTimeTicks); // load the variable address to the stack _ilGenerator.Emit(OpCodes.Ldloc, dateTimeTicks); // convert it to byte array _ilGenerator.EmitCall(OpCodes.Call, GetInt64BytesMethodInfo, null); // save it to local variable _ilGenerator.Emit(OpCodes.Stloc, dateTimeTicksByteArray); EmitWriteBytesArrayToStream(dateTimeTicksByteArray); }
public static int GetBytesCount(Type propertyType) { if (propertyType == typeof(DateTime)) { return sizeof(long) + 1; } else if (propertyType == typeof(DateTimeOffset)) { return sizeof(long) + sizeof(long); } else if (propertyType == typeof(bool)) { return sizeof(bool); } else if(propertyType == typeof(char)) { return sizeof(char); } else if (propertyType == typeof(decimal)) { return 16; } else { return Marshal.SizeOf(propertyType); } }
public void EmitWriteNullablePropertyCode(PropertyInfo property) { var nullableValue = _ilGenerator.DeclareLocal(property.PropertyType); var isNull = _ilGenerator.DeclareLocal(typeof(bool)); var isNullByte = _ilGenerator.DeclareLocal(typeof(byte)); var underlyingType = property.PropertyType.GetGenericArguments().Single(); var value = _ilGenerator.DeclareLocal(underlyingType); var valueBytes = _ilGenerator.DeclareLocal(typeof(byte[])); var nullableInfo = NullableInfo.GetNullableInfo(underlyingType); var nullFlagBranch = _ilGenerator.DefineLabel(); var byteFlagLabel = _ilGenerator.DefineLabel(); var noValueLabel = _ilGenerator.DefineLabel(); EmitLoadPropertyValueToStack(property); // save nullable value to local variable _ilGenerator.Emit(OpCodes.Stloc, nullableValue); // load address of the variable to stack _ilGenerator.Emit(OpCodes.Ldloca_S, nullableValue); // get HasValue property _ilGenerator.EmitCall(OpCodes.Call, nullableInfo.HasValueProperty, null); // load value '0' to stack _ilGenerator.Emit(OpCodes.Ldc_I4_0); // compare _ilGenerator.Emit(OpCodes.Ceq); // save to local boolean variable _ilGenerator.Emit(OpCodes.Stloc, isNull); // load to stack _ilGenerator.Emit(OpCodes.Ldloc, isNull); // jump to isNull branch, if needed _ilGenerator.Emit(OpCodes.Brtrue_S, nullFlagBranch); // load value '0' to stack _ilGenerator.Emit(OpCodes.Ldc_I4_0); // jump to byteFlagLabel _ilGenerator.Emit(OpCodes.Br_S, byteFlagLabel); _ilGenerator.MarkLabel(nullFlagBranch); // load value '1' to stack _ilGenerator.Emit(OpCodes.Ldc_I4_1); _ilGenerator.MarkLabel(byteFlagLabel); // convert to byte _ilGenerator.Emit(OpCodes.Conv_U1); // save to local variable _ilGenerator.Emit(OpCodes.Stloc, isNullByte); // load stream parameter to stack _ilGenerator.Emit(OpCodes.Ldarg_0); // load byte flag to the stack _ilGenerator.Emit(OpCodes.Ldloc, isNullByte); // write it to the stream _ilGenerator.EmitCall(OpCodes.Callvirt, StreamMethodsInfo.WriteByte, null); // load isNull flag to stack _ilGenerator.Emit(OpCodes.Ldloc, isNull); // load value '0' _ilGenerator.Emit(OpCodes.Ldc_I4_0); // compare _ilGenerator.Emit(OpCodes.Ceq); // jump to tne end, if no value presented _ilGenerator.Emit(OpCodes.Brfalse_S, noValueLabel); // load the address of the nullable to the stack _ilGenerator.Emit(OpCodes.Ldloca_S, nullableValue); // get actual value _ilGenerator.EmitCall(OpCodes.Call, nullableInfo.ValueProperty, null); EmitWriteValueFromStackToStream(underlyingType); _ilGenerator.MarkLabel(noValueLabel); }
private void EmitReadStringFromStreamToStack() { var bytesCoutArray = _ilGenerator.DeclareLocal(typeof(byte[])); var stringBytesCount = _ilGenerator.DeclareLocal(typeof(int)); var stringBytesArray = _ilGenerator.DeclareLocal(typeof(byte[])); var isNull = _ilGenerator.DeclareLocal(typeof(bool)); var isNotNullBranch = _ilGenerator.DefineLabel(); var endOfReadLabel = _ilGenerator.DefineLabel(); var propertyBytesCount = TypesInfo.GetBytesCount(typeof(int)); // push the amout of bytes to read onto the stack _ilGenerator.Emit(OpCodes.Ldc_I4, propertyBytesCount); // allocate array to store bytes _ilGenerator.Emit(OpCodes.Newarr, typeof(byte)); // stores the allocated array in the local variable _ilGenerator.Emit(OpCodes.Stloc, bytesCoutArray); // push the stream parameter _ilGenerator.Emit(OpCodes.Ldarg_0); // push the byte count array _ilGenerator.Emit(OpCodes.Ldloc, bytesCoutArray); // push '0' as the offset parameter _ilGenerator.Emit(OpCodes.Ldc_I4_0); // push the byte array again - to calculate its length _ilGenerator.Emit(OpCodes.Ldloc, bytesCoutArray); // get the length _ilGenerator.Emit(OpCodes.Ldlen); // convert the result to Int32 _ilGenerator.Emit(OpCodes.Conv_I4); // call the stream.Read method _ilGenerator.EmitCall(OpCodes.Callvirt, StreamMethodsInfo.Read, null); // pop amount of bytes read _ilGenerator.Emit(OpCodes.Pop); // push the bytes count array _ilGenerator.Emit(OpCodes.Ldloc, bytesCoutArray); // push '0' as the start index parameter _ilGenerator.Emit(OpCodes.Ldc_I4_0); // convert the bytes to Int32 _ilGenerator.EmitCall(OpCodes.Call, BytesToInt32MethodInfo, null); // save bytes count to local variable _ilGenerator.Emit(OpCodes.Stloc, stringBytesCount); // load it to the stack _ilGenerator.Emit(OpCodes.Ldloc, stringBytesCount); // put value '-1' to the stack _ilGenerator.Emit(OpCodes.Ldc_I4_M1); // compare bytes count and -1 _ilGenerator.Emit(OpCodes.Ceq); // save to boolean variable _ilGenerator.Emit(OpCodes.Stloc, isNull); // load to stack _ilGenerator.Emit(OpCodes.Ldloc, isNull); // if false, jump to isNotNullBranch _ilGenerator.Emit(OpCodes.Brfalse_S, isNotNullBranch); // push 'null' value _ilGenerator.Emit(OpCodes.Ldnull); // jump to the end of read fragment _ilGenerator.Emit(OpCodes.Br_S, endOfReadLabel); // not null string value branch _ilGenerator.MarkLabel(isNotNullBranch); // load bytes count to the stack _ilGenerator.Emit(OpCodes.Ldloc, stringBytesCount); // allocate array to store bytes _ilGenerator.Emit(OpCodes.Newarr, typeof(byte)); // save it to local variable _ilGenerator.Emit(OpCodes.Stloc, stringBytesArray); // push the stream parameter _ilGenerator.Emit(OpCodes.Ldarg_0); // load string bytes array to stack _ilGenerator.Emit(OpCodes.Ldloc, stringBytesArray); // push '0' as the start index parameter _ilGenerator.Emit(OpCodes.Ldc_I4_0); // load string bytes array to stack to get array length _ilGenerator.Emit(OpCodes.Ldloc, stringBytesArray); // get the length _ilGenerator.Emit(OpCodes.Ldlen); // convert the result to Int32 _ilGenerator.Emit(OpCodes.Conv_I4); // call the stream.Read method _ilGenerator.EmitCall(OpCodes.Callvirt, StreamMethodsInfo.Read, null); // pop amount of bytes read _ilGenerator.Emit(OpCodes.Pop); // load Encoding to stack _ilGenerator.EmitCall(OpCodes.Call, EncodingMembersInfo.EncodingGetter, null); // load string bytes _ilGenerator.Emit(OpCodes.Ldloc, stringBytesArray); // call Encoding.GetString() method _ilGenerator.EmitCall(OpCodes.Callvirt, EncodingMembersInfo.GetStringMethod, null); _ilGenerator.MarkLabel(endOfReadLabel); }
public void EmitReadArrayPropertyCode(PropertyInfo property) { var elementType = property.PropertyType.GetElementType(); var elementBytesArray = _ilGenerator.DeclareLocal(typeof(byte[])); var lengthBytes = _ilGenerator.DeclareLocal(typeof(byte[])); var arrayLength = _ilGenerator.DeclareLocal(typeof(int)); var array = _ilGenerator.DeclareLocal(property.PropertyType); var element = _ilGenerator.DeclareLocal(elementType); var index = _ilGenerator.DeclareLocal(typeof(int)); var isNullArrayLabel = _ilGenerator.DefineLabel(); var setPropertyLabel = _ilGenerator.DefineLabel(); var loopConditionLabel = _ilGenerator.DefineLabel(); var loopIterationLabel = _ilGenerator.DefineLabel(); // push deserialized object to stack _ilGenerator.Emit(OpCodes.Ldarg_1); EmitAllocateBytesArrayForType(typeof(int), lengthBytes); EmitReadByteArrayFromStream(lengthBytes); EmitConvertBytesArrayToPrimitiveValueOnStack(lengthBytes, typeof(int)); // save it to local variable _ilGenerator.Emit(OpCodes.Stloc, arrayLength); EmitJumpIfNoElements(arrayLength, isNullArrayLabel); // push array length to stack _ilGenerator.Emit(OpCodes.Ldloc, arrayLength); // create new array _ilGenerator.Emit(OpCodes.Newarr, elementType); // save it to the local variable _ilGenerator.Emit(OpCodes.Stloc, array); EmitZeroIndex(index); if (elementType != typeof(string)) { EmitAllocateBytesArrayForType(elementType, elementBytesArray); } // jump to the loop condition check _ilGenerator.Emit(OpCodes.Br_S, loopConditionLabel); _ilGenerator.MarkLabel(loopIterationLabel); if (elementType == typeof(string)) { EmitReadStringFromStreamToStack(); } else { EmitReadValueFromStreamToStack(elementType, elementBytesArray); } // save to local variable _ilGenerator.Emit(OpCodes.Stloc, element); // load array instance to stack _ilGenerator.Emit(OpCodes.Ldloc, array); // load element index _ilGenerator.Emit(OpCodes.Ldloc_S, index); // load the element to stack _ilGenerator.Emit(OpCodes.Ldloc_S, element); // set element to the array _ilGenerator.Emit(OpCodes.Stelem, elementType); EmitIndexIncrement(index); _ilGenerator.MarkLabel(loopConditionLabel); EmitIndexIsLessCheck(index, arrayLength); // jump to the iteration if true _ilGenerator.Emit(OpCodes.Brtrue_S, loopIterationLabel); // push filled array to stack _ilGenerator.Emit(OpCodes.Ldloc, array); // jump to SetProperty label _ilGenerator.Emit(OpCodes.Br_S, setPropertyLabel); _ilGenerator.MarkLabel(isNullArrayLabel); _ilGenerator.Emit(OpCodes.Ldnull); _ilGenerator.MarkLabel(setPropertyLabel); // call object's property setter _ilGenerator.EmitCall(OpCodes.Callvirt, property.SetMethod, null); }
var originalEntity = new Entity { Name = "Name", ShortName = string.Empty, Description = null, Label = 'L', Age = 32, Index = -7, IsVisible = true, Price = 225.87M, Rating = 4.8, Weigth = 130, ShortIndex = short.MaxValue, LongIndex = long.MinValue, UnsignedIndex = uint.MaxValue, ShortUnsignedIndex = 25, LongUnsignedIndex = 11, Id = Guid.NewGuid(), CreatedAt = DateTime.Now, CreatedAtUtc = DateTime.UtcNow, LastAccessed = DateTime.MinValue, ChangedAt = DateTimeOffset.Now, ChangedAtUtc = DateTimeOffset.UtcNow, References = null, Weeks = new List<short>() { 3, 12, 24, 48, 53, 61 }, PricesHistory = new decimal[] { 225.8M, 226M, 227.87M, 224.87M }, BitMap = new bool[] { true, true, false, true, false, false, true, true }, ChildrenIds = new Guid [] { Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid() }, Schedule = new DateTime [] { DateTime.Now.AddDays(-1), DateTime.Now.AddMonths(2), DateTime.Now.AddYears(10) }, Moments = new DateTimeOffset [] { DateTimeOffset.UtcNow.AddDays(-5), DateTimeOffset.Now.AddDays(10) }, Tags = new List<string> { "The quick brown fox jumps over the lazy dog", "Reflection.Emit", string.Empty, "0" }, AlternativeId = Guid.NewGuid() };
Serializer | Average elapsed, ms | Size, bytes ------------------------------------------------------------------------------- EmitSerializer | 9.9522 | 477 ------------------------------------------------------------------------------- ReflectionSerializer | 22.9454 | 477 ------------------------------------------------------------------------------- BinaryFormatter | 246.4836 | 1959 ------------------------------------------------------------------------------- Newtonsoft JsonSerializer | 87.1893 | 1156 EmitSerializer compiled in: 104.5019 ms
Source: https://habr.com/ru/post/358872/
All Articles