ProtobufTest/Assets/Plugs/protobuf-net/Serializers/DefaultValueDecorator.cs

259 lines
9.7 KiB
C#

#if !NO_RUNTIME
using System;
using System.Reflection;
using ProtoBuf.Meta;
namespace ProtoBuf.Serializers
{
sealed class DefaultValueDecorator : ProtoDecoratorBase
{
public override Type ExpectedType => Tail.ExpectedType;
public override bool RequiresOldValue => Tail.RequiresOldValue;
public override bool ReturnsValue => Tail.ReturnsValue;
private readonly object defaultValue;
public DefaultValueDecorator(TypeModel model, object defaultValue, IProtoSerializer tail) : base(tail)
{
if (defaultValue == null) throw new ArgumentNullException(nameof(defaultValue));
Type type = model.MapType(defaultValue.GetType());
if (type != tail.ExpectedType)
{
throw new ArgumentException("Default value is of incorrect type", "defaultValue");
}
this.defaultValue = defaultValue;
}
public override void Write(object value, ProtoWriter dest)
{
if (!object.Equals(value, defaultValue))
{
Tail.Write(value, dest);
}
}
public override object Read(object value, ProtoReader source)
{
return Tail.Read(value, source);
}
#if FEAT_COMPILER
protected override void EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
Compiler.CodeLabel done = ctx.DefineLabel();
if (valueFrom == null)
{
ctx.CopyValue(); // on the stack
Compiler.CodeLabel needToPop = ctx.DefineLabel();
EmitBranchIfDefaultValue(ctx, needToPop);
Tail.EmitWrite(ctx, null);
ctx.Branch(done, true);
ctx.MarkLabel(needToPop);
ctx.DiscardValue();
}
else
{
ctx.LoadValue(valueFrom); // variable/parameter
EmitBranchIfDefaultValue(ctx, done);
Tail.EmitWrite(ctx, valueFrom);
}
ctx.MarkLabel(done);
}
private void EmitBeq(Compiler.CompilerContext ctx, Compiler.CodeLabel label, Type type)
{
switch (Helpers.GetTypeCode(type))
{
case ProtoTypeCode.Boolean:
case ProtoTypeCode.Byte:
case ProtoTypeCode.Char:
case ProtoTypeCode.Double:
case ProtoTypeCode.Int16:
case ProtoTypeCode.Int32:
case ProtoTypeCode.Int64:
case ProtoTypeCode.SByte:
case ProtoTypeCode.Single:
case ProtoTypeCode.UInt16:
case ProtoTypeCode.UInt32:
case ProtoTypeCode.UInt64:
ctx.BranchIfEqual(label, false);
break;
default:
#if COREFX
MethodInfo method = type.GetMethod("op_Equality", new Type[] { type, type });
if (method == null || !method.IsPublic || !method.IsStatic) method = null;
#else
MethodInfo method = type.GetMethod("op_Equality", BindingFlags.Public | BindingFlags.Static,
null, new Type[] { type, type }, null);
#endif
if (method == null || method.ReturnType != ctx.MapType(typeof(bool)))
{
throw new InvalidOperationException("No suitable equality operator found for default-values of type: " + type.FullName);
}
ctx.EmitCall(method);
ctx.BranchIfTrue(label, false);
break;
}
}
private void EmitBranchIfDefaultValue(Compiler.CompilerContext ctx, Compiler.CodeLabel label)
{
Type expected = ExpectedType;
switch (Helpers.GetTypeCode(expected))
{
case ProtoTypeCode.Boolean:
if ((bool)defaultValue)
{
ctx.BranchIfTrue(label, false);
}
else
{
ctx.BranchIfFalse(label, false);
}
break;
case ProtoTypeCode.Byte:
if ((byte)defaultValue == (byte)0)
{
ctx.BranchIfFalse(label, false);
}
else
{
ctx.LoadValue((int)(byte)defaultValue);
EmitBeq(ctx, label, expected);
}
break;
case ProtoTypeCode.SByte:
if ((sbyte)defaultValue == (sbyte)0)
{
ctx.BranchIfFalse(label, false);
}
else
{
ctx.LoadValue((int)(sbyte)defaultValue);
EmitBeq(ctx, label, expected);
}
break;
case ProtoTypeCode.Int16:
if ((short)defaultValue == (short)0)
{
ctx.BranchIfFalse(label, false);
}
else
{
ctx.LoadValue((int)(short)defaultValue);
EmitBeq(ctx, label, expected);
}
break;
case ProtoTypeCode.UInt16:
if ((ushort)defaultValue == (ushort)0)
{
ctx.BranchIfFalse(label, false);
}
else
{
ctx.LoadValue((int)(ushort)defaultValue);
EmitBeq(ctx, label, expected);
}
break;
case ProtoTypeCode.Int32:
if ((int)defaultValue == (int)0)
{
ctx.BranchIfFalse(label, false);
}
else
{
ctx.LoadValue((int)defaultValue);
EmitBeq(ctx, label, expected);
}
break;
case ProtoTypeCode.UInt32:
if ((uint)defaultValue == (uint)0)
{
ctx.BranchIfFalse(label, false);
}
else
{
ctx.LoadValue((int)(uint)defaultValue);
EmitBeq(ctx, label, expected);
}
break;
case ProtoTypeCode.Char:
if ((char)defaultValue == (char)0)
{
ctx.BranchIfFalse(label, false);
}
else
{
ctx.LoadValue((int)(char)defaultValue);
EmitBeq(ctx, label, expected);
}
break;
case ProtoTypeCode.Int64:
ctx.LoadValue((long)defaultValue);
EmitBeq(ctx, label, expected);
break;
case ProtoTypeCode.UInt64:
ctx.LoadValue((long)(ulong)defaultValue);
EmitBeq(ctx, label, expected);
break;
case ProtoTypeCode.Double:
ctx.LoadValue((double)defaultValue);
EmitBeq(ctx, label, expected);
break;
case ProtoTypeCode.Single:
ctx.LoadValue((float)defaultValue);
EmitBeq(ctx, label, expected);
break;
case ProtoTypeCode.String:
ctx.LoadValue((string)defaultValue);
EmitBeq(ctx, label, expected);
break;
case ProtoTypeCode.Decimal:
{
decimal d = (decimal)defaultValue;
ctx.LoadValue(d);
EmitBeq(ctx, label, expected);
}
break;
case ProtoTypeCode.TimeSpan:
{
TimeSpan ts = (TimeSpan)defaultValue;
if (ts == TimeSpan.Zero)
{
ctx.LoadValue(typeof(TimeSpan).GetField("Zero"));
}
else
{
ctx.LoadValue(ts.Ticks);
ctx.EmitCall(ctx.MapType(typeof(TimeSpan)).GetMethod("FromTicks"));
}
EmitBeq(ctx, label, expected);
break;
}
case ProtoTypeCode.Guid:
{
ctx.LoadValue((Guid)defaultValue);
EmitBeq(ctx, label, expected);
break;
}
case ProtoTypeCode.DateTime:
{
ctx.LoadValue(((DateTime)defaultValue).ToBinary());
ctx.EmitCall(ctx.MapType(typeof(DateTime)).GetMethod("FromBinary"));
EmitBeq(ctx, label, expected);
break;
}
default:
throw new NotSupportedException("Type cannot be represented as a default value: " + expected.FullName);
}
}
protected override void EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
Tail.EmitRead(ctx, valueFrom);
}
#endif
}
}
#endif