[+] TEngineServer

[+] TEngineServer
This commit is contained in:
ALEXTANG
2023-07-13 17:17:26 +08:00
parent a69f53592e
commit 0c8f3a5f92
790 changed files with 52737 additions and 2533 deletions

View File

@@ -0,0 +1,310 @@
#if !NO_RUNTIME
using System;
using System.Collections;
using System.Reflection;
using ProtoBuf.Meta;
namespace ProtoBuf.Serializers
{
sealed class ArrayDecorator : ProtoDecoratorBase
{
private readonly int fieldNumber;
private const byte
OPTIONS_WritePacked = 1,
OPTIONS_OverwriteList = 2,
OPTIONS_SupportNull = 4;
private readonly byte options;
private readonly WireType packedWireType;
public ArrayDecorator(TypeModel model, IProtoSerializer tail, int fieldNumber, bool writePacked, WireType packedWireType, Type arrayType, bool overwriteList, bool supportNull)
: base(tail)
{
Helpers.DebugAssert(arrayType != null, "arrayType should be non-null");
Helpers.DebugAssert(arrayType.IsArray && arrayType.GetArrayRank() == 1, "should be single-dimension array; " + arrayType.FullName);
this.itemType = arrayType.GetElementType();
Type underlyingItemType = supportNull ? itemType : (Helpers.GetUnderlyingType(itemType) ?? itemType);
Helpers.DebugAssert(underlyingItemType == Tail.ExpectedType
|| (Tail.ExpectedType == model.MapType(typeof(object)) && !Helpers.IsValueType(underlyingItemType)), "invalid tail");
Helpers.DebugAssert(Tail.ExpectedType != model.MapType(typeof(byte)), "Should have used BlobSerializer");
if ((writePacked || packedWireType != WireType.None) && fieldNumber <= 0) throw new ArgumentOutOfRangeException("fieldNumber");
if (!ListDecorator.CanPack(packedWireType))
{
if (writePacked) throw new InvalidOperationException("Only simple data-types can use packed encoding");
packedWireType = WireType.None;
}
this.fieldNumber = fieldNumber;
this.packedWireType = packedWireType;
if (writePacked) options |= OPTIONS_WritePacked;
if (overwriteList) options |= OPTIONS_OverwriteList;
if (supportNull) options |= OPTIONS_SupportNull;
this.arrayType = arrayType;
}
readonly Type arrayType, itemType; // this is, for example, typeof(int[])
public override Type ExpectedType { get { return arrayType; } }
public override bool RequiresOldValue { get { return AppendToCollection; } }
public override bool ReturnsValue { get { return true; } }
private bool CanUsePackedPrefix() => CanUsePackedPrefix(packedWireType, itemType);
internal static bool CanUsePackedPrefix(WireType packedWireType, Type itemType)
{
// needs to be a suitably simple type *and* be definitely not nullable
switch (packedWireType)
{
case WireType.Fixed32:
case WireType.Fixed64:
break;
default:
return false; // nope
}
if (!Helpers.IsValueType(itemType)) return false;
return Helpers.GetUnderlyingType(itemType) == null;
}
#if FEAT_COMPILER
protected override void EmitWrite(ProtoBuf.Compiler.CompilerContext ctx, ProtoBuf.Compiler.Local valueFrom)
{
// int i and T[] arr
using (Compiler.Local arr = ctx.GetLocalWithValue(arrayType, valueFrom))
using (Compiler.Local i = new ProtoBuf.Compiler.Local(ctx, ctx.MapType(typeof(int))))
{
bool writePacked = (options & OPTIONS_WritePacked) != 0;
bool fixedLengthPacked = writePacked && CanUsePackedPrefix();
using (Compiler.Local token = (writePacked && !fixedLengthPacked) ? new Compiler.Local(ctx, ctx.MapType(typeof(SubItemToken))) : null)
{
Type mappedWriter = ctx.MapType(typeof(ProtoWriter));
if (writePacked)
{
ctx.LoadValue(fieldNumber);
ctx.LoadValue((int)WireType.String);
ctx.LoadReaderWriter();
ctx.EmitCall(mappedWriter.GetMethod("WriteFieldHeader"));
if (fixedLengthPacked)
{
// write directly - no need for buffering
ctx.LoadLength(arr, false);
ctx.LoadValue((int)packedWireType);
ctx.LoadReaderWriter();
ctx.EmitCall(mappedWriter.GetMethod("WritePackedPrefix"));
}
else
{
ctx.LoadValue(arr);
ctx.LoadReaderWriter();
ctx.EmitCall(mappedWriter.GetMethod("StartSubItem"));
ctx.StoreValue(token);
}
ctx.LoadValue(fieldNumber);
ctx.LoadReaderWriter();
ctx.EmitCall(mappedWriter.GetMethod("SetPackedField"));
}
EmitWriteArrayLoop(ctx, i, arr);
if (writePacked)
{
if (fixedLengthPacked)
{
ctx.LoadValue(fieldNumber);
ctx.LoadReaderWriter();
ctx.EmitCall(mappedWriter.GetMethod("ClearPackedField"));
}
else
{
ctx.LoadValue(token);
ctx.LoadReaderWriter();
ctx.EmitCall(mappedWriter.GetMethod("EndSubItem"));
}
}
}
}
}
private void EmitWriteArrayLoop(Compiler.CompilerContext ctx, Compiler.Local i, Compiler.Local arr)
{
// i = 0
ctx.LoadValue(0);
ctx.StoreValue(i);
// range test is last (to minimise branches)
Compiler.CodeLabel loopTest = ctx.DefineLabel(), processItem = ctx.DefineLabel();
ctx.Branch(loopTest, false);
ctx.MarkLabel(processItem);
// {...}
ctx.LoadArrayValue(arr, i);
if (SupportNull)
{
Tail.EmitWrite(ctx, null);
}
else
{
ctx.WriteNullCheckedTail(itemType, Tail, null);
}
// i++
ctx.LoadValue(i);
ctx.LoadValue(1);
ctx.Add();
ctx.StoreValue(i);
// i < arr.Length
ctx.MarkLabel(loopTest);
ctx.LoadValue(i);
ctx.LoadLength(arr, false);
ctx.BranchIfLess(processItem, false);
}
#endif
private bool AppendToCollection => (options & OPTIONS_OverwriteList) == 0;
private bool SupportNull { get { return (options & OPTIONS_SupportNull) != 0; } }
public override void Write(object value, ProtoWriter dest)
{
IList arr = (IList)value;
int len = arr.Count;
SubItemToken token;
bool writePacked = (options & OPTIONS_WritePacked) != 0;
bool fixedLengthPacked = writePacked && CanUsePackedPrefix();
if (writePacked)
{
ProtoWriter.WriteFieldHeader(fieldNumber, WireType.String, dest);
if (fixedLengthPacked)
{
ProtoWriter.WritePackedPrefix(arr.Count, packedWireType, dest);
token = new SubItemToken(); // default
}
else
{
token = ProtoWriter.StartSubItem(value, dest);
}
ProtoWriter.SetPackedField(fieldNumber, dest);
}
else
{
token = new SubItemToken(); // default
}
bool checkForNull = !SupportNull;
for (int i = 0; i < len; i++)
{
object obj = arr[i];
if (checkForNull && obj == null) { throw new NullReferenceException(); }
Tail.Write(obj, dest);
}
if (writePacked)
{
if (fixedLengthPacked)
{
ProtoWriter.ClearPackedField(fieldNumber, dest);
}
else
{
ProtoWriter.EndSubItem(token, dest);
}
}
}
public override object Read(object value, ProtoReader source)
{
int field = source.FieldNumber;
BasicList list = new BasicList();
if (packedWireType != WireType.None && source.WireType == WireType.String)
{
SubItemToken token = ProtoReader.StartSubItem(source);
while (ProtoReader.HasSubValue(packedWireType, source))
{
list.Add(Tail.Read(null, source));
}
ProtoReader.EndSubItem(token, source);
}
else
{
do
{
list.Add(Tail.Read(null, source));
} while (source.TryReadFieldHeader(field));
}
int oldLen = AppendToCollection ? ((value == null ? 0 : ((Array)value).Length)) : 0;
Array result = Array.CreateInstance(itemType, oldLen + list.Count);
if (oldLen != 0) ((Array)value).CopyTo(result, 0);
list.CopyTo(result, oldLen);
return result;
}
#if FEAT_COMPILER
protected override void EmitRead(ProtoBuf.Compiler.CompilerContext ctx, ProtoBuf.Compiler.Local valueFrom)
{
Type listType;
listType = ctx.MapType(typeof(System.Collections.Generic.List<>)).MakeGenericType(itemType);
Type expected = ExpectedType;
using (Compiler.Local oldArr = AppendToCollection ? ctx.GetLocalWithValue(expected, valueFrom) : null)
using (Compiler.Local newArr = new Compiler.Local(ctx, expected))
using (Compiler.Local list = new Compiler.Local(ctx, listType))
{
ctx.EmitCtor(listType);
ctx.StoreValue(list);
ListDecorator.EmitReadList(ctx, list, Tail, listType.GetMethod("Add"), packedWireType, false);
// leave this "using" here, as it can share the "FieldNumber" local with EmitReadList
using (Compiler.Local oldLen = AppendToCollection ? new ProtoBuf.Compiler.Local(ctx, ctx.MapType(typeof(int))) : null)
{
Type[] copyToArrayInt32Args = new Type[] { ctx.MapType(typeof(Array)), ctx.MapType(typeof(int)) };
if (AppendToCollection)
{
ctx.LoadLength(oldArr, true);
ctx.CopyValue();
ctx.StoreValue(oldLen);
ctx.LoadAddress(list, listType);
ctx.LoadValue(listType.GetProperty("Count"));
ctx.Add();
ctx.CreateArray(itemType, null); // length is on the stack
ctx.StoreValue(newArr);
ctx.LoadValue(oldLen);
Compiler.CodeLabel nothingToCopy = ctx.DefineLabel();
ctx.BranchIfFalse(nothingToCopy, true);
ctx.LoadValue(oldArr);
ctx.LoadValue(newArr);
ctx.LoadValue(0); // index in target
ctx.EmitCall(expected.GetMethod("CopyTo", copyToArrayInt32Args));
ctx.MarkLabel(nothingToCopy);
ctx.LoadValue(list);
ctx.LoadValue(newArr);
ctx.LoadValue(oldLen);
}
else
{
ctx.LoadAddress(list, listType);
ctx.LoadValue(listType.GetProperty("Count"));
ctx.CreateArray(itemType, null);
ctx.StoreValue(newArr);
ctx.LoadAddress(list, listType);
ctx.LoadValue(newArr);
ctx.LoadValue(0);
}
copyToArrayInt32Args[0] = expected; // // prefer: CopyTo(T[], int)
MethodInfo copyTo = listType.GetMethod("CopyTo", copyToArrayInt32Args);
if (copyTo == null)
{ // fallback: CopyTo(Array, int)
copyToArrayInt32Args[1] = ctx.MapType(typeof(Array));
copyTo = listType.GetMethod("CopyTo", copyToArrayInt32Args);
}
ctx.EmitCall(copyTo);
}
ctx.LoadValue(newArr);
}
}
#endif
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3689dde3ac5fd544a9e66158c9713872
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,59 @@
#if !NO_RUNTIME
using System;
#if COREFX
using System.Reflection;
#endif
#if FEAT_COMPILER
using System.Reflection.Emit;
#endif
namespace ProtoBuf.Serializers
{
sealed class BlobSerializer : IProtoSerializer
{
public Type ExpectedType { get { return expectedType; } }
static readonly Type expectedType = typeof(byte[]);
public BlobSerializer(ProtoBuf.Meta.TypeModel model, bool overwriteList)
{
this.overwriteList = overwriteList;
}
private readonly bool overwriteList;
public object Read(object value, ProtoReader source)
{
return ProtoReader.AppendBytes(overwriteList ? null : (byte[])value, source);
}
public void Write(object value, ProtoWriter dest)
{
ProtoWriter.WriteBytes((byte[])value, dest);
}
bool IProtoSerializer.RequiresOldValue { get { return !overwriteList; } }
bool IProtoSerializer.ReturnsValue { get { return true; } }
#if FEAT_COMPILER
void IProtoSerializer.EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
ctx.EmitBasicWrite("WriteBytes", valueFrom);
}
void IProtoSerializer.EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
if (overwriteList)
{
ctx.LoadNullRef();
}
else
{
ctx.LoadValue(valueFrom);
}
ctx.LoadReaderWriter();
ctx.EmitCall(ctx.MapType(typeof(ProtoReader))
.GetMethod("AppendBytes"));
}
#endif
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c04427a4647d6314e82d8a63882dcb8b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,41 @@
#if !NO_RUNTIME
using System;
namespace ProtoBuf.Serializers
{
sealed class BooleanSerializer : IProtoSerializer
{
static readonly Type expectedType = typeof(bool);
public BooleanSerializer(ProtoBuf.Meta.TypeModel model) { }
public Type ExpectedType => expectedType;
public void Write(object value, ProtoWriter dest)
{
ProtoWriter.WriteBoolean((bool)value, dest);
}
public object Read(object value, ProtoReader source)
{
Helpers.DebugAssert(value == null); // since replaces
return source.ReadBoolean();
}
bool IProtoSerializer.RequiresOldValue => false;
bool IProtoSerializer.ReturnsValue => true;
#if FEAT_COMPILER
void IProtoSerializer.EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
ctx.EmitBasicWrite("WriteBoolean", valueFrom);
}
void IProtoSerializer.EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
ctx.EmitBasicRead("ReadBoolean", ExpectedType);
}
#endif
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8b73f749f97802947812dc66867ed1f5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,42 @@
#if !NO_RUNTIME
using System;
namespace ProtoBuf.Serializers
{
sealed class ByteSerializer : IProtoSerializer
{
public Type ExpectedType { get { return expectedType; } }
static readonly Type expectedType = typeof(byte);
public ByteSerializer(ProtoBuf.Meta.TypeModel model) { }
bool IProtoSerializer.RequiresOldValue => false;
bool IProtoSerializer.ReturnsValue => true;
public void Write(object value, ProtoWriter dest)
{
ProtoWriter.WriteByte((byte)value, dest);
}
public object Read(object value, ProtoReader source)
{
Helpers.DebugAssert(value == null); // since replaces
return source.ReadByte();
}
#if FEAT_COMPILER
void IProtoSerializer.EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
ctx.EmitBasicWrite("WriteByte", valueFrom);
}
void IProtoSerializer.EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
ctx.EmitBasicRead("ReadByte", ExpectedType);
}
#endif
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c17779da4eb6b1d489531294afcb2a32
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,32 @@
#if !NO_RUNTIME
using System;
namespace ProtoBuf.Serializers
{
sealed class CharSerializer : UInt16Serializer
{
static readonly Type expectedType = typeof(char);
public CharSerializer(ProtoBuf.Meta.TypeModel model) : base(model)
{
}
public override Type ExpectedType => expectedType;
public override void Write(object value, ProtoWriter dest)
{
ProtoWriter.WriteUInt16((ushort)(char)value, dest);
}
public override object Read(object value, ProtoReader source)
{
Helpers.DebugAssert(value == null); // since replaces
return (char)source.ReadUInt16();
}
// no need for any special IL here; ushort and char are
// interchangeable as long as there is no boxing/unboxing
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 526090cb730f087469b7f20948f4932a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,88 @@
#if FEAT_COMPILER
using System;
using ProtoBuf.Meta;
namespace ProtoBuf.Serializers
{
sealed class CompiledSerializer : IProtoTypeSerializer
{
bool IProtoTypeSerializer.HasCallbacks(TypeModel.CallbackType callbackType)
{
return head.HasCallbacks(callbackType); // these routes only used when bits of the model not compiled
}
bool IProtoTypeSerializer.CanCreateInstance()
{
return head.CanCreateInstance();
}
object IProtoTypeSerializer.CreateInstance(ProtoReader source)
{
return head.CreateInstance(source);
}
public void Callback(object value, TypeModel.CallbackType callbackType, SerializationContext context)
{
head.Callback(value, callbackType, context); // these routes only used when bits of the model not compiled
}
public static CompiledSerializer Wrap(IProtoTypeSerializer head, TypeModel model)
{
CompiledSerializer result = head as CompiledSerializer;
if (result == null)
{
result = new CompiledSerializer(head, model);
Helpers.DebugAssert(((IProtoTypeSerializer)result).ExpectedType == head.ExpectedType);
}
return result;
}
private readonly IProtoTypeSerializer head;
private readonly Compiler.ProtoSerializer serializer;
private readonly Compiler.ProtoDeserializer deserializer;
private CompiledSerializer(IProtoTypeSerializer head, TypeModel model)
{
this.head = head;
serializer = Compiler.CompilerContext.BuildSerializer(head, model);
deserializer = Compiler.CompilerContext.BuildDeserializer(head, model);
}
bool IProtoSerializer.RequiresOldValue => head.RequiresOldValue;
bool IProtoSerializer.ReturnsValue => head.ReturnsValue;
Type IProtoSerializer.ExpectedType => head.ExpectedType;
void IProtoSerializer.Write(object value, ProtoWriter dest)
{
serializer(value, dest);
}
object IProtoSerializer.Read(object value, ProtoReader source)
{
return deserializer(value, source);
}
void IProtoSerializer.EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
head.EmitWrite(ctx, valueFrom);
}
void IProtoSerializer.EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
head.EmitRead(ctx, valueFrom);
}
void IProtoTypeSerializer.EmitCallback(Compiler.CompilerContext ctx, Compiler.Local valueFrom, TypeModel.CallbackType callbackType)
{
head.EmitCallback(ctx, valueFrom, callbackType);
}
void IProtoTypeSerializer.EmitCreateInstance(Compiler.CompilerContext ctx)
{
head.EmitCreateInstance(ctx);
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 90821da5568834a4682d1a42d7f66963
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,65 @@
#if !NO_RUNTIME
using System;
using System.Reflection;
namespace ProtoBuf.Serializers
{
internal sealed class DateTimeSerializer : IProtoSerializer
{
private static readonly Type expectedType = typeof(DateTime);
public Type ExpectedType => expectedType;
bool IProtoSerializer.RequiresOldValue => false;
bool IProtoSerializer.ReturnsValue => true;
private readonly bool includeKind, wellKnown;
public DateTimeSerializer(DataFormat dataFormat, ProtoBuf.Meta.TypeModel model)
{
wellKnown = dataFormat == DataFormat.WellKnown;
includeKind = model?.SerializeDateTimeKind() == true;
}
public object Read(object value, ProtoReader source)
{
if (wellKnown)
{
return BclHelpers.ReadTimestamp(source);
}
else
{
Helpers.DebugAssert(value == null); // since replaces
return BclHelpers.ReadDateTime(source);
}
}
public void Write(object value, ProtoWriter dest)
{
if (wellKnown)
BclHelpers.WriteTimestamp((DateTime)value, dest);
else if (includeKind)
BclHelpers.WriteDateTimeWithKind((DateTime)value, dest);
else
BclHelpers.WriteDateTime((DateTime)value, dest);
}
#if FEAT_COMPILER
void IProtoSerializer.EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
ctx.EmitWrite(ctx.MapType(typeof(BclHelpers)),
wellKnown ? nameof(BclHelpers.WriteTimestamp)
: includeKind ? nameof(BclHelpers.WriteDateTimeWithKind) : nameof(BclHelpers.WriteDateTime), valueFrom);
}
void IProtoSerializer.EmitRead(Compiler.CompilerContext ctx, Compiler.Local entity)
{
if (wellKnown) ctx.LoadValue(entity);
ctx.EmitBasicRead(ctx.MapType(typeof(BclHelpers)),
wellKnown ? nameof(BclHelpers.ReadTimestamp) : nameof(BclHelpers.ReadDateTime),
ExpectedType);
}
#endif
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: dfba0a8c252b2e54c96478c9e690c7d3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,42 @@
#if !NO_RUNTIME
using System;
namespace ProtoBuf.Serializers
{
sealed class DecimalSerializer : IProtoSerializer
{
static readonly Type expectedType = typeof(decimal);
public DecimalSerializer(ProtoBuf.Meta.TypeModel model) { }
public Type ExpectedType => expectedType;
bool IProtoSerializer.RequiresOldValue => false;
bool IProtoSerializer.ReturnsValue => true;
public object Read(object value, ProtoReader source)
{
Helpers.DebugAssert(value == null); // since replaces
return BclHelpers.ReadDecimal(source);
}
public void Write(object value, ProtoWriter dest)
{
BclHelpers.WriteDecimal((decimal)value, dest);
}
#if FEAT_COMPILER
void IProtoSerializer.EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
ctx.EmitWrite(ctx.MapType(typeof(BclHelpers)), "WriteDecimal", valueFrom);
}
void IProtoSerializer.EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
ctx.EmitBasicRead(ctx.MapType(typeof(BclHelpers)), "ReadDecimal", ExpectedType);
}
#endif
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 80efe6cca6916ab46b430c27dc58369c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,259 @@
#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

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ad3a3e386e17b67488f858d409d3e8a7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,42 @@
#if !NO_RUNTIME
using System;
namespace ProtoBuf.Serializers
{
sealed class DoubleSerializer : IProtoSerializer
{
static readonly Type expectedType = typeof(double);
public DoubleSerializer(ProtoBuf.Meta.TypeModel model) { }
public Type ExpectedType => expectedType;
bool IProtoSerializer.RequiresOldValue => false;
bool IProtoSerializer.ReturnsValue => true;
public object Read(object value, ProtoReader source)
{
Helpers.DebugAssert(value == null); // since replaces
return source.ReadDouble();
}
public void Write(object value, ProtoWriter dest)
{
ProtoWriter.WriteDouble((double)value, dest);
}
#if FEAT_COMPILER
void IProtoSerializer.EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
ctx.EmitBasicWrite("WriteDouble", valueFrom);
}
void IProtoSerializer.EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
ctx.EmitBasicRead("ReadDouble", ExpectedType);
}
#endif
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 65b598b3ebee04946abf8957a0f92762
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,267 @@
#if !NO_RUNTIME
using System;
using ProtoBuf.Meta;
using System.Reflection;
namespace ProtoBuf.Serializers
{
sealed class EnumSerializer : IProtoSerializer
{
public readonly struct EnumPair
{
public readonly object RawValue; // note that this is boxing, but I'll live with it
public readonly Enum TypedValue; // note that this is boxing, but I'll live with it
public readonly int WireValue;
public EnumPair(int wireValue, object raw, Type type)
{
WireValue = wireValue;
RawValue = raw;
TypedValue = (Enum)Enum.ToObject(type, raw);
}
}
private readonly Type enumType;
private readonly EnumPair[] map;
public EnumSerializer(Type enumType, EnumPair[] map)
{
this.enumType = enumType ?? throw new ArgumentNullException(nameof(enumType));
this.map = map;
if (map != null)
{
for (int i = 1; i < map.Length; i++)
for (int j = 0; j < i; j++)
{
if (map[i].WireValue == map[j].WireValue && !Equals(map[i].RawValue, map[j].RawValue))
{
throw new ProtoException("Multiple enums with wire-value " + map[i].WireValue.ToString());
}
if (Equals(map[i].RawValue, map[j].RawValue) && map[i].WireValue != map[j].WireValue)
{
throw new ProtoException("Multiple enums with deserialized-value " + map[i].RawValue);
}
}
}
}
private ProtoTypeCode GetTypeCode()
{
Type type = Helpers.GetUnderlyingType(enumType);
if (type == null) type = enumType;
return Helpers.GetTypeCode(type);
}
public Type ExpectedType => enumType;
bool IProtoSerializer.RequiresOldValue => false;
bool IProtoSerializer.ReturnsValue => true;
private int EnumToWire(object value)
{
unchecked
{
switch (GetTypeCode())
{ // unbox then convert to int
case ProtoTypeCode.Byte: return (int)(byte)value;
case ProtoTypeCode.SByte: return (int)(sbyte)value;
case ProtoTypeCode.Int16: return (int)(short)value;
case ProtoTypeCode.Int32: return (int)value;
case ProtoTypeCode.Int64: return (int)(long)value;
case ProtoTypeCode.UInt16: return (int)(ushort)value;
case ProtoTypeCode.UInt32: return (int)(uint)value;
case ProtoTypeCode.UInt64: return (int)(ulong)value;
default: throw new InvalidOperationException();
}
}
}
private object WireToEnum(int value)
{
unchecked
{
switch (GetTypeCode())
{ // convert from int then box
case ProtoTypeCode.Byte: return Enum.ToObject(enumType, (byte)value);
case ProtoTypeCode.SByte: return Enum.ToObject(enumType, (sbyte)value);
case ProtoTypeCode.Int16: return Enum.ToObject(enumType, (short)value);
case ProtoTypeCode.Int32: return Enum.ToObject(enumType, value);
case ProtoTypeCode.Int64: return Enum.ToObject(enumType, (long)value);
case ProtoTypeCode.UInt16: return Enum.ToObject(enumType, (ushort)value);
case ProtoTypeCode.UInt32: return Enum.ToObject(enumType, (uint)value);
case ProtoTypeCode.UInt64: return Enum.ToObject(enumType, (ulong)value);
default: throw new InvalidOperationException();
}
}
}
public object Read(object value, ProtoReader source)
{
Helpers.DebugAssert(value == null); // since replaces
int wireValue = source.ReadInt32();
if (map == null)
{
return WireToEnum(wireValue);
}
for (int i = 0; i < map.Length; i++)
{
if (map[i].WireValue == wireValue)
{
return map[i].TypedValue;
}
}
source.ThrowEnumException(ExpectedType, wireValue);
return null; // to make compiler happy
}
public void Write(object value, ProtoWriter dest)
{
if (map == null)
{
ProtoWriter.WriteInt32(EnumToWire(value), dest);
}
else
{
for (int i = 0; i < map.Length; i++)
{
if (object.Equals(map[i].TypedValue, value))
{
ProtoWriter.WriteInt32(map[i].WireValue, dest);
return;
}
}
ProtoWriter.ThrowEnumException(dest, value);
}
}
#if FEAT_COMPILER
void IProtoSerializer.EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
ProtoTypeCode typeCode = GetTypeCode();
if (map == null)
{
ctx.LoadValue(valueFrom);
ctx.ConvertToInt32(typeCode, false);
ctx.EmitBasicWrite("WriteInt32", null);
}
else
{
using (Compiler.Local loc = ctx.GetLocalWithValue(ExpectedType, valueFrom))
{
Compiler.CodeLabel @continue = ctx.DefineLabel();
for (int i = 0; i < map.Length; i++)
{
Compiler.CodeLabel tryNextValue = ctx.DefineLabel(), processThisValue = ctx.DefineLabel();
ctx.LoadValue(loc);
WriteEnumValue(ctx, typeCode, map[i].RawValue);
ctx.BranchIfEqual(processThisValue, true);
ctx.Branch(tryNextValue, true);
ctx.MarkLabel(processThisValue);
ctx.LoadValue(map[i].WireValue);
ctx.EmitBasicWrite("WriteInt32", null);
ctx.Branch(@continue, false);
ctx.MarkLabel(tryNextValue);
}
ctx.LoadReaderWriter();
ctx.LoadValue(loc);
ctx.CastToObject(ExpectedType);
ctx.EmitCall(ctx.MapType(typeof(ProtoWriter)).GetMethod("ThrowEnumException"));
ctx.MarkLabel(@continue);
}
}
}
void IProtoSerializer.EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
ProtoTypeCode typeCode = GetTypeCode();
if (map == null)
{
ctx.EmitBasicRead("ReadInt32", ctx.MapType(typeof(int)));
ctx.ConvertFromInt32(typeCode, false);
}
else
{
int[] wireValues = new int[map.Length];
object[] values = new object[map.Length];
for (int i = 0; i < map.Length; i++)
{
wireValues[i] = map[i].WireValue;
values[i] = map[i].RawValue;
}
using (Compiler.Local result = new Compiler.Local(ctx, ExpectedType))
using (Compiler.Local wireValue = new Compiler.Local(ctx, ctx.MapType(typeof(int))))
{
ctx.EmitBasicRead("ReadInt32", ctx.MapType(typeof(int)));
ctx.StoreValue(wireValue);
Compiler.CodeLabel @continue = ctx.DefineLabel();
foreach (BasicList.Group group in BasicList.GetContiguousGroups(wireValues, values))
{
Compiler.CodeLabel tryNextGroup = ctx.DefineLabel();
int groupItemCount = group.Items.Count;
if (groupItemCount == 1)
{
// discreet group; use an equality test
ctx.LoadValue(wireValue);
ctx.LoadValue(group.First);
Compiler.CodeLabel processThisValue = ctx.DefineLabel();
ctx.BranchIfEqual(processThisValue, true);
ctx.Branch(tryNextGroup, false);
WriteEnumValue(ctx, typeCode, processThisValue, @continue, group.Items[0], @result);
}
else
{
// implement as a jump-table-based switch
ctx.LoadValue(wireValue);
ctx.LoadValue(group.First);
ctx.Subtract(); // jump-tables are zero-based
Compiler.CodeLabel[] jmp = new Compiler.CodeLabel[groupItemCount];
for (int i = 0; i < groupItemCount; i++)
{
jmp[i] = ctx.DefineLabel();
}
ctx.Switch(jmp);
// write the default...
ctx.Branch(tryNextGroup, false);
for (int i = 0; i < groupItemCount; i++)
{
WriteEnumValue(ctx, typeCode, jmp[i], @continue, group.Items[i], @result);
}
}
ctx.MarkLabel(tryNextGroup);
}
// throw source.CreateEnumException(ExpectedType, wireValue);
ctx.LoadReaderWriter();
ctx.LoadValue(ExpectedType);
ctx.LoadValue(wireValue);
ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("ThrowEnumException"));
ctx.MarkLabel(@continue);
ctx.LoadValue(result);
}
}
}
private static void WriteEnumValue(Compiler.CompilerContext ctx, ProtoTypeCode typeCode, object value)
{
switch (typeCode)
{
case ProtoTypeCode.Byte: ctx.LoadValue((int)(byte)value); break;
case ProtoTypeCode.SByte: ctx.LoadValue((int)(sbyte)value); break;
case ProtoTypeCode.Int16: ctx.LoadValue((int)(short)value); break;
case ProtoTypeCode.Int32: ctx.LoadValue((int)(int)value); break;
case ProtoTypeCode.Int64: ctx.LoadValue((long)(long)value); break;
case ProtoTypeCode.UInt16: ctx.LoadValue((int)(ushort)value); break;
case ProtoTypeCode.UInt32: ctx.LoadValue((int)(uint)value); break;
case ProtoTypeCode.UInt64: ctx.LoadValue((long)(ulong)value); break;
default: throw new InvalidOperationException();
}
}
private static void WriteEnumValue(Compiler.CompilerContext ctx, ProtoTypeCode typeCode, Compiler.CodeLabel handler, Compiler.CodeLabel @continue, object value, Compiler.Local local)
{
ctx.MarkLabel(handler);
WriteEnumValue(ctx, typeCode, value);
ctx.StoreValue(local);
ctx.Branch(@continue, false); // "continue"
}
#endif
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ef6c6d630a8f5ca449eec10513147563
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,104 @@
#if !NO_RUNTIME
using System;
using System.Reflection;
namespace ProtoBuf.Serializers
{
sealed class FieldDecorator : ProtoDecoratorBase
{
public override Type ExpectedType => forType;
private readonly FieldInfo field;
private readonly Type forType;
public override bool RequiresOldValue => true;
public override bool ReturnsValue => false;
public FieldDecorator(Type forType, FieldInfo field, IProtoSerializer tail) : base(tail)
{
Helpers.DebugAssert(forType != null);
Helpers.DebugAssert(field != null);
this.forType = forType;
this.field = field;
}
public override void Write(object value, ProtoWriter dest)
{
Helpers.DebugAssert(value != null);
value = field.GetValue(value);
if (value != null) Tail.Write(value, dest);
}
public override object Read(object value, ProtoReader source)
{
Helpers.DebugAssert(value != null);
object newValue = Tail.Read((Tail.RequiresOldValue ? field.GetValue(value) : null), source);
if (newValue != null) field.SetValue(value, newValue);
return null;
}
#if FEAT_COMPILER
protected override void EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
ctx.LoadAddress(valueFrom, ExpectedType);
ctx.LoadValue(field);
ctx.WriteNullCheckedTail(field.FieldType, Tail, null);
}
protected override void EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
using (Compiler.Local loc = ctx.GetLocalWithValue(ExpectedType, valueFrom))
{
if (Tail.RequiresOldValue)
{
ctx.LoadAddress(loc, ExpectedType);
ctx.LoadValue(field);
}
// value is either now on the stack or not needed
ctx.ReadNullCheckedTail(field.FieldType, Tail, null);
// the field could be a backing field that needs to be raised back to
// the property if we're doing a full compile
MemberInfo member = field;
ctx.CheckAccessibility(ref member);
bool writeValue = member is FieldInfo;
if (writeValue)
{
if (Tail.ReturnsValue)
{
using (Compiler.Local newVal = new Compiler.Local(ctx, field.FieldType))
{
ctx.StoreValue(newVal);
if (Helpers.IsValueType(field.FieldType))
{
ctx.LoadAddress(loc, ExpectedType);
ctx.LoadValue(newVal);
ctx.StoreValue(field);
}
else
{
Compiler.CodeLabel allDone = ctx.DefineLabel();
ctx.LoadValue(newVal);
ctx.BranchIfFalse(allDone, true); // interpret null as "don't assign"
ctx.LoadAddress(loc, ExpectedType);
ctx.LoadValue(newVal);
ctx.StoreValue(field);
ctx.MarkLabel(allDone);
}
}
}
}
else
{
// can't use result
if (Tail.ReturnsValue)
{
ctx.DiscardValue();
}
}
}
}
#endif
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f7c1c3141cd2fad47b3112747b44314a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,43 @@
#if !NO_RUNTIME
using System;
namespace ProtoBuf.Serializers
{
sealed class GuidSerializer : IProtoSerializer
{
static readonly Type expectedType = typeof(Guid);
public GuidSerializer(ProtoBuf.Meta.TypeModel model) { }
public Type ExpectedType { get { return expectedType; } }
bool IProtoSerializer.RequiresOldValue => false;
bool IProtoSerializer.ReturnsValue => true;
public void Write(object value, ProtoWriter dest)
{
BclHelpers.WriteGuid((Guid)value, dest);
}
public object Read(object value, ProtoReader source)
{
Helpers.DebugAssert(value == null); // since replaces
return BclHelpers.ReadGuid(source);
}
#if FEAT_COMPILER
void IProtoSerializer.EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
ctx.EmitWrite(ctx.MapType(typeof(BclHelpers)), "WriteGuid", valueFrom);
}
void IProtoSerializer.EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
ctx.EmitBasicRead(ctx.MapType(typeof(BclHelpers)), "ReadGuid", ExpectedType);
}
#endif
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 362fe2dd035b0cb4eaff2c7b7337fc66
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,64 @@
#if !NO_RUNTIME
using System;
namespace ProtoBuf.Serializers
{
interface IProtoSerializer
{
/// <summary>
/// The type that this serializer is intended to work for.
/// </summary>
Type ExpectedType { get; }
/// <summary>
/// Perform the steps necessary to serialize this data.
/// </summary>
/// <param name="value">The value to be serialized.</param>
/// <param name="dest">The writer entity that is accumulating the output data.</param>
void Write(object value, ProtoWriter dest);
/// <summary>
/// Perform the steps necessary to deserialize this data.
/// </summary>
/// <param name="value">The current value, if appropriate.</param>
/// <param name="source">The reader providing the input data.</param>
/// <returns>The updated / replacement value.</returns>
object Read(object value, ProtoReader source);
/// <summary>
/// Indicates whether a Read operation <em>replaces</em> the existing value, or
/// <em>extends</em> the value. If false, the "value" parameter to Read is
/// discarded, and should be passed in as null.
/// </summary>
bool RequiresOldValue { get; }
/// <summary>
/// Now all Read operations return a value (although most do); if false no
/// value should be expected.
/// </summary>
bool ReturnsValue { get; }
#if FEAT_COMPILER
/// <summary>Emit the IL necessary to perform the given actions
/// to serialize this data.
/// </summary>
/// <param name="ctx">Details and utilities for the method being generated.</param>
/// <param name="valueFrom">The source of the data to work against;
/// If the value is only needed once, then LoadValue is sufficient. If
/// the value is needed multiple times, then note that a "null"
/// means "the top of the stack", in which case you should create your
/// own copy - GetLocalWithValue.</param>
void EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom);
/// <summary>
/// Emit the IL necessary to perform the given actions to deserialize this data.
/// </summary>
/// <param name="ctx">Details and utilities for the method being generated.</param>
/// <param name="entity">For nested values, the instance holding the values; note
/// that this is not always provided - a null means not supplied. Since this is always
/// a variable or argument, it is not necessary to consume this value.</param>
void EmitRead(Compiler.CompilerContext ctx, Compiler.Local entity);
#endif
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6acc35442de99c94aade5d43c7992338
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,20 @@
#if !NO_RUNTIME
using ProtoBuf.Meta;
namespace ProtoBuf.Serializers
{
interface IProtoTypeSerializer : IProtoSerializer
{
bool HasCallbacks(TypeModel.CallbackType callbackType);
bool CanCreateInstance();
object CreateInstance(ProtoReader source);
void Callback(object value, TypeModel.CallbackType callbackType, SerializationContext context);
#if FEAT_COMPILER
void EmitCallback(Compiler.CompilerContext ctx, Compiler.Local valueFrom, TypeModel.CallbackType callbackType);
#endif
#if FEAT_COMPILER
void EmitCreateInstance(Compiler.CompilerContext ctx);
#endif
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6974491708512ec41b7a5f29805e4c69
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,10 @@
#if !NO_RUNTIME
namespace ProtoBuf.Serializers
{
interface ISerializerProxy
{
IProtoSerializer Serializer { get; }
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f717dd1190cbe174587e3bff7dd3dd76
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,304 @@
#if !NO_RUNTIME
using System;
using System.Collections;
using System.Reflection;
using ProtoBuf.Meta;
namespace ProtoBuf.Serializers
{
sealed class ImmutableCollectionDecorator : ListDecorator
{
protected override bool RequireAdd { get { return false; } }
static Type ResolveIReadOnlyCollection(Type declaredType, Type t)
{
#if COREFX || PROFILE259
if (CheckIsIReadOnlyCollectionExactly(declaredType.GetTypeInfo())) return declaredType;
foreach (Type intImplBasic in declaredType.GetTypeInfo().ImplementedInterfaces)
{
TypeInfo intImpl = intImplBasic.GetTypeInfo();
if (CheckIsIReadOnlyCollectionExactly(intImpl)) return intImplBasic;
}
#else
if (CheckIsIReadOnlyCollectionExactly(declaredType)) return declaredType;
foreach (Type intImpl in declaredType.GetInterfaces())
{
if (CheckIsIReadOnlyCollectionExactly(intImpl)) return intImpl;
}
#endif
return null;
}
#if WINRT || COREFX || PROFILE259
static bool CheckIsIReadOnlyCollectionExactly(TypeInfo t)
#else
static bool CheckIsIReadOnlyCollectionExactly(Type t)
#endif
{
if (t != null && t.IsGenericType && t.Name.StartsWith("IReadOnlyCollection`"))
{
#if WINRT || COREFX || PROFILE259
Type[] typeArgs = t.GenericTypeArguments;
if (typeArgs.Length != 1 && typeArgs[0].GetTypeInfo().Equals(t)) return false;
#else
Type[] typeArgs = t.GetGenericArguments();
if (typeArgs.Length != 1 && typeArgs[0] != t) return false;
#endif
return true;
}
return false;
}
internal static bool IdentifyImmutable(TypeModel model, Type declaredType, out MethodInfo builderFactory, out PropertyInfo isEmpty, out PropertyInfo length, out MethodInfo add, out MethodInfo addRange, out MethodInfo finish)
{
builderFactory = add = addRange = finish = null;
isEmpty = length = null;
if (model == null || declaredType == null) return false;
#if COREFX || PROFILE259
TypeInfo declaredTypeInfo = declaredType.GetTypeInfo();
#else
Type declaredTypeInfo = declaredType;
#endif
// try to detect immutable collections; firstly, they are all generic, and all implement IReadOnlyCollection<T> for some T
if (!declaredTypeInfo.IsGenericType) return false;
#if COREFX || PROFILE259
Type[] typeArgs = declaredTypeInfo.GenericTypeArguments, effectiveType;
#else
Type[] typeArgs = declaredTypeInfo.GetGenericArguments(), effectiveType;
#endif
switch (typeArgs.Length)
{
case 1:
effectiveType = typeArgs;
break; // fine
case 2:
Type kvp = model.MapType(typeof(System.Collections.Generic.KeyValuePair<,>));
if (kvp == null) return false;
kvp = kvp.MakeGenericType(typeArgs);
effectiveType = new Type[] { kvp };
break;
default:
return false; // no clue!
}
if (ResolveIReadOnlyCollection(declaredType, null) == null) return false; // no IReadOnlyCollection<T> found
// and we want to use the builder API, so for generic Foo<T> or IFoo<T> we want to use Foo.CreateBuilder<T>
string name = declaredType.Name;
int i = name.IndexOf('`');
if (i <= 0) return false;
name = declaredTypeInfo.IsInterface ? name.Substring(1, i - 1) : name.Substring(0, i);
Type outerType = model.GetType(declaredType.Namespace + "." + name, declaredTypeInfo.Assembly);
// I hate special-cases...
if (outerType == null && name == "ImmutableSet")
{
outerType = model.GetType(declaredType.Namespace + ".ImmutableHashSet", declaredTypeInfo.Assembly);
}
if (outerType == null) return false;
#if PROFILE259
foreach (MethodInfo method in outerType.GetTypeInfo().DeclaredMethods)
#else
foreach (MethodInfo method in outerType.GetMethods())
#endif
{
if (!method.IsStatic || method.Name != "CreateBuilder" || !method.IsGenericMethodDefinition || method.GetParameters().Length != 0
|| method.GetGenericArguments().Length != typeArgs.Length) continue;
builderFactory = method.MakeGenericMethod(typeArgs);
break;
}
Type voidType = model.MapType(typeof(void));
if (builderFactory == null || builderFactory.ReturnType == null || builderFactory.ReturnType == voidType) return false;
#if COREFX
TypeInfo typeInfo = declaredType.GetTypeInfo();
#else
Type typeInfo = declaredType;
#endif
isEmpty = Helpers.GetProperty(typeInfo, "IsDefaultOrEmpty", false); //struct based immutabletypes can have both a "default" and "empty" state
if (isEmpty == null) isEmpty = Helpers.GetProperty(typeInfo, "IsEmpty", false);
if (isEmpty == null)
{
//Fallback to checking length if a "IsEmpty" property is not found
length = Helpers.GetProperty(typeInfo, "Length", false);
if (length == null) length = Helpers.GetProperty(typeInfo, "Count", false);
if (length == null) length = Helpers.GetProperty(ResolveIReadOnlyCollection(declaredType, effectiveType[0]), "Count", false);
if (length == null) return false;
}
add = Helpers.GetInstanceMethod(builderFactory.ReturnType, "Add", effectiveType);
if (add == null) return false;
finish = Helpers.GetInstanceMethod(builderFactory.ReturnType, "ToImmutable", Helpers.EmptyTypes);
if (finish == null || finish.ReturnType == null || finish.ReturnType == voidType) return false;
if (!(finish.ReturnType == declaredType || Helpers.IsAssignableFrom(declaredType, finish.ReturnType))) return false;
addRange = Helpers.GetInstanceMethod(builderFactory.ReturnType, "AddRange", new Type[] { declaredType });
if (addRange == null)
{
Type enumerable = model.MapType(typeof(System.Collections.Generic.IEnumerable<>), false);
if (enumerable != null)
{
addRange = Helpers.GetInstanceMethod(builderFactory.ReturnType, "AddRange", new Type[] { enumerable.MakeGenericType(effectiveType) });
}
}
return true;
}
private readonly MethodInfo builderFactory, add, addRange, finish;
private readonly PropertyInfo isEmpty, length;
internal ImmutableCollectionDecorator(TypeModel model, Type declaredType, Type concreteType, IProtoSerializer tail, int fieldNumber, bool writePacked, WireType packedWireType, bool returnList, bool overwriteList, bool supportNull,
MethodInfo builderFactory, PropertyInfo isEmpty, PropertyInfo length, MethodInfo add, MethodInfo addRange, MethodInfo finish)
: base(model, declaredType, concreteType, tail, fieldNumber, writePacked, packedWireType, returnList, overwriteList, supportNull)
{
this.builderFactory = builderFactory;
this.isEmpty = isEmpty;
this.length = length;
this.add = add;
this.addRange = addRange;
this.finish = finish;
}
public override object Read(object value, ProtoReader source)
{
object builderInstance = builderFactory.Invoke(null, null);
int field = source.FieldNumber;
object[] args = new object[1];
if (AppendToCollection && value != null && (isEmpty != null ? !(bool)isEmpty.GetValue(value, null) : (int)length.GetValue(value, null) != 0))
{
if (addRange != null)
{
args[0] = value;
addRange.Invoke(builderInstance, args);
}
else
{
foreach (object item in (ICollection)value)
{
args[0] = item;
add.Invoke(builderInstance, args);
}
}
}
if (packedWireType != WireType.None && source.WireType == WireType.String)
{
SubItemToken token = ProtoReader.StartSubItem(source);
while (ProtoReader.HasSubValue(packedWireType, source))
{
args[0] = Tail.Read(null, source);
add.Invoke(builderInstance, args);
}
ProtoReader.EndSubItem(token, source);
}
else
{
do
{
args[0] = Tail.Read(null, source);
add.Invoke(builderInstance, args);
} while (source.TryReadFieldHeader(field));
}
return finish.Invoke(builderInstance, null);
}
#if FEAT_COMPILER
protected override void EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
using (Compiler.Local oldList = AppendToCollection ? ctx.GetLocalWithValue(ExpectedType, valueFrom) : null)
using (Compiler.Local builder = new Compiler.Local(ctx, builderFactory.ReturnType))
{
ctx.EmitCall(builderFactory);
ctx.StoreValue(builder);
if (AppendToCollection)
{
Compiler.CodeLabel done = ctx.DefineLabel();
if (!Helpers.IsValueType(ExpectedType))
{
ctx.LoadValue(oldList);
ctx.BranchIfFalse(done, false); // old value null; nothing to add
}
ctx.LoadAddress(oldList, oldList.Type);
if (isEmpty != null)
{
ctx.EmitCall(Helpers.GetGetMethod(isEmpty, false, false));
ctx.BranchIfTrue(done, false); // old list is empty; nothing to add
}
else
{
ctx.EmitCall(Helpers.GetGetMethod(length, false, false));
ctx.BranchIfFalse(done, false); // old list is empty; nothing to add
}
Type voidType = ctx.MapType(typeof(void));
if (addRange != null)
{
ctx.LoadValue(builder);
ctx.LoadValue(oldList);
ctx.EmitCall(addRange);
if (addRange.ReturnType != null && add.ReturnType != voidType) ctx.DiscardValue();
}
else
{
// loop and call Add repeatedly
MethodInfo moveNext, current, getEnumerator = GetEnumeratorInfo(ctx.Model, out moveNext, out current);
Helpers.DebugAssert(moveNext != null);
Helpers.DebugAssert(current != null);
Helpers.DebugAssert(getEnumerator != null);
Type enumeratorType = getEnumerator.ReturnType;
using (Compiler.Local iter = new Compiler.Local(ctx, enumeratorType))
{
ctx.LoadAddress(oldList, ExpectedType);
ctx.EmitCall(getEnumerator);
ctx.StoreValue(iter);
using (ctx.Using(iter))
{
Compiler.CodeLabel body = ctx.DefineLabel(), next = ctx.DefineLabel();
ctx.Branch(next, false);
ctx.MarkLabel(body);
ctx.LoadAddress(builder, builder.Type);
ctx.LoadAddress(iter, enumeratorType);
ctx.EmitCall(current);
ctx.EmitCall(add);
if (add.ReturnType != null && add.ReturnType != voidType) ctx.DiscardValue();
ctx.MarkLabel(@next);
ctx.LoadAddress(iter, enumeratorType);
ctx.EmitCall(moveNext);
ctx.BranchIfTrue(body, false);
}
}
}
ctx.MarkLabel(done);
}
EmitReadList(ctx, builder, Tail, add, packedWireType, false);
ctx.LoadAddress(builder, builder.Type);
ctx.EmitCall(finish);
if (ExpectedType != finish.ReturnType)
{
ctx.Cast(ExpectedType);
}
}
}
#endif
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 00a3af58286d1674ca64bcf5fd9f0228
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,42 @@
#if !NO_RUNTIME
using System;
namespace ProtoBuf.Serializers
{
sealed class Int16Serializer : IProtoSerializer
{
static readonly Type expectedType = typeof(short);
public Int16Serializer(ProtoBuf.Meta.TypeModel model) { }
public Type ExpectedType => expectedType;
bool IProtoSerializer.RequiresOldValue => false;
bool IProtoSerializer.ReturnsValue => true;
public object Read(object value, ProtoReader source)
{
Helpers.DebugAssert(value == null); // since replaces
return source.ReadInt16();
}
public void Write(object value, ProtoWriter dest)
{
ProtoWriter.WriteInt16((short)value, dest);
}
#if FEAT_COMPILER
void IProtoSerializer.EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
ctx.EmitBasicWrite("WriteInt16", valueFrom);
}
void IProtoSerializer.EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
ctx.EmitBasicRead("ReadInt16", ExpectedType);
}
#endif
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e45229312d2a4fe45b519e513326b708
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,42 @@
#if !NO_RUNTIME
using System;
namespace ProtoBuf.Serializers
{
sealed class Int32Serializer : IProtoSerializer
{
static readonly Type expectedType = typeof(int);
public Int32Serializer(ProtoBuf.Meta.TypeModel model) { }
public Type ExpectedType => expectedType;
bool IProtoSerializer.RequiresOldValue => false;
bool IProtoSerializer.ReturnsValue => true;
public object Read(object value, ProtoReader source)
{
Helpers.DebugAssert(value == null); // since replaces
return source.ReadInt32();
}
public void Write(object value, ProtoWriter dest)
{
ProtoWriter.WriteInt32((int)value, dest);
}
#if FEAT_COMPILER
void IProtoSerializer.EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
ctx.EmitBasicWrite("WriteInt32", valueFrom);
}
void IProtoSerializer.EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
ctx.EmitBasicRead("ReadInt32", ExpectedType);
}
#endif
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4a7c49bc45156f442bfe84fc7eef04b9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,41 @@
#if !NO_RUNTIME
using System;
namespace ProtoBuf.Serializers
{
sealed class Int64Serializer : IProtoSerializer
{
static readonly Type expectedType = typeof(long);
public Int64Serializer(ProtoBuf.Meta.TypeModel model) { }
public Type ExpectedType => expectedType;
bool IProtoSerializer.RequiresOldValue => false;
bool IProtoSerializer.ReturnsValue => true;
public object Read(object value, ProtoReader source)
{
Helpers.DebugAssert(value == null); // since replaces
return source.ReadInt64();
}
public void Write(object value, ProtoWriter dest)
{
ProtoWriter.WriteInt64((long)value, dest);
}
#if FEAT_COMPILER
void IProtoSerializer.EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
ctx.EmitBasicWrite("WriteInt64", valueFrom);
}
void IProtoSerializer.EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
ctx.EmitBasicRead("ReadInt64", ExpectedType);
}
#endif
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 03f2770a306f45046b6e8eab757c9188
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,579 @@
#if !NO_RUNTIME
using System;
using System.Collections;
using ProtoBuf.Meta;
using System.Reflection;
namespace ProtoBuf.Serializers
{
class ListDecorator : ProtoDecoratorBase
{
internal static bool CanPack(WireType wireType)
{
switch (wireType)
{
case WireType.Fixed32:
case WireType.Fixed64:
case WireType.SignedVariant:
case WireType.Variant:
return true;
default:
return false;
}
}
private readonly byte options;
private const byte OPTIONS_IsList = 1,
OPTIONS_SuppressIList = 2,
OPTIONS_WritePacked = 4,
OPTIONS_ReturnList = 8,
OPTIONS_OverwriteList = 16,
OPTIONS_SupportNull = 32;
private readonly Type declaredType, concreteType;
private readonly MethodInfo add;
private readonly int fieldNumber;
private bool IsList { get { return (options & OPTIONS_IsList) != 0; } }
private bool SuppressIList { get { return (options & OPTIONS_SuppressIList) != 0; } }
private bool WritePacked { get { return (options & OPTIONS_WritePacked) != 0; } }
private bool SupportNull { get { return (options & OPTIONS_SupportNull) != 0; } }
private bool ReturnList { get { return (options & OPTIONS_ReturnList) != 0; } }
protected readonly WireType packedWireType;
internal static ListDecorator Create(TypeModel model, Type declaredType, Type concreteType, IProtoSerializer tail, int fieldNumber, bool writePacked, WireType packedWireType, bool returnList, bool overwriteList, bool supportNull)
{
if (returnList && ImmutableCollectionDecorator.IdentifyImmutable(model, declaredType,
out MethodInfo builderFactory,
out PropertyInfo isEmpty,
out PropertyInfo length,
out MethodInfo add,
out MethodInfo addRange,
out MethodInfo finish))
{
return new ImmutableCollectionDecorator(
model, declaredType, concreteType, tail, fieldNumber, writePacked, packedWireType, returnList, overwriteList, supportNull,
builderFactory, isEmpty, length, add, addRange, finish);
}
return new ListDecorator(model, declaredType, concreteType, tail, fieldNumber, writePacked, packedWireType, returnList, overwriteList, supportNull);
}
protected ListDecorator(TypeModel model, Type declaredType, Type concreteType, IProtoSerializer tail, int fieldNumber, bool writePacked, WireType packedWireType, bool returnList, bool overwriteList, bool supportNull)
: base(tail)
{
if (returnList) options |= OPTIONS_ReturnList;
if (overwriteList) options |= OPTIONS_OverwriteList;
if (supportNull) options |= OPTIONS_SupportNull;
if ((writePacked || packedWireType != WireType.None) && fieldNumber <= 0) throw new ArgumentOutOfRangeException("fieldNumber");
if (!CanPack(packedWireType))
{
if (writePacked) throw new InvalidOperationException("Only simple data-types can use packed encoding");
packedWireType = WireType.None;
}
this.fieldNumber = fieldNumber;
if (writePacked) options |= OPTIONS_WritePacked;
this.packedWireType = packedWireType;
if (declaredType == null) throw new ArgumentNullException("declaredType");
if (declaredType.IsArray) throw new ArgumentException("Cannot treat arrays as lists", "declaredType");
this.declaredType = declaredType;
this.concreteType = concreteType;
// look for a public list.Add(typedObject) method
if (RequireAdd)
{
bool isList;
add = TypeModel.ResolveListAdd(model, declaredType, tail.ExpectedType, out isList);
if (isList)
{
options |= OPTIONS_IsList;
string fullName = declaredType.FullName;
if (fullName != null && fullName.StartsWith("System.Data.Linq.EntitySet`1[["))
{ // see http://stackoverflow.com/questions/6194639/entityset-is-there-a-sane-reason-that-ilist-add-doesnt-set-assigned
options |= OPTIONS_SuppressIList;
}
}
if (add == null) throw new InvalidOperationException("Unable to resolve a suitable Add method for " + declaredType.FullName);
}
}
protected virtual bool RequireAdd => true;
public override Type ExpectedType => declaredType;
public override bool RequiresOldValue => AppendToCollection;
public override bool ReturnsValue => ReturnList;
protected bool AppendToCollection
{
get { return (options & OPTIONS_OverwriteList) == 0; }
}
#if FEAT_COMPILER
protected override void EmitRead(ProtoBuf.Compiler.CompilerContext ctx, ProtoBuf.Compiler.Local valueFrom)
{
/* This looks more complex than it is. Look at the non-compiled Read to
* see what it is trying to do, but note that it needs to cope with a
* few more scenarios. Note that it picks the **most specific** Add,
* unlike the runtime version that uses IList when possible. The core
* is just a "do {list.Add(readValue())} while {thereIsMore}"
*
* The complexity is due to:
* - value types vs reference types (boxing etc)
* - initialization if we need to pass in a value to the tail
* - handling whether or not the tail *returns* the value vs updates the input
*/
bool returnList = ReturnList;
using (Compiler.Local list = AppendToCollection ? ctx.GetLocalWithValue(ExpectedType, valueFrom) : new Compiler.Local(ctx, declaredType))
using (Compiler.Local origlist = (returnList && AppendToCollection && !Helpers.IsValueType(ExpectedType)) ? new Compiler.Local(ctx, ExpectedType) : null)
{
if (!AppendToCollection)
{ // always new
ctx.LoadNullRef();
ctx.StoreValue(list);
}
else if (returnList && origlist != null)
{ // need a copy
ctx.LoadValue(list);
ctx.StoreValue(origlist);
}
if (concreteType != null)
{
ctx.LoadValue(list);
Compiler.CodeLabel notNull = ctx.DefineLabel();
ctx.BranchIfTrue(notNull, true);
ctx.EmitCtor(concreteType);
ctx.StoreValue(list);
ctx.MarkLabel(notNull);
}
bool castListForAdd = !add.DeclaringType.IsAssignableFrom(declaredType);
EmitReadList(ctx, list, Tail, add, packedWireType, castListForAdd);
if (returnList)
{
if (AppendToCollection && origlist != null)
{
// remember ^^^^ we had a spare copy of the list on the stack; now we'll compare
ctx.LoadValue(origlist);
ctx.LoadValue(list); // [orig] [new-value]
Compiler.CodeLabel sameList = ctx.DefineLabel(), allDone = ctx.DefineLabel();
ctx.BranchIfEqual(sameList, true);
ctx.LoadValue(list);
ctx.Branch(allDone, true);
ctx.MarkLabel(sameList);
ctx.LoadNullRef();
ctx.MarkLabel(allDone);
}
else
{
ctx.LoadValue(list);
}
}
}
}
internal static void EmitReadList(ProtoBuf.Compiler.CompilerContext ctx, Compiler.Local list, IProtoSerializer tail, MethodInfo add, WireType packedWireType, bool castListForAdd)
{
using (Compiler.Local fieldNumber = new Compiler.Local(ctx, ctx.MapType(typeof(int))))
{
Compiler.CodeLabel readPacked = packedWireType == WireType.None ? new Compiler.CodeLabel() : ctx.DefineLabel();
if (packedWireType != WireType.None)
{
ctx.LoadReaderWriter();
ctx.LoadValue(typeof(ProtoReader).GetProperty("WireType"));
ctx.LoadValue((int)WireType.String);
ctx.BranchIfEqual(readPacked, false);
}
ctx.LoadReaderWriter();
ctx.LoadValue(typeof(ProtoReader).GetProperty("FieldNumber"));
ctx.StoreValue(fieldNumber);
Compiler.CodeLabel @continue = ctx.DefineLabel();
ctx.MarkLabel(@continue);
EmitReadAndAddItem(ctx, list, tail, add, castListForAdd);
ctx.LoadReaderWriter();
ctx.LoadValue(fieldNumber);
ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("TryReadFieldHeader"));
ctx.BranchIfTrue(@continue, false);
if (packedWireType != WireType.None)
{
Compiler.CodeLabel allDone = ctx.DefineLabel();
ctx.Branch(allDone, false);
ctx.MarkLabel(readPacked);
ctx.LoadReaderWriter();
ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("StartSubItem"));
Compiler.CodeLabel testForData = ctx.DefineLabel(), noMoreData = ctx.DefineLabel();
ctx.MarkLabel(testForData);
ctx.LoadValue((int)packedWireType);
ctx.LoadReaderWriter();
ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("HasSubValue"));
ctx.BranchIfFalse(noMoreData, false);
EmitReadAndAddItem(ctx, list, tail, add, castListForAdd);
ctx.Branch(testForData, false);
ctx.MarkLabel(noMoreData);
ctx.LoadReaderWriter();
ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("EndSubItem"));
ctx.MarkLabel(allDone);
}
}
}
private static void EmitReadAndAddItem(Compiler.CompilerContext ctx, Compiler.Local list, IProtoSerializer tail, MethodInfo add, bool castListForAdd)
{
ctx.LoadAddress(list, list.Type); // needs to be the reference in case the list is value-type (static-call)
if (castListForAdd) ctx.Cast(add.DeclaringType);
Type itemType = tail.ExpectedType;
bool tailReturnsValue = tail.ReturnsValue;
if (tail.RequiresOldValue)
{
if (Helpers.IsValueType(itemType) || !tailReturnsValue)
{
// going to need a variable
using (Compiler.Local item = new Compiler.Local(ctx, itemType))
{
if (Helpers.IsValueType(itemType))
{ // initialise the struct
ctx.LoadAddress(item, itemType);
ctx.EmitCtor(itemType);
}
else
{ // assign null
ctx.LoadNullRef();
ctx.StoreValue(item);
}
tail.EmitRead(ctx, item);
if (!tailReturnsValue) { ctx.LoadValue(item); }
}
}
else
{ // no variable; pass the null on the stack and take the value *off* the stack
ctx.LoadNullRef();
tail.EmitRead(ctx, null);
}
}
else
{
if (tailReturnsValue)
{ // out only (on the stack); just emit it
tail.EmitRead(ctx, null);
}
else
{ // doesn't take anything in nor return anything! WTF?
throw new InvalidOperationException();
}
}
// our "Add" is chosen either to take the correct type, or to take "object";
// we may need to box the value
Type addParamType = add.GetParameters()[0].ParameterType;
if (addParamType != itemType)
{
if (addParamType == ctx.MapType(typeof(object)))
{
ctx.CastToObject(itemType);
}
else if (Helpers.GetUnderlyingType(addParamType) == itemType)
{ // list is nullable
ConstructorInfo ctor = Helpers.GetConstructor(addParamType, new Type[] { itemType }, false);
ctx.EmitCtor(ctor); // the itemType on the stack is now a Nullable<ItemType>
}
else
{
throw new InvalidOperationException("Conflicting item/add type");
}
}
ctx.EmitCall(add, list.Type);
if (add.ReturnType != ctx.MapType(typeof(void)))
{
ctx.DiscardValue();
}
}
#endif
#if COREFX
private static readonly TypeInfo ienumeratorType = typeof(IEnumerator).GetTypeInfo(), ienumerableType = typeof (IEnumerable).GetTypeInfo();
#else
private static readonly System.Type ienumeratorType = typeof(IEnumerator), ienumerableType = typeof(IEnumerable);
#endif
protected MethodInfo GetEnumeratorInfo(TypeModel model, out MethodInfo moveNext, out MethodInfo current)
=> GetEnumeratorInfo(model, ExpectedType, Tail.ExpectedType, out moveNext, out current);
internal static MethodInfo GetEnumeratorInfo(TypeModel model, Type expectedType, Type itemType, out MethodInfo moveNext, out MethodInfo current)
{
#if COREFX
TypeInfo enumeratorType = null, iteratorType;
#else
Type enumeratorType = null, iteratorType;
#endif
// try a custom enumerator
MethodInfo getEnumerator = Helpers.GetInstanceMethod(expectedType, "GetEnumerator", null);
Type getReturnType = null;
if (getEnumerator != null)
{
getReturnType = getEnumerator.ReturnType;
iteratorType = getReturnType
#if COREFX || COREFX
.GetTypeInfo()
#endif
;
moveNext = Helpers.GetInstanceMethod(iteratorType, "MoveNext", null);
PropertyInfo prop = Helpers.GetProperty(iteratorType, "Current", false);
current = prop == null ? null : Helpers.GetGetMethod(prop, false, false);
#if PROFILE259
if (moveNext == null && (model.MapType(ienumeratorType).GetTypeInfo().IsAssignableFrom(iteratorType.GetTypeInfo())))
#else
if (moveNext == null && (model.MapType(ienumeratorType).IsAssignableFrom(iteratorType)))
#endif
{
moveNext = Helpers.GetInstanceMethod(model.MapType(ienumeratorType), "MoveNext", null);
}
// fully typed
if (moveNext != null && moveNext.ReturnType == model.MapType(typeof(bool))
&& current != null && current.ReturnType == itemType)
{
return getEnumerator;
}
moveNext = current = getEnumerator = null;
}
// try IEnumerable<T>
Type tmp = model.MapType(typeof(System.Collections.Generic.IEnumerable<>), false);
if (tmp != null)
{
tmp = tmp.MakeGenericType(itemType);
#if COREFX
enumeratorType = tmp.GetTypeInfo();
#else
enumeratorType = tmp;
#endif
}
;
#if PROFILE259
if (enumeratorType != null && enumeratorType.GetTypeInfo().IsAssignableFrom(expectedType
#else
if (enumeratorType != null && enumeratorType.IsAssignableFrom(expectedType
#endif
#if COREFX || PROFILE259
.GetTypeInfo()
#endif
))
{
getEnumerator = Helpers.GetInstanceMethod(enumeratorType, "GetEnumerator");
getReturnType = getEnumerator.ReturnType;
#if COREFX
iteratorType = getReturnType.GetTypeInfo();
#else
iteratorType = getReturnType;
#endif
moveNext = Helpers.GetInstanceMethod(model.MapType(ienumeratorType), "MoveNext");
current = Helpers.GetGetMethod(Helpers.GetProperty(iteratorType, "Current", false), false, false);
return getEnumerator;
}
// give up and fall-back to non-generic IEnumerable
enumeratorType = model.MapType(ienumerableType);
getEnumerator = Helpers.GetInstanceMethod(enumeratorType, "GetEnumerator");
getReturnType = getEnumerator.ReturnType;
iteratorType = getReturnType
#if COREFX
.GetTypeInfo()
#endif
;
moveNext = Helpers.GetInstanceMethod(iteratorType, "MoveNext");
current = Helpers.GetGetMethod(Helpers.GetProperty(iteratorType, "Current", false), false, false);
return getEnumerator;
}
#if FEAT_COMPILER
protected override void EmitWrite(ProtoBuf.Compiler.CompilerContext ctx, ProtoBuf.Compiler.Local valueFrom)
{
using (Compiler.Local list = ctx.GetLocalWithValue(ExpectedType, valueFrom))
{
MethodInfo getEnumerator = GetEnumeratorInfo(ctx.Model, out MethodInfo moveNext, out MethodInfo current);
Helpers.DebugAssert(moveNext != null);
Helpers.DebugAssert(current != null);
Helpers.DebugAssert(getEnumerator != null);
Type enumeratorType = getEnumerator.ReturnType;
bool writePacked = WritePacked;
using (Compiler.Local iter = new Compiler.Local(ctx, enumeratorType))
using (Compiler.Local token = writePacked ? new Compiler.Local(ctx, ctx.MapType(typeof(SubItemToken))) : null)
{
if (writePacked)
{
ctx.LoadValue(fieldNumber);
ctx.LoadValue((int)WireType.String);
ctx.LoadReaderWriter();
ctx.EmitCall(ctx.MapType(typeof(ProtoWriter)).GetMethod("WriteFieldHeader"));
ctx.LoadValue(list);
ctx.LoadReaderWriter();
ctx.EmitCall(ctx.MapType(typeof(ProtoWriter)).GetMethod("StartSubItem"));
ctx.StoreValue(token);
ctx.LoadValue(fieldNumber);
ctx.LoadReaderWriter();
ctx.EmitCall(ctx.MapType(typeof(ProtoWriter)).GetMethod("SetPackedField"));
}
ctx.LoadAddress(list, ExpectedType);
ctx.EmitCall(getEnumerator, ExpectedType);
ctx.StoreValue(iter);
using (ctx.Using(iter))
{
Compiler.CodeLabel body = ctx.DefineLabel(), next = ctx.DefineLabel();
ctx.Branch(next, false);
ctx.MarkLabel(body);
ctx.LoadAddress(iter, enumeratorType);
ctx.EmitCall(current, enumeratorType);
Type itemType = Tail.ExpectedType;
if (itemType != ctx.MapType(typeof(object)) && current.ReturnType == ctx.MapType(typeof(object)))
{
ctx.CastFromObject(itemType);
}
Tail.EmitWrite(ctx, null);
ctx.MarkLabel(@next);
ctx.LoadAddress(iter, enumeratorType);
ctx.EmitCall(moveNext, enumeratorType);
ctx.BranchIfTrue(body, false);
}
if (writePacked)
{
ctx.LoadValue(token);
ctx.LoadReaderWriter();
ctx.EmitCall(ctx.MapType(typeof(ProtoWriter)).GetMethod("EndSubItem"));
}
}
}
}
#endif
public override void Write(object value, ProtoWriter dest)
{
SubItemToken token;
bool writePacked = WritePacked;
bool fixedSizePacked = writePacked & CanUsePackedPrefix(value) && value is ICollection;
if (writePacked)
{
ProtoWriter.WriteFieldHeader(fieldNumber, WireType.String, dest);
if (fixedSizePacked)
{
ProtoWriter.WritePackedPrefix(((ICollection)value).Count, packedWireType, dest);
token = default(SubItemToken);
}
else
{
token = ProtoWriter.StartSubItem(value, dest);
}
ProtoWriter.SetPackedField(fieldNumber, dest);
}
else
{
token = new SubItemToken(); // default
}
bool checkForNull = !SupportNull;
foreach (object subItem in (IEnumerable)value)
{
if (checkForNull && subItem == null) { throw new NullReferenceException(); }
Tail.Write(subItem, dest);
}
if (writePacked)
{
if (fixedSizePacked)
{
ProtoWriter.ClearPackedField(fieldNumber, dest);
}
else
{
ProtoWriter.EndSubItem(token, dest);
}
}
}
private bool CanUsePackedPrefix(object obj) =>
ArrayDecorator.CanUsePackedPrefix(packedWireType, Tail.ExpectedType);
public override object Read(object value, ProtoReader source)
{
try
{
int field = source.FieldNumber;
object origValue = value;
if (value == null) value = Activator.CreateInstance(concreteType);
bool isList = IsList && !SuppressIList;
if (packedWireType != WireType.None && source.WireType == WireType.String)
{
SubItemToken token = ProtoReader.StartSubItem(source);
if (isList)
{
IList list = (IList)value;
while (ProtoReader.HasSubValue(packedWireType, source))
{
list.Add(Tail.Read(null, source));
}
}
else
{
object[] args = new object[1];
while (ProtoReader.HasSubValue(packedWireType, source))
{
args[0] = Tail.Read(null, source);
add.Invoke(value, args);
}
}
ProtoReader.EndSubItem(token, source);
}
else
{
if (isList)
{
IList list = (IList)value;
do
{
list.Add(Tail.Read(null, source));
} while (source.TryReadFieldHeader(field));
}
else
{
object[] args = new object[1];
do
{
args[0] = Tail.Read(null, source);
add.Invoke(value, args);
} while (source.TryReadFieldHeader(field));
}
}
return origValue == value ? null : value;
}
catch (TargetInvocationException tie)
{
if (tie.InnerException != null) throw tie.InnerException;
throw;
}
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: eb7a73aa78c887c478b0af6d506337d5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,298 @@
using ProtoBuf.Meta;
using System;
#if FEAT_COMPILER
using ProtoBuf.Compiler;
#endif
using System.Collections.Generic;
using System.Reflection;
namespace ProtoBuf.Serializers
{
class MapDecorator<TDictionary, TKey, TValue> : ProtoDecoratorBase where TDictionary : class, IDictionary<TKey, TValue>
{
private readonly Type concreteType;
private readonly IProtoSerializer keyTail;
private readonly int fieldNumber;
private readonly WireType wireType;
internal MapDecorator(TypeModel model, Type concreteType, IProtoSerializer keyTail, IProtoSerializer valueTail,
int fieldNumber, WireType wireType, WireType keyWireType, WireType valueWireType, bool overwriteList)
: base(DefaultValue == null
? (IProtoSerializer)new TagDecorator(2, valueWireType, false, valueTail)
: (IProtoSerializer)new DefaultValueDecorator(model, DefaultValue, new TagDecorator(2, valueWireType, false, valueTail)))
{
this.wireType = wireType;
this.keyTail = new DefaultValueDecorator(model, DefaultKey, new TagDecorator(1, keyWireType, false, keyTail));
this.fieldNumber = fieldNumber;
this.concreteType = concreteType ?? typeof(TDictionary);
if (keyTail.RequiresOldValue) throw new InvalidOperationException("Key tail should not require the old value");
if (!keyTail.ReturnsValue) throw new InvalidOperationException("Key tail should return a value");
if (!valueTail.ReturnsValue) throw new InvalidOperationException("Value tail should return a value");
AppendToCollection = !overwriteList;
}
private static readonly MethodInfo indexerSet = GetIndexerSetter();
private static MethodInfo GetIndexerSetter()
{
#if PROFILE259
foreach(var prop in typeof(TDictionary).GetRuntimeProperties())
#else
foreach (var prop in typeof(TDictionary).GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
#endif
{
if (prop.Name != "Item") continue;
if (prop.PropertyType != typeof(TValue)) continue;
var args = prop.GetIndexParameters();
if (args == null || args.Length != 1) continue;
if (args[0].ParameterType != typeof(TKey)) continue;
#if PROFILE259
var method = prop.SetMethod;
#else
var method = prop.GetSetMethod(true);
#endif
if (method != null)
{
return method;
}
}
throw new InvalidOperationException("Unable to resolve indexer for map");
}
private static readonly TKey DefaultKey = (typeof(TKey) == typeof(string)) ? (TKey)(object)"" : default(TKey);
private static readonly TValue DefaultValue = (typeof(TValue) == typeof(string)) ? (TValue)(object)"" : default(TValue);
public override Type ExpectedType => typeof(TDictionary);
public override bool ReturnsValue => true;
public override bool RequiresOldValue => AppendToCollection;
private bool AppendToCollection { get; }
public override object Read(object untyped, ProtoReader source)
{
TDictionary typed = AppendToCollection ? ((TDictionary)untyped) : null;
if (typed == null) typed = (TDictionary)Activator.CreateInstance(concreteType);
do
{
var key = DefaultKey;
var value = DefaultValue;
SubItemToken token = ProtoReader.StartSubItem(source);
int field;
while ((field = source.ReadFieldHeader()) > 0)
{
switch (field)
{
case 1:
key = (TKey)keyTail.Read(null, source);
break;
case 2:
value = (TValue)Tail.Read(Tail.RequiresOldValue ? (object)value : null, source);
break;
default:
source.SkipField();
break;
}
}
ProtoReader.EndSubItem(token, source);
typed[key] = value;
} while (source.TryReadFieldHeader(fieldNumber));
return typed;
}
public override void Write(object untyped, ProtoWriter dest)
{
foreach (var pair in (TDictionary)untyped)
{
ProtoWriter.WriteFieldHeader(fieldNumber, wireType, dest);
var token = ProtoWriter.StartSubItem(null, dest);
if (pair.Key != null) keyTail.Write(pair.Key, dest);
if (pair.Value != null) Tail.Write(pair.Value, dest);
ProtoWriter.EndSubItem(token, dest);
}
}
#if FEAT_COMPILER
protected override void EmitWrite(CompilerContext ctx, Local valueFrom)
{
Type itemType = typeof(KeyValuePair<TKey, TValue>);
MethodInfo moveNext, current, getEnumerator = ListDecorator.GetEnumeratorInfo(ctx.Model,
ExpectedType, itemType, out moveNext, out current);
Type enumeratorType = getEnumerator.ReturnType;
MethodInfo key = itemType.GetProperty(nameof(KeyValuePair<TKey, TValue>.Key)).GetGetMethod(),
@value = itemType.GetProperty(nameof(KeyValuePair<TKey, TValue>.Value)).GetGetMethod();
using (Compiler.Local list = ctx.GetLocalWithValue(ExpectedType, valueFrom))
using (Compiler.Local iter = new Compiler.Local(ctx, enumeratorType))
using (Compiler.Local token = new Compiler.Local(ctx, typeof(SubItemToken)))
using (Compiler.Local kvp = new Compiler.Local(ctx, itemType))
{
ctx.LoadAddress(list, ExpectedType);
ctx.EmitCall(getEnumerator, ExpectedType);
ctx.StoreValue(iter);
using (ctx.Using(iter))
{
Compiler.CodeLabel body = ctx.DefineLabel(), next = ctx.DefineLabel();
ctx.Branch(next, false);
ctx.MarkLabel(body);
ctx.LoadAddress(iter, enumeratorType);
ctx.EmitCall(current, enumeratorType);
if (itemType != ctx.MapType(typeof(object)) && current.ReturnType == ctx.MapType(typeof(object)))
{
ctx.CastFromObject(itemType);
}
ctx.StoreValue(kvp);
ctx.LoadValue(fieldNumber);
ctx.LoadValue((int)wireType);
ctx.LoadReaderWriter();
ctx.EmitCall(ctx.MapType(typeof(ProtoWriter)).GetMethod("WriteFieldHeader"));
ctx.LoadNullRef();
ctx.LoadReaderWriter();
ctx.EmitCall(ctx.MapType(typeof(ProtoWriter)).GetMethod("StartSubItem"));
ctx.StoreValue(token);
ctx.LoadAddress(kvp, itemType);
ctx.EmitCall(key, itemType);
ctx.WriteNullCheckedTail(typeof(TKey), keyTail, null);
ctx.LoadAddress(kvp, itemType);
ctx.EmitCall(value, itemType);
ctx.WriteNullCheckedTail(typeof(TValue), Tail, null);
ctx.LoadValue(token);
ctx.LoadReaderWriter();
ctx.EmitCall(ctx.MapType(typeof(ProtoWriter)).GetMethod("EndSubItem"));
ctx.MarkLabel(@next);
ctx.LoadAddress(iter, enumeratorType);
ctx.EmitCall(moveNext, enumeratorType);
ctx.BranchIfTrue(body, false);
}
}
}
protected override void EmitRead(CompilerContext ctx, Local valueFrom)
{
using (Compiler.Local list = AppendToCollection ? ctx.GetLocalWithValue(ExpectedType, valueFrom)
: new Compiler.Local(ctx, typeof(TDictionary)))
using (Compiler.Local token = new Compiler.Local(ctx, typeof(SubItemToken)))
using (Compiler.Local key = new Compiler.Local(ctx, typeof(TKey)))
using (Compiler.Local @value = new Compiler.Local(ctx, typeof(TValue)))
using (Compiler.Local fieldNumber = new Compiler.Local(ctx, ctx.MapType(typeof(int))))
{
if (!AppendToCollection)
{ // always new
ctx.LoadNullRef();
ctx.StoreValue(list);
}
if (concreteType != null)
{
ctx.LoadValue(list);
Compiler.CodeLabel notNull = ctx.DefineLabel();
ctx.BranchIfTrue(notNull, true);
ctx.EmitCtor(concreteType);
ctx.StoreValue(list);
ctx.MarkLabel(notNull);
}
var redoFromStart = ctx.DefineLabel();
ctx.MarkLabel(redoFromStart);
// key = default(TKey); value = default(TValue);
if (typeof(TKey) == typeof(string))
{
ctx.LoadValue("");
ctx.StoreValue(key);
}
else
{
ctx.InitLocal(typeof(TKey), key);
}
if (typeof(TValue) == typeof(string))
{
ctx.LoadValue("");
ctx.StoreValue(value);
}
else
{
ctx.InitLocal(typeof(TValue), @value);
}
// token = ProtoReader.StartSubItem(reader);
ctx.LoadReaderWriter();
ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("StartSubItem"));
ctx.StoreValue(token);
Compiler.CodeLabel @continue = ctx.DefineLabel(), processField = ctx.DefineLabel();
// while ...
ctx.Branch(@continue, false);
// switch(fieldNumber)
ctx.MarkLabel(processField);
ctx.LoadValue(fieldNumber);
CodeLabel @default = ctx.DefineLabel(), one = ctx.DefineLabel(), two = ctx.DefineLabel();
ctx.Switch(new[] { @default, one, two }); // zero based, hence explicit 0
// case 0: default: reader.SkipField();
ctx.MarkLabel(@default);
ctx.LoadReaderWriter();
ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("SkipField"));
ctx.Branch(@continue, false);
// case 1: key = ...
ctx.MarkLabel(one);
keyTail.EmitRead(ctx, null);
ctx.StoreValue(key);
ctx.Branch(@continue, false);
// case 2: value = ...
ctx.MarkLabel(two);
Tail.EmitRead(ctx, Tail.RequiresOldValue ? @value : null);
ctx.StoreValue(value);
// (fieldNumber = reader.ReadFieldHeader()) > 0
ctx.MarkLabel(@continue);
ctx.EmitBasicRead("ReadFieldHeader", ctx.MapType(typeof(int)));
ctx.CopyValue();
ctx.StoreValue(fieldNumber);
ctx.LoadValue(0);
ctx.BranchIfGreater(processField, false);
// ProtoReader.EndSubItem(token, reader);
ctx.LoadValue(token);
ctx.LoadReaderWriter();
ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("EndSubItem"));
// list[key] = value;
ctx.LoadAddress(list, ExpectedType);
ctx.LoadValue(key);
ctx.LoadValue(@value);
ctx.EmitCall(indexerSet);
// while reader.TryReadFieldReader(fieldNumber)
ctx.LoadReaderWriter();
ctx.LoadValue(this.fieldNumber);
ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("TryReadFieldHeader"));
ctx.BranchIfTrue(redoFromStart, false);
if (ReturnsValue)
{
ctx.LoadValue(list);
}
}
}
#endif
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 522b5c8a0fa5be14591bc9cbe3b194d3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,76 @@
#if !NO_RUNTIME
using System;
using System.Reflection;
namespace ProtoBuf.Serializers
{
sealed class MemberSpecifiedDecorator : ProtoDecoratorBase
{
public override Type ExpectedType => Tail.ExpectedType;
public override bool RequiresOldValue => Tail.RequiresOldValue;
public override bool ReturnsValue => Tail.ReturnsValue;
private readonly MethodInfo getSpecified, setSpecified;
public MemberSpecifiedDecorator(MethodInfo getSpecified, MethodInfo setSpecified, IProtoSerializer tail)
: base(tail)
{
if (getSpecified == null && setSpecified == null) throw new InvalidOperationException();
this.getSpecified = getSpecified;
this.setSpecified = setSpecified;
}
public override void Write(object value, ProtoWriter dest)
{
if (getSpecified == null || (bool)getSpecified.Invoke(value, null))
{
Tail.Write(value, dest);
}
}
public override object Read(object value, ProtoReader source)
{
object result = Tail.Read(value, source);
if (setSpecified != null) setSpecified.Invoke(value, new object[] { true });
return result;
}
#if FEAT_COMPILER
protected override void EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
if (getSpecified == null)
{
Tail.EmitWrite(ctx, valueFrom);
return;
}
using (Compiler.Local loc = ctx.GetLocalWithValue(ExpectedType, valueFrom))
{
ctx.LoadAddress(loc, ExpectedType);
ctx.EmitCall(getSpecified);
Compiler.CodeLabel done = ctx.DefineLabel();
ctx.BranchIfFalse(done, false);
Tail.EmitWrite(ctx, loc);
ctx.MarkLabel(done);
}
}
protected override void EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
if (setSpecified == null)
{
Tail.EmitRead(ctx, valueFrom);
return;
}
using (Compiler.Local loc = ctx.GetLocalWithValue(ExpectedType, valueFrom))
{
Tail.EmitRead(ctx, loc);
ctx.LoadAddress(loc, ExpectedType);
ctx.LoadValue(1); // true
ctx.EmitCall(setSpecified);
}
}
#endif
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 58836f822e85e2447817d187f3bcd5de
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,64 @@
#if !NO_RUNTIME
using System;
using System.Reflection;
using ProtoBuf.Meta;
namespace ProtoBuf.Serializers
{
sealed class NetObjectSerializer : IProtoSerializer
{
private readonly int key;
private readonly Type type;
private readonly BclHelpers.NetObjectOptions options;
public NetObjectSerializer(TypeModel model, Type type, int key, BclHelpers.NetObjectOptions options)
{
bool dynamicType = (options & BclHelpers.NetObjectOptions.DynamicType) != 0;
this.key = dynamicType ? -1 : key;
this.type = dynamicType ? model.MapType(typeof(object)) : type;
this.options = options;
}
public Type ExpectedType => type;
public bool ReturnsValue => true;
public bool RequiresOldValue => true;
public object Read(object value, ProtoReader source)
{
return BclHelpers.ReadNetObject(value, source, key, type == typeof(object) ? null : type, options);
}
public void Write(object value, ProtoWriter dest)
{
BclHelpers.WriteNetObject(value, dest, key, options);
}
#if FEAT_COMPILER
public void EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
ctx.LoadValue(valueFrom);
ctx.CastToObject(type);
ctx.LoadReaderWriter();
ctx.LoadValue(ctx.MapMetaKeyToCompiledKey(key));
if (type == ctx.MapType(typeof(object))) ctx.LoadNullRef();
else ctx.LoadValue(type);
ctx.LoadValue((int)options);
ctx.EmitCall(ctx.MapType(typeof(BclHelpers)).GetMethod("ReadNetObject"));
ctx.CastFromObject(type);
}
public void EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
ctx.LoadValue(valueFrom);
ctx.CastToObject(type);
ctx.LoadReaderWriter();
ctx.LoadValue(ctx.MapMetaKeyToCompiledKey(key));
ctx.LoadValue((int)options);
ctx.EmitCall(ctx.MapType(typeof(BclHelpers)).GetMethod("WriteNetObject"));
}
#endif
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: dd6edd815f76150449f39f7571679912
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,167 @@
#if !NO_RUNTIME
using System;
using System.Reflection;
using ProtoBuf.Meta;
namespace ProtoBuf.Serializers
{
sealed class NullDecorator : ProtoDecoratorBase
{
private readonly Type expectedType;
public const int Tag = 1;
public NullDecorator(TypeModel model, IProtoSerializer tail) : base(tail)
{
if (!tail.ReturnsValue)
throw new NotSupportedException("NullDecorator only supports implementations that return values");
Type tailType = tail.ExpectedType;
if (Helpers.IsValueType(tailType))
{
expectedType = model.MapType(typeof(Nullable<>)).MakeGenericType(tailType);
}
else
{
expectedType = tailType;
}
}
public override Type ExpectedType => expectedType;
public override bool ReturnsValue => true;
public override bool RequiresOldValue => true;
#if FEAT_COMPILER
protected override void EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
using (Compiler.Local oldValue = ctx.GetLocalWithValue(expectedType, valueFrom))
using (Compiler.Local token = new Compiler.Local(ctx, ctx.MapType(typeof(SubItemToken))))
using (Compiler.Local field = new Compiler.Local(ctx, ctx.MapType(typeof(int))))
{
ctx.LoadReaderWriter();
ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("StartSubItem"));
ctx.StoreValue(token);
Compiler.CodeLabel next = ctx.DefineLabel(), processField = ctx.DefineLabel(), end = ctx.DefineLabel();
ctx.MarkLabel(next);
ctx.EmitBasicRead("ReadFieldHeader", ctx.MapType(typeof(int)));
ctx.CopyValue();
ctx.StoreValue(field);
ctx.LoadValue(Tag); // = 1 - process
ctx.BranchIfEqual(processField, true);
ctx.LoadValue(field);
ctx.LoadValue(1); // < 1 - exit
ctx.BranchIfLess(end, false);
// default: skip
ctx.LoadReaderWriter();
ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("SkipField"));
ctx.Branch(next, true);
// process
ctx.MarkLabel(processField);
if (Tail.RequiresOldValue)
{
if (Helpers.IsValueType(expectedType))
{
ctx.LoadAddress(oldValue, expectedType);
ctx.EmitCall(expectedType.GetMethod("GetValueOrDefault", Helpers.EmptyTypes));
}
else
{
ctx.LoadValue(oldValue);
}
}
Tail.EmitRead(ctx, null);
// note we demanded always returns a value
if (Helpers.IsValueType(expectedType))
{
ctx.EmitCtor(expectedType, Tail.ExpectedType); // re-nullable<T> it
}
ctx.StoreValue(oldValue);
ctx.Branch(next, false);
// outro
ctx.MarkLabel(end);
ctx.LoadValue(token);
ctx.LoadReaderWriter();
ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("EndSubItem"));
ctx.LoadValue(oldValue); // load the old value
}
}
protected override void EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
using (Compiler.Local valOrNull = ctx.GetLocalWithValue(expectedType, valueFrom))
using (Compiler.Local token = new Compiler.Local(ctx, ctx.MapType(typeof(SubItemToken))))
{
ctx.LoadNullRef();
ctx.LoadReaderWriter();
ctx.EmitCall(ctx.MapType(typeof(ProtoWriter)).GetMethod("StartSubItem"));
ctx.StoreValue(token);
if (Helpers.IsValueType(expectedType))
{
ctx.LoadAddress(valOrNull, expectedType);
ctx.LoadValue(expectedType.GetProperty("HasValue"));
}
else
{
ctx.LoadValue(valOrNull);
}
Compiler.CodeLabel @end = ctx.DefineLabel();
ctx.BranchIfFalse(@end, false);
if (Helpers.IsValueType(expectedType))
{
ctx.LoadAddress(valOrNull, expectedType);
ctx.EmitCall(expectedType.GetMethod("GetValueOrDefault", Helpers.EmptyTypes));
}
else
{
ctx.LoadValue(valOrNull);
}
Tail.EmitWrite(ctx, null);
ctx.MarkLabel(@end);
ctx.LoadValue(token);
ctx.LoadReaderWriter();
ctx.EmitCall(ctx.MapType(typeof(ProtoWriter)).GetMethod("EndSubItem"));
}
}
#endif
public override object Read(object value, ProtoReader source)
{
SubItemToken tok = ProtoReader.StartSubItem(source);
int field;
while ((field = source.ReadFieldHeader()) > 0)
{
if (field == Tag)
{
value = Tail.Read(value, source);
}
else
{
source.SkipField();
}
}
ProtoReader.EndSubItem(tok, source);
return value;
}
public override void Write(object value, ProtoWriter dest)
{
SubItemToken token = ProtoWriter.StartSubItem(null, dest);
if (value != null)
{
Tail.Write(value, dest);
}
ProtoWriter.EndSubItem(token, dest);
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4728d79a9a96bde4097e4c599266a6e4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,111 @@
#if !NO_RUNTIME
using System;
using System.Net;
using ProtoBuf.Meta;
using System.Reflection;
namespace ProtoBuf.Serializers
{
sealed class ParseableSerializer : IProtoSerializer
{
private readonly MethodInfo parse;
public static ParseableSerializer TryCreate(Type type, TypeModel model)
{
if (type == null) throw new ArgumentNullException("type");
#if PORTABLE || COREFX || PROFILE259
MethodInfo method = null;
#if COREFX || PROFILE259
foreach (MethodInfo tmp in type.GetTypeInfo().GetDeclaredMethods("Parse"))
#else
foreach (MethodInfo tmp in type.GetMethods(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly))
#endif
{
ParameterInfo[] p;
if (tmp.Name == "Parse" && tmp.IsPublic && tmp.IsStatic && tmp.DeclaringType == type && (p = tmp.GetParameters()) != null && p.Length == 1 && p[0].ParameterType == typeof(string))
{
method = tmp;
break;
}
}
#else
MethodInfo method = type.GetMethod("Parse",
BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly,
null, new Type[] { model.MapType(typeof(string)) }, null);
#endif
if (method != null && method.ReturnType == type)
{
if (Helpers.IsValueType(type))
{
MethodInfo toString = GetCustomToString(type);
if (toString == null || toString.ReturnType != model.MapType(typeof(string))) return null; // need custom ToString, fools
}
return new ParseableSerializer(method);
}
return null;
}
private static MethodInfo GetCustomToString(Type type)
{
#if PORTABLE || COREFX || PROFILE259
MethodInfo method = Helpers.GetInstanceMethod(type, "ToString", Helpers.EmptyTypes);
if (method == null || !method.IsPublic || method.IsStatic || method.DeclaringType != type) return null;
return method;
#else
return type.GetMethod("ToString", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly,
null, Helpers.EmptyTypes, null);
#endif
}
private ParseableSerializer(MethodInfo parse)
{
this.parse = parse;
}
public Type ExpectedType => parse.DeclaringType;
bool IProtoSerializer.RequiresOldValue { get { return false; } }
bool IProtoSerializer.ReturnsValue { get { return true; } }
public object Read(object value, ProtoReader source)
{
Helpers.DebugAssert(value == null); // since replaces
return parse.Invoke(null, new object[] { source.ReadString() });
}
public void Write(object value, ProtoWriter dest)
{
ProtoWriter.WriteString(value.ToString(), dest);
}
#if FEAT_COMPILER
void IProtoSerializer.EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
Type type = ExpectedType;
if (Helpers.IsValueType(type))
{ // note that for structs, we've already asserted that a custom ToString
// exists; no need to handle the box/callvirt scenario
// force it to a variable if needed, so we can take the address
using (Compiler.Local loc = ctx.GetLocalWithValue(type, valueFrom))
{
ctx.LoadAddress(loc, type);
ctx.EmitCall(GetCustomToString(type));
}
}
else
{
ctx.EmitCall(ctx.MapType(typeof(object)).GetMethod("ToString"));
}
ctx.EmitBasicWrite("WriteString", valueFrom);
}
void IProtoSerializer.EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
ctx.EmitBasicRead("ReadString", ctx.MapType(typeof(string)));
ctx.EmitCall(parse);
}
#endif
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 562e4dd519901854fba22d511f594b82
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,167 @@
#if !NO_RUNTIME
using System;
using System.Reflection;
using ProtoBuf.Meta;
namespace ProtoBuf.Serializers
{
sealed class PropertyDecorator : ProtoDecoratorBase
{
public override Type ExpectedType => forType;
private readonly PropertyInfo property;
private readonly Type forType;
public override bool RequiresOldValue => true;
public override bool ReturnsValue => false;
private readonly bool readOptionsWriteValue;
private readonly MethodInfo shadowSetter;
public PropertyDecorator(TypeModel model, Type forType, PropertyInfo property, IProtoSerializer tail) : base(tail)
{
Helpers.DebugAssert(forType != null);
Helpers.DebugAssert(property != null);
this.forType = forType;
this.property = property;
SanityCheck(model, property, tail, out readOptionsWriteValue, true, true);
shadowSetter = GetShadowSetter(model, property);
}
private static void SanityCheck(TypeModel model, PropertyInfo property, IProtoSerializer tail, out bool writeValue, bool nonPublic, bool allowInternal)
{
if (property == null) throw new ArgumentNullException("property");
writeValue = tail.ReturnsValue && (GetShadowSetter(model, property) != null || (property.CanWrite && Helpers.GetSetMethod(property, nonPublic, allowInternal) != null));
if (!property.CanRead || Helpers.GetGetMethod(property, nonPublic, allowInternal) == null)
{
throw new InvalidOperationException("Cannot serialize property without a get accessor");
}
if (!writeValue && (!tail.RequiresOldValue || Helpers.IsValueType(tail.ExpectedType)))
{ // so we can't save the value, and the tail doesn't use it either... not helpful
// or: can't write the value, so the struct value will be lost
throw new InvalidOperationException("Cannot apply changes to property " + property.DeclaringType.FullName + "." + property.Name);
}
}
static MethodInfo GetShadowSetter(TypeModel model, PropertyInfo property)
{
#if COREFX
MethodInfo method = Helpers.GetInstanceMethod(property.DeclaringType.GetTypeInfo(), "Set" + property.Name, new Type[] { property.PropertyType });
#else
#if PROFILE259
Type reflectedType = property.DeclaringType;
#else
Type reflectedType = property.ReflectedType;
#endif
MethodInfo method = Helpers.GetInstanceMethod(reflectedType, "Set" + property.Name, new Type[] { property.PropertyType });
#endif
if (method == null || !method.IsPublic || method.ReturnType != model.MapType(typeof(void))) return null;
return method;
}
public override void Write(object value, ProtoWriter dest)
{
Helpers.DebugAssert(value != null);
value = property.GetValue(value, null);
if (value != null) Tail.Write(value, dest);
}
public override object Read(object value, ProtoReader source)
{
Helpers.DebugAssert(value != null);
object oldVal = Tail.RequiresOldValue ? property.GetValue(value, null) : null;
object newVal = Tail.Read(oldVal, source);
if (readOptionsWriteValue && newVal != null) // if the tail returns a null, intepret that as *no assign*
{
if (shadowSetter == null)
{
property.SetValue(value, newVal, null);
}
else
{
shadowSetter.Invoke(value, new object[] { newVal });
}
}
return null;
}
#if FEAT_COMPILER
protected override void EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
ctx.LoadAddress(valueFrom, ExpectedType);
ctx.LoadValue(property);
ctx.WriteNullCheckedTail(property.PropertyType, Tail, null);
}
protected override void EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
SanityCheck(ctx.Model, property, Tail, out bool writeValue, ctx.NonPublic, ctx.AllowInternal(property));
if (Helpers.IsValueType(ExpectedType) && valueFrom == null)
{
throw new InvalidOperationException("Attempt to mutate struct on the head of the stack; changes would be lost");
}
using (Compiler.Local loc = ctx.GetLocalWithValue(ExpectedType, valueFrom))
{
if (Tail.RequiresOldValue)
{
ctx.LoadAddress(loc, ExpectedType); // stack is: old-addr
ctx.LoadValue(property); // stack is: old-value
}
Type propertyType = property.PropertyType;
ctx.ReadNullCheckedTail(propertyType, Tail, null); // stack is [new-value]
if (writeValue)
{
using (Compiler.Local newVal = new Compiler.Local(ctx, property.PropertyType))
{
ctx.StoreValue(newVal); // stack is empty
Compiler.CodeLabel allDone = new Compiler.CodeLabel(); // <=== default structs
if (!Helpers.IsValueType(propertyType))
{ // if the tail returns a null, intepret that as *no assign*
allDone = ctx.DefineLabel();
ctx.LoadValue(newVal); // stack is: new-value
ctx.BranchIfFalse(@allDone, true); // stack is empty
}
// assign the value
ctx.LoadAddress(loc, ExpectedType); // parent-addr
ctx.LoadValue(newVal); // parent-obj|new-value
if (shadowSetter == null)
{
ctx.StoreValue(property); // empty
}
else
{
ctx.EmitCall(shadowSetter); // empty
}
if (!Helpers.IsValueType(propertyType))
{
ctx.MarkLabel(allDone);
}
}
}
else
{ // don't want return value; drop it if anything there
// stack is [new-value]
if (Tail.ReturnsValue) { ctx.DiscardValue(); }
}
}
}
#endif
internal static bool CanWrite(TypeModel model, MemberInfo member)
{
if (member == null) throw new ArgumentNullException(nameof(member));
if (member is PropertyInfo prop)
{
return prop.CanWrite || GetShadowSetter(model, prop) != null;
}
return member is FieldInfo; // fields are always writeable; anything else: JUST SAY NO!
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2b21fd2b2435fed4da28bd193e2ce80d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,24 @@
#if !NO_RUNTIME
using System;
namespace ProtoBuf.Serializers
{
abstract class ProtoDecoratorBase : IProtoSerializer
{
public abstract Type ExpectedType { get; }
protected readonly IProtoSerializer Tail;
protected ProtoDecoratorBase(IProtoSerializer tail) { this.Tail = tail; }
public abstract bool ReturnsValue { get; }
public abstract bool RequiresOldValue { get; }
public abstract void Write(object value, ProtoWriter dest);
public abstract object Read(object value, ProtoReader source);
#if FEAT_COMPILER
void IProtoSerializer.EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom) { EmitWrite(ctx, valueFrom); }
protected abstract void EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom);
void IProtoSerializer.EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom) { EmitRead(ctx, valueFrom); }
protected abstract void EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom);
#endif
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1e9afbb9465ade140ab0fcd217ea0b66
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,90 @@
#if !NO_RUNTIME
#if PORTABLE
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace ProtoBuf.Serializers
{
/// <summary>
/// Manipulates with uris via reflection rather than strongly typed objects.
/// This is because in PCLs, the Uri type may not match (WinRT uses Internal/Uri, .Net uses System/Uri)
/// </summary>
sealed class ReflectedUriDecorator : ProtoDecoratorBase
{
private readonly Type expectedType;
private readonly PropertyInfo absoluteUriProperty;
private readonly ConstructorInfo typeConstructor;
public ReflectedUriDecorator(Type type, ProtoBuf.Meta.TypeModel model, IProtoSerializer tail) : base(tail)
{
expectedType = type;
#if PROFILE259
absoluteUriProperty = expectedType.GetRuntimeProperty("AbsoluteUri");
IEnumerable<ConstructorInfo> constructors = expectedType.GetTypeInfo().DeclaredConstructors;
typeConstructor = null;
foreach(ConstructorInfo constructor in constructors)
{
ParameterInfo[] parameters = constructor.GetParameters();
ParameterInfo parameterFirst = parameters.FirstOrDefault();
Type stringType = typeof(string);
if (parameterFirst != null &&
parameterFirst.ParameterType == stringType)
{
typeConstructor = constructor;
break;
}
}
#else
absoluteUriProperty = expectedType.GetProperty("AbsoluteUri");
typeConstructor = expectedType.GetConstructor(new Type[] { typeof(string) });
#endif
}
public override Type ExpectedType { get { return expectedType; } }
public override bool RequiresOldValue { get { return false; } }
public override bool ReturnsValue { get { return true; } }
public override void Write(object value, ProtoWriter dest)
{
Tail.Write(absoluteUriProperty.GetValue(value, null), dest);
}
public override object Read(object value, ProtoReader source)
{
Helpers.DebugAssert(value == null); // not expecting incoming
string s = (string)Tail.Read(null, source);
return s.Length == 0 ? null : typeConstructor.Invoke(new object[] { s });
}
#if FEAT_COMPILER
protected override void EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
ctx.LoadValue(valueFrom);
ctx.LoadValue(absoluteUriProperty);
Tail.EmitWrite(ctx, null);
}
protected override void EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
Tail.EmitRead(ctx, valueFrom);
ctx.CopyValue();
Compiler.CodeLabel @nonEmpty = ctx.DefineLabel(), @end = ctx.DefineLabel();
ctx.LoadValue(typeof(string).GetProperty("Length"));
ctx.BranchIfTrue(@nonEmpty, true);
ctx.DiscardValue();
ctx.LoadNullRef();
ctx.Branch(@end, true);
ctx.MarkLabel(@nonEmpty);
ctx.EmitCtor(expectedType, ctx.MapType(typeof(string)));
ctx.MarkLabel(@end);
}
#endif
}
}
#endif
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6cf63470b1970d84ead637b9ee2bface
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,45 @@
#if !NO_RUNTIME
using System;
namespace ProtoBuf.Serializers
{
sealed class SByteSerializer : IProtoSerializer
{
static readonly Type expectedType = typeof(sbyte);
public SByteSerializer(ProtoBuf.Meta.TypeModel model)
{
}
public Type ExpectedType => expectedType;
bool IProtoSerializer.RequiresOldValue => false;
bool IProtoSerializer.ReturnsValue => true;
public object Read(object value, ProtoReader source)
{
Helpers.DebugAssert(value == null); // since replaces
return source.ReadSByte();
}
public void Write(object value, ProtoWriter dest)
{
ProtoWriter.WriteSByte((sbyte)value, dest);
}
#if FEAT_COMPILER
void IProtoSerializer.EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
ctx.EmitBasicWrite("WriteSByte", valueFrom);
}
void IProtoSerializer.EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
ctx.EmitBasicRead("ReadSByte", ExpectedType);
}
#endif
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 09de0f3e56d4834428cb8ba75929d216
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,45 @@
#if !NO_RUNTIME
using System;
using ProtoBuf.Meta;
namespace ProtoBuf.Serializers
{
sealed class SingleSerializer : IProtoSerializer
{
static readonly Type expectedType = typeof(float);
public Type ExpectedType { get { return expectedType; } }
public SingleSerializer(TypeModel model)
{
}
bool IProtoSerializer.RequiresOldValue => false;
bool IProtoSerializer.ReturnsValue => true;
public object Read(object value, ProtoReader source)
{
Helpers.DebugAssert(value == null); // since replaces
return source.ReadSingle();
}
public void Write(object value, ProtoWriter dest)
{
ProtoWriter.WriteSingle((float)value, dest);
}
#if FEAT_COMPILER
void IProtoSerializer.EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
ctx.EmitBasicWrite("WriteSingle", valueFrom);
}
void IProtoSerializer.EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
ctx.EmitBasicRead("ReadSingle", ExpectedType);
}
#endif
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9b89c111cae2d81469987d208a41af4b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,41 @@
#if !NO_RUNTIME
using System;
namespace ProtoBuf.Serializers
{
sealed class StringSerializer : IProtoSerializer
{
static readonly Type expectedType = typeof(string);
public StringSerializer(ProtoBuf.Meta.TypeModel model)
{
}
public Type ExpectedType => expectedType;
public void Write(object value, ProtoWriter dest)
{
ProtoWriter.WriteString((string)value, dest);
}
bool IProtoSerializer.RequiresOldValue => false;
bool IProtoSerializer.ReturnsValue => true;
public object Read(object value, ProtoReader source)
{
Helpers.DebugAssert(value == null); // since replaces
return source.ReadString();
}
#if FEAT_COMPILER
void IProtoSerializer.EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
ctx.EmitBasicWrite("WriteString", valueFrom);
}
void IProtoSerializer.EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
ctx.EmitBasicRead("ReadString", ExpectedType);
}
#endif
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 80c8f3efc5f697845b352b56780105ae
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,138 @@
#if !NO_RUNTIME
using System;
using ProtoBuf.Meta;
#if FEAT_COMPILER
using System.Reflection.Emit;
#endif
namespace ProtoBuf.Serializers
{
sealed class SubItemSerializer : IProtoTypeSerializer
{
bool IProtoTypeSerializer.HasCallbacks(TypeModel.CallbackType callbackType)
{
return ((IProtoTypeSerializer)proxy.Serializer).HasCallbacks(callbackType);
}
bool IProtoTypeSerializer.CanCreateInstance()
{
return ((IProtoTypeSerializer)proxy.Serializer).CanCreateInstance();
}
#if FEAT_COMPILER
void IProtoTypeSerializer.EmitCallback(Compiler.CompilerContext ctx, Compiler.Local valueFrom, TypeModel.CallbackType callbackType)
{
((IProtoTypeSerializer)proxy.Serializer).EmitCallback(ctx, valueFrom, callbackType);
}
void IProtoTypeSerializer.EmitCreateInstance(Compiler.CompilerContext ctx)
{
((IProtoTypeSerializer)proxy.Serializer).EmitCreateInstance(ctx);
}
#endif
void IProtoTypeSerializer.Callback(object value, TypeModel.CallbackType callbackType, SerializationContext context)
{
((IProtoTypeSerializer)proxy.Serializer).Callback(value, callbackType, context);
}
object IProtoTypeSerializer.CreateInstance(ProtoReader source)
{
return ((IProtoTypeSerializer)proxy.Serializer).CreateInstance(source);
}
private readonly int key;
private readonly Type type;
private readonly ISerializerProxy proxy;
private readonly bool recursionCheck;
public SubItemSerializer(Type type, int key, ISerializerProxy proxy, bool recursionCheck)
{
this.type = type ?? throw new ArgumentNullException(nameof(type));
this.proxy = proxy ?? throw new ArgumentNullException(nameof(proxy));
this.key = key;
this.recursionCheck = recursionCheck;
}
Type IProtoSerializer.ExpectedType => type;
bool IProtoSerializer.RequiresOldValue => true;
bool IProtoSerializer.ReturnsValue => true;
void IProtoSerializer.Write(object value, ProtoWriter dest)
{
if (recursionCheck)
{
ProtoWriter.WriteObject(value, key, dest);
}
else
{
ProtoWriter.WriteRecursionSafeObject(value, key, dest);
}
}
object IProtoSerializer.Read(object value, ProtoReader source)
{
return ProtoReader.ReadObject(value, key, source);
}
#if FEAT_COMPILER
bool EmitDedicatedMethod(Compiler.CompilerContext ctx, Compiler.Local valueFrom, bool read)
{
MethodBuilder method = ctx.GetDedicatedMethod(key, read);
if (method == null) return false;
using (Compiler.Local token = new ProtoBuf.Compiler.Local(ctx, ctx.MapType(typeof(SubItemToken))))
{
Type rwType = ctx.MapType(read ? typeof(ProtoReader) : typeof(ProtoWriter));
ctx.LoadValue(valueFrom);
if (!read) // write requires the object for StartSubItem; read doesn't
{ // (if recursion-check is disabled [subtypes] then null is fine too)
if (Helpers.IsValueType(type) || !recursionCheck) { ctx.LoadNullRef(); }
else { ctx.CopyValue(); }
}
ctx.LoadReaderWriter();
ctx.EmitCall(Helpers.GetStaticMethod(rwType, "StartSubItem",
read ? new Type[] { rwType } : new Type[] { ctx.MapType(typeof(object)), rwType }));
ctx.StoreValue(token);
// note: value already on the stack
ctx.LoadReaderWriter();
ctx.EmitCall(method);
// handle inheritance (we will be calling the *base* version of things,
// but we expect Read to return the "type" type)
if (read && type != method.ReturnType) ctx.Cast(this.type);
ctx.LoadValue(token);
ctx.LoadReaderWriter();
ctx.EmitCall(Helpers.GetStaticMethod(rwType, "EndSubItem", new Type[] { ctx.MapType(typeof(SubItemToken)), rwType }));
}
return true;
}
void IProtoSerializer.EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
if (!EmitDedicatedMethod(ctx, valueFrom, false))
{
ctx.LoadValue(valueFrom);
if (Helpers.IsValueType(type)) ctx.CastToObject(type);
ctx.LoadValue(ctx.MapMetaKeyToCompiledKey(key)); // re-map for formality, but would expect identical, else dedicated method
ctx.LoadReaderWriter();
ctx.EmitCall(Helpers.GetStaticMethod(ctx.MapType(typeof(ProtoWriter)), recursionCheck ? "WriteObject" : "WriteRecursionSafeObject", new Type[] { ctx.MapType(typeof(object)), ctx.MapType(typeof(int)), ctx.MapType(typeof(ProtoWriter)) }));
}
}
void IProtoSerializer.EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
if (!EmitDedicatedMethod(ctx, valueFrom, true))
{
ctx.LoadValue(valueFrom);
if (Helpers.IsValueType(type)) ctx.CastToObject(type);
ctx.LoadValue(ctx.MapMetaKeyToCompiledKey(key)); // re-map for formality, but would expect identical, else dedicated method
ctx.LoadReaderWriter();
ctx.EmitCall(Helpers.GetStaticMethod(ctx.MapType(typeof(ProtoReader)), "ReadObject"));
ctx.CastFromObject(type);
}
}
#endif
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6d29a14abe6c62349930c948a6d438c4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,157 @@
#if !NO_RUNTIME
using System;
using ProtoBuf.Meta;
using System.Reflection;
namespace ProtoBuf.Serializers
{
sealed class SurrogateSerializer : IProtoTypeSerializer
{
bool IProtoTypeSerializer.HasCallbacks(ProtoBuf.Meta.TypeModel.CallbackType callbackType) { return false; }
#if FEAT_COMPILER
void IProtoTypeSerializer.EmitCallback(Compiler.CompilerContext ctx, Compiler.Local valueFrom, ProtoBuf.Meta.TypeModel.CallbackType callbackType) { }
void IProtoTypeSerializer.EmitCreateInstance(Compiler.CompilerContext ctx) { throw new NotSupportedException(); }
#endif
bool IProtoTypeSerializer.CanCreateInstance() => false;
object IProtoTypeSerializer.CreateInstance(ProtoReader source) => throw new NotSupportedException();
void IProtoTypeSerializer.Callback(object value, ProtoBuf.Meta.TypeModel.CallbackType callbackType, SerializationContext context) { }
public bool ReturnsValue => false;
public bool RequiresOldValue => true;
public Type ExpectedType => forType;
private readonly Type forType, declaredType;
private readonly MethodInfo toTail, fromTail;
IProtoTypeSerializer rootTail;
public SurrogateSerializer(TypeModel model, Type forType, Type declaredType, IProtoTypeSerializer rootTail)
{
Helpers.DebugAssert(forType != null, "forType");
Helpers.DebugAssert(declaredType != null, "declaredType");
Helpers.DebugAssert(rootTail != null, "rootTail");
Helpers.DebugAssert(rootTail.RequiresOldValue, "RequiresOldValue");
Helpers.DebugAssert(!rootTail.ReturnsValue, "ReturnsValue");
Helpers.DebugAssert(declaredType == rootTail.ExpectedType || Helpers.IsSubclassOf(declaredType, rootTail.ExpectedType));
this.forType = forType;
this.declaredType = declaredType;
this.rootTail = rootTail;
toTail = GetConversion(model, true);
fromTail = GetConversion(model, false);
}
private static bool HasCast(TypeModel model, Type type, Type from, Type to, out MethodInfo op)
{
#if PROFILE259
System.Collections.Generic.List<MethodInfo> list = new System.Collections.Generic.List<MethodInfo>();
foreach (var item in type.GetRuntimeMethods())
{
if (item.IsStatic) list.Add(item);
}
MethodInfo[] found = list.ToArray();
#else
const BindingFlags flags = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
MethodInfo[] found = type.GetMethods(flags);
#endif
ParameterInfo[] paramTypes;
Type convertAttributeType = null;
for (int i = 0; i < found.Length; i++)
{
MethodInfo m = found[i];
if (m.ReturnType != to) continue;
paramTypes = m.GetParameters();
if (paramTypes.Length == 1 && paramTypes[0].ParameterType == from)
{
if (convertAttributeType == null)
{
convertAttributeType = model.MapType(typeof(ProtoConverterAttribute), false);
if (convertAttributeType == null)
{ // attribute isn't defined in the source assembly: stop looking
break;
}
}
if (m.IsDefined(convertAttributeType, true))
{
op = m;
return true;
}
}
}
for (int i = 0; i < found.Length; i++)
{
MethodInfo m = found[i];
if ((m.Name != "op_Implicit" && m.Name != "op_Explicit") || m.ReturnType != to)
{
continue;
}
paramTypes = m.GetParameters();
if (paramTypes.Length == 1 && paramTypes[0].ParameterType == from)
{
op = m;
return true;
}
}
op = null;
return false;
}
public MethodInfo GetConversion(TypeModel model, bool toTail)
{
Type to = toTail ? declaredType : forType;
Type from = toTail ? forType : declaredType;
MethodInfo op;
if (HasCast(model, declaredType, from, to, out op) || HasCast(model, forType, from, to, out op))
{
return op;
}
throw new InvalidOperationException("No suitable conversion operator found for surrogate: " +
forType.FullName + " / " + declaredType.FullName);
}
public void Write(object value, ProtoWriter writer)
{
rootTail.Write(toTail.Invoke(null, new object[] { value }), writer);
}
public object Read(object value, ProtoReader source)
{
// convert the incoming value
object[] args = { value };
value = toTail.Invoke(null, args);
// invoke the tail and convert the outgoing value
args[0] = rootTail.Read(value, source);
return fromTail.Invoke(null, args);
}
#if FEAT_COMPILER
void IProtoSerializer.EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
Helpers.DebugAssert(valueFrom != null); // don't support stack-head for this
using (Compiler.Local converted = new Compiler.Local(ctx, declaredType)) // declare/re-use local
{
ctx.LoadValue(valueFrom); // load primary onto stack
ctx.EmitCall(toTail); // static convert op, primary-to-surrogate
ctx.StoreValue(converted); // store into surrogate local
rootTail.EmitRead(ctx, converted); // downstream processing against surrogate local
ctx.LoadValue(converted); // load from surrogate local
ctx.EmitCall(fromTail); // static convert op, surrogate-to-primary
ctx.StoreValue(valueFrom); // store back into primary
}
}
void IProtoSerializer.EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
ctx.LoadValue(valueFrom);
ctx.EmitCall(toTail);
rootTail.EmitWrite(ctx, null);
}
#endif
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 517911a690de2bd4c97e81f35104a81a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,46 @@
using System;
#if !NO_RUNTIME
namespace ProtoBuf.Serializers
{
sealed class SystemTypeSerializer : IProtoSerializer
{
static readonly Type expectedType = typeof(Type);
public SystemTypeSerializer(ProtoBuf.Meta.TypeModel model)
{
}
public Type ExpectedType => expectedType;
void IProtoSerializer.Write(object value, ProtoWriter dest)
{
ProtoWriter.WriteType((Type)value, dest);
}
object IProtoSerializer.Read(object value, ProtoReader source)
{
Helpers.DebugAssert(value == null); // since replaces
return source.ReadType();
}
bool IProtoSerializer.RequiresOldValue => false;
bool IProtoSerializer.ReturnsValue => true;
#if FEAT_COMPILER
void IProtoSerializer.EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
ctx.EmitBasicWrite("WriteType", valueFrom);
}
void IProtoSerializer.EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
ctx.EmitBasicRead("ReadType", ExpectedType);
}
#endif
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f3b3869287521554a816dba994928f83
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,108 @@
#if !NO_RUNTIME
using System;
using System.Reflection;
using ProtoBuf.Meta;
namespace ProtoBuf.Serializers
{
sealed class TagDecorator : ProtoDecoratorBase, IProtoTypeSerializer
{
public bool HasCallbacks(TypeModel.CallbackType callbackType)
{
IProtoTypeSerializer pts = Tail as IProtoTypeSerializer;
return pts != null && pts.HasCallbacks(callbackType);
}
public bool CanCreateInstance()
{
IProtoTypeSerializer pts = Tail as IProtoTypeSerializer;
return pts != null && pts.CanCreateInstance();
}
public object CreateInstance(ProtoReader source)
{
return ((IProtoTypeSerializer)Tail).CreateInstance(source);
}
public void Callback(object value, TypeModel.CallbackType callbackType, SerializationContext context)
{
if (Tail is IProtoTypeSerializer pts)
{
pts.Callback(value, callbackType, context);
}
}
#if FEAT_COMPILER
public void EmitCallback(Compiler.CompilerContext ctx, Compiler.Local valueFrom, TypeModel.CallbackType callbackType)
{
// we only expect this to be invoked if HasCallbacks returned true, so implicitly Tail
// **must** be of the correct type
((IProtoTypeSerializer)Tail).EmitCallback(ctx, valueFrom, callbackType);
}
public void EmitCreateInstance(Compiler.CompilerContext ctx)
{
((IProtoTypeSerializer)Tail).EmitCreateInstance(ctx);
}
#endif
public override Type ExpectedType => Tail.ExpectedType;
public TagDecorator(int fieldNumber, WireType wireType, bool strict, IProtoSerializer tail)
: base(tail)
{
this.fieldNumber = fieldNumber;
this.wireType = wireType;
this.strict = strict;
}
public override bool RequiresOldValue => Tail.RequiresOldValue;
public override bool ReturnsValue => Tail.ReturnsValue;
private readonly bool strict;
private readonly int fieldNumber;
private readonly WireType wireType;
private bool NeedsHint => ((int)wireType & ~7) != 0;
public override object Read(object value, ProtoReader source)
{
Helpers.DebugAssert(fieldNumber == source.FieldNumber);
if (strict) { source.Assert(wireType); }
else if (NeedsHint) { source.Hint(wireType); }
return Tail.Read(value, source);
}
public override void Write(object value, ProtoWriter dest)
{
ProtoWriter.WriteFieldHeader(fieldNumber, wireType, dest);
Tail.Write(value, dest);
}
#if FEAT_COMPILER
protected override void EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
ctx.LoadValue((int)fieldNumber);
ctx.LoadValue((int)wireType);
ctx.LoadReaderWriter();
ctx.EmitCall(ctx.MapType(typeof(ProtoWriter)).GetMethod("WriteFieldHeader"));
Tail.EmitWrite(ctx, valueFrom);
}
protected override void EmitRead(ProtoBuf.Compiler.CompilerContext ctx, ProtoBuf.Compiler.Local valueFrom)
{
if (strict || NeedsHint)
{
ctx.LoadReaderWriter();
ctx.LoadValue((int)wireType);
ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod(strict ? "Assert" : "Hint"));
}
Tail.EmitRead(ctx, valueFrom);
}
#endif
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f807f0cbd3358f6479b78ab47c89ad48
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,63 @@
#if !NO_RUNTIME
using System;
namespace ProtoBuf.Serializers
{
sealed class TimeSpanSerializer : IProtoSerializer
{
static readonly Type expectedType = typeof(TimeSpan);
private readonly bool wellKnown;
public TimeSpanSerializer(DataFormat dataFormat, ProtoBuf.Meta.TypeModel model)
{
wellKnown = dataFormat == DataFormat.WellKnown;
}
public Type ExpectedType => expectedType;
bool IProtoSerializer.RequiresOldValue => false;
bool IProtoSerializer.ReturnsValue => true;
public object Read(object value, ProtoReader source)
{
if (wellKnown)
{
return BclHelpers.ReadDuration(source);
}
else
{
Helpers.DebugAssert(value == null); // since replaces
return BclHelpers.ReadTimeSpan(source);
}
}
public void Write(object value, ProtoWriter dest)
{
if (wellKnown)
{
BclHelpers.WriteDuration((TimeSpan)value, dest);
}
else
{
BclHelpers.WriteTimeSpan((TimeSpan)value, dest);
}
}
#if FEAT_COMPILER
void IProtoSerializer.EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
ctx.EmitWrite(ctx.MapType(typeof(BclHelpers)),
wellKnown ? nameof(BclHelpers.WriteDuration) : nameof(BclHelpers.WriteTimeSpan), valueFrom);
}
void IProtoSerializer.EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
if (wellKnown) ctx.LoadValue(valueFrom);
ctx.EmitBasicRead(ctx.MapType(typeof(BclHelpers)),
wellKnown ? nameof(BclHelpers.ReadDuration) : nameof(BclHelpers.ReadTimeSpan),
ExpectedType);
}
#endif
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 88ce40638421a9d4abd295d84d1991e8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,339 @@
#if !NO_RUNTIME
using System;
using System.Reflection;
using ProtoBuf.Meta;
namespace ProtoBuf.Serializers
{
sealed class TupleSerializer : IProtoTypeSerializer
{
private readonly MemberInfo[] members;
private readonly ConstructorInfo ctor;
private IProtoSerializer[] tails;
public TupleSerializer(RuntimeTypeModel model, ConstructorInfo ctor, MemberInfo[] members)
{
this.ctor = ctor ?? throw new ArgumentNullException(nameof(ctor));
this.members = members ?? throw new ArgumentNullException(nameof(members));
this.tails = new IProtoSerializer[members.Length];
ParameterInfo[] parameters = ctor.GetParameters();
for (int i = 0; i < members.Length; i++)
{
WireType wireType;
Type finalType = parameters[i].ParameterType;
Type itemType = null, defaultType = null;
MetaType.ResolveListTypes(model, finalType, ref itemType, ref defaultType);
Type tmp = itemType == null ? finalType : itemType;
bool asReference = false;
int typeIndex = model.FindOrAddAuto(tmp, false, true, false);
if (typeIndex >= 0)
{
asReference = model[tmp].AsReferenceDefault;
}
IProtoSerializer tail = ValueMember.TryGetCoreSerializer(model, DataFormat.Default, tmp, out wireType, asReference, false, false, true), serializer;
if (tail == null)
{
throw new InvalidOperationException("No serializer defined for type: " + tmp.FullName);
}
tail = new TagDecorator(i + 1, wireType, false, tail);
if (itemType == null)
{
serializer = tail;
}
else
{
if (finalType.IsArray)
{
serializer = new ArrayDecorator(model, tail, i + 1, false, wireType, finalType, false, false);
}
else
{
serializer = ListDecorator.Create(model, finalType, defaultType, tail, i + 1, false, wireType, true, false, false);
}
}
tails[i] = serializer;
}
}
public bool HasCallbacks(Meta.TypeModel.CallbackType callbackType)
{
return false;
}
#if FEAT_COMPILER
public void EmitCallback(Compiler.CompilerContext ctx, Compiler.Local valueFrom, Meta.TypeModel.CallbackType callbackType) { }
#endif
public Type ExpectedType => ctor.DeclaringType;
void IProtoTypeSerializer.Callback(object value, Meta.TypeModel.CallbackType callbackType, SerializationContext context) { }
object IProtoTypeSerializer.CreateInstance(ProtoReader source) { throw new NotSupportedException(); }
private object GetValue(object obj, int index)
{
PropertyInfo prop;
FieldInfo field;
if ((prop = members[index] as PropertyInfo) != null)
{
if (obj == null)
return Helpers.IsValueType(prop.PropertyType) ? Activator.CreateInstance(prop.PropertyType) : null;
return prop.GetValue(obj, null);
}
else if ((field = members[index] as FieldInfo) != null)
{
if (obj == null)
return Helpers.IsValueType(field.FieldType) ? Activator.CreateInstance(field.FieldType) : null;
return field.GetValue(obj);
}
else
{
throw new InvalidOperationException();
}
}
public object Read(object value, ProtoReader source)
{
object[] values = new object[members.Length];
bool invokeCtor = false;
if (value == null)
{
invokeCtor = true;
}
for (int i = 0; i < values.Length; i++)
values[i] = GetValue(value, i);
int field;
while ((field = source.ReadFieldHeader()) > 0)
{
invokeCtor = true;
if (field <= tails.Length)
{
IProtoSerializer tail = tails[field - 1];
values[field - 1] = tails[field - 1].Read(tail.RequiresOldValue ? values[field - 1] : null, source);
}
else
{
source.SkipField();
}
}
return invokeCtor ? ctor.Invoke(values) : value;
}
public void Write(object value, ProtoWriter dest)
{
for (int i = 0; i < tails.Length; i++)
{
object val = GetValue(value, i);
if (val != null) tails[i].Write(val, dest);
}
}
public bool RequiresOldValue => true;
public bool ReturnsValue => false;
Type GetMemberType(int index)
{
Type result = Helpers.GetMemberType(members[index]);
if (result == null) throw new InvalidOperationException();
return result;
}
bool IProtoTypeSerializer.CanCreateInstance() { return false; }
#if FEAT_COMPILER
public void EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
using (Compiler.Local loc = ctx.GetLocalWithValue(ctor.DeclaringType, valueFrom))
{
for (int i = 0; i < tails.Length; i++)
{
Type type = GetMemberType(i);
ctx.LoadAddress(loc, ExpectedType);
if (members[i] is FieldInfo)
{
ctx.LoadValue((FieldInfo)members[i]);
}
else if (members[i] is PropertyInfo)
{
ctx.LoadValue((PropertyInfo)members[i]);
}
ctx.WriteNullCheckedTail(type, tails[i], null);
}
}
}
void IProtoTypeSerializer.EmitCreateInstance(Compiler.CompilerContext ctx) { throw new NotSupportedException(); }
public void EmitRead(Compiler.CompilerContext ctx, Compiler.Local incoming)
{
using (Compiler.Local objValue = ctx.GetLocalWithValue(ExpectedType, incoming))
{
Compiler.Local[] locals = new Compiler.Local[members.Length];
try
{
for (int i = 0; i < locals.Length; i++)
{
Type type = GetMemberType(i);
bool store = true;
locals[i] = new Compiler.Local(ctx, type);
if (!Helpers.IsValueType(ExpectedType))
{
// value-types always read the old value
if (Helpers.IsValueType(type))
{
switch (Helpers.GetTypeCode(type))
{
case ProtoTypeCode.Boolean:
case ProtoTypeCode.Byte:
case ProtoTypeCode.Int16:
case ProtoTypeCode.Int32:
case ProtoTypeCode.SByte:
case ProtoTypeCode.UInt16:
case ProtoTypeCode.UInt32:
ctx.LoadValue(0);
break;
case ProtoTypeCode.Int64:
case ProtoTypeCode.UInt64:
ctx.LoadValue(0L);
break;
case ProtoTypeCode.Single:
ctx.LoadValue(0.0F);
break;
case ProtoTypeCode.Double:
ctx.LoadValue(0.0D);
break;
case ProtoTypeCode.Decimal:
ctx.LoadValue(0M);
break;
case ProtoTypeCode.Guid:
ctx.LoadValue(Guid.Empty);
break;
default:
ctx.LoadAddress(locals[i], type);
ctx.EmitCtor(type);
store = false;
break;
}
}
else
{
ctx.LoadNullRef();
}
if (store)
{
ctx.StoreValue(locals[i]);
}
}
}
Compiler.CodeLabel skipOld = Helpers.IsValueType(ExpectedType)
? new Compiler.CodeLabel()
: ctx.DefineLabel();
if (!Helpers.IsValueType(ExpectedType))
{
ctx.LoadAddress(objValue, ExpectedType);
ctx.BranchIfFalse(skipOld, false);
}
for (int i = 0; i < members.Length; i++)
{
ctx.LoadAddress(objValue, ExpectedType);
if (members[i] is FieldInfo)
{
ctx.LoadValue((FieldInfo)members[i]);
}
else if (members[i] is PropertyInfo)
{
ctx.LoadValue((PropertyInfo)members[i]);
}
ctx.StoreValue(locals[i]);
}
if (!Helpers.IsValueType(ExpectedType)) ctx.MarkLabel(skipOld);
using (Compiler.Local fieldNumber = new Compiler.Local(ctx, ctx.MapType(typeof(int))))
{
Compiler.CodeLabel @continue = ctx.DefineLabel(),
processField = ctx.DefineLabel(),
notRecognised = ctx.DefineLabel();
ctx.Branch(@continue, false);
Compiler.CodeLabel[] handlers = new Compiler.CodeLabel[members.Length];
for (int i = 0; i < members.Length; i++)
{
handlers[i] = ctx.DefineLabel();
}
ctx.MarkLabel(processField);
ctx.LoadValue(fieldNumber);
ctx.LoadValue(1);
ctx.Subtract(); // jump-table is zero-based
ctx.Switch(handlers);
// and the default:
ctx.Branch(notRecognised, false);
for (int i = 0; i < handlers.Length; i++)
{
ctx.MarkLabel(handlers[i]);
IProtoSerializer tail = tails[i];
Compiler.Local oldValIfNeeded = tail.RequiresOldValue ? locals[i] : null;
ctx.ReadNullCheckedTail(locals[i].Type, tail, oldValIfNeeded);
if (tail.ReturnsValue)
{
if (Helpers.IsValueType(locals[i].Type))
{
ctx.StoreValue(locals[i]);
}
else
{
Compiler.CodeLabel hasValue = ctx.DefineLabel(), allDone = ctx.DefineLabel();
ctx.CopyValue();
ctx.BranchIfTrue(hasValue, true); // interpret null as "don't assign"
ctx.DiscardValue();
ctx.Branch(allDone, true);
ctx.MarkLabel(hasValue);
ctx.StoreValue(locals[i]);
ctx.MarkLabel(allDone);
}
}
ctx.Branch(@continue, false);
}
ctx.MarkLabel(notRecognised);
ctx.LoadReaderWriter();
ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("SkipField"));
ctx.MarkLabel(@continue);
ctx.EmitBasicRead("ReadFieldHeader", ctx.MapType(typeof(int)));
ctx.CopyValue();
ctx.StoreValue(fieldNumber);
ctx.LoadValue(0);
ctx.BranchIfGreater(processField, false);
}
for (int i = 0; i < locals.Length; i++)
{
ctx.LoadValue(locals[i]);
}
ctx.EmitCtor(ctor);
ctx.StoreValue(objValue);
}
finally
{
for (int i = 0; i < locals.Length; i++)
{
if (locals[i] != null)
locals[i].Dispose(); // release for re-use
}
}
}
}
#endif
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7e57d0cd813299f40a1a32236d9931a9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,798 @@
#if !NO_RUNTIME
using System;
using ProtoBuf.Meta;
#if FEAT_COMPILER
#endif
using System.Reflection;
namespace ProtoBuf.Serializers
{
sealed class TypeSerializer : IProtoTypeSerializer
{
public bool HasCallbacks(TypeModel.CallbackType callbackType)
{
if (callbacks != null && callbacks[callbackType] != null) return true;
for (int i = 0; i < serializers.Length; i++)
{
if (serializers[i].ExpectedType != forType && ((IProtoTypeSerializer)serializers[i]).HasCallbacks(callbackType)) return true;
}
return false;
}
private readonly Type forType, constructType;
#if COREFX || PROFILE259
private readonly TypeInfo typeInfo;
#endif
public Type ExpectedType { get { return forType; } }
private readonly IProtoSerializer[] serializers;
private readonly int[] fieldNumbers;
private readonly bool isRootType, useConstructor, isExtensible, hasConstructor;
private readonly CallbackSet callbacks;
private readonly MethodInfo[] baseCtorCallbacks;
private readonly MethodInfo factory;
public TypeSerializer(TypeModel model, Type forType, int[] fieldNumbers, IProtoSerializer[] serializers, MethodInfo[] baseCtorCallbacks, bool isRootType, bool useConstructor, CallbackSet callbacks, Type constructType, MethodInfo factory)
{
Helpers.DebugAssert(forType != null);
Helpers.DebugAssert(fieldNumbers != null);
Helpers.DebugAssert(serializers != null);
Helpers.DebugAssert(fieldNumbers.Length == serializers.Length);
Helpers.Sort(fieldNumbers, serializers);
bool hasSubTypes = false;
for (int i = 0; i < fieldNumbers.Length; i++)
{
if (i != 0 && fieldNumbers[i] == fieldNumbers[i - 1]) throw new InvalidOperationException("Duplicate field-number detected; " +
fieldNumbers[i].ToString() + " on: " + forType.FullName);
if (!hasSubTypes && serializers[i].ExpectedType != forType)
{
hasSubTypes = true;
}
}
this.forType = forType;
this.factory = factory;
#if COREFX || PROFILE259
this.typeInfo = forType.GetTypeInfo();
#endif
if (constructType == null)
{
constructType = forType;
}
else
{
#if COREFX || PROFILE259
if (!typeInfo.IsAssignableFrom(constructType.GetTypeInfo()))
#else
if (!forType.IsAssignableFrom(constructType))
#endif
{
throw new InvalidOperationException(forType.FullName + " cannot be assigned from " + constructType.FullName);
}
}
this.constructType = constructType;
this.serializers = serializers;
this.fieldNumbers = fieldNumbers;
this.callbacks = callbacks;
this.isRootType = isRootType;
this.useConstructor = useConstructor;
if (baseCtorCallbacks != null && baseCtorCallbacks.Length == 0) baseCtorCallbacks = null;
this.baseCtorCallbacks = baseCtorCallbacks;
if (Helpers.GetUnderlyingType(forType) != null)
{
throw new ArgumentException("Cannot create a TypeSerializer for nullable types", "forType");
}
#if COREFX || PROFILE259
if (iextensible.IsAssignableFrom(typeInfo))
{
if (typeInfo.IsValueType || !isRootType || hasSubTypes)
#else
if (model.MapType(iextensible).IsAssignableFrom(forType))
{
if (forType.IsValueType || !isRootType || hasSubTypes)
#endif
{
throw new NotSupportedException("IExtensible is not supported in structs or classes with inheritance");
}
isExtensible = true;
}
#if COREFX || PROFILE259
TypeInfo constructTypeInfo = constructType.GetTypeInfo();
hasConstructor = !constructTypeInfo.IsAbstract && Helpers.GetConstructor(constructTypeInfo, Helpers.EmptyTypes, true) != null;
#else
hasConstructor = !constructType.IsAbstract && Helpers.GetConstructor(constructType, Helpers.EmptyTypes, true) != null;
#endif
if (constructType != forType && useConstructor && !hasConstructor)
{
throw new ArgumentException("The supplied default implementation cannot be created: " + constructType.FullName, "constructType");
}
}
#if COREFX || PROFILE259
private static readonly TypeInfo iextensible = typeof(IExtensible).GetTypeInfo();
#else
private static readonly System.Type iextensible = typeof(IExtensible);
#endif
private bool CanHaveInheritance
{
get
{
#if COREFX || PROFILE259
return (typeInfo.IsClass || typeInfo.IsInterface) && !typeInfo.IsSealed;
#else
return (forType.IsClass || forType.IsInterface) && !forType.IsSealed;
#endif
}
}
bool IProtoTypeSerializer.CanCreateInstance() { return true; }
object IProtoTypeSerializer.CreateInstance(ProtoReader source)
{
return CreateInstance(source, false);
}
public void Callback(object value, TypeModel.CallbackType callbackType, SerializationContext context)
{
if (callbacks != null) InvokeCallback(callbacks[callbackType], value, context);
IProtoTypeSerializer ser = (IProtoTypeSerializer)GetMoreSpecificSerializer(value);
if (ser != null) ser.Callback(value, callbackType, context);
}
private IProtoSerializer GetMoreSpecificSerializer(object value)
{
if (!CanHaveInheritance) return null;
Type actualType = value.GetType();
if (actualType == forType) return null;
for (int i = 0; i < serializers.Length; i++)
{
IProtoSerializer ser = serializers[i];
if (ser.ExpectedType != forType && Helpers.IsAssignableFrom(ser.ExpectedType, actualType))
{
return ser;
}
}
if (actualType == constructType) return null; // needs to be last in case the default concrete type is also a known sub-type
TypeModel.ThrowUnexpectedSubtype(forType, actualType); // might throw (if not a proxy)
return null;
}
public void Write(object value, ProtoWriter dest)
{
if (isRootType) Callback(value, TypeModel.CallbackType.BeforeSerialize, dest.Context);
// write inheritance first
IProtoSerializer next = GetMoreSpecificSerializer(value);
if (next != null) next.Write(value, dest);
// write all actual fields
//Helpers.DebugWriteLine(">> Writing fields for " + forType.FullName);
for (int i = 0; i < serializers.Length; i++)
{
IProtoSerializer ser = serializers[i];
if (ser.ExpectedType == forType)
{
//Helpers.DebugWriteLine(": " + ser.ToString());
ser.Write(value, dest);
}
}
//Helpers.DebugWriteLine("<< Writing fields for " + forType.FullName);
if (isExtensible) ProtoWriter.AppendExtensionData((IExtensible)value, dest);
if (isRootType) Callback(value, TypeModel.CallbackType.AfterSerialize, dest.Context);
}
public object Read(object value, ProtoReader source)
{
if (isRootType && value != null) { Callback(value, TypeModel.CallbackType.BeforeDeserialize, source.Context); }
int fieldNumber, lastFieldNumber = 0, lastFieldIndex = 0;
bool fieldHandled;
//Helpers.DebugWriteLine(">> Reading fields for " + forType.FullName);
while ((fieldNumber = source.ReadFieldHeader()) > 0)
{
fieldHandled = false;
if (fieldNumber < lastFieldNumber)
{
lastFieldNumber = lastFieldIndex = 0;
}
for (int i = lastFieldIndex; i < fieldNumbers.Length; i++)
{
if (fieldNumbers[i] == fieldNumber)
{
IProtoSerializer ser = serializers[i];
//Helpers.DebugWriteLine(": " + ser.ToString());
Type serType = ser.ExpectedType;
if (value == null)
{
if (serType == forType) value = CreateInstance(source, true);
}
else
{
if (serType != forType && ((IProtoTypeSerializer)ser).CanCreateInstance()
&& serType
#if COREFX || PROFILE259
.GetTypeInfo()
#endif
.IsSubclassOf(value.GetType()))
{
value = ProtoReader.Merge(source, value, ((IProtoTypeSerializer)ser).CreateInstance(source));
}
}
if (ser.ReturnsValue)
{
value = ser.Read(value, source);
}
else
{ // pop
ser.Read(value, source);
}
lastFieldIndex = i;
lastFieldNumber = fieldNumber;
fieldHandled = true;
break;
}
}
if (!fieldHandled)
{
//Helpers.DebugWriteLine(": [" + fieldNumber + "] (unknown)");
if (value == null) value = CreateInstance(source, true);
if (isExtensible)
{
source.AppendExtensionData((IExtensible)value);
}
else
{
source.SkipField();
}
}
}
//Helpers.DebugWriteLine("<< Reading fields for " + forType.FullName);
if (value == null) value = CreateInstance(source, true);
if (isRootType) { Callback(value, TypeModel.CallbackType.AfterDeserialize, source.Context); }
return value;
}
private object InvokeCallback(MethodInfo method, object obj, SerializationContext context)
{
object result = null;
object[] args;
if (method != null)
{ // pass in a streaming context if one is needed, else null
bool handled;
ParameterInfo[] parameters = method.GetParameters();
switch (parameters.Length)
{
case 0:
args = null;
handled = true;
break;
default:
args = new object[parameters.Length];
handled = true;
for (int i = 0; i < args.Length; i++)
{
object val;
Type paramType = parameters[i].ParameterType;
if (paramType == typeof(SerializationContext)) val = context;
else if (paramType == typeof(System.Type)) val = constructType;
#if PLAT_BINARYFORMATTER
else if (paramType == typeof(System.Runtime.Serialization.StreamingContext)) val = (System.Runtime.Serialization.StreamingContext)context;
#endif
else
{
val = null;
handled = false;
}
args[i] = val;
}
break;
}
if (handled)
{
result = method.Invoke(obj, args);
}
else
{
throw Meta.CallbackSet.CreateInvalidCallbackSignature(method);
}
}
return result;
}
object CreateInstance(ProtoReader source, bool includeLocalCallback)
{
//Helpers.DebugWriteLine("* creating : " + forType.FullName);
object obj;
if (factory != null)
{
obj = InvokeCallback(factory, null, source.Context);
}
else if (useConstructor)
{
if (!hasConstructor) TypeModel.ThrowCannotCreateInstance(constructType);
#if PROFILE259
ConstructorInfo constructorInfo = System.Linq.Enumerable.First(
constructType.GetTypeInfo().DeclaredConstructors, c => c.GetParameters().Length == 0);
obj = constructorInfo.Invoke(new object[] {});
#else
obj = Activator.CreateInstance(constructType
#if !(CF || PORTABLE || NETSTANDARD1_3 || NETSTANDARD1_4 || UAP)
, nonPublic: true
#endif
);
#endif
}
else
{
obj = BclHelpers.GetUninitializedObject(constructType);
}
ProtoReader.NoteObject(obj, source);
if (baseCtorCallbacks != null)
{
for (int i = 0; i < baseCtorCallbacks.Length; i++)
{
InvokeCallback(baseCtorCallbacks[i], obj, source.Context);
}
}
if (includeLocalCallback && callbacks != null) InvokeCallback(callbacks.BeforeDeserialize, obj, source.Context);
return obj;
}
bool IProtoSerializer.RequiresOldValue { get { return true; } }
bool IProtoSerializer.ReturnsValue { get { return false; } } // updates field directly
#if FEAT_COMPILER
void IProtoSerializer.EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
Type expected = ExpectedType;
using (Compiler.Local loc = ctx.GetLocalWithValue(expected, valueFrom))
{
// pre-callbacks
EmitCallbackIfNeeded(ctx, loc, TypeModel.CallbackType.BeforeSerialize);
Compiler.CodeLabel startFields = ctx.DefineLabel();
// inheritance
if (CanHaveInheritance)
{
for (int i = 0; i < serializers.Length; i++)
{
IProtoSerializer ser = serializers[i];
Type serType = ser.ExpectedType;
if (serType != forType)
{
Compiler.CodeLabel ifMatch = ctx.DefineLabel(), nextTest = ctx.DefineLabel();
ctx.LoadValue(loc);
ctx.TryCast(serType);
ctx.CopyValue();
ctx.BranchIfTrue(ifMatch, true);
ctx.DiscardValue();
ctx.Branch(nextTest, true);
ctx.MarkLabel(ifMatch);
if (Helpers.IsValueType(serType))
{
ctx.DiscardValue();
ctx.LoadValue(loc);
ctx.CastFromObject(serType);
}
ser.EmitWrite(ctx, null);
ctx.Branch(startFields, false);
ctx.MarkLabel(nextTest);
}
}
if (constructType != null && constructType != forType)
{
using (Compiler.Local actualType = new Compiler.Local(ctx, ctx.MapType(typeof(System.Type))))
{
// would have jumped to "fields" if an expected sub-type, so two options:
// a: *exactly* that type, b: an *unexpected* type
ctx.LoadValue(loc);
ctx.EmitCall(ctx.MapType(typeof(object)).GetMethod("GetType"));
ctx.CopyValue();
ctx.StoreValue(actualType);
ctx.LoadValue(forType);
ctx.BranchIfEqual(startFields, true);
ctx.LoadValue(actualType);
ctx.LoadValue(constructType);
ctx.BranchIfEqual(startFields, true);
}
}
else
{
// would have jumped to "fields" if an expected sub-type, so two options:
// a: *exactly* that type, b: an *unexpected* type
ctx.LoadValue(loc);
ctx.EmitCall(ctx.MapType(typeof(object)).GetMethod("GetType"));
ctx.LoadValue(forType);
ctx.BranchIfEqual(startFields, true);
}
// unexpected, then... note that this *might* be a proxy, which
// is handled by ThrowUnexpectedSubtype
ctx.LoadValue(forType);
ctx.LoadValue(loc);
ctx.EmitCall(ctx.MapType(typeof(object)).GetMethod("GetType"));
ctx.EmitCall(ctx.MapType(typeof(TypeModel)).GetMethod("ThrowUnexpectedSubtype",
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static));
}
// fields
ctx.MarkLabel(startFields);
for (int i = 0; i < serializers.Length; i++)
{
IProtoSerializer ser = serializers[i];
if (ser.ExpectedType == forType) ser.EmitWrite(ctx, loc);
}
// extension data
if (isExtensible)
{
ctx.LoadValue(loc);
ctx.LoadReaderWriter();
ctx.EmitCall(ctx.MapType(typeof(ProtoWriter)).GetMethod("AppendExtensionData"));
}
// post-callbacks
EmitCallbackIfNeeded(ctx, loc, TypeModel.CallbackType.AfterSerialize);
}
}
static void EmitInvokeCallback(Compiler.CompilerContext ctx, MethodInfo method, bool copyValue, Type constructType, Type type)
{
if (method != null)
{
if (copyValue) ctx.CopyValue(); // assumes the target is on the stack, and that we want to *retain* it on the stack
ParameterInfo[] parameters = method.GetParameters();
bool handled = true;
for (int i = 0; i < parameters.Length; i++)
{
Type parameterType = parameters[i].ParameterType;
if (parameterType == ctx.MapType(typeof(SerializationContext)))
{
ctx.LoadSerializationContext();
}
else if (parameterType == ctx.MapType(typeof(System.Type)))
{
Type tmp = constructType;
if (tmp == null) tmp = type; // no ?? in some C# profiles
ctx.LoadValue(tmp);
}
#if PLAT_BINARYFORMATTER
else if (parameterType == ctx.MapType(typeof(System.Runtime.Serialization.StreamingContext)))
{
ctx.LoadSerializationContext();
MethodInfo op = ctx.MapType(typeof(SerializationContext)).GetMethod("op_Implicit", new Type[] { ctx.MapType(typeof(SerializationContext)) });
if (op != null)
{ // it isn't always! (framework versions, etc)
ctx.EmitCall(op);
handled = true;
}
}
#endif
else
{
handled = false;
}
}
if (handled)
{
ctx.EmitCall(method);
if (constructType != null)
{
if (method.ReturnType == ctx.MapType(typeof(object)))
{
ctx.CastFromObject(type);
}
}
}
else
{
throw Meta.CallbackSet.CreateInvalidCallbackSignature(method);
}
}
}
private void EmitCallbackIfNeeded(Compiler.CompilerContext ctx, Compiler.Local valueFrom, TypeModel.CallbackType callbackType)
{
Helpers.DebugAssert(valueFrom != null);
if (isRootType && ((IProtoTypeSerializer)this).HasCallbacks(callbackType))
{
((IProtoTypeSerializer)this).EmitCallback(ctx, valueFrom, callbackType);
}
}
void IProtoTypeSerializer.EmitCallback(Compiler.CompilerContext ctx, Compiler.Local valueFrom, TypeModel.CallbackType callbackType)
{
bool actuallyHasInheritance = false;
if (CanHaveInheritance)
{
for (int i = 0; i < serializers.Length; i++)
{
IProtoSerializer ser = serializers[i];
if (ser.ExpectedType != forType && ((IProtoTypeSerializer)ser).HasCallbacks(callbackType))
{
actuallyHasInheritance = true;
}
}
}
Helpers.DebugAssert(((IProtoTypeSerializer)this).HasCallbacks(callbackType), "Shouldn't be calling this if there is nothing to do");
MethodInfo method = callbacks?[callbackType];
if (method == null && !actuallyHasInheritance)
{
return;
}
ctx.LoadAddress(valueFrom, ExpectedType);
EmitInvokeCallback(ctx, method, actuallyHasInheritance, null, forType);
if (actuallyHasInheritance)
{
Compiler.CodeLabel @break = ctx.DefineLabel();
for (int i = 0; i < serializers.Length; i++)
{
IProtoSerializer ser = serializers[i];
IProtoTypeSerializer typeser;
Type serType = ser.ExpectedType;
if (serType != forType &&
(typeser = (IProtoTypeSerializer)ser).HasCallbacks(callbackType))
{
Compiler.CodeLabel ifMatch = ctx.DefineLabel(), nextTest = ctx.DefineLabel();
ctx.CopyValue();
ctx.TryCast(serType);
ctx.CopyValue();
ctx.BranchIfTrue(ifMatch, true);
ctx.DiscardValue();
ctx.Branch(nextTest, false);
ctx.MarkLabel(ifMatch);
typeser.EmitCallback(ctx, null, callbackType);
ctx.Branch(@break, false);
ctx.MarkLabel(nextTest);
}
}
ctx.MarkLabel(@break);
ctx.DiscardValue();
}
}
void IProtoSerializer.EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
Type expected = ExpectedType;
Helpers.DebugAssert(valueFrom != null);
using (Compiler.Local loc = ctx.GetLocalWithValue(expected, valueFrom))
using (Compiler.Local fieldNumber = new Compiler.Local(ctx, ctx.MapType(typeof(int))))
{
// pre-callbacks
if (HasCallbacks(TypeModel.CallbackType.BeforeDeserialize))
{
if (Helpers.IsValueType(ExpectedType))
{
EmitCallbackIfNeeded(ctx, loc, TypeModel.CallbackType.BeforeDeserialize);
}
else
{ // could be null
Compiler.CodeLabel callbacksDone = ctx.DefineLabel();
ctx.LoadValue(loc);
ctx.BranchIfFalse(callbacksDone, false);
EmitCallbackIfNeeded(ctx, loc, TypeModel.CallbackType.BeforeDeserialize);
ctx.MarkLabel(callbacksDone);
}
}
Compiler.CodeLabel @continue = ctx.DefineLabel(), processField = ctx.DefineLabel();
ctx.Branch(@continue, false);
ctx.MarkLabel(processField);
foreach (BasicList.Group group in BasicList.GetContiguousGroups(fieldNumbers, serializers))
{
Compiler.CodeLabel tryNextField = ctx.DefineLabel();
int groupItemCount = group.Items.Count;
if (groupItemCount == 1)
{
// discreet group; use an equality test
ctx.LoadValue(fieldNumber);
ctx.LoadValue(group.First);
Compiler.CodeLabel processThisField = ctx.DefineLabel();
ctx.BranchIfEqual(processThisField, true);
ctx.Branch(tryNextField, false);
WriteFieldHandler(ctx, expected, loc, processThisField, @continue, (IProtoSerializer)group.Items[0]);
}
else
{ // implement as a jump-table-based switch
ctx.LoadValue(fieldNumber);
ctx.LoadValue(group.First);
ctx.Subtract(); // jump-tables are zero-based
Compiler.CodeLabel[] jmp = new Compiler.CodeLabel[groupItemCount];
for (int i = 0; i < groupItemCount; i++)
{
jmp[i] = ctx.DefineLabel();
}
ctx.Switch(jmp);
// write the default...
ctx.Branch(tryNextField, false);
for (int i = 0; i < groupItemCount; i++)
{
WriteFieldHandler(ctx, expected, loc, jmp[i], @continue, (IProtoSerializer)group.Items[i]);
}
}
ctx.MarkLabel(tryNextField);
}
EmitCreateIfNull(ctx, loc);
ctx.LoadReaderWriter();
if (isExtensible)
{
ctx.LoadValue(loc);
ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("AppendExtensionData"));
}
else
{
ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("SkipField"));
}
ctx.MarkLabel(@continue);
ctx.EmitBasicRead("ReadFieldHeader", ctx.MapType(typeof(int)));
ctx.CopyValue();
ctx.StoreValue(fieldNumber);
ctx.LoadValue(0);
ctx.BranchIfGreater(processField, false);
EmitCreateIfNull(ctx, loc);
// post-callbacks
EmitCallbackIfNeeded(ctx, loc, TypeModel.CallbackType.AfterDeserialize);
if (valueFrom != null && !loc.IsSame(valueFrom))
{
ctx.LoadValue(loc);
ctx.Cast(valueFrom.Type);
ctx.StoreValue(valueFrom);
}
}
}
private void WriteFieldHandler(
Compiler.CompilerContext ctx, Type expected, Compiler.Local loc,
Compiler.CodeLabel handler, Compiler.CodeLabel @continue, IProtoSerializer serializer)
{
ctx.MarkLabel(handler);
Type serType = serializer.ExpectedType;
if (serType == forType)
{
EmitCreateIfNull(ctx, loc);
serializer.EmitRead(ctx, loc);
}
else
{
//RuntimeTypeModel rtm = (RuntimeTypeModel)ctx.Model;
if (((IProtoTypeSerializer)serializer).CanCreateInstance())
{
Compiler.CodeLabel allDone = ctx.DefineLabel();
ctx.LoadValue(loc);
ctx.BranchIfFalse(allDone, false); // null is always ok
ctx.LoadValue(loc);
ctx.TryCast(serType);
ctx.BranchIfTrue(allDone, false); // not null, but of the correct type
// otherwise, need to convert it
ctx.LoadReaderWriter();
ctx.LoadValue(loc);
((IProtoTypeSerializer)serializer).EmitCreateInstance(ctx);
ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("Merge"));
ctx.Cast(expected);
ctx.StoreValue(loc); // Merge always returns a value
// nothing needs doing
ctx.MarkLabel(allDone);
}
if (Helpers.IsValueType(serType))
{
Compiler.CodeLabel initValue = ctx.DefineLabel();
Compiler.CodeLabel hasValue = ctx.DefineLabel();
using (Compiler.Local emptyValue = new Compiler.Local(ctx, serType))
{
ctx.LoadValue(loc);
ctx.BranchIfFalse(initValue, false);
ctx.LoadValue(loc);
ctx.CastFromObject(serType);
ctx.Branch(hasValue, false);
ctx.MarkLabel(initValue);
ctx.InitLocal(serType, emptyValue);
ctx.LoadValue(emptyValue);
ctx.MarkLabel(hasValue);
}
}
else
{
ctx.LoadValue(loc);
ctx.Cast(serType);
}
serializer.EmitRead(ctx, null);
}
if (serializer.ReturnsValue)
{ // update the variable
if (Helpers.IsValueType(serType))
{
// but box it first in case of value type
ctx.CastToObject(serType);
}
ctx.StoreValue(loc);
}
ctx.Branch(@continue, false); // "continue"
}
void IProtoTypeSerializer.EmitCreateInstance(Compiler.CompilerContext ctx)
{
// different ways of creating a new instance
bool callNoteObject = true;
if (factory != null)
{
EmitInvokeCallback(ctx, factory, false, constructType, forType);
}
else if (!useConstructor)
{ // DataContractSerializer style
ctx.LoadValue(constructType);
ctx.EmitCall(ctx.MapType(typeof(BclHelpers)).GetMethod("GetUninitializedObject"));
ctx.Cast(forType);
}
else if (Helpers.IsClass(constructType) && hasConstructor)
{ // XmlSerializer style
ctx.EmitCtor(constructType);
}
else
{
ctx.LoadValue(ExpectedType);
ctx.EmitCall(ctx.MapType(typeof(TypeModel)).GetMethod("ThrowCannotCreateInstance",
BindingFlags.Static | BindingFlags.Public));
ctx.LoadNullRef();
callNoteObject = false;
}
if (callNoteObject)
{
// track root object creation
ctx.CopyValue();
ctx.LoadReaderWriter();
ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("NoteObject",
BindingFlags.Static | BindingFlags.Public));
}
if (baseCtorCallbacks != null)
{
for (int i = 0; i < baseCtorCallbacks.Length; i++)
{
EmitInvokeCallback(ctx, baseCtorCallbacks[i], true, null, forType);
}
}
}
private void EmitCreateIfNull(Compiler.CompilerContext ctx, Compiler.Local storage)
{
Helpers.DebugAssert(storage != null);
if (!Helpers.IsValueType(ExpectedType))
{
Compiler.CodeLabel afterNullCheck = ctx.DefineLabel();
ctx.LoadValue(storage);
ctx.BranchIfTrue(afterNullCheck, false);
((IProtoTypeSerializer)this).EmitCreateInstance(ctx);
if (callbacks != null) EmitInvokeCallback(ctx, callbacks.BeforeDeserialize, true, null, forType);
ctx.StoreValue(storage);
ctx.MarkLabel(afterNullCheck);
}
}
#endif
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b3f577c98285d56469b3eb1c9190e174
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,43 @@
#if !NO_RUNTIME
using System;
namespace ProtoBuf.Serializers
{
class UInt16Serializer : IProtoSerializer
{
static readonly Type expectedType = typeof(ushort);
public UInt16Serializer(ProtoBuf.Meta.TypeModel model)
{
}
public virtual Type ExpectedType => expectedType;
bool IProtoSerializer.RequiresOldValue => false;
bool IProtoSerializer.ReturnsValue => true;
public virtual object Read(object value, ProtoReader source)
{
Helpers.DebugAssert(value == null); // since replaces
return source.ReadUInt16();
}
public virtual void Write(object value, ProtoWriter dest)
{
ProtoWriter.WriteUInt16((ushort)value, dest);
}
#if FEAT_COMPILER
void IProtoSerializer.EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
ctx.EmitBasicWrite("WriteUInt16", valueFrom);
}
void IProtoSerializer.EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
ctx.EmitBasicRead("ReadUInt16", ctx.MapType(typeof(ushort)));
}
#endif
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 95fff5b2239c48c4cbb32346fee1be94
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,43 @@
#if !NO_RUNTIME
using System;
namespace ProtoBuf.Serializers
{
sealed class UInt32Serializer : IProtoSerializer
{
static readonly Type expectedType = typeof(uint);
public UInt32Serializer(ProtoBuf.Meta.TypeModel model)
{
}
public Type ExpectedType => expectedType;
bool IProtoSerializer.RequiresOldValue => false;
bool IProtoSerializer.ReturnsValue => true;
public object Read(object value, ProtoReader source)
{
Helpers.DebugAssert(value == null); // since replaces
return source.ReadUInt32();
}
public void Write(object value, ProtoWriter dest)
{
ProtoWriter.WriteUInt32((uint)value, dest);
}
#if FEAT_COMPILER
void IProtoSerializer.EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
ctx.EmitBasicWrite("WriteUInt32", valueFrom);
}
void IProtoSerializer.EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
ctx.EmitBasicRead("ReadUInt32", ctx.MapType(typeof(uint)));
}
#endif
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 79149f5f69e868c45a17d389322e4bb7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,43 @@
#if !NO_RUNTIME
using System;
namespace ProtoBuf.Serializers
{
sealed class UInt64Serializer : IProtoSerializer
{
static readonly Type expectedType = typeof(ulong);
public UInt64Serializer(ProtoBuf.Meta.TypeModel model)
{
}
public Type ExpectedType => expectedType;
bool IProtoSerializer.RequiresOldValue => false;
bool IProtoSerializer.ReturnsValue => true;
public object Read(object value, ProtoReader source)
{
Helpers.DebugAssert(value == null); // since replaces
return source.ReadUInt64();
}
public void Write(object value, ProtoWriter dest)
{
ProtoWriter.WriteUInt64((ulong)value, dest);
}
#if FEAT_COMPILER
void IProtoSerializer.EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
ctx.EmitBasicWrite("WriteUInt64", valueFrom);
}
void IProtoSerializer.EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
ctx.EmitBasicRead("ReadUInt64", ExpectedType);
}
#endif
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e549c20b3409c4a4dbf0e7fc25062c71
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,62 @@
#if !NO_RUNTIME
using System;
using System.Reflection;
#if FEAT_COMPILER
using ProtoBuf.Compiler;
#endif
namespace ProtoBuf.Serializers
{
sealed class UriDecorator : ProtoDecoratorBase
{
static readonly Type expectedType = typeof(Uri);
public UriDecorator(ProtoBuf.Meta.TypeModel model, IProtoSerializer tail) : base(tail)
{
}
public override Type ExpectedType => expectedType;
public override bool RequiresOldValue => false;
public override bool ReturnsValue => true;
public override void Write(object value, ProtoWriter dest)
{
Tail.Write(((Uri)value).OriginalString, dest);
}
public override object Read(object value, ProtoReader source)
{
Helpers.DebugAssert(value == null); // not expecting incoming
string s = (string)Tail.Read(null, source);
return s.Length == 0 ? null : new Uri(s, UriKind.RelativeOrAbsolute);
}
#if FEAT_COMPILER
protected override void EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
ctx.LoadValue(valueFrom);
ctx.LoadValue(typeof(Uri).GetProperty("OriginalString"));
Tail.EmitWrite(ctx, null);
}
protected override void EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
Tail.EmitRead(ctx, valueFrom);
ctx.CopyValue();
Compiler.CodeLabel @nonEmpty = ctx.DefineLabel(), @end = ctx.DefineLabel();
ctx.LoadValue(typeof(string).GetProperty("Length"));
ctx.BranchIfTrue(@nonEmpty, true);
ctx.DiscardValue();
ctx.LoadNullRef();
ctx.Branch(@end, true);
ctx.MarkLabel(@nonEmpty);
ctx.LoadValue((int)UriKind.RelativeOrAbsolute);
ctx.EmitCtor(ctx.MapType(typeof(Uri)), ctx.MapType(typeof(string)), ctx.MapType(typeof(UriKind)));
ctx.MarkLabel(@end);
}
#endif
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b784c432eb5cbf742b3d96161e7c8d73
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: