/// <summary> /// Serializes an <paramref name="obj"/> of type <paramref name="type"/> into <paramref name="buffer"/> /// </summary> /// <param name="buffer">The buffer to serialize the object into</param> /// <param name="obj">The object to serialize</param> /// <param name="type">The object's type, or null to get it from <paramref name="obj"/></param> public void Serialize(TBuffer buffer, object obj, Type type = null) { type = type ?? obj?.GetType() ?? throw new ArgumentNullException("obj is null and no type has been specified"); if (!Serializers.TryGetValue(type, out var ser)) { var bufferParam = Parameter(typeof(TBuffer), "_buffer"); var objParam = Parameter(typeof(object), "_obj"); var exprs = new List <Expression>(); if (Options.WriteHeader) { //Write 243, or 244 if Options.WriteStructureSignature is on exprs.Add(Formats.Get(typeof(byte)).Serialize(new FormatContextWithValue(Formats, typeof(byte), bufferParam, Constant((byte)(Options.WriteStructureSignature ? MagicWithSignature : Magic))))); if (Options.WriteStructureSignature) { var hash = ClassSignature.Get(type); exprs.Add(Formats.Get(typeof(string)).Serialize(new FormatContextWithValue(Formats, typeof(string), bufferParam, Constant(hash)))); } } exprs.AddRange(GetBlock(objParam, bufferParam, type)); Serializers[type] = ser = Lambda <Action <TBuffer, object> >(Block(exprs), bufferParam, objParam).Compile(); } ser(buffer, obj); IEnumerable <Expression> GetBlock(Expression objExpr, Expression bufferExpr, Type objType) { var convertedObj = Convert(objExpr, objType); foreach (var field in GetFields(objType)) { var format = Formats.Get(field.MemberType); if (format == null) { if (Options.SerializeUnknownTypes) { yield return(Block(GetBlock(PropertyOrField(convertedObj, field.Name), bufferExpr, field.MemberType))); } else { throw new Exception($"Format not found for '{type.Name}.{field.Name}'"); } } else { yield return(format.Serialize(new FormatContextWithValue( Formats, field.MemberType, bufferExpr, PropertyOrField(convertedObj, field.Name), field.GetConcreteType()))); } } } }
/// <summary> /// Reads an object of type <paramref name="type"/> from <paramref name="buffer"/>. /// </summary> /// <param name="buffer">The buffer to read from</param> /// <param name="type">The type of the object to read</param> public object Deserialize(TBuffer buffer, Type type) { if (!Deserializers.TryGetValue(type, out var des)) { var exprs = new List <Expression>(); var bufferParam = Parameter(typeof(TBuffer), "_buffer"); var objVar = Variable(typeof(object), "_obj"); if (Options.CheckHeader) { var magicVar = Variable(typeof(byte), "_magic"); var hash = ClassSignature.Get(type); /* * byte magic = Read<byte>(); * if (magic == 244) * { * if (Read<string>() != "HASH") * { * if (Options.CheckStructureSignature) * throw new Exception(); * } * } * else if (magic != 243) * { * throw new Exception(); * } */ exprs.Add(Block(new[] { magicVar }, Assign(magicVar, Read <byte>()), IfThenElse( Equal(magicVar, Constant(MagicWithSignature)), IfThen(NotEqual(Read <string>(), Constant(hash)), Options.CheckStructureSignature ? InvalidHeaderException.Throw("Class structure signature mismatch") : Block()), IfThen(NotEqual(magicVar, Constant(Magic)), InvalidHeaderException.Throw("Invalid magic number"))))); } exprs.AddRange(GetBlock(objVar, bufferParam, type)); exprs.Add(objVar); Deserializers[type] = des = Lambda <Func <TBuffer, object> >(Block(new[] { objVar }, exprs), bufferParam).Compile(); Expression Read <T>() => Formats.Get(typeof(T))?.Deserialize(new FormatContext(Formats, typeof(T), bufferParam)) ?? throw new InvalidOperationException("Cannot deserialize type " + typeof(T).FullName); } return(des(buffer)); IEnumerable <Expression> GetBlock(Expression objExpr, Expression bufferExpr, Type objType) { var convertedObj = Convert(objExpr, objType); yield return(Assign(objExpr, New(objType))); foreach (var field in GetFields(objType)) { if (field.MemberType.IsInterface) { if (!field.Member.IsDefined(typeof(ConcreteTypeAttribute))) { throw new Exception("Fields of interface types must be decorated with ConcreteTypeAttribute"); } else if (!field.MemberType.IsAssignableFrom(field.GetConcreteType())) { throw new Exception($"The specified concrete type for field '{field.Name}' doesn't implement the field type ({field.MemberType.Name})"); } } var format = Formats.Get(field.MemberType); if (format == null) { if (Options.SerializeUnknownTypes) { yield return(Block(GetBlock(PropertyOrField(convertedObj, field.Name), bufferExpr, field.MemberType))); } else { throw new Exception($"Format not found for '{objType.Name}.{field.Name}'"); } } else { yield return(Assign(PropertyOrField(convertedObj, field.Name), format.Deserialize( new FormatContext(Formats, field.MemberType, bufferExpr, field.GetConcreteType())))); } } } }