private static ValueWriter GenerateFieldInfoSerializer(Serializer serializer, FieldInfo field) { if (serializer == null) { throw new ArgumentNullException(nameof(serializer)); } if (field == null) { throw new ArgumentNullException(nameof(field)); } //get the serializer for the type of the field var valueSerializer = serializer.GetSerializerByType(field.FieldType); //runtime generate a delegate that reads the content of the given field var getFieldValue = GenerateFieldInfoReader(field); //if the type is one of our special primitives, ignore manifest as the content will always only be of this type if (!serializer.Options.VersionTolerance && Serializer.IsPrimitiveType(field.FieldType)) { //primitive types does not need to write any manifest, if the field type is known //nor can they be null (StringSerializer has it's own null handling) ValueWriter fieldWriter = (stream, o, session) => { var value = getFieldValue(o); valueSerializer.WriteValue(stream, value, session); }; return(fieldWriter); } else { var valueType = field.FieldType; if (field.FieldType.IsGenericType && field.FieldType.GetGenericTypeDefinition() == typeof(Nullable <>)) { var nullableType = field.FieldType.GetGenericArguments()[0]; valueSerializer = serializer.GetSerializerByType(nullableType); valueType = nullableType; } var preserveObjectReferences = serializer.Options.PreserveObjectReferences; ValueWriter fieldWriter = (stream, o, session) => { var value = getFieldValue(o); stream.WriteObject(value, valueType, valueSerializer, preserveObjectReferences, session); }; return(fieldWriter); } }
private static Action <Stream, object, DeserializerSession> GenerateFieldInfoDeserializer(Serializer serializer, Type type, FieldInfo field) { if (serializer == null) { throw new ArgumentNullException(nameof(serializer)); } if (type == null) { throw new ArgumentNullException(nameof(type)); } if (field == null) { throw new ArgumentNullException(nameof(field)); } var s = serializer.GetSerializerByType(field.FieldType); Action <object, object> setter; if (field.IsInitOnly) { setter = field.SetValue; } else { var targetExp = Parameter(typeof(object), "target"); var valueExp = Parameter(typeof(object), "value"); // ReSharper disable once PossibleNullReferenceException Expression castTartgetExp = field.DeclaringType.IsValueType ? Unbox(targetExp, type) : Convert(targetExp, type); Expression castValueExp = Convert(valueExp, field.FieldType); var fieldExp = Field(castTartgetExp, field); var assignExp = Assign(fieldExp, castValueExp); setter = Lambda <Action <object, object> >(assignExp, targetExp, valueExp).Compile(); } if (!serializer.Options.VersionTolerance && Serializer.IsPrimitiveType(field.FieldType)) { //Only optimize if property names are not included. //if they are included, we need to be able to skip past unknown property data //e.g. if sender have added a new property that the receiveing end does not yet know about //which we cannot do w/o a manifest Action <Stream, object, DeserializerSession> fieldReader = (stream, o, session) => { var value = s.ReadValue(stream, session); setter(o, value); }; return(fieldReader); } else { Action <Stream, object, DeserializerSession> fieldReader = (stream, o, session) => { var value = stream.ReadObject(session); setter(o, value); }; return(fieldReader); } }
private ObjectReader GetFieldsReader([NotNull] Serializer serializer, [NotNull] IEnumerable <FieldInfo> fields, [NotNull] Type type) { var c = new Compiler <ObjectReader>(); var stream = c.Parameter <Stream>("stream"); var session = c.Parameter <DeserializerSession>("session"); var newExpression = c.NewObject(type); var target = c.Variable <object>("target"); var assignNewObjectToTarget = c.WriteVar(target, newExpression); c.Emit(assignNewObjectToTarget); if (serializer.Options.PreserveObjectReferences) { var trackDeserializedObjectMethod = typeof(DeserializerSession) .GetMethod(nameof(DeserializerSession.TrackDeserializedObject)); c.EmitCall(trackDeserializedObjectMethod, session, target); } //for (var i = 0; i < storedFieldCount; i++) //{ // var fieldName = stream.ReadLengthEncodedByteArray(session); // if (!Utils.Compare(fieldName, fieldNames[i])) // { // //TODO: field name mismatch // //this should really be a compare less equal or greater // //to know if the field is added or removed // //1) if names are equal, read the value and assign the field // //2) if the field is less than the expected field, then this field is an unknown new field // //we need to read this object and just ignore its content. // //3) if the field is greater than the expected, we need to check the next expected until // //the current is less or equal, then goto 1) // } //} var typedTarget = c.CastOrUnbox(target, type); var fieldsArray = fields.ToArray(); var serializers = fieldsArray.Select(field => serializer.GetSerializerByType(field.FieldType)).ToArray(); var preallocatedBufferSize = serializers.Length != 0 ? serializers.Max(s => s.PreallocatedByteBufferSize) : 0; if (preallocatedBufferSize > 0) { EmitPreallocatedBuffer(c, preallocatedBufferSize, session, typeof(DeserializerSession).GetMethod(nameof(DeserializerSession.GetBuffer))); } for (var i = 0; i < fieldsArray.Length; i++) { var field = fieldsArray[i]; var s = serializers[i]; int read; if (!serializer.Options.VersionTolerance && field.FieldType.IsWirePrimitive()) { //Only optimize if property names are not included. //if they are included, we need to be able to skip past unknown property data //e.g. if sender have added a new property that the receiveing end does not yet know about //which we cannot do w/o a manifest read = s.EmitReadValue(c, stream, session, field); } else { var method = typeof(StreamEx).GetMethod(nameof(StreamEx.ReadObject)); read = c.StaticCall(method, stream, session); read = c.Convert(read, field.FieldType); } var assignReadToField = c.WriteField(field, typedTarget, read); c.Emit(assignReadToField); } c.Emit(target); var readAllFields = c.Compile(); return(readAllFields); }
private static Action <Stream, object, SerializerSession> GenerateFieldDeserializer(Serializer serializer, Type type, FieldInfo field) { var s = serializer.GetSerializerByType(field.FieldType); Action <object, object> setter; if (field.IsInitOnly) { setter = field.SetValue; } else { ParameterExpression targetExp = Expression.Parameter(typeof(object), "target"); ParameterExpression valueExp = Expression.Parameter(typeof(object), "value"); Expression castTartgetExp = field.DeclaringType.IsValueType ? Expression.Unbox(targetExp, type) : Expression.Convert(targetExp, type); Expression castValueExp = Expression.Convert(valueExp, field.FieldType); MemberExpression fieldExp = Expression.Field(castTartgetExp, field); BinaryExpression assignExp = Expression.Assign(fieldExp, castValueExp); setter = Expression.Lambda <Action <object, object> >(assignExp, targetExp, valueExp).Compile(); } if (!serializer.Options.VersionTolerance && Serializer.IsPrimitiveType(field.FieldType)) { //Only optimize if property names are not included. //if they are included, we need to be able to skip past unknown property data //e.g. if sender have added a new property that the receiveing end does not yet know about //which we cannot do w/o a manifest Action <Stream, object, SerializerSession> fieldReader = (stream, o, session) => { var value = s.ReadValue(stream, session); setter(o, value); //var x = field.GetValue(o); //if (value != null && !value.Equals(x)) //{ //} // field.SetValue(o, value); }; return(fieldReader); } else { Action <Stream, object, SerializerSession> fieldReader = (stream, o, session) => { var value = stream.ReadObject(session); // field.SetValue(o, value); setter(o, value); //var x = field.GetValue(o); //if (value != null && !value.Equals(x)) //{ //} }; return(fieldReader); } }
//this generates a FieldWriter that writes all fields by unrolling all fields and calling them individually //no loops involved private ObjectWriter GetFieldsWriter([NotNull] Serializer serializer, [NotNull] IEnumerable <FieldInfo> fields, out int preallocatedBufferSize) { var c = new Compiler <ObjectWriter>(); var stream = c.Parameter <Stream>("stream"); var target = c.Parameter <object>("target"); var session = c.Parameter <SerializerSession>("session"); var preserveReferences = c.Constant(serializer.Options.PreserveObjectReferences); if (serializer.Options.PreserveObjectReferences) { var method = typeof(SerializerSession).GetMethod(nameof(SerializerSession.TrackSerializedObject)); c.EmitCall(method, session, target); } var fieldsArray = fields.ToArray(); var serializers = fieldsArray.Select(field => serializer.GetSerializerByType(field.FieldType)).ToArray(); preallocatedBufferSize = serializers.Length != 0 ? serializers.Max(s => s.PreallocatedByteBufferSize) : 0; if (preallocatedBufferSize > 0) { EmitPreallocatedBuffer(c, preallocatedBufferSize, session, typeof(SerializerSession).GetMethod("GetBuffer")); } for (var i = 0; i < fieldsArray.Length; i++) { var field = fieldsArray[i]; //get the serializer for the type of the field var valueSerializer = serializers[i]; //runtime Get a delegate that reads the content of the given field var cast = c.CastOrUnbox(target, field.DeclaringType); var readField = c.ReadField(field, cast); //if the type is one of our special primitives, ignore manifest as the content will always only be of this type if (!serializer.Options.VersionTolerance && field.FieldType.IsWirePrimitive()) { //primitive types does not need to write any manifest, if the field type is known valueSerializer.EmitWriteValue(c, stream, readField, session); } else { var converted = c.Convert <object>(readField); var valueType = field.FieldType; if (field.FieldType.IsNullable()) { var nullableType = field.FieldType.GetNullableElement(); valueSerializer = serializer.GetSerializerByType(nullableType); valueType = nullableType; } var vs = c.Constant(valueSerializer); var vt = c.Constant(valueType); var method = typeof(StreamEx).GetMethod(nameof(StreamEx.WriteObject)); c.EmitStaticCall(method, stream, converted, vt, vs, preserveReferences, session); } } return(c.Compile()); }